diff --git a/src/nvim/search.c b/src/nvim/search.c index a915594e26..4e96a9fcad 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -5303,6 +5303,16 @@ void f_matchfuzzypos(typval_T *argvars, typval_T *rettv, FunPtr fptr) do_fuzzymatch(argvars, rettv, true); } +/// Get line "lnum" and copy it into "buf[LSIZE]". +/// The copy is made because the regexp may make the line invalid when using a +/// mark. +static char_u *get_line_and_copy(linenr_T lnum, char_u *buf) +{ + char_u *line = ml_get(lnum); + STRLCPY(buf, line, LSIZE); + return buf; +} + /// Find identifiers or defines in included files. /// If p_ic && (compl_cont_status & CONT_SOL) then ptr must be in lowercase. /// @@ -5399,7 +5409,7 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo if (lnum > end_lnum) { // do at least one line lnum = end_lnum; } - line = ml_get(lnum); + line = get_line_and_copy(lnum, file_line); for (;;) { if (incl_regmatch.regprog != NULL @@ -5687,7 +5697,7 @@ search_line: if (lnum >= end_lnum) { goto exit_matched; } - line = ml_get(++lnum); + line = get_line_and_copy(++lnum, file_line); } else if (vim_fgets(line = file_line, LSIZE, files[depth].fp)) { goto exit_matched; @@ -5879,7 +5889,7 @@ exit_matched: if (++lnum > end_lnum) { break; } - line = ml_get(lnum); + line = get_line_and_copy(lnum, file_line); } already = NULL; } diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim index 5caceeb958..ef8d0a88fd 100644 --- a/src/nvim/testdir/test_arglist.vim +++ b/src/nvim/testdir/test_arglist.vim @@ -520,10 +520,30 @@ func Test_quit_with_arglist() throw 'Skipped: cannot run vim in terminal' endif let buf = RunVimInTerminal('', {'rows': 6}) + call term_sendkeys(buf, ":set nomore\n") call term_sendkeys(buf, ":args a b c\n") call term_sendkeys(buf, ":quit\n") + call term_wait(buf) call WaitForAssert({-> assert_match('^E173:', term_getline(buf, 6))}) call StopVimInTerminal(buf) + + " Try :confirm quit with unedited files in arglist + let buf = RunVimInTerminal('', {'rows': 6}) + call term_sendkeys(buf, ":set nomore\n") + call term_sendkeys(buf, ":args a b c\n") + call term_sendkeys(buf, ":confirm quit\n") + call term_wait(buf) + call WaitForAssert({-> assert_match('^\[Y\]es, (N)o: *$', + \ term_getline(buf, 6))}) + call term_sendkeys(buf, "N") + call term_wait(buf) + call term_sendkeys(buf, ":confirm quit\n") + call WaitForAssert({-> assert_match('^\[Y\]es, (N)o: *$', + \ term_getline(buf, 6))}) + call term_sendkeys(buf, "Y") + call term_wait(buf) + call WaitForAssert({-> assert_equal("finished", term_getstatus(buf))}) + only! endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 1936832400..438851a0ad 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -2226,7 +2226,7 @@ func Test_autocmd_bufreadpre() " (even though the position will be invalid, this should make Vim reset the " cursor position in the other window. wincmd p - 1 + 1 " set cpo+=g " won't do anything, but try to set the cursor on an invalid lnum autocmd BufReadPre :norm! 70gg " triggers BufReadPre, should not move the cursor in either window @@ -2241,8 +2241,11 @@ func Test_autocmd_bufreadpre() close close call delete('XAutocmdBufReadPre.txt') + " set cpo-=g endfunc +" FileChangedShell tested in test_filechanged.vim + " Tests for the following autocommands: " - FileWritePre writing a compressed file " - FileReadPost reading a compressed file @@ -2560,7 +2563,29 @@ func Test_BufWrite_lockmarks() call delete('Xtest2') endfunc -" FileChangedShell tested in test_filechanged.vim +" Test closing a window or editing another buffer from a FileChangedRO handler +" in a readonly buffer +func Test_FileChangedRO_winclose() + augroup FileChangedROTest + au! + autocmd FileChangedRO * quit + augroup END + new + set readonly + call assert_fails('normal i', 'E788:') + close + augroup! FileChangedROTest + + augroup FileChangedROTest + au! + autocmd FileChangedRO * edit Xfile + augroup END + new + set readonly + call assert_fails('normal i', 'E788:') + close + augroup! FileChangedROTest +endfunc func LogACmd() call add(g:logged, line('$')) diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 9885d356fd..3863d25ee7 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -1385,6 +1385,35 @@ func Test_cmdwin_tabpage() tabclose! endfunc +" Test for the :! command +func Test_cmd_bang() + if !has('unix') + return + endif + + let lines =<< trim [SCRIPT] + " Test for no previous command + call assert_fails('!!', 'E34:') + set nomore + " Test for cmdline expansion with :! + call setline(1, 'foo!') + silent !echo > Xfile.out + call assert_equal(['foo!'], readfile('Xfile.out')) + " Test for using previous command + silent !echo \! ! + call assert_equal(['! echo foo!'], readfile('Xfile.out')) + call writefile(v:errors, 'Xresult') + call delete('Xfile.out') + 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 error: "E135: *Filter* Autocommands must not change current buffer" func Test_cmd_bang_E135() new diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim index 5513892dc7..8b1c44c2b8 100644 --- a/src/nvim/testdir/test_excmd.vim +++ b/src/nvim/testdir/test_excmd.vim @@ -507,6 +507,42 @@ func Test_run_excmd_with_text_locked() call assert_fails("call feedkeys(\":\=execute('bnext')\\", 'xt')", 'E565:') endfunc +" Test for the :verbose command +func Test_verbose_cmd() + call assert_equal([' verbose=1'], split(execute('verbose set vbs'), "\n")) + call assert_equal([' verbose=0'], split(execute('0verbose set vbs'), "\n")) + let l = execute("4verbose set verbose | set verbose") + call assert_equal([' verbose=4', ' verbose=0'], split(l, "\n")) +endfunc + +" Test for the :delete command and the related abbreviated commands +func Test_excmd_delete() + new + call setline(1, ['foo', "\tbar"]) + call assert_equal(['^Ibar$'], split(execute('dl'), "\n")) + call setline(1, ['foo', "\tbar"]) + call assert_equal(['^Ibar$'], split(execute('dell'), "\n")) + call setline(1, ['foo', "\tbar"]) + call assert_equal(['^Ibar$'], split(execute('delel'), "\n")) + call setline(1, ['foo', "\tbar"]) + call assert_equal(['^Ibar$'], split(execute('deletl'), "\n")) + call setline(1, ['foo', "\tbar"]) + call assert_equal(['^Ibar$'], split(execute('deletel'), "\n")) + call setline(1, ['foo', "\tbar"]) + call assert_equal([' bar'], split(execute('dp'), "\n")) + call setline(1, ['foo', "\tbar"]) + call assert_equal([' bar'], split(execute('dep'), "\n")) + call setline(1, ['foo', "\tbar"]) + call assert_equal([' bar'], split(execute('delp'), "\n")) + call setline(1, ['foo', "\tbar"]) + call assert_equal([' bar'], split(execute('delep'), "\n")) + call setline(1, ['foo', "\tbar"]) + call assert_equal([' bar'], split(execute('deletp'), "\n")) + call setline(1, ['foo', "\tbar"]) + call assert_equal([' bar'], split(execute('deletep'), "\n")) + close! +endfunc + func Test_not_break_expression_register() call setreg('=', '1+1') if 0 diff --git a/src/nvim/testdir/test_expand.vim b/src/nvim/testdir/test_expand.vim index 5a51903712..d86fea4f45 100644 --- a/src/nvim/testdir/test_expand.vim +++ b/src/nvim/testdir/test_expand.vim @@ -81,7 +81,11 @@ func Test_expandcmd() call assert_fails('call expandcmd("make ")', 'E495:') enew call assert_fails('call expandcmd("make %")', 'E499:') - close + let $FOO="blue\tsky" + call setline(1, "$FOO") + call assert_equal("grep pat blue\tsky", expandcmd('grep pat ')) + unlet $FOO + close! endfunc " Test for expanding , and outside of sourcing a script @@ -108,5 +112,17 @@ func Test_source_sfile() call delete('Xresult') endfunc +" Test for expanding filenames multiple times in a command line +func Test_expand_filename_multicmd() + edit foo + call setline(1, 'foo!') + new + call setline(1, 'foo!') + new | new + call assert_equal(4, winnr('$')) + call assert_equal('foo!', bufname(winbufnr(1))) + call assert_equal('foo', bufname(winbufnr(2))) + %bwipe! +endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 6574876356..dee04e66dc 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -772,8 +772,12 @@ func Test_filetype_indent_off() new Xtest.vim filetype indent on call assert_equal(1, g:did_indent_on) + call assert_equal(['filetype detection:ON plugin:OFF indent:ON'], + \ execute('filetype')->split("\n")) filetype indent off call assert_equal(0, exists('g:did_indent_on')) + call assert_equal(['filetype detection:ON plugin:OFF indent:OFF'], + \ execute('filetype')->split("\n")) close endfunc diff --git a/src/nvim/testdir/test_filter_cmd.vim b/src/nvim/testdir/test_filter_cmd.vim index d465e48c7b..dae164b11c 100644 --- a/src/nvim/testdir/test_filter_cmd.vim +++ b/src/nvim/testdir/test_filter_cmd.vim @@ -45,6 +45,14 @@ func Test_filter_fails() call assert_fails('filter /pat', 'E476:') call assert_fails('filter /pat/', 'E476:') call assert_fails('filter /pat/ asdf', 'E492:') + " Using assert_fails() causes E476 instead of E866. So use a try-catch. + let caught_e866 = 0 + try + filter /\@>b/ ls + catch /E866:/ + let caught_e866 = 1 + endtry + call assert_equal(1, caught_e866) call assert_fails('filter!', 'E471:') call assert_fails('filter! pat', 'E476:') diff --git a/src/nvim/testdir/test_global.vim b/src/nvim/testdir/test_global.vim index ad561baf4a..feddf85346 100644 --- a/src/nvim/testdir/test_global.vim +++ b/src/nvim/testdir/test_global.vim @@ -66,6 +66,18 @@ func Test_global_print() close! endfunc +" Test for global command with newline character +func Test_global_newline() + new + call setline(1, ['foo']) + exe "g/foo/s/f/h/\s/o$/w/" + call assert_equal('how', getline(1)) + call setline(1, ["foo\bar"]) + exe "g/foo/s/foo\\\bar/xyz/" + call assert_equal('xyz', getline(1)) + close! +endfunc + func Test_wrong_delimiter() call assert_fails('g x^bxd', 'E146:') endfunc diff --git a/src/nvim/testdir/test_plus_arg_edit.vim b/src/nvim/testdir/test_plus_arg_edit.vim index 64533e71cf..c52044d064 100644 --- a/src/nvim/testdir/test_plus_arg_edit.vim +++ b/src/nvim/testdir/test_plus_arg_edit.vim @@ -18,7 +18,7 @@ func Test_edit_bad() e! ++enc=utf8 Xfile call assert_equal('[?][?][???][??]', getline(1)) - e! ++enc=utf8 ++bad=_ Xfile + e! ++encoding=utf8 ++bad=_ Xfile call assert_equal('[_][_][___][__]', getline(1)) e! ++enc=utf8 ++bad=drop Xfile diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 9881deaa45..5649652fd2 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -499,6 +499,7 @@ func Xtest_browse(cchar) \ 'RegularLine2'] Xfirst + call assert_fails('-5Xcc', 'E16:') call assert_fails('Xprev', 'E553') call assert_fails('Xpfile', 'E553') Xnfile @@ -4415,6 +4416,20 @@ func Test_splitview() call assert_equal(0, getloclist(0, {'winid' : 0}).winid) new | only + " Using :split or :vsplit from a quickfix window should behave like a :new + " or a :vnew command + copen + split + call assert_equal(3, winnr('$')) + let l = getwininfo() + call assert_equal([0, 0, 1], [l[0].quickfix, l[1].quickfix, l[2].quickfix]) + close + copen + vsplit + let l = getwininfo() + call assert_equal([0, 0, 1], [l[0].quickfix, l[1].quickfix, l[2].quickfix]) + new | only + call delete('Xtestfile1') call delete('Xtestfile2') endfunc diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim index b4ed811626..9aa51d501b 100644 --- a/src/nvim/testdir/test_search.vim +++ b/src/nvim/testdir/test_search.vim @@ -1600,6 +1600,33 @@ func Test_search_tilde_pat() 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 'smartcase' with utf-8. func Test_search_smartcase_utf8() new diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim index b180f27685..923e1cbf50 100644 --- a/src/nvim/testdir/test_swap.vim +++ b/src/nvim/testdir/test_swap.vim @@ -278,7 +278,6 @@ func Test_swap_recover_ext() autocmd SwapExists * let v:swapchoice = 'r' augroup END - " Create a valid swapfile by editing a file with a special extension. split Xtest.scr call setline(1, ['one', 'two', 'three']) @@ -311,6 +310,46 @@ func Test_swap_recover_ext() augroup! test_swap_recover_ext endfunc +" Test for closing a split window automatically when a swap file is detected +" and 'Q' is selected in the confirmation prompt. +func Test_swap_split_win() + autocmd! SwapExists + augroup test_swap_splitwin + autocmd! + autocmd SwapExists * let v:swapchoice = 'q' + augroup END + + " Create a valid swapfile by editing a file with a special extension. + split Xtest.scr + call setline(1, ['one', 'two', 'three']) + write " file is written, not modified + write " write again to make sure the swapfile is created + " read the swapfile as a Blob + let swapfile_name = swapname('%') + let swapfile_bytes = readfile(swapfile_name, 'B') + + " Close and delete the file and recreate the swap file. + quit + call delete('Xtest.scr') + call writefile(swapfile_bytes, swapfile_name) + " Split edit the file again. This should fail to open the window + try + split Xtest.scr + catch + " E308 should be caught, not E306. + call assert_exception('E308:') " Original file may have been changed + endtry + call assert_equal(1, winnr('$')) + + call delete('Xtest.scr') + call delete(swapfile_name) + + augroup test_swap_splitwin + autocmd! + augroup END + augroup! test_swap_splitwin +endfunc + " Test for selecting 'q' in the attention prompt func Test_swap_prompt_splitwin() CheckRunVimInTerminal diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim index bcb808e774..3fde7f3f06 100644 --- a/src/nvim/testdir/test_tagjump.vim +++ b/src/nvim/testdir/test_tagjump.vim @@ -1180,9 +1180,20 @@ func Test_inc_search() close! endfunc +" this was using a line from ml_get() freed by the regexp +func Test_isearch_copy_line() + new + norm o + norm 0 + 0norm o + sil! norm bc0 + sil! isearch \%') + bwipe! +endfunc + " Test for :dsearch, :dlist, :djump and :dsplit commands " Test for [d, ]d, [D, ]D, [ CTRL-D, ] CTRL-D and CTRL-W d commands -func Test_def_search() +func Test_macro_search() new call setline(1, ['#define FOO 1', '#define FOO 2', '#define FOO 3', \ '#define FOO 4', '#define FOO 5']) diff --git a/src/nvim/testdir/test_trycatch.vim b/src/nvim/testdir/test_trycatch.vim index 205ed095ea..598402fafe 100644 --- a/src/nvim/testdir/test_trycatch.vim +++ b/src/nvim/testdir/test_trycatch.vim @@ -1996,6 +1996,47 @@ func Test_reload_in_try_catch() call delete('Xreload') endfunc +" Test for errors with :catch, :throw, :finally {{{1 +func Test_try_catch_errors() + call assert_fails('throw |', 'E471:') + call assert_fails("throw \n ", 'E471:') + call assert_fails('catch abc', 'E603:') + call assert_fails('try | let i = 1| finally | catch | endtry', 'E604:') + call assert_fails('finally', 'E606:') + call assert_fails('try | finally | finally | endtry', 'E607:') + " v8.2.3486 has been ported, but v8.2.1183 hasn't, so E170 appears here. + " call assert_fails('try | for i in range(5) | endif | endtry', 'E580:') + call assert_fails('try | for i in range(5) | endif | endtry', 'E170:') + call assert_fails('try | while v:true | endtry', 'E170:') + call assert_fails('try | if v:true | endtry', 'E171:') +endfunc + +" Test for verbose messages with :try :catch, and :finally {{{1 +func Test_try_catch_verbose() + " This test works only when the language is English + if v:lang != "C" && v:lang !~ '^[Ee]n' + return + endif + + set verbose=14 + redir => msg + try + echo i + catch /E121:/ + finally + endtry + redir END + let expected = [ + \ 'Exception thrown: Vim(echo):E121: Undefined variable: i', + \ '', + \ 'Exception caught: Vim(echo):E121: Undefined variable: i', + \ '', + \ 'Exception finished: Vim(echo):E121: Undefined variable: i' + \ ] + call assert_equal(expected, split(msg, "\n")) + set verbose& +endfunc + " Test for using throw in a called function with following error {{{1 func Test_user_command_throw_in_function_call() let lines =<< trim END diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index 1bca5c420c..1323288676 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1782,6 +1782,29 @@ func Test_missing_end() call writefile(['try', 'echo "."'], 'Xscript') call assert_fails('source Xscript', 'E600:') call delete('Xscript') + + " Using endfor with :while + let caught_e732 = 0 + try + while v:true + endfor + catch /E732:/ + let caught_e732 = 1 + endtry + call assert_equal(1, caught_e732) + + " Using endwhile with :for + let caught_e733 = 0 + try + for i in range(1) + endwhile + catch /E733:/ + let caught_e733 = 1 + endtry + call assert_equal(1, caught_e733) + + " Missing 'in' in a :for statement + call assert_fails('for i range(1) | endfor', 'E690:') endfunc " Test for deep nesting of if/for/while/try statements {{{1 diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index 731b76c838..b7df750488 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -919,6 +919,14 @@ func Test_winpos_errors() call assert_fails('winpos 10', 'E466:') endfunc +" Test for +cmd in a :split command +func Test_split_cmd() + split +set\ readonly + call assert_equal(1, &readonly) + call assert_equal(2, winnr('$')) + close +endfunc + func Test_window_resize() throw 'Skipped: Nvim supports cmdheight=0' " Vertical :resize (absolute, relative, min and max size). diff --git a/test/functional/legacy/arglist_spec.lua b/test/functional/legacy/arglist_spec.lua index 46a016ab1c..8379e426e0 100644 --- a/test/functional/legacy/arglist_spec.lua +++ b/test/functional/legacy/arglist_spec.lua @@ -1,8 +1,10 @@ -- Test argument list commands local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') local clear, command, eq = helpers.clear, helpers.command, helpers.eq local eval, exc_exec, neq = helpers.eval, helpers.exc_exec, helpers.neq +local feed = helpers.feed local pcall_err = helpers.pcall_err describe('argument list commands', function() @@ -236,7 +238,43 @@ describe('argument list commands', function() end) it('quitting Vim with unedited files in the argument list throws E173', function() + command('set nomore') command('args a b c') eq('Vim(quit):E173: 2 more files to edit', pcall_err(command, 'quit')) end) + + it(':confirm quit with unedited files in arglist', function() + local screen = Screen.new(60, 6) + screen:attach() + command('set nomore') + command('args a b c') + feed(':confirm quit\n') + screen:expect([[ + | + ~ | + | + :confirm quit | + 2 more files to edit. Quit anyway? | + [Y]es, (N)o: ^ | + ]]) + feed('N') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + ~ | + | + ]]) + feed(':confirm quit\n') + screen:expect([[ + | + ~ | + | + :confirm quit | + 2 more files to edit. Quit anyway? | + [Y]es, (N)o: ^ | + ]]) + feed('Y') + end) end)