diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2023-01-25 18:31:31 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2023-01-25 18:31:31 +0000 |
commit | 9243becbedbb6a1592208051f8fa2b090dcc5e7d (patch) | |
tree | 607c2a862ec3f4399b8766383f6f8e04c4aa43b4 /src/nvim/testdir | |
parent | 9e40b6e9e1bc67f2d856adb837ee64dd0e25b717 (diff) | |
parent | 3c48d3c83fc21dbc0841f9210f04bdb073d73cd1 (diff) | |
download | rneovim-usermarks.tar.gz rneovim-usermarks.tar.bz2 rneovim-usermarks.zip |
Merge remote-tracking branch 'upstream/master' into usermarksusermarks
Diffstat (limited to 'src/nvim/testdir')
151 files changed, 21188 insertions, 9588 deletions
diff --git a/src/nvim/testdir/Make_all.mak b/src/nvim/testdir/Make_all.mak new file mode 100644 index 0000000000..b917877422 --- /dev/null +++ b/src/nvim/testdir/Make_all.mak @@ -0,0 +1 @@ +# Tests may depend on the existence of this file. diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 4641408069..9511b311e6 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -11,16 +11,7 @@ ROOT := ../../.. export SHELL := sh export NVIM_PRG := $(NVIM_PRG) -export TMPDIR := $(abspath Xtest-tmpdir) - -SCRIPTS_DEFAULT = \ - test42.out \ - -ifneq ($(OS),Windows_NT) - SCRIPTS_DEFAULTS := $(SCRIPTS_DEFAULT) \ - test49.out \ - -endif +export TMPDIR := $(abspath X-test-tmpdir) ifeq ($(OS),Windows_NT) FIXFF = fixff @@ -28,8 +19,6 @@ else FIXFF = endif -SCRIPTS ?= $(SCRIPTS_DEFAULT) - # Tests using runtest.vim. NEW_TESTS_ALOT := test_alot_utf8 test_alot test_alot_latin NEW_TESTS_IN_ALOT := $(shell sed -n '/^source/ s/^source //; s/\.vim$$//p' $(addsuffix .vim,$(NEW_TESTS_ALOT))) @@ -66,11 +55,7 @@ else endif endif -ifdef TESTNUM - SCRIPTS := test$(TESTNUM).out -endif - -nongui: nolog $(FIXFF) $(SCRIPTS) newtests report +nongui: nolog $(FIXFF) newtests report .gdbinit: @echo "[OLDTEST-PREP] Setting up .gdbinit" @@ -88,8 +73,6 @@ report: test1.out: $(NVIM_PRG) -$(SCRIPTS): $(NVIM_PRG) test1.out - NO_PLUGINS = --noplugin --headless # In vim, if the -u command line option is specified, compatible is turned on # and viminfo is not read. Unlike vim, neovim reads viminfo and requires the @@ -147,17 +130,6 @@ test1.out: .gdbinit test1.in @rm -f wrongtermsize @rm -rf X* viminfo -%.out: %.in .gdbinit - @echo "[OLDESTTEST] Running" $* - @rm -rf $*.failed test.ok $(RM_ON_RUN) - @mkdir -p $(TMPDIR) - @cp $*.ok test.ok - @/bin/sh runnvim.sh --oldesttest $(ROOT) $(NVIM_PRG) $* $(RUN_VIM) $*.in - @rm -rf X* test.ok viminfo - -# Explicit dependencies. -test49.out: test49.vim - nolog: @echo "[OLDTEST-PREP] Removing test.log and messages" @rm -f test.log messages @@ -179,4 +151,4 @@ newtestssilent: $(NEW_TESTS_RES) @echo "[OLDTEST] Running" $* @rm -rf $*.failed test.ok $(RM_ON_RUN) @mkdir -p $(TMPDIR) - @/bin/sh runnvim.sh $(ROOT) $(NVIM_PRG) $* $(RUN_VIMTEST) $(NO_INITS) -u NONE -S runtest.vim $*.vim + @/bin/sh runnvim.sh $(ROOT) $(NVIM_PRG) $* $(RUN_VIMTEST) $(NO_INITS) -u NONE --cmd "set shortmess-=F" -S runtest.vim $*.vim diff --git a/src/nvim/testdir/check.vim b/src/nvim/testdir/check.vim index 4107df99d6..680a59006b 100644 --- a/src/nvim/testdir/check.vim +++ b/src/nvim/testdir/check.vim @@ -1,11 +1,16 @@ source shared.vim source term_util.vim +command -nargs=1 MissingFeature throw 'Skipped: ' .. <args> .. ' feature missing' + " Command to check for the presence of a feature. command -nargs=1 CheckFeature call CheckFeature(<f-args>) func CheckFeature(name) + " if !has(a:name, 1) + " throw 'Checking for non-existent feature ' .. a:name + " endif if !has(a:name) - throw 'Skipped: ' .. a:name .. ' feature missing' + MissingFeature a:name endif endfunc @@ -39,6 +44,22 @@ func CheckFunction(name) endif endfunc +" Command to check for the presence of an Ex command +command -nargs=1 CheckCommand call CheckCommand(<f-args>) +func CheckCommand(name) + if !exists(':' .. a:name) + throw 'Skipped: ' .. a:name .. ' command not supported' + endif +endfunc + +" Command to check for the presence of a shell command +command -nargs=1 CheckExecutable call CheckExecutable(<f-args>) +func CheckExecutable(name) + if !executable(a:name) + throw 'Skipped: ' .. a:name .. ' program not executable' + endif +endfunc + " Command to check for the presence of python. Argument should have been " obtained with PythonProg() func CheckPython(name) @@ -71,12 +92,11 @@ func CheckUnix() endif endfunc -" Command to check for not running on a BSD system. -" TODO: using this checks should not be needed -command CheckNotBSD call CheckNotBSD() -func CheckNotBSD() - if has('bsd') - throw 'Skipped: does not work on BSD' +" Command to check for running on Linux +command CheckLinux call CheckLinux() +func CheckLinux() + if !has('linux') + throw 'Skipped: only works on Linux' endif endfunc @@ -105,6 +125,14 @@ func CheckCanRunGui() endif endfunc +" Command to Check for an environment variable +command -nargs=1 CheckEnv call CheckEnv(<f-args>) +func CheckEnv(name) + if empty(eval('$' .. a:name)) + throw 'Skipped: Environment variable ' .. a:name .. ' is not set' + endif +endfunc + " Command to check that we are using the GUI command CheckGui call CheckGui() func CheckGui() @@ -145,6 +173,22 @@ func CheckNotAsan() endif endfunc +" Command to check for not running under valgrind +command CheckNotValgrind call CheckNotValgrind() +func CheckNotValgrind() + if RunningWithValgrind() + throw 'Skipped: does not work well with valgrind' + endif +endfunc + +" Command to check for X11 based GUI +command CheckX11BasedGui call CheckX11BasedGui() +func CheckX11BasedGui() + if !g:x11_based_gui + throw 'Skipped: requires X11 based GUI' + endif +endfunc + " Command to check for satisfying any of the conditions. " e.g. CheckAnyOf Feature:bsd Feature:sun Linux command -nargs=+ CheckAnyOf call CheckAnyOf(<f-args>) diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index fcd3d5724c..3c5699af73 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -105,7 +105,7 @@ set nomore lang mess C " Nvim: append runtime from build dir, which contains the generated doc/tags. -let &runtimepath .= ','.expand($BUILD_DIR).'/runtime/' +let &runtimepath ..= ',' .. expand($BUILD_DIR) .. '/runtime/' let s:t_bold = &t_md let s:t_normal = &t_me @@ -114,6 +114,13 @@ if has('win32') let $PROMPT = '$P$G' endif +if has('mac') + " In MacOS, when starting a shell in a terminal, a bash deprecation warning + " message is displayed. This breaks the terminal test. Disable the warning + " message. + let $BASH_SILENCE_DEPRECATION_WARNING = 1 +endif + " Prepare for calling test_garbagecollect_now(). let v:testing = 1 @@ -164,16 +171,19 @@ func RunTheTest(test) endtry endif + au VimLeavePre * call EarlyExit(g:testfunc) if a:test =~ 'Test_nocatch_' " Function handles errors itself. This avoids skipping commands after the " error. + let g:skipped_reason = '' exe 'call ' . a:test + if g:skipped_reason != '' + call add(s:messages, ' Skipped') + call add(s:skipped, 'SKIPPED ' . a:test . ': ' . g:skipped_reason) + endif else try - let s:test = a:test - au VimLeavePre * call EarlyExit(s:test) exe 'call ' . a:test - au! VimLeavePre catch /^\cskipped/ call add(s:messages, ' Skipped') call add(s:skipped, 'SKIPPED ' . a:test . ': ' . substitute(v:exception, '^\S*\s\+', '', '')) @@ -181,6 +191,7 @@ func RunTheTest(test) call add(v:errors, 'Caught exception in ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint) endtry endif + au! VimLeavePre " In case 'insertmode' was set and something went wrong, make sure it is " reset to avoid trouble with anything else. @@ -243,11 +254,11 @@ func AfterTheTest(func_name) if len(v:errors) > 0 if match(s:may_fail_list, '^' .. a:func_name) >= 0 let s:fail_expected += 1 - call add(s:errors_expected, 'Found errors in ' . s:test . ':') + call add(s:errors_expected, 'Found errors in ' . g:testfunc . ':') call extend(s:errors_expected, v:errors) else let s:fail += 1 - call add(s:errors, 'Found errors in ' . s:test . ':') + call add(s:errors, 'Found errors in ' . g:testfunc . ':') call extend(s:errors, v:errors) endif let v:errors = [] @@ -408,22 +419,22 @@ endif let s:may_fail_list = [] if $TEST_MAY_FAIL != '' - " Split the list at commas and add () to make it match s:test. + " Split the list at commas and add () to make it match g:testfunc. let s:may_fail_list = split($TEST_MAY_FAIL, ',')->map({i, v -> v .. '()'}) endif " Execute the tests in alphabetical order. -for s:test in sort(s:tests) +for g:testfunc in sort(s:tests) " Silence, please! set belloff=all let prev_error = '' let total_errors = [] - let run_nr = 1 + let g:run_nr = 1 " A test can set g:test_is_flaky to retry running the test. let g:test_is_flaky = 0 - call RunTheTest(s:test) + call RunTheTest(g:testfunc) " Repeat a flaky test. Give up when: " - $TEST_NO_RETRY is not empty @@ -431,16 +442,16 @@ for s:test in sort(s:tests) " - it fails five times (with a different message) if len(v:errors) > 0 \ && $TEST_NO_RETRY == '' - \ && (index(s:flaky_tests, s:test) >= 0 + \ && (index(s:flaky_tests, g:testfunc) >= 0 \ || g:test_is_flaky) while 1 - call add(s:messages, 'Found errors in ' . s:test . ':') + call add(s:messages, 'Found errors in ' . g:testfunc . ':') call extend(s:messages, v:errors) - call add(total_errors, 'Run ' . run_nr . ':') + call add(total_errors, 'Run ' . g:run_nr . ':') call extend(total_errors, v:errors) - if run_nr == 5 || prev_error == v:errors[0] + if g:run_nr >= 5 || prev_error == v:errors[0] call add(total_errors, 'Flaky test failed too often, giving up') let v:errors = total_errors break @@ -455,9 +466,9 @@ for s:test in sort(s:tests) let prev_error = v:errors[0] let v:errors = [] - let run_nr += 1 + let g:run_nr += 1 - call RunTheTest(s:test) + call RunTheTest(g:testfunc) if len(v:errors) == 0 " Test passed on rerun. @@ -466,7 +477,7 @@ for s:test in sort(s:tests) endwhile endif - call AfterTheTest(s:test) + call AfterTheTest(g:testfunc) endfor call FinishTesting() diff --git a/src/nvim/testdir/samples/re.freeze.txt b/src/nvim/testdir/samples/re.freeze.txt new file mode 100644 index 0000000000..d768c23c5e --- /dev/null +++ b/src/nvim/testdir/samples/re.freeze.txt @@ -0,0 +1,6 @@ +:set re=0 or 2 +Search for the pattern: /\s\+\%#\@<!$/ +vim should not freeze. + +<td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td> + diff --git a/src/nvim/testdir/sautest/autoload/foo.vim b/src/nvim/testdir/sautest/autoload/foo.vim index 298e7275d8..21d33a0f4d 100644 --- a/src/nvim/testdir/sautest/autoload/foo.vim +++ b/src/nvim/testdir/sautest/autoload/foo.vim @@ -9,3 +9,7 @@ endfunc func foo#addFoo(head) return a:head .. 'foo' endfunc + +func foo#() + return 'empty' +endfunc diff --git a/src/nvim/testdir/script_util.vim b/src/nvim/testdir/script_util.vim index 9913b1dfaf..28d6a621d6 100644 --- a/src/nvim/testdir/script_util.vim +++ b/src/nvim/testdir/script_util.vim @@ -48,7 +48,7 @@ endfunc " delete it afterwards. However, if an exception is thrown the file may remain, " the caller should call DeleteTheScript() afterwards. let s:script_name = '' -function! ExecAsScript(funcname) +func ExecAsScript(funcname) " Make a script from the function passed as argument. let s:script_name = MakeScript(a:funcname) @@ -56,9 +56,9 @@ function! ExecAsScript(funcname) exec "source" s:script_name call delete(s:script_name) let s:script_name = '' -endfunction +endfunc -function! DeleteTheScript() +func DeleteTheScript() if s:script_name call delete(s:script_name) let s:script_name = '' diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim index 6bc3607b69..f895287469 100644 --- a/src/nvim/testdir/setup.vim +++ b/src/nvim/testdir/setup.vim @@ -5,7 +5,7 @@ if exists('s:did_load') set directory& set directory^=. set display= - set fillchars=vert:\|,fold:- + set fillchars=vert:\|,foldsep:\|,fold:- set formatoptions=tcq set fsync set laststatus=1 @@ -45,6 +45,11 @@ mapclear! aunmenu * tlunmenu * +" roughly equivalent to test_setmouse() in Vim +func Ntest_setmouse(row, col) + call nvim_input_mouse('move', '', '', 0, a:row - 1, a:col - 1) +endfunc + " Prevent Nvim log from writing to stderr. let $NVIM_LOG_FILE = exists($NVIM_LOG_FILE) ? $NVIM_LOG_FILE : 'Xnvim.log' diff --git a/src/nvim/testdir/shared.vim b/src/nvim/testdir/shared.vim index c2809844ac..33f6d9a2d0 100644 --- a/src/nvim/testdir/shared.vim +++ b/src/nvim/testdir/shared.vim @@ -9,7 +9,7 @@ source view_util.vim " {Nvim} " Filepath captured from output may be truncated, like this: -" /home/va...estdir/Xtest-tmpdir/nvimxbXN4i/10 +" /home/va...estdir/X-test-tmpdir/nvimxbXN4i/10 " Get last 2 segments, then combine with $TMPDIR. func! Fix_truncated_tmpfile(fname) " sanity check @@ -285,6 +285,12 @@ func GetVimCommand(...) return cmd endfunc +" Return one when it looks like the tests are run with valgrind, which means +" that everything is much slower. +func RunningWithValgrind() + return GetVimCommand() =~ '\<valgrind\>' +endfunc + " Get the command to run Vim, with --clean instead of "-u NONE". func GetVimCommandClean() let cmd = GetVimCommand() @@ -317,7 +323,6 @@ func RunVim(before, after, arguments) endfunc func RunVimPiped(before, after, arguments, pipecmd) - let $NVIM_LOG_FILE = exists($NVIM_LOG_FILE) ? $NVIM_LOG_FILE : 'Xnvim.log' let cmd = GetVimCommand() let args = '' if len(a:before) > 0 @@ -332,7 +337,9 @@ func RunVimPiped(before, after, arguments, pipecmd) " Optionally run Vim under valgrind " let cmd = 'valgrind --tool=memcheck --leak-check=yes --num-callers=25 --log-file=valgrind ' . cmd - exe "silent !" . a:pipecmd . cmd . args . ' ' . a:arguments + let $NVIM_LOG_FILE = exists($NVIM_LOG_FILE) ? $NVIM_LOG_FILE : 'Xnvim.log' + " Nvim does not support -Z flag, remove it. + exe "silent !" . a:pipecmd . cmd . args . ' ' . substitute(a:arguments, '-Z', '', 'g') if len(a:before) > 0 call delete('Xbefore.vim') @@ -364,4 +371,19 @@ func GetMessages() return msg_list endfunc +" Run the list of commands in 'cmds' and look for 'errstr' in exception. +" Note that assert_fails() cannot be used in some places and this function +" can be used. +func AssertException(cmds, errstr) + let save_exception = '' + try + for cmd in a:cmds + exe cmd + endfor + catch + let save_exception = v:exception + endtry + call assert_match(a:errstr, save_exception) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test42.in b/src/nvim/testdir/test42.in Binary files differdeleted file mode 100644 index 456f9ddb07..0000000000 --- a/src/nvim/testdir/test42.in +++ /dev/null diff --git a/src/nvim/testdir/test42.ok b/src/nvim/testdir/test42.ok Binary files differdeleted file mode 100644 index 183430d71c..0000000000 --- a/src/nvim/testdir/test42.ok +++ /dev/null diff --git a/src/nvim/testdir/test49.in b/src/nvim/testdir/test49.in deleted file mode 100644 index 435e62765b..0000000000 --- a/src/nvim/testdir/test49.in +++ /dev/null @@ -1,31 +0,0 @@ -This is a test of the script language. - -If after adding a new test, the test output doesn't appear properly in -test49.failed, try to add one or more "G"s at the line ending in "test.out" - -STARTTEST -:se nomore -:lang mess C -:so test49.vim -:" Go back to this file and append the results from register r. -:buf test49.in -G"rp:/^Results/,$w! test.out -:" -:" make valgrind happy -:redir => funclist -:silent func -:redir END -:for line in split(funclist, "\n") -: let name = matchstr(line, 'function \zs[A-Z]\w*\ze(') -: if name != '' -: exe "delfunc " . name -: endif -:endfor -:for v in keys(g:) -: silent! exe "unlet " . v -:endfor -:unlet v -:qa! -ENDTEST - -Results of test49.vim: diff --git a/src/nvim/testdir/test49.ok b/src/nvim/testdir/test49.ok deleted file mode 100644 index 9f283e808b..0000000000 --- a/src/nvim/testdir/test49.ok +++ /dev/null @@ -1,56 +0,0 @@ -Results of test49.vim: -*** Test 18: OK (67224583) -*** Test 19: OK (69275973) -*** Test 20: OK (1874575085) -*** Test 21: OK (147932225) -*** Test 22: OK (4161) -*** Test 23: OK (49) -*** Test 24: OK (41) -*** Test 27: OK (1996459) -*** Test 28: OK (1996459) -*** Test 29: OK (170428555) -*** Test 30: OK (190905173) -*** Test 31: OK (190905173) -*** Test 34: OK (2146584868) -*** Test 35: OK (2146584868) -*** Test 36: OK (1071644672) -*** Test 37: OK (1071644672) -*** Test 38: OK (357908480) -*** Test 39: OK (357908480) -*** Test 40: OK (357908480) -*** Test 49: OK (179000669) -*** Test 50: OK (363550045) -*** Test 52: OK (1247112011) -*** Test 53: OK (131071) -*** Test 54: OK (2047) -*** Test 55: OK (1023) -*** Test 56: OK (511) -*** Test 57: OK (2147450880) -*** Test 58: OK (624945) -*** Test 59: OK (2038431743) -*** Test 60: OK (311511339) -*** Test 61: OK (374889517) -*** Test 62: OK (286331153) -*** Test 63: OK (236978127) -*** Test 64: OK (1499645335) -*** Test 65: OK (70187) -*** Test 66: OK (5464) -*** Test 67: OK (212514423) -*** Test 68: OK (212514423) -*** Test 76: OK (1610087935) -*** Test 77: OK (1388671) -*** Test 78: OK (134217728) -*** Test 79: OK (70288929) -*** Test 80: OK (17895765) -*** Test 81: OK (387) -*** Test 82: OK (8454401) -*** Test 83: OK (2835) -*** Test 84: OK (934782101) -*** Test 85: OK (198689) ---- Test 86: No Crash for vimgrep on BufUnload -*** Test 86: OK (0) ---- Test 88: All tests were run with throwing exceptions on error. - The $VIMNOERRTHROW control is not configured. ---- Test 88: All tests were run with throwing exceptions on interrupt. - The $VIMNOINTTHROW control is not configured. -*** Test 88: OK (50443995) diff --git a/src/nvim/testdir/test49.vim b/src/nvim/testdir/test49.vim deleted file mode 100644 index 3ee5e9ff7c..0000000000 --- a/src/nvim/testdir/test49.vim +++ /dev/null @@ -1,6724 +0,0 @@ -" Vim script language tests -" Author: Servatius Brandt <Servatius.Brandt@fujitsu-siemens.com> -" Last Change: 2019 Nov 03 - -"------------------------------------------------------------------------------- -" Test environment {{{1 -"------------------------------------------------------------------------------- - - -" Adding new tests easily. {{{2 -" -" Writing new tests is eased considerably with the following functions and -" abbreviations (see "Commands for recording the execution path", "Automatic -" argument generation"). -" -" To get the abbreviations, execute the command -" -" :let test49_set_env = 1 | source test49.vim -" -" To get them always (from src/nvim/testdir), put a line -" -" au! BufRead test49.vim let test49_set_env = 1 | source test49.vim -" -" into the local .vimrc file in the src/nvim/testdir directory. -" -if exists("test49_set_env") && test49_set_env - - " Automatic argument generation for the test environment commands. - - function! Xsum() - let addend = substitute(getline("."), '^.*"\s*X:\s*\|^.*', '', "") - " Evaluate arithmetic expression. - if addend != "" - exec "let g:Xsum = g:Xsum + " . addend - endif - endfunction - - function! Xcheck() - let g:Xsum=0 - ?XpathINIT?,.call Xsum() - exec "norm A " - return g:Xsum - endfunction - - iab Xcheck Xcheck<Space><C-R>=Xcheck()<CR><C-O>x - - function! Xcomment(num) - let str = "" - let tabwidth = &sts ? &sts : &ts - let tabs = (48+tabwidth - a:num - virtcol(".")) / tabwidth - while tabs > 0 - let str = str . "\t" - let tabs = tabs - 1 - endwhile - let str = str . '" X:' - return str - endfunction - - function! Xloop() - let back = line(".") . "|norm" . virtcol(".") . "|" - norm 0 - let last = search('X\(loop\|path\)INIT\|Xloop\>', "bW") - exec back - let theline = getline(last) - if theline =~ 'X\(loop\|path\)INIT' - let num = 1 - else - let num = 2 * substitute(theline, '.*Xloop\s*\(\d\+\).*', '\1', "") - endif - ?X\(loop\|path\)INIT? - \s/\(XloopINIT!\=\s*\d\+\s\+\)\@<=\(\d\+\)/\=2*submatch(2)/ - exec back - exec "norm a " - return num . Xcomment(strlen(num)) - endfunction - - iab Xloop Xloop<Space><C-R>=Xloop()<CR><C-O>x - - function! Xpath(loopinit) - let back = line(".") . "|norm" . virtcol(".") . "|" - norm 0 - let last = search('XpathINIT\|Xpath\>\|XloopINIT', "bW") - exec back - let theline = getline(last) - if theline =~ 'XpathINIT' - let num = 1 - elseif theline =~ 'Xpath\>' - let num = 2 * substitute(theline, '.*Xpath\s*\(\d\+\).*', '\1', "") - else - let pattern = '.*XloopINIT!\=\s*\(\d\+\)\s*\(\d\+\).*' - let num = substitute(theline, pattern, '\1', "") - let factor = substitute(theline, pattern, '\2', "") - " The "<C-O>x" from the "Xpath" iab and the character triggering its - " expansion are in the input buffer. Save and clear typeahead so - " that it is not read away by the call to "input()" below. Restore - " afterwards. - call inputsave() - let loops = input("Number of iterations in previous loop? ") - call inputrestore() - while (loops > 0) - let num = num * factor - let loops = loops - 1 - endwhile - endif - exec "norm a " - if a:loopinit - return num . " 1" - endif - return num . Xcomment(strlen(num)) - endfunction - - iab Xpath Xpath<Space><C-R>=Xpath(0)<CR><C-O>x - iab XloopINIT XloopINIT<Space><C-R>=Xpath(1)<CR><C-O>x - - " Also useful (see ExtraVim below): - aug ExtraVim - au! - au BufEnter <sfile> syn region ExtraVim - \ start=+^if\s\+ExtraVim(.*)+ end=+^endif+ - \ transparent keepend - au BufEnter <sfile> syn match ExtraComment /^"/ - \ contained containedin=ExtraVim - au BufEnter <sfile> hi link ExtraComment vimComment - aug END - - aug Xpath - au BufEnter <sfile> syn keyword Xpath - \ XpathINIT Xpath XloopINIT Xloop XloopNEXT Xcheck Xout - au BufEnter <sfile> hi link Xpath Special - aug END - - do BufEnter <sfile> - - " Do not execute the tests when sourcing this file for getting the functions - " and abbreviations above, which are intended for easily adding new test - " cases; they are not needed for test execution. Unlet the variable - " controlling this so that an explicit ":source" command for this file will - " execute the tests. - unlet test49_set_env - finish - -endif - - -" Commands for recording the execution path. {{{2 -" -" The Xpath/Xloop commands can be used for computing the eXecution path by -" adding (different) powers of 2 from those script lines, for which the -" execution should be checked. Xloop provides different addends for each -" execution of a loop. Permitted values are 2^0 to 2^30, so that 31 execution -" points (multiply counted inside loops) can be tested. -" -" Note that the arguments of the following commands can be generated -" automatically, see below. -" -" Usage: {{{3 -" -" - Use XpathINIT at the beginning of the test. -" -" - Use Xpath to check if a line is executed. -" Argument: power of 2 (decimal). -" -" - To check multiple execution of loops use Xloop for automatically -" computing Xpath values: -" -" - Use XloopINIT before the loop. -" Two arguments: -" - the first Xpath value (power of 2) to be used (Xnext), -" - factor for computing a new Xnext value when reexecuting a loop -" (by a ":continue" or ":endwhile"); this should be 2^n where -" n is the number of Xloop commands inside the loop. -" If XloopINIT! is used, the first execution of XloopNEXT is -" a no-operation. -" -" - Use Xloop inside the loop: -" One argument: -" The argument and the Xnext value are multiplied to build the -" next Xpath value. No new Xnext value is prepared. The argument -" should be 2^(n-1) for the nth Xloop command inside the loop. -" If the loop has only one Xloop command, the argument can be -" omitted (default: 1). -" -" - Use XloopNEXT before ":continue" and ":endwhile". This computes a new -" Xnext value for the next execution of the loop by multiplying the old -" one with the factor specified in the XloopINIT command. No Argument. -" Alternatively, when XloopINIT! is used, a single XloopNEXT at the -" beginning of the loop can be used. -" -" Nested loops are not supported. -" -" - Use Xcheck at end of each test. It prints the test number, the expected -" execution path value, the test result ("OK" or "FAIL"), and, if the tests -" fails, the actual execution path. -" One argument: -" Expected Xpath/Xloop sum for the correct execution path. -" In order that this value can be computed automatically, do the -" following: For each line in the test with an Xpath and Xloop -" command, add a comment starting with "X:" and specifying an -" expression that evaluates to the value contributed by this line to -" the correct execution path. (For copying an Xpath argument of at -" least two digits into the comment, press <C-P>.) At the end of the -" test, just type "Xcheck" and press <Esc>. -" -" - In order to add additional information to the test output file, use the -" Xout command. Argument(s) like ":echo". -" -" Automatic argument generation: {{{3 -" -" The arguments of the Xpath, XloopINIT, Xloop, and Xcheck commands can be -" generated automatically, so that new tests can easily be written without -" mental arithmetic. The Xcheck argument is computed from the "X:" comments -" of the preceding Xpath and Xloop commands. See the commands and -" abbreviations at the beginning of this file. -" -" Implementation: {{{3 -" XpathINIT, Xpath, XloopINIT, Xloop, XloopNEXT, Xcheck, Xout. -" -" The variants for existing g:ExtraVimResult are needed when executing a script -" in an extra Vim process, see ExtraVim below. - -" EXTRA_VIM_START - do not change or remove this line. - -com! XpathINIT let g:Xpath = 0 - -if exists("g:ExtraVimResult") - com! -count -bar Xpath exec "!echo <count> >>" . g:ExtraVimResult -else - com! -count -bar Xpath let g:Xpath = g:Xpath + <count> -endif - -com! -count -nargs=1 -bang - \ XloopINIT let g:Xnext = <count> | - \ let g:Xfactor = <args> | - \ let g:Xskip = strlen("<bang>") - -if exists("g:ExtraVimResult") - com! -count=1 -bar Xloop exec "!echo " . (g:Xnext * <count>) . " >>" . - \ g:ExtraVimResult -else - com! -count=1 -bar Xloop let g:Xpath = g:Xpath + g:Xnext * <count> -endif - -com! XloopNEXT let g:Xnext = g:Xnext * - \ (g:Xskip ? 1 : g:Xfactor) | - \ let g:Xskip = 0 - -let @r = "" -let Xtest = 1 -com! -count Xcheck let Xresult = "*** Test " . - \ (Xtest<10?" ":Xtest<100?" ":"") . - \ Xtest . ": " . ( - \ (Xpath==<count>) ? "OK (".Xpath.")" : - \ "FAIL (".Xpath." instead of <count>)" - \ ) | - \ let @R = Xresult . "\n" | - \ echo Xresult | - \ let Xtest = Xtest + 1 - -if exists("g:ExtraVimResult") - com! -nargs=+ Xoutq exec "!echo @R:'" . - \ substitute(substitute(<q-args>, - \ "'", '&\\&&', "g"), "\n", "@NL@", "g") - \ . "' >>" . g:ExtraVimResult -else - com! -nargs=+ Xoutq let @R = "--- Test " . - \ (g:Xtest<10?" ":g:Xtest<100?" ":"") . - \ g:Xtest . ": " . substitute(<q-args>, - \ "\n", "&\t ", "g") . "\n" -endif -com! -nargs=+ Xout exec 'Xoutq' <args> - -" Switch off storing of lines for undoing changes. Speeds things up a little. -set undolevels=-1 - -" EXTRA_VIM_STOP - do not change or remove this line. - - -" ExtraVim() - Run a script file in an extra Vim process. {{{2 -" -" This is useful for testing immediate abortion of the script processing due to -" an error in a command dynamically enclosed by a :try/:tryend region or when an -" exception is thrown but not caught or when an interrupt occurs. It can also -" be used for testing :finish. -" -" An interrupt location can be specified by an "INTERRUPT" comment. A number -" telling how often this location is reached (in a loop or in several function -" calls) should be specified as argument. When missing, once per script -" invocation or function call is assumed. INTERRUPT locations are tested by -" setting a breakpoint in that line and using the ">quit" debug command when -" the breakpoint is reached. A function for which an INTERRUPT location is -" specified must be defined before calling it (or executing it as a script by -" using ExecAsScript below). -" -" This function is only called in normal modus ("g:ExtraVimResult" undefined). -" -" Tests to be executed as an extra script should be written as follows: -" -" column 1 column 1 -" | | -" v v -" -" XpathINIT XpathINIT -" if ExtraVim() if ExtraVim() -" ... " ... -" ... " ... -" endif endif -" Xcheck <number> Xcheck <number> -" -" Double quotes in column 1 are removed before the script is executed. -" They should be used if the test has unbalanced conditionals (:if/:endif, -" :while:/endwhile, :try/:endtry) or for a line with a syntax error. The -" extra script may use Xpath, XloopINIT, Xloop, XloopNEXT, and Xout as usual. -" -" A file name may be specified as argument. All messages of the extra Vim -" process are then redirected to the file. An existing file is overwritten. -" -let ExtraVimCount = 0 -let ExtraVimBase = expand("<sfile>") -let ExtraVimTestEnv = "" -" -function ExtraVim(...) - " Count how often this function is called. - let g:ExtraVimCount = g:ExtraVimCount + 1 - - " Disable folds to prevent that the ranges in the ":write" commands below - " are extended up to the end of a closed fold. This also speeds things up - " considerably. - set nofoldenable - - " Open a buffer for this test script and copy the test environment to - " a temporary file. Take account of parts relevant for the extra script - " execution only. - let current_buffnr = bufnr("%") - execute "view +1" g:ExtraVimBase - if g:ExtraVimCount == 1 - let g:ExtraVimTestEnv = tempname() - execute "/E" . "XTRA_VIM_START/+,/E" . "XTRA_VIM_STOP/-w" - \ g:ExtraVimTestEnv "|']+" - execute "/E" . "XTRA_VIM_START/+,/E" . "XTRA_VIM_STOP/-w >>" - \ g:ExtraVimTestEnv "|']+" - execute "/E" . "XTRA_VIM_START/+,/E" . "XTRA_VIM_STOP/-w >>" - \ g:ExtraVimTestEnv "|']+" - execute "/E" . "XTRA_VIM_START/+,/E" . "XTRA_VIM_STOP/-w >>" - \ g:ExtraVimTestEnv "|']+" - endif - - " Start the extra Vim script with a ":source" command for the test - " environment. The source line number where the extra script will be - " appended, needs to be passed as variable "ExtraVimBegin" to the script. - let extra_script = tempname() - exec "!echo 'source " . g:ExtraVimTestEnv . "' >" . extra_script - let extra_begin = 1 - - " Starting behind the test environment, skip over the first g:ExtraVimCount - " occurrences of "if ExtraVim()" and copy the following lines up to the - " matching "endif" to the extra Vim script. - execute "/E" . "ND_OF_TEST_ENVIRONMENT/" - exec 'norm ' . g:ExtraVimCount . '/^\s*if\s\+ExtraVim(.*)/+' . "\n" - execute ".,/^endif/-write >>" . extra_script - - " Open a buffer for the extra Vim script, delete all ^", and write the - " script if was actually modified. - execute "edit +" . (extra_begin + 1) extra_script - ,$s/^"//e - update - - " Count the INTERRUPTs and build the breakpoint and quit commands. - let breakpoints = "" - let debug_quits = "" - let in_func = 0 - exec extra_begin - while search( - \ '"\s*INTERRUPT\h\@!\|^\s*fu\%[nction]\>!\=\s*\%(\u\|s:\)\w*\s*(\|' - \ . '^\s*\\\|^\s*endf\%[unction]\>\|' - \ . '\%(^\s*fu\%[nction]!\=\s*\)\@<!\%(\u\|s:\)\w*\s*(\|' - \ . 'ExecAsScript\s\+\%(\u\|s:\)\w*', - \ "W") > 0 - let theline = getline(".") - if theline =~ '^\s*fu' - " Function definition. - let in_func = 1 - let func_start = line(".") - let func_name = substitute(theline, - \ '^\s*fu\%[nction]!\=\s*\(\%(\u\|s:\)\w*\).*', '\1', "") - elseif theline =~ '^\s*endf' - " End of function definition. - let in_func = 0 - else - let finding = substitute(theline, '.*\(\%' . col(".") . 'c.*\)', - \ '\1', "") - if finding =~ '^"\s*INTERRUPT\h\@!' - " Interrupt comment. Compose as many quit commands as - " specified. - let cnt = substitute(finding, - \ '^"\s*INTERRUPT\s*\(\d*\).*$', '\1', "") - let quits = "" - while cnt > 0 - " Use "\r" rather than "\n" to separate the quit commands. - " "\r" is not interpreted as command separator by the ":!" - " command below but works to separate commands in the - " external vim. - let quits = quits . "q\r" - let cnt = cnt - 1 - endwhile - if in_func - " Add the function breakpoint and note the number of quits - " to be used, if specified, or one for every call else. - let breakpoints = breakpoints . " -c 'breakadd func " . - \ (line(".") - func_start) . " " . - \ func_name . "'" - if quits != "" - let debug_quits = debug_quits . quits - elseif !exists("quits{func_name}") - let quits{func_name} = "q\r" - else - let quits{func_name} = quits{func_name} . "q\r" - endif - else - " Add the file breakpoint and the quits to be used for it. - let breakpoints = breakpoints . " -c 'breakadd file " . - \ line(".") . " " . extra_script . "'" - if quits == "" - let quits = "q\r" - endif - let debug_quits = debug_quits . quits - endif - else - " Add the quits to be used for calling the function or executing - " it as script file. - if finding =~ '^ExecAsScript' - " Sourcing function as script. - let finding = substitute(finding, - \ '^ExecAsScript\s\+\(\%(\u\|s:\)\w*\).*', '\1', "") - else - " Function call. - let finding = substitute(finding, - \ '^\(\%(\u\|s:\)\w*\).*', '\1', "") - endif - if exists("quits{finding}") - let debug_quits = debug_quits . quits{finding} - endif - endif - endif - endwhile - - " Close the buffer for the script and create an (empty) resultfile. - bwipeout - let resultfile = tempname() - exec "!>" . resultfile - - " Run the script in an extra vim. Switch to extra modus by passing the - " resultfile in ExtraVimResult. Redirect messages to the file specified as - " argument if any. Use ":debuggreedy" so that the commands provided on the - " pipe are consumed at the debug prompt. Use "-N" to enable command-line - " continuation ("C" in 'cpo'). Add "nviminfo" to 'viminfo' to avoid - " messing up the user's viminfo file. - let redirect = a:0 ? - \ " -c 'au VimLeave * redir END' -c 'redir\\! >" . a:1 . "'" : "" - exec "!echo '" . debug_quits . "q' | " .. v:progpath .. " -u NONE -N -es" . redirect . - \ " -c 'debuggreedy|set viminfo+=nviminfo'" . - \ " -c 'let ExtraVimBegin = " . extra_begin . "'" . - \ " -c 'let ExtraVimResult = \"" . resultfile . "\"'" . breakpoints . - \ " -S " . extra_script - - " Build the resulting sum for resultfile and add it to g:Xpath. Add Xout - " information provided by the extra Vim process to the test output. - let sum = 0 - exec "edit" resultfile - let line = 1 - while line <= line("$") - let theline = getline(line) - if theline =~ '^@R:' - exec 'Xout "' . substitute(substitute( - \ escape(escape(theline, '"'), '\"'), - \ '^@R:', '', ""), '@NL@', "\n", "g") . '"' - else - let sum = sum + getline(line) - endif - let line = line + 1 - endwhile - bwipeout - let g:Xpath = g:Xpath + sum - - " Delete the extra script and the resultfile. - call delete(extra_script) - call delete(resultfile) - - " Switch back to the buffer that was active when this function was entered. - exec "buffer" current_buffnr - - " Return 0. This protects extra scripts from being run in the main Vim - " process. - return 0 -endfunction - - -" ExtraVimThrowpoint() - Relative throwpoint in ExtraVim script {{{2 -" -" Evaluates v:throwpoint and returns the throwpoint relative to the beginning of -" an ExtraVim script as passed by ExtraVim() in ExtraVimBegin. -" -" EXTRA_VIM_START - do not change or remove this line. -function ExtraVimThrowpoint() - if !exists("g:ExtraVimBegin") - Xout "ExtraVimThrowpoint() used outside ExtraVim() script." - return v:throwpoint - endif - - if v:throwpoint =~ '^function\>' - return v:throwpoint - endif - - return "line " . - \ (substitute(v:throwpoint, '.*, line ', '', "") - g:ExtraVimBegin) . - \ " of ExtraVim() script" -endfunction -" EXTRA_VIM_STOP - do not change or remove this line. - - -" MakeScript() - Make a script file from a function. {{{2 -" -" Create a script that consists of the body of the function a:funcname. -" Replace any ":return" by a ":finish", any argument variable by a global -" variable, and every ":call" by a ":source" for the next following argument -" in the variable argument list. This function is useful if similar tests are -" to be made for a ":return" from a function call or a ":finish" in a script -" file. -" -" In order to execute a function specifying an INTERRUPT location (see ExtraVim) -" as a script file, use ExecAsScript below. -" -" EXTRA_VIM_START - do not change or remove this line. -function MakeScript(funcname, ...) - let script = tempname() - execute "redir! >" . script - execute "function" a:funcname - redir END - execute "edit" script - " Delete the "function" and the "endfunction" lines. Do not include the - " word "function" in the pattern since it might be translated if LANG is - " set. When MakeScript() is being debugged, this deletes also the debugging - " output of its line 3 and 4. - exec '1,/.*' . a:funcname . '(.*)/d' - /^\d*\s*endfunction\>/,$d - %s/^\d*//e - %s/return/finish/e - %s/\<a:\(\h\w*\)/g:\1/ge - normal gg0 - let cnt = 0 - while search('\<call\s*\%(\u\|s:\)\w*\s*(.*)', 'W') > 0 - let cnt = cnt + 1 - s/\<call\s*\%(\u\|s:\)\w*\s*(.*)/\='source ' . a:{cnt}/ - endwhile - g/^\s*$/d - write - bwipeout - return script -endfunction -" EXTRA_VIM_STOP - do not change or remove this line. - - -" ExecAsScript - Source a temporary script made from a function. {{{2 -" -" Make a temporary script file from the function a:funcname, ":source" it, and -" delete it afterwards. -" -" When inside ":if ExtraVim()", add a file breakpoint for each INTERRUPT -" location specified in the function. -" -" EXTRA_VIM_START - do not change or remove this line. -function ExecAsScript(funcname) - " Make a script from the function passed as argument. - let script = MakeScript(a:funcname) - - " When running in an extra Vim process, add a file breakpoint for each - " function breakpoint set when the extra Vim process was invoked by - " ExtraVim(). - if exists("g:ExtraVimResult") - let bplist = tempname() - execute "redir! >" . bplist - breaklist - redir END - execute "edit" bplist - " Get the line number from the function breakpoint. Works also when - " LANG is set. - execute 'v/^\s*\d\+\s\+func\s\+' . a:funcname . '\s.*/d' - %s/^\s*\d\+\s\+func\s\+\%(\u\|s:\)\w*\s\D*\(\d*\).*/\1/e - let cnt = 0 - while cnt < line("$") - let cnt = cnt + 1 - if getline(cnt) != "" - execute "breakadd file" getline(cnt) script - endif - endwhile - bwipeout! - call delete(bplist) - endif - - " Source and delete the script. - exec "source" script - call delete(script) -endfunction - -com! -nargs=1 -bar ExecAsScript call ExecAsScript(<f-args>) -" EXTRA_VIM_STOP - do not change or remove this line. - - -" END_OF_TEST_ENVIRONMENT - do not change or remove this line. - - -" Tests 1 to 17 were moved to test_vimscript.vim -let Xtest = 18 - -"------------------------------------------------------------------------------- -" Test 18: Interrupt (Ctrl-C pressed) {{{1 -" -" On an interrupt, the script processing is terminated immediately. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - if 1 - Xpath 1 " X: 1 - while 1 - Xpath 2 " X: 2 - if 1 - Xpath 4 " X: 4 - "INTERRUPT - Xpath 8 " X: 0 - break - finish - endif | Xpath 16 " X: 0 - Xpath 32 " X: 0 - endwhile | Xpath 64 " X: 0 - Xpath 128 " X: 0 - endif | Xpath 256 " X: 0 - Xpath 512 " X: 0 -endif - -if ExtraVim() - try - Xpath 1024 " X: 1024 - "INTERRUPT - Xpath 2048 " X: 0 - endtry | Xpath 4096 " X: 0 - Xpath 8192 " X: 0 -endif - -if ExtraVim() - function! F() - if 1 - Xpath 16384 " X: 16384 - while 1 - Xpath 32768 " X: 32768 - if 1 - Xpath 65536 " X: 65536 - "INTERRUPT - Xpath 131072 " X: 0 - break - return - endif | Xpath 262144 " X: 0 - Xpath Xpath 524288 " X: 0 - endwhile | Xpath 1048576 " X: 0 - Xpath Xpath 2097152 " X: 0 - endif | Xpath Xpath 4194304 " X: 0 - Xpath Xpath 8388608 " X: 0 - endfunction - - call F() | Xpath 16777216 " X: 0 - Xpath 33554432 " X: 0 -endif - -if ExtraVim() - function! G() - try - Xpath 67108864 " X: 67108864 - "INTERRUPT - Xpath 134217728 " X: 0 - endtry | Xpath 268435456 " X: 0 - Xpath 536870912 " X: 0 - endfunction - - call G() | Xpath 1073741824 " X: 0 - " The Xpath command does not accept 2^31 (negative); display explicitly: - exec "!echo 2147483648 >>" . g:ExtraVimResult - " X: 0 -endif - -Xcheck 67224583 - - -"------------------------------------------------------------------------------- -" Test 19: Aborting on errors inside :try/:endtry {{{1 -" -" An error in a command dynamically enclosed in a :try/:endtry region -" aborts script processing immediately. It does not matter whether -" the failing command is outside or inside a function and whether a -" function has an "abort" attribute. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - function! F() abort - Xpath 1 " X: 1 - asdf - Xpath 2 " X: 0 - endfunction - - try - Xpath 4 " X: 4 - call F() - Xpath 8 " X: 0 - endtry | Xpath 16 " X: 0 - Xpath 32 " X: 0 -endif - -if ExtraVim() - function! G() - Xpath 64 " X: 64 - asdf - Xpath 128 " X: 0 - endfunction - - try - Xpath 256 " X: 256 - call G() - Xpath 512 " X: 0 - endtry | Xpath 1024 " X: 0 - Xpath 2048 " X: 0 -endif - -if ExtraVim() - try - Xpath 4096 " X: 4096 - asdf - Xpath 8192 " X: 0 - endtry | Xpath 16384 " X: 0 - Xpath 32768 " X: 0 -endif - -if ExtraVim() - if 1 - try - Xpath 65536 " X: 65536 - asdf - Xpath 131072 " X: 0 - endtry | Xpath 262144 " X: 0 - endif | Xpath 524288 " X: 0 - Xpath 1048576 " X: 0 -endif - -if ExtraVim() - let p = 1 - while p - let p = 0 - try - Xpath 2097152 " X: 2097152 - asdf - Xpath 4194304 " X: 0 - endtry | Xpath 8388608 " X: 0 - endwhile | Xpath 16777216 " X: 0 - Xpath 33554432 " X: 0 -endif - -if ExtraVim() - let p = 1 - while p - let p = 0 -" try - Xpath 67108864 " X: 67108864 - endwhile | Xpath 134217728 " X: 0 - Xpath 268435456 " X: 0 -endif - -Xcheck 69275973 -"------------------------------------------------------------------------------- -" Test 20: Aborting on errors after :try/:endtry {{{1 -" -" When an error occurs after the last active :try/:endtry region has -" been left, termination behavior is as if no :try/:endtry has been -" seen. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - let p = 1 - while p - let p = 0 - try - Xpath 1 " X: 1 - endtry - asdf - endwhile | Xpath 2 " X: 0 - Xpath 4 " X: 4 -endif - -if ExtraVim() - while 1 - try - Xpath 8 " X: 8 - break - Xpath 16 " X: 0 - endtry - endwhile - Xpath 32 " X: 32 - asdf - Xpath 64 " X: 64 -endif - -if ExtraVim() - while 1 - try - Xpath 128 " X: 128 - break - Xpath 256 " X: 0 - finally - Xpath 512 " X: 512 - endtry - endwhile - Xpath 1024 " X: 1024 - asdf - Xpath 2048 " X: 2048 -endif - -if ExtraVim() - while 1 - try - Xpath 4096 " X: 4096 - finally - Xpath 8192 " X: 8192 - break - Xpath 16384 " X: 0 - endtry - endwhile - Xpath 32768 " X: 32768 - asdf - Xpath 65536 " X: 65536 -endif - -if ExtraVim() - let p = 1 - while p - let p = 0 - try - Xpath 131072 " X: 131072 - continue - Xpath 262144 " X: 0 - endtry - endwhile - Xpath 524288 " X: 524288 - asdf - Xpath 1048576 " X: 1048576 -endif - -if ExtraVim() - let p = 1 - while p - let p = 0 - try - Xpath 2097152 " X: 2097152 - continue - Xpath 4194304 " X: 0 - finally - Xpath 8388608 " X: 8388608 - endtry - endwhile - Xpath 16777216 " X: 16777216 - asdf - Xpath 33554432 " X: 33554432 -endif - -if ExtraVim() - let p = 1 - while p - let p = 0 - try - Xpath 67108864 " X: 67108864 - finally - Xpath 134217728 " X: 134217728 - continue - Xpath 268435456 " X: 0 - endtry - endwhile - Xpath 536870912 " X: 536870912 - asdf - Xpath 1073741824 " X: 1073741824 -endif - -Xcheck 1874575085 - - -"------------------------------------------------------------------------------- -" Test 21: :finally for :try after :continue/:break/:return/:finish {{{1 -" -" If a :try conditional stays inactive due to a preceding :continue, -" :break, :return, or :finish, its :finally clause should not be -" executed. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - function F() - let loops = 2 - XloopINIT! 1 256 - while loops > 0 - XloopNEXT - let loops = loops - 1 - try - if loops == 1 - Xloop 1 " X: 1 - continue - Xloop 2 " X: 0 - elseif loops == 0 - Xloop 4 " X: 4*256 - break - Xloop 8 " X: 0 - endif - - try " inactive - Xloop 16 " X: 0 - finally - Xloop 32 " X: 0 - endtry - finally - Xloop 64 " X: 64 + 64*256 - endtry - Xloop 128 " X: 0 - endwhile - - try - Xpath 65536 " X: 65536 - return - Xpath 131072 " X: 0 - try " inactive - Xpath 262144 " X: 0 - finally - Xpath 524288 " X: 0 - endtry - finally - Xpath 1048576 " X: 1048576 - endtry - Xpath 2097152 " X: 0 - endfunction - - try - Xpath 4194304 " X: 4194304 - call F() - Xpath 8388608 " X: 8388608 - finish - Xpath 16777216 " X: 0 - try " inactive - Xpath 33554432 " X: 0 - finally - Xpath 67108864 " X: 0 - endtry - finally - Xpath 134217728 " X: 134217728 - endtry - Xpath 268435456 " X: 0 -endif - -Xcheck 147932225 - - -"------------------------------------------------------------------------------- -" Test 22: :finally for a :try after an error/interrupt/:throw {{{1 -" -" If a :try conditional stays inactive due to a preceding error or -" interrupt or :throw, its :finally clause should not be executed. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - function! Error() - try - asdf " aborting error, triggering error exception - endtry - endfunction - - Xpath 1 " X: 1 - call Error() - Xpath 2 " X: 0 - - if 1 " not active due to error - try " not active since :if inactive - Xpath 4 " X: 0 - finally - Xpath 8 " X: 0 - endtry - endif - - try " not active due to error - Xpath 16 " X: 0 - finally - Xpath 32 " X: 0 - endtry -endif - -if ExtraVim() - function! Interrupt() - try - "INTERRUPT " triggering interrupt exception - endtry - endfunction - - Xpath 64 " X: 64 - call Interrupt() - Xpath 128 " X: 0 - - if 1 " not active due to interrupt - try " not active since :if inactive - Xpath 256 " X: 0 - finally - Xpath 512 " X: 0 - endtry - endif - - try " not active due to interrupt - Xpath 1024 " X: 0 - finally - Xpath 2048 " X: 0 - endtry -endif - -if ExtraVim() - function! Throw() - throw "xyz" - endfunction - - Xpath 4096 " X: 4096 - call Throw() - Xpath 8192 " X: 0 - - if 1 " not active due to :throw - try " not active since :if inactive - Xpath 16384 " X: 0 - finally - Xpath 32768 " X: 0 - endtry - endif - - try " not active due to :throw - Xpath 65536 " X: 0 - finally - Xpath 131072 " X: 0 - endtry -endif - -Xcheck 4161 - - -"------------------------------------------------------------------------------- -" Test 23: :catch clauses for a :try after a :throw {{{1 -" -" If a :try conditional stays inactive due to a preceding :throw, -" none of its :catch clauses should be executed. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - try - Xpath 1 " X: 1 - throw "xyz" - Xpath 2 " X: 0 - - if 1 " not active due to :throw - try " not active since :if inactive - Xpath 4 " X: 0 - catch /xyz/ - Xpath 8 " X: 0 - endtry - endif - catch /xyz/ - Xpath 16 " X: 16 - endtry - - Xpath 32 " X: 32 - throw "abc" - Xpath 64 " X: 0 - - try " not active due to :throw - Xpath 128 " X: 0 - catch /abc/ - Xpath 256 " X: 0 - endtry -endif - -Xcheck 49 - - -"------------------------------------------------------------------------------- -" Test 24: :endtry for a :try after a :throw {{{1 -" -" If a :try conditional stays inactive due to a preceding :throw, -" its :endtry should not rethrow the exception to the next surrounding -" active :try conditional. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - try " try 1 - try " try 2 - Xpath 1 " X: 1 - throw "xyz" " makes try 2 inactive - Xpath 2 " X: 0 - - try " try 3 - Xpath 4 " X: 0 - endtry " no rethrow to try 1 - catch /xyz/ " should catch although try 2 inactive - Xpath 8 " X: 8 - endtry - catch /xyz/ " try 1 active, but exception already caught - Xpath 16 " X: 0 - endtry - Xpath 32 " X: 32 -endif - -Xcheck 41 - -" Tests 25 and 26 were moved to test_trycatch.vim -let Xtest = 27 - - -"------------------------------------------------------------------------------- -" Test 27: Executing :finally clauses after :return {{{1 -" -" For a :return command dynamically enclosed in a :try/:endtry region, -" :finally clauses are executed and the called function is ended. -"------------------------------------------------------------------------------- - -XpathINIT - -function! F() - try - Xpath 1 " X: 1 - try - Xpath 2 " X: 2 - return - Xpath 4 " X: 0 - finally - Xpath 8 " X: 8 - endtry - Xpath 16 " X: 0 - finally - Xpath 32 " X: 32 - endtry - Xpath 64 " X: 0 -endfunction - -function! G() - try - Xpath 128 " X: 128 - return - Xpath 256 " X: 0 - finally - Xpath 512 " X: 512 - call F() - Xpath 1024 " X: 1024 - endtry - Xpath 2048 " X: 0 -endfunction - -function! H() - try - Xpath 4096 " X: 4096 - call G() - Xpath 8192 " X: 8192 - finally - Xpath 16384 " X: 16384 - return - Xpath 32768 " X: 0 - endtry - Xpath 65536 " X: 0 -endfunction - -try - Xpath 131072 " X: 131072 - call H() - Xpath 262144 " X: 262144 -finally - Xpath 524288 " X: 524288 -endtry -Xpath 1048576 " X: 1048576 - -Xcheck 1996459 - -" Leave F, G, and H for execution as scripts in the next test. - - -"------------------------------------------------------------------------------- -" Test 28: Executing :finally clauses after :finish {{{1 -" -" For a :finish command dynamically enclosed in a :try/:endtry region, -" :finally clauses are executed and the sourced file is finished. -" -" This test executes the bodies of the functions F, G, and H from the -" previous test as script files (:return replaced by :finish). -"------------------------------------------------------------------------------- - -XpathINIT - -let scriptF = MakeScript("F") " X: 1 + 2 + 8 + 32 -let scriptG = MakeScript("G", scriptF) " X: 128 + 512 + 1024 -let scriptH = MakeScript("H", scriptG) " X: 4096 + 8192 + 16384 - -try - Xpath 131072 " X: 131072 - exec "source" scriptH - Xpath 262144 " X: 262144 -finally - Xpath 524288 " X: 524288 -endtry -Xpath 1048576 " X: 1048576 - -call delete(scriptF) -call delete(scriptG) -call delete(scriptH) -unlet scriptF scriptG scriptH -delfunction F -delfunction G -delfunction H - -Xcheck 1996459 - - -"------------------------------------------------------------------------------- -" Test 29: Executing :finally clauses on errors {{{1 -" -" After an error in a command dynamically enclosed in a :try/:endtry -" region, :finally clauses are executed and the script processing is -" terminated. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - function! F() - while 1 - try - Xpath 1 " X: 1 - while 1 - try - Xpath 2 " X: 2 - asdf " error - Xpath 4 " X: 0 - finally - Xpath 8 " X: 8 - endtry | Xpath 16 " X: 0 - Xpath 32 " X: 0 - break - endwhile - Xpath 64 " X: 0 - finally - Xpath 128 " X: 128 - endtry | Xpath 256 " X: 0 - Xpath 512 " X: 0 - break - endwhile - Xpath 1024 " X: 0 - endfunction - - while 1 - try - Xpath 2048 " X: 2048 - while 1 - call F() - Xpath 4096 " X: 0 - break - endwhile | Xpath 8192 " X: 0 - Xpath 16384 " X: 0 - finally - Xpath 32768 " X: 32768 - endtry | Xpath 65536 " X: 0 - endwhile | Xpath 131072 " X: 0 - Xpath 262144 " X: 0 -endif - -if ExtraVim() - function! G() abort - if 1 - try - Xpath 524288 " X: 524288 - asdf " error - Xpath 1048576 " X: 0 - finally - Xpath 2097152 " X: 2097152 - endtry | Xpath 4194304 " X: 0 - endif | Xpath 8388608 " X: 0 - Xpath 16777216 " X: 0 - endfunction - - if 1 - try - Xpath 33554432 " X: 33554432 - call G() - Xpath 67108864 " X: 0 - finally - Xpath 134217728 " X: 134217728 - endtry | Xpath 268435456 " X: 0 - endif | Xpath 536870912 " X: 0 - Xpath 1073741824 " X: 0 -endif - -Xcheck 170428555 - - -"------------------------------------------------------------------------------- -" Test 30: Executing :finally clauses on interrupt {{{1 -" -" After an interrupt in a command dynamically enclosed in -" a :try/:endtry region, :finally clauses are executed and the -" script processing is terminated. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - XloopINIT 1 16 - - function! F() - try - Xloop 1 " X: 1 + 1*16 - "INTERRUPT - Xloop 2 " X: 0 - finally - Xloop 4 " X: 4 + 4*16 - endtry - Xloop 8 " X: 0 - endfunction - - try - Xpath 256 " X: 256 - try - Xpath 512 " X: 512 - "INTERRUPT - Xpath 1024 " X: 0 - finally - Xpath 2048 " X: 2048 - try - Xpath 4096 " X: 4096 - try - Xpath 8192 " X: 8192 - finally - Xpath 16384 " X: 16384 - try - Xpath 32768 " X: 32768 - "INTERRUPT - Xpath 65536 " X: 0 - endtry - Xpath 131072 " X: 0 - endtry - Xpath 262144 " X: 0 - endtry - Xpath 524288 " X: 0 - endtry - Xpath 1048576 " X: 0 - finally - Xpath 2097152 " X: 2097152 - try - Xpath 4194304 " X: 4194304 - call F() - Xpath 8388608 " X: 0 - finally - Xpath 16777216 " X: 16777216 - try - Xpath 33554432 " X: 33554432 - XloopNEXT - ExecAsScript F - Xpath 67108864 " X: 0 - finally - Xpath 134217728 " X: 134217728 - endtry - Xpath 268435456 " X: 0 - endtry - Xpath 536870912 " X: 0 - endtry - Xpath 1073741824 " X: 0 -endif - -Xcheck 190905173 - - -"------------------------------------------------------------------------------- -" Test 31: Executing :finally clauses after :throw {{{1 -" -" After a :throw dynamically enclosed in a :try/:endtry region, -" :finally clauses are executed and the script processing is -" terminated. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - XloopINIT 1 16 - - function! F() - try - Xloop 1 " X: 1 + 1*16 - throw "exception" - Xloop 2 " X: 0 - finally - Xloop 4 " X: 4 + 4*16 - endtry - Xloop 8 " X: 0 - endfunction - - try - Xpath 256 " X: 256 - try - Xpath 512 " X: 512 - throw "exception" - Xpath 1024 " X: 0 - finally - Xpath 2048 " X: 2048 - try - Xpath 4096 " X: 4096 - try - Xpath 8192 " X: 8192 - finally - Xpath 16384 " X: 16384 - try - Xpath 32768 " X: 32768 - throw "exception" - Xpath 65536 " X: 0 - endtry - Xpath 131072 " X: 0 - endtry - Xpath 262144 " X: 0 - endtry - Xpath 524288 " X: 0 - endtry - Xpath 1048576 " X: 0 - finally - Xpath 2097152 " X: 2097152 - try - Xpath 4194304 " X: 4194304 - call F() - Xpath 8388608 " X: 0 - finally - Xpath 16777216 " X: 16777216 - try - Xpath 33554432 " X: 33554432 - XloopNEXT - ExecAsScript F - Xpath 67108864 " X: 0 - finally - Xpath 134217728 " X: 134217728 - endtry - Xpath 268435456 " X: 0 - endtry - Xpath 536870912 " X: 0 - endtry - Xpath 1073741824 " X: 0 -endif - -Xcheck 190905173 - -" Tests 32 and 33 were moved to test_trycatch.vim -let Xtest = 34 - - -"------------------------------------------------------------------------------- -" Test 34: :finally reason discarded by :continue {{{1 -" -" When a :finally clause is executed due to a :continue, :break, -" :return, :finish, error, interrupt or :throw, the jump reason is -" discarded by a :continue in the finally clause. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - - XloopINIT! 1 8 - - function! C(jump) - XloopNEXT - let loop = 0 - while loop < 2 - let loop = loop + 1 - if loop == 1 - try - if a:jump == "continue" - continue - elseif a:jump == "break" - break - elseif a:jump == "return" || a:jump == "finish" - return - elseif a:jump == "error" - asdf - elseif a:jump == "interrupt" - "INTERRUPT - let dummy = 0 - elseif a:jump == "throw" - throw "abc" - endif - finally - continue " discards jump that caused the :finally - Xloop 1 " X: 0 - endtry - Xloop 2 " X: 0 - elseif loop == 2 - Xloop 4 " X: 4*(1+8+64+512+4096+32768+262144) - endif - endwhile - endfunction - - call C("continue") - Xpath 2097152 " X: 2097152 - call C("break") - Xpath 4194304 " X: 4194304 - call C("return") - Xpath 8388608 " X: 8388608 - let g:jump = "finish" - ExecAsScript C - unlet g:jump - Xpath 16777216 " X: 16777216 - try - call C("error") - Xpath 33554432 " X: 33554432 - finally - Xpath 67108864 " X: 67108864 - try - call C("interrupt") - Xpath 134217728 " X: 134217728 - finally - Xpath 268435456 " X: 268435456 - call C("throw") - Xpath 536870912 " X: 536870912 - endtry - endtry - Xpath 1073741824 " X: 1073741824 - - delfunction C - -endif - -Xcheck 2146584868 - - -"------------------------------------------------------------------------------- -" Test 35: :finally reason discarded by :break {{{1 -" -" When a :finally clause is executed due to a :continue, :break, -" :return, :finish, error, interrupt or :throw, the jump reason is -" discarded by a :break in the finally clause. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - - XloopINIT! 1 8 - - function! B(jump) - XloopNEXT - let loop = 0 - while loop < 2 - let loop = loop + 1 - if loop == 1 - try - if a:jump == "continue" - continue - elseif a:jump == "break" - break - elseif a:jump == "return" || a:jump == "finish" - return - elseif a:jump == "error" - asdf - elseif a:jump == "interrupt" - "INTERRUPT - let dummy = 0 - elseif a:jump == "throw" - throw "abc" - endif - finally - break " discards jump that caused the :finally - Xloop 1 " X: 0 - endtry - elseif loop == 2 - Xloop 2 " X: 0 - endif - endwhile - Xloop 4 " X: 4*(1+8+64+512+4096+32768+262144) - endfunction - - call B("continue") - Xpath 2097152 " X: 2097152 - call B("break") - Xpath 4194304 " X: 4194304 - call B("return") - Xpath 8388608 " X: 8388608 - let g:jump = "finish" - ExecAsScript B - unlet g:jump - Xpath 16777216 " X: 16777216 - try - call B("error") - Xpath 33554432 " X: 33554432 - finally - Xpath 67108864 " X: 67108864 - try - call B("interrupt") - Xpath 134217728 " X: 134217728 - finally - Xpath 268435456 " X: 268435456 - call B("throw") - Xpath 536870912 " X: 536870912 - endtry - endtry - Xpath 1073741824 " X: 1073741824 - - delfunction B - -endif - -Xcheck 2146584868 - - -"------------------------------------------------------------------------------- -" Test 36: :finally reason discarded by :return {{{1 -" -" When a :finally clause is executed due to a :continue, :break, -" :return, :finish, error, interrupt or :throw, the jump reason is -" discarded by a :return in the finally clause. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - - XloopINIT! 1 8 - - function! R(jump, retval) abort - XloopNEXT - let loop = 0 - while loop < 2 - let loop = loop + 1 - if loop == 1 - try - if a:jump == "continue" - continue - elseif a:jump == "break" - break - elseif a:jump == "return" - return - elseif a:jump == "error" - asdf - elseif a:jump == "interrupt" - "INTERRUPT - let dummy = 0 - elseif a:jump == "throw" - throw "abc" - endif - finally - return a:retval " discards jump that caused the :finally - Xloop 1 " X: 0 - endtry - elseif loop == 2 - Xloop 2 " X: 0 - endif - endwhile - Xloop 4 " X: 0 - endfunction - - let sum = -R("continue", -8) - Xpath 2097152 " X: 2097152 - let sum = sum - R("break", -16) - Xpath 4194304 " X: 4194304 - let sum = sum - R("return", -32) - Xpath 8388608 " X: 8388608 - try - let sum = sum - R("error", -64) - Xpath 16777216 " X: 16777216 - finally - Xpath 33554432 " X: 33554432 - try - let sum = sum - R("interrupt", -128) - Xpath 67108864 " X: 67108864 - finally - Xpath 134217728 " X: 134217728 - let sum = sum - R("throw", -256) - Xpath 268435456 " X: 268435456 - endtry - endtry - Xpath 536870912 " X: 536870912 - - let expected = 8 + 16 + 32 + 64 + 128 + 256 - if sum != expected - Xpath 1073741824 " X: 0 - Xout "sum =" . sum . ", expected: " . expected - endif - - unlet sum expected - delfunction R - -endif - -Xcheck 1071644672 - - -"------------------------------------------------------------------------------- -" Test 37: :finally reason discarded by :finish {{{1 -" -" When a :finally clause is executed due to a :continue, :break, -" :return, :finish, error, interrupt or :throw, the jump reason is -" discarded by a :finish in the finally clause. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - - XloopINIT! 1 8 - - function! F(jump) " not executed as function, transformed to a script - XloopNEXT - let loop = 0 - while loop < 2 - let loop = loop + 1 - if loop == 1 - try - if a:jump == "continue" - continue - elseif a:jump == "break" - break - elseif a:jump == "finish" - finish - elseif a:jump == "error" - asdf - elseif a:jump == "interrupt" - "INTERRUPT - let dummy = 0 - elseif a:jump == "throw" - throw "abc" - endif - finally - finish " discards jump that caused the :finally - Xloop 1 " X: 0 - endtry - elseif loop == 2 - Xloop 2 " X: 0 - endif - endwhile - Xloop 4 " X: 0 - endfunction - - let scriptF = MakeScript("F") - delfunction F - - let g:jump = "continue" - exec "source" scriptF - Xpath 2097152 " X: 2097152 - let g:jump = "break" - exec "source" scriptF - Xpath 4194304 " X: 4194304 - let g:jump = "finish" - exec "source" scriptF - Xpath 8388608 " X: 8388608 - try - let g:jump = "error" - exec "source" scriptF - Xpath 16777216 " X: 16777216 - finally - Xpath 33554432 " X: 33554432 - try - let g:jump = "interrupt" - exec "source" scriptF - Xpath 67108864 " X: 67108864 - finally - Xpath 134217728 " X: 134217728 - try - let g:jump = "throw" - exec "source" scriptF - Xpath 268435456 " X: 268435456 - finally - Xpath 536870912 " X: 536870912 - endtry - endtry - endtry - unlet g:jump - - call delete(scriptF) - unlet scriptF - -endif - -Xcheck 1071644672 - - -"------------------------------------------------------------------------------- -" Test 38: :finally reason discarded by an error {{{1 -" -" When a :finally clause is executed due to a :continue, :break, -" :return, :finish, error, interrupt or :throw, the jump reason is -" discarded by an error in the finally clause. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - - XloopINIT! 1 4 - - function! E(jump) - XloopNEXT - let loop = 0 - while loop < 2 - let loop = loop + 1 - if loop == 1 - try - if a:jump == "continue" - continue - elseif a:jump == "break" - break - elseif a:jump == "return" || a:jump == "finish" - return - elseif a:jump == "error" - asdf - elseif a:jump == "interrupt" - "INTERRUPT - let dummy = 0 - elseif a:jump == "throw" - throw "abc" - endif - finally - asdf " error; discards jump that caused the :finally - endtry - elseif loop == 2 - Xloop 1 " X: 0 - endif - endwhile - Xloop 2 " X: 0 - endfunction - - try - Xpath 16384 " X: 16384 - call E("continue") - Xpath 32768 " X: 0 - finally - try - Xpath 65536 " X: 65536 - call E("break") - Xpath 131072 " X: 0 - finally - try - Xpath 262144 " X: 262144 - call E("return") - Xpath 524288 " X: 0 - finally - try - Xpath 1048576 " X: 1048576 - let g:jump = "finish" - ExecAsScript E - Xpath 2097152 " X: 0 - finally - unlet g:jump - try - Xpath 4194304 " X: 4194304 - call E("error") - Xpath 8388608 " X: 0 - finally - try - Xpath 16777216 " X: 16777216 - call E("interrupt") - Xpath 33554432 " X: 0 - finally - try - Xpath 67108864 " X: 67108864 - call E("throw") - Xpath 134217728 " X: 0 - finally - Xpath 268435456 " X: 268435456 - delfunction E - endtry - endtry - endtry - endtry - endtry - endtry - endtry - Xpath 536870912 " X: 0 - -endif - -Xcheck 357908480 - - -"------------------------------------------------------------------------------- -" Test 39: :finally reason discarded by an interrupt {{{1 -" -" When a :finally clause is executed due to a :continue, :break, -" :return, :finish, error, interrupt or :throw, the jump reason is -" discarded by an interrupt in the finally clause. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - - XloopINIT! 1 4 - - function! I(jump) - XloopNEXT - let loop = 0 - while loop < 2 - let loop = loop + 1 - if loop == 1 - try - if a:jump == "continue" - continue - elseif a:jump == "break" - break - elseif a:jump == "return" || a:jump == "finish" - return - elseif a:jump == "error" - asdf - elseif a:jump == "interrupt" - "INTERRUPT - let dummy = 0 - elseif a:jump == "throw" - throw "abc" - endif - finally - "INTERRUPT - discards jump that caused the :finally - let dummy = 0 - endtry - elseif loop == 2 - Xloop 1 " X: 0 - endif - endwhile - Xloop 2 " X: 0 - endfunction - - try - Xpath 16384 " X: 16384 - call I("continue") - Xpath 32768 " X: 0 - finally - try - Xpath 65536 " X: 65536 - call I("break") - Xpath 131072 " X: 0 - finally - try - Xpath 262144 " X: 262144 - call I("return") - Xpath 524288 " X: 0 - finally - try - Xpath 1048576 " X: 1048576 - let g:jump = "finish" - ExecAsScript I - Xpath 2097152 " X: 0 - finally - unlet g:jump - try - Xpath 4194304 " X: 4194304 - call I("error") - Xpath 8388608 " X: 0 - finally - try - Xpath 16777216 " X: 16777216 - call I("interrupt") - Xpath 33554432 " X: 0 - finally - try - Xpath 67108864 " X: 67108864 - call I("throw") - Xpath 134217728 " X: 0 - finally - Xpath 268435456 " X: 268435456 - delfunction I - endtry - endtry - endtry - endtry - endtry - endtry - endtry - Xpath 536870912 " X: 0 - -endif - -Xcheck 357908480 - - -"------------------------------------------------------------------------------- -" Test 40: :finally reason discarded by :throw {{{1 -" -" When a :finally clause is executed due to a :continue, :break, -" :return, :finish, error, interrupt or :throw, the jump reason is -" discarded by a :throw in the finally clause. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - - XloopINIT! 1 4 - - function! T(jump) - XloopNEXT - let loop = 0 - while loop < 2 - let loop = loop + 1 - if loop == 1 - try - if a:jump == "continue" - continue - elseif a:jump == "break" - break - elseif a:jump == "return" || a:jump == "finish" - return - elseif a:jump == "error" - asdf - elseif a:jump == "interrupt" - "INTERRUPT - let dummy = 0 - elseif a:jump == "throw" - throw "abc" - endif - finally - throw "xyz" " discards jump that caused the :finally - endtry - elseif loop == 2 - Xloop 1 " X: 0 - endif - endwhile - Xloop 2 " X: 0 - endfunction - - try - Xpath 16384 " X: 16384 - call T("continue") - Xpath 32768 " X: 0 - finally - try - Xpath 65536 " X: 65536 - call T("break") - Xpath 131072 " X: 0 - finally - try - Xpath 262144 " X: 262144 - call T("return") - Xpath 524288 " X: 0 - finally - try - Xpath 1048576 " X: 1048576 - let g:jump = "finish" - ExecAsScript T - Xpath 2097152 " X: 0 - finally - unlet g:jump - try - Xpath 4194304 " X: 4194304 - call T("error") - Xpath 8388608 " X: 0 - finally - try - Xpath 16777216 " X: 16777216 - call T("interrupt") - Xpath 33554432 " X: 0 - finally - try - Xpath 67108864 " X: 67108864 - call T("throw") - Xpath 134217728 " X: 0 - finally - Xpath 268435456 " X: 268435456 - delfunction T - endtry - endtry - endtry - endtry - endtry - endtry - endtry - Xpath 536870912 " X: 0 - -endif - -Xcheck 357908480 - -" Tests 41 to 48 were moved to test_trycatch.vim -let Xtest = 49 - - -"------------------------------------------------------------------------------- -" Test 49: Throwing exceptions across functions {{{1 -" -" When an exception is thrown but not caught inside a function, the -" caller is checked for a matching :catch clause. -"------------------------------------------------------------------------------- - -XpathINIT - -function! C() - try - Xpath 1 " X: 1 - throw "arrgh" - Xpath 2 " X: 0 - catch /arrgh/ - Xpath 4 " X: 4 - endtry - Xpath 8 " X: 8 -endfunction - -XloopINIT! 16 16 - -function! T1() - XloopNEXT - try - Xloop 1 " X: 16 + 16*16 - throw "arrgh" - Xloop 2 " X: 0 - finally - Xloop 4 " X: 64 + 64*16 - endtry - Xloop 8 " X: 0 -endfunction - -function! T2() - try - Xpath 4096 " X: 4096 - call T1() - Xpath 8192 " X: 0 - finally - Xpath 16384 " X: 16384 - endtry - Xpath 32768 " X: 0 -endfunction - -try - Xpath 65536 " X: 65536 - call C() " throw and catch - Xpath 131072 " X: 131072 -catch /.*/ - Xpath 262144 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -try - Xpath 524288 " X: 524288 - call T1() " throw, one level - Xpath 1048576 " X: 0 -catch /arrgh/ - Xpath 2097152 " X: 2097152 -catch /.*/ - Xpath 4194304 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -try - Xpath 8388608 " X: 8388608 - call T2() " throw, two levels - Xpath 16777216 " X: 0 -catch /arrgh/ - Xpath 33554432 " X: 33554432 -catch /.*/ - Xpath 67108864 " X: 0 - Xout v:exception "in" v:throwpoint -endtry -Xpath 134217728 " X: 134217728 - -Xcheck 179000669 - -" Leave C, T1, and T2 for execution as scripts in the next test. - - -"------------------------------------------------------------------------------- -" Test 50: Throwing exceptions across script files {{{1 -" -" When an exception is thrown but not caught inside a script file, -" the sourcing script or function is checked for a matching :catch -" clause. -" -" This test executes the bodies of the functions C, T1, and T2 from -" the previous test as script files (:return replaced by :finish). -"------------------------------------------------------------------------------- - -XpathINIT - -let scriptC = MakeScript("C") " X: 1 + 4 + 8 -delfunction C - -XloopINIT! 16 16 - -let scriptT1 = MakeScript("T1") " X: 16 + 64 + 16*16 + 64*16 -delfunction T1 - -let scriptT2 = MakeScript("T2", scriptT1) " X: 4096 + 16384 -delfunction T2 - -function! F() - try - Xpath 65536 " X: 65536 - exec "source" g:scriptC - Xpath 131072 " X: 131072 - catch /.*/ - Xpath 262144 " X: 0 - Xout v:exception "in" v:throwpoint - endtry - - try - Xpath 524288 " X: 524288 - exec "source" g:scriptT1 - Xpath 1048576 " X: 0 - catch /arrgh/ - Xpath 2097152 " X: 2097152 - catch /.*/ - Xpath 4194304 " X: 0 - Xout v:exception "in" v:throwpoint - endtry -endfunction - -try - Xpath 8388608 " X: 8388608 - call F() - Xpath 16777216 " X: 16777216 - exec "source" scriptT2 - Xpath 33554432 " X: 0 -catch /arrgh/ - Xpath 67108864 " X: 67108864 -catch /.*/ - Xpath 134217728 " X: 0 - Xout v:exception "in" v:throwpoint -endtry -Xpath 268435456 " X: 268435456 - -call delete(scriptC) -call delete(scriptT1) -call delete(scriptT2) -unlet scriptC scriptT1 scriptT2 -delfunction F - -Xcheck 363550045 - -" Test 51 was moved to test_trycatch.vim -let Xtest = 52 - - -"------------------------------------------------------------------------------- -" Test 52: Uncaught exceptions {{{1 -" -" When an exception is thrown but not caught, an error message is -" displayed when the script is terminated. In case of an interrupt -" or error exception, the normal interrupt or error message(s) are -" displayed. -"------------------------------------------------------------------------------- - -XpathINIT - -let msgfile = tempname() - -function! MESSAGES(...) - try - exec "edit" g:msgfile - catch /^Vim(edit):/ - return 0 - endtry - - let english = v:lang == "C" || v:lang =~ '^[Ee]n' - let match = 1 - norm gg - - let num = a:0 / 2 - let cnt = 1 - while cnt <= num - let enr = a:{2*cnt - 1} - let emsg= a:{2*cnt} - let cnt = cnt + 1 - - if enr == "" - Xout "TODO: Add message number for:" emsg - elseif enr == "INT" - let enr = "" - endif - if enr == "" && !english - continue - endif - let pattern = (enr != "") ? enr . ':.*' : '' - if english - let pattern = pattern . emsg - endif - if !search(pattern, "W") - let match = 0 - Xout "No match for:" pattern - endif - norm $ - endwhile - - bwipeout! - return match -endfunction - -if ExtraVim(msgfile) - Xpath 1 " X: 1 - throw "arrgh" -endif - -Xpath 2 " X: 2 -if !MESSAGES('E605', "Exception not caught") - Xpath 4 " X: 0 -endif - -if ExtraVim(msgfile) - try - Xpath 8 " X: 8 - throw "oops" - catch /arrgh/ - Xpath 16 " X: 0 - endtry - Xpath 32 " X: 0 -endif - -Xpath 64 " X: 64 -if !MESSAGES('E605', "Exception not caught") - Xpath 128 " X: 0 -endif - -if ExtraVim(msgfile) - function! T() - throw "brrr" - endfunction - - try - Xpath 256 " X: 256 - throw "arrgh" - catch /.*/ - Xpath 512 " X: 512 - call T() - endtry - Xpath 1024 " X: 0 -endif - -Xpath 2048 " X: 2048 -if !MESSAGES('E605', "Exception not caught") - Xpath 4096 " X: 0 -endif - -if ExtraVim(msgfile) - try - Xpath 8192 " X: 8192 - throw "arrgh" - finally - Xpath 16384 " X: 16384 - throw "brrr" - endtry - Xpath 32768 " X: 0 -endif - -Xpath 65536 " X: 65536 -if !MESSAGES('E605', "Exception not caught") - Xpath 131072 " X: 0 -endif - -if ExtraVim(msgfile) - try - Xpath 262144 " X: 262144 - "INTERRUPT - endtry - Xpath 524288 " X: 0 -endif - -Xpath 1048576 " X: 1048576 -if !MESSAGES('INT', "Interrupted") - Xpath 2097152 " X: 0 -endif - -if ExtraVim(msgfile) - try - Xpath 4194304 " X: 4194304 - let x = novar " error E121/E15; exception: E121 - catch /E15:/ " should not catch - Xpath 8388608 " X: 0 - endtry - Xpath 16777216 " X: 0 -endif - -Xpath 33554432 " X: 33554432 -if !MESSAGES('E121', "Undefined variable", 'E15', "Invalid expression") - Xpath 67108864 " X: 0 -endif - -if ExtraVim(msgfile) - try - Xpath 134217728 " X: 134217728 -" unlet novar # " error E108/E488; exception: E488 - catch /E108:/ " should not catch - Xpath 268435456 " X: 0 - endtry - Xpath 536870912 " X: 0 -endif - -Xpath 1073741824 " X: 1073741824 -if !MESSAGES('E108', "No such variable", 'E488', "Trailing characters") - " The Xpath command does not accept 2^31 (negative); add explicitly: - let Xpath = Xpath + 2147483648 " X: 0 -endif - -call delete(msgfile) -unlet msgfile - -Xcheck 1247112011 - -" Leave MESSAGES() for the next tests. - - -"------------------------------------------------------------------------------- -" Test 53: Nesting errors: :endif/:else/:elseif {{{1 -" -" For nesting errors of :if conditionals the correct error messages -" should be given. -" -" This test reuses the function MESSAGES() from the previous test. -" This functions checks the messages in g:msgfile. -"------------------------------------------------------------------------------- - -XpathINIT - -let msgfile = tempname() - -if ExtraVim(msgfile) -" endif -endif -if MESSAGES('E580', ":endif without :if") - Xpath 1 " X: 1 -endif - -if ExtraVim(msgfile) -" while 1 -" endif -" endwhile -endif -if MESSAGES('E580', ":endif without :if") - Xpath 2 " X: 2 -endif - -if ExtraVim(msgfile) -" try -" finally -" endif -" endtry -endif -if MESSAGES('E580', ":endif without :if") - Xpath 4 " X: 4 -endif - -if ExtraVim(msgfile) -" try -" endif -" endtry -endif -if MESSAGES('E580', ":endif without :if") - Xpath 8 " X: 8 -endif - -if ExtraVim(msgfile) -" try -" throw "a" -" catch /a/ -" endif -" endtry -endif -if MESSAGES('E580', ":endif without :if") - Xpath 16 " X: 16 -endif - -if ExtraVim(msgfile) -" else -endif -if MESSAGES('E581', ":else without :if") - Xpath 32 " X: 32 -endif - -if ExtraVim(msgfile) -" while 1 -" else -" endwhile -endif -if MESSAGES('E581', ":else without :if") - Xpath 64 " X: 64 -endif - -if ExtraVim(msgfile) -" try -" finally -" else -" endtry -endif -if MESSAGES('E581', ":else without :if") - Xpath 128 " X: 128 -endif - -if ExtraVim(msgfile) -" try -" else -" endtry -endif -if MESSAGES('E581', ":else without :if") - Xpath 256 " X: 256 -endif - -if ExtraVim(msgfile) -" try -" throw "a" -" catch /a/ -" else -" endtry -endif -if MESSAGES('E581', ":else without :if") - Xpath 512 " X: 512 -endif - -if ExtraVim(msgfile) -" elseif -endif -if MESSAGES('E582', ":elseif without :if") - Xpath 1024 " X: 1024 -endif - -if ExtraVim(msgfile) -" while 1 -" elseif -" endwhile -endif -if MESSAGES('E582', ":elseif without :if") - Xpath 2048 " X: 2048 -endif - -if ExtraVim(msgfile) -" try -" finally -" elseif -" endtry -endif -if MESSAGES('E582', ":elseif without :if") - Xpath 4096 " X: 4096 -endif - -if ExtraVim(msgfile) -" try -" elseif -" endtry -endif -if MESSAGES('E582', ":elseif without :if") - Xpath 8192 " X: 8192 -endif - -if ExtraVim(msgfile) -" try -" throw "a" -" catch /a/ -" elseif -" endtry -endif -if MESSAGES('E582', ":elseif without :if") - Xpath 16384 " X: 16384 -endif - -if ExtraVim(msgfile) -" if 1 -" else -" else -" endif -endif -if MESSAGES('E583', "multiple :else") - Xpath 32768 " X: 32768 -endif - -if ExtraVim(msgfile) -" if 1 -" else -" elseif 1 -" endif -endif -if MESSAGES('E584', ":elseif after :else") - Xpath 65536 " X: 65536 -endif - -call delete(msgfile) -unlet msgfile - -Xcheck 131071 - -" Leave MESSAGES() for the next test. - - -"------------------------------------------------------------------------------- -" Test 54: Nesting errors: :while/:endwhile {{{1 -" -" For nesting errors of :while conditionals the correct error messages -" should be given. -" -" This test reuses the function MESSAGES() from the previous test. -" This functions checks the messages in g:msgfile. -"------------------------------------------------------------------------------- - -XpathINIT - -let msgfile = tempname() - -if ExtraVim(msgfile) -" endwhile -endif -if MESSAGES('E588', ":endwhile without :while") - Xpath 1 " X: 1 -endif - -if ExtraVim(msgfile) -" if 1 -" endwhile -" endif -endif -if MESSAGES('E588', ":endwhile without :while") - Xpath 2 " X: 2 -endif - -if ExtraVim(msgfile) -" while 1 -" if 1 -" endwhile -endif -if MESSAGES('E171', "Missing :endif") - Xpath 4 " X: 4 -endif - -if ExtraVim(msgfile) -" try -" finally -" endwhile -" endtry -endif -if MESSAGES('E588', ":endwhile without :while") - Xpath 8 " X: 8 -endif - -if ExtraVim(msgfile) -" while 1 -" try -" finally -" endwhile -endif -if MESSAGES('E600', "Missing :endtry") - Xpath 16 " X: 16 -endif - -if ExtraVim(msgfile) -" while 1 -" if 1 -" try -" finally -" endwhile -endif -if MESSAGES('E600', "Missing :endtry") - Xpath 32 " X: 32 -endif - -if ExtraVim(msgfile) -" while 1 -" try -" finally -" if 1 -" endwhile -endif -if MESSAGES('E171', "Missing :endif") - Xpath 64 " X: 64 -endif - -if ExtraVim(msgfile) -" try -" endwhile -" endtry -endif -if MESSAGES('E588', ":endwhile without :while") - Xpath 128 " X: 128 -endif - -if ExtraVim(msgfile) -" while 1 -" try -" endwhile -" endtry -" endwhile -endif -if MESSAGES('E588', ":endwhile without :while") - Xpath 256 " X: 256 -endif - -if ExtraVim(msgfile) -" try -" throw "a" -" catch /a/ -" endwhile -" endtry -endif -if MESSAGES('E588', ":endwhile without :while") - Xpath 512 " X: 512 -endif - -if ExtraVim(msgfile) -" while 1 -" try -" throw "a" -" catch /a/ -" endwhile -" endtry -" endwhile -endif -if MESSAGES('E588', ":endwhile without :while") - Xpath 1024 " X: 1024 -endif - - -call delete(msgfile) -unlet msgfile - -Xcheck 2047 - -" Leave MESSAGES() for the next test. - - -"------------------------------------------------------------------------------- -" Test 55: Nesting errors: :continue/:break {{{1 -" -" For nesting errors of :continue and :break commands the correct -" error messages should be given. -" -" This test reuses the function MESSAGES() from the previous test. -" This functions checks the messages in g:msgfile. -"------------------------------------------------------------------------------- - -XpathINIT - -let msgfile = tempname() - -if ExtraVim(msgfile) -" continue -endif -if MESSAGES('E586', ":continue without :while") - Xpath 1 " X: 1 -endif - -if ExtraVim(msgfile) -" if 1 -" continue -" endif -endif -if MESSAGES('E586', ":continue without :while") - Xpath 2 " X: 2 -endif - -if ExtraVim(msgfile) -" try -" finally -" continue -" endtry -endif -if MESSAGES('E586', ":continue without :while") - Xpath 4 " X: 4 -endif - -if ExtraVim(msgfile) -" try -" continue -" endtry -endif -if MESSAGES('E586', ":continue without :while") - Xpath 8 " X: 8 -endif - -if ExtraVim(msgfile) -" try -" throw "a" -" catch /a/ -" continue -" endtry -endif -if MESSAGES('E586', ":continue without :while") - Xpath 16 " X: 16 -endif - -if ExtraVim(msgfile) -" break -endif -if MESSAGES('E587', ":break without :while") - Xpath 32 " X: 32 -endif - -if ExtraVim(msgfile) -" if 1 -" break -" endif -endif -if MESSAGES('E587', ":break without :while") - Xpath 64 " X: 64 -endif - -if ExtraVim(msgfile) -" try -" finally -" break -" endtry -endif -if MESSAGES('E587', ":break without :while") - Xpath 128 " X: 128 -endif - -if ExtraVim(msgfile) -" try -" break -" endtry -endif -if MESSAGES('E587', ":break without :while") - Xpath 256 " X: 256 -endif - -if ExtraVim(msgfile) -" try -" throw "a" -" catch /a/ -" break -" endtry -endif -if MESSAGES('E587', ":break without :while") - Xpath 512 " X: 512 -endif - -call delete(msgfile) -unlet msgfile - -Xcheck 1023 - -" Leave MESSAGES() for the next test. - - -"------------------------------------------------------------------------------- -" Test 56: Nesting errors: :endtry {{{1 -" -" For nesting errors of :try conditionals the correct error messages -" should be given. -" -" This test reuses the function MESSAGES() from the previous test. -" This functions checks the messages in g:msgfile. -"------------------------------------------------------------------------------- - -XpathINIT - -let msgfile = tempname() - -if ExtraVim(msgfile) -" endtry -endif -if MESSAGES('E602', ":endtry without :try") - Xpath 1 " X: 1 -endif - -if ExtraVim(msgfile) -" if 1 -" endtry -" endif -endif -if MESSAGES('E602', ":endtry without :try") - Xpath 2 " X: 2 -endif - -if ExtraVim(msgfile) -" while 1 -" endtry -" endwhile -endif -if MESSAGES('E602', ":endtry without :try") - Xpath 4 " X: 4 -endif - -if ExtraVim(msgfile) -" try -" if 1 -" endtry -endif -if MESSAGES('E171', "Missing :endif") - Xpath 8 " X: 8 -endif - -if ExtraVim(msgfile) -" try -" while 1 -" endtry -endif -if MESSAGES('E170', "Missing :endwhile") - Xpath 16 " X: 16 -endif - -if ExtraVim(msgfile) -" try -" finally -" if 1 -" endtry -endif -if MESSAGES('E171', "Missing :endif") - Xpath 32 " X: 32 -endif - -if ExtraVim(msgfile) -" try -" finally -" while 1 -" endtry -endif -if MESSAGES('E170', "Missing :endwhile") - Xpath 64 " X: 64 -endif - -if ExtraVim(msgfile) - try - Xpath 4194304 " X: 4194304 - let x = novar " error E121; exception: E121 - catch /E15:/ " should not catch - Xpath 8388608 " X: 0 - endtry - Xpath 16777216 " X: 0 -endif - -Xpath 33554432 " X: 33554432 -if !MESSAGES('E121', "Undefined variable") - Xpath 67108864 " X: 0 -endif - -if ExtraVim(msgfile) -" try -" throw "a" -" catch /a/ -" while 1 -" endtry -endif -if MESSAGES('E170', "Missing :endwhile") - Xpath 256 " X: 256 -endif - -call delete(msgfile) -unlet msgfile - -delfunction MESSAGES - -Xcheck 511 - - -"------------------------------------------------------------------------------- -" Test 57: v:exception and v:throwpoint for user exceptions {{{1 -" -" v:exception evaluates to the value of the exception that was caught -" most recently and is not finished. (A caught exception is finished -" when the next ":catch", ":finally", or ":endtry" is reached.) -" v:throwpoint evaluates to the script/function name and line number -" where that exception has been thrown. -"------------------------------------------------------------------------------- - -XpathINIT - -function! FuncException() - let g:exception = v:exception -endfunction - -function! FuncThrowpoint() - let g:throwpoint = v:throwpoint -endfunction - -let scriptException = MakeScript("FuncException") -let scriptThrowPoint = MakeScript("FuncThrowpoint") - -command! CmdException let g:exception = v:exception -command! CmdThrowpoint let g:throwpoint = v:throwpoint - -XloopINIT! 1 2 - -function! CHECK(n, exception, throwname, throwline) - XloopNEXT - let error = 0 - if v:exception != a:exception - Xout a:n.": v:exception is" v:exception "instead of" a:exception - let error = 1 - endif - if v:throwpoint !~ a:throwname - let name = escape(a:throwname, '\') - Xout a:n.": v:throwpoint (".v:throwpoint.") does not match" name - let error = 1 - endif - if v:throwpoint !~ a:throwline - let line = escape(a:throwline, '\') - Xout a:n.": v:throwpoint (".v:throwpoint.") does not match" line - let error = 1 - endif - if error - Xloop 1 " X: 0 - endif -endfunction - -function! T(arg, line) - if a:line == 2 - throw a:arg " in line 2 - elseif a:line == 4 - throw a:arg " in line 4 - elseif a:line == 6 - throw a:arg " in line 6 - elseif a:line == 8 - throw a:arg " in line 8 - endif -endfunction - -function! G(arg, line) - call T(a:arg, a:line) -endfunction - -function! F(arg, line) - call G(a:arg, a:line) -endfunction - -let scriptT = MakeScript("T") -let scriptG = MakeScript("G", scriptT) -let scriptF = MakeScript("F", scriptG) - -try - Xpath 32768 " X: 32768 - call F("oops", 2) -catch /.*/ - Xpath 65536 " X: 65536 - let exception = v:exception - let throwpoint = v:throwpoint - call CHECK(1, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>') - exec "let exception = v:exception" - exec "let throwpoint = v:throwpoint" - call CHECK(2, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>') - CmdException - CmdThrowpoint - call CHECK(3, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>') - call FuncException() - call FuncThrowpoint() - call CHECK(4, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>') - exec "source" scriptException - exec "source" scriptThrowPoint - call CHECK(5, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>') - try - Xpath 131072 " X: 131072 - call G("arrgh", 4) - catch /.*/ - Xpath 262144 " X: 262144 - let exception = v:exception - let throwpoint = v:throwpoint - call CHECK(6, "arrgh", '\<G\[1]\.\.T\>', '\<4\>') - try - Xpath 524288 " X: 524288 - let g:arg = "autsch" - let g:line = 6 - exec "source" scriptF - catch /.*/ - Xpath 1048576 " X: 1048576 - let exception = v:exception - let throwpoint = v:throwpoint - " Symbolic links in tempname()s are not resolved, whereas resolving - " is done for v:throwpoint. Resolve the temporary file name for - " scriptT, so that it can be matched against v:throwpoint. - call CHECK(7, "autsch", resolve(scriptT), '\<6\>') - finally - Xpath 2097152 " X: 2097152 - let exception = v:exception - let throwpoint = v:throwpoint - call CHECK(8, "arrgh", '\<G\[1]\.\.T\>', '\<4\>') - try - Xpath 4194304 " X: 4194304 - let g:arg = "brrrr" - let g:line = 8 - exec "source" scriptG - catch /.*/ - Xpath 8388608 " X: 8388608 - let exception = v:exception - let throwpoint = v:throwpoint - " Resolve scriptT for matching it against v:throwpoint. - call CHECK(9, "brrrr", resolve(scriptT), '\<8\>') - finally - Xpath 16777216 " X: 16777216 - let exception = v:exception - let throwpoint = v:throwpoint - call CHECK(10, "arrgh", '\<G\[1]\.\.T\>', '\<4\>') - endtry - Xpath 33554432 " X: 33554432 - let exception = v:exception - let throwpoint = v:throwpoint - call CHECK(11, "arrgh", '\<G\[1]\.\.T\>', '\<4\>') - endtry - Xpath 67108864 " X: 67108864 - let exception = v:exception - let throwpoint = v:throwpoint - call CHECK(12, "arrgh", '\<G\[1]\.\.T\>', '\<4\>') - finally - Xpath 134217728 " X: 134217728 - let exception = v:exception - let throwpoint = v:throwpoint - call CHECK(13, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>') - endtry - Xpath 268435456 " X: 268435456 - let exception = v:exception - let throwpoint = v:throwpoint - call CHECK(14, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>') -finally - Xpath 536870912 " X: 536870912 - let exception = v:exception - let throwpoint = v:throwpoint - call CHECK(15, "", '^$', '^$') -endtry - -Xpath 1073741824 " X: 1073741824 - -unlet exception throwpoint -delfunction FuncException -delfunction FuncThrowpoint -call delete(scriptException) -call delete(scriptThrowPoint) -unlet scriptException scriptThrowPoint -delcommand CmdException -delcommand CmdThrowpoint -delfunction T -delfunction G -delfunction F -call delete(scriptT) -call delete(scriptG) -call delete(scriptF) -unlet scriptT scriptG scriptF - -Xcheck 2147450880 - - -"------------------------------------------------------------------------------- -" -" Test 58: v:exception and v:throwpoint for error/interrupt exceptions {{{1 -" -" v:exception and v:throwpoint work also for error and interrupt -" exceptions. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - - function! T(line) - if a:line == 2 - delfunction T " error (function in use) in line 2 - elseif a:line == 4 - let dummy = 0 " INTERRUPT1 - interrupt in line 4 - endif - endfunction - - while 1 - try - Xpath 1 " X: 1 - let caught = 0 - call T(2) - catch /.*/ - let caught = 1 - if v:exception !~ 'Vim(delfunction):' - Xpath 2 " X: 0 - endif - if v:throwpoint !~ '\<T\>' - Xpath 4 " X: 0 - endif - if v:throwpoint !~ '\<2\>' - Xpath 8 " X: 0 - endif - finally - Xpath 16 " X: 16 - if caught || $VIMNOERRTHROW - Xpath 32 " X: 32 - endif - if v:exception != "" - Xpath 64 " X: 0 - endif - if v:throwpoint != "" - Xpath 128 " X: 0 - endif - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - Xpath 256 " X: 256 - if v:exception != "" - Xpath 512 " X: 0 - endif - if v:throwpoint != "" - Xpath 1024 " X: 0 - endif - - while 1 - try - Xpath 2048 " X: 2048 - let caught = 0 - call T(4) - catch /.*/ - let caught = 1 - if v:exception != 'Vim:Interrupt' - Xpath 4096 " X: 0 - endif - if v:throwpoint !~ '\<T\>' - Xpath 8192 " X: 0 - endif - if v:throwpoint !~ '\<4\>' - Xpath 16384 " X: 0 - endif - finally - Xpath 32768 " X: 32768 - if caught || $VIMNOINTTHROW - Xpath 65536 " X: 65536 - endif - if v:exception != "" - Xpath 131072 " X: 0 - endif - if v:throwpoint != "" - Xpath 262144 " X: 0 - endif - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - Xpath 524288 " X: 524288 - if v:exception != "" - Xpath 1048576 " X: 0 - endif - if v:throwpoint != "" - Xpath 2097152 " X: 0 - endif - -endif - -Xcheck 624945 - - -"------------------------------------------------------------------------------- -" -" Test 59: v:exception and v:throwpoint when discarding exceptions {{{1 -" -" When a :catch clause is left by a ":break" etc or an error or -" interrupt exception, v:exception and v:throwpoint are reset. They -" are not affected by an exception that is discarded before being -" caught. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - - XloopINIT! 1 2 - - let sfile = expand("<sfile>") - - function! LineNumber() - return substitute(substitute(v:throwpoint, g:sfile, '', ""), - \ '\D*\(\d*\).*', '\1', "") - endfunction - - command! -nargs=1 SetLineNumber - \ try | throw "line" | catch /.*/ | let <args> = LineNumber() | endtry - - " Check v:exception/v:throwpoint against second/fourth parameter if - " specified, check for being empty else. - function! CHECK(n, ...) - XloopNEXT - let exception = a:0 != 0 ? a:1 : "" " second parameter (optional) - let emsg = a:0 != 0 ? a:2 : "" " third parameter (optional) - let line = a:0 != 0 ? a:3 : 0 " fourth parameter (optional) - let error = 0 - if emsg != "" - " exception is the error number, emsg the English error message text - if exception !~ '^E\d\+$' - Xout "TODO: Add message number for:" emsg - elseif v:lang == "C" || v:lang =~ '^[Ee]n' - if exception == "E492" && emsg == "Not an editor command" - let exception = '^Vim:' . exception . ': ' . emsg - else - let exception = '^Vim(\a\+):' . exception . ': ' . emsg - endif - else - if exception == "E492" - let exception = '^Vim:' . exception - else - let exception = '^Vim(\a\+):' . exception - endif - endif - endif - if exception == "" && v:exception != "" - Xout a:n.": v:exception is set:" v:exception - let error = 1 - elseif exception != "" && v:exception !~ exception - Xout a:n.": v:exception (".v:exception.") does not match" exception - let error = 1 - endif - if line == 0 && v:throwpoint != "" - Xout a:n.": v:throwpoint is set:" v:throwpoint - let error = 1 - elseif line != 0 && v:throwpoint !~ '\<' . line . '\>' - Xout a:n.": v:throwpoint (".v:throwpoint.") does not match" line - let error = 1 - endif - if !error - Xloop 1 " X: 2097151 - endif - endfunction - - while 1 - try - throw "x1" - catch /.*/ - break - endtry - endwhile - call CHECK(1) - - while 1 - try - throw "x2" - catch /.*/ - break - finally - call CHECK(2) - endtry - break - endwhile - call CHECK(3) - - while 1 - try - let errcaught = 0 - try - try - throw "x3" - catch /.*/ - SetLineNumber line_before_error - asdf - endtry - catch /.*/ - let errcaught = 1 - call CHECK(4, 'E492', "Not an editor command", - \ line_before_error + 1) - endtry - finally - if !errcaught && $VIMNOERRTHROW - call CHECK(4) - endif - break " discard error for $VIMNOERRTHROW - endtry - endwhile - call CHECK(5) - - Xpath 2097152 " X: 2097152 - - while 1 - try - let intcaught = 0 - try - try - throw "x4" - catch /.*/ - SetLineNumber two_lines_before_interrupt - "INTERRUPT - let dummy = 0 - endtry - catch /.*/ - let intcaught = 1 - call CHECK(6, "Vim:Interrupt", '', - \ two_lines_before_interrupt + 2) - endtry - finally - if !intcaught && $VIMNOINTTHROW - call CHECK(6) - endif - break " discard interrupt for $VIMNOINTTHROW - endtry - endwhile - call CHECK(7) - - Xpath 4194304 " X: 4194304 - - while 1 - try - let errcaught = 0 - try - try -" if 1 - SetLineNumber line_before_throw - throw "x5" - " missing endif - catch /.*/ - Xpath 8388608 " X: 0 - endtry - catch /.*/ - let errcaught = 1 - call CHECK(8, 'E171', "Missing :endif", line_before_throw + 3) - endtry - finally - if !errcaught && $VIMNOERRTHROW - call CHECK(8) - endif - break " discard error for $VIMNOERRTHROW - endtry - endwhile - call CHECK(9) - - Xpath 16777216 " X: 16777216 - - try - while 1 - try - throw "x6" - finally - break - endtry - break - endwhile - catch /.*/ - Xpath 33554432 " X: 0 - endtry - call CHECK(10) - - try - while 1 - try - throw "x7" - finally - break - endtry - break - endwhile - catch /.*/ - Xpath 67108864 " X: 0 - finally - call CHECK(11) - endtry - call CHECK(12) - - while 1 - try - let errcaught = 0 - try - try - throw "x8" - finally - SetLineNumber line_before_error - asdf - endtry - catch /.*/ - let errcaught = 1 - call CHECK(13, 'E492', "Not an editor command", - \ line_before_error + 1) - endtry - finally - if !errcaught && $VIMNOERRTHROW - call CHECK(13) - endif - break " discard error for $VIMNOERRTHROW - endtry - endwhile - call CHECK(14) - - Xpath 134217728 " X: 134217728 - - while 1 - try - let intcaught = 0 - try - try - throw "x9" - finally - SetLineNumber two_lines_before_interrupt - "INTERRUPT - endtry - catch /.*/ - let intcaught = 1 - call CHECK(15, "Vim:Interrupt", '', - \ two_lines_before_interrupt + 2) - endtry - finally - if !intcaught && $VIMNOINTTHROW - call CHECK(15) - endif - break " discard interrupt for $VIMNOINTTHROW - endtry - endwhile - call CHECK(16) - - Xpath 268435456 " X: 268435456 - - while 1 - try - let errcaught = 0 - try - try -" if 1 - SetLineNumber line_before_throw - throw "x10" - " missing endif - finally - call CHECK(17) - endtry - catch /.*/ - let errcaught = 1 - call CHECK(18, 'E171', "Missing :endif", line_before_throw + 3) - endtry - finally - if !errcaught && $VIMNOERRTHROW - call CHECK(18) - endif - break " discard error for $VIMNOERRTHROW - endtry - endwhile - call CHECK(19) - - Xpath 536870912 " X: 536870912 - - while 1 - try - let errcaught = 0 - try - try -" if 1 - SetLineNumber line_before_throw - throw "x11" - " missing endif - endtry - catch /.*/ - let errcaught = 1 - call CHECK(20, 'E171', "Missing :endif", line_before_throw + 3) - endtry - finally - if !errcaught && $VIMNOERRTHROW - call CHECK(20) - endif - break " discard error for $VIMNOERRTHROW - endtry - endwhile - call CHECK(21) - - Xpath 1073741824 " X: 1073741824 - -endif - -Xcheck 2038431743 - - -"------------------------------------------------------------------------------- -" -" Test 60: (Re)throwing v:exception; :echoerr. {{{1 -" -" A user exception can be rethrown after catching by throwing -" v:exception. An error or interrupt exception cannot be rethrown -" because Vim exceptions cannot be faked. A Vim exception using the -" value of v:exception can, however, be triggered by the :echoerr -" command. -"------------------------------------------------------------------------------- - -XpathINIT - -try - try - Xpath 1 " X: 1 - throw "oops" - catch /oops/ - Xpath 2 " X: 2 - throw v:exception " rethrow user exception - catch /.*/ - Xpath 4 " X: 0 - endtry -catch /^oops$/ " catches rethrown user exception - Xpath 8 " X: 8 -catch /.*/ - Xpath 16 " X: 0 -endtry - -function! F() - try - let caught = 0 - try - Xpath 32 " X: 32 - write /n/o/n/w/r/i/t/a/b/l/e/_/f/i/l/e - Xpath 64 " X: 0 - Xout "did_emsg was reset before executing " . - \ "BufWritePost autocommands." - catch /^Vim(write):/ - let caught = 1 - throw v:exception " throw error: cannot fake Vim exception - catch /.*/ - Xpath 128 " X: 0 - finally - Xpath 256 " X: 256 - if !caught && !$VIMNOERRTHROW - Xpath 512 " X: 0 - endif - endtry - catch /^Vim(throw):/ " catches throw error - let caught = caught + 1 - catch /.*/ - Xpath 1024 " X: 0 - finally - Xpath 2048 " X: 2048 - if caught != 2 - if !caught && !$VIMNOERRTHROW - Xpath 4096 " X: 0 - elseif caught - Xpath 8192 " X: 0 - endif - return | " discard error for $VIMNOERRTHROW - endif - endtry -endfunction - -call F() -delfunction F - -function! G() - try - let caught = 0 - try - Xpath 16384 " X: 16384 - asdf - catch /^Vim/ " catch error exception - let caught = 1 - " Trigger Vim error exception with value specified after :echoerr - let value = substitute(v:exception, '^Vim\((.*)\)\=:', '', "") - echoerr value - catch /.*/ - Xpath 32768 " X: 0 - finally - Xpath 65536 " X: 65536 - if !caught - if !$VIMNOERRTHROW - Xpath 131072 " X: 0 - else - let value = "Error" - echoerr value - endif - endif - endtry - catch /^Vim(echoerr):/ - let caught = caught + 1 - if v:exception !~ value - Xpath 262144 " X: 0 - endif - catch /.*/ - Xpath 524288 " X: 0 - finally - Xpath 1048576 " X: 1048576 - if caught != 2 - if !caught && !$VIMNOERRTHROW - Xpath 2097152 " X: 0 - elseif caught - Xpath 4194304 " X: 0 - endif - return | " discard error for $VIMNOERRTHROW - endif - endtry -endfunction - -call G() -delfunction G - -unlet! value caught - -if ExtraVim() - try - let errcaught = 0 - try - Xpath 8388608 " X: 8388608 - let intcaught = 0 - "INTERRUPT - catch /^Vim:/ " catch interrupt exception - let intcaught = 1 - " Trigger Vim error exception with value specified after :echoerr - echoerr substitute(v:exception, '^Vim\((.*)\)\=:', '', "") - catch /.*/ - Xpath 16777216 " X: 0 - finally - Xpath 33554432 " X: 33554432 - if !intcaught - if !$VIMNOINTTHROW - Xpath 67108864 " X: 0 - else - echoerr "Interrupt" - endif - endif - endtry - catch /^Vim(echoerr):/ - let errcaught = 1 - if v:exception !~ "Interrupt" - Xpath 134217728 " X: 0 - endif - finally - Xpath 268435456 " X: 268435456 - if !errcaught && !$VIMNOERRTHROW - Xpath 536870912 " X: 0 - endif - endtry -endif - -Xcheck 311511339 - - -"------------------------------------------------------------------------------- -" Test 61: Catching interrupt exceptions {{{1 -" -" When an interrupt occurs inside a :try/:endtry region, an -" interrupt exception is thrown and can be caught. Its value is -" "Vim:Interrupt". If the interrupt occurs after an error or a :throw -" but before a matching :catch is reached, all following :catches of -" that try block are ignored, but the interrupt exception can be -" caught by the next surrounding try conditional. An interrupt is -" ignored when there is a previous interrupt that has not been caught -" or causes a :finally clause to be executed. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - - while 1 - try - try - Xpath 1 " X: 1 - let caught = 0 - "INTERRUPT - Xpath 2 " X: 0 - catch /^Vim:Interrupt$/ - let caught = 1 - finally - Xpath 4 " X: 4 - if caught || $VIMNOINTTHROW - Xpath 8 " X: 8 - endif - endtry - catch /.*/ - Xpath 16 " X: 0 - Xout v:exception "in" v:throwpoint - finally - break " discard interrupt for $VIMNOINTTHROW - endtry - endwhile - - while 1 - try - try - let caught = 0 - try - Xpath 32 " X: 32 - asdf - Xpath 64 " X: 0 - catch /do_not_catch/ - Xpath 128 " X: 0 - catch /.*/ "INTERRUPT - throw interrupt if !$VIMNOERRTHROW - Xpath 256 " X: 0 - catch /.*/ - Xpath 512 " X: 0 - finally "INTERRUPT - throw interrupt if $VIMNOERRTHROW - Xpath 1024 " X: 1024 - endtry - catch /^Vim:Interrupt$/ - let caught = 1 - finally - Xpath 2048 " X: 2048 - if caught || $VIMNOINTTHROW - Xpath 4096 " X: 4096 - endif - endtry - catch /.*/ - Xpath 8192 " X: 0 - Xout v:exception "in" v:throwpoint - finally - break " discard interrupt for $VIMNOINTTHROW - endtry - endwhile - - while 1 - try - try - let caught = 0 - try - Xpath 16384 " X: 16384 - throw "x" - Xpath 32768 " X: 0 - catch /do_not_catch/ - Xpath 65536 " X: 0 - catch /x/ "INTERRUPT - Xpath 131072 " X: 0 - catch /.*/ - Xpath 262144 " X: 0 - endtry - catch /^Vim:Interrupt$/ - let caught = 1 - finally - Xpath 524288 " X: 524288 - if caught || $VIMNOINTTHROW - Xpath 1048576 " X: 1048576 - endif - endtry - catch /.*/ - Xpath 2097152 " X: 0 - Xout v:exception "in" v:throwpoint - finally - break " discard interrupt for $VIMNOINTTHROW - endtry - endwhile - - while 1 - try - let caught = 0 - try - Xpath 4194304 " X: 4194304 - "INTERRUPT - Xpath 8388608 " X: 0 - catch /do_not_catch/ "INTERRUPT - Xpath 16777216 " X: 0 - catch /^Vim:Interrupt$/ - let caught = 1 - finally - Xpath 33554432 " X: 33554432 - if caught || $VIMNOINTTHROW - Xpath 67108864 " X: 67108864 - endif - endtry - catch /.*/ - Xpath 134217728 " X: 0 - Xout v:exception "in" v:throwpoint - finally - break " discard interrupt for $VIMNOINTTHROW - endtry - endwhile - - Xpath 268435456 " X: 268435456 - -endif - -Xcheck 374889517 - - -"------------------------------------------------------------------------------- -" Test 62: Catching error exceptions {{{1 -" -" An error inside a :try/:endtry region is converted to an exception -" and can be caught. The error exception has a "Vim(cmdname):" prefix -" where cmdname is the name of the failing command, or a "Vim:" prefix -" if no command name is known. The "Vim" prefixes cannot be faked. -"------------------------------------------------------------------------------- - -XpathINIT - -function! MSG(enr, emsg) - let english = v:lang == "C" || v:lang =~ '^[Ee]n' - if a:enr == "" - Xout "TODO: Add message number for:" a:emsg - let v:errmsg = ":" . v:errmsg - endif - let match = 1 - if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg) - let match = 0 - if v:errmsg == "" - Xout "Message missing." - else - let v:errmsg = escape(v:errmsg, '"') - Xout "Unexpected message:" v:errmsg - endif - endif - return match -endfunction - -while 1 - try - try - let caught = 0 - unlet novar - catch /^Vim(unlet):/ - let caught = 1 - let v:errmsg = substitute(v:exception, '^Vim(unlet):', '', "") - finally - Xpath 1 " X: 1 - if !caught && !$VIMNOERRTHROW - Xpath 2 " X: 0 - endif - if !MSG('E108', "No such variable") - Xpath 4 " X: 0 - endif - endtry - catch /.*/ - Xpath 8 " X: 0 - Xout v:exception "in" v:throwpoint - finally - break " discard error for $VIMNOERRTHROW - endtry -endwhile - -while 1 - try - try - let caught = 0 - throw novar " error in :throw - catch /^Vim(throw):/ - let caught = 1 - let v:errmsg = substitute(v:exception, '^Vim(throw):', '', "") - finally - Xpath 16 " X: 16 - if !caught && !$VIMNOERRTHROW - Xpath 32 " X: 0 - endif - if caught ? !MSG('E121', "Undefined variable") - \ : !MSG('E15', "Invalid expression") - Xpath 64 " X: 0 - endif - endtry - catch /.*/ - Xpath 128 " X: 0 - Xout v:exception "in" v:throwpoint - finally - break " discard error for $VIMNOERRTHROW - endtry -endwhile - -while 1 - try - try - let caught = 0 - throw "Vim:faked" " error: cannot fake Vim exception - catch /^Vim(throw):/ - let caught = 1 - let v:errmsg = substitute(v:exception, '^Vim(throw):', '', "") - finally - Xpath 256 " X: 256 - if !caught && !$VIMNOERRTHROW - Xpath 512 " X: 0 - endif - if !MSG('E608', "Cannot :throw exceptions with 'Vim' prefix") - Xpath 1024 " X: 0 - endif - endtry - catch /.*/ - Xpath 2048 " X: 0 - Xout v:exception "in" v:throwpoint - finally - break " discard error for $VIMNOERRTHROW - endtry -endwhile - -function! F() - while 1 - " Missing :endwhile -endfunction - -while 1 - try - try - let caught = 0 - call F() - catch /^Vim(endfunction):/ - let caught = 1 - let v:errmsg = substitute(v:exception, '^Vim(endfunction):', '', "") - finally - Xpath 4096 " X: 4096 - if !caught && !$VIMNOERRTHROW - Xpath 8192 " X: 0 - endif - if !MSG('E170', "Missing :endwhile") - Xpath 16384 " X: 0 - endif - endtry - catch /.*/ - Xpath 32768 " X: 0 - Xout v:exception "in" v:throwpoint - finally - break " discard error for $VIMNOERRTHROW - endtry -endwhile - -while 1 - try - try - let caught = 0 - ExecAsScript F - catch /^Vim:/ - let caught = 1 - let v:errmsg = substitute(v:exception, '^Vim:', '', "") - finally - Xpath 65536 " X: 65536 - if !caught && !$VIMNOERRTHROW - Xpath 131072 " X: 0 - endif - if !MSG('E170', "Missing :endwhile") - Xpath 262144 " X: 0 - endif - endtry - catch /.*/ - Xpath 524288 " X: 0 - Xout v:exception "in" v:throwpoint - finally - break " discard error for $VIMNOERRTHROW - endtry -endwhile - -function! G() - call G() -endfunction - -while 1 - try - let mfd_save = &mfd - set mfd=3 - try - let caught = 0 - call G() - catch /^Vim(call):/ - let caught = 1 - let v:errmsg = substitute(v:exception, '^Vim(call):', '', "") - finally - Xpath 1048576 " X: 1048576 - if !caught && !$VIMNOERRTHROW - Xpath 2097152 " X: 0 - endif - if !MSG('E132', "Function call depth is higher than 'maxfuncdepth'") - Xpath 4194304 " X: 0 - endif - endtry - catch /.*/ - Xpath 8388608 " X: 0 - Xout v:exception "in" v:throwpoint - finally - let &mfd = mfd_save - break " discard error for $VIMNOERRTHROW - endtry -endwhile - -function! H() - return H() -endfunction - -while 1 - try - let mfd_save = &mfd - set mfd=3 - try - let caught = 0 - call H() - catch /^Vim(return):/ - let caught = 1 - let v:errmsg = substitute(v:exception, '^Vim(return):', '', "") - finally - Xpath 16777216 " X: 16777216 - if !caught && !$VIMNOERRTHROW - Xpath 33554432 " X: 0 - endif - if !MSG('E132', "Function call depth is higher than 'maxfuncdepth'") - Xpath 67108864 " X: 0 - endif - endtry - catch /.*/ - Xpath 134217728 " X: 0 - Xout v:exception "in" v:throwpoint - finally - let &mfd = mfd_save - break " discard error for $VIMNOERRTHROW - endtry -endwhile - -unlet! caught mfd_save -delfunction F -delfunction G -delfunction H -Xpath 268435456 " X: 268435456 - -Xcheck 286331153 - -" Leave MSG() for the next test. - - -"------------------------------------------------------------------------------- -" Test 63: Suppressing error exceptions by :silent!. {{{1 -" -" A :silent! command inside a :try/:endtry region suppresses the -" conversion of errors to an exception and the immediate abortion on -" error. When the commands executed by the :silent! themselves open -" a new :try/:endtry region, conversion of errors to exception and -" immediate abortion is switched on again - until the next :silent! -" etc. The :silent! has the effect of setting v:errmsg to the error -" message text (without displaying it) and continuing with the next -" script line. -" -" When a command triggering autocommands is executed by :silent! -" inside a :try/:endtry, the autocommand execution is not suppressed -" on error. -" -" This test reuses the function MSG() from the previous test. -"------------------------------------------------------------------------------- - -XpathINIT - -XloopINIT! 1 4 - -let taken = "" - -function! S(n) abort - XloopNEXT - let g:taken = g:taken . "E" . a:n - let v:errmsg = "" - exec "asdf" . a:n - - " Check that ":silent!" continues: - Xloop 1 - - " Check that ":silent!" sets "v:errmsg": - if MSG('E492', "Not an editor command") - Xloop 2 - endif -endfunction - -function! Foo() - while 1 - try - try - let caught = 0 - " This is not silent: - call S(3) " X: 0 * 16 - catch /^Vim:/ - let caught = 1 - let errmsg3 = substitute(v:exception, '^Vim:', '', "") - silent! call S(4) " X: 3 * 64 - finally - if !caught - let errmsg3 = v:errmsg - " Do call S(4) here if not executed in :catch. - silent! call S(4) - endif - Xpath 1048576 " X: 1048576 - if !caught && !$VIMNOERRTHROW - Xpath 2097152 " X: 0 - endif - let v:errmsg = errmsg3 - if !MSG('E492', "Not an editor command") - Xpath 4194304 " X: 0 - endif - silent! call S(5) " X: 3 * 256 - " Break out of try conditionals that cover ":silent!". This also - " discards the aborting error when $VIMNOERRTHROW is non-zero. - break - endtry - catch /.*/ - Xpath 8388608 " X: 0 - Xout v:exception "in" v:throwpoint - endtry - endwhile - " This is a double ":silent!" (see caller). - silent! call S(6) " X: 3 * 1024 -endfunction - -function! Bar() - try - silent! call S(2) " X: 3 * 4 - " X: 3 * 4096 - silent! execute "call Foo() | call S(7)" - silent! call S(8) " X: 3 * 16384 - endtry " normal end of try cond that covers ":silent!" - " This has a ":silent!" from the caller: - call S(9) " X: 3 * 65536 -endfunction - -silent! call S(1) " X: 3 * 1 -silent! call Bar() -silent! call S(10) " X: 3 * 262144 - -let expected = "E1E2E3E4E5E6E7E8E9E10" -if taken != expected - Xpath 16777216 " X: 0 - Xout "'taken' is" taken "instead of" expected -endif - -augroup TMP - autocmd BufWritePost * Xpath 33554432 " X: 33554432 -augroup END - -Xpath 67108864 " X: 67108864 -write /i/m/p/o/s/s/i/b/l/e -Xpath 134217728 " X: 134217728 - -autocmd! TMP -unlet! caught errmsg3 taken expected -delfunction S -delfunction Foo -delfunction Bar -delfunction MSG - -Xcheck 236978127 - - -"------------------------------------------------------------------------------- -" Test 64: Error exceptions after error, interrupt or :throw {{{1 -" -" When an error occurs after an interrupt or a :throw but before -" a matching :catch is reached, all following :catches of that try -" block are ignored, but the error exception can be caught by the next -" surrounding try conditional. Any previous error exception is -" discarded. An error is ignored when there is a previous error that -" has not been caught. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - - while 1 - try - try - Xpath 1 " X: 1 - let caught = 0 - while 1 -" if 1 - " Missing :endif - endwhile " throw error exception - catch /^Vim(/ - let caught = 1 - finally - Xpath 2 " X: 2 - if caught || $VIMNOERRTHROW - Xpath 4 " X: 4 - endif - endtry - catch /.*/ - Xpath 8 " X: 0 - Xout v:exception "in" v:throwpoint - finally - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - while 1 - try - try - Xpath 16 " X: 16 - let caught = 0 - try -" if 1 - " Missing :endif - catch /.*/ " throw error exception - Xpath 32 " X: 0 - catch /.*/ - Xpath 64 " X: 0 - endtry - catch /^Vim(/ - let caught = 1 - finally - Xpath 128 " X: 128 - if caught || $VIMNOERRTHROW - Xpath 256 " X: 256 - endif - endtry - catch /.*/ - Xpath 512 " X: 0 - Xout v:exception "in" v:throwpoint - finally - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - while 1 - try - try - let caught = 0 - try - Xpath 1024 " X: 1024 - "INTERRUPT - catch /do_not_catch/ - Xpath 2048 " X: 0 -" if 1 - " Missing :endif - catch /.*/ " throw error exception - Xpath 4096 " X: 0 - catch /.*/ - Xpath 8192 " X: 0 - endtry - catch /^Vim(/ - let caught = 1 - finally - Xpath 16384 " X: 16384 - if caught || $VIMNOERRTHROW - Xpath 32768 " X: 32768 - endif - endtry - catch /.*/ - Xpath 65536 " X: 0 - Xout v:exception "in" v:throwpoint - finally - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - while 1 - try - try - let caught = 0 - try - Xpath 131072 " X: 131072 - throw "x" - catch /do_not_catch/ - Xpath 262144 " X: 0 -" if 1 - " Missing :endif - catch /x/ " throw error exception - Xpath 524288 " X: 0 - catch /.*/ - Xpath 1048576 " X: 0 - endtry - catch /^Vim(/ - let caught = 1 - finally - Xpath 2097152 " X: 2097152 - if caught || $VIMNOERRTHROW - Xpath 4194304 " X: 4194304 - endif - endtry - catch /.*/ - Xpath 8388608 " X: 0 - Xout v:exception "in" v:throwpoint - finally - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - while 1 - try - try - let caught = 0 - Xpath 16777216 " X: 16777216 -" endif " :endif without :if; throw error exception -" if 1 - " Missing :endif - catch /do_not_catch/ " ignore new error - Xpath 33554432 " X: 0 - catch /^Vim(endif):/ - let caught = 1 - catch /^Vim(/ - Xpath 67108864 " X: 0 - finally - Xpath 134217728 " X: 134217728 - if caught || $VIMNOERRTHROW - Xpath 268435456 " X: 268435456 - endif - endtry - catch /.*/ - Xpath 536870912 " X: 0 - Xout v:exception "in" v:throwpoint - finally - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - Xpath 1073741824 " X: 1073741824 - -endif - -Xcheck 1499645335 - - -"------------------------------------------------------------------------------- -" Test 65: Errors in the /pattern/ argument of a :catch {{{1 -" -" On an error in the /pattern/ argument of a :catch, the :catch does -" not match. Any following :catches of the same :try/:endtry don't -" match either. Finally clauses are executed. -"------------------------------------------------------------------------------- - -XpathINIT - -function! MSG(enr, emsg) - let english = v:lang == "C" || v:lang =~ '^[Ee]n' - if a:enr == "" - Xout "TODO: Add message number for:" a:emsg - let v:errmsg = ":" . v:errmsg - endif - let match = 1 - if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg) - let match = 0 - if v:errmsg == "" - Xout "Message missing." - else - let v:errmsg = escape(v:errmsg, '"') - Xout "Unexpected message:" v:errmsg - endif - endif - return match -endfunction - -try - try - Xpath 1 " X: 1 - throw "oops" - catch /^oops$/ - Xpath 2 " X: 2 - catch /\)/ " not checked; exception has already been caught - Xpath 4 " X: 0 - endtry - Xpath 8 " X: 8 -catch /.*/ - Xpath 16 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -function! F() - try - let caught = 0 - try - try - Xpath 32 " X: 32 - throw "ab" - catch /abc/ " does not catch - Xpath 64 " X: 0 - catch /\)/ " error; discards exception - Xpath 128 " X: 0 - catch /.*/ " not checked - Xpath 256 " X: 0 - finally - Xpath 512 " X: 512 - endtry - Xpath 1024 " X: 0 - catch /^ab$/ " checked, but original exception is discarded - Xpath 2048 " X: 0 - catch /^Vim(catch):/ - let caught = 1 - let v:errmsg = substitute(v:exception, '^Vim(catch):', '', "") - finally - Xpath 4096 " X: 4096 - if !caught && !$VIMNOERRTHROW - Xpath 8192 " X: 0 - endif - if !MSG('E475', "Invalid argument") - Xpath 16384 " X: 0 - endif - if !caught - return | " discard error - endif - endtry - catch /.*/ - Xpath 32768 " X: 0 - Xout v:exception "in" v:throwpoint - endtry -endfunction - -call F() -Xpath 65536 " X: 65536 - -delfunction MSG -delfunction F -unlet! caught - -Xcheck 70187 - - -"------------------------------------------------------------------------------- -" Test 66: Stop range :call on error, interrupt, or :throw {{{1 -" -" When a function which is multiply called for a range since it -" doesn't handle the range itself has an error in a command -" dynamically enclosed by :try/:endtry or gets an interrupt or -" executes a :throw, no more calls for the remaining lines in the -" range are made. On an error in a command not dynamically enclosed -" by :try/:endtry, the function is executed again for the remaining -" lines in the range. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - - let file = tempname() - exec "edit" file - - insert -line 1 -line 2 -line 3 -. - - XloopINIT! 1 2 - - let taken = "" - let expected = "G1EF1E(1)F1E(2)F1E(3)G2EF2E(1)G3IF3I(1)G4TF4T(1)G5AF5A(1)" - - function! F(reason, n) abort - let g:taken = g:taken . "F" . a:n . - \ substitute(a:reason, '\(\l\).*', '\u\1', "") . - \ "(" . line(".") . ")" - - if a:reason == "error" - asdf - elseif a:reason == "interrupt" - "INTERRUPT - let dummy = 0 - elseif a:reason == "throw" - throw "xyz" - elseif a:reason == "aborting error" - XloopNEXT - if g:taken != g:expected - Xloop 1 " X: 0 - Xout "'taken' is" g:taken "instead of" g:expected - endif - try - bwipeout! - call delete(file) - asdf - endtry - endif - endfunction - - function! G(reason, n) - let g:taken = g:taken . "G" . a:n . - \ substitute(a:reason, '\(\l\).*', '\u\1', "") - 1,3call F(a:reason, a:n) - endfunction - - Xpath 8 " X: 8 - call G("error", 1) - try - Xpath 16 " X: 16 - try - call G("error", 2) - Xpath 32 " X: 0 - finally - Xpath 64 " X: 64 - try - call G("interrupt", 3) - Xpath 128 " X: 0 - finally - Xpath 256 " X: 256 - try - call G("throw", 4) - Xpath 512 " X: 0 - endtry - endtry - endtry - catch /xyz/ - Xpath 1024 " X: 1024 - catch /.*/ - Xpath 2048 " X: 0 - Xout v:exception "in" ExtraVimThrowpoint() - endtry - Xpath 4096 " X: 4096 - call G("aborting error", 5) - Xpath 8192 " X: 0 - Xout "'taken' is" taken "instead of" expected - -endif - -Xcheck 5464 - - -"------------------------------------------------------------------------------- -" Test 67: :throw across :call command {{{1 -" -" On a call command, an exception might be thrown when evaluating the -" function name, during evaluation of the arguments, or when the -" function is being executed. The exception can be caught by the -" caller. -"------------------------------------------------------------------------------- - -XpathINIT - -function! THROW(x, n) - if a:n == 1 - Xpath 1 " X: 1 - elseif a:n == 2 - Xpath 2 " X: 2 - elseif a:n == 3 - Xpath 4 " X: 4 - endif - throw a:x -endfunction - -function! NAME(x, n) - if a:n == 1 - Xpath 8 " X: 0 - elseif a:n == 2 - Xpath 16 " X: 16 - elseif a:n == 3 - Xpath 32 " X: 32 - elseif a:n == 4 - Xpath 64 " X: 64 - endif - return a:x -endfunction - -function! ARG(x, n) - if a:n == 1 - Xpath 128 " X: 0 - elseif a:n == 2 - Xpath 256 " X: 0 - elseif a:n == 3 - Xpath 512 " X: 512 - elseif a:n == 4 - Xpath 1024 " X: 1024 - endif - return a:x -endfunction - -function! F(x, n) - if a:n == 2 - Xpath 2048 " X: 0 - elseif a:n == 4 - Xpath 4096 " X: 4096 - endif -endfunction - -while 1 - try - let error = 0 - let v:errmsg = "" - - while 1 - try - Xpath 8192 " X: 8192 - call {NAME(THROW("name", 1), 1)}(ARG(4711, 1), 1) - Xpath 16384 " X: 0 - catch /^name$/ - Xpath 32768 " X: 32768 - catch /.*/ - let error = 1 - Xout "1:" v:exception "in" v:throwpoint - finally - if !error && $VIMNOERRTHROW && v:errmsg != "" - let error = 1 - Xout "1:" v:errmsg - endif - if error - Xpath 65536 " X: 0 - endif - let error = 0 - let v:errmsg = "" - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - while 1 - try - Xpath 131072 " X: 131072 - call {NAME("F", 2)}(ARG(THROW("arg", 2), 2), 2) - Xpath 262144 " X: 0 - catch /^arg$/ - Xpath 524288 " X: 524288 - catch /.*/ - let error = 1 - Xout "2:" v:exception "in" v:throwpoint - finally - if !error && $VIMNOERRTHROW && v:errmsg != "" - let error = 1 - Xout "2:" v:errmsg - endif - if error - Xpath 1048576 " X: 0 - endif - let error = 0 - let v:errmsg = "" - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - while 1 - try - Xpath 2097152 " X: 2097152 - call {NAME("THROW", 3)}(ARG("call", 3), 3) - Xpath 4194304 " X: 0 - catch /^call$/ - Xpath 8388608 " X: 8388608 - catch /^0$/ " default return value - Xpath 16777216 " X: 0 - Xout "3:" v:throwpoint - catch /.*/ - let error = 1 - Xout "3:" v:exception "in" v:throwpoint - finally - if !error && $VIMNOERRTHROW && v:errmsg != "" - let error = 1 - Xout "3:" v:errmsg - endif - if error - Xpath 33554432 " X: 0 - endif - let error = 0 - let v:errmsg = "" - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - while 1 - try - Xpath 67108864 " X: 67108864 - call {NAME("F", 4)}(ARG(4711, 4), 4) - Xpath 134217728 " X: 134217728 - catch /.*/ - let error = 1 - Xout "4:" v:exception "in" v:throwpoint - finally - if !error && $VIMNOERRTHROW && v:errmsg != "" - let error = 1 - Xout "4:" v:errmsg - endif - if error - Xpath 268435456 " X: 0 - endif - let error = 0 - let v:errmsg = "" - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - catch /^0$/ " default return value - Xpath 536870912 " X: 0 - Xout v:throwpoint - catch /.*/ - let error = 1 - Xout v:exception "in" v:throwpoint - finally - if !error && $VIMNOERRTHROW && v:errmsg != "" - let error = 1 - Xout v:errmsg - endif - if error - Xpath 1073741824 " X: 0 - endif - break " discard error for $VIMNOERRTHROW - endtry -endwhile - -unlet error -delfunction F - -Xcheck 212514423 - -" Leave THROW(), NAME(), and ARG() for the next test. - - -"------------------------------------------------------------------------------- -" Test 68: :throw across function calls in expressions {{{1 -" -" On a function call within an expression, an exception might be -" thrown when evaluating the function name, during evaluation of the -" arguments, or when the function is being executed. The exception -" can be caught by the caller. -" -" This test reuses the functions THROW(), NAME(), and ARG() from the -" previous test. -"------------------------------------------------------------------------------- - -XpathINIT - -function! F(x, n) - if a:n == 2 - Xpath 2048 " X: 0 - elseif a:n == 4 - Xpath 4096 " X: 4096 - endif - return a:x -endfunction - -unlet! var1 var2 var3 var4 - -while 1 - try - let error = 0 - let v:errmsg = "" - - while 1 - try - Xpath 8192 " X: 8192 - let var1 = {NAME(THROW("name", 1), 1)}(ARG(4711, 1), 1) - Xpath 16384 " X: 0 - catch /^name$/ - Xpath 32768 " X: 32768 - catch /.*/ - let error = 1 - Xout "1:" v:exception "in" v:throwpoint - finally - if !error && $VIMNOERRTHROW && v:errmsg != "" - let error = 1 - Xout "1:" v:errmsg - endif - if error - Xpath 65536 " X: 0 - endif - let error = 0 - let v:errmsg = "" - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - while 1 - try - Xpath 131072 " X: 131072 - let var2 = {NAME("F", 2)}(ARG(THROW("arg", 2), 2), 2) - Xpath 262144 " X: 0 - catch /^arg$/ - Xpath 524288 " X: 524288 - catch /.*/ - let error = 1 - Xout "2:" v:exception "in" v:throwpoint - finally - if !error && $VIMNOERRTHROW && v:errmsg != "" - let error = 1 - Xout "2:" v:errmsg - endif - if error - Xpath 1048576 " X: 0 - endif - let error = 0 - let v:errmsg = "" - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - while 1 - try - Xpath 2097152 " X: 2097152 - let var3 = {NAME("THROW", 3)}(ARG("call", 3), 3) - Xpath 4194304 " X: 0 - catch /^call$/ - Xpath 8388608 " X: 8388608 - catch /^0$/ " default return value - Xpath 16777216 " X: 0 - Xout "3:" v:throwpoint - catch /.*/ - let error = 1 - Xout "3:" v:exception "in" v:throwpoint - finally - if !error && $VIMNOERRTHROW && v:errmsg != "" - let error = 1 - Xout "3:" v:errmsg - endif - if error - Xpath 33554432 " X: 0 - endif - let error = 0 - let v:errmsg = "" - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - while 1 - try - Xpath 67108864 " X: 67108864 - let var4 = {NAME("F", 4)}(ARG(4711, 4), 4) - Xpath 134217728 " X: 134217728 - catch /.*/ - let error = 1 - Xout "4:" v:exception "in" v:throwpoint - finally - if !error && $VIMNOERRTHROW && v:errmsg != "" - let error = 1 - Xout "4:" v:errmsg - endif - if error - Xpath 268435456 " X: 0 - endif - let error = 0 - let v:errmsg = "" - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - catch /^0$/ " default return value - Xpath 536870912 " X: 0 - Xout v:throwpoint - catch /.*/ - let error = 1 - Xout v:exception "in" v:throwpoint - finally - if !error && $VIMNOERRTHROW && v:errmsg != "" - let error = 1 - Xout v:errmsg - endif - if error - Xpath 1073741824 " X: 0 - endif - break " discard error for $VIMNOERRTHROW - endtry -endwhile - -if exists("var1") || exists("var2") || exists("var3") || - \ !exists("var4") || var4 != 4711 - " The Xpath command does not accept 2^31 (negative); add explicitly: - let Xpath = Xpath + 2147483648 " X: 0 - if exists("var1") - Xout "var1 =" var1 - endif - if exists("var2") - Xout "var2 =" var2 - endif - if exists("var3") - Xout "var3 =" var3 - endif - if !exists("var4") - Xout "var4 unset" - elseif var4 != 4711 - Xout "var4 =" var4 - endif -endif - -unlet! error var1 var2 var3 var4 -delfunction THROW -delfunction NAME -delfunction ARG -delfunction F - -Xcheck 212514423 - -" Tests 69 to 75 were moved to test_trycatch.vim -let Xtest = 76 - - -"------------------------------------------------------------------------------- -" Test 76: Errors, interrupts, :throw during expression evaluation {{{1 -" -" When a function call made during expression evaluation is aborted -" due to an error inside a :try/:endtry region or due to an interrupt -" or a :throw, the expression evaluation is aborted as well. No -" message is displayed for the cancelled expression evaluation. On an -" error not inside :try/:endtry, the expression evaluation continues. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - - let taken = "" - - function! ERR(n) - let g:taken = g:taken . "E" . a:n - asdf - endfunction - - function! ERRabort(n) abort - let g:taken = g:taken . "A" . a:n - asdf - endfunction " returns -1; may cause follow-up msg for illegal var/func name - - function! WRAP(n, arg) - let g:taken = g:taken . "W" . a:n - let g:saved_errmsg = v:errmsg - return arg - endfunction - - function! INT(n) - let g:taken = g:taken . "I" . a:n - "INTERRUPT9 - let dummy = 0 - endfunction - - function! THR(n) - let g:taken = g:taken . "T" . a:n - throw "should not be caught" - endfunction - - function! CONT(n) - let g:taken = g:taken . "C" . a:n - endfunction - - function! MSG(n) - let g:taken = g:taken . "M" . a:n - let errmsg = (a:n >= 37 && a:n <= 44) ? g:saved_errmsg : v:errmsg - let msgptn = (a:n >= 10 && a:n <= 27) ? "^$" : "asdf" - if errmsg !~ msgptn - let g:taken = g:taken . "x" - Xout "Expr" a:n.": Unexpected message:" v:errmsg - endif - let v:errmsg = "" - let g:saved_errmsg = "" - endfunction - - let v:errmsg = "" - - try - let t = 1 - XloopINIT 1 2 - while t <= 9 - Xloop 1 " X: 511 - try - if t == 1 - let v{ERR(t) + CONT(t)} = 0 - elseif t == 2 - let v{ERR(t) + CONT(t)} - elseif t == 3 - let var = exists('v{ERR(t) + CONT(t)}') - elseif t == 4 - unlet v{ERR(t) + CONT(t)} - elseif t == 5 - function F{ERR(t) + CONT(t)}() - endfunction - elseif t == 6 - function F{ERR(t) + CONT(t)} - elseif t == 7 - let var = exists('*F{ERR(t) + CONT(t)}') - elseif t == 8 - delfunction F{ERR(t) + CONT(t)} - elseif t == 9 - let var = ERR(t) + CONT(t) - endif - catch /asdf/ - " v:errmsg is not set when the error message is converted to an - " exception. Set it to the original error message. - let v:errmsg = substitute(v:exception, '^Vim:', '', "") - catch /^Vim\((\a\+)\)\=:/ - " An error exception has been thrown after the original error. - let v:errmsg = "" - finally - call MSG(t) - let t = t + 1 - XloopNEXT - continue " discard an aborting error - endtry - endwhile - catch /.*/ - Xpath 512 " X: 0 - Xout v:exception "in" ExtraVimThrowpoint() - endtry - - try - let t = 10 - XloopINIT 1024 2 - while t <= 18 - Xloop 1 " X: 1024 * 511 - try - if t == 10 - let v{INT(t) + CONT(t)} = 0 - elseif t == 11 - let v{INT(t) + CONT(t)} - elseif t == 12 - let var = exists('v{INT(t) + CONT(t)}') - elseif t == 13 - unlet v{INT(t) + CONT(t)} - elseif t == 14 - function F{INT(t) + CONT(t)}() - endfunction - elseif t == 15 - function F{INT(t) + CONT(t)} - elseif t == 16 - let var = exists('*F{INT(t) + CONT(t)}') - elseif t == 17 - delfunction F{INT(t) + CONT(t)} - elseif t == 18 - let var = INT(t) + CONT(t) - endif - catch /^Vim\((\a\+)\)\=:\(Interrupt\)\@!/ - " An error exception has been triggered after the interrupt. - let v:errmsg = substitute(v:exception, - \ '^Vim\((\a\+)\)\=:', '', "") - finally - call MSG(t) - let t = t + 1 - XloopNEXT - continue " discard interrupt - endtry - endwhile - catch /.*/ - Xpath 524288 " X: 0 - Xout v:exception "in" ExtraVimThrowpoint() - endtry - - try - let t = 19 - XloopINIT 1048576 2 - while t <= 27 - Xloop 1 " X: 1048576 * 511 - try - if t == 19 - let v{THR(t) + CONT(t)} = 0 - elseif t == 20 - let v{THR(t) + CONT(t)} - elseif t == 21 - let var = exists('v{THR(t) + CONT(t)}') - elseif t == 22 - unlet v{THR(t) + CONT(t)} - elseif t == 23 - function F{THR(t) + CONT(t)}() - endfunction - elseif t == 24 - function F{THR(t) + CONT(t)} - elseif t == 25 - let var = exists('*F{THR(t) + CONT(t)}') - elseif t == 26 - delfunction F{THR(t) + CONT(t)} - elseif t == 27 - let var = THR(t) + CONT(t) - endif - catch /^Vim\((\a\+)\)\=:/ - " An error exception has been triggered after the :throw. - let v:errmsg = substitute(v:exception, - \ '^Vim\((\a\+)\)\=:', '', "") - finally - call MSG(t) - let t = t + 1 - XloopNEXT - continue " discard exception - endtry - endwhile - catch /.*/ - Xpath 536870912 " X: 0 - Xout v:exception "in" ExtraVimThrowpoint() - endtry - - let v{ERR(28) + CONT(28)} = 0 - call MSG(28) - let v{ERR(29) + CONT(29)} - call MSG(29) - let var = exists('v{ERR(30) + CONT(30)}') - call MSG(30) - unlet v{ERR(31) + CONT(31)} - call MSG(31) - function F{ERR(32) + CONT(32)}() - endfunction - call MSG(32) - function F{ERR(33) + CONT(33)} - call MSG(33) - let var = exists('*F{ERR(34) + CONT(34)}') - call MSG(34) - delfunction F{ERR(35) + CONT(35)} - call MSG(35) - let var = ERR(36) + CONT(36) - call MSG(36) - - let saved_errmsg = "" - - let v{WRAP(37, ERRabort(37)) + CONT(37)} = 0 - call MSG(37) - let v{WRAP(38, ERRabort(38)) + CONT(38)} - call MSG(38) - let var = exists('v{WRAP(39, ERRabort(39)) + CONT(39)}') - call MSG(39) - unlet v{WRAP(40, ERRabort(40)) + CONT(40)} - call MSG(40) - function F{WRAP(41, ERRabort(41)) + CONT(41)}() - endfunction - call MSG(41) - function F{WRAP(42, ERRabort(42)) + CONT(42)} - call MSG(42) - let var = exists('*F{WRAP(43, ERRabort(43)) + CONT(43)}') - call MSG(43) - delfunction F{WRAP(44, ERRabort(44)) + CONT(44)} - call MSG(44) - let var = ERRabort(45) + CONT(45) - call MSG(45) - - Xpath 1073741824 " X: 1073741824 - - let expected = "" - \ . "E1M1E2M2E3M3E4M4E5M5E6M6E7M7E8M8E9M9" - \ . "I10M10I11M11I12M12I13M13I14M14I15M15I16M16I17M17I18M18" - \ . "T19M19T20M20T21M21T22M22T23M23T24M24T25M25T26M26T27M27" - \ . "E28C28M28E29C29M29E30C30M30E31C31M31E32C32M32E33C33M33" - \ . "E34C34M34E35C35M35E36C36M36" - \ . "A37W37C37M37A38W38C38M38A39W39C39M39A40W40C40M40A41W41C41M41" - \ . "A42W42C42M42A43W43C43M43A44W44C44M44A45C45M45" - - if taken != expected - " The Xpath command does not accept 2^31 (negative); display explicitly: - exec "!echo 2147483648 >>" . g:ExtraVimResult - " X: 0 - Xout "'taken' is" taken "instead of" expected - if substitute(taken, - \ '\(.*\)E3C3M3x\(.*\)E30C30M30x\(.*\)A39C39M39x\(.*\)', - \ '\1E3M3\2E30C30M30\3A39C39M39\4', - \ "") == expected - Xout "Is ++emsg_skip for var with expr_start non-NULL" - \ "in f_exists ok?" - endif - endif - - unlet! v var saved_errmsg taken expected - call delete(WA_t5) - call delete(WA_t14) - call delete(WA_t23) - unlet! WA_t5 WA_t14 WA_t23 - delfunction WA_t5 - delfunction WA_t14 - delfunction WA_t23 - -endif - -Xcheck 1610087935 - - -"------------------------------------------------------------------------------- -" Test 77: Errors, interrupts, :throw in name{brace-expression} {{{1 -" -" When a function call made during evaluation of an expression in -" braces as part of a function name after ":function" is aborted due -" to an error inside a :try/:endtry region or due to an interrupt or -" a :throw, the expression evaluation is aborted as well, and the -" function definition is ignored, skipping all commands to the -" ":endfunction". On an error not inside :try/:endtry, the expression -" evaluation continues and the function gets defined, and can be -" called and deleted. -"------------------------------------------------------------------------------- - -XpathINIT - -XloopINIT 1 4 - -function! ERR() abort - Xloop 1 " X: 1 + 4 + 16 + 64 - asdf -endfunction " returns -1 - -function! OK() - Xloop 2 " X: 2 * (1 + 4 + 16) - let v:errmsg = "" - return 0 -endfunction - -let v:errmsg = "" - -Xpath 4096 " X: 4096 -function! F{1 + ERR() + OK()}(arg) - " F0 should be defined. - if exists("a:arg") && a:arg == "calling" - Xpath 8192 " X: 8192 - else - Xpath 16384 " X: 0 - endif -endfunction -if v:errmsg != "" - Xpath 32768 " X: 0 -endif -XloopNEXT - -Xpath 65536 " X: 65536 -call F{1 + ERR() + OK()}("calling") -if v:errmsg != "" - Xpath 131072 " X: 0 -endif -XloopNEXT - -Xpath 262144 " X: 262144 -delfunction F{1 + ERR() + OK()} -if v:errmsg != "" - Xpath 524288 " X: 0 -endif -XloopNEXT - -try - while 1 - let caught = 0 - try - Xpath 1048576 " X: 1048576 - function! G{1 + ERR() + OK()}(arg) - " G0 should not be defined, and the function body should be - " skipped. - if exists("a:arg") && a:arg == "calling" - Xpath 2097152 " X: 0 - else - Xpath 4194304 " X: 0 - endif - " Use an unmatched ":finally" to check whether the body is - " skipped when an error occurs in ERR(). This works whether or - " not the exception is converted to an exception. - finally - Xpath 8388608 " X: 0 - Xout "Body of G{1 + ERR() + OK()}() not skipped" - " Discard the aborting error or exception, and break the - " while loop. - break - " End the try conditional and start a new one to avoid - " ":catch after :finally" errors. - endtry - try - Xpath 16777216 " X: 0 - endfunction - - " When the function was not defined, this won't be reached - whether - " the body was skipped or not. When the function was defined, it - " can be called and deleted here. - Xpath 33554432 " X: 0 - Xout "G0() has been defined" - XloopNEXT - try - call G{1 + ERR() + OK()}("calling") - catch /.*/ - Xpath 67108864 " X: 0 - endtry - Xpath 134217728 " X: 0 - XloopNEXT - try - delfunction G{1 + ERR() + OK()} - catch /.*/ - Xpath 268435456 " X: 0 - endtry - catch /asdf/ - " Jumped to when the function is not defined and the body is - " skipped. - let caught = 1 - catch /.*/ - Xpath 536870912 " X: 0 - finally - if !caught && !$VIMNOERRTHROW - Xpath 1073741824 " X: 0 - endif - break " discard error for $VIMNOERRTHROW - endtry " jumped to when the body is not skipped - endwhile -catch /.*/ - " The Xpath command does not accept 2^31 (negative); add explicitly: - let Xpath = Xpath + 2147483648 " X: 0 - Xout "Body of G{1 + ERR() + OK()}() not skipped, exception caught" - Xout v:exception "in" v:throwpoint -endtry - -Xcheck 1388671 - - -"------------------------------------------------------------------------------- -" Test 78: Messages on parsing errors in expression evaluation {{{1 -" -" When an expression evaluation detects a parsing error, an error -" message is given and converted to an exception, and the expression -" evaluation is aborted. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - - let taken = "" - - function! F(n) - let g:taken = g:taken . "F" . a:n - endfunction - - function! MSG(n, enr, emsg) - let g:taken = g:taken . "M" . a:n - let english = v:lang == "C" || v:lang =~ '^[Ee]n' - if a:enr == "" - Xout "TODO: Add message number for:" a:emsg - let v:errmsg = ":" . v:errmsg - endif - if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg) - if v:errmsg == "" - Xout "Expr" a:n.": Message missing." - let g:taken = g:taken . "x" - else - let v:errmsg = escape(v:errmsg, '"') - Xout "Expr" a:n.": Unexpected message:" v:errmsg - Xout "Expected: " . a:enr . ': ' . a:emsg - let g:taken = g:taken . "X" - endif - endif - endfunction - - function! CONT(n) - let g:taken = g:taken . "C" . a:n - endfunction - - let v:errmsg = "" - XloopINIT 1 2 - - try - let t = 1 - while t <= 14 - let g:taken = g:taken . "T" . t - let v:errmsg = "" - try - let caught = 0 - if t == 1 - let v{novar + CONT(t)} = 0 - elseif t == 2 - let v{novar + CONT(t)} - elseif t == 3 - let var = exists('v{novar + CONT(t)}') - elseif t == 4 - unlet v{novar + CONT(t)} - elseif t == 5 - function F{novar + CONT(t)}() - endfunction - elseif t == 6 - function F{novar + CONT(t)} - elseif t == 7 - let var = exists('*F{novar + CONT(t)}') - elseif t == 8 - delfunction F{novar + CONT(t)} - elseif t == 9 - echo novar + CONT(t) - elseif t == 10 - echo v{novar + CONT(t)} - elseif t == 11 - echo F{novar + CONT(t)} - elseif t == 12 - let var = novar + CONT(t) - elseif t == 13 - let var = v{novar + CONT(t)} - elseif t == 14 - let var = F{novar + CONT(t)}() - endif - catch /^Vim\((\a\+)\)\=:/ - " v:errmsg is not set when the error message is converted to an - " exception. Set it to the original error message. - let v:errmsg = substitute(v:exception, - \ '^Vim\((\a\+)\)\=:', '', "") - let caught = 1 - finally - if t <= 8 && t != 3 && t != 7 - call MSG(t, 'E475', 'Invalid argument\>') - else - if !caught " no error exceptions ($VIMNOERRTHROW set) - call MSG(t, 'E15', "Invalid expression") - else - call MSG(t, 'E121', "Undefined variable") - endif - endif - let t = t + 1 - XloopNEXT - continue " discard an aborting error - endtry - endwhile - catch /.*/ - Xloop 1 " X: 0 - Xout t.":" v:exception "in" ExtraVimThrowpoint() - endtry - - function! T(n, expr, enr, emsg) - try - let g:taken = g:taken . "T" . a:n - let v:errmsg = "" - try - let caught = 0 - execute "let var = " . a:expr - catch /^Vim\((\a\+)\)\=:/ - " v:errmsg is not set when the error message is converted to an - " exception. Set it to the original error message. - let v:errmsg = substitute(v:exception, - \ '^Vim\((\a\+)\)\=:', '', "") - let caught = 1 - finally - if !caught " no error exceptions ($VIMNOERRTHROW set) - call MSG(a:n, 'E15', "Invalid expression") - else - call MSG(a:n, a:enr, a:emsg) - endif - XloopNEXT - " Discard an aborting error: - return - endtry - catch /.*/ - Xloop 1 " X: 0 - Xout a:n.":" v:exception "in" ExtraVimThrowpoint() - endtry - endfunction - - call T(15, 'Nofunc() + CONT(15)', 'E117', "Unknown function") - call T(16, 'F(1 2 + CONT(16))', 'E116', "Invalid arguments") - call T(17, 'F(1, 2) + CONT(17)', 'E118', "Too many arguments") - call T(18, 'F() + CONT(18)', 'E119', "Not enough arguments") - call T(19, '{(1} + CONT(19)', 'E110', "Missing ')'") - call T(20, '("abc"[1) + CONT(20)', 'E111', "Missing ']'") - call T(21, '(1 +) + CONT(21)', 'E15', "Invalid expression") - call T(22, '1 2 + CONT(22)', 'E15', "Invalid expression") - call T(23, '(1 ? 2) + CONT(23)', 'E109', "Missing ':' after '?'") - call T(24, '("abc) + CONT(24)', 'E114', "Missing quote") - call T(25, "('abc) + CONT(25)", 'E115', "Missing quote") - call T(26, '& + CONT(26)', 'E112', "Option name missing") - call T(27, '&asdf + CONT(27)', 'E113', "Unknown option") - - Xpath 134217728 " X: 134217728 - - let expected = "" - \ . "T1M1T2M2T3M3T4M4T5M5T6M6T7M7T8M8T9M9T10M10T11M11T12M12T13M13T14M14" - \ . "T15M15T16M16T17M17T18M18T19M19T20M20T21M21T22M22T23M23T24M24T25M25" - \ . "T26M26T27M27" - - if taken != expected - Xpath 268435456 " X: 0 - Xout "'taken' is" taken "instead of" expected - if substitute(taken, '\(.*\)T3M3x\(.*\)', '\1T3M3\2', "") == expected - Xout "Is ++emsg_skip for var with expr_start non-NULL" - \ "in f_exists ok?" - endif - endif - - unlet! var caught taken expected - call delete(WA_t5) - unlet! WA_t5 - delfunction WA_t5 - -endif - -Xcheck 134217728 - - -"------------------------------------------------------------------------------- -" Test 79: Throwing one of several errors for the same command {{{1 -" -" When several errors appear in a row (for instance during expression -" evaluation), the first as the most specific one is used when -" throwing an error exception. If, however, a syntax error is -" detected afterwards, this one is used for the error exception. -" On a syntax error, the next command is not executed, on a normal -" error, however, it is (relevant only in a function without the -" "abort" flag). v:errmsg is not set. -" -" If throwing error exceptions is configured off, v:errmsg is always -" set to the latest error message, that is, to the more general -" message or the syntax error, respectively. -"------------------------------------------------------------------------------- - -XpathINIT - -XloopINIT 1 2 - -function! NEXT(cmd) - exec a:cmd . " | Xloop 1" -endfunction - -call NEXT('echo novar') " X: 1 * 1 (checks nextcmd) -XloopNEXT -call NEXT('let novar #') " X: 0 * 2 (skips nextcmd) -XloopNEXT -call NEXT('unlet novar #') " X: 0 * 4 (skips nextcmd) -XloopNEXT -call NEXT('let {novar}') " X: 0 * 8 (skips nextcmd) -XloopNEXT -call NEXT('unlet{ novar}') " X: 0 * 16 (skips nextcmd) - -function! EXEC(cmd) - exec a:cmd -endfunction - -function! MATCH(expected, msg, enr, emsg) - let msg = a:msg - if a:enr == "" - Xout "TODO: Add message number for:" a:emsg - let msg = ":" . msg - endif - let english = v:lang == "C" || v:lang =~ '^[Ee]n' - if msg !~ '^'.a:enr.':' || (english && msg !~ a:emsg) - let match = 0 - if a:expected " no match although expected - if a:msg == "" - Xout "Message missing." - else - let msg = escape(msg, '"') - Xout "Unexpected message:" msg - Xout "Expected:" a:enr . ": " . a:emsg - endif - endif - else - let match = 1 - if !a:expected " match although not expected - let msg = escape(msg, '"') - Xout "Unexpected message:" msg - Xout "Expected none." - endif - endif - return match -endfunction - -try - - while 1 " dummy loop - try - let v:errmsg = "" - let caught = 0 - let thrmsg = "" - call EXEC('echo novar') " normal error - catch /^Vim\((\a\+)\)\=:/ - let caught = 1 - let thrmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "") - finally - Xpath 32 " X: 32 - if !caught - if !$VIMNOERRTHROW - Xpath 64 " X: 0 - endif - elseif !MATCH(1, thrmsg, 'E121', "Undefined variable") - \ || v:errmsg != "" - Xpath 128 " X: 0 - endif - if !caught && !MATCH(1, v:errmsg, 'E15', "Invalid expression") - Xpath 256 " X: 0 - endif - break " discard error if $VIMNOERRTHROW == 1 - endtry - endwhile - - Xpath 512 " X: 512 - let cmd = "let" - XloopINIT 1024 32 - while cmd != "" - try - let v:errmsg = "" - let caught = 0 - let thrmsg = "" - call EXEC(cmd . ' novar #') " normal plus syntax error - catch /^Vim\((\a\+)\)\=:/ - let caught = 1 - let thrmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "") - finally - Xloop 1 " X: 1024 * (1 + 32) - if !caught - if !$VIMNOERRTHROW - Xloop 2 " X: 0 - endif - else - if cmd == "let" - let match = MATCH(0, thrmsg, 'E121', "Undefined variable") - elseif cmd == "unlet" - let match = MATCH(0, thrmsg, 'E108', "No such variable") - endif - if match " normal error - Xloop 4 " X: 0 - endif - if !MATCH(1, thrmsg, 'E488', "Trailing characters") - \|| v:errmsg != "" - " syntax error - Xloop 8 " X: 0 - endif - endif - if !caught && !MATCH(1, v:errmsg, 'E488', "Trailing characters") - " last error - Xloop 16 " X: 0 - endif - if cmd == "let" - let cmd = "unlet" - else - let cmd = "" - endif - XloopNEXT - continue " discard error if $VIMNOERRTHROW == 1 - endtry - endwhile - - Xpath 1048576 " X: 1048576 - let cmd = "let" - XloopINIT 2097152 32 - while cmd != "" - try - let v:errmsg = "" - let caught = 0 - let thrmsg = "" - call EXEC(cmd . ' {novar}') " normal plus syntax error - catch /^Vim\((\a\+)\)\=:/ - let caught = 1 - let thrmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "") - finally - Xloop 1 " X: 2097152 * (1 + 32) - if !caught - if !$VIMNOERRTHROW - Xloop 2 " X: 0 - endif - else - if MATCH(0, thrmsg, 'E121', "Undefined variable") " normal error - Xloop 4 " X: 0 - endif - if !MATCH(1, thrmsg, 'E475', 'Invalid argument\>') - \ || v:errmsg != "" " syntax error - Xloop 8 " X: 0 - endif - endif - if !caught && !MATCH(1, v:errmsg, 'E475', 'Invalid argument\>') - " last error - Xloop 16 " X: 0 - endif - if cmd == "let" - let cmd = "unlet" - else - let cmd = "" - endif - XloopNEXT - continue " discard error if $VIMNOERRTHROW == 1 - endtry - endwhile - -catch /.*/ - " The Xpath command does not accept 2^31 (negative); add explicitly: - let Xpath = Xpath + 2147483648 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -unlet! next_command thrmsg match -delfunction NEXT -delfunction EXEC -delfunction MATCH - -Xcheck 70288929 - - -"------------------------------------------------------------------------------- -" Test 80: Syntax error in expression for illegal :elseif {{{1 -" -" If there is a syntax error in the expression after an illegal -" :elseif, an error message is given (or an error exception thrown) -" for the illegal :elseif rather than the expression error. -"------------------------------------------------------------------------------- - -XpathINIT - -function! MSG(enr, emsg) - let english = v:lang == "C" || v:lang =~ '^[Ee]n' - if a:enr == "" - Xout "TODO: Add message number for:" a:emsg - let v:errmsg = ":" . v:errmsg - endif - let match = 1 - if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg) - let match = 0 - if v:errmsg == "" - Xout "Message missing." - else - let v:errmsg = escape(v:errmsg, '"') - Xout "Unexpected message:" v:errmsg - endif - endif - return match -endfunction - -let v:errmsg = "" -if 0 -else -elseif 1 ||| 2 -endif -Xpath 1 " X: 1 -if !MSG('E584', ":elseif after :else") - Xpath 2 " X: 0 -endif - -let v:errmsg = "" -if 1 -else -elseif 1 ||| 2 -endif -Xpath 4 " X: 4 -if !MSG('E584', ":elseif after :else") - Xpath 8 " X: 0 -endif - -let v:errmsg = "" -elseif 1 ||| 2 -Xpath 16 " X: 16 -if !MSG('E582', ":elseif without :if") - Xpath 32 " X: 0 -endif - -let v:errmsg = "" -while 1 - elseif 1 ||| 2 -endwhile -Xpath 64 " X: 64 -if !MSG('E582', ":elseif without :if") - Xpath 128 " X: 0 -endif - -while 1 - try - try - let v:errmsg = "" - let caught = 0 - if 0 - else - elseif 1 ||| 2 - endif - catch /^Vim\((\a\+)\)\=:/ - let caught = 1 - let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "") - finally - Xpath 256 " X: 256 - if !caught && !$VIMNOERRTHROW - Xpath 512 " X: 0 - endif - if !MSG('E584', ":elseif after :else") - Xpath 1024 " X: 0 - endif - endtry - catch /.*/ - Xpath 2048 " X: 0 - Xout v:exception "in" v:throwpoint - finally - break " discard error for $VIMNOERRTHROW - endtry -endwhile - -while 1 - try - try - let v:errmsg = "" - let caught = 0 - if 1 - else - elseif 1 ||| 2 - endif - catch /^Vim\((\a\+)\)\=:/ - let caught = 1 - let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "") - finally - Xpath 4096 " X: 4096 - if !caught && !$VIMNOERRTHROW - Xpath 8192 " X: 0 - endif - if !MSG('E584', ":elseif after :else") - Xpath 16384 " X: 0 - endif - endtry - catch /.*/ - Xpath 32768 " X: 0 - Xout v:exception "in" v:throwpoint - finally - break " discard error for $VIMNOERRTHROW - endtry -endwhile - -while 1 - try - try - let v:errmsg = "" - let caught = 0 - elseif 1 ||| 2 - catch /^Vim\((\a\+)\)\=:/ - let caught = 1 - let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "") - finally - Xpath 65536 " X: 65536 - if !caught && !$VIMNOERRTHROW - Xpath 131072 " X: 0 - endif - if !MSG('E582', ":elseif without :if") - Xpath 262144 " X: 0 - endif - endtry - catch /.*/ - Xpath 524288 " X: 0 - Xout v:exception "in" v:throwpoint - finally - break " discard error for $VIMNOERRTHROW - endtry -endwhile - -while 1 - try - try - let v:errmsg = "" - let caught = 0 - while 1 - elseif 1 ||| 2 - endwhile - catch /^Vim\((\a\+)\)\=:/ - let caught = 1 - let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "") - finally - Xpath 1048576 " X: 1048576 - if !caught && !$VIMNOERRTHROW - Xpath 2097152 " X: 0 - endif - if !MSG('E582', ":elseif without :if") - Xpath 4194304 " X: 0 - endif - endtry - catch /.*/ - Xpath 8388608 " X: 0 - Xout v:exception "in" v:throwpoint - finally - break " discard error for $VIMNOERRTHROW - endtry -endwhile - -Xpath 16777216 " X: 16777216 - -unlet! caught -delfunction MSG - -Xcheck 17895765 - - -"------------------------------------------------------------------------------- -" Test 81: Discarding exceptions after an error or interrupt {{{1 -" -" When an exception is thrown from inside a :try conditional without -" :catch and :finally clauses and an error or interrupt occurs before -" the :endtry is reached, the exception is discarded. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - try - Xpath 1 " X: 1 - try - Xpath 2 " X: 2 - throw "arrgh" - Xpath 4 " X: 0 -" if 1 - Xpath 8 " X: 0 - " error after :throw: missing :endif - endtry - Xpath 16 " X: 0 - catch /arrgh/ - Xpath 32 " X: 0 - endtry - Xpath 64 " X: 0 -endif - -if ExtraVim() - try - Xpath 128 " X: 128 - try - Xpath 256 " X: 256 - throw "arrgh" - Xpath 512 " X: 0 - endtry " INTERRUPT - Xpath 1024 " X: 0 - catch /arrgh/ - Xpath 2048 " X: 0 - endtry - Xpath 4096 " X: 0 -endif - -Xcheck 387 - - -"------------------------------------------------------------------------------- -" Test 82: Ignoring :catch clauses after an error or interrupt {{{1 -" -" When an exception is thrown and an error or interrupt occurs before -" the matching :catch clause is reached, the exception is discarded -" and the :catch clause is ignored (also for the error or interrupt -" exception being thrown then). -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - try - try - Xpath 1 " X: 1 - throw "arrgh" - Xpath 2 " X: 0 -" if 1 - Xpath 4 " X: 0 - " error after :throw: missing :endif - catch /.*/ - Xpath 8 " X: 0 - Xout v:exception "in" ExtraVimThrowpoint() - catch /.*/ - Xpath 16 " X: 0 - Xout v:exception "in" ExtraVimThrowpoint() - endtry - Xpath 32 " X: 0 - catch /arrgh/ - Xpath 64 " X: 0 - endtry - Xpath 128 " X: 0 -endif - -if ExtraVim() - function! E() - try - try - Xpath 256 " X: 256 - throw "arrgh" - Xpath 512 " X: 0 -" if 1 - Xpath 1024 " X: 0 - " error after :throw: missing :endif - catch /.*/ - Xpath 2048 " X: 0 - Xout v:exception "in" ExtraVimThrowpoint() - catch /.*/ - Xpath 4096 " X: 0 - Xout v:exception "in" ExtraVimThrowpoint() - endtry - Xpath 8192 " X: 0 - catch /arrgh/ - Xpath 16384 " X: 0 - endtry - endfunction - - call E() - Xpath 32768 " X: 0 -endif - -if ExtraVim() - try - try - Xpath 65536 " X: 65536 - throw "arrgh" - Xpath 131072 " X: 0 - catch /.*/ "INTERRUPT - Xpath 262144 " X: 0 - Xout v:exception "in" ExtraVimThrowpoint() - catch /.*/ - Xpath 524288 " X: 0 - Xout v:exception "in" ExtraVimThrowpoint() - endtry - Xpath 1048576 " X: 0 - catch /arrgh/ - Xpath 2097152 " X: 0 - endtry - Xpath 4194304 " X: 0 -endif - -if ExtraVim() - function I() - try - try - Xpath 8388608 " X: 8388608 - throw "arrgh" - Xpath 16777216 " X: 0 - catch /.*/ "INTERRUPT - Xpath 33554432 " X: 0 - Xout v:exception "in" ExtraVimThrowpoint() - catch /.*/ - Xpath 67108864 " X: 0 - Xout v:exception "in" ExtraVimThrowpoint() - endtry - Xpath 134217728 " X: 0 - catch /arrgh/ - Xpath 268435456 " X: 0 - endtry - endfunction - - call I() - Xpath 536870912 " X: 0 -endif - -Xcheck 8454401 - - -"------------------------------------------------------------------------------- -" Test 83: Executing :finally clauses after an error or interrupt {{{1 -" -" When an exception is thrown and an error or interrupt occurs before -" the :finally of the innermost :try is reached, the exception is -" discarded and the :finally clause is executed. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - try - Xpath 1 " X: 1 - try - Xpath 2 " X: 2 - throw "arrgh" - Xpath 4 " X: 0 -" if 1 - Xpath 8 " X: 0 - " error after :throw: missing :endif - finally - Xpath 16 " X: 16 - endtry - Xpath 32 " X: 0 - catch /arrgh/ - Xpath 64 " X: 0 - endtry - Xpath 128 " X: 0 -endif - -if ExtraVim() - try - Xpath 256 " X: 256 - try - Xpath 512 " X: 512 - throw "arrgh" - Xpath 1024 " X: 0 - finally "INTERRUPT - Xpath 2048 " X: 2048 - endtry - Xpath 4096 " X: 0 - catch /arrgh/ - Xpath 8192 " X: 0 - endtry - Xpath 16384 " X: 0 -endif - -Xcheck 2835 - - -"------------------------------------------------------------------------------- -" Test 84: Exceptions in autocommand sequences. {{{1 -" -" When an exception occurs in a sequence of autocommands for -" a specific event, the rest of the sequence is not executed. The -" command that triggered the autocommand execution aborts, and the -" exception is propagated to the caller. -" -" For the FuncUndefined event under a function call expression or -" :call command, the function is not executed, even when it has -" been defined by the autocommands before the exception occurred. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - - function! INT() - "INTERRUPT - let dummy = 0 - endfunction - - aug TMP - autocmd! - - autocmd User x1 Xpath 1 " X: 1 - autocmd User x1 throw "x1" - autocmd User x1 Xpath 2 " X: 0 - - autocmd User x2 Xpath 4 " X: 4 - autocmd User x2 asdf - autocmd User x2 Xpath 8 " X: 0 - - autocmd User x3 Xpath 16 " X: 16 - autocmd User x3 call INT() - autocmd User x3 Xpath 32 " X: 0 - - autocmd FuncUndefined U1 function! U1() - autocmd FuncUndefined U1 Xpath 64 " X: 0 - autocmd FuncUndefined U1 endfunction - autocmd FuncUndefined U1 Xpath 128 " X: 128 - autocmd FuncUndefined U1 throw "U1" - autocmd FuncUndefined U1 Xpath 256 " X: 0 - - autocmd FuncUndefined U2 function! U2() - autocmd FuncUndefined U2 Xpath 512 " X: 0 - autocmd FuncUndefined U2 endfunction - autocmd FuncUndefined U2 Xpath 1024 " X: 1024 - autocmd FuncUndefined U2 ASDF - autocmd FuncUndefined U2 Xpath 2048 " X: 0 - - autocmd FuncUndefined U3 function! U3() - autocmd FuncUndefined U3 Xpath 4096 " X: 0 - autocmd FuncUndefined U3 endfunction - autocmd FuncUndefined U3 Xpath 8192 " X: 8192 - autocmd FuncUndefined U3 call INT() - autocmd FuncUndefined U3 Xpath 16384 " X: 0 - aug END - - try - try - Xpath 32768 " X: 32768 - doautocmd User x1 - catch /x1/ - Xpath 65536 " X: 65536 - endtry - - while 1 - try - Xpath 131072 " X: 131072 - let caught = 0 - doautocmd User x2 - catch /asdf/ - let caught = 1 - finally - Xpath 262144 " X: 262144 - if !caught && !$VIMNOERRTHROW - Xpath 524288 " X: 0 - " Propagate uncaught error exception, - else - " ... but break loop for caught error exception, - " or discard error and break loop if $VIMNOERRTHROW - break - endif - endtry - endwhile - - while 1 - try - Xpath 1048576 " X: 1048576 - let caught = 0 - doautocmd User x3 - catch /Vim:Interrupt/ - let caught = 1 - finally - Xpath 2097152 " X: 2097152 - if !caught && !$VIMNOINTTHROW - Xpath 4194304 " X: 0 - " Propagate uncaught interrupt exception, - else - " ... but break loop for caught interrupt exception, - " or discard interrupt and break loop if $VIMNOINTTHROW - break - endif - endtry - endwhile - - if exists("*U1") | delfunction U1 | endif - if exists("*U2") | delfunction U2 | endif - if exists("*U3") | delfunction U3 | endif - - try - Xpath 8388608 " X: 8388608 - call U1() - catch /U1/ - Xpath 16777216 " X: 16777216 - endtry - - while 1 - try - Xpath 33554432 " X: 33554432 - let caught = 0 - call U2() - catch /ASDF/ - let caught = 1 - finally - Xpath 67108864 " X: 67108864 - if !caught && !$VIMNOERRTHROW - Xpath 134217728 " X: 0 - " Propagate uncaught error exception, - else - " ... but break loop for caught error exception, - " or discard error and break loop if $VIMNOERRTHROW - break - endif - endtry - endwhile - - while 1 - try - Xpath 268435456 " X: 268435456 - let caught = 0 - call U3() - catch /Vim:Interrupt/ - let caught = 1 - finally - Xpath 536870912 " X: 536870912 - if !caught && !$VIMNOINTTHROW - Xpath 1073741824 " X: 0 - " Propagate uncaught interrupt exception, - else - " ... but break loop for caught interrupt exception, - " or discard interrupt and break loop if $VIMNOINTTHROW - break - endif - endtry - endwhile - catch /.*/ - " The Xpath command does not accept 2^31 (negative); display explicitly: - exec "!echo 2147483648 >>" . g:ExtraVimResult - Xout "Caught" v:exception "in" v:throwpoint - endtry - - unlet caught - delfunction INT - delfunction U1 - delfunction U2 - delfunction U3 - au! TMP - aug! TMP -endif - -Xcheck 934782101 - - -"------------------------------------------------------------------------------- -" Test 85: Error exceptions in autocommands for I/O command events {{{1 -" -" When an I/O command is inside :try/:endtry, autocommands to be -" executed after it should be skipped on an error (exception) in the -" command itself or in autocommands to be executed before the command. -" In the latter case, the I/O command should not be executed either. -" Example 1: BufWritePre, :write, BufWritePost -" Example 2: FileReadPre, :read, FileReadPost. -"------------------------------------------------------------------------------- - -XpathINIT - -function! MSG(enr, emsg) - let english = v:lang == "C" || v:lang =~ '^[Ee]n' - if a:enr == "" - Xout "TODO: Add message number for:" a:emsg - let v:errmsg = ":" . v:errmsg - endif - let match = 1 - if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg) - let match = 0 - if v:errmsg == "" - Xout "Message missing." - else - let v:errmsg = escape(v:errmsg, '"') - Xout "Unexpected message:" v:errmsg - endif - endif - return match -endfunction - -" Remove the autocommands for the events specified as arguments in all used -" autogroups. -function Delete_autocommands(...) - let augfile = tempname() - while 1 - try - exec "redir >" . augfile - aug - redir END - exec "edit" augfile - g/^$/d - norm G$ - let wrap = "w" - while search('\%( \|^\)\@<=.\{-}\%( \)\@=', wrap) > 0 - let wrap = "W" - exec "norm y/ \n" - let argno = 1 - while argno <= a:0 - exec "au!" escape(@", " ") a:{argno} - let argno = argno + 1 - endwhile - endwhile - catch /.*/ - finally - bwipeout! - call delete(augfile) - break " discard errors for $VIMNOERRTHROW - endtry - endwhile -endfunction - -call Delete_autocommands("BufWritePre", "BufWritePost") - -while 1 - try - try - let post = 0 - aug TMP - au! BufWritePost * let post = 1 - aug END - let caught = 0 - write /n/o/n/e/x/i/s/t/e/n/t - catch /^Vim(write):/ - let caught = 1 - let v:errmsg = substitute(v:exception, '^Vim(write):', '', "") - finally - Xpath 1 " X: 1 - if !caught && !$VIMNOERRTHROW - Xpath 2 " X: 0 - endif - let v:errmsg = substitute(v:errmsg, '^"/n/o/n/e/x/i/s/t/e/n/t" ', - \ '', "") - if !MSG('E212', "Can't open file for writing") - Xpath 4 " X: 0 - endif - if post - Xpath 8 " X: 0 - Xout "BufWritePost commands executed after write error" - endif - au! TMP - aug! TMP - endtry - catch /.*/ - Xpath 16 " X: 0 - Xout v:exception "in" v:throwpoint - finally - break " discard error for $VIMNOERRTHROW - endtry -endwhile - -while 1 - try - try - let post = 0 - aug TMP - au! BufWritePre * asdf - au! BufWritePost * let post = 1 - aug END - let tmpfile = tempname() - let caught = 0 - exec "write" tmpfile - catch /^Vim\((write)\)\=:/ - let caught = 1 - let v:errmsg = substitute(v:exception, '^Vim\((write)\)\=:', '', "") - finally - Xpath 32 " X: 32 - if !caught && !$VIMNOERRTHROW - Xpath 64 " X: 0 - endif - let v:errmsg = substitute(v:errmsg, '^"'.tmpfile.'" ', '', "") - if !MSG('E492', "Not an editor command") - Xpath 128 " X: 0 - endif - if filereadable(tmpfile) - Xpath 256 " X: 0 - Xout ":write command not suppressed after BufWritePre error" - endif - if post - Xpath 512 " X: 0 - Xout "BufWritePost commands executed after BufWritePre error" - endif - au! TMP - aug! TMP - endtry - catch /.*/ - Xpath 1024 " X: 0 - Xout v:exception "in" v:throwpoint - finally - break " discard error for $VIMNOERRTHROW - endtry -endwhile - -call delete(tmpfile) - -call Delete_autocommands("BufWritePre", "BufWritePost", - \ "BufReadPre", "BufReadPost", "FileReadPre", "FileReadPost") - -while 1 - try - try - let post = 0 - aug TMP - au! FileReadPost * let post = 1 - aug END - let caught = 0 - read /n/o/n/e/x/i/s/t/e/n/t - catch /^Vim(read):/ - let caught = 1 - let v:errmsg = substitute(v:exception, '^Vim(read):', '', "") - finally - Xpath 2048 " X: 2048 - if !caught && !$VIMNOERRTHROW - Xpath 4096 " X: 0 - endif - let v:errmsg = substitute(v:errmsg, ' /n/o/n/e/x/i/s/t/e/n/t$', - \ '', "") - if !MSG('E484', "Can't open file") - Xpath 8192 " X: 0 - endif - if post - Xpath 16384 " X: 0 - Xout "FileReadPost commands executed after write error" - endif - au! TMP - aug! TMP - endtry - catch /.*/ - Xpath 32768 " X: 0 - Xout v:exception "in" v:throwpoint - finally - break " discard error for $VIMNOERRTHROW - endtry -endwhile - -while 1 - try - let infile = tempname() - let tmpfile = tempname() - exec "!echo XYZ >" . infile - exec "edit" tmpfile - try - Xpath 65536 " X: 65536 - try - let post = 0 - aug TMP - au! FileReadPre * asdf - au! FileReadPost * let post = 1 - aug END - let caught = 0 - exec "0read" infile - catch /^Vim\((read)\)\=:/ - let caught = 1 - let v:errmsg = substitute(v:exception, '^Vim\((read)\)\=:', '', - \ "") - finally - Xpath 131072 " X: 131072 - if !caught && !$VIMNOERRTHROW - Xpath 262144 " X: 0 - endif - let v:errmsg = substitute(v:errmsg, ' '.infile.'$', '', "") - if !MSG('E492', "Not an editor command") - Xpath 524288 " X: 0 - endif - if getline("1") == "XYZ" - Xpath 1048576 " X: 0 - Xout ":read command not suppressed after FileReadPre error" - endif - if post - Xpath 2097152 " X: 0 - Xout "FileReadPost commands executed after " . - \ "FileReadPre error" - endif - au! TMP - aug! TMP - endtry - finally - bwipeout! - endtry - catch /.*/ - Xpath 4194304 " X: 0 - Xout v:exception "in" v:throwpoint - finally - break " discard error for $VIMNOERRTHROW - endtry -endwhile - -call delete(infile) -call delete(tmpfile) -unlet! caught post infile tmpfile -delfunction MSG -delfunction Delete_autocommands - -Xcheck 198689 - -"------------------------------------------------------------------------------- -" Test 86: setloclist crash {{{1 -" -" Executing a setloclist() on BufUnload shouldn't crash Vim -"------------------------------------------------------------------------------- - -func F - au BufUnload * :call setloclist(0, [{'bufnr':1, 'lnum':1, 'col':1, 'text': 'tango down'}]) - - :lvimgrep /.*/ *.mak -endfunc - -XpathINIT - -ExecAsScript F - -delfunction F -Xout "No Crash for vimgrep on BufUnload" -Xcheck 0 - -" Test 87 was moved to test_vimscript.vim -let Xtest = 88 - - -"------------------------------------------------------------------------------- -" Test 88: $VIMNOERRTHROW and $VIMNOINTTHROW support {{{1 -" -" It is possible to configure Vim for throwing exceptions on error -" or interrupt, controlled by variables $VIMNOERRTHROW and -" $VIMNOINTTHROW. This is just for increasing the number of tests. -" All tests here should run for all four combinations of setting -" these variables to 0 or 1. The variables are intended for the -" development phase only. In the final release, Vim should be -" configured to always use error and interrupt exceptions. -" -" The test result is "OK", -" -" - if the $VIMNOERRTHROW and the $VIMNOINTTHROW control are not -" configured and exceptions are thrown on error and on -" interrupt. -" -" - if the $VIMNOERRTHROW or the $VIMNOINTTHROW control is -" configured and works as intended. -" -" What actually happens, is shown in the test output. -" -" Otherwise, the test result is "FAIL", and the test output describes -" the problem. -" -" IMPORTANT: This must be the last test because it sets $VIMNOERRTHROW and -" $VIMNOINTTHROW. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - - function! ThrowOnError() - XloopNEXT - let caught = 0 - try - Xloop 1 " X: 1 + 8 + 64 - asdf - catch /.*/ - let caught = 1 " error exception caught - finally - Xloop 2 " X: 2 + 16 + 128 - return caught " discard aborting error - endtry - Xloop 4 " X: 0 - endfunction - - let quits_skipped = 0 - - function! ThrowOnInterrupt() - XloopNEXT - let caught = 0 - try - Xloop 1 " X: (1 + 8 + 64) * 512 - "INTERRUPT3 - let dummy = 0 - let g:quits_skipped = g:quits_skipped + 1 - catch /.*/ - let caught = 1 " interrupt exception caught - finally - Xloop 2 " X: (2 + 16 + 128) * 512 - return caught " discard interrupt - endtry - Xloop 4 " X: 0 - endfunction - - function! CheckThrow(Type) - execute 'return ThrowOn' . a:Type . '()' - endfunction - - function! CheckConfiguration(type) " type is "error" or "interrupt" - - let type = a:type - let Type = substitute(type, '.*', '\u&', "") - let VAR = '$VIMNO' . substitute(type, '\(...\).*', '\U\1', "") . 'THROW' - - if type == "error" - XloopINIT! 1 8 - elseif type == "interrupt" - XloopINIT! 512 8 - endif - - exec 'let requested_for_tests = exists(VAR) && ' . VAR . ' == 0' - exec 'let suppressed_for_tests = ' . VAR . ' != 0' - let used_in_tests = CheckThrow(Type) - - exec 'let ' . VAR . ' = 0' - let request_works = CheckThrow(Type) - - exec 'let ' . VAR . ' = 1' - let suppress_works = !CheckThrow(Type) - - if type == "error" - XloopINIT! 262144 8 - elseif type == "interrupt" - XloopINIT! 2097152 8 - - if g:quits_skipped != 0 - Xloop 1 " X: 0*2097152 - Xout "Test environment error. Interrupt breakpoints skipped: " - \ . g:quits_skipped . ".\n" - \ . "Cannot check whether interrupt exceptions are thrown." - return - endif - endif - - let failure = - \ !suppressed_for_tests && !used_in_tests - \ || !request_works - - let contradiction = - \ used_in_tests - \ ? suppressed_for_tests && !request_works - \ : !suppressed_for_tests - - if failure - " Failure in configuration. - Xloop 2 " X: 0 * 2* (262144 + 2097152) - elseif contradiction - " Failure in test logic. Should not happen. - Xloop 4 " X: 0 * 4 * (262144 + 2097152) - endif - - let var_control_configured = - \ request_works != used_in_tests - \ || suppress_works == used_in_tests - - let var_control_not_configured = - \ requested_for_tests || suppressed_for_tests - \ ? request_works && !suppress_works - \ : request_works == used_in_tests - \ && suppress_works != used_in_tests - - let with = used_in_tests ? "with" : "without" - - let set = suppressed_for_tests ? "non-zero" : - \ requested_for_tests ? "0" : "unset" - - let although = contradiction && !var_control_not_configured - \ ? ",\nalthough " - \ : ".\n" - - let output = "All tests were run " . with . " throwing exceptions on " - \ . type . although - - if !var_control_not_configured - let output = output . VAR . " was " . set . "." - - if !request_works && !requested_for_tests - let output = output . - \ "\n" . Type . " exceptions are not thrown when " . VAR . - \ " is\nset to 0." - endif - - if !suppress_works && (!used_in_tests || - \ !request_works && - \ !requested_for_tests && !suppressed_for_tests) - let output = output . - \ "\n" . Type . " exceptions are thrown when " . VAR . - \ " is set to 1." - endif - - if !failure && var_control_configured - let output = output . - \ "\nRun tests also with " . substitute(VAR, '^\$', '', "") - \ . "=" . used_in_tests . "." - \ . "\nThis is for testing in the development phase only." - \ . " Remove the \n" - \ . VAR . " control in the final release." - endif - else - let output = output . - \ "The " . VAR . " control is not configured." - endif - - Xout output - endfunction - - call CheckConfiguration("error") - Xpath 16777216 " X: 16777216 - call CheckConfiguration("interrupt") - Xpath 33554432 " X: 33554432 -endif - -Xcheck 50443995 - -" IMPORTANT: No test should be added after this test because it changes -" $VIMNOERRTHROW and $VIMNOINTTHROW. - - -"------------------------------------------------------------------------------- -" Modelines {{{1 -" vim: ts=8 sw=4 tw=80 fdm=marker -"------------------------------------------------------------------------------- diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index a83ef50abc..a3d240f27e 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -15,7 +15,6 @@ source test_fnamemodify.vim source test_ga.vim source test_glob2regpat.vim source test_global.vim -source test_lispwords.vim source test_move.vim source test_put.vim source test_reltime.vim diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim index 521c3fcd57..fb8b17cd16 100644 --- a/src/nvim/testdir/test_arglist.vim +++ b/src/nvim/testdir/test_arglist.vim @@ -1,5 +1,6 @@ " Test argument list commands +source check.vim source shared.vim source term_util.vim @@ -417,15 +418,19 @@ func Test_argdedupe() call Reset_arglist() argdedupe call assert_equal([], argv()) + args a a a aa b b a b aa argdedupe call assert_equal(['a', 'aa', 'b'], argv()) + args a b c argdedupe call assert_equal(['a', 'b', 'c'], argv()) + args a argdedupe call assert_equal(['a'], argv()) + args a A b B argdedupe if has('fname_case') @@ -433,11 +438,17 @@ func Test_argdedupe() else call assert_equal(['a', 'b'], argv()) endif + args a b a c a b last argdedupe next call assert_equal('c', expand('%:t')) + + args a ./a + argdedupe + call assert_equal(['a'], argv()) + %argd endfunc @@ -509,18 +520,14 @@ func Test_arglist_autocmd() new " redefine arglist; go to Xxx1 next! Xxx1 Xxx2 Xxx3 - " open window for all args; Reading Xxx2 will change the arglist and the - " third window will get Xxx1: - " win 1: Xxx1 - " win 2: Xxx2 - " win 3: Xxx1 - all + " open window for all args; Reading Xxx2 will try to change the arglist and + " that will fail + call assert_fails("all", "E1156:") call assert_equal('test file Xxx1', getline(1)) wincmd w - wincmd w - call assert_equal('test file Xxx1', getline(1)) - rewind call assert_equal('test file Xxx2', getline(1)) + wincmd w + call assert_equal('test file Xxx3', getline(1)) autocmd! BufReadPost Xxx2 enew! | only @@ -554,11 +561,9 @@ func Test_argdo() bwipe Xa.c Xb.c Xc.c endfunc -" Test for quiting Vim with unedited files in the argument list +" Test for quitting Vim with unedited files in the argument list func Test_quit_with_arglist() - if !CanRunVimInTerminal() - throw 'Skipped: cannot run vim in terminal' - endif + CheckRunVimInTerminal let buf = RunVimInTerminal('', {'rows': 6}) call term_sendkeys(buf, ":set nomore\n") call term_sendkeys(buf, ":args a b c\n") @@ -591,4 +596,153 @@ func Test_quit_with_arglist() call delete('.c.swp') endfunc +" Test for ":all" not working when in the cmdline window +func Test_all_not_allowed_from_cmdwin() + au BufEnter * all + next x + " Use try/catch here, somehow assert_fails() doesn't work on MS-Windows + " console. + let caught = 'no' + try + exe ":norm! 7q?apat\<CR>" + catch /E11:/ + let caught = 'yes' + endtry + call assert_equal('yes', caught) + au! BufEnter +endfunc + +func Test_clear_arglist_in_all() + n 0 00 000 0000 00000 000000 + au WinNew 0 n 0 + call assert_fails("all", "E1156") + au! * +endfunc + +" Test for the :all command +func Test_all_command() + %argdelete + + " :all command should not close windows with files in the argument list, + " but can rearrange the windows. + args Xargnew1 Xargnew2 + %bw! + edit Xargold1 + split Xargnew1 + let Xargnew1_winid = win_getid() + split Xargold2 + split Xargnew2 + let Xargnew2_winid = win_getid() + split Xargold3 + all + call assert_equal(2, winnr('$')) + call assert_equal([Xargnew1_winid, Xargnew2_winid], + \ [win_getid(1), win_getid(2)]) + call assert_equal([bufnr('Xargnew1'), bufnr('Xargnew2')], + \ [winbufnr(1), winbufnr(2)]) + + " :all command should close windows for files which are not in the + " argument list in the current tab page. + %bw! + edit Xargold1 + split Xargold2 + tabedit Xargold3 + split Xargold4 + tabedit Xargold5 + tabfirst + all + call assert_equal(3, tabpagenr('$')) + call assert_equal([bufnr('Xargnew1'), bufnr('Xargnew2')], tabpagebuflist(1)) + call assert_equal([bufnr('Xargold4'), bufnr('Xargold3')], tabpagebuflist(2)) + call assert_equal([bufnr('Xargold5')], tabpagebuflist(3)) + + " :tab all command should close windows for files which are not in the + " argument list across all the tab pages. + %bw! + edit Xargold1 + split Xargold2 + tabedit Xargold3 + split Xargold4 + tabedit Xargold5 + tabfirst + args Xargnew1 Xargnew2 + tab all + call assert_equal(2, tabpagenr('$')) + call assert_equal([bufnr('Xargnew1')], tabpagebuflist(1)) + call assert_equal([bufnr('Xargnew2')], tabpagebuflist(2)) + + " If a count is specified, then :all should open only that many windows. + %bw! + args Xargnew1 Xargnew2 Xargnew3 Xargnew4 Xargnew5 + all 3 + call assert_equal(3, winnr('$')) + call assert_equal([bufnr('Xargnew1'), bufnr('Xargnew2'), bufnr('Xargnew3')], + \ [winbufnr(1), winbufnr(2), winbufnr(3)]) + + " The :all command should not open more than 'tabpagemax' tab pages. + " If there are more files, then they should be opened in the last tab page. + %bw! + set tabpagemax=3 + tab all + call assert_equal(3, tabpagenr('$')) + call assert_equal([bufnr('Xargnew1')], tabpagebuflist(1)) + call assert_equal([bufnr('Xargnew2')], tabpagebuflist(2)) + call assert_equal([bufnr('Xargnew3'), bufnr('Xargnew4'), bufnr('Xargnew5')], + \ tabpagebuflist(3)) + set tabpagemax& + + " Without the 'hidden' option, modified buffers should not be closed. + args Xargnew1 Xargnew2 + %bw! + edit Xargtemp1 + call setline(1, 'temp buffer 1') + split Xargtemp2 + call setline(1, 'temp buffer 2') + all + call assert_equal(4, winnr('$')) + call assert_equal([bufnr('Xargtemp2'), bufnr('Xargtemp1'), bufnr('Xargnew1'), + \ bufnr('Xargnew2')], + \ [winbufnr(1), winbufnr(2), winbufnr(3), winbufnr(4)]) + + " With the 'hidden' option set, both modified and unmodified buffers in + " closed windows should be hidden. + set hidden + all + call assert_equal(2, winnr('$')) + call assert_equal([bufnr('Xargnew1'), bufnr('Xargnew2')], + \ [winbufnr(1), winbufnr(2)]) + call assert_equal([1, 1, 0, 0], [getbufinfo('Xargtemp1')[0].hidden, + \ getbufinfo('Xargtemp2')[0].hidden, + \ getbufinfo('Xargnew1')[0].hidden, + \ getbufinfo('Xargnew2')[0].hidden]) + set nohidden + + " When 'winheight' is set to a large value, :all should open only one + " window. + args Xargnew1 Xargnew2 Xargnew3 Xargnew4 Xargnew5 + %bw! + set winheight=9999 + call assert_fails('all', 'E36:') + call assert_equal([1, bufnr('Xargnew1')], [winnr('$'), winbufnr(1)]) + set winheight& + + " When 'winwidth' is set to a large value, :vert all should open only one + " window. + %bw! + set winwidth=9999 + call assert_fails('vert all', 'E36:') + call assert_equal([1, bufnr('Xargnew1')], [winnr('$'), winbufnr(1)]) + set winwidth& + + " empty argument list tests + %bw! + %argdelete + call assert_equal('', execute('args')) + all + call assert_equal(1, winnr('$')) + + %argdelete + %bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_assert.vim b/src/nvim/testdir/test_assert.vim index fdd8b0bef6..431908e95c 100644 --- a/src/nvim/testdir/test_assert.vim +++ b/src/nvim/testdir/test_assert.vim @@ -48,6 +48,11 @@ func Test_assert_equal() call assert_equal('XxxxxxxxxxxxxxxxxxxxxxX', 'XyyyyyyyyyyyyyyyyyyyyyyyyyX') call assert_match("Expected 'X\\\\\\[x occurs 21 times]X' but got 'X\\\\\\[y occurs 25 times]X'", v:errors[0]) call remove(v:errors, 0) + + " special characters are escaped + call assert_equal("\b\e\f\n\t\r\\\x01\x7f", 'x') + call assert_match('Expected ''\\b\\e\\f\\n\\t\\r\\\\\\x01\\x7f'' but got ''x''', v:errors[0]) + call remove(v:errors, 0) endfunc func Test_assert_equal_dict() @@ -146,6 +151,14 @@ func Test_assert_exception() try nocommand catch + call assert_equal(1, assert_exception('E12345:')) + endtry + call assert_match("Expected 'E12345:' but got 'Vim:E492: ", v:errors[0]) + call remove(v:errors, 0) + + try + nocommand + catch try " illegal argument, get NULL for error call assert_equal(1, assert_exception([])) @@ -153,6 +166,10 @@ func Test_assert_exception() call assert_equal(0, assert_exception('E730:')) endtry endtry + + call assert_equal(1, assert_exception('E492:')) + call assert_match('v:exception is not set', v:errors[0]) + call remove(v:errors, 0) endfunc func Test_wrong_error_type() @@ -202,6 +219,14 @@ func Test_assert_fail_fails() call assert_match("stupid: Expected 'E9876' but got 'E492:", v:errors[0]) call remove(v:errors, 0) + call assert_equal(1, assert_fails('xxx', ['E9876'])) + call assert_match("Expected \\['E9876'\\] but got 'E492:", v:errors[0]) + call remove(v:errors, 0) + + call assert_equal(1, assert_fails('xxx', ['E492:', 'E9876'])) + call assert_match("Expected \\['E492:', 'E9876'\\] but got 'E492:", v:errors[0]) + call remove(v:errors, 0) + call assert_equal(1, assert_fails('echo', '', 'echo command')) call assert_match("command did not fail: echo command", v:errors[0]) call remove(v:errors, 0) @@ -209,6 +234,41 @@ func Test_assert_fail_fails() call assert_equal(1, 'echo'->assert_fails('', 'echo command')) call assert_match("command did not fail: echo command", v:errors[0]) call remove(v:errors, 0) + + try + call assert_equal(1, assert_fails('xxx', [])) + catch + let exp = v:exception + endtry + call assert_match("E856: assert_fails() second argument", exp) + + try + call assert_equal(1, assert_fails('xxx', ['1', '2', '3'])) + catch + let exp = v:exception + endtry + call assert_match("E856: assert_fails() second argument", exp) + + try + call assert_equal(1, assert_fails('xxx', #{one: 1})) + catch + let exp = v:exception + endtry + call assert_match("E856: assert_fails() second argument", exp) + + try + call assert_equal(1, assert_fails('xxx', 'E492', '', 'burp')) + catch + let exp = v:exception + endtry + call assert_match("E1115: assert_fails() fourth argument must be a number", exp) + + try + call assert_equal(1, assert_fails('xxx', 'E492', '', 54, 123)) + catch + let exp = v:exception + endtry + call assert_match("E1116: assert_fails() fifth argument must be a string", exp) endfunc func Test_assert_fails_in_try_block() @@ -278,19 +338,18 @@ func Test_assert_with_msg() endfunc func Test_mouse_position() - throw 'Skipped: Nvim does not have test_setmouse()' let save_mouse = &mouse set mouse=a new call setline(1, ['line one', 'line two']) call assert_equal([0, 1, 1, 0], getpos('.')) - call test_setmouse(1, 5) + call Ntest_setmouse(1, 5) call feedkeys("\<LeftMouse>", "xt") call assert_equal([0, 1, 5, 0], getpos('.')) - call test_setmouse(2, 20) + call Ntest_setmouse(2, 20) call feedkeys("\<LeftMouse>", "xt") call assert_equal([0, 2, 8, 0], getpos('.')) - call test_setmouse(5, 1) + call Ntest_setmouse(5, 1) call feedkeys("\<LeftMouse>", "xt") call assert_equal([0, 2, 1, 0], getpos('.')) bwipe! diff --git a/src/nvim/testdir/test_autochdir.vim b/src/nvim/testdir/test_autochdir.vim index 4229095f9f..a8810047a0 100644 --- a/src/nvim/testdir/test_autochdir.vim +++ b/src/nvim/testdir/test_autochdir.vim @@ -122,9 +122,7 @@ endfunc func Test_multibyte() " using an invalid character should not cause a crash set wic - " Except on Windows, E472 is also thrown last, but v8.1.1183 isn't ported yet - " call assert_fails('tc *', has('win32') ? 'E480:' : 'E344:') - call assert_fails('tc *', has('win32') ? 'E480:' : 'E472:') + call assert_fails('tc *', has('win32') ? 'E480:' : 'E344:') set nowic endfunc diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 716511210d..83af0f6be0 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -4,6 +4,7 @@ source shared.vim source check.vim source term_util.vim source screendump.vim +source load.vim func s:cleanup_buffers() abort for bnr in range(1, bufnr('$')) @@ -20,8 +21,35 @@ func Test_vim_did_enter() " becomes one. endfunc +" Test for the CursorHold autocmd +func Test_CursorHold_autocmd() + CheckRunVimInTerminal + call writefile(['one', 'two', 'three'], 'Xfile') + let before =<< trim END + set updatetime=10 + au CursorHold * call writefile([line('.')], 'Xoutput', 'a') + END + call writefile(before, 'Xinit') + let buf = RunVimInTerminal('-S Xinit Xfile', {}) + call term_sendkeys(buf, "G") + call term_wait(buf, 20) + call term_sendkeys(buf, "gg") + call term_wait(buf) + call WaitForAssert({-> assert_equal(['1'], readfile('Xoutput')[-1:-1])}) + call term_sendkeys(buf, "j") + call term_wait(buf) + call WaitForAssert({-> assert_equal(['1', '2'], readfile('Xoutput')[-2:-1])}) + call term_sendkeys(buf, "j") + call term_wait(buf) + call WaitForAssert({-> assert_equal(['1', '2', '3'], readfile('Xoutput')[-3:-1])}) + call StopVimInTerminal(buf) + + call delete('Xinit') + call delete('Xoutput') + call delete('Xfile') +endfunc + if has('timers') - source load.vim func ExitInsertMode(id) call feedkeys("\<Esc>") @@ -148,6 +176,12 @@ func Test_autocmd_bufunload_with_tabnext() quit endfunc +func Test_argdelete_in_next() + au BufNew,BufEnter,BufLeave,BufWinEnter * argdel + call assert_fails('next a b', 'E1156:') + au! BufNew,BufEnter,BufLeave,BufWinEnter * +endfunc + func Test_autocmd_bufwinleave_with_tabfirst() tabedit augroup sample @@ -169,9 +203,7 @@ func Test_autocmd_bufunload_avoiding_SEGV_01() exe 'autocmd BufUnload <buffer> ' . (lastbuf + 1) . 'bwipeout!' augroup END - " Todo: check for E937 generated first - " call assert_fails('edit bb.txt', 'E937:') - call assert_fails('edit bb.txt', 'E517:') + call assert_fails('edit bb.txt', 'E937:') autocmd! test_autocmd_bufunload augroup! test_autocmd_bufunload @@ -263,6 +295,61 @@ func Test_win_tab_autocmd() unlet g:record endfunc +func Test_WinResized() + CheckRunVimInTerminal + + let lines =<< trim END + set scrolloff=0 + call setline(1, ['111', '222']) + vnew + call setline(1, ['aaa', 'bbb']) + new + call setline(1, ['foo', 'bar']) + + let g:resized = 0 + au WinResized * let g:resized += 1 + + func WriteResizedEvent() + call writefile([json_encode(v:event)], 'XresizeEvent') + endfunc + au WinResized * call WriteResizedEvent() + END + call writefile(lines, 'Xtest_winresized', 'D') + let buf = RunVimInTerminal('-S Xtest_winresized', {'rows': 10}) + + " redraw now to avoid a redraw after the :echo command + call term_sendkeys(buf, ":redraw!\<CR>") + call TermWait(buf) + + call term_sendkeys(buf, ":echo g:resized\<CR>") + call WaitForAssert({-> assert_match('^0$', term_getline(buf, 10))}, 1000) + + " increase window height, two windows will be reported + call term_sendkeys(buf, "\<C-W>+") + call TermWait(buf) + call term_sendkeys(buf, ":echo g:resized\<CR>") + call WaitForAssert({-> assert_match('^1$', term_getline(buf, 10))}, 1000) + + let event = readfile('XresizeEvent')[0]->json_decode() + call assert_equal({ + \ 'windows': [1002, 1001], + \ }, event) + + " increase window width, three windows will be reported + call term_sendkeys(buf, "\<C-W>>") + call TermWait(buf) + call term_sendkeys(buf, ":echo g:resized\<CR>") + call WaitForAssert({-> assert_match('^2$', term_getline(buf, 10))}, 1000) + + let event = readfile('XresizeEvent')[0]->json_decode() + call assert_equal({ + \ 'windows': [1002, 1001, 1000], + \ }, event) + + call delete('XresizeEvent') + call StopVimInTerminal(buf) +endfunc + func Test_WinScrolled() CheckRunVimInTerminal @@ -273,13 +360,17 @@ func Test_WinScrolled() endfor let win_id = win_getid() let g:matched = v:false + func WriteScrollEvent() + call writefile([json_encode(v:event)], 'XscrollEvent') + endfunc execute 'au WinScrolled' win_id 'let g:matched = v:true' let g:scrolled = 0 au WinScrolled * let g:scrolled += 1 au WinScrolled * let g:amatch = str2nr(expand('<amatch>')) au WinScrolled * let g:afile = str2nr(expand('<afile>')) + au WinScrolled * call WriteScrollEvent() END - call writefile(lines, 'Xtest_winscrolled') + call writefile(lines, 'Xtest_winscrolled', 'D') let buf = RunVimInTerminal('-S Xtest_winscrolled', {'rows': 6}) call term_sendkeys(buf, ":echo g:scrolled\<CR>") @@ -289,15 +380,33 @@ func Test_WinScrolled() call term_sendkeys(buf, "zlzh:echo g:scrolled\<CR>") call WaitForAssert({-> assert_match('^2 ', term_getline(buf, 6))}, 1000) + let event = readfile('XscrollEvent')[0]->json_decode() + call assert_equal({ + \ 'all': {'leftcol': 1, 'topline': 0, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0}, + \ '1000': {'leftcol': -1, 'topline': 0, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0} + \ }, event) + " Scroll up/down in Normal mode. call term_sendkeys(buf, "\<c-e>\<c-y>:echo g:scrolled\<CR>") call WaitForAssert({-> assert_match('^4 ', term_getline(buf, 6))}, 1000) + let event = readfile('XscrollEvent')[0]->json_decode() + call assert_equal({ + \ 'all': {'leftcol': 0, 'topline': 1, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0}, + \ '1000': {'leftcol': 0, 'topline': -1, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0} + \ }, event) + " Scroll up/down in Insert mode. call term_sendkeys(buf, "Mi\<c-x>\<c-e>\<Esc>i\<c-x>\<c-y>\<Esc>") call term_sendkeys(buf, ":echo g:scrolled\<CR>") call WaitForAssert({-> assert_match('^6 ', term_getline(buf, 6))}, 1000) + let event = readfile('XscrollEvent')[0]->json_decode() + call assert_equal({ + \ 'all': {'leftcol': 0, 'topline': 1, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0}, + \ '1000': {'leftcol': 0, 'topline': -1, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0} + \ }, event) + " Scroll the window horizontally to focus the last letter of the third line " containing only six characters. Moving to the previous and shorter lines " should trigger another autocommand as Vim has to make them visible. @@ -305,6 +414,12 @@ func Test_WinScrolled() call term_sendkeys(buf, ":echo g:scrolled\<CR>") call WaitForAssert({-> assert_match('^8 ', term_getline(buf, 6))}, 1000) + let event = readfile('XscrollEvent')[0]->json_decode() + call assert_equal({ + \ 'all': {'leftcol': 5, 'topline': 0, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0}, + \ '1000': {'leftcol': -5, 'topline': 0, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0} + \ }, event) + " Ensure the command was triggered for the specified window ID. call term_sendkeys(buf, ":echo g:matched\<CR>") call WaitForAssert({-> assert_match('^v:true ', term_getline(buf, 6))}, 1000) @@ -313,8 +428,38 @@ func Test_WinScrolled() call term_sendkeys(buf, ":echo g:amatch == win_id && g:afile == win_id\<CR>") call WaitForAssert({-> assert_match('^v:true ', term_getline(buf, 6))}, 1000) + call delete('XscrollEvent') + call StopVimInTerminal(buf) +endfunc + +func Test_WinScrolled_mouse() + CheckRunVimInTerminal + + let lines =<< trim END + set nowrap scrolloff=0 + set mouse=a term=xterm ttymouse=sgr mousetime=200 clipboard= + call setline(1, ['foo']->repeat(32)) + split + let g:scrolled = 0 + au WinScrolled * let g:scrolled += 1 + END + call writefile(lines, 'Xtest_winscrolled_mouse', 'D') + let buf = RunVimInTerminal('-S Xtest_winscrolled_mouse', {'rows': 10}) + + " With the upper split focused, send a scroll-down event to the unfocused one. + call test_setmouse(7, 1) + call term_sendkeys(buf, "\<ScrollWheelDown>") + call TermWait(buf) + call term_sendkeys(buf, ":echo g:scrolled\<CR>") + call WaitForAssert({-> assert_match('^1', term_getline(buf, 10))}, 1000) + + " Again, but this time while we're in insert mode. + call term_sendkeys(buf, "i\<ScrollWheelDown>\<Esc>") + call TermWait(buf) + call term_sendkeys(buf, ":echo g:scrolled\<CR>") + call WaitForAssert({-> assert_match('^2', term_getline(buf, 10))}, 1000) + call StopVimInTerminal(buf) - call delete('Xtest_winscrolled') endfunc func Test_WinScrolled_close_curwin() @@ -327,7 +472,7 @@ func Test_WinScrolled_close_curwin() au WinScrolled * close au VimLeave * call writefile(['123456'], 'Xtestout') END - call writefile(lines, 'Xtest_winscrolled_close_curwin') + call writefile(lines, 'Xtest_winscrolled_close_curwin', 'D') let buf = RunVimInTerminal('-S Xtest_winscrolled_close_curwin', {'rows': 6}) " This was using freed memory @@ -335,12 +480,158 @@ func Test_WinScrolled_close_curwin() call TermWait(buf) call StopVimInTerminal(buf) + " check the startup script finished to the end call assert_equal(['123456'], readfile('Xtestout')) - - call delete('Xtest_winscrolled_close_curwin') call delete('Xtestout') endfunc +func Test_WinScrolled_once_only() + CheckRunVimInTerminal + + let lines =<< trim END + set cmdheight=2 + call setline(1, ['aaa', 'bbb']) + let trigger_count = 0 + func ShowInfo(id) + echo g:trigger_count g:winid winlayout() + endfunc + + vsplit + split + " use a timer to show the info after a redraw + au WinScrolled * let trigger_count += 1 | let winid = expand('<amatch>') | call timer_start(100, 'ShowInfo') + wincmd j + wincmd l + END + call writefile(lines, 'Xtest_winscrolled_once', 'D') + let buf = RunVimInTerminal('-S Xtest_winscrolled_once', #{rows: 10, cols: 60, statusoff: 2}) + + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, 'Test_winscrolled_once_only_1', {}) + + call StopVimInTerminal(buf) +endfunc + +" Check that WinScrolled is not triggered immediately when defined and there +" are split windows. +func Test_WinScrolled_not_when_defined() + CheckRunVimInTerminal + + let lines =<< trim END + call setline(1, ['aaa', 'bbb']) + echo 'nothing happened' + func ShowTriggered(id) + echo 'triggered' + endfunc + END + call writefile(lines, 'Xtest_winscrolled_not', 'D') + let buf = RunVimInTerminal('-S Xtest_winscrolled_not', #{rows: 10, cols: 60, statusoff: 2}) + call term_sendkeys(buf, ":split\<CR>") + call TermWait(buf) + " use a timer to show the message after redrawing + call term_sendkeys(buf, ":au WinScrolled * call timer_start(100, 'ShowTriggered')\<CR>") + call VerifyScreenDump(buf, 'Test_winscrolled_not_when_defined_1', {}) + + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, 'Test_winscrolled_not_when_defined_2', {}) + + call StopVimInTerminal(buf) +endfunc + +func Test_WinScrolled_long_wrapped() + CheckRunVimInTerminal + + let lines =<< trim END + set scrolloff=0 + let height = winheight(0) + let width = winwidth(0) + let g:scrolled = 0 + au WinScrolled * let g:scrolled += 1 + call setline(1, repeat('foo', height * width)) + call cursor(1, height * width) + END + call writefile(lines, 'Xtest_winscrolled_long_wrapped', 'D') + let buf = RunVimInTerminal('-S Xtest_winscrolled_long_wrapped', {'rows': 6}) + + call term_sendkeys(buf, ":echo g:scrolled\<CR>") + call WaitForAssert({-> assert_match('^0 ', term_getline(buf, 6))}, 1000) + + call term_sendkeys(buf, 'gj') + call term_sendkeys(buf, ":echo g:scrolled\<CR>") + call WaitForAssert({-> assert_match('^1 ', term_getline(buf, 6))}, 1000) + + call term_sendkeys(buf, '0') + call term_sendkeys(buf, ":echo g:scrolled\<CR>") + call WaitForAssert({-> assert_match('^2 ', term_getline(buf, 6))}, 1000) + + call term_sendkeys(buf, '$') + call term_sendkeys(buf, ":echo g:scrolled\<CR>") + call WaitForAssert({-> assert_match('^3 ', term_getline(buf, 6))}, 1000) + + call StopVimInTerminal(buf) +endfunc + +func Test_WinScrolled_diff() + CheckRunVimInTerminal + + let lines =<< trim END + set diffopt+=foldcolumn:0 + call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']) + vnew + call setline(1, ['d', 'e', 'f', 'g', 'h', 'i']) + windo diffthis + func WriteScrollEvent() + call writefile([json_encode(v:event)], 'XscrollEvent') + endfunc + au WinScrolled * call WriteScrollEvent() + END + call writefile(lines, 'Xtest_winscrolled_diff', 'D') + let buf = RunVimInTerminal('-S Xtest_winscrolled_diff', {'rows': 8}) + + call term_sendkeys(buf, "\<C-E>") + call WaitForAssert({-> assert_match('^d', term_getline(buf, 3))}, 1000) + + let event = readfile('XscrollEvent')[0]->json_decode() + call assert_equal({ + \ 'all': {'leftcol': 0, 'topline': 1, 'topfill': 1, 'width': 0, 'height': 0, 'skipcol': 0}, + \ '1000': {'leftcol': 0, 'topline': 1, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0}, + \ '1001': {'leftcol': 0, 'topline': 0, 'topfill': -1, 'width': 0, 'height': 0, 'skipcol': 0} + \ }, event) + + call term_sendkeys(buf, "2\<C-E>") + call WaitForAssert({-> assert_match('^f', term_getline(buf, 3))}, 1000) + + let event = readfile('XscrollEvent')[0]->json_decode() + call assert_equal({ + \ 'all': {'leftcol': 0, 'topline': 2, 'topfill': 2, 'width': 0, 'height': 0, 'skipcol': 0}, + \ '1000': {'leftcol': 0, 'topline': 2, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0}, + \ '1001': {'leftcol': 0, 'topline': 0, 'topfill': -2, 'width': 0, 'height': 0, 'skipcol': 0} + \ }, event) + + call term_sendkeys(buf, "\<C-E>") + call WaitForAssert({-> assert_match('^g', term_getline(buf, 3))}, 1000) + + let event = readfile('XscrollEvent')[0]->json_decode() + call assert_equal({ + \ 'all': {'leftcol': 0, 'topline': 2, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0}, + \ '1000': {'leftcol': 0, 'topline': 1, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0}, + \ '1001': {'leftcol': 0, 'topline': 1, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0} + \ }, event) + + call term_sendkeys(buf, "2\<C-Y>") + call WaitForAssert({-> assert_match('^e', term_getline(buf, 3))}, 1000) + + let event = readfile('XscrollEvent')[0]->json_decode() + call assert_equal({ + \ 'all': {'leftcol': 0, 'topline': 3, 'topfill': 1, 'width': 0, 'height': 0, 'skipcol': 0}, + \ '1000': {'leftcol': 0, 'topline': -2, 'topfill': 0, 'width': 0, 'height': 0, 'skipcol': 0}, + \ '1001': {'leftcol': 0, 'topline': -1, 'topfill': 1, 'width': 0, 'height': 0, 'skipcol': 0} + \ }, event) + + call StopVimInTerminal(buf) + call delete('XscrollEvent') +endfunc + func Test_WinClosed() " Test that the pattern is matched against the closed window's ID, and both " <amatch> and <afile> are set to it. @@ -414,6 +705,26 @@ func Test_WinClosed_throws_with_tabs() augroup! test-WinClosed endfunc +" This used to trigger WinClosed twice for the same window, and the window's +" buffer was NULL in the second autocommand. +func Test_WinClosed_switch_tab() + edit Xa + split Xb + split Xc + tab split + new + augroup test-WinClosed + autocmd WinClosed * tabprev | bwipe! + augroup END + close + " Check that the tabline has been fully removed + call assert_equal([1, 1], win_screenpos(0)) + + autocmd! test-WinClosed + augroup! test-WinClosed + %bwipe! +endfunc + func s:AddAnAutocmd() augroup vimBarTest au BufReadCmd * echo 'hello' @@ -424,17 +735,20 @@ endfunc func Test_early_bar() " test that a bar is recognized before the {event} call s:AddAnAutocmd() - augroup vimBarTest | au! | augroup END + augroup vimBarTest | au! | let done = 77 | augroup END call assert_equal(1, len(split(execute('au vimBarTest'), "\n"))) + call assert_equal(77, done) call s:AddAnAutocmd() - augroup vimBarTest| au!| augroup END + augroup vimBarTest| au!| let done = 88 | augroup END call assert_equal(1, len(split(execute('au vimBarTest'), "\n"))) + call assert_equal(88, done) " test that a bar is recognized after the {event} call s:AddAnAutocmd() - augroup vimBarTest| au!BufReadCmd| augroup END + augroup vimBarTest| au!BufReadCmd| let done = 99 | augroup END call assert_equal(1, len(split(execute('au vimBarTest'), "\n"))) + call assert_equal(99, done) " test that a bar is recognized after the {group} call s:AddAnAutocmd() @@ -474,6 +788,8 @@ func Test_augroup_warning() redir END call assert_notmatch("W19:", res) au! VimEnter + + call assert_fails('augroup!', 'E471:') endfunc func Test_BufReadCmdHelp() @@ -493,6 +809,28 @@ func Test_BufReadCmdHelpJump() au! BufReadCmd endfunc +" BufReadCmd is triggered for a "nofile" buffer. Check all values. +func Test_BufReadCmdNofile() + for val in ['nofile', + \ 'nowrite', + \ 'acwrite', + \ 'quickfix', + \ 'help', + "\ 'terminal', + \ 'prompt', + "\ 'popup', + \ ] + new somefile + exe 'set buftype=' .. val + au BufReadCmd somefile call setline(1, 'triggered') + edit + call assert_equal('triggered', getline(1)) + + au! BufReadCmd + bwipe! + endfor +endfunc + func Test_augroup_deleted() " This caused a crash before E936 was introduced augroup x @@ -587,9 +925,28 @@ func Test_BufEnter() " On MS-Windows we can't edit the directory, make sure we wipe the right " buffer. bwipe! Xdir - call delete('Xdir', 'd') au! BufEnter + + " Editing a "nofile" buffer doesn't read the file but does trigger BufEnter + " for historic reasons. Also test other 'buftype' values. + for val in ['nofile', + \ 'nowrite', + \ 'acwrite', + \ 'quickfix', + \ 'help', + "\ 'terminal', + \ 'prompt', + "\ 'popup', + \ ] + new somefile + exe 'set buftype=' .. val + au BufEnter somefile call setline(1, 'some text') + edit + call assert_equal('some text', getline(1)) + bwipe! + au! BufEnter + endfor endfunc " Closing a window might cause an endless loop @@ -1766,6 +2123,21 @@ func Test_BufReadCmd() au! BufWriteCmd endfunc +func Test_BufWriteCmd() + autocmd BufWriteCmd Xbufwritecmd let g:written = 1 + new + file Xbufwritecmd + set buftype=acwrite + call mkdir('Xbufwritecmd') + write + " BufWriteCmd should be triggered even if a directory has the same name + call assert_equal(1, g:written) + call delete('Xbufwritecmd', 'd') + unlet g:written + au! BufWriteCmd + bwipe! +endfunc + func SetChangeMarks(start, end) exe a:start .. 'mark [' exe a:end .. 'mark ]' @@ -1860,9 +2232,7 @@ func Test_change_mark_in_autocmds() endfunc func Test_Filter_noshelltemp() - if !executable('cat') - return - endif + CheckExecutable cat enew! call setline(1, ['a', 'b', 'c', 'd']) @@ -1939,6 +2309,7 @@ endfunc func Test_autocommand_all_events() call assert_fails('au * * bwipe', 'E1155:') call assert_fails('au * x bwipe', 'E1155:') + call assert_fails('au! * x bwipe', 'E1155:') endfunc func Test_autocmd_user() @@ -2026,9 +2397,8 @@ function Test_dirchanged_auto() endfunc " Test TextChangedI and TextChangedP -" See test/functional/viml/completion_spec.lua' func Test_ChangedP() - CheckFunction test_override + throw 'Skipped: use test/functional/editor/completion_spec.lua' new call setline(1, ['foo', 'bar', 'foobar']) call test_override("char_avail", 1) @@ -2549,7 +2919,7 @@ func Test_autocmd_CmdWinEnter() call term_sendkeys(buf, "q:") call term_wait(buf) call term_sendkeys(buf, ":echo b:dummy_var\<cr>") - call WaitForAssert({-> assert_match('^This is a dummy', term_getline(buf, 6))}, 1000) + call WaitForAssert({-> assert_match('^This is a dummy', term_getline(buf, 6))}, 2000) call term_sendkeys(buf, ":echo &buftype\<cr>") call WaitForAssert({-> assert_notmatch('^nofile', term_getline(buf, 6))}, 1000) call term_sendkeys(buf, ":echo winnr\<cr>") @@ -2636,6 +3006,17 @@ func Test_FileType_spell() setglobal spellfile= endfunc +" this was wiping out the current buffer and using freed memory +func Test_SpellFileMissing_bwipe() + next 0 + au SpellFileMissing 0 bwipe + call assert_fails('set spell spelllang=0', 'E937:') + + au! SpellFileMissing + set nospell spelllang=en + bwipe +endfunc + " Test closing a window or editing another buffer from a FileChangedRO handler " in a readonly buffer func Test_FileChangedRO_winclose() @@ -2724,6 +3105,31 @@ func Test_autocmd_FileReadCmd() delfunc ReadFileCmd endfunc +" Test for passing invalid arguments to autocmd +func Test_autocmd_invalid_args() + " Additional character after * for event + call assert_fails('autocmd *a Xfile set ff=unix', 'E215:') + augroup Test + augroup END + " Invalid autocmd event + call assert_fails('autocmd Bufabc Xfile set ft=vim', 'E216:') + " Invalid autocmd event in a autocmd group + call assert_fails('autocmd Test Bufabc Xfile set ft=vim', 'E216:') + augroup! Test + " Execute all autocmds + call assert_fails('doautocmd * BufEnter', 'E217:') + call assert_fails('augroup! x1a2b3', 'E367:') + call assert_fails('autocmd BufNew <buffer=999> pwd', 'E680:') + call assert_fails('autocmd BufNew \) set ff=unix', 'E55:') +endfunc + +" Test for deep nesting of autocmds +func Test_autocmd_deep_nesting() + autocmd BufEnter Xfile doautocmd BufEnter Xfile + call assert_fails('doautocmd BufEnter Xfile', 'E218:') + autocmd! BufEnter Xfile +endfunc + " Tests for SigUSR1 autocmd event, which is only available on posix systems. func Test_autocmd_sigusr1() CheckUnix @@ -2781,7 +3187,7 @@ func Test_BufDelete_changebuf() augroup END let save_cpo = &cpo set cpo+=f - call assert_fails('r Xfile', 'E484:') + call assert_fails('r Xfile', ['E812:', 'E484:']) call assert_equal('somefile', @%) let &cpo = save_cpo augroup TestAuCmd @@ -2871,6 +3277,15 @@ func Test_Visual_doautoall_redraw() %bwipe! endfunc +" This was using freed memory. +func Test_BufNew_arglocal() + arglocal + au BufNew * arglocal + call assert_fails('drop xx', 'E1156:') + + au! BufNew +endfunc + func Test_autocmd_closes_window() au BufNew,BufWinLeave * e %e file yyy @@ -3039,19 +3454,29 @@ func Test_mode_changes() call assert_equal(5, g:nori_to_any) endif - if has('cmdwin') - let g:n_to_c = 0 - au ModeChanged n:c let g:n_to_c += 1 - let g:c_to_n = 0 - au ModeChanged c:n let g:c_to_n += 1 - let g:mode_seq += ['c', 'n', 'c', 'n'] - call feedkeys("q:\<C-C>\<Esc>", 'tnix') - call assert_equal(len(g:mode_seq) - 1, g:index) - call assert_equal(2, g:n_to_c) - call assert_equal(2, g:c_to_n) - unlet g:n_to_c - unlet g:c_to_n - endif + let g:n_to_c = 0 + au ModeChanged n:c let g:n_to_c += 1 + let g:c_to_n = 0 + au ModeChanged c:n let g:c_to_n += 1 + let g:mode_seq += ['c', 'n', 'c', 'n'] + call feedkeys("q:\<C-C>\<Esc>", 'tnix') + call assert_equal(len(g:mode_seq) - 1, g:index) + call assert_equal(2, g:n_to_c) + call assert_equal(2, g:c_to_n) + unlet g:n_to_c + unlet g:c_to_n + + let g:n_to_v = 0 + au ModeChanged n:v let g:n_to_v += 1 + let g:v_to_n = 0 + au ModeChanged v:n let g:v_to_n += 1 + let g:mode_seq += ['v', 'n'] + call feedkeys("v\<C-C>", 'tnix') + call assert_equal(len(g:mode_seq) - 1, g:index) + call assert_equal(1, g:n_to_v) + call assert_equal(1, g:v_to_n) + unlet g:n_to_v + unlet g:v_to_n au! ModeChanged delfunc TestMode @@ -3100,4 +3525,48 @@ func Test_noname_autocmd() augroup! test_noname_autocmd_group endfunc +func Test_autocmd_split_dummy() + " Autocommand trying to split a window containing a dummy buffer. + auto BufReadPre * exe "sbuf " .. expand("<abuf>") + " Avoid the "W11" prompt + au FileChangedShell * let v:fcs_choice = 'reload' + func Xautocmd_changelist() + cal writefile(['Xtestfile2:4:4'], 'Xerr') + edit Xerr + lex 'Xtestfile2:4:4' + endfunc + call Xautocmd_changelist() + " Should get E86, but it doesn't always happen (timing?) + silent! call Xautocmd_changelist() + + au! BufReadPre + au! FileChangedShell + delfunc Xautocmd_changelist + bwipe! Xerr + call delete('Xerr') +endfunc + +" This was crashing because there was only one window to execute autocommands +" in. +func Test_autocmd_nested_setbufvar() + CheckFeature python3 + + set hidden + edit Xaaa + edit Xbbb + call setline(1, 'bar') + enew + au BufWriteCmd Xbbb ++nested call setbufvar('Xaaa', '&ft', 'foo') | bw! Xaaa + au FileType foo call py3eval('vim.current.buffer.options["cindent"]') + wall + + au! BufWriteCmd + au! FileType foo + set nohidden + call delete('Xaaa') + call delete('Xbbb') + %bwipe! +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_autoload.vim b/src/nvim/testdir/test_autoload.vim index b8c4fa251f..e89fe3943b 100644 --- a/src/nvim/testdir/test_autoload.vim +++ b/src/nvim/testdir/test_autoload.vim @@ -10,6 +10,9 @@ func Test_autoload_dict_func() call assert_equal(1, g:called_foo_bar_echo) eval 'bar'->g:foo#addFoo()->assert_equal('barfoo') + + " empty name works in legacy script + call assert_equal('empty', foo#()) endfunc func Test_source_autoload() @@ -17,3 +20,6 @@ func Test_source_autoload() source sautest/autoload/sourced.vim call assert_equal(1, g:loaded_sourced_vim) endfunc + + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_backup.vim b/src/nvim/testdir/test_backup.vim index ce2bfe72bc..d304773da4 100644 --- a/src/nvim/testdir/test_backup.vim +++ b/src/nvim/testdir/test_backup.vim @@ -1,5 +1,7 @@ " Tests for the backup function +source check.vim + func Test_backup() set backup backupdir=. backupskip= new @@ -17,6 +19,22 @@ func Test_backup() call delete('Xbackup.txt~') endfunc +func Test_backup_backupskip() + set backup backupdir=. backupskip=*.txt + new + call setline(1, ['line1', 'line2']) + :f Xbackup.txt + :w! Xbackup.txt + " backup file is only created after + " writing a second time (before overwriting) + :w! Xbackup.txt + call assert_false(filereadable('Xbackup.txt~')) + bw! + set backup&vim backupdir&vim backupskip&vim + call delete('Xbackup.txt') + call delete('Xbackup.txt~') +endfunc + func Test_backup2() set backup backupdir=.// backupskip= new @@ -28,7 +46,7 @@ func Test_backup2() :w! Xbackup.txt sp *Xbackup.txt~ call assert_equal(['line1', 'line2', 'line3'], getline(1,'$')) - let f=expand('%') + let f = expand('%') call assert_match('%testdir%Xbackup.txt\~', f) bw! bw! @@ -48,7 +66,7 @@ func Test_backup2_backupcopy() :w! Xbackup.txt sp *Xbackup.txt~ call assert_equal(['line1', 'line2', 'line3'], getline(1,'$')) - let f=expand('%') + let f = expand('%') call assert_match('%testdir%Xbackup.txt\~', f) bw! bw! @@ -56,3 +74,16 @@ func Test_backup2_backupcopy() call delete(f) set backup&vim backupdir&vim backupcopy&vim backupskip&vim endfunc + +" Test for using a non-existing directory as a backup directory +func Test_non_existing_backupdir() + throw 'Skipped: Nvim auto-creates backup directory' + set backupdir=./non_existing_dir backupskip= + call writefile(['line1'], 'Xfile') + new Xfile + call assert_fails('write', 'E510:') + set backupdir&vim backupskip&vim + call delete('Xfile') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_blob.vim b/src/nvim/testdir/test_blob.vim index af42b3857d..046acb81e1 100644 --- a/src/nvim/testdir/test_blob.vim +++ b/src/nvim/testdir/test_blob.vim @@ -88,6 +88,7 @@ func Test_blob_get_range() call assert_equal(0z0011223344, b[:]) call assert_equal(0z0011223344, b[:-1]) call assert_equal(0z, b[5:6]) + call assert_equal(0z0011, b[-10:1]) endfunc func Test_blob_get() @@ -208,6 +209,7 @@ func Test_blob_add() call assert_equal(0z001122, b) call add(b, '51') call assert_equal(0z00112233, b) + call assert_equal(1, add(v:_null_blob, 0x22)) call assert_fails('call add(b, [9])', 'E745:') call assert_fails('call add("", 0x01)', 'E897:') @@ -250,6 +252,7 @@ func Test_blob_func_remove() call assert_fails("call remove(b, 3, 2)", 'E979:') call assert_fails("call remove(1, 0)", 'E896:') call assert_fails("call remove(b, b)", 'E974:') + call assert_fails("call remove(b, 1, [])", 'E745:') call assert_fails("call remove(v:_null_blob, 1, 2)", 'E979:') " Translated from v8.2.3284 @@ -272,6 +275,7 @@ endfunc " filter() item in blob func Test_blob_filter() + call assert_equal(v:_null_blob, filter(v:_null_blob, '0')) call assert_equal(0z, filter(0zDEADBEEF, '0')) call assert_equal(0zADBEEF, filter(0zDEADBEEF, 'v:val != 0xDE')) call assert_equal(0zDEADEF, filter(0zDEADBEEF, 'v:val != 0xBE')) @@ -294,9 +298,12 @@ func Test_blob_index() call assert_equal(2, index(0zDEADBEEF, 0xBE)) call assert_equal(-1, index(0zDEADBEEF, 0)) call assert_equal(2, index(0z11111111, 0x11, 2)) - call assert_equal(3, index(0z11110111, 0x11, 2)) + call assert_equal(3, 0z11110111->index(0x11, 2)) call assert_equal(2, index(0z11111111, 0x11, -2)) call assert_equal(3, index(0z11110111, 0x11, -2)) + call assert_equal(0, index(0z11110111, 0x11, -10)) + call assert_fails("echo index(0z11110111, 0x11, [])", 'E745:') + call assert_equal(-1, index(v:_null_blob, 1)) call assert_fails('call index("asdf", 0)', 'E897:') endfunc @@ -313,6 +320,9 @@ func Test_blob_insert() call assert_fails('call insert(b, -1)', 'E475:') call assert_fails('call insert(b, 257)', 'E475:') call assert_fails('call insert(b, 0, [9])', 'E745:') + call assert_fails('call insert(b, 0, -20)', 'E475:') + call assert_fails('call insert(b, 0, 20)', 'E475:') + call assert_fails('call insert(b, [])', 'E745:') call assert_equal(0, insert(v:_null_blob, 0x33)) " Translated from v8.2.3284 diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim index a37751e748..2e377aa434 100644 --- a/src/nvim/testdir/test_breakindent.vim +++ b/src/nvim/testdir/test_breakindent.vim @@ -4,11 +4,11 @@ " while the test is run, the breakindent caching gets in its way. " It helps to change the tabstop setting and force a redraw (e.g. see " Test_breakindent08()) -if !exists('+breakindent') - throw 'Skipped: breakindent option not supported' -endif +source check.vim +CheckOption breakindent source view_util.vim +source screendump.vim let s:input ="\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP" @@ -422,6 +422,7 @@ func Test_breakindent11() let width = strlen(text[1:]) + indent(2) + strlen(&sbr) * 3 " text wraps 3 times call assert_equal(width, strdisplaywidth(text)) call s:close_windows('set sbr=') + call assert_equal(4, strdisplaywidth("\t", 4)) endfunc func Test_breakindent11_vartabs() @@ -855,7 +856,7 @@ func Test_breakindent20_list() " check formatlistpat indent with different list level " showbreak and sbr - setl briopt=min:5,sbr,list:-1,shift:2 + setl briopt=min:5,sbr,list:-1 setl showbreak=> redraw! let expect = [ @@ -868,6 +869,44 @@ func Test_breakindent20_list() \ ] let lines = s:screen_lines2(1, 6, 20) call s:compare_lines(expect, lines) + + " check formatlistpat indent with different list level + " showbreak sbr and shift + setl briopt=min:5,sbr,list:-1,shift:2 + setl showbreak=> + redraw! + let expect = [ + \ "* Congress shall ", + \ "> make no law ", + \ "*** Congress shall ", + \ "> make no law ", + \ "**** Congress shall ", + \ "> make no law ", + \ ] + let lines = s:screen_lines2(1, 6, 20) + call s:compare_lines(expect, lines) + + " check breakindent works if breakindentopt=list:-1 + " for a non list content + %delete _ + call setline(1, [' Congress shall make no law', + \ ' Congress shall make no law', + \ ' Congress shall make no law']) + norm! 1gg + setl briopt=min:5,list:-1 + setl showbreak= + redraw! + let expect = [ + \ " Congress shall ", + \ " make no law ", + \ " Congress shall ", + \ " make no law ", + \ " Congress shall ", + \ " make no law ", + \ ] + let lines = s:screen_lines2(1, 6, 20) + call s:compare_lines(expect, lines) + call s:close_windows('set breakindent& briopt& linebreak& list& listchars& showbreak&') endfunc @@ -876,17 +915,164 @@ endfunc func Test_window_resize_with_linebreak() new 53vnew - set linebreak - set showbreak=>> - set breakindent - set breakindentopt=shift:4 + setl linebreak + setl showbreak=>> + setl breakindent + setl breakindentopt=shift:4 call setline(1, "\naaaaaaaaa\n\na\naaaaa\n¯aaaaaaaaaa\naaaaaaaaaaaa\naaa\n\"a:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaa\"\naaaaaaaa\n\"a") redraw! call assert_equal([" >>aa^@\"a: "], ScreenLines(2, 14)) vertical resize 52 redraw! call assert_equal([" >>aaa^@\"a:"], ScreenLines(2, 14)) + set linebreak& showbreak& breakindent& breakindentopt& %bw! endfunc +func Test_cursor_position_with_showbreak() + CheckScreendump + + let lines =<< trim END + vim9script + &signcolumn = 'yes' + &showbreak = '+ ' + var leftcol: number = win_getid()->getwininfo()->get(0, {})->get('textoff') + repeat('x', &columns - leftcol - 1)->setline(1) + 'second line'->setline(2) + END + call writefile(lines, 'XscriptShowbreak') + let buf = RunVimInTerminal('-S XscriptShowbreak', #{rows: 6}) + + call term_sendkeys(buf, "AX") + call VerifyScreenDump(buf, 'Test_cursor_position_with_showbreak', {}) + + call StopVimInTerminal(buf) + call delete('XscriptShowbreak') +endfunc + +func Test_no_spurious_match() + let s:input = printf('- y %s y %s', repeat('x', 50), repeat('x', 50)) + call s:test_windows('setl breakindent breakindentopt=list:-1 formatlistpat=^- hls') + let @/ = '\%>3v[y]' + redraw! + call searchcount().total->assert_equal(1) + " cleanup + set hls&vim + let s:input = "\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP" + bwipeout! +endfunc + +func Test_no_extra_indent() + call s:test_windows('setl breakindent breakindentopt=list:-1,min:10') + %d + let &l:formatlistpat='^\s*\d\+\.\s\+' + let text = 'word ' + let len = text->strcharlen() + let line1 = text->repeat((winwidth(0) / len) * 2) + let line2 = repeat(' ', 2) .. '1. ' .. line1 + call setline(1, [line2]) + redraw! + " 1) matches formatlist pattern, so indent + let expect = [ + \ " 1. word word word ", + \ " word word word ", + \ " word word ", + \ "~ ", + \ ] + let lines = s:screen_lines2(1, 4, 20) + call s:compare_lines(expect, lines) + " 2) change formatlist pattern + " -> indent adjusted + let &l:formatlistpat='^\s*\d\+\.' + let expect = [ + \ " 1. word word word ", + \ " word word word ", + \ " word word ", + \ "~ ", + \ ] + let lines = s:screen_lines2(1, 4, 20) + " 3) no local formatlist pattern, + " so use global one -> indent + let g_flp = &g:flp + let &g:formatlistpat='^\s*\d\+\.\s\+' + let &l:formatlistpat='' + let expect = [ + \ " 1. word word word ", + \ " word word word ", + \ " word word ", + \ "~ ", + \ ] + let lines = s:screen_lines2(1, 4, 20) + call s:compare_lines(expect, lines) + let &g:flp = g_flp + let &l:formatlistpat='^\s*\d\+\.' + " 4) add something in front, no additional indent + norm! gg0 + exe ":norm! 5iword \<esc>" + redraw! + let expect = [ + \ "word word word word ", + \ "word 1. word word ", + \ "word word word word ", + \ "word word ", + \ "~ ", + \ ] + let lines = s:screen_lines2(1, 5, 20) + call s:compare_lines(expect, lines) + bwipeout! +endfunc + +func Test_breakindent_column() + " restore original + let s:input ="\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP" + call s:test_windows('setl breakindent breakindentopt=column:10') + redraw! + " 1) default: does not indent, too wide :( + let expect = [ + \ " ", + \ " abcdefghijklmnop", + \ "qrstuvwxyzABCDEFGHIJ", + \ "KLMNOP " + \ ] + let lines = s:screen_lines2(1, 4, 20) + call s:compare_lines(expect, lines) + " 2) lower min value, so that breakindent works + setl breakindentopt+=min:5 + redraw! + let expect = [ + \ " ", + \ " abcdefghijklmnop", + \ " qrstuvwxyz", + \ " ABCDEFGHIJ", + \ " KLMNOP " + \ ] + let lines = s:screen_lines2(1, 5, 20) + " 3) set shift option -> no influence + setl breakindentopt+=shift:5 + redraw! + let expect = [ + \ " ", + \ " abcdefghijklmnop", + \ " qrstuvwxyz", + \ " ABCDEFGHIJ", + \ " KLMNOP " + \ ] + let lines = s:screen_lines2(1, 5, 20) + call s:compare_lines(expect, lines) + " 4) add showbreak value + setl showbreak=++ + redraw! + let expect = [ + \ " ", + \ " abcdefghijklmnop", + \ " ++qrstuvwx", + \ " ++yzABCDEF", + \ " ++GHIJKLMN", + \ " ++OP " + \ ] + let lines = s:screen_lines2(1, 6, 20) + call s:compare_lines(expect, lines) + bwipeout! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_buffer.vim b/src/nvim/testdir/test_buffer.vim index 67be3e6747..9220cc17b9 100644 --- a/src/nvim/testdir/test_buffer.vim +++ b/src/nvim/testdir/test_buffer.vim @@ -67,16 +67,7 @@ func Test_bunload_with_offset() call assert_fails('1,4bunload', 'E16:') call assert_fails(',100bunload', 'E16:') - " Use a try-catch for this test. When assert_fails() is used for this - " test, the command fails with E515: instead of E90: - let caught_E90 = 0 - try - $bunload - catch /E90:/ - let caught_E90 = 1 - endtry - call assert_equal(1, caught_E90) - call assert_fails('$bunload', 'E515:') + call assert_fails('$bunload', 'E90:') endfunc " Test for :buffer, :bnext, :bprevious, :brewind, :blast and :bmodified @@ -147,6 +138,7 @@ func Test_bdelete_cmd() %bwipe! call assert_fails('bdelete 5', 'E516:') call assert_fails('1,1bdelete 1 2', 'E488:') + call assert_fails('bdelete \)', 'E55:') " Deleting a unlisted and unloaded buffer edit Xfile1 @@ -154,6 +146,24 @@ func Test_bdelete_cmd() set nobuflisted enew call assert_fails('bdelete ' .. bnr, 'E516:') + + " Deleting more than one buffer + new Xbuf1 + new Xbuf2 + exe 'bdel ' .. bufnr('Xbuf2') .. ' ' .. bufnr('Xbuf1') + call assert_equal(1, winnr('$')) + call assert_equal(0, getbufinfo('Xbuf1')[0].loaded) + call assert_equal(0, getbufinfo('Xbuf2')[0].loaded) + + " Deleting more than one buffer and an invalid buffer + new Xbuf1 + new Xbuf2 + let cmd = "exe 'bdel ' .. bufnr('Xbuf2') .. ' xxx ' .. bufnr('Xbuf1')" + call assert_fails(cmd, 'E94:') + call assert_equal(2, winnr('$')) + call assert_equal(1, getbufinfo('Xbuf1')[0].loaded) + call assert_equal(0, getbufinfo('Xbuf2')[0].loaded) + %bwipe! endfunc @@ -168,6 +178,194 @@ func Test_buffer_error() %bwipe endfunc +" Test for the status messages displayed when unloading, deleting or wiping +" out buffers +func Test_buffer_statusmsg() + CheckEnglish + set report=1 + new Xbuf1 + new Xbuf2 + let bnr = bufnr() + exe "normal 2\<C-G>" + call assert_match('buf ' .. bnr .. ':', v:statusmsg) + bunload Xbuf1 Xbuf2 + call assert_equal('2 buffers unloaded', v:statusmsg) + bdel Xbuf1 Xbuf2 + call assert_equal('2 buffers deleted', v:statusmsg) + bwipe Xbuf1 Xbuf2 + call assert_equal('2 buffers wiped out', v:statusmsg) + set report& +endfunc + +" Test for quitting the 'swapfile exists' dialog with the split buffer +" command. +func Test_buffer_sbuf_cleanup() + call writefile([], 'Xfile') + " first open the file in a buffer + new Xfile + let bnr = bufnr() + close + " create the swap file + call writefile([], '.Xfile.swp') + " Remove the catch-all that runtest.vim adds + au! SwapExists + augroup BufTest + au! + autocmd SwapExists Xfile let v:swapchoice='q' + augroup END + exe 'sbuf ' . bnr + call assert_equal(1, winnr('$')) + call assert_equal(0, getbufinfo('Xfile')[0].loaded) + + " test for :sball + sball + call assert_equal(1, winnr('$')) + call assert_equal(0, getbufinfo('Xfile')[0].loaded) + + %bw! + set shortmess+=F + let v:statusmsg = '' + edit Xfile + call assert_equal('', v:statusmsg) + call assert_equal(1, winnr('$')) + call assert_equal(0, getbufinfo('Xfile')[0].loaded) + set shortmess& + + call delete('Xfile') + call delete('.Xfile.swp') + augroup BufTest + au! + augroup END + augroup! BufTest +endfunc + +" Test for deleting a modified buffer with :confirm +func Test_bdel_with_confirm() + " requires a UI to be active + throw 'Skipped: use test/functional/legacy/buffer_spec.lua' + CheckUnix + CheckNotGui + CheckFeature dialog_con + new + call setline(1, 'test') + call assert_fails('bdel', 'E89:') + call feedkeys('c', 'L') + confirm bdel + call assert_equal(2, winnr('$')) + call assert_equal(1, &modified) + call feedkeys('n', 'L') + confirm bdel + call assert_equal(1, winnr('$')) +endfunc + +" Test for editing another buffer from a modified buffer with :confirm +func Test_goto_buf_with_confirm() + " requires a UI to be active + throw 'Skipped: use test/functional/legacy/buffer_spec.lua' + CheckUnix + CheckNotGui + CheckFeature dialog_con + new Xfile + enew + call setline(1, 'test') + call assert_fails('b Xfile', 'E37:') + call feedkeys('c', 'L') + call assert_fails('confirm b Xfile', 'E37:') + call assert_equal(1, &modified) + call assert_equal('', @%) + call feedkeys('y', 'L') + call assert_fails('confirm b Xfile', ['', 'E37:']) + call assert_equal(1, &modified) + call assert_equal('', @%) + call feedkeys('n', 'L') + confirm b Xfile + call assert_equal('Xfile', @%) + close! +endfunc + +" Test for splitting buffer with 'switchbuf' +func Test_buffer_switchbuf() + new Xfile + wincmd w + set switchbuf=useopen + sbuf Xfile + call assert_equal(1, winnr()) + call assert_equal(2, winnr('$')) + set switchbuf=usetab + tabnew + sbuf Xfile + call assert_equal(1, tabpagenr()) + call assert_equal(2, tabpagenr('$')) + set switchbuf& + %bw +endfunc + +" Test for BufAdd autocommand wiping out the buffer +func Test_bufadd_autocmd_bwipe() + %bw! + augroup BufAdd_Wipe + au! + autocmd BufAdd Xfile %bw! + augroup END + edit Xfile + call assert_equal('', @%) + call assert_equal(0, bufexists('Xfile')) + augroup BufAdd_Wipe + au! + augroup END + augroup! BufAdd_Wipe +endfunc + +" Test for trying to load a buffer with text locked +" <C-\>e in the command line is used to lock the text +func Test_load_buf_with_text_locked() + new Xfile1 + edit Xfile2 + let cmd = ":\<C-\>eexecute(\"normal \<C-O>\")\<CR>\<C-C>" + call assert_fails("call feedkeys(cmd, 'xt')", 'E565:') + %bw! +endfunc + +" Test for using CTRL-^ to edit the alternative file keeping the cursor +" position with 'nostartofline'. Also test using the 'buf' command. +func Test_buffer_edit_altfile() + call writefile(repeat(['one two'], 50), 'Xfile1') + call writefile(repeat(['five six'], 50), 'Xfile2') + set nosol + edit Xfile1 + call cursor(25, 5) + edit Xfile2 + call cursor(30, 4) + exe "normal \<C-^>" + call assert_equal([0, 25, 5, 0], getpos('.')) + exe "normal \<C-^>" + call assert_equal([0, 30, 4, 0], getpos('.')) + buf Xfile1 + call assert_equal([0, 25, 5, 0], getpos('.')) + buf Xfile2 + call assert_equal([0, 30, 4, 0], getpos('.')) + set sol& + call delete('Xfile1') + call delete('Xfile2') +endfunc + +" Test for running the :sball command with a maximum window count and a +" modified buffer +func Test_sball_with_count() + %bw! + edit Xfile1 + call setline(1, ['abc']) + new Xfile2 + new Xfile3 + new Xfile4 + 2sball + call assert_equal(bufnr('Xfile4'), winbufnr(1)) + call assert_equal(bufnr('Xfile1'), winbufnr(2)) + call assert_equal(0, getbufinfo('Xfile2')[0].loaded) + call assert_equal(0, getbufinfo('Xfile3')[0].loaded) + %bw! +endfunc + func Test_badd_options() new SomeNewBuffer setlocal numberwidth=3 @@ -195,12 +393,12 @@ func Test_buffer_scheme() set noshellslash %bwipe! let bufnames = [ - \ #{id: 'b0', name: 'test://xyz/foo/b0' , match: 1}, - \ #{id: 'b1', name: 'test+abc://xyz/foo/b1', match: 0}, - \ #{id: 'b2', name: 'test_abc://xyz/foo/b2', match: 0}, - \ #{id: 'b3', name: 'test-abc://xyz/foo/b3', match: 1}, - \ #{id: 'b4', name: '-test://xyz/foo/b4' , match: 0}, - \ #{id: 'b5', name: 'test-://xyz/foo/b5' , match: 0}, + \ #{id: 'ssb0', name: 'test://xyz/foo/ssb0' , match: 1}, + \ #{id: 'ssb1', name: 'test+abc://xyz/foo/ssb1', match: 0}, + \ #{id: 'ssb2', name: 'test_abc://xyz/foo/ssb2', match: 0}, + \ #{id: 'ssb3', name: 'test-abc://xyz/foo/ssb3', match: 1}, + \ #{id: 'ssb4', name: '-test://xyz/foo/ssb4' , match: 0}, + \ #{id: 'ssb5', name: 'test-://xyz/foo/ssb5' , match: 0}, \] for buf in bufnames new `=buf.name` @@ -225,6 +423,32 @@ func Test_buf_pattern_invalid() vsplit 00000000000000000000000000 silent! buf [0--]\&\zs*\zs*e bwipe! + + " similar case with different code path + split 0 + edit ÿ + silent! buf [0--]\&\zs*\zs*0 + bwipe! +endfunc + +" Test for the 'maxmem' and 'maxmemtot' options +func Test_buffer_maxmem() + " use 1KB per buffer and 2KB for all the buffers + " set maxmem=1 maxmemtot=2 + new + let v:errmsg = '' + " try opening some files + edit test_arglist.vim + call assert_equal('test_arglist.vim', bufname()) + edit test_eval_stuff.vim + call assert_equal('test_eval_stuff.vim', bufname()) + b test_arglist.vim + call assert_equal('test_arglist.vim', bufname()) + b test_eval_stuff.vim + call assert_equal('test_eval_stuff.vim', bufname()) + close + call assert_equal('', v:errmsg) + " set maxmem& maxmemtot& endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_bufline.vim b/src/nvim/testdir/test_bufline.vim index 579d3a5eb5..0468025f55 100644 --- a/src/nvim/testdir/test_bufline.vim +++ b/src/nvim/testdir/test_bufline.vim @@ -2,14 +2,18 @@ source shared.vim source screendump.vim +source check.vim func Test_setbufline_getbufline() + " similar to Test_set_get_bufline() new let b = bufnr('%') hide call assert_equal(0, setbufline(b, 1, ['foo', 'bar'])) call assert_equal(['foo'], getbufline(b, 1)) + call assert_equal('foo', getbufoneline(b, 1)) call assert_equal(['bar'], getbufline(b, '$')) + call assert_equal('bar', getbufoneline(b, '$')) call assert_equal(['foo', 'bar'], getbufline(b, 1, 2)) exe "bd!" b call assert_equal([], getbufline(b, 1, 2)) @@ -18,13 +22,36 @@ func Test_setbufline_getbufline() call setline(1, ['a', 'b', 'c']) let b = bufnr('%') wincmd w + + call assert_equal(1, setbufline(b, 5, 'x')) call assert_equal(1, setbufline(b, 5, ['x'])) - call assert_equal(1, setbufline(bufnr('$') + 1, 1, ['x'])) + call assert_equal(1, setbufline(b, 5, [])) + call assert_equal(1, setbufline(b, 5, v:_null_list)) + + call assert_equal(1, 'x'->setbufline(bufnr('$') + 1, 1)) + call assert_equal(1, ['x']->setbufline(bufnr('$') + 1, 1)) + call assert_equal(1, []->setbufline(bufnr('$') + 1, 1)) + call assert_equal(1, v:_null_list->setbufline(bufnr('$') + 1, 1)) + + call assert_equal(['a', 'b', 'c'], getbufline(b, 1, '$')) + call assert_equal(0, setbufline(b, 4, ['d', 'e'])) call assert_equal(['c'], b->getbufline(3)) + call assert_equal('c', b->getbufoneline(3)) call assert_equal(['d'], getbufline(b, 4)) + call assert_equal('d', getbufoneline(b, 4)) call assert_equal(['e'], getbufline(b, 5)) + call assert_equal('e', getbufoneline(b, 5)) call assert_equal([], getbufline(b, 6)) + call assert_equal([], getbufline(b, 2, 1)) + + if has('job') + call setbufline(b, 2, [function('eval'), #{key: 123}, test_null_job()]) + call assert_equal(["function('eval')", + \ "{'key': 123}", + \ "no process"], + \ getbufline(b, 2, 4)) + endif exe "bwipe! " . b endfunc @@ -71,20 +98,53 @@ func Test_appendbufline() new let b = bufnr('%') hide + + new + call setline(1, ['line1', 'line2', 'line3']) + normal! 2gggg + call assert_equal(2, line("''")) + call assert_equal(0, appendbufline(b, 0, ['foo', 'bar'])) call assert_equal(['foo'], getbufline(b, 1)) call assert_equal(['bar'], getbufline(b, 2)) call assert_equal(['foo', 'bar'], getbufline(b, 1, 2)) + call assert_equal(0, appendbufline(b, 0, 'baz')) + call assert_equal(['baz', 'foo', 'bar'], getbufline(b, 1, 3)) + + " appendbufline() in a hidden buffer shouldn't move marks in current window. + call assert_equal(2, line("''")) + bwipe! + exe "bd!" b - call assert_equal([], getbufline(b, 1, 2)) + call assert_equal([], getbufline(b, 1, 3)) split Xtest call setline(1, ['a', 'b', 'c']) let b = bufnr('%') wincmd w + + call assert_equal(1, appendbufline(b, -1, 'x')) call assert_equal(1, appendbufline(b, -1, ['x'])) + call assert_equal(1, appendbufline(b, -1, [])) + call assert_equal(1, appendbufline(b, -1, v:_null_list)) + + call assert_equal(1, appendbufline(b, 4, 'x')) call assert_equal(1, appendbufline(b, 4, ['x'])) + call assert_equal(1, appendbufline(b, 4, [])) + call assert_equal(1, appendbufline(b, 4, v:_null_list)) + + call assert_equal(1, appendbufline(1234, 1, 'x')) call assert_equal(1, appendbufline(1234, 1, ['x'])) + call assert_equal(1, appendbufline(1234, 1, [])) + call assert_equal(1, appendbufline(1234, 1, v:_null_list)) + + call assert_equal(0, appendbufline(b, 1, [])) + call assert_equal(0, appendbufline(b, 1, v:_null_list)) + call assert_equal(1, appendbufline(b, 3, [])) + call assert_equal(1, appendbufline(b, 3, v:_null_list)) + + call assert_equal(['a', 'b', 'c'], getbufline(b, 1, '$')) + call assert_equal(0, appendbufline(b, 3, ['d', 'e'])) call assert_equal(['c'], getbufline(b, 3)) call assert_equal(['d'], getbufline(b, 4)) @@ -98,10 +158,21 @@ func Test_deletebufline() let b = bufnr('%') call setline(1, ['aaa', 'bbb', 'ccc']) hide + + new + call setline(1, ['line1', 'line2', 'line3']) + normal! 2gggg + call assert_equal(2, line("''")) + call assert_equal(0, deletebufline(b, 2)) call assert_equal(['aaa', 'ccc'], getbufline(b, 1, 2)) call assert_equal(0, deletebufline(b, 2, 8)) call assert_equal(['aaa'], getbufline(b, 1, 2)) + + " deletebufline() in a hidden buffer shouldn't move marks in current window. + call assert_equal(2, line("''")) + bwipe! + exe "bd!" b call assert_equal(1, b->deletebufline(1)) @@ -130,9 +201,8 @@ func Test_deletebufline() endfunc func Test_appendbufline_redraw() - if !CanRunVimInTerminal() - throw 'Skipped: cannot make screendumps' - endif + CheckScreendump + let lines =<< trim END new foo let winnr = 'foo'->bufwinnr() @@ -187,4 +257,40 @@ func Test_deletebufline_select_mode() bwipe! endfunc +func Test_setbufline_startup_nofile() + let before =<< trim [CODE] + set shortmess+=F + file Xresult + set buftype=nofile + call setbufline('', 1, 'success') + [CODE] + let after =<< trim [CODE] + set buftype= + write + quit + [CODE] + + if !RunVim(before, after, '--clean') + return + endif + call assert_equal(['success'], readfile('Xresult')) + call delete('Xresult') +endfunc + +" Test that setbufline(), appendbufline() and deletebufline() should fail and +" return 1 when "textlock" is active. +func Test_change_bufline_with_textlock() + new + inoremap <buffer> <expr> <F2> setbufline('', 1, '') + call assert_fails("normal a\<F2>", 'E565:') + call assert_equal('1', getline(1)) + inoremap <buffer> <expr> <F2> appendbufline('', 1, '') + call assert_fails("normal a\<F2>", 'E565:') + call assert_equal('11', getline(1)) + inoremap <buffer> <expr> <F2> deletebufline('', 1) + call assert_fails("normal a\<F2>", 'E565:') + call assert_equal('111', getline(1)) + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_cd.vim b/src/nvim/testdir/test_cd.vim index a1e53df774..2b37f2c7c0 100644 --- a/src/nvim/testdir/test_cd.vim +++ b/src/nvim/testdir/test_cd.vim @@ -5,7 +5,7 @@ source check.vim func Test_cd_large_path() " This used to crash with a heap write overflow. - call assert_fails('cd ' . repeat('x', 5000), 'E472:') + call assert_fails('cd ' . repeat('x', 5000), 'E344:') endfunc func Test_cd_up_and_down() @@ -45,9 +45,7 @@ func Test_cd_minus() call assert_equal(path, getcwd()) " Test for :cd - after a failed :cd - " v8.2.1183 is not ported yet - " call assert_fails('cd /nonexistent', 'E344:') - call assert_fails('cd /nonexistent', 'E472:') + call assert_fails('cd /nonexistent', 'E344:') call assert_equal(path, getcwd()) cd - call assert_equal(path_dotdot, getcwd()) @@ -68,30 +66,6 @@ func Test_cd_minus() call delete('Xresult') endfunc -func Test_cd_with_cpo_chdir() - e Xfoo - call setline(1, 'foo') - let path = getcwd() - " set cpo+=. - - " :cd should fail when buffer is modified and 'cpo' contains dot. - " call assert_fails('cd ..', 'E747:') - call assert_equal(path, getcwd()) - - " :cd with exclamation mark should succeed. - cd! .. - call assert_notequal(path, getcwd()) - - " :cd should succeed when buffer has been written. - w! - exe 'cd ' .. fnameescape(path) - call assert_equal(path, getcwd()) - - call delete('Xfoo') - set cpo& - bw! -endfunc - " Test for chdir() func Test_chdir_func() let topdir = getcwd() @@ -113,7 +87,7 @@ func Test_chdir_func() call assert_equal('z', fnamemodify(3->getcwd(2), ':t')) tabnext | wincmd t call assert_match('^\[tabpage\] .*/y$', trim(execute('verbose pwd'))) - call chdir('..') + eval '..'->chdir() call assert_equal('Xdir', fnamemodify(getcwd(1, 2), ':t')) call assert_equal('Xdir', fnamemodify(getcwd(2, 2), ':t')) call assert_equal('z', fnamemodify(getcwd(3, 2), ':t')) @@ -127,7 +101,7 @@ func Test_chdir_func() call assert_equal('testdir', fnamemodify(getcwd(1, 1), ':t')) " Error case - call assert_fails("call chdir('dir-abcd')", 'E472:') + call assert_fails("call chdir('dir-abcd')", 'E344:') silent! let d = chdir("dir_abcd") call assert_equal("", d) " Should not crash @@ -259,6 +233,8 @@ endfunc func Test_getcwd_actual_dir() CheckFunction test_autochdir + CheckOption autochdir + let startdir = getcwd() call mkdir('Xactual') call test_autochdir() diff --git a/src/nvim/testdir/test_charsearch.vim b/src/nvim/testdir/test_charsearch.vim index d386d74f8d..54e0a62ce5 100644 --- a/src/nvim/testdir/test_charsearch.vim +++ b/src/nvim/testdir/test_charsearch.vim @@ -43,36 +43,6 @@ func Test_charsearch() enew! endfunc -" Test for t,f,F,T movement commands and 'cpo-;' setting -func Test_search_cmds() - enew! - call append(0, ["aaa two three four", " zzz", "yyy ", - \ "bbb yee yoo four", "ccc two three four", - \ "ddd yee yoo four"]) - set cpo-=; - 1 - normal! 0tt;D - 2 - normal! 0fz;D - 3 - normal! $Fy;D - 4 - normal! $Ty;D - set cpo+=; - 5 - normal! 0tt;;D - 6 - normal! $Ty;;D - - call assert_equal('aaa two', getline(1)) - call assert_equal(' z', getline(2)) - call assert_equal('y', getline(3)) - call assert_equal('bbb y', getline(4)) - call assert_equal('ccc', getline(5)) - call assert_equal('ddd yee y', getline(6)) - enew! -endfunc - " Test for character search in virtual edit mode with <Tab> func Test_csearch_virtualedit() new @@ -81,7 +51,7 @@ func Test_csearch_virtualedit() normal! tb call assert_equal([0, 1, 2, 6], getpos('.')) set virtualedit& - close! + bw! endfunc " Test for character search failure in latin1 encoding @@ -95,7 +65,34 @@ func Test_charsearch_latin1() call assert_beeps('normal $Fz') call assert_beeps('normal $Tx') let &encoding = save_enc - close! + bw! +endfunc + +" Test for using character search to find a multibyte character with composing +" characters. +func Test_charsearch_composing_char() + new + call setline(1, "one two thq\u0328\u0301r\u0328\u0301ree") + call feedkeys("fr\u0328\u0301", 'xt') + call assert_equal([0, 1, 16, 0, 12], getcurpos()) + + " use character search with a multi-byte character followed by a + " non-composing character + call setline(1, "abc deȉf ghi") + call feedkeys("ggcf\u0209\u0210", 'xt') + call assert_equal("\u0210f ghi", getline(1)) + bw! +endfunc + +" Test for character search with 'hkmap' +func Test_charsearch_hkmap() + new + set hkmap + call setline(1, "ùðáâ÷ëòéïçìêöî") + call feedkeys("fë", 'xt') + call assert_equal([0, 1, 11, 0, 6], getcurpos()) + set hkmap& + bw! endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_checkpath.vim b/src/nvim/testdir/test_checkpath.vim index eff30cf205..f6a3bbce08 100644 --- a/src/nvim/testdir/test_checkpath.vim +++ b/src/nvim/testdir/test_checkpath.vim @@ -102,3 +102,20 @@ func Test_checkpath3() set include& set includeexpr& endfunc + +" Test for invalid regex in 'include' and 'define' options +func Test_checkpath_errors() + let save_include = &include + set include=\\%( + call assert_fails('checkpath', 'E53:') + let &include = save_include + + let save_define = &define + set define=\\%( + call assert_fails('dsearch abc', 'E53:') + let &define = save_define + + call assert_fails('psearch \%(', 'E53:') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_clientserver.vim b/src/nvim/testdir/test_clientserver.vim index edf36b413b..d8b29f6c42 100644 --- a/src/nvim/testdir/test_clientserver.vim +++ b/src/nvim/testdir/test_clientserver.vim @@ -2,15 +2,18 @@ source check.vim CheckFeature job + +if !has('clientserver') + call assert_fails('call remote_startserver("local")', 'E942:') +endif + CheckFeature clientserver source shared.vim func Check_X11_Connection() if has('x11') - if empty($DISPLAY) - throw 'Skipped: $DISPLAY is not set' - endif + CheckEnv DISPLAY try call remote_send('xxx', '') catch @@ -59,15 +62,16 @@ func Test_client_server() " the GUI and check that the remote command still works. " Need to wait for the GUI to start up, otherwise the send hangs in trying " to send to the terminal window. - if has('gui_athena') || has('gui_motif') - " For those GUIs, ignore the 'failed to create input context' error. + if has('gui_motif') + " For this GUI ignore the 'failed to create input context' error. call remote_send(name, ":call test_ignore_error('E285') | gui -f\<CR>") else call remote_send(name, ":gui -f\<CR>") endif " Wait for the server to be up and answering requests. - sleep 100m - call WaitForAssert({-> assert_true(name->remote_expr("v:version", "", 1) != "")}) + " When using valgrind this can be very, very slow. + sleep 1 + call WaitForAssert({-> assert_match('\d', name->remote_expr("v:version", "", 1))}, 10000) call remote_send(name, ":let testvar = 'maybe'\<CR>") call WaitForAssert({-> assert_equal('maybe', remote_expr(name, "testvar", "", 2))}) @@ -83,6 +87,7 @@ func Test_client_server() call writefile(['one', 'two'], 'Xclientfile') call system(cmd) call WaitForAssert({-> assert_equal('two', remote_expr(name, "getline(2)", "", 2))}) + call delete('Xclientfile') " Expression evaluated locally. if v:servername == '' @@ -96,7 +101,7 @@ func Test_client_server() call remote_send(v:servername, ":let g:testvar2 = 75\<CR>") call feedkeys('', 'x') call assert_equal(75, g:testvar2) - call assert_fails('let v = remote_expr(v:servername, "/2")', 'E449:') + call assert_fails('let v = remote_expr(v:servername, "/2")', ['E15:.*/2']) call remote_send(name, ":call server2client(expand('<client>'), 'got it')\<CR>", 'g:myserverid') call assert_equal('got it', g:myserverid->remote_read(2)) @@ -136,19 +141,19 @@ func Test_client_server() " Edit multiple files using --remote call system(cmd .. ' --remote Xfile1 Xfile2 Xfile3') - call assert_equal("Xfile1\nXfile2\nXfile3\n", remote_expr(name, 'argv()')) + call assert_match(".*Xfile1\n.*Xfile2\n.*Xfile3\n", remote_expr(name, 'argv()')) eval name->remote_send(":%bw!\<CR>") " Edit files in separate tab pages call system(cmd .. ' --remote-tab Xfile1 Xfile2 Xfile3') - call assert_equal('3', remote_expr(name, 'tabpagenr("$")')) - call assert_equal('Xfile2', remote_expr(name, 'bufname(tabpagebuflist(2)[0])')) + call WaitForAssert({-> assert_equal('3', remote_expr(name, 'tabpagenr("$")'))}) + call assert_match('.*\<Xfile2', remote_expr(name, 'bufname(tabpagebuflist(2)[0])')) eval name->remote_send(":%bw!\<CR>") " Edit a file using --remote-wait eval name->remote_send(":source $VIMRUNTIME/plugin/rrhelper.vim\<CR>") call system(cmd .. ' --remote-wait +enew Xfile1') - call assert_equal("Xfile1", remote_expr(name, 'bufname("#")')) + call assert_match('.*\<Xfile1', remote_expr(name, 'bufname("#")')) eval name->remote_send(":%bw!\<CR>") " Edit files using --remote-tab-wait @@ -176,8 +181,12 @@ func Test_client_server() endif endtry + call assert_fails('call remote_startserver([])', 'E730:') call assert_fails("let x = remote_peek([])", 'E730:') - call assert_fails("let x = remote_read('vim10')", 'E277:') + call assert_fails("let x = remote_read('vim10')", + \ has('unix') ? ['E573:.*vim10'] : 'E277:') + call assert_fails("call server2client('abc', 'xyz')", + \ has('unix') ? ['E573:.*abc'] : 'E258:') endfunc " Uncomment this line to get a debugging log diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 35886d42c5..cf1d56ae38 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -3,10 +3,24 @@ source check.vim source screendump.vim source view_util.vim +source shared.vim + +func SetUp() + func SaveLastScreenLine() + let g:Sline = Screenline(&lines - 1) + return '' + endfunc + cnoremap <expr> <F4> SaveLastScreenLine() +endfunc + +func TearDown() + delfunc SaveLastScreenLine + cunmap <F4> +endfunc func Test_complete_tab() call writefile(['testfile'], 'Xtestfile') - call feedkeys(":e Xtestf\t\r", "tx") + call feedkeys(":e Xtest\t\r", "tx") call assert_equal('testfile', getline(1)) " Pressing <Tab> after '%' completes the current file, also on MS-Windows @@ -24,6 +38,47 @@ func Test_complete_list() " used. call feedkeys(":chistory \<C-D>\<C-B>\"\<CR>", 'xt') call assert_equal("\"chistory \<C-D>", @:) + + " Test for displaying the tail of the completion matches + set wildmode=longest,full + call mkdir('Xtest') + call writefile([], 'Xtest/a.c') + call writefile([], 'Xtest/a.h') + let g:Sline = '' + call feedkeys(":e Xtest/\<C-D>\<F4>\<C-B>\"\<CR>", 'xt') + call assert_equal('a.c a.h', g:Sline) + call assert_equal('"e Xtest/', @:) + if has('win32') + " Test for 'completeslash' + set completeslash=backslash + call feedkeys(":e Xtest\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e Xtest\', @:) + call feedkeys(":e Xtest/\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e Xtest\a.', @:) + set completeslash=slash + call feedkeys(":e Xtest\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e Xtest/', @:) + call feedkeys(":e Xtest\\\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e Xtest/a.', @:) + set completeslash& + endif + + " Test for displaying the tail with wildcards + let g:Sline = '' + call feedkeys(":e Xtes?/\<C-D>\<F4>\<C-B>\"\<CR>", 'xt') + call assert_equal('Xtest/a.c Xtest/a.h', g:Sline) + call assert_equal('"e Xtes?/', @:) + let g:Sline = '' + call feedkeys(":e Xtes*/\<C-D>\<F4>\<C-B>\"\<CR>", 'xt') + call assert_equal('Xtest/a.c Xtest/a.h', g:Sline) + call assert_equal('"e Xtes*/', @:) + let g:Sline = '' + call feedkeys(":e Xtes[/\<C-D>\<F4>\<C-B>\"\<CR>", 'xt') + call assert_equal(':e Xtes[/', g:Sline) + call assert_equal('"e Xtes[/', @:) + + call delete('Xtest', 'rf') + set wildmode& endfunc func Test_complete_wildmenu() @@ -88,14 +143,25 @@ func Test_complete_wildmenu() call assert_equal('"e Xtestfile3 Xtestfile4', @:) cd - + " test for wildmenumode() + cnoremap <expr> <F2> wildmenumode() + call feedkeys(":cd Xdir\<Tab>\<F2>\<C-B>\"\<CR>", 'tx') + call assert_equal('"cd Xdir1/0', @:) + call feedkeys(":e Xdir1/\<Tab>\<F2>\<C-B>\"\<CR>", 'tx') + call assert_equal('"e Xdir1/Xdir2/1', @:) + cunmap <F2> + + " Test for canceling the wild menu by pressing <PageDown> or <PageUp>. + " After this pressing <Left> or <Right> should not change the selection. + call feedkeys(":sign \<Tab>\<PageDown>\<Left>\<Right>\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"sign define', @:) + call histadd('cmd', 'TestWildMenu') + call feedkeys(":sign \<Tab>\<PageUp>\<Left>\<Right>\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"TestWildMenu', @:) + " cleanup %bwipe - call delete('Xdir1/Xdir2/Xtestfile4') - call delete('Xdir1/Xdir2/Xtestfile3') - call delete('Xdir1/Xtestfile2') - call delete('Xdir1/Xtestfile1') - call delete('Xdir1/Xdir2', 'd') - call delete('Xdir1', 'd') + call delete('Xdir1', 'rf') set nowildmenu endfunc @@ -126,13 +192,74 @@ func Test_wildmenu_screendump() call delete('XTest_wildmenu') endfunc +func Test_redraw_in_autocmd() + CheckScreendump + + let lines =<< trim END + set cmdheight=2 + autocmd CmdlineChanged * redraw + END + call writefile(lines, 'XTest_redraw', 'D') + + let buf = RunVimInTerminal('-S XTest_redraw', {'rows': 8}) + call term_sendkeys(buf, ":for i in range(3)\<CR>") + call VerifyScreenDump(buf, 'Test_redraw_in_autocmd_1', {}) + + call term_sendkeys(buf, "let i =") + call VerifyScreenDump(buf, 'Test_redraw_in_autocmd_2', {}) + + " clean up + call term_sendkeys(buf, "\<CR>") + call StopVimInTerminal(buf) +endfunc + +func Test_redrawstatus_in_autocmd() + CheckScreendump + + let lines =<< trim END + set laststatus=2 + set statusline=%=:%{getcmdline()} + autocmd CmdlineChanged * redrawstatus + END + call writefile(lines, 'XTest_redrawstatus', 'D') + + let buf = RunVimInTerminal('-S XTest_redrawstatus', {'rows': 8}) + " :redrawstatus is postponed if messages have scrolled + call term_sendkeys(buf, ":echo \"one\\ntwo\\nthree\\nfour\"\<CR>") + call term_sendkeys(buf, ":foobar") + call VerifyScreenDump(buf, 'Test_redrawstatus_in_autocmd_1', {}) + " it is not postponed if messages have not scrolled + call term_sendkeys(buf, "\<Esc>:for in in range(3)") + call VerifyScreenDump(buf, 'Test_redrawstatus_in_autocmd_2', {}) + " with cmdheight=1 messages have scrolled when typing :endfor + call term_sendkeys(buf, "\<CR>:endfor") + call VerifyScreenDump(buf, 'Test_redrawstatus_in_autocmd_3', {}) + call term_sendkeys(buf, "\<CR>:set cmdheight=2\<CR>") + " with cmdheight=2 messages haven't scrolled when typing :for or :endfor + call term_sendkeys(buf, ":for in in range(3)") + call VerifyScreenDump(buf, 'Test_redrawstatus_in_autocmd_4', {}) + call term_sendkeys(buf, "\<CR>:endfor") + call VerifyScreenDump(buf, 'Test_redrawstatus_in_autocmd_5', {}) + + " clean up + call term_sendkeys(buf, "\<CR>") + call StopVimInTerminal(buf) +endfunc + func Test_changing_cmdheight() CheckScreendump let lines =<< trim END set cmdheight=1 laststatus=2 + func EchoTwo() + set laststatus=2 + set cmdheight=5 + echo 'foo' + echo 'bar' + set cmdheight=1 + endfunc END - call writefile(lines, 'XTest_cmdheight') + call writefile(lines, 'XTest_cmdheight', 'D') let buf = RunVimInTerminal('-S XTest_cmdheight', {'rows': 8}) call term_sendkeys(buf, ":resize -3\<CR>") @@ -150,20 +277,30 @@ func Test_changing_cmdheight() call term_sendkeys(buf, ":set cmdheight-=2\<CR>") call VerifyScreenDump(buf, 'Test_changing_cmdheight_4', {}) - " reducing window size and then setting cmdheight + " reducing window size and then setting cmdheight call term_sendkeys(buf, ":resize -1\<CR>") call term_sendkeys(buf, ":set cmdheight=1\<CR>") call VerifyScreenDump(buf, 'Test_changing_cmdheight_5', {}) + " setting 'cmdheight' works after outputting two messages + call term_sendkeys(buf, ":call EchoTwo()\<CR>") + call VerifyScreenDump(buf, 'Test_changing_cmdheight_6', {}) + + " clean up + call StopVimInTerminal(buf) +endfunc + +func Test_cmdheight_tabline() + CheckScreendump + + let buf = RunVimInTerminal('-c "set ls=2" -c "set stal=2" -c "set cmdheight=1"', {'rows': 6}) + call VerifyScreenDump(buf, 'Test_cmdheight_tabline_1', {}) + " clean up call StopVimInTerminal(buf) - call delete('XTest_cmdheight') endfunc func Test_map_completion() - if !has('cmdline_compl') - return - endif call feedkeys(":map <unique> <si\<Tab>\<Home>\"\<CR>", 'xt') call assert_equal('"map <unique> <silent>', getreg(':')) call feedkeys(":map <script> <un\<Tab>\<Home>\"\<CR>", 'xt') @@ -234,29 +371,30 @@ func Test_map_completion() unmap <Left> " set cpo-=k + call assert_fails('call feedkeys(":map \\\\%(\<Tab>\<Home>\"\<CR>", "xt")', 'E53:') + unmap <Middle>x set cpo&vim endfunc func Test_match_completion() - if !has('cmdline_compl') - return - endif hi Aardig ctermfg=green - call feedkeys(":match \<Tab>\<Home>\"\<CR>", 'xt') - call assert_equal('"match Aardig', getreg(':')) + " call feedkeys(":match \<Tab>\<Home>\"\<CR>", 'xt') + call feedkeys(":match A\<Tab>\<Home>\"\<CR>", 'xt') + call assert_equal('"match Aardig', @:) call feedkeys(":match \<S-Tab>\<Home>\"\<CR>", 'xt') - call assert_equal('"match none', getreg(':')) + call assert_equal('"match none', @:) + call feedkeys(":match | chist\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"match | chistory', @:) endfunc func Test_highlight_completion() - if !has('cmdline_compl') - return - endif hi Aardig ctermfg=green - call feedkeys(":hi \<Tab>\<Home>\"\<CR>", 'xt') + " call feedkeys(":hi \<Tab>\<Home>\"\<CR>", 'xt') + call feedkeys(":hi A\<Tab>\<Home>\"\<CR>", 'xt') call assert_equal('"hi Aardig', getreg(':')) - call feedkeys(":hi default \<Tab>\<Home>\"\<CR>", 'xt') + " call feedkeys(":hi default \<Tab>\<Home>\"\<CR>", 'xt') + call feedkeys(":hi default A\<Tab>\<Home>\"\<CR>", 'xt') call assert_equal('"hi default Aardig', getreg(':')) call feedkeys(":hi clear Aa\<Tab>\<Home>\"\<CR>", 'xt') call assert_equal('"hi clear Aardig', getreg(':')) @@ -276,39 +414,7 @@ func Test_highlight_completion() call assert_equal([], getcompletion('A', 'highlight')) endfunc -func Test_expr_completion() - if !has('cmdline_compl') - return - endif - for cmd in [ - \ 'let a = ', - \ 'const a = ', - \ 'if', - \ 'elseif', - \ 'while', - \ 'for', - \ 'echo', - \ 'echon', - \ 'execute', - \ 'echomsg', - \ 'echoerr', - \ 'call', - \ 'return', - \ 'cexpr', - \ 'caddexpr', - \ 'cgetexpr', - \ 'lexpr', - \ 'laddexpr', - \ 'lgetexpr'] - call feedkeys(":" . cmd . " getl\<Tab>\<Home>\"\<CR>", 'xt') - call assert_equal('"' . cmd . ' getline(', getreg(':')) - endfor -endfunc - func Test_getcompletion() - if !has('cmdline_compl') - return - endif let groupcount = len(getcompletion('', 'event')) call assert_true(groupcount > 0) let matchcount = len('File'->getcompletion('event')) @@ -323,6 +429,7 @@ func Test_getcompletion() call assert_true(matchcount > 0) let matchcount = len(getcompletion('File.', 'menu')) call assert_true(matchcount > 0) + source $VIMRUNTIME/delmenu.vim endif let l = getcompletion('v:n', 'var') @@ -333,6 +440,8 @@ func Test_getcompletion() args a.c b.c let l = getcompletion('', 'arglist') call assert_equal(['a.c', 'b.c'], l) + let l = getcompletion('a.', 'buffer') + call assert_equal(['a.c'], l) %argdelete let l = getcompletion('', 'augroup') @@ -388,6 +497,11 @@ func Test_getcompletion() let l = getcompletion('run', 'file', 1) call assert_true(index(l, 'runtest.vim') < 0) set wildignore& + " Directory name with space character + call mkdir('Xdir with space') + call assert_equal(['Xdir with space/'], getcompletion('Xdir\ w', 'shellcmd')) + call assert_equal(['./Xdir with space/'], getcompletion('./Xdir', 'shellcmd')) + call delete('Xdir with space', 'd') let l = getcompletion('ha', 'filetype') call assert_true(index(l, 'hamster') >= 0) @@ -462,11 +576,17 @@ func Test_getcompletion() call assert_equal(cmds, l) let l = getcompletion('list ', 'sign') call assert_equal(['Testing'], l) + let l = getcompletion('de*', 'sign') + call assert_equal(['define'], l) + let l = getcompletion('p?', 'sign') + call assert_equal(['place'], l) + let l = getcompletion('j.', 'sign') + call assert_equal(['jump'], l) endif " Command line completion tests let l = getcompletion('cd ', 'cmdline') - call assert_true(index(l, 'sautest/') >= 0) + call assert_true(index(l, 'samples/') >= 0) let l = getcompletion('cd NoMatch', 'cmdline') call assert_equal([], l) let l = getcompletion('let v:n', 'cmdline') @@ -514,10 +634,39 @@ func Test_getcompletion() call delete('Xtags') set tags& + edit a~b + enew + call assert_equal(['a~b'], getcompletion('a~', 'buffer')) + bw a~b + + if has('unix') + edit Xtest\ + enew + call assert_equal(['Xtest\'], getcompletion('Xtest\', 'buffer')) + bw Xtest\ + endif + + call assert_fails("call getcompletion('\\\\@!\\\\@=', 'buffer')", 'E871:') call assert_fails('call getcompletion("", "burp")', 'E475:') call assert_fails('call getcompletion("abc", [])', 'E475:') endfunc +" Test for getcompletion() with "fuzzy" in 'wildoptions' +func Test_getcompletion_wildoptions() + let save_wildoptions = &wildoptions + set wildoptions& + let l = getcompletion('space', 'option') + call assert_equal([], l) + let l = getcompletion('ier', 'command') + call assert_equal([], l) + set wildoptions=fuzzy + let l = getcompletion('space', 'option') + call assert_true(index(l, 'backspace') >= 0) + let l = getcompletion('ier', 'command') + call assert_true(index(l, 'compiler') >= 0) + let &wildoptions = save_wildoptions +endfunc + func Test_fullcommand() let tests = { \ '': '', @@ -590,7 +739,7 @@ func Test_expand_star_star() call mkdir('a/b', 'p') call writefile(['asdfasdf'], 'a/b/fileXname') call feedkeys(":find **/fileXname\<Tab>\<CR>", 'xt') - call assert_equal('find a/b/fileXname', getreg(':')) + call assert_equal('find a/b/fileXname', @:) bwipe! call delete('a', 'rf') endfunc @@ -738,7 +887,31 @@ func Test_cmdline_complete_user_cmd() call assert_equal('"Foo blue', @:) call feedkeys(":Foo b\<Tab>\<Home>\"\<cr>", 'tx') call assert_equal('"Foo blue', @:) + call feedkeys(":Foo a b\<Tab>\<Home>\"\<cr>", 'tx') + call assert_equal('"Foo a blue', @:) + call feedkeys(":Foo b\\\<Tab>\<Home>\"\<cr>", 'tx') + call assert_equal('"Foo b\', @:) + call feedkeys(":Foo b\\x\<Tab>\<Home>\"\<cr>", 'tx') + call assert_equal('"Foo b\x', @:) delcommand Foo + + redraw + call assert_equal('~', Screenline(&lines - 1)) + command! FooOne : + command! FooTwo : + + set nowildmenu + call feedkeys(":Foo\<Tab>\<Home>\"\<cr>", 'tx') + call assert_equal('"FooOne', @:) + call assert_equal('~', Screenline(&lines - 1)) + + call feedkeys(":Foo\<S-Tab>\<Home>\"\<cr>", 'tx') + call assert_equal('"FooTwo', @:) + call assert_equal('~', Screenline(&lines - 1)) + + delcommand FooOne + delcommand FooTwo + set wildmenu& endfunc func Test_complete_user_cmd() @@ -809,7 +982,7 @@ func Test_cmdline_complete_bang() endif endfunc -funct Test_cmdline_complete_languages() +func Test_cmdline_complete_languages() let lang = substitute(execute('language time'), '.*"\(.*\)"$', '\1', '') call assert_equal(lang, v:lc_time) @@ -846,10 +1019,8 @@ endfunc func Test_cmdline_complete_env_variable() let $X_VIM_TEST_COMPLETE_ENV = 'foo' - call feedkeys(":edit $X_VIM_TEST_COMPLETE_E\<C-A>\<C-B>\"\<CR>", 'tx') call assert_match('"edit $X_VIM_TEST_COMPLETE_ENV', @:) - unlet $X_VIM_TEST_COMPLETE_ENV endfunc @@ -926,6 +1097,10 @@ func Test_cmdline_complete_various() call feedkeys(":all abc\<C-A>\<C-B>\"\<CR>", 'xt') call assert_equal("\"all abc\<C-A>", @:) + " completion for :wincmd with :horizontal modifier + call feedkeys(":horizontal wincm\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"horizontal wincmd", @:) + " completion for a command with a command modifier call feedkeys(":topleft new\<C-A>\<C-B>\"\<CR>", 'xt') call assert_equal("\"topleft new", @:) @@ -934,14 +1109,6 @@ func Test_cmdline_complete_various() call feedkeys(":match Search /pat/\<C-A>\<C-B>\"\<CR>", 'xt') call assert_equal("\"match Search /pat/\<C-A>", @:) - " completion for the :s command - call feedkeys(":s/from/to/g\<C-A>\<C-B>\"\<CR>", 'xt') - call assert_equal("\"s/from/to/g\<C-A>", @:) - - " completion for the :dlist command - call feedkeys(":dlist 10 /pat/ a\<C-A>\<C-B>\"\<CR>", 'xt') - call assert_equal("\"dlist 10 /pat/ a\<C-A>", @:) - " completion for the :doautocmd command call feedkeys(":doautocmd User MyCmd a.c\<C-A>\<C-B>\"\<CR>", 'xt') call assert_equal("\"doautocmd User MyCmd a.c\<C-A>", @:) @@ -1001,7 +1168,7 @@ func Test_cmdline_complete_various() map <F3> :ls<CR> com -nargs=* -complete=mapping MyCmd call feedkeys(":MyCmd <F\<C-A>\<C-B>\"\<CR>", 'xt') - call assert_equal('"MyCmd <F3>', @:) + call assert_equal('"MyCmd <F3> <F4>', @:) mapclear delcom MyCmd @@ -1025,9 +1192,77 @@ func Test_cmdline_complete_various() call feedkeys(":e `a1b2c\t\<C-B>\"\<CR>", 'xt') call assert_equal('"e `a1b2c', @:) - " completion for the expression register - call feedkeys(":\"\<C-R>=float2\t\"\<C-B>\"\<CR>", 'xt') - call assert_equal('"float2nr("', @=) + " completion for :language command with an invalid argument + call feedkeys(":language dummy \t\<C-B>\"\<CR>", 'xt') + call assert_equal("\"language dummy \t", @:) + + " completion for commands after a :global command + call feedkeys(":g/a\\xb/clearj\t\<C-B>\"\<CR>", 'xt') + call assert_equal('"g/a\xb/clearjumps', @:) + + " completion with ambiguous user defined commands + com TCmd1 echo 'TCmd1' + com TCmd2 echo 'TCmd2' + call feedkeys(":TCmd \t\<C-B>\"\<CR>", 'xt') + call assert_equal('"TCmd ', @:) + delcom TCmd1 + delcom TCmd2 + + " completion after a range followed by a pipe (|) character + call feedkeys(":1,10 | chist\t\<C-B>\"\<CR>", 'xt') + call assert_equal('"1,10 | chistory', @:) + + " completion after a :global command + call feedkeys(":g/a/chist\t\<C-B>\"\<CR>", 'xt') + call assert_equal('"g/a/chistory', @:) + call feedkeys(":g/a\\/chist\t\<C-B>\"\<CR>", 'xt') + call assert_equal("\"g/a\\/chist\t", @:) + + " use <Esc> as the 'wildchar' for completion + set wildchar=<Esc> + call feedkeys(":g/a\\xb/clearj\<Esc>\<C-B>\"\<CR>", 'xt') + call assert_equal('"g/a\xb/clearjumps', @:) + " pressing <esc> twice should cancel the command + call feedkeys(":chist\<Esc>\<Esc>", 'xt') + call assert_equal('"g/a\xb/clearjumps', @:) + set wildchar& + + if has('unix') + " should be able to complete a file name that starts with a '~'. + call writefile([], '~Xtest') + call feedkeys(":e \\~X\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e \~Xtest', @:) + call delete('~Xtest') + + " should be able to complete a file name that has a '*' + call writefile([], 'Xx*Yy') + call feedkeys(":e Xx\*\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e Xx\*Yy', @:) + call delete('Xx*Yy') + + " use a literal star + call feedkeys(":e \\*\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e \*', @:) + endif + + call feedkeys(":py3f\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"py3file', @:) +endfunc + +" Test for 'wildignorecase' +func Test_cmdline_wildignorecase() + CheckUnix + call writefile([], 'XTEST') + set wildignorecase + call feedkeys(":e xt\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e XTEST', @:) + call assert_equal(['XTEST'], getcompletion('xt', 'file')) + let g:Sline = '' + call feedkeys(":e xt\<C-d>\<F4>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e xt', @:) + call assert_equal('XTEST', g:Sline) + set wildignorecase& + call delete('XTEST') endfunc func Test_cmdline_write_alternatefile() @@ -1051,6 +1286,18 @@ func Test_cmdline_write_alternatefile() bw! endfunc +func Test_cmdline_expand_cur_alt_file() + enew + file http://some.com/file.txt + call feedkeys(":e %\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e http://some.com/file.txt', @:) + edit another + call feedkeys(":e #\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e http://some.com/file.txt', @:) + bwipe + bwipe http://some.com/file.txt +endfunc + " using a leading backslash here set cpo+=C @@ -1072,7 +1319,7 @@ func Test_cmdline_search_range() call assert_equal('B', getline(2)) let @/ = 'apple' - call assert_fails('\/print', 'E486:') + call assert_fails('\/print', ['E486:.*apple']) bwipe! endfunc @@ -1082,7 +1329,7 @@ func Test_tick_mark_in_range() " If only the tick is passed as a range and no command is specified, there " should not be an error call feedkeys(":'\<CR>", 'xt') - call assert_equal("'", getreg(':')) + call assert_equal("'", @:) call assert_fails("',print", 'E78:') endfunc @@ -1188,6 +1435,11 @@ func Test_verbosefile() let log = readfile('Xlog') call assert_match("foo\nbar", join(log, "\n")) call delete('Xlog') + call mkdir('Xdir') + if !has('win32') " FIXME: no error on Windows, libuv bug? + call assert_fails('set verbosefile=Xdir', ['E484:.*Xdir', 'E474:']) + endif + call delete('Xdir', 'd') endfunc func Test_verbose_option() @@ -1260,6 +1512,16 @@ func Test_cmdline_overstrike() let &encoding = encoding_save endfunc +func Test_cmdwin_bug() + let winid = win_getid() + sp + try + call feedkeys("q::call win_gotoid(" .. winid .. ")\<CR>:q\<CR>", 'x!') + catch /^Vim\%((\a\+)\)\=:E11/ + endtry + bw! +endfunc + func Test_cmdwin_restore() CheckScreendump @@ -1414,6 +1676,7 @@ func Test_cmdline_expand_special() call assert_fails('e <afile>', 'E495:') call assert_fails('e <abuf>', 'E496:') call assert_fails('e <amatch>', 'E497:') + call writefile([], 'Xfile.cpp') call writefile([], 'Xfile.java') new Xfile.cpp @@ -1428,7 +1691,7 @@ func Test_cmdwin_jump_to_win() call assert_fails('call feedkeys("q:\<C-W>\<C-W>\<CR>", "xt")', 'E11:') new set modified - call assert_fails('call feedkeys("q/:qall\<CR>", "xt")', 'E162:') + call assert_fails('call feedkeys("q/:qall\<CR>", "xt")', ['E37:', 'E162:']) close! call feedkeys("q/:close\<CR>", "xt") call assert_equal(1, winnr('$')) @@ -1442,16 +1705,36 @@ endfunc func Test_cmdwin_tabpage() tabedit - " v8.2.1919 isn't ported yet, so E492 is thrown after E11 here. - " v8.2.1183 also isn't ported yet, so we also can't assert E11 directly. - " For now, assert E11 and E492 separately. When v8.2.1183 is ported, the - " assert for E492 will fail and this workaround should be removed. - " call assert_fails("silent norm q/g :I\<Esc>", 'E11:') - call assert_fails("silent norm q/g ", 'E11:') - call assert_fails("silent norm q/g :I\<Esc>", 'E492:') + call assert_fails("silent norm q/g :I\<Esc>", 'E11:') tabclose! endfunc +func Test_cmdwin_interrupted() + CheckScreendump + + " aborting the :smile output caused the cmdline window to use the current + " buffer. + let lines =<< trim [SCRIPT] + au WinNew * smile + [SCRIPT] + call writefile(lines, 'XTest_cmdwin') + + let buf = RunVimInTerminal('-S XTest_cmdwin', {'rows': 18}) + " open cmdwin + call term_sendkeys(buf, "q:") + call WaitForAssert({-> assert_match('-- More --', term_getline(buf, 18))}) + " quit more prompt for :smile command + call term_sendkeys(buf, "q") + call WaitForAssert({-> assert_match('^$', term_getline(buf, 18))}) + " execute a simple command + call term_sendkeys(buf, "aecho 'done'\<CR>") + call VerifyScreenDump(buf, 'Test_cmdwin_interrupted', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('XTest_cmdwin') +endfunc + " Test for backtick expression in the command line func Test_cmd_backtick() CheckNotMSWindows " FIXME: see #19297 @@ -1508,6 +1791,53 @@ func Test_cmd_bang_E135() %bwipe! endfunc +func Test_cmd_bang_args() + new + :.! + call assert_equal(0, v:shell_error) + + " Note that below there is one space char after the '!'. This caused a + " shell error in the past, see https://github.com/vim/vim/issues/11495. + :.! + call assert_equal(0, v:shell_error) + bwipe! + + CheckUnix + :.!pwd + call assert_equal(0, v:shell_error) + :.! pwd + call assert_equal(0, v:shell_error) + + " Note there is one space after 'pwd'. + :.! pwd + call assert_equal(0, v:shell_error) + + " Note there are two spaces after 'pwd'. + :.! pwd + call assert_equal(0, v:shell_error) + :.!ls ~ + call assert_equal(0, v:shell_error) + + " Note there is one space char after '~'. + :.!ls ~ + call assert_equal(0, v:shell_error) + + " Note there are two spaces after '~'. + :.!ls ~ + call assert_equal(0, v:shell_error) + + :.!echo "foo" + call assert_equal(getline('.'), "foo") + :.!echo "foo " + call assert_equal(getline('.'), "foo ") + :.!echo " foo " + call assert_equal(getline('.'), " foo ") + :.!echo " foo " + call assert_equal(getline('.'), " foo ") + + %bwipe! +endfunc + " Test for using ~ for home directory in cmdline completion matches func Test_cmdline_expand_home() call mkdir('Xdir') @@ -1543,22 +1873,16 @@ func Test_cmdline_ctrl_g() endfunc " Test for 'wildmode' -func Test_wildmode() +func Wildmode_tests() func T(a, c, p) return "oneA\noneB\noneC" endfunc command -nargs=1 -complete=custom,T MyCmd - func SaveScreenLine() - let g:Sline = Screenline(&lines - 1) - return '' - endfunc - cnoremap <expr> <F2> SaveScreenLine() - set nowildmenu set wildmode=full,list let g:Sline = '' - call feedkeys(":MyCmd \t\t\<F2>\<C-B>\"\<CR>", 'xt') + call feedkeys(":MyCmd \t\t\<F4>\<C-B>\"\<CR>", 'xt') call assert_equal('oneA oneB oneC', g:Sline) call assert_equal('"MyCmd oneA', @:) @@ -1574,7 +1898,7 @@ func Test_wildmode() set wildmode=list:longest let g:Sline = '' - call feedkeys(":MyCmd \t\<F2>\<C-B>\"\<CR>", 'xt') + call feedkeys(":MyCmd \t\<F4>\<C-B>\"\<CR>", 'xt') call assert_equal('oneA oneB oneC', g:Sline) call assert_equal('"MyCmd one', @:) @@ -1585,27 +1909,67 @@ func Test_wildmode() " Test for wildmode=longest with 'fileignorecase' set set wildmode=longest set fileignorecase - argadd AA AAA AAAA - call feedkeys(":buffer \t\<C-B>\"\<CR>", 'xt') - call assert_equal('"buffer AA', @:) + argadd AAA AAAA AAAAA + call feedkeys(":buffer a\t\<C-B>\"\<CR>", 'xt') + call assert_equal('"buffer AAA', @:) set fileignorecase& " Test for listing files with wildmode=list set wildmode=list let g:Sline = '' - call feedkeys(":b A\t\t\<F2>\<C-B>\"\<CR>", 'xt') - call assert_equal('AA AAA AAAA', g:Sline) + call feedkeys(":b A\t\t\<F4>\<C-B>\"\<CR>", 'xt') + call assert_equal('AAA AAAA AAAAA', g:Sline) call assert_equal('"b A', @:) + " when using longest completion match, matches shorter than the argument + " should be ignored (happens with :help) + set wildmode=longest,full + set wildmenu + call feedkeys(":help a*\t\<C-B>\"\<CR>", 'xt') + call assert_equal('"help a', @:) + " non existing file + call feedkeys(":e a1b2y3z4\t\<C-B>\"\<CR>", 'xt') + call assert_equal('"e a1b2y3z4', @:) + set wildmenu& + + " Test for longest file name completion with 'fileignorecase' + " On MS-Windows, file names are case insensitive. + if has('unix') + call writefile([], 'XTESTfoo') + call writefile([], 'Xtestbar') + set nofileignorecase + call feedkeys(":e XT\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e XTESTfoo', @:) + call feedkeys(":e Xt\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e Xtestbar', @:) + set fileignorecase + call feedkeys(":e XT\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e Xtest', @:) + call feedkeys(":e Xt\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e Xtest', @:) + set fileignorecase& + call delete('XTESTfoo') + call delete('Xtestbar') + endif + %argdelete delcommand MyCmd delfunc T - delfunc SaveScreenLine - cunmap <F2> set wildmode& %bwipe! endfunc +func Test_wildmode() + " Test with utf-8 encoding + call Wildmode_tests() + + " Test with latin1 encoding + let save_encoding = &encoding + " set encoding=latin1 + " call Wildmode_tests() + let &encoding = save_encoding +endfunc + " Test for interrupting the command-line completion func Test_interrupt_compl() func F(lead, cmdl, p) @@ -1627,6 +1991,14 @@ func Test_interrupt_compl() endtry call assert_equal(1, interrupted) + let interrupted = 0 + try + call feedkeys(":Tcmd tw\<C-d>\<C-B>\"\<CR>", 'xt') + catch /^Vim:Interrupt$/ + let interrupted = 1 + endtry + call assert_equal(1, interrupted) + delcommand Tcmd delfunc F set wildmode& @@ -1683,6 +2055,11 @@ func Test_cmdline_expr() call assert_equal("\"e \<C-\>\<C-Y>", @:) endfunc +" This was making the insert position negative +func Test_cmdline_expr_register() + exe "sil! norm! ?\<C-\>e0\<C-R>0\<Esc>?\<C-\>e0\<CR>" +endfunc + " Test for 'imcmdline' and 'imsearch' " This test doesn't actually test the input method functionality. func Test_cmdline_inputmethod() @@ -1809,8 +2186,45 @@ func Test_read_shellcmd() endif endfunc +" Test for going up and down the directory tree using 'wildmenu' +func Test_wildmenu_dirstack() + CheckUnix + %bw! + call mkdir('Xdir1/dir2/dir3/dir4', 'p') + call writefile([], 'Xdir1/file1_1.txt') + call writefile([], 'Xdir1/file1_2.txt') + call writefile([], 'Xdir1/dir2/file2_1.txt') + call writefile([], 'Xdir1/dir2/file2_2.txt') + call writefile([], 'Xdir1/dir2/dir3/file3_1.txt') + call writefile([], 'Xdir1/dir2/dir3/file3_2.txt') + call writefile([], 'Xdir1/dir2/dir3/dir4/file4_1.txt') + call writefile([], 'Xdir1/dir2/dir3/dir4/file4_2.txt') + set wildmenu + + cd Xdir1/dir2/dir3/dir4 + call feedkeys(":e \<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e file4_1.txt', @:) + call feedkeys(":e \<Tab>\<Up>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e ../dir4/', @:) + call feedkeys(":e \<Tab>\<Up>\<Up>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e ../../dir3/', @:) + call feedkeys(":e \<Tab>\<Up>\<Up>\<Up>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e ../../../dir2/', @:) + call feedkeys(":e \<Tab>\<Up>\<Up>\<Down>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e ../../dir3/dir4/', @:) + call feedkeys(":e \<Tab>\<Up>\<Up>\<Down>\<Down>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e ../../dir3/dir4/file4_1.txt', @:) + cd - + call feedkeys(":e Xdir1/\<Tab>\<Down>\<Down>\<Down>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e Xdir1/dir2/dir3/dir4/file4_1.txt', @:) + + call delete('Xdir1', 'rf') + set wildmenu& +endfunc + " Test for recalling newer or older cmdline from history with <Up>, <Down>, -" <S-Up>, <S-Down>, <PageUp>, <PageDown>, <C-p>, or <C-n>. +" <S-Up>, <S-Down>, <PageUp>, <PageDown>, <kPageUp>, <kPageDown>, <C-p>, or +" <C-n>. func Test_recalling_cmdline() CheckFeature cmdline_hist @@ -1818,17 +2232,18 @@ func Test_recalling_cmdline() cnoremap <Plug>(save-cmdline) <Cmd>let g:cmdlines += [getcmdline()]<CR> let histories = [ - \ {'name': 'cmd', 'enter': ':', 'exit': "\<Esc>"}, - \ {'name': 'search', 'enter': '/', 'exit': "\<Esc>"}, - \ {'name': 'expr', 'enter': ":\<C-r>=", 'exit': "\<Esc>\<Esc>"}, - \ {'name': 'input', 'enter': ":call input('')\<CR>", 'exit': "\<CR>"}, + \ #{name: 'cmd', enter: ':', exit: "\<Esc>"}, + \ #{name: 'search', enter: '/', exit: "\<Esc>"}, + \ #{name: 'expr', enter: ":\<C-r>=", exit: "\<Esc>\<Esc>"}, + \ #{name: 'input', enter: ":call input('')\<CR>", exit: "\<CR>"}, "\ TODO: {'name': 'debug', ...} \] let keypairs = [ - \ {'older': "\<Up>", 'newer': "\<Down>", 'prefixmatch': v:true}, - \ {'older': "\<S-Up>", 'newer': "\<S-Down>", 'prefixmatch': v:false}, - \ {'older': "\<PageUp>", 'newer': "\<PageDown>", 'prefixmatch': v:false}, - \ {'older': "\<C-p>", 'newer': "\<C-n>", 'prefixmatch': v:false}, + \ #{older: "\<Up>", newer: "\<Down>", prefixmatch: v:true}, + \ #{older: "\<S-Up>", newer: "\<S-Down>", prefixmatch: v:false}, + \ #{older: "\<PageUp>", newer: "\<PageDown>", prefixmatch: v:false}, + \ #{older: "\<kPageUp>", newer: "\<kPageDown>", prefixmatch: v:false}, + \ #{older: "\<C-p>", newer: "\<C-n>", prefixmatch: v:false}, \] let prefix = 'vi' for h in histories @@ -1857,6 +2272,59 @@ func Test_recalling_cmdline() cunmap <Plug>(save-cmdline) endfunc +func Test_cmd_map_cmdlineChanged() + let g:log = [] + cnoremap <F1> l<Cmd><CR>s + augroup test + autocmd! + autocmd CmdlineChanged : let g:log += [getcmdline()] + augroup END + + call feedkeys(":\<F1>\<CR>", 'xt') + call assert_equal(['l', 'ls'], g:log) + + let @b = 'b' + cnoremap <F1> a<C-R>b + let g:log = [] + call feedkeys(":\<F1>\<CR>", 'xt') + call assert_equal(['a', 'ab'], g:log) + + unlet g:log + cunmap <F1> + augroup test + autocmd! + augroup END +endfunc + +" Test for the 'suffixes' option +func Test_suffixes_opt() + call writefile([], 'Xfile') + call writefile([], 'Xfile.c') + call writefile([], 'Xfile.o') + set suffixes= + call feedkeys(":e Xfi*\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e Xfile Xfile.c Xfile.o', @:) + call feedkeys(":e Xfi*\<Tab>\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e Xfile.c', @:) + set suffixes=.c + call feedkeys(":e Xfi*\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e Xfile Xfile.o Xfile.c', @:) + call feedkeys(":e Xfi*\<Tab>\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e Xfile.o', @:) + set suffixes=,, + call feedkeys(":e Xfi*\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e Xfile.c Xfile.o Xfile', @:) + call feedkeys(":e Xfi*\<Tab>\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"e Xfile.o', @:) + set suffixes& + " Test for getcompletion() with different patterns + call assert_equal(['Xfile', 'Xfile.c', 'Xfile.o'], getcompletion('Xfile', 'file')) + call assert_equal(['Xfile'], getcompletion('Xfile$', 'file')) + call delete('Xfile') + call delete('Xfile.c') + call delete('Xfile.o') +endfunc + " Test for using a popup menu for the command line completion matches " (wildoptions=pum) func Test_wildmenu_pum() @@ -1868,39 +2336,63 @@ func Test_wildmenu_pum() set shm+=I set noruler set noshowcmd + + func CmdCompl(a, b, c) + return repeat(['aaaa'], 120) + endfunc + command -nargs=* -complete=customlist,CmdCompl Tcmd + + func MyStatusLine() abort + return 'status' + endfunc + func SetupStatusline() + set statusline=%!MyStatusLine() + set laststatus=2 + endfunc + + func MyTabLine() + return 'my tab line' + endfunc + func SetupTabline() + set statusline= + set tabline=%!MyTabLine() + set showtabline=2 + endfunc + + func DoFeedKeys() + let &wildcharm = char2nr("\t") + call feedkeys(":edit $VIMRUNTIME/\<Tab>\<Left>\<C-U>ab\<Tab>") + endfunc [CODE] call writefile(commands, 'Xtest') let buf = RunVimInTerminal('-S Xtest', #{rows: 10}) call term_sendkeys(buf, ":sign \<Tab>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_01', {}) + " going down the popup menu using <Down> call term_sendkeys(buf, "\<Down>\<Down>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_02', {}) + " going down the popup menu using <C-N> call term_sendkeys(buf, "\<C-N>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_03', {}) + " going up the popup menu using <C-P> call term_sendkeys(buf, "\<C-P>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_04', {}) + " going up the popup menu using <Up> call term_sendkeys(buf, "\<Up>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_05', {}) " pressing <C-E> should end completion and go back to the original match call term_sendkeys(buf, "\<C-E>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_06', {}) " pressing <C-Y> should select the current match and end completion call term_sendkeys(buf, "\<Tab>\<C-P>\<C-P>\<C-Y>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_07', {}) " With 'wildmode' set to 'longest,full', completing a match should display @@ -1908,31 +2400,25 @@ func Test_wildmenu_pum() call term_sendkeys(buf, ":\<C-U>set wildmode=longest,full\<CR>") call TermWait(buf) call term_sendkeys(buf, ":sign u\<Tab>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_08', {}) " pressing <Tab> should display the wildmenu call term_sendkeys(buf, "\<Tab>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_09', {}) " pressing <Tab> second time should select the next entry in the menu call term_sendkeys(buf, "\<Tab>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_10', {}) call term_sendkeys(buf, ":\<C-U>set wildmode=full\<CR>") - " " showing popup menu in different columns in the cmdline + " showing popup menu in different columns in the cmdline call term_sendkeys(buf, ":sign define \<Tab>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_11', {}) call term_sendkeys(buf, " \<Tab>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_12', {}) call term_sendkeys(buf, " \<Tab>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_13', {}) " Directory name completion @@ -1942,95 +2428,77 @@ func Test_wildmenu_pum() call writefile([], 'Xdir/XdirA/XdirB/XfileC') call term_sendkeys(buf, "\<C-U>e Xdi\<Tab>\<Tab>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_14', {}) " Pressing <Right> on a directory name should go into that directory call term_sendkeys(buf, "\<Right>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_15', {}) " Pressing <Left> on a directory name should go to the parent directory call term_sendkeys(buf, "\<Left>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_16', {}) " Pressing <C-A> when the popup menu is displayed should list all the - " matches and remove the popup menu + " matches but the popup menu should still remain call term_sendkeys(buf, "\<C-U>sign \<Tab>\<C-A>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_17', {}) " Pressing <C-D> when the popup menu is displayed should remove the popup " menu call term_sendkeys(buf, "\<C-U>sign \<Tab>\<C-D>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_18', {}) " Pressing <S-Tab> should open the popup menu with the last entry selected call term_sendkeys(buf, "\<C-U>\<CR>:sign \<S-Tab>\<C-P>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_19', {}) " Pressing <Esc> should close the popup menu and cancel the cmd line call term_sendkeys(buf, "\<C-U>\<CR>:sign \<Tab>\<Esc>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_20', {}) " Typing a character when the popup is open, should close the popup call term_sendkeys(buf, ":sign \<Tab>x") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_21', {}) " When the popup is open, entering the cmdline window should close the popup call term_sendkeys(buf, "\<C-U>sign \<Tab>\<C-F>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_22', {}) call term_sendkeys(buf, ":q\<CR>") " After the last popup menu item, <C-N> should show the original string call term_sendkeys(buf, ":sign u\<Tab>\<C-N>\<C-N>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_23', {}) " Use the popup menu for the command name call term_sendkeys(buf, "\<C-U>bu\<Tab>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_24', {}) " Pressing the left arrow should remove the popup menu call term_sendkeys(buf, "\<Left>\<Left>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_25', {}) " Pressing <BS> should remove the popup menu and erase the last character call term_sendkeys(buf, "\<C-E>\<C-U>sign \<Tab>\<BS>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_26', {}) " Pressing <C-W> should remove the popup menu and erase the previous word call term_sendkeys(buf, "\<C-E>\<C-U>sign \<Tab>\<C-W>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_27', {}) " Pressing <C-U> should remove the popup menu and erase the entire line call term_sendkeys(buf, "\<C-E>\<C-U>sign \<Tab>\<C-U>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_28', {}) " Using <C-E> to cancel the popup menu and then pressing <Up> should recall " the cmdline from history call term_sendkeys(buf, "sign xyz\<Esc>:sign \<Tab>\<C-E>\<Up>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_29', {}) " Check "list" still works call term_sendkeys(buf, "\<C-U>set wildmode=longest,list\<CR>") call term_sendkeys(buf, ":cn\<Tab>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_30', {}) call term_sendkeys(buf, "s") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_31', {}) " Tests a directory name contained full-width characters. @@ -2041,15 +2509,933 @@ func Test_wildmenu_pum() call term_sendkeys(buf, "\<C-U>set wildmode&\<CR>") call term_sendkeys(buf, ":\<C-U>e Xdir/あいう/\<Tab>") - call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_32', {}) + " Pressing <C-A> when the popup menu is displayed should list all the + " matches and pressing a key after that should remove the popup menu + call term_sendkeys(buf, "\<C-U>set wildmode=full\<CR>") + call term_sendkeys(buf, ":sign \<Tab>\<C-A>x") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_33', {}) + + " Pressing <C-A> when the popup menu is displayed should list all the + " matches and pressing <Left> after that should move the cursor + call term_sendkeys(buf, "\<C-U>abc\<Esc>") + call term_sendkeys(buf, ":sign \<Tab>\<C-A>\<Left>") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_34', {}) + + " When <C-A> displays a lot of matches (screen scrolls), all the matches + " should be displayed correctly on the screen. + call term_sendkeys(buf, "\<End>\<C-U>Tcmd \<Tab>\<C-A>\<Left>\<Left>") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_35', {}) + + " After using <C-A> to expand all the filename matches, pressing <Up> + " should not open the popup menu again. + call term_sendkeys(buf, "\<C-E>\<C-U>:cd Xdir/XdirA\<CR>") + call term_sendkeys(buf, ":e \<Tab>\<C-A>\<Up>") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_36', {}) + call term_sendkeys(buf, "\<C-E>\<C-U>:cd -\<CR>") + + " After using <C-A> to expand all the matches, pressing <S-Tab> used to + " crash Vim + call term_sendkeys(buf, ":sign \<Tab>\<C-A>\<S-Tab>") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_37', {}) + + " After removing the pum the command line is redrawn + call term_sendkeys(buf, ":edit foo\<CR>") + call term_sendkeys(buf, ":edit bar\<CR>") + call term_sendkeys(buf, ":ls\<CR>") + call term_sendkeys(buf, ":com\<Tab> ") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_38', {}) + call term_sendkeys(buf, "\<C-U>\<CR>") + + " Esc still works to abort the command when 'statusline' is set + call term_sendkeys(buf, ":call SetupStatusline()\<CR>") + call term_sendkeys(buf, ":si\<Tab>") + call term_sendkeys(buf, "\<Esc>") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_39', {}) + + " Esc still works to abort the command when 'tabline' is set + call term_sendkeys(buf, ":call SetupTabline()\<CR>") + call term_sendkeys(buf, ":si\<Tab>") + call term_sendkeys(buf, "\<Esc>") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_40', {}) + + " popup is cleared also when 'lazyredraw' is set + call term_sendkeys(buf, ":set showtabline=1 laststatus=1 lazyredraw\<CR>") + call term_sendkeys(buf, ":call DoFeedKeys()\<CR>") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_41', {}) + call term_sendkeys(buf, "\<Esc>") + + " Pressing <PageDown> should scroll the menu downward + call term_sendkeys(buf, ":sign \<Tab>\<PageDown>") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_42', {}) + call term_sendkeys(buf, "\<PageDown>") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_43', {}) + call term_sendkeys(buf, "\<PageDown>") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_44', {}) + call term_sendkeys(buf, "\<PageDown>") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_45', {}) + call term_sendkeys(buf, "\<C-U>sign \<Tab>\<Down>\<Down>\<PageDown>") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_46', {}) + + " Pressing <PageUp> should scroll the menu upward + call term_sendkeys(buf, "\<C-U>sign \<Tab>\<PageUp>") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_47', {}) + call term_sendkeys(buf, "\<PageUp>") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_48', {}) + call term_sendkeys(buf, "\<PageUp>") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_49', {}) + call term_sendkeys(buf, "\<PageUp>") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_50', {}) + call term_sendkeys(buf, "\<C-U>\<CR>") call StopVimInTerminal(buf) call delete('Xtest') call delete('Xdir', 'rf') endfunc +" Test for wildmenumode() with the cmdline popup menu +func Test_wildmenumode_with_pum() + set wildmenu + set wildoptions=pum + cnoremap <expr> <F2> wildmenumode() + call feedkeys(":sign \<Tab>\<F2>\<F2>\<C-B>\"\<CR>", 'xt') + call assert_equal('"sign define10', @:) + call feedkeys(":sign \<Tab>\<C-A>\<F2>\<C-B>\"\<CR>", 'xt') + call assert_equal('"sign define jump list place undefine unplace0', @:) + call feedkeys(":sign \<Tab>\<C-E>\<F2>\<C-B>\"\<CR>", 'xt') + call assert_equal('"sign 0', @:) + call feedkeys(":sign \<Tab>\<C-Y>\<F2>\<C-B>\"\<CR>", 'xt') + call assert_equal('"sign define0', @:) + set nowildmenu wildoptions& + cunmap <F2> +endfunc + +" Test for opening the cmdline completion popup menu from the terminal window. +" The popup menu should be positioned correctly over the status line of the +" bottom-most window. +func Test_wildmenu_pum_from_terminal() + CheckRunVimInTerminal + let python = PythonProg() + call CheckPython(python) + + %bw! + let cmds = ['set wildmenu wildoptions=pum'] + let pcmd = python .. ' -c "import sys; sys.stdout.write(sys.stdin.read())"' + call add(cmds, "call term_start('" .. pcmd .. "')") + call writefile(cmds, 'Xtest') + let buf = RunVimInTerminal('-S Xtest', #{rows: 10}) + call term_sendkeys(buf, "\r\r\r") + call term_wait(buf) + call term_sendkeys(buf, "\<C-W>:sign \<Tab>") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_term_01', {}) + call term_wait(buf) + call StopVimInTerminal(buf) + call delete('Xtest') +endfunc + +func Test_wildmenu_pum_clear_entries() + CheckRunVimInTerminal + + " This was using freed memory. Run in a terminal to get the pum to update. + let lines =<< trim END + set wildoptions=pum + set wildchar=<C-E> + END + call writefile(lines, 'XwildmenuTest', 'D') + let buf = RunVimInTerminal('-S XwildmenuTest', #{rows: 10}) + + call term_sendkeys(buf, ":\<C-E>\<C-E>") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_clear_entries_1', {}) + + set wildoptions& wildchar& +endfunc + +" Test for completion after a :substitute command followed by a pipe (|) +" character +func Test_cmdline_complete_substitute() + call feedkeys(":s | \t\<C-B>\"\<CR>", 'xt') + call assert_equal("\"s | \t", @:) + call feedkeys(":s/ | \t\<C-B>\"\<CR>", 'xt') + call assert_equal("\"s/ | \t", @:) + call feedkeys(":s/one | \t\<C-B>\"\<CR>", 'xt') + call assert_equal("\"s/one | \t", @:) + call feedkeys(":s/one/ | \t\<C-B>\"\<CR>", 'xt') + call assert_equal("\"s/one/ | \t", @:) + call feedkeys(":s/one/two | \t\<C-B>\"\<CR>", 'xt') + call assert_equal("\"s/one/two | \t", @:) + call feedkeys(":s/one/two/ | chist\t\<C-B>\"\<CR>", 'xt') + call assert_equal('"s/one/two/ | chistory', @:) + call feedkeys(":s/one/two/g \t\<C-B>\"\<CR>", 'xt') + call assert_equal("\"s/one/two/g \t", @:) + call feedkeys(":s/one/two/g | chist\t\<C-B>\"\<CR>", 'xt') + call assert_equal("\"s/one/two/g | chistory", @:) + call feedkeys(":s/one/t\\/ | \t\<C-B>\"\<CR>", 'xt') + call assert_equal("\"s/one/t\\/ | \t", @:) + call feedkeys(":s/one/t\"o/ | chist\t\<C-B>\"\<CR>", 'xt') + call assert_equal('"s/one/t"o/ | chistory', @:) + call feedkeys(":s/one/t|o/ | chist\t\<C-B>\"\<CR>", 'xt') + call assert_equal('"s/one/t|o/ | chistory', @:) + call feedkeys(":&\t\<C-B>\"\<CR>", 'xt') + call assert_equal("\"&\t", @:) +endfunc + +" Test for the :dlist command completion +func Test_cmdline_complete_dlist() + call feedkeys(":dlist 10 /pat/ a\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"dlist 10 /pat/ a\<C-A>", @:) + call feedkeys(":dlist 10 /pat/ \t\<C-B>\"\<CR>", 'xt') + call assert_equal("\"dlist 10 /pat/ \t", @:) + call feedkeys(":dlist 10 /pa\\t/\t\<C-B>\"\<CR>", 'xt') + call assert_equal("\"dlist 10 /pa\\t/\t", @:) + call feedkeys(":dlist 10 /pat\\\t\<C-B>\"\<CR>", 'xt') + call assert_equal("\"dlist 10 /pat\\\t", @:) + call feedkeys(":dlist 10 /pat/ | chist\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"dlist 10 /pat/ | chistory", @:) +endfunc + +" argument list (only for :argdel) fuzzy completion +func Test_fuzzy_completion_arglist() + argadd change.py count.py charge.py + set wildoptions& + call feedkeys(":argdel cge\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"argdel cge', @:) + set wildoptions=fuzzy + call feedkeys(":argdel cge\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"argdel change.py charge.py', @:) + %argdelete + set wildoptions& +endfunc + +" autocmd group name fuzzy completion +func Test_fuzzy_completion_autocmd() + set wildoptions& + augroup MyFuzzyGroup + augroup END + call feedkeys(":augroup mfg\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"augroup mfg', @:) + call feedkeys(":augroup My*p\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"augroup MyFuzzyGroup', @:) + set wildoptions=fuzzy + call feedkeys(":augroup mfg\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"augroup MyFuzzyGroup', @:) + call feedkeys(":augroup My*p\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"augroup My*p', @:) + augroup! MyFuzzyGroup + set wildoptions& +endfunc + +" buffer name fuzzy completion +func Test_fuzzy_completion_bufname() + set wildoptions& + " Use a long name to reduce the risk of matching a random directory name + edit SomeRandomFileWithLetters.txt + enew + call feedkeys(":b SRFWL\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"b SRFWL', @:) + call feedkeys(":b S*FileWithLetters.txt\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"b SomeRandomFileWithLetters.txt', @:) + set wildoptions=fuzzy + call feedkeys(":b SRFWL\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"b SomeRandomFileWithLetters.txt', @:) + call feedkeys(":b S*FileWithLetters.txt\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"b S*FileWithLetters.txt', @:) + %bw! + set wildoptions& +endfunc + +" buffer name (full path) fuzzy completion +func Test_fuzzy_completion_bufname_fullpath() + CheckUnix + set wildoptions& + call mkdir('Xcmd/Xstate/Xfile.js', 'p') + edit Xcmd/Xstate/Xfile.js + cd Xcmd/Xstate + enew + call feedkeys(":b CmdStateFile\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"b CmdStateFile', @:) + set wildoptions=fuzzy + call feedkeys(":b CmdStateFile\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_match('Xcmd/Xstate/Xfile.js$', @:) + cd - + call delete('Xcmd', 'rf') + set wildoptions& +endfunc + +" :behave suboptions fuzzy completion +func Test_fuzzy_completion_behave() + set wildoptions& + call feedkeys(":behave xm\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"behave xm', @:) + call feedkeys(":behave xt*m\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"behave xterm', @:) + set wildoptions=fuzzy + call feedkeys(":behave xm\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"behave xterm', @:) + call feedkeys(":behave xt*m\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"behave xt*m', @:) + let g:Sline = '' + call feedkeys(":behave win\<C-D>\<F4>\<C-B>\"\<CR>", 'tx') + call assert_equal('mswin', g:Sline) + call assert_equal('"behave win', @:) + set wildoptions& +endfunc + +" " colorscheme name fuzzy completion - NOT supported +" func Test_fuzzy_completion_colorscheme() +" endfunc + +" built-in command name fuzzy completion +func Test_fuzzy_completion_cmdname() + set wildoptions& + call feedkeys(":sbwin\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"sbwin', @:) + call feedkeys(":sbr*d\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"sbrewind', @:) + set wildoptions=fuzzy + call feedkeys(":sbwin\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"sbrewind', @:) + call feedkeys(":sbr*d\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"sbr*d', @:) + set wildoptions& +endfunc + +" " compiler name fuzzy completion - NOT supported +" func Test_fuzzy_completion_compiler() +" endfunc + +" :cscope suboptions fuzzy completion +func Test_fuzzy_completion_cscope() + CheckFeature cscope + set wildoptions& + call feedkeys(":cscope ret\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"cscope ret', @:) + call feedkeys(":cscope re*t\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"cscope reset', @:) + set wildoptions=fuzzy + call feedkeys(":cscope ret\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"cscope reset', @:) + call feedkeys(":cscope re*t\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"cscope re*t', @:) + set wildoptions& +endfunc + +" :diffget/:diffput buffer name fuzzy completion +func Test_fuzzy_completion_diff() + new SomeBuffer + diffthis + new OtherBuffer + diffthis + set wildoptions& + call feedkeys(":diffget sbuf\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"diffget sbuf', @:) + call feedkeys(":diffput sbuf\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"diffput sbuf', @:) + set wildoptions=fuzzy + call feedkeys(":diffget sbuf\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"diffget SomeBuffer', @:) + call feedkeys(":diffput sbuf\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"diffput SomeBuffer', @:) + %bw! + set wildoptions& +endfunc + +" " directory name fuzzy completion - NOT supported +" func Test_fuzzy_completion_dirname() +" endfunc + +" environment variable name fuzzy completion +func Test_fuzzy_completion_env() + set wildoptions& + call feedkeys(":echo $VUT\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"echo $VUT', @:) + set wildoptions=fuzzy + call feedkeys(":echo $VUT\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"echo $VIMRUNTIME', @:) + set wildoptions& +endfunc + +" autocmd event fuzzy completion +func Test_fuzzy_completion_autocmd_event() + set wildoptions& + call feedkeys(":autocmd BWout\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"autocmd BWout', @:) + set wildoptions=fuzzy + call feedkeys(":autocmd BWout\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"autocmd BufWipeout', @:) + set wildoptions& +endfunc + +" vim expression fuzzy completion +func Test_fuzzy_completion_expr() + let g:PerPlaceCount = 10 + set wildoptions& + call feedkeys(":let c = ppc\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"let c = ppc', @:) + set wildoptions=fuzzy + call feedkeys(":let c = ppc\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"let c = PerPlaceCount', @:) + set wildoptions& +endfunc + +" " file name fuzzy completion - NOT supported +" func Test_fuzzy_completion_filename() +" endfunc + +" " files in path fuzzy completion - NOT supported +" func Test_fuzzy_completion_filesinpath() +" endfunc + +" " filetype name fuzzy completion - NOT supported +" func Test_fuzzy_completion_filetype() +" endfunc + +" user defined function name completion +func Test_fuzzy_completion_userdefined_func() + set wildoptions& + call feedkeys(":call Test_f_u_f\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"call Test_f_u_f', @:) + set wildoptions=fuzzy + call feedkeys(":call Test_f_u_f\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"call Test_fuzzy_completion_userdefined_func()', @:) + set wildoptions& +endfunc + +" <SNR> functions should be sorted to the end +func Test_fuzzy_completion_userdefined_snr_func() + func s:Sendmail() + endfunc + func SendSomemail() + endfunc + func S1e2n3dmail() + endfunc + set wildoptions=fuzzy + call feedkeys(":call sendmail\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"call SendSomemail() S1e2n3dmail() ' + \ .. expand("<SID>") .. 'Sendmail()', @:) + set wildoptions& + delfunc s:Sendmail + delfunc SendSomemail + delfunc S1e2n3dmail +endfunc + +" user defined command name completion +func Test_fuzzy_completion_userdefined_cmd() + set wildoptions& + call feedkeys(":MsFeat\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"MsFeat', @:) + set wildoptions=fuzzy + call feedkeys(":MsFeat\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"MissingFeature', @:) + set wildoptions& +endfunc + +" " :help tag fuzzy completion - NOT supported +" func Test_fuzzy_completion_helptag() +" endfunc + +" highlight group name fuzzy completion +func Test_fuzzy_completion_hlgroup() + set wildoptions& + call feedkeys(":highlight SKey\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"highlight SKey', @:) + call feedkeys(":highlight Sp*Key\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"highlight SpecialKey', @:) + set wildoptions=fuzzy + call feedkeys(":highlight SKey\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"highlight SpecialKey', @:) + call feedkeys(":highlight Sp*Key\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"highlight Sp*Key', @:) + set wildoptions& +endfunc + +" :history suboptions fuzzy completion +func Test_fuzzy_completion_history() + set wildoptions& + call feedkeys(":history dg\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"history dg', @:) + call feedkeys(":history se*h\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"history search', @:) + set wildoptions=fuzzy + call feedkeys(":history dg\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"history debug', @:) + call feedkeys(":history se*h\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"history se*h', @:) + set wildoptions& +endfunc + +" :language locale name fuzzy completion +func Test_fuzzy_completion_lang() + CheckUnix + set wildoptions& + call feedkeys(":lang psx\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"lang psx', @:) + set wildoptions=fuzzy + call feedkeys(":lang psx\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"lang POSIX', @:) + set wildoptions& +endfunc + +" :mapclear buffer argument fuzzy completion +func Test_fuzzy_completion_mapclear() + set wildoptions& + call feedkeys(":mapclear buf\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"mapclear buf', @:) + set wildoptions=fuzzy + call feedkeys(":mapclear buf\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"mapclear <buffer>', @:) + set wildoptions& +endfunc + +" map name fuzzy completion +func Test_fuzzy_completion_mapname() + " test regex completion works + set wildoptions=fuzzy + call feedkeys(":cnoremap <ex\<Tab> <esc> \<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"cnoremap <expr> <esc> \<Tab>", @:) + nmap <plug>MyLongMap :p<CR> + call feedkeys(":nmap MLM\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"nmap <Plug>MyLongMap", @:) + call feedkeys(":nmap MLM \<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"nmap MLM \t", @:) + call feedkeys(":nmap <F2> one two \<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"nmap <F2> one two \t", @:) + " duplicate entries should be removed + vmap <plug>MyLongMap :<C-U>#<CR> + call feedkeys(":nmap MLM\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"nmap <Plug>MyLongMap", @:) + nunmap <plug>MyLongMap + vunmap <plug>MyLongMap + call feedkeys(":nmap ABC\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"nmap ABC\t", @:) + " results should be sorted by best match + nmap <Plug>format : + nmap <Plug>goformat : + nmap <Plug>TestFOrmat : + nmap <Plug>fendoff : + nmap <Plug>state : + nmap <Plug>FendingOff : + call feedkeys(":nmap <Plug>fo\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"nmap <Plug>format <Plug>TestFOrmat <Plug>FendingOff <Plug>goformat <Plug>fendoff", @:) + nunmap <Plug>format + nunmap <Plug>goformat + nunmap <Plug>TestFOrmat + nunmap <Plug>fendoff + nunmap <Plug>state + nunmap <Plug>FendingOff + set wildoptions& +endfunc + +" abbreviation fuzzy completion +func Test_fuzzy_completion_abbr() + set wildoptions=fuzzy + call feedkeys(":iabbr wait\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"iabbr <nowait>", @:) + iabbr WaitForCompletion WFC + call feedkeys(":iabbr fcl\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"iabbr WaitForCompletion", @:) + call feedkeys(":iabbr a1z\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"iabbr a1z\t", @:) + + iunabbrev WaitForCompletion + set wildoptions& +endfunc + +" menu name fuzzy completion +func Test_fuzzy_completion_menu() + CheckFeature menu + + source $VIMRUNTIME/menu.vim + set wildoptions& + call feedkeys(":menu pup\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"menu pup', @:) + set wildoptions=fuzzy + call feedkeys(":menu pup\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"menu PopUp.', @:) + + set wildoptions& + source $VIMRUNTIME/delmenu.vim +endfunc + +" :messages suboptions fuzzy completion +func Test_fuzzy_completion_messages() + set wildoptions& + call feedkeys(":messages clr\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"messages clr', @:) + set wildoptions=fuzzy + call feedkeys(":messages clr\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"messages clear', @:) + set wildoptions& +endfunc + +" :set option name fuzzy completion +func Test_fuzzy_completion_option() + set wildoptions& + call feedkeys(":set brkopt\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"set brkopt', @:) + set wildoptions=fuzzy + call feedkeys(":set brkopt\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"set breakindentopt', @:) + set wildoptions& + call feedkeys(":set fixeol\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"set fixendofline', @:) + set wildoptions=fuzzy + call feedkeys(":set fixeol\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"set fixendofline', @:) + set wildoptions& +endfunc + +" :set <term_option> +func Test_fuzzy_completion_term_option() + throw 'Skipped: Nvim does not support term options' + set wildoptions& + call feedkeys(":set t_E\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"set t_EC', @:) + call feedkeys(":set <t_E\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"set <t_EC>', @:) + set wildoptions=fuzzy + call feedkeys(":set t_E\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"set t_EC', @:) + call feedkeys(":set <t_E\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"set <t_EC>', @:) + set wildoptions& +endfunc + +" " :packadd directory name fuzzy completion - NOT supported +" func Test_fuzzy_completion_packadd() +" endfunc + +" " shell command name fuzzy completion - NOT supported +" func Test_fuzzy_completion_shellcmd() +" endfunc + +" :sign suboptions fuzzy completion +func Test_fuzzy_completion_sign() + set wildoptions& + call feedkeys(":sign ufe\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"sign ufe', @:) + set wildoptions=fuzzy + call feedkeys(":sign ufe\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"sign undefine', @:) + set wildoptions& +endfunc + +" :syntax suboptions fuzzy completion +func Test_fuzzy_completion_syntax_cmd() + set wildoptions& + call feedkeys(":syntax kwd\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"syntax kwd', @:) + set wildoptions=fuzzy + call feedkeys(":syntax kwd\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"syntax keyword', @:) + set wildoptions& +endfunc + +" syntax group name fuzzy completion +func Test_fuzzy_completion_syntax_group() + set wildoptions& + call feedkeys(":syntax list mpar\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"syntax list mpar', @:) + set wildoptions=fuzzy + call feedkeys(":syntax list mpar\<Tab>\<C-B>\"\<CR>", 'tx') + " Fuzzy match prefers NvimParenthesis over MatchParen + " call assert_equal('"syntax list MatchParen', @:) + call assert_equal('"syntax list NvimParenthesis', @:) + set wildoptions& +endfunc + +" :syntime suboptions fuzzy completion +func Test_fuzzy_completion_syntime() + CheckFeature profile + set wildoptions& + call feedkeys(":syntime clr\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"syntime clr', @:) + set wildoptions=fuzzy + call feedkeys(":syntime clr\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"syntime clear', @:) + set wildoptions& +endfunc + +" " tag name fuzzy completion - NOT supported +" func Test_fuzzy_completion_tagname() +" endfunc + +" " tag name and file fuzzy completion - NOT supported +" func Test_fuzzy_completion_tagfile() +" endfunc + +" " user names fuzzy completion - how to test this functionality? +" func Test_fuzzy_completion_username() +" endfunc + +" user defined variable name fuzzy completion +func Test_fuzzy_completion_userdefined_var() + let g:SomeVariable=10 + set wildoptions& + call feedkeys(":let SVar\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"let SVar', @:) + set wildoptions=fuzzy + call feedkeys(":let SVar\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"let SomeVariable', @:) + set wildoptions& +endfunc + +" Test for sorting the results by the best match +func Test_fuzzy_completion_cmd_sort_results() + %bw! + command T123format : + command T123goformat : + command T123TestFOrmat : + command T123fendoff : + command T123state : + command T123FendingOff : + set wildoptions=fuzzy + call feedkeys(":T123fo\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"T123format T123TestFOrmat T123FendingOff T123goformat T123fendoff', @:) + delcommand T123format + delcommand T123goformat + delcommand T123TestFOrmat + delcommand T123fendoff + delcommand T123state + delcommand T123FendingOff + %bw + set wildoptions& +endfunc + +" Test for fuzzy completion of a command with lower case letters and a number +func Test_fuzzy_completion_cmd_alnum() + command Foo2Bar : + set wildoptions=fuzzy + call feedkeys(":foo2\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"Foo2Bar', @:) + call feedkeys(":foo\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"Foo2Bar', @:) + call feedkeys(":bar\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"Foo2Bar', @:) + delcommand Foo2Bar + set wildoptions& +endfunc + +" Test for command completion for a command starting with 'k' +func Test_fuzzy_completion_cmd_k() + command KillKillKill : + set wildoptions& + call feedkeys(":killkill\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"killkill\<Tab>", @:) + set wildoptions=fuzzy + call feedkeys(":killkill\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"KillKillKill', @:) + delcom KillKillKill + set wildoptions& +endfunc + +" Test for fuzzy completion for user defined custom completion function +func Test_fuzzy_completion_custom_func() + func Tcompl(a, c, p) + return "format\ngoformat\nTestFOrmat\nfendoff\nstate" + endfunc + command -nargs=* -complete=custom,Tcompl Fuzzy : + set wildoptions& + call feedkeys(":Fuzzy fo\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"Fuzzy format", @:) + call feedkeys(":Fuzzy xy\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"Fuzzy xy", @:) + call feedkeys(":Fuzzy ttt\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"Fuzzy ttt", @:) + set wildoptions=fuzzy + call feedkeys(":Fuzzy \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"Fuzzy format goformat TestFOrmat fendoff state", @:) + call feedkeys(":Fuzzy fo\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"Fuzzy format TestFOrmat goformat fendoff", @:) + call feedkeys(":Fuzzy xy\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"Fuzzy xy", @:) + call feedkeys(":Fuzzy ttt\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"Fuzzy TestFOrmat", @:) + delcom Fuzzy + set wildoptions& +endfunc + +" Test for :breakadd argument completion +func Test_cmdline_complete_breakadd() + call feedkeys(":breakadd \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd expr file func here", @:) + call feedkeys(":breakadd \<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd expr", @:) + call feedkeys(":breakadd \<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd expr", @:) + call feedkeys(":breakadd he\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd here", @:) + call feedkeys(":breakadd he\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd here", @:) + call feedkeys(":breakadd abc\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd abc", @:) + call assert_equal(['expr', 'file', 'func', 'here'], getcompletion('', 'breakpoint')) + let l = getcompletion('not', 'breakpoint') + call assert_equal([], l) + + " Test for :breakadd file [lnum] <file> + call writefile([], 'Xscript') + call feedkeys(":breakadd file Xsc\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd file Xscript", @:) + call feedkeys(":breakadd file Xsc\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd file Xscript", @:) + call feedkeys(":breakadd file 20 Xsc\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd file 20 Xscript", @:) + call feedkeys(":breakadd file 20 Xsc\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd file 20 Xscript", @:) + call feedkeys(":breakadd file 20x Xsc\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd file 20x Xsc\t", @:) + call feedkeys(":breakadd file 20\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd file 20\t", @:) + call feedkeys(":breakadd file 20x\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd file 20x\t", @:) + call feedkeys(":breakadd file Xscript \<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd file Xscript ", @:) + call feedkeys(":breakadd file X1B2C3\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd file X1B2C3", @:) + call delete('Xscript') + + " Test for :breakadd func [lnum] <function> + func Xbreak_func() + endfunc + call feedkeys(":breakadd func Xbr\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd func Xbreak_func", @:) + call feedkeys(":breakadd func Xbr\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd func Xbreak_func", @:) + call feedkeys(":breakadd func 20 Xbr\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd func 20 Xbreak_func", @:) + call feedkeys(":breakadd func 20 Xbr\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd func 20 Xbreak_func", @:) + call feedkeys(":breakadd func 20x Xbr\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd func 20x Xbr\t", @:) + call feedkeys(":breakadd func 20\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd func 20\t", @:) + call feedkeys(":breakadd func 20x\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd func 20x\t", @:) + call feedkeys(":breakadd func Xbreak_func \<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd func Xbreak_func ", @:) + call feedkeys(":breakadd func X1B2C3\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd func X1B2C3", @:) + delfunc Xbreak_func + + " Test for :breakadd expr <expression> + let g:Xtest_var = 10 + call feedkeys(":breakadd expr Xtest\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd expr Xtest_var", @:) + call feedkeys(":breakadd expr Xtest\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd expr Xtest_var", @:) + call feedkeys(":breakadd expr Xtest_var \<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd expr Xtest_var ", @:) + call feedkeys(":breakadd expr X1B2C3\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd expr X1B2C3", @:) + unlet g:Xtest_var + + " Test for :breakadd here + call feedkeys(":breakadd here Xtest\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd here Xtest", @:) + call feedkeys(":breakadd here Xtest\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd here Xtest", @:) + call feedkeys(":breakadd here \<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakadd here ", @:) +endfunc + +" Test for :breakdel argument completion +func Test_cmdline_complete_breakdel() + call feedkeys(":breakdel \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel file func here", @:) + call feedkeys(":breakdel \<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel file", @:) + call feedkeys(":breakdel \<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel file", @:) + call feedkeys(":breakdel he\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel here", @:) + call feedkeys(":breakdel he\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel here", @:) + call feedkeys(":breakdel abc\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel abc", @:) + + " Test for :breakdel file [lnum] <file> + call writefile([], 'Xscript') + call feedkeys(":breakdel file Xsc\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel file Xscript", @:) + call feedkeys(":breakdel file Xsc\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel file Xscript", @:) + call feedkeys(":breakdel file 20 Xsc\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel file 20 Xscript", @:) + call feedkeys(":breakdel file 20 Xsc\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel file 20 Xscript", @:) + call feedkeys(":breakdel file 20x Xsc\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel file 20x Xsc\t", @:) + call feedkeys(":breakdel file 20\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel file 20\t", @:) + call feedkeys(":breakdel file 20x\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel file 20x\t", @:) + call feedkeys(":breakdel file Xscript \<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel file Xscript ", @:) + call feedkeys(":breakdel file X1B2C3\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel file X1B2C3", @:) + call delete('Xscript') + + " Test for :breakdel func [lnum] <function> + func Xbreak_func() + endfunc + call feedkeys(":breakdel func Xbr\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel func Xbreak_func", @:) + call feedkeys(":breakdel func Xbr\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel func Xbreak_func", @:) + call feedkeys(":breakdel func 20 Xbr\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel func 20 Xbreak_func", @:) + call feedkeys(":breakdel func 20 Xbr\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel func 20 Xbreak_func", @:) + call feedkeys(":breakdel func 20x Xbr\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel func 20x Xbr\t", @:) + call feedkeys(":breakdel func 20\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel func 20\t", @:) + call feedkeys(":breakdel func 20x\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel func 20x\t", @:) + call feedkeys(":breakdel func Xbreak_func \<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel func Xbreak_func ", @:) + call feedkeys(":breakdel func X1B2C3\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel func X1B2C3", @:) + delfunc Xbreak_func + + " Test for :breakdel here + call feedkeys(":breakdel here Xtest\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel here Xtest", @:) + call feedkeys(":breakdel here Xtest\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel here Xtest", @:) + call feedkeys(":breakdel here \<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"breakdel here ", @:) +endfunc + +" Test for :scriptnames argument completion +func Test_cmdline_complete_scriptnames() + set wildmenu + call writefile(['let a = 1'], 'Xa1b2c3.vim') + source Xa1b2c3.vim + call feedkeys(":script \<Tab>\<Left>\<Left>\<C-B>\"\<CR>", 'tx') + call assert_match("\"script .*Xa1b2c3.vim$", @:) + call feedkeys(":script \<Tab>\<Left>\<Left>\<C-B>\"\<CR>", 'tx') + call assert_match("\"script .*Xa1b2c3.vim$", @:) + call feedkeys(":script b2c3\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"script b2c3", @:) + call feedkeys(":script 2\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_match("\"script 2\<Tab>$", @:) + call feedkeys(":script \<Tab>\<Left>\<Left> \<Tab>\<C-B>\"\<CR>", 'tx') + call assert_match("\"script .*Xa1b2c3.vim $", @:) + call feedkeys(":script \<Tab>\<Left>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"script ", @:) + call assert_match('Xa1b2c3.vim$', getcompletion('.*Xa1b2.*', 'scriptnames')[0]) + call assert_equal([], getcompletion('Xa1b2', 'scriptnames')) + new + call feedkeys(":script \<Tab>\<Left>\<Left>\<CR>", 'tx') + call assert_equal('Xa1b2c3.vim', fnamemodify(@%, ':t')) + bw! + call delete('Xa1b2c3.vim') + set wildmenu& +endfunc + " this was going over the end of IObuff func Test_report_error_with_composing() let caught = 'no' @@ -2074,6 +3460,16 @@ func Test_cmdline_complete_substitute_short() endfor endfunc +" Test for :! shell command argument completion +func Test_cmdline_complete_bang_cmd_argument() + set wildoptions=fuzzy + call feedkeys(":!vim test_cmdline.\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"!vim test_cmdline.vim', @:) + set wildoptions& + call feedkeys(":!vim test_cmdline.\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"!vim test_cmdline.vim', @:) +endfunc + func Check_completion() call assert_equal('let a', getcmdline()) call assert_equal(6, getcmdpos()) @@ -2129,4 +3525,57 @@ func Test_wildmenu_pum_disable_while_shown() set wildoptions& wildmenu& endfunc +func Test_setcmdline() + func SetText(text, pos) + autocmd CmdlineChanged * let g:cmdtype = expand('<afile>') + call assert_equal(0, setcmdline(a:text)) + call assert_equal(a:text, getcmdline()) + call assert_equal(len(a:text) + 1, getcmdpos()) + call assert_equal(getcmdtype(), g:cmdtype) + unlet g:cmdtype + autocmd! CmdlineChanged + + call assert_equal(0, setcmdline(a:text, a:pos)) + call assert_equal(a:text, getcmdline()) + call assert_equal(a:pos, getcmdpos()) + + call assert_fails('call setcmdline("' .. a:text .. '", -1)', 'E487:') + call assert_fails('call setcmdline({}, 0)', 'E1174:') + call assert_fails('call setcmdline("' .. a:text .. '", {})', 'E1210:') + + return '' + endfunc + + call feedkeys(":\<C-R>=SetText('set rtp?', 2)\<CR>\<CR>", 'xt') + call assert_equal('set rtp?', @:) + + call feedkeys(":let g:str = input('? ')\<CR>", 't') + call feedkeys("\<C-R>=SetText('foo', 4)\<CR>\<CR>", 'xt') + call assert_equal('foo', g:str) + unlet g:str + + delfunc SetText + + " setcmdline() returns 1 when not editing the command line. + call assert_equal(1, 'foo'->setcmdline()) + + " Called in custom function + func CustomComplete(A, L, P) + call assert_equal(0, setcmdline("DoCmd ")) + return "January\nFebruary\nMars\n" + endfunc + + com! -nargs=* -complete=custom,CustomComplete DoCmd : + call feedkeys(":DoCmd \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"DoCmd January February Mars', @:) + delcom DoCmd + delfunc CustomComplete + + " Called in <expr> + cnoremap <expr>a setcmdline('let foo=') + call feedkeys(":a\<CR>", 'tx') + call assert_equal('let foo=0', @:) + cunmap a +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_comments.vim b/src/nvim/testdir/test_comments.vim new file mode 100644 index 0000000000..c34b85c42d --- /dev/null +++ b/src/nvim/testdir/test_comments.vim @@ -0,0 +1,277 @@ +" Tests for the various flags in the 'comments' option + +" Test for the 'n' flag in 'comments' +func Test_comment_nested() + new + setlocal comments=n:> fo+=ro + exe "normal i> B\nD\<C-C>ggOA\<C-C>joC\<C-C>Go\<BS>>>> F\nH" + exe "normal 5GOE\<C-C>6GoG" + let expected =<< trim END + > A + > B + > C + > D + >>>> E + >>>> F + >>>> G + >>>> H + END + call assert_equal(expected, getline(1, '$')) + close! +endfunc + +" Test for the 'b' flag in 'comments' +func Test_comment_blank() + new + setlocal comments=b:* fo+=ro + exe "normal i* E\nF\n\<BS>G\nH\<C-C>ggOC\<C-C>O\<BS>B\<C-C>OA\<C-C>2joD" + let expected =<< trim END + A + *B + * C + * D + * E + * F + *G + H + END + call assert_equal(expected, getline(1, '$')) + close! +endfunc + +" Test for the 'f' flag in 'comments' (only the first line has a comment +" string) +func Test_comment_firstline() + new + setlocal comments=f:- fo+=ro + exe "normal i- B\nD\<C-C>ggoC\<C-C>ggOA\<C-C>" + call assert_equal(['A', '- B', ' C', ' D'], getline(1, '$')) + %d + setlocal comments=:- + exe "normal i- B\nD\<C-C>ggoC\<C-C>ggOA\<C-C>" + call assert_equal(['- A', '- B', '- C', '- D'], getline(1, '$')) + close! +endfunc + +" Test for the 's', 'm' and 'e' flags in 'comments' +" Test for automatically adding comment leaders in insert mode +func Test_comment_threepiece() + new + setlocal expandtab + call setline(1, ["\t/*"]) + setlocal formatoptions=croql + call cursor(1, 3) + call feedkeys("A\<cr>\<cr>/", 'tnix') + call assert_equal(["\t/*", " *", " */"], getline(1, '$')) + + " If a comment ends in a single line, then don't add it in the next line + %d + call setline(1, '/* line1 */') + call feedkeys("A\<CR>next line", 'xt') + call assert_equal(['/* line1 */', 'next line'], getline(1, '$')) + + %d + " Copy the trailing indentation from the leader comment to a new line + setlocal autoindent noexpandtab + call feedkeys("a\t/*\tone\ntwo\n/", 'xt') + call assert_equal(["\t/*\tone", "\t *\ttwo", "\t */"], getline(1, '$')) + close! +endfunc + +" Test for the 'r' flag in 'comments' (right align comment) +func Test_comment_rightalign() + new + setlocal comments=sr:/***,m:**,ex-2:******/ fo+=ro + exe "normal i=\<C-C>o\t /***\nD\n/" + exe "normal 2GOA\<C-C>joB\<C-C>jOC\<C-C>joE\<C-C>GOF\<C-C>joG" + let expected =<< trim END + = + A + /*** + ** B + ** C + ** D + ** E + ** F + ******/ + G + END + call assert_equal(expected, getline(1, '$')) + close! +endfunc + +" Test for the 'O' flag in 'comments' +func Test_comment_O() + new + setlocal comments=Ob:* fo+=ro + exe "normal i* B\nD\<C-C>kOA\<C-C>joC" + let expected =<< trim END + A + * B + * C + * D + END + call assert_equal(expected, getline(1, '$')) + close! +endfunc + +" Test for using a multibyte character as a comment leader +func Test_comment_multibyte_leader() + new + let t =<< trim END + { + X + Xa + XaY + XY + XYZ + X Y + X YZ + XX + XXa + XXY + } + END + call setline(1, t) + call cursor(2, 1) + + set tw=2 fo=cqm comments=n:X + exe "normal gqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgq" + let t =<< trim END + X + Xa + XaY + XY + XYZ + X Y + X YZ + XX + XXa + XXY + END + exe "normal o\n" . join(t, "\n") + + let expected =<< trim END + { + X + Xa + Xa + XY + XY + XY + XZ + X Y + X Y + X Z + XX + XXa + XXY + + X + Xa + Xa + XY + XY + XY + XZ + X Y + X Y + X Z + XX + XXa + XXY + } + END + call assert_equal(expected, getline(1, '$')) + + set tw& fo& comments& + close! +endfunc + +" Test for a space character in 'comments' setting +func Test_comment_space() + new + setlocal comments=b:\ > fo+=ro + exe "normal i> B\nD\<C-C>ggOA\<C-C>joC" + exe "normal Go > F\nH\<C-C>kOE\<C-C>joG" + let expected =<< trim END + A + > B + C + D + > E + > F + > G + > H + END + call assert_equal(expected, getline(1, '$')) + close! +endfunc + +" Test for formatting lines with and without comments +func Test_comment_format_lines() + new + call setline(1, ['one', '/* two */', 'three']) + normal gggqG + call assert_equal(['one', '/* two */', 'three'], getline(1, '$')) + close! +endfunc + +" Test for using 'a' in 'formatoptions' with comments +func Test_comment_autoformat() + new + setlocal formatoptions+=a + call feedkeys("a- one\n- two\n", 'xt') + call assert_equal(['- one', '- two', ''], getline(1, '$')) + + %d + call feedkeys("a\none\n", 'xt') + call assert_equal(['', 'one', ''], getline(1, '$')) + + setlocal formatoptions+=aw + %d + call feedkeys("aone \ntwo\n", 'xt') + call assert_equal(['one two', ''], getline(1, '$')) + + %d + call feedkeys("aone\ntwo\n", 'xt') + call assert_equal(['one', 'two', ''], getline(1, '$')) + + close! +endfunc + +" Test for joining lines with comments ('j' flag in 'formatoptions') +func Test_comment_join_lines_fo_j() + new + setlocal fo+=j comments=:// + call setline(1, ['i++; // comment1', ' // comment2']) + normal J + call assert_equal('i++; // comment1 comment2', getline(1)) + setlocal fo-=j + call setline(1, ['i++; // comment1', ' // comment2']) + normal J + call assert_equal('i++; // comment1 // comment2', getline(1)) + " Test with nested comments + setlocal fo+=j comments=n:>,n:) + call setline(1, ['i++; > ) > ) comment1', ' > ) comment2']) + normal J + call assert_equal('i++; > ) > ) comment1 comment2', getline(1)) + close! +endfunc + +" Test for formatting lines where only the first line has a comment. +func Test_comment_format_firstline_comment() + new + setlocal formatoptions=tcq + call setline(1, ['- one two', 'three']) + normal gggqG + call assert_equal(['- one two three'], getline(1, '$')) + + %d + call setline(1, ['- one', '- two']) + normal gggqG + call assert_equal(['- one', '- two'], getline(1, '$')) + close! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_compiler.vim b/src/nvim/testdir/test_compiler.vim index 3dc8710d63..ec7d143030 100644 --- a/src/nvim/testdir/test_compiler.vim +++ b/src/nvim/testdir/test_compiler.vim @@ -1,9 +1,11 @@ " Test the :compiler command +source check.vim +source shared.vim + func Test_compiler() - if !executable('perl') - return - endif + CheckExecutable perl + CheckFeature quickfix " $LANG changes the output of Perl. if $LANG != '' diff --git a/src/nvim/testdir/test_const.vim b/src/nvim/testdir/test_const.vim index 0d064617a5..4a6c8779dd 100644 --- a/src/nvim/testdir/test_const.vim +++ b/src/nvim/testdir/test_const.vim @@ -194,6 +194,14 @@ func Test_lockvar() if 0 | lockvar x | endif let x = 'again' + + let val = [1, 2, 3] + lockvar 0 val + let val[0] = 9 + call assert_equal([9, 2, 3], val) + call add(val, 4) + call assert_equal([9, 2, 3, 4], val) + call assert_fails('let val = [4, 5, 6]', 'E1122:') endfunc @@ -231,6 +239,14 @@ func Test_const_with_special_variables() call assert_fails('const &filetype = "vim"', 'E996:') call assert_fails('const &l:filetype = "vim"', 'E996:') call assert_fails('const &g:encoding = "utf-8"', 'E996:') + + call assert_fails('const [a, $CONST_FOO] = [369, "abc"]', 'E996:') + call assert_equal(369, a) + call assert_equal(v:null, getenv("CONST_FOO")) + + call assert_fails('const [b; $CONST_FOO] = [246, 2, "abc"]', 'E996:') + call assert_equal(246, b) + call assert_equal(v:null, getenv("CONST_FOO")) endfunc func Test_const_with_eval_name() @@ -274,3 +290,5 @@ func Test_lock_depth_is_2() const d2 = #{a: 0, b: lvar, c: 4} let d2.b[1] = 'd' endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_cpoptions.vim b/src/nvim/testdir/test_cpoptions.vim new file mode 100644 index 0000000000..b9307ab30b --- /dev/null +++ b/src/nvim/testdir/test_cpoptions.vim @@ -0,0 +1,927 @@ +" Test for the various 'cpoptions' (cpo) flags + +source check.vim +source shared.vim +source view_util.vim + +" Test for the 'a' flag in 'cpo'. Reading a file should set the alternate +" file name. +func Test_cpo_a() + let save_cpo = &cpo + call writefile(['one'], 'XfileCpoA') + " Wipe out all the buffers, so that the alternate file is empty + edit Xfoo | %bw + set cpo-=a + new + read XfileCpoA + call assert_equal('', @#) + %d + set cpo+=a + read XfileCpoA + call assert_equal('XfileCpoA', @#) + close! + call delete('XfileCpoA') + let &cpo = save_cpo +endfunc + +" Test for the 'A' flag in 'cpo'. Writing a file should set the alternate +" file name. +func Test_cpo_A() + let save_cpo = &cpo + " Wipe out all the buffers, so that the alternate file is empty + edit Xfoo | %bw + set cpo-=A + new Xfile1 + write Xfile2 + call assert_equal('', @#) + %bw + call delete('Xfile2') + new Xfile1 + set cpo+=A + write Xfile2 + call assert_equal('Xfile2', @#) + close! + call delete('Xfile2') + let &cpo = save_cpo +endfunc + +" Test for the 'b' flag in 'cpo'. "\|" at the end of a map command is +" recognized as the end of the map. +func Test_cpo_b() + let save_cpo = &cpo + set cpo+=b + nnoremap <F5> :pwd\<CR>\|let i = 1 + call assert_equal(':pwd\<CR>\', maparg('<F5>')) + nunmap <F5> + exe "nnoremap <F5> :pwd\<C-V>|let i = 1" + call assert_equal(':pwd|let i = 1', maparg('<F5>')) + nunmap <F5> + set cpo-=b + nnoremap <F5> :pwd\<CR>\|let i = 1 + call assert_equal(':pwd\<CR>|let i = 1', maparg('<F5>')) + let &cpo = save_cpo + nunmap <F5> +endfunc + +" Test for the 'B' flag in 'cpo'. A backslash in mappings, abbreviations, user +" commands and menu commands has no special meaning. +func Test_cpo_B() + let save_cpo = &cpo + new + set cpo-=B + iabbr <buffer> abc ab\<BS>d + exe "normal iabc " + call assert_equal('ab<BS>d ', getline(1)) + %d + set cpo+=B + iabbr <buffer> abc ab\<BS>d + exe "normal iabc " + call assert_equal('abd ', getline(1)) + close! + let &cpo = save_cpo +endfunc + +" Test for the 'c' flag in 'cpo'. +func Test_cpo_c() + let save_cpo = &cpo + set cpo+=c + new + call setline(1, ' abababababab') + exe "normal gg/abab\<CR>" + call assert_equal(3, searchcount().total) + set cpo-=c + exe "normal gg/abab\<CR>" + call assert_equal(5, searchcount().total) + close! + let &cpo = save_cpo +endfunc + +" Test for the 'C' flag in 'cpo' (line continuation) +func Test_cpo_C() + let save_cpo = &cpo + call writefile(['let l = [', '\ 1,', '\ 2]'], 'XfileCpoC') + set cpo-=C + source XfileCpoC + call assert_equal([1, 2], g:l) + set cpo+=C + call assert_fails('source XfileCpoC', ['E697:', 'E10:']) + call delete('XfileCpoC') + let &cpo = save_cpo +endfunc + +" Test for the 'd' flag in 'cpo' (tags relative to the current file) +func Test_cpo_d() + let save_cpo = &cpo + call mkdir('XdirCpoD') + call writefile(["one\tXfile1\t/^one$/"], 'tags') + call writefile(["two\tXfile2\t/^two$/"], 'XdirCpoD/tags') + set tags=./tags + set cpo-=d + edit XdirCpoD/Xfile + call assert_equal('two', taglist('.*')[0].name) + set cpo+=d + call assert_equal('one', taglist('.*')[0].name) + %bw! + call delete('tags') + call delete('XdirCpoD', 'rf') + set tags& + let &cpo = save_cpo +endfunc + +" Test for the 'D' flag in 'cpo' (digraph after a r, f or t) +func Test_cpo_D() + CheckFeature digraphs + let save_cpo = &cpo + new + set cpo-=D + call setline(1, 'abcdefgh|') + exe "norm! 1gg0f\<c-k>!!" + call assert_equal(9, col('.')) + set cpo+=D + exe "norm! 1gg0f\<c-k>!!" + call assert_equal(1, col('.')) + set cpo-=D + close! + let &cpo = save_cpo +endfunc + +" Test for the 'e' flag in 'cpo' +func Test_cpo_e() + let save_cpo = &cpo + let @a='let i = 45' + set cpo+=e + call feedkeys(":@a\<CR>", 'xt') + call assert_equal(45, i) + set cpo-=e + call feedkeys(":@a\<CR>6\<CR>", 'xt') + call assert_equal(456, i) + let &cpo = save_cpo +endfunc + +" Test for the 'E' flag in 'cpo' with yank, change, delete, etc. operators +func Test_cpo_E() + new + call setline(1, '') + set cpo+=E + " yank an empty line + call assert_beeps('normal "ayl') + " change an empty line + call assert_beeps('normal lcTa') + call assert_beeps('normal 0c0') + " delete an empty line + call assert_beeps('normal D') + call assert_beeps('normal dl') + call assert_equal('', getline(1)) + " change case of an empty line + call assert_beeps('normal gul') + call assert_beeps('normal gUl') + " replace a character + call assert_beeps('normal vrx') + " increment and decrement + call assert_beeps('exe "normal v\<C-A>"') + call assert_beeps('exe "normal v\<C-X>"') + set cpo-=E + close! +endfunc + +" Test for the 'f' flag in 'cpo' (read in an empty buffer sets the file name) +func Test_cpo_f() + let save_cpo = &cpo + new + set cpo-=f + read test_cpoptions.vim + call assert_equal('', @%) + %d + set cpo+=f + read test_cpoptions.vim + call assert_equal('test_cpoptions.vim', @%) + close! + let &cpo = save_cpo +endfunc + +" Test for the 'F' flag in 'cpo' (write in an empty buffer sets the file name) +func Test_cpo_F() + let save_cpo = &cpo + new + set cpo-=F + write XfileCpoF + call assert_equal('', @%) + call delete('XfileCpoF') + set cpo+=F + write XfileCpoF + call assert_equal('XfileCpoF', @%) + close! + call delete('XfileCpoF') + let &cpo = save_cpo +endfunc + +" Test for the 'g' flag in 'cpo' (jump to line 1 when re-editing a file) +func Test_cpo_g() + throw 'Skipped: Nvim does not support cpoptions flag "g"' + let save_cpo = &cpo + new test_cpoptions.vim + set cpo-=g + normal 20G + edit + call assert_equal(20, line('.')) + set cpo+=g + edit + call assert_equal(1, line('.')) + close! + let &cpo = save_cpo +endfunc + +" Test for inserting text in a line with only spaces ('H' flag in 'cpoptions') +func Test_cpo_H() + throw 'Skipped: Nvim does not support cpoptions flag "H"' + let save_cpo = &cpo + new + set cpo-=H + call setline(1, ' ') + normal! Ia + call assert_equal(' a', getline(1)) + set cpo+=H + call setline(1, ' ') + normal! Ia + call assert_equal(' a ', getline(1)) + close! + let &cpo = save_cpo +endfunc + +" TODO: Add a test for the 'i' flag in 'cpo' +" Interrupting the reading of a file will leave it modified. + +" Test for the 'I' flag in 'cpo' (deleting autoindent when using arrow keys) +func Test_cpo_I() + let save_cpo = &cpo + new + setlocal autoindent + set cpo+=I + exe "normal i one\<CR>\<Up>" + call assert_equal(' ', getline(2)) + set cpo-=I + %d + exe "normal i one\<CR>\<Up>" + call assert_equal('', getline(2)) + close! + let &cpo = save_cpo +endfunc + +" Test for the 'j' flag in 'cpo' is in the test_join.vim file. + +" Test for the 'J' flag in 'cpo' (two spaces after a sentence) +func Test_cpo_J() + let save_cpo = &cpo + new + set cpo-=J + call setline(1, 'one. two! three? four."'' five.)]') + normal 0 + for colnr in [6, 12, 19, 28, 34] + normal ) + call assert_equal(colnr, col('.')) + endfor + for colnr in [28, 19, 12, 6, 1] + normal ( + call assert_equal(colnr, col('.')) + endfor + set cpo+=J + normal 0 + for colnr in [12, 28, 34] + normal ) + call assert_equal(colnr, col('.')) + endfor + for colnr in [28, 12, 1] + normal ( + call assert_equal(colnr, col('.')) + endfor + close! + let &cpo = save_cpo +endfunc + +" TODO: Add a test for the 'k' flag in 'cpo'. +" Disable the recognition of raw key codes in mappings, abbreviations, and the +" "to" part of menu commands. + +" TODO: Add a test for the 'K' flag in 'cpo'. +" Don't wait for a key code to complete when it is halfway a mapping. + +" Test for the 'l' flag in 'cpo' (backslash in a [] range) +func Test_cpo_l() + let save_cpo = &cpo + new + call setline(1, ['', "a\tc" .. '\t']) + set cpo-=l + exe 'normal gg/[\t]' .. "\<CR>" + call assert_equal([2, 8], [col('.'), virtcol('.')]) + set cpo+=l + exe 'normal gg/[\t]' .. "\<CR>" + call assert_equal([4, 10], [col('.'), virtcol('.')]) + close! + let &cpo = save_cpo +endfunc + +" Test for inserting tab in virtual replace mode ('L' flag in 'cpoptions') +func Test_cpo_L() + let save_cpo = &cpo + new + set cpo-=L + call setline(1, 'abcdefghijklmnopqr') + exe "normal 0gR\<Tab>" + call assert_equal("\<Tab>ijklmnopqr", getline(1)) + set cpo+=L + set list + call setline(1, 'abcdefghijklmnopqr') + exe "normal 0gR\<Tab>" + call assert_equal("\<Tab>cdefghijklmnopqr", getline(1)) + set nolist + call setline(1, 'abcdefghijklmnopqr') + exe "normal 0gR\<Tab>" + call assert_equal("\<Tab>ijklmnopqr", getline(1)) + close! + let &cpo = save_cpo +endfunc + +" TODO: Add a test for the 'm' flag in 'cpo'. +" When included, a showmatch will always wait half a second. When not +" included, a showmatch will wait half a second or until a character is typed. + +" Test for the 'M' flag in 'cpo' (% with escape parenthesis) +func Test_cpo_M() + let save_cpo = &cpo + new + call setline(1, ['( \( )', '\( ( \)']) + + set cpo-=M + call cursor(1, 1) + normal % + call assert_equal(6, col('.')) + call cursor(1, 4) + call assert_beeps('normal %') + call cursor(2, 2) + normal % + call assert_equal(7, col('.')) + call cursor(2, 4) + call assert_beeps('normal %') + + set cpo+=M + call cursor(1, 4) + normal % + call assert_equal(6, col('.')) + call cursor(1, 1) + call assert_beeps('normal %') + call cursor(2, 4) + normal % + call assert_equal(7, col('.')) + call cursor(2, 1) + call assert_beeps('normal %') + + close! + let &cpo = save_cpo +endfunc + +" Test for the 'n' flag in 'cpo' (using number column for wrapped lines) +func Test_cpo_n() + let save_cpo = &cpo + new + call setline(1, repeat('a', &columns)) + setlocal number + set cpo-=n + redraw! + call assert_equal(' aaaa', Screenline(2)) + set cpo+=n + redraw! + call assert_equal('aaaa', Screenline(2)) + close! + let &cpo = save_cpo +endfunc + +" Test for the 'o' flag in 'cpo' (line offset to search command) +func Test_cpo_o() + let save_cpo = &cpo + new + call setline(1, ['', 'one', 'two', 'three', 'one', 'two', 'three']) + set cpo-=o + exe "normal /one/+2\<CR>" + normal n + call assert_equal(7, line('.')) + set cpo+=o + exe "normal /one/+2\<CR>" + normal n + call assert_equal(5, line('.')) + close! + let &cpo = save_cpo +endfunc + +" Test for the 'O' flag in 'cpo' (overwriting an existing file) +func Test_cpo_O() + let save_cpo = &cpo + new XfileCpoO + call setline(1, 'one') + call writefile(['two'], 'XfileCpoO') + set cpo-=O + call assert_fails('write', 'E13:') + set cpo+=O + write + call assert_equal(['one'], readfile('XfileCpoO')) + close! + call delete('XfileCpoO') + let &cpo = save_cpo +endfunc + +" Test for the 'p' flag in 'cpo' is in the test_lispwords.vim file. + +" Test for the 'P' flag in 'cpo' (appending to a file sets the current file +" name) +func Test_cpo_P() + let save_cpo = &cpo + call writefile([], 'XfileCpoP') + new + call setline(1, 'one') + set cpo+=F + set cpo-=P + write >> XfileCpoP + call assert_equal('', @%) + set cpo+=P + write >> XfileCpoP + call assert_equal('XfileCpoP', @%) + close! + call delete('XfileCpoP') + let &cpo = save_cpo +endfunc + +" Test for the 'q' flag in 'cpo' (joining multiple lines) +func Test_cpo_q() + let save_cpo = &cpo + new + call setline(1, ['one', 'two', 'three', 'four', 'five']) + set cpo-=q + normal gg4J + call assert_equal(14, col('.')) + %d + call setline(1, ['one', 'two', 'three', 'four', 'five']) + set cpo+=q + normal gg4J + call assert_equal(4, col('.')) + close! + let &cpo = save_cpo +endfunc + +" Test for the 'r' flag in 'cpo' (redo command with a search motion) +func Test_cpo_r() + let save_cpo = &cpo + new + call setline(1, repeat(['one two three four'], 2)) + set cpo-=r + exe "normal ggc/two\<CR>abc " + let @/ = 'three' + normal 2G. + call assert_equal('abc two three four', getline(2)) + %d + call setline(1, repeat(['one two three four'], 2)) + set cpo+=r + exe "normal ggc/two\<CR>abc " + let @/ = 'three' + normal 2G. + call assert_equal('abc three four', getline(2)) + close! + let &cpo = save_cpo +endfunc + +" Test for the 'R' flag in 'cpo' (clear marks after a filter command) +func Test_cpo_R() + CheckUnix + let save_cpo = &cpo + new + call setline(1, ['three', 'one', 'two']) + set cpo-=R + 3mark r + %!sort + call assert_equal(3, line("'r")) + %d + call setline(1, ['three', 'one', 'two']) + set cpo+=R + 3mark r + %!sort + call assert_equal(0, line("'r")) + close! + let &cpo = save_cpo +endfunc + +" TODO: Add a test for the 's' flag in 'cpo'. +" Set buffer options when entering the buffer for the first time. If not +" present the options are set when the buffer is created. + +" Test for the 'S' flag in 'cpo' (copying buffer options) +func Test_cpo_S() + let save_cpo = &cpo + new Xfile1 + set noautoindent + new Xfile2 + set cpo-=S + set autoindent + wincmd p + call assert_equal(0, &autoindent) + wincmd p + call assert_equal(1, &autoindent) + set cpo+=S + wincmd p + call assert_equal(1, &autoindent) + set noautoindent + wincmd p + call assert_equal(0, &autoindent) + wincmd t + close! + close! + let &cpo = save_cpo +endfunc + +" Test for the 't' flag in 'cpo' is in the test_tagjump.vim file. + +" Test for the 'u' flag in 'cpo' (Vi-compatible undo) +func Test_cpo_u() + let save_cpo = &cpo + new + set cpo-=u + exe "normal iabc\<C-G>udef\<C-G>ughi" + normal uu + call assert_equal('abc', getline(1)) + %d + set cpo+=u + exe "normal iabc\<C-G>udef\<C-G>ughi" + normal uu + call assert_equal('abcdefghi', getline(1)) + close! + let &cpo = save_cpo +endfunc + +" TODO: Add a test for the 'v' flag in 'cpo'. +" Backspaced characters remain visible on the screen in Insert mode. + +" Test for the 'w' flag in 'cpo' ('cw' on a blank character changes only one +" character) +func Test_cpo_w() + throw 'Skipped: Nvim does not support cpoptions flag "w"' + let save_cpo = &cpo + new + set cpo+=w + call setline(1, 'here are some words') + norm! 1gg0elcwZZZ + call assert_equal('hereZZZ are some words', getline('.')) + norm! 1gg2elcWYYY + call assert_equal('hereZZZ areYYY some words', getline('.')) + set cpo-=w + call setline(1, 'here are some words') + norm! 1gg0elcwZZZ + call assert_equal('hereZZZare some words', getline('.')) + norm! 1gg2elcWYYY + call assert_equal('hereZZZare someYYYwords', getline('.')) + close! + let &cpo = save_cpo +endfunc + +" Test for the 'W' flag in 'cpo' is in the test_writefile.vim file + +" Test for the 'x' flag in 'cpo' (Esc on command-line executes command) +func Test_cpo_x() + let save_cpo = &cpo + set cpo-=x + let i = 1 + call feedkeys(":let i=10\<Esc>", 'xt') + call assert_equal(1, i) + set cpo+=x + call feedkeys(":let i=10\<Esc>", 'xt') + call assert_equal(10, i) + let &cpo = save_cpo +endfunc + +" Test for the 'X' flag in 'cpo' ('R' with a count) +func Test_cpo_X() + let save_cpo = &cpo + new + call setline(1, 'aaaaaa') + set cpo-=X + normal gg4Rx + call assert_equal('xxxxaa', getline(1)) + normal ggRy + normal 4. + call assert_equal('yyyyaa', getline(1)) + call setline(1, 'aaaaaa') + set cpo+=X + normal gg4Rx + call assert_equal('xxxxaaaaa', getline(1)) + normal ggRy + normal 4. + call assert_equal('yyyyxxxaaaaa', getline(1)) + close! + let &cpo = save_cpo +endfunc + +" Test for the 'y' flag in 'cpo' (repeating a yank command) +func Test_cpo_y() + let save_cpo = &cpo + new + call setline(1, ['one', 'two']) + set cpo-=y + normal ggyy + normal 2G. + call assert_equal("one\n", @") + %d + call setline(1, ['one', 'two']) + set cpo+=y + normal ggyy + normal 2G. + call assert_equal("two\n", @") + close! + let &cpo = save_cpo +endfunc + +" Test for the 'Z' flag in 'cpo' (write! resets 'readonly') +func Test_cpo_Z() + let save_cpo = &cpo + call writefile([], 'XfileCpoZ') + new XfileCpoZ + setlocal readonly + set cpo-=Z + write! + call assert_equal(0, &readonly) + set cpo+=Z + setlocal readonly + write! + call assert_equal(1, &readonly) + close! + call delete('XfileCpoZ') + let &cpo = save_cpo +endfunc + +" Test for the '!' flag in 'cpo' is in the test_normal.vim file + +" Test for displaying dollar when changing text ('$' flag in 'cpoptions') +func Test_cpo_dollar() + throw 'Skipped: use test/functional/legacy/cpoptions_spec.lua' + new + let g:Line = '' + func SaveFirstLine() + let g:Line = Screenline(1) + return '' + endfunc + inoremap <expr> <buffer> <F2> SaveFirstLine() + call test_override('redraw_flag', 1) + set cpo+=$ + call setline(1, 'one two three') + redraw! + exe "normal c2w\<F2>vim" + call assert_equal('one tw$ three', g:Line) + call assert_equal('vim three', getline(1)) + set cpo-=$ + call test_override('ALL', 0) + delfunc SaveFirstLine + %bw! +endfunc + +" Test for the '%' flag in 'cpo' (parenthesis matching inside strings) +func Test_cpo_percent() + let save_cpo = &cpo + new + call setline(1, ' if (strcmp("ab)cd(", s))') + set cpo-=% + normal 8|% + call assert_equal(28, col('.')) + normal 15|% + call assert_equal(27, col('.')) + normal 27|% + call assert_equal(15, col('.')) + call assert_beeps("normal 19|%") + call assert_beeps("normal 22|%") + set cpo+=% + normal 8|% + call assert_equal(28, col('.')) + normal 15|% + call assert_equal(19, col('.')) + normal 27|% + call assert_equal(22, col('.')) + normal 19|% + call assert_equal(15, col('.')) + normal 22|% + call assert_equal(27, col('.')) + close! + let &cpo = save_cpo +endfunc + +" Test for cursor movement with '-' in 'cpoptions' +func Test_cpo_minus() + throw 'Skipped: Nvim does not support cpoptions flag "-"' + new + call setline(1, ['foo', 'bar', 'baz']) + let save_cpo = &cpo + set cpo+=- + call assert_beeps('normal 10j') + call assert_equal(1, line('.')) + normal G + call assert_beeps('normal 10k') + call assert_equal(3, line('.')) + call assert_fails(10, 'E16:') + close! + let &cpo = save_cpo +endfunc + +" Test for the '+' flag in 'cpo' ('write file' command resets the 'modified' +" flag) +func Test_cpo_plus() + let save_cpo = &cpo + call writefile([], 'XfileCpoPlus') + new XfileCpoPlus + call setline(1, 'foo') + write X1 + call assert_equal(1, &modified) + set cpo+=+ + write X2 + call assert_equal(0, &modified) + close! + call delete('XfileCpoPlus') + call delete('X1') + call delete('X2') + let &cpo = save_cpo +endfunc + +" Test for the '*' flag in 'cpo' (':*' is same as ':@') +func Test_cpo_star() + throw 'Skipped: Nvim does not support cpoptions flag "*"' + let save_cpo = &cpo + let x = 0 + new + set cpo-=* + let @a = 'let x += 1' + call assert_fails('*a', 'E20:') + set cpo+=* + *a + call assert_equal(1, x) + close! + let &cpo = save_cpo +endfunc + +" Test for the '<' flag in 'cpo' is in the test_mapping.vim file + +" Test for the '>' flag in 'cpo' (use a new line when appending to a register) +func Test_cpo_gt() + let save_cpo = &cpo + new + call setline(1, 'one two') + set cpo-=> + let @r = '' + normal gg"Rye + normal "Rye + call assert_equal("oneone", @r) + set cpo+=> + let @r = '' + normal gg"Rye + normal "Rye + call assert_equal("\none\none", @r) + close! + let &cpo = save_cpo +endfunc + +" Test for the ';' flag in 'cpo' +" Test for t,f,F,T movement commands and 'cpo-;' setting +func Test_cpo_semicolon() + let save_cpo = &cpo + new + call append(0, ["aaa two three four", " zzz", "yyy ", + \ "bbb yee yoo four", "ccc two three four", + \ "ddd yee yoo four"]) + set cpo-=; + 1 + normal! 0tt;D + 2 + normal! 0fz;D + 3 + normal! $Fy;D + 4 + normal! $Ty;D + set cpo+=; + 5 + normal! 0tt;;D + 6 + normal! $Ty;;D + + call assert_equal('aaa two', getline(1)) + call assert_equal(' z', getline(2)) + call assert_equal('y', getline(3)) + call assert_equal('bbb y', getline(4)) + call assert_equal('ccc', getline(5)) + call assert_equal('ddd yee y', getline(6)) + close! + let &cpo = save_cpo +endfunc + +" Test for the '#' flag in 'cpo' (count before 'D', 'o' and 'O' operators) +func Test_cpo_hash() + throw 'Skipped: Nvim does not support cpoptions flag "#"' + let save_cpo = &cpo + new + set cpo-=# + call setline(1, ['one', 'two', 'three']) + normal gg2D + call assert_equal(['three'], getline(1, '$')) + normal gg2ofour + call assert_equal(['three', 'four', 'four'], getline(1, '$')) + normal gg2Otwo + call assert_equal(['two', 'two', 'three', 'four', 'four'], getline(1, '$')) + %d + set cpo+=# + call setline(1, ['one', 'two', 'three']) + normal gg2D + call assert_equal(['', 'two', 'three'], getline(1, '$')) + normal gg2oone + call assert_equal(['', 'one', 'two', 'three'], getline(1, '$')) + normal gg2Ozero + call assert_equal(['zero', '', 'one', 'two', 'three'], getline(1, '$')) + close! + let &cpo = save_cpo +endfunc + +" Test for the '&' flag in 'cpo'. The swap file is kept when a buffer is still +" loaded and ':preserve' is used. +func Test_cpo_ampersand() + throw 'Skipped: Nvim does not support cpoptions flag "&"' + call writefile(['one'], 'XfileCpoAmp') + let after =<< trim [CODE] + set cpo+=& + preserve + qall + [CODE] + if RunVim([], after, 'XfileCpoAmp') + call assert_equal(1, filereadable('.XfileCpoAmp.swp')) + call delete('.XfileCpoAmp.swp') + endif + call delete('XfileCpoAmp') +endfunc + +" Test for the '\' flag in 'cpo' (backslash in a [] range in a search pattern) +func Test_cpo_backslash() + throw 'Skipped: Nvim does not support cpoptions flag "\"' + let save_cpo = &cpo + new + call setline(1, ['', " \\-string"]) + set cpo-=\ + exe 'normal gg/[ \-]' .. "\<CR>n" + call assert_equal(3, col('.')) + set cpo+=\ + exe 'normal gg/[ \-]' .. "\<CR>n" + call assert_equal(2, col('.')) + close! + let &cpo = save_cpo +endfunc + +" Test for the '/' flag in 'cpo' is in the test_substitute.vim file + +" Test for the '{' flag in 'cpo' (the "{" and "}" commands stop at a { +" character at the start of a line) +func Test_cpo_brace() + throw 'Skipped: Nvim does not support cpoptions flag "{"' + let save_cpo = &cpo + new + call setline(1, ['', '{', ' int i;', '}', '']) + set cpo-={ + normal gg} + call assert_equal(5, line('.')) + normal G{ + call assert_equal(1, line('.')) + set cpo+={ + normal gg} + call assert_equal(2, line('.')) + normal G{ + call assert_equal(2, line('.')) + close! + let &cpo = save_cpo +endfunc + +" Test for the '.' flag in 'cpo' (:cd command fails if the current buffer is +" modified) +func Test_cpo_dot() + throw 'Skipped: Nvim does not support cpoptions flag "."' + let save_cpo = &cpo + new Xfoo + call setline(1, 'foo') + let save_dir = getcwd() + set cpo+=. + + " :cd should fail when buffer is modified and 'cpo' contains dot. + call assert_fails('cd ..', 'E747:') + call assert_equal(save_dir, getcwd()) + + " :cd with exclamation mark should succeed. + cd! .. + call assert_notequal(save_dir, getcwd()) + + " :cd should succeed when buffer has been written. + w! + exe 'cd ' .. fnameescape(save_dir) + call assert_equal(save_dir, getcwd()) + + call delete('Xfoo') + set cpo& + close! + let &cpo = save_cpo +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_cscope.vim b/src/nvim/testdir/test_cscope.vim deleted file mode 100644 index 76ea35fa10..0000000000 --- a/src/nvim/testdir/test_cscope.vim +++ /dev/null @@ -1,344 +0,0 @@ -" Test for cscope commands. - -source check.vim -CheckFeature cscope -CheckFeature quickfix - -if !executable('cscope') - throw 'Skipped: cscope program missing' -endif - -func CscopeSetupOrClean(setup) - if a:setup - noa sp samples/memfile_test.c - saveas! Xmemfile_test.c - call system('cscope -bk -fXcscope.out Xmemfile_test.c') - call system('cscope -bk -fXcscope2.out Xmemfile_test.c') - cscope add Xcscope.out - set cscopequickfix=s-,g-,d-,c-,t-,e-,f-,i-,a- - else - cscope kill -1 - for file in ['Xcscope.out', 'Xcscope2.out', 'Xmemfile_test.c'] - call delete(file) - endfo - endif -endfunc - -func Test_cscopeWithCscopeConnections() - call CscopeSetupOrClean(1) - " Test: E568: duplicate cscope database not added - try - set nocscopeverbose - cscope add Xcscope.out - set cscopeverbose - catch - call assert_report('exception thrown') - endtry - call assert_fails('cscope add', 'E560') - call assert_fails('cscope add Xcscope.out', 'E568') - call assert_fails('cscope add doesnotexist.out', 'E563') - if has('unix') - call assert_fails('cscope add /dev/null', 'E564:') - endif - - " Test: Find this C-Symbol - for cmd in ['cs find s main', 'cs find 0 main'] - let a = execute(cmd) - " Test where it moves the cursor - call assert_equal('main(void)', getline('.')) - " Test the output of the :cs command - call assert_match('\n(1 of 1): <<main>> main(void )', a) - endfor - - " Test: Find this definition - for cmd in ['cs find g test_mf_hash', - \ 'cs find 1 test_mf_hash', - \ 'cs find 1 test_mf_hash'] " leading space ignored. - exe cmd - call assert_equal(['', '/*', ' * Test mf_hash_*() functions.', ' */', ' static void', 'test_mf_hash(void)', '{'], getline(line('.')-5, line('.')+1)) - endfor - - " Test: Find functions called by this function - for cmd in ['cs find d test_mf_hash', 'cs find 2 test_mf_hash'] - let a = execute(cmd) - call assert_match('\n(1 of 42): <<mf_hash_init>> mf_hash_init(&ht);', a) - call assert_equal(' mf_hash_init(&ht);', getline('.')) - endfor - - " Test: Find functions calling this function - for cmd in ['cs find c test_mf_hash', 'cs find 3 test_mf_hash'] - let a = execute(cmd) - call assert_match('\n(1 of 1): <<main>> test_mf_hash();', a) - call assert_equal(' test_mf_hash();', getline('.')) - endfor - - " Test: Find this text string - for cmd in ['cs find t Bram', 'cs find 4 Bram'] - let a = execute(cmd) - call assert_match('(1 of 1): <<<unknown>>> \* VIM - Vi IMproved^Iby Bram Moolenaar', a) - call assert_equal(' * VIM - Vi IMproved by Bram Moolenaar', getline('.')) - endfor - - " Test: Find this egrep pattern - " test all matches returned by cscope - for cmd in ['cs find e ^\#includ.', 'cs find 6 ^\#includ.'] - let a = execute(cmd) - call assert_match('\n(1 of 3): <<<unknown>>> #include <assert.h>', a) - call assert_equal('#include <assert.h>', getline('.')) - cnext - call assert_equal('#include "main.c"', getline('.')) - cnext - call assert_equal('#include "memfile.c"', getline('.')) - call assert_fails('cnext', 'E553:') - endfor - - " Test: Find the same egrep pattern using lcscope this time. - let a = execute('lcs find e ^\#includ.') - call assert_match('\n(1 of 3): <<<unknown>>> #include <assert.h>', a) - call assert_equal('#include <assert.h>', getline('.')) - lnext - call assert_equal('#include "main.c"', getline('.')) - lnext - call assert_equal('#include "memfile.c"', getline('.')) - call assert_fails('lnext', 'E553:') - - " Test: Find this file - for cmd in ['cs find f Xmemfile_test.c', 'cs find 7 Xmemfile_test.c'] - enew - let a = execute(cmd) - call assert_true(a =~ '"Xmemfile_test.c" \d\+L, \d\+B') - call assert_equal('Xmemfile_test.c', @%) - endfor - - " Test: Find files #including this file - for cmd in ['cs find i assert.h', 'cs find 8 assert.h'] - enew - let a = execute(cmd) - let alines = split(a, '\n', 1) - call assert_equal('', alines[0]) - call assert_true(alines[1] =~ '"Xmemfile_test.c" \d\+L, \d\+B') - call assert_equal('(1 of 1): <<global>> #include <assert.h>', alines[2]) - call assert_equal('#include <assert.h>', getline('.')) - endfor - - " Test: Invalid find command - call assert_fails('cs find', 'E560:') - call assert_fails('cs find x', 'E560:') - - if has('float') - " Test: Find places where this symbol is assigned a value - " this needs a cscope >= 15.8 - " unfortunately, Travis has cscope version 15.7 - let cscope_version = systemlist('cscope --version')[0] - let cs_version = str2float(matchstr(cscope_version, '\d\+\(\.\d\+\)\?')) - if cs_version >= 15.8 - for cmd in ['cs find a item', 'cs find 9 item'] - let a = execute(cmd) - call assert_equal(['', '(1 of 4): <<test_mf_hash>> item = (mf_hashitem_T *)lalloc_clear(sizeof(*item), FALSE);'], split(a, '\n', 1)) - call assert_equal(' item = (mf_hashitem_T *)lalloc_clear(sizeof(*item), FALSE);', getline('.')) - cnext - call assert_equal(' item = mf_hash_find(&ht, key);', getline('.')) - cnext - call assert_equal(' item = mf_hash_find(&ht, key);', getline('.')) - cnext - call assert_equal(' item = mf_hash_find(&ht, key);', getline('.')) - endfor - endif - endif - - " Test: leading whitespace is not removed for cscope find text - let a = execute('cscope find t test_mf_hash') - call assert_equal(['', '(1 of 1): <<<unknown>>> test_mf_hash();'], split(a, '\n', 1)) - call assert_equal(' test_mf_hash();', getline('.')) - - " Test: test with scscope - let a = execute('scs find t Bram') - call assert_match('(1 of 1): <<<unknown>>> \* VIM - Vi IMproved^Iby Bram Moolenaar', a) - call assert_equal(' * VIM - Vi IMproved by Bram Moolenaar', getline('.')) - - " Test: cscope help - for cmd in ['cs', 'cs help', 'cs xxx'] - let a = execute(cmd) - call assert_match('^cscope commands:\n', a) - call assert_match('\nadd :', a) - call assert_match('\nfind :', a) - call assert_match('\nhelp : Show this message', a) - call assert_match('\nkill : Kill a connection', a) - call assert_match('\nreset: Reinit all connections', a) - call assert_match('\nshow : Show connections', a) - endfor - let a = execute('scscope help') - call assert_match('This cscope command does not support splitting the window\.', a) - - " Test: reset connections - let a = execute('cscope reset') - call assert_match('\nAdded cscope database.*Xcscope.out (#0)', a) - call assert_match('\nAll cscope databases reset', a) - - " Test: cscope show - let a = execute('cscope show') - call assert_match('\n 0 \d\+.*Xcscope.out\s*<none>', a) - - " Test: cstag and 'csto' option - set csto=0 - let a = execute('cstag TEST_COUNT') - call assert_match('(1 of 1): <<TEST_COUNT>> #define TEST_COUNT 50000', a) - call assert_equal('#define TEST_COUNT 50000', getline('.')) - call assert_fails('cstag DOES_NOT_EXIST', 'E257:') - set csto=1 - let a = execute('cstag index_to_key') - call assert_match('(1 of 1): <<index_to_key>> #define index_to_key(i) ((i) ^ 15167)', a) - call assert_equal('#define index_to_key(i) ((i) ^ 15167)', getline('.')) - call assert_fails('cstag DOES_NOT_EXIST', 'E257:') - call assert_fails('cstag', 'E562:') - let save_tags = &tags - set tags= - call assert_fails('cstag DOES_NOT_EXIST', 'E257:') - let a = execute('cstag index_to_key') - call assert_match('(1 of 1): <<index_to_key>> #define index_to_key(i) ((i) ^ 15167)', a) - let &tags = save_tags - - " Test: 'cst' option - set nocst - call assert_fails('tag TEST_COUNT', 'E426:') - set cst - let a = execute('tag TEST_COUNT') - call assert_match('(1 of 1): <<TEST_COUNT>> #define TEST_COUNT 50000', a) - call assert_equal('#define TEST_COUNT 50000', getline('.')) - let a = execute('tags') - call assert_match('1 1 TEST_COUNT\s\+\d\+\s\+#define index_to_key', a) - - " Test: 'cscoperelative' - call mkdir('Xcscoperelative') - cd Xcscoperelative - let a = execute('cs find g test_mf_hash') - call assert_notequal('test_mf_hash(void)', getline('.')) - set cscoperelative - let a = execute('cs find g test_mf_hash') - call assert_equal('test_mf_hash(void)', getline('.')) - set nocscoperelative - cd .. - call delete('Xcscoperelative', 'd') - - " Test: E259: no match found - call assert_fails('cscope find g DOES_NOT_EXIST', 'E259:') - - " Test: this should trigger call to cs_print_tags() - " Unclear how to check result though, we just exercise the code. - set cst cscopequickfix=s0 - call feedkeys(":cs find s main\<CR>", 't') - - " Test: cscope kill - call assert_fails('cscope kill', 'E560:') - call assert_fails('cscope kill 2', 'E261:') - call assert_fails('cscope kill xxx', 'E261:') - - let a = execute('cscope kill 0') - call assert_match('cscope connection 0 closed', a) - - cscope add Xcscope.out - let a = execute('cscope kill Xcscope.out') - call assert_match('cscope connection Xcscope.out closed', a) - - cscope add Xcscope.out . - let a = execute('cscope kill -1') - call assert_match('cscope connection .*Xcscope.out closed', a) - let a = execute('cscope kill -1') - call assert_equal('', a) - - " Test: 'csprg' option - call assert_equal('cscope', &csprg) - set csprg=doesnotexist - call assert_fails('cscope add Xcscope2.out', 'E609:') - set csprg=cscope - - " Test: multiple cscope connections - cscope add Xcscope.out - cscope add Xcscope2.out . -C - let a = execute('cscope show') - call assert_match('\n 0 \d\+.*Xcscope.out\s*<none>', a) - call assert_match('\n 1 \d\+.*Xcscope2.out\s*\.', a) - - " Test: test Ex command line completion - call feedkeys(":cs \<C-A>\<C-B>\"\<CR>", 'tx') - call assert_equal('"cs add find help kill reset show', @:) - - call feedkeys(":scs \<C-A>\<C-B>\"\<CR>", 'tx') - call assert_equal('"scs find', @:) - - call feedkeys(":cs find \<C-A>\<C-B>\"\<CR>", 'tx') - call assert_equal('"cs find a c d e f g i s t', @:) - - call feedkeys(":cs kill \<C-A>\<C-B>\"\<CR>", 'tx') - call assert_equal('"cs kill -1 0 1', @:) - - call feedkeys(":cs add Xcscope\<C-A>\<C-B>\"\<CR>", 'tx') - call assert_equal('"cs add Xcscope.out Xcscope2.out', @:) - - " Test: cscope_connection() - call assert_equal(cscope_connection(), 1) - call assert_equal(cscope_connection(0, 'out'), 1) - call assert_equal(cscope_connection(0, 'xxx'), 1) - - call assert_equal(cscope_connection(1, 'out'), 1) - call assert_equal(cscope_connection(1, 'xxx'), 0) - - call assert_equal(cscope_connection(2, 'out'), 0) - call assert_equal(cscope_connection(2, getcwd() .. '/Xcscope.out', 1), 1) - - call assert_equal(cscope_connection(3, 'xxx', '..'), 0) - call assert_equal(cscope_connection(3, 'out', 'xxx'), 0) - call assert_equal(cscope_connection(3, 'out', '.'), 1) - - call assert_equal(cscope_connection(4, 'out', '.'), 0) - - call assert_equal(cscope_connection(5, 'out'), 0) - call assert_equal(cscope_connection(-1, 'out'), 0) - - call CscopeSetupOrClean(0) -endfunc - -" Test ":cs add {dir}" (add the {dir}/cscope.out database) -func Test_cscope_add_dir() - call mkdir('Xcscopedir', 'p') - - " Cscope doesn't handle symlinks, so this needs to be resolved in case a - " shadow directory is being used. - let memfile = resolve('./samples/memfile_test.c') - call system('cscope -bk -fXcscopedir/cscope.out ' . memfile) - - cs add Xcscopedir - let a = execute('cscope show') - let lines = split(a, "\n", 1) - call assert_equal(3, len(lines)) - call assert_equal(' # pid database name prepend path', lines[0]) - call assert_equal('', lines[1]) - call assert_match('^ 0 \d\+.*Xcscopedir/cscope.out\s\+<none>$', lines[2]) - - cs kill -1 - call delete('Xcscopedir/cscope.out') - call assert_fails('cs add Xcscopedir', 'E563:') - - call delete('Xcscopedir', 'd') -endfunc - -func Test_cscopequickfix() - set cscopequickfix=s-,g-,d+,c-,t+,e-,f0,i-,a- - call assert_equal('s-,g-,d+,c-,t+,e-,f0,i-,a-', &cscopequickfix) - - call assert_fails('set cscopequickfix=x-', 'E474:') - call assert_fails('set cscopequickfix=s', 'E474:') - call assert_fails('set cscopequickfix=s7', 'E474:') - call assert_fails('set cscopequickfix=s-a', 'E474:') -endfunc - -func Test_withoutCscopeConnection() - call assert_equal(cscope_connection(), 0) - - call assert_fails('cscope find s main', 'E567:') - let a = execute('cscope show') - call assert_match('no cscope connections', a) -endfunc - - -" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim index 3b8a5f27ad..bb8e7cd5c5 100644 --- a/src/nvim/testdir/test_cursor_func.vim +++ b/src/nvim/testdir/test_cursor_func.vim @@ -1,7 +1,10 @@ " Tests for cursor() and other functions that get/set the cursor position +source check.vim + func Test_wrong_arguments() call assert_fails('call cursor(1. 3)', 'E474:') + call assert_fails('call cursor(v:_null_list)', 'E474:') endfunc func Test_move_cursor() @@ -22,7 +25,7 @@ func Test_move_cursor() call cursor(3, 0) call assert_equal([3, 1, 0, 1], getcurpos()[1:]) " below last line goes to last line - call cursor(9, 1) + eval [9, 1]->cursor() call assert_equal([4, 1, 0, 1], getcurpos()[1:]) " pass string arguments call cursor('3', '3') @@ -32,7 +35,7 @@ func Test_move_cursor() call cursor(1, 1, 1) call assert_equal([1, 1, 1], getcurpos()[1:3]) - call assert_equal(-1, cursor(-1, -1)) + call assert_fails('call cursor(-1, -1)', 'E475:') quit! endfunc @@ -82,34 +85,85 @@ func Test_screenpos() let winid = win_getid() let [winrow, wincol] = win_screenpos(winid) call assert_equal({'row': winrow, - \ 'col': wincol + 0, - \ 'curscol': wincol + 7, - \ 'endcol': wincol + 7}, winid->screenpos(1, 1)) + \ 'col': wincol + 0, + \ 'curscol': wincol + 7, + \ 'endcol': wincol + 7}, winid->screenpos(1, 1)) call assert_equal({'row': winrow, - \ 'col': wincol + 13, - \ 'curscol': wincol + 13, - \ 'endcol': wincol + 13}, winid->screenpos(1, 7)) + \ 'col': wincol + 13, + \ 'curscol': wincol + 13, + \ 'endcol': wincol + 13}, winid->screenpos(1, 7)) call assert_equal({'row': winrow + 2, - \ 'col': wincol + 1, - \ 'curscol': wincol + 1, - \ 'endcol': wincol + 1}, screenpos(winid, 2, 22)) + \ 'col': wincol + 1, + \ 'curscol': wincol + 1, + \ 'endcol': wincol + 1}, screenpos(winid, 2, 22)) setlocal number call assert_equal({'row': winrow + 3, - \ 'col': wincol + 9, - \ 'curscol': wincol + 9, - \ 'endcol': wincol + 9}, screenpos(winid, 2, 22)) + \ 'col': wincol + 9, + \ 'curscol': wincol + 9, + \ 'endcol': wincol + 9}, screenpos(winid, 2, 22)) + + let wininfo = getwininfo(winid)[0] + call setline(3, ['x']->repeat(wininfo.height)) + call setline(line('$') + 1, 'x'->repeat(wininfo.width * 3)) + setlocal nonumber display=lastline so=0 + exe "normal G\<C-Y>\<C-Y>" + redraw + call assert_equal({'row': winrow + wininfo.height - 1, + \ 'col': wincol + 7, + \ 'curscol': wincol + 7, + \ 'endcol': wincol + 7}, winid->screenpos(line('$'), 8)) + call assert_equal({'row': 0, 'col': 0, 'curscol': 0, 'endcol': 0}, + \ winid->screenpos(line('$'), 22)) + close call assert_equal({}, screenpos(999, 1, 1)) + bwipe! + set display& - call assert_equal({'col': 1, 'row': 1, 'endcol': 1, 'curscol': 1}, screenpos(win_getid(), 1, 1)) + call assert_equal(#{col: 1, row: 1, endcol: 1, curscol: 1}, screenpos(win_getid(), 1, 1)) " nmenu WinBar.TEST : setlocal winbar=TEST - call assert_equal({'col': 1, 'row': 2, 'endcol': 1, 'curscol': 1}, screenpos(win_getid(), 1, 1)) + call assert_equal(#{col: 1, row: 2, endcol: 1, curscol: 1}, screenpos(win_getid(), 1, 1)) " nunmenu WinBar.TEST setlocal winbar& endfunc +func Test_screenpos_fold() + CheckFeature folding + + enew! + call setline(1, range(10)) + 3,5fold + redraw + call assert_equal(2, screenpos(1, 2, 1).row) + call assert_equal(#{col: 1, row: 3, endcol: 1, curscol: 1}, screenpos(1, 3, 1)) + call assert_equal(#{col: 1, row: 3, endcol: 1, curscol: 1}, screenpos(1, 4, 1)) + call assert_equal(#{col: 1, row: 3, endcol: 1, curscol: 1}, screenpos(1, 5, 1)) + setlocal number + call assert_equal(#{col: 5, row: 3, endcol: 5, curscol: 5}, screenpos(1, 3, 1)) + call assert_equal(#{col: 5, row: 3, endcol: 5, curscol: 5}, screenpos(1, 4, 1)) + call assert_equal(#{col: 5, row: 3, endcol: 5, curscol: 5}, screenpos(1, 5, 1)) + call assert_equal(4, screenpos(1, 6, 1).row) + bwipe! +endfunc + +func Test_screenpos_diff() + CheckFeature diff + + enew! + call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']) + vnew + call setline(1, ['a', 'b', 'c', 'g', 'h', 'i']) + windo diffthis + wincmd w + call assert_equal(#{col: 3, row: 7, endcol: 3, curscol: 3}, screenpos(0, 4, 1)) + + windo diffoff + bwipe! + bwipe! +endfunc + func Test_screenpos_number() rightbelow new rightbelow 73vsplit @@ -121,6 +175,9 @@ func Test_screenpos_number() let pos = screenpos(winid, 1, 66) call assert_equal(winrow, pos.row) call assert_equal(wincol + 66 + 3, pos.col) + + call assert_fails('echo screenpos(0, 2, 1)', 'E966:') + close bwipe! endfunc @@ -241,8 +298,9 @@ endfunc " Test for the charcol() function func Test_charcol() - call assert_fails('call charcol({})', 'E731:') - call assert_equal(0, charcol(0)) + call assert_fails('call charcol({})', 'E1222:') + call assert_fails('call charcol(".", [])', 'E1210:') + call assert_fails('call charcol(0)', 'E1222:') new call setline(1, ['', "01\tà4è678", 'Ⅵ', '012345678']) @@ -298,6 +356,25 @@ func Test_charcol() call assert_equal([1, 10, 2, 10, 7], g:InsertCurrentCol) iunmap <F3> + " Test for getting the column number in another window. + let winid = win_getid() + new + call win_execute(winid, 'normal 1G') + call assert_equal(1, charcol('.', winid)) + call assert_equal(1, charcol('$', winid)) + call win_execute(winid, 'normal 2G6l') + call assert_equal(7, charcol('.', winid)) + call assert_equal(10, charcol('$', winid)) + + " calling from another tab page also works + tabnew + call assert_equal(7, charcol('.', winid)) + call assert_equal(10, charcol('$', winid)) + tabclose + + " unknown window ID + call assert_equal(0, charcol('.', 10001)) + %bw! endfunc @@ -353,8 +430,14 @@ func Test_setcursorcharpos() normal G call setcursorcharpos([1, 1]) call assert_equal([1, 1], [line('.'), col('.')]) + call setcursorcharpos([2, 7, 0]) call assert_equal([2, 9], [line('.'), col('.')]) + call setcursorcharpos([0, 7, 0]) + call assert_equal([2, 9], [line('.'), col('.')]) + call setcursorcharpos(0, 7, 0) + call assert_equal([2, 9], [line('.'), col('.')]) + call setcursorcharpos(3, 4) call assert_equal([3, 1], [line('.'), col('.')]) call setcursorcharpos([3, 1]) @@ -373,4 +456,26 @@ func Test_setcursorcharpos() %bw! endfunc +" Test for virtcol2col() +func Test_virtcol2col() + new + call setline(1, ["a\tb\tc"]) + call assert_equal(1, virtcol2col(0, 1, 1)) + call assert_equal(2, virtcol2col(0, 1, 2)) + call assert_equal(2, virtcol2col(0, 1, 8)) + call assert_equal(3, virtcol2col(0, 1, 9)) + call assert_equal(4, virtcol2col(0, 1, 10)) + call assert_equal(4, virtcol2col(0, 1, 16)) + call assert_equal(5, virtcol2col(0, 1, 17)) + call assert_equal(-1, virtcol2col(10, 1, 1)) + call assert_equal(-1, virtcol2col(0, 10, 1)) + call assert_equal(-1, virtcol2col(0, -1, 1)) + call assert_equal(-1, virtcol2col(0, 1, -1)) + call assert_equal(5, virtcol2col(0, 1, 20)) + call assert_fails('echo virtcol2col("0", 1, 20)', 'E1210:') + call assert_fails('echo virtcol2col(0, "1", 20)', 'E1210:') + call assert_fails('echo virtcol2col(0, 1, "1")', 'E1210:') + bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_cursorline.vim b/src/nvim/testdir/test_cursorline.vim index e85e9304a3..70f39a8601 100644 --- a/src/nvim/testdir/test_cursorline.vim +++ b/src/nvim/testdir/test_cursorline.vim @@ -3,26 +3,26 @@ source check.vim source screendump.vim -function! s:screen_attr(lnum) abort +func s:screen_attr(lnum) abort return map(range(1, 8), 'screenattr(a:lnum, v:val)') -endfunction +endfunc -function! s:test_windows(h, w) abort +func s:test_windows(h, w) abort call NewWindow(a:h, a:w) -endfunction +endfunc -function! s:close_windows() abort +func s:close_windows() abort call CloseWindow() -endfunction +endfunc -function! s:new_hi() abort +func s:new_hi() abort redir => save_hi silent! hi CursorLineNr redir END let save_hi = join(split(substitute(save_hi, '\s*xxx\s*', ' ', ''), "\n"), '') exe 'hi' save_hi 'ctermbg=0 guibg=Black' return save_hi -endfunction +endfunc func Test_cursorline_highlight1() let save_hi = s:new_hi() @@ -319,7 +319,7 @@ func Test_cursorline_cursorbind_horizontal_scroll() let lines =<< trim END call setline(1, 'aa bb cc dd ee ff gg hh ii jj kk ll mm' .. - \ ' nn oo pp qq rr ss tt uu vv ww xx yy zz') + \ ' nn oo pp qq rr ss tt uu vv ww xx yy zz') set nowrap " The following makes the cursor apparent on the screen dump set sidescroll=1 cursorcolumn diff --git a/src/nvim/testdir/test_debugger.vim b/src/nvim/testdir/test_debugger.vim index e038c0096a..f5177c8fb2 100644 --- a/src/nvim/testdir/test_debugger.vim +++ b/src/nvim/testdir/test_debugger.vim @@ -15,14 +15,18 @@ func CheckCWD() endfunc command! -nargs=0 -bar CheckCWD call CheckCWD() +" "options" argument can contain: +" 'msec' - time to wait for a match +" 'match' - "pattern" to use "lines" as pattern instead of text func CheckDbgOutput(buf, lines, options = {}) " Verify the expected output let lnum = 20 - len(a:lines) + let msec = get(a:options, 'msec', 1000) for l in a:lines if get(a:options, 'match', 'equal') ==# 'pattern' - call WaitForAssert({-> assert_match(l, term_getline(a:buf, lnum))}, 200) + call WaitForAssert({-> assert_match(l, term_getline(a:buf, lnum))}, msec) else - call WaitForAssert({-> assert_equal(l, term_getline(a:buf, lnum))}, 200) + call WaitForAssert({-> assert_equal(l, term_getline(a:buf, lnum))}, msec) endif let lnum += 1 endfor @@ -32,7 +36,7 @@ endfunc " If the expected output argument is supplied, then check for it. func RunDbgCmd(buf, cmd, ...) call term_sendkeys(a:buf, a:cmd . "\r") - call term_wait(a:buf) + call term_wait(a:buf, 20) if a:0 != 0 let options = #{match: 'equal'} @@ -198,7 +202,7 @@ func Test_Debugger() " Start a debug session, so that reading the last line from the terminal " works properly. - call RunDbgCmd(buf, ':debug echo Foo()') + call RunDbgCmd(buf, ':debug echo Foo()', ['cmd: echo Foo()']) " No breakpoints call RunDbgCmd(buf, 'breakl', ['No breakpoints defined']) @@ -814,9 +818,10 @@ func Test_Backtrace_CmdLine() \ '-S Xtest1.vim -c "debug call GlobalFunction()"', \ {'wait_for_ruler': 0}) - " Need to wait for the vim-in-terminal to be ready + " Need to wait for the vim-in-terminal to be ready. + " With valgrind this can take quite long. call CheckDbgOutput(buf, ['command line', - \ 'cmd: call GlobalFunction()']) + \ 'cmd: call GlobalFunction()'], #{msec: 5000}) " At this point the ontly thing in the stack is the cmdline call RunDbgCmd(buf, 'backtrace', [ @@ -967,14 +972,14 @@ func Test_debug_backtrace_level() " set a breakpoint and source file1.vim let buf = RunVimInTerminal( \ '-c "breakadd file 1 Xtest1.vim" -S Xtest1.vim', - \ #{ wait_for_ruler: 0 } ) + \ #{wait_for_ruler: 0}) call CheckDbgOutput(buf, [ \ 'Breakpoint in "' .. file1 .. '" line 1', \ 'Entering Debug mode. Type "cont" to continue.', \ 'command line..script ' .. file1, \ 'line 1: let s:file1_var = ''file1''' - \ ]) + \ ], #{msec: 5000}) " step through the initial declarations call RunDbgCmd(buf, 'step', [ 'line 2: let g:global_var = ''global''' ] ) @@ -1140,7 +1145,6 @@ func Test_breakpt_endif_intr() let caught_intr = 0 debuggreedy call feedkeys(":call F()\<CR>quit\<CR>", "xt") - call F() catch /^Vim:Interrupt$/ call assert_match('\.F, line 4', v:throwpoint) let caught_intr = 1 @@ -1171,7 +1175,6 @@ func Test_breakpt_else_intr() let caught_intr = 0 debuggreedy call feedkeys(":call F()\<CR>quit\<CR>", "xt") - call F() catch /^Vim:Interrupt$/ call assert_match('\.F, line 4', v:throwpoint) let caught_intr = 1 @@ -1200,7 +1203,6 @@ func Test_breakpt_endwhile_intr() let caught_intr = 0 debuggreedy call feedkeys(":call F()\<CR>quit\<CR>", "xt") - call F() catch /^Vim:Interrupt$/ call assert_match('\.F, line 4', v:throwpoint) let caught_intr = 1 @@ -1212,38 +1214,24 @@ func Test_breakpt_endwhile_intr() delfunc F endfunc -" Test for setting a breakpoint on an :endtry where an exception is pending to -" be processed and then quit the script. This should generate an interrupt and -" the thrown exception should be ignored. -func Test_breakpt_endtry_intr() - func F() - try - let g:Xpath ..= 'a' - throw "abc" - endtry - invalid_command +" Test for setting a breakpoint on a script local function +func Test_breakpt_scriptlocal_func() + let g:Xpath = '' + func s:G() + let g:Xpath ..= 'a' endfunc - let g:Xpath = '' - breakadd func 4 F - try - let caught_intr = 0 - let caught_abc = 0 - debuggreedy - call feedkeys(":call F()\<CR>quit\<CR>", "xt") - call F() - catch /abc/ - let caught_abc = 1 - catch /^Vim:Interrupt$/ - call assert_match('\.F, line 4', v:throwpoint) - let caught_intr = 1 - endtry + let funcname = expand("<SID>") .. "G" + exe "breakadd func 1 " .. funcname + debuggreedy + redir => output + call feedkeys(":call " .. funcname .. "()\<CR>c\<CR>", "xt") + redir END 0debuggreedy - call assert_equal(1, caught_intr) - call assert_equal(0, caught_abc) + call assert_match('Breakpoint in "' .. funcname .. '" line 1', output) call assert_equal('a', g:Xpath) breakdel * - delfunc F + exe "delfunc " .. funcname endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_delete.vim b/src/nvim/testdir/test_delete.vim index b23a3bd025..6b49f153c6 100644 --- a/src/nvim/testdir/test_delete.vim +++ b/src/nvim/testdir/test_delete.vim @@ -1,5 +1,7 @@ " Test for delete(). +source check.vim + func Test_file_delete() split Xfile call setline(1, ['a', 'b']) @@ -41,9 +43,7 @@ func Test_recursive_delete() endfunc func Test_symlink_delete() - if !has('unix') - return - endif + CheckUnix split Xfile call setline(1, ['a', 'b']) wq @@ -56,9 +56,7 @@ func Test_symlink_delete() endfunc func Test_symlink_dir_delete() - if !has('unix') - return - endif + CheckUnix call mkdir('Xdir1') silent !ln -s Xdir1 Xlink call assert_true(isdirectory('Xdir1')) @@ -70,9 +68,7 @@ func Test_symlink_dir_delete() endfunc func Test_symlink_recursive_delete() - if !has('unix') - return - endif + CheckUnix call mkdir('Xdir3') call mkdir('Xdir3/subdir') call mkdir('Xdir4') diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index ea453b7174..0049398776 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -1,4 +1,5 @@ " Tests for diff mode + source shared.vim source screendump.vim source check.vim @@ -243,6 +244,36 @@ func Test_diffput_two() bwipe! b endfunc +" Test for :diffget/:diffput with a range that is inside a diff chunk +func Test_diffget_diffput_range() + call setline(1, range(1, 10)) + new + call setline(1, range(11, 20)) + windo diffthis + 3,5diffget + call assert_equal(['13', '14', '15'], getline(3, 5)) + call setline(1, range(1, 10)) + 4,8diffput + wincmd p + call assert_equal(['13', '4', '5', '6', '7', '8', '19'], getline(3, 9)) + %bw! +endfunc + +" Test for :diffget/:diffput with an empty buffer and a non-empty buffer +func Test_diffget_diffput_empty_buffer() + %d _ + new + call setline(1, 'one') + windo diffthis + diffget + call assert_equal(['one'], getline(1, '$')) + %d _ + diffput + wincmd p + call assert_equal([''], getline(1, '$')) + %bw! +endfunc + " :diffput and :diffget completes names of buffers which " are in diff mode and which are different then current buffer. " No completion when the current window is not in diff mode. @@ -621,9 +652,7 @@ func Test_diff_move_to() endfunc func Test_diffexpr() - if !executable('diff') - return - endif + CheckExecutable diff func DiffExpr() " Prepend some text to check diff type detection @@ -647,17 +676,31 @@ func Test_diffexpr() call assert_equal(normattr, screenattr(1, 1)) call assert_equal(normattr, screenattr(2, 1)) call assert_notequal(normattr, screenattr(3, 1)) + diffoff! + " Try using an non-existing function for 'diffexpr'. + set diffexpr=NewDiffFunc() + call assert_fails('windo diffthis', ['E117:', 'E97:']) diffoff! + + " Using a script-local function + func s:NewDiffExpr() + endfunc + set diffexpr=s:NewDiffExpr() + call assert_equal(expand('<SID>') .. 'NewDiffExpr()', &diffexpr) + set diffexpr=<SID>NewDiffExpr() + call assert_equal(expand('<SID>') .. 'NewDiffExpr()', &diffexpr) + %bwipe! set diffexpr& diffopt& + delfunc DiffExpr + delfunc s:NewDiffExpr endfunc func Test_diffpatch() " The patch program on MS-Windows may fail or hang. - if !executable('patch') || !has('unix') - return - endif + CheckExecutable patch + CheckUnix new insert *************** @@ -744,17 +787,13 @@ func Test_diff_hlID() call diff_hlID(-1, 1)->synIDattr("name")->assert_equal("") - call assert_equal(diff_hlID(1, 1), hlID("DiffChange")) call diff_hlID(1, 1)->synIDattr("name")->assert_equal("DiffChange") - call assert_equal(diff_hlID(1, 2), hlID("DiffText")) call diff_hlID(1, 2)->synIDattr("name")->assert_equal("DiffText") call diff_hlID(2, 1)->synIDattr("name")->assert_equal("") - call assert_equal(diff_hlID(3, 1), hlID("DiffAdd")) call diff_hlID(3, 1)->synIDattr("name")->assert_equal("DiffAdd") - call diff_hlID(4, 1)->synIDattr("name")->assert_equal("") + eval 4->diff_hlID(1)->synIDattr("name")->assert_equal("") wincmd w - call assert_equal(diff_hlID(1, 1), hlID("DiffChange")) call assert_equal(synIDattr(diff_hlID(1, 1), "name"), "DiffChange") call assert_equal(synIDattr(diff_hlID(2, 1), "name"), "") call assert_equal(synIDattr(diff_hlID(3, 1), "name"), "") @@ -1202,6 +1241,42 @@ func Test_diff_maintains_change_mark() delfunc DiffMaintainsChangeMark endfunc +" Test for 'patchexpr' +func Test_patchexpr() + let g:patch_args = [] + func TPatch() + call add(g:patch_args, readfile(v:fname_in)) + call add(g:patch_args, readfile(v:fname_diff)) + call writefile(['output file'], v:fname_out) + endfunc + set patchexpr=TPatch() + + call writefile(['input file'], 'Xinput') + call writefile(['diff file'], 'Xdiff') + %bwipe! + edit Xinput + diffpatch Xdiff + call assert_equal('output file', getline(1)) + call assert_equal('Xinput.new', bufname()) + call assert_equal(2, winnr('$')) + call assert_true(&diff) + + " Using a script-local function + func s:NewPatchExpr() + endfunc + set patchexpr=s:NewPatchExpr() + call assert_equal(expand('<SID>') .. 'NewPatchExpr()', &patchexpr) + set patchexpr=<SID>NewPatchExpr() + call assert_equal(expand('<SID>') .. 'NewPatchExpr()', &patchexpr) + + call delete('Xinput') + call delete('Xdiff') + set patchexpr& + delfunc TPatch + delfunc s:NewPatchExpr + %bwipe! +endfunc + func Test_diff_rnu() CheckScreendump @@ -1295,6 +1370,89 @@ func Test_diff_filler_cursorcolumn() call delete('Xtest_diff_cuc') endfunc +" Test for adding/removing lines inside diff chunks, between diff chunks +" and before diff chunks +func Test_diff_modify_chunks() + enew! + let w2_id = win_getid() + call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']) + new + let w1_id = win_getid() + call setline(1, ['a', '2', '3', 'd', 'e', 'f', '7', '8', 'i']) + windo diffthis + + " remove a line between two diff chunks and create a new diff chunk + call win_gotoid(w2_id) + 5d + call win_gotoid(w1_id) + call diff_hlID(5, 1)->synIDattr('name')->assert_equal('DiffAdd') + + " add a line between two diff chunks + call win_gotoid(w2_id) + normal! 4Goe + call win_gotoid(w1_id) + call diff_hlID(4, 1)->synIDattr('name')->assert_equal('') + call diff_hlID(5, 1)->synIDattr('name')->assert_equal('') + + " remove all the lines in a diff chunk. + call win_gotoid(w2_id) + 7,8d + call win_gotoid(w1_id) + let hl = range(1, 9)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')}) + call assert_equal(['', 'DiffText', 'DiffText', '', '', '', 'DiffAdd', + \ 'DiffAdd', ''], hl) + + " remove lines from one diff chunk to just before the next diff chunk + call win_gotoid(w2_id) + call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']) + 2,6d + call win_gotoid(w1_id) + let hl = range(1, 9)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')}) + call assert_equal(['', 'DiffText', 'DiffText', 'DiffAdd', 'DiffAdd', + \ 'DiffAdd', 'DiffAdd', 'DiffAdd', ''], hl) + + " remove lines just before the top of a diff chunk + call win_gotoid(w2_id) + call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']) + 5,6d + call win_gotoid(w1_id) + let hl = range(1, 9)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')}) + call assert_equal(['', 'DiffText', 'DiffText', '', 'DiffText', 'DiffText', + \ 'DiffAdd', 'DiffAdd', ''], hl) + + " remove line after the end of a diff chunk + call win_gotoid(w2_id) + call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']) + 4d + call win_gotoid(w1_id) + let hl = range(1, 9)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')}) + call assert_equal(['', 'DiffText', 'DiffText', 'DiffAdd', '', '', 'DiffText', + \ 'DiffText', ''], hl) + + " remove lines starting from the end of one diff chunk and ending inside + " another diff chunk + call win_gotoid(w2_id) + call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']) + 4,7d + call win_gotoid(w1_id) + let hl = range(1, 9)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')}) + call assert_equal(['', 'DiffText', 'DiffText', 'DiffText', 'DiffAdd', + \ 'DiffAdd', 'DiffAdd', 'DiffAdd', ''], hl) + + " removing the only remaining diff chunk should make the files equal + call win_gotoid(w2_id) + call setline(1, ['a', '2', '3', 'x', 'd', 'e', 'f', 'x', '7', '8', 'i']) + 8d + let hl = range(1, 10)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')}) + call assert_equal(['', '', '', 'DiffAdd', '', '', '', '', '', ''], hl) + call win_gotoid(w2_id) + 4d + call win_gotoid(w1_id) + let hl = range(1, 9)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')}) + call assert_equal(['', '', '', '', '', '', '', '', ''], hl) + + %bw! +endfunc func Test_diff_binary() CheckScreendump diff --git a/src/nvim/testdir/test_digraph.vim b/src/nvim/testdir/test_digraph.vim index acc34e5e7c..f08dff8605 100644 --- a/src/nvim/testdir/test_digraph.vim +++ b/src/nvim/testdir/test_digraph.vim @@ -211,6 +211,8 @@ func Test_digraphs() call Put_Dig("00") call Put_Dig("el") call assert_equal(['␀', 'ü', '∞', 'l'], getline(line('.')-3,line('.'))) + call assert_fails('exe "digraph a\<Esc> 100"', 'E104:') + call assert_fails('exe "digraph \<Esc>a 100"', 'E104:') call assert_fails('digraph xy z', 'E39:') call assert_fails('digraph x', 'E1214:') bw! @@ -491,6 +493,17 @@ func Test_show_digraph_cp1251() bwipe! endfunc +" Test for error in a keymap file +func Test_loadkeymap_error() + if !has('keymap') + return + endif + call assert_fails('loadkeymap', 'E105:') + call writefile(['loadkeymap', 'a'], 'Xkeymap') + call assert_fails('source Xkeymap', 'E791:') + call delete('Xkeymap') +endfunc + " Test for the characters displayed on the screen when entering a digraph func Test_entering_digraph() CheckRunVimInTerminal diff --git a/src/nvim/testdir/test_display.vim b/src/nvim/testdir/test_display.vim index 6938abbc28..b642f39c9f 100644 --- a/src/nvim/testdir/test_display.vim +++ b/src/nvim/testdir/test_display.vim @@ -195,8 +195,6 @@ func Test_edit_long_file_name() call VerifyScreenDump(buf, 'Test_long_file_name_1', {}) - call term_sendkeys(buf, ":q\<cr>") - " clean up call StopVimInTerminal(buf) call delete(longName) @@ -228,7 +226,6 @@ endfunc " Test for scrolling that modifies buffer during visual block func Test_visual_block_scroll() - " See test/functional/legacy/visual_mode_spec.lua CheckScreendump let lines =<< trim END @@ -239,7 +236,7 @@ func Test_visual_block_scroll() END let filename = 'Xvisualblockmodifiedscroll' - call writefile(lines, filename) + call writefile(lines, filename, 'D') let buf = RunVimInTerminal('-S '.filename, #{rows: 7}) call term_sendkeys(buf, "V\<C-D>\<C-D>") @@ -247,11 +244,40 @@ func Test_visual_block_scroll() call VerifyScreenDump(buf, 'Test_display_visual_block_scroll', {}) call StopVimInTerminal(buf) - call delete(filename) +endfunc + +" Test for clearing paren highlight when switching buffers +func Test_matchparen_clear_highlight() + CheckScreendump + + let lines =<< trim END + source $VIMRUNTIME/plugin/matchparen.vim + set hidden + call setline(1, ['()']) + normal 0 + + func OtherBuffer() + enew + exe "normal iaa\<Esc>0" + endfunc + END + call writefile(lines, 'XMatchparenClear', 'D') + let buf = RunVimInTerminal('-S XMatchparenClear', #{rows: 5}) + call VerifyScreenDump(buf, 'Test_matchparen_clear_highlight_1', {}) + + call term_sendkeys(buf, ":call OtherBuffer()\<CR>:\<Esc>") + call VerifyScreenDump(buf, 'Test_matchparen_clear_highlight_2', {}) + + call term_sendkeys(buf, "\<C-^>:\<Esc>") + call VerifyScreenDump(buf, 'Test_matchparen_clear_highlight_1', {}) + + call term_sendkeys(buf, "\<C-^>:\<Esc>") + call VerifyScreenDump(buf, 'Test_matchparen_clear_highlight_2', {}) + + call StopVimInTerminal(buf) endfunc func Test_display_scroll_at_topline() - " See test/functional/legacy/display_spec.lua CheckScreendump let buf = RunVimInTerminal('', #{cols: 20}) @@ -309,6 +335,88 @@ func Test_eob_fillchars() close endfunc +" Test for 'foldopen', 'foldclose' and 'foldsep' in 'fillchars' +func Test_fold_fillchars() + new + set fdc=2 foldenable foldmethod=manual + call setline(1, ['one', 'two', 'three', 'four', 'five']) + 2,4fold + " First check for the default setting for a closed fold + let lines = ScreenLines([1, 3], 8) + let expected = [ + \ ' one ', + \ '+ +-- 3', + \ ' five ' + \ ] + call assert_equal(expected, lines) + normal 2Gzo + " check the characters for an open fold + let lines = ScreenLines([1, 5], 8) + let expected = [ + \ ' one ', + \ '- two ', + \ '| three ', + \ '| four ', + \ ' five ' + \ ] + call assert_equal(expected, lines) + + " change the setting + set fillchars=vert:\|,fold:-,eob:~,foldopen:[,foldclose:],foldsep:- + + " check the characters for an open fold + let lines = ScreenLines([1, 5], 8) + let expected = [ + \ ' one ', + \ '[ two ', + \ '- three ', + \ '- four ', + \ ' five ' + \ ] + call assert_equal(expected, lines) + + " check the characters for a closed fold + normal 2Gzc + let lines = ScreenLines([1, 3], 8) + let expected = [ + \ ' one ', + \ '] +-- 3', + \ ' five ' + \ ] + call assert_equal(expected, lines) + + %bw! + set fillchars& fdc& foldmethod& foldenable& +endfunc + +func Test_local_fillchars() + CheckScreendump + + let lines =<< trim END + call setline(1, ['window 1']->repeat(3)) + setlocal fillchars=stl:1,stlnc:a,vert:=,eob:x + vnew + call setline(1, ['window 2']->repeat(3)) + setlocal fillchars=stl:2,stlnc:b,vert:+,eob:y + new + wincmd J + call setline(1, ['window 3']->repeat(3)) + setlocal fillchars=stl:3,stlnc:c,vert:<,eob:z + vnew + call setline(1, ['window 4']->repeat(3)) + setlocal fillchars=stl:4,stlnc:d,vert:>,eob:o + END + call writefile(lines, 'Xdisplayfillchars') + let buf = RunVimInTerminal('-S Xdisplayfillchars', #{rows: 12}) + call VerifyScreenDump(buf, 'Test_display_fillchars_1', {}) + + call term_sendkeys(buf, ":wincmd k\r") + call VerifyScreenDump(buf, 'Test_display_fillchars_2', {}) + + call StopVimInTerminal(buf) + call delete('Xdisplayfillchars') +endfunc + func Test_display_linebreak_breakat() new vert resize 25 @@ -325,30 +433,49 @@ func Test_display_linebreak_breakat() let &breakat=_breakat endfunc -func Test_display_lastline() - CheckScreendump - +func Run_Test_display_lastline(euro) let lines =<< trim END - call setline(1, ['aaa', 'b'->repeat(100)]) + call setline(1, ['aaa', 'b'->repeat(200)]) set display=truncate + vsplit 100wincmd < END - call writefile(lines, 'XdispLastline') + if a:euro != '' + let lines[2] = 'set fillchars=vert:\|,lastline:€' + endif + call writefile(lines, 'XdispLastline', 'D') let buf = RunVimInTerminal('-S XdispLastline', #{rows: 10}) - call VerifyScreenDump(buf, 'Test_display_lastline_1', {}) + call VerifyScreenDump(buf, $'Test_display_lastline_{a:euro}1', {}) call term_sendkeys(buf, ":set display=lastline\<CR>") - call VerifyScreenDump(buf, 'Test_display_lastline_2', {}) + call VerifyScreenDump(buf, $'Test_display_lastline_{a:euro}2', {}) call term_sendkeys(buf, ":100wincmd >\<CR>") - call VerifyScreenDump(buf, 'Test_display_lastline_3', {}) + call VerifyScreenDump(buf, $'Test_display_lastline_{a:euro}3', {}) call term_sendkeys(buf, ":set display=truncate\<CR>") - call VerifyScreenDump(buf, 'Test_display_lastline_4', {}) + call VerifyScreenDump(buf, $'Test_display_lastline_{a:euro}4', {}) + + call term_sendkeys(buf, ":close\<CR>") + call term_sendkeys(buf, ":3split\<CR>") + call VerifyScreenDump(buf, $'Test_display_lastline_{a:euro}5', {}) + + call term_sendkeys(buf, ":close\<CR>") + call term_sendkeys(buf, ":2vsplit\<CR>") + call VerifyScreenDump(buf, $'Test_display_lastline_{a:euro}6', {}) call StopVimInTerminal(buf) - call delete('XdispLastline') +endfunc + +func Test_display_lastline() + CheckScreendump + + call Run_Test_display_lastline('') + call Run_Test_display_lastline('euro_') + + call assert_fails(':set fillchars=lastline:', 'E474:') + call assert_fails(':set fillchars=lastline:〇', 'E474:') endfunc diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim index e26bbdc5be..fd54f77ccb 100644 --- a/src/nvim/testdir/test_edit.vim +++ b/src/nvim/testdir/test_edit.vim @@ -330,6 +330,16 @@ func Test_edit_11_indentexpr() set cinkeys&vim indentkeys&vim set nocindent indentexpr= delfu Do_Indent + + " Using a script-local function + func s:NewIndentExpr() + endfunc + set indentexpr=s:NewIndentExpr() + call assert_equal(expand('<SID>') .. 'NewIndentExpr()', &indentexpr) + set indentexpr=<SID>NewIndentExpr() + call assert_equal(expand('<SID>') .. 'NewIndentExpr()', &indentexpr) + set indentexpr& + bw! endfunc @@ -713,23 +723,32 @@ endfunc func Test_edit_CTRL_N() " Check keyword completion - new - set complete=. - call setline(1, ['INFER', 'loWER', '', '', ]) - call cursor(3, 1) - call feedkeys("Ai\<c-n>\<cr>\<esc>", "tnix") - call feedkeys("ILO\<c-n>\<cr>\<esc>", 'tnix') - call assert_equal(['INFER', 'loWER', 'i', 'LO', '', ''], getline(1, '$')) - %d - call setline(1, ['INFER', 'loWER', '', '', ]) - call cursor(3, 1) - set ignorecase infercase - call feedkeys("Ii\<c-n>\<cr>\<esc>", "tnix") - call feedkeys("ILO\<c-n>\<cr>\<esc>", 'tnix') - call assert_equal(['INFER', 'loWER', 'infer', 'LOWER', '', ''], getline(1, '$')) - - set noignorecase noinfercase complete& - bw! + " for e in ['latin1', 'utf-8'] + for e in ['utf-8'] + exe 'set encoding=' .. e + new + set complete=. + call setline(1, ['INFER', 'loWER', '', '', ]) + call cursor(3, 1) + call feedkeys("Ai\<c-n>\<cr>\<esc>", "tnix") + call feedkeys("ILO\<c-n>\<cr>\<esc>", 'tnix') + call assert_equal(['INFER', 'loWER', 'i', 'LO', '', ''], getline(1, '$'), e) + %d + call setline(1, ['INFER', 'loWER', '', '', ]) + call cursor(3, 1) + set ignorecase infercase + call feedkeys("Ii\<c-n>\<cr>\<esc>", "tnix") + call feedkeys("ILO\<c-n>\<cr>\<esc>", 'tnix') + call assert_equal(['INFER', 'loWER', 'infer', 'LOWER', '', ''], getline(1, '$'), e) + set noignorecase noinfercase + %d + call setline(1, ['one word', 'two word']) + exe "normal! Goo\<C-P>\<C-X>\<C-P>" + call assert_equal('one word', getline(3)) + %d + set complete& + bw! + endfor endfunc func Test_edit_CTRL_O() @@ -893,6 +912,24 @@ func Test_edit_CTRL_T() bw! endfunc +" Test thesaurus completion with different encodings +func Test_thesaurus_complete_with_encoding() + call writefile(['angry furious mad enraged'], 'Xthesaurus') + set thesaurus=Xthesaurus + " for e in ['latin1', 'utf-8'] + for e in ['utf-8'] + exe 'set encoding=' .. e + new + call setline(1, 'mad') + call cursor(1, 1) + call feedkeys("A\<c-x>\<c-t>\<cr>\<esc>", 'tnix') + call assert_equal(['mad', ''], getline(1, '$')) + bw! + endfor + set thesaurus= + call delete('Xthesaurus') +endfunc + " Test 'thesaurusfunc' func MyThesaurus(findstart, base) let mythesaurus = [ @@ -1201,15 +1238,11 @@ func Test_edit_MOUSE() call assert_equal(24, line('w0')) call assert_equal([0, 24, 2, 0], getpos('.')) - " call test_setmouse(4, 3) - call nvim_input_mouse('left', 'press', '', 0, 3, 2) " set mouse position - call getchar() " discard mouse event but keep mouse position + call Ntest_setmouse(4, 3) call feedkeys("A\<LeftMouse>\<esc>", 'tnix') call assert_equal([0, 27, 2, 0], getpos('.')) set mousemodel=extend - " call test_setmouse(5, 3) - call nvim_input_mouse('right', 'press', '', 0, 4, 2) " set mouse position - call getchar() " discard mouse event but keep mouse position + call Ntest_setmouse(5, 3) call feedkeys("A\<RightMouse>\<esc>\<esc>", 'tnix') call assert_equal([0, 28, 2, 0], getpos('.')) set mousemodel& @@ -1410,9 +1443,7 @@ endfunc func Test_edit_rightleft() " Cursor in rightleft mode moves differently - if !exists("+rightleft") - return - endif + CheckFeature rightleft call NewWindow(10, 20) call setline(1, ['abc', 'def', 'ghi']) call cursor(1, 2) @@ -1457,6 +1488,13 @@ func Test_edit_rightleft() \" ihg", \" ~"] call assert_equal(join(expect, "\n"), join(lines, "\n")) + %d _ + " call test_override('redraw_flag', 1) + " call test_override('char_avail', 1) + call feedkeys("a\<C-V>x41", "xt") + redraw! + call assert_equal(repeat(' ', 19) .. 'A', Screenline(1)) + " call test_override('ALL', 0) set norightleft bw! endfunc @@ -1701,40 +1739,6 @@ func Test_edit_illegal_filename() close! endfunc -" Test for inserting text in a line with only spaces ('H' flag in 'cpoptions') -func Test_edit_cpo_H() - throw 'Skipped: Nvim does not support cpoptions flag "H"' - new - call setline(1, ' ') - normal! Ia - call assert_equal(' a', getline(1)) - set cpo+=H - call setline(1, ' ') - normal! Ia - call assert_equal(' a ', getline(1)) - set cpo-=H - close! -endfunc - -" Test for inserting tab in virtual replace mode ('L' flag in 'cpoptions') -func Test_edit_cpo_L() - new - call setline(1, 'abcdefghijklmnopqr') - exe "normal 0gR\<Tab>" - call assert_equal("\<Tab>ijklmnopqr", getline(1)) - set cpo+=L - set list - call setline(1, 'abcdefghijklmnopqr') - exe "normal 0gR\<Tab>" - call assert_equal("\<Tab>cdefghijklmnopqr", getline(1)) - set nolist - call setline(1, 'abcdefghijklmnopqr') - exe "normal 0gR\<Tab>" - call assert_equal("\<Tab>ijklmnopqr", getline(1)) - set cpo-=L - %bw! -endfunc - " Test for editing a directory func Test_edit_is_a_directory() CheckEnglish @@ -1842,7 +1846,8 @@ endfunc " Test for editing a file without read permission func Test_edit_file_no_read_perm() CheckUnix - CheckNotBSD + CheckNotRoot + call writefile(['one', 'two'], 'Xfile') call setfperm('Xfile', '-w-------') new @@ -1868,17 +1873,117 @@ func Test_edit_insertmode_ex_edit() call writefile(lines, 'Xtest_edit_insertmode_ex_edit') let buf = RunVimInTerminal('-S Xtest_edit_insertmode_ex_edit', #{rows: 6}) - call TermWait(buf, 50) - call assert_match('^-- INSERT --\s*$', term_getline(buf, 6)) + call WaitForAssert({-> assert_match('^-- INSERT --\s*$', term_getline(buf, 6))}) call term_sendkeys(buf, "\<C-B>\<C-L>") - call TermWait(buf, 50) - call assert_notmatch('^-- INSERT --\s*$', term_getline(buf, 6)) + call WaitForAssert({-> assert_notmatch('^-- INSERT --\s*$', term_getline(buf, 6))}) " clean up call StopVimInTerminal(buf) call delete('Xtest_edit_insertmode_ex_edit') endfunc +" Pressing escape in 'insertmode' should beep +" FIXME: Execute this later, when using valgrind it makes the next test +" Test_edit_insertmode_ex_edit() fail. +func Test_z_edit_insertmode_esc_beeps() + new + " set insertmode + " call assert_beeps("call feedkeys(\"one\<Esc>\", 'xt')") + set insertmode& + " unsupported "CTRL-G l" command should beep in insert mode. + call assert_beeps("normal i\<C-G>l") + bwipe! +endfunc + +" Test for 'hkmap' and 'hkmapp' +func Test_edit_hkmap() + CheckFeature rightleft + if has('win32') && !has('gui') + " Test fails on the MS-Windows terminal version + return + endif + new + + set revins hkmap + let str = 'abcdefghijklmnopqrstuvwxyz' + let str ..= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + let str ..= '`/'',.;' + call feedkeys('i' .. str, 'xt') + let expected = "óõú,.;" + let expected ..= "ZYXWVUTSRQPONMLKJIHGFEDCBA" + let expected ..= "æèñ'äåàãø/ôíîöêìçïéòë÷âáðù" + call assert_equal(expected, getline(1)) + + %d + set revins hkmap hkmapp + let str = 'abcdefghijklmnopqrstuvwxyz' + let str ..= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + call feedkeys('i' .. str, 'xt') + let expected = "õYXWVUTSRQóOïíLKJIHGFEDêBA" + let expected ..= "öòXùåèúæø'ôñðîì÷çéäâóǟãëáà" + call assert_equal(expected, getline(1)) + + set revins& hkmap& hkmapp& + close! +endfunc + +" Test for 'allowrevins' and using CTRL-_ in insert mode +func Test_edit_allowrevins() + CheckFeature rightleft + new + set allowrevins + call feedkeys("iABC\<C-_>DEF\<C-_>GHI", 'xt') + call assert_equal('ABCFEDGHI', getline(1)) + set allowrevins& + close! +endfunc + +" Test for inserting a register in insert mode using CTRL-R +func Test_edit_insert_reg() + throw 'Skipped: use test/functional/legacy/edit_spec.lua' + new + let g:Line = '' + func SaveFirstLine() + let g:Line = Screenline(1) + return 'r' + endfunc + inoremap <expr> <buffer> <F2> SaveFirstLine() + call test_override('redraw_flag', 1) + call test_override('char_avail', 1) + let @r = 'sample' + call feedkeys("a\<C-R>=SaveFirstLine()\<CR>", "xt") + call assert_equal('"', g:Line) + call test_override('ALL', 0) + close! +endfunc + +" When a character is inserted at the last position of the last line in a +" window, the window contents should be scrolled one line up. If the top line +" is part of a fold, then the entire fold should be scrolled up. +func Test_edit_lastline_scroll() + new + let h = winheight(0) + let lines = ['one', 'two', 'three'] + let lines += repeat(['vim'], h - 4) + call setline(1, lines) + call setline(h, repeat('x', winwidth(0) - 1)) + call feedkeys("GAx", 'xt') + redraw! + call assert_equal(h - 1, winline()) + call assert_equal(2, line('w0')) + + " scroll with a fold + 1,2fold + normal gg + call setline(h + 1, repeat('x', winwidth(0) - 1)) + call feedkeys("GAx", 'xt') + redraw! + call assert_equal(h - 1, winline()) + call assert_equal(3, line('w0')) + + close! +endfunc + func Test_edit_browse() " in the GUI this opens a file picker, we only test the terminal behavior CheckNotGui @@ -1904,4 +2009,76 @@ func Test_read_invalid() set encoding=utf-8 endfunc +" Test for the 'revins' option +func Test_edit_revins() + CheckFeature rightleft + new + set revins + exe "normal! ione\ttwo three" + call assert_equal("eerht owt\teno", getline(1)) + call setline(1, "one\ttwo three") + normal! gg$bi a + call assert_equal("one\ttwo a three", getline(1)) + exe "normal! $bi\<BS>\<BS>" + call assert_equal("one\ttwo a ree", getline(1)) + exe "normal! 0wi\<C-W>" + call assert_equal("one\t a ree", getline(1)) + exe "normal! 0wi\<C-U>" + call assert_equal("one\t ", getline(1)) + " newline in insert mode starts at the end of the line + call setline(1, 'one two three') + exe "normal! wi\nfour" + call assert_equal(['one two three', 'ruof'], getline(1, '$')) + set revins& + bw! +endfunc + +" Test for getting the character of the line below after "p" +func Test_edit_put_CTRL_E() + " set encoding=latin1 + new + let @" = '' + sil! norm orggRx + sil! norm pr + call assert_equal(['r', 'r'], getline(1, 2)) + bwipe! + set encoding=utf-8 +endfunc + +" Test toggling of input method. See :help i_CTRL-^ +func Test_edit_CTRL_hat() + CheckFeature xim + + " FIXME: test fails with Motif GUI. + " test also fails when running in the GUI. + CheckFeature gui_gtk + CheckNotGui + + new + + call assert_equal(0, &iminsert) + call feedkeys("i\<C-^>", 'xt') + call assert_equal(2, &iminsert) + call feedkeys("i\<C-^>", 'xt') + call assert_equal(0, &iminsert) + + bwipe! +endfunc + +" Weird long file name was going over the end of NameBuff +func Test_edit_overlong_file_name() + CheckUnix + + file 0000000000000000000000000000 + file %%%%%%%%%%%%%%%%%%%%%%%%%% + file %%%%%% + set readonly + set ls=2 + + redraw! + set noreadonly ls& + bwipe! +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_escaped_glob.vim b/src/nvim/testdir/test_escaped_glob.vim index 1a4fd8bdab..9f53c76a2c 100644 --- a/src/nvim/testdir/test_escaped_glob.vim +++ b/src/nvim/testdir/test_escaped_glob.vim @@ -1,7 +1,7 @@ " Test whether glob()/globpath() return correct results with certain escaped " characters. -function SetUp() +func SetUp() " consistent sorting of file names set nofileignorecase endfunction diff --git a/src/nvim/testdir/test_eval_stuff.vim b/src/nvim/testdir/test_eval_stuff.vim index eff1376d3c..46482c34a1 100644 --- a/src/nvim/testdir/test_eval_stuff.vim +++ b/src/nvim/testdir/test_eval_stuff.vim @@ -1,5 +1,8 @@ " Tests for various eval things. +source view_util.vim +source shared.vim + function s:foo() abort try return [] == 0 @@ -17,13 +20,8 @@ func Test_nocatch_restore_silent_emsg() throw 1 catch endtry - echoerr 'wrong' - let c1 = nr2char(screenchar(&lines, 1)) - let c2 = nr2char(screenchar(&lines, 2)) - let c3 = nr2char(screenchar(&lines, 3)) - let c4 = nr2char(screenchar(&lines, 4)) - let c5 = nr2char(screenchar(&lines, 5)) - call assert_equal('wrong', c1 . c2 . c3 . c4 . c5) + echoerr 'wrong again' + call assert_equal('wrong again', ScreenLine(&lines)) endfunc func Test_mkdir_p() @@ -87,22 +85,54 @@ func Test_for_over_null_string() let &enc = save_enc endfunc +func Test_for_invalid_line_count() + let lines =<< trim END + 111111111111111111111111 for line in ['one'] + endfor + END + call writefile(lines, 'XinvalidFor') + " only test that this doesn't crash + call RunVim([], [], '-u NONE -e -s -S XinvalidFor -c qa') + + call delete('XinvalidFor') +endfunc + func Test_readfile_binary() new call setline(1, ['one', 'two', 'three']) setlocal ff=dos - silent write XReadfile - let lines = readfile('XReadfile') + silent write XReadfile_bin + let lines = 'XReadfile_bin'->readfile() call assert_equal(['one', 'two', 'three'], lines) - let lines = readfile('XReadfile', '', 2) + let lines = readfile('XReadfile_bin', '', 2) call assert_equal(['one', 'two'], lines) - let lines = readfile('XReadfile', 'b') + let lines = readfile('XReadfile_bin', 'b') call assert_equal(["one\r", "two\r", "three\r", ""], lines) - let lines = readfile('XReadfile', 'b', 2) + let lines = readfile('XReadfile_bin', 'b', 2) call assert_equal(["one\r", "two\r"], lines) bwipe! - call delete('XReadfile') + call delete('XReadfile_bin') +endfunc + +func Test_readfile_binary_empty() + call writefile([], 'Xempty-file') + " This used to compare uninitialized memory in Vim <= 8.2.4065 + call assert_equal([''], readfile('Xempty-file', 'b')) + call delete('Xempty-file') +endfunc + +func Test_readfile_bom() + call writefile(["\ufeffFOO", "FOO\ufeffBAR"], 'XReadfile_bom') + call assert_equal(['FOO', 'FOOBAR'], readfile('XReadfile_bom')) + call delete('XReadfile_bom') +endfunc + +func Test_readfile_max() + call writefile(range(1, 4), 'XReadfile_max') + call assert_equal(['1', '2'], readfile('XReadfile_max', '', 2)) + call assert_equal(['3', '4'], readfile('XReadfile_max', '', -2)) + call delete('XReadfile_max') endfunc func Test_let_errmsg() @@ -332,6 +362,11 @@ func Test_curly_assignment() unlet g:gvar endfunc +func Test_deep_recursion() + " this was running out of stack + call assert_fails("exe 'if ' .. repeat('(', 1002)", 'E1169: Expression too recursive: ((') +endfunc + " K_SPECIAL in the modified character used be escaped, which causes " double-escaping with feedkeys() or as the return value of an <expr> mapping, " and doesn't match what getchar() returns, diff --git a/src/nvim/testdir/test_ex_mode.vim b/src/nvim/testdir/test_ex_mode.vim index 2f734cba26..93100732ed 100644 --- a/src/nvim/testdir/test_ex_mode.vim +++ b/src/nvim/testdir/test_ex_mode.vim @@ -97,7 +97,6 @@ func Test_Ex_substitute() call term_sendkeys(buf, ":vi\<CR>") call WaitForAssert({-> assert_match('foo bar', term_getline(buf, 1))}, 1000) - call term_sendkeys(buf, ":q!\n") call StopVimInTerminal(buf) endfunc diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim index dac7a6989d..44bed890f5 100644 --- a/src/nvim/testdir/test_excmd.vim +++ b/src/nvim/testdir/test_excmd.vim @@ -78,6 +78,14 @@ func Test_file_cmd() call assert_fails('3file', 'E474:') call assert_fails('0,0file', 'E474:') call assert_fails('0file abc', 'E474:') + if !has('win32') + " Change the name of the buffer to the same name + new Xfile1 + file Xfile1 + call assert_equal('Xfile1', @%) + call assert_equal('Xfile1', @#) + bw! + endif endfunc " Test for the :drop command @@ -230,7 +238,6 @@ endfunc " Test for the :language command func Test_language_cmd() CheckNotMSWindows " FIXME: why does this fail on Windows CI? - CheckNotBSD " FIXME: why does this fail on OpenBSD CI? CheckFeature multi_lang call assert_fails('language ctype non_existing_lang', 'E197:') @@ -477,20 +484,23 @@ func Test_winsize_cmd() endfunc " Test for the :redir command +" NOTE: if you run tests as root this will fail. Don't run tests as root! func Test_redir_cmd() call assert_fails('redir @@', 'E475:') call assert_fails('redir abc', 'E475:') + call assert_fails('redir => 1abc', 'E474:') + call assert_fails('redir => a b', 'E488:') + call assert_fails('redir => abc[1]', 'E121:') + let b = 0zFF + call assert_fails('redir =>> b', 'E734:') + unlet b + if has('unix') + " Redirecting to a directory name call mkdir('Xdir') call assert_fails('redir > Xdir', 'E17:') call delete('Xdir', 'd') endif - if !has('bsd') - call writefile([], 'Xfile') - call setfperm('Xfile', 'r--r--r--') - call assert_fails('redir! > Xfile', 'E190:') - call delete('Xfile') - endif " Test for redirecting to a register redir @q> | echon 'clean ' | redir END @@ -503,6 +513,16 @@ func Test_redir_cmd() call assert_equal('blue sky', color) endfunc +func Test_redir_cmd_readonly() + CheckNotRoot + + " Redirecting to a read-only file + call writefile([], 'Xfile') + call setfperm('Xfile', 'r--r--r--') + call assert_fails('redir! > Xfile', 'E190:') + call delete('Xfile') +endfunc + " Test for the :filetype command func Test_filetype_cmd() call assert_fails('filetype abc', 'E475:') @@ -568,10 +588,12 @@ endfunc " Test for the :verbose command func Test_verbose_cmd() - call assert_equal([' verbose=1'], split(execute('verbose set vbs'), "\n")) + set verbose=3 + call assert_match(' verbose=1\n\s*Last set from ', 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")) + set verbose=0 + call assert_match(' verbose=4\n\s*Last set from .*\n verbose=0', + \ execute("4verbose set verbose | set verbose")) endfunc " Test for the :delete command and the related abbreviated commands @@ -661,12 +683,24 @@ func Sandbox_tests() if has('unix') call assert_fails('cd `pwd`', 'E48:') endif + " some options cannot be changed in a sandbox + call assert_fails('set exrc', 'E48:') + call assert_fails('set cdpath', 'E48:') + if has('xim') && has('gui_gtk') + call assert_fails('set imstyle', 'E48:') + endif endfunc func Test_sandbox() sandbox call Sandbox_tests() endfunc +func Test_command_not_implemented_E319() + if !has('mzscheme') + call assert_fails('mzscheme', 'E319:') + endif +endfunc + func Test_not_break_expression_register() call setreg('=', '1+1') if 0 diff --git a/src/nvim/testdir/test_exists.vim b/src/nvim/testdir/test_exists.vim index 471c77853d..62c66192ef 100644 --- a/src/nvim/testdir/test_exists.vim +++ b/src/nvim/testdir/test_exists.vim @@ -68,6 +68,10 @@ func Test_exists() " Existing environment variable let $EDITOR_NAME = 'Vim Editor' call assert_equal(1, exists('$EDITOR_NAME')) + if has('unix') + " ${name} environment variables are supported only on Unix-like systems + call assert_equal(1, exists('${VIM}')) + endif " Non-existing environment variable call assert_equal(0, exists('$NON_ENV_VAR')) @@ -323,3 +327,5 @@ endfunc func Test_exists_funcarg() call FuncArg_Tests("arg1", "arg2") endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_exit.vim b/src/nvim/testdir/test_exit.vim index 37be293950..6dbfb7047c 100644 --- a/src/nvim/testdir/test_exit.vim +++ b/src/nvim/testdir/test_exit.vim @@ -117,6 +117,7 @@ func Test_exit_error_reading_input() CheckNotMSWindows " The early exit causes memory not to be freed somehow CheckNotAsan + CheckNotValgrind call writefile([":au VimLeave * call writefile(['l = ' .. v:exiting], 'Xtestout')", ":tabnew", "q:"], 'Xscript', 'b') diff --git a/src/nvim/testdir/test_expand.vim b/src/nvim/testdir/test_expand.vim index ce414e4b11..4f5bb67d21 100644 --- a/src/nvim/testdir/test_expand.vim +++ b/src/nvim/testdir/test_expand.vim @@ -90,14 +90,26 @@ func Test_expandcmd() " Test for expression expansion `= let $FOO= "blue" call assert_equal("blue sky", expandcmd("`=$FOO .. ' sky'`")) + let x = expandcmd("`=axbycz`") + call assert_equal('`=axbycz`', x) + call assert_fails('let x = expandcmd("`=axbycz`", #{errmsg: 1})', 'E121:') + let x = expandcmd("`=axbycz`", #{abc: []}) + call assert_equal('`=axbycz`', x) " Test for env variable with spaces let $FOO= "foo bar baz" call assert_equal("e foo bar baz", expandcmd("e $FOO")) - if has('unix') - " test for using the shell to expand a command argument - call assert_equal('{1..4}', expandcmd('{1..4}')) + if has('unix') && executable('bash') + " test for using the shell to expand a command argument. + " only bash supports the {..} syntax + set shell=bash + let x = expandcmd('{1..4}') + call assert_equal('{1..4}', x) + call assert_fails("let x = expandcmd('{1..4}', #{errmsg: v:true})", 'E77:') + let x = expandcmd('{1..4}', #{error: v:true}) + call assert_equal('{1..4}', x) + set shell& endif unlet $FOO @@ -116,13 +128,21 @@ func Test_source_sfile() :call assert_equal('edit <cword>', expandcmd("edit <cword>")) :call assert_equal('edit <cexpr>', expandcmd("edit <cexpr>")) :call assert_fails('autocmd User MyCmd echo "<sfile>"', 'E498:') + : + :call assert_equal('', expand('<script>')) + :verbose echo expand('<script>') + :call add(v:errors, v:errmsg) + :verbose echo expand('<sfile>') + :call add(v:errors, v:errmsg) :call writefile(v:errors, 'Xresult') :qall! - [SCRIPT] call writefile(lines, 'Xscript') if RunVim([], [], '--clean -s Xscript') - call assert_equal([], readfile('Xresult')) + call assert_equal([ + \ 'E1274: No script file name to substitute for "<script>"', + \ 'E498: no :source file name to substitute for "<sfile>"'], + \ readfile('Xresult')) endif call delete('Xscript') call delete('Xresult') @@ -147,4 +167,63 @@ func Test_expandcmd_shell_nonomatch() call assert_equal('$*', expandcmd('$*')) endfunc +func Test_expand_script_source() + let lines0 =<< trim [SCRIPT] + call extend(g:script_level, [expand('<script>:t')]) + so Xscript1 + func F0() + call extend(g:func_level, [expand('<script>:t')]) + endfunc + + au User * call extend(g:au_level, [expand('<script>:t')]) + [SCRIPT] + + let lines1 =<< trim [SCRIPT] + call extend(g:script_level, [expand('<script>:t')]) + so Xscript2 + func F1() + call extend(g:func_level, [expand('<script>:t')]) + endfunc + + au User * call extend(g:au_level, [expand('<script>:t')]) + [SCRIPT] + + let lines2 =<< trim [SCRIPT] + call extend(g:script_level, [expand('<script>:t')]) + func F2() + call extend(g:func_level, [expand('<script>:t')]) + endfunc + + au User * call extend(g:au_level, [expand('<script>:t')]) + [SCRIPT] + + call writefile(lines0, 'Xscript0') + call writefile(lines1, 'Xscript1') + call writefile(lines2, 'Xscript2') + + " Check the expansion of <script> at different levels. + let g:script_level = [] + let g:func_level = [] + let g:au_level = [] + + so Xscript0 + call F0() + call F1() + call F2() + doautocmd User + + call assert_equal(['Xscript0', 'Xscript1', 'Xscript2'], g:script_level) + call assert_equal(['Xscript0', 'Xscript1', 'Xscript2'], g:func_level) + call assert_equal(['Xscript2', 'Xscript1', 'Xscript0'], g:au_level) + + unlet g:script_level g:func_level + delfunc F0 + delfunc F1 + delfunc F2 + + call delete('Xscript0') + call delete('Xscript1') + call delete('Xscript2') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_expand_func.vim b/src/nvim/testdir/test_expand_func.vim index df01d84f19..454d76f0aa 100644 --- a/src/nvim/testdir/test_expand_func.vim +++ b/src/nvim/testdir/test_expand_func.vim @@ -107,10 +107,15 @@ endfunc func Test_expand() new - call assert_equal("", expand('%:S')) + call assert_equal("", expand('%:S')) call assert_equal('3', '<slnum>'->expand()) call assert_equal(['4'], expand('<slnum>', v:false, v:true)) " Don't add any line above this, otherwise <slnum> will change. + call assert_equal("", expand('%')) + set verbose=1 + call assert_equal("", expand('%')) + set verbose=0 + call assert_equal("", expand('%:p')) quit endfunc @@ -134,6 +139,7 @@ func Test_expand_wildignore() call assert_equal('test_expand_func.vim', expand('test_expand_func.vim', 1)) call assert_equal(['test_expand_func.vim'], \ expand('test_expand_func.vim', 1, 1)) + call assert_fails("call expand('*', [])", 'E745:') set wildignore& endfunc diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index 5b10e691e5..47f7f5eb0e 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -1,5 +1,7 @@ " Tests for expressions. +source check.vim + func Test_equal() let base = {} func base.method() @@ -44,6 +46,7 @@ func Test_dict() call assert_equal('zero', d[0]) call assert_true(has_key(d, '')) call assert_true(has_key(d, 'a')) + call assert_fails("let i = has_key([], 'a')", 'E715:') let d[''] = 'none' let d['a'] = 'aaa' @@ -62,6 +65,8 @@ func Test_strgetchar() call assert_equal(-1, strgetchar('axb', -1)) call assert_equal(-1, strgetchar('axb', 3)) call assert_equal(-1, strgetchar('', 0)) + call assert_fails("let c=strgetchar([], 1)", 'E730:') + call assert_fails("let c=strgetchar('axb', [])", 'E745:') endfunc func Test_strcharpart() @@ -76,22 +81,27 @@ func Test_strcharpart() call assert_equal('', strcharpart('axb', -2, 2)) call assert_equal('a', strcharpart('axb', -1, 2)) + + call assert_equal('edit', "editor"[-10:3]) +endfunc + +func Test_getreg_empty_list() + call assert_equal('', getreg('x')) + call assert_equal([], getreg('x', 1, 1)) + let x = getreg('x', 1, 1) + let y = x + call add(x, 'foo') + call assert_equal(['foo'], y) + call assert_fails('call getreg([])', 'E730:') endfunc func Test_loop_over_null_list() - let null_list = submatch(1, 1) + let null_list = v:_null_list for i in null_list call assert_report('should not get here') endfor endfunc -func Test_compare_null_dict() - call assert_fails('let x = v:_null_dict[10]') - call assert_equal({}, {}) - call assert_equal(v:_null_dict, v:_null_dict) - call assert_notequal({}, v:_null_dict) -endfunc - func Test_set_reg_null_list() call setreg('x', v:_null_list) endfunc @@ -478,49 +488,6 @@ function Test_max_min_errors() call assert_fails('call min(v:true)', 'min()') endfunc -func Test_substitute_expr() - let g:val = 'XXX' - call assert_equal('XXX', substitute('yyy', 'y*', '\=g:val', '')) - call assert_equal('XXX', substitute('yyy', 'y*', {-> g:val}, '')) - call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)', - \ '\=nr2char("0x" . submatch(1))', 'g')) - call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)', - \ {-> nr2char("0x" . submatch(1))}, 'g')) - - call assert_equal('231', substitute('123', '\(.\)\(.\)\(.\)', - \ {-> submatch(2) . submatch(3) . submatch(1)}, '')) - - func Recurse() - return substitute('yyy', 'y\(.\)y', {-> submatch(1)}, '') - endfunc - " recursive call works - call assert_equal('-y-x-', substitute('xxx', 'x\(.\)x', {-> '-' . Recurse() . '-' . submatch(1) . '-'}, '')) -endfunc - -func Test_invalid_submatch() - " This was causing invalid memory access in Vim-7.4.2232 and older - call assert_fails("call substitute('x', '.', {-> submatch(10)}, '')", 'E935:') -endfunc - -func Test_substitute_expr_arg() - call assert_equal('123456789-123456789=', substitute('123456789', - \ '\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)', - \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, '')) - - call assert_equal('123456-123456=789', substitute('123456789', - \ '\(.\)\(.\)\(.\)\(a*\)\(n*\)\(.\)\(.\)\(.\)\(x*\)', - \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, '')) - - call assert_equal('123456789-123456789x=', substitute('123456789', - \ '\(.\)\(.\)\(.*\)', - \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . 'x' . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, '')) - - call assert_fails("call substitute('xxx', '.', {m -> string(add(m, 'x'))}, '')", 'E742:') - call assert_fails("call substitute('xxx', '.', {m -> string(insert(m, 'x'))}, '')", 'E742:') - call assert_fails("call substitute('xxx', '.', {m -> string(extend(m, ['x']))}, '')", 'E742:') - call assert_fails("call substitute('xxx', '.', {m -> string(remove(m, 1))}, '')", 'E742:') -endfunc - func Test_function_with_funcref() let s:f = function('type') let s:fref = function(s:f) @@ -529,6 +496,14 @@ func Test_function_with_funcref() call assert_fails("call function('foo()')", 'E475:') call assert_fails("call function('foo()')", 'foo()') + call assert_fails("function('')", 'E129:') + + let Len = {s -> strlen(s)} + call assert_equal(6, Len('foobar')) + let name = string(Len) + " can evaluate "function('<lambda>99')" + call execute('let Ref = ' .. name) + call assert_equal(4, Ref('text')) endfunc func Test_funcref() @@ -547,6 +522,22 @@ func Test_funcref() call assert_fails('echo funcref("{")', 'E475:') let OneByRef = funcref("One", repeat(["foo"], 20)) call assert_fails('let OneByRef = funcref("One", repeat(["foo"], 21))', 'E118:') + call assert_fails('echo function("min") =~ function("min")', 'E694:') +endfunc + +" Test for calling function() and funcref() outside of a Vim script context. +func Test_function_outside_script() + let cleanup =<< trim END + call writefile([execute('messages')], 'Xtest.out') + qall + END + call writefile(cleanup, 'Xverify.vim') + call RunVim([], [], "-c \"echo function('s:abc')\" -S Xverify.vim") + call assert_match('E81: Using <SID> not in a', readfile('Xtest.out')[0]) + call RunVim([], [], "-c \"echo funcref('s:abc')\" -S Xverify.vim") + call assert_match('E81: Using <SID> not in a', readfile('Xtest.out')[0]) + call delete('Xtest.out') + call delete('Xverify.vim') endfunc func Test_setmatches() @@ -560,6 +551,7 @@ func Test_setmatches() endif eval set->setmatches() call assert_equal(exp, getmatches()) + call assert_fails('let m = setmatches([], [])', 'E745:') endfunc func Test_empty_concatenate() @@ -586,3 +578,99 @@ func Test_eval_after_if() if 0 | eval SetVal('a') | endif | call SetVal('b') call assert_equal('b', s:val) endfunc + +func Test_divide_by_zero() + " only tests that this doesn't crash, the result is not important + echo 0 / 0 + echo 0 / 0 / -1 +endfunc + +" Test for command-line completion of expressions +func Test_expr_completion() + CheckFeature cmdline_compl + for cmd in [ + \ 'let a = ', + \ 'const a = ', + \ 'if', + \ 'elseif', + \ 'while', + \ 'for', + \ 'echo', + \ 'echon', + \ 'execute', + \ 'echomsg', + \ 'echoerr', + \ 'call', + \ 'return', + \ 'cexpr', + \ 'caddexpr', + \ 'cgetexpr', + \ 'lexpr', + \ 'laddexpr', + \ 'lgetexpr'] + call feedkeys(":" . cmd . " getl\<Tab>\<Home>\"\<CR>", 'xt') + call assert_equal('"' . cmd . ' getline(', getreg(':')) + endfor + + " completion for the expression register + call feedkeys(":\"\<C-R>=float2\t\"\<C-B>\"\<CR>", 'xt') + call assert_equal('"float2nr("', @=) + + " completion for window local variables + let w:wvar1 = 10 + let w:wvar2 = 10 + call feedkeys(":echo w:wvar\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"echo w:wvar1 w:wvar2', @:) + unlet w:wvar1 w:wvar2 + + " completion for tab local variables + let t:tvar1 = 10 + let t:tvar2 = 10 + call feedkeys(":echo t:tvar\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"echo t:tvar1 t:tvar2', @:) + unlet t:tvar1 t:tvar2 + + " completion for variables + let g:tvar1 = 1 + let g:tvar2 = 2 + call feedkeys(":let g:tv\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"let g:tvar1 g:tvar2', @:) + " completion for variables after a || + call feedkeys(":echo 1 || g:tv\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"echo 1 || g:tvar1 g:tvar2', @:) + + " completion for options + call feedkeys(":echo &compat\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"echo &compatible', @:) + call feedkeys(":echo 1 && &compat\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"echo 1 && &compatible', @:) + call feedkeys(":echo &g:equala\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"echo &g:equalalways', @:) + + " completion for string + call feedkeys(":echo \"Hello\\ World\"\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"echo \"Hello\\ World\"\<C-A>", @:) + call feedkeys(":echo 'Hello World'\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"echo 'Hello World'\<C-A>", @:) + + " completion for command after a | + call feedkeys(":echo 'Hello' | cwin\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"echo 'Hello' | cwindow", @:) + + " completion for environment variable + let $X_VIM_TEST_COMPLETE_ENV = 'foo' + call feedkeys(":let $X_VIM_TEST_COMPLETE_E\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_match('"let $X_VIM_TEST_COMPLETE_ENV', @:) + unlet $X_VIM_TEST_COMPLETE_ENV +endfunc + +" Test for errors in expression evaluation +func Test_expr_eval_error() + call assert_fails("let i = 'abc' . []", 'E730:') + call assert_fails("let l = [] + 10", 'E745:') + call assert_fails("let v = 10 + []", 'E745:') + call assert_fails("let v = 10 / []", 'E745:') + call assert_fails("let v = -{}", 'E728:') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_filechanged.vim b/src/nvim/testdir/test_filechanged.vim index b77f02afd1..fef0eb732f 100644 --- a/src/nvim/testdir/test_filechanged.vim +++ b/src/nvim/testdir/test_filechanged.vim @@ -140,7 +140,8 @@ func Test_FileChangedShell_edit() endfunc func Test_FileChangedShell_edit_dialog() - throw 'Skipped: requires a UI to be active' + " requires a UI to be active + throw 'Skipped: use test/functional/legacy/filechanged_spec.lua' CheckNotGui CheckUnix " Using low level feedkeys() does not work on MS-Windows. @@ -190,7 +191,8 @@ func Test_FileChangedShell_edit_dialog() endfunc func Test_file_changed_dialog() - throw 'Skipped: requires a UI to be active' + " requires a UI to be active + throw 'Skipped: use test/functional/legacy/filechanged_spec.lua' CheckUnix CheckNotGui au! FileChangedShell diff --git a/src/nvim/testdir/test_fileformat.vim b/src/nvim/testdir/test_fileformat.vim index 81127ea59a..8d727a68c4 100644 --- a/src/nvim/testdir/test_fileformat.vim +++ b/src/nvim/testdir/test_fileformat.vim @@ -31,6 +31,15 @@ func Test_fileformat_autocommand() bw! endfunc +func Test_fileformat_nomodifiable() + new + setlocal nomodifiable + + call assert_fails('set fileformat=latin1', 'E21:') + + bw +endfunc + " Convert the contents of a file into a literal string func s:file2str(fname) let b = readfile(a:fname, 'B') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index e3a8370661..cddb1349f5 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -88,15 +88,17 @@ let s:filename_checks = { \ 'bindzone': ['named.root', '/bind/db.file', '/named/db.file', 'any/bind/db.file', 'any/named/db.file'], \ 'bitbake': ['file.bb', 'file.bbappend', 'file.bbclass', 'build/conf/local.conf', 'meta/conf/layer.conf', 'build/conf/bbappend.conf', 'meta-layer/conf/distro/foo.conf'], \ 'blank': ['file.bl'], + \ 'blueprint': ['file.blp'], \ 'bsdl': ['file.bsd', 'file.bsdl'], \ 'bst': ['file.bst'], - \ 'bzl': ['file.bazel', 'file.bzl', 'WORKSPACE'], + \ 'bzl': ['file.bazel', 'file.bzl', 'WORKSPACE', 'WORKSPACE.bzlmod'], \ 'bzr': ['bzr_log.any', 'bzr_log.file'], \ 'c': ['enlightenment/file.cfg', 'file.qc', 'file.c', 'some-enlightenment/file.cfg'], \ 'cabal': ['file.cabal'], \ 'cabalconfig': ['cabal.config'], \ 'cabalproject': ['cabal.project', 'cabal.project.local'], \ 'calendar': ['calendar', '/.calendar/file', '/share/calendar/any/calendar.file', '/share/calendar/calendar.file', 'any/share/calendar/any/calendar.file', 'any/share/calendar/calendar.file'], + \ 'capnp': ['file.capnp'], \ 'catalog': ['catalog', 'sgml.catalogfile', 'sgml.catalog', 'sgml.catalog-file'], \ 'cdl': ['file.cdl'], \ 'cdrdaoconf': ['/etc/cdrdao.conf', '/etc/defaults/cdrdao', '/etc/default/cdrdao', '.cdrdao', 'any/etc/cdrdao.conf', 'any/etc/default/cdrdao', 'any/etc/defaults/cdrdao'], @@ -107,6 +109,7 @@ let s:filename_checks = { \ 'ch': ['file.chf'], \ 'chaiscript': ['file.chai'], \ 'chaskell': ['file.chs'], + \ 'chatito': ['file.chatito'], \ 'chill': ['file..ch'], \ 'chordpro': ['file.chopro', 'file.crd', 'file.cho', 'file.crdpro', 'file.chordpro'], \ 'cl': ['file.eni'], @@ -120,10 +123,11 @@ let s:filename_checks = { \ 'conaryrecipe': ['file.recipe'], \ 'conf': ['auto.master'], \ 'config': ['configure.in', 'configure.ac', '/etc/hostname.file', 'any/etc/hostname.file'], - \ 'confini': ['/etc/pacman.conf', 'any/etc/pacman.conf', 'mpv.conf'], + \ 'confini': ['/etc/pacman.conf', 'any/etc/pacman.conf', 'mpv.conf', 'any/.aws/config', 'any/.aws/credentials'], \ 'context': ['tex/context/any/file.tex', 'file.mkii', 'file.mkiv', 'file.mkvi', 'file.mkxl', 'file.mklx'], \ 'cook': ['file.cook'], \ 'cpp': ['file.cxx', 'file.c++', 'file.hh', 'file.hxx', 'file.hpp', 'file.ipp', 'file.moc', 'file.tcc', 'file.inl', 'file.tlh'], + \ 'cqlang': ['file.cql'], \ 'crm': ['file.crm'], \ 'crontab': ['crontab', 'crontab.file', '/etc/cron.d/file', 'any/etc/cron.d/file'], \ 'cs': ['file.cs', 'file.csx'], @@ -159,7 +163,7 @@ let s:filename_checks = { \ 'dnsmasq': ['/etc/dnsmasq.conf', '/etc/dnsmasq.d/file', 'any/etc/dnsmasq.conf', 'any/etc/dnsmasq.d/file'], \ 'dockerfile': ['Containerfile', 'Dockerfile', 'dockerfile', 'file.Dockerfile', 'file.dockerfile', 'Dockerfile.debian', 'Containerfile.something'], \ 'dosbatch': ['file.bat'], - \ 'dosini': ['.editorconfig', '/etc/yum.conf', 'file.ini', 'npmrc', '.npmrc', 'php.ini', 'php.ini-5', 'php.ini-file', '/etc/yum.repos.d/file', 'any/etc/yum.conf', 'any/etc/yum.repos.d/file', 'file.wrap'], + \ 'dosini': ['/etc/yum.conf', 'file.ini', 'npmrc', '.npmrc', 'php.ini', 'php.ini-5', 'php.ini-file', '/etc/yum.repos.d/file', 'any/etc/yum.conf', 'any/etc/yum.repos.d/file', 'file.wrap'], \ 'dot': ['file.dot', 'file.gv'], \ 'dracula': ['file.drac', 'file.drc', 'filelvs', 'filelpe', 'drac.file', 'lpe', 'lvs', 'some-lpe', 'some-lvs'], \ 'dtd': ['file.dtd'], @@ -171,6 +175,7 @@ let s:filename_checks = { \ 'dylanlid': ['file.lid'], \ 'ecd': ['file.ecd'], \ 'edif': ['file.edf', 'file.edif', 'file.edo'], + \ 'editorconfig': ['.editorconfig'], \ 'eelixir': ['file.eex', 'file.leex'], \ 'elinks': ['elinks.conf'], \ 'elixir': ['file.ex', 'file.exs', 'mix.lock'], @@ -201,6 +206,7 @@ let s:filename_checks = { \ 'fpcmake': ['file.fpc'], \ 'framescript': ['file.fsl'], \ 'freebasic': ['file.fb'], + \ 'fsh': ['file.fsh'], \ 'fsharp': ['file.fs', 'file.fsi', 'file.fsx'], \ 'fstab': ['fstab', 'mtab'], \ 'fusion': ['file.fusion'], @@ -209,11 +215,14 @@ let s:filename_checks = { \ 'gdmo': ['file.mo', 'file.gdmo'], \ 'gdresource': ['file.tscn', 'file.tres'], \ 'gdscript': ['file.gd'], + \ 'gdshader': ['file.gdshader', 'file.shader'], \ 'gedcom': ['file.ged', 'lltxxxxx.txt', '/tmp/lltmp', '/tmp/lltmp-file', 'any/tmp/lltmp', 'any/tmp/lltmp-file'], \ 'gemtext': ['file.gmi', 'file.gemini'], \ 'gift': ['file.gift'], + \ 'gitattributes': ['file.git/info/attributes', '.gitattributes', '/.config/git/attributes', '/etc/gitattributes', '/usr/local/etc/gitattributes', 'some.git/info/attributes'], \ 'gitcommit': ['COMMIT_EDITMSG', 'MERGE_MSG', 'TAG_EDITMSG', 'NOTES_EDITMSG', 'EDIT_DESCRIPTION'], \ 'gitconfig': ['file.git/config', 'file.git/config.worktree', 'file.git/worktrees/x/config.worktree', '.gitconfig', '.gitmodules', 'file.git/modules//config', '/.config/git/config', '/etc/gitconfig', '/usr/local/etc/gitconfig', '/etc/gitconfig.d/file', 'any/etc/gitconfig.d/file', '/.gitconfig.d/file', 'any/.config/git/config', 'any/.gitconfig.d/file', 'some.git/config', 'some.git/modules/any/config'], + \ 'gitignore': ['file.git/info/exclude', '.gitignore', '/.config/git/ignore', 'some.git/info/exclude'], \ 'gitolite': ['gitolite.conf', '/gitolite-admin/conf/file', 'any/gitolite-admin/conf/file'], \ 'gitrebase': ['git-rebase-todo'], \ 'gitsendemail': ['.gitsendemail.msg.xxxxxx'], @@ -224,6 +233,7 @@ let s:filename_checks = { \ 'gnuplot': ['file.gpi', '.gnuplot'], \ 'go': ['file.go'], \ 'gomod': ['go.mod'], + \ 'gosum': ['go.sum'], \ 'gowork': ['go.work'], \ 'gp': ['file.gp', '.gprc'], \ 'gpg': ['/.gnupg/options', '/.gnupg/gpg.conf', '/usr/any/gnupg/options.skel', 'any/.gnupg/gpg.conf', 'any/.gnupg/options', 'any/usr/any/gnupg/options.skel'], @@ -235,6 +245,7 @@ let s:filename_checks = { \ 'grub': ['/boot/grub/menu.lst', '/boot/grub/grub.conf', '/etc/grub.conf', 'any/boot/grub/grub.conf', 'any/boot/grub/menu.lst', 'any/etc/grub.conf'], \ 'gsp': ['file.gsp'], \ 'gtkrc': ['.gtkrc', 'gtkrc', '.gtkrc-file', 'gtkrc-file'], + \ 'gyp': ['file.gyp', 'file.gypi'], \ 'hack': ['file.hack', 'file.hackpartial'], \ 'haml': ['file.haml'], \ 'hamster': ['file.hsm'], @@ -250,6 +261,7 @@ let s:filename_checks = { \ 'hex': ['file.hex', 'file.h32'], \ 'hgcommit': ['hg-editor-file.txt'], \ 'hjson': ['file.hjson'], + \ 'hlsplaylist': ['file.m3u', 'file.m3u8'], \ 'hog': ['file.hog', 'snort.conf', 'vision.conf'], \ 'hollywood': ['file.hws'], \ 'hoon': ['file.hoon'], @@ -274,16 +286,18 @@ let s:filename_checks = { \ 'jam': ['file.jpl', 'file.jpr', 'JAM-file.file', 'JAM.file', 'Prl-file.file', 'Prl.file'], \ 'java': ['file.java', 'file.jav'], \ 'javacc': ['file.jj', 'file.jjt'], - \ 'javascript': ['file.js', 'file.javascript', 'file.es', 'file.mjs', 'file.cjs'], + \ 'javascript': ['file.js', 'file.jsm', 'file.javascript', 'file.es', 'file.mjs', 'file.cjs'], \ 'javascript.glimmer': ['file.gjs'], \ 'javascriptreact': ['file.jsx'], \ 'jess': ['file.clp'], \ 'jgraph': ['file.jgr'], + \ 'jq': ['file.jq'], \ 'jovial': ['file.jov', 'file.j73', 'file.jovial'], - \ 'jproperties': ['file.properties', 'file.properties_xx', 'file.properties_xx_xx', 'some.properties_xx_xx_file'], - \ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', '.babelrc', '.eslintrc', '.prettierrc', '.firebaserc', 'file.slnf'], + \ 'jproperties': ['file.properties', 'file.properties_xx', 'file.properties_xx_xx', 'some.properties_xx_xx_file', 'org.eclipse.xyz.prefs'], + \ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', '.prettierrc', '.firebaserc', 'file.slnf'], \ 'json5': ['file.json5'], - \ 'jsonc': ['file.jsonc'], + \ 'jsonc': ['file.jsonc', '.babelrc', '.eslintrc', '.jsfmtrc', '.jshintrc', '.hintrc', '.swrc', 'jsconfig.json', 'tsconfig.json', 'tsconfig.test.json', 'tsconfig-test.json'], + \ 'jsonnet': ['file.jsonnet', 'file.libsonnet'], \ 'jsp': ['file.jsp'], \ 'julia': ['file.jl'], \ 'kconfig': ['Kconfig', 'Kconfig.debug', 'Kconfig.file'], @@ -320,8 +334,9 @@ let s:filename_checks = { \ 'lpc': ['file.lpc', 'file.ulpc'], \ 'lsl': ['file.lsl'], \ 'lss': ['file.lss'], - \ 'lua': ['file.lua', 'file.rockspec', 'file.nse'], + \ 'lua': ['file.lua', 'file.rockspec', 'file.nse', '.luacheckrc'], \ 'lynx': ['lynx.cfg'], + \ 'lyrics': ['file.lrc'], \ 'm3build': ['m3makefile', 'm3overrides'], \ 'm3quake': ['file.quake', 'cm3.cfg'], \ 'm4': ['file.at'], @@ -340,6 +355,7 @@ let s:filename_checks = { \ 'maxima': ['file.demo', 'file.dmt', 'file.dm1', 'file.dm2', 'file.dm3', \ 'file.wxm', 'maxima-init.mac'], \ 'mel': ['file.mel'], + \ 'mermaid': ['file.mmd', 'file.mmdc', 'file.mermaid'], \ 'meson': ['meson.build', 'meson_options.txt'], \ 'messages': ['/log/auth', '/log/cron', '/log/daemon', '/log/debug', '/log/kern', '/log/lpr', '/log/mail', '/log/messages', '/log/news/news', '/log/syslog', '/log/user', \ '/log/auth.log', '/log/cron.log', '/log/daemon.log', '/log/debug.log', '/log/kern.log', '/log/lpr.log', '/log/mail.log', '/log/messages.log', '/log/news/news.log', '/log/syslog.log', '/log/user.log', @@ -357,7 +373,7 @@ let s:filename_checks = { \ 'mmp': ['file.mmp'], \ 'modconf': ['/etc/modules.conf', '/etc/modules', '/etc/conf.modules', '/etc/modprobe.file', 'any/etc/conf.modules', 'any/etc/modprobe.file', 'any/etc/modules', 'any/etc/modules.conf'], \ 'modula2': ['file.m2', 'file.mi'], - \ 'modula3': ['file.m3', 'file.mg', 'file.i3', 'file.ig'], + \ 'modula3': ['file.m3', 'file.mg', 'file.i3', 'file.ig', 'file.lm3'], \ 'monk': ['file.isc', 'file.monk', 'file.ssc', 'file.tsc'], \ 'moo': ['file.moo'], \ 'moonscript': ['file.moon'], @@ -378,12 +394,14 @@ let s:filename_checks = { \ 'neomuttrc': ['Neomuttrc', '.neomuttrc', '.neomuttrc-file', '/.neomutt/neomuttrc', '/.neomutt/neomuttrc-file', 'Neomuttrc', 'Neomuttrc-file', 'any/.neomutt/neomuttrc', 'any/.neomutt/neomuttrc-file', 'neomuttrc', 'neomuttrc-file'], \ 'netrc': ['.netrc'], \ 'nginx': ['file.nginx', 'nginxfile.conf', 'filenginx.conf', 'any/etc/nginx/file', 'any/usr/local/nginx/conf/file', 'any/nginx/file.conf'], + \ 'nim': ['file.nim', 'file.nims', 'file.nimble'], \ 'ninja': ['file.ninja'], \ 'nix': ['file.nix'], \ 'nqc': ['file.nqc'], \ 'nroff': ['file.tr', 'file.nr', 'file.roff', 'file.tmac', 'file.mom', 'tmac.file'], \ 'nsis': ['file.nsi', 'file.nsh'], \ 'obj': ['file.obj'], + \ 'obse': ['file.obl', 'file.obse', 'file.oblivion', 'file.obscript'], \ 'ocaml': ['file.ml', 'file.mli', 'file.mll', 'file.mly', '.ocamlinit', 'file.mlt', 'file.mlp', 'file.mlip', 'file.mli.cppo', 'file.ml.cppo'], \ 'occam': ['file.occ'], \ 'octave': ['octaverc', '.octaverc', 'octave.conf'], @@ -391,6 +409,7 @@ let s:filename_checks = { \ 'opam': ['opam', 'file.opam', 'file.opam.template'], \ 'openroad': ['file.or'], \ 'openscad': ['file.scad'], + \ 'openvpn': ['file.ovpn', '/etc/openvpn/client/client.conf', '/usr/share/openvpn/examples/server.conf'], \ 'opl': ['file.OPL', 'file.OPl', 'file.OpL', 'file.Opl', 'file.oPL', 'file.oPl', 'file.opL', 'file.opl'], \ 'ora': ['file.ora'], \ 'org': ['file.org', 'file.org_archive'], @@ -403,10 +422,10 @@ let s:filename_checks = { \ 'pccts': ['file.g'], \ 'pcmk': ['file.pcmk'], \ 'pdf': ['file.pdf'], - \ 'perl': ['file.plx', 'file.al', 'file.psgi', 'gitolite.rc', '.gitolite.rc', 'example.gitolite.rc'], + \ 'perl': ['file.plx', 'file.al', 'file.psgi', 'gitolite.rc', '.gitolite.rc', 'example.gitolite.rc', '.latexmkrc', 'latexmkrc'], \ 'pf': ['pf.conf'], - \ 'pfmain': ['main.cf'], - \ 'php': ['file.php', 'file.php9', 'file.phtml', 'file.ctp', 'file.phpt'], + \ 'pfmain': ['main.cf', 'main.cf.proto'], + \ 'php': ['file.php', 'file.php9', 'file.phtml', 'file.ctp', 'file.phpt', 'file.theme'], \ 'pike': ['file.pike', 'file.pmod'], \ 'pilrc': ['file.rcp'], \ 'pine': ['.pinerc', 'pinerc', '.pinercex', 'pinercex'], @@ -417,6 +436,7 @@ let s:filename_checks = { \ 'plsql': ['file.pls', 'file.plsql'], \ 'po': ['file.po', 'file.pot'], \ 'pod': ['file.pod'], + \ 'poefilter': ['file.filter'], \ 'poke': ['file.pk'], \ 'postscr': ['file.ps', 'file.pfa', 'file.afm', 'file.eps', 'file.epsf', 'file.epsi', 'file.ai'], \ 'pov': ['file.pov'], @@ -443,7 +463,7 @@ let s:filename_checks = { \ 'ql': ['file.ql', 'file.qll'], \ 'quake': ['anybaseq2/file.cfg', 'anyid1/file.cfg', 'quake3/file.cfg', 'baseq2/file.cfg', 'id1/file.cfg', 'quake1/file.cfg', 'some-baseq2/file.cfg', 'some-id1/file.cfg', 'some-quake1/file.cfg'], \ 'quarto': ['file.qmd'], - \ 'r': ['file.r'], + \ 'r': ['file.r', '.Rprofile', 'Rprofile', 'Rprofile.site'], \ 'radiance': ['file.rad', 'file.mat'], \ 'raku': ['file.pm6', 'file.p6', 'file.t6', 'file.pod6', 'file.raku', 'file.rakumod', 'file.rakudoc', 'file.rakutest'], \ 'raml': ['file.raml'], @@ -507,9 +527,11 @@ let s:filename_checks = { \ 'slrnrc': ['.slrnrc'], \ 'slrnsc': ['file.score'], \ 'sm': ['sendmail.cf'], + \ 'smali': ['file.smali'], \ 'smarty': ['file.tpl'], \ 'smcl': ['file.hlp', 'file.ihlp', 'file.smcl'], \ 'smith': ['file.smt', 'file.smith'], + \ 'smithy': ['file.smithy'], \ 'sml': ['file.sml'], \ 'snobol4': ['file.sno', 'file.spt'], \ 'solidity': ['file.sol'], @@ -525,13 +547,15 @@ let s:filename_checks = { \ 'squid': ['squid.conf'], \ 'squirrel': ['file.nut'], \ 'srec': ['file.s19', 'file.s28', 'file.s37', 'file.mot', 'file.srec'], + \ 'srt': ['file.srt'], + \ 'ssa': ['file.ass', 'file.ssa'], \ 'sshconfig': ['ssh_config', '/.ssh/config', '/etc/ssh/ssh_config.d/file.conf', 'any/etc/ssh/ssh_config.d/file.conf', 'any/.ssh/config', 'any/.ssh/file.conf'], \ 'sshdconfig': ['sshd_config', '/etc/ssh/sshd_config.d/file.conf', 'any/etc/ssh/sshd_config.d/file.conf'], \ 'st': ['file.st'], \ 'stata': ['file.ado', 'file.do', 'file.imata', 'file.mata'], \ 'stp': ['file.stp'], \ 'sudoers': ['any/etc/sudoers', 'sudoers.tmp', '/etc/sudoers', 'any/etc/sudoers.d/file'], - \ 'supercollider': ['file.quark'], + \ 'supercollider': ['file.quark'], \ 'surface': ['file.sface'], \ 'svelte': ['file.svelte'], \ 'svg': ['file.svg'], @@ -557,6 +581,7 @@ let s:filename_checks = { \ 'texmf': ['texmf.cnf'], \ 'text': ['file.text', 'file.txt', 'README', 'LICENSE', 'COPYING', 'AUTHORS', '/usr/share/doc/bash-completion/AUTHORS', '/etc/apt/apt.conf.d/README', '/etc/Muttrc.d/README'], \ 'tf': ['file.tf', '.tfrc', 'tfrc'], + \ 'thrift': ['file.thrift'], \ 'tidy': ['.tidyrc', 'tidyrc', 'tidy.conf'], \ 'tilde': ['file.t.html'], \ 'tla': ['file.tla'], @@ -572,6 +597,7 @@ let s:filename_checks = { \ 'tssop': ['file.tssop'], \ 'tsv': ['file.tsv'], \ 'twig': ['file.twig'], + \ 'typescript': ['file.mts', 'file.cts'], \ 'typescript.glimmer': ['file.gts'], \ 'typescriptreact': ['file.tsx'], \ 'uc': ['file.uc'], @@ -588,11 +614,16 @@ let s:filename_checks = { \ 'usw2kagtlog': ['usw2kagt.log', 'USW2KAGT.LOG', 'usw2kagt.file.log', 'USW2KAGT.FILE.LOG', 'file.usw2kagt.log', 'FILE.USW2KAGT.LOG'], \ 'vala': ['file.vala'], \ 'vb': ['file.sba', 'file.vb', 'file.vbs', 'file.dsm', 'file.ctl'], + \ 'vdf': ['file.vdf'], + \ 'vdmpp': ['file.vpp', 'file.vdmpp'], + \ 'vdmrt': ['file.vdmrt'], + \ 'vdmsl': ['file.vdm', 'file.vdmsl'], \ 'vera': ['file.vr', 'file.vri', 'file.vrh'], \ 'verilog': ['file.v'], \ 'verilogams': ['file.va', 'file.vams'], \ 'vgrindefs': ['vgrindefs'], \ 'vhdl': ['file.hdl', 'file.vhd', 'file.vhdl', 'file.vbe', 'file.vst', 'file.vhdl_123', 'file.vho', 'some.vhdl_1', 'some.vhdl_1-file'], + \ 'vhs': ['file.tape'], \ 'vim': ['file.vim', 'file.vba', '.exrc', '_exrc', 'some-vimrc', 'some-vimrc-file', 'vimrc', 'vimrc-file'], \ 'viminfo': ['.viminfo', '_viminfo'], \ 'vmasm': ['file.mar'], @@ -601,6 +632,7 @@ let s:filename_checks = { \ 'vroom': ['file.vroom'], \ 'vue': ['file.vue'], \ 'wast': ['file.wast', 'file.wat'], + \ 'wdl': ['file.wdl'], \ 'webmacro': ['file.wm'], \ 'wget': ['.wgetrc', 'wgetrc'], \ 'wget2': ['.wget2rc', 'wget2rc'], @@ -623,12 +655,13 @@ let s:filename_checks = { \ 'xsd': ['file.xsd'], \ 'xslt': ['file.xsl', 'file.xslt'], \ 'yacc': ['file.yy', 'file.yxx', 'file.y++'], - \ 'yaml': ['file.yaml', 'file.yml'], + \ 'yaml': ['file.yaml', 'file.yml', '.clang-format', '.clang-tidy'], \ 'yang': ['file.yang'], \ 'z8a': ['file.z8a'], \ 'zig': ['file.zig'], \ 'zimbu': ['file.zu'], \ 'zimbutempl': ['file.zut'], + \ 'zir': ['file.zir'], \ 'zsh': ['.zprofile', '/etc/zprofile', '.zfbfmarks', 'file.zsh', '.zcompdump', '.zlogin', '.zlogout', '.zshenv', '.zshrc', '.zcompdump-file', '.zlog', '.zlog-file', '.zsh', '.zsh-file', 'any/etc/zprofile', 'zlog', 'zlog-file', 'zsh', 'zsh-file'], \ \ 'help': [$VIMRUNTIME . '/doc/help.txt'], @@ -682,6 +715,13 @@ let s:script_checks = { \ ['__libc_start_main and something']], \ 'clojure': [['#!/path/clojure']], \ 'scala': [['#!/path/scala']], + \ 'sh': [['#!/path/sh'], + \ ['#!/path/bash'], + \ ['#!/path/bash2'], + \ ['#!/path/dash'], + \ ['#!/path/ksh'], + \ ['#!/path/ksh93']], + \ 'csh': [['#!/path/csh']], \ 'tcsh': [['#!/path/tcsh']], \ 'zsh': [['#!/path/zsh']], \ 'tcl': [['#!/path/tclsh'], @@ -918,7 +958,9 @@ func Test_d_file() call assert_equal('d', &filetype) bwipe! + " clean up filetype off + call delete('Xfile.d') endfunc func Test_dat_file() @@ -1346,7 +1388,7 @@ func Test_mod_file() unlet g:filetype_mod bwipe! - " RAPID header start with a line containing only "%%%", + " RAPID header start with a line containing only "%%%", " but is not always present. call writefile(['%%%'], 'modfile.mod') split modfile.mod @@ -1362,7 +1404,7 @@ func Test_mod_file() bwipe! call delete('modfile.Mod') - " RAPID is not case sensitive, embedded spaces, sysmodule, + " RAPID is not case sensitive, embedded spaces, sysmodule, " file starts with empty line(s). call writefile(['', 'MODULE rapidmödüle (SYSMODULE,NOSTEPIN)'], 'modfile.MOD') split modfile.MOD @@ -1490,7 +1532,7 @@ func Test_prg_file() unlet g:filetype_prg bwipe! - " RAPID header start with a line containing only "%%%", + " RAPID header start with a line containing only "%%%", " but is not always present. call writefile(['%%%'], 'prgfile.prg') split prgfile.prg @@ -1506,7 +1548,7 @@ func Test_prg_file() bwipe! call delete('prgfile.Prg') - " RAPID is not case sensitive, embedded spaces, sysmodule, + " RAPID is not case sensitive, embedded spaces, sysmodule, " file starts with empty line(s). call writefile(['', 'MODULE rapidmödüle (SYSMODULE,NOSTEPIN)'], 'prgfile.PRG') split prgfile.PRG @@ -1521,13 +1563,6 @@ endfunc func Test_sc_file() filetype on - " SC file methods are defined 'Class : Method' - call writefile(['SCNvimDocRenderer : SCDocHTMLRenderer {'], 'srcfile.sc') - split srcfile.sc - call assert_equal('supercollider', &filetype) - bwipe! - call delete('srcfile.sc') - " SC classes are defined with '+ Class {}' call writefile(['+ SCNvim {', '*methodArgs {|method|'], 'srcfile.sc') split srcfile.sc @@ -1617,7 +1652,7 @@ func Test_sys_file() unlet g:filetype_sys bwipe! - " RAPID header start with a line containing only "%%%", + " RAPID header start with a line containing only "%%%", " but is not always present. call writefile(['%%%'], 'sysfile.sys') split sysfile.sys @@ -1633,7 +1668,7 @@ func Test_sys_file() bwipe! call delete('sysfile.Sys') - " RAPID is not case sensitive, embedded spaces, sysmodule, + " RAPID is not case sensitive, embedded spaces, sysmodule, " file starts with empty line(s). call writefile(['', 'MODULE rapidmödüle (SYSMODULE,NOSTEPIN)'], 'sysfile.SYS') split sysfile.SYS @@ -1647,17 +1682,45 @@ endfunc func Test_tex_file() filetype on - " only tests one case, should do more + call writefile(['%& pdflatex'], 'Xfile.tex') + split Xfile.tex + call assert_equal('tex', &filetype) + bwipe + + call writefile(['\newcommand{\test}{some text}'], 'Xfile.tex') + split Xfile.tex + call assert_equal('tex', &filetype) + bwipe + + " tex_flavor is unset + call writefile(['%& plain'], 'Xfile.tex') + split Xfile.tex + call assert_equal('plaintex', &filetype) + bwipe + + let g:tex_flavor = 'plain' + call writefile(['just some text'], 'Xfile.tex') + split Xfile.tex + call assert_equal('plaintex', &filetype) + bwipe + let lines =<< trim END - % This is a sentence. + % This is a comment. - This is a sentence. + \usemodule[translate] END - call writefile(lines, "Xfile.tex") + call writefile(lines, 'Xfile.tex') split Xfile.tex - call assert_equal('plaintex', &filetype) + call assert_equal('context', &filetype) bwipe + let g:tex_flavor = 'context' + call writefile(['just some text'], 'Xfile.tex') + split Xfile.tex + call assert_equal('context', &filetype) + bwipe + unlet g:tex_flavor + call delete('Xfile.tex') filetype off endfunc @@ -1748,6 +1811,11 @@ func Test_cls_file() call assert_equal('tex', &filetype) bwipe! + call writefile(['\NeedsTeXFormat{LaTeX2e}'], 'Xfile.cls') + split Xfile.cls + call assert_equal('tex', &filetype) + bwipe! + " Rexx call writefile(['# rexx'], 'Xfile.cls') @@ -1820,6 +1888,44 @@ func Test_sig_file() filetype off endfunc +" Test dist#ft#FTsil() +func Test_sil_file() + filetype on + + split Xfile.sil + call assert_equal('sil', &filetype) + bwipe! + + let lines =<< trim END + // valid + let protoErasedPathA = \ABCProtocol.a + + // also valid + let protoErasedPathA = + \ABCProtocol.a + END + call writefile(lines, 'Xfile.sil') + + split Xfile.sil + call assert_equal('sil', &filetype) + bwipe! + + " SILE + + call writefile(['% some comment'], 'Xfile.sil') + split Xfile.sil + call assert_equal('sile', &filetype) + bwipe! + + call writefile(['\begin[papersize=a6]{document}foo\end{document}'], 'Xfile.sil') + split Xfile.sil + call assert_equal('sile', &filetype) + bwipe! + + call delete('Xfile.sil') + filetype off +endfunc + func Test_inc_file() filetype on @@ -1899,4 +2005,36 @@ func Test_inc_file() filetype off endfunc +func Test_lsl_file() + filetype on + + call writefile(['looks like Linden Scripting Language'], 'Xfile.lsl') + split Xfile.lsl + call assert_equal('lsl', &filetype) + bwipe! + + " Test dist#ft#FTlsl() + + let g:filetype_lsl = 'larch' + split Xfile.lsl + call assert_equal('larch', &filetype) + bwipe! + unlet g:filetype_lsl + + " Larch Shared Language + + call writefile(['% larch comment'], 'Xfile.lsl') + split Xfile.lsl + call assert_equal('larch', &filetype) + bwipe! + + call writefile(['foo: trait'], 'Xfile.lsl') + split Xfile.lsl + call assert_equal('larch', &filetype) + bwipe! + + call delete('Xfile.lsl') + filetype off +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_filter_map.vim b/src/nvim/testdir/test_filter_map.vim index 1cd3a2287b..c75177ea39 100644 --- a/src/nvim/testdir/test_filter_map.vim +++ b/src/nvim/testdir/test_filter_map.vim @@ -86,6 +86,13 @@ func Test_map_filter_fails() call assert_fails('call filter([1], "42 +")', 'E15:') call assert_fails("let l = map('abc', '\"> \" . v:val')", 'E896:') call assert_fails("let l = filter('abc', '\"> \" . v:val')", 'E896:') + call assert_fails("let l = filter([1, 2, 3], '{}')", 'E728:') + call assert_fails("let l = filter({'k' : 10}, '{}')", 'E728:') + call assert_fails("let l = filter([1, 2], {})", 'E731:') + call assert_equal(v:_null_list, filter(v:_null_list, 0)) + call assert_equal(v:_null_dict, filter(v:_null_dict, 0)) + call assert_equal(v:_null_list, map(v:_null_list, '"> " .. v:val')) + call assert_equal(v:_null_dict, map(v:_null_dict, '"> " .. v:val')) endfunc func Test_map_and_modify() diff --git a/src/nvim/testdir/test_fixeol.vim b/src/nvim/testdir/test_fixeol.vim index 32cb059e26..41d47d6a06 100644 --- a/src/nvim/testdir/test_fixeol.vim +++ b/src/nvim/testdir/test_fixeol.vim @@ -1,16 +1,17 @@ -" Tests for 'fixeol' and 'eol' +" Tests for 'fixeol', 'eof' and 'eol' + func Test_fixeol() " first write two test files – with and without trailing EOL " use Unix fileformat for consistency set ff=unix enew! - call setline('.', 'with eol') + call setline('.', 'with eol or eof') w! XXEol enew! - set noeol nofixeol - call setline('.', 'without eol') + set noeof noeol nofixeol + call setline('.', 'without eol or eof') w! XXNoEol - set eol fixeol + set eol eof fixeol bwipe XXEol XXNoEol " try editing files with 'fixeol' disabled @@ -33,16 +34,85 @@ func Test_fixeol() w >>XXTestEol w >>XXTestNoEol - call assert_equal(['with eol', 'END'], readfile('XXEol')) - call assert_equal(['without eolEND'], readfile('XXNoEol')) - call assert_equal(['with eol', 'stays eol', 'END'], readfile('XXTestEol')) - call assert_equal(['without eol', 'stays withoutEND'], + call assert_equal(['with eol or eof', 'END'], readfile('XXEol')) + call assert_equal(['without eol or eofEND'], readfile('XXNoEol')) + call assert_equal(['with eol or eof', 'stays eol', 'END'], readfile('XXTestEol')) + call assert_equal(['without eol or eof', 'stays withoutEND'], \ readfile('XXTestNoEol')) call delete('XXEol') call delete('XXNoEol') call delete('XXTestEol') call delete('XXTestNoEol') - set ff& fixeol& eol& + set ff& fixeol& eof& eol& + enew! +endfunc + +func Test_eof() + let data = 0z68656c6c6f.0d0a.776f726c64 " "hello\r\nworld" + + " 1. Eol, Eof + " read + call writefile(data + 0z0d0a.1a, 'XXEolEof') + e! XXEolEof + call assert_equal(['hello', 'world'], getline(1, 2)) + call assert_equal([1, 1], [&eol, &eof]) + " write + set fixeol + w! + call assert_equal(data + 0z0d0a, readblob('XXEolEof')) + set nofixeol + w! + call assert_equal(data + 0z0d0a.1a, readblob('XXEolEof')) + + " 2. NoEol, Eof + " read + call writefile(data + 0z1a, 'XXNoEolEof') + e! XXNoEolEof + call assert_equal(['hello', 'world'], getline(1, 2)) + call assert_equal([0, 1], [&eol, &eof]) + " write + set fixeol + w! + call assert_equal(data + 0z0d0a, readblob('XXNoEolEof')) + set nofixeol + w! + call assert_equal(data + 0z1a, readblob('XXNoEolEof')) + + " 3. Eol, NoEof + " read + call writefile(data + 0z0d0a, 'XXEolNoEof') + e! XXEolNoEof + call assert_equal(['hello', 'world'], getline(1, 2)) + call assert_equal([1, 0], [&eol, &eof]) + " write + set fixeol + w! + call assert_equal(data + 0z0d0a, readblob('XXEolNoEof')) + set nofixeol + w! + call assert_equal(data + 0z0d0a, readblob('XXEolNoEof')) + + " 4. NoEol, NoEof + " read + call writefile(data, 'XXNoEolNoEof') + e! XXNoEolNoEof + call assert_equal(['hello', 'world'], getline(1, 2)) + call assert_equal([0, 0], [&eol, &eof]) + " write + set fixeol + w! + call assert_equal(data + 0z0d0a, readblob('XXNoEolNoEof')) + set nofixeol + w! + call assert_equal(data, readblob('XXNoEolNoEof')) + + call delete('XXEolEof') + call delete('XXNoEolEof') + call delete('XXEolNoEof') + call delete('XXNoEolNoEof') + set ff& fixeol& eof& eol& enew! endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_fnamemodify.vim b/src/nvim/testdir/test_fnamemodify.vim index 5ae2a5ee17..258a2093bd 100644 --- a/src/nvim/testdir/test_fnamemodify.vim +++ b/src/nvim/testdir/test_fnamemodify.vim @@ -11,6 +11,7 @@ func Test_fnamemodify() call assert_equal('/', fnamemodify('.', ':p')[-1:]) call assert_equal('r', fnamemodify('.', ':p:h')[-1:]) call assert_equal('t', fnamemodify('test.out', ':p')[-1:]) + call assert_equal($HOME .. "/foo" , fnamemodify('~/foo', ':p')) call assert_equal('test.out', fnamemodify('test.out', ':.')) call assert_equal('a', fnamemodify('../testdir/a', ':.')) call assert_equal('~/testdir/test.out', fnamemodify('test.out', ':~')) @@ -95,4 +96,9 @@ func Test_fnamemodify_er() call assert_equal('', fnamemodify(v:_null_string, v:_null_string)) endfunc +func Test_fnamemodify_fail() + call assert_fails('call fnamemodify({}, ":p")', 'E731:') + call assert_fails('call fnamemodify("x", {})', 'E731:') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim index 327f0f73f2..19415286ad 100644 --- a/src/nvim/testdir/test_fold.vim +++ b/src/nvim/testdir/test_fold.vim @@ -1,5 +1,6 @@ " Test for folding +source check.vim source view_util.vim source screendump.vim @@ -71,6 +72,54 @@ func Test_address_fold() quit! endfunc +func Test_address_offsets() + " check the help for :range-closed-fold + enew + call setline(1, [ + \ '1 one', + \ '2 two', + \ '3 three', + \ '4 four FOLDED', + \ '5 five FOLDED', + \ '6 six', + \ '7 seven', + \ '8 eight', + \]) + set foldmethod=manual + normal 4Gvjzf + 3,4+2yank + call assert_equal([ + \ '3 three', + \ '4 four FOLDED', + \ '5 five FOLDED', + \ '6 six', + \ '7 seven', + \ ], getreg(0,1,1)) + + enew! + call setline(1, [ + \ '1 one', + \ '2 two', + \ '3 three FOLDED', + \ '4 four FOLDED', + \ '5 five FOLDED', + \ '6 six FOLDED', + \ '7 seven', + \ '8 eight', + \]) + normal 3Gv3jzf + 2,4-1yank + call assert_equal([ + \ '2 two', + \ '3 three FOLDED', + \ '4 four FOLDED', + \ '5 five FOLDED', + \ '6 six FOLDED', + \ ], getreg(0,1,1)) + + bwipe! +endfunc + func Test_indent_fold() new call setline(1, ['', 'a', ' b', ' c']) @@ -93,10 +142,44 @@ func Test_indent_fold2() bw! endfunc +" Test for fold indent with indents greater than 'foldnestmax' +func Test_indent_fold_max() + new + setlocal foldmethod=indent + setlocal shiftwidth=2 + " 'foldnestmax' default value is 20 + call setline(1, "\t\t\t\t\t\ta") + call assert_equal(20, foldlevel(1)) + setlocal foldnestmax=10 + call assert_equal(10, foldlevel(1)) + setlocal foldnestmax=-1 + call assert_equal(0, foldlevel(1)) + bw! +endfunc + +func Test_indent_fold_tabstop() + call setline(1, ['0', ' 1', ' 1', "\t2", "\t2"]) + setlocal shiftwidth=4 + setlocal foldcolumn=1 + setlocal foldlevel=2 + setlocal foldmethod=indent + redraw + call assert_equal('2 2', ScreenLines(5, 10)[0]) + vsplit + windo diffthis + botright new + " This 'tabstop' value should not be used for folding in other buffers. + setlocal tabstop=4 + diffoff! + redraw + call assert_equal('2 2', ScreenLines(5, 10)[0]) + + bwipe! + bwipe! +endfunc + func Test_manual_fold_with_filter() - if !executable('cat') - return - endif + CheckExecutable cat for type in ['manual', 'marker'] exe 'set foldmethod=' . type new @@ -418,27 +501,6 @@ func Test_move_folds_around_indent() bw! endfunc -" test for patch 7.3.637 -" Cannot catch the error caused by a foldopen when there is no fold. -func Test_foldopen_exception() - enew! - let a = 'No error caught' - try - foldopen - catch - let a = matchstr(v:exception,'^[^ ]*') - endtry - call assert_equal('Vim(foldopen):E490:', a) - - let a = 'No error caught' - try - foobar - catch - let a = matchstr(v:exception,'^[^ ]*') - endtry - call assert_match('E492:', a) -endfunc - func Test_folddoopen_folddoclosed() new call setline(1, range(1, 9)) @@ -492,11 +554,24 @@ func Test_fold_error() bw! endfunc +func Test_foldtext_recursive() + new + call setline(1, ['{{{', 'some text', '}}}']) + setlocal foldenable foldmethod=marker foldtext=foldtextresult(v\:foldstart) + " This was crashing because of endless recursion. + 2foldclose + redraw + call assert_equal(1, foldlevel(2)) + call assert_equal(1, foldclosed(2)) + call assert_equal(3, foldclosedend(2)) + bwipe! +endfunc + " Various fold related tests " Basic test if a fold can be created, opened, moving to the end and closed func Test_fold_manual() - enew! + new set fdm=manual let content = ['1 aa', '2 bb', '3 cc'] @@ -511,13 +586,67 @@ func Test_fold_manual() normal zc call assert_equal('1 aa', getline(foldclosed('.'))) + " Create a fold inside a closed fold after setting 'foldlevel' + %d _ + call setline(1, range(1, 5)) + 1,5fold + normal zR + 2,4fold + set foldlevel=1 + 3fold + call assert_equal([1, 3, 3, 3, 1], map(range(1, 5), {->foldlevel(v:val)})) + set foldlevel& + + " Create overlapping folds (at the start and at the end) + normal zE + 2,3fold + normal zR + 3,4fold + call assert_equal([0, 2, 2, 1, 0], map(range(1, 5), {->foldlevel(v:val)})) + normal zE + 3,4fold + normal zR + 2,3fold + call assert_equal([0, 1, 2, 2, 0], map(range(1, 5), {->foldlevel(v:val)})) + + " Create a nested fold across two non-adjoining folds + %d _ + call setline(1, range(1, 7)) + 1,2fold + normal zR + 4,5fold + normal zR + 6,7fold + normal zR + 1,5fold + call assert_equal([2, 2, 1, 2, 2, 1, 1], + \ map(range(1, 7), {->foldlevel(v:val)})) + + " A newly created nested fold should be closed + %d _ + call setline(1, range(1, 6)) + 1,6fold + normal zR + 3,4fold + normal zR + 2,5fold + call assert_equal([1, 2, 3, 3, 2, 1], map(range(1, 6), {->foldlevel(v:val)})) + call assert_equal(2, foldclosed(4)) + call assert_equal(5, foldclosedend(4)) + + " Test zO, zC and zA on a line with no folds. + normal zE + call assert_fails('normal zO', 'E490:') + call assert_fails('normal zC', 'E490:') + call assert_fails('normal zA', 'E490:') + set fdm& - enew! + bw! endfunc " test folding with markers. func Test_fold_marker() - enew! + new set fdm=marker fdl=1 fdc=3 let content = ['4 dd {{{', '5 ee {{{ }}}', '6 ff }}}'] @@ -531,13 +660,22 @@ func Test_fold_marker() normal kYpj call assert_equal(0, foldlevel('.')) + " Use only closing fold marker (without and with a count) + set fdl& + %d _ + call setline(1, ['one }}}', 'two']) + call assert_equal([0, 0], [foldlevel(1), foldlevel(2)]) + %d _ + call setline(1, ['one }}}4', 'two']) + call assert_equal([4, 3], [foldlevel(1), foldlevel(2)]) + set fdm& fdl& fdc& - enew! + bw! endfunc " test create fold markers with C filetype func Test_fold_create_marker_in_C() - enew! + bw! set fdm=marker fdl=9 set filetype=c @@ -562,12 +700,12 @@ func Test_fold_create_marker_in_C() endfor set fdm& fdl& - enew! + bw! endfunc " test folding with indent func Test_fold_indent() - enew! + new set fdm=indent sw=2 let content = ['1 aa', '2 bb', '3 cc'] @@ -579,16 +717,14 @@ func Test_fold_indent() call assert_equal(1, foldlevel('.')) set fdm& sw& - enew! + bw! endfunc " test syntax folding func Test_fold_syntax() - if !has('syntax') - return - endif + CheckFeature syntax - enew! + new set fdm=syntax fdl=0 syn region Hup start="dd" end="ii" fold contains=Fd1,Fd2,Fd3 @@ -612,7 +748,7 @@ func Test_fold_syntax() syn clear Fd1 Fd2 Fd3 Hup set fdm& fdl& - enew! + bw! endfunc func Flvl() @@ -631,7 +767,7 @@ endfun " test expression folding func Test_fold_expr() - enew! + new set fdm=expr fde=Flvl() let content = ['1 aa', @@ -659,14 +795,14 @@ func Test_fold_expr() call assert_equal(0, foldlevel('.')) set fdm& fde& - enew! + bw! endfunc " Bug with fdm=indent and moving folds " Moving a fold a few times, messes up the folds below the moved fold. " Fixed by 7.4.700 func Test_fold_move() - enew! + new set fdm=indent sw=2 fdl=0 let content = ['', '', 'Line1', ' Line2', ' Line3', @@ -686,24 +822,33 @@ func Test_fold_move() call assert_equal('+-- 2 lines: Line8', 10->foldtextresult()) set fdm& sw& fdl& - enew! + bw! endfunc -func Test_foldtext_recursive() +" test for patch 7.3.637 +" Cannot catch the error caused by a foldopen when there is no fold. +func Test_foldopen_exception() new - call setline(1, ['{{{', 'some text', '}}}']) - setlocal foldenable foldmethod=marker foldtext=foldtextresult(v\:foldstart) - " This was crashing because of endless recursion. - 2foldclose - redraw - call assert_equal(1, foldlevel(2)) - call assert_equal(1, foldclosed(2)) - call assert_equal(3, foldclosedend(2)) - bwipe! + let a = 'No error caught' + try + foldopen + catch + let a = matchstr(v:exception,'^[^ ]*') + endtry + call assert_equal('Vim(foldopen):E490:', a) + + let a = 'No error caught' + try + foobar + catch + let a = matchstr(v:exception,'^[^ ]*') + endtry + call assert_match('E492:', a) + bw! endfunc func Test_fold_last_line_with_pagedown() - enew! + new set fdm=manual let expect = '+-- 11 lines: 9---' @@ -723,13 +868,11 @@ func Test_fold_last_line_with_pagedown() call assert_equal(expect, ScreenLines(1, len(expect))[0]) set fdm& - enew! + bw! endfunc func Test_folds_with_rnu() - if !CanRunVimInTerminal() - throw 'Skipped: cannot make screendumps' - endif + CheckScreendump call writefile([ \ 'set fdm=marker rnu foldcolumn=2', @@ -816,6 +959,55 @@ func Test_fold_delete_first_line() set foldmethod& endfunc +" Add a test for deleting the outer fold of a nested fold and promoting the +" inner folds to one level up with already a fold at that level following the +" nested fold. +func Test_fold_delete_recursive_fold() + new + call setline(1, range(1, 7)) + 2,3fold + normal zR + 4,5fold + normal zR + 1,5fold + normal zR + 6,7fold + normal zR + normal 1Gzd + normal 1Gzj + call assert_equal(2, line('.')) + normal zj + call assert_equal(4, line('.')) + normal zj + call assert_equal(6, line('.')) + bw! +endfunc + +" Test for errors in 'foldexpr' +func Test_fold_expr_error() + new + call setline(1, ['one', 'two', 'three']) + " In a window with no folds, foldlevel() should return 0 + call assert_equal(0, foldlevel(1)) + + " Return a list from the expression + set foldexpr=[] + set foldmethod=expr + for i in range(3) + call assert_equal(0, foldlevel(i)) + endfor + + " expression error + set foldexpr=[{] + set foldmethod=expr + for i in range(3) + call assert_equal(0, foldlevel(i)) + endfor + + set foldmethod& foldexpr& + close! +endfunc + func Test_undo_fold_deletion() new set fdm=marker @@ -899,6 +1091,264 @@ func Test_fold_relative_move() call assert_equal(2, winline()) set fdm& sw& wrap& tw& + bw! +endfunc + +" Test for calling foldlevel() from a fold expression +let g:FoldLevels = [] +func FoldExpr1(lnum) + let f = [a:lnum] + for i in range(1, line('$')) + call add(f, foldlevel(i)) + endfor + call add(g:FoldLevels, f) + return getline(a:lnum)[0] == "\t" +endfunc + +func Test_foldexpr_foldlevel() + new + call setline(1, ['one', "\ttwo", "\tthree"]) + setlocal foldmethod=expr + setlocal foldexpr=FoldExpr1(v:lnum) + setlocal foldenable + setlocal foldcolumn=3 + redraw! + call assert_equal([[1, -1, -1, -1], [2, -1, -1, -1], [3, 0, 1, -1]], + \ g:FoldLevels) + set foldmethod& foldexpr& foldenable& foldcolumn& + bw! +endfunc + +" Test for returning different values from a fold expression +func FoldExpr2(lnum) + if a:lnum == 1 || a:lnum == 4 + return -2 + elseif a:lnum == 2 + return 'a1' + elseif a:lnum == 3 + return 's4' + endif + return '=' +endfunc + +func Test_foldexpr_2() + new + call setline(1, ['one', 'two', 'three', 'four']) + setlocal foldexpr=FoldExpr2(v:lnum) + setlocal foldmethod=expr + call assert_equal([0, 1, 1, 0], [foldlevel(1), foldlevel(2), foldlevel(3), + \ foldlevel(4)]) + bw! +endfunc + +" Test for the 'foldclose' option +func Test_foldclose_opt() + CheckScreendump + + let lines =<< trim END + set foldmethod=manual foldclose=all foldopen=all + call setline(1, ['one', 'two', 'three', 'four']) + 2,3fold + func XsaveFoldLevels() + redraw! + call writefile([json_encode([foldclosed(1), foldclosed(2), foldclosed(3), + \ foldclosed(4)])], 'Xoutput', 'a') + endfunc + END + call writefile(lines, 'Xscript') + let rows = 10 + let buf = RunVimInTerminal('-S Xscript', {'rows': rows}) + call term_wait(buf) + call term_sendkeys(buf, ":set noruler\n") + call term_wait(buf) + call term_sendkeys(buf, ":call XsaveFoldLevels()\n") + call term_sendkeys(buf, "2G") + call WaitForAssert({-> assert_equal('two', term_getline(buf, 2))}) + call term_sendkeys(buf, ":call XsaveFoldLevels()\n") + call term_sendkeys(buf, "4G") + call WaitForAssert({-> assert_equal('four', term_getline(buf, 3))}) + call term_sendkeys(buf, ":call XsaveFoldLevels()\n") + call term_sendkeys(buf, "3G") + call WaitForAssert({-> assert_equal('three', term_getline(buf, 3))}) + call term_sendkeys(buf, ":call XsaveFoldLevels()\n") + call term_sendkeys(buf, "1G") + call WaitForAssert({-> assert_equal('four', term_getline(buf, 3))}) + call term_sendkeys(buf, ":call XsaveFoldLevels()\n") + call term_sendkeys(buf, "2G") + call WaitForAssert({-> assert_equal('two', term_getline(buf, 2))}) + call term_sendkeys(buf, "k") + call WaitForAssert({-> assert_equal('four', term_getline(buf, 3))}) + + " clean up + call StopVimInTerminal(buf) + + call assert_equal(['[-1,2,2,-1]', '[-1,-1,-1,-1]', '[-1,2,2,-1]', + \ '[-1,-1,-1,-1]', '[-1,2,2,-1]'], readfile('Xoutput')) + call delete('Xscript') + call delete('Xoutput') +endfunc + +" Test for foldtextresult() +func Test_foldtextresult() + new + call assert_equal('', foldtextresult(-1)) + call assert_equal('', foldtextresult(0)) + call assert_equal('', foldtextresult(1)) + call setline(1, ['one', 'two', 'three', 'four']) + 2,3fold + call assert_equal('', foldtextresult(1)) + call assert_equal('+-- 2 lines: two', foldtextresult(2)) + setlocal foldtext= + call assert_equal('+-- 2 lines folded ', foldtextresult(2)) + + " Fold text for a C comment fold + %d _ + setlocal foldtext& + call setline(1, ['', '/*', ' * Comment', ' */', '']) + 2,4fold + call assert_equal('+-- 3 lines: Comment', foldtextresult(2)) + + bw! +endfunc + +" Test for merging two recursive folds when an intermediate line with no fold +" is removed +func Test_fold_merge_recursive() + new + call setline(1, [' one', ' two', 'xxxx', ' three', + \ ' four', "\tfive"]) + setlocal foldmethod=indent shiftwidth=2 + 3d_ + %foldclose + call assert_equal([1, 5], [foldclosed(5), foldclosedend(1)]) + bw! +endfunc + +" Test for moving a line which is the start of a fold from a recursive fold to +" outside. The fold length should reduce. +func Test_fold_move_foldlevel() + new + call setline(1, ['a{{{', 'b{{{', 'c{{{', 'd}}}', 'e}}}', 'f}}}', 'g']) + setlocal foldmethod=marker + normal zR + call assert_equal([3, 2, 1], [foldlevel(4), foldlevel(5), foldlevel(6)]) + 3move 7 + call assert_equal([2, 1, 0], [foldlevel(3), foldlevel(4), foldlevel(5)]) + call assert_equal(1, foldlevel(7)) + + " Move a line from outside a fold to inside the fold. + %d _ + call setline(1, ['a', 'b{{{', 'c}}}']) + normal zR + 1move 2 + call assert_equal([1, 1, 1], [foldlevel(1), foldlevel(2), foldlevel(3)]) + + " Move the start of one fold to inside another fold + %d _ + call setline(1, ['a', 'b{{{', 'c}}}', 'd{{{', 'e}}}']) + normal zR + call assert_equal([0, 1, 1, 1, 1], [foldlevel(1), foldlevel(2), + \ foldlevel(3), foldlevel(4), foldlevel(5)]) + 1,2move 4 + call assert_equal([0, 1, 1, 2, 2], [foldlevel(1), foldlevel(2), + \ foldlevel(3), foldlevel(4), foldlevel(5)]) + + bw! +endfunc + +" Test for using zj and zk to move downwards and upwards to the start and end +" of the next fold. +" Test for using [z and ]z in a closed fold to jump to the beginning and end +" of the fold. +func Test_fold_jump() + new + call setline(1, ["\t1", "\t2", "\t\t3", "\t\t4", "\t\t\t5", "\t\t\t6", "\t\t7", "\t\t8", "\t9", "\t10"]) + setlocal foldmethod=indent + normal zR + normal zj + call assert_equal(3, line('.')) + normal zj + call assert_equal(5, line('.')) + call assert_beeps('normal zj') + call assert_equal(5, line('.')) + call assert_beeps('normal 9Gzj') + call assert_equal(9, line('.')) + normal Gzk + call assert_equal(8, line('.')) + normal zk + call assert_equal(6, line('.')) + call assert_beeps('normal zk') + call assert_equal(6, line('.')) + call assert_beeps('normal 2Gzk') + call assert_equal(2, line('.')) + + " Using [z or ]z in a closed fold should not move the cursor + %d _ + call setline(1, ["1", "\t2", "\t3", "\t4", "\t5", "\t6", "7"]) + normal zR4Gzc + call assert_equal(4, line('.')) + call assert_beeps('normal [z') + call assert_equal(4, line('.')) + call assert_beeps('normal ]z') + call assert_equal(4, line('.')) + bw! +endfunc + +" Test for using a script-local function for 'foldexpr' +func Test_foldexpr_scriptlocal_func() + func! s:FoldFunc() + let g:FoldLnum = v:lnum + endfunc + new | only + call setline(1, 'abc') + let g:FoldLnum = 0 + set foldmethod=expr foldexpr=s:FoldFunc() + redraw! + call assert_equal(expand('<SID>') .. 'FoldFunc()', &foldexpr) + call assert_equal(1, g:FoldLnum) + set foldmethod& foldexpr= + bw! + new | only + call setline(1, 'abc') + let g:FoldLnum = 0 + set foldmethod=expr foldexpr=<SID>FoldFunc() + redraw! + call assert_equal(expand('<SID>') .. 'FoldFunc()', &foldexpr) + call assert_equal(1, g:FoldLnum) + set foldmethod& foldexpr= + delfunc s:FoldFunc + bw! +endfunc + +" Test for using a script-local function for 'foldtext' +func Test_foldtext_scriptlocal_func() + func! s:FoldText() + let g:FoldTextArgs = [v:foldstart, v:foldend] + return foldtext() + endfunc + new | only + call setline(1, range(50)) + let g:FoldTextArgs = [] + set foldmethod=manual + set foldtext=s:FoldText() + norm! 4Gzf4j + redraw! + call assert_equal(expand('<SID>') .. 'FoldText()', &foldtext) + call assert_equal([4, 8], g:FoldTextArgs) + set foldtext& + bw! + new | only + call setline(1, range(50)) + let g:FoldTextArgs = [] + set foldmethod=manual + set foldtext=<SID>FoldText() + norm! 8Gzf4j + redraw! + call assert_equal(expand('<SID>') .. 'FoldText()', &foldtext) + call assert_equal([8, 12], g:FoldTextArgs) + set foldtext& + bw! + delfunc s:FoldText endfunc " Make sure a fold containing a nested fold is split correctly when using @@ -989,4 +1439,61 @@ func Test_indent_append_blank_small_fold_close() bw! endfunc +func Test_sort_closed_fold() + CheckExecutable sort + + call setline(1, [ + \ 'Section 1', + \ ' how', + \ ' now', + \ ' brown', + \ ' cow', + \ 'Section 2', + \ ' how', + \ ' now', + \ ' brown', + \ ' cow', + \]) + setlocal foldmethod=indent sw=3 + normal 2G + + " The "!!" expands to ".,.+3" and must only sort four lines + call feedkeys("!!sort\<CR>", 'xt') + call assert_equal([ + \ 'Section 1', + \ ' brown', + \ ' cow', + \ ' how', + \ ' now', + \ 'Section 2', + \ ' how', + \ ' now', + \ ' brown', + \ ' cow', + \ ], getline(1, 10)) + + bwipe! +endfunc + +func Test_indent_with_L_command() + " The "L" command moved the cursor to line zero, causing the text saved for + " undo to use line number -1, which caused trouble for undo later. + new + sil! norm 8R
V{zf8=Lu + bwipe! +endfunc + +" Make sure that when there is a fold at the bottom of the buffer and a newline +" character is appended to the line, the fold gets expanded (instead of the new +" line not being part of the fold). +func Test_expand_fold_at_bottom_of_buffer() + new + " create a fold on the only line + fold + execute "normal A\<CR>" + call assert_equal([1, 1], range(1, 2)->map('foldlevel(v:val)')) + + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 44b6f0373e..500c30c76b 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -2,6 +2,9 @@ source shared.vim source check.vim +source term_util.vim +source screendump.vim +source vim9.vim " Must be done first, since the alternate buffer must be unset. func Test_00_bufexists() @@ -19,6 +22,27 @@ func Test_00_bufexists() call assert_equal(0, bufexists('Xfoo')) endfunc +func Test_has() + throw 'Skipped: Nvim has removed some features' + call assert_equal(1, has('eval')) + call assert_equal(1, has('eval', 1)) + + if has('unix') + call assert_equal(1, or(has('ttyin'), 1)) + call assert_equal(0, and(has('ttyout'), 0)) + call assert_equal(1, has('multi_byte_encoding')) + endif + call assert_equal(1, has('vcon', 1)) + call assert_equal(1, has('mouse_gpm_enabled', 1)) + + call assert_equal(0, has('nonexistent')) + call assert_equal(0, has('nonexistent', 1)) + + " Will we ever have patch 9999? + let ver = 'patch-' .. v:version / 100 .. '.' .. v:version % 100 .. '.9999' + call assert_equal(0, has(ver)) +endfunc + func Test_empty() call assert_equal(1, empty('')) call assert_equal(0, empty('a')) @@ -67,9 +91,11 @@ func Test_len() call assert_equal(2, len('ab')) call assert_equal(0, len([])) + call assert_equal(0, len(v:_null_list)) call assert_equal(2, len([2, 1])) call assert_equal(0, len({})) + call assert_equal(0, len(v:_null_dict)) call assert_equal(2, len({'a': 1, 'b': 2})) " call assert_fails('call len(v:none)', 'E701:') @@ -87,6 +113,10 @@ func Test_max() call assert_fails('call max(1)', 'E712:') " call assert_fails('call max(v:none)', 'E712:') + + " check we only get one error + call assert_fails('call max([#{}, [1]])', ['E728:', 'E728:']) + call assert_fails('call max(#{a: {}, b: [1]})', ['E728:', 'E728:']) endfunc func Test_min() @@ -100,6 +130,11 @@ func Test_min() call assert_fails('call min(1)', 'E712:') " call assert_fails('call min(v:none)', 'E712:') + call assert_fails('call min([1, {}])', 'E728:') + + " check we only get one error + call assert_fails('call min([[1], #{}])', ['E745:', 'E745:']) + call assert_fails('call min(#{a: [1], b: #{}})', ['E745:', 'E745:']) endfunc func Test_strwidth() @@ -188,7 +223,7 @@ func Test_str2nr() if has('float') call assert_fails('call str2nr(1.2)', 'E806:') endif - call assert_fails('call str2nr(10, [])', 'E474:') + call assert_fails('call str2nr(10, [])', 'E745:') endfunc func Test_strftime() @@ -381,6 +416,8 @@ func Test_strpart() call assert_equal('abcdefg', 'abcdefg'->strpart(-2)) call assert_equal('fg', strpart('abcdefg', 5, 4)) call assert_equal('defg', strpart('abcdefg', 3)) + call assert_equal('', strpart('abcdefg', 10)) + call assert_fails("let s=strpart('abcdef', [])", 'E745:') call assert_equal('lép', strpart('éléphant', 2, 4)) call assert_equal('léphant', strpart('éléphant', 2)) @@ -462,6 +499,12 @@ func Test_tolower() " invalid memory. call tolower("\xC0\x80\xC0") call tolower("123\xC0\x80\xC0") + + " Test in latin1 encoding + let save_enc = &encoding + " set encoding=latin1 + call assert_equal("abc", tolower("ABC")) + let &encoding = save_enc endfunc func Test_toupper() @@ -533,11 +576,27 @@ func Test_toupper() " invalid memory. call toupper("\xC0\x80\xC0") call toupper("123\xC0\x80\xC0") + + " Test in latin1 encoding + let save_enc = &encoding + " set encoding=latin1 + call assert_equal("ABC", toupper("abc")) + let &encoding = save_enc endfunc func Test_tr() call assert_equal('foo', tr('bar', 'bar', 'foo')) call assert_equal('zxy', 'cab'->tr('abc', 'xyz')) + call assert_fails("let s=tr([], 'abc', 'def')", 'E730:') + call assert_fails("let s=tr('abc', [], 'def')", 'E730:') + call assert_fails("let s=tr('abc', 'abc', [])", 'E730:') + call assert_fails("let s=tr('abcd', 'abcd', 'def')", 'E475:') + " set encoding=latin1 + call assert_fails("let s=tr('abcd', 'abcd', 'def')", 'E475:') + call assert_equal('hEllO', tr('hello', 'eo', 'EO')) + call assert_equal('hello', tr('hello', 'xy', 'ab')) + call assert_fails('call tr("abc", "123", "₁₂")', 'E475:') + set encoding=utf8 endfunc " Tests for the mode() function @@ -752,13 +811,41 @@ func Test_mode() delfunction OperatorFunc endfunc +" Test for append() func Test_append() enew! split call append(0, ["foo"]) + call append(1, []) + call append(1, v:_null_list) + call assert_equal(['foo', ''], getline(1, '$')) split only undo + undo + + " Using $ instead of '$' must give an error + call assert_fails("call append($, 'foobar')", 'E116:') + + call assert_fails("call append({}, '')", ['E728:', 'E728:']) +endfunc + +" Test for setline() +func Test_setline() + new + call setline(0, ["foo"]) + call setline(0, []) + call setline(0, v:_null_list) + call setline(1, ["bar"]) + call setline(1, []) + call setline(1, v:_null_list) + call setline(2, []) + call setline(2, v:_null_list) + call setline(3, []) + call setline(3, v:_null_list) + call setline(2, ["baz"]) + call assert_equal(['bar', 'baz'], getline(1, '$')) + close! endfunc func Test_getbufvar() @@ -792,6 +879,10 @@ func Test_getbufvar() call assert_equal(0, getbufvar(bnr, '&autoindent')) call assert_equal(0, getbufvar(bnr, '&autoindent', 1)) + " Set and get a buffer-local variable + call setbufvar(bnr, 'bufvar_test', ['one', 'two']) + call assert_equal(['one', 'two'], getbufvar(bnr, 'bufvar_test')) + " Open new window with forced option values set fileformats=unix,dos new ++ff=dos ++bin ++enc=iso-8859-2 @@ -830,6 +921,8 @@ func Test_stridx() call assert_equal(-1, stridx('hello', 'l', 10)) call assert_equal(2, stridx('hello', 'll')) call assert_equal(-1, stridx('hello', 'hello world')) + call assert_fails("let n=stridx('hello', [])", 'E730:') + call assert_fails("let n=stridx([], 'l')", 'E730:') endfunc func Test_strridx() @@ -846,6 +939,8 @@ func Test_strridx() call assert_equal(-1, strridx('hello', 'l', -1)) call assert_equal(2, strridx('hello', 'll')) call assert_equal(-1, strridx('hello', 'hello world')) + call assert_fails("let n=strridx('hello', [])", 'E730:') + call assert_fails("let n=strridx([], 'l')", 'E730:') endfunc func Test_match_func() @@ -855,6 +950,13 @@ func Test_match_func() call assert_equal(-1, match('testing', 'ing', 8)) call assert_equal(1, match(['vim', 'testing', 'execute'], 'ing')) call assert_equal(-1, match(['vim', 'testing', 'execute'], 'img')) + call assert_fails("let x=match('vim', [])", 'E730:') + call assert_equal(3, match(['a', 'b', 'c', 'a'], 'a', 1)) + call assert_equal(-1, match(['a', 'b', 'c', 'a'], 'a', 5)) + call assert_equal(4, match('testing', 'ing', -1)) + call assert_fails("let x=match('testing', 'ing', 0, [])", 'E745:') + call assert_equal(-1, match(v:_null_list, 2)) + call assert_equal(-1, match('abc', '\\%(')) endfunc func Test_matchend() @@ -961,6 +1063,7 @@ func Test_byte2line_line2byte() bw! endfunc +" Test for byteidx() and byteidxcomp() functions func Test_byteidx() let a = '.é.' " one char of two bytes call assert_equal(0, byteidx(a, 0)) @@ -980,6 +1083,7 @@ func Test_byteidx() call assert_equal(4, b->byteidx(2)) call assert_equal(5, b->byteidx(3)) call assert_equal(-1, b->byteidx(4)) + call assert_fails("call byteidx([], 0)", 'E730:') call assert_equal(0, b->byteidxcomp(0)) call assert_equal(1, b->byteidxcomp(1)) @@ -987,6 +1091,7 @@ func Test_byteidx() call assert_equal(4, b->byteidxcomp(3)) call assert_equal(5, b->byteidxcomp(4)) call assert_equal(-1, b->byteidxcomp(5)) + call assert_fails("call byteidxcomp([], 0)", 'E730:') endfunc " Test for charidx() @@ -997,7 +1102,9 @@ func Test_charidx() call assert_equal(2, charidx(a, 4)) call assert_equal(3, charidx(a, 7)) call assert_equal(-1, charidx(a, 8)) + call assert_equal(-1, charidx(a, -1)) call assert_equal(-1, charidx('', 0)) + call assert_equal(-1, charidx(v:_null_string, 0)) " count composing characters call assert_equal(0, charidx(a, 0, 1)) @@ -1010,8 +1117,8 @@ func Test_charidx() call assert_fails('let x = charidx([], 1)', 'E474:') call assert_fails('let x = charidx("abc", [])', 'E474:') call assert_fails('let x = charidx("abc", 1, [])', 'E474:') - call assert_fails('let x = charidx("abc", 1, -1)', 'E474:') - call assert_fails('let x = charidx("abc", 1, 2)', 'E474:') + call assert_fails('let x = charidx("abc", 1, -1)', 'E1023:') + call assert_fails('let x = charidx("abc", 1, 2)', 'E1023:') endfunc func Test_count() @@ -1096,6 +1203,10 @@ func Test_filewritable() call assert_equal(0, filewritable('doesnotexist')) + call mkdir('Xdir') + call assert_equal(2, filewritable('Xdir')) + call delete('Xdir', 'd') + call delete('Xfilewritable') bw! endfunc @@ -1179,6 +1290,7 @@ func Test_col() norm gg4|mx6|mY2| call assert_equal(2, col('.')) call assert_equal(7, col('$')) + call assert_equal(2, col('v')) call assert_equal(4, col("'x")) call assert_equal(6, col("'Y")) call assert_equal(2, [1, 2]->col()) @@ -1189,6 +1301,47 @@ func Test_col() call assert_equal(0, col([2, '$'])) call assert_equal(0, col([1, 100])) call assert_equal(0, col([1])) + call assert_equal(0, col(v:_null_list)) + call assert_fails('let c = col({})', 'E1222:') + call assert_fails('let c = col(".", [])', 'E1210:') + + " test for getting the visual start column + func T() + let g:Vcol = col('v') + return '' + endfunc + let g:Vcol = 0 + xmap <expr> <F2> T() + exe "normal gg3|ve\<F2>" + call assert_equal(3, g:Vcol) + xunmap <F2> + delfunc T + + " Test for the visual line start and end marks '< and '> + call setline(1, ['one', 'one two', 'one two three']) + "normal! ggVG + call feedkeys("ggVG\<Esc>", 'xt') + call assert_equal(1, col("'<")) + call assert_equal(14, col("'>")) + " Delete the last line of the visually selected region + $d + call assert_notequal(14, col("'>")) + + " Test with 'virtualedit' + set virtualedit=all + call cursor(1, 10) + call assert_equal(4, col('.')) + set virtualedit& + + " Test for getting the column number in another window + let winid = win_getid() + new + call win_execute(winid, 'normal 1G$') + call assert_equal(3, col('.', winid)) + call win_execute(winid, 'normal 2G') + call assert_equal(8, col('$', winid)) + call assert_equal(0, col('.', 5001)) + bw! endfunc @@ -1232,12 +1385,15 @@ endfunc " Test for the inputdialog() function func Test_inputdialog() - CheckNotGui - - call feedkeys(":let v=inputdialog('Q:', 'xx', 'yy')\<CR>\<CR>", 'xt') - call assert_equal('xx', v) - call feedkeys(":let v=inputdialog('Q:', 'xx', 'yy')\<CR>\<Esc>", 'xt') - call assert_equal('yy', v) + if has('gui_running') + call assert_fails('let v=inputdialog([], "xx")', 'E730:') + call assert_fails('let v=inputdialog("Q", [])', 'E730:') + else + call feedkeys(":let v=inputdialog('Q:', 'xx', 'yy')\<CR>\<CR>", 'xt') + call assert_equal('xx', v) + call feedkeys(":let v=inputdialog('Q:', 'xx', 'yy')\<CR>\<Esc>", 'xt') + call assert_equal('yy', v) + endif endfunc " Test for inputlist() @@ -1270,26 +1426,39 @@ func Test_inputlist() call assert_equal(2, c) " Use mouse to make a selection - " call test_setmouse(&lines - 3, 2) - call nvim_input_mouse('left', 'press', '', 0, &lines - 4, 1) " set mouse position - call getchar() " discard mouse event but keep mouse position + call Ntest_setmouse(&lines - 3, 2) call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>\<LeftMouse>", 'tx') call assert_equal(1, c) " Mouse click outside of the list - " call test_setmouse(&lines - 6, 2) - call nvim_input_mouse('left', 'press', '', 0, &lines - 7, 1) " set mouse position - call getchar() " discard mouse event but keep mouse position + call Ntest_setmouse(&lines - 6, 2) call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>\<LeftMouse>", 'tx') call assert_equal(-2, c) call assert_fails('call inputlist("")', 'E686:') endfunc +func Test_range_inputlist() + " flush out any garbage left in the buffer + while getchar(0) + endwhile + + call feedkeys(":let result = inputlist(range(10))\<CR>1\<CR>", 'x') + call assert_equal(1, result) + call feedkeys(":let result = inputlist(range(3, 10))\<CR>1\<CR>", 'x') + call assert_equal(1, result) + + unlet result +endfunc + func Test_balloon_show() CheckFeature balloon_eval " This won't do anything but must not crash either. call balloon_show('hi!') + if !has('gui_running') + call balloon_show(range(3)) + call balloon_show([]) + endif endfunc func Test_shellescape() @@ -1420,12 +1589,15 @@ func Test_trim() call assert_equal("vim", trim(" vim ", " ", 0)) call assert_equal("vim ", trim(" vim ", " ", 1)) call assert_equal(" vim", trim(" vim ", " ", 2)) - call assert_fails('call trim(" vim ", " ", [])', 'E745:') - call assert_fails('call trim(" vim ", " ", -1)', 'E475:') - call assert_fails('call trim(" vim ", " ", 3)', 'E475:') + call assert_fails('eval trim(" vim ", " ", [])', 'E745:') + call assert_fails('eval trim(" vim ", " ", -1)', 'E475:') + call assert_fails('eval trim(" vim ", " ", 3)', 'E475:') + call assert_fails('eval trim(" vim ", 0)', 'E475:') let chars = join(map(range(1, 0x20) + [0xa0], {n -> n->nr2char()}), '') call assert_equal("x", trim(chars . "x" . chars)) + + call assert_fails('let c=trim([])', 'E730:') endfunc " Test for reg_recording() and reg_executing() @@ -1534,13 +1706,12 @@ func Test_getchar() call assert_equal(0, getchar(0)) call setline(1, 'xxxx') - " call test_setmouse(1, 3) - " let v:mouse_win = 9 - " let v:mouse_winid = 9 - " let v:mouse_lnum = 9 - " let v:mouse_col = 9 - " call feedkeys("\<S-LeftMouse>", '') - call nvim_input_mouse('left', 'press', 'S', 0, 0, 2) + call Ntest_setmouse(1, 3) + let v:mouse_win = 9 + let v:mouse_winid = 9 + let v:mouse_lnum = 9 + let v:mouse_col = 9 + call feedkeys("\<S-LeftMouse>", '') call assert_equal("\<S-LeftMouse>", getchar()) call assert_equal(1, v:mouse_win) call assert_equal(win_getid(1), v:mouse_winid) @@ -1598,11 +1769,11 @@ func Test_libcall_libcallnr() call assert_equal(4, 'abcd'->libcallnr(libc, 'strlen')) call assert_equal(char2nr('A'), char2nr('a')->libcallnr(libc, 'toupper')) - call assert_fails("call libcall(libc, 'Xdoesnotexist_', '')", 'E364:') - call assert_fails("call libcallnr(libc, 'Xdoesnotexist_', '')", 'E364:') + call assert_fails("call libcall(libc, 'Xdoesnotexist_', '')", ['', 'E364:']) + call assert_fails("call libcallnr(libc, 'Xdoesnotexist_', '')", ['', 'E364:']) - call assert_fails("call libcall('Xdoesnotexist_', 'getenv', 'HOME')", 'E364:') - call assert_fails("call libcallnr('Xdoesnotexist_', 'strlen', 'abcd')", 'E364:') + call assert_fails("call libcall('Xdoesnotexist_', 'getenv', 'HOME')", ['', 'E364:']) + call assert_fails("call libcallnr('Xdoesnotexist_', 'strlen', 'abcd')", ['', 'E364:']) endfunc sandbox function Fsandbox() @@ -1619,6 +1790,10 @@ func Test_func_sandbox() call assert_fails('call Fsandbox()', 'E48:') delfunc Fsandbox + + " From a sandbox try to set a predefined variable (which cannot be modified + " from a sandbox) + call assert_fails('sandbox let v:lnum = 10', 'E794:') endfunc func EditAnotherFile() @@ -1697,6 +1872,62 @@ func Test_platform_name() endif endfunc +" Test confirm({msg} [, {choices} [, {default} [, {type}]]]) +func Test_confirm() + " requires a UI to be active + throw 'Skipped: use test/functional/vimscript/input_spec.lua' + CheckUnix + CheckNotGui + + call feedkeys('o', 'L') + let a = confirm('Press O to proceed') + call assert_equal(1, a) + + call feedkeys('y', 'L') + let a = 'Are you sure?'->confirm("&Yes\n&No") + call assert_equal(1, a) + + call feedkeys('n', 'L') + let a = confirm('Are you sure?', "&Yes\n&No") + call assert_equal(2, a) + + " confirm() should return 0 when pressing CTRL-C. + call feedkeys("\<C-C>", 'L') + let a = confirm('Are you sure?', "&Yes\n&No") + call assert_equal(0, a) + + " <Esc> requires another character to avoid it being seen as the start of an + " escape sequence. Zero should be harmless. + eval "\<Esc>0"->feedkeys('L') + let a = confirm('Are you sure?', "&Yes\n&No") + call assert_equal(0, a) + + " Default choice is returned when pressing <CR>. + call feedkeys("\<CR>", 'L') + let a = confirm('Are you sure?', "&Yes\n&No") + call assert_equal(1, a) + + call feedkeys("\<CR>", 'L') + let a = confirm('Are you sure?', "&Yes\n&No", 2) + call assert_equal(2, a) + + call feedkeys("\<CR>", 'L') + let a = confirm('Are you sure?', "&Yes\n&No", 0) + call assert_equal(0, a) + + " Test with the {type} 4th argument + for type in ['Error', 'Question', 'Info', 'Warning', 'Generic'] + call feedkeys('y', 'L') + let a = confirm('Are you sure?', "&Yes\n&No\n", 1, type) + call assert_equal(1, a) + endfor + + call assert_fails('call confirm([])', 'E730:') + call assert_fails('call confirm("Are you sure?", [])', 'E730:') + call assert_fails('call confirm("Are you sure?", "&Yes\n&No\n", [])', 'E745:') + call assert_fails('call confirm("Are you sure?", "&Yes\n&No\n", 0, [])', 'E730:') +endfunc + func Test_readdir() call mkdir('Xdir') call writefile([], 'Xdir/foo.txt') @@ -1724,7 +1955,7 @@ func Test_readdir() let files = readdir('Xdir', {x -> len(add(l, x)) == 2 ? -1 : 1}) call assert_equal(1, len(files)) - call delete('Xdir', 'rf') + eval 'Xdir'->delete('rf') endfunc func Test_delete_rf() @@ -1756,6 +1987,7 @@ func Test_call() call assert_equal(3, 'len'->call([123])) call assert_fails("call call('len', 123)", 'E714:') call assert_equal(0, call('', [])) + call assert_equal(0, call('len', v:_null_list)) function Mylen() dict return len(self.data) @@ -1763,10 +1995,15 @@ func Test_call() let mydict = {'data': [0, 1, 2, 3], 'len': function("Mylen")} eval mydict.len->call([], mydict)->assert_equal(4) call assert_fails("call call('Mylen', [], 0)", 'E715:') + call assert_fails('call foo', 'E107:') endfunc func Test_char2nr() call assert_equal(12354, char2nr('あ', 1)) + call assert_equal(120, 'x'->char2nr()) + " set encoding=latin1 + call assert_equal(120, 'x'->char2nr()) + set encoding=utf-8 endfunc func Test_charclass() @@ -1819,17 +2056,329 @@ func Test_bufadd_bufload() exe 'bwipe ' .. buf2 call assert_equal(0, bufexists(buf2)) + " When 'buftype' is "nofile" then bufload() does not read the file. + " Other values too. + for val in [['nofile', 0], + \ ['nowrite', 1], + \ ['acwrite', 1], + \ ['quickfix', 0], + \ ['help', 1], + "\ ['terminal', 0], + \ ['prompt', 0], + "\ ['popup', 0], + \ ] + bwipe! XotherName + let buf = bufadd('XotherName') + call setbufvar(buf, '&bt', val[0]) + call bufload(buf) + call assert_equal(val[1] ? ['some', 'text'] : [''], getbufline(buf, 1, '$'), val[0]) + endfor + bwipe someName bwipe XotherName call assert_equal(0, bufexists('someName')) call delete('XotherName') endfunc +func Test_range() + " destructuring + let [x, y] = range(2) + call assert_equal([0, 1], [x, y]) + + " index + call assert_equal(4, range(1, 10)[3]) + + " add() + call assert_equal([0, 1, 2, 3], add(range(3), 3)) + call assert_equal([0, 1, 2, [0, 1, 2]], add([0, 1, 2], range(3))) + call assert_equal([0, 1, 2, [0, 1, 2]], add(range(3), range(3))) + + " append() + new + call append('.', range(5)) + call assert_equal(['', '0', '1', '2', '3', '4'], getline(1, '$')) + bwipe! + + " appendbufline() + new + call appendbufline(bufnr(''), '.', range(5)) + call assert_equal(['0', '1', '2', '3', '4', ''], getline(1, '$')) + bwipe! + + " call() + func TwoArgs(a, b) + return [a:a, a:b] + endfunc + call assert_equal([0, 1], call('TwoArgs', range(2))) + + " col() + new + call setline(1, ['foo', 'bar']) + call assert_equal(2, col(range(1, 2))) + bwipe! + + " complete() + execute "normal! a\<C-r>=[complete(col('.'), range(10)), ''][1]\<CR>" + " complete_info() + execute "normal! a\<C-r>=[complete(col('.'), range(10)), ''][1]\<CR>\<C-r>=[complete_info(range(5)), ''][1]\<CR>" + + " copy() + call assert_equal([1, 2, 3], copy(range(1, 3))) + + " count() + call assert_equal(0, count(range(0), 3)) + call assert_equal(0, count(range(2), 3)) + call assert_equal(1, count(range(5), 3)) + + " cursor() + new + call setline(1, ['aaa', 'bbb', 'ccc']) + call cursor(range(1, 2)) + call assert_equal([2, 1], [col('.'), line('.')]) + bwipe! + + " deepcopy() + call assert_equal([1, 2, 3], deepcopy(range(1, 3))) + + " empty() + call assert_true(empty(range(0))) + call assert_false(empty(range(2))) + + " execute() + new + call setline(1, ['aaa', 'bbb', 'ccc']) + call execute(range(3)) + call assert_equal(2, line('.')) + bwipe! + + " extend() + call assert_equal([1, 2, 3, 4], extend([1], range(2, 4))) + call assert_equal([1, 2, 3, 4], extend(range(1, 1), range(2, 4))) + call assert_equal([1, 2, 3, 4], extend(range(1, 1), [2, 3, 4])) + + " filter() + call assert_equal([1, 3], filter(range(5), 'v:val % 2')) + + " funcref() + call assert_equal([0, 1], funcref('TwoArgs', range(2))()) + + " function() + call assert_equal([0, 1], function('TwoArgs', range(2))()) + + " garbagecollect() + let thelist = [1, range(2), 3] + let otherlist = range(3) + call test_garbagecollect_now() + + " get() + call assert_equal(4, get(range(1, 10), 3)) + call assert_equal(-1, get(range(1, 10), 42, -1)) + + " index() + call assert_equal(1, index(range(1, 5), 2)) + call assert_fails("echo index([1, 2], 1, [])", 'E745:') + + " insert() + call assert_equal([42, 1, 2, 3, 4, 5], insert(range(1, 5), 42)) + call assert_equal([42, 1, 2, 3, 4, 5], insert(range(1, 5), 42, 0)) + call assert_equal([1, 42, 2, 3, 4, 5], insert(range(1, 5), 42, 1)) + call assert_equal([1, 2, 3, 4, 42, 5], insert(range(1, 5), 42, 4)) + call assert_equal([1, 2, 3, 4, 42, 5], insert(range(1, 5), 42, -1)) + call assert_equal([1, 2, 3, 4, 5, 42], insert(range(1, 5), 42, 5)) + + " join() + call assert_equal('0 1 2 3 4', join(range(5))) + + " json_encode() + " call assert_equal('[0,1,2,3]', json_encode(range(4))) + call assert_equal('[0, 1, 2, 3]', json_encode(range(4))) + + " len() + call assert_equal(0, len(range(0))) + call assert_equal(2, len(range(2))) + call assert_equal(5, len(range(0, 12, 3))) + call assert_equal(4, len(range(3, 0, -1))) + + " list2str() + call assert_equal('ABC', list2str(range(65, 67))) + call assert_fails('let s = list2str(5)', 'E474:') + + " lock() + let thelist = range(5) + lockvar thelist + + " map() + call assert_equal([0, 2, 4, 6, 8], map(range(5), 'v:val * 2')) + + " match() + call assert_equal(3, match(range(5), 3)) + + " matchaddpos() + highlight MyGreenGroup ctermbg=green guibg=green + call matchaddpos('MyGreenGroup', range(line('.'), line('.'))) + + " matchend() + call assert_equal(4, matchend(range(5), '4')) + call assert_equal(3, matchend(range(1, 5), '4')) + call assert_equal(-1, matchend(range(1, 5), '42')) + + " matchstrpos() + call assert_equal(['4', 4, 0, 1], matchstrpos(range(5), '4')) + call assert_equal(['4', 3, 0, 1], matchstrpos(range(1, 5), '4')) + call assert_equal(['', -1, -1, -1], matchstrpos(range(1, 5), '42')) + + " max() reverse() + call assert_equal(0, max(range(0))) + call assert_equal(0, max(range(10, 9))) + call assert_equal(9, max(range(10))) + call assert_equal(18, max(range(0, 20, 3))) + call assert_equal(20, max(range(20, 0, -3))) + call assert_equal(99999, max(range(100000))) + call assert_equal(99999, max(range(99999, 0, -1))) + call assert_equal(99999, max(reverse(range(100000)))) + call assert_equal(99999, max(reverse(range(99999, 0, -1)))) + + " min() reverse() + call assert_equal(0, min(range(0))) + call assert_equal(0, min(range(10, 9))) + call assert_equal(5, min(range(5, 10))) + call assert_equal(5, min(range(5, 10, 3))) + call assert_equal(2, min(range(20, 0, -3))) + call assert_equal(0, min(range(100000))) + call assert_equal(0, min(range(99999, 0, -1))) + call assert_equal(0, min(reverse(range(100000)))) + call assert_equal(0, min(reverse(range(99999, 0, -1)))) + + " remove() + call assert_equal(1, remove(range(1, 10), 0)) + call assert_equal(2, remove(range(1, 10), 1)) + call assert_equal(9, remove(range(1, 10), 8)) + call assert_equal(10, remove(range(1, 10), 9)) + call assert_equal(10, remove(range(1, 10), -1)) + call assert_equal([3, 4, 5], remove(range(1, 10), 2, 4)) + + " repeat() + call assert_equal([0, 1, 2, 0, 1, 2], repeat(range(3), 2)) + call assert_equal([0, 1, 2], repeat(range(3), 1)) + call assert_equal([], repeat(range(3), 0)) + call assert_equal([], repeat(range(5, 4), 2)) + call assert_equal([], repeat(range(5, 4), 0)) + + " reverse() + call assert_equal([2, 1, 0], reverse(range(3))) + call assert_equal([0, 1, 2, 3], reverse(range(3, 0, -1))) + call assert_equal([9, 8, 7, 6, 5, 4, 3, 2, 1, 0], reverse(range(10))) + call assert_equal([20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10], reverse(range(10, 20))) + call assert_equal([16, 13, 10], reverse(range(10, 18, 3))) + call assert_equal([19, 16, 13, 10], reverse(range(10, 19, 3))) + call assert_equal([19, 16, 13, 10], reverse(range(10, 20, 3))) + call assert_equal([11, 14, 17, 20], reverse(range(20, 10, -3))) + call assert_equal([], reverse(range(0))) + + " TODO: setpos() + " new + " call setline(1, repeat([''], bufnr(''))) + " call setline(bufnr('') + 1, repeat('x', bufnr('') * 2 + 6)) + " call setpos('x', range(bufnr(''), bufnr('') + 3)) + " bwipe! + + " setreg() + call setreg('a', range(3)) + call assert_equal("0\n1\n2\n", getreg('a')) + + " settagstack() + call settagstack(1, #{items : range(4)}) + + " sign_define() + call assert_fails("call sign_define(range(5))", "E715:") + call assert_fails("call sign_placelist(range(5))", "E715:") + + " sign_undefine() + " call assert_fails("call sign_undefine(range(5))", "E908:") + call assert_fails("call sign_undefine(range(5))", "E155:") + + " sign_unplacelist() + call assert_fails("call sign_unplacelist(range(5))", "E715:") + + " sort() + call assert_equal([0, 1, 2, 3, 4, 5], sort(range(5, 0, -1))) + + " string() + call assert_equal('[0, 1, 2, 3, 4]', string(range(5))) + + " taglist() with 'tagfunc' + func TagFunc(pattern, flags, info) + return range(10) + endfunc + set tagfunc=TagFunc + call assert_fails("call taglist('asdf')", 'E987:') + set tagfunc= + + " term_start() + if has('terminal') && has('termguicolors') + call assert_fails('call term_start(range(3, 4))', 'E474:') + let g:terminal_ansi_colors = range(16) + if has('win32') + let cmd = "cmd /c dir" + else + let cmd = "ls" + endif + call assert_fails('call term_start("' .. cmd .. '", #{term_finish: "close"})', 'E475:') + unlet g:terminal_ansi_colors + endif + + " type() + call assert_equal(v:t_list, type(range(5))) + + " uniq() + call assert_equal([0, 1, 2, 3, 4], uniq(range(5))) + + " errors + call assert_fails('let x=range(2, 8, 0)', 'E726:') + call assert_fails('let x=range(3, 1)', 'E727:') + call assert_fails('let x=range(1, 3, -2)', 'E727:') + call assert_fails('let x=range([])', 'E745:') + call assert_fails('let x=range(1, [])', 'E745:') + call assert_fails('let x=range(1, 4, [])', 'E745:') +endfunc + +func Test_garbagecollect_now_fails() + let v:testing = 0 + call assert_fails('call test_garbagecollect_now()', 'E1142:') + let v:testing = 1 +endfunc + " Test for the eval() function func Test_eval() call assert_fails("call eval('5 a')", 'E488:') endfunc +" Test for the keytrans() function +func Test_keytrans() + call assert_equal('<Space>', keytrans(' ')) + call assert_equal('<lt>', keytrans('<')) + call assert_equal('<lt>Tab>', keytrans('<Tab>')) + call assert_equal('<Tab>', keytrans("\<Tab>")) + call assert_equal('<C-V>', keytrans("\<C-V>")) + call assert_equal('<BS>', keytrans("\<BS>")) + call assert_equal('<Home>', keytrans("\<Home>")) + call assert_equal('<C-Home>', keytrans("\<C-Home>")) + call assert_equal('<M-Home>', keytrans("\<M-Home>")) + call assert_equal('<C-Space>', keytrans("\<C-Space>")) + call assert_equal('<M-Space>', keytrans("\<*M-Space>")) + call assert_equal('<M-x>', "\<*M-x>"->keytrans()) + call assert_equal('<C-I>', "\<*C-I>"->keytrans()) + call assert_equal('<S-3>', "\<*S-3>"->keytrans()) + call assert_equal('π', 'π'->keytrans()) + call assert_equal('<M-π>', "\<M-π>"->keytrans()) + call assert_equal('ě', 'ě'->keytrans()) + call assert_equal('<M-ě>', "\<M-ě>"->keytrans()) + call assert_equal('', ''->keytrans()) + call assert_equal('', v:_null_string->keytrans()) + call assert_fails('call keytrans(1)', 'E1174:') + call assert_fails('call keytrans()', 'E119:') +endfunc + " Test for the nr2char() function func Test_nr2char() " set encoding=latin1 @@ -1842,6 +2391,13 @@ func Test_nr2char() call assert_equal("\x80\xfc\b" .. nr2char(0x40000000), eval('"\<M-' .. nr2char(0x40000000) .. '>"')) endfunc +" Test for screenattr(), screenchar() and screenchars() functions +func Test_screen_functions() + call assert_equal(-1, screenattr(-1, -1)) + call assert_equal(-1, screenchar(-1, -1)) + call assert_equal([], screenchars(-1, -1)) +endfunc + " Test for getcurpos() and setpos() func Test_getcurpos_setpos() new @@ -1872,9 +2428,7 @@ endfunc func Test_getmousepos() enew! call setline(1, "\t\t\t1234") - " call test_setmouse(1, 1) - call nvim_input_mouse('left', 'press', '', 0, 0, 0) - call getchar() " wait for and consume the mouse press + call Ntest_setmouse(1, 1) call assert_equal(#{ \ screenrow: 1, \ screencol: 1, @@ -1884,9 +2438,7 @@ func Test_getmousepos() \ line: 1, \ column: 1, \ }, getmousepos()) - " call test_setmouse(1, 25) - call nvim_input_mouse('left', 'press', '', 0, 0, 24) - call getchar() " wait for and consume the mouse press + call Ntest_setmouse(1, 25) call assert_equal(#{ \ screenrow: 1, \ screencol: 25, @@ -1896,9 +2448,7 @@ func Test_getmousepos() \ line: 1, \ column: 4, \ }, getmousepos()) - " call test_setmouse(1, 50) - call nvim_input_mouse('left', 'press', '', 0, 0, 49) - call getchar() " wait for and consume the mouse press + call Ntest_setmouse(1, 50) call assert_equal(#{ \ screenrow: 1, \ screencol: 50, @@ -1911,9 +2461,7 @@ func Test_getmousepos() " If the mouse is positioned past the last buffer line, "line" and "column" " should act like it's positioned on the last buffer line. - " call test_setmouse(2, 25) - call nvim_input_mouse('left', 'press', '', 0, 1, 24) - call getchar() " wait for and consume the mouse press + call Ntest_setmouse(2, 25) call assert_equal(#{ \ screenrow: 2, \ screencol: 25, @@ -1923,9 +2471,7 @@ func Test_getmousepos() \ line: 1, \ column: 4, \ }, getmousepos()) - " call test_setmouse(2, 50) - call nvim_input_mouse('left', 'press', '', 0, 1, 49) - call getchar() " wait for and consume the mouse press + call Ntest_setmouse(2, 50) call assert_equal(#{ \ screenrow: 2, \ screencol: 50, @@ -1938,6 +2484,28 @@ func Test_getmousepos() bwipe! endfunc +" Test for glob() +func Test_glob() + call assert_equal('', glob(v:_null_string)) + call assert_equal('', globpath(v:_null_string, v:_null_string)) + call assert_fails("let x = globpath(&rtp, 'syntax/c.vim', [])", 'E745:') + + call writefile([], 'Xglob1') + call writefile([], 'XGLOB2') + set wildignorecase + " Sort output of glob() otherwise we end up with different + " ordering depending on whether file system is case-sensitive. + call assert_equal(['XGLOB2', 'Xglob1'], sort(glob('Xglob[12]', 0, 1))) + " wildignorecase shall be applied even when the pattern contains no wildcards. + call assert_equal('XGLOB2', glob('xglob2')) + set wildignorecase& + + call delete('Xglob1') + call delete('XGLOB2') + + call assert_fails("call glob('*', 0, {})", 'E728:') +endfunc + func HasDefault(msg = 'msg') return a:msg endfunc @@ -1946,4 +2514,46 @@ func Test_default_arg_value() call assert_equal('msg', HasDefault()) endfunc +" Test for gettext() +func Test_gettext() + call assert_fails('call gettext(1)', 'E475:') +endfunc + +func Test_builtin_check() + call assert_fails('let g:["trim"] = {x -> " " .. x}', 'E704:') + call assert_fails('let g:.trim = {x -> " " .. x}', 'E704:') + call assert_fails('let l:["trim"] = {x -> " " .. x}', 'E704:') + call assert_fails('let l:.trim = {x -> " " .. x}', 'E704:') + let lines =<< trim END + vim9script + var s:trim = (x) => " " .. x + END + call CheckScriptFailure(lines, 'E704:') + + call assert_fails('call extend(g:, #{foo: { -> "foo" }})', 'E704:') + let g:bar = 123 + call extend(g:, #{bar: { -> "foo" }}, "keep") + call assert_fails('call extend(g:, #{bar: { -> "foo" }}, "force")', 'E704:') + unlet g:bar + + call assert_fails('call extend(l:, #{foo: { -> "foo" }})', 'E704:') + let bar = 123 + call extend(l:, #{bar: { -> "foo" }}, "keep") + call assert_fails('call extend(l:, #{bar: { -> "foo" }}, "force")', 'E704:') + unlet bar + + call assert_fails('call extend(g:, #{foo: function("extend")})', 'E704:') + let g:bar = 123 + call extend(g:, #{bar: function("extend")}, "keep") + call assert_fails('call extend(g:, #{bar: function("extend")}, "force")', 'E704:') + unlet g:bar + + call assert_fails('call extend(l:, #{foo: function("extend")})', 'E704:') + let bar = 123 + call extend(l:, #{bar: function("extend")}, "keep") + call assert_fails('call extend(l:, #{bar: function("extend")}, "force")', 'E704:') + unlet bar +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_getcwd.vim b/src/nvim/testdir/test_getcwd.vim index a75583cd2c..073f8303dc 100644 --- a/src/nvim/testdir/test_getcwd.vim +++ b/src/nvim/testdir/test_getcwd.vim @@ -24,7 +24,7 @@ endfunc " Do all test in a separate window to avoid E211 when we recursively " delete the Xtopdir directory during cleanup -function SetUp() +func SetUp() set visualbell set nocp viminfo+=nviminfo diff --git a/src/nvim/testdir/test_getvar.vim b/src/nvim/testdir/test_getvar.vim index 5a96548893..e6b6341fce 100644 --- a/src/nvim/testdir/test_getvar.vim +++ b/src/nvim/testdir/test_getvar.vim @@ -133,11 +133,20 @@ func Test_get_lambda() call assert_equal([], get(l:L, 'args')) endfunc +func s:FooBar() +endfunc + " get({func}, {what} [, {default}]) func Test_get_func() let l:F = function('tr') call assert_equal('tr', get(l:F, 'name')) call assert_equal(l:F, get(l:F, 'func')) + + let Fb_func = function('s:FooBar') + call assert_match('<SNR>\d\+_FooBar', get(Fb_func, 'name')) + let Fb_ref = funcref('s:FooBar') + call assert_match('<SNR>\d\+_FooBar', get(Fb_ref, 'name')) + call assert_equal({'func has': 'no dict'}, get(l:F, 'dict', {'func has': 'no dict'})) call assert_equal(0, get(l:F, 'dict')) call assert_equal([], get(l:F, 'args')) diff --git a/src/nvim/testdir/test_gf.vim b/src/nvim/testdir/test_gf.vim index b2bb189688..e369645328 100644 --- a/src/nvim/testdir/test_gf.vim +++ b/src/nvim/testdir/test_gf.vim @@ -226,6 +226,32 @@ func Test_gf_includeexpr() delfunc IncFunc endfunc +" Test for using a script-local function for 'includeexpr' +func Test_includeexpr_scriptlocal_func() + func! s:IncludeFunc() + let g:IncludeFname = v:fname + return '' + endfunc + set includeexpr=s:IncludeFunc() + call assert_equal(expand('<SID>') .. 'IncludeFunc()', &includeexpr) + new | only + call setline(1, 'TestFile1') + let g:IncludeFname = '' + call assert_fails('normal! gf', 'E447:') + call assert_equal('TestFile1', g:IncludeFname) + bw! + set includeexpr=<SID>IncludeFunc() + call assert_equal(expand('<SID>') .. 'IncludeFunc()', &includeexpr) + new | only + call setline(1, 'TestFile2') + let g:IncludeFname = '' + call assert_fails('normal! gf', 'E447:') + call assert_equal('TestFile2', g:IncludeFname) + set includeexpr& + delfunc s:IncludeFunc + bw! +endfunc + " Check that expanding directories can handle more than 255 entries. func Test_gf_subdirs_wildcard() let cwd = getcwd() diff --git a/src/nvim/testdir/test_global.vim b/src/nvim/testdir/test_global.vim index cb6851250c..44a8784348 100644 --- a/src/nvim/testdir/test_global.vim +++ b/src/nvim/testdir/test_global.vim @@ -39,7 +39,7 @@ endfunc func Test_global_error() call assert_fails('g\\a', 'E10:') call assert_fails('g', 'E148:') - call assert_fails('g/\(/y', 'E476:') + call assert_fails('g/\(/y', 'E54:') endfunc " Test for printing lines using :g with different search patterns @@ -72,6 +72,18 @@ func Test_global_print() close! endfunc +func Test_global_empty_pattern() + " populate history + silent g/hello/ + + redir @a + g// + redir END + + call assert_match('Pattern not found: hello', @a) + " ^~~~~ this was previously empty +endfunc + " Test for global command with newline character func Test_global_newline() new @@ -91,6 +103,7 @@ endfunc " Test for interrupting :global using Ctrl-C func Test_interrupt_global() CheckRunVimInTerminal + let lines =<< trim END cnoremap ; <Cmd>sleep 10<CR> call setline(1, repeat(['foo'], 5)) @@ -100,14 +113,14 @@ func Test_interrupt_global() call term_sendkeys(buf, ":g/foo/norm :\<C-V>;\<CR>") " Wait for :sleep to start - call term_wait(buf) + call TermWait(buf, 100) call term_sendkeys(buf, "\<C-C>") call WaitForAssert({-> assert_match('Interrupted', term_getline(buf, 6))}, 1000) " Also test in Ex mode call term_sendkeys(buf, "gQg/foo/norm :\<C-V>;\<CR>") " Wait for :sleep to start - call term_wait(buf) + call TermWait(buf, 100) call term_sendkeys(buf, "\<C-C>") call WaitForAssert({-> assert_match('Interrupted', term_getline(buf, 5))}, 1000) diff --git a/src/nvim/testdir/test_gui.vim b/src/nvim/testdir/test_gui.vim new file mode 100644 index 0000000000..c3f1f3163a --- /dev/null +++ b/src/nvim/testdir/test_gui.vim @@ -0,0 +1,43 @@ + +func Test_colorscheme() + " call assert_equal('16777216', &t_Co) + + let colorscheme_saved = exists('g:colors_name') ? g:colors_name : 'default' + let g:color_count = 0 + augroup TestColors + au! + au ColorScheme * let g:color_count += 1 + \ | let g:after_colors = g:color_count + \ | let g:color_after = expand('<amatch>') + au ColorSchemePre * let g:color_count += 1 + \ | let g:before_colors = g:color_count + \ | let g:color_pre = expand('<amatch>') + augroup END + + colorscheme torte + redraw! + call assert_equal('dark', &background) + call assert_equal(1, g:before_colors) + call assert_equal(2, g:after_colors) + call assert_equal('torte', g:color_pre) + call assert_equal('torte', g:color_after) + call assert_equal("\ntorte", execute('colorscheme')) + + let a = substitute(execute('hi Search'), "\n\\s\\+", ' ', 'g') + " FIXME: temporarily check less while the colorscheme changes + " call assert_match("\nSearch xxx term=reverse cterm=reverse ctermfg=196 ctermbg=16 gui=reverse guifg=#ff0000 guibg=#000000", a) + " call assert_match("\nSearch xxx term=reverse ", a) + + call assert_fails('colorscheme does_not_exist', 'E185:') + call assert_equal('does_not_exist', g:color_pre) + call assert_equal('torte', g:color_after) + + exec 'colorscheme' colorscheme_saved + augroup TestColors + au! + augroup END + unlet g:color_count g:after_colors g:before_colors + redraw! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_hardcopy.vim b/src/nvim/testdir/test_hardcopy.vim deleted file mode 100644 index e390bd5cc8..0000000000 --- a/src/nvim/testdir/test_hardcopy.vim +++ /dev/null @@ -1,204 +0,0 @@ -" Test :hardcopy - -source check.vim - -func Test_printoptions() - edit test_hardcopy.vim - syn on - - for opt in ['left:5in,right:10pt,top:8mm,bottom:2pc', - \ 'left:2in,top:30pt,right:16mm,bottom:3pc', - \ 'header:3,syntax:y,number:y,wrap:n', - \ 'header:3,syntax:n,number:y,wrap:y', - \ 'header:0,syntax:a,number:y,wrap:y', - \ 'duplex:short,collate:n,jobsplit:y,portrait:n', - \ 'duplex:long,collate:y,jobsplit:n,portrait:y', - \ 'duplex:off,collate:y,jobsplit:y,portrait:y', - \ 'paper:10x14', - \ 'paper:A3', - \ 'paper:A4', - \ 'paper:A5', - \ 'paper:B4', - \ 'paper:B5', - \ 'paper:executive', - \ 'paper:folio', - \ 'paper:ledger', - \ 'paper:legal', - \ 'paper:letter', - \ 'paper:quarto', - \ 'paper:statement', - \ 'paper:tabloid', - \ 'formfeed:y', - \ ''] - exe 'set printoptions=' .. opt - if has('postscript') - 1,50hardcopy > Xhardcopy_printoptions - let lines = readfile('Xhardcopy_printoptions') - call assert_true(len(lines) > 20, opt) - call assert_true(lines[0] =~ 'PS-Adobe', opt) - call delete('Xhardcopy_printoptions') - endif - endfor - - call assert_fails('set printoptions=paper', 'E550:') - call assert_fails('set printoptions=shredder:on', 'E551:') - call assert_fails('set printoptions=left:no', 'E552:') - set printoptions& - bwipe -endfunc - -func Test_printmbfont() - " Print a help page which contains tabs, underlines (etc) to recover more code. - help syntax.txt - syn on - - for opt in [':WadaMin-Regular,b:WadaMin-Bold,i:WadaMin-Italic,o:WadaMin-Bold-Italic,c:yes,a:no', - \ ''] - exe 'set printmbfont=' .. opt - if has('postscript') - hardcopy > Xhardcopy_printmbfont - let lines = readfile('Xhardcopy_printmbfont') - call assert_true(len(lines) > 20, opt) - call assert_true(lines[0] =~ 'PS-Adobe', opt) - call delete('Xhardcopy_printmbfont') - endif - endfor - set printmbfont& - bwipe -endfunc - -func Test_printmbcharset() - CheckFeature postscript - - " digraph.txt has plenty of non-latin1 characters. - help digraph.txt - set printmbcharset=ISO10646 printencoding=utf-8 - for courier in ['yes', 'no'] - for ascii in ['yes', 'no'] - exe 'set printmbfont=r:WadaMin-Regular,b:WadaMin-Bold,i:WadaMin-Italic,o:WadaMin-BoldItalic' - \ .. ',c:' .. courier .. ',a:' .. ascii - hardcopy > Xhardcopy_printmbcharset - let lines = readfile('Xhardcopy_printmbcharset') - call assert_true(len(lines) > 20) - call assert_true(lines[0] =~ 'PS-Adobe') - endfor - endfor - - set printmbcharset=does-not-exist printencoding=utf-8 printmbfont=r:WadaMin-Regular - call assert_fails('hardcopy > Xhardcopy_printmbcharset', 'E456:') - - set printmbcharset=GB_2312-80 printencoding=utf-8 printmbfont=r:WadaMin-Regular - call assert_fails('hardcopy > Xhardcopy_printmbcharset', 'E673:') - - set printmbcharset=ISO10646 printencoding=utf-8 printmbfont= - call assert_fails('hardcopy > Xhardcopy_printmbcharset', 'E675:') - - call delete('Xhardcopy_printmbcharset') - set printmbcharset& printencoding& printmbfont& - bwipe -endfunc - -func Test_printexpr() - CheckFeature postscript - - " Not a very useful printexpr value, but enough to test - " hardcopy with 'printexpr'. - function PrintFile(fname) - call writefile(['Test printexpr: ' .. v:cmdarg], - \ 'Xhardcopy_printexpr') - call delete(a:fname) - return 0 - endfunc - set printexpr=PrintFile(v:fname_in) - - help help - hardcopy dummy args - call assert_equal(['Test printexpr: dummy args'], - \ readfile('Xhardcopy_printexpr')) - call delete('Xhardcopy_printexpr') - - " Function returns 1 to test print failure. - function PrintFails(fname) - call delete(a:fname) - return 1 - endfunc - set printexpr=PrintFails(v:fname_in) - call assert_fails('hardcopy', 'E365:') - - set printexpr& - bwipe -endfunc - -func Test_errors() - CheckFeature postscript - - edit test_hardcopy.vim - call assert_fails('hardcopy >', 'E324:') - bwipe -endfunc - -func Test_dark_background() - edit test_hardcopy.vim - syn on - - for bg in ['dark', 'light'] - exe 'set background=' .. bg - - if has('postscript') - hardcopy > Xhardcopy_dark_background - let lines = readfile('Xhardcopy_dark_background') - call assert_true(len(lines) > 20) - call assert_true(lines[0] =~ 'PS-Adobe') - call delete('Xhardcopy_dark_background') - endif - endfor - - set background& - bwipe -endfun - -func Test_empty_buffer() - CheckFeature postscript - - new - call assert_equal("\nNo text to be printed", execute('hardcopy')) - bwipe -endfunc - -func Test_printheader_parsing() - " Only test that this doesn't throw an error. - set printheader=%<%f\ %h%m%r%=%-14.(%l,%c%V%)\ %P - set printheader=%<%f%h%m%r%=%b\ 0x%B\ \ %l,%c%V\ %P - set printheader=%<%f%=\ [%1*%M%*%n%R%H]\ %-19(%3l,%02c%03V%)%O'%02b' - set printheader=...%r%{VarExists('b:gzflag','\ [GZ]')}%h... - set printheader= - set printheader& -endfunc - -func Test_fname_with_spaces() - CheckFeature postscript - - split t\ e\ s\ t.txt - call setline(1, ['just', 'some', 'text']) - hardcopy > %.ps - call assert_true(filereadable('t e s t.txt.ps')) - call delete('t e s t.txt.ps') - bwipe! -endfunc - -func Test_illegal_byte() - CheckFeature postscript - if &enc != 'utf-8' - return - endif - - new - " conversion of 0xff will fail, this used to cause a crash - call setline(1, "\xff") - hardcopy >Xpstest - - bwipe! - call delete('Xpstest') -endfunc - -" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_help.vim b/src/nvim/testdir/test_help.vim index dbb36facee..08dd3dcb9a 100644 --- a/src/nvim/testdir/test_help.vim +++ b/src/nvim/testdir/test_help.vim @@ -1,5 +1,23 @@ " Tests for :help +source check.vim +source vim9.vim + +func SetUp() + let s:vimruntime = $VIMRUNTIME + let s:runtimepath = &runtimepath + " Set $VIMRUNTIME to $BUILD_DIR/runtime and remove the original $VIMRUNTIME + " path from &runtimepath so that ":h local-additions" won't pick up builtin + " help files. + let $VIMRUNTIME = expand($BUILD_DIR) .. '/runtime' + set runtimepath-=../../../runtime +endfunc + +func TearDown() + let $VIMRUNTIME = s:vimruntime + let &runtimepath = s:runtimepath +endfunc + func Test_help_restore_snapshot() help set buftype= @@ -79,16 +97,42 @@ func Test_help_local_additions() call writefile(['*mydoc-ext.txt* my extended awesome doc'], 'Xruntime/doc/mydoc-ext.txt') let rtp_save = &rtp set rtp+=./Xruntime - help - 1 - call search('mydoc.txt') - call assert_equal('|mydoc.txt| my awesome doc', getline('.')) - 1 - call search('mydoc-ext.txt') - call assert_equal('|mydoc-ext.txt| my extended awesome doc', getline('.')) + help local-additions + let lines = getline(line(".") + 1, search("^$") - 1) + call assert_equal([ + \ '|mydoc-ext.txt| my extended awesome doc', + \ '|mydoc.txt| my awesome doc' + \ ], lines) + call delete('Xruntime/doc/mydoc-ext.txt') + close + + call mkdir('Xruntime-ja/doc', 'p') + call writefile(["local-additions\thelp.jax\t/*local-additions*"], 'Xruntime-ja/doc/tags-ja') + call writefile(['*help.txt* This is jax file', '', + \ 'LOCAL ADDITIONS: *local-additions*', ''], 'Xruntime-ja/doc/help.jax') + call writefile(['*work.txt* This is jax file'], 'Xruntime-ja/doc/work.jax') + call writefile(['*work2.txt* This is jax file'], 'Xruntime-ja/doc/work2.jax') + set rtp+=./Xruntime-ja + + help local-additions@en + let lines = getline(line(".") + 1, search("^$") - 1) + call assert_equal([ + \ '|mydoc.txt| my awesome doc' + \ ], lines) + close + + help local-additions@ja + let lines = getline(line(".") + 1, search("^$") - 1) + call assert_equal([ + \ '|mydoc.txt| my awesome doc', + \ '|help.txt| This is jax file', + \ '|work.txt| This is jax file', + \ '|work2.txt| This is jax file', + \ ], lines) close call delete('Xruntime', 'rf') + call delete('Xruntime-ja', 'rf') let &rtp = rtp_save endfunc @@ -98,6 +142,7 @@ func Test_help_completion() endfunc " Test for the :helptags command +" NOTE: if you run tests as root this will fail. Don't run tests as root! func Test_helptag_cmd() call mkdir('Xdir/a/doc', 'p') @@ -111,28 +156,12 @@ func Test_helptag_cmd() call assert_equal(["help-tags\ttags\t1"], readfile('Xdir/tags')) call delete('Xdir/tags') - " The following tests fail on FreeBSD for some reason - if has('unix') && !has('bsd') - " Read-only tags file - call mkdir('Xdir/doc', 'p') - call writefile([''], 'Xdir/doc/tags') - call writefile([], 'Xdir/doc/sample.txt') - call setfperm('Xdir/doc/tags', 'r-xr--r--') - call assert_fails('helptags Xdir/doc', 'E152:', getfperm('Xdir/doc/tags')) - - let rtp = &rtp - let &rtp = 'Xdir' - helptags ALL - let &rtp = rtp - - call delete('Xdir/doc/tags') - - " No permission to read the help file - call setfperm('Xdir/a/doc/sample.txt', '-w-------') - call assert_fails('helptags Xdir', 'E153:', getfperm('Xdir/a/doc/sample.txt')) - call delete('Xdir/a/doc/sample.txt') - call delete('Xdir/tags') - endif + " Test parsing tags + call writefile(['*tag1*', 'Example: >', ' *notag*', 'Example end: *tag2*'], + \ 'Xdir/a/doc/sample.txt') + helptags Xdir + call assert_equal(["tag1\ta/doc/sample.txt\t/*tag1*", + \ "tag2\ta/doc/sample.txt\t/*tag2*"], readfile('Xdir/tags')) " Duplicate tags in the help file call writefile(['*tag1*', '*tag1*', '*tag2*'], 'Xdir/a/doc/sample.txt') @@ -141,6 +170,43 @@ func Test_helptag_cmd() call delete('Xdir', 'rf') endfunc +func Test_helptag_cmd_readonly() + CheckUnix + CheckNotRoot + + " Read-only tags file + call mkdir('Xdir/doc', 'p') + call writefile([''], 'Xdir/doc/tags') + call writefile([], 'Xdir/doc/sample.txt') + call setfperm('Xdir/doc/tags', 'r-xr--r--') + call assert_fails('helptags Xdir/doc', 'E152:', getfperm('Xdir/doc/tags')) + + let rtp = &rtp + let &rtp = 'Xdir' + helptags ALL + let &rtp = rtp + + call delete('Xdir/doc/tags') + + " No permission to read the help file + call mkdir('Xdir/b/doc', 'p') + call writefile([], 'Xdir/b/doc/sample.txt') + call setfperm('Xdir/b/doc/sample.txt', '-w-------') + call assert_fails('helptags Xdir', 'E153:', getfperm('Xdir/b/doc/sample.txt')) + call delete('Xdir', 'rf') +endfunc + +" Test for setting the 'helpheight' option in the help window +func Test_help_window_height() + let &cmdheight = &lines - 23 + set helpheight=10 + help + set helpheight=14 + call assert_equal(14, winheight(0)) + set helpheight& cmdheight=1 + close +endfunc + func Test_help_long_argument() try exe 'help \%' .. repeat('0', 1021) @@ -149,5 +215,15 @@ func Test_help_long_argument() endtry endfunc +func Test_help_using_visual_match() + let lines =<< trim END + call setline(1, ' ') + /^ + exe "normal \<C-V>\<C-V>" + h5\%V] + END + call CheckScriptFailure(lines, 'E149:') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_help_tagjump.vim b/src/nvim/testdir/test_help_tagjump.vim index e84726bbfc..eae1a241e3 100644 --- a/src/nvim/testdir/test_help_tagjump.vim +++ b/src/nvim/testdir/test_help_tagjump.vim @@ -1,17 +1,5 @@ " Tests for :help! {subject} -func SetUp() - " v:progpath is …/build/bin/nvim and we need …/build/runtime - " to be added to &rtp - let builddir = fnamemodify(exepath(v:progpath), ':h:h') - let s:rtp = &rtp - let &rtp .= printf(',%s/runtime', builddir) -endfunc - -func TearDown() - let &rtp = s:rtp -endfunc - func Test_help_tagjump() help call assert_equal("help", &filetype) @@ -38,18 +26,18 @@ func Test_help_tagjump() call assert_true(getline('.') =~ '\*quotestar\*') helpclose - " The test result is different in vim. There ":help ??" will jump to the - " falsy operator ??, which hasn't been ported to neovim yet. Instead, neovim - " jumps to the tag "g??". This test result needs to be changed if neovim - " ports the falsy operator. - help ?? + " help sm?le + help ch?ckhealth call assert_equal("help", &filetype) - call assert_true(getline('.') =~ '\*g??\*') + " call assert_true(getline('.') =~ '\*:smile\*') + call assert_true(getline('.') =~ '\*:checkhealth\*') helpclose - help ch?ckhealth + help ?? call assert_equal("help", &filetype) - call assert_true(getline('.') =~ '\*:checkhealth\*') + " *??* tag needs patch 8.2.1794 + " call assert_true(getline('.') =~ '\*??\*') + call assert_true(getline('.') =~ '\*g??\*') helpclose help :? diff --git a/src/nvim/testdir/test_hide.vim b/src/nvim/testdir/test_hide.vim index 41b1a4ad7c..b3ce395523 100644 --- a/src/nvim/testdir/test_hide.vim +++ b/src/nvim/testdir/test_hide.vim @@ -1,6 +1,6 @@ " Tests for :hide command/modifier and 'hidden' option -function SetUp() +func SetUp() let s:save_hidden = &hidden let s:save_bufhidden = &bufhidden let s:save_autowrite = &autowrite diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim index efdf44a0d6..8a102f2e65 100644 --- a/src/nvim/testdir/test_highlight.vim +++ b/src/nvim/testdir/test_highlight.vim @@ -536,9 +536,7 @@ func Test_termguicolors() endfunc func Test_cursorline_after_yank() - if !CanRunVimInTerminal() - throw 'Skipped: cannot make screendumps' - endif + CheckScreendump call writefile([ \ 'set cul rnu', @@ -578,9 +576,7 @@ func Test_put_before_cursorline() endfunc func Test_cursorline_with_visualmode() - if !CanRunVimInTerminal() - throw 'Skipped: cannot make screendumps' - endif + CheckScreendump call writefile([ \ 'set cul', @@ -722,7 +718,7 @@ func Test_1_highlight_Normalgroup_exists() elseif has('gui_gtk2') || has('gui_gnome') || has('gui_gtk3') " expect is DEFAULT_FONT of gui_gtk_x11.c call assert_match('hi Normal\s*font=Monospace 10', hlNormal) - elseif has('gui_motif') || has('gui_athena') + elseif has('gui_motif') " expect is DEFAULT_FONT of gui_x11.c call assert_match('hi Normal\s*font=7x13', hlNormal) elseif has('win32') @@ -731,7 +727,8 @@ func Test_1_highlight_Normalgroup_exists() endif endfunc -function Test_no_space_before_xxx() +" Do this test last, sometimes restoring the columns doesn't work +func Test_z_no_space_before_xxx() " Note: we need to create this highlight group in the test because it does not exist in Neovim execute('hi StatusLineTermNC ctermfg=green') let l:org_columns = &columns @@ -739,13 +736,23 @@ function Test_no_space_before_xxx() let l:hi_StatusLineTermNC = join(split(execute('hi StatusLineTermNC'))) call assert_match('StatusLineTermNC xxx', l:hi_StatusLineTermNC) let &columns = l:org_columns -endfunction +endfunc " Test for :highlight command errors func Test_highlight_cmd_errors() if has('gui_running') || has('nvim') + " This test doesn't fail in the MS-Windows console version. + call assert_fails('hi Xcomment ctermfg=fg', 'E419:') + call assert_fails('hi Xcomment ctermfg=bg', 'E420:') call assert_fails('hi ' .. repeat('a', 201) .. ' ctermfg=black', 'E1249:') endif + + " Try using a very long terminal code. Define a dummy terminal code for this + " test. + let &t_fo = "\<Esc>1;" + let c = repeat("t_fo,", 100) . "t_fo" + " call assert_fails('exe "hi Xgroup1 start=" . c', 'E422:') + let &t_fo = "" endfunc " Test for using RGB color values in a highlight group @@ -812,11 +819,11 @@ func Test_highlight_clear_restores_context() let patContextDefault = fnamemodify(scriptContextDefault, ':t') .. ' line 1' let patContextRelink = fnamemodify(scriptContextRelink, ':t') .. ' line 2' - exec "source" scriptContextDefault + exec 'source ' .. scriptContextDefault let hlContextDefault = execute("verbose hi Context") call assert_match(patContextDefault, hlContextDefault) - exec "source" scriptContextRelink + exec 'source ' .. scriptContextRelink let hlContextRelink = execute("verbose hi Context") call assert_match(patContextRelink, hlContextRelink) diff --git a/src/nvim/testdir/test_history.vim b/src/nvim/testdir/test_history.vim index feb521e232..f1c31dee04 100644 --- a/src/nvim/testdir/test_history.vim +++ b/src/nvim/testdir/test_history.vim @@ -95,6 +95,23 @@ function Test_History() call assert_fails('call histnr([])', 'E730:') call assert_fails('history xyz', 'E488:') call assert_fails('history ,abc', 'E488:') + call assert_fails('call histdel(":", "\\%(")', 'E53:') +endfunction + +function Test_history_truncates_long_entry() + " History entry short enough to fit on the screen should not be truncated. + call histadd(':', 'echo x' .. repeat('y', &columns - 17) .. 'z') + let a = execute('history : -1') + + call assert_match("^\n # cmd history\n" + \ .. "> *\\d\\+ echo x" .. repeat('y', &columns - 17) .. 'z$', a) + + " Long history entry should be truncated to fit on the screen, with, '...' + " inserted in the string to indicate the that there is truncation. + call histadd(':', 'echo x' .. repeat('y', &columns - 16) .. 'z') + let a = execute('history : -1') + call assert_match("^\n # cmd history\n" + \ .. "> *\\d\\+ echo xy\\+\.\.\.y\\+z$", a) endfunction function Test_Search_history_window() diff --git a/src/nvim/testdir/test_increment.vim b/src/nvim/testdir/test_increment.vim index 2559654f25..3c2b88ef9f 100644 --- a/src/nvim/testdir/test_increment.vim +++ b/src/nvim/testdir/test_increment.vim @@ -476,6 +476,10 @@ func Test_visual_increment_20() exec "norm! \<C-A>" call assert_equal(["b"], getline(1, '$')) call assert_equal([0, 1, 1, 0], getpos('.')) + " decrement a and A and increment z and Z + call setline(1, ['a', 'A', 'z', 'Z']) + exe "normal 1G\<C-X>2G\<C-X>3G\<C-A>4G\<C-A>" + call assert_equal(['a', 'A', 'z', 'Z'], getline(1, '$')) endfunc " 21) block-wise increment on part of hexadecimal @@ -566,12 +570,14 @@ endfunc " 1) <ctrl-a> " 0b11111111111111111111111111111111 func Test_visual_increment_26() - set nrformats+=alpha + set nrformats+=bin call setline(1, ["0b11111111111111111111111111111110"]) exec "norm! \<C-V>$\<C-A>" call assert_equal(["0b11111111111111111111111111111111"], getline(1, '$')) call assert_equal([0, 1, 1, 0], getpos('.')) - set nrformats-=alpha + exec "norm! \<C-V>$\<C-X>" + call assert_equal(["0b11111111111111111111111111111110"], getline(1, '$')) + set nrformats-=bin endfunc " 27) increment with 'rightreft', if supported @@ -772,7 +778,6 @@ func Test_normal_increment_03() endfunc func Test_increment_empty_line() - new call setline(1, ['0', '0', '0', '0', '0', '0', '']) exe "normal Gvgg\<C-A>" call assert_equal(['1', '1', '1', '1', '1', '1', ''], getline(1, 7)) @@ -783,8 +788,13 @@ func Test_increment_empty_line() exe "normal! c\<C-A>l" exe "normal! c\<C-X>l" call assert_equal('one two', getline(1)) +endfunc - bwipe! +" Try incrementing/decrementing a non-digit/alpha character +func Test_increment_special_char() + call setline(1, '!') + call assert_beeps("normal \<C-A>") + call assert_beeps("normal \<C-X>") endfunc " Try incrementing/decrementing a number when nrformats contains unsigned @@ -867,4 +877,21 @@ func Test_normal_increment_with_virtualedit() set virtualedit& endfunc +" Test for incrementing a signed hexadecimal and octal number +func Test_normal_increment_signed_hexoct_nr() + new + " negative sign before a hex number should be ignored + call setline(1, ["-0x9"]) + exe "norm \<C-A>" + call assert_equal(["-0xa"], getline(1, '$')) + exe "norm \<C-X>" + call assert_equal(["-0x9"], getline(1, '$')) + call setline(1, ["-007"]) + exe "norm \<C-A>" + call assert_equal(["-010"], getline(1, '$')) + exe "norm \<C-X>" + call assert_equal(["-007"], getline(1, '$')) + bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim index 179218e48a..ec1379a378 100644 --- a/src/nvim/testdir/test_ins_complete.vim +++ b/src/nvim/testdir/test_ins_complete.vim @@ -1,5 +1,8 @@ +" Test for insert completion + source screendump.vim source check.vim +source vim9.vim " Test for insert expansion func Test_ins_complete() @@ -44,11 +47,11 @@ func Test_ins_complete() exe "normal o\<C-X>\<C-P>\<C-P>\<C-X>\<C-X>\<C-N>\<C-X>\<C-N>\<C-N>" call assert_equal('run1 run2', getline('.')) - set cpt=.,w,i + set cpt=.,\ ,w,i " i-add-expands and switches to local exe "normal OM\<C-N>\<C-X>\<C-N>\<C-X>\<C-N>\<C-X>\<C-X>\<C-X>\<C-P>" call assert_equal("Makefile\tto\trun3", getline('.')) - " add-expands lines (it would end in an empty line if it didn't ignored + " add-expands lines (it would end in an empty line if it didn't ignore " itself) exe "normal o\<C-X>\<C-L>\<C-X>\<C-L>\<C-P>\<C-P>" call assert_equal("Makefile\tto\trun3", getline('.')) @@ -68,6 +71,11 @@ func Test_ins_complete() call assert_equal('Xtest11.one', getline('.')) normal ddk + " Test for expanding a non-existing filename + exe "normal oa1b2X3Y4\<C-X>\<C-F>" + call assert_equal('a1b2X3Y4', getline('.')) + normal ddk + set cpt=w " checks make_cyclic in other window exe "normal oST\<C-N>\<C-P>\<C-P>\<C-P>\<C-P>" @@ -183,17 +191,17 @@ func s:CompleteDone_CompleteFuncDict( findstart, base ) endif return { - \ 'words': [ - \ { - \ 'word': 'aword', - \ 'abbr': 'wrd', - \ 'menu': 'extra text', - \ 'info': 'words are cool', - \ 'kind': 'W', - \ 'user_data': ['one', 'two'] - \ } - \ ] - \ } + \ 'words': [ + \ { + \ 'word': 'aword', + \ 'abbr': 'wrd', + \ 'menu': 'extra text', + \ 'info': 'words are cool', + \ 'kind': 'W', + \ 'user_data': ['one', 'two'] + \ } + \ ] + \ } endfunc func s:CompleteDone_CheckCompletedItemNone() @@ -253,16 +261,16 @@ func s:CompleteDone_CompleteFuncDictNoUserData(findstart, base) endif return { - \ 'words': [ - \ { - \ 'word': 'aword', - \ 'abbr': 'wrd', - \ 'menu': 'extra text', - \ 'info': 'words are cool', - \ 'kind': 'W' - \ } - \ ] - \ } + \ 'words': [ + \ { + \ 'word': 'aword', + \ 'abbr': 'wrd', + \ 'menu': 'extra text', + \ 'info': 'words are cool', + \ 'kind': 'W', + \ } + \ ] + \ } endfunc func s:CompleteDone_CheckCompletedItemDictNoUserData() @@ -573,6 +581,33 @@ func Test_pum_with_folds_two_tabs() call delete('Xpumscript') endfunc +func Test_pum_with_preview_win() + CheckScreendump + + let lines =<< trim END + funct Omni_test(findstart, base) + if a:findstart + return col(".") - 1 + endif + return [#{word: "one", info: "1info"}, #{word: "two", info: "2info"}, #{word: "three", info: "3info"}] + endfunc + set omnifunc=Omni_test + set completeopt+=longest + END + + call writefile(lines, 'Xpreviewscript') + let buf = RunVimInTerminal('-S Xpreviewscript', #{rows: 12}) + call term_wait(buf, 100) + call term_sendkeys(buf, "Gi\<C-X>\<C-O>") + call term_wait(buf, 200) + call term_sendkeys(buf, "\<C-N>") + call VerifyScreenDump(buf, 'Test_pum_with_preview_win', {}) + + call term_sendkeys(buf, "\<Esc>") + call StopVimInTerminal(buf) + call delete('Xpreviewscript') +endfunc + " Test for inserting the tag search pattern in insert mode func Test_ins_compl_tag_sft() call writefile([ @@ -682,6 +717,42 @@ func Test_complete_func_error() call assert_equal([], complete_info(['items']).items) endfunc +" Test for recursively starting completion mode using complete() +func Test_recursive_complete_func() + func ListColors() + call complete(5, ["red", "blue"]) + return '' + endfunc + new + call setline(1, ['a1', 'a2']) + set complete=. + exe "normal Goa\<C-X>\<C-L>\<C-R>=ListColors()\<CR>\<C-N>" + call assert_equal('a2blue', getline(3)) + delfunc ListColors + bw! +endfunc + +" Test for using complete() with completeopt+=longest +func Test_complete_with_longest() + new + inoremap <buffer> <f3> <cmd>call complete(1, ["iaax", "iaay", "iaaz"])<cr> + + " default: insert first match + set completeopt& + call setline(1, ['i']) + exe "normal Aa\<f3>\<esc>" + call assert_equal('iaax', getline(1)) + + " with longest: insert longest prefix + set completeopt+=longest + call setline(1, ['i']) + exe "normal Aa\<f3>\<esc>" + call assert_equal('iaa', getline(1)) + set completeopt& + bwipe! +endfunc + + " Test for completing words following a completed word in a line func Test_complete_wrapscan() " complete words from another buffer @@ -721,6 +792,17 @@ func Test_complete_across_line() close! endfunc +" Test for completing words with a '.' at the end of a word. +func Test_complete_joinspaces() + new + call setline(1, ['one two.', 'three. four']) + set joinspaces + exe "normal Goon\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>" + call assert_equal("one two. three. four", getline(3)) + set joinspaces& + bw! +endfunc + " Test for using CTRL-L to add one character when completing matching func Test_complete_add_onechar() new @@ -741,6 +823,39 @@ func Test_complete_add_onechar() close! endfunc +" Test for using CTRL-X CTRL-L to complete whole lines lines +func Test_complete_wholeline() + new + " complete one-line + call setline(1, ['a1', 'a2']) + exe "normal ggoa\<C-X>\<C-L>" + call assert_equal(['a1', 'a1', 'a2'], getline(1, '$')) + " go to the next match (wrapping around the buffer) + exe "normal 2GCa\<C-X>\<C-L>\<C-N>" + call assert_equal(['a1', 'a', 'a2'], getline(1, '$')) + " go to the next match + exe "normal 2GCa\<C-X>\<C-L>\<C-N>\<C-N>" + call assert_equal(['a1', 'a2', 'a2'], getline(1, '$')) + exe "normal 2GCa\<C-X>\<C-L>\<C-N>\<C-N>\<C-N>" + call assert_equal(['a1', 'a1', 'a2'], getline(1, '$')) + " repeat the test using CTRL-L + " go to the next match (wrapping around the buffer) + exe "normal 2GCa\<C-X>\<C-L>\<C-L>" + call assert_equal(['a1', 'a2', 'a2'], getline(1, '$')) + " go to the next match + exe "normal 2GCa\<C-X>\<C-L>\<C-L>\<C-L>" + call assert_equal(['a1', 'a', 'a2'], getline(1, '$')) + exe "normal 2GCa\<C-X>\<C-L>\<C-L>\<C-L>\<C-L>" + call assert_equal(['a1', 'a1', 'a2'], getline(1, '$')) + %d + " use CTRL-X CTRL-L to add one more line + call setline(1, ['a1', 'b1']) + setlocal complete=. + exe "normal ggOa\<C-X>\<C-L>\<C-X>\<C-L>\<C-X>\<C-L>" + call assert_equal(['a1', 'b1', '', 'a1', 'b1'], getline(1, '$')) + bw! +endfunc + " Test insert completion with 'cindent' (adjust the indent) func Test_complete_with_cindent() new @@ -829,6 +944,25 @@ func Test_complete_stop() close! endfunc +" Test for typing CTRL-R in insert completion mode to insert a register +" content. +func Test_complete_reginsert() + new + call setline(1, ['a1', 'a12', 'a123', 'a1234']) + + " if a valid CTRL-X mode key is returned from <C-R>=, then it should be + " processed. Otherwise, CTRL-X mode should be stopped and the key should be + " inserted. + exe "normal Goa\<C-P>\<C-R>=\"\\<C-P>\"\<CR>" + call assert_equal('a123', getline(5)) + let @r = "\<C-P>\<C-P>" + exe "normal GCa\<C-P>\<C-R>r" + call assert_equal('a12', getline(5)) + exe "normal GCa\<C-P>\<C-R>=\"x\"\<CR>" + call assert_equal('a1234x', getline(5)) + bw! +endfunc + func Test_issue_7021() CheckMSWindows @@ -842,6 +976,322 @@ func Test_issue_7021() set completeslash= endfunc +" Test for 'longest' setting in 'completeopt' with latin1 and utf-8 encodings +func Test_complete_longest_match() + " for e in ['latin1', 'utf-8'] + for e in ['utf-8'] + exe 'set encoding=' .. e + new + set complete=. + set completeopt=menu,longest + call setline(1, ['pfx_a1', 'pfx_a12', 'pfx_a123', 'pfx_b1']) + exe "normal Gopfx\<C-P>" + call assert_equal('pfx_', getline(5)) + bw! + endfor + + " Test for completing additional words with longest match set + new + call setline(1, ['abc1', 'abd2']) + exe "normal Goab\<C-P>\<C-X>\<C-P>" + call assert_equal('ab', getline(3)) + bw! + set complete& completeopt& +endfunc + +" Test for removing the first displayed completion match and selecting the +" match just before that. +func Test_complete_erase_firstmatch() + new + call setline(1, ['a12', 'a34', 'a56']) + set complete=. + exe "normal Goa\<C-P>\<BS>\<BS>3\<CR>" + call assert_equal('a34', getline('$')) + set complete& + bw! +endfunc + +" Test for completing words from unloaded buffers +func Test_complete_from_unloadedbuf() + call writefile(['abc'], "Xfile1") + call writefile(['def'], "Xfile2") + edit Xfile1 + edit Xfile2 + new | close + enew + bunload Xfile1 Xfile2 + set complete=u + " complete from an unloaded buffer + exe "normal! ia\<C-P>" + call assert_equal('abc', getline(1)) + exe "normal! od\<C-P>" + call assert_equal('def', getline(2)) + set complete& + %bw! + call delete("Xfile1") + call delete("Xfile2") +endfunc + +" Test for completing whole lines from unloaded buffers +func Test_complete_wholeline_unloadedbuf() + call writefile(['a line1', 'a line2', 'a line3'], "Xfile1") + edit Xfile1 + enew + set complete=u + exe "normal! ia\<C-X>\<C-L>\<C-P>" + call assert_equal('a line2', getline(1)) + %d + " completing from an unlisted buffer should fail + bdel Xfile1 + exe "normal! ia\<C-X>\<C-L>\<C-P>" + call assert_equal('a', getline(1)) + set complete& + %bw! + call delete("Xfile1") +endfunc + +" Test for completing words from unlisted buffers +func Test_complete_from_unlistedbuf() + call writefile(['abc'], "Xfile1") + call writefile(['def'], "Xfile2") + edit Xfile1 + edit Xfile2 + new | close + bdel Xfile1 Xfile2 + set complete=U + " complete from an unlisted buffer + exe "normal! ia\<C-P>" + call assert_equal('abc', getline(1)) + exe "normal! od\<C-P>" + call assert_equal('def', getline(2)) + set complete& + %bw! + call delete("Xfile1") + call delete("Xfile2") +endfunc + +" Test for completing whole lines from unlisted buffers +func Test_complete_wholeline_unlistedbuf() + call writefile(['a line1', 'a line2', 'a line3'], "Xfile1") + edit Xfile1 + enew + set complete=U + " completing from a unloaded buffer should fail + exe "normal! ia\<C-X>\<C-L>\<C-P>" + call assert_equal('a', getline(1)) + %d + bdel Xfile1 + exe "normal! ia\<C-X>\<C-L>\<C-P>" + call assert_equal('a line2', getline(1)) + set complete& + %bw! + call delete("Xfile1") +endfunc + +" Test for adding a multibyte character using CTRL-L in completion mode +func Test_complete_mbyte_char_add() + new + set complete=. + call setline(1, 'abė') + exe "normal! oa\<C-P>\<BS>\<BS>\<C-L>\<C-L>" + call assert_equal('abė', getline(2)) + " Test for a leader with multibyte character + %d + call setline(1, 'abėĕ') + exe "normal! oabė\<C-P>" + call assert_equal('abėĕ', getline(2)) + bw! +endfunc + +" Test for using <C-X><C-P> for local expansion even if 'complete' is set to +" not to complete matches from the local buffer. Also test using multiple +" <C-X> to cancel the current completion mode. +func Test_complete_local_expansion() + new + set complete=t + call setline(1, ['abc', 'def']) + exe "normal! Go\<C-X>\<C-P>" + call assert_equal("def", getline(3)) + exe "normal! Go\<C-P>" + call assert_equal("", getline(4)) + exe "normal! Go\<C-X>\<C-N>" + call assert_equal("abc", getline(5)) + exe "normal! Go\<C-N>" + call assert_equal("", getline(6)) + + " use multiple <C-X> to cancel the previous completion mode + exe "normal! Go\<C-P>\<C-X>\<C-P>" + call assert_equal("", getline(7)) + exe "normal! Go\<C-P>\<C-X>\<C-X>\<C-P>" + call assert_equal("", getline(8)) + exe "normal! Go\<C-P>\<C-X>\<C-X>\<C-X>\<C-P>" + call assert_equal("abc", getline(9)) + + " interrupt the current completion mode + set completeopt=menu,noinsert + exe "normal! Go\<C-X>\<C-F>\<C-X>\<C-X>\<C-P>\<C-Y>" + call assert_equal("abc", getline(10)) + + " when only one <C-X> is used to interrupt, do normal expansion + exe "normal! Go\<C-X>\<C-F>\<C-X>\<C-P>" + call assert_equal("", getline(11)) + set completeopt& + + " using two <C-X> in non-completion mode and restarting the same mode + exe "normal! God\<C-X>\<C-X>\<C-P>\<C-X>\<C-X>\<C-P>\<C-Y>" + call assert_equal("def", getline(12)) + + " test for adding a match from the original empty text + %d + call setline(1, 'abc def g') + exe "normal! o\<C-X>\<C-P>\<C-N>\<C-X>\<C-P>" + call assert_equal('def', getline(2)) + exe "normal! 0C\<C-X>\<C-N>\<C-P>\<C-X>\<C-N>" + call assert_equal('abc', getline(2)) + + bw! +endfunc + +" Test for undoing changes after a insert-mode completion +func Test_complete_undo() + new + set complete=. + " undo with 'ignorecase' + call setline(1, ['ABOVE', 'BELOW']) + set ignorecase + exe "normal! Goab\<C-G>u\<C-P>" + call assert_equal("ABOVE", getline(3)) + undo + call assert_equal("ab", getline(3)) + set ignorecase& + %d + " undo with longest match + set completeopt=menu,longest + call setline(1, ['above', 'about']) + exe "normal! Goa\<C-G>u\<C-P>" + call assert_equal("abo", getline(3)) + undo + call assert_equal("a", getline(3)) + set completeopt& + %d + " undo for line completion + call setline(1, ['above that change', 'below that change']) + exe "normal! Goabove\<C-G>u\<C-X>\<C-L>" + call assert_equal("above that change", getline(3)) + undo + call assert_equal("above", getline(3)) + + bw! +endfunc + +" Test for completing a very long word +func Test_complete_long_word() + set complete& + new + call setline(1, repeat('x', 950) .. ' one two three') + exe "normal! Gox\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>" + call assert_equal(repeat('x', 950) .. ' one two three', getline(2)) + %d + " should fail when more than 950 characters are in a word + call setline(1, repeat('x', 951) .. ' one two three') + exe "normal! Gox\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>" + call assert_equal(repeat('x', 951), getline(2)) + + " Test for adding a very long word to an existing completion + %d + call setline(1, ['abc', repeat('x', 1016) .. '012345']) + exe "normal! Goab\<C-P>\<C-X>\<C-P>" + call assert_equal('abc ' .. repeat('x', 1016) .. '0123', getline(3)) + bw! +endfunc + +" Test for some fields in the complete items used by complete() +func Test_complete_items() + func CompleteItems(idx) + let items = [[#{word: "one", dup: 1, user_data: 'u1'}, #{word: "one", dup: 1, user_data: 'u2'}], + \ [#{word: "one", dup: 0, user_data: 'u3'}, #{word: "one", dup: 0, user_data: 'u4'}], + \ [#{word: "one", icase: 1, user_data: 'u7'}, #{word: "oNE", icase: 1, user_data: 'u8'}], + \ [#{user_data: 'u9'}], + \ [#{word: "", user_data: 'u10'}], + \ [#{word: "", empty: 1, user_data: 'u11'}]] + call complete(col('.'), items[a:idx]) + return '' + endfunc + new + exe "normal! i\<C-R>=CompleteItems(0)\<CR>\<C-N>\<C-Y>" + call assert_equal('u2', v:completed_item.user_data) + call assert_equal('one', getline(1)) + exe "normal! o\<C-R>=CompleteItems(1)\<CR>\<C-Y>" + call assert_equal('u3', v:completed_item.user_data) + call assert_equal('one', getline(2)) + exe "normal! o\<C-R>=CompleteItems(1)\<CR>\<C-N>" + call assert_equal('', getline(3)) + set completeopt=menu,noinsert + exe "normal! o\<C-R>=CompleteItems(2)\<CR>one\<C-N>\<C-Y>" + call assert_equal('oNE', getline(4)) + call assert_equal('u8', v:completed_item.user_data) + set completeopt& + exe "normal! o\<C-R>=CompleteItems(3)\<CR>" + call assert_equal('', getline(5)) + exe "normal! o\<C-R>=CompleteItems(4)\<CR>" + call assert_equal('', getline(6)) + exe "normal! o\<C-R>=CompleteItems(5)\<CR>" + call assert_equal('', getline(7)) + call assert_equal('u11', v:completed_item.user_data) + " pass invalid argument to complete() + let cmd = "normal! o\<C-R>=complete(1, [[]])\<CR>" + call assert_fails('exe cmd', 'E730:') + bw! + delfunc CompleteItems +endfunc + +" Test for the "refresh" item in the dict returned by an insert completion +" function +func Test_complete_item_refresh_always() + let g:CallCount = 0 + func! Tcomplete(findstart, base) + if a:findstart + " locate the start of the word + let line = getline('.') + let start = col('.') - 1 + while start > 0 && line[start - 1] =~ '\a' + let start -= 1 + endwhile + return start + else + let g:CallCount += 1 + let res = ["update1", "update12", "update123"] + return #{words: res, refresh: 'always'} + endif + endfunc + new + set completeopt=menu,longest + set completefunc=Tcomplete + exe "normal! iup\<C-X>\<C-U>\<BS>\<BS>\<BS>\<BS>\<BS>" + call assert_equal('up', getline(1)) + call assert_equal(2, g:CallCount) + set completeopt& + set completefunc& + bw! + delfunc Tcomplete +endfunc + +" Test for completing from a thesaurus file without read permission +func Test_complete_unreadable_thesaurus_file() + CheckUnix + CheckNotRoot + + call writefile(['about', 'above'], 'Xfile') + call setfperm('Xfile', '---r--r--') + new + set complete=sXfile + exe "normal! ia\<C-P>" + call assert_equal('a', getline(1)) + bw! + call delete('Xfile') + set complete& +endfunc + " Test to ensure 'Scanning...' messages are not recorded in messages history func Test_z1_complete_no_history() new @@ -857,7 +1307,7 @@ endfunc " A mapping is not used for the key after CTRL-X. func Test_no_mapping_for_ctrl_x_key() new - inoremap <C-K> <Cmd>let was_mapped = 'yes'<CR> + inoremap <buffer> <C-K> <Cmd>let was_mapped = 'yes'<CR> setlocal dictionary=README.txt call feedkeys("aexam\<C-X>\<C-K> ", 'xt') call assert_equal('example ', getline(1)) @@ -865,6 +1315,813 @@ func Test_no_mapping_for_ctrl_x_key() bwipe! endfunc +" Test for different ways of setting the 'completefunc' option +func Test_completefunc_callback() + func CompleteFunc1(callnr, findstart, base) + call add(g:CompleteFunc1Args, [a:callnr, a:findstart, a:base]) + return a:findstart ? 0 : [] + endfunc + func CompleteFunc2(findstart, base) + call add(g:CompleteFunc2Args, [a:findstart, a:base]) + return a:findstart ? 0 : [] + endfunc + + let lines =<< trim END + #" Test for using a global function name + LET &completefunc = 'g:CompleteFunc2' + new + call setline(1, 'global') + LET g:CompleteFunc2Args = [] + call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') + call assert_equal([[1, ''], [0, 'global']], g:CompleteFunc2Args) + bw! + + #" Test for using a function() + set completefunc=function('g:CompleteFunc1',\ [10]) + new + call setline(1, 'one') + LET g:CompleteFunc1Args = [] + call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') + call assert_equal([[10, 1, ''], [10, 0, 'one']], g:CompleteFunc1Args) + bw! + + #" Using a funcref variable to set 'completefunc' + VAR Fn = function('g:CompleteFunc1', [11]) + LET &completefunc = Fn + new + call setline(1, 'two') + LET g:CompleteFunc1Args = [] + call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') + call assert_equal([[11, 1, ''], [11, 0, 'two']], g:CompleteFunc1Args) + bw! + + #" Using string(funcref_variable) to set 'completefunc' + LET Fn = function('g:CompleteFunc1', [12]) + LET &completefunc = string(Fn) + new + call setline(1, 'two') + LET g:CompleteFunc1Args = [] + call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') + call assert_equal([[12, 1, ''], [12, 0, 'two']], g:CompleteFunc1Args) + bw! + + #" Test for using a funcref() + set completefunc=funcref('g:CompleteFunc1',\ [13]) + new + call setline(1, 'three') + LET g:CompleteFunc1Args = [] + call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') + call assert_equal([[13, 1, ''], [13, 0, 'three']], g:CompleteFunc1Args) + bw! + + #" Using a funcref variable to set 'completefunc' + LET Fn = funcref('g:CompleteFunc1', [14]) + LET &completefunc = Fn + new + call setline(1, 'four') + LET g:CompleteFunc1Args = [] + call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') + call assert_equal([[14, 1, ''], [14, 0, 'four']], g:CompleteFunc1Args) + bw! + + #" Using a string(funcref_variable) to set 'completefunc' + LET Fn = funcref('g:CompleteFunc1', [15]) + LET &completefunc = string(Fn) + new + call setline(1, 'four') + LET g:CompleteFunc1Args = [] + call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') + call assert_equal([[15, 1, ''], [15, 0, 'four']], g:CompleteFunc1Args) + bw! + + #" Test for using a lambda function with set + VAR optval = "LSTART a, b LMIDDLE CompleteFunc1(16, a, b) LEND" + LET optval = substitute(optval, ' ', '\\ ', 'g') + exe "set completefunc=" .. optval + new + call setline(1, 'five') + LET g:CompleteFunc1Args = [] + call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') + call assert_equal([[16, 1, ''], [16, 0, 'five']], g:CompleteFunc1Args) + bw! + + #" Set 'completefunc' to a lambda expression + LET &completefunc = LSTART a, b LMIDDLE CompleteFunc1(17, a, b) LEND + new + call setline(1, 'six') + LET g:CompleteFunc1Args = [] + call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') + call assert_equal([[17, 1, ''], [17, 0, 'six']], g:CompleteFunc1Args) + bw! + + #" Set 'completefunc' to string(lambda_expression) + LET &completefunc = 'LSTART a, b LMIDDLE CompleteFunc1(18, a, b) LEND' + new + call setline(1, 'six') + LET g:CompleteFunc1Args = [] + call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') + call assert_equal([[18, 1, ''], [18, 0, 'six']], g:CompleteFunc1Args) + bw! + + #" Set 'completefunc' to a variable with a lambda expression + VAR Lambda = LSTART a, b LMIDDLE CompleteFunc1(19, a, b) LEND + LET &completefunc = Lambda + new + call setline(1, 'seven') + LET g:CompleteFunc1Args = [] + call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') + call assert_equal([[19, 1, ''], [19, 0, 'seven']], g:CompleteFunc1Args) + bw! + + #" Set 'completefunc' to a string(variable with a lambda expression) + LET Lambda = LSTART a, b LMIDDLE CompleteFunc1(20, a, b) LEND + LET &completefunc = string(Lambda) + new + call setline(1, 'seven') + LET g:CompleteFunc1Args = [] + call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') + call assert_equal([[20, 1, ''], [20, 0, 'seven']], g:CompleteFunc1Args) + bw! + + #" Test for using a lambda function with incorrect return value + LET Lambda = LSTART a, b LMIDDLE strlen(a) LEND + LET &completefunc = Lambda + new + call setline(1, 'eight') + call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') + bw! + + #" Test for clearing the 'completefunc' option + set completefunc='' + set completefunc& + call assert_fails("set completefunc=function('abc')", "E700:") + call assert_fails("set completefunc=funcref('abc')", "E700:") + + #" set 'completefunc' to a non-existing function + set completefunc=CompleteFunc2 + call setline(1, 'five') + call assert_fails("set completefunc=function('NonExistingFunc')", 'E700:') + call assert_fails("LET &completefunc = function('NonExistingFunc')", 'E700:') + LET g:CompleteFunc2Args = [] + call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') + call assert_equal([[1, ''], [0, 'five']], g:CompleteFunc2Args) + bw! + END + call CheckLegacyAndVim9Success(lines) + + " Test for using a script-local function name + func s:CompleteFunc3(findstart, base) + call add(g:CompleteFunc3Args, [a:findstart, a:base]) + return a:findstart ? 0 : [] + endfunc + set completefunc=s:CompleteFunc3 + new + call setline(1, 'script1') + let g:CompleteFunc3Args = [] + call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') + call assert_equal([[1, ''], [0, 'script1']], g:CompleteFunc3Args) + bw! + + let &completefunc = 's:CompleteFunc3' + new + call setline(1, 'script2') + let g:CompleteFunc3Args = [] + call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') + call assert_equal([[1, ''], [0, 'script2']], g:CompleteFunc3Args) + bw! + delfunc s:CompleteFunc3 + + " invalid return value + let &completefunc = {a -> 'abc'} + call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') + + " Using Vim9 lambda expression in legacy context should fail + " set completefunc=(a,\ b)\ =>\ CompleteFunc1(21,\ a,\ b) + new | only + let g:CompleteFunc1Args = [] + " call assert_fails('call feedkeys("A\<C-X>\<C-U>\<Esc>", "x")', 'E117:') + call assert_equal([], g:CompleteFunc1Args) + + " set 'completefunc' to a partial with dict. This used to cause a crash. + func SetCompleteFunc() + let params = {'complete': function('g:DictCompleteFunc')} + let &completefunc = params.complete + endfunc + func g:DictCompleteFunc(_) dict + endfunc + call SetCompleteFunc() + new + call SetCompleteFunc() + bw + call test_garbagecollect_now() + new + set completefunc= + wincmd w + set completefunc= + %bw! + delfunc g:DictCompleteFunc + delfunc SetCompleteFunc + + " Vim9 tests + let lines =<< trim END + vim9script + + def Vim9CompleteFunc(callnr: number, findstart: number, base: string): any + add(g:Vim9completeFuncArgs, [callnr, findstart, base]) + return findstart ? 0 : [] + enddef + + # Test for using a def function with completefunc + set completefunc=function('Vim9CompleteFunc',\ [60]) + new | only + setline(1, 'one') + g:Vim9completeFuncArgs = [] + feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') + assert_equal([[60, 1, ''], [60, 0, 'one']], g:Vim9completeFuncArgs) + bw! + + # Test for using a global function name + &completefunc = g:CompleteFunc2 + new | only + setline(1, 'two') + g:CompleteFunc2Args = [] + feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') + assert_equal([[1, ''], [0, 'two']], g:CompleteFunc2Args) + bw! + + # Test for using a script-local function name + def s:LocalCompleteFunc(findstart: number, base: string): any + add(g:LocalCompleteFuncArgs, [findstart, base]) + return findstart ? 0 : [] + enddef + &completefunc = s:LocalCompleteFunc + new | only + setline(1, 'three') + g:LocalCompleteFuncArgs = [] + feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') + assert_equal([[1, ''], [0, 'three']], g:LocalCompleteFuncArgs) + bw! + END + call CheckScriptSuccess(lines) + + " cleanup + set completefunc& + delfunc CompleteFunc1 + delfunc CompleteFunc2 + unlet g:CompleteFunc1Args g:CompleteFunc2Args + %bw! +endfunc + +" Test for different ways of setting the 'omnifunc' option +func Test_omnifunc_callback() + func OmniFunc1(callnr, findstart, base) + call add(g:OmniFunc1Args, [a:callnr, a:findstart, a:base]) + return a:findstart ? 0 : [] + endfunc + func OmniFunc2(findstart, base) + call add(g:OmniFunc2Args, [a:findstart, a:base]) + return a:findstart ? 0 : [] + endfunc + + let lines =<< trim END + #" Test for using a function name + LET &omnifunc = 'g:OmniFunc2' + new + call setline(1, 'zero') + LET g:OmniFunc2Args = [] + call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') + call assert_equal([[1, ''], [0, 'zero']], g:OmniFunc2Args) + bw! + + #" Test for using a function() + set omnifunc=function('g:OmniFunc1',\ [10]) + new + call setline(1, 'one') + LET g:OmniFunc1Args = [] + call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') + call assert_equal([[10, 1, ''], [10, 0, 'one']], g:OmniFunc1Args) + bw! + + #" Using a funcref variable to set 'omnifunc' + VAR Fn = function('g:OmniFunc1', [11]) + LET &omnifunc = Fn + new + call setline(1, 'two') + LET g:OmniFunc1Args = [] + call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') + call assert_equal([[11, 1, ''], [11, 0, 'two']], g:OmniFunc1Args) + bw! + + #" Using a string(funcref_variable) to set 'omnifunc' + LET Fn = function('g:OmniFunc1', [12]) + LET &omnifunc = string(Fn) + new + call setline(1, 'two') + LET g:OmniFunc1Args = [] + call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') + call assert_equal([[12, 1, ''], [12, 0, 'two']], g:OmniFunc1Args) + bw! + + #" Test for using a funcref() + set omnifunc=funcref('g:OmniFunc1',\ [13]) + new + call setline(1, 'three') + LET g:OmniFunc1Args = [] + call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') + call assert_equal([[13, 1, ''], [13, 0, 'three']], g:OmniFunc1Args) + bw! + + #" Use let to set 'omnifunc' to a funcref + LET Fn = funcref('g:OmniFunc1', [14]) + LET &omnifunc = Fn + new + call setline(1, 'four') + LET g:OmniFunc1Args = [] + call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') + call assert_equal([[14, 1, ''], [14, 0, 'four']], g:OmniFunc1Args) + bw! + + #" Using a string(funcref) to set 'omnifunc' + LET Fn = funcref("g:OmniFunc1", [15]) + LET &omnifunc = string(Fn) + new + call setline(1, 'four') + LET g:OmniFunc1Args = [] + call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') + call assert_equal([[15, 1, ''], [15, 0, 'four']], g:OmniFunc1Args) + bw! + + #" Test for using a lambda function with set + VAR optval = "LSTART a, b LMIDDLE OmniFunc1(16, a, b) LEND" + LET optval = substitute(optval, ' ', '\\ ', 'g') + exe "set omnifunc=" .. optval + new + call setline(1, 'five') + LET g:OmniFunc1Args = [] + call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') + call assert_equal([[16, 1, ''], [16, 0, 'five']], g:OmniFunc1Args) + bw! + + #" Set 'omnifunc' to a lambda expression + LET &omnifunc = LSTART a, b LMIDDLE OmniFunc1(17, a, b) LEND + new + call setline(1, 'six') + LET g:OmniFunc1Args = [] + call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') + call assert_equal([[17, 1, ''], [17, 0, 'six']], g:OmniFunc1Args) + bw! + + #" Set 'omnifunc' to a string(lambda_expression) + LET &omnifunc = 'LSTART a, b LMIDDLE OmniFunc1(18, a, b) LEND' + new + call setline(1, 'six') + LET g:OmniFunc1Args = [] + call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') + call assert_equal([[18, 1, ''], [18, 0, 'six']], g:OmniFunc1Args) + bw! + + #" Set 'omnifunc' to a variable with a lambda expression + VAR Lambda = LSTART a, b LMIDDLE OmniFunc1(19, a, b) LEND + LET &omnifunc = Lambda + new + call setline(1, 'seven') + LET g:OmniFunc1Args = [] + call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') + call assert_equal([[19, 1, ''], [19, 0, 'seven']], g:OmniFunc1Args) + bw! + + #" Set 'omnifunc' to a string(variable with a lambda expression) + LET Lambda = LSTART a, b LMIDDLE OmniFunc1(20, a, b) LEND + LET &omnifunc = string(Lambda) + new + call setline(1, 'seven') + LET g:OmniFunc1Args = [] + call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') + call assert_equal([[20, 1, ''], [20, 0, 'seven']], g:OmniFunc1Args) + bw! + + #" Test for using a lambda function with incorrect return value + LET Lambda = LSTART a, b LMIDDLE strlen(a) LEND + LET &omnifunc = Lambda + new + call setline(1, 'eight') + call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') + bw! + + #" Test for clearing the 'omnifunc' option + set omnifunc='' + set omnifunc& + call assert_fails("set omnifunc=function('abc')", "E700:") + call assert_fails("set omnifunc=funcref('abc')", "E700:") + + #" set 'omnifunc' to a non-existing function + set omnifunc=OmniFunc2 + call setline(1, 'nine') + call assert_fails("set omnifunc=function('NonExistingFunc')", 'E700:') + call assert_fails("LET &omnifunc = function('NonExistingFunc')", 'E700:') + LET g:OmniFunc2Args = [] + call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') + call assert_equal([[1, ''], [0, 'nine']], g:OmniFunc2Args) + bw! + END + call CheckLegacyAndVim9Success(lines) + + " Test for using a script-local function name + func s:OmniFunc3(findstart, base) + call add(g:OmniFunc3Args, [a:findstart, a:base]) + return a:findstart ? 0 : [] + endfunc + set omnifunc=s:OmniFunc3 + new + call setline(1, 'script1') + let g:OmniFunc3Args = [] + call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') + call assert_equal([[1, ''], [0, 'script1']], g:OmniFunc3Args) + bw! + + let &omnifunc = 's:OmniFunc3' + new + call setline(1, 'script2') + let g:OmniFunc3Args = [] + call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') + call assert_equal([[1, ''], [0, 'script2']], g:OmniFunc3Args) + bw! + delfunc s:OmniFunc3 + + " invalid return value + let &omnifunc = {a -> 'abc'} + call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') + + " Using Vim9 lambda expression in legacy context should fail + " set omnifunc=(a,\ b)\ =>\ OmniFunc1(21,\ a,\ b) + new | only + let g:OmniFunc1Args = [] + " call assert_fails('call feedkeys("A\<C-X>\<C-O>\<Esc>", "x")', 'E117:') + call assert_equal([], g:OmniFunc1Args) + + " set 'omnifunc' to a partial with dict. This used to cause a crash. + func SetOmniFunc() + let params = {'omni': function('g:DictOmniFunc')} + let &omnifunc = params.omni + endfunc + func g:DictOmniFunc(_) dict + endfunc + call SetOmniFunc() + new + call SetOmniFunc() + bw + call test_garbagecollect_now() + new + set omnifunc= + wincmd w + set omnifunc= + %bw! + delfunc g:DictOmniFunc + delfunc SetOmniFunc + + " Vim9 tests + let lines =<< trim END + vim9script + + def Vim9omniFunc(callnr: number, findstart: number, base: string): any + add(g:Vim9omniFunc_Args, [callnr, findstart, base]) + return findstart ? 0 : [] + enddef + + # Test for using a def function with omnifunc + set omnifunc=function('Vim9omniFunc',\ [60]) + new | only + setline(1, 'one') + g:Vim9omniFunc_Args = [] + feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') + assert_equal([[60, 1, ''], [60, 0, 'one']], g:Vim9omniFunc_Args) + bw! + + # Test for using a global function name + &omnifunc = g:OmniFunc2 + new | only + setline(1, 'two') + g:OmniFunc2Args = [] + feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') + assert_equal([[1, ''], [0, 'two']], g:OmniFunc2Args) + bw! + + # Test for using a script-local function name + def s:LocalOmniFunc(findstart: number, base: string): any + add(g:LocalOmniFuncArgs, [findstart, base]) + return findstart ? 0 : [] + enddef + &omnifunc = s:LocalOmniFunc + new | only + setline(1, 'three') + g:LocalOmniFuncArgs = [] + feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') + assert_equal([[1, ''], [0, 'three']], g:LocalOmniFuncArgs) + bw! + END + call CheckScriptSuccess(lines) + + " cleanup + set omnifunc& + delfunc OmniFunc1 + delfunc OmniFunc2 + unlet g:OmniFunc1Args g:OmniFunc2Args + %bw! +endfunc + +" Test for different ways of setting the 'thesaurusfunc' option +func Test_thesaurusfunc_callback() + func TsrFunc1(callnr, findstart, base) + call add(g:TsrFunc1Args, [a:callnr, a:findstart, a:base]) + return a:findstart ? 0 : [] + endfunc + func TsrFunc2(findstart, base) + call add(g:TsrFunc2Args, [a:findstart, a:base]) + return a:findstart ? 0 : ['sunday'] + endfunc + + let lines =<< trim END + #" Test for using a function name + LET &thesaurusfunc = 'g:TsrFunc2' + new + call setline(1, 'zero') + LET g:TsrFunc2Args = [] + call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') + call assert_equal([[1, ''], [0, 'zero']], g:TsrFunc2Args) + bw! + + #" Test for using a function() + set thesaurusfunc=function('g:TsrFunc1',\ [10]) + new + call setline(1, 'one') + LET g:TsrFunc1Args = [] + call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') + call assert_equal([[10, 1, ''], [10, 0, 'one']], g:TsrFunc1Args) + bw! + + #" Using a funcref variable to set 'thesaurusfunc' + VAR Fn = function('g:TsrFunc1', [11]) + LET &thesaurusfunc = Fn + new + call setline(1, 'two') + LET g:TsrFunc1Args = [] + call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') + call assert_equal([[11, 1, ''], [11, 0, 'two']], g:TsrFunc1Args) + bw! + + #" Using a string(funcref_variable) to set 'thesaurusfunc' + LET Fn = function('g:TsrFunc1', [12]) + LET &thesaurusfunc = string(Fn) + new + call setline(1, 'two') + LET g:TsrFunc1Args = [] + call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') + call assert_equal([[12, 1, ''], [12, 0, 'two']], g:TsrFunc1Args) + bw! + + #" Test for using a funcref() + set thesaurusfunc=funcref('g:TsrFunc1',\ [13]) + new + call setline(1, 'three') + LET g:TsrFunc1Args = [] + call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') + call assert_equal([[13, 1, ''], [13, 0, 'three']], g:TsrFunc1Args) + bw! + + #" Using a funcref variable to set 'thesaurusfunc' + LET Fn = funcref('g:TsrFunc1', [14]) + LET &thesaurusfunc = Fn + new + call setline(1, 'four') + LET g:TsrFunc1Args = [] + call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') + call assert_equal([[14, 1, ''], [14, 0, 'four']], g:TsrFunc1Args) + bw! + + #" Using a string(funcref_variable) to set 'thesaurusfunc' + LET Fn = funcref('g:TsrFunc1', [15]) + LET &thesaurusfunc = string(Fn) + new + call setline(1, 'four') + LET g:TsrFunc1Args = [] + call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') + call assert_equal([[15, 1, ''], [15, 0, 'four']], g:TsrFunc1Args) + bw! + + #" Test for using a lambda function + VAR optval = "LSTART a, b LMIDDLE TsrFunc1(16, a, b) LEND" + LET optval = substitute(optval, ' ', '\\ ', 'g') + exe "set thesaurusfunc=" .. optval + new + call setline(1, 'five') + LET g:TsrFunc1Args = [] + call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') + call assert_equal([[16, 1, ''], [16, 0, 'five']], g:TsrFunc1Args) + bw! + + #" Test for using a lambda function with set + LET &thesaurusfunc = LSTART a, b LMIDDLE TsrFunc1(17, a, b) LEND + new + call setline(1, 'six') + LET g:TsrFunc1Args = [] + call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') + call assert_equal([[17, 1, ''], [17, 0, 'six']], g:TsrFunc1Args) + bw! + + #" Set 'thesaurusfunc' to a string(lambda expression) + LET &thesaurusfunc = 'LSTART a, b LMIDDLE TsrFunc1(18, a, b) LEND' + new + call setline(1, 'six') + LET g:TsrFunc1Args = [] + call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') + call assert_equal([[18, 1, ''], [18, 0, 'six']], g:TsrFunc1Args) + bw! + + #" Set 'thesaurusfunc' to a variable with a lambda expression + VAR Lambda = LSTART a, b LMIDDLE TsrFunc1(19, a, b) LEND + LET &thesaurusfunc = Lambda + new + call setline(1, 'seven') + LET g:TsrFunc1Args = [] + call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') + call assert_equal([[19, 1, ''], [19, 0, 'seven']], g:TsrFunc1Args) + bw! + + #" Set 'thesaurusfunc' to a string(variable with a lambda expression) + LET Lambda = LSTART a, b LMIDDLE TsrFunc1(20, a, b) LEND + LET &thesaurusfunc = string(Lambda) + new + call setline(1, 'seven') + LET g:TsrFunc1Args = [] + call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') + call assert_equal([[20, 1, ''], [20, 0, 'seven']], g:TsrFunc1Args) + bw! + + #" Test for using a lambda function with incorrect return value + LET Lambda = LSTART a, b LMIDDLE strlen(a) LEND + LET &thesaurusfunc = Lambda + new + call setline(1, 'eight') + call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') + bw! + + #" Test for clearing the 'thesaurusfunc' option + set thesaurusfunc='' + set thesaurusfunc& + call assert_fails("set thesaurusfunc=function('abc')", "E700:") + call assert_fails("set thesaurusfunc=funcref('abc')", "E700:") + + #" set 'thesaurusfunc' to a non-existing function + set thesaurusfunc=TsrFunc2 + call setline(1, 'ten') + call assert_fails("set thesaurusfunc=function('NonExistingFunc')", 'E700:') + call assert_fails("LET &thesaurusfunc = function('NonExistingFunc')", 'E700:') + LET g:TsrFunc2Args = [] + call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') + call assert_equal([[1, ''], [0, 'ten']], g:TsrFunc2Args) + bw! + + #" Use a buffer-local value and a global value + set thesaurusfunc& + setlocal thesaurusfunc=function('g:TsrFunc1',\ [22]) + call setline(1, 'sun') + LET g:TsrFunc1Args = [] + call feedkeys("A\<C-X>\<C-T>\<Esc>", "x") + call assert_equal('sun', getline(1)) + call assert_equal([[22, 1, ''], [22, 0, 'sun']], g:TsrFunc1Args) + new + call setline(1, 'sun') + LET g:TsrFunc1Args = [] + call feedkeys("A\<C-X>\<C-T>\<Esc>", "x") + call assert_equal('sun', getline(1)) + call assert_equal([], g:TsrFunc1Args) + set thesaurusfunc=function('g:TsrFunc1',\ [23]) + wincmd w + call setline(1, 'sun') + LET g:TsrFunc1Args = [] + call feedkeys("A\<C-X>\<C-T>\<Esc>", "x") + call assert_equal('sun', getline(1)) + call assert_equal([[22, 1, ''], [22, 0, 'sun']], g:TsrFunc1Args) + :%bw! + END + call CheckLegacyAndVim9Success(lines) + + " Test for using a script-local function name + func s:TsrFunc3(findstart, base) + call add(g:TsrFunc3Args, [a:findstart, a:base]) + return a:findstart ? 0 : [] + endfunc + set tsrfu=s:TsrFunc3 + new + call setline(1, 'script1') + let g:TsrFunc3Args = [] + call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') + call assert_equal([[1, ''], [0, 'script1']], g:TsrFunc3Args) + bw! + + let &tsrfu = 's:TsrFunc3' + new + call setline(1, 'script2') + let g:TsrFunc3Args = [] + call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') + call assert_equal([[1, ''], [0, 'script2']], g:TsrFunc3Args) + bw! + delfunc s:TsrFunc3 + + " invalid return value + let &thesaurusfunc = {a -> 'abc'} + call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') + + " Using Vim9 lambda expression in legacy context should fail + " set thesaurusfunc=(a,\ b)\ =>\ TsrFunc1(21,\ a,\ b) + new | only + let g:TsrFunc1Args = [] + " call assert_fails('call feedkeys("A\<C-X>\<C-T>\<Esc>", "x")', 'E117:') + call assert_equal([], g:TsrFunc1Args) + bw! + + " set 'thesaurusfunc' to a partial with dict. This used to cause a crash. + func SetTsrFunc() + let params = {'thesaurus': function('g:DictTsrFunc')} + let &thesaurusfunc = params.thesaurus + endfunc + func g:DictTsrFunc(_) dict + endfunc + call SetTsrFunc() + new + call SetTsrFunc() + bw + call test_garbagecollect_now() + new + set thesaurusfunc= + wincmd w + %bw! + delfunc SetTsrFunc + + " set buffer-local 'thesaurusfunc' to a partial with dict. This used to + " cause a crash. + func SetLocalTsrFunc() + let params = {'thesaurus': function('g:DictTsrFunc')} + let &l:thesaurusfunc = params.thesaurus + endfunc + call SetLocalTsrFunc() + call test_garbagecollect_now() + call SetLocalTsrFunc() + set thesaurusfunc= + bw! + delfunc g:DictTsrFunc + delfunc SetLocalTsrFunc + + " Vim9 tests + let lines =<< trim END + vim9script + + def Vim9tsrFunc(callnr: number, findstart: number, base: string): any + add(g:Vim9tsrFunc_Args, [callnr, findstart, base]) + return findstart ? 0 : [] + enddef + + # Test for using a def function with thesaurusfunc + set thesaurusfunc=function('Vim9tsrFunc',\ [60]) + new | only + setline(1, 'one') + g:Vim9tsrFunc_Args = [] + feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') + assert_equal([[60, 1, ''], [60, 0, 'one']], g:Vim9tsrFunc_Args) + bw! + + # Test for using a global function name + &thesaurusfunc = g:TsrFunc2 + new | only + setline(1, 'two') + g:TsrFunc2Args = [] + feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') + assert_equal([[1, ''], [0, 'two']], g:TsrFunc2Args) + bw! + + # Test for using a script-local function name + def s:LocalTsrFunc(findstart: number, base: string): any + add(g:LocalTsrFuncArgs, [findstart, base]) + return findstart ? 0 : [] + enddef + &thesaurusfunc = s:LocalTsrFunc + new | only + setline(1, 'three') + g:LocalTsrFuncArgs = [] + feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') + assert_equal([[1, ''], [0, 'three']], g:LocalTsrFuncArgs) + bw! + END + call CheckScriptSuccess(lines) + + " cleanup + set thesaurusfunc& + delfunc TsrFunc1 + delfunc TsrFunc2 + unlet g:TsrFunc1Args g:TsrFunc2Args + %bw! +endfunc + func FooBarComplete(findstart, base) if a:findstart return col('.') - 1 @@ -884,4 +2141,52 @@ func Test_complete_smartindent() delfunction! FooBarComplete endfunc +func Test_complete_overrun() + " this was going past the end of the copied text + new + sil norm si0s0 + bwipe! +endfunc + +func Test_infercase_very_long_line() + " this was truncating the line when inferring case + new + let longLine = "blah "->repeat(300) + let verylongLine = "blah "->repeat(400) + call setline(1, verylongLine) + call setline(2, longLine) + set ic infercase + exe "normal 2Go\<C-X>\<C-L>\<Esc>" + call assert_equal(longLine, getline(3)) + + " check that the too long text is NUL terminated + %del + norm o + norm 1987ax + exec "norm ox\<C-X>\<C-L>" + call assert_equal(repeat('x', 1987), getline(3)) + + bwipe! + set noic noinfercase +endfunc + +func Test_ins_complete_add() + " this was reading past the end of allocated memory + new + norm o + norm 7o + sil! norm o + + bwipe! +endfunc + +func Test_ins_complete_end_of_line() + " this was reading past the end of the line + new + norm 8oý + sil! norm o + + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_lambda.vim b/src/nvim/testdir/test_lambda.vim index c1fe47d1c9..025eb016a8 100644 --- a/src/nvim/testdir/test_lambda.vim +++ b/src/nvim/testdir/test_lambda.vim @@ -62,7 +62,8 @@ endfunc function Test_lambda_fails() call assert_equal(3, {a, b -> a + b}(1, 2)) call assert_fails('echo {a, a -> a + a}(1, 2)', 'E853:') - call assert_fails('echo {a, b -> a + b)}(1, 2)', 'E15:') + call assert_fails('echo {a, b -> a + b)}(1, 2)', 'E451:') + echo assert_fails('echo 10->{a -> a + 2}', 'E107:') endfunc func Test_not_lambda() @@ -125,7 +126,7 @@ func Test_lambda_closure_counter() endfunc let l:F = s:foo() - call garbagecollect() + call test_garbagecollect_now() call assert_equal(1, l:F()) call assert_equal(2, l:F()) call assert_equal(3, l:F()) @@ -208,9 +209,9 @@ func Test_lambda_circular_reference() endfunc call s:Foo() - call garbagecollect() + call test_garbagecollect_now() let i = 0 | while i < 10000 | call s:Foo() | let i+= 1 | endwhile - call garbagecollect() + call test_garbagecollect_now() endfunc func Test_lambda_combination() @@ -239,11 +240,16 @@ func Test_closure_counter() endfunc let l:F = s:foo() - call garbagecollect() + call test_garbagecollect_now() call assert_equal(1, l:F()) call assert_equal(2, l:F()) call assert_equal(3, l:F()) call assert_equal(4, l:F()) + + call assert_match("^\n function <SNR>\\d\\+_bar() closure" + \ .. "\n1 let x += 1" + \ .. "\n2 return x" + \ .. "\n endfunction$", execute('func s:bar')) endfunc func Test_closure_unlet() @@ -257,7 +263,7 @@ func Test_closure_unlet() endfunc call assert_false(has_key(s:foo(), 'x')) - call garbagecollect() + call test_garbagecollect_now() endfunc func LambdaFoo() @@ -294,7 +300,7 @@ func Test_named_function_closure() endfunc call Afoo() call assert_equal(14, s:Abar()) - call garbagecollect() + call test_garbagecollect_now() call assert_equal(14, s:Abar()) endfunc @@ -308,3 +314,21 @@ func Test_lambda_error() " This was causing a crash call assert_fails('ec{@{->{d->()()', 'E15') endfunc + +func Test_closure_error() + let l =<< trim END + func F1() closure + return 1 + endfunc + END + call writefile(l, 'Xscript') + let caught_932 = 0 + try + source Xscript + catch /E932:/ + let caught_932 = 1 + endtry + call assert_equal(1, caught_932) +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_langmap.vim b/src/nvim/testdir/test_langmap.vim index 4f831aa40b..aaed77e109 100644 --- a/src/nvim/testdir/test_langmap.vim +++ b/src/nvim/testdir/test_langmap.vim @@ -49,6 +49,41 @@ func Test_langmap() call feedkeys(';', 'tx') call assert_equal(5, col('.')) + set langmap=RL + let g:counter = 0 + nnoremap L;L <Cmd>let g:counter += 1<CR> + nnoremap <C-L> <Cmd>throw 'This mapping should not be triggered'<CR> + + " 'langmap' is applied to keys without modifiers when matching a mapping + call feedkeys('R;R', 'tx') + call assert_equal(1, g:counter) + nunmap L;L + unlet g:counter + + delete + call assert_equal('', getline(1)) + undo + call assert_equal('Hello World', getline(1)) + " 'langmap' does not change Ctrl-R to Ctrl-L for consistency + call feedkeys("\<*C-R>", 'tx') + call assert_equal('', getline(1)) + + set langmap=6L + undo + setlocal bufhidden=hide + let oldbuf = bufnr() + enew + call assert_notequal(oldbuf, bufnr()) + " 'langmap' does not change Ctrl-6 to Ctrl-L for consistency + " Ctrl-6 becomes Ctrl-^ after merging the Ctrl modifier + call feedkeys("\<*C-6>", 'tx') + call assert_equal(oldbuf, bufnr()) + setlocal bufhidden& + + nunmap <C-L> + set langmap& quit! endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_legacy_filetype.vim b/src/nvim/testdir/test_legacy_filetype.vim deleted file mode 100644 index 772faaadb0..0000000000 --- a/src/nvim/testdir/test_legacy_filetype.vim +++ /dev/null @@ -1,4 +0,0 @@ -let g:do_legacy_filetype = 1 -filetype on - -source test_filetype.vim diff --git a/src/nvim/testdir/test_let.vim b/src/nvim/testdir/test_let.vim index 6cb736a38a..35745e9c6a 100644 --- a/src/nvim/testdir/test_let.vim +++ b/src/nvim/testdir/test_let.vim @@ -6,6 +6,10 @@ func Test_let() let Test104#numvar = function('tr') call assert_equal("function('tr')", string(Test104#numvar)) + let foo#tr = function('tr') + call assert_equal("function('tr')", string(foo#tr)) + unlet foo#tr + let a = 1 let b = 2 @@ -25,9 +29,62 @@ func Test_let() let s = "\na #1\nb #2" call assert_equal(s, out) + " Test for displaying a string variable + let s = 'vim' + let out = execute('let s') + let s = "\ns vim" + call assert_equal(s, out) + + " Test for displaying a list variable + let l = [1, 2] + let out = execute('let l') + let s = "\nl [1, 2]" + call assert_equal(s, out) + + " Test for displaying a dict variable + let d = {'k' : 'v'} + let out = execute('let d') + let s = "\nd {'k': 'v'}" + call assert_equal(s, out) + + " Test for displaying a function reference variable + let F = function('min') + let out = execute('let F') + let s = "\nF *min()" + call assert_equal(s, out) + let x = 0 if 0 | let x = 1 | endif call assert_equal(0, x) + + " Display a list item using an out of range index + let l = [10] + call assert_fails('let l[1]', 'E684:') + + " List special variable dictionaries + let g:Test_Global_Var = 5 + call assert_match("\nTest_Global_Var #5", execute('let g:')) + unlet g:Test_Global_Var + + let b:Test_Buf_Var = 8 + call assert_match("\nb:Test_Buf_Var #8", execute('let b:')) + unlet b:Test_Buf_Var + + let w:Test_Win_Var = 'foo' + call assert_equal("\nw:Test_Win_Var foo", execute('let w:')) + unlet w:Test_Win_Var + + let t:Test_Tab_Var = 'bar' + call assert_equal("\nt:Test_Tab_Var bar", execute('let t:')) + unlet t:Test_Tab_Var + + let s:Test_Script_Var = [7] + call assert_match("\ns:Test_Script_Var \\[7]", execute('let s:')) + unlet s:Test_Script_Var + + let l:Test_Local_Var = {'k' : 5} + call assert_match("\nl:Test_Local_Var {'k': 5}", execute('let l:')) + call assert_match("v:errors []", execute('let v:')) endfunc func s:set_arg1(a) abort @@ -201,16 +258,68 @@ func Test_let_option_error() let &fillchars = _w endfunc +" Errors with the :let statement func Test_let_errors() let s = 'abcd' call assert_fails('let s[1] = 5', 'E689:') let l = [1, 2, 3] call assert_fails('let l[:] = 5', 'E709:') + + call assert_fails('let x:lnum=5', ['E121:', 'E488:']) + call assert_fails('let v:=5', 'E461:') + call assert_fails('let [a]', 'E474:') + call assert_fails('let [a, b] = [', 'E697:') + call assert_fails('let [a, b] = [10, 20', 'E696:') + call assert_fails('let [a, b] = 10', 'E714:') + call assert_fails('let [a, , b] = [10, 20]', 'E475:') + call assert_fails('let [a, b&] = [10, 20]', 'E475:') + call assert_fails('let $ = 10', 'E475:') + call assert_fails('let $FOO[1] = "abc"', 'E18:') + call assert_fails('let &buftype[1] = "nofile"', 'E18:') + let s = "var" + let var = 1 + call assert_fails('let var += [1,2]', 'E734:') + call assert_fails('let {s}.1 = 2', 'E1203:') + call assert_fails('let a[1] = 5', 'E121:') + let l = [[1,2]] + call assert_fails('let l[:][0] = [5]', 'E708:') + let d = {'k' : 4} + call assert_fails('let d.# = 5', 'E488:') + call assert_fails('let d.m += 5', 'E716:') + call assert_fails('let m = d[{]', 'E15:') + let l = [1, 2] + call assert_fails('let l[2] = 0', 'E684:') + call assert_fails('let l[0:1] = [1, 2, 3]', 'E710:') + call assert_fails('let l[-2:-3] = [3, 4]', 'E684:') + call assert_fails('let l[0:4] = [5, 6]', 'E711:') + call assert_fails('let l -= 2', 'E734:') + call assert_fails('let l += 2', 'E734:') + call assert_fails('let g:["a;b"] = 10', 'E461:') + call assert_fails('let g:.min = function("max")', 'E704:') + call assert_fails('let g:cos = "" | let g:.cos = {-> 42}', 'E704:') + if has('channel') + let ch = test_null_channel() + call assert_fails('let ch += 1', 'E734:') + endif + call assert_fails('let name = "a" .. "b",', 'E488: Trailing characters: ,') + + " This test works only when the language is English + if v:lang == "C" || v:lang =~ '^[Ee]n' + call assert_fails('let [a ; b;] = [10, 20]', + \ 'Double ; in list of variables') + endif endfunc func Test_let_heredoc_fails() call assert_fails('let v =<< marker', 'E991:') + try + exe "let v =<< TEXT | abc | TEXT" + call assert_report('No exception thrown') + catch /E488:/ + catch + call assert_report("Caught exception: " .. v:exception) + endtry let text =<< trim END func WrongSyntax() @@ -243,6 +352,10 @@ func Test_let_heredoc_fails() call writefile(text, 'XheredocBadMarker') call assert_fails('source XheredocBadMarker', 'E221:') call delete('XheredocBadMarker') + + call writefile(['let v =<< TEXT', 'abc'], 'XheredocMissingMarker') + call assert_fails('source XheredocMissingMarker', 'E990:') + call delete('XheredocMissingMarker') endfunc func Test_let_heredoc_trim_no_indent_marker() @@ -361,3 +474,5 @@ E END call assert_equal([' x', ' \y', ' z'], [a, b, c]) endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_lispwords.vim b/src/nvim/testdir/test_lispindent.vim index 4144fb0521..2d6060bba3 100644 --- a/src/nvim/testdir/test_lispwords.vim +++ b/src/nvim/testdir/test_lispindent.vim @@ -86,6 +86,37 @@ func Test_lisp_indent() set nolisp endfunc +func Test_lispindent_negative() + " in legacy script there is no error + call assert_equal(-1, lispindent(-1)) +endfunc + +func Test_lispindent_with_indentexpr() + enew + setl ai lisp nocin indentexpr=11 + exe "normal a(x\<CR>1\<CR>2)\<Esc>" + let expected = ['(x', ' 1', ' 2)'] + call assert_equal(expected, getline(1, 3)) + " with Lisp indenting the first line is not indented + normal 1G=G + call assert_equal(expected, getline(1, 3)) + + %del + setl lispoptions=expr:1 indentexpr=5 + exe "normal a(x\<CR>1\<CR>2)\<Esc>" + let expected_expr = ['(x', ' 1', ' 2)'] + call assert_equal(expected_expr, getline(1, 3)) + normal 2G2<<=G + call assert_equal(expected_expr, getline(1, 3)) + + setl lispoptions=expr:0 + " with Lisp indenting the first line is not indented + normal 1G3<<=G + call assert_equal(expected, getline(1, 3)) + + bwipe! +endfunc + func Test_lisp_indent_works() " This was reading beyond the end of the line new diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim index 2f4e1db4a1..9ecd83265a 100644 --- a/src/nvim/testdir/test_listdict.vim +++ b/src/nvim/testdir/test_listdict.vim @@ -31,6 +31,12 @@ func Test_list_slice() call assert_equal([1, 'as''d', [1, 2, function('strlen')]], l[:-2]) call assert_equal([1, 'as''d', [1, 2, function('strlen')], {'a': 1}], l[0:8]) call assert_equal([], l[8:-1]) + call assert_equal([], l[0:-10]) + " perform an operation on a list slice + let l = [1, 2, 3] + let l[:1] += [1, 2] + let l[2:] -= [1] + call assert_equal([2, 4, 2], l) endfunc " List identity @@ -104,6 +110,8 @@ func Test_list_range_assign() let l = [0] let l[:] = [1, 2] call assert_equal([1, 2], l) + let l[-4:-1] = [5, 6] + call assert_equal([5, 6], l) endfunc " Test removing items in list @@ -143,6 +151,20 @@ func Test_list_func_remove() call assert_fails("call remove(l, l)", 'E745:') endfunc +" List add() function +func Test_list_add() + let l = [] + call add(l, 1) + call add(l, [2, 3]) + call add(l, []) + call add(l, v:_null_list) + call add(l, {'k' : 3}) + call add(l, {}) + call add(l, v:_null_dict) + call assert_equal([1, [2, 3], [], [], {'k' : 3}, {}, {}], l) + " call assert_equal(1, add(v:_null_list, 4)) +endfunc + " Tests for Dictionary type func Test_dict() @@ -165,6 +187,26 @@ func Test_dict() call assert_equal({'c': 'ccc', '1': 99, 'b': [1, 2, function('strlen')], '3': 33, '-1': {'a': 1}}, d) call filter(d, 'v:key =~ ''[ac391]''') call assert_equal({'c': 'ccc', '1': 99, '3': 33, '-1': {'a': 1}}, d) + + " duplicate key + call assert_fails("let d = {'k' : 10, 'k' : 20}", 'E721:') + " missing comma + call assert_fails("let d = {'k' : 10 'k' : 20}", 'E722:') + " missing curly brace + call assert_fails("let d = {'k' : 10,", 'E723:') + " invalid key + call assert_fails('let d = #{++ : 10}', 'E15:') + " wrong type for key + call assert_fails('let d={[] : 10}', 'E730:') + " undefined variable as value + call assert_fails("let d={'k' : i}", 'E121:') + + " allow key starting with number at the start, not a curly expression + call assert_equal({'1foo': 77}, #{1foo: 77}) + + " #{expr} is not a curly expression + let x = 'x' + call assert_equal(#{g: x}, #{g:x}) endfunc " Dictionary identity @@ -247,6 +289,16 @@ func Test_dict_func() call assert_equal('xxx3', Fn('xxx')) endfunc +func Test_dict_assign() + let d = {} + let d.1 = 1 + let d._ = 2 + call assert_equal({'1': 1, '_': 2}, d) + + let n = 0 + call assert_fails('let n.key = 3', 'E1203: Dot can only be used on a dictionary: n.key = 3') +endfunc + " Function in script-local List or Dict func Test_script_local_dict_func() let g:dict = {} @@ -259,7 +311,7 @@ func Test_script_local_dict_func() unlet g:dict endfunc -" Test removing items in la dictionary +" Test removing items in a dictionary func Test_dict_func_remove() let d = {1:'a', 2:'b', 3:'c'} call assert_equal('b', remove(d, 2)) @@ -294,17 +346,18 @@ func Test_dict_deepcopy() let l = [4, d, 6] let d[3] = l let dc = deepcopy(d) - call assert_fails('call deepcopy(d, 1)', 'E698') + call assert_fails('call deepcopy(d, 1)', 'E698:') let l2 = [0, l, l, 3] let l[1] = l2 let l3 = deepcopy(l2) call assert_true(l3[1] is l3[2]) + call assert_fails("call deepcopy([1, 2], 2)", 'E1023:') endfunc " Locked variables func Test_list_locked_var() let expected = [ - \ [['0000-000', 'ppppppp'], + \ [['1000-000', 'ppppppF'], \ ['0000-000', 'ppppppp'], \ ['0000-000', 'ppppppp']], \ [['1000-000', 'ppppppF'], @@ -331,7 +384,7 @@ func Test_list_locked_var() exe "unlockvar " . depth . " l" endif let ps = islocked("l").islocked("l[1]").islocked("l[1][1]").islocked("l[1][1][0]").'-'.islocked("l[2]").islocked("l[2]['6']").islocked("l[2]['6'][7]") - call assert_equal(expected[depth][u][0], ps) + call assert_equal(expected[depth][u][0], ps, 'depth: ' .. depth) let ps = '' try let l[1][1][0] = 99 @@ -375,15 +428,20 @@ func Test_list_locked_var() catch let ps .= 'F' endtry - call assert_equal(expected[depth][u][1], ps) + call assert_equal(expected[depth][u][1], ps, 'depth: ' .. depth) endfor endfor + call assert_fails("let x=islocked('a b')", 'E488:') + let mylist = [1, 2, 3] + call assert_fails("let x = islocked('mylist[1:2]')", 'E786:') + let mydict = {'k' : 'v'} + call assert_fails("let x = islocked('mydict.a')", 'E716:') endfunc " Unletting locked variables func Test_list_locked_var_unlet() let expected = [ - \ [['0000-000', 'ppppppp'], + \ [['1000-000', 'ppppppp'], \ ['0000-000', 'ppppppp'], \ ['0000-000', 'ppppppp']], \ [['1000-000', 'ppFppFp'], @@ -411,7 +469,7 @@ func Test_list_locked_var_unlet() exe "unlockvar " . depth . " l" endif let ps = islocked("l").islocked("l[1]").islocked("l[1][1]").islocked("l[1][1][0]").'-'.islocked("l[2]").islocked("l[2]['6']").islocked("l[2]['6'][7]") - call assert_equal(expected[depth][u][0], ps) + call assert_equal(expected[depth][u][0], ps, 'depth: ' .. depth) let ps = '' try unlet l[2]['6'][7] @@ -458,6 +516,11 @@ func Test_list_locked_var_unlet() call assert_equal(expected[depth][u][1], ps) endfor endfor + " Deleting a list range should fail if the range is locked + let l = [1, 2, 3, 4] + lockvar l[1:2] + call assert_fails('unlet l[1:2]', 'E741:') + unlet l endfunc " Locked variables and :unlet or list / dict functions @@ -567,6 +630,18 @@ func Test_let_lock_list() unlet l endfunc +" Locking part of the list +func Test_let_lock_list_items() + let l = [1, 2, 3, 4] + lockvar l[2:] + call assert_equal(0, islocked('l[0]')) + call assert_equal(1, islocked('l[2]')) + call assert_equal(1, islocked('l[3]')) + call assert_fails('let l[2] = 10', 'E741:') + call assert_fails('let l[3] = 20', 'E741:') + unlet l +endfunc + " lockvar/islocked() triggering script autoloading func Test_lockvar_script_autoload() let old_rtp = &rtp @@ -598,26 +673,35 @@ func Test_func_arg_list() call s:arg_list_test(1, 2, [3, 4], {5: 6}) endfunc +func Test_dict_item_locked() +endfunc + " Tests for reverse(), sort(), uniq() func Test_reverse_sort_uniq() let l = ['-0', 'A11', 2, 2, 'xaaa', 4, 'foo', 'foo6', 'foo', [0, 1, 2], 'x8', [0, 1, 2], 1.5] call assert_equal(['-0', 'A11', 2, 'xaaa', 4, 'foo', 'foo6', 'foo', [0, 1, 2], 'x8', [0, 1, 2], 1.5], uniq(copy(l))) call assert_equal([1.5, [0, 1, 2], 'x8', [0, 1, 2], 'foo', 'foo6', 'foo', 4, 'xaaa', 2, 2, 'A11', '-0'], reverse(l)) call assert_equal([1.5, [0, 1, 2], 'x8', [0, 1, 2], 'foo', 'foo6', 'foo', 4, 'xaaa', 2, 2, 'A11', '-0'], reverse(reverse(l))) - call assert_equal(['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]], sort(l)) - call assert_equal([[0, 1, 2], [0, 1, 2], 4, 2, 2, 1.5, 'xaaa', 'x8', 'foo6', 'foo', 'foo', 'A11', '-0'], reverse(sort(l))) - call assert_equal(['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]], sort(reverse(sort(l)))) - call assert_equal(['-0', 'A11', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 4, [0, 1, 2]], uniq(sort(l))) - - let l=[7, 9, 'one', 18, 12, 22, 'two', 10.0e-16, -1, 'three', 0xff, 0.22, 'four'] - call assert_equal([-1, 'one', 'two', 'three', 'four', 1.0e-15, 0.22, 7, 9, 12, 18, 22, 255], sort(copy(l), 'n')) - - let l=[7, 9, 18, 12, 22, 10.0e-16, -1, 0xff, 0, -0, 0.22, 'bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', {}, []] - call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 1)) - call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 'i')) - call assert_equal(['BAR', 'Bar', 'FOO', 'FOOBAR', 'Foo', 'bar', 'foo', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l))) + if has('float') + call assert_equal(['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]], sort(l)) + call assert_equal([[0, 1, 2], [0, 1, 2], 4, 2, 2, 1.5, 'xaaa', 'x8', 'foo6', 'foo', 'foo', 'A11', '-0'], reverse(sort(l))) + call assert_equal(['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]], sort(reverse(sort(l)))) + call assert_equal(['-0', 'A11', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 4, [0, 1, 2]], uniq(sort(l))) + + let l = [7, 9, 'one', 18, 12, 22, 'two', 10.0e-16, -1, 'three', 0xff, 0.22, 'four'] + call assert_equal([-1, 'one', 'two', 'three', 'four', 1.0e-15, 0.22, 7, 9, 12, 18, 22, 255], sort(copy(l), 'n')) + + let l = [7, 9, 18, 12, 22, 10.0e-16, -1, 0xff, 0, -0, 0.22, 'bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', {}, []] + call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 1)) + call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 'i')) + call assert_equal(['BAR', 'Bar', 'FOO', 'FOOBAR', 'Foo', 'bar', 'foo', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l))) + endif call assert_fails('call reverse("")', 'E899:') + call assert_fails('call uniq([1, 2], {x, y -> []})', 'E745:') + call assert_fails("call sort([1, 2], function('min'), 1)", "E715:") + call assert_fails("call sort([1, 2], function('invalid_func'))", "E700:") + call assert_fails("call sort([1, 2], function('min'))", "E118:") endfunc " reduce a list or a blob @@ -665,7 +749,7 @@ func Test_reduce() call assert_equal(42, reduce(v:_null_blob, function('add'), 42)) endfunc -" splitting a string to a List +" splitting a string to a List using split() func Test_str_split() call assert_equal(['aa', 'bb'], split(' aa bb ')) call assert_equal(['aa', 'bb'], split(' aa bb ', '\W\+', 0)) @@ -676,6 +760,9 @@ func Test_str_split() call assert_equal(['aa', '', 'bb', 'cc', ''], split('aa,,bb, cc,', ',\s*', 1)) call assert_equal(['a', 'b', 'c'], split('abc', '\zs')) call assert_equal(['', 'a', '', 'b', '', 'c', ''], split('abc', '\zs', 1)) + call assert_fails("call split('abc', [])", 'E730:') + call assert_fails("call split('abc', 'b', [])", 'E745:') + call assert_equal(['abc'], split('abc', '\\%(')) endfunc " compare recursively linked list and dict @@ -687,6 +774,12 @@ func Test_listdict_compare() call assert_true(d == d) call assert_false(l != deepcopy(l)) call assert_false(d != deepcopy(d)) + + " comparison errors + call assert_fails('echo [1, 2] =~ {}', 'E691:') + call assert_fails('echo [1, 2] =~ [1, 2]', 'E692:') + call assert_fails('echo {} =~ 5', 'E735:') + call assert_fails('echo {} =~ {}', 'E736:') endfunc " compare complex recursively linked list and dict @@ -860,6 +953,67 @@ func Test_scope_dict() call s:check_scope_dict('v', v:true) endfunc +" Test for deep nesting of lists (> 100) +func Test_deep_nested_list() + let deep_list = [] + let l = deep_list + for i in range(102) + let newlist = [] + call add(l, newlist) + let l = newlist + endfor + call add(l, 102) + + call assert_fails('let m = deepcopy(deep_list)', 'E698:') + call assert_fails('lockvar 110 deep_list', 'E743:') + call assert_fails('unlockvar 110 deep_list', 'E743:') + " Nvim implements :echo very differently + " call assert_fails('let x = execute("echo deep_list")', 'E724:') + call test_garbagecollect_now() + unlet deep_list +endfunc + +" Test for deep nesting of dicts (> 100) +func Test_deep_nested_dict() + let deep_dict = {} + let d = deep_dict + for i in range(102) + let newdict = {} + let d.k = newdict + let d = newdict + endfor + let d.k = 'v' + + call assert_fails('let m = deepcopy(deep_dict)', 'E698:') + call assert_fails('lockvar 110 deep_dict', 'E743:') + call assert_fails('unlockvar 110 deep_dict', 'E743:') + " Nvim implements :echo very differently + " call assert_fails('let x = execute("echo deep_dict")', 'E724:') + call test_garbagecollect_now() + unlet deep_dict +endfunc + +" List and dict indexing tests +func Test_listdict_index() + call assert_fails('echo function("min")[0]', 'E695:') + call assert_fails('echo v:true[0]', 'E909:') + let d = {'k' : 10} + call assert_fails('echo d.', 'E15:') + call assert_fails('echo d[1:2]', 'E719:') + call assert_fails("let v = [4, 6][{-> 1}]", 'E729:') + call assert_fails("let v = range(5)[2:[]]", 'E730:') + call assert_fails("let v = range(5)[2:{-> 2}(]", ['E15:', 'E116:']) + call assert_fails("let v = range(5)[2:3", 'E111:') + call assert_fails("let l = insert([1,2,3], 4, 10)", 'E684:') + call assert_fails("let l = insert([1,2,3], 4, -10)", 'E684:') + call assert_fails("let l = insert([1,2,3], 4, [])", 'E745:') + let l = [1, 2, 3] + call assert_fails("let l[i] = 3", 'E121:') + call assert_fails("let l[1.1] = 4", 'E806:') + call assert_fails("let l[:i] = [4, 5]", 'E121:') + call assert_fails("let l[:3.2] = [4, 5]", 'E806:') +endfunc + " Test for a null list func Test_null_list() let l = v:_null_list @@ -896,3 +1050,21 @@ func Test_null_list() call assert_equal(1, islocked('l')) unlockvar l endfunc + +" Test for a null dict +func Test_null_dict() + call assert_equal(v:_null_dict, v:_null_dict) + let d = v:_null_dict + call assert_equal({}, d) + call assert_equal(0, len(d)) + call assert_equal(1, empty(d)) + call assert_equal(0, items(d)) + call assert_equal(0, keys(d)) + call assert_equal(0, values(d)) + call assert_false(has_key(d, 'k')) + call assert_equal('{}', string(d)) + call assert_fails('let x = v:_null_dict[10]') + call assert_equal({}, {}) +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_listlbr.vim b/src/nvim/testdir/test_listlbr.vim index affa0f96fa..1cbdba5d76 100644 --- a/src/nvim/testdir/test_listlbr.vim +++ b/src/nvim/testdir/test_listlbr.vim @@ -7,6 +7,7 @@ CheckOption linebreak CheckFeature conceal source view_util.vim +source screendump.vim function s:screen_lines(lnum, width) abort return ScreenLines(a:lnum, a:width) @@ -133,6 +134,45 @@ func Test_linebreak_with_visual_operations() call s:close_windows() endfunc +" Test that cursor is drawn at correct position after an operator when +" 'linebreak' is enabled. +func Test_linebreak_reset_restore() + CheckScreendump + + " f_wincol() calls validate_cursor() + let lines =<< trim END + set linebreak showcmd noshowmode formatexpr=wincol()-wincol() + call setline(1, repeat('a', &columns - 10) .. ' bbbbbbbbbb c') + END + call writefile(lines, 'XlbrResetRestore', 'D') + let buf = RunVimInTerminal('-S XlbrResetRestore', {'rows': 8}) + + call term_sendkeys(buf, '$v$') + call WaitForAssert({-> assert_equal(13, term_getcursor(buf)[1])}) + call term_sendkeys(buf, 'zo') + call WaitForAssert({-> assert_equal(12, term_getcursor(buf)[1])}) + + call term_sendkeys(buf, '$v$') + call WaitForAssert({-> assert_equal(13, term_getcursor(buf)[1])}) + call term_sendkeys(buf, 'gq') + call WaitForAssert({-> assert_equal(12, term_getcursor(buf)[1])}) + + call term_sendkeys(buf, "$\<C-V>$") + call WaitForAssert({-> assert_equal(13, term_getcursor(buf)[1])}) + call term_sendkeys(buf, 'I') + call WaitForAssert({-> assert_equal(12, term_getcursor(buf)[1])}) + + call term_sendkeys(buf, "\<Esc>$v$") + call WaitForAssert({-> assert_equal(13, term_getcursor(buf)[1])}) + call term_sendkeys(buf, 's') + call WaitForAssert({-> assert_equal(12, term_getcursor(buf)[1])}) + call VerifyScreenDump(buf, 'Test_linebreak_reset_restore_1', {}) + + " clean up + call term_sendkeys(buf, "\<Esc>") + call StopVimInTerminal(buf) +endfunc + func Test_virtual_block() call s:test_windows('setl sbr=+') call setline(1, [ diff --git a/src/nvim/testdir/test_makeencoding.vim b/src/nvim/testdir/test_makeencoding.vim index c53c07d991..e297bdc228 100644 --- a/src/nvim/testdir/test_makeencoding.vim +++ b/src/nvim/testdir/test_makeencoding.vim @@ -107,3 +107,19 @@ func Test_make() lclose endfor endfunc + +" Test for an error file with a long line that needs an encoding conversion +func Test_longline_conversion() + new + call setline(1, ['Xfile:10:' .. repeat("\xe0", 2000)]) + write ++enc=latin1 Xerr.out + bw! + set errorformat& + set makeencoding=latin1 + cfile Xerr.out + call assert_equal(repeat("\u00e0", 2000), getqflist()[0].text) + call delete('Xerr.out') + set makeencoding& +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_maparg.vim b/src/nvim/testdir/test_maparg.vim index dad4c81a7b..f903f5b934 100644 --- a/src/nvim/testdir/test_maparg.vim +++ b/src/nvim/testdir/test_maparg.vim @@ -158,11 +158,11 @@ func Test_range_map() call assert_equal("abcd", getline(1)) endfunc -func One_mapset_test(keys) - exe 'nnoremap ' .. a:keys .. ' original<CR>' +func One_mapset_test(keys, rhs) + exe 'nnoremap ' .. a:keys .. ' ' .. a:rhs let orig = maparg(a:keys, 'n', 0, 1) call assert_equal(a:keys, orig.lhs) - call assert_equal('original<CR>', orig.rhs) + call assert_equal(a:rhs, orig.rhs) call assert_equal('n', orig.mode) exe 'nunmap ' .. a:keys @@ -172,15 +172,16 @@ func One_mapset_test(keys) call mapset('n', 0, orig) let d = maparg(a:keys, 'n', 0, 1) call assert_equal(a:keys, d.lhs) - call assert_equal('original<CR>', d.rhs) + call assert_equal(a:rhs, d.rhs) call assert_equal('n', d.mode) exe 'nunmap ' .. a:keys endfunc func Test_mapset() - call One_mapset_test('K') - call One_mapset_test('<F3>') + call One_mapset_test('K', 'original<CR>') + call One_mapset_test('<F3>', 'original<CR>') + call One_mapset_test('<F3>', '<lt>Nop>') " Check <> key conversion new @@ -203,6 +204,26 @@ func Test_mapset() iunmap K + " Test that <Nop> is restored properly + inoremap K <Nop> + call feedkeys("SK\<Esc>", 'xt') + call assert_equal('', getline(1)) + + let orig = maparg('K', 'i', 0, 1) + call assert_equal('K', orig.lhs) + call assert_equal('<Nop>', orig.rhs) + call assert_equal('i', orig.mode) + + inoremap K foo + call feedkeys("SK\<Esc>", 'xt') + call assert_equal('foo', getline(1)) + + call mapset('i', 0, orig) + call feedkeys("SK\<Esc>", 'xt') + call assert_equal('', getline(1)) + + iunmap K + " Test literal <CR> using a backslash let cpo_save = &cpo set cpo-=B @@ -248,6 +269,8 @@ func Test_mapset() bwipe! call assert_fails('call mapset([], v:false, {})', 'E730:') + call assert_fails('call mapset("i", 0, "")', 'E715:') + call assert_fails('call mapset("i", 0, {})', 'E460:') endfunc func Check_ctrlb_map(d, check_alt) diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim index e1d0b9a9ba..5c5a65d4ca 100644 --- a/src/nvim/testdir/test_mapping.vim +++ b/src/nvim/testdir/test_mapping.vim @@ -395,7 +395,9 @@ func Test_motionforce_omap() endfunc func Test_error_in_map_expr() - if !has('terminal') || (has('win32') && has('gui_running')) + " Unlike CheckRunVimInTerminal this does work in a win32 console + CheckFeature terminal + if has('win32') && has('gui_running') throw 'Skipped: cannot run Vim in a terminal window' endif @@ -765,6 +767,11 @@ func Test_mapcomplete() mapclear endfunc +func GetAbbrText() + unabbr hola + return 'hello' +endfunc + " Test for <expr> in abbreviation func Test_expr_abbr() new @@ -777,10 +784,17 @@ func Test_expr_abbr() " invalid <expr> abbreviation abbr <expr> hte GetAbbr() call assert_fails('normal ihte ', 'E117:') - call assert_equal(' ', getline(1)) + call assert_equal('', getline(1)) unabbr <expr> hte - close! + " evaluating the expression deletes the abbreviation + abbr <expr> hola GetAbbrText() + call assert_equal('GetAbbrText()', maparg('hola', 'i', '1')) + call feedkeys("ahola \<Esc>", 'xt') + call assert_equal('hello ', getline('.')) + call assert_equal('', maparg('hola', 'i', '1')) + + bwipe! endfunc " Test for storing mappings in different modes in a vimrc file @@ -975,6 +989,21 @@ func Test_abbreviate_multi_byte() bwipe! endfunc +" Test for abbreviations with 'latin1' encoding +func Test_abbreviate_latin1_encoding() + " set encoding=latin1 + call assert_fails('abbr ab#$c ABC', 'E474:') + new + iabbr <buffer> #i #include + iabbr <buffer> ## #enddef + exe "normal i#i\<C-]>" + call assert_equal('#include', getline(1)) + exe "normal 0Di##\<C-]>" + call assert_equal('#enddef', getline(1)) + %bw! + set encoding=utf-8 +endfunc ++ " Test for <Plug> always being mapped, even when used with "noremap". func Test_plug_remap() let g:foo = 0 @@ -1006,15 +1035,14 @@ func Test_plug_remap() endfunc func Test_mouse_drag_mapped_start_select() - CheckFunction test_setmouse set mouse=a set selectmode=key,mouse func ClickExpr() - call test_setmouse(1, 1) + call Ntest_setmouse(1, 1) return "\<LeftMouse>" endfunc func DragExpr() - call test_setmouse(1, 2) + call Ntest_setmouse(1, 2) return "\<LeftDrag>" endfunc nnoremap <expr> <F2> ClickExpr() @@ -1034,16 +1062,39 @@ func Test_mouse_drag_mapped_start_select() set mouse& endfunc +func Test_mouse_drag_statusline() + set laststatus=2 + set mouse=a + func ClickExpr() + call Ntest_setmouse(&lines - 1, 1) + return "\<LeftMouse>" + endfunc + func DragExpr() + call Ntest_setmouse(&lines - 2, 1) + return "\<LeftDrag>" + endfunc + nnoremap <expr> <F2> ClickExpr() + nnoremap <expr> <F3> DragExpr() + + " this was causing a crash in win_drag_status_line() + call feedkeys("\<F2>:tabnew\<CR>\<F3>", 'tx') + + nunmap <F2> + nunmap <F3> + delfunc ClickExpr + delfunc DragExpr + set laststatus& mouse& +endfunc + " Test for mapping <LeftDrag> in Insert mode func Test_mouse_drag_insert_map() - CheckFunction test_setmouse set mouse=a func ClickExpr() - call test_setmouse(1, 1) + call Ntest_setmouse(1, 1) return "\<LeftMouse>" endfunc func DragExpr() - call test_setmouse(1, 2) + call Ntest_setmouse(1, 2) return "\<LeftDrag>" endfunc inoremap <expr> <F2> ClickExpr() @@ -1129,4 +1180,14 @@ func Test_map_after_timed_out_nop() call delete('Xtest_map_after_timed_out_nop') endfunc +func Test_using_past_typeahead() + nnoremap :00 0 + exe "norm :set \x80\xfb0=0\<CR>" + exe "sil norm :0\x0f\<C-U>\<CR>" + + exe "norm :set \x80\xfb0=\<CR>" + nunmap :00 +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_marks.vim b/src/nvim/testdir/test_marks.vim index 74e63d9d69..a7ccca498c 100644 --- a/src/nvim/testdir/test_marks.vim +++ b/src/nvim/testdir/test_marks.vim @@ -91,6 +91,17 @@ func Test_setpos() call assert_equal([0, 1, 21341234, 0], getpos("'a")) call assert_equal(4, virtcol("'a")) + " Test with invalid buffer number, line number and column number + call cursor(2, 2) + call setpos('.', [-1, 1, 1, 0]) + call assert_equal([2, 2], [line('.'), col('.')]) + call setpos('.', [0, -1, 1, 0]) + call assert_equal([2, 2], [line('.'), col('.')]) + call setpos('.', [0, 1, -1, 0]) + call assert_equal([2, 2], [line('.'), col('.')]) + + call assert_fails("call setpos('ab', [0, 1, 1, 0])", 'E474:') + bwipe! call win_gotoid(twowin) bwipe! @@ -293,4 +304,17 @@ func Test_getmarklist() close! endfunc +" This was using freed memory +func Test_jump_mark_autocmd() + next 00 + edit 0 + sargument + au BufEnter 0 all + sil norm + + au! BufEnter + bwipe! +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_match.vim b/src/nvim/testdir/test_match.vim index 70271aa32f..600b6132a9 100644 --- a/src/nvim/testdir/test_match.vim +++ b/src/nvim/testdir/test_match.vim @@ -2,6 +2,7 @@ " matchaddpos(), matcharg(), matchdelete(), and setmatches(). source screendump.vim +source check.vim function Test_match() highlight MyGroup1 term=bold ctermbg=red guibg=red @@ -35,8 +36,8 @@ function Test_match() let m1 = matchadd("MyGroup1", "TODO") let m2 = matchadd("MyGroup2", "FIXME", 42) let m3 = matchadd("MyGroup3", "XXX", 60, 17) - let ans = [{'group': 'MyGroup1', 'pattern': 'TODO', 'priority': 10, 'id': 4}, - \ {'group': 'MyGroup2', 'pattern': 'FIXME', 'priority': 42, 'id': 5}, + let ans = [{'group': 'MyGroup1', 'pattern': 'TODO', 'priority': 10, 'id': 1000}, + \ {'group': 'MyGroup2', 'pattern': 'FIXME', 'priority': 42, 'id': 1001}, \ {'group': 'MyGroup3', 'pattern': 'XXX', 'priority': 60, 'id': 17}] call assert_equal(ans, getmatches()) @@ -117,7 +118,7 @@ function Test_match() call clearmatches() call setline(1, 'abcdΣabcdef') - eval "MyGroup1"->matchaddpos([[1, 4, 2], [1, 9, 2]]) + eval "MyGroup1"->matchaddpos([[1, 4, 2], [1, 9, 2]], 10, 42) 1 redraw! let v1 = screenattr(1, 1) @@ -128,7 +129,7 @@ function Test_match() let v8 = screenattr(1, 8) let v9 = screenattr(1, 9) let v10 = screenattr(1, 10) - call assert_equal([{'group': 'MyGroup1', 'id': 11, 'priority': 10, 'pos1': [1, 4, 2], 'pos2': [1, 9, 2]}], getmatches()) + call assert_equal([{'group': 'MyGroup1', 'id': 42, 'priority': 10, 'pos1': [1, 4, 2], 'pos2': [1, 9, 2]}], getmatches()) call assert_notequal(v1, v4) call assert_equal(v5, v4) call assert_equal(v6, v1) @@ -142,7 +143,7 @@ function Test_match() let m=getmatches() call clearmatches() call setmatches(m) - call assert_equal([{'group': 'MyGroup1', 'id': 11, 'priority': 10, 'pos1': [1, 4, 2], 'pos2': [1,9, 2]}, {'group': 'MyGroup1', 'pattern': '\%2lmatchadd', 'priority': 10, 'id': 12}], getmatches()) + call assert_equal([{'group': 'MyGroup1', 'id': 42, 'priority': 10, 'pos1': [1, 4, 2], 'pos2': [1,9, 2]}, {'group': 'MyGroup1', 'pattern': '\%2lmatchadd', 'priority': 10, 'id': 1106}], getmatches()) highlight MyGroup1 NONE highlight MyGroup2 NONE @@ -159,12 +160,14 @@ endfunc func Test_matchadd_error() call clearmatches() " Nvim: not an error anymore: + " call assert_fails("call matchadd('GroupDoesNotExist', 'X')", 'E28:') call matchadd('GroupDoesNotExist', 'X') - call assert_equal([{'group': 'GroupDoesNotExist', 'pattern': 'X', 'priority': 10, 'id': 13}], getmatches()) - call assert_fails("call matchadd('Search', '\\(')", 'E475:') + call assert_equal([{'group': 'GroupDoesNotExist', 'pattern': 'X', 'priority': 10, 'id': 1206}], getmatches()) + call assert_fails("call matchadd('Search', '\\(')", 'E54:') call assert_fails("call matchadd('Search', 'XXX', 1, 123, 1)", 'E715:') call assert_fails("call matchadd('Error', 'XXX', 1, 3)", 'E798:') call assert_fails("call matchadd('Error', 'XXX', 1, 0)", 'E799:') + call assert_fails("call matchadd('Error', 'XXX', [], 0)", 'E745:') endfunc func Test_matchaddpos() @@ -219,6 +222,21 @@ func Test_matchaddpos() set hlsearch& endfunc +" Add 12 match positions (previously the limit was 8 positions). +func Test_matchaddpos_dump() + CheckScreendump + + let lines =<< trim END + call setline(1, ['1234567890123']->repeat(14)) + call matchaddpos('Search', range(1, 12)->map({i, v -> [v, v]})) + END + call writefile(lines, 'Xmatchaddpos', 'D') + let buf = RunVimInTerminal('-S Xmatchaddpos', #{rows: 14}) + call VerifyScreenDump(buf, 'Test_matchaddpos_1', {}) + + call StopVimInTerminal(buf) +endfunc + func Test_matchaddpos_otherwin() syntax on new @@ -237,8 +255,8 @@ func Test_matchaddpos_otherwin() let savematches = getmatches(winid) let expect = [ - \ {'group': 'Search', 'pattern': '4', 'priority': 10, 'id': 4}, - \ {'group': 'Error', 'id': 5, 'priority': 10, 'pos1': [1, 2, 1], 'pos2': [2, 2, 1]}, + \ {'group': 'Search', 'pattern': '4', 'priority': 10, 'id': 1000}, + \ {'group': 'Error', 'id': 1001, 'priority': 10, 'pos1': [1, 2, 1], 'pos2': [2, 2, 1]}, \] call assert_equal(expect, savematches) @@ -289,7 +307,10 @@ func Test_matchaddpos_error() call assert_fails("call matchaddpos('Error', [1], 1, 123, 1)", 'E715:') call assert_fails("call matchaddpos('Error', [1], 1, 5, {'window':12345})", 'E957:') " Why doesn't the following error have an error code E...? + " call assert_fails("call matchaddpos('Error', [{}])", 'E290:') call assert_fails("call matchaddpos('Error', [{}])", 'E5031:') + call assert_equal(-1, matchaddpos('Error', v:_null_list)) + call assert_fails("call matchaddpos('Error', [1], [], 1)", 'E745:') endfunc func OtherWindowCommon() @@ -306,9 +327,8 @@ func OtherWindowCommon() endfunc func Test_matchdelete_other_window() - if !CanRunVimInTerminal() - throw 'Skipped: cannot make screendumps' - endif + CheckScreendump + let buf = OtherWindowCommon() call term_sendkeys(buf, ":call matchdelete(mid, winid)\<CR>") call VerifyScreenDump(buf, 'Test_matchdelete_1', {}) @@ -323,9 +343,7 @@ func Test_matchdelete_error() endfunc func Test_matchclear_other_window() - if !CanRunVimInTerminal() - throw 'Skipped: cannot make screendumps' - endif + CheckRunVimInTerminal let buf = OtherWindowCommon() call term_sendkeys(buf, ":call clearmatches(winid)\<CR>") call VerifyScreenDump(buf, 'Test_matchclear_1', {}) @@ -335,9 +353,7 @@ func Test_matchclear_other_window() endfunc func Test_matchadd_other_window() - if !CanRunVimInTerminal() - throw 'Skipped: cannot make screendumps' - endif + CheckRunVimInTerminal let buf = OtherWindowCommon() call term_sendkeys(buf, ":call matchadd('Search', 'Hello', 1, -1, #{window: winid})\<CR>") call term_sendkeys(buf, ":\<CR>") @@ -347,5 +363,80 @@ func Test_matchadd_other_window() call delete('XscriptMatchCommon') endfunc +func Test_match_in_linebreak() + CheckRunVimInTerminal + + let lines =<< trim END + set breakindent linebreak breakat+=] + call printf('%s]%s', repeat('x', 50), repeat('x', 70))->setline(1) + call matchaddpos('ErrorMsg', [[1, 51]]) + END + call writefile(lines, 'XscriptMatchLinebreak') + let buf = RunVimInTerminal('-S XscriptMatchLinebreak', #{rows: 10}) + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_match_linebreak', {}) + + call StopVimInTerminal(buf) + call delete('XscriptMatchLinebreak') +endfunc + +func Test_match_with_incsearch() + CheckRunVimInTerminal + + let lines =<< trim END + set incsearch + call setline(1, range(20)) + call matchaddpos('ErrorMsg', [3]) + END + call writefile(lines, 'XmatchWithIncsearch') + let buf = RunVimInTerminal('-S XmatchWithIncsearch', #{rows: 6}) + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_match_with_incsearch_1', {}) + + call term_sendkeys(buf, ":s/0") + call VerifyScreenDump(buf, 'Test_match_with_incsearch_2', {}) + + call term_sendkeys(buf, "\<CR>") + call StopVimInTerminal(buf) + call delete('XmatchWithIncsearch') +endfunc + +" Test for deleting matches outside of the screen redraw top/bottom lines +" This should cause a redraw of those lines. +func Test_matchdelete_redraw() + new + call setline(1, range(1, 500)) + call cursor(250, 1) + let m1 = matchaddpos('Search', [[250]]) + let m2 = matchaddpos('Search', [[10], [450]]) + redraw! + let m3 = matchaddpos('Search', [[240], [260]]) + call matchdelete(m2) + let m = getmatches() + call assert_equal(2, len(m)) + call assert_equal([250], m[0].pos1) + redraw! + call matchdelete(m1) + call assert_equal(1, len(getmatches())) + bw! +endfunc + +func Test_match_tab_with_linebreak() + CheckRunVimInTerminal + + let lines =<< trim END + set linebreak + call setline(1, "\tix") + call matchadd('ErrorMsg', '\t') + END + call writefile(lines, 'XscriptMatchTabLinebreak') + let buf = RunVimInTerminal('-S XscriptMatchTabLinebreak', #{rows: 10}) + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_match_tab_linebreak', {}) + + call StopVimInTerminal(buf) + call delete('XscriptMatchTabLinebreak') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_matchfuzzy.vim b/src/nvim/testdir/test_matchfuzzy.vim index c836bc87aa..b46550fbc3 100644 --- a/src/nvim/testdir/test_matchfuzzy.vim +++ b/src/nvim/testdir/test_matchfuzzy.vim @@ -6,9 +6,7 @@ source check.vim " Test for matchfuzzy() func Test_matchfuzzy() call assert_fails('call matchfuzzy(10, "abc")', 'E686:') - " Needs v8.2.1183; match the final error that's thrown for now - " call assert_fails('call matchfuzzy(["abc"], [])', 'E730:') - call assert_fails('call matchfuzzy(["abc"], [])', 'E475:') + call assert_fails('call matchfuzzy(["abc"], [])', 'E730:') call assert_fails("let x = matchfuzzy(v:_null_list, 'foo')", 'E686:') call assert_fails('call matchfuzzy(["abc"], v:_null_string)', 'E475:') call assert_equal([], matchfuzzy([], 'abc')) @@ -75,12 +73,9 @@ func Test_matchfuzzy() call assert_fails("let x = matchfuzzy(l, 'day', {'text_cb' : {a, b -> 1}})", 'E119:') call assert_equal([], matchfuzzy(l, 'cam')) " Nvim's callback implementation is different, so E6000 is expected instead, - " but we need v8.2.1183 to assert it " call assert_fails("let x = matchfuzzy(l, 'cam', {'text_cb' : []})", 'E921:') - " call assert_fails("let x = matchfuzzy(l, 'cam', {'text_cb' : []})", 'E6000:') - call assert_fails("let x = matchfuzzy(l, 'cam', {'text_cb' : []})", 'E475:') - " call assert_fails("let x = matchfuzzy(l, 'foo', {'key' : []})", 'E730:') - call assert_fails("let x = matchfuzzy(l, 'foo', {'key' : []})", 'E475:') + call assert_fails("let x = matchfuzzy(l, 'cam', {'text_cb' : []})", 'E6000:') + call assert_fails("let x = matchfuzzy(l, 'foo', {'key' : []})", 'E730:') call assert_fails("let x = matchfuzzy(l, 'cam', v:_null_dict)", 'E715:') call assert_fails("let x = matchfuzzy(l, 'foo', {'key' : v:_null_string})", 'E475:') " Nvim doesn't have null functions @@ -155,12 +150,9 @@ func Test_matchfuzzypos() call assert_fails("let x = matchfuzzypos(l, 'day', {'text_cb' : {a, b -> 1}})", 'E119:') call assert_equal([[], [], []], matchfuzzypos(l, 'cam')) " Nvim's callback implementation is different, so E6000 is expected instead, - " but we need v8.2.1183 to assert it " call assert_fails("let x = matchfuzzypos(l, 'cam', {'text_cb' : []})", 'E921:') - " call assert_fails("let x = matchfuzzypos(l, 'cam', {'text_cb' : []})", 'E6000:') - call assert_fails("let x = matchfuzzypos(l, 'cam', {'text_cb' : []})", 'E475:') - " call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : []})", 'E730:') - call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : []})", 'E475:') + call assert_fails("let x = matchfuzzypos(l, 'cam', {'text_cb' : []})", 'E6000:') + call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : []})", 'E730:') call assert_fails("let x = matchfuzzypos(l, 'cam', v:_null_dict)", 'E715:') call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : v:_null_string})", 'E475:') " Nvim doesn't have null functions diff --git a/src/nvim/testdir/test_menu.vim b/src/nvim/testdir/test_menu.vim index 75992d3313..a1121632e6 100644 --- a/src/nvim/testdir/test_menu.vim +++ b/src/nvim/testdir/test_menu.vim @@ -153,7 +153,7 @@ func Test_menu_errors() call assert_fails('menu Test.Foo.Bar', 'E327:') call assert_fails('cmenu Test.Foo', 'E328:') call assert_fails('emenu x Test.Foo', 'E475:') - call assert_fails('emenu Test.Foo.Bar', 'E334:') + call assert_fails('emenu Test.Foo.Bar', 'E327:') call assert_fails('menutranslate Test', 'E474:') silent! unmenu Foo @@ -252,6 +252,7 @@ func Test_menu_info() nmenu Test.abc <Nop> call assert_equal('<Nop>', menu_info('Test.abc').rhs) call assert_fails('call menu_info([])', 'E730:') + call assert_fails('call menu_info("", [])', 'E730:') nunmenu Test " Test for defining menus in different modes @@ -429,7 +430,7 @@ func Test_menu_special() nunmenu Test.Sign endfunc -" Test for "icon=filname" in a toolbar +" Test for "icon=filename" in a toolbar func Test_menu_icon() CheckFeature toolbar nmenu icon=myicon.xpm Toolbar.Foo :echo "Foo"<CR> @@ -481,6 +482,35 @@ func Test_popup_menu() unmenu PopUp endfunc +" Test for MenuPopup autocommand +func Test_autocmd_MenuPopup() + CheckNotGui + + set mouse=a + set mousemodel=popup + aunmenu * + autocmd MenuPopup * exe printf( + \ 'anoremenu PopUp.Foo <Cmd>let g:res = ["%s", "%s"]<CR>', + \ expand('<afile>'), expand('<amatch>')) + + call feedkeys("\<RightMouse>\<Down>\<CR>", 'tnix') + call assert_equal(['n', 'n'], g:res) + + call feedkeys("v\<RightMouse>\<Down>\<CR>\<Esc>", 'tnix') + call assert_equal(['v', 'v'], g:res) + + call feedkeys("gh\<RightMouse>\<Down>\<CR>\<Esc>", 'tnix') + call assert_equal(['s', 's'], g:res) + + call feedkeys("i\<RightMouse>\<Down>\<CR>\<Esc>", 'tnix') + call assert_equal(['i', 'i'], g:res) + + autocmd! MenuPopup + aunmenu PopUp.Foo + unlet g:res + set mouse& mousemodel& +endfunc + " Test for listing the menus using the :menu command func Test_show_menus() " In the GUI, tear-off menu items are present in the output below diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim index 3a607ff533..bfdebdac79 100644 --- a/src/nvim/testdir/test_messages.vim +++ b/src/nvim/testdir/test_messages.vim @@ -171,6 +171,38 @@ func Test_echospace() set ruler& showcmd& endfunc +func Test_warning_scroll() + CheckRunVimInTerminal + let lines =<< trim END + call test_override('ui_delay', 50) + set noruler + set readonly + undo + END + call writefile(lines, 'XTestWarningScroll', 'D') + let buf = RunVimInTerminal('', #{rows: 8}) + + " When the warning comes from a script, messages are scrolled so that the + " stacktrace is visible. + call term_sendkeys(buf, ":source XTestWarningScroll\n") + " only match the final colon in the line that shows the source + call WaitForAssert({-> assert_match(':$', term_getline(buf, 5))}) + call WaitForAssert({-> assert_equal('line 4:W10: Warning: Changing a readonly file', term_getline(buf, 6))}) + call WaitForAssert({-> assert_equal('Already at oldest change', term_getline(buf, 7))}) + call WaitForAssert({-> assert_equal('Press ENTER or type command to continue', term_getline(buf, 8))}) + call term_sendkeys(buf, "\n") + + " When the warning does not come from a script, messages are not scrolled. + call term_sendkeys(buf, ":enew\n") + call term_sendkeys(buf, ":set readonly\n") + call term_sendkeys(buf, 'u') + call WaitForAssert({-> assert_equal('W10: Warning: Changing a readonly file', term_getline(buf, 8))}) + call WaitForAssert({-> assert_equal('Already at oldest change', term_getline(buf, 8))}) + + " clean up + call StopVimInTerminal(buf) +endfunc + " Test more-prompt (see :help more-prompt). func Test_message_more() CheckRunVimInTerminal @@ -284,6 +316,66 @@ func Test_message_more() call StopVimInTerminal(buf) endfunc +" Test more-prompt scrollback +func Test_message_more_scrollback() + CheckRunVimInTerminal + + let lines =<< trim END + set t_ut= + hi Normal ctermfg=15 ctermbg=0 + for i in range(100) + echo i + endfor + END + call writefile(lines, 'XmoreScrollback', 'D') + let buf = RunVimInTerminal('-S XmoreScrollback', {'rows': 10}) + call VerifyScreenDump(buf, 'Test_more_scrollback_1', {}) + + call term_sendkeys(buf, 'f') + call TermWait(buf) + call term_sendkeys(buf, 'b') + call VerifyScreenDump(buf, 'Test_more_scrollback_2', {}) + + call term_sendkeys(buf, 'q') + call TermWait(buf) + call StopVimInTerminal(buf) +endfunc + +" Test verbose message before echo command +func Test_echo_verbose_system() + CheckRunVimInTerminal + CheckUnix + + let buf = RunVimInTerminal('', {'rows': 10}) + call term_sendkeys(buf, ":4 verbose echo system('seq 20')\<CR>") + " Note that the screendump is filtered to remove the name of the temp file + call VerifyScreenDump(buf, 'Test_verbose_system_1', {}) + + " display a page and go back, results in exactly the same view + call term_sendkeys(buf, ' ') + call TermWait(buf, 50) + call term_sendkeys(buf, 'b') + call VerifyScreenDump(buf, 'Test_verbose_system_1', {}) + + " do the same with 'cmdheight' set to 2 + call term_sendkeys(buf, 'q') + call TermWait(buf) + call term_sendkeys(buf, ":set ch=2\<CR>") + call TermWait(buf) + call term_sendkeys(buf, ":4 verbose echo system('seq 20')\<CR>") + call VerifyScreenDump(buf, 'Test_verbose_system_2', {}) + + call term_sendkeys(buf, ' ') + call TermWait(buf, 50) + call term_sendkeys(buf, 'b') + call VerifyScreenDump(buf, 'Test_verbose_system_2', {}) + + call term_sendkeys(buf, 'q') + call TermWait(buf) + call StopVimInTerminal(buf) +endfunc + + func Test_ask_yesno() CheckRunVimInTerminal let buf = RunVimInTerminal('', {'rows': 6}) @@ -325,14 +417,14 @@ func Test_quit_long_message() let content =<< trim END echom range(9999)->join("\x01") END - call writefile(content, 'Xtest_quit_message') - let buf = RunVimInTerminal('-S Xtest_quit_message', #{rows: 6}) + call writefile(content, 'Xtest_quit_message', 'D') + let buf = RunVimInTerminal('-S Xtest_quit_message', #{rows: 10, wait_for_ruler: 0}) + call WaitForAssert({-> assert_match('^-- More --', term_getline(buf, 10))}) call term_sendkeys(buf, "q") call VerifyScreenDump(buf, 'Test_quit_long_message', {}) " clean up call StopVimInTerminal(buf) - call delete('Xtest_quit_message') endfunc " this was missing a terminating NUL @@ -398,7 +490,7 @@ func Test_cmdheight_zero() " Check change/restore cmdheight when macro call feedkeys("qa", "xt") - call assert_equal(1, &cmdheight) + call assert_equal(0, &cmdheight) call feedkeys("q", "xt") call assert_equal(0, &cmdheight) diff --git a/src/nvim/testdir/test_method.vim b/src/nvim/testdir/test_method.vim index cdf688b857..ca3b736429 100644 --- a/src/nvim/testdir/test_method.vim +++ b/src/nvim/testdir/test_method.vim @@ -35,6 +35,7 @@ func Test_list_method() call assert_equal(v:t_list, l->type()) call assert_equal([1, 2, 3], [1, 1, 2, 3, 3]->uniq()) call assert_fails('eval l->values()', 'E715:') + call assert_fails('echo []->len', 'E107:') endfunc func Test_dict_method() @@ -130,9 +131,9 @@ func Test_method_syntax() eval [1, 2, 3] \ ->sort( \ ) - call assert_fails('eval [1, 2, 3]-> sort()', 'E260:') + call assert_fails('eval [1, 2, 3]-> sort()', 'E15:') call assert_fails('eval [1, 2, 3]->sort ()', 'E274:') - call assert_fails('eval [1, 2, 3]-> sort ()', 'E260:') + call assert_fails('eval [1, 2, 3]-> sort ()', 'E15:') endfunc func Test_method_lambda() @@ -152,6 +153,22 @@ endfunc func Test_method_not_supported() call assert_fails('eval 123->changenr()', 'E276:') + call assert_fails('echo "abc"->invalidfunc()', 'E117:') + " Test for too many or too few arguments to a method + call assert_fails('let n="abc"->len(2)', 'E118:') + call assert_fails('let n=10->setwinvar()', 'E119:') endfunc -" vim: shiftwidth=2 sts=2 expandtab +" Test for passing optional arguments to methods +func Test_method_args() + let v:errors = [] + let n = 10->assert_inrange(1, 5, "Test_assert_inrange") + if v:errors[0] !~ 'Test_assert_inrange' + call assert_report(v:errors[0]) + else + " Test passed + let v:errors = [] + endif +endfunc + +" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim index 8ec408e62e..972397cb91 100644 --- a/src/nvim/testdir/test_mksession.vim +++ b/src/nvim/testdir/test_mksession.vim @@ -851,9 +851,7 @@ func Test_mksession_shortmess_with_A() edit Xtestfile write let fname = swapname('%') - " readblob() needs patch 8.2.2343 - " let cont = readblob(fname) - let cont = readfile(fname, 'B') + let cont = readblob(fname) set sessionoptions-=options mksession Xtestsession bwipe! @@ -942,6 +940,19 @@ func Test_mkvimrc() endfor call s:ClearMappings() + + " the 'pastetoggle', 'wildchar' and 'wildcharm' option values should be + " stored as key names in the vimrc file + set pastetoggle=<F5> + set wildchar=<F6> + set wildcharm=<F7> + call assert_fails('mkvimrc Xtestvimrc') + mkvimrc! Xtestvimrc + call assert_notequal(-1, index(readfile('Xtestvimrc'), 'set pastetoggle=<F5>')) + call assert_notequal(-1, index(readfile('Xtestvimrc'), 'set wildchar=<F6>')) + call assert_notequal(-1, index(readfile('Xtestvimrc'), 'set wildcharm=<F7>')) + set pastetoggle& wildchar& wildcharm& + call delete('Xtestvimrc') endfunc @@ -979,6 +990,38 @@ func Test_altfile() call assert_equal('Xtwoalt', bufname('#')) only bwipe! + call delete('Xtest_altfile') +endfunc + +" Test for creating views with manual folds +func Test_mkview_manual_fold() + call writefile(range(1,10), 'Xfile') + new Xfile + " create recursive folds + 5,6fold + 4,7fold + mkview Xview + normal zE + source Xview + call assert_equal([-1, 4, 4, 4, 4, -1], [foldclosed(3), foldclosed(4), + \ foldclosed(5), foldclosed(6), foldclosed(7), foldclosed(8)]) + " open one level of fold + 4foldopen + mkview! Xview + normal zE + source Xview + call assert_equal([-1, -1, 5, 5, -1, -1], [foldclosed(3), foldclosed(4), + \ foldclosed(5), foldclosed(6), foldclosed(7), foldclosed(8)]) + " open all the folds + %foldopen! + mkview! Xview + normal zE + source Xview + call assert_equal([-1, -1, -1, -1, -1, -1], [foldclosed(3), foldclosed(4), + \ foldclosed(5), foldclosed(6), foldclosed(7), foldclosed(8)]) + call delete('Xfile') + call delete('Xview') + bw! endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_modeline.vim b/src/nvim/testdir/test_modeline.vim index b3fe79f545..613722fdbd 100644 --- a/src/nvim/testdir/test_modeline.vim +++ b/src/nvim/testdir/test_modeline.vim @@ -1,5 +1,7 @@ " Tests for parsing the modeline. +source check.vim + func Test_modeline_invalid() " This was reading allocated memory in the past. call writefile(['vi:0', 'nothing'], 'Xmodeline') @@ -281,6 +283,88 @@ func Test_modeline_fails_modelineexpr() call s:modeline_fails('titlestring', 'titlestring=Something()', 'E992:') endfunc +func Test_modeline_setoption_verbose() + let modeline = &modeline + set modeline + + let lines =<< trim END + 1 vim:ts=2 + 2 two + 3 three + 4 four + 5 five + 6 six + 7 seven + 8 eight + END + call writefile(lines, 'Xmodeline') + edit Xmodeline + let info = split(execute('verbose set tabstop?'), "\n") + call assert_match('^\s*Last set from modeline line 1$', info[-1]) + bwipe! + + let lines =<< trim END + 1 one + 2 two + 3 three + 4 vim:ts=4 + 5 five + 6 six + 7 seven + 8 eight + END + call writefile(lines, 'Xmodeline') + edit Xmodeline + let info = split(execute('verbose set tabstop?'), "\n") + call assert_match('^\s*Last set from modeline line 4$', info[-1]) + bwipe! + + let lines =<< trim END + 1 one + 2 two + 3 three + 4 four + 5 five + 6 six + 7 seven + 8 vim:ts=8 + END + call writefile(lines, 'Xmodeline') + edit Xmodeline + let info = split(execute('verbose set tabstop?'), "\n") + call assert_match('^\s*Last set from modeline line 8$', info[-1]) + bwipe! + + let &modeline = modeline + call delete('Xmodeline') +endfunc + +" Test for the 'modeline' default value in compatible and non-compatible modes +" for root and non-root accounts +func Test_modeline_default() + " set compatible + " call assert_false(&modeline) + set nocompatible + call assert_equal(IsRoot() ? 0 : 1, &modeline) + " set compatible&vi + " call assert_false(&modeline) + set compatible&vim + call assert_equal(IsRoot() ? 0 : 1, &modeline) + set compatible& modeline& +endfunc + +" Some options cannot be set from the modeline when 'diff' option is set +func Test_modeline_diff_buffer() + call writefile(['vim: diff foldmethod=marker wrap'], 'Xfile') + set foldmethod& nowrap + new Xfile + call assert_equal('manual', &foldmethod) + call assert_false(&wrap) + set wrap& + call delete('Xfile') + bw +endfunc + func Test_modeline_disable() set modeline call writefile(['vim: sw=2', 'vim: nomodeline', 'vim: sw=3'], 'Xmodeline_disable') diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 2092b508ea..7c90b444e5 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -3,6 +3,7 @@ source shared.vim source check.vim source view_util.vim +source vim9.vim source screendump.vim func Setup_NewWindow() @@ -35,14 +36,14 @@ func CountSpaces(type, ...) else silent exe "normal! `[v`]y" endif - let g:a=strlen(substitute(@@, '[^ ]', '', 'g')) + let g:a = strlen(substitute(@@, '[^ ]', '', 'g')) let &selection = sel_save let @@ = reg_save endfunc func OpfuncDummy(type, ...) " for testing operatorfunc - let g:opt=&linebreak + let g:opt = &linebreak if a:0 " Invoked from Visual mode, use gv command. silent exe "normal! gvy" @@ -53,7 +54,7 @@ func OpfuncDummy(type, ...) endif " Create a new dummy window new - let g:bufnr=bufnr('%') + let g:bufnr = bufnr('%') endfunc func Test_normal00_optrans() @@ -120,6 +121,39 @@ func Test_normal01_keymodel() call feedkeys("Vkk\<Up>yy", 'tx') call assert_equal(['47', '48', '49', '50'], getreg(0, 0, 1)) + " Test for using special keys to start visual selection + %d + call setline(1, ['red fox tail', 'red fox tail', 'red fox tail']) + set keymodel=startsel + " Test for <S-PageUp> and <S-PageDown> + call cursor(1, 1) + call feedkeys("\<S-PageDown>y", 'xt') + call assert_equal([0, 1, 1, 0], getpos("'<")) + call assert_equal([0, 3, 1, 0], getpos("'>")) + call feedkeys("Gz\<CR>8|\<S-PageUp>y", 'xt') + call assert_equal([0, 2, 1, 0], getpos("'<")) + call assert_equal([0, 3, 8, 0], getpos("'>")) + " Test for <S-C-Home> and <S-C-End> + call cursor(2, 12) + call feedkeys("\<S-C-Home>y", 'xt') + call assert_equal([0, 1, 1, 0], getpos("'<")) + call assert_equal([0, 2, 12, 0], getpos("'>")) + call cursor(1, 4) + call feedkeys("\<S-C-End>y", 'xt') + call assert_equal([0, 1, 4, 0], getpos("'<")) + call assert_equal([0, 3, 13, 0], getpos("'>")) + " Test for <S-C-Left> and <S-C-Right> + call cursor(2, 5) + call feedkeys("\<S-C-Right>y", 'xt') + call assert_equal([0, 2, 5, 0], getpos("'<")) + call assert_equal([0, 2, 9, 0], getpos("'>")) + call cursor(2, 9) + call feedkeys("\<S-C-Left>y", 'xt') + call assert_equal([0, 2, 5, 0], getpos("'<")) + call assert_equal([0, 2, 9, 0], getpos("'>")) + + set keymodel& + " clean up bw! endfunc @@ -140,16 +174,15 @@ func Test_normal03_join() $ :j 10 call assert_equal('100', getline('.')) + call assert_beeps('normal GVJ') " clean up bw! endfunc +" basic filter test func Test_normal04_filter() - " basic filter test " only test on non windows platform - if has('win32') - return - endif + CheckNotMSWindows call Setup_NewWindow() 1 call feedkeys("!!sed -e 's/^/| /'\n", 'tx') @@ -222,12 +255,49 @@ func Test_normal_formatexpr_returns_nonzero() close! endfunc +" Test for using a script-local function for 'formatexpr' +func Test_formatexpr_scriptlocal_func() + func! s:Format() + let g:FormatArgs = [v:lnum, v:count] + endfunc + set formatexpr=s:Format() + call assert_equal(expand('<SID>') .. 'Format()', &formatexpr) + new | only + call setline(1, range(1, 40)) + let g:FormatArgs = [] + normal! 2GVjgq + call assert_equal([2, 2], g:FormatArgs) + bw! + set formatexpr=<SID>Format() + call assert_equal(expand('<SID>') .. 'Format()', &formatexpr) + new | only + call setline(1, range(1, 40)) + let g:FormatArgs = [] + normal! 4GVjgq + call assert_equal([4, 2], g:FormatArgs) + bw! + let &formatexpr = 's:Format()' + new | only + call setline(1, range(1, 40)) + let g:FormatArgs = [] + normal! 6GVjgq + call assert_equal([6, 2], g:FormatArgs) + bw! + let &formatexpr = '<SID>Format()' + new | only + call setline(1, range(1, 40)) + let g:FormatArgs = [] + normal! 8GVjgq + call assert_equal([8, 2], g:FormatArgs) + setlocal formatexpr= + delfunc s:Format + bw! +endfunc + +" basic test for formatprg func Test_normal06_formatprg() - " basic test for formatprg " only test on non windows platform - if has('win32') - return - endif + CheckNotMSWindows " uses sed to number non-empty lines call writefile(['#!/bin/sh', 'sed ''/./=''|sed ''/./{', 'N', 's/\n/ /', '}'''], 'Xsed_format.sh') @@ -240,16 +310,24 @@ func Test_normal06_formatprg() set formatprg=./Xsed_format.sh norm! gggqG call assert_equal(expected, getline(1, '$')) - bw! + %d - 10new call setline(1, text) set formatprg=donothing setlocal formatprg=./Xsed_format.sh norm! gggqG call assert_equal(expected, getline(1, '$')) - bw! + %d + + " Check for the command-line ranges added to 'formatprg' + set formatprg=cat + call setline(1, ['one', 'two', 'three', 'four', 'five']) + call feedkeys('gggqG', 'xt') + call assert_equal('.,$!cat', @:) + call feedkeys('2Ggq2j', 'xt') + call assert_equal('.,.+2!cat', @:) + bw! " clean up set formatprg= setlocal formatprg= @@ -263,18 +341,16 @@ func Test_normal07_internalfmt() 10new call setline(1, list) set tw=12 - norm! gggqG + norm! ggVGgq call assert_equal(['1 2 3', '4 5 6', '7 8 9', '10 11 '], getline(1, '$')) " clean up set tw=0 bw! endfunc +" basic tests for foldopen/folddelete func Test_normal08_fold() - " basic tests for foldopen/folddelete - if !has("folding") - return - endif + CheckFeature folding call Setup_NewWindow() 50 setl foldenable fdm=marker @@ -352,70 +428,6 @@ func Test_normal09a_operatorfunc() norm V10j,, call assert_equal(22, g:a) - " Use a lambda function for 'opfunc' - unmap <buffer> ,, - call cursor(1, 1) - let g:a=0 - nmap <buffer><silent> ,, :set opfunc={type\ ->\ CountSpaces(type)}<CR>g@ - vmap <buffer><silent> ,, :<C-U>call CountSpaces(visualmode(), 1)<CR> - 50 - norm V2j,, - call assert_equal(6, g:a) - norm V,, - call assert_equal(2, g:a) - norm ,,l - call assert_equal(0, g:a) - 50 - exe "norm 0\<c-v>10j2l,," - call assert_equal(11, g:a) - 50 - norm V10j,, - call assert_equal(22, g:a) - - " use a partial function for 'opfunc' - let g:OpVal = 0 - func! Test_opfunc1(x, y, type) - let g:OpVal = a:x + a:y - endfunc - set opfunc=function('Test_opfunc1',\ [5,\ 7]) - normal! g@l - call assert_equal(12, g:OpVal) - " delete the function and try to use g@ - delfunc Test_opfunc1 - call test_garbagecollect_now() - call assert_fails('normal! g@l', 'E117:') - set opfunc= - - " use a funcref for 'opfunc' - let g:OpVal = 0 - func! Test_opfunc2(x, y, type) - let g:OpVal = a:x + a:y - endfunc - set opfunc=funcref('Test_opfunc2',\ [4,\ 3]) - normal! g@l - call assert_equal(7, g:OpVal) - " delete the function and try to use g@ - delfunc Test_opfunc2 - call test_garbagecollect_now() - call assert_fails('normal! g@l', 'E933:') - set opfunc= - - " Try to use a function with two arguments for 'operatorfunc' - let g:OpVal = 0 - func! Test_opfunc3(x, y) - let g:OpVal = 4 - endfunc - set opfunc=Test_opfunc3 - call assert_fails('normal! g@l', 'E119:') - call assert_equal(0, g:OpVal) - set opfunc= - delfunc Test_opfunc3 - unlet g:OpVal - - " Try to use a lambda function with two arguments for 'operatorfunc' - set opfunc={x,\ y\ ->\ 'done'} - call assert_fails('normal! g@l', 'E119:') - " clean up unmap <buffer> ,, set opfunc= @@ -484,6 +496,227 @@ func Test_normal09c_operatorfunc() set operatorfunc= endfunc +" Test for different ways of setting the 'operatorfunc' option +func Test_opfunc_callback() + new + func OpFunc1(callnr, type) + let g:OpFunc1Args = [a:callnr, a:type] + endfunc + func OpFunc2(type) + let g:OpFunc2Args = [a:type] + endfunc + + let lines =<< trim END + #" Test for using a function name + LET &opfunc = 'g:OpFunc2' + LET g:OpFunc2Args = [] + normal! g@l + call assert_equal(['char'], g:OpFunc2Args) + + #" Test for using a function() + set opfunc=function('g:OpFunc1',\ [10]) + LET g:OpFunc1Args = [] + normal! g@l + call assert_equal([10, 'char'], g:OpFunc1Args) + + #" Using a funcref variable to set 'operatorfunc' + VAR Fn = function('g:OpFunc1', [11]) + LET &opfunc = Fn + LET g:OpFunc1Args = [] + normal! g@l + call assert_equal([11, 'char'], g:OpFunc1Args) + + #" Using a string(funcref_variable) to set 'operatorfunc' + LET Fn = function('g:OpFunc1', [12]) + LET &operatorfunc = string(Fn) + LET g:OpFunc1Args = [] + normal! g@l + call assert_equal([12, 'char'], g:OpFunc1Args) + + #" Test for using a funcref() + set operatorfunc=funcref('g:OpFunc1',\ [13]) + LET g:OpFunc1Args = [] + normal! g@l + call assert_equal([13, 'char'], g:OpFunc1Args) + + #" Using a funcref variable to set 'operatorfunc' + LET Fn = funcref('g:OpFunc1', [14]) + LET &opfunc = Fn + LET g:OpFunc1Args = [] + normal! g@l + call assert_equal([14, 'char'], g:OpFunc1Args) + + #" Using a string(funcref_variable) to set 'operatorfunc' + LET Fn = funcref('g:OpFunc1', [15]) + LET &opfunc = string(Fn) + LET g:OpFunc1Args = [] + normal! g@l + call assert_equal([15, 'char'], g:OpFunc1Args) + + #" Test for using a lambda function using set + VAR optval = "LSTART a LMIDDLE OpFunc1(16, a) LEND" + LET optval = substitute(optval, ' ', '\\ ', 'g') + exe "set opfunc=" .. optval + LET g:OpFunc1Args = [] + normal! g@l + call assert_equal([16, 'char'], g:OpFunc1Args) + + #" Test for using a lambda function using LET + LET &opfunc = LSTART a LMIDDLE OpFunc1(17, a) LEND + LET g:OpFunc1Args = [] + normal! g@l + call assert_equal([17, 'char'], g:OpFunc1Args) + + #" Set 'operatorfunc' to a string(lambda expression) + LET &opfunc = 'LSTART a LMIDDLE OpFunc1(18, a) LEND' + LET g:OpFunc1Args = [] + normal! g@l + call assert_equal([18, 'char'], g:OpFunc1Args) + + #" Set 'operatorfunc' to a variable with a lambda expression + VAR Lambda = LSTART a LMIDDLE OpFunc1(19, a) LEND + LET &opfunc = Lambda + LET g:OpFunc1Args = [] + normal! g@l + call assert_equal([19, 'char'], g:OpFunc1Args) + + #" Set 'operatorfunc' to a string(variable with a lambda expression) + LET Lambda = LSTART a LMIDDLE OpFunc1(20, a) LEND + LET &opfunc = string(Lambda) + LET g:OpFunc1Args = [] + normal! g@l + call assert_equal([20, 'char'], g:OpFunc1Args) + + #" Try to use 'operatorfunc' after the function is deleted + func g:TmpOpFunc1(type) + let g:TmpOpFunc1Args = [21, a:type] + endfunc + LET &opfunc = function('g:TmpOpFunc1') + delfunc g:TmpOpFunc1 + call test_garbagecollect_now() + LET g:TmpOpFunc1Args = [] + call assert_fails('normal! g@l', 'E117:') + call assert_equal([], g:TmpOpFunc1Args) + + #" Try to use a function with two arguments for 'operatorfunc' + func g:TmpOpFunc2(x, y) + let g:TmpOpFunc2Args = [a:x, a:y] + endfunc + set opfunc=TmpOpFunc2 + LET g:TmpOpFunc2Args = [] + call assert_fails('normal! g@l', 'E119:') + call assert_equal([], g:TmpOpFunc2Args) + delfunc TmpOpFunc2 + + #" Try to use a lambda function with two arguments for 'operatorfunc' + LET &opfunc = LSTART a, b LMIDDLE OpFunc1(22, b) LEND + LET g:OpFunc1Args = [] + call assert_fails('normal! g@l', 'E119:') + call assert_equal([], g:OpFunc1Args) + + #" Test for clearing the 'operatorfunc' option + set opfunc='' + set opfunc& + call assert_fails("set opfunc=function('abc')", "E700:") + call assert_fails("set opfunc=funcref('abc')", "E700:") + + #" set 'operatorfunc' to a non-existing function + LET &opfunc = function('g:OpFunc1', [23]) + call assert_fails("set opfunc=function('NonExistingFunc')", 'E700:') + call assert_fails("LET &opfunc = function('NonExistingFunc')", 'E700:') + LET g:OpFunc1Args = [] + normal! g@l + call assert_equal([23, 'char'], g:OpFunc1Args) + END + call CheckTransLegacySuccess(lines) + + " Test for using a script-local function name + func s:OpFunc3(type) + let g:OpFunc3Args = [a:type] + endfunc + set opfunc=s:OpFunc3 + let g:OpFunc3Args = [] + normal! g@l + call assert_equal(['char'], g:OpFunc3Args) + + let &opfunc = 's:OpFunc3' + let g:OpFunc3Args = [] + normal! g@l + call assert_equal(['char'], g:OpFunc3Args) + delfunc s:OpFunc3 + + " Using Vim9 lambda expression in legacy context should fail + " set opfunc=(a)\ =>\ OpFunc1(24,\ a) + let g:OpFunc1Args = [] + " call assert_fails('normal! g@l', 'E117:') + call assert_equal([], g:OpFunc1Args) + + " set 'operatorfunc' to a partial with dict. This used to cause a crash. + func SetOpFunc() + let operator = {'execute': function('OperatorExecute')} + let &opfunc = operator.execute + endfunc + func OperatorExecute(_) dict + endfunc + call SetOpFunc() + call test_garbagecollect_now() + set operatorfunc= + delfunc SetOpFunc + delfunc OperatorExecute + + " Vim9 tests + let lines =<< trim END + vim9script + + def g:Vim9opFunc(val: number, type: string): void + g:OpFunc1Args = [val, type] + enddef + + # Test for using a def function with opfunc + set opfunc=function('g:Vim9opFunc',\ [60]) + g:OpFunc1Args = [] + normal! g@l + assert_equal([60, 'char'], g:OpFunc1Args) + + # Test for using a global function name + &opfunc = g:OpFunc2 + g:OpFunc2Args = [] + normal! g@l + assert_equal(['char'], g:OpFunc2Args) + bw! + + # Test for using a script-local function name + def s:LocalOpFunc(type: string): void + g:LocalOpFuncArgs = [type] + enddef + &opfunc = s:LocalOpFunc + g:LocalOpFuncArgs = [] + normal! g@l + assert_equal(['char'], g:LocalOpFuncArgs) + bw! + END + call CheckScriptSuccess(lines) + + " setting 'opfunc' to a script local function outside of a script context + " should fail + let cleanup =<< trim END + call writefile([execute('messages')], 'Xtest.out') + qall + END + call writefile(cleanup, 'Xverify.vim') + call RunVim([], [], "-c \"set opfunc=s:abc\" -S Xverify.vim") + call assert_match('E81: Using <SID> not in a', readfile('Xtest.out')[0]) + call delete('Xtest.out') + call delete('Xverify.vim') + + " cleanup + set opfunc& + delfunc OpFunc1 + delfunc OpFunc2 + unlet g:OpFunc1Args g:OpFunc2Args + %bw! +endfunc + func Test_normal10_expand() " Test for expand() 10new @@ -506,6 +739,14 @@ func Test_normal10_expand() call assert_equal(expected[i], expand('<cexpr>'), 'i == ' . i) endfor + " Test for <cexpr> in state.val and ptr->val + call setline(1, 'x = state.val;') + call cursor(1, 10) + call assert_equal('state.val', expand('<cexpr>')) + call setline(1, 'x = ptr->val;') + call cursor(1, 9) + call assert_equal('ptr->val', expand('<cexpr>')) + if executable('echo') " Test expand(`...`) i.e. backticks command expansion. " MS-Windows has a trailing space. @@ -514,11 +755,25 @@ func Test_normal10_expand() " Test expand(`=...`) i.e. backticks expression expansion call assert_equal('5', expand('`=2+3`')) + call assert_equal('3.14', expand('`=3.14`')) " clean up bw! endfunc +" Test for expand() in latin1 encoding +func Test_normal_expand_latin1() + new + let save_enc = &encoding + " set encoding=latin1 + call setline(1, 'val = item->color;') + call cursor(1, 11) + call assert_equal('color', expand("<cword>")) + call assert_equal('item->color', expand("<cexpr>")) + let &encoding = save_enc + bw! +endfunc + func Test_normal11_showcmd() " test for 'showcmd' 10new @@ -543,6 +798,13 @@ func Test_normal11_showcmd() redraw! call assert_match('1-3$', Screenline(&lines)) call feedkeys("v", 'xt') + " test for visually selecting the end of line + call setline(1, ["foobar"]) + call feedkeys("$vl", 'xt') + redraw! + call assert_match('2$', Screenline(&lines)) + call feedkeys("y", 'xt') + call assert_equal("r\n", @") bw! endfunc @@ -1024,6 +1286,22 @@ func Test_vert_scroll_cmds() close! endfunc +func Test_scroll_in_ex_mode() + " This was using invalid memory because w_botline was invalid. + let lines =<< trim END + diffsplit + norm os00( + call writefile(['done'], 'Xdone') + qa! + END + call writefile(lines, 'Xscript') + call assert_equal(1, RunVim([], [], '--clean -X -Z -e -s -S Xscript')) + call assert_equal(['done'], readfile('Xdone')) + + call delete('Xscript') + call delete('Xdone') +endfunc + " Test for the 'sidescroll' option func Test_sidescroll_opt() new @@ -1431,10 +1709,8 @@ func Test_normal18_z_fold() endfunc func Test_normal20_exmode() - if !has("unix") - " Reading from redirected file doesn't work on MS-Windows - return - endif + " Reading from redirected file doesn't work on MS-Windows + CheckNotMSWindows call writefile(['1a', 'foo', 'bar', '.', 'w! Xfile2', 'q!'], 'Xscript') call writefile(['1', '2'], 'Xfile') call system(GetVimCommand() .. ' -e -s < Xscript Xfile') @@ -1455,6 +1731,7 @@ func Test_normal21_nv_hat() edit Xfoo | %bw call assert_fails(':buffer #', 'E86') call assert_fails(':execute "normal! \<C-^>"', 'E23') + call assert_fails("normal i\<C-R>#", 'E23:') " Test for the expected behavior when switching between two named buffers. edit Xfoo | edit Xbar @@ -1586,7 +1863,7 @@ func Test_normal23_K() call setline(1, '---') call assert_fails('normal! ggv2lK', 'E349:') call setline(1, ['abc', 'xyz']) - call assert_fails("normal! gg2lv2h\<C-]>", 'E426:') + call assert_fails("normal! gg2lv2h\<C-]>", 'E433:') call assert_beeps("normal! ggVjK") " clean up @@ -2061,6 +2338,16 @@ func Test_normal30_changecase() call assert_equal(['aaaaaa', 'AAAAaa'], getline(1, 2)) set whichwrap& + " try changing the case with a double byte encoding (DBCS) + %bw! + let enc = &enc + " set encoding=cp932 + call setline(1, "\u8470") + normal ~ + normal gU$gu$gUgUg~g~gugu + call assert_equal("\u8470", getline(1)) + let &encoding = enc + " clean up bw! endfunc @@ -2152,6 +2439,19 @@ func Test_normal31_r_cmd() " r command should fail in operator pending mode call assert_beeps('normal! cr') + " replace a tab character in visual mode + %d + call setline(1, ["a\tb", "c\td", "e\tf"]) + normal gglvjjrx + call assert_equal(['axx', 'xxx', 'xxf'], getline(1, '$')) + + " replace with a multibyte character (with multiple composing characters) + %d + new + call setline(1, 'aaa') + exe "normal $ra\u0328\u0301" + call assert_equal("aaa\u0328\u0301", getline(1)) + " clean up set noautoindent bw! @@ -2176,9 +2476,7 @@ endfunc " Test for g`, g;, g,, g&, gv, gk, gj, gJ, g0, g^, g_, gm, g$, gM, g CTRL-G, " gi and gI commands func Test_normal33_g_cmd2() - if !has("jumplist") - return - endif + CheckFeature jumplist call Setup_NewWindow() " Test for g` clearjumps @@ -2637,7 +2935,6 @@ endfunc " Test for cw cW ce func Test_normal39_cw() " Test for cw and cW on whitespace - " and cpo+=w setting new set tw=0 call append(0, 'here are some words') @@ -2645,14 +2942,6 @@ func Test_normal39_cw() call assert_equal('hereZZZare some words', getline('.')) norm! 1gg0elcWYYY call assert_equal('hereZZZareYYYsome words', getline('.')) - " Nvim: no "w" flag in 'cpoptions'. - " set cpo+=w - " call setline(1, 'here are some words') - " norm! 1gg0elcwZZZ - " call assert_equal('hereZZZ are some words', getline('.')) - " norm! 1gg2elcWYYY - " call assert_equal('hereZZZ areYYY some words', getline('.')) - set cpo-=w norm! 2gg0cwfoo call assert_equal('foo', getline('.')) @@ -2812,24 +3101,6 @@ func Test_normal47_visual_buf_wipe() set nomodified endfunc -func Test_normal47_autocmd() - " disabled, does not seem to be possible currently - throw "Skipped: not possible to test cursorhold autocmd while waiting for input in normal_cmd" - new - call append(0, repeat('-',20)) - au CursorHold * call feedkeys('2l', '') - 1 - set updatetime=20 - " should delete 12 chars (d12l) - call feedkeys('d1', '!') - call assert_equal('--------', getline(1)) - - " clean up - au! CursorHold - set updatetime=4000 - bw! -endfunc - func Test_normal48_wincmd() new exe "norm! \<c-w>c" @@ -2847,9 +3118,8 @@ func Test_normal49_counts() endfunc func Test_normal50_commandline() - if !has("timers") || !has("cmdline_hist") - return - endif + CheckFeature timers + CheckFeature cmdline_hist func! DoTimerWork(id) call assert_equal('[Command Line]', bufname('')) " should fail, with E11, but does fail with E23? @@ -2878,9 +3148,7 @@ func Test_normal50_commandline() endfunc func Test_normal51_FileChangedRO() - if !has("autocmd") - return - endif + CheckFeature autocmd call writefile(['foo'], 'Xreadonly.log') new Xreadonly.log setl ro @@ -2895,9 +3163,7 @@ func Test_normal51_FileChangedRO() endfunc func Test_normal52_rl() - if !has("rightleft") - return - endif + CheckFeature rightleft new call setline(1, 'abcde fghij klmnopq') norm! 1gg$ @@ -2929,22 +3195,6 @@ func Test_normal52_rl() bw! endfunc -func Test_normal53_digraph() - if !has('digraphs') - return - endif - new - call setline(1, 'abcdefgh|') - exe "norm! 1gg0f\<c-k>!!" - call assert_equal(9, col('.')) - set cpo+=D - exe "norm! 1gg0f\<c-k>!!" - call assert_equal(1, col('.')) - - set cpo-=D - bw! -endfunc - func Test_normal54_Ctrl_bsl() new call setline(1, 'abcdefghijklmn') @@ -3265,46 +3515,6 @@ func Test_normal_gk_gj() set cpoptions& number& numberwidth& wrap& endfunc -" Test for cursor movement with '-' in 'cpoptions' -func Test_normal_cpo_minus() - throw 'Skipped: Nvim does not support cpoptions flag "-"' - new - call setline(1, ['foo', 'bar', 'baz']) - let save_cpo = &cpo - set cpo+=- - call assert_beeps('normal 10j') - call assert_equal(1, line('.')) - normal G - call assert_beeps('normal 10k') - call assert_equal(3, line('.')) - call assert_fails(10, 'E16:') - let &cpo = save_cpo - close! -endfunc - -" Test for displaying dollar when changing text ('$' flag in 'cpoptions') -func Test_normal_cpo_dollar() - throw 'Skipped: use test/functional/legacy/cpoptions_spec.lua' - new - let g:Line = '' - func SaveFirstLine() - let g:Line = Screenline(1) - return '' - endfunc - inoremap <expr> <buffer> <F2> SaveFirstLine() - call test_override('redraw_flag', 1) - set cpo+=$ - call setline(1, 'one two three') - redraw! - exe "normal c2w\<F2>vim" - call assert_equal('one tw$ three', g:Line) - call assert_equal('vim three', getline(1)) - set cpo-=$ - call test_override('ALL', 0) - delfunc SaveFirstLine - %bw! -endfunc - " Test for using : to run a multi-line Ex command in operator pending mode func Test_normal_yank_with_excmd() new @@ -3369,6 +3579,31 @@ func Test_normal_colon_op() close! endfunc +" Test for d and D commands +func Test_normal_delete_cmd() + new + " D in an empty line + call setline(1, '') + normal D + call assert_equal('', getline(1)) + " D in an empty line in virtualedit mode + set virtualedit=all + normal D + call assert_equal('', getline(1)) + set virtualedit& + " delete to a readonly register + call setline(1, ['abcd']) + call assert_beeps('normal ":d2l') + + " D and d with 'nomodifiable' + call setline(1, ['abcd']) + setlocal nomodifiable + call assert_fails('normal D', 'E21:') + call assert_fails('normal d$', 'E21:') + + close! +endfunc + " Test for deleting or changing characters across lines with 'whichwrap' " containing 's'. Should count <EOL> as one character. func Test_normal_op_across_lines() @@ -3476,6 +3711,27 @@ func Test_normal_percent_jump() close! endfunc +" Test for << and >> commands to shift text by 'shiftwidth' +func Test_normal_shift_rightleft() + new + call setline(1, ['one', '', "\t", ' two', "\tthree", ' four']) + set shiftwidth=2 tabstop=8 + normal gg6>> + call assert_equal([' one', '', "\t ", ' two', "\t three", "\tfour"], + \ getline(1, '$')) + normal ggVG2>> + call assert_equal([' one', '', "\t ", "\ttwo", + \ "\t three", "\t four"], getline(1, '$')) + normal gg6<< + call assert_equal([' one', '', "\t ", ' two', "\t three", + \ "\t four"], getline(1, '$')) + normal ggVG2<< + call assert_equal(['one', '', "\t", ' two', "\tthree", ' four'], + \ getline(1, '$')) + set shiftwidth& tabstop& + bw! +endfunc + " Some commands like yy, cc, dd, >>, << and !! accept a count after " typing the first letter of the command. func Test_normal_count_after_operator() @@ -3539,4 +3795,37 @@ func Test_normal_count_out_of_range() bwipe! endfunc +" Test that mouse shape is restored to Normal mode after failed "c" operation. +func Test_mouse_shape_after_failed_change() + CheckFeature mouseshape + CheckCanRunGui + + let lines =<< trim END + set mouseshape+=o:busy + setlocal nomodifiable + let g:mouse_shapes = [] + + func SaveMouseShape(timer) + let g:mouse_shapes += [getmouseshape()] + endfunc + + func SaveAndQuit(timer) + call writefile(g:mouse_shapes, 'Xmouseshapes') + quit + endfunc + + call timer_start(50, {_ -> feedkeys('c')}) + call timer_start(100, 'SaveMouseShape') + call timer_start(150, {_ -> feedkeys('c')}) + call timer_start(200, 'SaveMouseShape') + call timer_start(250, 'SaveAndQuit') + END + call writefile(lines, 'Xmouseshape.vim', 'D') + call RunVim([], [], "-g -S Xmouseshape.vim") + sleep 300m + call assert_equal(['busy', 'arrow'], readfile('Xmouseshapes')) + + call delete('Xmouseshapes') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index fdfc1c0f89..f51de94bac 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -22,6 +22,21 @@ func Test_whichwrap() set whichwrap=h,h,h call assert_equal('h', &whichwrap) + " For compatibility with Vim 3.0 and before, number values are also + " supported for 'whichwrap' + set whichwrap=1 + call assert_equal('b', &whichwrap) + set whichwrap=2 + call assert_equal('s', &whichwrap) + set whichwrap=4 + call assert_equal('h,l', &whichwrap) + set whichwrap=8 + call assert_equal('<,>', &whichwrap) + set whichwrap=16 + call assert_equal('[,]', &whichwrap) + set whichwrap=31 + call assert_equal('b,s,h,l,<,>,[,]', &whichwrap) + set whichwrap& endfunc @@ -142,6 +157,20 @@ func Test_path_keep_commas() set path& endfunc +func Test_path_too_long() + exe 'set path=' .. repeat('x', 10000) + call assert_fails('find x', 'E854:') + set path& +endfunc + +func Test_signcolumn() + CheckFeature signs + call assert_equal("auto", &signcolumn) + set signcolumn=yes + set signcolumn=no + call assert_fails('set signcolumn=nope') +endfunc + func Test_filetype_valid() set ft=valid_name call assert_equal("valid_name", &filetype) @@ -234,6 +263,7 @@ func Test_complete() new call feedkeys("i\<C-N>\<Esc>", 'xt') bwipe! + call assert_fails('set complete=ix', 'E535:') set complete& endfun @@ -247,7 +277,7 @@ func Test_set_completion() call feedkeys(":setglobal di\<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"setglobal dictionary diff diffexpr diffopt digraph directory display', @:) - " Expand boolan options. When doing :set no<Tab> + " Expand boolean options. When doing :set no<Tab> " vim displays the options names without "no" but completion uses "no...". call feedkeys(":set nodi\<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"set nodiff digraph', @:) @@ -266,6 +296,15 @@ func Test_set_completion() call feedkeys(":set fileencodings:\<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"set fileencodings:ucs-bom,utf-8,default,latin1', @:) + " Expand key codes. + " call feedkeys(":set <H\<C-A>\<C-B>\"\<CR>", 'tx') + " call assert_equal('"set <Help> <Home>', @:) + + " Expand terminal options. + " call feedkeys(":set t_A\<C-A>\<C-B>\"\<CR>", 'tx') + " call assert_equal('"set t_AB t_AF t_AU t_AL', @:) + " call assert_fails('call feedkeys(":set <t_afoo>=\<C-A>\<CR>", "xt")', 'E474:') + " Expand directories. call feedkeys(":set cdpath=./\<C-A>\<C-B>\"\<CR>", 'tx') call assert_match('./samples/ ', @:) @@ -361,10 +400,20 @@ func Test_set_errors() call assert_fails('set winminwidth=10 winwidth=9', 'E592:') call assert_fails("set showbreak=\x01", 'E595:') call assert_fails('set t_foo=', 'E846:') + call assert_fails('set tabstop??', 'E488:') + call assert_fails('set wrapscan!!', 'E488:') + call assert_fails('set tabstop&&', 'E488:') + call assert_fails('set wrapscan<<', 'E488:') + call assert_fails('set wrapscan=1', 'E474:') + call assert_fails('set autoindent@', 'E488:') + call assert_fails('set wildchar=<abc>', 'E474:') + call assert_fails('set cmdheight=1a', 'E521:') + call assert_fails('set invcmdheight', 'E474:') if has('python') || has('python3') call assert_fails('set pyxversion=6', 'E474:') endif call assert_fails("let &tabstop='ab'", 'E521:') + call assert_fails('set spellcapcheck=%\\(', 'E54:') call assert_fails('set sessionoptions=curdir,sesdir', 'E474:') call assert_fails('set foldmarker={{{,', 'E474:') call assert_fails('set sessionoptions=sesdir,curdir', 'E474:') @@ -384,6 +433,7 @@ func Test_set_errors() set nomodifiable call assert_fails('set fileencoding=latin1', 'E21:') set modifiable& + " call assert_fails('set t_#-&', 'E522:') endfunc func CheckWasSet(name) @@ -397,9 +447,8 @@ endfunc " Must be executed before other tests that set 'term'. func Test_000_term_option_verbose() - if has('nvim') || has('gui_running') - return - endif + throw "Skipped: Nvim does not support setting 'term'" + CheckNotGui call CheckWasNotSet('t_cm') @@ -431,32 +480,37 @@ func Test_copy_context() endfunc func Test_set_ttytype() - " Nvim does not support 'ttytype'. - if !has('nvim') && !has('gui_running') && has('unix') - " Setting 'ttytype' used to cause a double-free when exiting vim and - " when vim is compiled with -DEXITFREE. - set ttytype=ansi - call assert_equal('ansi', &ttytype) - call assert_equal(&ttytype, &term) - set ttytype=xterm - call assert_equal('xterm', &ttytype) - call assert_equal(&ttytype, &term) - try - set ttytype= - call assert_report('set ttytype= did not fail') - catch /E529/ - endtry - - " Some systems accept any terminal name and return dumb settings, - " check for failure of finding the entry and for missing 'cm' entry. - try - set ttytype=xxx - call assert_report('set ttytype=xxx did not fail') - catch /E522\|E437/ - endtry - - set ttytype& - call assert_equal(&ttytype, &term) + throw "Skipped: Nvim does not support 'ttytype'" + CheckUnix + CheckNotGui + + " Setting 'ttytype' used to cause a double-free when exiting vim and + " when vim is compiled with -DEXITFREE. + set ttytype=ansi + call assert_equal('ansi', &ttytype) + call assert_equal(&ttytype, &term) + set ttytype=xterm + call assert_equal('xterm', &ttytype) + call assert_equal(&ttytype, &term) + try + set ttytype= + call assert_report('set ttytype= did not fail') + catch /E529/ + endtry + + " Some systems accept any terminal name and return dumb settings, + " check for failure of finding the entry and for missing 'cm' entry. + try + set ttytype=xxx + call assert_report('set ttytype=xxx did not fail') + catch /E522\|E437/ + endtry + + set ttytype& + call assert_equal(&ttytype, &term) + + if has('gui') && !has('gui_running') + call assert_fails('set term=gui', 'E531:') endif endfunc @@ -774,7 +828,13 @@ func Test_shell() CheckUnix let save_shell = &shell set shell= - call assert_fails('shell', 'E91:') + let caught_e91 = 0 + try + shell + catch /E91:/ + let caught_e91 = 1 + endtry + call assert_equal(1, caught_e91) let &shell = save_shell endfunc @@ -833,6 +893,107 @@ func Test_debug_option() set debug& endfunc +" Test for the default CDPATH option +func Test_opt_default_cdpath() + let after =<< trim [CODE] + call assert_equal(',/path/to/dir1,/path/to/dir2', &cdpath) + call writefile(v:errors, 'Xtestout') + qall + [CODE] + if has('unix') + let $CDPATH='/path/to/dir1:/path/to/dir2' + else + let $CDPATH='/path/to/dir1;/path/to/dir2' + endif + if RunVim([], after, '') + call assert_equal([], readfile('Xtestout')) + call delete('Xtestout') + endif +endfunc + +" Test for setting keycodes using set +func Test_opt_set_keycode() + call assert_fails('set <t_k1=l', 'E474:') + call assert_fails('set <Home=l', 'E474:') + set <t_k9>=abcd + " call assert_equal('abcd', &t_k9) + set <t_k9>& + set <F9>=xyz + " call assert_equal('xyz', &t_k9) + set <t_k9>& +endfunc + +" Test for changing options in a sandbox +func Test_opt_sandbox() + for opt in ['backupdir', 'cdpath', 'exrc'] + call assert_fails('sandbox set ' .. opt .. '?', 'E48:') + endfor +endfunc + +" Test for setting an option with local value to global value +func Test_opt_local_to_global() + setglobal equalprg=gprg + setlocal equalprg=lprg + call assert_equal('gprg', &g:equalprg) + call assert_equal('lprg', &l:equalprg) + call assert_equal('lprg', &equalprg) + set equalprg< + call assert_equal('', &l:equalprg) + call assert_equal('gprg', &equalprg) + setglobal equalprg=gnewprg + setlocal equalprg=lnewprg + setlocal equalprg< + call assert_equal('gnewprg', &l:equalprg) + call assert_equal('gnewprg', &equalprg) + set equalprg& + + " Test for setting the global/local value of a boolean option + setglobal autoread + setlocal noautoread + call assert_false(&autoread) + set autoread< + call assert_true(&autoread) + setglobal noautoread + setlocal autoread + setlocal autoread< + call assert_false(&autoread) + set autoread& +endfunc + +func Test_set_in_sandbox() + " Some boolean options cannot be set in sandbox, some can. + call assert_fails('sandbox set modelineexpr', 'E48:') + sandbox set number + call assert_true(&number) + set number& + + " Some boolean options cannot be set in sandbox, some can. + if has('python') || has('python3') + call assert_fails('sandbox set pyxversion=3', 'E48:') + endif + sandbox set tabstop=4 + call assert_equal(4, &tabstop) + set tabstop& + + " Some string options cannot be set in sandbox, some can. + call assert_fails('sandbox set backupdir=/tmp', 'E48:') + sandbox set filetype=perl + call assert_equal('perl', &filetype) + set filetype& +endfunc + +" Test for incrementing, decrementing and multiplying a number option value +func Test_opt_num_op() + set shiftwidth=4 + set sw+=2 + call assert_equal(6, &sw) + set sw-=2 + call assert_equal(4, &sw) + set sw^=2 + call assert_equal(8, &sw) + set shiftwidth& +endfunc + " Test for setting option values using v:false and v:true func Test_opt_boolean() set number& @@ -969,6 +1130,93 @@ func Test_opt_reset_scroll() call delete('Xscroll') endfunc +" Check that VIM_POSIX env variable influences default value of 'cpo' and 'shm' +func Test_VIM_POSIX() + throw 'Skipped: Nvim does not support $VIM_POSIX' + let saved_VIM_POSIX = getenv("VIM_POSIX") + + call setenv('VIM_POSIX', "1") + let after =<< trim [CODE] + call writefile([&cpo, &shm], 'X_VIM_POSIX') + qall + [CODE] + if RunVim([], after, '') + call assert_equal(['aAbBcCdDeEfFgHiIjJkKlLmMnoOpPqrRsStuvwWxXyZ$!%*-+<>#{|&/\.;', + \ 'AS'], readfile('X_VIM_POSIX')) + endif + + call setenv('VIM_POSIX', v:null) + let after =<< trim [CODE] + call writefile([&cpo, &shm], 'X_VIM_POSIX') + qall + [CODE] + if RunVim([], after, '') + call assert_equal(['aAbBcCdDeEfFgHiIjJkKlLmMnoOpPqrRsStuvwWxXyZ$!%*-+<>;', + \ 'S'], readfile('X_VIM_POSIX')) + endif + + call delete('X_VIM_POSIX') + call setenv('VIM_POSIX', saved_VIM_POSIX) +endfunc + +" Test for setting an option to a Vi or Vim default +func Test_opt_default() + throw 'Skipped: Nvim has different defaults' + set formatoptions&vi + call assert_equal('vt', &formatoptions) + set formatoptions&vim + call assert_equal('tcq', &formatoptions) +endfunc + +" Test for the 'cmdheight' option +func Test_cmdheight() + %bw! + let ht = &lines + set cmdheight=9999 + call assert_equal(1, winheight(0)) + call assert_equal(ht - 1, &cmdheight) + set cmdheight& +endfunc + +" To specify a control character as a option value, '^' can be used +func Test_opt_control_char() + set wildchar=^v + call assert_equal("\<C-V>", nr2char(&wildchar)) + set wildcharm=^r + call assert_equal("\<C-R>", nr2char(&wildcharm)) + " Bug: This doesn't work for the 'cedit' and 'termwinkey' options + set wildchar& wildcharm& +endfunc + +" Test for the 'errorbells' option +func Test_opt_errorbells() + set errorbells + call assert_beeps('s/a1b2/x1y2/') + set noerrorbells +endfunc + +func Test_opt_scrolljump() + help + resize 10 + + " Test with positive 'scrolljump'. + set scrolljump=2 + norm! Lj + call assert_equal({'lnum':11, 'leftcol':0, 'col':0, 'topfill':0, + \ 'topline':3, 'coladd':0, 'skipcol':0, 'curswant':0}, + \ winsaveview()) + + " Test with negative 'scrolljump' (percentage of window height). + set scrolljump=-40 + norm! ggLj + call assert_equal({'lnum':11, 'leftcol':0, 'col':0, 'topfill':0, + \ 'topline':5, 'coladd':0, 'skipcol':0, 'curswant':0}, + \ winsaveview()) + + set scrolljump& + bw +endfunc + " Test for the 'cdhome' option func Test_opt_cdhome() if has('unix') || has('vms') @@ -990,6 +1238,30 @@ func Test_opt_cdhome() set cdhome& endfunc +func Test_set_completion_2() + CheckOption termguicolors + + " Test default option completion + set wildoptions= + call feedkeys(":set termg\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"set termguicolors', @:) + + call feedkeys(":set notermg\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"set notermguicolors', @:) + + " Test fuzzy option completion + set wildoptions=fuzzy + call feedkeys(":set termg\<C-A>\<C-B>\"\<CR>", 'tx') + " Nvim doesn't have 'termencoding' + " call assert_equal('"set termguicolors termencoding', @:) + call assert_equal('"set termguicolors', @:) + + call feedkeys(":set notermg\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"set notermguicolors', @:) + + set wildoptions= +endfunc + func Test_switchbuf_reset() set switchbuf=useopen sblast @@ -1003,4 +1275,30 @@ func Test_switchbuf_reset() only! endfunc +" :set empty string for global 'keywordprg' falls back to ":help" +func Test_keywordprg_empty() + let k = &keywordprg + set keywordprg=man + call assert_equal('man', &keywordprg) + set keywordprg= + call assert_equal(':help', &keywordprg) + set keywordprg=man + call assert_equal('man', &keywordprg) + call assert_equal("\n keywordprg=:help", execute('set kp= kp?')) + let &keywordprg = k +endfunc + +" check that the very first buffer created does not have 'endoffile' set +func Test_endoffile_default() + let after =<< trim [CODE] + call writefile([execute('set eof?')], 'Xtestout') + qall! + [CODE] + if RunVim([], after, '') + call assert_equal(["\nnoendoffile"], readfile('Xtestout')) + endif + call delete('Xtestout') +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_partial.vim b/src/nvim/testdir/test_partial.vim index 8c90f21600..3020668f1b 100644 --- a/src/nvim/testdir/test_partial.vim +++ b/src/nvim/testdir/test_partial.vim @@ -82,6 +82,9 @@ func Test_partial_dict() let dict = {"tr": function('tr', ['hello', 'h', 'H'])} call assert_equal("Hello", dict.tr()) + + call assert_fails("let F=function('setloclist', 10)", "E923:") + call assert_fails("let F=function('setloclist', [], [])", "E922:") endfunc func Test_partial_implicit() @@ -354,3 +357,5 @@ func Test_compare_partials() call assert_true(F1 isnot# F1d1) " Partial /= non-partial call assert_true(d1.f1 isnot# d1.f1) " handle_subscript creates new partial each time endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_paste.vim b/src/nvim/testdir/test_paste.vim new file mode 100644 index 0000000000..dad3c2c6a0 --- /dev/null +++ b/src/nvim/testdir/test_paste.vim @@ -0,0 +1,76 @@ + +" Test for 'pastetoggle' +func Test_pastetoggle() + new + set pastetoggle=<F4> + set nopaste + call feedkeys("iHello\<F4>", 'xt') + call assert_true(&paste) + call feedkeys("i\<F4>", 'xt') + call assert_false(&paste) + call assert_equal('Hello', getline(1)) + " command-line completion for 'pastetoggle' value + call feedkeys(":set pastetoggle=\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"set pastetoggle=<F4>', @:) + set pastetoggle& + bwipe! +endfunc + +" Test for restoring option values when 'paste' is disabled +func Test_paste_opt_restore() + set autoindent expandtab ruler showmatch + if has('rightleft') + set revins hkmap + endif + set smarttab softtabstop=3 textwidth=27 wrapmargin=12 + if has('vartabs') + set varsofttabstop=10,20 + endif + + " enabling 'paste' should reset the above options + set paste + call assert_false(&autoindent) + call assert_false(&expandtab) + if has('rightleft') + call assert_false(&revins) + call assert_false(&hkmap) + endif + call assert_false(&ruler) + call assert_false(&showmatch) + call assert_false(&smarttab) + call assert_equal(0, &softtabstop) + call assert_equal(0, &textwidth) + call assert_equal(0, &wrapmargin) + if has('vartabs') + call assert_equal('', &varsofttabstop) + endif + + " disabling 'paste' should restore the option values + set nopaste + call assert_true(&autoindent) + call assert_true(&expandtab) + if has('rightleft') + call assert_true(&revins) + call assert_true(&hkmap) + endif + call assert_true(&ruler) + call assert_true(&showmatch) + call assert_true(&smarttab) + call assert_equal(3, &softtabstop) + call assert_equal(27, &textwidth) + call assert_equal(12, &wrapmargin) + if has('vartabs') + call assert_equal('10,20', &varsofttabstop) + endif + + set autoindent& expandtab& ruler& showmatch& + if has('rightleft') + set revins& hkmap& + endif + set smarttab& softtabstop& textwidth& wrapmargin& + if has('vartabs') + set varsofttabstop& + endif +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim index 3d1e3fa6db..791cce4431 100644 --- a/src/nvim/testdir/test_popup.vim +++ b/src/nvim/testdir/test_popup.vim @@ -359,7 +359,7 @@ func Test_completefunc_opens_new_window_one() /^one call assert_fails('call feedkeys("A\<C-X>\<C-U>\<C-N>\<Esc>", "x")', 'E565:') call assert_equal(winid, win_getid()) - call assert_equal('oneDEF', getline(1)) + call assert_equal('onedef', getline(1)) q! endfunc @@ -384,9 +384,7 @@ func Test_completefunc_opens_new_window_two() /^two call assert_fails('call feedkeys("A\<C-X>\<C-U>\<C-N>\<Esc>", "x")', 'E565:') call assert_equal(winid, win_getid()) - " v8.2.1919 hasn't been ported yet - " call assert_equal('twodef', getline(1)) - call assert_equal('twoDEF', getline(1)) + call assert_equal('twodef', getline(1)) q! endfunc @@ -677,9 +675,9 @@ func Test_complete_CTRLN_startofbuffer() endfunc func Test_popup_and_window_resize() - if !has('terminal') || has('gui_running') - return - endif + CheckFeature terminal + CheckNotGui + let h = winheight(0) if h < 15 return @@ -864,7 +862,6 @@ func Test_popup_position() endfunc func Test_popup_command() - CheckScreendump CheckFeature menu menu Test.Foo Foo @@ -872,6 +869,23 @@ func Test_popup_command() call assert_fails('popup Test.Foo.X', 'E327:') call assert_fails('popup Foo', 'E337:') unmenu Test.Foo +endfunc + +func Test_popup_command_dump() + CheckFeature menu + CheckScreendump + + let script =<< trim END + func StartTimer() + call timer_start(100, {-> ChangeMenu()}) + endfunc + func ChangeMenu() + aunmenu PopUp.&Paste + nnoremenu 1.40 PopUp.&Paste :echomsg "pasted"<CR> + echomsg 'changed' + endfunc + END + call writefile(script, 'XtimerScript') let lines =<< trim END one two three four five @@ -879,12 +893,12 @@ func Test_popup_command() one more two three four five END call writefile(lines, 'Xtest') - let buf = RunVimInTerminal('Xtest', {}) + let buf = RunVimInTerminal('-S XtimerScript Xtest', {}) call term_sendkeys(buf, ":source $VIMRUNTIME/menu.vim\<CR>") call term_sendkeys(buf, "/X\<CR>:popup PopUp\<CR>") call VerifyScreenDump(buf, 'Test_popup_command_01', {}) - " Select a word + " go to the Paste entry in the menu call term_sendkeys(buf, "jj") call VerifyScreenDump(buf, 'Test_popup_command_02', {}) @@ -893,8 +907,20 @@ func Test_popup_command() call VerifyScreenDump(buf, 'Test_popup_command_03', {}) call term_sendkeys(buf, "\<Esc>") + + " Set a timer to change a menu entry while it's displayed. The text should + " not change but the command does. Making the screendump also verifies that + " "changed" shows up, which means the timer triggered + call term_sendkeys(buf, "/X\<CR>:call StartTimer() | popup PopUp\<CR>") + call VerifyScreenDump(buf, 'Test_popup_command_04', {}) + + " Select the Paste entry, executes the changed menu item. + call term_sendkeys(buf, "jj\<CR>") + call VerifyScreenDump(buf, 'Test_popup_command_05', {}) + call StopVimInTerminal(buf) call delete('Xtest') + call delete('XtimerScript') endfunc func Test_popup_complete_backwards() @@ -948,9 +974,9 @@ func Test_complete_o_tab() endfunc func Test_menu_only_exists_in_terminal() - if !exists(':tlmenu') || has('gui_running') - return - endif + CheckCommand tlmenu + CheckNotGui + tlnoremenu &Edit.&Paste<Tab>"+gP <C-W>"+ aunmenu * try @@ -1129,15 +1155,15 @@ func Test_CompleteChanged() autocmd! AAAAA_Group set complete& completeopt& - delfunc! OnPumchange + delfunc! OnPumChange bw! endfunc -function! GetPumPosition() +func GetPumPosition() call assert_true( pumvisible() ) let g:pum_pos = pum_getpos() return '' -endfunction +endfunc func Test_pum_getpos() new diff --git a/src/nvim/testdir/test_preview.vim b/src/nvim/testdir/test_preview.vim index 6c4ae414d3..b7b908e761 100644 --- a/src/nvim/testdir/test_preview.vim +++ b/src/nvim/testdir/test_preview.vim @@ -1,5 +1,8 @@ " Tests for the preview window +source check.vim +CheckFeature quickfix + func Test_Psearch() " this used to cause ml_get errors help @@ -13,6 +16,8 @@ func Test_Psearch() endfunc func Test_window_preview() + CheckFeature quickfix + " Open a preview window pedit Xa call assert_equal(2, winnr('$')) @@ -32,6 +37,8 @@ func Test_window_preview() endfunc func Test_window_preview_from_help() + CheckFeature quickfix + filetype on call writefile(['/* some C code */'], 'Xpreview.c') help diff --git a/src/nvim/testdir/test_profile.vim b/src/nvim/testdir/test_profile.vim index fdb6f13e2b..9165f7bace 100644 --- a/src/nvim/testdir/test_profile.vim +++ b/src/nvim/testdir/test_profile.vim @@ -40,8 +40,8 @@ func Test_profile_func() call writefile(lines, 'Xprofile_func.vim') call system(GetVimCommand() \ . ' -es --clean' - \ . ' -c "so Xprofile_func.vim"' - \ . ' -c "qall!"') + \ . ' --cmd "so Xprofile_func.vim"' + \ . ' --cmd "qall!"') call assert_equal(0, v:shell_error) let lines = readfile('Xprofile_func.log') @@ -403,6 +403,47 @@ func Test_profile_completion() call feedkeys(":profile start test_prof\<C-A>\<C-B>\"\<CR>", 'tx') call assert_match('^"profile start.* test_profile\.vim', @:) + + call feedkeys(":profile file test_prof\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_match('"profile file test_profile\.vim', @:) + call feedkeys(":profile file test_prof\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_match('"profile file test_profile\.vim', @:) + call feedkeys(":profile file test_prof \<Tab>\<C-B>\"\<CR>", 'tx') + call assert_match('"profile file test_prof ', @:) + call feedkeys(":profile file X1B2C3\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_match('"profile file X1B2C3', @:) + + func Xprof_test() + endfunc + call feedkeys(":profile func Xprof\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"profile func Xprof_test', @:) + call feedkeys(":profile func Xprof\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"profile func Xprof_test', @:) + call feedkeys(":profile func Xprof \<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"profile func Xprof ', @:) + call feedkeys(":profile func X1B2C3\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"profile func X1B2C3', @:) + + call feedkeys(":profdel \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"profdel file func', @:) + call feedkeys(":profdel fu\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"profdel func', @:) + call feedkeys(":profdel he\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"profdel he', @:) + call feedkeys(":profdel here \<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"profdel here ', @:) + call feedkeys(":profdel file test_prof\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"profdel file test_profile.vim', @:) + call feedkeys(":profdel file X1B2C3\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"profdel file X1B2C3', @:) + call feedkeys(":profdel func Xprof\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"profdel func Xprof_test', @:) + call feedkeys(":profdel func Xprof_test \<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"profdel func Xprof_test ', @:) + call feedkeys(":profdel func X1B2C3\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"profdel func X1B2C3', @:) + + delfunc Xprof_test endfunc func Test_profile_errors() @@ -475,7 +516,7 @@ func Test_profdel_func() call Foo3() [CODE] call writefile(lines, 'Xprofile_file.vim') - call system(GetVimCommandClean() . ' -es -c "so Xprofile_file.vim" -c q') + call system(GetVimCommandClean() . ' -es --cmd "so Xprofile_file.vim" --cmd q') call assert_equal(0, v:shell_error) let lines = readfile('Xprofile_file.log') diff --git a/src/nvim/testdir/test_prompt_buffer.vim b/src/nvim/testdir/test_prompt_buffer.vim index 8f94a8572b..b8f6c5240c 100644 --- a/src/nvim/testdir/test_prompt_buffer.vim +++ b/src/nvim/testdir/test_prompt_buffer.vim @@ -180,6 +180,8 @@ func Test_prompt_buffer_edit() call assert_beeps('normal! S') call assert_beeps("normal! \<C-A>") call assert_beeps("normal! \<C-X>") + call assert_beeps("normal! dp") + call assert_beeps("normal! do") " pressing CTRL-W in the prompt buffer should trigger the window commands call assert_equal(1, winnr()) exe "normal A\<C-W>\<C-W>" @@ -223,7 +225,7 @@ func Test_prompt_buffer_getbufinfo() %bwipe! endfunc -function! Test_prompt_while_writing_to_hidden_buffer() +func Test_prompt_while_writing_to_hidden_buffer() throw 'skipped: TODO' call CanTestPromptBuffer() CheckUnix diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index f6d573d76b..8dc4173d60 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -1,6 +1,7 @@ " Test for the quickfix feature. source check.vim +source vim9.vim CheckFeature quickfix source screendump.vim @@ -85,6 +86,12 @@ func s:setup_commands(cchar) endif endfunc +" This must be run before any error lists are created. +func Test_AA_cc_no_errors() + call assert_fails('cc', 'E42:') + call assert_fails('ll', 'E42:') +endfunc + " Tests for the :clist and :llist commands func XlistTests(cchar) call s:setup_commands(a:cchar) @@ -98,9 +105,15 @@ func XlistTests(cchar) call assert_true(v:errmsg ==# 'E42: No Errors') " Populate the list and then try - Xgetexpr ['non-error 1', 'Xtestfile1:1:3:Line1', - \ 'non-error 2', 'Xtestfile2:2:2:Line2', - \ 'non-error| 3', 'Xtestfile3:3:1:Line3'] + let lines =<< trim END + non-error 1 + Xtestfile1:1:3:Line1 + non-error 2 + Xtestfile2:2:2:Line2 + non-error| 3 + Xtestfile3:3:1:Line3 + END + Xgetexpr lines " List only valid entries let l = split(execute('Xlist', ''), "\n") @@ -255,6 +268,7 @@ func XwindowTests(cchar) " Opening the location list window without any errors should fail if a:cchar == 'l' call assert_fails('lopen', 'E776:') + call assert_fails('lwindow', 'E776:') endif " Create a list with no valid entries @@ -265,8 +279,12 @@ func XwindowTests(cchar) call assert_true(winnr('$') == 1) " Create a list with valid entries - Xgetexpr ['Xtestfile1:1:3:Line1', 'Xtestfile2:2:2:Line2', - \ 'Xtestfile3:3:1:Line3'] + let lines =<< trim END + Xtestfile1:1:3:Line1 + Xtestfile2:2:2:Line2 + Xtestfile3:3:1:Line3 + END + Xgetexpr lines " Open the window Xwindow @@ -329,8 +347,12 @@ func XwindowTests(cchar) if a:cchar == 'c' " Opening the quickfix window in multiple tab pages should reuse the " quickfix buffer - Xgetexpr ['Xtestfile1:1:3:Line1', 'Xtestfile2:2:2:Line2', - \ 'Xtestfile3:3:1:Line3'] + let lines =<< trim END + Xtestfile1:1:3:Line1 + Xtestfile2:2:2:Line2 + Xtestfile3:3:1:Line3 + END + Xgetexpr lines Xopen let qfbufnum = bufnr('%') tabnew @@ -365,14 +387,16 @@ func Test_copenHeight_tabline() set tabline& showtabline& endfunc - " Tests for the :cfile, :lfile, :caddfile, :laddfile, :cgetfile and :lgetfile " commands. func XfileTests(cchar) call s:setup_commands(a:cchar) - call writefile(['Xtestfile1:700:10:Line 700', - \ 'Xtestfile2:800:15:Line 800'], 'Xqftestfile1') + let lines =<< trim END + Xtestfile1:700:10:Line 700 + Xtestfile2:800:15:Line 800 + END + call writefile(lines, 'Xqftestfile1') enew! Xfile Xqftestfile1 @@ -396,8 +420,11 @@ func XfileTests(cchar) call assert_true(len(l) == 3 && \ l[2].lnum == 900 && l[2].col == 30 && l[2].text ==# 'Line 900') - call writefile(['Xtestfile1:222:77:Line 222', - \ 'Xtestfile2:333:88:Line 333'], 'Xqftestfile1') + let lines =<< trim END + Xtestfile1:222:77:Line 222 + Xtestfile2:333:88:Line 333 + END + call writefile(lines, 'Xqftestfile1') enew! Xgetfile Xqftestfile1 @@ -427,8 +454,11 @@ func XbufferTests(cchar) call s:setup_commands(a:cchar) enew! - silent! call setline(1, ['Xtestfile7:700:10:Line 700', - \ 'Xtestfile8:800:15:Line 800']) + let lines =<< trim END + Xtestfile7:700:10:Line 700 + Xtestfile8:800:15:Line 800 + END + silent! call setline(1, lines) Xbuffer! let l = g:Xgetlist() call assert_true(len(l) == 2 && @@ -436,8 +466,11 @@ func XbufferTests(cchar) \ l[1].lnum == 800 && l[1].col == 15 && l[1].text ==# 'Line 800') enew! - silent! call setline(1, ['Xtestfile9:900:55:Line 900', - \ 'Xtestfile10:950:66:Line 950']) + let lines =<< trim END + Xtestfile9:900:55:Line 900 + Xtestfile10:950:66:Line 950 + END + silent! call setline(1, lines) Xgetbuffer let l = g:Xgetlist() call assert_true(len(l) == 2 && @@ -445,8 +478,11 @@ func XbufferTests(cchar) \ l[1].lnum == 950 && l[1].col == 66 && l[1].text ==# 'Line 950') enew! - silent! call setline(1, ['Xtestfile11:700:20:Line 700', - \ 'Xtestfile12:750:25:Line 750']) + let lines =<< trim END + Xtestfile11:700:20:Line 700 + Xtestfile12:750:25:Line 750 + END + silent! call setline(1, lines) Xaddbuffer let l = g:Xgetlist() call assert_true(len(l) == 4 && @@ -516,12 +552,15 @@ func Xtest_browse(cchar) call s:create_test_file('Xqftestfile1') call s:create_test_file('Xqftestfile2') - Xgetexpr ['Xqftestfile1:5:Line5', - \ 'Xqftestfile1:6:Line6', - \ 'Xqftestfile2:10:Line10', - \ 'Xqftestfile2:11:Line11', - \ 'RegularLine1', - \ 'RegularLine2'] + let lines =<< trim END + Xqftestfile1:5:Line5 + Xqftestfile1:6:Line6 + Xqftestfile2:10:Line10 + Xqftestfile2:11:Line11 + RegularLine1 + RegularLine2 + END + Xgetexpr lines Xfirst call assert_fails('-5Xcc', 'E16:') @@ -571,10 +610,13 @@ func Xtest_browse(cchar) call assert_equal(5, line('.')) " Jumping to an error from the error window using cc command - Xgetexpr ['Xqftestfile1:5:Line5', - \ 'Xqftestfile1:6:Line6', - \ 'Xqftestfile2:10:Line10', - \ 'Xqftestfile2:11:Line11'] + let lines =<< trim END + Xqftestfile1:5:Line5 + Xqftestfile1:6:Line6 + Xqftestfile2:10:Line10 + Xqftestfile2:11:Line11 + END + Xgetexpr lines Xopen 10Xcc call assert_equal(11, line('.')) @@ -699,7 +741,7 @@ func s:test_xhelpgrep(cchar) " Search for non existing help string call assert_fails('Xhelpgrep a1b2c3', 'E480:') " Invalid regular expression - call assert_fails('Xhelpgrep \@<!', 'E480:') + call assert_fails('Xhelpgrep \@<!', 'E866:') endfunc func Test_helpgrep() @@ -708,6 +750,35 @@ func Test_helpgrep() call s:test_xhelpgrep('l') endfunc +" When running the :helpgrep command, if an autocmd modifies the 'cpoptions' +" value, then Vim crashes. (issue fixed by 7.2b-004 and 8.2.4453) +func Test_helpgrep_restore_cpo_aucmd() + let save_cpo = &cpo + augroup QF_Test + au! + autocmd BufNew * set cpo=acd + augroup END + + helpgrep quickfix + call assert_equal('acd', &cpo) + %bw! + + set cpo&vim + augroup QF_Test + au! + autocmd BufReadPost * set cpo= + augroup END + + helpgrep buffer + call assert_equal('', &cpo) + + augroup QF_Test + au! + augroup END + %bw! + let &cpo = save_cpo +endfunc + func Test_errortitle() augroup QfBufWinEnter au! @@ -1073,20 +1144,21 @@ func s:dir_stack_tests(cchar) let save_efm=&efm set efm=%DEntering\ dir\ '%f',%f:%l:%m,%XLeaving\ dir\ '%f' - let lines = ["Entering dir 'dir1/a'", - \ 'habits2.txt:1:Nine Healthy Habits', - \ "Entering dir 'b'", - \ 'habits3.txt:2:0 Hours of television', - \ 'habits2.txt:7:5 Small meals', - \ "Entering dir 'dir1/c'", - \ 'habits4.txt:3:1 Hour of exercise', - \ "Leaving dir 'dir1/c'", - \ "Leaving dir 'dir1/a'", - \ 'habits1.txt:4:2 Liters of water', - \ "Entering dir 'dir2'", - \ 'habits5.txt:5:3 Cups of hot green tea', - \ "Leaving dir 'dir2'" - \] + let lines =<< trim END + Entering dir 'dir1/a' + habits2.txt:1:Nine Healthy Habits + Entering dir 'b' + habits3.txt:2:0 Hours of television + habits2.txt:7:5 Small meals + Entering dir 'dir1/c' + habits4.txt:3:1 Hour of exercise + Leaving dir 'dir1/c' + Leaving dir 'dir1/a' + habits1.txt:4:2 Liters of water + Entering dir 'dir2' + habits5.txt:5:3 Cups of hot green tea + Leaving dir 'dir2' + END Xexpr "" for l in lines @@ -1120,19 +1192,19 @@ func Test_efm_dirstack() call mkdir('dir1/c') call mkdir('dir2') - let lines = ["Nine Healthy Habits", - \ "0 Hours of television", - \ "1 Hour of exercise", - \ "2 Liters of water", - \ "3 Cups of hot green tea", - \ "4 Short mental breaks", - \ "5 Small meals", - \ "6 AM wake up time", - \ "7 Minutes of laughter", - \ "8 Hours of sleep (at least)", - \ "9 PM end of the day and off to bed" - \ ] - + let lines =<< trim END + Nine Healthy Habits + 0 Hours of television + 1 Hour of exercise + 2 Liters of water + 3 Cups of hot green tea + 4 Short mental breaks + 5 Small meals + 6 AM wake up time + 7 Minutes of laughter + 8 Hours of sleep (at least) + 9 PM end of the day and off to bed + END call writefile(lines, 'habits1.txt') call writefile(lines, 'dir1/a/habits2.txt') call writefile(lines, 'dir1/a/b/habits3.txt') @@ -1158,7 +1230,13 @@ func Xefm_ignore_continuations(cchar) \ '%-Wignored %m %l,' . \ '%+Cmore ignored %m %l,' . \ '%Zignored end' - Xgetexpr ['ignored warning 1', 'more ignored continuation 2', 'ignored end', 'error resync 4'] + let lines =<< trim END + ignored warning 1 + more ignored continuation 2 + ignored end + error resync 4 + END + Xgetexpr lines let l = map(g:Xgetlist(), '[v:val.text, v:val.valid, v:val.lnum, v:val.type]') call assert_equal([['resync', 1, 4, 'E']], l) @@ -1204,8 +1282,14 @@ func Xinvalid_efm_Tests(cchar) set efm= call assert_fails('Xexpr "abc.txt:1:Hello world"', 'E378:') + " Empty directory name. When there is an error in parsing new entries, make + " sure the previous quickfix list is made the current list. + set efm& + cexpr ["one", "two"] + let qf_id = getqflist(#{id: 0}).id set efm=%DEntering\ dir\ abc,%f:%l:%m call assert_fails('Xexpr ["Entering dir abc", "abc.txt:1:Hello world"]', 'E379:') + call assert_equal(qf_id, getqflist(#{id: 0}).id) let &efm = save_efm endfunc @@ -1396,8 +1480,14 @@ func Test_efm_error_type() " error type set efm=%f:%l:%t:%m - cexpr ["Xfile1:10:E:msg1", "Xfile1:20:W:msg2", "Xfile1:30:I:msg3", - \ "Xfile1:40:N:msg4", "Xfile1:50:R:msg5"] + let lines =<< trim END + Xfile1:10:E:msg1 + Xfile1:20:W:msg2 + Xfile1:30:I:msg3 + Xfile1:40:N:msg4 + Xfile1:50:R:msg5 + END + cexpr lines let output = split(execute('clist'), "\n") call assert_equal([ \ ' 1 Xfile1:10 error: msg1', @@ -1408,8 +1498,14 @@ func Test_efm_error_type() " error type and a error number set efm=%f:%l:%t:%n:%m - cexpr ["Xfile1:10:E:2:msg1", "Xfile1:20:W:4:msg2", "Xfile1:30:I:6:msg3", - \ "Xfile1:40:N:8:msg4", "Xfile1:50:R:3:msg5"] + let lines =<< trim END + Xfile1:10:E:2:msg1 + Xfile1:20:W:4:msg2 + Xfile1:30:I:6:msg3 + Xfile1:40:N:8:msg4 + Xfile1:50:R:3:msg5 + END + cexpr lines let output = split(execute('clist'), "\n") call assert_equal([ \ ' 1 Xfile1:10 error 2: msg1', @@ -1434,8 +1530,13 @@ func Test_efm_end_lnum_col() " multiple lines set efm=%A%n)%m,%Z%f:%l-%e:%c-%k - cexpr ["1)msg1", "Xfile1:14-24:1-2", - \ "2)msg2", "Xfile1:24-34:3-4"] + let lines =<< trim END + 1)msg1 + Xfile1:14-24:1-2 + 2)msg2 + Xfile1:24-34:3-4 + END + cexpr lines let output = split(execute('clist'), "\n") call assert_equal([ \ ' 1 Xfile1:14-24 col 1-2 error 1: msg1', @@ -1459,7 +1560,7 @@ func XquickfixChangedByAutocmd(cchar) endfunc endif - augroup testgroup + augroup QF_Test au! autocmd BufReadCmd test_changed.txt call ReadFunc() augroup END @@ -1473,7 +1574,24 @@ func XquickfixChangedByAutocmd(cchar) endfor call assert_fails('Xrewind', ErrorNr . ':') - augroup! testgroup + augroup QF_Test + au! + augroup END + + if a:cchar == 'c' + cexpr ["Xtest1:1:Line"] + cwindow + only + augroup QF_Test + au! + autocmd WinEnter * call setqflist([], 'f') + augroup END + call assert_fails('exe "normal \<CR>"', 'E925:') + augroup QF_Test + au! + augroup END + endif + %bw! endfunc func Test_quickfix_was_changed_by_autocmd() @@ -1611,6 +1729,9 @@ func SetXlistTests(cchar, bnum) \ " {'bufnr':999, 'lnum':5}])", 'E92:') call g:Xsetlist([[1, 2,3]]) call assert_equal(0, len(g:Xgetlist())) + call assert_fails('call g:Xsetlist([], [])', 'E928:') + call g:Xsetlist([v:_null_dict]) + call assert_equal([], g:Xgetlist()) endfunc func Test_setqflist() @@ -1825,12 +1946,12 @@ func Test_cgetfile_on_long_lines() " Problematic values if the line is longer than 4096 bytes. Then 1024 bytes " are read at a time. for len in [4078, 4079, 4080, 5102, 5103, 5104, 6126, 6127, 6128, 7150, 7151, 7152] - let lines = [ - \ '/tmp/file1:1:1:aaa', - \ '/tmp/file2:1:1:%s', - \ '/tmp/file3:1:1:bbb', - \ '/tmp/file4:1:1:ccc', - \ ] + let lines =<< trim END + /tmp/file1:1:1:aaa + /tmp/file2:1:1:%s + /tmp/file3:1:1:bbb + /tmp/file4:1:1:ccc + END let lines[1] = substitute(lines[1], '%s', repeat('x', len), '') call writefile(lines, 'Xcqetfile.txt') cgetfile Xcqetfile.txt @@ -1857,12 +1978,15 @@ func Test_switchbuf() let file1_winid = win_getid() new Xqftestfile2 let file2_winid = win_getid() - cgetexpr ['Xqftestfile1:5:Line5', - \ 'Xqftestfile1:6:Line6', - \ 'Xqftestfile2:10:Line10', - \ 'Xqftestfile2:11:Line11', - \ 'Xqftestfile3:15:Line15', - \ 'Xqftestfile3:16:Line16'] + let lines =<< trim END + Xqftestfile1:5:Line5 + Xqftestfile1:6:Line6 + Xqftestfile2:10:Line10 + Xqftestfile2:11:Line11 + Xqftestfile3:15:Line15 + Xqftestfile3:16:Line16 + END + cgetexpr lines new let winid = win_getid() @@ -2524,21 +2648,23 @@ func Test_Autocmd() silent! cexpr non_existing_func() silent! caddexpr non_existing_func() silent! cgetexpr non_existing_func() - let l = ['precexpr', - \ 'postcexpr', - \ 'precaddexpr', - \ 'postcaddexpr', - \ 'precgetexpr', - \ 'postcgetexpr', - \ 'precexpr', - \ 'postcexpr', - \ 'precaddexpr', - \ 'postcaddexpr', - \ 'precgetexpr', - \ 'postcgetexpr', - \ 'precexpr', - \ 'precaddexpr', - \ 'precgetexpr'] + let l =<< trim END + precexpr + postcexpr + precaddexpr + postcaddexpr + precgetexpr + postcgetexpr + precexpr + postcexpr + precaddexpr + postcaddexpr + precgetexpr + postcgetexpr + precexpr + precaddexpr + precgetexpr + END call assert_equal(l, g:acmds) let g:acmds = [] @@ -2556,15 +2682,17 @@ func Test_Autocmd() exe 'silent! cgetbuffer ' . bnum exe 'silent! caddbuffer ' . bnum enew! - let l = ['precbuffer', - \ 'postcbuffer', - \ 'precgetbuffer', - \ 'postcgetbuffer', - \ 'precaddbuffer', - \ 'postcaddbuffer', - \ 'precbuffer', - \ 'precgetbuffer', - \ 'precaddbuffer'] + let l =<< trim END + precbuffer + postcbuffer + precgetbuffer + postcgetbuffer + precaddbuffer + postcaddbuffer + precbuffer + precgetbuffer + precaddbuffer + END call assert_equal(l, g:acmds) call writefile(['Xtest:1:Line1'], 'Xtest') @@ -2579,24 +2707,26 @@ func Test_Autocmd() silent! cfile do_not_exist silent! caddfile do_not_exist silent! cgetfile do_not_exist - let l = ['precfile', - \ 'postcfile', - \ 'precaddfile', - \ 'postcaddfile', - \ 'precgetfile', - \ 'postcgetfile', - \ 'precfile', - \ 'postcfile', - \ 'precaddfile', - \ 'postcaddfile', - \ 'precgetfile', - \ 'postcgetfile', - \ 'precfile', - \ 'postcfile', - \ 'precaddfile', - \ 'postcaddfile', - \ 'precgetfile', - \ 'postcgetfile'] + let l =<< trim END + precfile + postcfile + precaddfile + postcaddfile + precgetfile + postcgetfile + precfile + postcfile + precaddfile + postcaddfile + precgetfile + postcgetfile + precfile + postcfile + precaddfile + postcaddfile + precgetfile + postcgetfile + END call assert_equal(l, g:acmds) let g:acmds = [] @@ -2609,20 +2739,22 @@ func Test_Autocmd() set makeprg= silent! make set makeprg& - let l = ['prehelpgrep', - \ 'posthelpgrep', - \ 'prehelpgrep', - \ 'posthelpgrep', - \ 'previmgrep', - \ 'postvimgrep', - \ 'previmgrepadd', - \ 'postvimgrepadd', - \ 'previmgrep', - \ 'postvimgrep', - \ 'previmgrepadd', - \ 'postvimgrepadd', - \ 'premake', - \ 'postmake'] + let l =<< trim END + prehelpgrep + posthelpgrep + prehelpgrep + posthelpgrep + previmgrep + postvimgrep + previmgrepadd + postvimgrepadd + previmgrep + postvimgrep + previmgrepadd + postvimgrepadd + premake + postmake + END call assert_equal(l, g:acmds) if has('unix') @@ -2642,22 +2774,24 @@ func Test_Autocmd() silent lgrep Grep_Autocmd_Text test_quickfix.vim silent lgrepadd GrepAdd_Autocmd_Text test_quickfix.vim set grepprg&vim - let l = ['pregrep', - \ 'postgrep', - \ 'pregrepadd', - \ 'postgrepadd', - \ 'pregrep', - \ 'postgrep', - \ 'pregrepadd', - \ 'postgrepadd', - \ 'pregrep', - \ 'postgrep', - \ 'pregrepadd', - \ 'postgrepadd', - \ 'prelgrep', - \ 'postlgrep', - \ 'prelgrepadd', - \ 'postlgrepadd'] + let l =<< trim END + pregrep + postgrep + pregrepadd + postgrepadd + pregrep + postgrep + pregrepadd + postgrepadd + pregrep + postgrep + pregrepadd + postgrepadd + prelgrep + postlgrep + prelgrepadd + postlgrepadd + END call assert_equal(l, g:acmds) endif @@ -2816,15 +2950,16 @@ func Test_cwindow_highlight() CheckScreendump let lines =<< trim END - call setline(1, ['some', 'text', 'with', 'matches']) - write XCwindow - vimgrep e XCwindow - redraw - cwindow 4 + call setline(1, ['some', 'text', 'with', 'matches']) + write XCwindow + vimgrep e XCwindow + redraw + cwindow 4 END call writefile(lines, 'XtestCwindow') let buf = RunVimInTerminal('-S XtestCwindow', #{rows: 12}) call VerifyScreenDump(buf, 'Test_quickfix_cwindow_1', {}) + call term_sendkeys(buf, ":cnext\<CR>") call VerifyScreenDump(buf, 'Test_quickfix_cwindow_2', {}) @@ -2837,10 +2972,13 @@ endfunc func XvimgrepTests(cchar) call s:setup_commands(a:cchar) - call writefile(['Editor:VIM vim', - \ 'Editor:Emacs EmAcS', - \ 'Editor:Notepad NOTEPAD'], 'Xtestfile1') - call writefile(['Linux', 'MacOS', 'MS-Windows'], 'Xtestfile2') + let lines =<< trim END + Editor:VIM vim + Editor:Emacs EmAcS + Editor:Notepad NOTEPAD + END + call writefile(lines, 'Xtestfile1') + call writefile(['Linux', 'macOS', 'MS-Windows'], 'Xtestfile2') " Error cases call assert_fails('Xvimgrep /abc *', 'E682:') @@ -2854,7 +2992,7 @@ func XvimgrepTests(cchar) Xexpr "" Xvimgrepadd Notepad Xtestfile1 - Xvimgrepadd MacOS Xtestfile2 + Xvimgrepadd macOS Xtestfile2 let l = g:Xgetlist() call assert_equal(2, len(l)) call assert_equal('Editor:Notepad NOTEPAD', l[0].text) @@ -2884,6 +3022,19 @@ func XvimgrepTests(cchar) call assert_equal(0, getbufinfo('Xtestfile1')[0].loaded) call assert_equal([], getbufinfo('Xtestfile2')) + " Test for opening the dummy buffer used by vimgrep in a window. The new + " window should be closed + %bw! + augroup QF_Test + au! + autocmd BufReadPre * exe "sb " .. expand("<abuf>") + augroup END + call assert_fails("Xvimgrep /sublime/ Xtestfile1", 'E480:') + call assert_equal(1, winnr('$')) + augroup QF_Test + au! + augroup END + call delete('Xtestfile1') call delete('Xtestfile2') endfunc @@ -3087,7 +3238,7 @@ func Test_cclose_from_copen() endfunc func Test_cclose_in_autocmd() - " Problem is only triggered if "starting" is zero, so that the OptionsSet + " Problem is only triggered if "starting" is zero, so that the OptionSet " event will be triggered. " call test_override('starting', 1) augroup QF_Test @@ -3102,7 +3253,7 @@ func Test_cclose_in_autocmd() " call test_override('starting', 0) endfunc -" Check that ":file" without an argument is possible even when curbuf is locked +" Check that ":file" without an argument is possible even when "curbuf->b_ro_locked" " is set. func Test_file_from_copen() " Works without argument. @@ -3148,6 +3299,21 @@ func Test_resize_from_copen() endtry endfunc +func Test_filetype_autocmd() + " this changes the location list while it is in use to fill a buffer + lexpr '' + lopen + augroup FT_loclist + au FileType * call setloclist(0, [], 'f') + augroup END + silent! lolder + lexpr '' + + augroup FT_loclist + au! FileType + augroup END +endfunc + func Test_vimgrep_with_textlock() new @@ -3291,9 +3457,9 @@ func Xmultidirstack_tests(cchar) let l1 = g:Xgetlist({'nr':1, 'items':1}) let l2 = g:Xgetlist({'nr':2, 'items':1}) - call assert_equal(expand('Xone/a/one.txt'), bufname(l1.items[1].bufnr)) + call assert_equal('Xone/a/one.txt', bufname(l1.items[1].bufnr)) call assert_equal(3, l1.items[1].lnum) - call assert_equal(expand('Xtwo/a/two.txt'), bufname(l2.items[1].bufnr)) + call assert_equal('Xtwo/a/two.txt', bufname(l2.items[1].bufnr)) call assert_equal(5, l2.items[1].lnum) endfunc @@ -3337,14 +3503,15 @@ func Xmultifilestack_tests(cchar) " error line ends with a file stack. let efm_val = 'Error\ l%l\ in\ %f,' let efm_val .= '%-P%>(%f%r,Error\ l%l\ in\ %m,%-Q)%r' - let l = g:Xgetlist({'lines' : [ - \ '(one.txt', - \ 'Error l4 in one.txt', - \ ') (two.txt', - \ 'Error l6 in two.txt', - \ ')', - \ 'Error l8 in one.txt' - \ ], 'efm' : efm_val}) + let lines =<< trim END + (one.txt + Error l4 in one.txt + ) (two.txt + Error l6 in two.txt + ) + Error l8 in one.txt + END + let l = g:Xgetlist({'lines': lines, 'efm' : efm_val}) call assert_equal(3, len(l.items)) call assert_equal('one.txt', bufname(l.items[0].bufnr)) call assert_equal(4, l.items[0].lnum) @@ -3622,7 +3789,15 @@ func Xqfjump_tests(cchar) call g:Xsetlist([], 'f') setlocal buftype=nofile new - call g:Xsetlist([], ' ', {'lines' : ['F1:1:1:Line1', 'F1:2:2:Line2', 'F2:1:1:Line1', 'F2:2:2:Line2', 'F3:1:1:Line1', 'F3:2:2:Line2']}) + let lines =<< trim END + F1:1:1:Line1 + F1:2:2:Line2 + F2:1:1:Line1 + F2:2:2:Line2 + F3:1:1:Line1 + F3:2:2:Line2 + END + call g:Xsetlist([], ' ', {'lines': lines}) Xopen let winid = win_getid() wincmd p @@ -3758,6 +3933,22 @@ func Xgetlist_empty_tests(cchar) endif endfunc +func Test_empty_list_quickfixtextfunc() + " This was crashing. Can only reproduce by running it in a separate Vim + " instance. + let lines =<< trim END + func s:Func(o) + cgetexpr '0' + endfunc + cope + let &quickfixtextfunc = 's:Func' + cgetfile [ex + END + call writefile(lines, 'Xquickfixtextfunc') + call RunVim([], [], '-e -s -S Xquickfixtextfunc -c qa') + call delete('Xquickfixtextfunc') +endfunc + func Test_getqflist() call Xgetlist_empty_tests('c') call Xgetlist_empty_tests('l') @@ -3943,8 +4134,8 @@ endfunc func Test_lvimgrep_crash2() au BufNewFile x sfind - call assert_fails('lvimgrep x x', 'E480:') - call assert_fails('lvimgrep x x x', 'E480:') + call assert_fails('lvimgrep x x', 'E471:') + call assert_fails('lvimgrep x x x', 'E471:') au! BufNewFile endfunc @@ -4055,14 +4246,19 @@ endfunc " The following test used to crash Vim func Test_lhelpgrep_autocmd() lhelpgrep quickfix - autocmd QuickFixCmdPost * call setloclist(0, [], 'f') + augroup QF_Test + au! + autocmd QuickFixCmdPost * call setloclist(0, [], 'f') + augroup END lhelpgrep buffer call assert_equal('help', &filetype) call assert_equal(0, getloclist(0, {'nr' : '$'}).nr) lhelpgrep tabpage call assert_equal('help', &filetype) call assert_equal(1, getloclist(0, {'nr' : '$'}).nr) - au! QuickFixCmdPost + augroup QF_Test + au! + augroup END new | only augroup QF_Test @@ -4075,7 +4271,7 @@ func Test_lhelpgrep_autocmd() wincmd w call assert_fails('helpgrep quickfix', 'E925:') augroup QF_Test - au! BufEnter + au! augroup END new | only @@ -4085,7 +4281,7 @@ func Test_lhelpgrep_autocmd() augroup END call assert_fails('helpgrep quickfix', 'E925:') augroup QF_Test - au! BufEnter + au! augroup END new | only @@ -4095,10 +4291,43 @@ func Test_lhelpgrep_autocmd() augroup END call assert_fails('lhelpgrep quickfix', 'E926:') augroup QF_Test - au! BufEnter + au! augroup END + " Replace the contents of a help window location list when it is still in + " use. new | only + lhelpgrep quickfix + wincmd w + augroup QF_Test + au! + autocmd WinEnter * call setloclist(0, [], 'r') + augroup END + call assert_fails('lhelpgrep win_getid', 'E926:') + augroup QF_Test + au! + augroup END + + %bw! +endfunc + +" The following test used to crash Vim +func Test_lhelpgrep_autocmd_free_loclist() + %bw! + lhelpgrep quickfix + wincmd w + augroup QF_Test + au! + autocmd WinEnter * call setloclist(0, [], 'f') + augroup END + lhelpgrep win_getid + wincmd w + wincmd w + wincmd w + augroup QF_Test + au! + augroup END + %bw! endfunc " Test for shortening/simplifying the file name when opening the @@ -4816,9 +5045,20 @@ func Xtest_below(cchar) endif " Test for lines with multiple quickfix entries - Xexpr ["X1:5:L5", "X2:5:1:L5_1", "X2:5:2:L5_2", "X2:5:3:L5_3", - \ "X2:10:1:L10_1", "X2:10:2:L10_2", "X2:10:3:L10_3", - \ "X2:15:1:L15_1", "X2:15:2:L15_2", "X2:15:3:L15_3", "X3:3:L3"] + let lines =<< trim END + X1:5:L5 + X2:5:1:L5_1 + X2:5:2:L5_2 + X2:5:3:L5_3 + X2:10:1:L10_1 + X2:10:2:L10_2 + X2:10:3:L10_3 + X2:15:1:L15_1 + X2:15:2:L15_2 + X2:15:3:L15_3 + X3:3:L3 + END + Xexpr lines edit +1 X2 Xbelow 2 call assert_equal(['X2', 10, 1], [@%, line('.'), col('.')]) @@ -4882,33 +5122,32 @@ func Test_cbelow() endfunc func Test_quickfix_count() - let commands = [ - \ 'cNext', - \ 'cNfile', - \ 'cabove', - \ 'cbelow', - \ 'cfirst', - \ 'clast', - \ 'cnewer', - \ 'cnext', - \ 'cnfile', - \ 'colder', - \ 'cprevious', - \ 'crewind', - \ - \ 'lNext', - \ 'lNfile', - \ 'labove', - \ 'lbelow', - \ 'lfirst', - \ 'llast', - \ 'lnewer', - \ 'lnext', - \ 'lnfile', - \ 'lolder', - \ 'lprevious', - \ 'lrewind', - \ ] + let commands =<< trim END + cNext + cNfile + cabove + cbelow + cfirst + clast + cnewer + cnext + cnfile + colder + cprevious + crewind + lNext + lNfile + labove + lbelow + lfirst + llast + lnewer + lnext + lnfile + lolder + lprevious + lrewind + END for cmd in commands call assert_fails('-1' .. cmd, 'E16:') call assert_fails('.' .. cmd, 'E16:') @@ -5090,6 +5329,29 @@ func Test_lhelpgrep_from_help_window() new | only! endfunc +" Test for the crash fixed by 7.3.715 +func Test_setloclist_crash() + %bw! + let g:BufNum = bufnr() + augroup QF_Test + au! + au BufUnload * call setloclist(0, [{'bufnr':g:BufNum, 'lnum':1, 'col':1, 'text': 'tango down'}]) + augroup END + + try + lvimgrep /.*/ *.mak + catch /E926:/ + endtry + call assert_equal('tango down', getloclist(0, {'items' : 0}).items[0].text) + call assert_equal(1, getloclist(0, {'size' : 0}).size) + + augroup QF_Test + au! + augroup END + unlet g:BufNum + %bw! +endfunc + " Test for adding an invalid entry with the quickfix window open and making " sure that the window contents are not changed func Test_add_invalid_entry_with_qf_window() @@ -5289,6 +5551,7 @@ func Xtest_getqflist_by_idx(cchar) call assert_equal('L20', l[0].text) call assert_equal([], g:Xgetlist({'idx' : -1, 'items' : 0}).items) call assert_equal([], g:Xgetlist({'idx' : 3, 'items' : 0}).items) + call assert_equal({}, g:Xgetlist(#{idx: "abc"})) %bwipe! endfunc @@ -5347,6 +5610,19 @@ func Xtest_qftextfunc(cchar) call assert_equal('F1|10 col 2-7| green', getline(1)) call assert_equal('F1|20-25 col 4-8| blue', getline(2)) Xclose + + set efm=%f:%l:%c:%m + set quickfixtextfunc=Tqfexpr + " Update the list with only the cwindow + Xwindow + only + call g:Xsetlist([ + \ { 'filename': 'F2', 'lnum': 20, 'col': 2, + \ 'end_col': 7, 'text': 'red'} + \ ]) + call assert_equal(['F2-L20C2-red'], getline(1, '$')) + new + Xclose set efm& set quickfixtextfunc& @@ -5470,6 +5746,155 @@ func Test_qftextfunc() call Xtest_qftextfunc('l') endfunc +func Test_qftextfunc_callback() + let lines =<< trim END + set efm=%f:%l:%c:%m + + #" Test for using a function name + LET &qftf = 'g:Tqfexpr' + cexpr "F0:0:0:L0" + copen + call assert_equal('F0-L0C0-L0', getline(1)) + cclose + + #" Test for using a function() + set qftf=function('g:Tqfexpr') + cexpr "F1:1:1:L1" + copen + call assert_equal('F1-L1C1-L1', getline(1)) + cclose + + #" Using a funcref variable to set 'quickfixtextfunc' + VAR Fn = function('g:Tqfexpr') + LET &qftf = Fn + cexpr "F2:2:2:L2" + copen + call assert_equal('F2-L2C2-L2', getline(1)) + cclose + + #" Using string(funcref_variable) to set 'quickfixtextfunc' + LET Fn = function('g:Tqfexpr') + LET &qftf = string(Fn) + cexpr "F3:3:3:L3" + copen + call assert_equal('F3-L3C3-L3', getline(1)) + cclose + + #" Test for using a funcref() + set qftf=funcref('g:Tqfexpr') + cexpr "F4:4:4:L4" + copen + call assert_equal('F4-L4C4-L4', getline(1)) + cclose + + #" Using a funcref variable to set 'quickfixtextfunc' + LET Fn = funcref('g:Tqfexpr') + LET &qftf = Fn + cexpr "F5:5:5:L5" + copen + call assert_equal('F5-L5C5-L5', getline(1)) + cclose + + #" Using a string(funcref_variable) to set 'quickfixtextfunc' + LET Fn = funcref('g:Tqfexpr') + LET &qftf = string(Fn) + cexpr "F5:5:5:L5" + copen + call assert_equal('F5-L5C5-L5', getline(1)) + cclose + + #" Test for using a lambda function with set + VAR optval = "LSTART a LMIDDLE Tqfexpr(a) LEND" + LET optval = substitute(optval, ' ', '\\ ', 'g') + exe "set qftf=" .. optval + cexpr "F6:6:6:L6" + copen + call assert_equal('F6-L6C6-L6', getline(1)) + cclose + + #" Set 'quickfixtextfunc' to a lambda expression + LET &qftf = LSTART a LMIDDLE Tqfexpr(a) LEND + cexpr "F7:7:7:L7" + copen + call assert_equal('F7-L7C7-L7', getline(1)) + cclose + + #" Set 'quickfixtextfunc' to string(lambda_expression) + LET &qftf = "LSTART a LMIDDLE Tqfexpr(a) LEND" + cexpr "F8:8:8:L8" + copen + call assert_equal('F8-L8C8-L8', getline(1)) + cclose + + #" Set 'quickfixtextfunc' to a variable with a lambda expression + VAR Lambda = LSTART a LMIDDLE Tqfexpr(a) LEND + LET &qftf = Lambda + cexpr "F9:9:9:L9" + copen + call assert_equal('F9-L9C9-L9', getline(1)) + cclose + + #" Set 'quickfixtextfunc' to a string(variable with a lambda expression) + LET Lambda = LSTART a LMIDDLE Tqfexpr(a) LEND + LET &qftf = string(Lambda) + cexpr "F9:9:9:L9" + copen + call assert_equal('F9-L9C9-L9', getline(1)) + cclose + END + call CheckLegacyAndVim9Success(lines) + + " Test for using a script-local function name + func s:TqfFunc2(info) + let g:TqfFunc2Args = [a:info.start_idx, a:info.end_idx] + return '' + endfunc + let g:TqfFunc2Args = [] + set quickfixtextfunc=s:TqfFunc2 + cexpr "F10:10:10:L10" + cclose + call assert_equal([1, 1], g:TqfFunc2Args) + + let &quickfixtextfunc = 's:TqfFunc2' + cexpr "F11:11:11:L11" + cclose + call assert_equal([1, 1], g:TqfFunc2Args) + delfunc s:TqfFunc2 + + " set 'quickfixtextfunc' to a partial with dict. This used to cause a crash. + func SetQftfFunc() + let params = {'qftf': function('g:DictQftfFunc')} + let &quickfixtextfunc = params.qftf + endfunc + func g:DictQftfFunc(_) dict + endfunc + call SetQftfFunc() + new + call SetQftfFunc() + bw + call test_garbagecollect_now() + new + set qftf= + wincmd w + set qftf= + :%bw! + + " set per-quickfix list 'quickfixtextfunc' to a partial with dict. This used + " to cause a crash. + let &qftf = '' + func SetLocalQftfFunc() + let params = {'qftf': function('g:DictQftfFunc')} + call setqflist([], 'a', {'quickfixtextfunc' : params.qftf}) + endfunc + call SetLocalQftfFunc() + call test_garbagecollect_now() + call setqflist([], 'a', {'quickfixtextfunc' : ''}) + delfunc g:DictQftfFunc + delfunc SetQftfFunc + delfunc SetLocalQftfFunc + set efm& +endfunc + " Test for updating a location list for some other window and check that " 'qftextfunc' uses the correct location list. func Test_qftextfunc_other_loclist() @@ -5535,9 +5960,12 @@ func Test_locationlist_open_in_newtab() %bwipe! - lgetexpr ['Xqftestfile1:5:Line5', - \ 'Xqftestfile2:10:Line10', - \ 'Xqftestfile3:16:Line16'] + let lines =<< trim END + Xqftestfile1:5:Line5 + Xqftestfile2:10:Line10 + Xqftestfile3:16:Line16 + END + lgetexpr lines silent! llast call assert_equal(1, tabpagenr('$')) @@ -5578,6 +6006,21 @@ func Test_win_gettype() lclose endfunc +fun Test_vimgrep_nomatch() + call XexprTests('c') + call g:Xsetlist([{'lnum':10,'text':'Line1'}]) + copen + if has("win32") + call assert_fails('vimgrep foo *.zzz', 'E479:') + let expected = [{'lnum': 10, 'bufnr': 0, 'end_lnum': 0, 'pattern': '', 'valid': 0, 'vcol': 0, 'nr': 0, 'module': '', 'type': '', 'end_col': 0, 'col': 0, 'text': 'Line1'}] + else + call assert_fails('vimgrep foo *.zzz', 'E480:') + let expected = [] + endif + call assert_equal(expected, getqflist()) + cclose +endfunc + " Test for opening the quickfix window in two tab pages and then closing one " of the quickfix windows. This should not make the quickfix buffer unlisted. " (github issue #9300). @@ -5646,7 +6089,7 @@ func Test_lopen_bwipe_all() qall! END call writefile(lines, 'Xscript') - if RunVim([], [], '--clean -n -S Xscript') + if RunVim([], [], '-u NONE -n -X -Z -e -m -s -S Xscript') call assert_equal(['done'], readfile('Xresult')) endif @@ -5654,5 +6097,140 @@ func Test_lopen_bwipe_all() call delete('Xresult') endfunc +" Test for calling setqflist() function recursively +func Test_recursive_setqflist() + augroup QF_Test + au! + autocmd BufWinEnter quickfix call setqflist([], 'r') + augroup END + + copen + call assert_fails("call setqflist([], 'a')", 'E952:') + + augroup QF_Test + au! + augroup END + %bw! +endfunc + +" Test for failure to create a new window when selecting a file from the +" quickfix window +func Test_cwindow_newwin_fails() + cgetexpr ["Xfile1:10:L10", "Xfile1:20:L20"] + cwindow + only + let qf_wid = win_getid() + " create the maximum number of scratch windows + let hor_win_count = (&lines - 1)/2 + let hor_split_count = hor_win_count - 1 + for s in range(1, hor_split_count) | new | set buftype=nofile | endfor + call win_gotoid(qf_wid) + call assert_fails('exe "normal \<CR>"', 'E36:') + %bw! +endfunc + +" Test for updating the location list when only the location list window is +" present and the corresponding file window is closed. +func Test_loclist_update_with_llwin_only() + %bw! + new + wincmd w + lexpr ["Xfile1:1:Line1"] + lopen + wincmd p + close + call setloclist(2, [], 'r', {'lines': ["Xtest2:2:Line2"]}) + call assert_equal(['Xtest2|2| Line2'], getbufline(winbufnr(2), 1, '$')) + %bw! +endfunc + +" Test for getting the quickfix list after a buffer with an error is wiped out +func Test_getqflist_wiped_out_buffer() + %bw! + cexpr ["Xtest1:34:Wiped out"] + let bnum = bufnr('Xtest1') + call assert_equal(bnum, getqflist()[0].bufnr) + bw Xtest1 + call assert_equal(0, getqflist()[0].bufnr) + %bw! +endfunc + +" Test for the status message that is displayed when opening a new quickfix +" list +func Test_qflist_statusmsg() + cexpr "1\n2" + cexpr "1\n2\n3\ntest_quickfix.vim:1:msg" + call assert_equal('(4 of 4): msg', v:statusmsg) + call setqflist([], 'f') + %bw! + + " When creating a new quickfix list, if an autocmd changes the quickfix list + " in the stack, then an error message should be displayed. + augroup QF_Test + au! + au BufEnter test_quickfix.vim colder + augroup END + cexpr "1\n2" + call assert_fails('cexpr "1\n2\n3\ntest_quickfix.vim:1:msg"', 'E925:') + call setqflist([], 'f') + augroup QF_Test + au! + augroup END + %bw! + + augroup QF_Test + au! + au BufEnter test_quickfix.vim caddexpr "4" + augroup END + call assert_fails('cexpr "1\n2\n3\ntest_quickfix.vim:1:msg"', 'E925:') + call setqflist([], 'f') + augroup QF_Test + au! + augroup END + %bw! +endfunc + +func Test_quickfixtextfunc_recursive() + func s:QFTfunc(o) + cgete '0' + endfunc + copen + let &quickfixtextfunc = 's:QFTfunc' + cex "" + + let &quickfixtextfunc = '' + cclose +endfunc + +" Test for replacing the location list from an autocmd. This used to cause a +" read from freed memory. +func Test_loclist_replace_autocmd() + %bw! + call setloclist(0, [], 'f') + let s:bufnr = bufnr() + cal setloclist(0, [{'0': 0, '': ''}]) + au BufEnter * cal setloclist(1, [{'t': ''}, {'bufnr': s:bufnr}], 'r') + lopen + try + exe "norm j\<CR>" + catch + endtry + lnext + %bw! + call setloclist(0, [], 'f') +endfunc + +func s:QfTf(_) +endfunc + +func Test_setqflist_cb_arg() + " This was changing the callback name in the dictionary. + let d = #{quickfixtextfunc: 's:QfTf'} + call setqflist([], 'a', d) + call assert_equal('s:QfTf', d.quickfixtextfunc) + + call setqflist([], 'f') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_quotestar.vim b/src/nvim/testdir/test_quotestar.vim index 93865869fa..6a6719da8b 100644 --- a/src/nvim/testdir/test_quotestar.vim +++ b/src/nvim/testdir/test_quotestar.vim @@ -98,16 +98,17 @@ func Do_test_quotestar_for_x11() " Running in a terminal and the GUI is available: Tell the server to open " the GUI and check that the remote command still works. - " Need to wait for the GUI to start up, otherwise the send hangs in trying - " to send to the terminal window. - if has('gui_athena') || has('gui_motif') + if has('gui_motif') " For those GUIs, ignore the 'failed to create input context' error. call remote_send(name, ":call test_ignore_error('E285') | gui -f\<CR>") else call remote_send(name, ":gui -f\<CR>") endif " Wait for the server in the GUI to be up and answering requests. + " First need to wait for the GUI to start up, otherwise the send hangs in + " trying to send to the terminal window. " On some systems and with valgrind this can be very slow. + sleep 1 call WaitForAssert({-> assert_match("1", remote_expr(name, "has('gui_running')", "", 1))}, 10000) call remote_send(name, ":let @* = 'maybe'\<CR>") diff --git a/src/nvim/testdir/test_random.vim b/src/nvim/testdir/test_random.vim index 6d3f7dcfd9..5fdbfe9cd8 100644 --- a/src/nvim/testdir/test_random.vim +++ b/src/nvim/testdir/test_random.vim @@ -1,5 +1,8 @@ " Tests for srand() and rand() +source check.vim +source shared.vim + func Test_Rand() let r = srand(123456789) call assert_equal([1573771921, 319883699, 2742014374, 1324369493], r) @@ -9,18 +12,9 @@ func Test_Rand() call assert_equal(2658065534, rand(r)) call assert_equal(3104308804, rand(r)) - " Nvim does not support test_settime - " call test_settime(12341234) let s = srand() - if !has('win32') && filereadable('/dev/urandom') - " using /dev/urandom - call assert_notequal(s, srand()) - " else - " " using time() - " call assert_equal(s, srand()) - " call test_settime(12341235) - " call assert_notequal(s, srand()) - endif + " using /dev/urandom or used time, result is different each time + call assert_notequal(s, srand()) " Nvim does not support test_srand_seed " call test_srand_seed(123456789) @@ -33,13 +27,11 @@ func Test_Rand() endif call assert_fails('echo srand([1])', 'E745:') call assert_fails('echo rand("burp")', 'E475:') - call assert_fails('echo rand([1, 2, 3])', 'E475:') - call assert_fails('echo rand([[1], 2, 3, 4])', 'E475:') - call assert_fails('echo rand([1, [2], 3, 4])', 'E475:') - call assert_fails('echo rand([1, 2, [3], 4])', 'E475:') - call assert_fails('echo rand([1, 2, 3, [4]])', 'E475:') - - " call test_settime(0) + call assert_fails('echo rand([1, 2, 3])', 'E730:') + call assert_fails('echo rand([[1], 2, 3, 4])', 'E730:') + call assert_fails('echo rand([1, [2], 3, 4])', 'E730:') + call assert_fails('echo rand([1, 2, [3], 4])', 'E730:') + call assert_fails('echo rand([1, 2, 3, [4]])', 'E730:') endfunc func Test_issue_5587() @@ -48,4 +40,20 @@ func Test_issue_5587() call rand() endfunc +func Test_srand() + CheckNotGui + + let cmd = GetVimCommand() .. ' -V -es -c "echo rand()" -c qa!' + let bad = 0 + for _ in range(10) + echo cmd + let result1 = system(cmd) + let result2 = system(cmd) + if result1 ==# result2 + let bad += 1 + endif + endfor + call assert_inrange(0, 4, bad) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_recover.vim b/src/nvim/testdir/test_recover.vim index fc073cacd2..92e22687af 100644 --- a/src/nvim/testdir/test_recover.vim +++ b/src/nvim/testdir/test_recover.vim @@ -1,5 +1,7 @@ " Test :recover +source check.vim + func Test_recover_root_dir() " This used to access invalid memory. split Xtest @@ -23,6 +25,21 @@ func Test_recover_root_dir() set dir& endfunc +" Make a copy of the current swap file to "Xswap". +" Return the name of the swap file. +func CopySwapfile() + preserve + " get the name of the swap file + let swname = split(execute("swapname"))[0] + let swname = substitute(swname, '[[:blank:][:cntrl:]]*\(.\{-}\)[[:blank:][:cntrl:]]*$', '\1', '') + " make a copy of the swap file in Xswap + set binary + exe 'sp ' . swname + w! Xswap + set nobinary + return swname +endfunc + " Inserts 10000 lines with text to fill the swap file with two levels of pointer " blocks. Then recovers from the swap file and checks all text is restored. " @@ -40,15 +57,9 @@ func Test_swap_file() let i += 1 endwhile $delete - preserve - " get the name of the swap file - let swname = split(execute("swapname"))[0] - let swname = substitute(swname, '[[:blank:][:cntrl:]]*\(.\{-}\)[[:blank:][:cntrl:]]*$', '\1', '') - " make a copy of the swap file in Xswap - set binary - exe 'sp ' . swname - w! Xswap - set nobinary + + let swname = CopySwapfile() + new only! bwipe! Xtest @@ -69,3 +80,388 @@ func Test_swap_file() set undolevels& enew! | only endfunc + +func Test_nocatch_process_still_running() + let g:skipped_reason = 'test_override() is N/A' + return + " sysinfo.uptime probably only works on Linux + if !has('linux') + let g:skipped_reason = 'only works on Linux' + return + endif + " the GUI dialog can't be handled + if has('gui_running') + let g:skipped_reason = 'only works in the terminal' + return + endif + + " don't intercept existing swap file here + au! SwapExists + + " Edit a file and grab its swapfile. + edit Xswaptest + call setline(1, ['a', 'b', 'c']) + let swname = CopySwapfile() + + " Forget we edited this file + new + only! + bwipe! Xswaptest + + call rename('Xswap', swname) + call feedkeys('e', 'tL') + redir => editOutput + edit Xswaptest + redir END + call assert_match('E325: ATTENTION', editOutput) + call assert_match('file name: .*Xswaptest', editOutput) + call assert_match('process ID: \d* (STILL RUNNING)', editOutput) + + " Forget we edited this file + new + only! + bwipe! Xswaptest + + " pretend we rebooted + call test_override("uptime", 0) + sleep 1 + + call feedkeys('e', 'tL') + redir => editOutput + edit Xswaptest + redir END + call assert_match('E325: ATTENTION', editOutput) + call assert_notmatch('(STILL RUNNING)', editOutput) + + call test_override("ALL", 0) + call delete(swname) +endfunc + +" Test for :recover with multiple swap files +func Test_recover_multiple_swap_files() + CheckUnix + new Xfile1 + call setline(1, ['a', 'b', 'c']) + preserve + let b = readblob(swapname('')) + call writefile(b, '.Xfile1.swm') + call writefile(b, '.Xfile1.swn') + call writefile(b, '.Xfile1.swo') + %bw! + call feedkeys(":recover Xfile1\<CR>3\<CR>q", 'xt') + call assert_equal(['a', 'b', 'c'], getline(1, '$')) + " try using out-of-range number to select a swap file + bw! + call feedkeys(":recover Xfile1\<CR>4\<CR>q", 'xt') + call assert_equal('Xfile1', @%) + call assert_equal([''], getline(1, '$')) + bw! + call feedkeys(":recover Xfile1\<CR>0\<CR>q", 'xt') + call assert_equal('Xfile1', @%) + call assert_equal([''], getline(1, '$')) + bw! + + call delete('.Xfile1.swm') + call delete('.Xfile1.swn') + call delete('.Xfile1.swo') +endfunc + +" Test for :recover using an empty swap file +func Test_recover_empty_swap_file() + CheckUnix + call writefile([], '.Xfile1.swp') + let msg = execute('recover Xfile1') + call assert_match('Unable to read block 0 from .Xfile1.swp', msg) + call assert_equal('Xfile1', @%) + bw! + + " make sure there are no old swap files laying around + for f in glob('.sw?', 0, 1) + call delete(f) + endfor + + " :recover from an empty buffer + call assert_fails('recover', 'E305:') + call delete('.Xfile1.swp') +endfunc + +" Test for :recover using a corrupted swap file +" Refer to the comments in the memline.c file for the swap file headers +" definition. +func Test_recover_corrupted_swap_file() + CheckUnix + + " recover using a partial swap file + call writefile(0z1234, '.Xfile1.swp') + call assert_fails('recover Xfile1', 'E295:') + bw! + + " recover using invalid content in the swap file + call writefile([repeat('1', 2*1024)], '.Xfile1.swp') + call assert_fails('recover Xfile1', 'E307:') + call delete('.Xfile1.swp') + + " :recover using a swap file with a corrupted header + edit Xfile1 + preserve + let sn = swapname('') + let b = readblob(sn) + let save_b = copy(b) + bw! + + " Not all fields are written in a system-independent manner. Detect whether + " the test is running on a little or big-endian system, so the correct + " corruption values can be set. + " The B0_MAGIC_LONG field may be 32-bit or 64-bit, depending on the system, + " even though the value stored is only 32-bits. Therefore, need to check + " both the high and low 32-bits to compute these values. + let little_endian = (b[1008:1011] == 0z33323130) || (b[1012:1015] == 0z33323130) + let system_64bit = little_endian ? (b[1012:1015] == 0z00000000) : (b[1008:1011] == 0z00000000) + + " clear the B0_MAGIC_LONG field + if system_64bit + let b[1008:1015] = 0z00000000.00000000 + else + let b[1008:1011] = 0z00000000 + endif + call writefile(b, sn) + let msg = execute('recover Xfile1') + call assert_match('the file has been damaged', msg) + call assert_equal('Xfile1', @%) + call assert_equal([''], getline(1, '$')) + bw! + + " reduce the page size + let b = copy(save_b) + let b[12:15] = 0z00010000 + call writefile(b, sn) + let msg = execute('recover Xfile1') + call assert_match('page size is smaller than minimum value', msg) + call assert_equal('Xfile1', @%) + call assert_equal([''], getline(1, '$')) + bw! + + " clear the pointer ID + let b = copy(save_b) + let b[4096:4097] = 0z0000 + call writefile(b, sn) + call assert_fails('recover Xfile1', 'E310:') + call assert_equal('Xfile1', @%) + call assert_equal([''], getline(1, '$')) + bw! + + " set the number of pointers in a pointer block to zero + let b = copy(save_b) + let b[4098:4099] = 0z0000 + call writefile(b, sn) + call assert_fails('recover Xfile1', 'E312:') + call assert_equal('Xfile1', @%) + call assert_equal(['???EMPTY BLOCK'], getline(1, '$')) + bw! + + " set the block number in a pointer entry to a negative number + let b = copy(save_b) + if system_64bit + let b[4104:4111] = little_endian ? 0z00000000.00000080 : 0z80000000.00000000 + else + let b[4104:4107] = little_endian ? 0z00000080 : 0z80000000 + endif + call writefile(b, sn) + call assert_fails('recover Xfile1', 'E312:') + call assert_equal('Xfile1', @%) + call assert_equal(['???LINES MISSING'], getline(1, '$')) + bw! + + " clear the data block ID + let b = copy(save_b) + let b[8192:8193] = 0z0000 + call writefile(b, sn) + call assert_fails('recover Xfile1', 'E312:') + call assert_equal('Xfile1', @%) + call assert_equal(['???BLOCK MISSING'], getline(1, '$')) + bw! + + " set the number of lines in the data block to zero + let b = copy(save_b) + if system_64bit + let b[8208:8215] = 0z00000000.00000000 + else + let b[8208:8211] = 0z00000000 + endif + call writefile(b, sn) + call assert_fails('recover Xfile1', 'E312:') + call assert_equal('Xfile1', @%) + call assert_equal(['??? from here until ???END lines may have been inserted/deleted', + \ '???END'], getline(1, '$')) + bw! + + " use an invalid text start for the lines in a data block + let b = copy(save_b) + if system_64bit + let b[8216:8219] = 0z00000000 + else + let b[8212:8215] = 0z00000000 + endif + call writefile(b, sn) + call assert_fails('recover Xfile1', 'E312:') + call assert_equal('Xfile1', @%) + call assert_equal(['???'], getline(1, '$')) + bw! + + " use an incorrect text end (db_txt_end) for the data block + let b = copy(save_b) + let b[8204:8207] = little_endian ? 0z80000000 : 0z00000080 + call writefile(b, sn) + call assert_fails('recover Xfile1', 'E312:') + call assert_equal('Xfile1', @%) + call assert_equal(['??? from here until ???END lines may be messed up', '', + \ '???END'], getline(1, '$')) + bw! + + " remove the data block + let b = copy(save_b) + call writefile(b[:8191], sn) + call assert_fails('recover Xfile1', 'E312:') + call assert_equal('Xfile1', @%) + call assert_equal(['???MANY LINES MISSING'], getline(1, '$')) + + bw! + call delete(sn) +endfunc + +" Test for :recover using an encrypted swap file +func Test_recover_encrypted_swap_file() + CheckFeature cryptv + CheckUnix + + " Recover an encrypted file from the swap file without the original file + new Xfile1 + call feedkeys(":X\<CR>vim\<CR>vim\<CR>", 'xt') + call setline(1, ['aaa', 'bbb', 'ccc']) + preserve + let b = readblob('.Xfile1.swp') + call writefile(b, '.Xfile1.swm') + bw! + call feedkeys(":recover Xfile1\<CR>vim\<CR>\<CR>", 'xt') + call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$')) + bw! + call delete('.Xfile1.swm') + + " Recover an encrypted file from the swap file with the original file + new Xfile1 + call feedkeys(":X\<CR>vim\<CR>vim\<CR>", 'xt') + call setline(1, ['aaa', 'bbb', 'ccc']) + update + call setline(1, ['111', '222', '333']) + preserve + let b = readblob('.Xfile1.swp') + call writefile(b, '.Xfile1.swm') + bw! + call feedkeys(":recover Xfile1\<CR>vim\<CR>\<CR>", 'xt') + call assert_equal(['111', '222', '333'], getline(1, '$')) + call assert_true(&modified) + bw! + call delete('.Xfile1.swm') + call delete('Xfile1') +endfunc + +" Test for :recover using a unreadable swap file +func Test_recover_unreadble_swap_file() + CheckUnix + CheckNotRoot + new Xfile1 + let b = readblob('.Xfile1.swp') + call writefile(b, '.Xfile1.swm') + bw! + call setfperm('.Xfile1.swm', '-w-------') + call assert_fails('recover Xfile1', 'E306:') + call delete('.Xfile1.swm') +endfunc + +" Test for using :recover when the original file and the swap file have the +" same contents. +func Test_recover_unmodified_file() + CheckUnix + call writefile(['aaa', 'bbb', 'ccc'], 'Xfile1') + edit Xfile1 + preserve + let b = readblob('.Xfile1.swp') + %bw! + call writefile(b, '.Xfile1.swz') + let msg = execute('recover Xfile1') + call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$')) + call assert_false(&modified) + call assert_match('Buffer contents equals file contents', msg) + bw! + call delete('Xfile1') + call delete('.Xfile1.swz') +endfunc + +" Test for recovering a file when editing a symbolically linked file +func Test_recover_symbolic_link() + CheckUnix + call writefile(['aaa', 'bbb', 'ccc'], 'Xfile1') + silent !ln -s Xfile1 Xfile2 + edit Xfile2 + call assert_equal('.Xfile1.swp', fnamemodify(swapname(''), ':t')) + preserve + let b = readblob('.Xfile1.swp') + %bw! + call writefile([], 'Xfile1') + call writefile(b, '.Xfile1.swp') + silent! recover Xfile2 + call assert_equal(['aaa', 'bbb', 'ccc'], getline(1, '$')) + call assert_true(&modified) + update + %bw! + call assert_equal(['aaa', 'bbb', 'ccc'], readfile('Xfile1')) + call delete('Xfile1') + call delete('Xfile2') + call delete('.Xfile1.swp') +endfunc + +" Test for recovering a file when an autocmd moves the cursor to an invalid +" line. This used to result in an internal error (E315) which is fixed +" by 8.2.2966. +func Test_recover_invalid_cursor_pos() + call writefile([], 'Xfile1') + edit Xfile1 + preserve + let b = readblob('.Xfile1.swp') + bw! + augroup Test + au! + au BufReadPost Xfile1 normal! 3G + augroup END + call writefile(range(1, 3), 'Xfile1') + call writefile(b, '.Xfile1.swp') + try + recover Xfile1 + catch /E308:/ + " this test is for the :E315 internal error. + " ignore the 'E308: Original file may have been changed' error + endtry + redraw! + augroup Test + au! + augroup END + augroup! Test + call delete('Xfile1') + call delete('.Xfile1.swp') +endfunc + +" Test for recovering a buffer without a name +func Test_noname_buffer() + new + call setline(1, ['one', 'two']) + preserve + let sn = swapname('') + let b = readblob(sn) + bw! + call writefile(b, sn) + exe "recover " .. sn + call assert_equal(['one', 'two'], getline(1, '$')) + call delete(sn) +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim index d08a980787..ece6ae518e 100644 --- a/src/nvim/testdir/test_regexp_latin.vim +++ b/src/nvim/testdir/test_regexp_latin.vim @@ -101,8 +101,33 @@ func Test_multi_failure() set re=2 call assert_fails('/a**', 'E871:') call assert_fails('/a*\+', 'E871:') - call assert_fails('/a\{a}', 'E870:') + call assert_fails('/a\{a}', 'E554:') + set re=0 +endfunc + +func Test_column_success_failure() + new + call setline(1, 'xbar') + + set re=1 + %s/\%>0v./A/ + call assert_equal('Abar', getline(1)) + call assert_fails('/\%v', 'E71:') + call assert_fails('/\%>v', 'E71:') + call assert_fails('/\%c', 'E71:') + call assert_fails('/\%<c', 'E71:') + call assert_fails('/\%l', 'E71:') + set re=2 + %s/\%>0v./B/ + call assert_equal('Bbar', getline(1)) + call assert_fails('/\%v', 'E1273:') + call assert_fails('/\%>v', 'E1273:') + call assert_fails('/\%c', 'E1273:') + call assert_fails('/\%<c', 'E1273:') + call assert_fails('/\%l', 'E1273:') + set re=0 + bwipe! endfunc func Test_recursive_addstate() @@ -146,6 +171,10 @@ func Test_regexp_single_line_pat() call add(tl, [2, 'c*', 'abdef', '']) call add(tl, [2, 'bc\+', 'abccccdef', 'bcccc']) call add(tl, [2, 'bc\+', 'abdef']) " no match + " match escape character in a string + call add(tl, [2, '.\e.', "one\<Esc>two", "e\<Esc>t"]) + " match backspace character in a string + call add(tl, [2, '.\b.', "one\<C-H>two", "e\<C-H>t"]) " match newline character in a string call add(tl, [2, 'o\nb', "foo\nbar", "o\nb"]) @@ -895,6 +924,8 @@ func Test_regexp_error() call assert_fails("call matchlist('x x', '\\%#=2 \\zs*')", 'E888:') call assert_fails("call matchlist('x x', '\\%#=2 \\ze*')", 'E888:') call assert_fails('exe "normal /\\%#=1\\%[x\\%[x]]\<CR>"', 'E369:') + call assert_fails("call matchstr('abcd', '\\%o841\\%o142')", 'E678:') + call assert_equal('', matchstr('abcd', '\%o181\%o142')) endfunc " Test for using the last substitute string pattern (~) @@ -1027,6 +1058,28 @@ func Test_using_invalid_visual_position() bwipe! endfunc +func Test_using_two_engines_pattern() + new + call setline(1, ['foobar=0', 'foobar=1', 'foobar=2']) + " \%#= at the end of the pattern + for i in range(0, 2) + for j in range(0, 2) + exe "set re=" .. i + call cursor(j + 1, 7) + call assert_fails("%s/foobar\\%#=" .. j, 'E1281:') + endfor + endfor + set re=0 + + " \%#= at the start of the pattern + for i in range(0, 2) + call cursor(i + 1, 7) + exe ":%s/\\%#=" .. i .. "foobar=" .. i .. "/xx" + endfor + call assert_equal(['xx', 'xx', 'xx'], getline(1, '$')) + bwipe! +endfunc + func Test_recursive_substitute_expr() new func Repl() diff --git a/src/nvim/testdir/test_regexp_utf8.vim b/src/nvim/testdir/test_regexp_utf8.vim index 14b9724d67..2253242a7c 100644 --- a/src/nvim/testdir/test_regexp_utf8.vim +++ b/src/nvim/testdir/test_regexp_utf8.vim @@ -258,8 +258,7 @@ func Test_multibyte_chars() " When there is no match use only the first two items. let tl = [] - " Multi-byte character tests. These will fail unless vim is compiled - " with Multibyte (FEAT_MBYTE) or BIG/HUGE features. + " Multi-byte character tests. call add(tl, [2, '[[:alpha:][=a=]]\+', '879 aiaãâaiuvna ', 'aiaãâaiuvna']) call add(tl, [2, '[[=a=]]\+', 'ddaãâbcd', 'aãâ']) " equivalence classes call add(tl, [2, '[^ม ]\+', 'มม oijasoifjos ifjoisj f osij j มมมมม abcd', 'oijasoifjos']) diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index 11dd3badb6..bbf1aa53b5 100644 --- a/src/nvim/testdir/test_registers.vim +++ b/src/nvim/testdir/test_registers.vim @@ -263,8 +263,16 @@ func Test_get_register() call assert_equal('', getreg("\<C-F>")) call assert_equal('', getreg("\<C-W>")) call assert_equal('', getreg("\<C-L>")) + " Change the last used register to '"' for the next test + normal! ""yy + let @" = 'happy' + call assert_equal('happy', getreg()) + call assert_equal('happy', getreg('')) call assert_equal('', getregtype('!')) + call assert_fails('echo getregtype([])', 'E730:') + call assert_equal('v', getregtype()) + call assert_equal('v', getregtype('')) " Test for inserting an invalid register content call assert_beeps('exe "normal i\<C-R>!"') @@ -277,7 +285,9 @@ func Test_get_register() " Test for inserting a multi-line register in the command line call feedkeys(":\<C-R>r\<Esc>", 'xt') - call assert_equal("a\rb", histget(':', -1)) " Modified because of #6137 + " Nvim: no trailing CR because of #6137 + " call assert_equal("a\rb\r", histget(':', -1)) + call assert_equal("a\rb", histget(':', -1)) call assert_fails('let r = getreg("=", [])', 'E745:') call assert_fails('let r = getreg("=", 1, [])', 'E745:') @@ -289,6 +299,7 @@ endfunc func Test_set_register() call assert_fails("call setreg('#', 200)", 'E86:') + " call assert_fails("call setreg('a', test_unknown())", 'E908:') edit Xfile_alt_1 let b1 = bufnr('') @@ -349,6 +360,12 @@ func Test_set_register() normal 0".gP call assert_equal('abcabcabc', getline(1)) + let @"='' + call setreg('', '1') + call assert_equal('1', @") + call setreg('@', '2') + call assert_equal('2', @") + enew! endfunc @@ -474,6 +491,21 @@ func Test_get_reginfo() let info = getreginfo('"') call assert_equal('z', info.points_to) + let @a="a1b2" + nnoremap <F2> <Cmd>let g:RegInfo = getreginfo()<CR> + exe "normal \"a\<F2>" + call assert_equal({'regcontents': ['a1b2'], 'isunnamed': v:false, + \ 'regtype': 'v'}, g:RegInfo) + nunmap <F2> + unlet g:RegInfo + + " The type of "isunnamed" was VAR_SPECIAL but should be VAR_BOOL. Can only + " be noticed when using json_encod(). + call setreg('a', 'foo') + let reginfo = getreginfo('a') + let expected = #{regcontents: ['foo'], isunnamed: v:false, regtype: 'v'} + call assert_equal(json_encode(expected), json_encode(reginfo)) + bwipe! endfunc diff --git a/src/nvim/testdir/test_reltime.vim b/src/nvim/testdir/test_reltime.vim index b381f1ddbb..f4ce5de118 100644 --- a/src/nvim/testdir/test_reltime.vim +++ b/src/nvim/testdir/test_reltime.vim @@ -24,4 +24,8 @@ func Test_reltime() call assert_true(reltimefloat(differs) < 0.1) call assert_true(reltimefloat(differs) > 0.0) + call assert_equal(0, reltime({})) + call assert_equal(0, reltime({}, {})) endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_retab.vim b/src/nvim/testdir/test_retab.vim index 1650a03876..a4f95053c0 100644 --- a/src/nvim/testdir/test_retab.vim +++ b/src/nvim/testdir/test_retab.vim @@ -1,4 +1,7 @@ " Test :retab + +source check.vim + func SetUp() new call setline(1, "\ta \t b c ") @@ -81,19 +84,32 @@ func Test_retab_error() call assert_fails('ret 80000000000000000000', 'E475:') endfunc +func RetabLoop() + while 1 + set ts=4000 + retab 4 + endwhile +endfunc + func Test_retab_endless() - new + " inside try/catch we can catch the error message call setline(1, "\t0\t") let caught = 'no' try - while 1 - set ts=4000 - retab 4 - endwhile - catch /E1240/ - let caught = 'yes' + call RetabLoop() + catch /E1240:/ + let caught = v:exception endtry - bwipe! + call assert_match('E1240:', caught) + + set tabstop& +endfunc + +func Test_nocatch_retab_endless() + " when not inside try/catch an interrupt is generated to get out of loops + call setline(1, "\t0\t") + call assert_fails('call RetabLoop()', ['E1240:', 'Interrupted']) + set tabstop& endfunc diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim index 3d1bbfb726..885043accf 100644 --- a/src/nvim/testdir/test_search.vim +++ b/src/nvim/testdir/test_search.vim @@ -294,6 +294,9 @@ 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('.')) @@ -897,9 +900,7 @@ func Test_incsearch_cmdline_modifier() endfunc func Test_incsearch_scrolling() - if !CanRunVimInTerminal() - throw 'Skipped: cannot make screendumps' - endif + CheckRunVimInTerminal call assert_equal(0, &scrolloff) call writefile([ \ 'let dots = repeat(".", 120)', @@ -956,24 +957,24 @@ func Test_incsearch_search_dump() call delete('Xis_search_script') endfunc -func Test_hlsearch_block_visual_match() +func Test_hlsearch_dump() + CheckOption hlsearch 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 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, "G\<C-V>$kk\<Esc>") - sleep 100m - call term_sendkeys(buf, "/\\%V\<CR>") - sleep 100m - call VerifyScreenDump(buf, 'Test_hlsearch_block_visual_match', {}) + call term_sendkeys(buf, "/\\_.*\<CR>") + call VerifyScreenDump(buf, 'Test_hlsearch_2', {}) call StopVimInTerminal(buf) - call delete('Xhlsearch_block') + call delete('Xhlsearch_script') endfunc func Test_hlsearch_and_visual() @@ -996,6 +997,26 @@ func Test_hlsearch_and_visual() 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\<C-V>$kk\<Esc>") + sleep 100m + call term_sendkeys(buf, "/\\%V\<CR>") + sleep 100m + call VerifyScreenDump(buf, 'Test_hlsearch_block_visual_match', {}) + + call StopVimInTerminal(buf) + call delete('Xhlsearch_block') +endfunc + func Test_incsearch_substitute() CheckFunction test_override CheckOption incsearch @@ -1017,6 +1038,21 @@ func Test_incsearch_substitute() call Incsearch_cleanup() endfunc +func Test_incsearch_substitute_long_line() + CheckFunction test_override + new + call test_override("char_avail", 1) + set incsearch + + call repeat('x', 100000)->setline(1) + call feedkeys(':s/\%c', 'xt') + redraw + call feedkeys("\<Esc>", 'xt') + + call Incsearch_cleanup() + bwipe! +endfunc + func Test_hlsearch_cursearch() CheckScreendump @@ -1341,18 +1377,19 @@ func Test_subst_word_under_cursor() set noincsearch endfunc -func Test_incsearch_substitute_long_line() - CheckFunction test_override - new - call test_override("char_avail", 1) - set incsearch - - call repeat('x', 100000)->setline(1) - call feedkeys(':s/\%c', 'xt') - redraw - call feedkeys("\<Esc>", 'xt') +func Test_search_skip_all_matches() + enew + call setline(1, ['no match here', + \ 'match this line', + \ 'nope', + \ 'match in this line', + \ 'last line', + \ ]) + call cursor(1, 1) + let lnum = search('this', '', 0, 0, 'getline(".") =~ "this line"') + " Only check that no match is found. Previously it searched forever. + call assert_equal(0, lnum) - call Incsearch_cleanup() bwipe! endfunc @@ -1629,8 +1666,8 @@ func Test_search_with_no_last_pat() call assert_fails(";//p", 'E35:') call assert_fails("??p", 'E35:') call assert_fails(";??p", 'E35:') - call assert_fails('g//p', 'E476:') - call assert_fails('v//p', 'E476:') + call assert_fails('g//p', ['E35:', 'E476:']) + call assert_fails('v//p', ['E35:', 'E476:']) call writefile(v:errors, 'Xresult') qall! [SCRIPT] @@ -1651,8 +1688,8 @@ func Test_search_tilde_pat() call assert_fails('exe "normal /~\<CR>"', 'E33:') call assert_fails('exe "normal ?~\<CR>"', 'E33:') set regexpengine=2 - call assert_fails('exe "normal /~\<CR>"', 'E383:') - call assert_fails('exe "normal ?~\<CR>"', 'E383:') + call assert_fails('exe "normal /~\<CR>"', ['E33:', 'E383:']) + call assert_fails('exe "normal ?~\<CR>"', ['E33:', 'E383:']) set regexpengine& call writefile(v:errors, 'Xresult') qall! @@ -1732,6 +1769,25 @@ func Test_invalid_regexp() 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(l[4], v) +endfunc + +" Test for the \%V atom to search within visually selected text +func Test_search_in_visual_area() + new + call setline(1, ['foo bar1', 'foo bar2', 'foo bar3', 'foo bar4']) + exe "normal 2GVjo/\\%Vbar\<CR>\<Esc>" + call assert_equal([2, 5], [line('.'), col('.')]) + exe "normal 2GVj$?\\%Vbar\<CR>\<Esc>" + call assert_equal([3, 5], [line('.'), col('.')]) + close! +endfunc + " Test for searching with 'smartcase' and 'ignorecase' func Test_search_smartcase() new @@ -1776,7 +1832,7 @@ func Test_search_smartcase_utf8() set ignorecase& smartcase& let &encoding = save_enc - close! + bwipe! endfunc " Test searching past the end of a file @@ -1785,7 +1841,29 @@ func Test_search_past_eof() call setline(1, ['Line']) exe "normal /\\n\\zs\<CR>" call assert_equal([1, 4], [line('.'), col('.')]) - close! + bwipe! +endfunc + +" Test setting the start of the match and still finding a next match in the +" same line. +func Test_search_set_start_same_line() + new + set cpo-=c + + call setline(1, ['1', '2', '3 .', '4', '5']) + exe "normal /\\_s\\zs\\S\<CR>" + call assert_equal([2, 1], [line('.'), col('.')]) + exe 'normal n' + call assert_equal([3, 1], [line('.'), col('.')]) + exe 'normal n' + call assert_equal([3, 3], [line('.'), col('.')]) + exe 'normal n' + call assert_equal([4, 1], [line('.'), col('.')]) + exe 'normal n' + call assert_equal([5, 1], [line('.'), col('.')]) + + set cpo+=c + bwipe! endfunc " Test for various search offsets @@ -1907,6 +1985,100 @@ func Test_incsearch_highlighting_newline() 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, "\<Esc>") + + " 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$\<esc>" + " \%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$\<esc>" + " \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$\<esc>" + " \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 diff --git a/src/nvim/testdir/test_search_stat.vim b/src/nvim/testdir/test_search_stat.vim index 89e09cf85b..77bd50ada2 100644 --- a/src/nvim/testdir/test_search_stat.vim +++ b/src/nvim/testdir/test_search_stat.vim @@ -260,6 +260,14 @@ endfunc func Test_searchcount_fails() call assert_fails('echo searchcount("boo!")', 'E715:') + call assert_fails('echo searchcount({"timeout" : []})', 'E745:') + call assert_fails('echo searchcount({"maxcount" : []})', 'E745:') + call assert_fails('echo searchcount({"pattern" : []})', 'E730:') + call assert_fails('echo searchcount({"pos" : 1})', 'E475:') + call assert_fails('echo searchcount({"pos" : [1]})', 'E475:') + call assert_fails('echo searchcount({"pos" : [[], 2, 3]})', 'E745:') + call assert_fails('echo searchcount({"pos" : [1, [], 3]})', 'E745:') + call assert_fails('echo searchcount({"pos" : [1, 2, []]})', 'E745:') endfunc func Test_searchcount_in_statusline() diff --git a/src/nvim/testdir/test_selectmode.vim b/src/nvim/testdir/test_selectmode.vim index f2cab45450..041f0592f1 100644 --- a/src/nvim/testdir/test_selectmode.vim +++ b/src/nvim/testdir/test_selectmode.vim @@ -34,6 +34,9 @@ func Test_selectmode_start() set selectmode=cmd call feedkeys('gvabc', 'xt') call assert_equal('abctdef', getline(1)) + " arrow keys without shift should not start selection + call feedkeys("A\<Home>\<Right>\<Left>ro", 'xt') + call assert_equal('roabctdef', getline(1)) set selectmode= keymodel= bw! endfunc diff --git a/src/nvim/testdir/test_set.vim b/src/nvim/testdir/test_set.vim index 2b1e9eeee0..7215772a00 100644 --- a/src/nvim/testdir/test_set.vim +++ b/src/nvim/testdir/test_set.vim @@ -26,4 +26,23 @@ function Test_set_add() let &wig = wig_save endfunction + +" :set, :setlocal, :setglobal without arguments show values of options. +func Test_set_no_arg() + set textwidth=79 + let a = execute('set') + call assert_match("^\n--- Options ---\n.*textwidth=79\\>", a) + set textwidth& + + setlocal textwidth=78 + let a = execute('setlocal') + call assert_match("^\n--- Local option values ---\n.*textwidth=78\\>", a) + setlocal textwidth& + + setglobal textwidth=77 + let a = execute('setglobal') + call assert_match("^\n--- Global option values ---\n.*textwidth=77\\>", a) + setglobal textwidth& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_shell.vim b/src/nvim/testdir/test_shell.vim new file mode 100644 index 0000000000..8b9c7a5b12 --- /dev/null +++ b/src/nvim/testdir/test_shell.vim @@ -0,0 +1,209 @@ +" Test for the shell related options ('shell', 'shellcmdflag', 'shellpipe', +" 'shellquote', 'shellredir', 'shellxescape', and 'shellxquote') + +source check.vim +source shared.vim + +func Test_shell_options() + " The expected value of 'shellcmdflag', 'shellpipe', 'shellquote', + " 'shellredir', 'shellxescape', 'shellxquote' for the supported shells. + let shells = [] + if has('unix') + let shells += [['sh', '-c', '2>&1| tee', '', '>%s 2>&1', '', ''], + \ ['ksh', '-c', '2>&1| tee', '', '>%s 2>&1', '', ''], + \ ['mksh', '-c', '2>&1| tee', '', '>%s 2>&1', '', ''], + \ ['zsh', '-c', '2>&1| tee', '', '>%s 2>&1', '', ''], + \ ['zsh-beta', '-c', '2>&1| tee', '', '>%s 2>&1', '', ''], + \ ['bash', '-c', '2>&1| tee', '', '>%s 2>&1', '', ''], + \ ['fish', '-c', '2>&1| tee', '', '>%s 2>&1', '', ''], + \ ['ash', '-c', '2>&1| tee', '', '>%s 2>&1', '', ''], + \ ['dash', '-c', '2>&1| tee', '', '>%s 2>&1', '', ''], + \ ['csh', '-c', '|& tee', '', '>&', '', ''], + \ ['tcsh', '-c', '|& tee', '', '>&', '', '']] + endif + if has('win32') + let shells += [['cmd', '/s /c', '>%s 2>&1', '', '>%s 2>&1', '', '"']] + endif + + " start a new Vim instance with 'shell' set to each of the supported shells + " and check the default shell option settings + let after =<< trim END + let l = [&shell, &shellcmdflag, &shellpipe, &shellquote] + let l += [&shellredir, &shellxescape, &shellxquote] + call writefile([json_encode(l)], 'Xtestout') + qall! + END + for e in shells + if RunVim([], after, '--cmd "set shell=' .. e[0] .. '"') + call assert_equal(e, json_decode(readfile('Xtestout')[0])) + endif + endfor + + " Test shellescape() for each of the shells. + for e in shells + exe 'set shell=' .. e[0] + if e[0] =~# '.*csh$' || e[0] =~# '.*csh.exe$' + let str1 = "'cmd \"arg1\" '\\''arg2'\\'' \\!%#'" + let str2 = "'cmd \"arg1\" '\\''arg2'\\'' \\\\!\\%\\#'" + elseif e[0] =~# '.*powershell$' || e[0] =~# '.*powershell.exe$' + let str1 = "'cmd \"arg1\" ''arg2'' !%#'" + let str2 = "'cmd \"arg1\" ''arg2'' \\!\\%\\#'" + else + let str1 = "'cmd \"arg1\" '\\''arg2'\\'' !%#'" + let str2 = "'cmd \"arg1\" '\\''arg2'\\'' \\!\\%\\#'" + endif + call assert_equal(str1, shellescape("cmd \"arg1\" 'arg2' !%#"), e[0]) + call assert_equal(str2, shellescape("cmd \"arg1\" 'arg2' !%#", 1), e[0]) + + " Try running an external command with the shell. + if executable(e[0]) + " set the shell options for the current 'shell' + let [&shellcmdflag, &shellpipe, &shellquote, &shellredir, + \ &shellxescape, &shellxquote] = e[1:6] + new + r !echo hello + call assert_equal('hello', substitute(getline(2), '\W', '', 'g'), e[0]) + bwipe! + endif + endfor + set shell& shellcmdflag& shellpipe& shellquote& + set shellredir& shellxescape& shellxquote& + call delete('Xtestout') +endfunc + +" Test for the 'shell' option +func Test_shell() + throw 'Skipped: Nvim missing :shell currently' + CheckUnix + let save_shell = &shell + set shell= + let caught_e91 = 0 + try + shell + catch /E91:/ + let caught_e91 = 1 + endtry + call assert_equal(1, caught_e91) + let &shell = save_shell +endfunc + +" Test for the 'shellquote' option +func Test_shellquote() + CheckUnix + set shellquote=# + set verbose=20 + redir => v + silent! !echo Hello + redir END + set verbose& + set shellquote& + call assert_match(': "#echo Hello#"', v) +endfunc + +" Test for the 'shellescape' option +func Test_shellescape() + let save_shell = &shell + set shell=bash + call assert_equal("'text'", shellescape('text')) + call assert_equal("'te\"xt'", 'te"xt'->shellescape()) + call assert_equal("'te'\\''xt'", shellescape("te'xt")) + + call assert_equal("'te%xt'", shellescape("te%xt")) + call assert_equal("'te\\%xt'", shellescape("te%xt", 1)) + call assert_equal("'te#xt'", shellescape("te#xt")) + call assert_equal("'te\\#xt'", shellescape("te#xt", 1)) + call assert_equal("'te!xt'", shellescape("te!xt")) + call assert_equal("'te\\!xt'", shellescape("te!xt", 1)) + + call assert_equal("'te\nxt'", shellescape("te\nxt")) + call assert_equal("'te\\\nxt'", shellescape("te\nxt", 1)) + set shell=tcsh + call assert_equal("'te\\!xt'", shellescape("te!xt")) + call assert_equal("'te\\\\!xt'", shellescape("te!xt", 1)) + call assert_equal("'te\\\nxt'", shellescape("te\nxt")) + call assert_equal("'te\\\\\nxt'", shellescape("te\nxt", 1)) + + let &shell = save_shell +endfunc + +" Test for 'shellslash' +func Test_shellslash() + CheckOption shellslash + let save_shellslash = &shellslash + " The shell and cmdflag, and expected slash in tempname with shellslash set or + " unset. The assert checks the file separator before the leafname. + " ".*\\\\[^\\\\]*$" + let shells = [['cmd', '/c', '/', '/'], + \ ['powershell', '-Command', '/', '/'], + \ ['sh', '-c', '/', '/']] + for e in shells + exe 'set shell=' .. e[0] .. ' | set shellcmdflag=' .. e[1] + set noshellslash + let file = tempname() + call assert_match('^.\+' .. e[2] .. '[^' .. e[2] .. ']\+$', file, e[0] .. ' ' .. e[1] .. ' nossl') + set shellslash + let file = tempname() + call assert_match('^.\+' .. e[3] .. '[^' .. e[3] .. ']\+$', file, e[0] .. ' ' .. e[1] .. ' ssl') + endfor + let &shellslash = save_shellslash +endfunc + +" Test for 'shellxquote' +func Test_shellxquote() + CheckUnix + + let save_shell = &shell + let save_sxq = &shellxquote + let save_sxe = &shellxescape + + call writefile(['#!/bin/sh', 'echo "Cmd: [$*]" > Xlog'], 'Xtestshell') + call setfperm('Xtestshell', "r-x------") + set shell=./Xtestshell + + set shellxquote=\\" + call feedkeys(":!pwd\<CR>\<CR>", 'xt') + call assert_equal(['Cmd: [-c "pwd"]'], readfile('Xlog')) + + set shellxquote=( + call feedkeys(":!pwd\<CR>\<CR>", 'xt') + call assert_equal(['Cmd: [-c (pwd)]'], readfile('Xlog')) + + set shellxquote=\\"( + call feedkeys(":!pwd\<CR>\<CR>", 'xt') + call assert_equal(['Cmd: [-c "(pwd)"]'], readfile('Xlog')) + + set shellxescape=\"&<<()@^ + set shellxquote=( + call feedkeys(":!pwd\"&<<{}@^\<CR>\<CR>", 'xt') + call assert_equal(['Cmd: [-c (pwd^"^&^<^<{}^@^^)]'], readfile('Xlog')) + + let &shell = save_shell + let &shellxquote = save_sxq + let &shellxescape = save_sxe + call delete('Xtestshell') + call delete('Xlog') +endfunc + +" Test for using the shell set in the $SHELL environment variable +func Test_set_shell() + let after =<< trim [CODE] + call writefile([&shell], "Xtestout") + quit! + [CODE] + + if has('win32') + let $SHELL = 'C:\with space\cmd.exe' + let expected = '"C:\with space\cmd.exe"' + else + let $SHELL = '/bin/with space/sh' + let expected = '"/bin/with space/sh"' + endif + + if RunVimPiped([], after, '', '') + let lines = readfile('Xtestout') + call assert_equal(expected, lines[0]) + endif + call delete('Xtestout') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_signals.vim b/src/nvim/testdir/test_signals.vim index 338c0d79ff..c291c68e0d 100644 --- a/src/nvim/testdir/test_signals.vim +++ b/src/nvim/testdir/test_signals.vim @@ -16,8 +16,9 @@ endfunc " Test signal WINCH (window resize signal) func Test_signal_WINCH() throw 'skipped: Nvim cannot avoid terminal resize' - if has('gui_running') || !HasSignal('WINCH') - return + CheckNotGui + if !HasSignal('WINCH') + throw 'Skipped: WINCH signal not supported' endif " We do not actually want to change the size of the terminal. @@ -52,7 +53,7 @@ endfunc " Test signal PWR, which should update the swap file. func Test_signal_PWR() if !HasSignal('PWR') - return + throw 'Skipped: PWR signal not supported' endif " Set a very large 'updatetime' and 'updatecount', so that we can be sure @@ -78,6 +79,33 @@ func Test_signal_PWR() set updatetime& updatecount& endfunc +" Test signal INT. Handler sets got_int. It should be like typing CTRL-C. +func Test_signal_INT() + CheckRunVimInTerminal + if !HasSignal('INT') + throw 'Skipped: INT signal not supported' + endif + + " Skip the rest of the test when running with valgrind as signal INT is not + " received somehow by Vim when running with valgrind. + let cmd = GetVimCommand() + if cmd =~ 'valgrind' + throw 'Skipped: cannot test signal INT with valgrind' + endif + + let buf = RunVimInTerminal('', {'rows': 6}) + let pid_vim = term_getjob(buf)->job_info().process + + " Check that an endless loop in Vim is interrupted by signal INT. + call term_sendkeys(buf, ":while 1 | endwhile\n") + call WaitForAssert({-> assert_equal(':while 1 | endwhile', term_getline(buf, 6))}) + exe 'silent !kill -s INT ' .. pid_vim + call term_sendkeys(buf, ":call setline(1, 'INTERUPTED')\n") + call WaitForAssert({-> assert_equal('INTERUPTED', term_getline(buf, 1))}) + + call StopVimInTerminal(buf) +endfunc + " Test a deadly signal. " " There are several deadly signals: SISEGV, SIBUS, SIGTERM... @@ -91,9 +119,7 @@ func Test_deadly_signal_TERM() if !HasSignal('TERM') throw 'Skipped: TERM signal not supported' endif - if !CanRunVimInTerminal() - throw 'Skipped: cannot run vim in terminal' - endif + CheckRunVimInTerminal let cmd = GetVimCommand() if cmd =~ 'valgrind' throw 'Skipped: cannot test signal TERM with valgrind' @@ -128,8 +154,7 @@ func Test_deadly_signal_TERM() call assert_equal(['foo'], getline(1, '$')) let result = readfile('XautoOut') - call assert_match('VimLeavePre triggered', result[0]) - call assert_match('VimLeave triggered', result[1]) + call assert_equal(["VimLeavePre triggered", "VimLeave triggered"], result) %bwipe! call delete('.Xsig_TERM.swp') diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim index ff9ba3d8ed..8311955a15 100644 --- a/src/nvim/testdir/test_signs.vim +++ b/src/nvim/testdir/test_signs.vim @@ -15,13 +15,13 @@ func Test_sign() " the icon name when listing signs. sign define Sign1 text=x - call Sign_command_ignore_error('sign define Sign2 text=xy texthl=Title linehl=Error culhl=Search icon=../../pixmaps/stock_vim_find_help.png') + call Sign_command_ignore_error('sign define Sign2 text=xy texthl=Title linehl=Error culhl=Search numhl=Number icon=../../pixmaps/stock_vim_find_help.png') " Test listing signs. let a=execute('sign list') call assert_match('^\nsign Sign1 text=x \nsign Sign2 ' . \ 'icon=../../pixmaps/stock_vim_find_help.png .*text=xy ' . - \ 'linehl=Error texthl=Title culhl=Search$', a) + \ 'linehl=Error texthl=Title culhl=Search numhl=Number$', a) let a=execute('sign list Sign1') call assert_equal("\nsign Sign1 text=x ", a) @@ -127,26 +127,34 @@ func Test_sign() call assert_fails("sign define Sign4 text=\\ ab linehl=Comment", 'E239:') " an empty highlight argument for an existing sign clears it - sign define SignY texthl=TextHl culhl=CulHl linehl=LineHl + sign define SignY texthl=TextHl culhl=CulHl linehl=LineHl numhl=NumHl let sl = sign_getdefined('SignY')[0] call assert_equal('TextHl', sl.texthl) call assert_equal('CulHl', sl.culhl) call assert_equal('LineHl', sl.linehl) + call assert_equal('NumHl', sl.numhl) - sign define SignY texthl= culhl=CulHl linehl=LineHl + sign define SignY texthl= culhl=CulHl linehl=LineHl numhl=NumHl let sl = sign_getdefined('SignY')[0] call assert_false(has_key(sl, 'texthl')) call assert_equal('CulHl', sl.culhl) call assert_equal('LineHl', sl.linehl) + call assert_equal('NumHl', sl.numhl) sign define SignY linehl= let sl = sign_getdefined('SignY')[0] call assert_false(has_key(sl, 'linehl')) call assert_equal('CulHl', sl.culhl) + call assert_equal('NumHl', sl.numhl) sign define SignY culhl= let sl = sign_getdefined('SignY')[0] call assert_false(has_key(sl, 'culhl')) + call assert_equal('NumHl', sl.numhl) + + sign define SignY numhl= + let sl = sign_getdefined('SignY')[0] + call assert_false(has_key(sl, 'numhl')) sign undefine SignY @@ -158,7 +166,7 @@ func Test_sign() sign define Sign5 text=X\ linehl=Comment sign undefine Sign5 - sign define Sign5 linehl=Comment text=X\ + sign define Sign5 linehl=Comment text=X\ sign undefine Sign5 " define sign with backslash @@ -417,8 +425,8 @@ func Test_sign_funcs() let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Error', \ 'culhl': 'Visual', 'numhl': 'Number'} call assert_equal(0, "sign1"->sign_define(attr)) - call assert_equal([{'name' : 'sign1', 'texthl' : 'Error', 'linehl': 'Search', - \ 'culhl': 'Visual', 'numhl': 'Number', 'text' : '=>'}], + call assert_equal([{'name' : 'sign1', 'texthl' : 'Error', 'linehl' : 'Search', + \ 'culhl' : 'Visual', 'numhl': 'Number', 'text' : '=>'}], \ sign_getdefined()) " Define a new sign without attributes and then update it @@ -483,13 +491,13 @@ func Test_sign_funcs() call assert_fails('call sign_place(5, "", "sign1", "@", {"lnum" : 10})', \ 'E158:') call assert_fails('call sign_place(5, "", "sign1", [], {"lnum" : 10})', - \ 'E158:') + \ 'E730:') call assert_fails('call sign_place(21, "", "sign1", "Xsign", \ {"lnum" : -1})', 'E474:') call assert_fails('call sign_place(22, "", "sign1", "Xsign", \ {"lnum" : 0})', 'E474:') call assert_fails('call sign_place(22, "", "sign1", "Xsign", - \ {"lnum" : []})', 'E474:') + \ {"lnum" : []})', 'E745:') call assert_equal(-1, sign_place(1, "*", "sign1", "Xsign", {"lnum" : 10})) " Tests for sign_getplaced() @@ -535,9 +543,9 @@ func Test_sign_funcs() call assert_equal(15, sign_place(15, '', 'sign1', 'Xsign', {'lnum' : 20})) call assert_equal(15, sign_place(15, '', 'sign2', 'Xsign')) call assert_equal([{'bufnr' : bufnr(''), 'signs' : - \ [{'id' : 15, 'group' : '', 'lnum' : 20, 'name' : 'sign2', - \ 'priority' : 10}]}], - \ sign_getplaced()) + \ [{'id' : 15, 'group' : '', 'lnum' : 20, 'name' : 'sign2', + \ 'priority' : 10}]}], + \ sign_getplaced()) " Tests for sign_undefine() call assert_equal(0, sign_undefine("sign1")) @@ -1165,7 +1173,7 @@ func Test_sign_unplace() call delete("Xsign2") endfunc -" Tests for auto-generating the sign identifier +" Tests for auto-generating the sign identifier. func Test_aaa_sign_id_autogen() enew | only call sign_unplace('*') @@ -1650,10 +1658,34 @@ func Test_sign_lnum_adjust() " changes made by this function. let &undolevels=&undolevels + " Nvim: make sign adjustment when deleting lines match Vim + set signcolumn=yes:1 + " Delete the line with the sign call deletebufline('', 4) let l = sign_getplaced(bufnr('')) - call assert_equal(0, len(l[0].signs)) + call assert_equal(4, l[0].signs[0].lnum) + + " Undo the delete operation + undo + let l = sign_getplaced(bufnr('')) + call assert_equal(5, l[0].signs[0].lnum) + + " Break the undo + let &undolevels=&undolevels + + " Delete few lines at the end of the buffer including the line with the sign + " Sign line number should not change (as it is placed outside of the buffer) + call deletebufline('', 3, 6) + let l = sign_getplaced(bufnr('')) + call assert_equal(5, l[0].signs[0].lnum) + + " Undo the delete operation. Sign should be restored to the previous line + undo + let l = sign_getplaced(bufnr('')) + call assert_equal(5, l[0].signs[0].lnum) + + set signcolumn& sign unplace * group=* sign undefine sign1 @@ -1731,7 +1763,7 @@ func Test_sign_jump_func() call assert_fails("call sign_jump(5, 'g5', 'foo')", 'E157:') call assert_fails('call sign_jump([], "", "foo")', 'E745:') call assert_fails('call sign_jump(2, [], "foo")', 'E730:') - call assert_fails('call sign_jump(2, "", {})', 'E158:') + call assert_fails('call sign_jump(2, "", {})', 'E731:') call assert_fails('call sign_jump(2, "", "baz")', 'E158:') sign unplace * group=* @@ -1741,9 +1773,7 @@ endfunc " Test for correct cursor position after the sign column appears or disappears. func Test_sign_cursor_position() - if !CanRunVimInTerminal() - throw 'Skipped: cannot make screendumps' - endif + CheckRunVimInTerminal let lines =<< trim END call setline(1, [repeat('x', 75), 'mmmm', 'yyyy']) @@ -1790,8 +1820,8 @@ func Test_sign_numcol() set number set signcolumn=number sign define sign1 text==> - sign place 10 line=1 name=sign1 sign define sign2 text=V + sign place 10 line=1 name=sign1 redraw! call assert_equal("=> 01234", s:ScreenLine(1, 1, 8)) diff --git a/src/nvim/testdir/test_sleep.vim b/src/nvim/testdir/test_sleep.vim index f71855fd4b..a428f380b0 100644 --- a/src/nvim/testdir/test_sleep.vim +++ b/src/nvim/testdir/test_sleep.vim @@ -21,6 +21,7 @@ func! Test_sleep_bang() call s:assert_takes_longer('sl 50m', 50) call s:assert_takes_longer('sl! 50m', 50) call s:assert_takes_longer('1sleep', 1000) + call s:assert_takes_longer('normal 1gs', 1000) endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_sort.vim b/src/nvim/testdir/test_sort.vim index 9895ad754c..534393b724 100644 --- a/src/nvim/testdir/test_sort.vim +++ b/src/nvim/testdir/test_sort.vim @@ -80,7 +80,7 @@ func Test_sort_default() call assert_equal(['2', 'A', 'AA', 'a', 1, 3.3], sort([3.3, 1, "2", "A", "a", "AA"], '')) call assert_equal(['2', 'A', 'AA', 'a', 1, 3.3], sort([3.3, 1, "2", "A", "a", "AA"], 0)) call assert_equal(['2', 'A', 'a', 'AA', 1, 3.3], sort([3.3, 1, "2", "A", "a", "AA"], 1)) - call assert_fails('call sort([3.3, 1, "2"], 3)', "E474") + call assert_fails('call sort([3.3, 1, "2"], 3)', "E474:") endfunc " Tests for the ":sort" command. @@ -1360,7 +1360,8 @@ func Test_sort_cmd() call setline(1, ['line1', 'line2']) call assert_fails('sort no', 'E474:') call assert_fails('sort c', 'E475:') - call assert_fails('sort #pat%', 'E682:') + call assert_fails('sort #pat%', 'E654:') + call assert_fails('sort /\%(/', 'E53:') enew! endfunc diff --git a/src/nvim/testdir/test_source.vim b/src/nvim/testdir/test_source.vim index ba6fd5ad95..d4d96e36bf 100644 --- a/src/nvim/testdir/test_source.vim +++ b/src/nvim/testdir/test_source.vim @@ -1,5 +1,8 @@ " Tests for the :source command. +source check.vim +source view_util.vim + func Test_source_autocmd() call writefile([ \ 'let did_source = 1', @@ -87,4 +90,24 @@ func Test_source_autocmd_sfile() call delete('Xscript.vim') endfunc +func Test_source_error() + call assert_fails('scriptencoding utf-8', 'E167:') + call assert_fails('finish', 'E168:') + " call assert_fails('scriptversion 2', 'E984:') +endfunc + +" Test for sourcing a script recursively +func Test_nested_script() + CheckRunVimInTerminal + call writefile([':source! Xscript.vim', ''], 'Xscript.vim') + let buf = RunVimInTerminal('', {'rows': 6}) + call term_wait(buf) + call term_sendkeys(buf, ":set noruler\n") + call term_sendkeys(buf, ":source! Xscript.vim\n") + call term_wait(buf) + call WaitForAssert({-> assert_match('E22: Scripts nested too deep\s*', term_getline(buf, 6))}) + call delete('Xscript.vim') + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim index 8ab8204b10..c840e834b9 100644 --- a/src/nvim/testdir/test_spell.vim +++ b/src/nvim/testdir/test_spell.vim @@ -147,7 +147,7 @@ func Test_spell_file_missing() augroup TestSpellFileMissing autocmd! SpellFileMissing * bwipe augroup END - call assert_fails('set spell spelllang=ab_cd', 'E797:') + call assert_fails('set spell spelllang=ab_cd', 'E937:') " clean up augroup TestSpellFileMissing @@ -159,6 +159,19 @@ func Test_spell_file_missing() %bwipe! endfunc +func Test_spell_file_missing_bwipe() + " this was using a window that was wiped out in a SpellFileMissing autocmd + set spelllang=xy + au SpellFileMissing * n0 + set spell + au SpellFileMissing * bw + snext somefile + + au! SpellFileMissing + bwipe! + set nospell spelllang=en +endfunc + func Test_spelldump() " In case the spell file is not found avoid getting the download dialog, we " would get stuck at the prompt. @@ -329,6 +342,11 @@ func Test_spellsuggest() call assert_equal(['Third'], spellsuggest('THird', 1)) call assert_equal(['All'], spellsuggest('ALl', 1)) + " Special suggestion for repeated 'the the'. + call assert_inrange(0, 2, index(spellsuggest('the the', 3), 'the')) + call assert_inrange(0, 2, index(spellsuggest('the the', 3), 'the')) + call assert_inrange(0, 2, index(spellsuggest('The the', 3), 'The')) + call assert_fails("call spellsuggest('maxch', [])", 'E745:') call assert_fails("call spellsuggest('maxch', 2, [])", 'E745:') @@ -474,6 +492,35 @@ func Test_spellsuggest_option_expr() bwipe! endfunc +" Test for 'spellsuggest' expr errrors +func Test_spellsuggest_expr_errors() + " 'spellsuggest' + func MySuggest() + return range(3) + endfunc + set spell spellsuggest=expr:MySuggest() + call assert_equal([], spellsuggest('baord', 3)) + + " Test for 'spellsuggest' expression returning a non-list value + func! MySuggest2() + return 'good' + endfunc + set spellsuggest=expr:MySuggest2() + call assert_equal([], spellsuggest('baord')) + + " Test for 'spellsuggest' expression returning a list with dict values + func! MySuggest3() + return [[{}, {}]] + endfunc + set spellsuggest=expr:MySuggest3() + call assert_fails("call spellsuggest('baord')", 'E731:') + + set nospell spellsuggest& + delfunc MySuggest + delfunc MySuggest2 + delfunc MySuggest3 +endfunc + func Test_spellsuggest_timeout() set spellsuggest=timeout:30 set spellsuggest=timeout:-123 @@ -484,8 +531,23 @@ func Test_spellsuggest_timeout() call assert_fails('set spellsuggest=timeout:--9', 'E474:') endfunc +func Test_spellsuggest_visual_end_of_line() + let enc_save = &encoding + " set encoding=iso8859 + + " This was reading beyond the end of the line. + norm R00000000000 + sil norm 0 + sil! norm i00000) + sil! norm i00000) + call feedkeys("\<CR>") + norm z= + + let &encoding = enc_save +endfunc + func Test_spellinfo() - throw 'skipped: Nvim does not support enc=latin1' + throw 'Skipped: Nvim does not support enc=latin1' new let runtime = substitute($VIMRUNTIME, '\\', '/', 'g') @@ -1401,3 +1463,5 @@ let g:test_data_aff_sal = [ \"SAL ZZ- _", \"SAL Z S", \ ] + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim index 39fafbf7b4..1ee1d0dfe3 100644 --- a/src/nvim/testdir/test_startup.vim +++ b/src/nvim/testdir/test_startup.vim @@ -267,10 +267,9 @@ endfunc " Test the -V[N] argument to set the 'verbose' option to [N] func Test_V_arg() - if has('gui_running') - " Can't catch the output of gvim. - return - endif + " Can't catch the output of gvim. + CheckNotGui + let out = system(GetVimCommand() . ' --clean -es -X -V0 -c "set verbose?" -cq') call assert_equal(" verbose=0\n", out) @@ -431,7 +430,7 @@ endfunction " Test the -reverse and +reverse arguments (for GUI only). func Test_reverse() CheckCanRunGui - CheckAnyOf Feature:gui_gtk Feature:gui_motif Feature:gui_athena + CheckAnyOf Feature:gui_gtk Feature:gui_motif let after =<< trim [CODE] call writefile([&background], "Xtest_reverse") @@ -452,7 +451,7 @@ endfunc " Test the -background and -foreground arguments (for GUI only). func Test_background_foreground() CheckCanRunGui - CheckAnyOf Feature:gui_gtk Feature:gui_motif Feature:gui_athena + CheckAnyOf Feature:gui_gtk Feature:gui_motif " Is there a better way to check the effect of -background & -foreground " other than merely looking at &background (dark or light)? @@ -479,7 +478,7 @@ func Test_font() if has('gui_gtk') let font = 'Courier 14' - elseif has('gui_motif') || has('gui_athena') + elseif has('gui_motif') let font = '-misc-fixed-bold-*' else throw 'Skipped: test does not set a valid font for this GUI' @@ -501,10 +500,10 @@ endfunc " Test the -geometry argument (for GUI only). func Test_geometry() CheckCanRunGui - CheckAnyOf Feature:gui_gtk Feature:gui_motif Feature:gui_athena + CheckAnyOf Feature:gui_gtk Feature:gui_motif - if has('gui_motif') || has('gui_athena') - " FIXME: With GUI Athena or Motif, the value of getwinposx(), + if has('gui_motif') + " FIXME: With GUI Motif the value of getwinposx(), " getwinposy() and getwinpos() do not match exactly the " value given in -geometry. Why? " So only check &columns and &lines for those GUIs. @@ -521,9 +520,18 @@ func Test_geometry() call writefile([&columns, &lines, getwinposx(), getwinposy(), string(getwinpos())], "Xtest_geometry") qall [CODE] - if RunVim([], after, '-f -g -geometry 31x13+41+43') + " Some window managers have a bar at the top that pushes windows down, + " need to use at least 130, let's do 150 + if RunVim([], after, '-f -g -geometry 31x13+41+150') let lines = readfile('Xtest_geometry') - call assert_equal(['31', '13', '41', '43', '[41, 43]'], lines) + " Depending on the GUI library and the windowing system the final size + " might be a bit different, allow for some tolerance. Tuned based on + " actual failures. + call assert_inrange(31, 35, str2nr(lines[0])) + call assert_equal('13', lines[1]) + call assert_equal('41', lines[2]) + call assert_equal('150', lines[3]) + call assert_equal('[41, 150]', lines[4]) endif endif @@ -533,7 +541,7 @@ endfunc " Test the -iconic argument (for GUI only). func Test_iconic() CheckCanRunGui - CheckAnyOf Feature:gui_gtk Feature:gui_motif Feature:gui_athena + CheckAnyOf Feature:gui_gtk Feature:gui_motif call RunVim([], [], '-f -g -iconic -cq') @@ -543,10 +551,9 @@ endfunc func Test_invalid_args() - if !has('unix') || has('gui_running') - " can't get output of Vim. - return - endif + " must be able to get the output of Vim. + CheckUnix + CheckNotGui for opt in ['-Y', '--does-not-exist'] let out = split(system(GetVimCommand() .. ' ' .. opt), "\n") @@ -603,7 +610,7 @@ func Test_invalid_args() call assert_equal(0, v:shell_error) if has('quickfix') - " Detect invalid repeated arguments '-t foo -t foo", '-q foo -q foo'. + " Detect invalid repeated arguments '-t foo -t foo', '-q foo -q foo'. for opt in ['-t', '-q'] let out = split(system(GetVimCommand() .. repeat(' ' .. opt .. ' foo', 2)), "\n") call assert_equal(1, v:shell_error) @@ -622,6 +629,12 @@ func Test_invalid_args() endfor if has('gui_gtk') + let out = split(system(GetVimCommand() .. ' --socketid'), "\n") + call assert_equal(1, v:shell_error) + call assert_match('^VIM - Vi IMproved .* (.*)$', out[0]) + call assert_equal('Argument missing after: "--socketid"', out[1]) + call assert_equal('More info with: "vim -h"', out[2]) + for opt in ['--socketid x', '--socketid 0xg'] let out = split(system(GetVimCommand() .. ' ' .. opt), "\n") call assert_equal(1, v:shell_error) @@ -629,6 +642,7 @@ func Test_invalid_args() call assert_equal('Invalid argument for: "--socketid"', out[1]) call assert_equal('More info with: "vim -h"', out[2]) endfor + endif endfunc @@ -711,27 +725,6 @@ func Test_read_stdin() call delete('Xtestout') endfunc -func Test_set_shell() - let after =<< trim [CODE] - call writefile([&shell], "Xtestout") - quit! - [CODE] - - if has('win32') - let $SHELL = 'C:\with space\cmd.exe' - let expected = '"C:\with space\cmd.exe"' - else - let $SHELL = '/bin/with space/sh' - let expected = '"/bin/with space/sh"' - endif - - if RunVimPiped([], after, '', '') - let lines = readfile('Xtestout') - call assert_equal(expected, lines[0]) - endif - call delete('Xtestout') -endfunc - func Test_progpath() " Tests normally run with "./vim" or "../vim", these must have been expanded " to a full path. @@ -747,10 +740,9 @@ func Test_progpath() endfunc func Test_silent_ex_mode() - if !has('unix') || has('gui_running') - " can't get output of Vim. - return - endif + " must be able to get the output of Vim. + CheckUnix + CheckNotGui " This caused an ml_get error. let out = system(GetVimCommand() . ' -u NONE -es -c''set verbose=1|h|exe "%norm\<c-y>\<c-d>"'' -c cq') @@ -758,10 +750,9 @@ func Test_silent_ex_mode() endfunc func Test_default_term() - if !has('unix') || has('gui_running') - " can't get output of Vim. - return - endif + " must be able to get the output of Vim. + CheckUnix + CheckNotGui let save_term = $TERM let $TERM = 'unknownxxx' @@ -796,10 +787,17 @@ func Test_zzz_startinsert() call delete('Xtestout') endfunc +func Test_issue_3969() + " Can't catch the output of gvim. + CheckNotGui + + " Check that message is not truncated. + let out = system(GetVimCommand() . ' -es -X -V1 -c "echon ''hello''" -cq') + call assert_equal('hello', out) +endfunc + func Test_start_with_tabs() - if !CanRunVimInTerminal() - return - endif + CheckRunVimInTerminal let buf = RunVimInTerminal('-p a b c', {}) call VerifyScreenDump(buf, 'Test_start_with_tabs', {}) @@ -855,7 +853,7 @@ func Test_t_arg() call writefile([' first', ' second', ' third'], 'Xfile1') for t_arg in ['-t second', '-tsecond'] - if RunVim(before, after, '-t second') + if RunVim(before, after, t_arg) call assert_equal(['Xfile1:L2C5'], readfile('Xtestout'), t_arg) call delete('Xtestout') endif @@ -943,6 +941,7 @@ endfunc " Test for enabling the lisp mode on startup func Test_l_arg() + throw 'Skipped: Nvim -l arg differs from Vim' let after =<< trim [CODE] let s = 'lisp=' .. &lisp .. ', showmatch=' .. &showmatch call writefile([s], 'Xtestout') @@ -956,9 +955,7 @@ endfunc " Test for specifying a non-existing vimrc file using "-u" func Test_missing_vimrc() - if !CanRunVimInTerminal() - throw 'Skipped: cannot run vim in terminal' - endif + CheckRunVimInTerminal let after =<< trim [CODE] call assert_match('^E282:', v:errmsg) call writefile(v:errors, 'Xtestout') @@ -1016,6 +1013,7 @@ endfunc " Test for using the 'exrc' option func Test_exrc() + throw 'Skipped: Nvim requires user input for the exrc option' let after =<< trim [CODE] call assert_equal(1, &exrc) call assert_equal(1, &secure) @@ -1045,7 +1043,7 @@ func Test_io_not_a_terminal() \ 'Vim: Warning: Input is not from a terminal'], l) endfunc -" Test for --not-a-term avoiding escape codes. +" Test for not being a term avoiding escape codes. func Test_not_a_term() CheckUnix CheckNotGui @@ -1056,18 +1054,14 @@ func Test_not_a_term() let redir = &shellredir .. ' Xvimout' endif - " Without --not-a-term there are a few escape sequences. - " This will take 2 seconds because of the missing --not-a-term + " As nvim checks the environment by itself there will be no escape sequences + " This will also happen to take two (2) seconds. let cmd = GetVimProg() .. ' --cmd quit ' .. redir exe "silent !" . cmd - call assert_match("\<Esc>", readfile('Xvimout')->join()) + call assert_notmatch("\e", readfile('Xvimout')->join()) call delete('Xvimout') - " With --not-a-term there are no escape sequences. - let cmd = GetVimProg() .. ' --not-a-term --cmd quit ' .. redir - exe "silent !" . cmd - call assert_notmatch("\<Esc>", readfile('Xvimout')->join()) - call delete('Xvimout') + " --not-a-term flag has thus been deleted endfunc @@ -1271,4 +1265,19 @@ func Test_progname() call delete('Xprogname', 'd') endfunc +" Test for doing a write from .vimrc +func Test_write_in_vimrc() + call writefile(['silent! write'], 'Xvimrc') + let after =<< trim [CODE] + call assert_match('E32: ', v:errmsg) + call writefile(v:errors, 'Xtestout') + qall + [CODE] + if RunVim([], after, '-u Xvimrc') + call assert_equal([], readfile('Xtestout')) + call delete('Xtestout') + endif + call delete('Xvimrc') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_startup_utf8.vim b/src/nvim/testdir/test_startup_utf8.vim index bb4304396e..2ee6ecc41d 100644 --- a/src/nvim/testdir/test_startup_utf8.vim +++ b/src/nvim/testdir/test_startup_utf8.vim @@ -63,9 +63,7 @@ func Test_read_fifo_utf8() endfunc func Test_detect_ambiwidth() - if !CanRunVimInTerminal() - throw 'Skipped: cannot run Vim in a terminal window' - endif + CheckRunVimInTerminal " Use the title termcap entries to output the escape sequence. call writefile([ diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim index 6bde052442..990c852ccd 100644 --- a/src/nvim/testdir/test_statusline.vim +++ b/src/nvim/testdir/test_statusline.vim @@ -565,4 +565,45 @@ func Test_statusline_highlight_truncate() call delete('XTest_statusline') endfunc +func Test_statusline_showcmd() + CheckScreendump + + let lines =<< trim END + func MyStatusLine() + return '%S' + endfunc + + set laststatus=2 + set statusline=%!MyStatusLine() + set showcmdloc=statusline + call setline(1, ['a', 'b', 'c']) + set foldopen+=jump + 1,2fold + 3 + END + call writefile(lines, 'XTest_statusline', 'D') + + let buf = RunVimInTerminal('-S XTest_statusline', {'rows': 6}) + + call term_sendkeys(buf, "g") + call VerifyScreenDump(buf, 'Test_statusline_showcmd_1', {}) + + " typing "gg" should open the fold + call term_sendkeys(buf, "g") + call VerifyScreenDump(buf, 'Test_statusline_showcmd_2', {}) + + call term_sendkeys(buf, "\<C-V>Gl") + call VerifyScreenDump(buf, 'Test_statusline_showcmd_3', {}) + + call term_sendkeys(buf, "\<Esc>1234") + call VerifyScreenDump(buf, 'Test_statusline_showcmd_4', {}) + + call term_sendkeys(buf, "\<Esc>:set statusline=\<CR>") + call term_sendkeys(buf, ":\<CR>") + call term_sendkeys(buf, "1234") + call VerifyScreenDump(buf, 'Test_statusline_showcmd_5', {}) + + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim index b3a80072d9..c99a0d456d 100644 --- a/src/nvim/testdir/test_substitute.vim +++ b/src/nvim/testdir/test_substitute.vim @@ -1,6 +1,8 @@ " Tests for the substitute (:s) command source shared.vim +source check.vim +source screendump.vim func Test_multiline_subst() enew! @@ -444,13 +446,19 @@ func Test_substitute_errors() call assert_fails('s/FOO/bar/', 'E486:') call assert_fails('s/foo/bar/@', 'E488:') - call assert_fails('s/\(/bar/', 'E476:') + call assert_fails('s/\(/bar/', 'E54:') call assert_fails('s afooabara', 'E146:') call assert_fails('s\\a', 'E10:') setl nomodifiable call assert_fails('s/foo/bar/', 'E21:') + call assert_fails("let s=substitute([], 'a', 'A', 'g')", 'E730:') + call assert_fails("let s=substitute('abcda', [], 'A', 'g')", 'E730:') + call assert_fails("let s=substitute('abcda', 'a', [], 'g')", 'E730:') + call assert_fails("let s=substitute('abcda', 'a', 'A', [])", 'E730:') + call assert_fails("let s=substitute('abc', '\\%(', 'A', 'g')", 'E53:') + bwipe! endfunc @@ -486,6 +494,9 @@ func Test_sub_replace_1() call assert_equal("x\<C-M>x", substitute('xXx', 'X', "\r", '')) call assert_equal("YyyY", substitute('Y', 'Y', '\L\uyYy\l\EY', '')) call assert_equal("zZZz", substitute('Z', 'Z', '\U\lZzZ\u\Ez', '')) + " \v or \V after $ + call assert_equal('abxx', substitute('abcd', 'xy$\v|cd$', 'xx', '')) + call assert_equal('abxx', substitute('abcd', 'xy$\V\|cd\$', 'xx', '')) endfunc func Test_sub_replace_2() @@ -637,12 +648,16 @@ endfunc func SubReplacer(text, submatches) return a:text .. a:submatches[0] .. a:text endfunc +func SubReplacerVar(text, ...) + return a:text .. a:1[0] .. a:text +endfunc func SubReplacer20(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, submatches) return a:t3 .. a:submatches[0] .. a:t11 endfunc func Test_substitute_partial() call assert_equal('1foo2foo3', substitute('123', '2', function('SubReplacer', ['foo']), 'g')) + call assert_equal('1foo2foo3', substitute('123', '2', function('SubReplacerVar', ['foo']), 'g')) " 19 arguments plus one is just OK let Replacer = function('SubReplacer20', repeat(['foo'], 19)) @@ -668,6 +683,21 @@ func Test_sub_cmd_9() bw! endfunc +func Test_sub_highlight_zero_match() + CheckRunVimInTerminal + + let lines =<< trim END + call setline(1, ['one', 'two', 'three']) + END + call writefile(lines, 'XscriptSubHighlight', 'D') + let buf = RunVimInTerminal('-S XscriptSubHighlight', #{rows: 8, cols: 60}) + call term_sendkeys(buf, ":%s/^/ /c\<CR>") + call VerifyScreenDump(buf, 'Test_sub_highlight_zer_match_1', {}) + + call term_sendkeys(buf, "\<Esc>") + call StopVimInTerminal(buf) +endfunc + func Test_nocatch_sub_failure_handling() " normal error results in all replacements func Foo() @@ -812,9 +842,9 @@ endfunc func Test_sub_with_no_last_pat() let lines =<< trim [SCRIPT] call assert_fails('~', 'E33:') - call assert_fails('s//abc/g', 'E476:') - call assert_fails('s\/bar', 'E476:') - call assert_fails('s\&bar&', 'E476:') + call assert_fails('s//abc/g', 'E35:') + call assert_fails('s\/bar', 'E35:') + call assert_fails('s\&bar&', 'E33:') call writefile(v:errors, 'Xresult') qall! [SCRIPT] @@ -841,6 +871,40 @@ endfunc func Test_substitute() call assert_equal('a1a2a3a', substitute('123', '\zs', 'a', 'g')) + " Substitute with special keys + call assert_equal("a\<End>c", substitute('abc', "a.c", "a\<End>c", '')) +endfunc + +func Test_substitute_expr() + let g:val = 'XXX' + call assert_equal('XXX', substitute('yyy', 'y*', '\=g:val', '')) + call assert_equal('XXX', substitute('yyy', 'y*', {-> g:val}, '')) + call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)', + \ '\=nr2char("0x" . submatch(1))', 'g')) + call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)', + \ {-> nr2char("0x" . submatch(1))}, 'g')) + + call assert_equal('231', substitute('123', '\(.\)\(.\)\(.\)', + \ {-> submatch(2) . submatch(3) . submatch(1)}, '')) + + func Recurse() + return substitute('yyy', 'y\(.\)y', {-> submatch(1)}, '') + endfunc + " recursive call works + call assert_equal('-y-x-', substitute('xxx', 'x\(.\)x', {-> '-' . Recurse() . '-' . submatch(1) . '-'}, '')) + + call assert_fails("let s=submatch([])", 'E745:') + call assert_fails("let s=submatch(2, [])", 'E745:') +endfunc + +func Test_invalid_submatch() + " This was causing invalid memory access in Vim-7.4.2232 and older + call assert_fails("call substitute('x', '.', {-> submatch(10)}, '')", 'E935:') + call assert_fails('eval submatch(-1)', 'E935:') + call assert_equal('', submatch(0)) + call assert_equal('', submatch(1)) + call assert_equal([], submatch(0, 1)) + call assert_equal([], submatch(1, 1)) endfunc func Test_submatch_list_concatenate() @@ -849,6 +913,44 @@ func Test_submatch_list_concatenate() call substitute('A1', pat, Rep, '')->assert_equal("[['A1'], ['1']]") endfunc +func Test_substitute_expr_arg() + call assert_equal('123456789-123456789=', substitute('123456789', + \ '\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)', + \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, '')) + + call assert_equal('123456-123456=789', substitute('123456789', + \ '\(.\)\(.\)\(.\)\(a*\)\(n*\)\(.\)\(.\)\(.\)\(x*\)', + \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, '')) + + call assert_equal('123456789-123456789x=', substitute('123456789', + \ '\(.\)\(.\)\(.*\)', + \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . 'x' . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, '')) + + call assert_fails("call substitute('xxx', '.', {m -> string(add(m, 'x'))}, '')", 'E742:') + call assert_fails("call substitute('xxx', '.', {m -> string(insert(m, 'x'))}, '')", 'E742:') + call assert_fails("call substitute('xxx', '.', {m -> string(extend(m, ['x']))}, '')", 'E742:') + call assert_fails("call substitute('xxx', '.', {m -> string(remove(m, 1))}, '')", 'E742:') +endfunc + +" Test for using a function to supply the substitute string +func Test_substitute_using_func() + func Xfunc() + return '1234' + endfunc + call assert_equal('a1234f', substitute('abcdef', 'b..e', + \ function("Xfunc"), '')) + delfunc Xfunc +endfunc + +" Test for using submatch() with a multiline match +func Test_substitute_multiline_submatch() + new + call setline(1, ['line1', 'line2', 'line3', 'line4']) + %s/^line1\(\_.\+\)line4$/\=submatch(1)/ + call assert_equal(['', 'line2', 'line3', ''], getline(1, '$')) + close! +endfunc + func Test_substitute_skipped_range() new if 0 @@ -974,6 +1076,19 @@ func Test_sub_open_cmdline_win() call delete('Xresult') endfunc +" This was editing a script file from the expression +func Test_sub_edit_scriptfile() + new + norm o0000000000000000000000000000000000000000000000000000 + func EditScript() + silent! scr! Xfile + endfunc + s/\%')/\=EditScript() + + delfunc EditScript + bwipe! +endfunc + " Test for the 2-letter and 3-letter :substitute commands func Test_substitute_short_cmd() new diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim index 923e1cbf50..cf46b4c5bd 100644 --- a/src/nvim/testdir/test_swap.vim +++ b/src/nvim/testdir/test_swap.vim @@ -2,6 +2,7 @@ source check.vim source shared.vim +source term_util.vim func s:swapname() return trim(execute('swapname')) @@ -202,8 +203,8 @@ func Test_swapfile_delete() " This test won't work as root because root can successfully run kill(1, 0) if !IsRoot() " Write the swapfile with a modified PID, now it will be automatically - " deleted. Process one should never be Vim. - let swapfile_bytes[24:27] = 0z01000000 + " deleted. Process 0x3fffffff most likely does not exist. + let swapfile_bytes[24:27] = 0zffffff3f call writefile(swapfile_bytes, swapfile_name) let s:swapname = '' split XswapfileText @@ -374,24 +375,26 @@ func Test_swap_prompt_splitwin() call WaitForAssert({-> assert_match('^1$', term_getline(buf, 20))}) call StopVimInTerminal(buf) - " This caused Vim to crash when typing "q". - " TODO: it does not actually reproduce the crash. - call writefile(['au BufAdd * set virtualedit=all'], 'Xvimrc') - - let buf = RunVimInTerminal('-u Xvimrc Xfile1', {'rows': 20, 'wait_for_ruler': 0}) - call TermWait(buf) - call WaitForAssert({-> assert_match('^\[O\]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort:', term_getline(buf, 20))}) + " This caused Vim to crash when typing "q" at the swap file prompt. + let buf = RunVimInTerminal('-c "au bufadd * let foo_w = wincol()"', {'rows': 18}) + call term_sendkeys(buf, ":e Xfile1\<CR>") + call WaitForAssert({-> assert_match('More', term_getline(buf, 18))}) + call term_sendkeys(buf, " ") + call WaitForAssert({-> assert_match('^\[O\]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort:', term_getline(buf, 18))}) call term_sendkeys(buf, "q") + call TermWait(buf) + " check that Vim is still running + call term_sendkeys(buf, ":echo 'hello'\<CR>") + call WaitForAssert({-> assert_match('^hello', term_getline(buf, 18))}) + call term_sendkeys(buf, ":%bwipe!\<CR>") + call StopVimInTerminal(buf) %bwipe! call delete('Xfile1') - call delete('Xvimrc') endfunc func Test_swap_symlink() - if !has("unix") - return - endif + CheckUnix call writefile(['text'], 'Xtestfile') silent !ln -s -f Xtestfile Xtestlink @@ -418,6 +421,161 @@ func Test_swap_symlink() call delete('Xswapdir', 'rf') endfunc +func s:get_unused_pid(base) + if has('job') + " Execute 'echo' as a temporary job, and return its pid as an unused pid. + if has('win32') + let cmd = 'cmd /c echo' + else + let cmd = 'echo' + endif + let j = job_start(cmd) + while job_status(j) ==# 'run' + sleep 10m + endwhile + if job_status(j) ==# 'dead' + return job_info(j).process + endif + endif + " Must add four for MS-Windows to see it as a different one. + return a:base + 4 +endfunc + +func s:blob_to_pid(b) + return a:b[3] * 16777216 + a:b[2] * 65536 + a:b[1] * 256 + a:b[0] +endfunc + +func s:pid_to_blob(i) + let b = 0z + let b[0] = and(a:i, 0xff) + let b[1] = and(a:i / 256, 0xff) + let b[2] = and(a:i / 65536, 0xff) + let b[3] = and(a:i / 16777216, 0xff) + return b +endfunc + +func Test_swap_auto_delete() + " 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') + + " Forget about the file, recreate the swap file, then edit it again. The + " swap file should be automatically deleted. + bwipe! + " Change the process ID to avoid the "still running" warning. + let swapfile_bytes[24:27] = s:pid_to_blob(s:get_unused_pid( + \ s:blob_to_pid(swapfile_bytes[24:27]))) + call writefile(swapfile_bytes, swapfile_name) + edit Xtest.scr + " will end up using the same swap file after deleting the existing one + call assert_equal(swapfile_name, swapname('%')) + bwipe! + + " create the swap file again, but change the host name so that it won't be + " deleted + autocmd! SwapExists + augroup test_swap_recover_ext + autocmd! + autocmd SwapExists * let v:swapchoice = 'e' + augroup END + + " change the host name + let swapfile_bytes[28 + 40] = swapfile_bytes[28 + 40] + 2 + call writefile(swapfile_bytes, swapfile_name) + edit Xtest.scr + call assert_equal(1, filereadable(swapfile_name)) + " will use another same swap file name + call assert_notequal(swapfile_name, swapname('%')) + bwipe! + + call delete('Xtest.scr') + call delete(swapfile_name) + augroup test_swap_recover_ext + autocmd! + augroup END + augroup! test_swap_recover_ext +endfunc + +" Test for renaming a buffer when the swap file is deleted out-of-band +func Test_missing_swap_file() + CheckUnix + new Xfile2 + call delete(swapname('')) + call assert_fails('file Xfile3', 'E301:') + call assert_equal('Xfile3', bufname()) + call assert_true(bufexists('Xfile2')) + call assert_true(bufexists('Xfile3')) + %bw! +endfunc + +" Test for :preserve command +func Test_preserve() + new Xfile4 + setlocal noswapfile + call assert_fails('preserve', 'E313:') + bw! +endfunc + +" Test for the v:swapchoice variable +func Test_swapchoice() + call writefile(['aaa', 'bbb'], 'Xfile5') + edit Xfile5 + preserve + let swapfname = swapname('') + let b = readblob(swapfname) + bw! + call writefile(b, swapfname) + + autocmd! SwapExists + + " Test for v:swapchoice = 'o' (readonly) + augroup test_swapchoice + autocmd! + autocmd SwapExists * let v:swapchoice = 'o' + augroup END + edit Xfile5 + call assert_true(&readonly) + call assert_equal(['aaa', 'bbb'], getline(1, '$')) + %bw! + call assert_true(filereadable(swapfname)) + + " Test for v:swapchoice = 'a' (abort) + augroup test_swapchoice + autocmd! + autocmd SwapExists * let v:swapchoice = 'a' + augroup END + try + edit Xfile5 + catch /^Vim:Interrupt$/ + endtry + call assert_equal('', @%) + call assert_true(bufexists('Xfile5')) + %bw! + call assert_true(filereadable(swapfname)) + + " Test for v:swapchoice = 'd' (delete) + augroup test_swapchoice + autocmd! + autocmd SwapExists * let v:swapchoice = 'd' + augroup END + edit Xfile5 + call assert_equal('Xfile5', @%) + %bw! + call assert_false(filereadable(swapfname)) + + call delete('Xfile5') + call delete(swapfname) + augroup test_swapchoice + autocmd! + augroup END + augroup! test_swapchoice +endfunc + func Test_no_swap_file() call assert_equal("\nNo swap file", execute('swapname')) endfunc diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index 7ba0149971..45230c4208 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -113,6 +113,9 @@ func Test_syntime() let a = execute('syntime report') call assert_equal("\nNo Syntax items defined for this buffer", a) + let a = execute('syntime clear') + call assert_equal("\nNo Syntax items defined for this buffer", a) + view samples/memfile_test.c setfiletype cpp redraw @@ -171,6 +174,10 @@ func Test_syntax_list() let a = execute('syntax list') call assert_equal("\nNo Syntax items defined for this buffer", a) + syntax keyword Type int containedin=g1 skipwhite skipempty skipnl nextgroup=Abc + let exp = "Type xxx containedin=g1 nextgroup=Abc skipnl skipwhite skipempty int" + call assert_equal(exp, split(execute("syntax list"), "\n")[1]) + bd endfunc @@ -188,22 +195,26 @@ func Test_syntax_completion() call assert_equal('"syn sync ccomment clear fromstart linebreaks= linecont lines= match maxlines= minlines= region', @:) " Check that clearing "Aap" avoids it showing up before Boolean. - hi Aap ctermfg=blue + hi @Aap ctermfg=blue call feedkeys(":syn list \<C-A>\<C-B>\"\<CR>", 'tx') - call assert_match('^"syn list Aap Boolean Character ', @:) - hi clear Aap + call assert_match('^"syn list @Aap @boolean @character ', @:) + hi clear @Aap call feedkeys(":syn list \<C-A>\<C-B>\"\<CR>", 'tx') - call assert_match('^"syn list Boolean Character ', @:) + call assert_match('^"syn list @boolean @character ', @:) call feedkeys(":syn match \<C-A>\<C-B>\"\<CR>", 'tx') - call assert_match('^"syn match Boolean Character ', @:) + call assert_match('^"syn match @boolean @character ', @:) + + syn cluster Aax contains=Aap + call feedkeys(":syn list @A\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_match('^"syn list @Aax', @:) endfunc func Test_echohl_completion() call feedkeys(":echohl no\<C-A>\<C-B>\"\<CR>", 'tx') " call assert_equal('"echohl NonText Normal none', @:) - call assert_equal('"echohl NonText Normal NormalFloat NormalNC none', @:) + call assert_equal('"echohl NonText Normal NormalFloat none', @:) endfunc func Test_syntax_arg_skipped() @@ -343,6 +354,18 @@ func Test_syntax_arg_skipped() syn clear endfunc +" Check for an error. Used when multiple errors are thrown and we are checking +" for an earliest error. +func AssertFails(cmd, errcode) + let save_exception = '' + try + exe a:cmd + catch + let save_exception = v:exception + endtry + call assert_match(a:errcode, save_exception) +endfunc + func Test_syntax_invalid_arg() call assert_fails('syntax case asdf', 'E390:') if has('conceal') @@ -350,11 +373,51 @@ func Test_syntax_invalid_arg() endif call assert_fails('syntax spell asdf', 'E390:') call assert_fails('syntax clear @ABCD', 'E391:') - call assert_fails('syntax include @Xxx', 'E397:') - call assert_fails('syntax region X start="{"', 'E399:') + call assert_fails('syntax include random_file', 'E484:') + call assert_fails('syntax include <afile>', 'E495:') call assert_fails('syntax sync x', 'E404:') call assert_fails('syntax keyword Abc a[', 'E789:') call assert_fails('syntax keyword Abc a[bc]d', 'E890:') + call assert_fails('syntax cluster Abc add=A add=', 'E406:') + + " Test for too many \z\( and unmatched \z\( + " Not able to use assert_fails() here because both E50:/E879: and E475: + " messages are emitted. + set regexpengine=1 + call AssertFails("syntax region MyRegion start='\\z\\(' end='\\*/'", 'E52:') + + let cmd = "syntax region MyRegion start='" + let cmd ..= repeat("\\z\\(.\\)", 10) .. "' end='\*/'" + call AssertFails(cmd, 'E50:') + + set regexpengine=2 + call AssertFails("syntax region MyRegion start='\\z\\(' end='\\*/'", 'E54:') + + let cmd = "syntax region MyRegion start='" + let cmd ..= repeat("\\z\\(.\\)", 10) .. "' end='\*/'" + call AssertFails(cmd, 'E879:') + set regexpengine& + + call AssertFails('syntax keyword cMyItem grouphere G1', 'E393:') + call AssertFails('syntax sync match Abc grouphere MyItem "abc"', 'E394:') + call AssertFails('syn keyword Type contains int', 'E395:') + call assert_fails('syntax include @Xxx', 'E397:') + call AssertFails('syntax region X start', 'E398:') + call assert_fails('syntax region X start="{"', 'E399:') + call AssertFails('syntax cluster contains=Abc', 'E400:') + call AssertFails("syntax match Character /'.'", 'E401:') + call AssertFails("syntax match Character /'.'/a", 'E402:') + call assert_fails('syntax sync linecont /\%(/', 'E53:') + call assert_fails('syntax sync linecont /pat', 'E404:') + call assert_fails('syntax sync linecont', 'E404:') + call assert_fails('syntax sync linecont /pat1/ linecont /pat2/', 'E403:') + call assert_fails('syntax sync minlines=a', 'E404:') + call AssertFails('syntax match ABC /x/ contains=', 'E406:') + call AssertFails("syntax match Character contains /'.'/", 'E405:') + call AssertFails('syntax match ccFoo "Foo" nextgroup=ALLBUT,F', 'E407:') + call AssertFails('syntax region Block start="{" contains=F,ALLBUT', 'E408:') + call AssertFails("syntax match Characters contains=a.*x /'.'/", 'E409:') + call assert_fails('syntax match Search /abc/ contains=ALLBUT,/\%(/', 'E53:') endfunc func Test_syn_sync() @@ -382,6 +445,7 @@ func Test_syn_clear() hi clear Foo call assert_equal('Foo', synIDattr(hlID("Foo"), "name")) hi clear Bar + call assert_fails('syntax clear invalid_syngroup', 'E28:') endfunc func Test_invalid_name() @@ -389,7 +453,7 @@ func Test_invalid_name() syn keyword Nop yes call assert_fails("syntax keyword Wr\x17ong bar", 'E669:') syntax keyword @Wrong bar - call assert_match('W18:', execute('1messages')) + call assert_fails("syntax keyword @#Wrong bar", 'E5248:') syn clear hi clear Nop hi clear @Wrong @@ -475,15 +539,16 @@ func Test_conceal() call assert_match('16 ', ScreenLines(2, 7)[0]) call assert_equal([[0, '', 0], [1, '', 1], [1, '', 1], [1, '', 2], [1, '', 2], [0, '', 0]], map(range(1, 6), 'synconcealed(2, v:val)')) + call AssertFails("syntax match Entity '&' conceal cchar=\<Tab>", 'E844:') + syn clear set conceallevel& bw! endfunc func Test_bg_detection() - if has('gui_running') - return - endif + CheckNotGui + " auto-detection of &bg, make sure sure it isn't set anywhere before " this test hi Normal ctermbg=0 @@ -560,15 +625,15 @@ func Test_synstack_synIDtrans() call assert_equal(['cComment', 'cTodo'], map(synstack(line("."), col(".")), 'synIDattr(v:val, "name")')) call assert_equal(['Comment', 'Todo'], map(synstack(line("."), col(".")), 'synIDattr(synIDtrans(v:val), "name")')) + call assert_fails("let n=synIDtrans([])", 'E745:') + syn clear bw! endfunc " Check highlighting for a small piece of C code with a screen dump. func Test_syntax_c() - if !CanRunVimInTerminal() - throw 'Skipped: cannot make screendumps' - endif + CheckRunVimInTerminal call writefile([ \ '/* comment line at the top */', \ 'int main(int argc, char **argv) { // another comment', @@ -603,6 +668,24 @@ func Test_syntax_c() call delete('Xtest.c') endfun +" Test \z(...) along with \z1 +func Test_syn_zsub() + new + syntax on + call setline(1, 'xxx start foo xxx not end foo xxx end foo xxx') + let l:expected = ' ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ' + + for l:re in [0, 1, 2] + " Example taken from :help :syn-ext-match + syntax region Z start="start \z(\I\i*\)" skip="not end \z1" end="end \z1" + eval AssertHighlightGroups(1, 1, l:expected, 1, 'regexp=' .. l:re) + syntax clear Z + endfor + + set re& + bw! +endfunc + " Using \z() in a region with NFA failing should not crash. func Test_syn_wrong_z_one() new diff --git a/src/nvim/testdir/test_system.vim b/src/nvim/testdir/test_system.vim index 18692f42c9..6c8373b335 100644 --- a/src/nvim/testdir/test_system.vim +++ b/src/nvim/testdir/test_system.vim @@ -141,3 +141,5 @@ func Test_system_with_shell_quote() call delete('Xdir with spaces', 'rf') endtry endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_tabline.vim b/src/nvim/testdir/test_tabline.vim index e58a412c5a..d9bef09067 100644 --- a/src/nvim/testdir/test_tabline.vim +++ b/src/nvim/testdir/test_tabline.vim @@ -1,6 +1,9 @@ " Test for tabline source shared.vim +source view_util.vim +source check.vim +source screendump.vim func TablineWithCaughtError() let s:func_in_tabline_called = 1 @@ -147,4 +150,58 @@ func Test_tabline_20_format_items_no_overrun() set showtabline& tabline& endfunc +func Test_mouse_click_in_tab() + " This used to crash because TabPageIdxs[] was not initialized + let lines =<< trim END + tabnew + set mouse=a + exe "norm \<LeftMouse>" + END + call writefile(lines, 'Xclickscript') + call RunVim([], [], "-e -s -S Xclickscript -c qa") + + call delete('Xclickscript') +endfunc + +func Test_tabline_showcmd() + CheckScreendump + + let lines =<< trim END + func MyTabLine() + return '%S' + endfunc + + set showtabline=2 + set tabline=%!MyTabLine() + set showcmdloc=tabline + call setline(1, ['a', 'b', 'c']) + set foldopen+=jump + 1,2fold + 3 + END + call writefile(lines, 'XTest_tabline', 'D') + + let buf = RunVimInTerminal('-S XTest_tabline', {'rows': 6}) + + call term_sendkeys(buf, "g") + call VerifyScreenDump(buf, 'Test_tabline_showcmd_1', {}) + + " typing "gg" should open the fold + call term_sendkeys(buf, "g") + call VerifyScreenDump(buf, 'Test_tabline_showcmd_2', {}) + + call term_sendkeys(buf, "\<C-V>Gl") + call VerifyScreenDump(buf, 'Test_tabline_showcmd_3', {}) + + call term_sendkeys(buf, "\<Esc>1234") + call VerifyScreenDump(buf, 'Test_tabline_showcmd_4', {}) + + call term_sendkeys(buf, "\<Esc>:set tabline=\<CR>") + call term_sendkeys(buf, ":\<CR>") + call term_sendkeys(buf, "1234") + call VerifyScreenDump(buf, 'Test_tabline_showcmd_5', {}) + + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim index 6d468ec9de..b97aa409d8 100644 --- a/src/nvim/testdir/test_tabpage.vim +++ b/src/nvim/testdir/test_tabpage.vim @@ -591,9 +591,7 @@ func Test_tabs() endfunc func Test_tabpage_cmdheight() - if !CanRunVimInTerminal() - throw 'Skipped: cannot make screendumps' - endif + CheckRunVimInTerminal call writefile([ \ 'set laststatus=2', \ 'set cmdheight=2', @@ -623,6 +621,17 @@ func Test_tabpage_close_cmdwin() tabonly endfunc +" Pressing <C-PageUp> in insert mode should go to the previous tab page +" and <C-PageDown> should go to the next tab page +func Test_tabpage_Ctrl_Pageup() + tabnew + call feedkeys("i\<C-PageUp>", 'xt') + call assert_equal(1, tabpagenr()) + call feedkeys("i\<C-PageDown>", 'xt') + call assert_equal(2, tabpagenr()) + %bw! +endfunc + " Return the terminal key code for selecting a tab page from the tabline. This " sequence contains the following codes: a CSI (0x9b), KS_TABLINE (0xf0), " KS_FILLER (0x58) and then the tab page number. diff --git a/src/nvim/testdir/test_tagfunc.vim b/src/nvim/testdir/test_tagfunc.vim index ffc1d63b90..cba96d3504 100644 --- a/src/nvim/testdir/test_tagfunc.vim +++ b/src/nvim/testdir/test_tagfunc.vim @@ -1,5 +1,9 @@ " Test 'tagfunc' +source vim9.vim +source check.vim +source screendump.vim + func TagFunc(pat, flag, info) let g:tagfunc_args = [a:pat, a:flag, a:info] let tags = [] @@ -85,7 +89,7 @@ func Test_tagfunc() return v:null endfunc set tags= tfu=NullTagFunc - call assert_fails('tag nothing', 'E426') + call assert_fails('tag nothing', 'E433') delf NullTagFunc bwipe! @@ -117,4 +121,297 @@ func Test_tagfunc_settagstack() delfunc Mytagfunc2 endfunc +" Script local tagfunc callback function +func s:ScriptLocalTagFunc(pat, flags, info) + let g:ScriptLocalFuncArgs = [a:pat, a:flags, a:info] + return v:null +endfunc + +" Test for different ways of setting the 'tagfunc' option +func Test_tagfunc_callback() + func TagFunc1(callnr, pat, flags, info) + let g:TagFunc1Args = [a:callnr, a:pat, a:flags, a:info] + return v:null + endfunc + func TagFunc2(pat, flags, info) + let g:TagFunc2Args = [a:pat, a:flags, a:info] + return v:null + endfunc + + let lines =<< trim END + #" Test for using a function name + LET &tagfunc = 'g:TagFunc2' + new + LET g:TagFunc2Args = [] + call assert_fails('tag a10', 'E433:') + call assert_equal(['a10', '', {}], g:TagFunc2Args) + bw! + + #" Test for using a function() + set tagfunc=function('g:TagFunc1',\ [10]) + new + LET g:TagFunc1Args = [] + call assert_fails('tag a11', 'E433:') + call assert_equal([10, 'a11', '', {}], g:TagFunc1Args) + bw! + + #" Using a funcref variable to set 'tagfunc' + VAR Fn = function('g:TagFunc1', [11]) + LET &tagfunc = Fn + new + LET g:TagFunc1Args = [] + call assert_fails('tag a12', 'E433:') + call assert_equal([11, 'a12', '', {}], g:TagFunc1Args) + bw! + + #" Using a string(funcref_variable) to set 'tagfunc' + LET Fn = function('g:TagFunc1', [12]) + LET &tagfunc = string(Fn) + new + LET g:TagFunc1Args = [] + call assert_fails('tag a12', 'E433:') + call assert_equal([12, 'a12', '', {}], g:TagFunc1Args) + bw! + + #" Test for using a funcref() + set tagfunc=funcref('g:TagFunc1',\ [13]) + new + LET g:TagFunc1Args = [] + call assert_fails('tag a13', 'E433:') + call assert_equal([13, 'a13', '', {}], g:TagFunc1Args) + bw! + + #" Using a funcref variable to set 'tagfunc' + LET Fn = funcref('g:TagFunc1', [14]) + LET &tagfunc = Fn + new + LET g:TagFunc1Args = [] + call assert_fails('tag a14', 'E433:') + call assert_equal([14, 'a14', '', {}], g:TagFunc1Args) + bw! + + #" Using a string(funcref_variable) to set 'tagfunc' + LET Fn = funcref('g:TagFunc1', [15]) + LET &tagfunc = string(Fn) + new + LET g:TagFunc1Args = [] + call assert_fails('tag a14', 'E433:') + call assert_equal([15, 'a14', '', {}], g:TagFunc1Args) + bw! + + #" Test for using a lambda function + VAR optval = "LSTART a, b, c LMIDDLE TagFunc1(16, a, b, c) LEND" + LET optval = substitute(optval, ' ', '\\ ', 'g') + exe "set tagfunc=" .. optval + new + LET g:TagFunc1Args = [] + call assert_fails('tag a17', 'E433:') + call assert_equal([16, 'a17', '', {}], g:TagFunc1Args) + bw! + + #" Set 'tagfunc' to a lambda expression + LET &tagfunc = LSTART a, b, c LMIDDLE TagFunc1(17, a, b, c) LEND + new + LET g:TagFunc1Args = [] + call assert_fails('tag a18', 'E433:') + call assert_equal([17, 'a18', '', {}], g:TagFunc1Args) + bw! + + #" Set 'tagfunc' to a string(lambda expression) + LET &tagfunc = 'LSTART a, b, c LMIDDLE TagFunc1(18, a, b, c) LEND' + new + LET g:TagFunc1Args = [] + call assert_fails('tag a18', 'E433:') + call assert_equal([18, 'a18', '', {}], g:TagFunc1Args) + bw! + + #" Set 'tagfunc' to a variable with a lambda expression + VAR Lambda = LSTART a, b, c LMIDDLE TagFunc1(19, a, b, c) LEND + LET &tagfunc = Lambda + new + LET g:TagFunc1Args = [] + call assert_fails("tag a19", "E433:") + call assert_equal([19, 'a19', '', {}], g:TagFunc1Args) + bw! + + #" Set 'tagfunc' to a string(variable with a lambda expression) + LET Lambda = LSTART a, b, c LMIDDLE TagFunc1(20, a, b, c) LEND + LET &tagfunc = string(Lambda) + new + LET g:TagFunc1Args = [] + call assert_fails("tag a19", "E433:") + call assert_equal([20, 'a19', '', {}], g:TagFunc1Args) + bw! + + #" Test for using a lambda function with incorrect return value + LET Lambda = LSTART a, b, c LMIDDLE strlen(a) LEND + LET &tagfunc = string(Lambda) + new + call assert_fails("tag a20", "E987:") + bw! + + #" Test for clearing the 'tagfunc' option + set tagfunc='' + set tagfunc& + call assert_fails("set tagfunc=function('abc')", "E700:") + call assert_fails("set tagfunc=funcref('abc')", "E700:") + + #" set 'tagfunc' to a non-existing function + LET &tagfunc = function('g:TagFunc2', [21]) + LET g:TagFunc2Args = [] + call assert_fails("set tagfunc=function('NonExistingFunc')", 'E700:') + call assert_fails("LET &tagfunc = function('NonExistingFunc')", 'E700:') + call assert_fails("tag axb123", 'E426:') + call assert_equal([], g:TagFunc2Args) + bw! + END + call CheckLegacyAndVim9Success(lines) + + " Test for using a script-local function name + func s:TagFunc3(pat, flags, info) + let g:TagFunc3Args = [a:pat, a:flags, a:info] + return v:null + endfunc + set tagfunc=s:TagFunc3 + new + let g:TagFunc3Args = [] + call assert_fails('tag a21', 'E433:') + call assert_equal(['a21', '', {}], g:TagFunc3Args) + bw! + let &tagfunc = 's:TagFunc3' + new + let g:TagFunc3Args = [] + call assert_fails('tag a22', 'E433:') + call assert_equal(['a22', '', {}], g:TagFunc3Args) + bw! + delfunc s:TagFunc3 + + " invalid return value + let &tagfunc = "{a -> 'abc'}" + call assert_fails("echo taglist('a')", "E987:") + + " Using Vim9 lambda expression in legacy context should fail + " set tagfunc=(a,\ b,\ c)\ =>\ g:TagFunc1(21,\ a,\ b,\ c) + new + let g:TagFunc1Args = [] + " call assert_fails("tag a17", "E117:") + call assert_equal([], g:TagFunc1Args) + bw! + + " Test for using a script local function + set tagfunc=<SID>ScriptLocalTagFunc + new + let g:ScriptLocalFuncArgs = [] + call assert_fails('tag a15', 'E433:') + call assert_equal(['a15', '', {}], g:ScriptLocalFuncArgs) + bw! + + " Test for using a script local funcref variable + let Fn = function("s:ScriptLocalTagFunc") + let &tagfunc= Fn + new + let g:ScriptLocalFuncArgs = [] + call assert_fails('tag a16', 'E433:') + call assert_equal(['a16', '', {}], g:ScriptLocalFuncArgs) + bw! + + " Test for using a string(script local funcref variable) + let Fn = function("s:ScriptLocalTagFunc") + let &tagfunc= string(Fn) + new + let g:ScriptLocalFuncArgs = [] + call assert_fails('tag a16', 'E433:') + call assert_equal(['a16', '', {}], g:ScriptLocalFuncArgs) + bw! + + " set 'tagfunc' to a partial with dict. This used to cause a crash. + func SetTagFunc() + let params = {'tagfn': function('g:DictTagFunc')} + let &tagfunc = params.tagfn + endfunc + func g:DictTagFunc(_) dict + endfunc + call SetTagFunc() + new + call SetTagFunc() + bw + call test_garbagecollect_now() + new + set tagfunc= + wincmd w + set tagfunc= + :%bw! + delfunc g:DictTagFunc + delfunc SetTagFunc + + " Vim9 tests + let lines =<< trim END + vim9script + + def Vim9tagFunc(callnr: number, pat: string, flags: string, info: dict<any>): any + g:Vim9tagFuncArgs = [callnr, pat, flags, info] + return null + enddef + + # Test for using a def function with completefunc + set tagfunc=function('Vim9tagFunc',\ [60]) + new + g:Vim9tagFuncArgs = [] + assert_fails('tag a10', 'E433:') + assert_equal([60, 'a10', '', {}], g:Vim9tagFuncArgs) + + # Test for using a global function name + &tagfunc = g:TagFunc2 + new + g:TagFunc2Args = [] + assert_fails('tag a11', 'E433:') + assert_equal(['a11', '', {}], g:TagFunc2Args) + bw! + + # Test for using a script-local function name + def s:LocalTagFunc(pat: string, flags: string, info: dict<any> ): any + g:LocalTagFuncArgs = [pat, flags, info] + return null + enddef + &tagfunc = s:LocalTagFunc + new + g:LocalTagFuncArgs = [] + assert_fails('tag a12', 'E433:') + assert_equal(['a12', '', {}], g:LocalTagFuncArgs) + bw! + END + call CheckScriptSuccess(lines) + + " cleanup + delfunc TagFunc1 + delfunc TagFunc2 + set tagfunc& + %bw! +endfunc + +func Test_tagfunc_wipes_buffer() + func g:Tag0unc0(t,f,o) + bwipe + endfunc + set tagfunc=g:Tag0unc0 + new + cal assert_fails('tag 0', 'E987:') + + delfunc g:Tag0unc0 + set tagfunc= +endfunc + +func Test_tagfunc_closes_window() + split any + func MytagfuncClose(pat, flags, info) + close + return [{'name' : 'mytag', 'filename' : 'Xtest', 'cmd' : '1'}] + endfunc + set tagfunc=MytagfuncClose + call assert_fails('tag xyz', 'E1299:') + + set tagfunc= +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim index 04c0218f74..be60a3535c 100644 --- a/src/nvim/testdir/test_tagjump.vim +++ b/src/nvim/testdir/test_tagjump.vim @@ -8,7 +8,7 @@ func Test_ptag_with_notagstack() CheckFeature quickfix set notagstack - call assert_fails('ptag does_not_exist_tag_name', 'E426') + call assert_fails('ptag does_not_exist_tag_name', 'E433') set tagstack&vim endfunc @@ -184,6 +184,10 @@ function Test_keyword_jump() call search("start") exe "normal! 5\<C-W>\<C-I>" call assert_equal(" start OK if found this line", getline('.')) + + " invalid tag search pattern + call assert_fails('tag /\%(/', 'E426:') + enew! | only call delete('Xtestfile') call delete('Xinclude') @@ -227,15 +231,13 @@ func Test_tag_symbolic() endfunc " Tests for tag search with !_TAG_FILE_ENCODING. -" Depends on the test83-tags2 and test83-tags3 files. func Test_tag_file_encoding() - throw 'skipped: Nvim removed test83-tags2, test83-tags3' if has('vms') - return + throw 'Skipped: does not work on VMS' endif if !has('iconv') || iconv("\x82\x60", "cp932", "utf-8") != "\uff21" - return + throw 'Skipped: iconv does not work' endif let save_enc = &encoding @@ -260,18 +262,31 @@ func Test_tag_file_encoding() " case2: new - set tags=test83-tags2 + let content = ['!_TAG_FILE_ENCODING cp932 //', + \ "\x82`\x82a\x82b Xtags2.txt /\x82`\x82a\x82b"] + call writefile(content, 'Xtags') + set tags=Xtags tag /.BC call assert_equal('Xtags2.txt', expand('%:t')) call assert_equal('ABC', getline('.')) + call delete('Xtags') close " case3: new - set tags=test83-tags3 + let contents = [ + \ "!_TAG_FILE_SORTED 1 //", + \ "!_TAG_FILE_ENCODING cp932 //"] + for i in range(1, 100) + call add(contents, 'abc' .. i + \ .. " Xtags3.txt /\x82`\x82a\x82b") + endfor + call writefile(contents, 'Xtags') + set tags=Xtags tag abc50 call assert_equal('Xtags3.txt', expand('%:t')) call assert_equal('ABC', getline('.')) + call delete('Xtags') close set tags& @@ -282,6 +297,7 @@ func Test_tag_file_encoding() call delete('Xtags1') endfunc +" Test for emacs-style tags file (TAGS) func Test_tagjump_etags() if !has('emacs_tags') return @@ -322,6 +338,7 @@ func Test_tagjump_etags() \ ], 'Xtags2') tag main call assert_equal(2, line('.')) + call assert_fails('tag bar', 'E426:') " corrupted tag line call writefile([ @@ -345,7 +362,28 @@ func Test_tagjump_etags() \ "Xmain.c,64", \ ";;;;\x7f1,0", \ ], 'Xtags') - call assert_fails('tag foo', 'E426:') + call assert_fails('tag foo', 'E431:') + + " end of file after a CTRL-L line + call writefile([ + \ "\x0c", + \ "Xmain.c,64", + \ "void foo() {}\x7ffoo\x011,0", + \ "\x0c", + \ ], 'Xtags') + call assert_fails('tag main', 'E426:') + + " error in an included tags file + call writefile([ + \ "\x0c", + \ "Xtags2,include" + \ ], 'Xtags') + call writefile([ + \ "\x0c", + \ "Xmain.c,64", + \ "void foo() {}", + \ ], 'Xtags2') + call assert_fails('tag foo', 'E431:') call delete('Xtags') call delete('Xtags2') @@ -371,6 +409,7 @@ func Test_getsettagstack() call assert_fails("call settagstack(1, {'items' : 10})", 'E714') call assert_fails("call settagstack(1, {'items' : []}, 10)", 'E928') call assert_fails("call settagstack(1, {'items' : []}, 'b')", 'E962') + call assert_equal(-1, settagstack(0, v:_null_dict)) set tags=Xtags call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", @@ -766,11 +805,11 @@ endfunc " Test for an unsorted tags file func Test_tag_sort() - call writefile([ + let l = [ \ "first\tXfoo\t1", \ "ten\tXfoo\t3", - \ "six\tXfoo\t2"], - \ 'Xtags') + \ "six\tXfoo\t2"] + call writefile(l, 'Xtags') set tags=Xtags let code =<< trim [CODE] int first() {} @@ -781,7 +820,14 @@ func Test_tag_sort() call assert_fails('tag first', 'E432:') + " When multiple tag files are not sorted, then message should be displayed + " multiple times + call writefile(l, 'Xtags2') + set tags=Xtags,Xtags2 + call assert_fails('tag first', ['E432:', 'E432:']) + call delete('Xtags') + call delete('Xtags2') call delete('Xfoo') set tags& %bwipe @@ -1099,7 +1145,7 @@ func Test_tselect_listing() call writefile([ \ "!_TAG_FILE_ENCODING\tutf-8\t//", \ "first\tXfoo\t1" .. ';"' .. "\tv\ttyperef:typename:int\tfile:", - \ "first\tXfoo\t2" .. ';"' .. "\tv\ttyperef:typename:char\tfile:"], + \ "first\tXfoo\t2" .. ';"' .. "\tkind:v\ttyperef:typename:char\tfile:"], \ 'Xtags') set tags=Xtags @@ -1422,4 +1468,148 @@ func Test_tag_length() set tags& taglength& endfunc +" Tests for errors in a tags file +func Test_tagfile_errors() + set tags=Xtags + + " missing search pattern or line number for a tag + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "foo\tXfile\t"], 'Xtags', 'b') + call writefile(['foo'], 'Xfile') + + enew + tag foo + call assert_equal('', @%) + let caught_431 = v:false + try + eval taglist('.*') + catch /:E431:/ + let caught_431 = v:true + endtry + call assert_equal(v:true, caught_431) + + " tag name and file name are not separated by a tab + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "foo Xfile 1"], 'Xtags') + call assert_fails('tag foo', 'E431:') + + " file name and search pattern are not separated by a tab + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "foo\tXfile 1;"], 'Xtags') + call assert_fails('tag foo', 'E431:') + + call delete('Xtags') + call delete('Xfile') + set tags& +endfunc + +" When :stag fails to open the file, should close the new window +func Test_stag_close_window_on_error() + new | only + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "foo\tXfile\t1"], 'Xtags') + call writefile(['foo'], 'Xfile') + call writefile([], '.Xfile.swp') + " Remove the catch-all that runtest.vim adds + au! SwapExists + augroup StagTest + au! + autocmd SwapExists Xfile let v:swapchoice='q' + augroup END + + stag foo + call assert_equal(1, winnr('$')) + call assert_equal('', @%) + + augroup StagTest + au! + augroup END + call delete('Xfile') + call delete('.Xfile.swp') + set tags& +endfunc + +" Test for 'tagbsearch' (binary search) +func Test_tagbsearch() + " If a tags file header says the tags are sorted, but the tags are actually + " unsorted, then binary search should fail and linear search should work. + call writefile([ + \ "!_TAG_FILE_ENCODING\tutf-8\t//", + \ "!_TAG_FILE_SORTED\t1\t/0=unsorted, 1=sorted, 2=foldcase/", + \ "third\tXfoo\t3", + \ "second\tXfoo\t2", + \ "first\tXfoo\t1"], + \ 'Xtags') + set tags=Xtags + let code =<< trim [CODE] + int first() {} + int second() {} + int third() {} + [CODE] + call writefile(code, 'Xfoo') + + enew + set tagbsearch + call assert_fails('tag first', 'E426:') + call assert_equal('', bufname()) + call assert_fails('tag second', 'E426:') + call assert_equal('', bufname()) + tag third + call assert_equal('Xfoo', bufname()) + call assert_equal(3, line('.')) + %bw! + + set notagbsearch + tag first + call assert_equal('Xfoo', bufname()) + call assert_equal(1, line('.')) + enew + tag second + call assert_equal('Xfoo', bufname()) + call assert_equal(2, line('.')) + enew + tag third + call assert_equal('Xfoo', bufname()) + call assert_equal(3, line('.')) + %bw! + + " If a tags file header says the tags are unsorted, but the tags are + " actually sorted, then binary search should work. + call writefile([ + \ "!_TAG_FILE_ENCODING\tutf-8\t//", + \ "!_TAG_FILE_SORTED\t0\t/0=unsorted, 1=sorted, 2=foldcase/", + \ "first\tXfoo\t1", + \ "second\tXfoo\t2", + \ "third\tXfoo\t3"], + \ 'Xtags') + + set tagbsearch + tag first + call assert_equal('Xfoo', bufname()) + call assert_equal(1, line('.')) + enew + tag second + call assert_equal('Xfoo', bufname()) + call assert_equal(2, line('.')) + enew + tag third + call assert_equal('Xfoo', bufname()) + call assert_equal(3, line('.')) + %bw! + + " Binary search fails on EOF + call writefile([ + \ "!_TAG_FILE_ENCODING\tutf-8\t//", + \ "!_TAG_FILE_SORTED\t1\t/0=unsorted, 1=sorted, 2=foldcase/", + \ "bar\tXfoo\t1", + \ "foo\tXfoo\t2"], + \ 'Xtags') + call assert_fails('tag bbb', 'E426:') + + call delete('Xtags') + call delete('Xfoo') + set tags& tagbsearch& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_taglist.vim b/src/nvim/testdir/test_taglist.vim index be46773256..0387ef2bd8 100644 --- a/src/nvim/testdir/test_taglist.vim +++ b/src/nvim/testdir/test_taglist.vim @@ -36,6 +36,14 @@ func Test_taglist() call assert_equal('d', cmd[0]['kind']) call assert_equal('call cursor(3, 4)', cmd[0]['cmd']) + " Use characters with value > 127 in the tag extra field. + call writefile([ + \ "vFoo\tXfoo\t4" .. ';"' .. "\ttypename:int\ta£££\tv", + \ ], 'Xtags') + call assert_equal('v', taglist('vFoo')[0].kind) + + call assert_fails("let l=taglist([])", 'E730:') + call delete('Xtags') set tags& bwipe @@ -82,13 +90,11 @@ func Test_taglist_ctags_etags() endfunc func Test_tags_too_long() - call assert_fails('tag ' . repeat('x', 1020), 'E426') + call assert_fails('tag ' . repeat('x', 1020), ['E433', 'E426']) tags endfunc func Test_tagfiles() - " Nvim: different default for 'tags'. - set tags=./tags,tags call assert_equal([], tagfiles()) call writefile(["FFoo\tXfoo\t1"], 'Xtags1') @@ -221,6 +227,11 @@ func Test_format_error() endtry call assert_true(caught_exception) + " no field after the filename for a tag + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "foo\tXfile"], 'Xtags') + call assert_fails("echo taglist('foo')", 'E431:') + set tags& call delete('Xtags') endfunc @@ -241,4 +252,30 @@ func Test_tag_complete_wildoptions() set tags& endfunc +func Test_tag_complete_with_overlong_line() + let tagslines =<< trim END + !_TAG_FILE_FORMAT 2 // + !_TAG_FILE_SORTED 1 // + !_TAG_FILE_ENCODING utf-8 // + inboundGSV a 1;" r + inboundGovernor a 2;" kind:⊢ type:forall (muxMode :: MuxMode) socket peerAddr versionNumber m a b. (MonadAsync m, MonadCatch m, MonadEvaluate m, MonadThrow m, MonadThrow (STM m), MonadTime m, MonadTimer m, MonadMask m, Ord peerAddr, HasResponder muxMode ~ True) => Tracer m (RemoteTransitionTrace peerAddr) -> Tracer m (InboundGovernorTrace peerAddr) -> ServerControlChannel muxMode peerAddr ByteString m a b -> DiffTime -> MuxConnectionManager muxMode socket peerAddr versionNumber ByteString m a b -> StrictTVar m InboundGovernorObservableState -> m Void + inboundGovernorCounters a 3;" kind:⊢ type:InboundGovernorState muxMode peerAddr m a b -> InboundGovernorCounters + END + call writefile(tagslines, 'Xtags') + set tags=Xtags + + " try with binary search + set tagbsearch + call feedkeys(":tag inbou\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"tag inboundGSV inboundGovernor inboundGovernorCounters', @:) + " try with linear search + set notagbsearch + call feedkeys(":tag inbou\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"tag inboundGSV inboundGovernor inboundGovernorCounters', @:) + set tagbsearch& + + call delete('Xtags') + set tags& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_termcodes.vim b/src/nvim/testdir/test_termcodes.vim index eda485c512..99bc2d1d37 100644 --- a/src/nvim/testdir/test_termcodes.vim +++ b/src/nvim/testdir/test_termcodes.vim @@ -1,4 +1,30 @@ +" Test for translation of special key codes (<xF1>, <xF2>, etc.) +func Test_Keycode_Translation() + let keycodes = [ + \ ["<xUp>", "<Up>"], + \ ["<xDown>", "<Down>"], + \ ["<xLeft>", "<Left>"], + \ ["<xRight>", "<Right>"], + \ ["<xHome>", "<Home>"], + \ ["<xEnd>", "<End>"], + \ ["<zHome>", "<Home>"], + \ ["<zEnd>", "<End>"], + \ ["<xF1>", "<F1>"], + \ ["<xF2>", "<F2>"], + \ ["<xF3>", "<F3>"], + \ ["<xF4>", "<F4>"], + \ ["<S-xF1>", "<S-F1>"], + \ ["<S-xF2>", "<S-F2>"], + \ ["<S-xF3>", "<S-F3>"], + \ ["<S-xF4>", "<S-F4>"]] + for [k1, k2] in keycodes + exe "nnoremap " .. k1 .. " 2wx" + call assert_true(maparg(k1, 'n', 0, 1).lhs == k2) + exe "nunmap " .. k1 + endfor +endfunc + " Test for terminal keycodes that doesn't have termcap entries func Test_special_term_keycodes() new diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim index 0fc56083aa..7a13de12f4 100644 --- a/src/nvim/testdir/test_textformat.vim +++ b/src/nvim/testdir/test_textformat.vim @@ -962,78 +962,6 @@ func Test_tw_2_fo_tm_noai() bwipe! endfunc -func Test_tw_2_fo_cqm_com() - new - let t =<< trim END - { - X - Xa - XaY - XY - XYZ - X Y - X YZ - XX - XXa - XXY - } - END - call setline(1, t) - call cursor(2, 1) - - set tw=2 fo=cqm comments=n:X - exe "normal gqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgq" - let t =<< trim END - X - Xa - XaY - XY - XYZ - X Y - X YZ - XX - XXa - XXY - END - exe "normal o\n" . join(t, "\n") - - let expected =<< trim END - { - X - Xa - Xa - XY - XY - XY - XZ - X Y - X Y - X Z - XX - XXa - XXY - - X - Xa - Xa - XY - XY - XY - XZ - X Y - X Y - X Z - XX - XXa - XXY - } - END - call assert_equal(expected, getline(1, '$')) - - set tw& fo& comments& - bwipe! -endfunc - func Test_tw_2_fo_tm_replace() new let t =<< trim END @@ -1116,6 +1044,22 @@ func Test_empty_matchpairs() bwipe! endfunc +func Test_mps_error() + let encoding_save = &encoding + + " for e in ['utf-8', 'latin1'] + for e in ['utf-8'] + exe 'set encoding=' .. e + + call assert_fails('set mps=<:', 'E474:', e) + call assert_fails('set mps=:>', 'E474:', e) + call assert_fails('set mps=<>', 'E474:', e) + call assert_fails('set mps=<:>_', 'E474:', e) + endfor + + let &encoding = encoding_save +endfunc + " Test for ra on multi-byte characters func Test_ra_multibyte() new @@ -1161,140 +1105,6 @@ func Test_whichwrap_multi_byte() bwipe! endfunc -" Test for automatically adding comment leaders in insert mode -func Test_threepiece_comment() - new - setlocal expandtab - call setline(1, ["\t/*"]) - setlocal formatoptions=croql - call cursor(1, 3) - call feedkeys("A\<cr>\<cr>/", 'tnix') - call assert_equal(["\t/*", " *", " */"], getline(1, '$')) - - " If a comment ends in a single line, then don't add it in the next line - %d - call setline(1, '/* line1 */') - call feedkeys("A\<CR>next line", 'xt') - call assert_equal(['/* line1 */', 'next line'], getline(1, '$')) - - %d - " Copy the trailing indentation from the leader comment to a new line - setlocal autoindent noexpandtab - call feedkeys("a\t/*\tone\ntwo\n/", 'xt') - call assert_equal(["\t/*\tone", "\t *\ttwo", "\t */"], getline(1, '$')) - close! -endfunc - -" Test for the 'f' flag in 'comments' (only the first line has the comment -" string) -func Test_firstline_comment() - new - setlocal comments=f:- fo+=ro - exe "normal i- B\nD\<C-C>ggoC\<C-C>ggOA\<C-C>" - call assert_equal(['A', '- B', ' C', ' D'], getline(1, '$')) - %d - setlocal comments=:- - exe "normal i- B\nD\<C-C>ggoC\<C-C>ggOA\<C-C>" - call assert_equal(['- A', '- B', '- C', '- D'], getline(1, '$')) - %bw! -endfunc - -" Test for the 'r' flag in 'comments' (right align comment) -func Test_comment_rightalign() - new - setlocal comments=sr:/***,m:**,ex-2:******/ fo+=ro - exe "normal i=\<C-C>o\t /***\nD\n/" - exe "normal 2GOA\<C-C>joB\<C-C>jOC\<C-C>joE\<C-C>GOF\<C-C>joG" - let expected =<< trim END - = - A - /*** - ** B - ** C - ** D - ** E - ** F - ******/ - G - END - call assert_equal(expected, getline(1, '$')) - %bw! -endfunc - -" Test for the 'b' flag in 'comments' -func Test_comment_blank() - new - setlocal comments=b:* fo+=ro - exe "normal i* E\nF\n\<BS>G\nH\<C-C>ggOC\<C-C>O\<BS>B\<C-C>OA\<C-C>2joD" - let expected =<< trim END - A - *B - * C - * D - * E - * F - *G - H - END - call assert_equal(expected, getline(1, '$')) - %bw! -endfunc - -" Test for the 'n' flag in comments -func Test_comment_nested() - new - setlocal comments=n:> fo+=ro - exe "normal i> B\nD\<C-C>ggOA\<C-C>joC\<C-C>Go\<BS>>>> F\nH" - exe "normal 5GOE\<C-C>6GoG" - let expected =<< trim END - > A - > B - > C - > D - >>>> E - >>>> F - >>>> G - >>>> H - END - call assert_equal(expected, getline(1, '$')) - %bw! -endfunc - -" Test for a space character in 'comments' setting -func Test_comment_space() - new - setlocal comments=b:\ > fo+=ro - exe "normal i> B\nD\<C-C>ggOA\<C-C>joC" - exe "normal Go > F\nH\<C-C>kOE\<C-C>joG" - let expected =<< trim END - A - > B - C - D - > E - > F - > G - > H - END - call assert_equal(expected, getline(1, '$')) - %bw! -endfunc - -" Test for the 'O' flag in 'comments' -func Test_comment_O() - new - setlocal comments=Ob:* fo+=ro - exe "normal i* B\nD\<C-C>kOA\<C-C>joC" - let expected =<< trim END - A - * B - * C - * D - END - call assert_equal(expected, getline(1, '$')) - %bw! -endfunc - " Test for 'a' and 'w' flags in 'formatoptions' func Test_fo_a_w() new @@ -1302,6 +1112,20 @@ func Test_fo_a_w() call feedkeys("iabc abc a abc\<Esc>k0weade", 'xt') call assert_equal(['abc abcde ', 'a abc'], getline(1, '$')) + " when a line ends with space, it is not broken up. + %d + call feedkeys("ione two to ", 'xt') + call assert_equal('one two to ', getline(1)) + + " when a line ends with spaces and backspace is used in the next line, the + " last space in the previous line should be removed. + %d + set backspace=indent,eol,start + call setline(1, ['one ', 'two']) + exe "normal 2Gi\<BS>" + call assert_equal(['one two'], getline(1, '$')) + set backspace& + " Test for 'a', 'w' and '1' options. setlocal textwidth=0 setlocal fo=1aw @@ -1334,25 +1158,6 @@ func Test_fo_a_w() %bw! endfunc -" Test for 'j' flag in 'formatoptions' -func Test_fo_j() - new - setlocal fo+=j comments=:// - call setline(1, ['i++; // comment1', ' // comment2']) - normal J - call assert_equal('i++; // comment1 comment2', getline(1)) - setlocal fo-=j - call setline(1, ['i++; // comment1', ' // comment2']) - normal J - call assert_equal('i++; // comment1 // comment2', getline(1)) - " Test with nested comments - setlocal fo+=j comments=n:>,n:) - call setline(1, ['i++; > ) > ) comment1', ' > ) comment2']) - normal J - call assert_equal('i++; > ) > ) comment1 comment2', getline(1)) - %bw! -endfunc - " Test for formatting lines using gq in visual mode func Test_visual_gq_format() new @@ -1487,53 +1292,6 @@ func Test_fo_2() close! endfunc -" Test for formatting lines where only the first line has a comment. -func Test_fo_gq_with_firstline_comment() - new - setlocal formatoptions=tcq - call setline(1, ['- one two', 'three']) - normal gggqG - call assert_equal(['- one two three'], getline(1, '$')) - - %d - call setline(1, ['- one', '- two']) - normal gggqG - call assert_equal(['- one', '- two'], getline(1, '$')) - close! -endfunc - -" Test for trying to join a comment line with a non-comment line -func Test_join_comments() - new - call setline(1, ['one', '/* two */', 'three']) - normal gggqG - call assert_equal(['one', '/* two */', 'three'], getline(1, '$')) - close! -endfunc - -" Test for using 'a' in 'formatoptions' with comments -func Test_autoformat_comments() - new - setlocal formatoptions+=a - call feedkeys("a- one\n- two\n", 'xt') - call assert_equal(['- one', '- two', ''], getline(1, '$')) - - %d - call feedkeys("a\none\n", 'xt') - call assert_equal(['', 'one', ''], getline(1, '$')) - - setlocal formatoptions+=aw - %d - call feedkeys("aone \ntwo\n", 'xt') - call assert_equal(['one two', ''], getline(1, '$')) - - %d - call feedkeys("aone\ntwo\n", 'xt') - call assert_equal(['one', 'two', ''], getline(1, '$')) - - close! -endfunc - " This was leaving the cursor after the end of a line. Complicated way to " have the problem show up with valgrind. func Test_correct_cursor_position() diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index 6adf503f14..f94ee6c9f3 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -3,6 +3,7 @@ source check.vim CheckFeature timers +source screendump.vim source shared.vim source term_util.vim source load.vim @@ -46,9 +47,6 @@ endfunc func Test_timer_repeat_many() let g:val = 0 let timer = timer_start(50, 'MyHandler', {'repeat': -1}) - if has('mac') - sleep 200m - endif sleep 200m call timer_stop(timer) call assert_inrange((has('mac') ? 1 : 2), LoadAdjust(5), g:val) @@ -266,8 +264,9 @@ endfunc func Test_timer_peek_and_get_char() if !has('unix') && !has('gui_running') - return + throw 'Skipped: cannot feed low-level input' endif + call timer_start(0, 'FeedAndPeek') let intr = timer_start(100, 'Interrupt') let c = getchar() @@ -277,9 +276,9 @@ endfunc func Test_timer_getchar_zero() if has('win32') && !has('gui_running') - " Console: no low-level input - return + throw 'Skipped: cannot feed low-level input' endif + CheckFunction reltimefloat " Measure the elapsed time to avoid a hang when it fails. let start = reltime() @@ -305,9 +304,7 @@ func Test_timer_ex_mode() endfunc func Test_timer_restore_count() - if !CanRunVimInTerminal() - throw 'Skipped: cannot run Vim in a terminal window' - endif + CheckRunVimInTerminal " Check that v:count is saved and restored, not changed by a timer. call writefile([ \ 'nnoremap <expr><silent> L v:count ? v:count . "l" : "l"', @@ -349,11 +346,13 @@ func Test_nocatch_timer_garbage_collect() let a = {'foo', 'bar'} endfunc func FeedChar(id) - call feedkeys('x', 't') + call feedkeys(":\<CR>", 't') endfunc call timer_start(300, 'FeedChar') call timer_start(100, 'CauseAnError') - let x = getchar() + let x = getchar() " wait for error in timer + let x = getchar(0) " read any remaining chars + let x = getchar(0) set ut& call test_override('no_wait_return', 1) @@ -409,6 +408,30 @@ func Test_timer_invalid_callback() call assert_fails('call timer_start(0, "0")', 'E921') endfunc +func Test_timer_changing_function_list() + CheckRunVimInTerminal + + " Create a large number of functions. Should get the "more" prompt. + " The typing "G" triggers the timer, which changes the function table. + let lines =<< trim END + for func in map(range(1,99), "'Func' .. v:val") + exe "func " .. func .. "()" + endfunc + endfor + au CmdlineLeave : call timer_start(0, {-> 0}) + END + call writefile(lines, 'XTest_timerchange') + let buf = RunVimInTerminal('-S XTest_timerchange', #{rows: 10}) + call term_sendkeys(buf, ":fu\<CR>") + call WaitForAssert({-> assert_match('-- More --', term_getline(buf, 10))}) + call term_sendkeys(buf, "G") + call WaitForAssert({-> assert_match('E454', term_getline(buf, 9))}) + call term_sendkeys(buf, "\<Esc>") + + call StopVimInTerminal(buf) + call delete('XTest_timerchange') +endfunc + func Test_timer_using_win_execute_undo_sync() let bufnr1 = bufnr() new diff --git a/src/nvim/testdir/test_trycatch.vim b/src/nvim/testdir/test_trycatch.vim index 646594e482..ef20e03126 100644 --- a/src/nvim/testdir/test_trycatch.vim +++ b/src/nvim/testdir/test_trycatch.vim @@ -3,6 +3,7 @@ source check.vim source shared.vim +source vim9.vim "------------------------------------------------------------------------------- " Test environment {{{1 @@ -2000,15 +2001,34 @@ endfunc 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('catch abc', 'E654:') 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 | for i in range(5) | endif | endtry', 'E580:') call assert_fails('try | while v:true | endtry', 'E170:') call assert_fails('try | if v:true | endtry', 'E171:') + + " this was using a negative index in cstack[] + let lines =<< trim END + try + for + if + endwhile + if + finally + END + call CheckScriptFailure(lines, 'E690:') + + let lines =<< trim END + try + for + if + endwhile + if + endtry + END + call CheckScriptFailure(lines, 'E690:') endfunc " Test for verbose messages with :try :catch, and :finally {{{1 @@ -2027,16 +2047,179 @@ func Test_try_catch_verbose() 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' - \ ] + \ '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")) + + " Test for verbose messages displayed when an exception is discarded + redir => msg + try + try + throw 'abc' + finally + throw 'xyz' + endtry + catch + endtry + redir END + let expected = [ + \ 'Exception thrown: abc', '', + \ 'Exception made pending: abc', '', + \ 'Exception thrown: xyz', '', + \ 'Exception discarded: abc', '', + \ 'Exception caught: xyz', '', + \ 'Exception finished: xyz'] + call assert_equal(expected, split(msg, "\n")) + + " Test for messages displayed when :throw is resumed after :finally + redir => msg + try + try + throw 'abc' + finally + endtry + catch + endtry + redir END + let expected = [ + \ 'Exception thrown: abc', '', + \ 'Exception made pending: abc', '', + \ 'Exception resumed: abc', '', + \ 'Exception caught: abc', '', + \ 'Exception finished: abc'] + call assert_equal(expected, split(msg, "\n")) + + " Test for messages displayed when :break is resumed after :finally + redir => msg + for i in range(1) + try + break + finally + endtry + endfor + redir END + let expected = [':break made pending', '', ':break resumed'] call assert_equal(expected, split(msg, "\n")) + + " Test for messages displayed when :continue is resumed after :finally + redir => msg + for i in range(1) + try + continue + finally + endtry + endfor + redir END + let expected = [':continue made pending', '', ':continue resumed'] + call assert_equal(expected, split(msg, "\n")) + + " Test for messages displayed when :return is resumed after :finally + func Xtest() + try + return 'vim' + finally + endtry + endfunc + redir => msg + call Xtest() + redir END + let expected = [ + \ 'calling Xtest()', '', + \ ':return vim made pending', '', + \ ':return vim resumed', '', + \ 'Xtest returning ''vim''', '', + \ 'continuing in Test_try_catch_verbose'] + call assert_equal(expected, split(msg, "\n")) + delfunc Xtest + + " Test for messages displayed when :finish is resumed after :finally + call writefile(['try', 'finish', 'finally', 'endtry'], 'Xscript') + redir => msg + source Xscript + redir END + let expected = [ + \ ':finish made pending', '', + \ ':finish resumed', '', + \ 'finished sourcing Xscript', + \ 'continuing in Test_try_catch_verbose'] + call assert_equal(expected, split(msg, "\n")[1:]) + call delete('Xscript') + + " Test for messages displayed when a pending :continue is discarded by an + " exception in a finally handler + redir => msg + try + for i in range(1) + try + continue + finally + throw 'abc' + endtry + endfor + catch + endtry + redir END + let expected = [ + \ ':continue made pending', '', + \ 'Exception thrown: abc', '', + \ ':continue discarded', '', + \ 'Exception caught: abc', '', + \ 'Exception finished: abc'] + call assert_equal(expected, split(msg, "\n")) + set verbose& endfunc +" Test for throwing an exception from a BufEnter autocmd {{{1 +func Test_BufEnter_exception() + augroup bufenter_exception + au! + autocmd BufEnter Xfile1 throw 'abc' + augroup END + + let caught_abc = 0 + try + sp Xfile1 + catch /^abc/ + let caught_abc = 1 + endtry + call assert_equal(1, caught_abc) + call assert_equal(1, winnr('$')) + + augroup bufenter_exception + au! + augroup END + augroup! bufenter_exception + %bwipe! + + " Test for recursively throwing exceptions in autocmds + augroup bufenter_exception + au! + autocmd BufEnter Xfile1 throw 'bufenter' + autocmd BufLeave Xfile1 throw 'bufleave' + augroup END + + let ex_count = 0 + try + try + sp Xfile1 + catch /^bufenter/ + let ex_count += 1 + endtry + catch /^bufleave/ + let ex_count += 10 + endtry + call assert_equal(10, ex_count) + call assert_equal(2, winnr('$')) + + augroup bufenter_exception + au! + augroup END + augroup! bufenter_exception + %bwipe! +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 @@ -2060,6 +2243,23 @@ func Test_user_command_throw_in_function_call() unlet g:caught endfunc +" Test that after reporting an uncaught exception there is no error for a +" missing :endif +func Test_after_exception_no_endif_error() + function Throw() + throw "Failure" + endfunction + + function Foo() + if 1 + call Throw() + endif + endfunction + call assert_fails('call Foo()', ['E605:', 'E605:']) + delfunc Throw + delfunc Foo +endfunc + " Test for using throw in a called function with following endtry {{{1 func Test_user_command_function_call_with_endtry() let lines =<< trim END diff --git a/src/nvim/testdir/test_undo.vim b/src/nvim/testdir/test_undo.vim index eb47af08d7..ee8b52caaf 100644 --- a/src/nvim/testdir/test_undo.vim +++ b/src/nvim/testdir/test_undo.vim @@ -3,6 +3,9 @@ " undo-able pieces. Do that by setting 'undolevels'. " Also tests :earlier and :later. +source check.vim +source screendump.vim + func Test_undotree() new @@ -773,4 +776,30 @@ func Test_undo_mark() bwipe! endfunc +func Test_undo_after_write() + " use a terminal to make undo work like when text is typed + CheckRunVimInTerminal + + let lines =<< trim END + edit Xtestfile.txt + set undolevels=100 undofile + imap . <Cmd>write<CR> + write + END + call writefile(lines, 'Xtest_undo_after_write', 'D') + let buf = RunVimInTerminal('-S Xtest_undo_after_write', #{rows: 6}) + + call term_sendkeys(buf, "Otest.\<CR>boo!!!\<Esc>") + sleep 100m + call term_sendkeys(buf, "u") + call VerifyScreenDump(buf, 'Test_undo_after_write_1', {}) + + call term_sendkeys(buf, "u") + call VerifyScreenDump(buf, 'Test_undo_after_write_2', {}) + + call StopVimInTerminal(buf) + call delete('Xtestfile.txt') +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_unlet.vim b/src/nvim/testdir/test_unlet.vim index b02bdaab3b..4779d17906 100644 --- a/src/nvim/testdir/test_unlet.vim +++ b/src/nvim/testdir/test_unlet.vim @@ -1,12 +1,9 @@ " Tests for :unlet func Test_read_only() - try - " this caused a crash - unlet v:count - catch - call assert_true(v:exception =~ ':E795:') - endtry + " these caused a crash + call assert_fails('unlet v:count', 'E795:') + call assert_fails('unlet v:errmsg', 'E795:') endfunc func Test_existing() @@ -18,15 +15,19 @@ endfunc func Test_not_existing() unlet! does_not_exist - try - unlet does_not_exist - catch - call assert_true(v:exception =~ ':E108:') - endtry + call assert_fails('unlet does_not_exist', 'E108:') endfunc func Test_unlet_fails() call assert_fails('unlet v:["count"]', 'E46:') + call assert_fails('unlet $', 'E475:') + let v = {} + call assert_fails('unlet v[:]', 'E719:') + let l = [] + call assert_fails("unlet l['k'", 'E111:') + let d = {'k' : 1} + call assert_fails("unlet d.k2", 'E716:') + call assert_fails("unlet {a};", 'E488:') endfunc func Test_unlet_env() @@ -62,3 +63,5 @@ func Test_unlet_complete() call feedkeys(":unlet $FOO\t\n", 'tx') call assert_true(!exists('$FOOBAR') || empty($FOOBAR)) endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_user_func.vim b/src/nvim/testdir/test_user_func.vim index 5231ef7b4f..4742293ed5 100644 --- a/src/nvim/testdir/test_user_func.vim +++ b/src/nvim/testdir/test_user_func.vim @@ -3,6 +3,9 @@ " Also test that a builtin function cannot be replaced. " Also test for regression when calling arbitrary expression. +source check.vim +source shared.vim + func Table(title, ...) let ret = a:title let idx = 1 @@ -83,10 +86,14 @@ func Test_user_func() normal o[(one again call assert_equal('1. one again', getline('.')) + " Try to overwrite a function in the global (g:) scope call assert_equal(3, max([1, 2, 3])) call assert_fails("call extend(g:, {'max': function('min')})", 'E704') call assert_equal(3, max([1, 2, 3])) + " Try to overwrite an user defined function with a function reference + call assert_fails("let Expr1 = function('min')", 'E705:') + " Regression: the first line below used to throw ?E110: Missing ')'? " Second is here just to prove that this line is correct when not skipping " rhs of &&. @@ -142,8 +149,8 @@ func Test_default_arg() call assert_equal(res.optional, 2) call assert_equal(res['0'], 1) - call assert_fails("call MakeBadFunc()", 'E989') - call assert_fails("fu F(a=1 ,) | endf", 'E475') + call assert_fails("call MakeBadFunc()", 'E989:') + call assert_fails("fu F(a=1 ,) | endf", 'E1068:') " Since neovim does not have v:none, the ability to use the default " argument with the intermediate argument set to v:none has been omitted. @@ -156,6 +163,16 @@ func Test_default_arg() \ .. "1 return deepcopy(a:)\n" \ .. " endfunction", \ execute('func Args2')) + + " Error in default argument expression + let l =<< trim END + func F1(x = y) + return a:x * 2 + endfunc + echo F1() + END + let @a = l->join("\n") + call assert_fails("exe @a", 'E121:') endfunc func s:addFoo(lead) @@ -169,3 +186,319 @@ endfunc func Test_failed_call_in_try() try | call UnknownFunc() | catch | endtry endfunc + +" Test for listing user-defined functions +func Test_function_list() + call assert_fails("function Xabc", 'E123:') +endfunc + +" Test for <sfile>, <slnum> in a function +func Test_sfile_in_function() + func Xfunc() + call assert_match('..Test_sfile_in_function\[5]..Xfunc', expand('<sfile>')) + call assert_equal('2', expand('<slnum>')) + endfunc + call Xfunc() + delfunc Xfunc +endfunc + +" Test trailing text after :endfunction {{{1 +func Test_endfunction_trailing() + call assert_false(exists('*Xtest')) + + exe "func Xtest()\necho 'hello'\nendfunc\nlet done = 'yes'" + call assert_true(exists('*Xtest')) + call assert_equal('yes', done) + delfunc Xtest + unlet done + + exe "func Xtest()\necho 'hello'\nendfunc|let done = 'yes'" + call assert_true(exists('*Xtest')) + call assert_equal('yes', done) + delfunc Xtest + unlet done + + " trailing line break + exe "func Xtest()\necho 'hello'\nendfunc\n" + call assert_true(exists('*Xtest')) + delfunc Xtest + + set verbose=1 + exe "func Xtest()\necho 'hello'\nendfunc \" garbage" + call assert_notmatch('W22:', split(execute('1messages'), "\n")[0]) + call assert_true(exists('*Xtest')) + delfunc Xtest + + exe "func Xtest()\necho 'hello'\nendfunc garbage" + call assert_match('W22:', split(execute('1messages'), "\n")[0]) + call assert_true(exists('*Xtest')) + delfunc Xtest + set verbose=0 + + func Xtest(a1, a2) + echo a:a1 .. a:a2 + endfunc + set verbose=15 + redir @a + call Xtest(123, repeat('x', 100)) + redir END + call assert_match('calling Xtest(123, ''xxxxxxx.*x\.\.\.x.*xxxx'')', getreg('a')) + delfunc Xtest + set verbose=0 + + function Foo() + echo 'hello' + endfunction | echo 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' + delfunc Foo +endfunc + +func Test_delfunction_force() + delfunc! Xtest + delfunc! Xtest + func Xtest() + echo 'nothing' + endfunc + delfunc! Xtest + delfunc! Xtest + + " Try deleting the current function + call assert_fails('delfunc Test_delfunction_force', 'E131:') +endfunc + +func Test_function_defined_line() + CheckNotGui + + let lines =<< trim [CODE] + " F1 + func F1() + " F2 + func F2() + " + " + " + return + endfunc + " F3 + execute "func F3()\n\n\n\nreturn\nendfunc" + " F4 + execute "func F4()\n + \\n + \\n + \\n + \return\n + \endfunc" + endfunc + " F5 + execute "func F5()\n\n\n\nreturn\nendfunc" + " F6 + execute "func F6()\n + \\n + \\n + \\n + \return\n + \endfunc" + call F1() + verbose func F1 + verbose func F2 + verbose func F3 + verbose func F4 + verbose func F5 + verbose func F6 + qall! + [CODE] + + call writefile(lines, 'Xtest.vim') + let res = system(GetVimCommandClean() .. ' -es -X -S Xtest.vim') + call assert_equal(0, v:shell_error) + + let m = matchstr(res, 'function F1()[^[:print:]]*[[:print:]]*') + call assert_match(' line 2$', m) + + let m = matchstr(res, 'function F2()[^[:print:]]*[[:print:]]*') + call assert_match(' line 4$', m) + + let m = matchstr(res, 'function F3()[^[:print:]]*[[:print:]]*') + call assert_match(' line 11$', m) + + let m = matchstr(res, 'function F4()[^[:print:]]*[[:print:]]*') + call assert_match(' line 13$', m) + + let m = matchstr(res, 'function F5()[^[:print:]]*[[:print:]]*') + call assert_match(' line 21$', m) + + let m = matchstr(res, 'function F6()[^[:print:]]*[[:print:]]*') + call assert_match(' line 23$', m) + + call delete('Xtest.vim') +endfunc + +" Test for defining a function reference in the global scope +func Test_add_funcref_to_global_scope() + let x = g: + let caught_E862 = 0 + try + func x.Xfunc() + return 1 + endfunc + catch /E862:/ + let caught_E862 = 1 + endtry + call assert_equal(1, caught_E862) +endfunc + +func Test_funccall_garbage_collect() + func Func(x, ...) + call add(a:x, a:000) + endfunc + call Func([], []) + " Must not crash cause by invalid freeing + call test_garbagecollect_now() + call assert_true(v:true) + delfunc Func +endfunc + +" Test for script-local function +func <SID>DoLast() + call append(line('$'), "last line") +endfunc + +func s:DoNothing() + call append(line('$'), "nothing line") +endfunc + +func Test_script_local_func() + set nocp nomore viminfo+=nviminfo + new + nnoremap <buffer> _x :call <SID>DoNothing()<bar>call <SID>DoLast()<bar>delfunc <SID>DoNothing<bar>delfunc <SID>DoLast<cr> + + normal _x + call assert_equal('nothing line', getline(2)) + call assert_equal('last line', getline(3)) + close! + + " Try to call a script local function in global scope + let lines =<< trim [CODE] + :call assert_fails('call s:Xfunc()', 'E81:') + :call assert_fails('let x = call("<SID>Xfunc", [])', 'E120:') + :call writefile(v:errors, 'Xresult') + :qall + + [CODE] + call writefile(lines, 'Xscript') + if RunVim([], [], '-s Xscript') + call assert_equal([], readfile('Xresult')) + endif + call delete('Xresult') + call delete('Xscript') +endfunc + +" Test for errors in defining new functions +func Test_func_def_error() + call assert_fails('func Xfunc abc ()', 'E124:') + call assert_fails('func Xfunc(', 'E125:') + call assert_fails('func xfunc()', 'E128:') + + " Try to redefine a function that is in use + let caught_E127 = 0 + try + func! Test_func_def_error() + endfunc + catch /E127:/ + let caught_E127 = 1 + endtry + call assert_equal(1, caught_E127) + + " Try to define a function in a dict twice + let d = {} + let lines =<< trim END + func d.F1() + return 1 + endfunc + END + let l = join(lines, "\n") . "\n" + exe l + call assert_fails('exe l', 'E717:') + + " Define an autoload function with an incorrect file name + call writefile(['func foo#Bar()', 'return 1', 'endfunc'], 'Xscript') + call assert_fails('source Xscript', 'E746:') + call delete('Xscript') + + " Try to list functions using an invalid search pattern + call assert_fails('function /\%(/', 'E53:') +endfunc + +" Test for deleting a function +func Test_del_func() + call assert_fails('delfunction Xabc', 'E130:') + let d = {'a' : 10} + call assert_fails('delfunc d.a', 'E718:') + func d.fn() + return 1 + endfunc + + " cannot delete the dict function by number + let nr = substitute(execute('echo d'), '.*function(''\(\d\+\)'').*', '\1', '') + call assert_fails('delfunction g:' .. nr, 'E475: Invalid argument: g:') + + delfunc d.fn + call assert_equal({'a' : 10}, d) +endfunc + +" Test for calling return outside of a function +func Test_return_outside_func() + call writefile(['return 10'], 'Xscript') + call assert_fails('source Xscript', 'E133:') + call delete('Xscript') +endfunc + +" Test for errors in calling a function +func Test_func_arg_error() + " Too many arguments + call assert_fails("call call('min', range(1,20))", 'E118:') + call assert_fails("call call('min', range(1,21))", 'E699:') + call assert_fails('echo min(0,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,0,1)', + \ 'E740:') + + " Missing dict argument + func Xfunc() dict + return 1 + endfunc + call assert_fails('call Xfunc()', 'E725:') + delfunc Xfunc +endfunc + +func Test_func_dict() + let mydict = {'a': 'b'} + function mydict.somefunc() dict + return len(self) + endfunc + + call assert_equal("{'a': 'b', 'somefunc': function('3')}", string(mydict)) + call assert_equal(2, mydict.somefunc()) + call assert_match("^\n function \\d\\\+() dict" + \ .. "\n1 return len(self)" + \ .. "\n endfunction$", execute('func mydict.somefunc')) + call assert_fails('call mydict.nonexist()', 'E716:') +endfunc + +func Test_func_range() + new + call setline(1, range(1, 8)) + func FuncRange() range + echo a:firstline + echo a:lastline + endfunc + 3 + call assert_equal("\n3\n3", execute('call FuncRange()')) + call assert_equal("\n4\n6", execute('4,6 call FuncRange()')) + call assert_equal("\n function FuncRange() range" + \ .. "\n1 echo a:firstline" + \ .. "\n2 echo a:lastline" + \ .. "\n endfunction", + \ execute('function FuncRange')) + + bwipe! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_usercommands.vim b/src/nvim/testdir/test_usercommands.vim index e37fe43b22..6910361345 100644 --- a/src/nvim/testdir/test_usercommands.vim +++ b/src/nvim/testdir/test_usercommands.vim @@ -79,6 +79,19 @@ function Test_cmdmods() call assert_equal('silent!', g:mods) tab MyCmd call assert_equal('tab', g:mods) + 0tab MyCmd + call assert_equal('0tab', g:mods) + tab split + tab MyCmd + call assert_equal('tab', g:mods) + 1tab MyCmd + call assert_equal('1tab', g:mods) + tabprev + tab MyCmd + call assert_equal('tab', g:mods) + 2tab MyCmd + call assert_equal('2tab', g:mods) + 2tabclose topleft MyCmd call assert_equal('topleft', g:mods) to MyCmd @@ -101,6 +114,10 @@ function Test_cmdmods() call assert_equal('vertical', g:mods) vert MyCmd call assert_equal('vertical', g:mods) + horizontal MyCmd + call assert_equal('horizontal', g:mods) + hor MyCmd + call assert_equal('horizontal', g:mods) aboveleft belowright botright browse confirm hide keepalt keepjumps \ keepmarks keeppatterns lockmarks noautocmd noswapfile silent @@ -306,6 +323,11 @@ func Test_CmdErrors() call assert_fails('com DoCmd :', 'E174:') comclear call assert_fails('delcom DoCmd', 'E184:') + + " These used to leak memory + call assert_fails('com! -complete=custom,CustomComplete _ :', 'E182:') + call assert_fails('com! -complete=custom,CustomComplete docmd :', 'E183:') + call assert_fails('com! -complete=custom,CustomComplete -xxx DoCmd :', 'E181:') endfunc func CustomComplete(A, L, P) @@ -313,7 +335,7 @@ func CustomComplete(A, L, P) endfunc func CustomCompleteList(A, L, P) - return [ "Monday", "Tuesday", "Wednesday", {}] + return [ "Monday", "Tuesday", "Wednesday", {}, v:_null_string] endfunc func Test_CmdCompletion() @@ -332,6 +354,14 @@ func Test_CmdCompletion() call feedkeys(":com -complete=co\<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"com -complete=color command compiler', @:) + " try completion for unsupported argument values + call feedkeys(":com -newarg=\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"com -newarg=\t", @:) + + " command completion after the name in a user defined command + call feedkeys(":com MyCmd chist\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"com MyCmd chistory", @:) + command! DoCmd1 : command! DoCmd2 : call feedkeys(":com \<C-A>\<C-B>\"\<CR>", 'tx') @@ -343,6 +373,10 @@ func Test_CmdCompletion() call feedkeys(":delcom DoC\<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"delcom DoCmd1 DoCmd2', @:) + " try argument completion for a command without completion + call feedkeys(":DoCmd1 \<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"DoCmd1 \t", @:) + delcom DoCmd1 call feedkeys(":delcom DoC\<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"delcom DoCmd2', @:) @@ -361,6 +395,21 @@ func Test_CmdCompletion() call feedkeys(":DoCmd \<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"DoCmd mswin xterm', @:) + " Test for file name completion + com! -nargs=1 -complete=file DoCmd : + call feedkeys(":DoCmd READM\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"DoCmd README.txt', @:) + + " Test for buffer name completion + com! -nargs=1 -complete=buffer DoCmd : + let bnum = bufadd('BufForUserCmd') + call setbufvar(bnum, '&buflisted', 1) + call feedkeys(":DoCmd BufFor\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"DoCmd BufForUserCmd', @:) + bwipe BufForUserCmd + call feedkeys(":DoCmd BufFor\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"DoCmd BufFor', @:) + com! -nargs=* -complete=custom,CustomComplete DoCmd : call feedkeys(":DoCmd \<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"DoCmd January February Mars', @:) @@ -383,6 +432,17 @@ func Test_CmdCompletion() com! -nargs=? -complete=custom,min DoCmd call assert_fails("call feedkeys(':DoCmd \t', 'tx')", 'E118:') + " custom completion for a pattern with a backslash + let g:ArgLead = '' + func! CustCompl(A, L, P) + let g:ArgLead = a:A + return ['one', 'two', 'three'] + endfunc + com! -nargs=? -complete=customlist,CustCompl DoCmd + call feedkeys(":DoCmd a\\\t", 'xt') + call assert_equal('a\', g:ArgLead) + delfunc CustCompl + delcom DoCmd endfunc @@ -600,6 +660,27 @@ func Test_command_list() call assert_equal("\nNo user-defined commands found", execute('command')) endfunc +" Test for a custom user completion returning the wrong value type +func Test_usercmd_custom() + func T1(a, c, p) + return "a\nb\n" + endfunc + command -nargs=* -complete=customlist,T1 TCmd1 + call feedkeys(":TCmd1 \<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"TCmd1 ', @:) + delcommand TCmd1 + delfunc T1 + + func T2(a, c, p) + return {} + endfunc + command -nargs=* -complete=customlist,T2 TCmd2 + call feedkeys(":TCmd2 \<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"TCmd2 ', @:) + delcommand TCmd2 + delfunc T2 +endfunc + func Test_delcommand_buffer() command Global echo 'global' command -buffer OneBuffer echo 'one' @@ -640,5 +721,30 @@ func Test_recursive_define() endwhile endfunc +" Test for using buffer-local ambiguous user-defined commands +func Test_buflocal_ambiguous_usercmd() + new + command -buffer -nargs=1 -complete=sign TestCmd1 echo "Hello" + command -buffer -nargs=1 -complete=sign TestCmd2 echo "World" + + call assert_fails("call feedkeys(':TestCmd\<CR>', 'xt')", 'E464:') + call feedkeys(":TestCmd \<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"TestCmd ', @:) + + delcommand TestCmd1 + delcommand TestCmd2 + bw! +endfunc + +" Test for using a multibyte character in a user command +func Test_multibyte_in_usercmd() + command SubJapanesePeriodToDot exe "%s/\u3002/./g" + new + call setline(1, "Hello\u3002") + SubJapanesePeriodToDot + call assert_equal('Hello.', getline(1)) + bw! + delcommand SubJapanesePeriodToDot +endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim index ab3503c282..e5f6d68720 100644 --- a/src/nvim/testdir/test_utf8.vim +++ b/src/nvim/testdir/test_utf8.vim @@ -2,6 +2,7 @@ source check.vim source view_util.vim +source screendump.vim " Visual block Insert adjusts for multi-byte char func Test_visual_block_insert() @@ -12,7 +13,7 @@ func Test_visual_block_insert() bwipeout! endfunc -" Test for built-in function strchars() +" Test for built-in functions strchars() and strcharlen() func Test_strchars() let inp = ["a", "あいa", "A\u20dd", "A\u20dd\u20dd", "\u20dd"] let exp = [[1, 1, 1], [3, 3, 3], [2, 2, 1], [3, 3, 1], [1, 1, 1]] @@ -21,6 +22,15 @@ func Test_strchars() call assert_equal(exp[i][1], inp[i]->strchars(0)) call assert_equal(exp[i][2], strchars(inp[i], 1)) endfor + + let exp = [1, 3, 1, 1, 1] + for i in range(len(inp)) + call assert_equal(exp[i], inp[i]->strcharlen()) + call assert_equal(exp[i], strcharlen(inp[i])) + endfor + + call assert_fails("let v=strchars('abc', [])", 'E745:') + call assert_fails("let v=strchars('abc', 2)", 'E1023:') endfunc " Test for customlist completion @@ -131,9 +141,13 @@ func Test_list2str_str2list_latin1() let save_encoding = &encoding " set encoding=latin1 - + let lres = str2list(s, 1) let sres = list2str(l, 1) + call assert_equal([65, 66, 67], str2list("ABC")) + + " Try converting a list to a string in latin-1 encoding + call assert_equal([1, 2, 3], str2list(list2str([1, 2, 3]))) let &encoding = save_encoding call assert_equal(l, lres) @@ -153,6 +167,39 @@ func Test_setcellwidths() call assert_equal(2, strwidth("\u1339")) call assert_equal(1, strwidth("\u133a")) + for aw in ['single', 'double'] + exe 'set ambiwidth=' . aw + " Handle \u0080 to \u009F as control chars even on MS-Windows. + set isprint=@,161-255 + + call setcellwidths([]) + " Control chars + call assert_equal(4, strwidth("\u0081")) + call assert_equal(6, strwidth("\uFEFF")) + " Ambiguous width chars + call assert_equal((aw == 'single') ? 1 : 2, strwidth("\u00A1")) + call assert_equal((aw == 'single') ? 1 : 2, strwidth("\u2010")) + + call setcellwidths([[0x81, 0x81, 1], [0xA1, 0xA1, 1], + \ [0x2010, 0x2010, 1], [0xFEFF, 0xFEFF, 1]]) + " Control chars + call assert_equal(4, strwidth("\u0081")) + call assert_equal(6, strwidth("\uFEFF")) + " Ambiguous width chars + call assert_equal(1, strwidth("\u00A1")) + call assert_equal(1, strwidth("\u2010")) + + call setcellwidths([[0x81, 0x81, 2], [0xA1, 0xA1, 2], + \ [0x2010, 0x2010, 2], [0xFEFF, 0xFEFF, 2]]) + " Control chars + call assert_equal(4, strwidth("\u0081")) + call assert_equal(6, strwidth("\uFEFF")) + " Ambiguous width chars + call assert_equal(2, strwidth("\u00A1")) + call assert_equal(2, strwidth("\u2010")) + endfor + set ambiwidth& isprint& + call setcellwidths([]) call assert_fails('call setcellwidths(1)', 'E714:') @@ -185,6 +232,42 @@ func Test_setcellwidths() call setcellwidths([]) endfunc +func Test_getcellwidths() + call setcellwidths([]) + call assert_equal([], getcellwidths()) + + let widthlist = [ + \ [0x1330, 0x1330, 2], + \ [9999, 10000, 1], + \ [0x1337, 0x1339, 2], + \] + let widthlistsorted = [ + \ [0x1330, 0x1330, 2], + \ [0x1337, 0x1339, 2], + \ [9999, 10000, 1], + \] + call setcellwidths(widthlist) + call assert_equal(widthlistsorted, getcellwidths()) + + call setcellwidths([]) +endfunc + +func Test_setcellwidths_dump() + CheckRunVimInTerminal + + let lines =<< trim END + call setline(1, "\ue5ffDesktop") + END + call writefile(lines, 'XCellwidths', 'D') + let buf = RunVimInTerminal('-S XCellwidths', {'rows': 6}) + call VerifyScreenDump(buf, 'Test_setcellwidths_dump_1', {}) + + call term_sendkeys(buf, ":call setcellwidths([[0xe5ff, 0xe5ff, 2]])\<CR>") + call VerifyScreenDump(buf, 'Test_setcellwidths_dump_2', {}) + + call StopVimInTerminal(buf) +endfunc + func Test_print_overlong() " Text with more composing characters than MB_MAXBYTES. new diff --git a/src/nvim/testdir/test_vartabs.vim b/src/nvim/testdir/test_vartabs.vim index 68fe15ff93..e12c71d521 100644 --- a/src/nvim/testdir/test_vartabs.vim +++ b/src/nvim/testdir/test_vartabs.vim @@ -97,7 +97,7 @@ func Test_vartabs() .retab! call assert_equal("\t\t\t\tl", getline(1)) - " Test for 'retab' with same vlaues as vts + " Test for 'retab' with same values as vts set ts=8 sts=0 vts=5,3,6,2 vsts= exe "norm! S l" .retab! 5,3,6,2 @@ -379,6 +379,8 @@ func Test_vartabs_shiftwidth() let lines = ScreenLines([1, 3], winwidth(0)) call s:compare_lines(expect4, lines) + call assert_fails('call shiftwidth([])', 'E745:') + " cleanup bw! bw! @@ -429,4 +431,18 @@ func Test_varsofttabstop() close! endfunc +" Setting 'shiftwidth' to a negative value, should set it to either the value +" of 'tabstop' (if 'vartabstop' is not set) or to the first value in +" 'vartabstop' +func Test_shiftwidth_vartabstop() + throw 'Skipped: Nvim removed this behavior in #6377' + setlocal tabstop=7 vartabstop= + call assert_fails('set shiftwidth=-1', 'E487:') + call assert_equal(7, &shiftwidth) + setlocal tabstop=7 vartabstop=5,7,10 + call assert_fails('set shiftwidth=-1', 'E487:') + call assert_equal(5, &shiftwidth) + setlocal shiftwidth& vartabstop& tabstop& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_viminfo.vim b/src/nvim/testdir/test_viminfo.vim new file mode 100644 index 0000000000..e792db90ab --- /dev/null +++ b/src/nvim/testdir/test_viminfo.vim @@ -0,0 +1,26 @@ + +" Test for errors in setting 'viminfo' +func Test_viminfo_option_error() + " Missing number + call assert_fails('set viminfo=\"', 'E526:') + for c in split("'/:<@s", '\zs') + call assert_fails('set viminfo=' .. c, 'E526:') + endfor + + " Missing comma + call assert_fails('set viminfo=%10!', 'E527:') + call assert_fails('set viminfo=!%10', 'E527:') + call assert_fails('set viminfo=h%10', 'E527:') + call assert_fails('set viminfo=c%10', 'E527:') + call assert_fails('set viminfo=:10%10', 'E527:') + + " Missing ' setting + call assert_fails('set viminfo=%10', 'E528:') +endfunc + +func Test_viminfo_oldfiles_newfile() + let v:oldfiles = v:_null_list + call assert_equal("\nNo old files", execute('oldfiles')) +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index 0f204cdd0c..b0c4baf7c2 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1,16 +1,15 @@ " Test various aspects of the Vim script language. -" Most of this was formerly in test49. +" Most of this was formerly in test49.vim (developed by Servatius Brandt +" <Servatius.Brandt@fujitsu-siemens.com>) source check.vim source shared.vim +source script_util.vim "------------------------------------------------------------------------------- " Test environment {{{1 "------------------------------------------------------------------------------- -com! XpathINIT let g:Xpath = '' -com! -nargs=1 -bar Xpath let g:Xpath = g:Xpath . <args> - " Append a message to the "messages" file func Xout(text) split messages @@ -20,67 +19,31 @@ endfunc com! -nargs=1 Xout call Xout(<args>) -" MakeScript() - Make a script file from a function. {{{2 -" -" Create a script that consists of the body of the function a:funcname. -" Replace any ":return" by a ":finish", any argument variable by a global -" variable, and every ":call" by a ":source" for the next following argument -" in the variable argument list. This function is useful if similar tests are -" to be made for a ":return" from a function call or a ":finish" in a script -" file. -func MakeScript(funcname, ...) - let script = tempname() - execute "redir! >" . script - execute "function" a:funcname - redir END - execute "edit" script - " Delete the "function" and the "endfunction" lines. Do not include the - " word "function" in the pattern since it might be translated if LANG is - " set. When MakeScript() is being debugged, this deletes also the debugging - " output of its line 3 and 4. - exec '1,/.*' . a:funcname . '(.*)/d' - /^\d*\s*endfunction\>/,$d - %s/^\d*//e - %s/return/finish/e - %s/\<a:\(\h\w*\)/g:\1/ge - normal gg0 - let cnt = 0 - while search('\<call\s*\%(\u\|s:\)\w*\s*(.*)', 'W') > 0 - let cnt = cnt + 1 - s/\<call\s*\%(\u\|s:\)\w*\s*(.*)/\='source ' . a:{cnt}/ - endwhile - g/^\s*$/d - write - bwipeout - return script -endfunc - -" ExecAsScript - Source a temporary script made from a function. {{{2 -" -" Make a temporary script file from the function a:funcname, ":source" it, and -" delete it afterwards. However, if an exception is thrown the file may remain, -" the caller should call DeleteTheScript() afterwards. -let s:script_name = '' -function! ExecAsScript(funcname) - " Make a script from the function passed as argument. - let s:script_name = MakeScript(a:funcname) - - " Source and delete the script. - exec "source" s:script_name - call delete(s:script_name) - let s:script_name = '' -endfunction - -function! DeleteTheScript() - if s:script_name - call delete(s:script_name) - let s:script_name = '' - endif +" Create a new instance of Vim and run the commands in 'test' and then 'verify' +" The commands in 'test' are expected to store the test results in the Xtest.out +" file. If the test passes successfully, then Xtest.out should be empty. +func RunInNewVim(test, verify) + let init =<< trim END + set cpo-=C " support line-continuation in sourced script + source script_util.vim + XpathINIT + XloopINIT + END + let cleanup =<< trim END + call writefile(v:errors, 'Xtest.out') + qall + END + call writefile(init, 'Xtest.vim') + call writefile(a:test, 'Xtest.vim', 'a') + call writefile(a:verify, 'Xverify.vim') + call writefile(cleanup, 'Xverify.vim', 'a') + call RunVim([], [], "-S Xtest.vim -S Xverify.vim") + call assert_equal([], readfile('Xtest.out')) + call delete('Xtest.out') + call delete('Xtest.vim') + call delete('Xverify.vim') endfunc -com! -nargs=1 -bar ExecAsScript call ExecAsScript(<f-args>) - - "------------------------------------------------------------------------------- " Test 1: :endwhile in function {{{1 " @@ -90,7 +53,7 @@ com! -nargs=1 -bar ExecAsScript call ExecAsScript(<f-args>) " tests will hang. "------------------------------------------------------------------------------- -function! T1_F() +func T1_F() Xpath 'a' let first = 1 while 1 @@ -104,9 +67,9 @@ function! T1_F() return endif endwhile -endfunction +endfunc -function! T1_G() +func T1_G() Xpath 'h' let first = 1 while 1 @@ -121,7 +84,7 @@ function! T1_G() endif if 1 " unmatched :if endwhile -endfunction +endfunc func Test_endwhile_function() XpathINIT @@ -175,7 +138,7 @@ endfunc " Test 3: :if, :elseif, :while, :continue, :break {{{1 "------------------------------------------------------------------------------- -function Test_if_while() +func Test_if_while() XpathINIT if 1 Xpath 'a' @@ -231,11 +194,21 @@ function Test_if_while() call assert_equal('ab3j3b2c2b1f1h1km', g:Xpath) endfunc +" Check double quote after skipped "elseif" does not give error E15 +func Test_skipped_elseif() + if "foo" ==? "foo" + let result = "first" + elseif "foo" ==? "foo" + let result = "second" + endif + call assert_equal('first', result) +endfunc + "------------------------------------------------------------------------------- " Test 4: :return {{{1 "------------------------------------------------------------------------------- -function! T4_F() +func T4_F() if 1 Xpath 'a' let loops = 3 @@ -253,15 +226,15 @@ function! T4_F() else Xpath 'g' endif -endfunction +endfunc -function Test_return() +func Test_return() XpathINIT call T4_F() Xpath '4' call assert_equal('ab3e3b2c24', g:Xpath) -endfunction +endfunc "------------------------------------------------------------------------------- @@ -271,14 +244,14 @@ endfunction " test as a script file (:return replaced by :finish). "------------------------------------------------------------------------------- -function Test_finish() +func Test_finish() XpathINIT ExecAsScript T4_F Xpath '5' call DeleteTheScript() call assert_equal('ab3e3b2c25', g:Xpath) -endfunction +endfunc @@ -412,7 +385,7 @@ delfunction G31 delfunction G32 delfunction G33 -function Test_defining_functions() +func Test_defining_functions() call assert_equal('ade2ie3ibcg0h1g1h2g2h3fg0h1g1h2g2h3m', g:test6_result) call assert_equal('F1G1F2G21G22G23F3G31G32G33', g:test6_calls) endfunc @@ -476,7 +449,7 @@ endfunc XpathINIT -function! T8_F() +func T8_F() if 1 Xpath 'a' while 1 @@ -508,9 +481,9 @@ function! T8_F() return novar " returns (default return value 0) Xpath 'q' return 1 " not reached -endfunction +endfunc -function! T8_G() abort +func T8_G() abort if 1 Xpath 'r' while 1 @@ -524,9 +497,9 @@ function! T8_G() abort Xpath 'x' return -4 " not reached -endfunction +endfunc -function! T8_H() abort +func T8_H() abort while 1 Xpath 'A' if 1 @@ -540,7 +513,7 @@ function! T8_H() abort Xpath 'F' return -4 " not reached -endfunction +endfunc " Aborted functions (T8_G and T8_H) return -1. let g:test8_sum = (T8_F() + 1) - 4 * T8_G() - 8 * T8_H() @@ -567,7 +540,7 @@ endfunc XpathINIT -function! F() abort +func F() abort Xpath 'a' let result = G() " not aborted Xpath 'b' @@ -575,30 +548,30 @@ function! F() abort Xpath 'c' endif return 1 -endfunction +endfunc -function! G() " no abort attribute +func G() " no abort attribute Xpath 'd' if H() != -1 " aborted Xpath 'e' endif Xpath 'f' return 2 -endfunction +endfunc -function! H() abort +func H() abort Xpath 'g' call I() " aborted Xpath 'h' return 4 -endfunction +endfunc -function! I() abort +func I() abort Xpath 'i' asdf " error Xpath 'j' return 8 -endfunction +endfunc if F() != 1 Xpath 'k' @@ -626,7 +599,7 @@ endfunc XpathINIT -function! MSG(enr, emsg) +func MSG(enr, emsg) let english = v:lang == "C" || v:lang =~ '^[Ee]n' if a:enr == "" Xout "TODO: Add message number for:" a:emsg @@ -710,10 +683,10 @@ XpathINIT let calls = 0 -function! P(num) +func P(num) let g:calls = g:calls + a:num " side effect on call return 0 -endfunction +endfunc if 1 Xpath 'a' @@ -756,23 +729,23 @@ endfunc XpathINIT -function! NULL() +func NULL() Xpath 'a' return 0 -endfunction +endfunc -function! ZERO() +func ZERO() Xpath 'b' return 0 -endfunction +endfunc -function! F0() +func! F0() Xpath 'c' -endfunction +endfunc -function! F1(arg) +func! F1(arg) Xpath 'e' -endfunction +endfunc let V0 = 1 @@ -1092,7 +1065,5387 @@ func Test_unmatched_if_in_while() endfunc "------------------------------------------------------------------------------- +" Test 18: Interrupt (Ctrl-C pressed) {{{1 +" +" On an interrupt, the script processing is terminated immediately. +"------------------------------------------------------------------------------- + +func Test_interrupt_while_if() + let test =<< trim [CODE] + try + if 1 + Xpath 'a' + while 1 + Xpath 'b' + if 1 + Xpath 'c' + call interrupt() + call assert_report('should not get here') + break + finish + endif | call assert_report('should not get here') + call assert_report('should not get here') + endwhile | call assert_report('should not get here') + call assert_report('should not get here') + endif | call assert_report('should not get here') + call assert_report('should not get here') + catch /^Vim:Interrupt$/ + Xpath 'd' + endtry | Xpath 'e' + Xpath 'f' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcdef', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_interrupt_try() + let test =<< trim [CODE] + try + try + Xpath 'a' + call interrupt() + call assert_report('should not get here') + endtry | call assert_report('should not get here') + call assert_report('should not get here') + catch /^Vim:Interrupt$/ + Xpath 'b' + endtry | Xpath 'c' + Xpath 'd' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcd', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_interrupt_func_while_if() + let test =<< trim [CODE] + func F() + if 1 + Xpath 'a' + while 1 + Xpath 'b' + if 1 + Xpath 'c' + call interrupt() + call assert_report('should not get here') + break + return + endif | call assert_report('should not get here') + call assert_report('should not get here') + endwhile | call assert_report('should not get here') + call assert_report('should not get here') + endif | call assert_report('should not get here') + call assert_report('should not get here') + endfunc + + Xpath 'd' + try + call F() | call assert_report('should not get here') + catch /^Vim:Interrupt$/ + Xpath 'e' + endtry | Xpath 'f' + Xpath 'g' + [CODE] + let verify =<< trim [CODE] + call assert_equal('dabcefg', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_interrupt_func_try() + let test =<< trim [CODE] + func G() + try + Xpath 'a' + call interrupt() + call assert_report('should not get here') + endtry | call assert_report('should not get here') + call assert_report('should not get here') + endfunc + + Xpath 'b' + try + call G() | call assert_report('should not get here') + catch /^Vim:Interrupt$/ + Xpath 'c' + endtry | Xpath 'd' + Xpath 'e' + [CODE] + let verify =<< trim [CODE] + call assert_equal('bacde', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 19: Aborting on errors inside :try/:endtry {{{1 +" +" An error in a command dynamically enclosed in a :try/:endtry region +" aborts script processing immediately. It does not matter whether +" the failing command is outside or inside a function and whether a +" function has an "abort" attribute. +"------------------------------------------------------------------------------- + +func Test_try_error_abort_1() + let test =<< trim [CODE] + func F() abort + Xpath 'a' + asdf + call assert_report('should not get here') + endfunc + + try + Xpath 'b' + call F() + call assert_report('should not get here') + endtry | call assert_report('should not get here') + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('ba', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_try_error_abort_2() + let test =<< trim [CODE] + func G() + Xpath 'a' + asdf + call assert_report('should not get here') + endfunc + + try + Xpath 'b' + call G() + call assert_report('should not get here') + endtry | call assert_report('should not get here') + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('ba', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_try_error_abort_3() + let test =<< trim [CODE] + try + Xpath 'a' + asdf + call assert_report('should not get here') + endtry | call assert_report('should not get here') + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('a', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_try_error_abort_4() + let test =<< trim [CODE] + if 1 + try + Xpath 'a' + asdf + call assert_report('should not get here') + endtry | call assert_report('should not get here') + endif | call assert_report('should not get here') + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('a', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_try_error_abort_5() + let test =<< trim [CODE] + let p = 1 + while p + let p = 0 + try + Xpath 'a' + asdf + call assert_report('should not get here') + endtry | call assert_report('should not get here') + endwhile | call assert_report('should not get here') + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('a', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_try_error_abort_6() + let test =<< trim [CODE] + let p = 1 + Xpath 'a' + while p + Xpath 'b' + let p = 0 + try + Xpath 'c' + endwhile | call assert_report('should not get here') + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('abc', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 20: Aborting on errors after :try/:endtry {{{1 +" +" When an error occurs after the last active :try/:endtry region has +" been left, termination behavior is as if no :try/:endtry has been +" seen. +"------------------------------------------------------------------------------- + +func Test_error_after_try_1() + let test =<< trim [CODE] + let p = 1 + while p + let p = 0 + Xpath 'a' + try + Xpath 'b' + endtry + asdf + call assert_report('should not get here') + endwhile | call assert_report('should not get here') + Xpath 'c' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abc', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_error_after_try_2() + let test =<< trim [CODE] + while 1 + try + Xpath 'a' + break + call assert_report('should not get here') + endtry + endwhile + Xpath 'b' + asdf + Xpath 'c' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abc', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_error_after_try_3() + let test =<< trim [CODE] + while 1 + try + Xpath 'a' + break + call assert_report('should not get here') + finally + Xpath 'b' + endtry + endwhile + Xpath 'c' + asdf + Xpath 'd' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcd', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_error_after_try_4() + let test =<< trim [CODE] + while 1 + try + Xpath 'a' + finally + Xpath 'b' + break + call assert_report('should not get here') + endtry + endwhile + Xpath 'c' + asdf + Xpath 'd' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcd', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_error_after_try_5() + let test =<< trim [CODE] + let p = 1 + while p + let p = 0 + try + Xpath 'a' + continue + call assert_report('should not get here') + endtry + endwhile + Xpath 'b' + asdf + Xpath 'c' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abc', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_error_after_try_6() + let test =<< trim [CODE] + let p = 1 + while p + let p = 0 + try + Xpath 'a' + continue + call assert_report('should not get here') + finally + Xpath 'b' + endtry + endwhile + Xpath 'c' + asdf + Xpath 'd' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcd', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_error_after_try_7() + let test =<< trim [CODE] + let p = 1 + while p + let p = 0 + try + Xpath 'a' + finally + Xpath 'b' + continue + call assert_report('should not get here') + endtry + endwhile + Xpath 'c' + asdf + Xpath 'd' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcd', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 21: :finally for :try after :continue/:break/:return/:finish {{{1 +" +" If a :try conditional stays inactive due to a preceding :continue, +" :break, :return, or :finish, its :finally clause should not be +" executed. +"------------------------------------------------------------------------------- + +func Test_finally_after_loop_ctrl_statement() + let test =<< trim [CODE] + func F() + let loops = 2 + while loops > 0 + XloopNEXT + let loops = loops - 1 + try + if loops == 1 + Xloop 'a' + continue + call assert_report('should not get here') + elseif loops == 0 + Xloop 'b' + break + call assert_report('should not get here') + endif + + try " inactive + call assert_report('should not get here') + finally + call assert_report('should not get here') + endtry + finally + Xloop 'c' + endtry + call assert_report('should not get here') + endwhile + + try + Xpath 'd' + return + call assert_report('should not get here') + try " inactive + call assert_report('should not get here') + finally + call assert_report('should not get here') + endtry + finally + Xpath 'e' + endtry + call assert_report('should not get here') + endfunc + + try + Xpath 'f' + call F() + Xpath 'g' + finish + call assert_report('should not get here') + try " inactive + call assert_report('should not get here') + finally + call assert_report('should not get here') + endtry + finally + Xpath 'h' + endtry + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('fa2c2b3c3degh', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 22: :finally for a :try after an error/interrupt/:throw {{{1 +" +" If a :try conditional stays inactive due to a preceding error or +" interrupt or :throw, its :finally clause should not be executed. +"------------------------------------------------------------------------------- + +func Test_finally_after_error_in_func() + let test =<< trim [CODE] + func Error() + try + Xpath 'b' + asdf " aborting error, triggering error exception + call assert_report('should not get here') + endtry + call assert_report('should not get here') + endfunc + + Xpath 'a' + call Error() + call assert_report('should not get here') + + if 1 " not active due to error + try " not active since :if inactive + call assert_report('should not get here') + finally + call assert_report('should not get here') + endtry + endif + + try " not active due to error + call assert_report('should not get here') + finally + call assert_report('should not get here') + endtry + [CODE] + let verify =<< trim [CODE] + call assert_equal('ab', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_finally_after_interrupt() + let test =<< trim [CODE] + func Interrupt() + try + Xpath 'a' + call interrupt() " triggering interrupt exception + call assert_report('should not get here') + endtry + endfunc + + Xpath 'b' + try + call Interrupt() + catch /^Vim:Interrupt$/ + Xpath 'c' + finish + endtry + call assert_report('should not get here') + + if 1 " not active due to interrupt + try " not active since :if inactive + call assert_report('should not get here') + finally + call assert_report('should not get here') + endtry + endif + + try " not active due to interrupt + call assert_report('should not get here') + finally + call assert_report('should not get here') + endtry + [CODE] + let verify =<< trim [CODE] + call assert_equal('bac', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_finally_after_throw() + let test =<< trim [CODE] + func Throw() + Xpath 'a' + throw 'xyz' + endfunc + + Xpath 'b' + call Throw() + call assert_report('should not get here') + + if 1 " not active due to :throw + try " not active since :if inactive + call assert_report('should not get here') + finally + call assert_report('should not get here') + endtry + endif + + try " not active due to :throw + call assert_report('should not get here') + finally + call assert_report('should not get here') + endtry + [CODE] + let verify =<< trim [CODE] + call assert_equal('ba', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 23: :catch clauses for a :try after a :throw {{{1 +" +" If a :try conditional stays inactive due to a preceding :throw, +" none of its :catch clauses should be executed. +"------------------------------------------------------------------------------- + +func Test_catch_after_throw() + let test =<< trim [CODE] + try + Xpath 'a' + throw "xyz" + call assert_report('should not get here') + + if 1 " not active due to :throw + try " not active since :if inactive + call assert_report('should not get here') + catch /xyz/ + call assert_report('should not get here') + endtry + endif + catch /xyz/ + Xpath 'b' + endtry + + Xpath 'c' + throw "abc" + call assert_report('should not get here') + + try " not active due to :throw + call assert_report('should not get here') + catch /abc/ + call assert_report('should not get here') + endtry + [CODE] + let verify =<< trim [CODE] + call assert_equal('abc', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 24: :endtry for a :try after a :throw {{{1 +" +" If a :try conditional stays inactive due to a preceding :throw, +" its :endtry should not rethrow the exception to the next surrounding +" active :try conditional. +"------------------------------------------------------------------------------- + +func Test_endtry_after_throw() + let test =<< trim [CODE] + try " try 1 + try " try 2 + Xpath 'a' + throw "xyz" " makes try 2 inactive + call assert_report('should not get here') + + try " try 3 + call assert_report('should not get here') + endtry " no rethrow to try 1 + catch /xyz/ " should catch although try 2 inactive + Xpath 'b' + endtry + catch /xyz/ " try 1 active, but exception already caught + call assert_report('should not get here') + endtry + Xpath 'c' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abc', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 27: Executing :finally clauses after :return {{{1 +" +" For a :return command dynamically enclosed in a :try/:endtry region, +" :finally clauses are executed and the called function is ended. +"------------------------------------------------------------------------------- + +func T27_F() + try + Xpath 'a' + try + Xpath 'b' + return + call assert_report('should not get here') + finally + Xpath 'c' + endtry + Xpath 'd' + finally + Xpath 'e' + endtry + call assert_report('should not get here') +endfunc + +func T27_G() + try + Xpath 'f' + return + call assert_report('should not get here') + finally + Xpath 'g' + call T27_F() + Xpath 'h' + endtry + call assert_report('should not get here') +endfunc + +func T27_H() + try + Xpath 'i' + call T27_G() + Xpath 'j' + finally + Xpath 'k' + return + call assert_report('should not get here') + endtry + call assert_report('should not get here') +endfunction + +func Test_finally_after_return() + XpathINIT + try + Xpath 'l' + call T27_H() + Xpath 'm' + finally + Xpath 'n' + endtry + call assert_equal('lifgabcehjkmn', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +" Test 28: Executing :finally clauses after :finish {{{1 +" +" For a :finish command dynamically enclosed in a :try/:endtry region, +" :finally clauses are executed and the sourced file is finished. +" +" This test executes the bodies of the functions F, G, and H from the +" previous test as script files (:return replaced by :finish). +"------------------------------------------------------------------------------- + +func Test_finally_after_finish() + XpathINIT + + let scriptF = MakeScript("T27_F") + let scriptG = MakeScript("T27_G", scriptF) + let scriptH = MakeScript("T27_H", scriptG) + + try + Xpath 'A' + exec "source" scriptH + Xpath 'B' + finally + Xpath 'C' + endtry + Xpath 'D' + call assert_equal('AifgabcehjkBCD', g:Xpath) + call delete(scriptF) + call delete(scriptG) + call delete(scriptH) +endfunc + +"------------------------------------------------------------------------------- +" Test 29: Executing :finally clauses on errors {{{1 +" +" After an error in a command dynamically enclosed in a :try/:endtry +" region, :finally clauses are executed and the script processing is +" terminated. +"------------------------------------------------------------------------------- + +func Test_finally_after_error_1() + let test =<< trim [CODE] + func F() + while 1 + try + Xpath 'a' + while 1 + try + Xpath 'b' + asdf " error + call assert_report('should not get here') + finally + Xpath 'c' + endtry | call assert_report('should not get here') + call assert_report('should not get here') + break + endwhile + call assert_report('should not get here') + finally + Xpath 'd' + endtry | call assert_report('should not get here') + call assert_report('should not get here') + break + endwhile + call assert_report('should not get here') + endfunc + + while 1 + try + Xpath 'e' + while 1 + call F() + call assert_report('should not get here') + break + endwhile | call assert_report('should not get here') + call assert_report('should not get here') + finally + Xpath 'f' + endtry | call assert_report('should not get here') + endwhile | call assert_report('should not get here') + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('eabcdf', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_finally_after_error_2() + let test =<< trim [CODE] + func G() abort + if 1 + try + Xpath 'a' + asdf " error + call assert_report('should not get here') + finally + Xpath 'b' + endtry | Xpath 'c' + endif | Xpath 'd' + call assert_report('should not get here') + endfunc + + if 1 + try + Xpath 'e' + call G() + call assert_report('should not get here') + finally + Xpath 'f' + endtry | call assert_report('should not get here') + endif | call assert_report('should not get here') + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('eabf', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 30: Executing :finally clauses on interrupt {{{1 +" +" After an interrupt in a command dynamically enclosed in +" a :try/:endtry region, :finally clauses are executed and the +" script processing is terminated. +"------------------------------------------------------------------------------- + +func Test_finally_on_interrupt() + let test =<< trim [CODE] + func F() + try + Xloop 'a' + call interrupt() + call assert_report('should not get here') + finally + Xloop 'b' + endtry + call assert_report('should not get here') + endfunc + + try + try + Xpath 'c' + try + Xpath 'd' + call interrupt() + call assert_report('should not get here') + finally + Xpath 'e' + try + Xpath 'f' + try + Xpath 'g' + finally + Xpath 'h' + try + Xpath 'i' + call interrupt() + call assert_report('should not get here') + endtry + call assert_report('should not get here') + endtry + call assert_report('should not get here') + endtry + call assert_report('should not get here') + endtry + call assert_report('should not get here') + finally + Xpath 'j' + try + Xpath 'k' + call F() + call assert_report('should not get here') + finally + Xpath 'l' + try + Xpath 'm' + XloopNEXT + ExecAsScript F + call assert_report('should not get here') + finally + Xpath 'n' + endtry + call assert_report('should not get here') + endtry + call assert_report('should not get here') + endtry + call assert_report('should not get here') + catch /^Vim:Interrupt$/ + Xpath 'o' + endtry + [CODE] + let verify =<< trim [CODE] + call assert_equal('cdefghijka1b1lma2b2no', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 31: Executing :finally clauses after :throw {{{1 +" +" After a :throw dynamically enclosed in a :try/:endtry region, +" :finally clauses are executed and the script processing is +" terminated. +"------------------------------------------------------------------------------- + +func Test_finally_after_throw_2() + let test =<< trim [CODE] + func F() + try + Xloop 'a' + throw "exception" + call assert_report('should not get here') + finally + Xloop 'b' + endtry + call assert_report('should not get here') + endfunc + + try + Xpath 'c' + try + Xpath 'd' + throw "exception" + call assert_report('should not get here') + finally + Xpath 'e' + try + Xpath 'f' + try + Xpath 'g' + finally + Xpath 'h' + try + Xpath 'i' + throw "exception" + call assert_report('should not get here') + endtry + call assert_report('should not get here') + endtry + call assert_report('should not get here') + endtry + call assert_report('should not get here') + endtry + call assert_report('should not get here') + finally + Xpath 'j' + try + Xpath 'k' + call F() + call assert_report('should not get here') + finally + Xpath 'l' + try + Xpath 'm' + XloopNEXT + ExecAsScript F + call assert_report('should not get here') + finally + Xpath 'n' + endtry + call assert_report('should not get here') + endtry + call assert_report('should not get here') + endtry + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('cdefghijka1b1lma2b2n', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 34: :finally reason discarded by :continue {{{1 +" +" When a :finally clause is executed due to a :continue, :break, +" :return, :finish, error, interrupt or :throw, the jump reason is +" discarded by a :continue in the finally clause. +"------------------------------------------------------------------------------- + +func Test_finally_after_continue() + let test =<< trim [CODE] + func C(jump) + XloopNEXT + let loop = 0 + while loop < 2 + let loop = loop + 1 + if loop == 1 + try + if a:jump == "continue" + continue + elseif a:jump == "break" + break + elseif a:jump == "return" || a:jump == "finish" + return + elseif a:jump == "error" + asdf + elseif a:jump == "interrupt" + call interrupt() + let dummy = 0 + elseif a:jump == "throw" + throw "abc" + endif + finally + continue " discards jump that caused the :finally + call assert_report('should not get here') + endtry + call assert_report('should not get here') + elseif loop == 2 + Xloop 'a' + endif + endwhile + endfunc + + call C("continue") + Xpath 'b' + call C("break") + Xpath 'c' + call C("return") + Xpath 'd' + let g:jump = "finish" + ExecAsScript C + unlet g:jump + Xpath 'e' + try + call C("error") + Xpath 'f' + finally + Xpath 'g' + try + call C("interrupt") + Xpath 'h' + finally + Xpath 'i' + call C("throw") + Xpath 'j' + endtry + endtry + Xpath 'k' + [CODE] + let verify =<< trim [CODE] + call assert_equal('a2ba3ca4da5ea6fga7hia8jk', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 35: :finally reason discarded by :break {{{1 +" +" When a :finally clause is executed due to a :continue, :break, +" :return, :finish, error, interrupt or :throw, the jump reason is +" discarded by a :break in the finally clause. +"------------------------------------------------------------------------------- + +func Test_finally_discard_by_break() + let test =<< trim [CODE] + func B(jump) + XloopNEXT + let loop = 0 + while loop < 2 + let loop = loop + 1 + if loop == 1 + try + if a:jump == "continue" + continue + elseif a:jump == "break" + break + elseif a:jump == "return" || a:jump == "finish" + return + elseif a:jump == "error" + asdf + elseif a:jump == "interrupt" + call interrupt() + let dummy = 0 + elseif a:jump == "throw" + throw "abc" + endif + finally + break " discards jump that caused the :finally + call assert_report('should not get here') + endtry + elseif loop == 2 + call assert_report('should not get here') + endif + endwhile + Xloop 'a' + endfunc + + call B("continue") + Xpath 'b' + call B("break") + Xpath 'c' + call B("return") + Xpath 'd' + let g:jump = "finish" + ExecAsScript B + unlet g:jump + Xpath 'e' + try + call B("error") + Xpath 'f' + finally + Xpath 'g' + try + call B("interrupt") + Xpath 'h' + finally + Xpath 'i' + call B("throw") + Xpath 'j' + endtry + endtry + Xpath 'k' + [CODE] + let verify =<< trim [CODE] + call assert_equal('a2ba3ca4da5ea6fga7hia8jk', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 36: :finally reason discarded by :return {{{1 +" +" When a :finally clause is executed due to a :continue, :break, +" :return, :finish, error, interrupt or :throw, the jump reason is +" discarded by a :return in the finally clause. +"------------------------------------------------------------------------------- + +func Test_finally_discard_by_return() + let test =<< trim [CODE] + func R(jump, retval) abort + let loop = 0 + while loop < 2 + let loop = loop + 1 + if loop == 1 + try + if a:jump == "continue" + continue + elseif a:jump == "break" + break + elseif a:jump == "return" + return + elseif a:jump == "error" + asdf + elseif a:jump == "interrupt" + call interrupt() + let dummy = 0 + elseif a:jump == "throw" + throw "abc" + endif + finally + return a:retval " discards jump that caused the :finally + call assert_report('should not get here') + endtry + elseif loop == 2 + call assert_report('should not get here') + endif + endwhile + call assert_report('should not get here') + endfunc + + let sum = -R("continue", -8) + Xpath 'a' + let sum = sum - R("break", -16) + Xpath 'b' + let sum = sum - R("return", -32) + Xpath 'c' + try + let sum = sum - R("error", -64) + Xpath 'd' + finally + Xpath 'e' + try + let sum = sum - R("interrupt", -128) + Xpath 'f' + finally + Xpath 'g' + let sum = sum - R("throw", -256) + Xpath 'h' + endtry + endtry + Xpath 'i' + + let expected = 8 + 16 + 32 + 64 + 128 + 256 + call assert_equal(sum, expected) + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcdefghi', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 37: :finally reason discarded by :finish {{{1 +" +" When a :finally clause is executed due to a :continue, :break, +" :return, :finish, error, interrupt or :throw, the jump reason is +" discarded by a :finish in the finally clause. +"------------------------------------------------------------------------------- + +func Test_finally_discard_by_finish() + let test =<< trim [CODE] + func F(jump) " not executed as function, transformed to a script + let loop = 0 + while loop < 2 + let loop = loop + 1 + if loop == 1 + try + if a:jump == "continue" + continue + elseif a:jump == "break" + break + elseif a:jump == "finish" + finish + elseif a:jump == "error" + asdf + elseif a:jump == "interrupt" + call interrupt() + let dummy = 0 + elseif a:jump == "throw" + throw "abc" + endif + finally + finish " discards jump that caused the :finally + call assert_report('should not get here') + endtry + elseif loop == 2 + call assert_report('should not get here') + endif + endwhile + call assert_report('should not get here') + endfunc + + let scriptF = MakeScript("F") + delfunction F + + let g:jump = "continue" + exec "source" scriptF + Xpath 'a' + let g:jump = "break" + exec "source" scriptF + Xpath 'b' + let g:jump = "finish" + exec "source" scriptF + Xpath 'c' + try + let g:jump = "error" + exec "source" scriptF + Xpath 'd' + finally + Xpath 'e' + try + let g:jump = "interrupt" + exec "source" scriptF + Xpath 'f' + finally + Xpath 'g' + try + let g:jump = "throw" + exec "source" scriptF + Xpath 'h' + finally + Xpath 'i' + endtry + endtry + endtry + unlet g:jump + call delete(scriptF) + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcdefghi', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 38: :finally reason discarded by an error {{{1 +" +" When a :finally clause is executed due to a :continue, :break, +" :return, :finish, error, interrupt or :throw, the jump reason is +" discarded by an error in the finally clause. +"------------------------------------------------------------------------------- + +func Test_finally_discard_by_error() + let test =<< trim [CODE] + func E(jump) + let loop = 0 + while loop < 2 + let loop = loop + 1 + if loop == 1 + try + if a:jump == "continue" + continue + elseif a:jump == "break" + break + elseif a:jump == "return" || a:jump == "finish" + return + elseif a:jump == "error" + asdf + elseif a:jump == "interrupt" + call interrupt() + let dummy = 0 + elseif a:jump == "throw" + throw "abc" + endif + finally + asdf " error; discards jump that caused the :finally + endtry + elseif loop == 2 + call assert_report('should not get here') + endif + endwhile + call assert_report('should not get here') + endfunc + + try + Xpath 'a' + call E("continue") + call assert_report('should not get here') + finally + try + Xpath 'b' + call E("break") + call assert_report('should not get here') + finally + try + Xpath 'c' + call E("return") + call assert_report('should not get here') + finally + try + Xpath 'd' + let g:jump = "finish" + ExecAsScript E + call assert_report('should not get here') + finally + unlet g:jump + try + Xpath 'e' + call E("error") + call assert_report('should not get here') + finally + try + Xpath 'f' + call E("interrupt") + call assert_report('should not get here') + finally + try + Xpath 'g' + call E("throw") + call assert_report('should not get here') + finally + Xpath 'h' + delfunction E + endtry + endtry + endtry + endtry + endtry + endtry + endtry + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcdefgh', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 39: :finally reason discarded by an interrupt {{{1 +" +" When a :finally clause is executed due to a :continue, :break, +" :return, :finish, error, interrupt or :throw, the jump reason is +" discarded by an interrupt in the finally clause. +"------------------------------------------------------------------------------- + +func Test_finally_discarded_by_interrupt() + let test =<< trim [CODE] + func I(jump) + let loop = 0 + while loop < 2 + let loop = loop + 1 + if loop == 1 + try + if a:jump == "continue" + continue + elseif a:jump == "break" + break + elseif a:jump == "return" || a:jump == "finish" + return + elseif a:jump == "error" + asdf + elseif a:jump == "interrupt" + call interrupt() + let dummy = 0 + elseif a:jump == "throw" + throw "abc" + endif + finally + call interrupt() + let dummy = 0 + endtry + elseif loop == 2 + call assert_report('should not get here') + endif + endwhile + call assert_report('should not get here') + endfunc + + try + try + Xpath 'a' + call I("continue") + call assert_report('should not get here') + finally + try + Xpath 'b' + call I("break") + call assert_report('should not get here') + finally + try + Xpath 'c' + call I("return") + call assert_report('should not get here') + finally + try + Xpath 'd' + let g:jump = "finish" + ExecAsScript I + call assert_report('should not get here') + finally + unlet g:jump + try + Xpath 'e' + call I("error") + call assert_report('should not get here') + finally + try + Xpath 'f' + call I("interrupt") + call assert_report('should not get here') + finally + try + Xpath 'g' + call I("throw") + call assert_report('should not get here') + finally + Xpath 'h' + delfunction I + endtry + endtry + endtry + endtry + endtry + endtry + endtry + call assert_report('should not get here') + catch /^Vim:Interrupt$/ + Xpath 'A' + endtry + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcdefghA', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 40: :finally reason discarded by :throw {{{1 +" +" When a :finally clause is executed due to a :continue, :break, +" :return, :finish, error, interrupt or :throw, the jump reason is +" discarded by a :throw in the finally clause. +"------------------------------------------------------------------------------- + +func Test_finally_discard_by_throw() + let test =<< trim [CODE] + func T(jump) + let loop = 0 + while loop < 2 + let loop = loop + 1 + if loop == 1 + try + if a:jump == "continue" + continue + elseif a:jump == "break" + break + elseif a:jump == "return" || a:jump == "finish" + return + elseif a:jump == "error" + asdf + elseif a:jump == "interrupt" + call interrupt() + let dummy = 0 + elseif a:jump == "throw" + throw "abc" + endif + finally + throw "xyz" " discards jump that caused the :finally + endtry + elseif loop == 2 + call assert_report('should not get here') + endif + endwhile + call assert_report('should not get here') + endfunc + + try + Xpath 'a' + call T("continue") + call assert_report('should not get here') + finally + try + Xpath 'b' + call T("break") + call assert_report('should not get here') + finally + try + Xpath 'c' + call T("return") + call assert_report('should not get here') + finally + try + Xpath 'd' + let g:jump = "finish" + ExecAsScript T + call assert_report('should not get here') + finally + unlet g:jump + try + Xpath 'e' + call T("error") + call assert_report('should not get here') + finally + try + Xpath 'f' + call T("interrupt") + call assert_report('should not get here') + finally + try + Xpath 'g' + call T("throw") + call assert_report('should not get here') + finally + Xpath 'h' + delfunction T + endtry + endtry + endtry + endtry + endtry + endtry + endtry + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcdefgh', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 49: Throwing exceptions across functions {{{1 +" +" When an exception is thrown but not caught inside a function, the +" caller is checked for a matching :catch clause. +"------------------------------------------------------------------------------- + +func T49_C() + try + Xpath 'a' + throw "arrgh" + call assert_report('should not get here') + catch /arrgh/ + Xpath 'b' + endtry + Xpath 'c' +endfunc + +func T49_T1() + XloopNEXT + try + Xloop 'd' + throw "arrgh" + call assert_report('should not get here') + finally + Xloop 'e' + endtry + Xloop 'f' +endfunc + +func T49_T2() + try + Xpath 'g' + call T49_T1() + call assert_report('should not get here') + finally + Xpath 'h' + endtry + call assert_report('should not get here') +endfunc + +func Test_throw_exception_across_funcs() + XpathINIT + XloopINIT + try + Xpath 'i' + call T49_C() " throw and catch + Xpath 'j' + catch /.*/ + call assert_report('should not get here') + endtry + + try + Xpath 'k' + call T49_T1() " throw, one level + call assert_report('should not get here') + catch /arrgh/ + Xpath 'l' + catch /.*/ + call assert_report('should not get here') + endtry + + try + Xpath 'm' + call T49_T2() " throw, two levels + call assert_report('should not get here') + catch /arrgh/ + Xpath 'n' + catch /.*/ + call assert_report('should not get here') + endtry + Xpath 'o' + + call assert_equal('iabcjkd2e2lmgd3e3hno', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +" Test 50: Throwing exceptions across script files {{{1 +" +" When an exception is thrown but not caught inside a script file, +" the sourcing script or function is checked for a matching :catch +" clause. +" +" This test executes the bodies of the functions C, T1, and T2 from +" the previous test as script files (:return replaced by :finish). +"------------------------------------------------------------------------------- + +func T50_F() + try + Xpath 'A' + exec "source" g:scriptC + Xpath 'B' + catch /.*/ + call assert_report('should not get here') + endtry + + try + Xpath 'C' + exec "source" g:scriptT1 + call assert_report('should not get here') + catch /arrgh/ + Xpath 'D' + catch /.*/ + call assert_report('should not get here') + endtry +endfunc + +func Test_throw_across_script() + XpathINIT + XloopINIT + let g:scriptC = MakeScript("T49_C") + let g:scriptT1 = MakeScript("T49_T1") + let scriptT2 = MakeScript("T49_T2", g:scriptT1) + + try + Xpath 'E' + call T50_F() + Xpath 'F' + exec "source" scriptT2 + call assert_report('should not get here') + catch /arrgh/ + Xpath 'G' + catch /.*/ + call assert_report('should not get here') + endtry + Xpath 'H' + call assert_equal('EAabcBCd2e2DFgd3e3hGH', g:Xpath) + + call delete(g:scriptC) + call delete(g:scriptT1) + call delete(scriptT2) + unlet g:scriptC g:scriptT1 scriptT2 +endfunc + +"------------------------------------------------------------------------------- +" Test 52: Uncaught exceptions {{{1 +" +" When an exception is thrown but not caught, an error message is +" displayed when the script is terminated. In case of an interrupt +" or error exception, the normal interrupt or error message(s) are +" displayed. +"------------------------------------------------------------------------------- + +func Test_uncaught_exception_1() + CheckEnglish + + let test =<< trim [CODE] + Xpath 'a' + throw "arrgh" + call assert_report('should not get here')` + [CODE] + let verify =<< trim [CODE] + call assert_equal('E605: Exception not caught: arrgh', v:errmsg) + call assert_equal('a', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_uncaught_exception_2() + CheckEnglish + + let test =<< trim [CODE] + try + Xpath 'a' + throw "oops" + call assert_report('should not get here')` + catch /arrgh/ + call assert_report('should not get here')` + endtry + call assert_report('should not get here')` + [CODE] + let verify =<< trim [CODE] + call assert_equal('E605: Exception not caught: oops', v:errmsg) + call assert_equal('a', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_uncaught_exception_3() + CheckEnglish + + let test =<< trim [CODE] + func T() + Xpath 'c' + throw "brrr" + call assert_report('should not get here')` + endfunc + + try + Xpath 'a' + throw "arrgh" + call assert_report('should not get here')` + catch /.*/ + Xpath 'b' + call T() + call assert_report('should not get here')` + endtry + call assert_report('should not get here')` + [CODE] + let verify =<< trim [CODE] + call assert_equal('E605: Exception not caught: brrr', v:errmsg) + call assert_equal('abc', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_uncaught_exception_4() + CheckEnglish + + let test =<< trim [CODE] + try + Xpath 'a' + throw "arrgh" + call assert_report('should not get here')` + finally + Xpath 'b' + throw "brrr" + call assert_report('should not get here')` + endtry + call assert_report('should not get here')` + [CODE] + let verify =<< trim [CODE] + call assert_equal('E605: Exception not caught: brrr', v:errmsg) + call assert_equal('ab', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_uncaught_exception_5() + CheckEnglish + + " Need to catch and handle interrupt, otherwise the test will wait for the + " user to press <Enter> to continue + let test =<< trim [CODE] + try + try + Xpath 'a' + call interrupt() + call assert_report('should not get here') + endtry + call assert_report('should not get here') + catch /^Vim:Interrupt$/ + Xpath 'b' + endtry + [CODE] + let verify =<< trim [CODE] + call assert_equal('ab', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_uncaught_exception_6() + CheckEnglish + + let test =<< trim [CODE] + try + Xpath 'a' + let x = novar " error E121; exception: E121 + catch /E15:/ " should not catch + call assert_report('should not get here') + endtry + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('a', g:Xpath) + call assert_equal('E121: Undefined variable: novar', v:errmsg) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_uncaught_exception_7() + CheckEnglish + + let test =<< trim [CODE] + try + Xpath 'a' + " error E108/E488; exception: E488 + unlet novar # + catch /E108:/ " should not catch + call assert_report('should not get here') + endtry + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('a', g:Xpath) + call assert_equal('E488: Trailing characters: #', v:errmsg) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 53: Nesting errors: :endif/:else/:elseif {{{1 +" +" For nesting errors of :if conditionals the correct error messages +" should be given. +"------------------------------------------------------------------------------- + +func Test_nested_if_else_errors() + CheckEnglish + + " :endif without :if + let code =<< trim END + endif + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if') + + " :endif without :if + let code =<< trim END + while 1 + endif + endwhile + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if') + + " :endif without :if + let code =<< trim END + try + finally + endif + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if') + + " :endif without :if + let code =<< trim END + try + endif + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if') + + " :endif without :if + let code =<< trim END + try + throw "a" + catch /a/ + endif + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if') + + " :else without :if + let code =<< trim END + else + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if') + + " :else without :if + let code =<< trim END + while 1 + else + endwhile + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if') + + " :else without :if + let code =<< trim END + try + finally + else + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if') + + " :else without :if + let code =<< trim END + try + else + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if') + + " :else without :if + let code =<< trim END + try + throw "a" + catch /a/ + else + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if') + + " :elseif without :if + let code =<< trim END + elseif 1 + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if') + + " :elseif without :if + let code =<< trim END + while 1 + elseif 1 + endwhile + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if') + + " :elseif without :if + let code =<< trim END + try + finally + elseif 1 + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if') + + " :elseif without :if + let code =<< trim END + try + elseif 1 + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if') + + " :elseif without :if + let code =<< trim END + try + throw "a" + catch /a/ + elseif 1 + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if') + + " multiple :else + let code =<< trim END + if 1 + else + else + endif + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(else):E583: multiple :else') + + " :elseif after :else + let code =<< trim END + if 1 + else + elseif 1 + endif + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(elseif):E584: :elseif after :else') + + call delete('Xtest') +endfunc + +"------------------------------------------------------------------------------- +" Test 54: Nesting errors: :while/:endwhile {{{1 +" +" For nesting errors of :while conditionals the correct error messages +" should be given. +" +" This test reuses the function MESSAGES() from the previous test. +" This functions checks the messages in g:msgfile. "------------------------------------------------------------------------------- + +func Test_nested_while_error() + CheckEnglish + + " :endwhile without :while + let code =<< trim END + endwhile + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while') + + " :endwhile without :while + let code =<< trim END + if 1 + endwhile + endif + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while') + + " Missing :endif + let code =<< trim END + while 1 + if 1 + endwhile + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endwhile):E171: Missing :endif') + + " :endwhile without :while + let code =<< trim END + try + finally + endwhile + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while') + + " Missing :endtry + let code =<< trim END + while 1 + try + finally + endwhile + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endwhile):E600: Missing :endtry') + + " Missing :endtry + let code =<< trim END + while 1 + if 1 + try + finally + endwhile + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endwhile):E600: Missing :endtry') + + " Missing :endif + let code =<< trim END + while 1 + try + finally + if 1 + endwhile + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endwhile):E171: Missing :endif') + + " :endwhile without :while + let code =<< trim END + try + endwhile + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while') + + " :endwhile without :while + let code =<< trim END + while 1 + try + endwhile + endtry + endwhile + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while') + + " :endwhile without :while + let code =<< trim END + try + throw "a" + catch /a/ + endwhile + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while') + + " :endwhile without :while + let code =<< trim END + while 1 + try + throw "a" + catch /a/ + endwhile + endtry + endwhile + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while') + + call delete('Xtest') +endfunc + +"------------------------------------------------------------------------------- +" Test 55: Nesting errors: :continue/:break {{{1 +" +" For nesting errors of :continue and :break commands the correct +" error messages should be given. +" +" This test reuses the function MESSAGES() from the previous test. +" This functions checks the messages in g:msgfile. +"------------------------------------------------------------------------------- + +func Test_nested_cont_break_error() + CheckEnglish + + " :continue without :while + let code =<< trim END + continue + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for') + + " :continue without :while + let code =<< trim END + if 1 + continue + endif + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for') + + " :continue without :while + let code =<< trim END + try + finally + continue + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for') + + " :continue without :while + let code =<< trim END + try + continue + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for') + + " :continue without :while + let code =<< trim END + try + throw "a" + catch /a/ + continue + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for') + + " :break without :while + let code =<< trim END + break + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for') + + " :break without :while + let code =<< trim END + if 1 + break + endif + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for') + + " :break without :while + let code =<< trim END + try + finally + break + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for') + + " :break without :while + let code =<< trim END + try + break + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for') + + " :break without :while + let code =<< trim END + try + throw "a" + catch /a/ + break + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for') + + call delete('Xtest') +endfunc + +"------------------------------------------------------------------------------- +" Test 56: Nesting errors: :endtry {{{1 +" +" For nesting errors of :try conditionals the correct error messages +" should be given. +" +" This test reuses the function MESSAGES() from the previous test. +" This functions checks the messages in g:msgfile. +"------------------------------------------------------------------------------- + +func Test_nested_endtry_error() + CheckEnglish + + " :endtry without :try + let code =<< trim END + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endtry):E602: :endtry without :try') + + " :endtry without :try + let code =<< trim END + if 1 + endtry + endif + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endtry):E602: :endtry without :try') + + " :endtry without :try + let code =<< trim END + while 1 + endtry + endwhile + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endtry):E602: :endtry without :try') + + " Missing :endif + let code =<< trim END + try + if 1 + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endtry):E171: Missing :endif') + + " Missing :endwhile + let code =<< trim END + try + while 1 + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endtry):E170: Missing :endwhile') + + " Missing :endif + let code =<< trim END + try + finally + if 1 + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endtry):E171: Missing :endif') + + " Missing :endwhile + let code =<< trim END + try + finally + while 1 + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endtry):E170: Missing :endwhile') + + " Missing :endif + let code =<< trim END + try + throw "a" + catch /a/ + if 1 + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endtry):E171: Missing :endif') + + " Missing :endwhile + let code =<< trim END + try + throw "a" + catch /a/ + while 1 + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endtry):E170: Missing :endwhile') + + call delete('Xtest') +endfunc + +"------------------------------------------------------------------------------- +" Test 57: v:exception and v:throwpoint for user exceptions {{{1 +" +" v:exception evaluates to the value of the exception that was caught +" most recently and is not finished. (A caught exception is finished +" when the next ":catch", ":finally", or ":endtry" is reached.) +" v:throwpoint evaluates to the script/function name and line number +" where that exception has been thrown. +"------------------------------------------------------------------------------- + +func Test_user_exception_info() + CheckEnglish + + XpathINIT + XloopINIT + + func FuncException() + let g:exception = v:exception + endfunc + + func FuncThrowpoint() + let g:throwpoint = v:throwpoint + endfunc + + let scriptException = MakeScript("FuncException") + let scriptThrowPoint = MakeScript("FuncThrowpoint") + + command! CmdException let g:exception = v:exception + command! CmdThrowpoint let g:throwpoint = v:throwpoint + + func T(arg, line) + if a:line == 2 + throw a:arg " in line 2 + elseif a:line == 4 + throw a:arg " in line 4 + elseif a:line == 6 + throw a:arg " in line 6 + elseif a:line == 8 + throw a:arg " in line 8 + endif + endfunc + + func G(arg, line) + call T(a:arg, a:line) + endfunc + + func F(arg, line) + call G(a:arg, a:line) + endfunc + + let scriptT = MakeScript("T") + let scriptG = MakeScript("G", scriptT) + let scriptF = MakeScript("F", scriptG) + + try + Xpath 'a' + call F("oops", 2) + catch /.*/ + Xpath 'b' + let exception = v:exception + let throwpoint = v:throwpoint + call assert_equal("oops", v:exception) + call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint) + call assert_match('\<2\>', v:throwpoint) + + exec "let exception = v:exception" + exec "let throwpoint = v:throwpoint" + call assert_equal("oops", v:exception) + call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint) + call assert_match('\<2\>', v:throwpoint) + + CmdException + CmdThrowpoint + call assert_equal("oops", v:exception) + call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint) + call assert_match('\<2\>', v:throwpoint) + + call FuncException() + call FuncThrowpoint() + call assert_equal("oops", v:exception) + call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint) + call assert_match('\<2\>', v:throwpoint) + + exec "source" scriptException + exec "source" scriptThrowPoint + call assert_equal("oops", v:exception) + call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint) + call assert_match('\<2\>', v:throwpoint) + + try + Xpath 'c' + call G("arrgh", 4) + catch /.*/ + Xpath 'd' + let exception = v:exception + let throwpoint = v:throwpoint + call assert_equal("arrgh", v:exception) + call assert_match('\<G\[1]\.\.T\>', v:throwpoint) + call assert_match('\<4\>', v:throwpoint) + + try + Xpath 'e' + let g:arg = "autsch" + let g:line = 6 + exec "source" scriptF + catch /.*/ + Xpath 'f' + let exception = v:exception + let throwpoint = v:throwpoint + call assert_equal("autsch", v:exception) + call assert_match(fnamemodify(scriptT, ':t'), v:throwpoint) + call assert_match('\<6\>', v:throwpoint) + finally + Xpath 'g' + let exception = v:exception + let throwpoint = v:throwpoint + call assert_equal("arrgh", v:exception) + call assert_match('\<G\[1]\.\.T\>', v:throwpoint) + call assert_match('\<4\>', v:throwpoint) + try + Xpath 'h' + let g:arg = "brrrr" + let g:line = 8 + exec "source" scriptG + catch /.*/ + Xpath 'i' + let exception = v:exception + let throwpoint = v:throwpoint + " Resolve scriptT for matching it against v:throwpoint. + call assert_equal("brrrr", v:exception) + call assert_match(fnamemodify(scriptT, ':t'), v:throwpoint) + call assert_match('\<8\>', v:throwpoint) + finally + Xpath 'j' + let exception = v:exception + let throwpoint = v:throwpoint + call assert_equal("arrgh", v:exception) + call assert_match('\<G\[1]\.\.T\>', v:throwpoint) + call assert_match('\<4\>', v:throwpoint) + endtry + Xpath 'k' + let exception = v:exception + let throwpoint = v:throwpoint + call assert_equal("arrgh", v:exception) + call assert_match('\<G\[1]\.\.T\>', v:throwpoint) + call assert_match('\<4\>', v:throwpoint) + endtry + Xpath 'l' + let exception = v:exception + let throwpoint = v:throwpoint + call assert_equal("arrgh", v:exception) + call assert_match('\<G\[1]\.\.T\>', v:throwpoint) + call assert_match('\<4\>', v:throwpoint) + finally + Xpath 'm' + let exception = v:exception + let throwpoint = v:throwpoint + call assert_equal("oops", v:exception) + call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint) + call assert_match('\<2\>', v:throwpoint) + endtry + Xpath 'n' + let exception = v:exception + let throwpoint = v:throwpoint + call assert_equal("oops", v:exception) + call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint) + call assert_match('\<2\>', v:throwpoint) + finally + Xpath 'o' + let exception = v:exception + let throwpoint = v:throwpoint + call assert_equal("", v:exception) + call assert_match('^$', v:throwpoint) + call assert_match('^$', v:throwpoint) + endtry + + call assert_equal('abcdefghijklmno', g:Xpath) + + unlet exception throwpoint + delfunction FuncException + delfunction FuncThrowpoint + call delete(scriptException) + call delete(scriptThrowPoint) + unlet scriptException scriptThrowPoint + delcommand CmdException + delcommand CmdThrowpoint + delfunction T + delfunction G + delfunction F + call delete(scriptT) + call delete(scriptG) + call delete(scriptF) + unlet scriptT scriptG scriptF +endfunc + +"------------------------------------------------------------------------------- +" +" Test 58: v:exception and v:throwpoint for error/interrupt exceptions {{{1 +" +" v:exception and v:throwpoint work also for error and interrupt +" exceptions. +"------------------------------------------------------------------------------- + +func Test_execption_info_for_error() + CheckEnglish + + let test =<< trim [CODE] + func T(line) + if a:line == 2 + delfunction T " error (function in use) in line 2 + elseif a:line == 4 + call interrupt() + endif + endfunc + + while 1 + try + Xpath 'a' + call T(2) + call assert_report('should not get here') + catch /.*/ + Xpath 'b' + if v:exception !~ 'Vim(delfunction):' + call assert_report('should not get here') + endif + if v:throwpoint !~ '\<T\>' + call assert_report('should not get here') + endif + if v:throwpoint !~ '\<2\>' + call assert_report('should not get here') + endif + finally + Xpath 'c' + if v:exception != "" + call assert_report('should not get here') + endif + if v:throwpoint != "" + call assert_report('should not get here') + endif + break + endtry + endwhile + + Xpath 'd' + if v:exception != "" + call assert_report('should not get here') + endif + if v:throwpoint != "" + call assert_report('should not get here') + endif + + while 1 + try + Xpath 'e' + call T(4) + call assert_report('should not get here') + catch /.*/ + Xpath 'f' + if v:exception != 'Vim:Interrupt' + call assert_report('should not get here') + endif + if v:throwpoint !~ 'function T' + call assert_report('should not get here') + endif + if v:throwpoint !~ '\<4\>' + call assert_report('should not get here') + endif + finally + Xpath 'g' + if v:exception != "" + call assert_report('should not get here') + endif + if v:throwpoint != "" + call assert_report('should not get here') + endif + break + endtry + endwhile + + Xpath 'h' + if v:exception != "" + call assert_report('should not get here') + endif + if v:throwpoint != "" + call assert_report('should not get here') + endif + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcdefgh', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" +" Test 59: v:exception and v:throwpoint when discarding exceptions {{{1 +" +" When a :catch clause is left by a ":break" etc or an error or +" interrupt exception, v:exception and v:throwpoint are reset. They +" are not affected by an exception that is discarded before being +" caught. +"------------------------------------------------------------------------------- +func Test_exception_info_on_discard() + CheckEnglish + + let test =<< trim [CODE] + let sfile = expand("<sfile>") + + while 1 + try + throw "x1" + catch /.*/ + break + endtry + endwhile + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + + while 1 + try + throw "x2" + catch /.*/ + break + finally + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + endtry + break + endwhile + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + + while 1 + try + let errcaught = 0 + try + try + throw "x3" + catch /.*/ + let lnum = expand("<sflnum>") + asdf + endtry + catch /.*/ + let errcaught = 1 + call assert_match('Vim:E492: Not an editor command:', v:exception) + call assert_match('line ' .. (lnum + 1), v:throwpoint) + endtry + finally + call assert_equal(1, errcaught) + break + endtry + endwhile + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + + Xpath 'a' + + while 1 + try + let intcaught = 0 + try + try + throw "x4" + catch /.*/ + let lnum = expand("<sflnum>") + call interrupt() + endtry + catch /.*/ + let intcaught = 1 + call assert_match('Vim:Interrupt', v:exception) + call assert_match('line ' .. (lnum + 1), v:throwpoint) + endtry + finally + call assert_equal(1, intcaught) + break + endtry + endwhile + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + + Xpath 'b' + + while 1 + try + let errcaught = 0 + try + try + if 1 + let lnum = expand("<sflnum>") + throw "x5" + " missing endif + catch /.*/ + call assert_report('should not get here') + endtry + catch /.*/ + let errcaught = 1 + call assert_match('Vim(catch):E171: Missing :endif:', v:exception) + call assert_match('line ' .. (lnum + 3), v:throwpoint) + endtry + finally + call assert_equal(1, errcaught) + break + endtry + endwhile + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + + Xpath 'c' + + try + while 1 + try + throw "x6" + finally + break + endtry + break + endwhile + catch /.*/ + call assert_report('should not get here') + endtry + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + + try + while 1 + try + throw "x7" + finally + break + endtry + break + endwhile + catch /.*/ + call assert_report('should not get here') + finally + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + endtry + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + + while 1 + try + let errcaught = 0 + try + try + throw "x8" + finally + let lnum = expand("<sflnum>") + asdf + endtry + catch /.*/ + let errcaught = 1 + call assert_match('Vim:E492: Not an editor command:', v:exception) + call assert_match('line ' .. (lnum + 1), v:throwpoint) + endtry + finally + call assert_equal(1, errcaught) + break + endtry + endwhile + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + + Xpath 'd' + + while 1 + try + let intcaught = 0 + try + try + throw "x9" + finally + let lnum = expand("<sflnum>") + call interrupt() + endtry + catch /.*/ + let intcaught = 1 + call assert_match('Vim:Interrupt', v:exception) + call assert_match('line ' .. (lnum + 1), v:throwpoint) + endtry + finally + call assert_equal(1, intcaught) + break + endtry + endwhile + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + + Xpath 'e' + + while 1 + try + let errcaught = 0 + try + try + if 1 + let lnum = expand("<sflnum>") + throw "x10" + " missing endif + finally + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + endtry + catch /.*/ + let errcaught = 1 + call assert_match('Vim(finally):E171: Missing :endif:', v:exception) + call assert_match('line ' .. (lnum + 3), v:throwpoint) + endtry + finally + call assert_equal(1, errcaught) + break + endtry + endwhile + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + + Xpath 'f' + + while 1 + try + let errcaught = 0 + try + try + if 1 + let lnum = expand("<sflnum>") + throw "x11" + " missing endif + endtry + catch /.*/ + let errcaught = 1 + call assert_match('Vim(endtry):E171: Missing :endif:', v:exception) + call assert_match('line ' .. (lnum + 3), v:throwpoint) + endtry + finally + call assert_equal(1, errcaught) + break + endtry + endwhile + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + + Xpath 'g' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcdefg', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" +" Test 60: (Re)throwing v:exception; :echoerr. {{{1 +" +" A user exception can be rethrown after catching by throwing +" v:exception. An error or interrupt exception cannot be rethrown +" because Vim exceptions cannot be faked. A Vim exception using the +" value of v:exception can, however, be triggered by the :echoerr +" command. +"------------------------------------------------------------------------------- + +func Test_rethrow_exception_1() + XpathINIT + try + try + Xpath 'a' + throw "oops" + catch /oops/ + Xpath 'b' + throw v:exception " rethrow user exception + catch /.*/ + call assert_report('should not get here') + endtry + catch /^oops$/ " catches rethrown user exception + Xpath 'c' + catch /.*/ + call assert_report('should not get here') + endtry + call assert_equal('abc', g:Xpath) +endfunc + +func Test_rethrow_exception_2() + XpathINIT + try + let caught = 0 + try + Xpath 'a' + write /n/o/n/w/r/i/t/a/b/l/e/_/f/i/l/e + call assert_report('should not get here') + catch /^Vim(write):/ + let caught = 1 + throw v:exception " throw error: cannot fake Vim exception + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'b' + call assert_equal(1, caught) + endtry + catch /^Vim(throw):/ " catches throw error + let caught = caught + 1 + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'c' + call assert_equal(2, caught) + endtry + call assert_equal('abc', g:Xpath) +endfunc + +func Test_rethrow_exception_3() + XpathINIT + try + let caught = 0 + try + Xpath 'a' + asdf + catch /^Vim/ " catch error exception + let caught = 1 + " Trigger Vim error exception with value specified after :echoerr + let value = substitute(v:exception, '^Vim\((.*)\)\=:', '', "") + echoerr value + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'b' + call assert_equal(1, caught) + endtry + catch /^Vim(echoerr):/ + let caught = caught + 1 + call assert_match(value, v:exception) + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'c' + call assert_equal(2, caught) + endtry + call assert_equal('abc', g:Xpath) +endfunc + +func Test_rethrow_exception_3() + XpathINIT + try + let errcaught = 0 + try + Xpath 'a' + let intcaught = 0 + call interrupt() + catch /^Vim:/ " catch interrupt exception + let intcaught = 1 + " Trigger Vim error exception with value specified after :echoerr + echoerr substitute(v:exception, '^Vim\((.*)\)\=:', '', "") + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'b' + call assert_equal(1, intcaught) + endtry + catch /^Vim(echoerr):/ + let errcaught = 1 + call assert_match('Interrupt', v:exception) + finally + Xpath 'c' + call assert_equal(1, errcaught) + endtry + call assert_equal('abc', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +" Test 61: Catching interrupt exceptions {{{1 +" +" When an interrupt occurs inside a :try/:endtry region, an +" interrupt exception is thrown and can be caught. Its value is +" "Vim:Interrupt". If the interrupt occurs after an error or a :throw +" but before a matching :catch is reached, all following :catches of +" that try block are ignored, but the interrupt exception can be +" caught by the next surrounding try conditional. An interrupt is +" ignored when there is a previous interrupt that has not been caught +" or causes a :finally clause to be executed. +"------------------------------------------------------------------------------- + +func Test_catch_intr_exception() + let test =<< trim [CODE] + while 1 + try + try + Xpath 'a' + call interrupt() + call assert_report('should not get here') + catch /^Vim:Interrupt$/ + Xpath 'b' + finally + Xpath 'c' + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'd' + break + endtry + endwhile + + while 1 + try + try + try + Xpath 'e' + asdf + call assert_report('should not get here') + catch /do_not_catch/ + call assert_report('should not get here') + catch /.*/ + Xpath 'f' + call interrupt() + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'g' + call interrupt() + call assert_report('should not get here') + endtry + catch /^Vim:Interrupt$/ + Xpath 'h' + finally + Xpath 'i' + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'j' + break + endtry + endwhile + + while 1 + try + try + try + Xpath 'k' + throw "x" + call assert_report('should not get here') + catch /do_not_catch/ + call assert_report('should not get here') + catch /x/ + Xpath 'l' + call interrupt() + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + endtry + catch /^Vim:Interrupt$/ + Xpath 'm' + finally + Xpath 'n' + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'o' + break + endtry + endwhile + + while 1 + try + try + Xpath 'p' + call interrupt() + call assert_report('should not get here') + catch /do_not_catch/ + call interrupt() + call assert_report('should not get here') + catch /^Vim:Interrupt$/ + Xpath 'q' + finally + Xpath 'r' + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 's' + break + endtry + endwhile + + Xpath 't' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcdefghijklmnopqrst', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 62: Catching error exceptions {{{1 +" +" An error inside a :try/:endtry region is converted to an exception +" and can be caught. The error exception has a "Vim(cmdname):" prefix +" where cmdname is the name of the failing command, or a "Vim:" prefix +" if no command name is known. The "Vim" prefixes cannot be faked. +"------------------------------------------------------------------------------- + +func Test_catch_err_exception_1() + XpathINIT + while 1 + try + try + let caught = 0 + unlet novar + catch /^Vim(unlet):/ + Xpath 'a' + let caught = 1 + let v:errmsg = substitute(v:exception, '^Vim(unlet):', '', "") + finally + Xpath 'b' + call assert_equal(1, caught) + call assert_match('E108: No such variable: "novar"', v:errmsg) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'c' + break + endtry + call assert_report('should not get here') + endwhile + call assert_equal('abc', g:Xpath) +endfunc + +func Test_catch_err_exception_2() + XpathINIT + while 1 + try + try + let caught = 0 + throw novar " error in :throw + catch /^Vim(throw):/ + Xpath 'a' + let caught = 1 + let v:errmsg = substitute(v:exception, '^Vim(throw):', '', "") + finally + Xpath 'b' + call assert_equal(1, caught) + call assert_match('E121: Undefined variable: novar', v:errmsg) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'c' + break + endtry + call assert_report('should not get here') + endwhile + call assert_equal('abc', g:Xpath) +endfunc + +func Test_catch_err_exception_3() + XpathINIT + while 1 + try + try + let caught = 0 + throw "Vim:faked" " error: cannot fake Vim exception + catch /^Vim(throw):/ + Xpath 'a' + let caught = 1 + let v:errmsg = substitute(v:exception, '^Vim(throw):', '', "") + finally + Xpath 'b' + call assert_equal(1, caught) + call assert_match("E608: Cannot :throw exceptions with 'Vim' prefix", + \ v:errmsg) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'c' + break + endtry + call assert_report('should not get here') + endwhile + call assert_equal('abc', g:Xpath) +endfunc + +func Test_catch_err_exception_4() + XpathINIT + func F() + while 1 + " Missing :endwhile + endfunc + + while 1 + try + try + let caught = 0 + call F() + catch /^Vim(endfunction):/ + Xpath 'a' + let caught = 1 + let v:errmsg = substitute(v:exception, '^Vim(endfunction):', '', "") + finally + Xpath 'b' + call assert_equal(1, caught) + call assert_match("E170: Missing :endwhile", v:errmsg) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'c' + break + endtry + call assert_report('should not get here') + endwhile + call assert_equal('abc', g:Xpath) + delfunc F +endfunc + +func Test_catch_err_exception_5() + XpathINIT + func F() + while 1 + " Missing :endwhile + endfunc + + while 1 + try + try + let caught = 0 + ExecAsScript F + catch /^Vim:/ + Xpath 'a' + let caught = 1 + let v:errmsg = substitute(v:exception, '^Vim:', '', "") + finally + Xpath 'b' + call assert_equal(1, caught) + call assert_match("E170: Missing :endwhile", v:errmsg) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'c' + break + endtry + call assert_report('should not get here') + endwhile + call assert_equal('abc', g:Xpath) + delfunc F +endfunc + +func Test_catch_err_exception_6() + XpathINIT + func G() + call G() + endfunc + + while 1 + try + let mfd_save = &mfd + set mfd=3 + try + let caught = 0 + call G() + catch /^Vim(call):/ + Xpath 'a' + let caught = 1 + let v:errmsg = substitute(v:exception, '^Vim(call):', '', "") + finally + Xpath 'b' + call assert_equal(1, caught) + call assert_match("E132: Function call depth is higher than 'maxfuncdepth'", v:errmsg) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'c' + let &mfd = mfd_save + break + endtry + call assert_report('should not get here') + endwhile + call assert_equal('abc', g:Xpath) + delfunc G +endfunc + +func Test_catch_err_exception_7() + XpathINIT + func H() + return H() + endfunc + + while 1 + try + let mfd_save = &mfd + set mfd=3 + try + let caught = 0 + call H() + catch /^Vim(return):/ + Xpath 'a' + let caught = 1 + let v:errmsg = substitute(v:exception, '^Vim(return):', '', "") + finally + Xpath 'b' + call assert_equal(1, caught) + call assert_match("E132: Function call depth is higher than 'maxfuncdepth'", v:errmsg) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'c' + let &mfd = mfd_save + break " discard error for $VIMNOERRTHROW + endtry + call assert_report('should not get here') + endwhile + + call assert_equal('abc', g:Xpath) + delfunc H +endfunc + +"------------------------------------------------------------------------------- +" Test 63: Suppressing error exceptions by :silent!. {{{1 +" +" A :silent! command inside a :try/:endtry region suppresses the +" conversion of errors to an exception and the immediate abortion on +" error. When the commands executed by the :silent! themselves open +" a new :try/:endtry region, conversion of errors to exception and +" immediate abortion is switched on again - until the next :silent! +" etc. The :silent! has the effect of setting v:errmsg to the error +" message text (without displaying it) and continuing with the next +" script line. +" +" When a command triggering autocommands is executed by :silent! +" inside a :try/:endtry, the autocommand execution is not suppressed +" on error. +" +" This test reuses the function MSG() from the previous test. +"------------------------------------------------------------------------------- + +func Test_silent_exception() + XpathINIT + XloopINIT + let g:taken = "" + + func S(n) abort + XloopNEXT + let g:taken = g:taken . "E" . a:n + let v:errmsg = "" + exec "asdf" . a:n + + " Check that ":silent!" continues: + Xloop 'a' + + " Check that ":silent!" sets "v:errmsg": + call assert_match("E492: Not an editor command", v:errmsg) + endfunc + + func Foo() + while 1 + try + try + let caught = 0 + " This is not silent: + call S(3) + catch /^Vim:/ + Xpath 'b' + let caught = 1 + let errmsg3 = substitute(v:exception, '^Vim:', '', "") + silent! call S(4) + finally + call assert_equal(1, caught) + Xpath 'c' + call assert_match("E492: Not an editor command", errmsg3) + silent! call S(5) + " Break out of try conditionals that cover ":silent!". This also + " discards the aborting error when $VIMNOERRTHROW is non-zero. + break + endtry + catch /.*/ + call assert_report('should not get here') + endtry + endwhile + " This is a double ":silent!" (see caller). + silent! call S(6) + endfunc + + func Bar() + try + silent! call S(2) + silent! execute "call Foo() | call S(7)" + silent! call S(8) + endtry " normal end of try cond that covers ":silent!" + " This has a ":silent!" from the caller: + call S(9) + endfunc + + silent! call S(1) + silent! call Bar() + silent! call S(10) + + call assert_equal("E1E2E3E4E5E6E7E8E9E10", g:taken) + + augroup TMP + au! + autocmd BufWritePost * Xpath 'd' + augroup END + + Xpath 'e' + silent! write /i/m/p/o/s/s/i/b/l/e + Xpath 'f' + + call assert_equal('a2a3ba5ca6a7a8a9a10a11edf', g:Xpath) + + augroup TMP + au! + augroup END + augroup! TMP + delfunction S + delfunction Foo + delfunction Bar +endfunc + +"------------------------------------------------------------------------------- +" Test 64: Error exceptions after error, interrupt or :throw {{{1 +" +" When an error occurs after an interrupt or a :throw but before +" a matching :catch is reached, all following :catches of that try +" block are ignored, but the error exception can be caught by the next +" surrounding try conditional. Any previous error exception is +" discarded. An error is ignored when there is a previous error that +" has not been caught. +"------------------------------------------------------------------------------- + +func Test_exception_after_error_1() + XpathINIT + while 1 + try + try + Xpath 'a' + let caught = 0 + while 1 + if 1 + " Missing :endif + endwhile " throw error exception + catch /^Vim(/ + Xpath 'b' + let caught = 1 + finally + Xpath 'c' + call assert_equal(1, caught) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'd' + break + endtry + call assert_report('should not get here') + endwhile + call assert_equal('abcd', g:Xpath) +endfunc + +func Test_exception_after_error_2() + XpathINIT + while 1 + try + try + Xpath 'a' + let caught = 0 + try + if 1 + " Missing :endif + catch /.*/ " throw error exception + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + endtry + catch /^Vim(/ + Xpath 'b' + let caught = 1 + finally + Xpath 'c' + call assert_equal(1, caught) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'd' + break + endtry + call assert_report('should not get here') + endwhile + call assert_equal('abcd', g:Xpath) +endfunc + +func Test_exception_after_error_3() + XpathINIT + while 1 + try + try + let caught = 0 + try + Xpath 'a' + call interrupt() + catch /do_not_catch/ + call assert_report('should not get here') + if 1 + " Missing :endif + catch /.*/ " throw error exception + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + endtry + catch /^Vim(/ + Xpath 'b' + let caught = 1 + finally + Xpath 'c' + call assert_equal(1, caught) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'd' + break + endtry + call assert_report('should not get here') + endwhile + call assert_equal('abcd', g:Xpath) +endfunc + +func Test_exception_after_error_4() + XpathINIT + while 1 + try + try + let caught = 0 + try + Xpath 'a' + throw "x" + catch /do_not_catch/ + call assert_report('should not get here') + if 1 + " Missing :endif + catch /x/ " throw error exception + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + endtry + catch /^Vim(/ + Xpath 'b' + let caught = 1 + finally + Xpath 'c' + call assert_equal(1, caught) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'd' + break + endtry + call assert_report('should not get here') + endwhile + call assert_equal('abcd', g:Xpath) +endfunc + +func Test_exception_after_error_5() + XpathINIT + while 1 + try + try + let caught = 0 + Xpath 'a' + endif " :endif without :if; throw error exception + if 1 + " Missing :endif + catch /do_not_catch/ " ignore new error + call assert_report('should not get here') + catch /^Vim(endif):/ + Xpath 'b' + let caught = 1 + catch /^Vim(/ + call assert_report('should not get here') + finally + Xpath 'c' + call assert_equal(1, caught) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'd' + break + endtry + call assert_report('should not get here') + endwhile + call assert_equal('abcd', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +" Test 65: Errors in the /pattern/ argument of a :catch {{{1 +" +" On an error in the /pattern/ argument of a :catch, the :catch does +" not match. Any following :catches of the same :try/:endtry don't +" match either. Finally clauses are executed. +"------------------------------------------------------------------------------- + +func Test_catch_pattern_error() + CheckEnglish + XpathINIT + + try + try + Xpath 'a' + throw "oops" + catch /^oops$/ + Xpath 'b' + catch /\)/ " not checked; exception has already been caught + call assert_report('should not get here') + endtry + Xpath 'c' + catch /.*/ + call assert_report('should not get here') + endtry + call assert_equal('abc', g:Xpath) + + XpathINIT + func F() + try + try + try + Xpath 'a' + throw "ab" + catch /abc/ " does not catch + call assert_report('should not get here') + catch /\)/ " error; discards exception + call assert_report('should not get here') + catch /.*/ " not checked + call assert_report('should not get here') + finally + Xpath 'b' + endtry + call assert_report('should not get here') + catch /^ab$/ " checked, but original exception is discarded + call assert_report('should not get here') + catch /^Vim(catch):/ + Xpath 'c' + call assert_match('Vim(catch):E475: Invalid argument:', v:exception) + finally + Xpath 'd' + endtry + Xpath 'e' + catch /.*/ + call assert_report('should not get here') + endtry + Xpath 'f' + endfunc + + call F() + call assert_equal('abcdef', g:Xpath) + + delfunc F +endfunc + +"------------------------------------------------------------------------------- +" Test 66: Stop range :call on error, interrupt, or :throw {{{1 +" +" When a function which is multiply called for a range since it +" doesn't handle the range itself has an error in a command +" dynamically enclosed by :try/:endtry or gets an interrupt or +" executes a :throw, no more calls for the remaining lines in the +" range are made. On an error in a command not dynamically enclosed +" by :try/:endtry, the function is executed again for the remaining +" lines in the range. +"------------------------------------------------------------------------------- + +func Test_stop_range_on_error() + let test =<< trim [CODE] + let file = tempname() + exec "edit" file + call setline(1, ['line 1', 'line 2', 'line 3']) + let taken = "" + let expected = "G1EF1E(1)F1E(2)F1E(3)G2EF2E(1)G3IF3I(1)G4TF4T(1)G5AF5A(1)" + + func F(reason, n) abort + let g:taken = g:taken .. "F" .. a:n .. + \ substitute(a:reason, '\(\l\).*', '\u\1', "") .. + \ "(" .. line(".") .. ")" + + if a:reason == "error" + asdf + elseif a:reason == "interrupt" + call interrupt() + elseif a:reason == "throw" + throw "xyz" + elseif a:reason == "aborting error" + XloopNEXT + call assert_equal(g:taken, g:expected) + try + bwipeout! + call delete(g:file) + asdf + endtry + endif + endfunc + + func G(reason, n) + let g:taken = g:taken .. "G" .. a:n .. + \ substitute(a:reason, '\(\l\).*', '\u\1', "") + 1,3call F(a:reason, a:n) + endfunc + + Xpath 'a' + call G("error", 1) + try + Xpath 'b' + try + call G("error", 2) + call assert_report('should not get here') + finally + Xpath 'c' + try + call G("interrupt", 3) + call assert_report('should not get here') + finally + Xpath 'd' + try + call G("throw", 4) + call assert_report('should not get here') + endtry + endtry + endtry + catch /xyz/ + Xpath 'e' + catch /.*/ + call assert_report('should not get here') + endtry + Xpath 'f' + call G("aborting error", 5) + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcdef', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 67: :throw across :call command {{{1 +" +" On a call command, an exception might be thrown when evaluating the +" function name, during evaluation of the arguments, or when the +" function is being executed. The exception can be caught by the +" caller. +"------------------------------------------------------------------------------- + +func THROW(x, n) + if a:n == 1 + Xpath 'A' + elseif a:n == 2 + Xpath 'B' + elseif a:n == 3 + Xpath 'C' + endif + throw a:x +endfunc + +func NAME(x, n) + if a:n == 1 + call assert_report('should not get here') + elseif a:n == 2 + Xpath 'D' + elseif a:n == 3 + Xpath 'E' + elseif a:n == 4 + Xpath 'F' + endif + return a:x +endfunc + +func ARG(x, n) + if a:n == 1 + call assert_report('should not get here') + elseif a:n == 2 + call assert_report('should not get here') + elseif a:n == 3 + Xpath 'G' + elseif a:n == 4 + Xpath 'I' + endif + return a:x +endfunc + +func Test_throw_across_call_cmd() + XpathINIT + + func F(x, n) + if a:n == 2 + call assert_report('should not get here') + elseif a:n == 4 + Xpath 'a' + endif + endfunc + + while 1 + try + let v:errmsg = "" + + while 1 + try + Xpath 'b' + call {NAME(THROW("name", 1), 1)}(ARG(4711, 1), 1) + call assert_report('should not get here') + catch /^name$/ + Xpath 'c' + catch /.*/ + call assert_report('should not get here') + finally + call assert_equal("", v:errmsg) + let v:errmsg = "" + break + endtry + endwhile + + while 1 + try + Xpath 'd' + call {NAME("F", 2)}(ARG(THROW("arg", 2), 2), 2) + call assert_report('should not get here') + catch /^arg$/ + Xpath 'e' + catch /.*/ + call assert_report('should not get here') + finally + call assert_equal("", v:errmsg) + let v:errmsg = "" + break + endtry + endwhile + + while 1 + try + Xpath 'f' + call {NAME("THROW", 3)}(ARG("call", 3), 3) + call assert_report('should not get here') + catch /^call$/ + Xpath 'g' + catch /^0$/ " default return value + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + finally + call assert_equal("", v:errmsg) + let v:errmsg = "" + break + endtry + endwhile + + while 1 + try + Xpath 'h' + call {NAME("F", 4)}(ARG(4711, 4), 4) + Xpath 'i' + catch /.*/ + call assert_report('should not get here') + finally + call assert_equal("", v:errmsg) + let v:errmsg = "" + break + endtry + endwhile + + catch /^0$/ " default return value + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + finally + call assert_equal("", v:errmsg) + let v:errmsg = "" + break + endtry + endwhile + + call assert_equal('bAcdDBefEGCghFIai', g:Xpath) + delfunction F +endfunc + +"------------------------------------------------------------------------------- +" Test 68: :throw across function calls in expressions {{{1 +" +" On a function call within an expression, an exception might be +" thrown when evaluating the function name, during evaluation of the +" arguments, or when the function is being executed. The exception +" can be caught by the caller. +" +" This test reuses the functions THROW(), NAME(), and ARG() from the +" previous test. +"------------------------------------------------------------------------------- + +func Test_throw_across_call_expr() + XpathINIT + + func F(x, n) + if a:n == 2 + call assert_report('should not get here') + elseif a:n == 4 + Xpath 'a' + endif + return a:x + endfunction + + while 1 + try + let error = 0 + let v:errmsg = "" + + while 1 + try + Xpath 'b' + let var1 = {NAME(THROW("name", 1), 1)}(ARG(4711, 1), 1) + call assert_report('should not get here') + catch /^name$/ + Xpath 'c' + catch /.*/ + call assert_report('should not get here') + finally + call assert_equal("", v:errmsg) + let v:errmsg = "" + break + endtry + endwhile + call assert_true(!exists('var1')) + + while 1 + try + Xpath 'd' + let var2 = {NAME("F", 2)}(ARG(THROW("arg", 2), 2), 2) + call assert_report('should not get here') + catch /^arg$/ + Xpath 'e' + catch /.*/ + call assert_report('should not get here') + finally + call assert_equal("", v:errmsg) + let v:errmsg = "" + break + endtry + endwhile + call assert_true(!exists('var2')) + + while 1 + try + Xpath 'f' + let var3 = {NAME("THROW", 3)}(ARG("call", 3), 3) + call assert_report('should not get here') + catch /^call$/ + Xpath 'g' + catch /^0$/ " default return value + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + finally + call assert_equal("", v:errmsg) + let v:errmsg = "" + break + endtry + endwhile + call assert_true(!exists('var3')) + + while 1 + try + Xpath 'h' + let var4 = {NAME("F", 4)}(ARG(4711, 4), 4) + Xpath 'i' + catch /.*/ + call assert_report('should not get here') + finally + call assert_equal("", v:errmsg) + let v:errmsg = "" + break + endtry + endwhile + call assert_true(exists('var4') && var4 == 4711) + + catch /^0$/ " default return value + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + finally + call assert_equal("", v:errmsg) + break + endtry + endwhile + + call assert_equal('bAcdDBefEGCghFIai', g:Xpath) + delfunc F +endfunc + +"------------------------------------------------------------------------------- +" Test 76: Errors, interrupts, :throw during expression evaluation {{{1 +" +" When a function call made during expression evaluation is aborted +" due to an error inside a :try/:endtry region or due to an interrupt +" or a :throw, the expression evaluation is aborted as well. No +" message is displayed for the cancelled expression evaluation. On an +" error not inside :try/:endtry, the expression evaluation continues. +"------------------------------------------------------------------------------- + +func Test_expr_eval_error() + let test =<< trim [CODE] + let taken = "" + + func ERR(n) + let g:taken = g:taken .. "E" .. a:n + asdf + endfunc + + func ERRabort(n) abort + let g:taken = g:taken .. "A" .. a:n + asdf + endfunc " returns -1; may cause follow-up msg for illegal var/func name + + func WRAP(n, arg) + let g:taken = g:taken .. "W" .. a:n + let g:saved_errmsg = v:errmsg + return arg + endfunc + + func INT(n) + let g:taken = g:taken .. "I" .. a:n + call interrupt() + endfunc + + func THR(n) + let g:taken = g:taken .. "T" .. a:n + throw "should not be caught" + endfunc + + func CONT(n) + let g:taken = g:taken .. "C" .. a:n + endfunc + + func MSG(n) + let g:taken = g:taken .. "M" .. a:n + let errmsg = (a:n >= 37 && a:n <= 44) ? g:saved_errmsg : v:errmsg + let msgptn = (a:n >= 10 && a:n <= 27) ? "^$" : "asdf" + call assert_match(msgptn, errmsg) + let v:errmsg = "" + let g:saved_errmsg = "" + endfunc + + let v:errmsg = "" + + try + let t = 1 + while t <= 9 + Xloop 'a' + try + if t == 1 + let v{ERR(t) + CONT(t)} = 0 + elseif t == 2 + let v{ERR(t) + CONT(t)} + elseif t == 3 + let var = exists('v{ERR(t) + CONT(t)}') + elseif t == 4 + unlet v{ERR(t) + CONT(t)} + elseif t == 5 + function F{ERR(t) + CONT(t)}() + endfunction + elseif t == 6 + function F{ERR(t) + CONT(t)} + elseif t == 7 + let var = exists('*F{ERR(t) + CONT(t)}') + elseif t == 8 + delfunction F{ERR(t) + CONT(t)} + elseif t == 9 + let var = ERR(t) + CONT(t) + endif + catch /asdf/ + " v:errmsg is not set when the error message is converted to an + " exception. Set it to the original error message. + let v:errmsg = substitute(v:exception, '^Vim:', '', "") + catch /^Vim\((\a\+)\)\=:/ + " An error exception has been thrown after the original error. + let v:errmsg = "" + finally + call MSG(t) + let t = t + 1 + XloopNEXT + continue " discard an aborting error + endtry + endwhile + catch /.*/ + call assert_report('should not get here') + endtry + + try + let t = 10 + while t <= 18 + Xloop 'b' + try + if t == 10 + let v{INT(t) + CONT(t)} = 0 + elseif t == 11 + let v{INT(t) + CONT(t)} + elseif t == 12 + let var = exists('v{INT(t) + CONT(t)}') + elseif t == 13 + unlet v{INT(t) + CONT(t)} + elseif t == 14 + function F{INT(t) + CONT(t)}() + endfunction + elseif t == 15 + function F{INT(t) + CONT(t)} + elseif t == 16 + let var = exists('*F{INT(t) + CONT(t)}') + elseif t == 17 + delfunction F{INT(t) + CONT(t)} + elseif t == 18 + let var = INT(t) + CONT(t) + endif + catch /^Vim\((\a\+)\)\=:\(Interrupt\)\@!/ + " An error exception has been triggered after the interrupt. + let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "") + finally + call MSG(t) + let t = t + 1 + XloopNEXT + continue " discard interrupt + endtry + endwhile + catch /.*/ + call assert_report('should not get here') + endtry + + try + let t = 19 + while t <= 27 + Xloop 'c' + try + if t == 19 + let v{THR(t) + CONT(t)} = 0 + elseif t == 20 + let v{THR(t) + CONT(t)} + elseif t == 21 + let var = exists('v{THR(t) + CONT(t)}') + elseif t == 22 + unlet v{THR(t) + CONT(t)} + elseif t == 23 + function F{THR(t) + CONT(t)}() + endfunction + elseif t == 24 + function F{THR(t) + CONT(t)} + elseif t == 25 + let var = exists('*F{THR(t) + CONT(t)}') + elseif t == 26 + delfunction F{THR(t) + CONT(t)} + elseif t == 27 + let var = THR(t) + CONT(t) + endif + catch /^Vim\((\a\+)\)\=:/ + " An error exception has been triggered after the :throw. + let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "") + finally + call MSG(t) + let t = t + 1 + XloopNEXT + continue " discard exception + endtry + endwhile + catch /.*/ + call assert_report('should not get here') + endtry + + let v{ERR(28) + CONT(28)} = 0 + call MSG(28) + let v{ERR(29) + CONT(29)} + call MSG(29) + let var = exists('v{ERR(30) + CONT(30)}') + call MSG(30) + unlet v{ERR(31) + CONT(31)} + call MSG(31) + function F{ERR(32) + CONT(32)}() + endfunction + call MSG(32) + function F{ERR(33) + CONT(33)} + call MSG(33) + let var = exists('*F{ERR(34) + CONT(34)}') + call MSG(34) + delfunction F{ERR(35) + CONT(35)} + call MSG(35) + let var = ERR(36) + CONT(36) + call MSG(36) + + let saved_errmsg = "" + + let v{WRAP(37, ERRabort(37)) + CONT(37)} = 0 + call MSG(37) + let v{WRAP(38, ERRabort(38)) + CONT(38)} + call MSG(38) + let var = exists('v{WRAP(39, ERRabort(39)) + CONT(39)}') + call MSG(39) + unlet v{WRAP(40, ERRabort(40)) + CONT(40)} + call MSG(40) + function F{WRAP(41, ERRabort(41)) + CONT(41)}() + endfunction + call MSG(41) + function F{WRAP(42, ERRabort(42)) + CONT(42)} + call MSG(42) + let var = exists('*F{WRAP(43, ERRabort(43)) + CONT(43)}') + call MSG(43) + delfunction F{WRAP(44, ERRabort(44)) + CONT(44)} + call MSG(44) + let var = ERRabort(45) + CONT(45) + call MSG(45) + Xpath 'd' + + let expected = "" + \ .. "E1M1E2M2E3M3E4M4E5M5E6M6E7M7E8M8E9M9" + \ .. "I10M10I11M11I12M12I13M13I14M14I15M15I16M16I17M17I18M18" + \ .. "T19M19T20M20T21M21T22M22T23M23T24M24T25M25T26M26T27M27" + \ .. "E28C28M28E29C29M29E30C30M30E31C31M31E32C32M32E33C33M33" + \ .. "E34C34M34E35C35M35E36C36M36" + \ .. "A37W37C37M37A38W38C38M38A39W39C39M39A40W40C40M40A41W41C41M41" + \ .. "A42W42C42M42A43W43C43M43A44W44C44M44A45C45M45" + call assert_equal(expected, taken) + [CODE] + let verify =<< trim [CODE] + let expected = "a1a2a3a4a5a6a7a8a9" + \ .. "b10b11b12b13b14b15b16b17b18" + \ .. "c19c20c21c22c23c24c25c26c27d" + call assert_equal(expected, g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 77: Errors, interrupts, :throw in name{brace-expression} {{{1 +" +" When a function call made during evaluation of an expression in +" braces as part of a function name after ":function" is aborted due +" to an error inside a :try/:endtry region or due to an interrupt or +" a :throw, the expression evaluation is aborted as well, and the +" function definition is ignored, skipping all commands to the +" ":endfunction". On an error not inside :try/:endtry, the expression +" evaluation continues and the function gets defined, and can be +" called and deleted. +"------------------------------------------------------------------------------- +func Test_brace_expr_error() + let test =<< trim [CODE] + func ERR() abort + Xloop 'a' + asdf + endfunc " returns -1 + + func OK() + Xloop 'b' + let v:errmsg = "" + return 0 + endfunc + + let v:errmsg = "" + + Xpath 'c' + func F{1 + ERR() + OK()}(arg) + " F0 should be defined. + if exists("a:arg") && a:arg == "calling" + Xpath 'd' + else + call assert_report('should not get here') + endif + endfunction + call assert_equal("", v:errmsg) + XloopNEXT + + Xpath 'e' + call F{1 + ERR() + OK()}("calling") + call assert_equal("", v:errmsg) + XloopNEXT + + Xpath 'f' + delfunction F{1 + ERR() + OK()} + call assert_equal("", v:errmsg) + XloopNEXT + + try + while 1 + try + Xpath 'g' + func G{1 + ERR() + OK()}(arg) + " G0 should not be defined, and the function body should be + " skipped. + call assert_report('should not get here') + " Use an unmatched ":finally" to check whether the body is + " skipped when an error occurs in ERR(). This works whether or + " not the exception is converted to an exception. + finally + call assert_report('should not get here') + endtry + try + call assert_report('should not get here') + endfunction + + call assert_report('should not get here') + catch /asdf/ + " Jumped to when the function is not defined and the body is + " skipped. + Xpath 'h' + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'i' + break + endtry " jumped to when the body is not skipped + endwhile + catch /.*/ + call assert_report('should not get here') + endtry + [CODE] + let verify =<< trim [CODE] + call assert_equal('ca1b1ea2b2dfa3b3ga4hi', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 78: Messages on parsing errors in expression evaluation {{{1 +" +" When an expression evaluation detects a parsing error, an error +" message is given and converted to an exception, and the expression +" evaluation is aborted. +"------------------------------------------------------------------------------- +func Test_expr_eval_error_msg() + CheckEnglish + + let test =<< trim [CODE] + let taken = "" + + func F(n) + let g:taken = g:taken . "F" . a:n + endfunc + + func MSG(n, enr, emsg) + let g:taken = g:taken . "M" . a:n + call assert_match('^' .. a:enr .. ':', v:errmsg) + call assert_match(a:emsg, v:errmsg) + endfunc + + func CONT(n) + let g:taken = g:taken . "C" . a:n + endfunc + + let v:errmsg = "" + try + let t = 1 + while t <= 14 + let g:taken = g:taken . "T" . t + let v:errmsg = "" + try + if t == 1 + let v{novar + CONT(t)} = 0 + elseif t == 2 + let v{novar + CONT(t)} + elseif t == 3 + let var = exists('v{novar + CONT(t)}') + elseif t == 4 + unlet v{novar + CONT(t)} + elseif t == 5 + function F{novar + CONT(t)}() + endfunction + elseif t == 6 + function F{novar + CONT(t)} + elseif t == 7 + let var = exists('*F{novar + CONT(t)}') + elseif t == 8 + delfunction F{novar + CONT(t)} + elseif t == 9 + echo novar + CONT(t) + elseif t == 10 + echo v{novar + CONT(t)} + elseif t == 11 + echo F{novar + CONT(t)} + elseif t == 12 + let var = novar + CONT(t) + elseif t == 13 + let var = v{novar + CONT(t)} + elseif t == 14 + let var = F{novar + CONT(t)}() + endif + catch /^Vim\((\a\+)\)\=:/ + Xloop 'a' + " v:errmsg is not set when the error message is converted to an + " exception. Set it to the original error message. + let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "") + finally + Xloop 'b' + if t <= 8 && t != 3 && t != 7 + call MSG(t, 'E475', 'Invalid argument\>') + else + call MSG(t, 'E121', "Undefined variable") + endif + let t = t + 1 + XloopNEXT + continue " discard an aborting error + endtry + endwhile + catch /.*/ + call assert_report('should not get here') + endtry + + func T(n, expr, enr, emsg) + try + let g:taken = g:taken . "T" . a:n + let v:errmsg = "" + try + execute "let var = " . a:expr + catch /^Vim\((\a\+)\)\=:/ + Xloop 'c' + " v:errmsg is not set when the error message is converted to an + " exception. Set it to the original error message. + let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "") + finally + Xloop 'd' + call MSG(a:n, a:enr, a:emsg) + XloopNEXT + " Discard an aborting error: + return + endtry + catch /.*/ + call assert_report('should not get here') + endtry + endfunc + + call T(15, 'Nofunc() + CONT(15)', 'E117', "Unknown function") + call T(16, 'F(1 2 + CONT(16))', 'E116', "Invalid arguments") + call T(17, 'F(1, 2) + CONT(17)', 'E118', "Too many arguments") + call T(18, 'F() + CONT(18)', 'E119', "Not enough arguments") + call T(19, '{(1} + CONT(19)', 'E110', "Missing ')'") + call T(20, '("abc"[1) + CONT(20)', 'E111', "Missing ']'") + call T(21, '(1 +) + CONT(21)', 'E15', "Invalid expression") + call T(22, '1 2 + CONT(22)', 'E488', "Trailing characters: 2 +") + call T(23, '(1 ? 2) + CONT(23)', 'E109', "Missing ':' after '?'") + call T(24, '("abc) + CONT(24)', 'E114', "Missing quote") + call T(25, "('abc) + CONT(25)", 'E115', "Missing quote") + call T(26, '& + CONT(26)', 'E112', "Option name missing") + call T(27, '&asdf + CONT(27)', 'E113', "Unknown option") + + let expected = "" + \ .. "T1M1T2M2T3M3T4M4T5M5T6M6T7M7T8M8T9M9T10M10T11M11T12M12T13M13T14M14" + \ .. "T15M15T16M16T17M17T18M18T19M19T20M20T21M21T22M22T23M23T24M24T25M25" + \ .. "T26M26T27M27" + + call assert_equal(expected, taken) + [CODE] + let verify =<< trim [CODE] + let expected = "a1b1a2b2a3b3a4b4a5b5a6b6a7b7a8b8a9b9a10b10a11b11a12b12" + \ .. "a13b13a14b14c15d15c16d16c17d17c18d18c19d19c20d20" + \ .. "c21d21c22d22c23d23c24d24c25d25c26d26c27d27" + call assert_equal(expected, g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 79: Throwing one of several errors for the same command {{{1 +" +" When several errors appear in a row (for instance during expression +" evaluation), the first as the most specific one is used when +" throwing an error exception. If, however, a syntax error is +" detected afterwards, this one is used for the error exception. +" On a syntax error, the next command is not executed, on a normal +" error, however, it is (relevant only in a function without the +" "abort" flag). v:errmsg is not set. +" +" If throwing error exceptions is configured off, v:errmsg is always +" set to the latest error message, that is, to the more general +" message or the syntax error, respectively. +"------------------------------------------------------------------------------- +func Test_throw_multi_error() + CheckEnglish + + let test =<< trim [CODE] + func NEXT(cmd) + exec a:cmd . " | Xloop 'a'" + endfun + + call NEXT('echo novar') " (checks nextcmd) + XloopNEXT + call NEXT('let novar #') " (skips nextcmd) + XloopNEXT + call NEXT('unlet novar #') " (skips nextcmd) + XloopNEXT + call NEXT('let {novar}') " (skips nextcmd) + XloopNEXT + call NEXT('unlet{ novar}') " (skips nextcmd) + + call assert_equal('a1', g:Xpath) + XpathINIT + XloopINIT + + func EXEC(cmd) + exec a:cmd + endfunc + + try + while 1 " dummy loop + try + let v:errmsg = "" + call EXEC('echo novar') " normal error + catch /^Vim\((\a\+)\)\=:/ + Xpath 'b' + call assert_match('E121: Undefined variable: novar', v:exception) + finally + Xpath 'c' + call assert_equal("", v:errmsg) + break + endtry + endwhile + + Xpath 'd' + let cmd = "let" + while cmd != "" + try + let v:errmsg = "" + call EXEC(cmd . ' novar #') " normal plus syntax error + catch /^Vim\((\a\+)\)\=:/ + Xloop 'e' + call assert_match('E488: Trailing characters', v:exception) + finally + Xloop 'f' + call assert_equal("", v:errmsg) + if cmd == "let" + let cmd = "unlet" + else + let cmd = "" + endif + XloopNEXT + continue + endtry + endwhile + + Xpath 'g' + let cmd = "let" + while cmd != "" + try + let v:errmsg = "" + call EXEC(cmd . ' {novar}') " normal plus syntax error + catch /^Vim\((\a\+)\)\=:/ + Xloop 'h' + call assert_match('E475: Invalid argument: {novar}', v:exception) + finally + Xloop 'i' + call assert_equal("", v:errmsg) + if cmd == "let" + let cmd = "unlet" + else + let cmd = "" + endif + XloopNEXT + continue + endtry + endwhile + catch /.*/ + call assert_report('should not get here') + endtry + Xpath 'j' + [CODE] + let verify =<< trim [CODE] + call assert_equal('bcde1f1e2f2gh3i3h4i4j', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 80: Syntax error in expression for illegal :elseif {{{1 +" +" If there is a syntax error in the expression after an illegal +" :elseif, an error message is given (or an error exception thrown) +" for the illegal :elseif rather than the expression error. +"------------------------------------------------------------------------------- +func Test_if_syntax_error() + CheckEnglish + + let test =<< trim [CODE] + let v:errmsg = "" + if 0 + else + elseif 1 ||| 2 + endif + Xpath 'a' + call assert_match('E584: :elseif after :else', v:errmsg) + + let v:errmsg = "" + if 1 + else + elseif 1 ||| 2 + endif + Xpath 'b' + call assert_match('E584: :elseif after :else', v:errmsg) + + let v:errmsg = "" + elseif 1 ||| 2 + Xpath 'c' + call assert_match('E582: :elseif without :if', v:errmsg) + + let v:errmsg = "" + while 1 + elseif 1 ||| 2 + endwhile + Xpath 'd' + call assert_match('E582: :elseif without :if', v:errmsg) + + while 1 + try + try + let v:errmsg = "" + if 0 + else + elseif 1 ||| 2 + endif + catch /^Vim\((\a\+)\)\=:/ + Xpath 'e' + call assert_match('E584: :elseif after :else', v:exception) + finally + Xpath 'f' + call assert_equal("", v:errmsg) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'g' + break + endtry + endwhile + + while 1 + try + try + let v:errmsg = "" + if 1 + else + elseif 1 ||| 2 + endif + catch /^Vim\((\a\+)\)\=:/ + Xpath 'h' + call assert_match('E584: :elseif after :else', v:exception) + finally + Xpath 'i' + call assert_equal("", v:errmsg) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'j' + break + endtry + endwhile + + while 1 + try + try + let v:errmsg = "" + elseif 1 ||| 2 + catch /^Vim\((\a\+)\)\=:/ + Xpath 'k' + call assert_match('E582: :elseif without :if', v:exception) + finally + Xpath 'l' + call assert_equal("", v:errmsg) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'm' + break + endtry + endwhile + + while 1 + try + try + let v:errmsg = "" + while 1 + elseif 1 ||| 2 + endwhile + catch /^Vim\((\a\+)\)\=:/ + Xpath 'n' + call assert_match('E582: :elseif without :if', v:exception) + finally + Xpath 'o' + call assert_equal("", v:errmsg) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'p' + break + endtry + endwhile + Xpath 'q' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcdefghijklmnopq', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 81: Discarding exceptions after an error or interrupt {{{1 +" +" When an exception is thrown from inside a :try conditional without +" :catch and :finally clauses and an error or interrupt occurs before +" the :endtry is reached, the exception is discarded. +"------------------------------------------------------------------------------- + +func Test_discard_exception_after_error_1() + let test =<< trim [CODE] + try + Xpath 'a' + try + Xpath 'b' + throw "arrgh" + call assert_report('should not get here') + if 1 + call assert_report('should not get here') + " error after :throw: missing :endif + endtry + call assert_report('should not get here') + catch /arrgh/ + call assert_report('should not get here') + endtry + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('ab', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +" interrupt the code before the endtry is invoked +func Test_discard_exception_after_error_2() + XpathINIT + let lines =<< trim [CODE] + try + Xpath 'a' + try + Xpath 'b' + throw "arrgh" + call assert_report('should not get here') + endtry " interrupt here + call assert_report('should not get here') + catch /arrgh/ + call assert_report('should not get here') + endtry + call assert_report('should not get here') + [CODE] + call writefile(lines, 'Xscript') + + breakadd file 7 Xscript + try + let caught_intr = 0 + debuggreedy + call feedkeys(":source Xscript\<CR>quit\<CR>", "xt") + catch /^Vim:Interrupt$/ + call assert_match('Xscript, line 7', v:throwpoint) + let caught_intr = 1 + endtry + 0debuggreedy + call assert_equal(1, caught_intr) + call assert_equal('ab', g:Xpath) + breakdel * + call delete('Xscript') +endfunc + +"------------------------------------------------------------------------------- +" Test 82: Ignoring :catch clauses after an error or interrupt {{{1 +" +" When an exception is thrown and an error or interrupt occurs before +" the matching :catch clause is reached, the exception is discarded +" and the :catch clause is ignored (also for the error or interrupt +" exception being thrown then). +"------------------------------------------------------------------------------- + +func Test_ignore_catch_after_error_1() + let test =<< trim [CODE] + try + try + Xpath 'a' + throw "arrgh" + call assert_report('should not get here') + if 1 + call assert_report('should not get here') + " error after :throw: missing :endif + catch /.*/ + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + endtry + call assert_report('should not get here') + catch /arrgh/ + call assert_report('should not get here') + endtry + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('a', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_ignore_catch_after_error_2() + let test =<< trim [CODE] + func E() + try + try + Xpath 'a' + throw "arrgh" + call assert_report('should not get here') + if 1 + call assert_report('should not get here') + " error after :throw: missing :endif + catch /.*/ + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + endtry + call assert_report('should not get here') + catch /arrgh/ + call assert_report('should not get here') + endtry + endfunc + + call E() + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('a', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +" interrupt right before a catch is invoked in a script +func Test_ignore_catch_after_intr_1() + XpathINIT + let lines =<< trim [CODE] + try + try + Xpath 'a' + throw "arrgh" + call assert_report('should not get here') + catch /.*/ " interrupt here + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + endtry + call assert_report('should not get here') + catch /arrgh/ + call assert_report('should not get here') + endtry + call assert_report('should not get here') + [CODE] + call writefile(lines, 'Xscript') + + breakadd file 6 Xscript + try + let caught_intr = 0 + debuggreedy + call feedkeys(":source Xscript\<CR>quit\<CR>", "xt") + catch /^Vim:Interrupt$/ + call assert_match('Xscript, line 6', v:throwpoint) + let caught_intr = 1 + endtry + 0debuggreedy + call assert_equal(1, caught_intr) + call assert_equal('a', g:Xpath) + breakdel * + call delete('Xscript') +endfunc + +" interrupt right before a catch is invoked inside a function. +func Test_ignore_catch_after_intr_2() + XpathINIT + func F() + try + try + Xpath 'a' + throw "arrgh" + call assert_report('should not get here') + catch /.*/ " interrupt here + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + endtry + call assert_report('should not get here') + catch /arrgh/ + call assert_report('should not get here') + endtry + call assert_report('should not get here') + endfunc + + breakadd func 6 F + try + let caught_intr = 0 + debuggreedy + call feedkeys(":call F()\<CR>quit\<CR>", "xt") + catch /^Vim:Interrupt$/ + call assert_match('\.F, line 6', v:throwpoint) + let caught_intr = 1 + endtry + 0debuggreedy + call assert_equal(1, caught_intr) + call assert_equal('a', g:Xpath) + breakdel * + delfunc F +endfunc + +"------------------------------------------------------------------------------- +" Test 83: Executing :finally clauses after an error or interrupt {{{1 +" +" When an exception is thrown and an error or interrupt occurs before +" the :finally of the innermost :try is reached, the exception is +" discarded and the :finally clause is executed. +"------------------------------------------------------------------------------- + +func Test_finally_after_error() + let test =<< trim [CODE] + try + Xpath 'a' + try + Xpath 'b' + throw "arrgh" + call assert_report('should not get here') + if 1 + call assert_report('should not get here') + " error after :throw: missing :endif + finally + Xpath 'c' + endtry + call assert_report('should not get here') + catch /arrgh/ + call assert_report('should not get here') + endtry + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('abc', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +" interrupt the code right before the finally is invoked +func Test_finally_after_intr() + XpathINIT + let lines =<< trim [CODE] + try + Xpath 'a' + try + Xpath 'b' + throw "arrgh" + call assert_report('should not get here') + finally " interrupt here + Xpath 'c' + endtry + call assert_report('should not get here') + catch /arrgh/ + call assert_report('should not get here') + endtry + call assert_report('should not get here') + [CODE] + call writefile(lines, 'Xscript') + + breakadd file 7 Xscript + try + let caught_intr = 0 + debuggreedy + call feedkeys(":source Xscript\<CR>quit\<CR>", "xt") + catch /^Vim:Interrupt$/ + call assert_match('Xscript, line 7', v:throwpoint) + let caught_intr = 1 + endtry + 0debuggreedy + call assert_equal(1, caught_intr) + call assert_equal('abc', g:Xpath) + breakdel * + call delete('Xscript') +endfunc + +"------------------------------------------------------------------------------- +" Test 84: Exceptions in autocommand sequences. {{{1 +" +" When an exception occurs in a sequence of autocommands for +" a specific event, the rest of the sequence is not executed. The +" command that triggered the autocommand execution aborts, and the +" exception is propagated to the caller. +" +" For the FuncUndefined event under a function call expression or +" :call command, the function is not executed, even when it has +" been defined by the autocommands before the exception occurred. +"------------------------------------------------------------------------------- + +func Test_autocmd_exception() + let test =<< trim [CODE] + func INT() + call interrupt() + endfunc + + aug TMP + autocmd! + + autocmd User x1 Xpath 'a' + autocmd User x1 throw "x1" + autocmd User x1 call assert_report('should not get here') + + autocmd User x2 Xpath 'b' + autocmd User x2 asdf + autocmd User x2 call assert_report('should not get here') + + autocmd User x3 Xpath 'c' + autocmd User x3 call INT() + autocmd User x3 call assert_report('should not get here') + + autocmd FuncUndefined U1 func U1() + autocmd FuncUndefined U1 call assert_report('should not get here') + autocmd FuncUndefined U1 endfunc + autocmd FuncUndefined U1 Xpath 'd' + autocmd FuncUndefined U1 throw "U1" + autocmd FuncUndefined U1 call assert_report('should not get here') + + autocmd FuncUndefined U2 func U2() + autocmd FuncUndefined U2 call assert_report('should not get here') + autocmd FuncUndefined U2 endfunc + autocmd FuncUndefined U2 Xpath 'e' + autocmd FuncUndefined U2 ASDF + autocmd FuncUndefined U2 call assert_report('should not get here') + + autocmd FuncUndefined U3 func U3() + autocmd FuncUndefined U3 call assert_report('should not get here') + autocmd FuncUndefined U3 endfunc + autocmd FuncUndefined U3 Xpath 'f' + autocmd FuncUndefined U3 call INT() + autocmd FuncUndefined U3 call assert_report('should not get here') + aug END + + try + try + Xpath 'g' + doautocmd User x1 + catch /x1/ + Xpath 'h' + endtry + + while 1 + try + Xpath 'i' + doautocmd User x2 + catch /asdf/ + Xpath 'j' + finally + Xpath 'k' + break + endtry + endwhile + + while 1 + try + Xpath 'l' + doautocmd User x3 + catch /Vim:Interrupt/ + Xpath 'm' + finally + Xpath 'n' + " ... but break loop for caught interrupt exception, + " or discard interrupt and break loop if $VIMNOINTTHROW + break + endtry + endwhile + + if exists("*U1") | delfunction U1 | endif + if exists("*U2") | delfunction U2 | endif + if exists("*U3") | delfunction U3 | endif + + try + Xpath 'o' + call U1() + catch /U1/ + Xpath 'p' + endtry + + while 1 + try + Xpath 'q' + call U2() + catch /ASDF/ + Xpath 'r' + finally + Xpath 's' + " ... but break loop for caught error exception, + " or discard error and break loop if $VIMNOERRTHROW + break + endtry + endwhile + + while 1 + try + Xpath 't' + call U3() + catch /Vim:Interrupt/ + Xpath 'u' + finally + Xpath 'v' + " ... but break loop for caught interrupt exception, + " or discard interrupt and break loop if $VIMNOINTTHROW + break + endtry + endwhile + catch /.*/ + call assert_report('should not get here') + endtry + Xpath 'w' + [CODE] + let verify =<< trim [CODE] + call assert_equal('gahibjklcmnodpqerstfuvw', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 85: Error exceptions in autocommands for I/O command events {{{1 +" +" When an I/O command is inside :try/:endtry, autocommands to be +" executed after it should be skipped on an error (exception) in the +" command itself or in autocommands to be executed before the command. +" In the latter case, the I/O command should not be executed either. +" Example 1: BufWritePre, :write, BufWritePost +" Example 2: FileReadPre, :read, FileReadPost. +"------------------------------------------------------------------------------- + +func Test_autocmd_error_io_exception() + let test =<< trim [CODE] + " Remove the autocommands for the events specified as arguments in all used + " autogroups. + func Delete_autocommands(...) + let augfile = tempname() + while 1 + try + exec "redir >" . augfile + aug + redir END + exec "edit" augfile + g/^$/d + norm G$ + let wrap = "w" + while search('\%( \|^\)\@<=.\{-}\%( \)\@=', wrap) > 0 + let wrap = "W" + exec "norm y/ \n" + let argno = 1 + while argno <= a:0 + exec "au!" escape(@", " ") a:{argno} + let argno = argno + 1 + endwhile + endwhile + catch /.*/ + finally + bwipeout! + call delete(augfile) + break + endtry + endwhile + endfunc + + call Delete_autocommands("BufWritePre", "BufWritePost") + + while 1 + try + try + let post = 0 + aug TMP + au! BufWritePost * let post = 1 + aug END + write /n/o/n/e/x/i/s/t/e/n/t + catch /^Vim(write):/ + Xpath 'a' + call assert_match("E212: Can't open file for writing", v:exception) + finally + Xpath 'b' + call assert_equal(0, post) + au! TMP + aug! TMP + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'c' + break + endtry + endwhile + + while 1 + try + try + let post = 0 + aug TMP + au! BufWritePre * asdf + au! BufWritePost * let post = 1 + aug END + let tmpfile = tempname() + exec "write" tmpfile + catch /^Vim\((write)\)\=:/ + Xpath 'd' + call assert_match('E492: Not an editor command', v:exception) + finally + Xpath 'e' + if filereadable(tmpfile) + call assert_report('should not get here') + endif + call assert_equal(0, post) + au! TMP + aug! TMP + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'f' + break + endtry + endwhile + + call delete(tmpfile) + + call Delete_autocommands("BufWritePre", "BufWritePost", + \ "BufReadPre", "BufReadPost", "FileReadPre", "FileReadPost") + + while 1 + try + try + let post = 0 + aug TMP + au! FileReadPost * let post = 1 + aug END + let caught = 0 + read /n/o/n/e/x/i/s/t/e/n/t + catch /^Vim(read):/ + Xpath 'g' + call assert_match("E484: Can't open file", v:exception) + finally + Xpath 'h' + call assert_equal(0, post) + au! TMP + aug! TMP + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'i' + break + endtry + endwhile + + while 1 + try + let infile = tempname() + let tmpfile = tempname() + call writefile(["XYZ"], infile) + exec "edit" tmpfile + try + Xpath 'j' + try + let post = 0 + aug TMP + au! FileReadPre * asdf + au! FileReadPost * let post = 1 + aug END + exec "0read" infile + catch /^Vim\((read)\)\=:/ + Xpath 'k' + call assert_match('E492: Not an editor command', v:exception) + finally + Xpath 'l' + if getline("1") == "XYZ" + call assert_report('should not get here') + endif + call assert_equal(0, post) + au! TMP + aug! TMP + endtry + finally + Xpath 'm' + bwipeout! + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'n' + break + endtry + endwhile + + call delete(infile) + call delete(tmpfile) + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcdefghijklmn', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + "------------------------------------------------------------------------------- " Test 87 using (expr) ? funcref : funcref {{{1 " @@ -1370,10 +6723,10 @@ endfunc " Test 95: lines of :append, :change, :insert {{{1 "------------------------------------------------------------------------------- -function! DefineFunction(name, body) +func DefineFunction(name, body) let func = join(['function! ' . a:name . '()'] + a:body + ['endfunction'], "\n") exec func -endfunction +endfunc func Test_script_lines() " :append @@ -1443,7 +6796,7 @@ endfunc " Undefined behavior was detected by ubsan with line continuation " after an empty line. "------------------------------------------------------------------------------- -func Test_script_emty_line_continuation() +func Test_script_empty_line_continuation() \ endfunc @@ -1495,55 +6848,6 @@ func Test_bitwise_functions() call assert_fails("call invert({})", 'E728:') endfunc -" Test trailing text after :endfunction {{{1 -func Test_endfunction_trailing() - call assert_false(exists('*Xtest')) - - exe "func Xtest()\necho 'hello'\nendfunc\nlet done = 'yes'" - call assert_true(exists('*Xtest')) - call assert_equal('yes', done) - delfunc Xtest - unlet done - - exe "func Xtest()\necho 'hello'\nendfunc|let done = 'yes'" - call assert_true(exists('*Xtest')) - call assert_equal('yes', done) - delfunc Xtest - unlet done - - " trailing line break - exe "func Xtest()\necho 'hello'\nendfunc\n" - call assert_true(exists('*Xtest')) - delfunc Xtest - - set verbose=1 - exe "func Xtest()\necho 'hello'\nendfunc \" garbage" - call assert_notmatch('W22:', split(execute('1messages'), "\n")[0]) - call assert_true(exists('*Xtest')) - delfunc Xtest - - exe "func Xtest()\necho 'hello'\nendfunc garbage" - call assert_match('W22:', split(execute('1messages'), "\n")[0]) - call assert_true(exists('*Xtest')) - delfunc Xtest - set verbose=0 - - function Foo() - echo 'hello' - endfunction | echo 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' - delfunc Foo -endfunc - -func Test_delfunction_force() - delfunc! Xtest - delfunc! Xtest - func Xtest() - echo 'nothing' - endfunc - delfunc! Xtest - delfunc! Xtest -endfunc - " Test using bang after user command {{{1 func Test_user_command_with_bang() command -bang Nieuw let nieuw = 1 @@ -1553,26 +6857,6 @@ func Test_user_command_with_bang() delcommand Nieuw endfunc -" Test for script-local function -func <SID>DoLast() - call append(line('$'), "last line") -endfunc - -func s:DoNothing() - call append(line('$'), "nothing line") -endfunc - -func Test_script_local_func() - set nocp nomore viminfo+=nviminfo - new - nnoremap <buffer> _x :call <SID>DoNothing()<bar>call <SID>DoLast()<bar>delfunc <SID>DoNothing<bar>delfunc <SID>DoLast<cr> - - normal _x - call assert_equal('nothing line', getline(2)) - call assert_equal('last line', getline(3)) - enew! | close -endfunc - func Test_script_expand_sfile() let lines =<< trim END func s:snr() @@ -1653,6 +6937,22 @@ func Test_compound_assignment_operators() call assert_equal(4.2, x) call assert_fails('let x %= 0.5', 'E734') call assert_fails('let x .= "f"', 'E734') + let x = !3.14 + call assert_equal(0.0, x) + + " integer and float operations + let x = 1 + let x *= 2.1 + call assert_equal(2.1, x) + let x = 1 + let x /= 0.25 + call assert_equal(4.0, x) + let x = 1 + call assert_fails('let x %= 0.25', 'E734:') + let x = 1 + call assert_fails('let x .= 0.25', 'E734:') + let x = 1.0 + call assert_fails('let x += [1.1]', 'E734:') endif " Test for environment variable @@ -1716,87 +7016,6 @@ func Test_unlet_env() call assert_equal('', $TESTVAR) endfunc -func Test_funccall_garbage_collect() - func Func(x, ...) - call add(a:x, a:000) - endfunc - call Func([], []) - " Must not crash cause by invalid freeing - call test_garbagecollect_now() - call assert_true(v:true) - delfunc Func -endfunc - -func Test_function_defined_line() - if has('gui_running') - " Can't catch the output of gvim. - return - endif - - let lines =<< trim [CODE] - " F1 - func F1() - " F2 - func F2() - " - " - " - return - endfunc - " F3 - execute "func F3()\n\n\n\nreturn\nendfunc" - " F4 - execute "func F4()\n - \\n - \\n - \\n - \return\n - \endfunc" - endfunc - " F5 - execute "func F5()\n\n\n\nreturn\nendfunc" - " F6 - execute "func F6()\n - \\n - \\n - \\n - \return\n - \endfunc" - call F1() - verbose func F1 - verbose func F2 - verbose func F3 - verbose func F4 - verbose func F5 - verbose func F6 - qall! - [CODE] - - call writefile(lines, 'Xtest.vim') - let res = system(GetVimCommandClean() .. ' -es -X -S Xtest.vim') - call assert_equal(0, v:shell_error) - - let m = matchstr(res, 'function F1()[^[:print:]]*[[:print:]]*') - call assert_match(' line 2$', m) - - let m = matchstr(res, 'function F2()[^[:print:]]*[[:print:]]*') - call assert_match(' line 4$', m) - - let m = matchstr(res, 'function F3()[^[:print:]]*[[:print:]]*') - call assert_match(' line 11$', m) - - let m = matchstr(res, 'function F4()[^[:print:]]*[[:print:]]*') - call assert_match(' line 13$', m) - - let m = matchstr(res, 'function F5()[^[:print:]]*[[:print:]]*') - call assert_match(' line 21$', m) - - let m = matchstr(res, 'function F6()[^[:print:]]*[[:print:]]*') - call assert_match(' line 23$', m) - - call delete('Xtest.vim') -endfunc - " Test for missing :endif, :endfor, :endwhile and :endtry {{{1 func Test_missing_end() call writefile(['if 2 > 1', 'echo ">"'], 'Xscript') @@ -1829,15 +7048,19 @@ func Test_missing_end() endtry call assert_equal(1, caught_e733) + " Using endfunc with :if + call assert_fails('exe "if 1 | endfunc | endif"', 'E193:') + " Missing 'in' in a :for statement call assert_fails('for i range(1) | endfor', 'E690:') + + " Incorrect number of variables in for + call assert_fails('for [i,] in range(3) | endfor', 'E475:') endfunc " Test for deep nesting of if/for/while/try statements {{{1 func Test_deep_nest() - if !CanRunVimInTerminal() - throw 'Skipped: cannot run vim in terminal' - endif + CheckRunVimInTerminal let lines =<< trim [SCRIPT] " Deep nesting of if ... endif @@ -1875,6 +7098,15 @@ func Test_deep_nest() @a let @a = '' endfunc + + " Deep nesting of function ... endfunction + func Test5() + let @a = join(repeat(['function X()'], 51), "\n") + let @a ..= "\necho v:true\n" + let @a ..= join(repeat(['endfunction'], 51), "\n") + @a + let @a = '' + endfunc [SCRIPT] call writefile(lines, 'Xscript') @@ -1882,20 +7114,31 @@ func Test_deep_nest() " Deep nesting of if ... endif call term_sendkeys(buf, ":call Test1()\n") + call term_wait(buf) call WaitForAssert({-> assert_match('^E579:', term_getline(buf, 5))}) " Deep nesting of for ... endfor call term_sendkeys(buf, ":call Test2()\n") + call term_wait(buf) call WaitForAssert({-> assert_match('^E585:', term_getline(buf, 5))}) " Deep nesting of while ... endwhile call term_sendkeys(buf, ":call Test3()\n") + call term_wait(buf) call WaitForAssert({-> assert_match('^E585:', term_getline(buf, 5))}) " Deep nesting of try ... endtry call term_sendkeys(buf, ":call Test4()\n") + call term_wait(buf) call WaitForAssert({-> assert_match('^E601:', term_getline(buf, 5))}) + " Deep nesting of function ... endfunction + call term_sendkeys(buf, ":call Test5()\n") + call term_wait(buf) + call WaitForAssert({-> assert_match('^E1058:', term_getline(buf, 4))}) + call term_sendkeys(buf, "\<C-C>\n") + call term_wait(buf) + "let l = '' "for i in range(1, 6) " let l ..= term_getline(buf, i) . "\n" @@ -1906,14 +7149,113 @@ func Test_deep_nest() call delete('Xscript') endfunc -" Test for <sfile>, <slnum> in a function {{{1 -func Test_sfile_in_function() - func Xfunc() - call assert_match('..Test_sfile_in_function\[5]..Xfunc', expand('<sfile>')) - call assert_equal('2', expand('<slnum>')) - endfunc - call Xfunc() - delfunc Xfunc +" Test for errors in converting to float from various types {{{1 +func Test_float_conversion_errors() + if has('float') + call assert_fails('let x = 4.0 % 2.0', 'E804') + call assert_fails('echo 1.1[0]', 'E806') + call assert_fails('echo sort([function("min"), 1], "f")', 'E891:') + call assert_fails('echo 3.2 == "vim"', 'E892:') + call assert_fails('echo sort([[], 1], "f")', 'E893:') + call assert_fails('echo sort([{}, 1], "f")', 'E894:') + call assert_fails('echo 3.2 == v:true', 'E362:') + " call assert_fails('echo 3.2 == v:none', 'E907:') + endif +endfunc + +" invalid function names {{{1 +func Test_invalid_function_names() + " function name not starting with capital + let caught_e128 = 0 + try + func! g:test() + echo "test" + endfunc + catch /E128:/ + let caught_e128 = 1 + endtry + call assert_equal(1, caught_e128) + + " function name includes a colon + let caught_e884 = 0 + try + func! b:test() + echo "test" + endfunc + catch /E884:/ + let caught_e884 = 1 + endtry + call assert_equal(1, caught_e884) + + " function name followed by # + let caught_e128 = 0 + try + func! test2() "# + echo "test2" + endfunc + catch /E128:/ + let caught_e128 = 1 + endtry + call assert_equal(1, caught_e128) + + " function name starting with/without "g:", buffer-local funcref. + function! g:Foo(n) + return 'called Foo(' . a:n . ')' + endfunction + let b:my_func = function('Foo') + call assert_equal('called Foo(1)', b:my_func(1)) + call assert_equal('called Foo(2)', g:Foo(2)) + call assert_equal('called Foo(3)', Foo(3)) + delfunc g:Foo + + " script-local function used in Funcref must exist. + let lines =<< trim END + func s:Testje() + return "foo" + endfunc + let Bar = function('s:Testje') + call assert_equal(0, exists('s:Testje')) + call assert_equal(1, exists('*s:Testje')) + call assert_equal(1, exists('Bar')) + call assert_equal(1, exists('*Bar')) + END + call writefile(lines, 'Xscript') + source Xscript + call delete('Xscript') +endfunc + +" substring and variable name {{{1 +func Test_substring_var() + let str = 'abcdef' + let n = 3 + call assert_equal('def', str[n:]) + call assert_equal('abcd', str[:n]) + call assert_equal('d', str[n:n]) + unlet n + let nn = 3 + call assert_equal('def', str[nn:]) + call assert_equal('abcd', str[:nn]) + call assert_equal('d', str[nn:nn]) + unlet nn + let b:nn = 4 + call assert_equal('ef', str[b:nn:]) + call assert_equal('abcde', str[:b:nn]) + call assert_equal('e', str[b:nn:b:nn]) + unlet b:nn +endfunc + +" Test using s: with a typed command {{{1 +func Test_typed_script_var() + CheckRunVimInTerminal + + let buf = RunVimInTerminal('', {'rows': 6}) + + " Deep nesting of if ... endif + call term_sendkeys(buf, ":echo get(s:, 'foo', 'x')\n") + call TermWait(buf) + call WaitForAssert({-> assert_match('^E116:', term_getline(buf, 5))}) + + call StopVimInTerminal(buf) endfunc func Test_for_over_string() diff --git a/src/nvim/testdir/test_virtualedit.vim b/src/nvim/testdir/test_virtualedit.vim index 522ca17675..2bf8c3fc77 100644 --- a/src/nvim/testdir/test_virtualedit.vim +++ b/src/nvim/testdir/test_virtualedit.vim @@ -80,6 +80,10 @@ func Test_edit_change() call setline(1, "\t⒌") normal Cx call assert_equal('x', getline(1)) + " Do a visual block change + call setline(1, ['a', 'b', 'c']) + exe "normal gg3l\<C-V>2jcx" + call assert_equal(['a x', 'b x', 'c x'], getline(1, '$')) bwipe! set virtualedit= endfunc @@ -289,6 +293,16 @@ func Test_replace_after_eol() call append(0, '"r"') normal gg$5lrxa call assert_equal('"r" x', getline(1)) + " visual block replace + %d _ + call setline(1, ['a', '', 'b']) + exe "normal 2l\<C-V>2jrx" + call assert_equal(['a x', ' x', 'b x'], getline(1, '$')) + " visual characterwise selection replace after eol + %d _ + call setline(1, 'a') + normal 4lv2lrx + call assert_equal('a xxx', getline(1)) bwipe! set virtualedit= endfunc @@ -346,6 +360,48 @@ func Test_yank_paste_small_del_reg() set virtualedit= endfunc +" Test for delete that breaks a tab into spaces +func Test_delete_break_tab() + new + call setline(1, "one\ttwo") + set virtualedit=all + normal v3ld + call assert_equal(' two', getline(1)) + set virtualedit& + close! +endfunc + +" Test for using <BS>, <C-W> and <C-U> in virtual edit mode +" to erase character, word and line. +func Test_ve_backspace() + new + call setline(1, 'sample') + set virtualedit=all + set backspace=indent,eol,start + exe "normal 15|i\<BS>\<BS>" + call assert_equal([0, 1, 7, 5], getpos('.')) + exe "normal 15|i\<C-W>" + call assert_equal([0, 1, 6, 0], getpos('.')) + exe "normal 15|i\<C-U>" + call assert_equal([0, 1, 1, 0], getpos('.')) + set backspace& + set virtualedit& + close! +endfunc + +" Test for delete (x) on EOL character and after EOL +func Test_delete_past_eol() + new + call setline(1, "ab") + set virtualedit=all + exe "normal 2lx" + call assert_equal('ab', getline(1)) + exe "normal 10lx" + call assert_equal('ab', getline(1)) + set virtualedit& + bw! +endfunc + " After calling s:TryVirtualeditReplace(), line 1 will contain one of these " two strings, depending on whether virtual editing is on or off. let s:result_ve_on = 'a x' @@ -481,4 +537,53 @@ func Test_global_local_virtualedit() set virtualedit& endfunc +func Test_virtualedit_mouse() + let save_mouse = &mouse + set mouse=a + set virtualedit=all + new + + call setline(1, ["text\tword"]) + redraw + call Ntest_setmouse(1, 4) + call feedkeys("\<LeftMouse>", "xt") + call assert_equal([0, 1, 4, 0, 4], getcurpos()) + call Ntest_setmouse(1, 5) + call feedkeys("\<LeftMouse>", "xt") + call assert_equal([0, 1, 5, 0, 5], getcurpos()) + call Ntest_setmouse(1, 6) + call feedkeys("\<LeftMouse>", "xt") + call assert_equal([0, 1, 5, 1, 6], getcurpos()) + call Ntest_setmouse(1, 7) + call feedkeys("\<LeftMouse>", "xt") + call assert_equal([0, 1, 5, 2, 7], getcurpos()) + call Ntest_setmouse(1, 8) + call feedkeys("\<LeftMouse>", "xt") + call assert_equal([0, 1, 5, 3, 8], getcurpos()) + call Ntest_setmouse(1, 9) + call feedkeys("\<LeftMouse>", "xt") + call assert_equal([0, 1, 6, 0, 9], getcurpos()) + call Ntest_setmouse(1, 15) + call feedkeys("\<LeftMouse>", "xt") + call assert_equal([0, 1, 10, 2, 15], getcurpos()) + + bwipe! + let &mouse = save_mouse + set virtualedit& +endfunc + +" this was replacing the NUL at the end of the line +func Test_virtualedit_replace_after_tab() + new + s/\v/ 0 + set ve=all + let @" = '' + sil! norm vPvr0 + + call assert_equal("\t0", getline(1)) + set ve& + bwipe! +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index 9c1ad0c099..14d62089cf 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -225,6 +225,15 @@ func Test_virtual_replace() exe "normal iabcdefghijklmnopqrst\<Esc>0gRAB\tIJKLMNO\tQR" call assert_equal(['AB......CDEFGHI.Jkl', \ 'AB IJKLMNO QRst'], getline(12, 13)) + + " Test inserting Tab with 'noexpandtab' and 'softabstop' set to 4 + %d + call setline(1, 'aaaaaaaaaaaaa') + set softtabstop=4 + exe "normal gggR\<Tab>\<Tab>x" + call assert_equal("\txaaaa", getline(1)) + set softtabstop& + enew! set noai bs&vim if exists('save_t_kD') @@ -474,6 +483,18 @@ func Test_visual_block_put() bw! endfunc +func Test_visual_block_put_invalid() + enew! + behave mswin + norm yy + norm v)Ps/^/ + " this was causing the column to become negative + silent norm ggv)P + + bwipe! + behave xterm +endfunc + " Visual modes (v V CTRL-V) followed by an operator; count; repeating func Test_visual_mode_op() new @@ -1269,7 +1290,7 @@ func Test_visual_block_with_virtualedit() endfunc func Test_visual_block_ctrl_w_f() - " Emtpy block selected in new buffer should not result in an error. + " Empty block selected in new buffer should not result in an error. au! BufNew foo sil norm f edit foo @@ -1317,6 +1338,18 @@ func Test_visual_reselect_with_count() call delete('XvisualReselect') endfunc +func Test_visual_reselect_exclusive() + new + call setline(1, ['abcde', 'abcde']) + set selection=exclusive + normal 1G0viwd + normal 2G01vd + call assert_equal(['', ''], getline(1, 2)) + + set selection& + bwipe! +endfunc + func Test_visual_block_insert_round_off() new " The number of characters are tuned to fill a 4096 byte allocated block, @@ -1492,5 +1525,4 @@ func Test_switch_buffer_ends_visual_mode() exe 'bwipe!' buf2 endfunc - " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_winbuf_close.vim b/src/nvim/testdir/test_winbuf_close.vim index f4878c2397..26b4ba8778 100644 --- a/src/nvim/testdir/test_winbuf_close.vim +++ b/src/nvim/testdir/test_winbuf_close.vim @@ -115,7 +115,7 @@ func Test_winbuf_close() call assert_equal('Xtest2', bufname('%')) quit! call assert_equal('Xtest3', bufname('%')) - call assert_fails('silent! quit!', 'E162') + call assert_fails('silent! quit!', 'E37') call assert_equal('Xtest1', bufname('%')) call delete('Xtest1') @@ -192,7 +192,23 @@ func Test_tabwin_close() call win_execute(l:wid, 'close') " Should not crash. call assert_true(v:true) - %bwipe! + + " This tests closing a window in another tab, while leaving the tab open + " i.e. two windows in another tab. + tabedit + let w:this_win = 42 + new + let othertab_wid = win_getid() + tabprevious + call win_execute(othertab_wid, 'q') + " drawing the tabline helps check that the other tab's windows and buffers + " are still valid + redrawtabline + " but to be certain, ensure we can focus the other tab too + tabnext + call assert_equal(42, w:this_win) + + bwipe! endfunc " Test when closing a split window (above/below) restores space to the window diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index 83a3216534..c25b1f1157 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -1,5 +1,8 @@ " Tests for window cmd (:wincmd, :split, :vsplit, :resize and etc...) +source check.vim +source screendump.vim + func Test_window_cmd_ls0_with_split() set ls=0 set splitbelow @@ -343,6 +346,46 @@ func Test_window_height() bw Xa Xb Xc endfunc +func Test_wincmd_equal() + edit Xone + below split Xtwo + rightbelow vsplit Xthree + call assert_equal('Xone', bufname(winbufnr(1))) + call assert_equal('Xtwo', bufname(winbufnr(2))) + call assert_equal('Xthree', bufname(winbufnr(3))) + + " Xone and Xtwo should be about the same height + let [wh1, wh2] = [winheight(1), winheight(2)] + call assert_inrange(wh1 - 1, wh1 + 1, wh2) + " Xtwo and Xthree should be about the same width + let [ww2, ww3] = [winwidth(2), winwidth(3)] + call assert_inrange(ww2 - 1, ww2 + 1, ww3) + + 1wincmd w + 10wincmd _ + 2wincmd w + 20wincmd | + call assert_equal(10, winheight(1)) + call assert_equal(20, winwidth(2)) + + " equalizing horizontally doesn't change the heights + hor wincmd = + call assert_equal(10, winheight(1)) + let [ww2, ww3] = [winwidth(2), winwidth(3)] + call assert_inrange(ww2 - 1, ww2 + 1, ww3) + + 2wincmd w + 20wincmd | + call assert_equal(20, winwidth(2)) + " equalizing vertically doesn't change the widths + vert wincmd = + call assert_equal(20, winwidth(2)) + let [wh1, wh2] = [winheight(1), winheight(2)] + call assert_inrange(wh1 - 1, wh1 + 1, wh2) + + bwipe Xone Xtwo Xthree +endfunc + func Test_window_width() e Xa vsplit Xb @@ -393,7 +436,15 @@ func Test_window_width() call assert_inrange(ww1, ww1 + 1, ww2) call assert_inrange(ww3, ww3 + 1, ww2) - bw Xa Xb Xc + " when the current window width is less than the new 'winwidth', the current + " window width should be increased. + enew | only + split + 10vnew + set winwidth=15 + call assert_equal(15, winwidth(0)) + + %bw! endfunc func Test_equalalways_on_close() @@ -450,10 +501,13 @@ func Test_win_screenpos() call assert_equal([1, 32], win_screenpos(2)) call assert_equal([12, 1], win_screenpos(3)) call assert_equal([0, 0], win_screenpos(4)) + call assert_fails('let l = win_screenpos([])', 'E745:') only endfunc func Test_window_jump_tag() + CheckFeature quickfix + help /iccf call assert_match('^|iccf|', getline('.')) @@ -491,6 +545,7 @@ func Test_window_newtab() call assert_equal(2, tabpagenr('$')) call assert_equal(['Xb', 'Xa'], map(tabpagebuflist(1), 'bufname(v:val)')) call assert_equal(['Xc' ], map(2->tabpagebuflist(), 'bufname(v:val)')) + call assert_equal(['Xc' ], map(tabpagebuflist(), 'bufname(v:val)')) %bw! endfunc @@ -541,6 +596,11 @@ func Test_window_contents() call assert_equal(59, line("w0")) call assert_equal('59 ', s3) + %d + call setline(1, ['one', 'two', 'three']) + call assert_equal(1, line('w0')) + call assert_equal(3, line('w$')) + bwipeout! call test_garbagecollect_now() endfunc @@ -677,6 +737,7 @@ func Test_relative_cursor_position_in_one_line_window() only! bwipe! + call assert_fails('call winrestview(v:_null_dict)', 'E474:') endfunc func Test_relative_cursor_position_after_move_and_resize() @@ -853,6 +914,10 @@ func Test_winnr() call assert_fails("echo winnr('ll')", 'E15:') call assert_fails("echo winnr('5')", 'E15:') call assert_equal(4, winnr('0h')) + call assert_fails("let w = winnr([])", 'E730:') + call assert_equal('unknown', win_gettype(-1)) + call assert_equal(-1, winheight(-1)) + call assert_equal(-1, winwidth(-1)) tabnew call assert_equal(8, tabpagewinnr(1, 'j')) @@ -873,9 +938,12 @@ func Test_winrestview() call assert_equal(view, winsaveview()) bwipe! + call assert_fails('call winrestview(v:_null_dict)', 'E474:') endfunc func Test_win_splitmove() + CheckFeature quickfix + edit a leftabove split b leftabove vsplit c @@ -901,6 +969,7 @@ func Test_win_splitmove() call assert_equal(bufname(winbufnr(2)), 'b') call assert_equal(bufname(winbufnr(3)), 'a') call assert_equal(bufname(winbufnr(4)), 'd') + call assert_fails('call win_splitmove(winnr(), winnr("k"), v:_null_dict)', 'E474:') only | bd call assert_fails('call win_splitmove(winnr(), 123)', 'E957:') @@ -1073,6 +1142,18 @@ func Test_split_cmds_with_no_room() call Run_noroom_for_newwindow_test('v') endfunc +" Test for various wincmd failures +func Test_wincmd_fails() + only! + call assert_beeps("normal \<C-W>w") + call assert_beeps("normal \<C-W>p") + call assert_beeps("normal \<C-W>gk") + call assert_beeps("normal \<C-W>r") + call assert_beeps("normal \<C-W>K") + call assert_beeps("normal \<C-W>H") + call assert_beeps("normal \<C-W>2gt") +endfunc + func Test_window_resize() " Vertical :resize (absolute, relative, min and max size). vsplit @@ -1158,7 +1239,7 @@ endfunc " Test for jumping to a vertical/horizontal neighbor window based on the " current cursor position -func Test_window_goto_neightbor() +func Test_window_goto_neighbor() %bw! " Vertical window movement @@ -1337,17 +1418,20 @@ func Test_win_move_separator() call assert_equal(w0, winwidth(0)) call assert_true(win_move_separator(0, -1)) call assert_equal(w0, winwidth(0)) + " check that win_move_separator doesn't error with offsets beyond moving " possibility call assert_true(win_move_separator(id, 5000)) call assert_true(winwidth(id) > w) call assert_true(win_move_separator(id, -5000)) call assert_true(winwidth(id) < w) + " check that win_move_separator returns false for an invalid window wincmd = let w = winwidth(0) call assert_false(win_move_separator(-1, 1)) call assert_equal(w, winwidth(0)) + " check that win_move_separator returns false for a floating window let id = nvim_open_win( \ 0, 0, #{relative: 'editor', row: 2, col: 2, width: 5, height: 3}) @@ -1355,6 +1439,13 @@ func Test_win_move_separator() call assert_false(win_move_separator(id, 1)) call assert_equal(w, winwidth(id)) call nvim_win_close(id, 1) + + " check that using another tabpage fails without crash + let id = win_getid() + tabnew + call assert_fails('call win_move_separator(id, -1)', 'E1308:') + tabclose + %bwipe! endfunc @@ -1403,17 +1494,20 @@ func Test_win_move_statusline() call assert_true(id->win_move_statusline(-offset)) call assert_equal(h, winheight(id)) endfor + " check that win_move_statusline doesn't error with offsets beyond moving " possibility call assert_true(win_move_statusline(id, 5000)) call assert_true(winheight(id) > h) call assert_true(win_move_statusline(id, -5000)) call assert_true(winheight(id) < h) + " check that win_move_statusline returns false for an invalid window wincmd = let h = winheight(0) call assert_false(win_move_statusline(-1, 1)) call assert_equal(h, winheight(0)) + " check that win_move_statusline returns false for a floating window let id = nvim_open_win( \ 0, 0, #{relative: 'editor', row: 2, col: 2, width: 5, height: 3}) @@ -1421,7 +1515,319 @@ func Test_win_move_statusline() call assert_false(win_move_statusline(id, 1)) call assert_equal(h, winheight(id)) call nvim_win_close(id, 1) + + " check that using another tabpage fails without crash + let id = win_getid() + tabnew + call assert_fails('call win_move_statusline(id, -1)', 'E1308:') + tabclose + %bwipe! endfunc +func Test_win_equal_last_status() + let save_lines = &lines + set lines=20 + set splitbelow + set laststatus=0 + + split | split | quit + call assert_equal(winheight(1), winheight(2)) + + let &lines = save_lines + set splitbelow& + set laststatus& +endfunc + +" Test "screen" and "cursor" values for 'splitkeep' with a sequence of +" split operations for various options: with and without a winbar, +" tabline, for each possible value of 'laststatus', 'scrolloff', +" 'equalalways', and with the cursor at the top, middle and bottom. +func Test_splitkeep_options() + " disallow window resizing + " let save_WS = &t_WS + " set t_WS= + + let gui = has("gui_running") + inoremap <expr> c "<cmd>copen<bar>wincmd k<CR>" + for run in range(0, 20) + let &splitkeep = run > 10 ? 'topline' : 'screen' + let &scrolloff = (!(run % 4) ? 0 : run) + let &laststatus = (run % 3) + let &splitbelow = (run % 3) + let &equalalways = (run % 2) + " Nvim: both windows have a winbar after splitting + " let wsb = (run % 2) && &splitbelow + let wsb = 0 + let tl = (gui ? 0 : ((run % 5) ? 1 : 0)) + let pos = !(run % 3) ? 'H' : ((run % 2) ? 'M' : 'L') + tabnew | tabonly! | redraw + execute (run % 5) ? 'tabnew' : '' + " execute (run % 2) ? 'nnoremenu 1.10 WinBar.Test :echo' : '' + let &winbar = (run % 2) ? '%f' : '' + call setline(1, range(1, 256)) + " No scroll for restore_snapshot + norm G + try + copen | close | colder + catch /E380/ + endtry + call assert_equal(257 - winheight(0), line("w0")) + + " No scroll for firstwin horizontal split + execute 'norm gg' . pos + split | redraw | wincmd k + call assert_equal(1, line("w0")) + call assert_equal(&scroll, winheight(0) / 2) + wincmd j + call assert_equal(&spk == 'topline' ? 1 : win_screenpos(0)[0] - tl - wsb, line("w0")) + + " No scroll when resizing windows + wincmd k | resize +2 | redraw + call assert_equal(1, line("w0")) + wincmd j + call assert_equal(&spk == 'topline' ? 1 : win_screenpos(0)[0] - tl - wsb, line("w0")) + + " No scroll when dragging statusline + call win_move_statusline(1, -3) + call assert_equal(&spk == 'topline' ? 1 : win_screenpos(0)[0] - tl - wsb, line("w0")) + wincmd k + call assert_equal(1, line("w0")) + + " No scroll when changing shellsize + set lines+=2 + call assert_equal(1, line("w0")) + wincmd j + call assert_equal(&spk == 'topline' ? 1 : win_screenpos(0)[0] - tl - wsb, line("w0")) + set lines-=2 + call assert_equal(&spk == 'topline' ? 1 : win_screenpos(0)[0] - tl - wsb, line("w0")) + wincmd k + call assert_equal(1, line("w0")) + + " No scroll when equalizing windows + wincmd = + call assert_equal(1, line("w0")) + wincmd j + call assert_equal(&spk == 'topline' ? 1 : win_screenpos(0)[0] - tl - wsb, line("w0")) + wincmd k + call assert_equal(1, line("w0")) + + " No scroll in windows split multiple times + vsplit | split | 4wincmd w + call assert_equal(&spk == 'topline' ? 1 : win_screenpos(0)[0] - tl - wsb, line("w0")) + 1wincmd w | quit | wincmd l | split + call assert_equal(&spk == 'topline' ? 1 : win_screenpos(0)[0] - tl - wsb, line("w0")) + wincmd j + call assert_equal(&spk == 'topline' ? 1 : win_screenpos(0)[0] - tl - wsb, line("w0")) + + " No scroll in small window + 2wincmd w | only | 5split | wincmd k + call assert_equal(1, line("w0")) + wincmd j + call assert_equal(&spk == 'topline' ? 1 : win_screenpos(0)[0] - tl - wsb, line("w0")) + + " No scroll for vertical split + quit | vsplit | wincmd l + call assert_equal(1, line("w0")) + wincmd h + call assert_equal(1, line("w0")) + + " No scroll in windows split and quit multiple times + quit | redraw | split | split | quit | redraw + call assert_equal(&spk == 'topline' ? 1 : win_screenpos(0)[0] - tl - wsb, line("w0")) + + " No scroll for new buffer + 1wincmd w | only | copen | wincmd k + call assert_equal(1, line("w0")) + only + call assert_equal(1, line("w0")) + above copen | wincmd j + call assert_equal(&spk == 'topline' ? 1 : win_screenpos(0)[0] - tl, line("w0")) + + " No scroll when opening cmdwin, and no cursor move when closing cmdwin. + only | norm ggL + let curpos = getcurpos() + norm q: + call assert_equal(1, line("w0")) + call assert_equal(curpos, getcurpos()) + + " Scroll when cursor becomes invalid in insert mode + norm Lic + call assert_equal(curpos, getcurpos()) + + " No scroll when topline not equal to 1 + only | execute "norm gg5\<C-e>" | split | wincmd k + call assert_equal(6, line("w0")) + wincmd j + call assert_equal(&spk == 'topline' ? 6 : 5 + win_screenpos(0)[0] - tl - wsb, line("w0")) + endfor + + tabnew | tabonly! | %bwipeout! + iunmap c + set scrolloff& + set splitbelow& + set laststatus& + set equalalways& + set splitkeep& + " let &t_WS = save_WS +endfunc + +function Test_splitkeep_cmdwin_cursor_position() + set splitkeep=screen + call setline(1, range(&lines)) + + " No scroll when cursor is at near bottom of window and cusor position + " recompution (done by line('w0') in this test) happens while in cmdwin. + normal! G + let firstline = line('w0') + autocmd CmdwinEnter * ++once autocmd WinEnter * ++once call line('w0') + execute "normal! q:\<C-w>q" + redraw! + call assert_equal(firstline, line('w0')) + + " User script can change cursor position successfully while in cmdwin and it + " shouldn't be changed when closing cmdwin. + execute "normal! Gq:\<Cmd>call win_execute(winnr('#')->win_getid(), 'call cursor(1, 1)')\<CR>\<C-w>q" + call assert_equal(1, line('.')) + call assert_equal(1, col('.')) + + execute "normal! Gq:\<Cmd>autocmd WinEnter * ++once call cursor(1, 1)\<CR>\<C-w>q" + call assert_equal(1, line('.')) + call assert_equal(1, col('.')) + + %bwipeout! + set splitkeep& +endfunction + +function Test_splitkeep_misc() + set splitkeep=screen + set splitbelow + + call setline(1, range(1, &lines)) + norm Gzz + let top = line('w0') + " No scroll when aucmd_win is opened + call setbufvar(bufnr("test", 1) , '&buftype', 'nofile') + call assert_equal(top, line('w0')) + " No scroll when tab is changed/closed + tab help | close + call assert_equal(top, line('w0')) + " No scroll when help is closed and buffer line count < window height + norm ggdG + call setline(1, range(1, &lines - 10)) + norm G + let top = line('w0') + help | quit + call assert_equal(top, line('w0')) + " No error when resizing window in autocmd and buffer length changed + autocmd FileType qf exe "resize" line('$') + cexpr getline(1, '$') + copen + wincmd p + norm dd + cexpr getline(1, '$') + + %bwipeout! + set splitbelow& + set splitkeep& +endfunc + +function Test_splitkeep_callback() + CheckScreendump + let lines =<< trim END + set splitkeep=screen + call setline(1, range(&lines)) + function C1(a, b) + split | wincmd p + endfunction + function C2(a, b) + close | split + endfunction + nn j <cmd>call job_start([&sh, &shcf, "true"], { 'exit_cb': 'C1' })<CR> + nn t <cmd>call popup_create(term_start([&sh, &shcf, "true"], + \ { 'hidden': 1, 'exit_cb': 'C2' }), {})<CR> + END + call writefile(lines, 'XTestSplitkeepCallback', 'D') + let buf = RunVimInTerminal('-S XTestSplitkeepCallback', #{rows: 8}) + + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_splitkeep_callback_1', {}) + + call term_sendkeys(buf, ":quit\<CR>Ht") + call VerifyScreenDump(buf, 'Test_splitkeep_callback_2', {}) + + call term_sendkeys(buf, ":set sb\<CR>:quit\<CR>Gj") + call VerifyScreenDump(buf, 'Test_splitkeep_callback_3', {}) + + call term_sendkeys(buf, ":quit\<CR>Gt") + call VerifyScreenDump(buf, 'Test_splitkeep_callback_4', {}) + + call StopVimInTerminal(buf) +endfunc + +function Test_splitkeep_fold() + CheckScreendump + + let lines =<< trim END + set splitkeep=screen + set foldmethod=marker + set number + let line = 1 + for n in range(1, &lines) + call setline(line, ['int FuncName() {/*{{{*/', 1, 2, 3, 4, 5, '}/*}}}*/', + \ 'after fold']) + let line += 8 + endfor + END + call writefile(lines, 'XTestSplitkeepFold', 'D') + let buf = RunVimInTerminal('-S XTestSplitkeepFold', #{rows: 10}) + + call term_sendkeys(buf, "L:wincmd s\<CR>") + call VerifyScreenDump(buf, 'Test_splitkeep_fold_1', {}) + + call term_sendkeys(buf, ":quit\<CR>") + call VerifyScreenDump(buf, 'Test_splitkeep_fold_2', {}) + + call term_sendkeys(buf, "H:below split\<CR>") + call VerifyScreenDump(buf, 'Test_splitkeep_fold_3', {}) + + call term_sendkeys(buf, ":wincmd k\<CR>:quit\<CR>") + call VerifyScreenDump(buf, 'Test_splitkeep_fold_4', {}) + + call StopVimInTerminal(buf) +endfunction + +function Test_splitkeep_status() + CheckScreendump + + let lines =<< trim END + call setline(1, ['a', 'b', 'c']) + set nomodified + set splitkeep=screen + let win = winnr() + wincmd s + wincmd j + END + call writefile(lines, 'XTestSplitkeepStatus', 'D') + let buf = RunVimInTerminal('-S XTestSplitkeepStatus', #{rows: 10}) + + call term_sendkeys(buf, ":call win_move_statusline(win, 1)\<CR>") + call VerifyScreenDump(buf, 'Test_splitkeep_status_1', {}) + + call StopVimInTerminal(buf) +endfunction + +function Test_new_help_window_on_error() + help change.txt + execute "normal! /CTRL-@\<CR>" + silent! execute "normal! \<C-W>]" + + let wincount = winnr('$') + help 'mod' + + call assert_equal(wincount, winnr('$')) + call assert_equal(expand("<cword>"), "'mod'") +endfunction + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_window_id.vim b/src/nvim/testdir/test_window_id.vim index 8bf4ede350..396a49b55f 100644 --- a/src/nvim/testdir/test_window_id.vim +++ b/src/nvim/testdir/test_window_id.vim @@ -1,5 +1,7 @@ " Test using the window ID. +source check.vim + func Test_win_getid() edit one let id1 = win_getid() @@ -90,10 +92,16 @@ func Test_win_getid() split call assert_equal(sort([id5, win_getid()]), sort(win_findbuf(bufnr5))) + call assert_fails('let w = win_getid([])', 'E745:') + call assert_equal(0, win_getid(-1)) + call assert_equal(-1, win_getid(1, -1)) + only! endfunc func Test_win_getid_curtab() + CheckFeature quickfix + tabedit X tabfirst copen @@ -127,4 +135,8 @@ func Test_winlayout() let w2 = win_getid() call assert_equal(['leaf', w2], 2->winlayout()) tabclose + + call assert_equal([], winlayout(-1)) endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim index a8735bcaf1..6019cee193 100644 --- a/src/nvim/testdir/test_writefile.vim +++ b/src/nvim/testdir/test_writefile.vim @@ -57,7 +57,29 @@ func Test_writefile_fails_conversion() call assert_fails('write ++enc=cp932', 'E513:') call assert_equal(contents, readfile('Xfile')) + " With 'backupcopy' set, if there is a conversion error, the backup file is + " still created. + set backupcopy=yes writebackup& backup& + call delete('Xfile' .. &backupext) + call assert_fails('write ++enc=cp932', 'E513:') + call assert_equal(contents, readfile('Xfile')) + call assert_equal(contents, readfile('Xfile' .. &backupext)) + set backupcopy& + %bw! + + " Conversion error during write + new + call setline(1, ["\U10000000"]) + let output = execute('write! ++enc=utf-16 Xfile') + call assert_match('CONVERSION ERROR', output) + let output = execute('write! ++enc=ucs-2 Xfile') + call assert_match('CONVERSION ERROR', output) + call delete('Xfilz~') + call delete('Xfily~') + %bw! + call delete('Xfile') + call delete('Xfile' .. &backupext) bwipe! set backup& writebackup& backupdir&vim backupskip&vim endfunc @@ -136,9 +158,7 @@ func Test_writefile_sync_arg() endfunc func Test_writefile_sync_dev_stdout() - if !has('unix') - return - endif + CheckUnix if filewritable('/dev/stdout') " Just check that this doesn't cause an error. call writefile(['one'], '/dev/stdout', 's') @@ -216,6 +236,12 @@ func Test_saveas() syntax off %bw! call delete('Xsaveas.pl') + + " :saveas fails for "nofile" buffer + set buftype=nofile + call assert_fails('saveas Xsafile', 'E676: No matching autocommands for buftype=nofile buffer') + + bwipe! endfunc func Test_write_errors() @@ -254,6 +280,225 @@ func Test_write_errors() close call delete('Xfile') + + " Nvim treats NULL list/blob more like empty list/blob + " call writefile(v:_null_list, 'Xfile') + " call assert_false(filereadable('Xfile')) + " call writefile(v:_null_blob, 'Xfile') + " call assert_false(filereadable('Xfile')) + call assert_fails('call writefile([], "")', 'E482:') + + " very long file name + let long_fname = repeat('n', 5000) + call assert_fails('exe "w " .. long_fname', 'E75:') + call assert_fails('call writefile([], long_fname)', 'E482:') + + " Test for writing to a block device on Unix-like systems + if has('unix') && getfperm('/dev/loop0') != '' + \ && getftype('/dev/loop0') == 'bdev' && !IsRoot() + new + edit /dev/loop0 + call assert_fails('write', 'E503: ') + call assert_fails('write!', 'E503: ') + close! + endif +endfunc + +" Test for writing to a file which is modified after Vim read it +func Test_write_file_mtime() + CheckEnglish + CheckRunVimInTerminal + + " First read the file into a buffer + call writefile(["Line1", "Line2"], 'Xfile') + let old_ftime = getftime('Xfile') + let buf = RunVimInTerminal('Xfile', #{rows : 10}) + call term_wait(buf) + call term_sendkeys(buf, ":set noswapfile\<CR>") + call term_wait(buf) + + " Modify the file directly. Make sure the file modification time is + " different. Note that on Linux/Unix, the file is considered modified + " outside, only if the difference is 2 seconds or more + sleep 1 + call writefile(["Line3", "Line4"], 'Xfile') + let new_ftime = getftime('Xfile') + while new_ftime - old_ftime < 2 + sleep 100m + call writefile(["Line3", "Line4"], 'Xfile') + let new_ftime = getftime('Xfile') + endwhile + + " Try to overwrite the file and check for the prompt + call term_sendkeys(buf, ":w\<CR>") + call term_wait(buf) + call WaitForAssert({-> assert_equal("WARNING: The file has been changed since reading it!!!", term_getline(buf, 9))}) + call assert_equal("Do you really want to write to it (y/n)?", + \ term_getline(buf, 10)) + call term_sendkeys(buf, "n\<CR>") + call term_wait(buf) + call assert_equal(new_ftime, getftime('Xfile')) + call term_sendkeys(buf, ":w\<CR>") + call term_wait(buf) + call term_sendkeys(buf, "y\<CR>") + call term_wait(buf) + call WaitForAssert({-> assert_equal('Line2', readfile('Xfile')[1])}) + + " clean up + call StopVimInTerminal(buf) + call delete('Xfile') +endfunc + +" Test for an autocmd unloading a buffer during a write command +func Test_write_autocmd_unloadbuf_lockmark() + augroup WriteTest + autocmd BufWritePre Xfile enew | write + augroup END + e Xfile + call assert_fails('lockmarks write', ['E32', 'E203:']) + augroup WriteTest + au! + augroup END + augroup! WriteTest +endfunc + +" Test for writing a buffer with 'acwrite' but without autocmds +func Test_write_acwrite_error() + new Xfile + call setline(1, ['line1', 'line2', 'line3']) + set buftype=acwrite + call assert_fails('write', 'E676:') + call assert_fails('1,2write!', 'E676:') + call assert_fails('w >>', 'E676:') + close! +endfunc + +" Test for adding and removing lines from an autocmd when writing a buffer +func Test_write_autocmd_add_remove_lines() + new Xfile + call setline(1, ['aaa', 'bbb', 'ccc', 'ddd']) + + " Autocmd deleting lines from the file when writing a partial file + augroup WriteTest2 + au! + autocmd FileWritePre Xfile 1,2d + augroup END + call assert_fails('2,3w!', 'E204:') + + " Autocmd adding lines to a file when writing a partial file + augroup WriteTest2 + au! + autocmd FileWritePre Xfile call append(0, ['xxx', 'yyy']) + augroup END + %d + call setline(1, ['aaa', 'bbb', 'ccc', 'ddd']) + 1,2w! + call assert_equal(['xxx', 'yyy', 'aaa', 'bbb'], readfile('Xfile')) + + " Autocmd deleting lines from the file when writing the whole file + augroup WriteTest2 + au! + autocmd BufWritePre Xfile 1,2d + augroup END + %d + call setline(1, ['aaa', 'bbb', 'ccc', 'ddd']) + w + call assert_equal(['ccc', 'ddd'], readfile('Xfile')) + + augroup WriteTest2 + au! + augroup END + augroup! WriteTest2 + + close! + call delete('Xfile') +endfunc + +" Test for writing to a readonly file +func Test_write_readonly() + call writefile([], 'Xfile') + call setfperm('Xfile', "r--------") + edit Xfile + set noreadonly backupskip= + call assert_fails('write', 'E505:') + let save_cpo = &cpo + set cpo+=W + call assert_fails('write!', 'E504:') + let &cpo = save_cpo + call setline(1, ['line1']) + write! + call assert_equal(['line1'], readfile('Xfile')) + + " Auto-saving a readonly file should fail with 'autowriteall' + %bw! + e Xfile + set noreadonly autowriteall + call setline(1, ['aaaa']) + call assert_fails('n', 'E505:') + set cpo+=W + call assert_fails('n', 'E504:') + set cpo-=W + set autowriteall& + + set backupskip& + call delete('Xfile') + %bw! +endfunc + +" Test for 'patchmode' +func Test_patchmode() + call writefile(['one'], 'Xfile') + set patchmode=.orig nobackup backupskip= writebackup + new Xfile + call setline(1, 'two') + " first write should create the .orig file + write + call assert_equal(['one'], readfile('Xfile.orig')) + call setline(1, 'three') + " subsequent writes should not create/modify the .orig file + write + call assert_equal(['one'], readfile('Xfile.orig')) + + " use 'patchmode' with 'nobackup' and 'nowritebackup' to create an empty + " original file + call delete('Xfile') + call delete('Xfile.orig') + %bw! + set patchmode=.orig nobackup nowritebackup + edit Xfile + call setline(1, ['xxx']) + write + call assert_equal(['xxx'], readfile('Xfile')) + call assert_equal([], readfile('Xfile.orig')) + + set patchmode& backup& backupskip& writebackup& + call delete('Xfile') + call delete('Xfile.orig') +endfunc + +" Test for writing to a file in a readonly directory +" NOTE: if you run tests as root this will fail. Don't run tests as root! +func Test_write_readonly_dir() + " On MS-Windows, modifying files in a read-only directory is allowed. + CheckUnix + " Root can do it too. + CheckNotRoot + + call mkdir('Xdir/') + call writefile(['one'], 'Xdir/Xfile1') + call setfperm('Xdir', 'r-xr--r--') + " try to create a new file in the directory + new Xdir/Xfile2 + call setline(1, 'two') + call assert_fails('write', 'E212:') + " try to create a backup file in the directory + edit! Xdir/Xfile1 + set backupdir=./Xdir backupskip= + set patchmode=.orig + call assert_fails('write', 'E509:') + call setfperm('Xdir', 'rwxr--r--') + call delete('Xdir', 'rf') + set backupdir& backupskip& patchmode& endfunc " Test for writing a file using invalid file encoding @@ -266,15 +511,15 @@ endfunc " Tests for reading and writing files with conversion for Win32. func Test_write_file_encoding() - throw 'skipped: Nvim does not support :w ++enc=cp1251' + throw 'Skipped: Nvim does not support encoding=latin1' CheckMSWindows let save_encoding = &encoding let save_fileencodings = &fileencodings - set encoding& fileencodings& + set encoding=latin1 fileencodings& let text =<< trim END - 1 utf-8 text: ÐÐ»Ñ Vim version 6.2. ÐоÑледнее изменение: 1970 Jan 01 - 2 cp1251 text: Äëÿ Vim version 6.2. Ïîñëåäíåå èçìåíåíèå: 1970 Jan 01 - 3 cp866 text: «ï Vim version 6.2. ®á«¥¤¥¥ ¨§¬¥¥¨¥: 1970 Jan 01 + 1 utf-8 text: Для Vim version 6.2. Последнее изменение: 1970 Jan 01 + 2 cp1251 text: Vim version 6.2. : 1970 Jan 01 + 3 cp866 text: Vim version 6.2. : 1970 Jan 01 END call writefile(text, 'Xfile') edit Xfile @@ -289,9 +534,9 @@ func Test_write_file_encoding() .w ++enc=cp866 >> Xtest .w! ++enc=utf-8 Xutf8 let expected =<< trim END - 1 utf-8 text: ÐÐ»Ñ Vim version 6.2. ÐоÑледнее изменение: 1970 Jan 01 - 1 utf-8 text: Äëÿ Vim version 6.2. Ïîñëåäíåå èçìåíåíèå: 1970 Jan 01 - 1 utf-8 text: «ï Vim version 6.2. ®á«¥¤¥¥ ¨§¬¥¥¨¥: 1970 Jan 01 + 1 utf-8 text: Для Vim version 6.2. Последнее изменение: 1970 Jan 01 + 1 utf-8 text: Vim version 6.2. : 1970 Jan 01 + 1 utf-8 text: Vim version 6.2. : 1970 Jan 01 END call assert_equal(expected, readfile('Xtest')) @@ -302,9 +547,9 @@ func Test_write_file_encoding() .w ++enc=cp866 >> Xtest .w! ++enc=cp1251 Xcp1251 let expected =<< trim END - 2 cp1251 text: ÐÐ»Ñ Vim version 6.2. ÐоÑледнее изменение: 1970 Jan 01 - 2 cp1251 text: Äëÿ Vim version 6.2. Ïîñëåäíåå èçìåíåíèå: 1970 Jan 01 - 2 cp1251 text: «ï Vim version 6.2. ®á«¥¤¥¥ ¨§¬¥¥¨¥: 1970 Jan 01 + 2 cp1251 text: Для Vim version 6.2. Последнее изменение: 1970 Jan 01 + 2 cp1251 text: Vim version 6.2. : 1970 Jan 01 + 2 cp1251 text: Vim version 6.2. : 1970 Jan 01 END call assert_equal(expected, readfile('Xtest')) @@ -315,9 +560,9 @@ func Test_write_file_encoding() .w ++enc=cp866 >> Xtest .w! ++enc=cp866 Xcp866 let expected =<< trim END - 3 cp866 text: ÐÐ»Ñ Vim version 6.2. ÐоÑледнее изменение: 1970 Jan 01 - 3 cp866 text: Äëÿ Vim version 6.2. Ïîñëåäíåå èçìåíåíèå: 1970 Jan 01 - 3 cp866 text: «ï Vim version 6.2. ®á«¥¤¥¥ ¨§¬¥¥¨¥: 1970 Jan 01 + 3 cp866 text: Для Vim version 6.2. Последнее изменение: 1970 Jan 01 + 3 cp866 text: Vim version 6.2. : 1970 Jan 01 + 3 cp866 text: Vim version 6.2. : 1970 Jan 01 END call assert_equal(expected, readfile('Xtest')) @@ -331,9 +576,9 @@ func Test_write_file_encoding() e Xcp866 .w ++enc=utf-8 >> Xtest let expected =<< trim END - 1 utf-8 text: ÐÐ»Ñ Vim version 6.2. ÐоÑледнее изменение: 1970 Jan 01 - 2 cp1251 text: ÐÐ»Ñ Vim version 6.2. ÐоÑледнее изменение: 1970 Jan 01 - 3 cp866 text: ÐÐ»Ñ Vim version 6.2. ÐоÑледнее изменение: 1970 Jan 01 + 1 utf-8 text: Для Vim version 6.2. Последнее изменение: 1970 Jan 01 + 2 cp1251 text: Для Vim version 6.2. Последнее изменение: 1970 Jan 01 + 3 cp866 text: Для Vim version 6.2. Последнее изменение: 1970 Jan 01 END call assert_equal(expected, readfile('Xtest')) @@ -347,9 +592,9 @@ func Test_write_file_encoding() e Xcp866 .w ++enc=cp1251 >> Xtest let expected =<< trim END - 1 utf-8 text: Äëÿ Vim version 6.2. Ïîñëåäíåå èçìåíåíèå: 1970 Jan 01 - 2 cp1251 text: Äëÿ Vim version 6.2. Ïîñëåäíåå èçìåíåíèå: 1970 Jan 01 - 3 cp866 text: Äëÿ Vim version 6.2. Ïîñëåäíåå èçìåíåíèå: 1970 Jan 01 + 1 utf-8 text: Vim version 6.2. : 1970 Jan 01 + 2 cp1251 text: Vim version 6.2. : 1970 Jan 01 + 3 cp866 text: Vim version 6.2. : 1970 Jan 01 END call assert_equal(expected, readfile('Xtest')) @@ -363,9 +608,9 @@ func Test_write_file_encoding() e Xcp866 .w ++enc=cp866 >> Xtest let expected =<< trim END - 1 utf-8 text: «ï Vim version 6.2. ®á«¥¤¥¥ ¨§¬¥¥¨¥: 1970 Jan 01 - 2 cp1251 text: «ï Vim version 6.2. ®á«¥¤¥¥ ¨§¬¥¥¨¥: 1970 Jan 01 - 3 cp866 text: «ï Vim version 6.2. ®á«¥¤¥¥ ¨§¬¥¥¨¥: 1970 Jan 01 + 1 utf-8 text: Vim version 6.2. : 1970 Jan 01 + 2 cp1251 text: Vim version 6.2. : 1970 Jan 01 + 3 cp866 text: Vim version 6.2. : 1970 Jan 01 END call assert_equal(expected, readfile('Xtest')) @@ -523,10 +768,177 @@ func Test_read_write_bin() call assert_equal(0z6E6F656F6C0A, readfile('XNoEolSetEol', 'B')) call delete('XNoEolSetEol') - set ff& + set ff& fixeol& bwipe! XNoEolSetEol endfunc +" Test for the 'backupcopy' option when writing files +func Test_backupcopy() + CheckUnix + set backupskip= + " With the default 'backupcopy' setting, saving a symbolic link file + " should not break the link. + set backupcopy& + call writefile(['1111'], 'Xfile1') + silent !ln -s Xfile1 Xfile2 + new Xfile2 + call setline(1, ['2222']) + write + close + call assert_equal(['2222'], readfile('Xfile1')) + call assert_equal('Xfile1', resolve('Xfile2')) + call assert_equal('link', getftype('Xfile2')) + call delete('Xfile1') + call delete('Xfile2') + + " With the 'backupcopy' set to 'breaksymlink', saving a symbolic link file + " should break the link. + set backupcopy=yes,breaksymlink + call writefile(['1111'], 'Xfile1') + silent !ln -s Xfile1 Xfile2 + new Xfile2 + call setline(1, ['2222']) + write + close + call assert_equal(['1111'], readfile('Xfile1')) + call assert_equal(['2222'], readfile('Xfile2')) + call assert_equal('Xfile2', resolve('Xfile2')) + call assert_equal('file', getftype('Xfile2')) + call delete('Xfile1') + call delete('Xfile2') + set backupcopy& + + " With the default 'backupcopy' setting, saving a hard link file + " should not break the link. + set backupcopy& + call writefile(['1111'], 'Xfile1') + silent !ln Xfile1 Xfile2 + new Xfile2 + call setline(1, ['2222']) + write + close + call assert_equal(['2222'], readfile('Xfile1')) + call delete('Xfile1') + call delete('Xfile2') + + " With the 'backupcopy' set to 'breaksymlink', saving a hard link file + " should break the link. + set backupcopy=yes,breakhardlink + call writefile(['1111'], 'Xfile1') + silent !ln Xfile1 Xfile2 + new Xfile2 + call setline(1, ['2222']) + write + call assert_equal(['1111'], readfile('Xfile1')) + call assert_equal(['2222'], readfile('Xfile2')) + call delete('Xfile1') + call delete('Xfile2') + + " If a backup file is already present, then a slightly modified filename + " should be used as the backup file. Try with 'backupcopy' set to 'yes' and + " 'no'. + %bw + call writefile(['aaaa'], 'Xfile') + call writefile(['bbbb'], 'Xfile.bak') + set backupcopy=yes backupext=.bak + new Xfile + call setline(1, ['cccc']) + write + close + call assert_equal(['cccc'], readfile('Xfile')) + call assert_equal(['bbbb'], readfile('Xfile.bak')) + set backupcopy=no backupext=.bak + new Xfile + call setline(1, ['dddd']) + write + close + call assert_equal(['dddd'], readfile('Xfile')) + call assert_equal(['bbbb'], readfile('Xfile.bak')) + call delete('Xfile') + call delete('Xfile.bak') + + " Write to a device file (in Unix-like systems) which cannot be backed up. + if has('unix') + set writebackup backupcopy=yes nobackup + new + call setline(1, ['aaaa']) + let output = execute('write! /dev/null') + call assert_match('"/dev/null" \[Device]', output) + close + set writebackup backupcopy=no nobackup + new + call setline(1, ['aaaa']) + let output = execute('write! /dev/null') + call assert_match('"/dev/null" \[Device]', output) + close + set backup writebackup& backupcopy& + new + call setline(1, ['aaaa']) + let output = execute('write! /dev/null') + call assert_match('"/dev/null" \[Device]', output) + close + endif + + set backupcopy& backupskip& backupext& backup& +endfunc + +" Test for writing a file with 'encoding' set to 'utf-16' +func Test_write_utf16() + new + call setline(1, ["\U00010001"]) + write ++enc=utf-16 Xfile + bw! + call assert_equal(0zD800DC01, readfile('Xfile', 'B')[0:3]) + call delete('Xfile') +endfunc + +" Test for trying to save a backup file when the backup file is a symbolic +" link to the original file. The backup file should not be modified. +func Test_write_backup_symlink() + CheckUnix + call mkdir('Xbackup') + let save_backupdir = &backupdir + set backupdir=.,./Xbackup + call writefile(['1111'], 'Xfile') + silent !ln -s Xfile Xfile.bak + + new Xfile + set backup backupcopy=yes backupext=.bak + write + call assert_equal('link', getftype('Xfile.bak')) + call assert_equal('Xfile', resolve('Xfile.bak')) + " backup file should be created in the 'backup' directory + if !has('bsd') + " This check fails on FreeBSD + call assert_true(filereadable('./Xbackup/Xfile.bak')) + endif + set backup& backupcopy& backupext& + %bw + + call delete('Xfile') + call delete('Xfile.bak') + call delete('Xbackup', 'rf') + let &backupdir = save_backupdir +endfunc + +" Test for ':write ++bin' and ':write ++nobin' +func Test_write_binary_file() + " create a file without an eol/eof character + call writefile(0z616161, 'Xfile1', 'b') + new Xfile1 + write ++bin Xfile2 + write ++nobin Xfile3 + call assert_equal(0z616161, readblob('Xfile2')) + if has('win32') + call assert_equal(0z6161610D.0A, readblob('Xfile3')) + else + call assert_equal(0z6161610A, readblob('Xfile3')) + endif + call delete('Xfile1') + call delete('Xfile2') + call delete('Xfile3') +endfunc + " Check that buffer is written before triggering QuitPre func Test_wq_quitpre_autocommand() edit Xsomefile diff --git a/src/nvim/testdir/view_util.vim b/src/nvim/testdir/view_util.vim index 1cdce21602..a4d0e56af9 100644 --- a/src/nvim/testdir/view_util.vim +++ b/src/nvim/testdir/view_util.vim @@ -19,7 +19,7 @@ endfunc " Get text on the screen, including composing characters. " ScreenLines(lnum, width) or " ScreenLines([start, end], width) -function! ScreenLines(lnum, width) abort +func ScreenLines(lnum, width) abort redraw! if type(a:lnum) == v:t_list let start = a:lnum[0] @@ -33,9 +33,9 @@ function! ScreenLines(lnum, width) abort let lines += [join(map(range(1, a:width), 'screenstring(l, v:val)'), '')] endfor return lines -endfunction +endfunc -function! ScreenAttrs(lnum, width) abort +func ScreenAttrs(lnum, width) abort redraw! if type(a:lnum) == v:t_list let start = a:lnum[0] @@ -49,16 +49,16 @@ function! ScreenAttrs(lnum, width) abort let attrs += [map(range(1, a:width), 'screenattr(l, v:val)')] endfor return attrs -endfunction +endfunc -function! NewWindow(height, width) abort +func NewWindow(height, width) abort exe a:height . 'new' exe a:width . 'vsp' set winfixwidth winfixheight redraw! -endfunction +endfunc -function! CloseWindow() abort +func CloseWindow() abort bw! redraw! -endfunction +endfunc diff --git a/src/nvim/testdir/vim9.vim b/src/nvim/testdir/vim9.vim new file mode 100644 index 0000000000..3c0ff2b2dd --- /dev/null +++ b/src/nvim/testdir/vim9.vim @@ -0,0 +1,112 @@ + +" Use a different file name for each run. +let s:sequence = 1 + +func CheckScriptFailure(lines, error, lnum = -3) + if get(a:lines, 0, '') ==# 'vim9script' + return + endif + let cwd = getcwd() + let fname = 'XScriptFailure' .. s:sequence + let s:sequence += 1 + call writefile(a:lines, fname) + try + call assert_fails('so ' .. fname, a:error, a:lines, a:lnum) + finally + call chdir(cwd) + call delete(fname) + endtry +endfunc + +func CheckScriptSuccess(lines) + if get(a:lines, 0, '') ==# 'vim9script' + return + endif + let cwd = getcwd() + let fname = 'XScriptSuccess' .. s:sequence + let s:sequence += 1 + call writefile(a:lines, fname) + try + exe 'so ' .. fname + finally + call chdir(cwd) + call delete(fname) + endtry +endfunc + +" Check that "lines" inside a legacy function has no error. +func CheckLegacySuccess(lines) + let cwd = getcwd() + let fname = 'XlegacySuccess' .. s:sequence + let s:sequence += 1 + call writefile(['func Func()'] + a:lines + ['endfunc'], fname) + try + exe 'so ' .. fname + call Func() + finally + delfunc! Func + call chdir(cwd) + call delete(fname) + endtry +endfunc + +" Check that "lines" inside a legacy function results in the expected error +func CheckLegacyFailure(lines, error) + let cwd = getcwd() + let fname = 'XlegacyFails' .. s:sequence + let s:sequence += 1 + call writefile(['func Func()'] + a:lines + ['endfunc', 'call Func()'], fname) + try + call assert_fails('so ' .. fname, a:error) + finally + delfunc! Func + call chdir(cwd) + call delete(fname) + endtry +endfunc + +" Execute "lines" in a legacy function, translated as in +" CheckLegacyAndVim9Success() +func CheckTransLegacySuccess(lines) + let legacylines = a:lines->deepcopy()->map({_, v -> + \ v->substitute('\<VAR\>', 'let', 'g') + \ ->substitute('\<LET\>', 'let', 'g') + \ ->substitute('\<LSTART\>', '{', 'g') + \ ->substitute('\<LMIDDLE\>', '->', 'g') + \ ->substitute('\<LEND\>', '}', 'g') + \ ->substitute('\<TRUE\>', '1', 'g') + \ ->substitute('\<FALSE\>', '0', 'g') + \ ->substitute('#"', ' "', 'g') + \ }) + call CheckLegacySuccess(legacylines) +endfunc + +" Execute "lines" in a legacy function +" Use 'VAR' for a declaration. +" Use 'LET' for an assignment +" Use ' #"' for a comment +" Use LSTART arg LMIDDLE expr LEND for lambda +" Use 'TRUE' for 1 +" Use 'FALSE' for 0 +func CheckLegacyAndVim9Success(lines) + call CheckTransLegacySuccess(a:lines) +endfunc + +" Execute "lines" in a legacy function +" Use 'VAR' for a declaration. +" Use 'LET' for an assignment +" Use ' #"' for a comment +func CheckLegacyAndVim9Failure(lines, error) + if type(a:error) == type('string') + let legacyError = error + else + let legacyError = error[0] + endif + + let legacylines = a:lines->deepcopy()->map({_, v -> + \ v->substitute('\<VAR\>', 'let', 'g') + \ ->substitute('\<LET\>', 'let', 'g') + \ ->substitute('#"', ' "', 'g') + \ }) + call CheckLegacyFailure(legacylines, legacyError) +endfunc |