" Test for the search command source shared.vim source screendump.vim source check.vim func Test_search_cmdline() CheckOption incsearch " need to disable char_avail, " so that expansion of commandline works call test_override("char_avail", 1) new call setline(1, [' 1', ' 2 these', ' 3 the', ' 4 their', ' 5 there', ' 6 their', ' 7 the', ' 8 them', ' 9 these', ' 10 foobar']) " Test 1 " CTRL-N / CTRL-P skips through the previous search history set noincsearch :1 call feedkeys("/foobar\", 'tx') call feedkeys("/the\", 'tx') call assert_equal('the', @/) call feedkeys("/thes\\\", 'tx') call assert_equal('foobar', @/) " Test 2 " Ctrl-G goes from one match to the next " until the end of the buffer set incsearch nowrapscan :1 " first match call feedkeys("/the\", 'tx') call assert_equal(' 2 these', getline('.')) :1 " second match call feedkeys("/the\\", 'tx') call assert_equal(' 3 the', getline('.')) call assert_equal([0, 0, 0, 0], getpos('"')) :1 " third match call feedkeys("/the".repeat("\", 2)."\", 'tx') call assert_equal(' 4 their', getline('.')) :1 " fourth match call feedkeys("/the".repeat("\", 3)."\", 'tx') call assert_equal(' 5 there', getline('.')) :1 " fifth match call feedkeys("/the".repeat("\", 4)."\", 'tx') call assert_equal(' 6 their', getline('.')) :1 " sixth match call feedkeys("/the".repeat("\", 5)."\", 'tx') call assert_equal(' 7 the', getline('.')) :1 " seventh match call feedkeys("/the".repeat("\", 6)."\", 'tx') call assert_equal(' 8 them', getline('.')) :1 " eighth match call feedkeys("/the".repeat("\", 7)."\", 'tx') call assert_equal(' 9 these', getline('.')) :1 " no further match call feedkeys("/the".repeat("\", 8)."\", 'tx') call assert_equal(' 9 these', getline('.')) call assert_equal([0, 0, 0, 0], getpos('"')) " Test 3 " Ctrl-G goes from one match to the next " and continues back at the top set incsearch wrapscan :1 " first match call feedkeys("/the\", 'tx') call assert_equal(' 2 these', getline('.')) :1 " second match call feedkeys("/the\\", 'tx') call assert_equal(' 3 the', getline('.')) :1 " third match call feedkeys("/the".repeat("\", 2)."\", 'tx') call assert_equal(' 4 their', getline('.')) :1 " fourth match call feedkeys("/the".repeat("\", 3)."\", 'tx') call assert_equal(' 5 there', getline('.')) :1 " fifth match call feedkeys("/the".repeat("\", 4)."\", 'tx') call assert_equal(' 6 their', getline('.')) :1 " sixth match call feedkeys("/the".repeat("\", 5)."\", 'tx') call assert_equal(' 7 the', getline('.')) :1 " seventh match call feedkeys("/the".repeat("\", 6)."\", 'tx') call assert_equal(' 8 them', getline('.')) :1 " eighth match call feedkeys("/the".repeat("\", 7)."\", 'tx') call assert_equal(' 9 these', getline('.')) :1 " back at first match call feedkeys("/the".repeat("\", 8)."\", 'tx') call assert_equal(' 2 these', getline('.')) " Test 4 " CTRL-T goes to the previous match set incsearch nowrapscan $ " first match call feedkeys("?the\", 'tx') call assert_equal(' 9 these', getline('.')) $ " first match call feedkeys("?the\\", 'tx') call assert_equal(' 9 these', getline('.')) $ " second match call feedkeys("?the".repeat("\", 1)."\", 'tx') call assert_equal(' 8 them', getline('.')) $ " last match call feedkeys("?the".repeat("\", 7)."\", 'tx') call assert_equal(' 2 these', getline('.')) $ " last match call feedkeys("?the".repeat("\", 8)."\", 'tx') call assert_equal(' 2 these', getline('.')) " Test 5 " CTRL-T goes to the previous match set incsearch wrapscan $ " first match call feedkeys("?the\", 'tx') call assert_equal(' 9 these', getline('.')) $ " first match at the top call feedkeys("?the\\", 'tx') call assert_equal(' 2 these', getline('.')) $ " second match call feedkeys("?the".repeat("\", 1)."\", 'tx') call assert_equal(' 8 them', getline('.')) $ " last match call feedkeys("?the".repeat("\", 7)."\", 'tx') call assert_equal(' 2 these', getline('.')) $ " back at the bottom of the buffer call feedkeys("?the".repeat("\", 8)."\", 'tx') call assert_equal(' 9 these', getline('.')) " Test 6 " CTRL-L adds to the search pattern set incsearch wrapscan 1 " first match call feedkeys("/the\\", 'tx') call assert_equal(' 2 these', getline('.')) 1 " go to next match of 'thes' call feedkeys("/the\\\", 'tx') call assert_equal(' 9 these', getline('.')) 1 " wrap around call feedkeys("/the\\\\", 'tx') call assert_equal(' 2 these', getline('.')) 1 " wrap around set nowrapscan call feedkeys("/the\\\\", 'tx') call assert_equal(' 9 these', getline('.')) " Test 7 " remove from match, but stay at current match set incsearch wrapscan 1 " first match call feedkeys("/thei\", 'tx') call assert_equal(' 4 their', getline('.')) 1 " delete one char, add another call feedkeys("/thei\s\", 'tx') call assert_equal(' 2 these', getline('.')) 1 " delete one char, add another, go to previous match, add one char call feedkeys("/thei\s\\\\", 'tx') call assert_equal(' 9 these', getline('.')) 1 " delete all chars, start from the beginning again call feedkeys("/them". repeat("\",4).'the\>'."\", 'tx') call assert_equal(' 3 the', getline('.')) " clean up call test_override("char_avail", 0) bw! endfunc func Test_search_cmdline2() CheckOption incsearch " need to disable char_avail, " so that expansion of commandline works call test_override("char_avail", 1) new call setline(1, [' 1', ' 2 these', ' 3 the theother']) " Test 1 " Ctrl-T goes correctly back and forth set incsearch 1 " first match call feedkeys("/the\", 'tx') call assert_equal(' 2 these', getline('.')) 1 " go to next match (on next line) call feedkeys("/the\\", 'tx') call assert_equal(' 3 the theother', getline('.')) 1 " go to next match (still on line 3) call feedkeys("/the\\\", 'tx') call assert_equal(' 3 the theother', getline('.')) 1 " go to next match (still on line 3) call feedkeys("/the\\\\", 'tx') call assert_equal(' 3 the theother', getline('.')) 1 " go to previous match (on line 3) call feedkeys("/the\\\\\", 'tx') call assert_equal(' 3 the theother', getline('.')) 1 " go to previous match (on line 3) call feedkeys("/the\\\\\\", 'tx') call assert_equal(' 3 the theother', getline('.')) 1 " go to previous match (on line 2) call feedkeys("/the\\\\\\\", 'tx') call assert_equal(' 2 these', getline('.')) 1 " go to previous match (on line 2) call feedkeys("/the\\\\", 'tx') call assert_equal('theother', @/) " Test 2: keep the view, " after deleting a character from the search cmd call setline(1, [' 1', ' 2 these', ' 3 the', ' 4 their', ' 5 there', ' 6 their', ' 7 the', ' 8 them', ' 9 these', ' 10 foobar']) resize 5 1 call feedkeys("/foo\\", 'tx') redraw call assert_equal({'lnum': 10, 'leftcol': 0, 'col': 4, 'topfill': 0, 'topline': 6, 'coladd': 0, 'skipcol': 0, 'curswant': 4}, winsaveview()) " remove all history entries for i in range(11) call histdel('/') endfor " Test 3: reset the view, " after deleting all characters from the search cmd norm! 1gg0 " unfortunately, neither "/foo\\", nor "/foo\\\\", " nor "/foo\\" works to delete the commandline. " In that case Vim should return "E35 no previous regular expression", " but it looks like Vim still sees /foo and therefore the test fails. " Therefore, disabling this test "call assert_fails(feedkeys("/foo\\", 'tx'), 'E35:') "call assert_equal({'lnum': 1, 'leftcol': 0, 'col': 0, 'topfill': 0, 'topline': 1, 'coladd': 0, 'skipcol': 0, 'curswant': 0}, winsaveview()) " clean up set noincsearch call test_override("char_avail", 0) bw! endfunc func Test_use_sub_pat() split let @/ = '' func X() s/^/a/ / endfunc call X() bwipe! endfunc func Test_searchpair() new call setline(1, ['other code', 'here [', ' [', ' " cursor here', ' ]]']) " should not give an error for using "42" call assert_equal(0, searchpair('a', 'b', 'c', '', 42)) 4 call assert_equal(3, searchpair('\[', '', ']', 'bW')) call assert_equal([0, 3, 2, 0], getpos('.')) 4 call assert_equal(2, searchpair('\[', '', ']', 'bWr')) call assert_equal([0, 2, 6, 0], getpos('.')) 4 call assert_equal(1, searchpair('\[', '', ']', 'bWm')) call assert_equal([0, 3, 2, 0], getpos('.')) 4|norm ^ call assert_equal(5, searchpair('\[', '', ']', 'Wn')) call assert_equal([0, 4, 2, 0], getpos('.')) 4 call assert_equal(2, searchpair('\[', '', ']', 'bW', \ 'getline(".") =~ "^\\s*\["')) call assert_equal([0, 2, 6, 0], getpos('.')) set nomagic 4 call assert_equal(3, searchpair('\[', '', ']', 'bW')) call assert_equal([0, 3, 2, 0], getpos('.')) set magic 4|norm ^ call assert_equal(0, searchpair('{', '', '}', 'bW')) call assert_equal([0, 4, 2, 0], getpos('.')) %d call setline(1, ['if 1', ' if 2', ' else', ' endif 2', 'endif 1']) /\', '\', '\', 'W')) call assert_equal([0, 5, 1, 0], getpos('.')) /\', '\', '\', 'W')) call assert_equal([0, 3, 3, 0], getpos('.')) bwipe! endfunc func Test_searchpair_timeout() CheckFeature reltime func Waitabit() sleep 20m return 1 " skip match endfunc new call setline(1, range(100)) call setline(1, "(start here") call setline(100, "end here)") let starttime = reltime() " A timeout of 100 msec should happen after about five times of 20 msec wait " in Waitabit(). When the timeout applies to each search the elapsed time " will be much longer. call assert_equal(0, searchpair('(', '\d', ')', '', "Waitabit()", 0, 100)) let elapsed = reltime(starttime)->reltimefloat() call assert_inrange(0.09, 0.300, elapsed) bwipe! endfunc func SearchpairSkip() let id = synID(line('.'), col('.'), 0) let attr = synIDattr(id, 'name') return attr !~ 'comment' endfunc func Test_searchpair_timeout_with_skip() let g:test_is_flaky = 1 edit ../evalfunc.c if has('win32') " Windows timeouts are rather coarse grained, about 16ms. let ms = 20 let min_time = 0.016 let max_time = min_time * 10.0 else let ms = 1 let min_time = 0.001 let max_time = min_time * 10.0 if RunningWithValgrind() let max_time += 0.04 " this can be slow with valgrind endif endif let start = reltime() let found = searchpair('(', '', ')', 'crnm', 'SearchpairSkip()', 0, ms) let elapsed = reltimefloat(reltime(start)) call assert_inrange(min_time, max_time, elapsed) bwipe! endfunc func Test_searchpairpos() new call setline(1, ['other code', 'here [', ' [', ' " cursor here', ' ]]']) 4 call assert_equal([3, 2], searchpairpos('\[', '', ']', 'bW')) call assert_equal([0, 3, 2, 0], getpos('.')) 4 call assert_equal([2, 6], searchpairpos('\[', '', ']', 'bWr')) call assert_equal([0, 2, 6, 0], getpos('.')) 4|norm ^ call assert_equal([5, 2], searchpairpos('\[', '', ']', 'Wn')) call assert_equal([0, 4, 2, 0], getpos('.')) 4 call assert_equal([2, 6], searchpairpos('\[', '', ']', 'bW', \ 'getline(".") =~ "^\\s*\["')) call assert_equal([0, 2, 6, 0], getpos('.')) 4 call assert_equal([2, 6], searchpairpos('\[', '', ']', 'bWr')) call assert_equal([0, 2, 6, 0], getpos('.')) set nomagic 4 call assert_equal([3, 2], searchpairpos('\[', '', ']', 'bW')) call assert_equal([0, 3, 2, 0], getpos('.')) set magic 4|norm ^ call assert_equal([0, 0], searchpairpos('{', '', '}', 'bW')) call assert_equal([0, 4, 2, 0], getpos('.')) %d call setline(1, ['if 1', ' if 2', ' else', ' endif 2', 'endif 1']) /\', '\', '\', 'W')) call assert_equal([0, 5, 1, 0], getpos('.')) /\', '\', '\', 'W')) call assert_equal([0, 3, 3, 0], getpos('.')) q! endfunc func Test_searchpair_errors() call assert_fails("call searchpair([0], 'middle', 'end', 'bW', 'skip', 99, 100)", 'E730: Using a List as a String') call assert_fails("call searchpair('start', {-> 0}, 'end', 'bW', 'skip', 99, 100)", 'E729: Using a Funcref as a String') call assert_fails("call searchpair('start', 'middle', {'one': 1}, 'bW', 'skip', 99, 100)", 'E731: Using a Dictionary as a String') call assert_fails("call searchpair('start', 'middle', 'end', 'flags', 'skip', 99, 100)", 'E475: Invalid argument: flags') call assert_fails("call searchpair('start', 'middle', 'end', 'bW', 'func', -99, 100)", 'E475: Invalid argument: -99') call assert_fails("call searchpair('start', 'middle', 'end', 'bW', 'func', 99, -100)", 'E475: Invalid argument: -100') call assert_fails("call searchpair('start', 'middle', 'end', 'e')", 'E475: Invalid argument: e') call assert_fails("call searchpair('start', 'middle', 'end', 'sn')", 'E475: Invalid argument: sn') endfunc func Test_searchpairpos_errors() call assert_fails("call searchpairpos([0], 'middle', 'end', 'bW', 'skip', 99, 100)", 'E730: Using a List as a String') call assert_fails("call searchpairpos('start', {-> 0}, 'end', 'bW', 'skip', 99, 100)", 'E729: Using a Funcref as a String') call assert_fails("call searchpairpos('start', 'middle', {'one': 1}, 'bW', 'skip', 99, 100)", 'E731: Using a Dictionary as a String') call assert_fails("call searchpairpos('start', 'middle', 'end', 'flags', 'skip', 99, 100)", 'E475: Invalid argument: flags') call assert_fails("call searchpairpos('start', 'middle', 'end', 'bW', 'func', -99, 100)", 'E475: Invalid argument: -99') call assert_fails("call searchpairpos('start', 'middle', 'end', 'bW', 'func', 99, -100)", 'E475: Invalid argument: -100') call assert_fails("call searchpairpos('start', 'middle', 'end', 'e')", 'E475: Invalid argument: e') call assert_fails("call searchpairpos('start', 'middle', 'end', 'sn')", 'E475: Invalid argument: sn') endfunc func Test_searchpair_skip() func Zero() return 0 endfunc func Partial(x) return a:x endfunc new call setline(1, ['{', 'foo', 'foo', 'foo', '}']) 3 | call assert_equal(1, searchpair('{', '', '}', 'bWn', '')) 3 | call assert_equal(1, searchpair('{', '', '}', 'bWn', '0')) 3 | call assert_equal(1, searchpair('{', '', '}', 'bWn', {-> 0})) 3 | call assert_equal(1, searchpair('{', '', '}', 'bWn', function('Zero'))) 3 | call assert_equal(1, searchpair('{', '', '}', 'bWn', function('Partial', [0]))) bw! endfunc func Test_searchpair_leak() new call setline(1, 'if one else another endif') " The error in the skip expression caused memory to leak. call assert_fails("call searchpair('\\', '\\', '\\', '', '\"foo\" 2')", 'E15:') bwipe! endfunc func Test_searchc() " These commands used to cause memory overflow in searchc(). new norm ixx exe "norm 0t\u93cf" bw! endfunc func Cmdline3_prep() " need to disable char_avail, " so that expansion of commandline works call test_override("char_avail", 1) new call setline(1, [' 1', ' 2 the~e', ' 3 the theother']) set incsearch endfunc func Incsearch_cleanup() set noincsearch call test_override("char_avail", 0) bw! endfunc func Test_search_cmdline3() CheckOption incsearch call Cmdline3_prep() 1 " first match call feedkeys("/the\\", 'tx') call assert_equal(' 2 the~e', getline('.')) call Incsearch_cleanup() endfunc func Test_search_cmdline3s() CheckOption incsearch call Cmdline3_prep() 1 call feedkeys(":%s/the\/xxx\", 'tx') call assert_equal(' 2 xxxe', getline('.')) undo call feedkeys(":%subs/the\/xxx\", 'tx') call assert_equal(' 2 xxxe', getline('.')) undo call feedkeys(":%substitute/the\/xxx\", 'tx') call assert_equal(' 2 xxxe', getline('.')) undo call feedkeys(":%smagic/the.e/xxx\", 'tx') call assert_equal(' 2 xxx', getline('.')) undo call assert_fails(":%snomagic/the.e/xxx\", 'E486:') " call feedkeys(":%snomagic/the\\.e/xxx\", 'tx') call assert_equal(' 2 xxx', getline('.')) call Incsearch_cleanup() endfunc func Test_search_cmdline3g() CheckOption incsearch call Cmdline3_prep() 1 call feedkeys(":g/the\/d\", 'tx') call assert_equal(' 3 the theother', getline(2)) undo call feedkeys(":global/the\/d\", 'tx') call assert_equal(' 3 the theother', getline(2)) undo call feedkeys(":g!/the\/d\", 'tx') call assert_equal(1, line('$')) call assert_equal(' 2 the~e', getline(1)) undo call feedkeys(":global!/the\/d\", 'tx') call assert_equal(1, line('$')) call assert_equal(' 2 the~e', getline(1)) call Incsearch_cleanup() endfunc func Test_search_cmdline3v() CheckOption incsearch call Cmdline3_prep() 1 call feedkeys(":v/the\/d\", 'tx') call assert_equal(1, line('$')) call assert_equal(' 2 the~e', getline(1)) undo call feedkeys(":vglobal/the\/d\", 'tx') call assert_equal(1, line('$')) call assert_equal(' 2 the~e', getline(1)) call Incsearch_cleanup() endfunc func Test_search_cmdline4() CheckOption incsearch " need to disable char_avail, " so that expansion of commandline works call test_override("char_avail", 1) new call setline(1, [' 1 the first', ' 2 the second', ' 3 the third']) set incsearch $ call feedkeys("?the\\", 'tx') call assert_equal(' 3 the third', getline('.')) $ call feedkeys("?the\\\", 'tx') call assert_equal(' 1 the first', getline('.')) $ call feedkeys("?the\\\\", 'tx') call assert_equal(' 2 the second', getline('.')) $ call feedkeys("?the\\", 'tx') call assert_equal(' 1 the first', getline('.')) $ call feedkeys("?the\\\", 'tx') call assert_equal(' 3 the third', getline('.')) $ call feedkeys("?the\\\\", 'tx') call assert_equal(' 2 the second', getline('.')) " clean up set noincsearch call test_override("char_avail", 0) bw! endfunc func Test_search_cmdline5() CheckOption incsearch " Do not call test_override("char_avail", 1) so that and work " regardless char_avail. new call setline(1, [' 1 the first', ' 2 the second', ' 3 the third', '']) set incsearch 1 call feedkeys("/the\\\", 'tx') call assert_equal(' 3 the third', getline('.')) $ call feedkeys("?the\\\\", 'tx') call assert_equal(' 1 the first', getline('.')) " clean up set noincsearch bw! endfunc func Test_search_cmdline6() " Test that consecutive matches " are caught by / CheckOption incsearch " need to disable char_avail, " so that expansion of commandline works call test_override("char_avail", 1) new call setline(1, [' bbvimb', '']) set incsearch " first match norm! gg0 call feedkeys("/b\", 'tx') call assert_equal([0,1,2,0], getpos('.')) " second match norm! gg0 call feedkeys("/b\\", 'tx') call assert_equal([0,1,3,0], getpos('.')) " third match norm! gg0 call feedkeys("/b\\\", 'tx') call assert_equal([0,1,7,0], getpos('.')) " first match again norm! gg0 call feedkeys("/b\\\\", 'tx') call assert_equal([0,1,2,0], getpos('.')) set nowrapscan " last match norm! gg0 call feedkeys("/b\\\\", 'tx') call assert_equal([0,1,7,0], getpos('.')) " clean up set wrapscan&vim set noincsearch call test_override("char_avail", 0) bw! endfunc func Test_search_cmdline7() " Test that pressing in an empty command line " does not move the cursor CheckOption incsearch " need to disable char_avail, " so that expansion of commandline works call test_override("char_avail", 1) new let @/ = 'b' call setline(1, [' bbvimb', '']) set incsearch " first match norm! gg0 " moves to next match of previous search pattern, just like / call feedkeys("/\\", 'tx') call assert_equal([0,1,2,0], getpos('.')) " moves to next match of previous search pattern, just like / call feedkeys("/\", 'tx') call assert_equal([0,1,3,0], getpos('.')) " moves to next match of previous search pattern, just like / call feedkeys("/\\", 'tx') call assert_equal([0,1,7,0], getpos('.')) " using an offset uses the last search pattern call cursor(1, 1) call setline(1, ['1 bbvimb', ' 2 bbvimb']) let @/ = 'b' call feedkeys("//e\\", 'tx') call assert_equal('1 bbvimb', getline('.')) call assert_equal(4, col('.')) set noincsearch call test_override("char_avail", 0) bw! endfunc func Test_search_cmdline8() " Highlighting is cleared in all windows " since hls applies to all windows CheckOption incsearch CheckFeature terminal CheckNotGui if has("win32") throw "Skipped: Bug with sending to terminal window not fixed yet" endif let h = winheight(0) if h < 3 return endif " Prepare buffer text let lines = ['abb vim vim vi', 'vimvivim'] call writefile(lines, 'Xsearch.txt') let buf = term_start([GetVimProg(), '--clean', '-c', 'set noswapfile', 'Xsearch.txt'], {'term_rows': 3}) call WaitForAssert({-> assert_equal(lines, [term_getline(buf, 1), term_getline(buf, 2)])}) call term_sendkeys(buf, ":set incsearch hlsearch\") call term_sendkeys(buf, ":14vsp\") call term_sendkeys(buf, "/vim\") call term_sendkeys(buf, "/b\") call term_sendkeys(buf, "gg0") call TermWait(buf, 250) let screen_line = term_scrape(buf, 1) let [a0,a1,a2,a3] = [screen_line[3].attr, screen_line[4].attr, \ screen_line[18].attr, screen_line[19].attr] call assert_notequal(a0, a1) call assert_notequal(a0, a3) call assert_notequal(a1, a2) call assert_equal(a0, a2) call assert_equal(a1, a3) " clean up call delete('Xsearch.txt') bwipe! endfunc " Tests for regexp with various magic settings func Run_search_regexp_magic_opt() put ='1 a aa abb abbccc' exe 'normal! /a*b\{2}c\+/e' . "\" call assert_equal([0, 2, 17, 0], getpos('.')) put ='2 d dd dee deefff' exe 'normal! /\Md\*e\{2}f\+/e' . "\" call assert_equal([0, 3, 17, 0], getpos('.')) set nomagic put ='3 g gg ghh ghhiii' exe 'normal! /g\*h\{2}i\+/e' . "\" call assert_equal([0, 4, 17, 0], getpos('.')) put ='4 j jj jkk jkklll' exe 'normal! /\mj*k\{2}l\+/e' . "\" call assert_equal([0, 5, 17, 0], getpos('.')) put ='5 m mm mnn mnnooo' exe 'normal! /\vm*n{2}o+/e' . "\" call assert_equal([0, 6, 17, 0], getpos('.')) put ='6 x ^aa$ x' exe 'normal! /\V^aa$' . "\" call assert_equal([0, 7, 5, 0], getpos('.')) set magic put ='7 (a)(b) abbaa' exe 'normal! /\v(a)(b)\2\1\1/e' . "\" call assert_equal([0, 8, 14, 0], getpos('.')) put ='8 axx [ab]xx' exe 'normal! /\V[ab]\(\[xy]\)\1' . "\" call assert_equal([0, 9, 7, 0], getpos('.')) %d endfunc func Test_search_regexp() enew! set regexpengine=1 call Run_search_regexp_magic_opt() set regexpengine=2 call Run_search_regexp_magic_opt() set regexpengine& set undolevels=100 put ='9 foobar' put ='' exe "normal! a\u\" normal G exe 'normal! dv?bar?' . "\" call assert_equal('9 foo', getline('.')) call assert_equal([0, 2, 5, 0], getpos('.')) call assert_equal(2, line('$')) normal u call assert_equal('9 foobar', getline('.')) call assert_equal([0, 2, 6, 0], getpos('.')) call assert_equal(3, line('$')) set undolevels& enew! endfunc func Test_search_cmdline_incsearch_highlight() CheckOption incsearch set incsearch hlsearch " need to disable char_avail, " so that expansion of commandline works call test_override("char_avail", 1) new call setline(1, ['aaa 1 the first', ' 2 the second', ' 3 the third']) 1 call feedkeys("/second\", 'tx') call assert_equal('second', @/) call assert_equal(' 2 the second', getline('.')) " Canceling search won't change @/ 1 let @/ = 'last pattern' call feedkeys("/third\", 'tx') call assert_equal('last pattern', @/) call feedkeys("/third\", 'tx') call assert_equal('last pattern', @/) call feedkeys("/3\\", 'tx') call assert_equal('last pattern', @/) call feedkeys("/third\\\", 'tx') call assert_equal('last pattern', @/) " clean up set noincsearch nohlsearch bw! endfunc func Test_search_cmdline_incsearch_highlight_attr() CheckOption incsearch CheckFeature terminal CheckNotGui let h = winheight(0) if h < 3 return endif " Prepare buffer text let lines = ['abb vim vim vi', 'vimvivim'] call writefile(lines, 'Xsearch.txt') let buf = term_start([GetVimProg(), '--clean', '-c', 'set noswapfile', 'Xsearch.txt'], {'term_rows': 3}) call WaitForAssert({-> assert_equal(lines, [term_getline(buf, 1), term_getline(buf, 2)])}) " wait for vim to complete initialization call TermWait(buf) " Get attr of normal(a0), incsearch(a1), hlsearch(a2) highlight call term_sendkeys(buf, ":set incsearch hlsearch\") call term_sendkeys(buf, '/b') call TermWait(buf, 100) let screen_line1 = term_scrape(buf, 1) call assert_true(len(screen_line1) > 2) " a0: attr_normal let a0 = screen_line1[0].attr " a1: attr_incsearch let a1 = screen_line1[1].attr " a2: attr_hlsearch let a2 = screen_line1[2].attr call assert_notequal(a0, a1) call assert_notequal(a0, a2) call assert_notequal(a1, a2) call term_sendkeys(buf, "\gg0") " Test incremental highlight search call term_sendkeys(buf, "/vim") call TermWait(buf, 100) " Buffer: " abb vim vim vi " vimvivim " Search: /vim let attr_line1 = [a0,a0,a0,a0,a1,a1,a1,a0,a2,a2,a2,a0,a0,a0] let attr_line2 = [a2,a2,a2,a0,a0,a2,a2,a2] call assert_equal(attr_line1, map(term_scrape(buf, 1)[:len(attr_line1)-1], 'v:val.attr')) call assert_equal(attr_line2, map(term_scrape(buf, 2)[:len(attr_line2)-1], 'v:val.attr')) " Test call term_sendkeys(buf, "\\") call TermWait(buf, 100) let attr_line1 = [a0,a0,a0,a0,a2,a2,a2,a0,a2,a2,a2,a0,a0,a0] let attr_line2 = [a1,a1,a1,a0,a0,a2,a2,a2] call assert_equal(attr_line1, map(term_scrape(buf, 1)[:len(attr_line1)-1], 'v:val.attr')) call assert_equal(attr_line2, map(term_scrape(buf, 2)[:len(attr_line2)-1], 'v:val.attr')) " Test call term_sendkeys(buf, "\") call TermWait(buf, 100) let attr_line1 = [a0,a0,a0,a0,a2,a2,a2,a0,a1,a1,a1,a0,a0,a0] let attr_line2 = [a2,a2,a2,a0,a0,a2,a2,a2] call assert_equal(attr_line1, map(term_scrape(buf, 1)[:len(attr_line1)-1], 'v:val.attr')) call assert_equal(attr_line2, map(term_scrape(buf, 2)[:len(attr_line2)-1], 'v:val.attr')) " Type Enter and a1(incsearch highlight) should become a2(hlsearch highlight) call term_sendkeys(buf, "\") call TermWait(buf, 100) let attr_line1 = [a0,a0,a0,a0,a2,a2,a2,a0,a2,a2,a2,a0,a0,a0] let attr_line2 = [a2,a2,a2,a0,a0,a2,a2,a2] call assert_equal(attr_line1, map(term_scrape(buf, 1)[:len(attr_line1)-1], 'v:val.attr')) call assert_equal(attr_line2, map(term_scrape(buf, 2)[:len(attr_line2)-1], 'v:val.attr')) " Test nohlsearch. a2(hlsearch highlight) should become a0(normal highlight) call term_sendkeys(buf, ":1\") call term_sendkeys(buf, ":set nohlsearch\") call term_sendkeys(buf, "/vim") call TermWait(buf, 100) let attr_line1 = [a0,a0,a0,a0,a1,a1,a1,a0,a0,a0,a0,a0,a0,a0] let attr_line2 = [a0,a0,a0,a0,a0,a0,a0,a0] call assert_equal(attr_line1, map(term_scrape(buf, 1)[:len(attr_line1)-1], 'v:val.attr')) call assert_equal(attr_line2, map(term_scrape(buf, 2)[:len(attr_line2)-1], 'v:val.attr')) call delete('Xsearch.txt') call delete('Xsearch.txt') bwipe! endfunc func Test_incsearch_cmdline_modifier() CheckOption incsearch call test_override("char_avail", 1) new call setline(1, ['foo']) set incsearch " Test that error E14 does not occur in parsing command modifier. call feedkeys("V:tab", 'tx') call Incsearch_cleanup() endfunc func Test_incsearch_scrolling() CheckRunVimInTerminal call assert_equal(0, &scrolloff) call writefile([ \ 'let dots = repeat(".", 120)', \ 'set incsearch cmdheight=2 scrolloff=0', \ 'call setline(1, [dots, dots, dots, "", "target", dots, dots])', \ 'normal gg', \ 'redraw', \ ], 'Xscript') let buf = RunVimInTerminal('-S Xscript', {'rows': 9, 'cols': 70}) " Need to send one key at a time to force a redraw call term_sendkeys(buf, '/') sleep 100m call term_sendkeys(buf, 't') sleep 100m call term_sendkeys(buf, 'a') sleep 100m call term_sendkeys(buf, 'r') sleep 100m call term_sendkeys(buf, 'g') call VerifyScreenDump(buf, 'Test_incsearch_scrolling_01', {}) call term_sendkeys(buf, "\") call StopVimInTerminal(buf) call delete('Xscript') endfunc func Test_incsearch_search_dump() CheckOption incsearch CheckScreendump call writefile([ \ 'set incsearch hlsearch scrolloff=0', \ 'for n in range(1, 8)', \ ' call setline(n, "foo " . n)', \ 'endfor', \ '3', \ ], 'Xis_search_script') let buf = RunVimInTerminal('-S Xis_search_script', {'rows': 9, 'cols': 70}) " Give Vim a chance to redraw to get rid of the spaces in line 2 caused by " the 'ambiwidth' check. sleep 100m " Need to send one key at a time to force a redraw. call term_sendkeys(buf, '/fo') call VerifyScreenDump(buf, 'Test_incsearch_search_01', {}) call term_sendkeys(buf, "\") sleep 100m call term_sendkeys(buf, '/\v') call VerifyScreenDump(buf, 'Test_incsearch_search_02', {}) call term_sendkeys(buf, "\") call StopVimInTerminal(buf) call delete('Xis_search_script') endfunc func Test_hlsearch_dump() CheckOption hlsearch CheckScreendump call writefile([ \ 'set hlsearch cursorline', \ 'call setline(1, ["xxx", "xxx", "xxx"])', \ '/.*', \ '2', \ ], 'Xhlsearch_script') let buf = RunVimInTerminal('-S Xhlsearch_script', {'rows': 6, 'cols': 50}) call VerifyScreenDump(buf, 'Test_hlsearch_1', {}) call term_sendkeys(buf, "/\\_.*\") call VerifyScreenDump(buf, 'Test_hlsearch_2', {}) call StopVimInTerminal(buf) call delete('Xhlsearch_script') endfunc func Test_hlsearch_and_visual() CheckOption hlsearch CheckScreendump call writefile([ \ 'set hlsearch', \ 'call setline(1, repeat(["xxx yyy zzz"], 3))', \ 'hi Search cterm=bold', \ '/yyy', \ 'call cursor(1, 6)', \ ], 'Xhlvisual_script') let buf = RunVimInTerminal('-S Xhlvisual_script', {'rows': 6, 'cols': 40}) call term_sendkeys(buf, "vjj") call VerifyScreenDump(buf, 'Test_hlsearch_visual_1', {}) call term_sendkeys(buf, "\") call StopVimInTerminal(buf) call delete('Xhlvisual_script') endfunc func Test_hlsearch_block_visual_match() CheckScreendump let lines =<< trim END set hlsearch call setline(1, ['aa', 'bbbb', 'cccccc']) END call writefile(lines, 'Xhlsearch_block') let buf = RunVimInTerminal('-S Xhlsearch_block', {'rows': 9, 'cols': 60}) call term_sendkeys(buf, "G\$kk\") sleep 100m call term_sendkeys(buf, "/\\%V\") sleep 100m call VerifyScreenDump(buf, 'Test_hlsearch_block_visual_match', {}) call StopVimInTerminal(buf) call delete('Xhlsearch_block') endfunc func Test_incsearch_substitute() CheckOption incsearch call test_override("char_avail", 1) new set incsearch for n in range(1, 10) call setline(n, 'foo ' . n) endfor 4 call feedkeys(":.,.+2s/foo\o\o/xxx\", 'tx') call assert_equal('foo 3', getline(3)) call assert_equal('xxx 4', getline(4)) call assert_equal('xxx 5', getline(5)) call assert_equal('xxx 6', getline(6)) call assert_equal('foo 7', getline(7)) call Incsearch_cleanup() endfunc func Test_incsearch_substitute_long_line() new call test_override("char_avail", 1) set incsearch call repeat('x', 100000)->setline(1) call feedkeys(':s/\%c', 'xt') redraw call feedkeys("\", 'xt') call Incsearch_cleanup() bwipe! endfunc func Test_hlsearch_cursearch() CheckScreendump let lines =<< trim END set hlsearch scrolloff=0 call setline(1, ['one', 'foo', 'bar', 'baz', 'foo the foo and foo', 'bar']) hi Search ctermbg=yellow hi CurSearch ctermbg=blue END call writefile(lines, 'Xhlsearch_cursearch') let buf = RunVimInTerminal('-S Xhlsearch_cursearch', {'rows': 9, 'cols': 60}) call term_sendkeys(buf, "gg/foo\") call VerifyScreenDump(buf, 'Test_hlsearch_cursearch_single_line_1', {}) call term_sendkeys(buf, "n") call VerifyScreenDump(buf, 'Test_hlsearch_cursearch_single_line_2', {}) call term_sendkeys(buf, "n") call VerifyScreenDump(buf, 'Test_hlsearch_cursearch_single_line_2a', {}) call term_sendkeys(buf, "n") call VerifyScreenDump(buf, 'Test_hlsearch_cursearch_single_line_2b', {}) call term_sendkeys(buf, ":call setline(5, 'foo')\") call term_sendkeys(buf, "0?\") call VerifyScreenDump(buf, 'Test_hlsearch_cursearch_single_line_3', {}) call term_sendkeys(buf, "gg/foo\\nbar\") call VerifyScreenDump(buf, 'Test_hlsearch_cursearch_multiple_line_1', {}) call term_sendkeys(buf, ":call setline(1, ['---', 'abcdefg', 'hijkl', '---', 'abcdefg', 'hijkl'])\") call term_sendkeys(buf, "gg/efg\\nhij\") call VerifyScreenDump(buf, 'Test_hlsearch_cursearch_multiple_line_2', {}) call term_sendkeys(buf, "h\") call VerifyScreenDump(buf, 'Test_hlsearch_cursearch_multiple_line_3', {}) call term_sendkeys(buf, "j\") call VerifyScreenDump(buf, 'Test_hlsearch_cursearch_multiple_line_4', {}) call term_sendkeys(buf, "h\") call VerifyScreenDump(buf, 'Test_hlsearch_cursearch_multiple_line_5', {}) " check clearing CurSearch when using it for another match call term_sendkeys(buf, "G?^abcd\Y") call term_sendkeys(buf, "kkP") call VerifyScreenDump(buf, 'Test_hlsearch_cursearch_changed_1', {}) call StopVimInTerminal(buf) call delete('Xhlsearch_cursearch') endfunc " Similar to Test_incsearch_substitute() but with a screendump halfway. func Test_incsearch_substitute_dump() CheckOption incsearch CheckScreendump call writefile([ \ 'set incsearch hlsearch scrolloff=0', \ 'for n in range(1, 10)', \ ' call setline(n, "foo " . n)', \ 'endfor', \ 'call setline(11, "bar 11")', \ '3', \ ], 'Xis_subst_script') let buf = RunVimInTerminal('-S Xis_subst_script', {'rows': 9, 'cols': 70}) " Give Vim a chance to redraw to get rid of the spaces in line 2 caused by " the 'ambiwidth' check. sleep 100m " Need to send one key at a time to force a redraw. " Select three lines at the cursor with typed pattern. call term_sendkeys(buf, ':.,.+2s/') sleep 100m call term_sendkeys(buf, 'f') sleep 100m call term_sendkeys(buf, 'o') sleep 100m call term_sendkeys(buf, 'o') call VerifyScreenDump(buf, 'Test_incsearch_substitute_01', {}) call term_sendkeys(buf, "\") " Select three lines at the cursor using previous pattern. call term_sendkeys(buf, "/foo\") sleep 100m call term_sendkeys(buf, ':.,.+2s//') call VerifyScreenDump(buf, 'Test_incsearch_substitute_02', {}) " Deleting last slash should remove the match. call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_incsearch_substitute_03', {}) call term_sendkeys(buf, "\") " Reverse range is accepted call term_sendkeys(buf, ':5,2s/foo') call VerifyScreenDump(buf, 'Test_incsearch_substitute_04', {}) call term_sendkeys(buf, "\") " White space after the command is skipped call term_sendkeys(buf, ':2,3sub /fo') call VerifyScreenDump(buf, 'Test_incsearch_substitute_05', {}) call term_sendkeys(buf, "\") " Command modifiers are skipped call term_sendkeys(buf, ':above below browse botr confirm keepmar keepalt keeppat keepjum filter xxx hide lockm leftabove noau noswap rightbel sandbox silent silent! $tab top unsil vert verbose 4,5s/fo.') call VerifyScreenDump(buf, 'Test_incsearch_substitute_06', {}) call term_sendkeys(buf, "\") " Cursorline highlighting at match call term_sendkeys(buf, ":set cursorline\") call term_sendkeys(buf, 'G9G') call term_sendkeys(buf, ':9,11s/bar') call VerifyScreenDump(buf, 'Test_incsearch_substitute_07', {}) call term_sendkeys(buf, "\") " Cursorline highlighting at cursor when no match call term_sendkeys(buf, ':9,10s/bar') call VerifyScreenDump(buf, 'Test_incsearch_substitute_08', {}) call term_sendkeys(buf, "\") " Only \v handled as empty pattern, does not move cursor call term_sendkeys(buf, '3G4G') call term_sendkeys(buf, ":nohlsearch\") call term_sendkeys(buf, ':6,7s/\v') call VerifyScreenDump(buf, 'Test_incsearch_substitute_09', {}) call term_sendkeys(buf, "\") call term_sendkeys(buf, ":set nocursorline\") " All matches are highlighted for 'hlsearch' after the incsearch canceled call term_sendkeys(buf, "1G*") call term_sendkeys(buf, ":2,5s/foo") sleep 100m call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_incsearch_substitute_10', {}) call term_sendkeys(buf, ":split\") call term_sendkeys(buf, ":let @/ = 'xyz'\") call term_sendkeys(buf, ":%s/.") call VerifyScreenDump(buf, 'Test_incsearch_substitute_11', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_incsearch_substitute_12', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_incsearch_substitute_13', {}) call term_sendkeys(buf, ":%bwipe!\") call term_sendkeys(buf, ":only!\") " get :'<,'>s command in history call term_sendkeys(buf, ":set cmdheight=2\") call term_sendkeys(buf, "aasdfasdf\") call term_sendkeys(buf, "V:s/a/b/g\") " Using '<,'> does not give E20 call term_sendkeys(buf, ":new\") call term_sendkeys(buf, "aasdfasdf\") call term_sendkeys(buf, ":\\") call VerifyScreenDump(buf, 'Test_incsearch_substitute_14', {}) call term_sendkeys(buf, "") call StopVimInTerminal(buf) call delete('Xis_subst_script') endfunc func Test_incsearch_highlighting() CheckOption incsearch CheckScreendump call writefile([ \ 'set incsearch hlsearch', \ 'call setline(1, "hello/there")', \ ], 'Xis_subst_hl_script') let buf = RunVimInTerminal('-S Xis_subst_hl_script', {'rows': 4, 'cols': 20}) " Give Vim a chance to redraw to get rid of the spaces in line 2 caused by " the 'ambiwidth' check. sleep 300m " Using a different search delimiter should still highlight matches " that contain a '/'. call term_sendkeys(buf, ":%s;ello/the") call VerifyScreenDump(buf, 'Test_incsearch_substitute_15', {}) call term_sendkeys(buf, "") call StopVimInTerminal(buf) call delete('Xis_subst_hl_script') endfunc func Test_incsearch_with_change() CheckFeature timers CheckOption incsearch CheckScreendump call writefile([ \ 'set incsearch hlsearch scrolloff=0', \ 'call setline(1, ["one", "two ------ X", "three"])', \ 'call timer_start(200, { _ -> setline(2, "x")})', \ ], 'Xis_change_script') let buf = RunVimInTerminal('-S Xis_change_script', {'rows': 9, 'cols': 70}) " Give Vim a chance to redraw to get rid of the spaces in line 2 caused by " the 'ambiwidth' check. sleep 300m " Highlight X, it will be deleted by the timer callback. call term_sendkeys(buf, ':%s/X') call VerifyScreenDump(buf, 'Test_incsearch_change_01', {}) call term_sendkeys(buf, "\") call StopVimInTerminal(buf) call delete('Xis_change_script') endfunc " Similar to Test_incsearch_substitute_dump() for :sort func Test_incsearch_sort_dump() CheckOption incsearch CheckScreendump call writefile([ \ 'set incsearch hlsearch scrolloff=0', \ 'call setline(1, ["another one 2", "that one 3", "the one 1"])', \ ], 'Xis_sort_script') let buf = RunVimInTerminal('-S Xis_sort_script', {'rows': 9, 'cols': 70}) " Give Vim a chance to redraw to get rid of the spaces in line 2 caused by " the 'ambiwidth' check. sleep 100m call term_sendkeys(buf, ':sort ni u /on') call VerifyScreenDump(buf, 'Test_incsearch_sort_01', {}) call term_sendkeys(buf, "\") call term_sendkeys(buf, ':sort! /on') call VerifyScreenDump(buf, 'Test_incsearch_sort_02', {}) call term_sendkeys(buf, "\") call StopVimInTerminal(buf) call delete('Xis_sort_script') endfunc " Similar to Test_incsearch_substitute_dump() for :vimgrep famiry func Test_incsearch_vimgrep_dump() CheckOption incsearch CheckScreendump call writefile([ \ 'set incsearch hlsearch scrolloff=0', \ 'call setline(1, ["another one 2", "that one 3", "the one 1"])', \ ], 'Xis_vimgrep_script') let buf = RunVimInTerminal('-S Xis_vimgrep_script', {'rows': 9, 'cols': 70}) " Give Vim a chance to redraw to get rid of the spaces in line 2 caused by " the 'ambiwidth' check. sleep 100m " Need to send one key at a time to force a redraw. call term_sendkeys(buf, ':vimgrep on') call VerifyScreenDump(buf, 'Test_incsearch_vimgrep_01', {}) call term_sendkeys(buf, "\") call term_sendkeys(buf, ':vimg /on/ *.txt') call VerifyScreenDump(buf, 'Test_incsearch_vimgrep_02', {}) call term_sendkeys(buf, "\") call term_sendkeys(buf, ':vimgrepadd "\") call term_sendkeys(buf, ':lv "tha') call VerifyScreenDump(buf, 'Test_incsearch_vimgrep_04', {}) call term_sendkeys(buf, "\") call term_sendkeys(buf, ':lvimgrepa "the" **/*.txt') call VerifyScreenDump(buf, 'Test_incsearch_vimgrep_05', {}) call term_sendkeys(buf, "\") call StopVimInTerminal(buf) call delete('Xis_vimgrep_script') endfunc func Test_keep_last_search_pattern() CheckOption incsearch new call setline(1, ['foo', 'foo', 'foo']) set incsearch call test_override("char_avail", 1) let @/ = 'bar' call feedkeys(":/foo/s//\", 'ntx') call assert_equal('bar', @/) " no error message if pattern not found call feedkeys(":/xyz/s//\", 'ntx') call assert_equal('bar', @/) bwipe! call test_override("ALL", 0) set noincsearch endfunc func Test_word_under_cursor_after_match() CheckOption incsearch new call setline(1, 'foo bar') set incsearch call test_override("char_avail", 1) try call feedkeys("/foo\\\", 'ntx') catch /E486:/ endtry call assert_equal('foobar', @/) bwipe! call test_override("ALL", 0) set noincsearch endfunc func Test_subst_word_under_cursor() CheckOption incsearch new call setline(1, ['int SomeLongName;', 'for (xxx = 1; xxx < len; ++xxx)']) set incsearch call test_override("char_avail", 1) call feedkeys("/LongName\", 'ntx') call feedkeys(":%s/xxx/\\/g\", 'ntx') call assert_equal('for (SomeLongName = 1; SomeLongName < len; ++SomeLongName)', getline(2)) bwipe! call test_override("ALL", 0) set noincsearch endfunc func Test_search_undefined_behaviour() CheckFeature terminal let h = winheight(0) if h < 3 return endif " did cause an undefined left shift let g:buf = term_start([GetVimProg(), '--clean', '-e', '-s', '-c', 'call search(getline("."))', 'samples/test000'], {'term_rows': 3}) call assert_equal([''], getline(1, '$')) call term_sendkeys(g:buf, ":qa!\") bwipe! endfunc func Test_search_undefined_behaviour2() call search("\%UC0000000") endfunc " Test for search('multi-byte char', 'bce') func Test_search_multibyte() let save_enc = &encoding set encoding=utf8 enew! call append('$', 'A') call cursor(2, 1) call assert_equal(2, search('A', 'bce', line('.'))) enew! let &encoding = save_enc endfunc " This was causing E874. Also causes an invalid read? func Test_look_behind() new call setline(1, '0\|\&\n\@<=') call search(getline(".")) bwipe! endfunc func Test_search_visual_area_linewise() new call setline(1, ['aa', 'bb', 'cc']) exe "normal 2GV\" for engine in [1, 2] exe 'set regexpengine=' .. engine exe "normal gg/\\%'<\>" call assert_equal([0, 2, 1, 0, 1], getcurpos(), 'engine ' .. engine) exe "normal gg/\\%'>\" call assert_equal([0, 2, 2, 0, 2], getcurpos(), 'engine ' .. engine) endfor bwipe! set regexpengine& endfunc func Test_search_sentence() new " this used to cause a crash /\%'( / bwipe endfunc " Test that there is no crash when there is a last search pattern but no last " substitute pattern. func Test_no_last_substitute_pat() " Use viminfo to set the last search pattern to a string and make the last " substitute pattern the most recent used and make it empty (NULL). call writefile(['~MSle0/bar', '~MSle0~&'], 'Xviminfo') rviminfo! Xviminfo call assert_fails('normal n', 'E35:') call delete('Xviminfo') endfunc func Test_search_Ctrl_L_combining() " Make sure, that Ctrl-L works correctly with combining characters. " It uses an artificial example of an 'a' with 4 combining chars: " 'a' U+0061 Dec:97 LATIN SMALL LETTER A a /\%u61\Z "\u0061" " ' ̀' U+0300 Dec:768 COMBINING GRAVE ACCENT ̀ /\%u300\Z "\u0300" " ' ́' U+0301 Dec:769 COMBINING ACUTE ACCENT ́ /\%u301\Z "\u0301" " ' ̇' U+0307 Dec:775 COMBINING DOT ABOVE ̇ /\%u307\Z "\u0307" " ' ̣' U+0323 Dec:803 COMBINING DOT BELOW ̣ /\%u323 "\u0323" " Those should also appear on the commandline CheckOption incsearch call Cmdline3_prep() 1 let bufcontent = ['', 'Miạ̀́̇m'] call append('$', bufcontent) call feedkeys("/Mi\\\", 'tx') call assert_equal(5, line('.')) call assert_equal(bufcontent[1], @/) call Incsearch_cleanup() endfunc func Test_large_hex_chars1() " This used to cause a crash, the character becomes an NFA state. try /\%Ufffffc23 catch call assert_match('E678:', v:exception) endtry try set re=1 /\%Ufffffc23 catch call assert_match('E678:', v:exception) endtry set re& endfunc func Test_large_hex_chars2() " This used to cause a crash, the character becomes an NFA state. try /[\Ufffffc1f] catch call assert_match('E486:', v:exception) endtry try set re=1 /[\Ufffffc1f] catch call assert_match('E486:', v:exception) endtry set re& endfunc func Test_one_error_msg() " This was also giving an internal error call assert_fails('call search(" \\((\\v[[=P=]]){185}+ ")', 'E871:') endfunc func Test_incsearch_add_char_under_cursor() CheckOption incsearch set incsearch new call setline(1, ['find match', 'anything']) 1 call test_override('char_avail', 1) call feedkeys("fc/m\\\\\\", 'tx') call assert_equal('match', @/) call test_override('char_avail', 0) set incsearch& bwipe! endfunc " Test for the search() function with match at the cursor position func Test_search_match_at_curpos() new call append(0, ['foobar', '', 'one two', '']) normal gg eval 'foobar'->search('c') call assert_equal([1, 1], [line('.'), col('.')]) normal j call search('^$', 'c') call assert_equal([2, 1], [line('.'), col('.')]) call search('^$', 'bc') call assert_equal([2, 1], [line('.'), col('.')]) exe "normal /two\" call search('.', 'c') call assert_equal([3, 5], [line('.'), col('.')]) close! endfunc " Test for error cases with the search() function func Test_search_errors() call assert_fails("call search('pat', [])", 'E730:') call assert_fails("call search('pat', 'b', {})", 'E728:') call assert_fails("call search('pat', 'b', 1, [])", 'E745:') call assert_fails("call search('pat', 'ns')", 'E475:') call assert_fails("call search('pat', 'mr')", 'E475:') new call setline(1, ['foo', 'bar']) call assert_fails('call feedkeys("/foo/;/bar/;\", "tx")', 'E386:') bwipe! endfunc func Test_search_timeout() new " use a complicated pattern that should be slow with the BT engine let pattern = '\%#=1a*.*X\@<=b*' " use a timeout of 50 msec let search_timeout = 0.05 " fill the buffer so that it takes 15 times the timeout to search let slow_target_timeout = search_timeout * 15.0 " Fill the buffer with more and more text until searching takes more time " than slow_target_timeout. for n in range(40, 400, 30) call setline(1, ['aaa', repeat('abc ', n), 'ccc']) let start = reltime() call search(pattern, '', 0) let elapsed = reltimefloat(reltime(start)) if elapsed > slow_target_timeout break endif endfor call assert_true(elapsed > slow_target_timeout) " Check that the timeout kicks in, the time should be less than half of what " we measured without the timeout. This is permissive, because the timer is " known to overrun, especially when using valgrind. let max_time = elapsed / 2.0 let start = reltime() call search(pattern, '', 0, float2nr(search_timeout * 1000)) let elapsed = reltimefloat(reltime(start)) call assert_inrange(search_timeout * 0.9, max_time, elapsed) bwipe! endfunc func Test_search_display_pattern() new call setline(1, ['foo', 'bar', 'foobar']) call cursor(1, 1) let @/ = 'foo' let pat = @/->escape('()*?'. '\s\+') let g:a = execute(':unsilent :norm! n') call assert_match(pat, g:a) " right-left if exists("+rightleft") set rl call cursor(1, 1) let @/ = 'foo' let pat = 'oof/\s\+' let g:a = execute(':unsilent :norm! n') call assert_match(pat, g:a) set norl endif endfunc func Test_searchdecl() let lines =<< trim END int global; func() { int global; if (cond) { int local; } int local; // comment } END new call setline(1, lines) 10 call assert_equal(0, searchdecl('local', 0, 0)) call assert_equal(7, getcurpos()[1]) 10 call assert_equal(0, 'local'->searchdecl(0, 1)) call assert_equal(9, getcurpos()[1]) 10 call assert_equal(0, searchdecl('global')) call assert_equal(5, getcurpos()[1]) 10 call assert_equal(0, searchdecl('global', 1)) call assert_equal(1, getcurpos()[1]) bwipe! endfunc func Test_search_special() " this was causing illegal memory access and an endless loop set t_PE= exe "norm /\x80PS" endfunc " Test for command failures when the last search pattern is not set. " Need to run this in a new vim instance where last search pattern is not set. func Test_search_with_no_last_pat() let lines =<< trim [SCRIPT] call assert_fails("normal i\/\e", 'E35:') call assert_fails("exe '/'", 'E35:') call assert_fails("exe '?'", 'E35:') call assert_fails("/", 'E35:') call assert_fails("?", 'E35:') call assert_fails("normal n", 'E35:') call assert_fails("normal N", 'E35:') call assert_fails("normal gn", 'E35:') call assert_fails("normal gN", 'E35:') call assert_fails("normal cgn", 'E35:') call assert_fails("normal cgN", 'E35:') let p = [] let p = @/ call assert_equal('', p) call assert_fails("normal :\/", 'E35:') call assert_fails("//p", 'E35:') call assert_fails(";//p", 'E35:') call assert_fails("??p", 'E35:') call assert_fails(";??p", 'E35:') call assert_fails('g//p', ['E35:', 'E476:']) call assert_fails('v//p', ['E35:', 'E476:']) call writefile(v:errors, 'Xresult') qall! [SCRIPT] call writefile(lines, 'Xscript') if RunVim([], [], '--clean -S Xscript') call assert_equal([], readfile('Xresult')) endif call delete('Xscript') call delete('Xresult') endfunc " Test for using tilde (~) atom in search. This should use the last used " substitute pattern func Test_search_tilde_pat() let lines =<< trim [SCRIPT] set regexpengine=1 call assert_fails('exe "normal /~\"', 'E33:') call assert_fails('exe "normal ?~\"', 'E33:') set regexpengine=2 call assert_fails('exe "normal /~\"', ['E33:', 'E383:']) call assert_fails('exe "normal ?~\"', ['E33:', 'E383:']) set regexpengine& call writefile(v:errors, 'Xresult') qall! [SCRIPT] call writefile(lines, 'Xscript') if RunVim([], [], '--clean -S Xscript') call assert_equal([], readfile('Xresult')) endif call delete('Xscript') call delete('Xresult') endfunc " Test for searching a pattern that is not present with 'nowrapscan' func Test_search_pat_not_found() new set nowrapscan let @/ = '1abcxyz2' call assert_fails('normal n', 'E385:') call assert_fails('normal N', 'E384:') set wrapscan& close endfunc " Test for v:searchforward variable func Test_searchforward_var() new call setline(1, ['foo', '', 'foo']) call cursor(2, 1) let @/ = 'foo' let v:searchforward = 0 normal N call assert_equal(3, line('.')) call cursor(2, 1) let v:searchforward = 1 normal N call assert_equal(1, line('.')) close! endfunc " Test for invalid regular expressions func Test_invalid_regexp() set regexpengine=1 call assert_fails("call search(repeat('\\(.\\)', 10))", 'E51:') call assert_fails("call search('\\%(')", 'E53:') call assert_fails("call search('\\(')", 'E54:') call assert_fails("call search('\\)')", 'E55:') call assert_fails("call search('x\\@#')", 'E59:') call assert_fails('call search(''\v%(%(%(%(%(%(%(%(%(%(%(a){1}){1}){1}){1}){1}){1}){1}){1}){1}){1}){1}'')', 'E60:') call assert_fails("call search('a\\+*')", 'E61:') call assert_fails("call search('\\_m')", 'E63:') call assert_fails("call search('\\+')", 'E64:') call assert_fails("call search('\\1')", 'E65:') call assert_fails("call search('\\z\\(\\)')", 'E66:') call assert_fails("call search('\\z2')", 'E67:') call assert_fails("call search('\\zx')", 'E68:') call assert_fails("call search('\\%[ab')", 'E69:') call assert_fails("call search('\\%[]')", 'E70:') call assert_fails("call search('\\%a')", 'E71:') call assert_fails("call search('ab\\%[\\(cd\\)]')", 'E369:') call assert_fails("call search('ab\\%[\\%(cd\\)]')", 'E369:') set regexpengine=2 call assert_fails("call search('\\_')", 'E865:') call assert_fails("call search('\\+')", 'E866:') call assert_fails("call search('\\zx')", 'E867:') call assert_fails("call search('\\%a')", 'E867:') call assert_fails("call search('x\\@#')", 'E869:') call assert_fails("call search(repeat('\\(.\\)', 10))", 'E872:') call assert_fails("call search('\\_m')", 'E877:') call assert_fails("call search('\\%(')", 'E53:') call assert_fails("call search('\\(')", 'E54:') call assert_fails("call search('\\)')", 'E55:') call assert_fails("call search('\\z\\(\\)')", 'E66:') call assert_fails("call search('\\z2')", 'E67:') call assert_fails("call search('\\zx')", 'E867:') call assert_fails("call search('\\%[ab')", 'E69:') call assert_fails("call search('\\%[]')", 'E70:') call assert_fails("call search('\\%9999999999999999999999999999v')", 'E951:') set regexpengine& call assert_fails("call search('\\%#=3ab')", 'E864:') endfunc " Test for searching a very complex pattern in a string. Should switch the " regexp engine from NFA to the old engine. func Test_regexp_switch_engine() let l = readfile('samples/re.freeze.txt') let v = substitute(l[4], '..\@\" call assert_equal([2, 5], [line('.'), col('.')]) exe "normal 2GVj$?\\%Vbar\\" call assert_equal([3, 5], [line('.'), col('.')]) close! endfunc " Test for searching with 'smartcase' and 'ignorecase' func Test_search_smartcase() new call setline(1, ['', 'Hello']) set noignorecase nosmartcase call assert_fails('exe "normal /\\a\\_.\\(.*\\)O\"', 'E486:') set ignorecase nosmartcase exe "normal /\\a\\_.\\(.*\\)O\" call assert_equal([2, 1], [line('.'), col('.')]) call cursor(1, 1) set ignorecase smartcase call assert_fails('exe "normal /\\a\\_.\\(.*\\)O\"', 'E486:') exe "normal /\\a\\_.\\(.*\\)o\" call assert_equal([2, 1], [line('.'), col('.')]) " Test for using special atoms with 'smartcase' call setline(1, ['', ' Hello\ ']) call cursor(1, 1) call feedkeys('/\_.\%(\uello\)\' .. "\", 'xt') call assert_equal([2, 4], [line('.'), col('.')]) set ignorecase& smartcase& close! endfun " Test 'smartcase' with utf-8. func Test_search_smartcase_utf8() new let save_enc = &encoding set encoding=utf8 ignorecase smartcase call setline(1, 'Café cafÉ') 1s/café/x/g call assert_equal('x x', getline(1)) call setline(1, 'Café cafÉ') 1s/cafÉ/x/g call assert_equal('Café x', getline(1)) set ignorecase& smartcase& let &encoding = save_enc close! endfunc " Test searching past the end of a file func Test_search_past_eof() new call setline(1, ['Line']) exe "normal /\\n\\zs\" call assert_equal([1, 4], [line('.'), col('.')]) close! endfunc " Test for various search offsets func Test_search_offset() " With /e, for a match in the first column of a line, the cursor should be " placed at the end of the previous line. new call setline(1, ['one two', 'three four']) call search('two\_.', 'e') call assert_equal([1, 7], [line('.'), col('.')]) " with cursor at the beginning of the file, use /s+1 call cursor(1, 1) exe "normal /two/s+1\" call assert_equal([1, 6], [line('.'), col('.')]) " with cursor at the end of the file, use /e-1 call cursor(2, 10) exe "normal ?three?e-1\" call assert_equal([2, 4], [line('.'), col('.')]) " line offset - after the last line call cursor(1, 1) exe "normal /three/+1\" call assert_equal([2, 1], [line('.'), col('.')]) " line offset - before the first line call cursor(2, 1) exe "normal ?one?-1\" call assert_equal([1, 1], [line('.'), col('.')]) " character offset - before the first character in the file call cursor(2, 1) exe "normal ?one?s-1\" call assert_equal([1, 1], [line('.'), col('.')]) call cursor(2, 1) exe "normal ?one?e-3\" call assert_equal([1, 1], [line('.'), col('.')]) " character offset - after the last character in the file call cursor(1, 1) exe "normal /four/s+4\" call assert_equal([2, 10], [line('.'), col('.')]) call cursor(1, 1) exe "normal /four/e+1\" call assert_equal([2, 10], [line('.'), col('.')]) close! endfunc " Test for searching for matching parenthesis using % func Test_search_match_paren() new call setline(1, "abc(def')'ghi'('jk'\\t'lm)no") " searching for a matching parenthesis should skip single quoted characters call cursor(1, 4) normal % call assert_equal([1, 25], [line('.'), col('.')]) normal % call assert_equal([1, 4], [line('.'), col('.')]) call cursor(1, 5) normal ]) call assert_equal([1, 25], [line('.'), col('.')]) call cursor(1, 24) normal [( call assert_equal([1, 4], [line('.'), col('.')]) " matching parenthesis in 'virtualedit' mode with cursor after the eol call setline(1, 'abc(defgh)') set virtualedit=all normal 20|% call assert_equal(4, col('.')) set virtualedit& close! endfunc " Test for searching a pattern and stopping before a specified line func Test_search_stopline() new call setline(1, ['', '', '', 'vim']) call assert_equal(0, search('vim', 'n', 3)) call assert_equal(4, search('vim', 'n', 4)) call setline(1, ['vim', '', '', '']) call cursor(4, 1) call assert_equal(0, search('vim', 'bn', 2)) call assert_equal(1, search('vim', 'bn', 1)) close! endfunc func Test_incsearch_highlighting_newline() CheckRunVimInTerminal CheckOption incsearch CheckScreendump new call test_override("char_avail", 1) let commands =<< trim [CODE] set incsearch nohls call setline(1, ['test', 'xxx']) [CODE] call writefile(commands, 'Xincsearch_nl') let buf = RunVimInTerminal('-S Xincsearch_nl', {'rows': 5, 'cols': 10}) call term_sendkeys(buf, '/test') call VerifyScreenDump(buf, 'Test_incsearch_newline1', {}) " Need to send one key at a time to force a redraw call term_sendkeys(buf, '\n') call VerifyScreenDump(buf, 'Test_incsearch_newline2', {}) call term_sendkeys(buf, 'x') call VerifyScreenDump(buf, 'Test_incsearch_newline3', {}) call term_sendkeys(buf, 'x') call VerifyScreenDump(buf, 'Test_incsearch_newline4', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_incsearch_newline5', {}) call StopVimInTerminal(buf) " clean up call delete('Xincsearch_nl') call test_override("char_avail", 0) bw endfunc func Test_incsearch_substitute_dump2() CheckOption incsearch CheckScreendump call writefile([ \ 'set incsearch hlsearch scrolloff=0', \ 'for n in range(1, 4)', \ ' call setline(n, "foo " . n)', \ 'endfor', \ 'call setline(5, "abc|def")', \ '3', \ ], 'Xis_subst_script2') let buf = RunVimInTerminal('-S Xis_subst_script2', {'rows': 9, 'cols': 70}) call term_sendkeys(buf, ':%s/\vabc|') sleep 100m call VerifyScreenDump(buf, 'Test_incsearch_sub_01', {}) call term_sendkeys(buf, "\") " The following should not be highlighted call term_sendkeys(buf, ':1,5s/\v|') sleep 100m call VerifyScreenDump(buf, 'Test_incsearch_sub_02', {}) call StopVimInTerminal(buf) call delete('Xis_subst_script2') endfunc func Test_pattern_is_uppercase_smartcase() new let input=['abc', 'ABC', 'Abc', 'abC'] call setline(1, input) call cursor(1,1) " default, matches firstline %s/abc//g call assert_equal(['', 'ABC', 'Abc', 'abC'], \ getline(1, '$')) set smartcase ignorecase sil %d call setline(1, input) call cursor(1,1) " with smartcase and incsearch set, matches everything %s/abc//g call assert_equal(['', '', '', ''], getline(1, '$')) sil %d call setline(1, input) call cursor(1,1) " with smartcase and incsearch set and found an uppercase letter, " match only that. %s/abC//g call assert_equal(['abc', 'ABC', 'Abc', ''], \ getline(1, '$')) sil %d call setline(1, input) call cursor(1,1) exe "norm! vG$\" " \%V should not be detected as uppercase letter %s/\%Vabc//g call assert_equal(['', '', '', ''], getline(1, '$')) call setline(1, input) call cursor(1,1) exe "norm! vG$\" " \v%V should not be detected as uppercase letter %s/\v%Vabc//g call assert_equal(['', '', '', ''], getline(1, '$')) call setline(1, input) call cursor(1,1) exe "norm! vG$\" " \v%VabC should be detected as uppercase letter %s/\v%VabC//g call assert_equal(['abc', 'ABC', 'Abc', ''], \ getline(1, '$')) call setline(1, input) call cursor(1,1) " \Vabc should match everything %s/\Vabc//g call assert_equal(['', '', '', ''], getline(1, '$')) call setline(1, input + ['_abc']) " _ matches normally %s/\v_.*//g call assert_equal(['abc', 'ABC', 'Abc', 'abC', ''], getline(1, '$')) set smartcase& ignorecase& bw! endfunc func Test_no_last_search_pattern() CheckOption incsearch let @/ = "" set incsearch " these were causing a crash call feedkeys("//\", 'xt') call feedkeys("//\", 'xt') call feedkeys("??\", 'xt') call feedkeys("??\", 'xt') endfunc func Test_search_with_invalid_range() new let lines =<< trim END /\%.v 5/ c END call writefile(lines, 'Xrangesearch') source Xrangesearch bwipe! call delete('Xrangesearch') endfunc " vim: shiftwidth=2 sts=2 expandtab