From b90ac5e96e40706ffb039db64b92fcd16fa78c04 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 16 Jun 2021 10:59:36 +0200 Subject: [PATCH 01/28] patch 8.2.3009: startup test may hang Problem: Startup test may hang. Solution: Do not run the test in the GUI. --- src/testdir/test_startup.vim | 3 ++- src/version.c | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_startup.vim b/src/testdir/test_startup.vim index 7e98591ba2..a6e52ce573 100644 --- a/src/testdir/test_startup.vim +++ b/src/testdir/test_startup.vim @@ -1302,6 +1302,8 @@ func Test_write_in_vimrc() endfunc func Test_echo_true_in_cmd() + CheckNotGui + let lines =<< trim END echo v:true call writefile(['done'], 'Xresult') @@ -1313,7 +1315,6 @@ func Test_echo_true_in_cmd() endif call delete('Xscript') call delete('Xresult') - endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index f3b34037dd..1c680f2f06 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3009, /**/ 3008, /**/ From 41a7f82dea525b3398bf372cbb9c268455845800 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Wed, 16 Jun 2021 15:53:17 +0200 Subject: [PATCH 02/28] patch 8.2.3010: not enough testing for viminfo code Problem: Not enough testing for viminfo code. Solution: Add a few more tests. (Yegappan Lakshmanan, closes #8390) --- src/register.c | 3 + src/testdir/test_fileformat.vim | 16 ++ src/testdir/test_smartindent.vim | 23 +++ src/testdir/test_viminfo.vim | 318 ++++++++++++++++++++++++++++++- src/version.c | 2 + 5 files changed, 360 insertions(+), 2 deletions(-) diff --git a/src/register.c b/src/register.c index c30787afef..4774e2a995 100644 --- a/src/register.c +++ b/src/register.c @@ -462,6 +462,9 @@ stuff_yank(int regname, char_u *p) return OK; } +/* + * Last executed register (@ command) + */ static int execreg_lastc = NUL; int diff --git a/src/testdir/test_fileformat.vim b/src/testdir/test_fileformat.vim index 07819dc2e6..6ba6e49ada 100644 --- a/src/testdir/test_fileformat.vim +++ b/src/testdir/test_fileformat.vim @@ -1,5 +1,7 @@ " Test for 'fileformat' +source shared.vim + " Test behavior of fileformat after bwipeout of last buffer func Test_fileformat_after_bw() bwipeout @@ -308,4 +310,18 @@ func Test_fileformat_plusplus_read() call assert_fails('e ++abc1 Xfile1', 'E474:') endfunc +" When Vim starts up with an empty buffer the first item in 'fileformats' is +" used as the 'fileformat'. +func Test_fileformat_on_startup() + let after =<< trim END + call writefile([&fileformat], 'Xfile', 'a') + quit + END + call RunVim(["set ffs=dos,unix,mac"], after, '') + call RunVim(["set ffs=mac,dos,unix"], after, '') + call RunVim(["set ffs=unix,mac,dos"], after, '') + call assert_equal(['dos', 'mac', 'unix'], readfile('Xfile')) + call delete('Xfile') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_smartindent.vim b/src/testdir/test_smartindent.vim index 47ded64d0f..7b58af2491 100644 --- a/src/testdir/test_smartindent.vim +++ b/src/testdir/test_smartindent.vim @@ -111,4 +111,27 @@ func Test_si_comment_line_continuation() close! endfunc +" When 'paste' is set, 'smartindent' should not take effect. +func Test_si_with_paste() + new + setlocal smartindent autoindent + set paste + " insert text that will trigger smartindent + exe "norm! i {\nif (x)\ni = 1;\n#define FOO 1\nj = 2;\n}" + exe "norm! Ok = 3;" + exe "norm! 4G>>" + call assert_equal([' {', 'if (x)', 'i = 1;', '#define FOO 1', + \ 'j = 2;', 'k = 3;', '}'], getline(1, '$')) + call assert_true(&smartindent) + set nopaste + %d _ + exe "norm! i {\nif (x)\ni = 1;\n#define FOO 1\nj = 2;\n}" + exe "norm! Ok = 3;" + exe "norm! 4G>>" + call assert_equal([' {', "\t if (x)", "\t\t i = 1;", + \ '#define FOO 1', "\t\t j = 2;", "\t k = 3;", ' }'], + \ getline(1, '$')) + bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_viminfo.vim b/src/testdir/test_viminfo.vim index ef74e09dbd..e70fed898a 100644 --- a/src/testdir/test_viminfo.vim +++ b/src/testdir/test_viminfo.vim @@ -4,7 +4,7 @@ source check.vim source term_util.vim source shared.vim -function Test_viminfo_read_and_write() +func Test_viminfo_read_and_write() " First clear 'history', so that "hislen" is zero. Then set it again, " simulating Vim starting up. set history=0 @@ -12,6 +12,7 @@ function Test_viminfo_read_and_write() set history=1000 call histdel(':') + let @/='' let lines = [ \ '# comment line', \ '*encoding=utf-8', @@ -89,6 +90,36 @@ func Test_global_vars() call assert_equal(test_null, g:MY_GLOBAL_NULL) call assert_equal(test_none, g:MY_GLOBAL_NONE) + " When reading global variables from viminfo, if a variable cannot be + " modified, then the value should not be changed. + unlet g:MY_GLOBAL_STRING + unlet g:MY_GLOBAL_NUM + unlet g:MY_GLOBAL_FLOAT + unlet g:MY_GLOBAL_DICT + unlet g:MY_GLOBAL_LIST + unlet g:MY_GLOBAL_BLOB + + const g:MY_GLOBAL_STRING = 'New Value' + const g:MY_GLOBAL_NUM = 987 + const g:MY_GLOBAL_FLOAT = 1.16 + const g:MY_GLOBAL_DICT = {'editor': 'vim'} + const g:MY_GLOBAL_LIST = [5, 7, 13] + const g:MY_GLOBAL_BLOB = 0zDEADBEEF + call assert_fails('rv! Xviminfo', 'E741:') + call assert_equal('New Value', g:MY_GLOBAL_STRING) + call assert_equal(987, g:MY_GLOBAL_NUM) + call assert_equal(1.16, g:MY_GLOBAL_FLOAT) + call assert_equal({'editor': 'vim'}, g:MY_GLOBAL_DICT) + call assert_equal([5, 7 , 13], g:MY_GLOBAL_LIST) + call assert_equal(0zDEADBEEF, g:MY_GLOBAL_BLOB) + + unlet g:MY_GLOBAL_STRING + unlet g:MY_GLOBAL_NUM + unlet g:MY_GLOBAL_FLOAT + unlet g:MY_GLOBAL_DICT + unlet g:MY_GLOBAL_LIST + unlet g:MY_GLOBAL_BLOB + " Test for invalid values for a blob, list, dict in a viminfo file call writefile([ \ "!GLOB_BLOB_1\tBLO\t123", @@ -510,6 +541,20 @@ func Test_viminfo_bad_syntax() call delete('Xviminfo') endfunc +func Test_viminfo_bad_register_syntax() + let lines = [] + call add(lines, '|1,4') + call add(lines, '|3') " invalid number of fields for a register type + call add(lines, '|3,1,1,1,1,,1,"x"') " invalid value for the width field + call add(lines, '|3,0,80,1,1,1,1,"x"') " invalid register number + call add(lines, '|3,0,10,5,1,1,1,"x"') " invalid register type + call add(lines, '|3,0,10,1,20,1,1,"x"') " invalid line count + call add(lines, '|3,0,10,1,0,1,1') " zero line count + call writefile(lines, 'Xviminfo') + rviminfo Xviminfo + call delete('Xviminfo') +endfunc + func Test_viminfo_file_marks() silent! bwipe test_viminfo.vim silent! bwipe Xviminfo @@ -664,6 +709,7 @@ func Test_viminfo_bufferlist() " If there are arguments, then :rviminfo doesn't read the buffer list. " Need to delete all the arguments for :rviminfo to work. %argdelete + set viminfo&vim edit Xfile1 edit Xfile2 @@ -684,9 +730,42 @@ func Test_viminfo_bufferlist() call assert_equal('Xfile1', bufname(l[1].bufnr)) call assert_equal('Xfile2', bufname(l[2].bufnr)) + " The quickfix, terminal, unlisted, unnamed buffers are not stored in the + " viminfo file + %bw! + edit Xfile1 + new + setlocal nobuflisted + new + copen + if has('terminal') + terminal + endif + wviminfo! Xviminfo + %bwipe! + rviminfo Xviminfo + let l = getbufinfo() + call assert_equal(2, len(l)) + call assert_true(bufexists('Xfile1')) + + " If a count is specified for '%', then only that many buffers should be + " stored in the viminfo file. + %bw! + set viminfo&vim + new Xbuf1 + new Xbuf2 + set viminfo+=%1 + wviminfo! Xviminfo + %bwipe! + rviminfo! Xviminfo + let l = getbufinfo() + call assert_equal(2, len(l)) + call assert_true(bufexists('Xbuf1')) + call assert_false(bufexists('Xbuf2')) + call delete('Xviminfo') %bwipe - set viminfo-=% + set viminfo&vim endfunc " Test for errors in a viminfo file @@ -747,6 +826,12 @@ func Test_viminfo_registers_old() \ ' Vim', \ '"a CHAR 0', \ ' red', + \ '"c BLOCK 0', + \ ' a', + \ ' d', + \ '"d LINE 0', + \ ' abc', + \ ' def', \ '"m@ CHAR 0', \ " :echo 'Hello'\", \ "", @@ -760,7 +845,12 @@ func Test_viminfo_registers_old() silent! normal @t rviminfo! Xviminfo call assert_equal('red', getreg('a')) + call assert_equal("v", getregtype('a')) call assert_equal('two', getreg('b')) + call assert_equal("a\nd", getreg('c')) + call assert_equal("\1", getregtype('c')) + call assert_equal("abc\ndef\n", getreg('d')) + call assert_equal("V", getregtype('d')) call assert_equal(":echo 'Hello'\", getreg('m')) call assert_equal('Vim', getreg('"')) call assert_equal("\nHello", execute('normal @@')) @@ -914,4 +1004,228 @@ func Test_viminfo_oldfiles_newfile() let &viminfofile = save_viminfofile endfunc +" When writing CTRL-V or "\n" to a viminfo file, it is converted to CTRL-V +" CTRL-V and CTRL-V n respectively. +func Test_viminfo_with_Ctrl_V() + silent! exe "normal! /\\\n" + wviminfo Xviminfo + call assert_notequal(-1, readfile('Xviminfo')->index("?/\\")) + let @/ = 'abc' + rviminfo! Xviminfo + call assert_equal("\", @/) + silent! exe "normal! /\\\n" + wviminfo Xviminfo + call assert_notequal(-1, readfile('Xviminfo')->index("?/\n")) + let @/ = 'abc' + rviminfo! Xviminfo + call assert_equal("\n", @/) + call delete('Xviminfo') +endfunc + +" Test for the 'r' field in 'viminfo' (removal media) +func Test_viminfo_removable_media() + CheckUnix + if !isdirectory('/tmp') || getftype('/tmp') != 'dir' + return + endif + let save_viminfo = &viminfo + set viminfo+=r/tmp + edit /tmp/Xvima1b2c3 + wviminfo Xviminfo + let matches = readfile('Xviminfo')->filter("v:val =~ 'Xvima1b2c3'") + call assert_equal(0, matches->len()) + let &viminfo = save_viminfo + call delete('Xviminfo') +endfunc + +" Test for the 'h' flag in 'viminfo'. If 'h' is not present, then the last +" search pattern read from 'viminfo' should be highlighted with 'hlsearch'. +" If 'h' is present, then the last search pattern should not be highlighted. +func Test_viminfo_hlsearch() + set viminfo&vim + + new + call setline(1, ['one two three']) + " save the screen attribute for the Search highlighted text and the normal + " text for later comparison + set hlsearch + let @/ = 'three' + redraw! + let hiSearch = screenattr(1, 9) + let hiNormal = screenattr(1, 1) + + set viminfo-=h + let @/='two' + wviminfo! Xviminfo + let @/='one' + rviminfo! Xviminfo + redraw! + call assert_equal(hiSearch, screenattr(1, 5)) + call assert_equal(hiSearch, screenattr(1, 6)) + call assert_equal(hiSearch, screenattr(1, 7)) + + set viminfo+=h + let @/='two' + wviminfo! Xviminfo + let @/='one' + rviminfo! Xviminfo + redraw! + call assert_equal(hiNormal, screenattr(1, 5)) + call assert_equal(hiNormal, screenattr(1, 6)) + call assert_equal(hiNormal, screenattr(1, 7)) + + call delete('Xviminfo') + set hlsearch& viminfo&vim + bw! +endfunc + +" Test for restoring the magicness of the last search pattern from the viminfo +" file. +func Test_viminfo_last_spat_magic() + set viminfo&vim + new + call setline(1, ' one abc a.c') + + " restore 'nomagic' + set nomagic + exe "normal gg/a.c\" + wviminfo! Xviminfo + set magic + exe "normal gg/one\" + rviminfo! Xviminfo + exe "normal! gg/\" + call assert_equal(10, col('.')) + + " restore 'magic' + set magic + exe "normal gg/a.c\" + wviminfo! Xviminfo + set nomagic + exe "normal gg/one\" + rviminfo! Xviminfo + exe "normal! gg/\" + call assert_equal(6, col('.')) + + call delete('Xviminfo') + set viminfo&vim magic& + bw! +endfunc + +" Test for restoring the smartcase of the last search pattern from the viminfo +" file. +func Test_viminfo_last_spat_smartcase() + new + call setline(1, ' one abc Abc') + set ignorecase smartcase + + " Searching with * should disable smartcase + exe "normal! gg$b*" + wviminfo! Xviminfo + exe "normal gg/one\" + rviminfo! Xviminfo + exe "normal! gg/\" + call assert_equal(6, col('.')) + + call delete('Xviminfo') + set ignorecase& smartcase& viminfo& + bw! +endfunc + +" Test for restoring the last search pattern with a line or character offset +" from the viminfo file. +func Test_viminfo_last_spat_offset() + new + call setline(1, ['one', 'two', 'three', 'four', 'five']) + " line offset + exe "normal! /two/+2\" + wviminfo! Xviminfo + exe "normal gg/five\" + rviminfo! Xviminfo + exe "normal! gg/\" + call assert_equal(4, line('.')) + " character offset + exe "normal! gg/^th/e+2\" + wviminfo! Xviminfo + exe "normal gg/two\" + rviminfo! Xviminfo + exe "normal! gg/\" + call assert_equal([3, 4], [line('.'), col('.')]) + call delete('Xviminfo') + bw! +endfunc + +" Test for saving and restoring the last executed register (@ command) +" from the viminfo file +func Test_viminfo_last_exec_reg() + let g:val = 1 + let @a = ":let g:val += 1\n" + normal! @a + wviminfo! Xviminfo + let @b = '' + normal! @b + rviminfo! Xviminfo + normal @@ + call assert_equal(3, g:val) + call delete('Xviminfo') +endfunc + +" Test for merging file marks in a viminfo file +func Test_viminfo_merge_file_marks() + for [f, l, t] in [['a.txt', 5, 10], ['b.txt', 10, 20]] + call test_settime(t) + exe 'edit ' .. f + call setline(1, range(1, 20)) + exe l . 'mark a' + wviminfo Xviminfo + bw! + endfor + call test_settime(30) + for [f, l] in [['a.txt', 5], ['b.txt', 10]] + exe 'edit ' .. f + rviminfo! Xviminfo + call assert_equal(l, line("'a")) + bw! + endfor + call delete('Xviminfo') + call test_settime(0) +endfunc + +" Test for merging file marks from a old viminfo file +func Test_viminfo_merge_old_filemarks() + let lines = [] + call add(lines, '|1,4') + call add(lines, '> ' .. fnamemodify('a.txt', ':p:~')) + call add(lines, "\tb\t7\t0\n") + call writefile(lines, 'Xviminfo') + edit b.txt + call setline(1, range(1, 20)) + 12mark b + wviminfo Xviminfo + bw! + edit a.txt + rviminfo! Xviminfo + call assert_equal(7, line("'b")) + edit b.txt + rviminfo! Xviminfo + call assert_equal(12, line("'b")) + call delete('Xviminfo') +endfunc + +" Test for merging the jump list from a old viminfo file +func Test_viminfo_merge_old_jumplist() + let lines = [] + call add(lines, "-' 10 1 " .. fnamemodify('a.txt', ':p:~')) + call add(lines, "-' 20 1 " .. fnamemodify('a.txt', ':p:~')) + call add(lines, "-' 30 1 " .. fnamemodify('b.txt', ':p:~')) + call add(lines, "-' 40 1 " .. fnamemodify('b.txt', ':p:~')) + call writefile(lines, 'Xviminfo') + clearjumps + rviminfo! Xviminfo + let l = getjumplist()[0] + call assert_equal([40, 30, 20, 10], [l[0].lnum, l[1].lnum, l[2].lnum, + \ l[3].lnum]) + bw! + call delete('Xviminfo') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 1c680f2f06..9198e2cb3a 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3010, /**/ 3009, /**/ From 6bc30b05e6081bcaece6d1a7fcfca238ea5a194f Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 16 Jun 2021 19:19:55 +0200 Subject: [PATCH 03/28] patch 8.2.3011: Vim9: cannot get argument values during debugging Problem: Vim9: cannot get argument values during debugging. Solution: Lookup names in the list of arguments. Put debug instruction halfway for command. --- src/testdir/test_debugger.vim | 43 ++++++++++++++++++++++++++++++----- src/version.c | 2 ++ src/vim9compile.c | 18 +++++++++++++-- src/vim9execute.c | 21 +++++++++++++---- 4 files changed, 71 insertions(+), 13 deletions(-) diff --git a/src/testdir/test_debugger.vim b/src/testdir/test_debugger.vim index bbf74258b2..5326576d02 100644 --- a/src/testdir/test_debugger.vim +++ b/src/testdir/test_debugger.vim @@ -937,12 +937,20 @@ func Test_debug_def_function() let file =<< trim END vim9script def g:Func() - var n: number - def Closure(): number - return n + 3 - enddef - n += Closure() - echo 'result: ' .. n + var n: number + def Closure(): number + return n + 3 + enddef + n += Closure() + echo 'result: ' .. n + enddef + + def g:FuncWithArgs(text: string, nr: number, ...items: list) + echo text .. nr + for it in items + echo it + endfor + echo "done" enddef END call writefile(file, 'Xtest.vim') @@ -954,7 +962,30 @@ func Test_debug_def_function() \ ['cmd: call Func()']) call RunDbgCmd(buf, 'next', ['result: 3']) call term_sendkeys(buf, "\r") + call RunDbgCmd(buf, 'cont') + call RunDbgCmd(buf, + \ ':debug call FuncWithArgs("asdf", 42, 1, 2, 3)', + \ ['cmd: call FuncWithArgs("asdf", 42, 1, 2, 3)']) + call RunDbgCmd(buf, 'step', ['line 1: echo text .. nr']) + call RunDbgCmd(buf, 'echo text', ['asdf']) + call RunDbgCmd(buf, 'echo nr', ['42']) + call RunDbgCmd(buf, 'echo items', ['[1, 2, 3]']) + call RunDbgCmd(buf, 'step', ['asdf42', 'function FuncWithArgs', 'line 2: for it in items']) + call RunDbgCmd(buf, 'echo it', ['1']) + call RunDbgCmd(buf, 'step', ['line 3: echo it']) + call RunDbgCmd(buf, 'step', ['1', 'function FuncWithArgs', 'line 4: endfor']) + call RunDbgCmd(buf, 'step', ['line 2: for it in items']) + call RunDbgCmd(buf, 'echo it', ['2']) + call RunDbgCmd(buf, 'step', ['line 3: echo it']) + call RunDbgCmd(buf, 'step', ['2', 'function FuncWithArgs', 'line 4: endfor']) + call RunDbgCmd(buf, 'step', ['line 2: for it in items']) + call RunDbgCmd(buf, 'echo it', ['3']) + call RunDbgCmd(buf, 'step', ['line 3: echo it']) + call RunDbgCmd(buf, 'step', ['3', 'function FuncWithArgs', 'line 4: endfor']) + call RunDbgCmd(buf, 'step', ['line 5: echo "done"']) + + call RunDbgCmd(buf, 'cont') call StopVimInTerminal(buf) call delete('Xtest.vim') endfunc diff --git a/src/version.c b/src/version.c index 9198e2cb3a..dbb6d7ecda 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3011, /**/ 3010, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 663a52dc7b..404c819e78 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -7711,6 +7711,7 @@ compile_for(char_u *arg_start, cctx_T *cctx) int semicolon = FALSE; size_t varlen; garray_T *stack = &cctx->ctx_type_stack; + garray_T *instr = &cctx->ctx_instr; scope_T *scope; lvar_T *loop_lvar; // loop iteration variable lvar_T *var_lvar; // variable for "var" @@ -7737,6 +7738,13 @@ compile_for(char_u *arg_start, cctx_T *cctx) if (may_get_next_line_error(wp, &p, cctx) == FAIL) return NULL; + // Remove the already generated ISN_DEBUG, it is written below the ISN_FOR + // instruction. + if (cctx->ctx_compile_type == CT_DEBUG && instr->ga_len > 0 + && ((isn_T *)instr->ga_data)[instr->ga_len - 1] + .isn_type == ISN_DEBUG) + --instr->ga_len; + scope = new_scope(cctx, FOR_SCOPE); if (scope == NULL) return NULL; @@ -7788,11 +7796,12 @@ compile_for(char_u *arg_start, cctx_T *cctx) item_type = vartype->tt_member->tt_member; } - // CMDMOD_REV must come before the FOR instruction + // CMDMOD_REV must come before the FOR instruction. generate_undo_cmdmods(cctx); // "for_end" is set when ":endfor" is found scope->se_u.se_for.fs_top_label = current_instr_idx(cctx); + generate_FOR(cctx, loop_lvar->lv_idx); arg = arg_start; @@ -7893,6 +7902,10 @@ compile_for(char_u *arg_start, cctx_T *cctx) vim_free(name); } + if (cctx->ctx_compile_type == CT_DEBUG) + // Add ISN_DEBUG here, so that the loop variables can be inspected. + generate_instr_debug(cctx); + return arg_end; failed: @@ -7927,7 +7940,7 @@ compile_endfor(char_u *arg, cctx_T *cctx) // At end of ":for" scope jump back to the FOR instruction. generate_JUMP(cctx, JUMP_ALWAYS, forscope->fs_top_label); - // Fill in the "end" label in the FOR statement so it can jump here + // Fill in the "end" label in the FOR statement so it can jump here. isn = ((isn_T *)instr->ga_data) + forscope->fs_top_label; isn->isn_arg.forloop.for_end = instr->ga_len; @@ -8234,6 +8247,7 @@ compile_catch(char_u *arg, cctx_T *cctx UNUSED) #ifdef FEAT_PROFILE // the profile-start should be after the jump if (cctx->ctx_compile_type == CT_PROFILE + && instr->ga_len > 0 && ((isn_T *)instr->ga_data)[instr->ga_len - 1] .isn_type == ISN_PROF_START) --instr->ga_len; diff --git a/src/vim9execute.c b/src/vim9execute.c index e464732dda..bbcf7ffc3c 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -1394,7 +1394,7 @@ typedef struct subs_expr_S { // Set when calling do_debug(). static ectx_T *debug_context = NULL; -static int debug_arg_count; +static int debug_var_count; /* * When debugging lookup "name" and return the typeval. @@ -1405,20 +1405,31 @@ lookup_debug_var(char_u *name) { int idx; dfunc_T *dfunc; + ufunc_T *ufunc; ectx_T *ectx = debug_context; + int varargs_off; if (ectx == NULL) return NULL; dfunc = ((dfunc_T *)def_functions.ga_data) + ectx->ec_dfunc_idx; // Go through the local variable names, from last to first. - for (idx = debug_arg_count - 1; idx >= 0; --idx) + for (idx = debug_var_count - 1; idx >= 0; --idx) { - char_u *s = ((char_u **)dfunc->df_var_names.ga_data)[idx]; - if (STRCMP(s, name) == 0) + if (STRCMP(((char_u **)dfunc->df_var_names.ga_data)[idx], name) == 0) return STACK_TV_VAR(idx); } + // Go through argument names. + ufunc = dfunc->df_ufunc; + varargs_off = ufunc->uf_va_name == NULL ? 0 : 1; + for (idx = 0; idx < ufunc->uf_args.ga_len; ++idx) + if (STRCMP(((char_u **)(ufunc->uf_args.ga_data))[idx], name) == 0) + return STACK_TV(ectx->ec_frame_idx - ufunc->uf_args.ga_len + - varargs_off + idx); + if (ufunc->uf_va_name != NULL && STRCMP(ufunc->uf_va_name, name) == 0) + return STACK_TV(ectx->ec_frame_idx - 1); + return NULL; } @@ -4152,7 +4163,7 @@ exec_instructions(ectx_T *ectx) SOURCING_LNUM = iptr->isn_lnum; debug_context = ectx; - debug_arg_count = iptr->isn_arg.number; + debug_var_count = iptr->isn_arg.number; line = ((char_u **)ufunc->uf_lines.ga_data)[ iptr->isn_lnum - 1]; if (line == NULL) From 29f0dc3689eafcf7888e06d57d1cf79e62c5c148 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Wed, 16 Jun 2021 19:28:34 +0200 Subject: [PATCH 04/28] patch 8.2.3012: when 'rightleft' is set the line number is drawn reversed Problem: When 'rightleft' is set the line number is sometimes drawn reversed. Solution: Adjust how space is handled. (Christian Brabandt, closes #8389, closes #8391) --- src/drawline.c | 5 +++-- src/testdir/test_number.vim | 22 ++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/drawline.c b/src/drawline.c index 7b6777e64c..b1210b78f4 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -1121,8 +1121,9 @@ win_line( int t; // like rl_mirror(), but keep the space at the end - p2 = skiptowhite(extra) - 1; - for (p1 = extra; p1 < p2; ++p1, --p2) + p2 = skipwhite(extra); + p2 = skiptowhite(p2) - 1; + for (p1 = skipwhite(extra); p1 < p2; ++p1, --p2) { t = *p1; *p1 = *p2; diff --git a/src/testdir/test_number.vim b/src/testdir/test_number.vim index 93f8282e07..13f8de719d 100644 --- a/src/testdir/test_number.vim +++ b/src/testdir/test_number.vim @@ -298,4 +298,26 @@ func Test_relativenumber_colors() call delete('XTest_relnr') endfunc +" Test for displaying line numbers with 'rightleft' +func Test_number_rightleft() + CheckFeature rightleft + new + setlocal number + setlocal rightleft + call setline(1, range(1, 1000)) + normal! 9Gzt + redraw! + call assert_match('^\s\+9 9$', Screenline(1)) + normal! 10Gzt + redraw! + call assert_match('^\s\+01 10$', Screenline(1)) + normal! 100Gzt + redraw! + call assert_match('^\s\+001 100$', Screenline(1)) + normal! 1000Gzt + redraw! + call assert_match('^\s\+0001 1000$', Screenline(1)) + bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index dbb6d7ecda..1b23c664d5 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3012, /**/ 3011, /**/ From 4cea536bdf48df459e7ad651dfee006844bbf2c0 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 16 Jun 2021 22:24:40 +0200 Subject: [PATCH 05/28] patch 8.2.3013: Vim: when debugging only first line of command is displayed Problem: Vim: when debugging only the first line of a command using line continuation is displayed. Solution: Find the next command and concatenate lines until that one. (closes #8392) --- src/testdir/test_debugger.vim | 45 ++++++++++++++++--------- src/version.c | 2 ++ src/vim9execute.c | 62 ++++++++++++++++++++++++++--------- 3 files changed, 78 insertions(+), 31 deletions(-) diff --git a/src/testdir/test_debugger.vim b/src/testdir/test_debugger.vim index 5326576d02..a6e3966096 100644 --- a/src/testdir/test_debugger.vim +++ b/src/testdir/test_debugger.vim @@ -885,19 +885,19 @@ func Test_Backtrace_DefFunction() \ ':debug call GlobalFunction()', \ ['cmd: call GlobalFunction()']) - call RunDbgCmd(buf, 'step', ['line 1: var some = "some var"']) - call RunDbgCmd(buf, 'step', ['line 2: CallAFunction()']) + call RunDbgCmd(buf, 'step', ['line 1: var some = "some var"']) + call RunDbgCmd(buf, 'step', ['line 2: CallAFunction()']) call RunDbgCmd(buf, 'echo some', ['some var']) call RunDbgCmd(buf, 'backtrace', [ \ '\V>backtrace', \ '\V->0 function GlobalFunction', - \ '\Vline 2: CallAFunction()', + \ '\Vline 2: CallAFunction()', \ ], \ #{match: 'pattern'}) - call RunDbgCmd(buf, 'step', ['line 1: SourceAnotherFile()']) - call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim']) + call RunDbgCmd(buf, 'step', ['line 1: SourceAnotherFile()']) + call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim']) " Repeated line, because we fist are in the compiled function before the " EXEC and then in do_cmdline() before the :source command. call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim']) @@ -952,6 +952,13 @@ func Test_debug_def_function() endfor echo "done" enddef + + def g:FuncWithDict() + var d = { + a: 1, + b: 2, + } + enddef END call writefile(file, 'Xtest.vim') @@ -967,23 +974,29 @@ func Test_debug_def_function() call RunDbgCmd(buf, \ ':debug call FuncWithArgs("asdf", 42, 1, 2, 3)', \ ['cmd: call FuncWithArgs("asdf", 42, 1, 2, 3)']) - call RunDbgCmd(buf, 'step', ['line 1: echo text .. nr']) + call RunDbgCmd(buf, 'step', ['line 1: echo text .. nr']) call RunDbgCmd(buf, 'echo text', ['asdf']) call RunDbgCmd(buf, 'echo nr', ['42']) call RunDbgCmd(buf, 'echo items', ['[1, 2, 3]']) - call RunDbgCmd(buf, 'step', ['asdf42', 'function FuncWithArgs', 'line 2: for it in items']) + call RunDbgCmd(buf, 'step', ['asdf42', 'function FuncWithArgs', 'line 2: for it in items']) call RunDbgCmd(buf, 'echo it', ['1']) - call RunDbgCmd(buf, 'step', ['line 3: echo it']) - call RunDbgCmd(buf, 'step', ['1', 'function FuncWithArgs', 'line 4: endfor']) - call RunDbgCmd(buf, 'step', ['line 2: for it in items']) + call RunDbgCmd(buf, 'step', ['line 3: echo it']) + call RunDbgCmd(buf, 'step', ['1', 'function FuncWithArgs', 'line 4: endfor']) + call RunDbgCmd(buf, 'step', ['line 2: for it in items']) call RunDbgCmd(buf, 'echo it', ['2']) - call RunDbgCmd(buf, 'step', ['line 3: echo it']) - call RunDbgCmd(buf, 'step', ['2', 'function FuncWithArgs', 'line 4: endfor']) - call RunDbgCmd(buf, 'step', ['line 2: for it in items']) + call RunDbgCmd(buf, 'step', ['line 3: echo it']) + call RunDbgCmd(buf, 'step', ['2', 'function FuncWithArgs', 'line 4: endfor']) + call RunDbgCmd(buf, 'step', ['line 2: for it in items']) call RunDbgCmd(buf, 'echo it', ['3']) - call RunDbgCmd(buf, 'step', ['line 3: echo it']) - call RunDbgCmd(buf, 'step', ['3', 'function FuncWithArgs', 'line 4: endfor']) - call RunDbgCmd(buf, 'step', ['line 5: echo "done"']) + call RunDbgCmd(buf, 'step', ['line 3: echo it']) + call RunDbgCmd(buf, 'step', ['3', 'function FuncWithArgs', 'line 4: endfor']) + call RunDbgCmd(buf, 'step', ['line 5: echo "done"']) + call RunDbgCmd(buf, 'cont') + + call RunDbgCmd(buf, + \ ':debug call FuncWithDict()', + \ ['cmd: call FuncWithDict()']) + call RunDbgCmd(buf, 'step', ['line 1: var d = { a: 1, b: 2, }']) call RunDbgCmd(buf, 'cont') call StopVimInTerminal(buf) diff --git a/src/version.c b/src/version.c index 1b23c664d5..2dff11cd7a 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3013, /**/ 3012, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index bbcf7ffc3c..33e9244245 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -1433,6 +1433,52 @@ lookup_debug_var(char_u *name) return NULL; } + static void +handle_debug(isn_T *iptr, ectx_T *ectx) +{ + char_u *line; + ufunc_T *ufunc = (((dfunc_T *)def_functions.ga_data) + + ectx->ec_dfunc_idx)->df_ufunc; + isn_T *ni; + int end_lnum = iptr->isn_lnum; + garray_T ga; + int lnum; + + SOURCING_LNUM = iptr->isn_lnum; + debug_context = ectx; + debug_var_count = iptr->isn_arg.number; + + for (ni = iptr + 1; ni->isn_type != ISN_FINISH; ++ni) + if (ni->isn_type == ISN_DEBUG + || ni->isn_type == ISN_RETURN + || ni->isn_type == ISN_RETURN_VOID) + { + end_lnum = ni->isn_lnum; + break; + } + + if (end_lnum > iptr->isn_lnum) + { + ga_init2(&ga, sizeof(char_u *), 10); + for (lnum = iptr->isn_lnum; lnum < end_lnum; ++lnum) + if (ga_grow(&ga, 1) == OK) + ((char_u **)(ga.ga_data))[ga.ga_len++] = + skipwhite(((char_u **)ufunc->uf_lines.ga_data)[lnum - 1]); + line = ga_concat_strings(&ga, " "); + vim_free(ga.ga_data); + } + else + line = ((char_u **)ufunc->uf_lines.ga_data)[iptr->isn_lnum - 1]; + if (line == NULL) + line = (char_u *)"[empty]"; + + do_debug(line); + debug_context = NULL; + + if (end_lnum > iptr->isn_lnum) + vim_free(line); +} + /* * Execute instructions in execution context "ectx". * Return OK or FAIL; @@ -4156,21 +4202,7 @@ exec_instructions(ectx_T *ectx) case ISN_DEBUG: if (ex_nesting_level <= debug_break_level) - { - char_u *line; - ufunc_T *ufunc = (((dfunc_T *)def_functions.ga_data) - + ectx->ec_dfunc_idx)->df_ufunc; - - SOURCING_LNUM = iptr->isn_lnum; - debug_context = ectx; - debug_var_count = iptr->isn_arg.number; - line = ((char_u **)ufunc->uf_lines.ga_data)[ - iptr->isn_lnum - 1]; - if (line == NULL) - line = (char_u *)"[empty]"; - do_debug(line); - debug_context = NULL; - } + handle_debug(iptr, ectx); break; case ISN_SHUFFLE: From 6e9695525e212466398a269a54bc4d53625673f0 Mon Sep 17 00:00:00 2001 From: Dominique Pelle Date: Thu, 17 Jun 2021 13:53:41 +0200 Subject: [PATCH 06/28] patch 8.2.3014: Coverity warns for freeing static string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Coverity warns for freeing static string. Solution: Do not assign static string to pointer. (Dominique PellĂ©, closes #8397) --- src/version.c | 2 ++ src/vim9execute.c | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/version.c b/src/version.c index 2dff11cd7a..99c086f5b5 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3014, /**/ 3013, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index 33e9244245..fabce4dbe7 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -1469,10 +1469,8 @@ handle_debug(isn_T *iptr, ectx_T *ectx) } else line = ((char_u **)ufunc->uf_lines.ga_data)[iptr->isn_lnum - 1]; - if (line == NULL) - line = (char_u *)"[empty]"; - do_debug(line); + do_debug(line == NULL ? (char_u *)"[empty]" : line); debug_context = NULL; if (end_lnum > iptr->isn_lnum) From 74f4a965bc6e2a9c41cce2f644e861168702922f Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 17 Jun 2021 21:03:07 +0200 Subject: [PATCH 07/28] patch 8.2.3015: Vim9: Assigning to @# requires a string Problem: Vim9: Assigning to @# requires a string. (Naohiro Ono) Solution: Accent a number or a string. (closes #8396) --- src/globals.h | 3 +++ src/testdir/test_vim9_assign.vim | 13 +++++++++++++ src/version.c | 2 ++ src/vim9compile.c | 17 ++++++++++++++--- src/vim9execute.c | 3 +-- 5 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/globals.h b/src/globals.h index 77ea169434..07acc2602b 100644 --- a/src/globals.h +++ b/src/globals.h @@ -418,6 +418,9 @@ EXTERN type_T t_blob INIT6(VAR_BLOB, 0, 0, TTFLAG_STATIC, NULL, NULL); EXTERN type_T t_job INIT6(VAR_JOB, 0, 0, TTFLAG_STATIC, NULL, NULL); EXTERN type_T t_channel INIT6(VAR_CHANNEL, 0, 0, TTFLAG_STATIC, NULL, NULL); +// Special value used for @#. +EXTERN type_T t_number_or_string INIT6(VAR_STRING, 0, 0, TTFLAG_STATIC, NULL, NULL); + EXTERN type_T t_func_unknown INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_unknown, NULL); EXTERN type_T t_func_void INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_void, NULL); EXTERN type_T t_func_any INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_any, NULL); diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim index 7b7b62305b..1cbdcfb466 100644 --- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -1820,6 +1820,19 @@ def Test_assign_command_modifier() CheckDefAndScriptSuccess(lines) enddef +def Test_assign_alt_buf_register() + var lines =<< trim END + edit 'file_b1' + var b1 = bufnr() + edit 'file_b2' + var b2 = bufnr() + assert_equal(b1, bufnr('#')) + @# = b2 + assert_equal(b2, bufnr('#')) + END + CheckDefAndScriptSuccess(lines) +enddef + def Test_script_funcref_case() var lines =<< trim END var Len = (s: string): number => len(s) + 1 diff --git a/src/version.c b/src/version.c index 99c086f5b5..4f33d35d87 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3015, /**/ 3014, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 404c819e78..e4656c99af 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -5852,7 +5852,7 @@ get_var_dest( return FAIL; } *dest = dest_reg; - *type = &t_string; + *type = name[1] == '#' ? &t_number_or_string : &t_string; } else if (STRNCMP(name, "g:", 2) == 0) { @@ -5927,7 +5927,8 @@ generate_store_var( case dest_env: return generate_STORE(cctx, ISN_STOREENV, 0, name + 1); case dest_reg: - return generate_STORE(cctx, ISN_STOREREG, name[1], NULL); + return generate_STORE(cctx, ISN_STOREREG, + name[1] == '@' ? '"' : name[1], NULL); case dest_vimvar: return generate_STORE(cctx, ISN_STOREV, vimvaridx, NULL); case dest_script: @@ -6843,9 +6844,19 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) goto theend; } } - else if (*p != '=' && need_type(rhs_type, lhs.lhs_member_type, + else + { + type_T *lhs_type = lhs.lhs_member_type; + + // Special case: assigning to @# can use a number or a + // string. + if (lhs_type == &t_number_or_string + && rhs_type->tt_type == VAR_NUMBER) + lhs_type = &t_number; + if (*p != '=' && need_type(rhs_type, lhs_type, -1, 0, cctx, FALSE, FALSE) == FAIL) goto theend; + } } else if (cmdidx == CMD_final) { diff --git a/src/vim9execute.c b/src/vim9execute.c index fabce4dbe7..2577e63e27 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -2182,8 +2182,7 @@ exec_instructions(ectx_T *ectx) --ectx->ec_stack.ga_len; tv = STACK_TV_BOT(0); - write_reg_contents(reg == '@' ? '"' : reg, - tv_get_string(tv), -1, FALSE); + write_reg_contents(reg, tv_get_string(tv), -1, FALSE); clear_tv(tv); } break; From fae55a9cb0838e4c2e634e55a3468af4a75fbdf2 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 17 Jun 2021 22:08:30 +0200 Subject: [PATCH 08/28] patch 8.2.3016: confusing error when expression is followed by comma Problem: Confusing error when expression is followed by comma. Solution: Give a different error for trailing text. (closes #8395) --- src/eval.c | 12 ++++++++++-- src/testdir/test_eval_stuff.vim | 2 +- src/testdir/test_let.vim | 1 + src/testdir/test_vim9_expr.vim | 10 +++++----- src/testdir/test_vim9_script.vim | 6 +++--- src/testdir/test_viminfo.vim | 2 +- src/testdir/test_vimscript.vim | 2 +- src/version.c | 2 ++ 8 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/eval.c b/src/eval.c index ffc7122cde..33ea8504a2 100644 --- a/src/eval.c +++ b/src/eval.c @@ -2218,12 +2218,15 @@ eval0( int did_emsg_before = did_emsg; int called_emsg_before = called_emsg; int flags = evalarg == NULL ? 0 : evalarg->eval_flags; + int end_error = FALSE; p = skipwhite(arg); ret = eval1(&p, rettv, evalarg); p = skipwhite(p); - if (ret == FAIL || !ends_excmd2(arg, p)) + if (ret != FAIL) + end_error = !ends_excmd2(arg, p); + if (ret == FAIL || end_error) { if (ret != FAIL) clear_tv(rettv); @@ -2238,7 +2241,12 @@ eval0( && called_emsg == called_emsg_before && (flags & EVAL_CONSTANT) == 0 && (!in_vim9script() || !vim9_bad_comment(p))) - semsg(_(e_invexpr2), arg); + { + if (end_error) + semsg(_(e_trailing_arg), p); + else + semsg(_(e_invexpr2), arg); + } // Some of the expression may not have been consumed. Do not check for // a next command to avoid more errors, unless "|" is following, which diff --git a/src/testdir/test_eval_stuff.vim b/src/testdir/test_eval_stuff.vim index 0c1e75df59..1fbb74bc4e 100644 --- a/src/testdir/test_eval_stuff.vim +++ b/src/testdir/test_eval_stuff.vim @@ -165,7 +165,7 @@ func Test_string_concat_scriptversion2() call assert_fails('echo a . b', 'E15:') call assert_fails('let a .= b', 'E985:') - call assert_fails('let vers = 1.2.3', 'E15:') + call assert_fails('let vers = 1.2.3', 'E488:') if has('float') let f = .5 diff --git a/src/testdir/test_let.vim b/src/testdir/test_let.vim index 1b8f74bcfe..c05a4cbc20 100644 --- a/src/testdir/test_let.vim +++ b/src/testdir/test_let.vim @@ -314,6 +314,7 @@ func Test_let_errors() 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' diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index d7fb1df825..974ca8548b 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -2340,7 +2340,7 @@ def Test_expr7_dict() CheckScriptFailure(['vim9script', "var x = {xxx: 1,"], 'E723:', 2) CheckDefAndScriptFailure2(["var x = {['a']: xxx}"], 'E1001:', 'E121:', 1) CheckDefAndScriptFailure(["var x = {a: 1, a: 2}"], 'E721:', 1) - CheckDefExecAndScriptFailure2(["var x = g:anint.member"], 'E715:', 'E15:', 1) + CheckDefExecAndScriptFailure2(["var x = g:anint.member"], 'E715:', 'E488:', 1) CheckDefExecAndScriptFailure(["var x = g:dict_empty.member"], 'E716:', 1) CheckDefExecAndScriptFailure(['var x: dict = {a: 234, b: "1"}'], 'E1012:', 1) @@ -3052,7 +3052,7 @@ func Test_expr7_fails() call CheckDefAndScriptFailure2(["var x = [notfound]"], "E1001:", 'E121:', 1) - call CheckDefAndScriptFailure2(["var X = () => 123)"], "E488:", 'E15:', 1) + call CheckDefAndScriptFailure(["var X = () => 123)"], 'E488:', 1) call CheckDefAndScriptFailure(["var x = 123->((x) => x + 5)"], "E107:", 1) call CheckDefAndScriptFailure(["var x = ¬exist"], 'E113:', 1) @@ -3070,7 +3070,7 @@ func Test_expr7_fails() call CheckDefExecAndScriptFailure(["var x = +g:alist"], 'E745:', 1) call CheckDefExecAndScriptFailure(["var x = +g:adict"], 'E728:', 1) - call CheckDefAndScriptFailure2(["var x = ''", "var y = x.memb"], 'E715:', 'E15:', 2) + call CheckDefAndScriptFailure2(["var x = ''", "var y = x.memb"], 'E715:', 'E488:', 2) call CheckDefAndScriptFailure2(["'yes'->", "Echo()"], 'E488: Trailing characters: ->', 'E260: Missing name after ->', 1) @@ -3354,8 +3354,8 @@ func Test_expr7_trailing_fails() endfunc func Test_expr_fails() - call CheckDefAndScriptFailure2(["var x = '1'is2"], 'E488:', 'E15:', 1) - call CheckDefAndScriptFailure2(["var x = '1'isnot2"], 'E488:', 'E15:', 1) + call CheckDefAndScriptFailure(["var x = '1'is2"], 'E488:', 1) + call CheckDefAndScriptFailure(["var x = '1'isnot2"], 'E488:', 1) call CheckDefAndScriptFailure2(["CallMe ('yes')"], 'E476:', 'E492:', 1) diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index 9237fdfad8..4b0ab2bad7 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -3212,7 +3212,7 @@ def Test_vim9_comment_not_compiled() 'if 1# comment3', ' echo "yes"', 'endif', - ], 'E15:') + ], 'E488:') CheckScriptFailure([ 'vim9script', @@ -3221,7 +3221,7 @@ def Test_vim9_comment_not_compiled() 'elseif 2#comment', ' echo "no"', 'endif', - ], 'E15:') + ], 'E488:') CheckScriptSuccess([ 'vim9script', @@ -3231,7 +3231,7 @@ def Test_vim9_comment_not_compiled() CheckScriptFailure([ 'vim9script', 'var v = 1# comment6', - ], 'E15:') + ], 'E488:') CheckScriptSuccess([ 'vim9script', diff --git a/src/testdir/test_viminfo.vim b/src/testdir/test_viminfo.vim index e70fed898a..0518b91d06 100644 --- a/src/testdir/test_viminfo.vim +++ b/src/testdir/test_viminfo.vim @@ -128,7 +128,7 @@ func Test_global_vars() \ "!GLOB_BLOB_4\tBLO\t0z12 ab", \ "!GLOB_LIST_1\tLIS\t1 2", \ "!GLOB_DICT_1\tDIC\t1 2"], 'Xviminfo') - call assert_fails('rv! Xviminfo', 'E15:') + call assert_fails('rv! Xviminfo', 'E488:') call assert_equal('123', g:GLOB_BLOB_1) call assert_equal(1, type(g:GLOB_BLOB_1)) call assert_equal('012', g:GLOB_BLOB_2) diff --git a/src/testdir/test_vimscript.vim b/src/testdir/test_vimscript.vim index 89c100189b..de876589af 100644 --- a/src/testdir/test_vimscript.vim +++ b/src/testdir/test_vimscript.vim @@ -5570,7 +5570,7 @@ func Test_expr_eval_error_msg() 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(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") diff --git a/src/version.c b/src/version.c index 4f33d35d87..a03b3fe13c 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3016, /**/ 3015, /**/ From 59b50c3bee908694ae4ac10b26bfebf99d09d466 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 17 Jun 2021 22:27:48 +0200 Subject: [PATCH 09/28] patch 8.2.3017: Vim9: debugger shows too many lines Problem: Vim9: debugger shows too many lines. Solution: Truncate at a comment, "enddef", etc. (closes #8392) --- src/testdir/test_debugger.vim | 5 +++++ src/version.c | 2 ++ src/vim9execute.c | 12 ++++++++++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/testdir/test_debugger.vim b/src/testdir/test_debugger.vim index a6e3966096..411909d1c7 100644 --- a/src/testdir/test_debugger.vim +++ b/src/testdir/test_debugger.vim @@ -958,6 +958,10 @@ func Test_debug_def_function() a: 1, b: 2, } + # comment + def Inner() + eval 1 + enddef enddef END call writefile(file, 'Xtest.vim') @@ -997,6 +1001,7 @@ func Test_debug_def_function() \ ':debug call FuncWithDict()', \ ['cmd: call FuncWithDict()']) call RunDbgCmd(buf, 'step', ['line 1: var d = { a: 1, b: 2, }']) + call RunDbgCmd(buf, 'step', ['line 6: def Inner()']) call RunDbgCmd(buf, 'cont') call StopVimInTerminal(buf) diff --git a/src/version.c b/src/version.c index a03b3fe13c..69aa0e7f59 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3017, /**/ 3016, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index 2577e63e27..218357a825 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -1461,9 +1461,17 @@ handle_debug(isn_T *iptr, ectx_T *ectx) { ga_init2(&ga, sizeof(char_u *), 10); for (lnum = iptr->isn_lnum; lnum < end_lnum; ++lnum) + { + char_u *p = skipwhite( + ((char_u **)ufunc->uf_lines.ga_data)[lnum - 1]); + + if (*p == '#') + break; if (ga_grow(&ga, 1) == OK) - ((char_u **)(ga.ga_data))[ga.ga_len++] = - skipwhite(((char_u **)ufunc->uf_lines.ga_data)[lnum - 1]); + ((char_u **)(ga.ga_data))[ga.ga_len++] = p; + if (STRNCMP(p, "def ", 4) == 0) + break; + } line = ga_concat_strings(&ga, " "); vim_free(ga.ga_data); } From ad52f96a2d3169cb1b915c1d4a6ba26ba6e5bd0a Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Sat, 19 Jun 2021 18:22:53 +0200 Subject: [PATCH 10/28] patch 8.2.3018: 'quickfixtextfunc' formatting is lost when switching buffers Problem: Formatting using quickfixtextfunc is lost when updating location lists for different buffers. (Yorick Peterse) Solution: Use the right window for the locaiton list. (Yegappan Lakshmanan, closes #8400, closes #8403) --- src/quickfix.c | 12 +++++++- src/testdir/test_quickfix.vim | 58 +++++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/quickfix.c b/src/quickfix.c index ad07a5b4ee..4974cb4790 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -4473,7 +4473,17 @@ qf_update_buffer(qf_info_T *qi, qfline_T *old_last) int qf_winid = 0; if (IS_LL_STACK(qi)) - qf_winid = curwin->w_id; + { + if (curwin->w_llist == qi) + win = curwin; + else + { + win = qf_find_win_with_loclist(qi); + if (win == NULL) + return; + } + qf_winid = win->w_id; + } if (old_last == NULL) // set curwin/curbuf to buf and save a few things diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim index 18b774c8b9..263dbbd6e2 100644 --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -5231,6 +5231,64 @@ func Test_qftextfunc() call Xtest_qftextfunc('l') 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() + %bw! + call setloclist(0, [], 'f') + + " create a window and a location list for it and open the location list + " window + lexpr ['F1:10:12:one', 'F1:20:14:two'] + let w1_id = win_getid() + call setloclist(0, [], ' ', + \ {'lines': ['F1:10:12:one', 'F1:20:14:two'], + \ 'quickfixtextfunc': + \ {d -> map(getloclist(d.winid, {'id' : d.id, + \ 'items' : 1}).items[d.start_idx-1:d.end_idx-1], + \ "'Line ' .. v:val.lnum .. ', Col ' .. v:val.col")}}) + lwindow + let w2_id = win_getid() + + " create another window and a location list for it and open the location + " list window + topleft new + let w3_id = win_getid() + call setloclist(0, [], ' ', + \ {'lines': ['F2:30:32:eleven', 'F2:40:34:twelve'], + \ 'quickfixtextfunc': + \ {d -> map(getloclist(d.winid, {'id' : d.id, + \ 'items' : 1}).items[d.start_idx-1:d.end_idx-1], + \ "'Ligne ' .. v:val.lnum .. ', Colonne ' .. v:val.col")}}) + lwindow + let w4_id = win_getid() + + topleft new + lexpr ['F3:50:52:green', 'F3:60:54:blue'] + let w5_id = win_getid() + + " change the location list for some other window + call setloclist(0, [], 'r', {'lines': ['F3:55:56:aaa', 'F3:57:58:bbb']}) + call setloclist(w1_id, [], 'r', {'lines': ['F1:62:63:bbb', 'F1:64:65:ccc']}) + call setloclist(w3_id, [], 'r', {'lines': ['F2:76:77:ddd', 'F2:78:79:eee']}) + call assert_equal(['Line 62, Col 63', 'Line 64, Col 65'], + \ getbufline(winbufnr(w2_id), 1, '$')) + call assert_equal(['Ligne 76, Colonne 77', 'Ligne 78, Colonne 79'], + \ getbufline(winbufnr(w4_id), 1, '$')) + call setloclist(w2_id, [], 'r', {'lines': ['F1:32:33:fff', 'F1:34:35:ggg']}) + call setloclist(w4_id, [], 'r', {'lines': ['F2:46:47:hhh', 'F2:48:49:jjj']}) + call assert_equal(['Line 32, Col 33', 'Line 34, Col 35'], + \ getbufline(winbufnr(w2_id), 1, '$')) + call assert_equal(['Ligne 46, Colonne 47', 'Ligne 48, Colonne 49'], + \ getbufline(winbufnr(w4_id), 1, '$')) + + call win_gotoid(w5_id) + lwindow + call assert_equal(['F3|55 col 56| aaa', 'F3|57 col 58| bbb'], + \ getline(1, '$')) + %bw! +endfunc + " Running :lhelpgrep command more than once in a help window, doesn't jump to " the help topic func Test_lhelpgrep_from_help_window() diff --git a/src/version.c b/src/version.c index 69aa0e7f59..347074a1f5 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3018, /**/ 3017, /**/ From 6864efa59636ccede2af24e3f5f92d78d210d77b Mon Sep 17 00:00:00 2001 From: thinca Date: Sat, 19 Jun 2021 20:45:20 +0200 Subject: [PATCH 11/28] patch 8.2.3019: location list only has the start position. Problem: Location list only has the start position. Solution: Make it possible to add an end position. (Shane-XB-Qian, closes #8393) --- runtime/doc/eval.txt | 3 + src/quickfix.c | 91 ++++++++++++++----- .../dumps/Test_quickfix_cwindow_1.dump | 6 +- .../dumps/Test_quickfix_cwindow_2.dump | 6 +- src/testdir/test_quickfix.vim | 37 +++++++- src/testdir/test_tagjump.vim | 13 +-- src/testdir/test_vim9_expr.vim | 2 + src/version.c | 2 + 8 files changed, 122 insertions(+), 38 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index fdc63eaa3d..dd2d24232a 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -5841,7 +5841,10 @@ getqflist([{what}]) *getqflist()* bufname() to get the name module module name lnum line number in the buffer (first line is 1) + end_lnum + end of line number if the item is multiline col column number (first column is 1) + end_col end of column number if the item has range vcol |TRUE|: "col" is visual column |FALSE|: "col" is byte index nr error number diff --git a/src/quickfix.c b/src/quickfix.c index 4974cb4790..36574b80f7 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -30,13 +30,16 @@ struct qfline_S qfline_T *qf_next; // pointer to next error in the list qfline_T *qf_prev; // pointer to previous error in the list linenr_T qf_lnum; // line number where the error occurred + linenr_T qf_end_lnum; // line number when the error has range or zero int qf_fnum; // file number for the line int qf_col; // column where the error occurred + int qf_end_col; // column when the error has range or zero int qf_nr; // error number char_u *qf_module; // module name for this error char_u *qf_pattern; // search pattern for the error char_u *qf_text; // description of the error - char_u qf_viscol; // set to TRUE if qf_col is screen column + char_u qf_viscol; // set to TRUE if qf_col and qf_end_col is + // screen column char_u qf_cleared; // set to TRUE if line has been deleted char_u qf_type; // type of the error (mostly 'E'); 1 for // :helpgrep @@ -165,7 +168,7 @@ static efm_T *fmt_start = NULL; // cached across qf_parse_line() calls static callback_T qftf_cb; static void qf_new_list(qf_info_T *qi, char_u *qf_title); -static int qf_add_entry(qf_list_T *qfl, char_u *dir, char_u *fname, char_u *module, int bufnum, char_u *mesg, long lnum, int col, int vis_col, char_u *pattern, int nr, int type, int valid); +static int qf_add_entry(qf_list_T *qfl, char_u *dir, char_u *fname, char_u *module, int bufnum, char_u *mesg, long lnum, long end_lnum, int col, int end_col, int vis_col, char_u *pattern, int nr, int type, int valid); static void qf_free(qf_list_T *qfl); static char_u *qf_types(int, int); static int qf_get_fnum(qf_list_T *qfl, char_u *, char_u *); @@ -174,6 +177,7 @@ static char_u *qf_pop_dir(struct dir_stack_T **); static char_u *qf_guess_filepath(qf_list_T *qfl, char_u *); static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, int newwin); static void qf_fmt_text(char_u *text, char_u *buf, int bufsize); +static void qf_range_text(qfline_T *qfp, char_u *buf, int bufsize); static int qf_win_pos_update(qf_info_T *qi, int old_qf_index); static win_T *qf_find_win(qf_info_T *qi); static buf_T *qf_find_buf(qf_info_T *qi); @@ -899,7 +903,9 @@ typedef struct { char_u *errmsg; int errmsglen; long lnum; + long end_lnum; int col; + int end_col; char_u use_viscol; char_u *pattern; int enr; @@ -1235,7 +1241,9 @@ qf_parse_get_fields( if (!qf_multiscan) fields->errmsg[0] = NUL; fields->lnum = 0; + fields->end_lnum = 0; fields->col = 0; + fields->end_col = 0; fields->use_viscol = FALSE; fields->enr = -1; fields->type = 0; @@ -1630,7 +1638,9 @@ qf_init_process_nextline( 0, fields->errmsg, fields->lnum, + fields->end_lnum, fields->col, + fields->end_col, fields->use_viscol, fields->pattern, fields->enr, @@ -2053,7 +2063,9 @@ qf_add_entry( int bufnum, // buffer number or zero char_u *mesg, // message long lnum, // line number + long end_lnum, // line number for end int col, // column + int end_col, // column for end int vis_col, // using visual column char_u *pattern, // search pattern int nr, // error number @@ -2082,7 +2094,9 @@ qf_add_entry( return QF_FAIL; } qfp->qf_lnum = lnum; + qfp->qf_end_lnum = end_lnum; qfp->qf_col = col; + qfp->qf_end_col = end_col; qfp->qf_viscol = vis_col; if (pattern == NULL || *pattern == NUL) qfp->qf_pattern = NULL; @@ -2239,7 +2253,9 @@ copy_loclist_entries(qf_list_T *from_qfl, qf_list_T *to_qfl) 0, from_qfp->qf_text, from_qfp->qf_lnum, + from_qfp->qf_end_lnum, from_qfp->qf_col, + from_qfp->qf_end_col, from_qfp->qf_viscol, from_qfp->qf_pattern, from_qfp->qf_nr, @@ -3555,11 +3571,8 @@ qf_list_entry(qfline_T *qfp, int qf_idx, int cursel) msg_puts_attr(":", qfSepAttr); if (qfp->qf_lnum == 0) IObuff[0] = NUL; - else if (qfp->qf_col == 0) - sprintf((char *)IObuff, "%ld", qfp->qf_lnum); else - sprintf((char *)IObuff, "%ld col %d", - qfp->qf_lnum, qfp->qf_col); + qf_range_text(qfp, IObuff, IOSIZE); sprintf((char *)IObuff + STRLEN(IObuff), "%s", (char *)qf_types(qfp->qf_type, qfp->qf_nr)); msg_puts_attr((char *)IObuff, qfLineAttr); @@ -3685,6 +3698,37 @@ qf_fmt_text(char_u *text, char_u *buf, int bufsize) buf[i] = NUL; } +/* + * Range information from lnum, col, end_lnum, and end_col. + * Put the result in "buf[bufsize]". + */ + static void +qf_range_text(qfline_T *qfp, char_u *buf, int bufsize) +{ + int len; + vim_snprintf((char *)buf, bufsize, "%ld", qfp->qf_lnum); + len = (int)STRLEN(buf); + + if (qfp->qf_end_lnum > 0 && qfp->qf_lnum != qfp->qf_end_lnum) + { + vim_snprintf((char *)buf + len, bufsize - len, + "-%ld", qfp->qf_end_lnum); + len += (int)STRLEN(buf + len); + } + if (qfp->qf_col > 0) + { + vim_snprintf((char *)buf + len, bufsize - len, " col %d", qfp->qf_col); + len += (int)STRLEN(buf + len); + if (qfp->qf_end_col > 0 && qfp->qf_col != qfp->qf_end_col) + { + vim_snprintf((char *)buf + len, bufsize - len, + "-%d", qfp->qf_end_col); + len += (int)STRLEN(buf + len); + } + } + buf[len] = NUL; +} + /* * Display information (list number, list size and the title) about a * quickfix/location list. @@ -4565,17 +4609,9 @@ qf_buf_add_line( if (qfp->qf_lnum > 0) { - vim_snprintf((char *)IObuff + len, IOSIZE - len, "%ld", - qfp->qf_lnum); + qf_range_text(qfp, IObuff + len, IOSIZE - len); len += (int)STRLEN(IObuff + len); - if (qfp->qf_col > 0) - { - vim_snprintf((char *)IObuff + len, IOSIZE - len, - " col %d", qfp->qf_col); - len += (int)STRLEN(IObuff + len); - } - vim_snprintf((char *)IObuff + len, IOSIZE - len, "%s", (char *)qf_types(qfp->qf_type, qfp->qf_nr)); len += (int)STRLEN(IObuff + len); @@ -5953,7 +5989,9 @@ vgr_match_buflines( ml_get_buf(buf, regmatch->startpos[0].lnum + lnum, FALSE), regmatch->startpos[0].lnum + lnum, + regmatch->endpos[0].lnum + lnum, regmatch->startpos[0].col + 1, + regmatch->endpos[0].col + 1, FALSE, // vis_col NULL, // search pattern 0, // nr @@ -5996,7 +6034,9 @@ vgr_match_buflines( duplicate_name ? 0 : buf->b_fnum, str, lnum, + 0, matches[0] + col + 1, + 0, FALSE, // vis_col NULL, // search pattern 0, // nr @@ -6626,10 +6666,12 @@ get_qfline_items(qfline_T *qfp, list_T *list) buf[0] = qfp->qf_type; buf[1] = NUL; if (dict_add_number(dict, "bufnr", (long)bufnum) == FAIL - || dict_add_number(dict, "lnum", (long)qfp->qf_lnum) == FAIL - || dict_add_number(dict, "col", (long)qfp->qf_col) == FAIL - || dict_add_number(dict, "vcol", (long)qfp->qf_viscol) == FAIL - || dict_add_number(dict, "nr", (long)qfp->qf_nr) == FAIL + || dict_add_number(dict, "lnum", (long)qfp->qf_lnum) == FAIL + || dict_add_number(dict, "end_lnum", (long)qfp->qf_end_lnum) == FAIL + || dict_add_number(dict, "col", (long)qfp->qf_col) == FAIL + || dict_add_number(dict, "end_col", (long)qfp->qf_end_col) == FAIL + || dict_add_number(dict, "vcol", (long)qfp->qf_viscol) == FAIL + || dict_add_number(dict, "nr", (long)qfp->qf_nr) == FAIL || dict_add_string(dict, "module", qfp->qf_module) == FAIL || dict_add_string(dict, "pattern", qfp->qf_pattern) == FAIL || dict_add_string(dict, "text", qfp->qf_text) == FAIL @@ -7143,8 +7185,8 @@ qf_add_entry_from_dict( { static int did_bufnr_emsg; char_u *filename, *module, *pattern, *text, *type; - int bufnum, valid, status, col, vcol, nr; - long lnum; + int bufnum, valid, status, col, end_col, vcol, nr; + long lnum, end_lnum; if (first_entry) did_bufnr_emsg = FALSE; @@ -7153,7 +7195,9 @@ qf_add_entry_from_dict( module = dict_get_string(d, (char_u *)"module", TRUE); bufnum = (int)dict_get_number(d, (char_u *)"bufnr"); lnum = (int)dict_get_number(d, (char_u *)"lnum"); + end_lnum = (int)dict_get_number(d, (char_u *)"end_lnum"); col = (int)dict_get_number(d, (char_u *)"col"); + end_col = (int)dict_get_number(d, (char_u *)"end_col"); vcol = (int)dict_get_number(d, (char_u *)"vcol"); nr = (int)dict_get_number(d, (char_u *)"nr"); type = dict_get_string(d, (char_u *)"type", TRUE); @@ -7190,7 +7234,9 @@ qf_add_entry_from_dict( bufnum, text, lnum, + end_lnum, col, + end_col, vcol, // vis_col pattern, // search pattern nr, @@ -8058,8 +8104,11 @@ hgr_search_file( 0, line, lnum, + 0, (int)(p_regmatch->startp[0] - line) + 1, // col + (int)(p_regmatch->endp[0] - line) + + 1, // end_col FALSE, // vis_col NULL, // search pattern 0, // nr diff --git a/src/testdir/dumps/Test_quickfix_cwindow_1.dump b/src/testdir/dumps/Test_quickfix_cwindow_1.dump index ef153f9089..c2b487232e 100644 --- a/src/testdir/dumps/Test_quickfix_cwindow_1.dump +++ b/src/testdir/dumps/Test_quickfix_cwindow_1.dump @@ -4,9 +4,9 @@ |m|a|t|c|h|e|s| @67 |~+0#4040ff13&| @73 |X+1#0000000&|C|w|i|n|d|o|w| @48|1|,|4| @11|A|l@1 ->X+0#0000e05#ffff4012|C|w|i|n|d|o|w||+0#0000000&|1+0#af5f00255&| |c|o|l| |4||+0#0000000&| |s|o|m|e| @52 -|X+0#0000e05#ffffff0|C|w|i|n|d|o|w||+0#0000000&|2+0#af5f00255&| |c|o|l| |2||+0#0000000&| |t|e|x|t| @52 -|X+0#0000e05&|C|w|i|n|d|o|w||+0#0000000&|4+0#af5f00255&| |c|o|l| |6||+0#0000000&| |m|a|t|c|h|e|s| @49 +>X+0#0000e05#ffff4012|C|w|i|n|d|o|w||+0#0000000&|1+0#af5f00255&| |c|o|l| |4|-|5||+0#0000000&| |s|o|m|e| @50 +|X+0#0000e05#ffffff0|C|w|i|n|d|o|w||+0#0000000&|2+0#af5f00255&| |c|o|l| |2|-|3||+0#0000000&| |t|e|x|t| @50 +|X+0#0000e05&|C|w|i|n|d|o|w||+0#0000000&|4+0#af5f00255&| |c|o|l| |6|-|7||+0#0000000&| |m|a|t|c|h|e|s| @47 |~+0#4040ff13&| @73 |[+3#0000000&|Q|u|i|c|k|f|i|x| |L|i|s|t|]| |:|v|i|m|g|r|e|p| |e| |X|C|w|i|n|d|o|w| @20|1|,|1| @12|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_quickfix_cwindow_2.dump b/src/testdir/dumps/Test_quickfix_cwindow_2.dump index 4c87ca053f..0e7fa3c9ee 100644 --- a/src/testdir/dumps/Test_quickfix_cwindow_2.dump +++ b/src/testdir/dumps/Test_quickfix_cwindow_2.dump @@ -4,9 +4,9 @@ |m|a|t|c|h|e|s| @67 |~+0#4040ff13&| @73 |X+3#0000000&|C|w|i|n|d|o|w| @48|2|,|2| @11|A|l@1 -|X+0#0000e05&|C|w|i|n|d|o|w||+0#0000000&|1+0#af5f00255&| |c|o|l| |4||+0#0000000&| |s|o|m|e| @52 -|X+0#0000e05#ffff4012|C|w|i|n|d|o|w||+0#0000000&|2+0#af5f00255&| |c|o|l| |2||+0#0000000&| |t|e|x|t| @52 -|X+0#0000e05#ffffff0|C|w|i|n|d|o|w||+0#0000000&|4+0#af5f00255&| |c|o|l| |6||+0#0000000&| |m|a|t|c|h|e|s| @49 +|X+0#0000e05&|C|w|i|n|d|o|w||+0#0000000&|1+0#af5f00255&| |c|o|l| |4|-|5||+0#0000000&| |s|o|m|e| @50 +|X+0#0000e05#ffff4012|C|w|i|n|d|o|w||+0#0000000&|2+0#af5f00255&| |c|o|l| |2|-|3||+0#0000000&| |t|e|x|t| @50 +|X+0#0000e05#ffffff0|C|w|i|n|d|o|w||+0#0000000&|4+0#af5f00255&| |c|o|l| |6|-|7||+0#0000000&| |m|a|t|c|h|e|s| @47 |~+0#4040ff13&| @73 |[+1#0000000&|Q|u|i|c|k|f|i|x| |L|i|s|t|]| |:|v|i|m|g|r|e|p| |e| |X|C|w|i|n|d|o|w| @20|2|,|1| @12|A|l@1 |:+0&&|c|n|e|x|t| @68 diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim index 263dbbd6e2..e82ce683ec 100644 --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -134,6 +134,21 @@ func XlistTests(cchar) call assert_equal([' 2 Xtestfile1:1 col 3: Line1', \ ' 3: non-error 2', ' 4 Xtestfile2:2 col 2: Line2'], l) + " Ranged entries + call g:Xsetlist([{'lnum':10,'text':'Line1'}, + \ {'lnum':20,'col':10,'text':'Line2'}, + \ {'lnum':30,'col':15,'end_col':20,'text':'Line3'}, + \ {'lnum':40,'end_lnum':45,'text':'Line4'}, + \ {'lnum':50,'end_lnum':55,'col':15,'text':'Line5'}, + \ {'lnum':60,'end_lnum':65,'col':25,'end_col':35,'text':'Line6'}]) + let l = split(execute('Xlist', ""), "\n") + call assert_equal([' 1:10: Line1', + \ ' 2:20 col 10: Line2', + \ ' 3:30 col 15-20: Line3', + \ ' 4:40-45: Line4', + \ ' 5:50-55 col 15: Line5', + \ ' 6:60-65 col 25-35: Line6'], l) + " Different types of errors call g:Xsetlist([{'lnum':10,'col':5,'type':'W', 'text':'Warning','nr':11}, \ {'lnum':20,'col':10,'type':'e','text':'Error','nr':22}, @@ -644,6 +659,7 @@ func s:test_xhelpgrep(cchar) call assert_true(&buftype == 'help') call assert_true(winnr() == 1) call assert_true(winnr('$') == 2) + call assert_match('|\d\+ col \d\+-\d\+|', getbufline(winbufnr(2), 1)[0]) " This wipes out the buffer, make sure that doesn't cause trouble. Xclose @@ -1514,10 +1530,13 @@ func SetXlistTests(cchar, bnum) call s:setup_commands(a:cchar) call g:Xsetlist([{'bufnr': a:bnum, 'lnum': 1}, - \ {'bufnr': a:bnum, 'lnum': 2}]) + \ {'bufnr': a:bnum, 'lnum': 2, 'end_lnum': 3, 'col': 4, 'end_col': 5}]) let l = g:Xgetlist() call assert_equal(2, len(l)) call assert_equal(2, l[1].lnum) + call assert_equal(3, l[1].end_lnum) + call assert_equal(4, l[1].col) + call assert_equal(5, l[1].end_col) Xnext call g:Xsetlist([{'bufnr': a:bnum, 'lnum': 3}], 'a') @@ -2852,7 +2871,9 @@ func XvimgrepTests(cchar) let l = g:Xgetlist() call assert_equal(2, len(l)) call assert_equal(8, l[0].col) + call assert_equal(11, l[0].end_col) call assert_equal(12, l[1].col) + call assert_equal(15, l[1].end_col) 1Xvimgrep ?Editor? Xtestfile* let l = g:Xgetlist() @@ -5098,15 +5119,21 @@ func Xtest_qftextfunc(cchar) call assert_equal('Tqfexpr', &quickfixtextfunc) call assert_equal('', \ g:Xgetlist({'quickfixtextfunc' : 1}).quickfixtextfunc) - Xexpr ['F1:10:2:green', 'F1:20:4:blue'] + call g:Xsetlist([ + \ { 'filename': 'F1', 'lnum': 10, 'col': 2, + \ 'end_col': 7, 'text': 'green'}, + \ { 'filename': 'F1', 'lnum': 20, 'end_lnum': 25, 'col': 4, + \ 'end_col': 8, 'text': 'blue'}, + \ ]) + Xwindow call assert_equal('F1-L10C2-green', getline(1)) call assert_equal('F1-L20C4-blue', getline(2)) Xclose set quickfixtextfunc&vim Xwindow - call assert_equal('F1|10 col 2| green', getline(1)) - call assert_equal('F1|20 col 4| blue', getline(2)) + 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& set quickfixtextfunc& @@ -5339,7 +5366,7 @@ func Test_add_invalid_entry_with_qf_window() call setqflist(['bb'], 'a') call assert_equal(1, line('$')) call assert_equal(['Xfile1|10| aa'], getline(1, '$')) - call assert_equal([{'lnum': 10, 'bufnr': bufnr('Xfile1'), 'col': 0, 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'module': '', 'text': 'aa'}], getqflist()) + call assert_equal([{'lnum': 10, 'end_lnum': 0, 'bufnr': bufnr('Xfile1'), 'col': 0, 'end_col': 0, 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'module': '', 'text': 'aa'}], getqflist()) cclose endfunc diff --git a/src/testdir/test_tagjump.vim b/src/testdir/test_tagjump.vim index c789dc9615..92af0d0b0f 100644 --- a/src/testdir/test_tagjump.vim +++ b/src/testdir/test_tagjump.vim @@ -837,15 +837,16 @@ func Test_ltag() ltag third call assert_equal('Xfoo', bufname('')) call assert_equal(3, line('.')) - call assert_equal([{'lnum': 3, 'bufnr': bufnr('Xfoo'), 'col': 0, - \ 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': 0, 'type': '', - \ 'module': '', 'text': 'third'}], getloclist(0)) + call assert_equal([{'lnum': 3, 'end_lnum': 0, 'bufnr': bufnr('Xfoo'), + \ 'col': 0, 'end_col': 0, 'pattern': '', 'valid': 1, 'vcol': 0, + \ 'nr': 0, 'type': '', 'module': '', 'text': 'third'}], getloclist(0)) ltag second call assert_equal(2, line('.')) - call assert_equal([{'lnum': 0, 'bufnr': bufnr('Xfoo'), 'col': 0, - \ 'pattern': '^\Vint second() {}\$', 'valid': 1, 'vcol': 0, 'nr': 0, - \ 'type': '', 'module': '', 'text': 'second'}], getloclist(0)) + call assert_equal([{'lnum': 0, 'end_lnum': 0, 'bufnr': bufnr('Xfoo'), + \ 'col': 0, 'end_col': 0, 'pattern': '^\Vint second() {}\$', + \ 'valid': 1, 'vcol': 0, 'nr': 0, 'type': '', 'module': '', + \ 'text': 'second'}], getloclist(0)) call delete('Xtags') call delete('Xfoo') diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index 974ca8548b..9170cc9563 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -2943,7 +2943,9 @@ def Test_expr7_method_call() loclist->setloclist(0) assert_equal([{bufnr: bufnr, lnum: 42, + end_lnum: 0, col: 17, + end_col: 0, text: 'wrong', pattern: '', valid: 1, diff --git a/src/version.c b/src/version.c index 347074a1f5..adde02e851 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3019, /**/ 3018, /**/ From 2fb749568662c86992aea3b596458b9e470f223d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=3D=3FUTF-8=3Fq=3FDundar=3D20G=3DC3=3DB6c=3F=3D?= Date: Sat, 19 Jun 2021 21:38:25 +0200 Subject: [PATCH 12/28] patch 8.2.3020: unreachable code Problem: Unreachable code. Solution: Remove the code. (closes #8406) --- src/ex_docmd.c | 3 --- src/version.c | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 45f94017f2..2a9983f8a8 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -4523,9 +4523,6 @@ invalid_range(exarg_T *eap) #endif break; case ADDR_UNSIGNED: - if (eap->line2 < 0) - return _(e_invrange); - break; case ADDR_NONE: // Will give an error elsewhere. break; diff --git a/src/version.c b/src/version.c index adde02e851..d1201f4f6b 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3020, /**/ 3019, /**/ From 208f0b48b2c616b29f377a1408290111ed2663f7 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 20 Jun 2021 12:40:08 +0200 Subject: [PATCH 13/28] patch 8.2.3021: spaces allowed between option name and "!", "?", etc. Problem: Spaces allowed between option name and "!", "?", etc. Solution: Disallow spaces in Vim9 script, it was not documented. (closes #8408) --- src/option.c | 7 ++++--- src/testdir/test_vim9_script.vim | 20 ++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/option.c b/src/option.c index dd44fe4ac4..535213c19d 100644 --- a/src/option.c +++ b/src/option.c @@ -1306,9 +1306,10 @@ do_set( // remember character after option name afterchar = arg[len]; - // skip white space, allow ":set ai ?" - while (VIM_ISWHITE(arg[len])) - ++len; + if (!in_vim9script()) + // skip white space, allow ":set ai ?", ":set hlsearch !" + while (VIM_ISWHITE(arg[len])) + ++len; adding = FALSE; prepending = FALSE; diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index 4b0ab2bad7..1584f173a8 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -3937,6 +3937,26 @@ def Test_mapping_line_number() delfunc g:FuncA enddef +def Test_option_modifier() + var lines =<< trim END + set hlsearch & hlsearch ! + call assert_equal(1, &hlsearch) + END + CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + set hlsearch & + END + CheckScriptFailure(lines, 'E518:') + + lines =<< trim END + vim9script + set hlsearch & hlsearch ! + END + CheckScriptFailure(lines, 'E518:') +enddef + " Keep this last, it messes up highlighting. def Test_substitute_cmd() new diff --git a/src/version.c b/src/version.c index d1201f4f6b..d217fe615b 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3021, /**/ 3020, /**/ From f573c6e1ed58d46d694c802eaf5ae3662a952744 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sun, 20 Jun 2021 14:02:16 +0200 Subject: [PATCH 14/28] patch 8.2.3022: available encryption methods are not strong enough Problem: Available encryption methods are not strong enough. Solution: Add initial support for xchaha20. (Christian Brabandt, closes #8394) --- .github/workflows/ci.yml | 3 +- runtime/doc/eval.txt | 1 + runtime/doc/options.txt | 17 + runtime/doc/various.txt | 1 + src/INSTALLpc.txt | 3 + src/Make_cyg_ming.mak | 15 +- src/Make_mvc.mak | 29 +- src/auto/configure | 66 ++++ src/blowfish.c | 7 +- src/bufwrite.c | 19 +- src/config.h.in | 1 + src/configure.ac | 37 ++ src/crypt.c | 385 +++++++++++++++++-- src/crypt_zip.c | 6 +- src/errors.h | 19 + src/evalfunc.c | 7 + src/feature.h | 7 + src/fileio.c | 40 +- src/memline.c | 38 +- src/option.c | 4 + src/optionstr.c | 6 +- src/proto/blowfish.pro | 4 +- src/proto/crypt.pro | 14 +- src/proto/crypt_zip.pro | 4 +- src/structs.h | 5 +- src/testdir/samples/crypt_sodium_invalid.txt | Bin 0 -> 16504 bytes src/testdir/test_crypt.vim | 127 +++++- src/undo.c | 12 +- src/version.c | 7 + 29 files changed, 820 insertions(+), 64 deletions(-) create mode 100644 src/testdir/samples/crypt_sodium_invalid.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9127455bc1..e569b42397 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,7 +71,8 @@ jobs: cscope \ libgtk2.0-dev \ desktop-file-utils \ - libtool-bin + libtool-bin \ + libsodium-dev - name: Install clang-11 if: matrix.compiler == 'clang' diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index dd2d24232a..4620623c63 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -11971,6 +11971,7 @@ scrollbind Compiled with 'scrollbind' support. (always true) showcmd Compiled with 'showcmd' support. signs Compiled with |:sign| support. smartindent Compiled with 'smartindent' support. +sodium Compiled with libsodium for better crypt support sound Compiled with sound support, e.g. `sound_playevent()` spell Compiled with spell checking support |spell|. startuptime Compiled with |--startuptime| support. diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index e600dd91dd..19ddd9cb5f 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2384,6 +2384,23 @@ A jump table for the options with a short description can be found at |Q_op|. you write the file the encrypted bytes will be different. The whole undo file is encrypted, not just the pieces of text. + *E1193* *E1194* *E1195* *E1196* + *E1197* *E1198* *E1199* *E1200* *E1201* + xchacha20 XChaCha20 Cipher with Poly1305 Message Authentication + Code. Medium strong till strong encryption. + Encryption is provided by the libsodium library, it + requires Vim to be built with |+sodium| + It adds a seed and a message authentication code (MAC) + to the file. This needs at least a Vim 8.2.3022 to + read the encrypted file. + Encryption of swap files is not supported, therefore + no swap file will be used when xchacha20 encryption is + enabled. + Encryption of undo files is not yet supported, + therefore no undo file will currently be written. + CURRENTLY EXPERIMENTAL: Files written with this method + might have to be read back with the same version of + Vim if the binary format changes later. You should use "blowfish2", also to re-encrypt older files. diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index eb45922ccf..0ad12e8042 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -444,6 +444,7 @@ m *+ruby/dyn* Ruby interface |ruby-dynamic| |/dyn| T *+scrollbind* |'scrollbind'| B *+signs* |:sign| N *+smartindent* |'smartindent'| +B *+sodium* compiled with libsodium for better encryption support B *+sound* |sound_playevent()|, |sound_playfile()| functions, etc. N *+spell* spell checking support, see |spell| N *+startuptime* |--startuptime| argument diff --git a/src/INSTALLpc.txt b/src/INSTALLpc.txt index b5c1a0b58c..d5516c891a 100644 --- a/src/INSTALLpc.txt +++ b/src/INSTALLpc.txt @@ -322,6 +322,9 @@ MSYS2 has its own git package, and you can also install it via pacman: $ pacman -S git +For enabling libsodium support, you also need to install the package + + $ pacman -S mingw-w64-x86_64-libsodium 2.3. Keep the build environment up-to-date diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak index c33200b33e..e93165dbf4 100644 --- a/src/Make_cyg_ming.mak +++ b/src/Make_cyg_ming.mak @@ -41,6 +41,9 @@ DEBUG=no # set to yes to measure code coverage COVERAGE=no +# better encryption support using libsodium +#SODIUM=yes + # set to SIZE for size, SPEED for speed, MAXSPEED for maximum optimization OPTIMIZE=MAXSPEED @@ -517,6 +520,10 @@ CXXFLAGS = -std=gnu++11 WINDRES_FLAGS = EXTRA_LIBS = +ifdef SODIUM +DEFINES += -DHAVE_SODIUM +endif + ifdef GETTEXT DEFINES += -DHAVE_GETTEXT -DHAVE_LOCALE_H GETTEXTINCLUDE = $(GETTEXT)/include @@ -660,6 +667,10 @@ DEFINES += -DFEAT_DIRECTX_COLOR_EMOJI endif endif +ifeq ($(SODIUM),yes) +SODIUMLIB = -lsodium +endif + # Only allow XPM for a GUI build. ifeq (yes, $(GUI)) @@ -1064,7 +1075,7 @@ $(EXEOBJC): | $(OUTDIR) ifeq ($(VIMDLL),yes) $(TARGET): $(OBJ) - $(LINK) $(CFLAGS) $(LFLAGS) -o $@ $(OBJ) $(LIB) -lole32 -luuid -lgdi32 $(LUA_LIB) $(MZSCHEME_LIBDIR) $(MZSCHEME_LIB) $(PYTHONLIB) $(PYTHON3LIB) $(RUBYLIB) + $(LINK) $(CFLAGS) $(LFLAGS) -o $@ $(OBJ) $(LIB) -lole32 -luuid -lgdi32 $(LUA_LIB) $(MZSCHEME_LIBDIR) $(MZSCHEME_LIB) $(PYTHONLIB) $(PYTHON3LIB) $(RUBYLIB) $(SODIUMLIB) $(GVIMEXE): $(EXEOBJG) $(VIMDLLBASE).dll $(CC) -L. $(EXELFLAGS) -mwindows -o $@ $(EXEOBJG) -l$(VIMDLLBASE) @@ -1073,7 +1084,7 @@ $(VIMEXE): $(EXEOBJC) $(VIMDLLBASE).dll $(CC) -L. $(EXELFLAGS) -o $@ $(EXEOBJC) -l$(VIMDLLBASE) else $(TARGET): $(OBJ) - $(LINK) $(CFLAGS) $(LFLAGS) -o $@ $(OBJ) $(LIB) -lole32 -luuid $(LUA_LIB) $(MZSCHEME_LIBDIR) $(MZSCHEME_LIB) $(PYTHONLIB) $(PYTHON3LIB) $(RUBYLIB) + $(LINK) $(CFLAGS) $(LFLAGS) -o $@ $(OBJ) $(LIB) -lole32 -luuid $(LUA_LIB) $(MZSCHEME_LIBDIR) $(MZSCHEME_LIB) $(PYTHONLIB) $(PYTHON3LIB) $(RUBYLIB) $(SODIUMLIB) endif upx: exes diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index 42b6f8ddf6..c61bb27ab7 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -41,6 +41,9 @@ # # Sound support: SOUND=yes (default is yes) # +# Sodium support: SODIUM=[Path to Sodium directory] +# You need to install the msvc package from https://download.libsodium.org/libsodium/releases/ +# # DLL support (EXPERIMENTAL): VIMDLL=yes (default is no) # Creates vim{32,64}.dll, and stub gvim.exe and vim.exe. # The shared codes between the GUI and the console are built into @@ -372,6 +375,26 @@ SOUND = no ! endif !endif +!ifndef SODIUM +SODIUM = no +!endif + +!if "$(SODIUM)" != "no" +! if "$(CPU)" == "AMD64" +SOD_LIB = $(SODIUM)\x64\Release\v140\dynamic +! elseif "$(CPU)" == "i386" +SOD_LIB = $(SODIUM)\x86\Release\v140\dynamic +! else +SODIUM = no +! endif +!endif + +!if "$(SODIUM)" != "no" +SOD_INC = -I $(SODIUM)\include +SOD_DEFS = -DFEAT_SODIUM +SOD_LIB = $(SOD_LIB)\libsodium.lib +!endif + !ifndef NETBEANS NETBEANS = $(GUI) !endif @@ -491,7 +514,7 @@ CON_LIB = $(CON_LIB) /DELAYLOAD:comdlg32.dll /DELAYLOAD:ole32.dll DelayImp.lib CFLAGS = -c /W3 /GF /nologo $(CVARS) -I. -Iproto -DHAVE_PATHDEF -DWIN32 \ $(CSCOPE_DEFS) $(TERM_DEFS) $(SOUND_DEFS) $(NETBEANS_DEFS) $(CHANNEL_DEFS) \ - $(NBDEBUG_DEFS) $(XPM_DEFS) \ + $(NBDEBUG_DEFS) $(XPM_DEFS) $(SOD_DEFS) \ $(DEFINES) -DWINVER=$(WINVER) -D_WIN32_WINNT=$(WINVER) #>>>>> end of choices @@ -703,7 +726,7 @@ CFLAGS = $(CFLAGS) $(CFLAGS_DEPR) INCL = vim.h alloc.h ascii.h ex_cmds.h feature.h errors.h globals.h \ keymap.h macros.h option.h os_dos.h os_win32.h proto.h regexp.h \ - spell.h structs.h term.h beval.h $(NBDEBUG_INCL) + spell.h structs.h term.h beval.h $(NBDEBUG_INCL) $(SOD_INC) OBJ = \ $(OUTDIR)\arabic.obj \ @@ -1282,7 +1305,7 @@ conflags = $(conflags) /map /mapinfo:lines LINKARGS1 = $(linkdebug) $(conflags) LINKARGS2 = $(CON_LIB) $(GUI_LIB) $(NODEFAULTLIB) $(LIBC) $(OLE_LIB) user32.lib \ $(LUA_LIB) $(MZSCHEME_LIB) $(PERL_LIB) $(PYTHON_LIB) $(PYTHON3_LIB) $(RUBY_LIB) \ - $(TCL_LIB) $(SOUND_LIB) $(NETBEANS_LIB) $(XPM_LIB) $(LINK_PDB) + $(TCL_LIB) $(SOUND_LIB) $(NETBEANS_LIB) $(XPM_LIB) $(SOD_LIB) $(LINK_PDB) # Report link time code generation progress if used. !ifdef NODEBUG diff --git a/src/auto/configure b/src/auto/configure index 7ecc40ca51..cd678fd9f1 100755 --- a/src/auto/configure +++ b/src/auto/configure @@ -839,6 +839,7 @@ with_motif_lib with_tlib enable_largefile enable_canberra +enable_libsodium enable_acl enable_gpm enable_sysmouse @@ -1513,6 +1514,7 @@ Optional Features: --disable-desktop-database-update update disabled --disable-largefile omit support for large files --disable-canberra Do not use libcanberra. + --disable-libsodium Do not use libsodium. --disable-acl No check for ACL support. --disable-gpm Don't use gpm (Linux mouse daemon). --disable-sysmouse Don't use sysmouse (mouse in *BSD console). @@ -13005,6 +13007,70 @@ rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-libsodium argument" >&5 +$as_echo_n "checking --enable-libsodium argument... " >&6; } +# Check whether --enable-libsodium was given. +if test "${enable_libsodium+set}" = set; then : + enableval=$enable_libsodium; +else + enable_libsodium="maybe" +fi + + +if test "$enable_libsodium" = "maybe"; then + if test "$features" = "big" -o "$features" = "huge"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Defaulting to yes" >&5 +$as_echo "Defaulting to yes" >&6; } + enable_libsodium="yes" + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Defaulting to no" >&5 +$as_echo "Defaulting to no" >&6; } + enable_libsodium="no" + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_libsodium" >&5 +$as_echo "$enable_libsodium" >&6; } +fi +if test "$enable_libsodium" = "yes"; then + if test "x$PKG_CONFIG" != "xno"; then + libsodium_lib=`$PKG_CONFIG --libs libsodium 2>/dev/null` + libsodium_cflags=`$PKG_CONFIG --cflags libsodium 2>/dev/null` + fi + if test "x$libsodium_lib" = "x"; then + libsodium_lib=-lsodium + libsodium_cflags= + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libcanberra" >&5 +$as_echo_n "checking for libcanberra... " >&6; } + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $libsodium_cflags" + LIBS="$LIBS $libsodium_lib" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + # include + +int +main () +{ + + printf("%d", sodium_init()); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_SODIUM 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no; try installing libsodium-dev" >&5 +$as_echo "no; try installing libsodium-dev" >&6; }; CFLAGS="$ac_save_CFLAGS"; LIBS="$ac_save_LIBS" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for st_blksize" >&5 $as_echo_n "checking for st_blksize... " >&6; } diff --git a/src/blowfish.c b/src/blowfish.c index 342bcc406e..4502a1c5db 100644 --- a/src/blowfish.c +++ b/src/blowfish.c @@ -596,7 +596,8 @@ crypt_blowfish_encode( cryptstate_T *state, char_u *from, size_t len, - char_u *to) + char_u *to, + int last UNUSED) { bf_state_T *bfs = state->method_state; size_t i; @@ -619,7 +620,8 @@ crypt_blowfish_decode( cryptstate_T *state, char_u *from, size_t len, - char_u *to) + char_u *to, + int last UNUSED) { bf_state_T *bfs = state->method_state; size_t i; @@ -680,5 +682,4 @@ blowfish_self_test(void) } return OK; } - #endif // FEAT_CRYPT diff --git a/src/bufwrite.c b/src/bufwrite.c index c7c832cff7..c91bcd9958 100644 --- a/src/bufwrite.c +++ b/src/bufwrite.c @@ -30,6 +30,7 @@ struct bw_info int bw_flags; // FIO_ flags #ifdef FEAT_CRYPT buf_T *bw_buffer; // buffer being written + int bw_finish; // finish encrypting #endif char_u bw_rest[CONV_RESTLEN]; // not converted bytes int bw_restlen; // nr of bytes in bw_rest[] @@ -493,14 +494,14 @@ buf_write_bytes(struct bw_info *ip) if (crypt_works_inplace(ip->bw_buffer->b_cryptstate)) { # endif - crypt_encode_inplace(ip->bw_buffer->b_cryptstate, buf, len); + crypt_encode_inplace(ip->bw_buffer->b_cryptstate, buf, len, ip->bw_finish); # ifdef CRYPT_NOT_INPLACE } else { char_u *outbuf; - len = crypt_encode_alloc(curbuf->b_cryptstate, buf, len, &outbuf); + len = crypt_encode_alloc(curbuf->b_cryptstate, buf, len, &outbuf, ip->bw_finish); if (len == 0) return OK; // Crypt layer is buffering, will flush later. wlen = write_eintr(ip->bw_fd, outbuf, len); @@ -724,6 +725,7 @@ buf_write( #endif #ifdef FEAT_CRYPT write_info.bw_buffer = buf; + write_info.bw_finish = FALSE; #endif // After writing a file changedtick changes but we don't want to display @@ -2015,6 +2017,13 @@ restore_backup: ++s; if (++len != bufsize) continue; +#ifdef FEAT_CRYPT + if (write_info.bw_fd > 0 && lnum == end + && (write_info.bw_flags & FIO_ENCRYPTED) + && *buf->b_p_key != NUL && !filtering + && *ptr == NUL) + write_info.bw_finish = TRUE; + #endif if (buf_write_bytes(&write_info) == FAIL) { end = 0; // write error: break loop @@ -2118,6 +2127,12 @@ restore_backup: if (len > 0 && end > 0) { write_info.bw_len = len; +#ifdef FEAT_CRYPT + if (write_info.bw_fd > 0 && lnum >= end + && (write_info.bw_flags & FIO_ENCRYPTED) + && *buf->b_p_key != NUL && !filtering) + write_info.bw_finish = TRUE; + #endif if (buf_write_bytes(&write_info) == FAIL) end = 0; // write error nchars += len; diff --git a/src/config.h.in b/src/config.h.in index fbf4b2449c..0808cc3587 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -208,6 +208,7 @@ #undef HAVE_STRPTIME #undef HAVE_STRTOL #undef HAVE_CANBERRA +#undef HAVE_SODIUM #undef HAVE_ST_BLKSIZE #undef HAVE_SYSCONF #undef HAVE_SYSCTL diff --git a/src/configure.ac b/src/configure.ac index 9810ea1fc4..84b54dbf0a 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -3767,6 +3767,43 @@ if test "$enable_canberra" = "yes"; then AC_MSG_RESULT(no; try installing libcanberra-dev); CFLAGS="$ac_save_CFLAGS"; LIBS="$ac_save_LIBS") fi +AC_MSG_CHECKING(--enable-libsodium argument) +AC_ARG_ENABLE(libsodium, + [ --disable-libsodium Do not use libsodium.], + , [enable_libsodium="maybe"]) + +if test "$enable_libsodium" = "maybe"; then + if test "$features" = "big" -o "$features" = "huge"; then + AC_MSG_RESULT(Defaulting to yes) + enable_libsodium="yes" + else + AC_MSG_RESULT(Defaulting to no) + enable_libsodium="no" + fi +else + AC_MSG_RESULT($enable_libsodium) +fi +if test "$enable_libsodium" = "yes"; then + if test "x$PKG_CONFIG" != "xno"; then + libsodium_lib=`$PKG_CONFIG --libs libsodium 2>/dev/null` + libsodium_cflags=`$PKG_CONFIG --cflags libsodium 2>/dev/null` + fi + if test "x$libsodium_lib" = "x"; then + libsodium_lib=-lsodium + libsodium_cflags= + fi + AC_MSG_CHECKING(for libcanberra) + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $libsodium_cflags" + LIBS="$LIBS $libsodium_lib" + AC_TRY_LINK([ + # include + ], [ + printf("%d", sodium_init()); ], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SODIUM), + AC_MSG_RESULT(no; try installing libsodium-dev); CFLAGS="$ac_save_CFLAGS"; LIBS="$ac_save_LIBS") +fi dnl fstatfs() can take 2 to 4 arguments, try to use st_blksize if possible AC_MSG_CHECKING(for st_blksize) diff --git a/src/crypt.c b/src/crypt.c index 0164f1ce9d..35d4e14dc3 100644 --- a/src/crypt.c +++ b/src/crypt.c @@ -12,6 +12,10 @@ */ #include "vim.h" +#ifdef FEAT_SODIUM +# include +#endif + #if defined(FEAT_CRYPT) || defined(PROTO) /* * Optional encryption support. @@ -33,7 +37,7 @@ typedef struct { char *name; // encryption name as used in 'cryptmethod' char *magic; // magic bytes stored in file header int salt_len; // length of salt, or 0 when not using salt - int seed_len; // length of seed, or 0 when not using salt + int seed_len; // length of seed, or 0 when not using seed #ifdef CRYPT_NOT_INPLACE int works_inplace; // encryption/decryption can be done in-place #endif @@ -49,16 +53,16 @@ typedef struct { // Function pointers for encoding/decoding from one buffer into another. // Optional, however, these or the _buffer ones should be configured. void (*encode_fn)(cryptstate_T *state, char_u *from, size_t len, - char_u *to); + char_u *to, int last); void (*decode_fn)(cryptstate_T *state, char_u *from, size_t len, - char_u *to); + char_u *to, int last); // Function pointers for encoding and decoding, can buffer data if needed. // Optional (however, these or the above should be configured). long (*encode_buffer_fn)(cryptstate_T *state, char_u *from, size_t len, - char_u **newptr); + char_u **newptr, int last); long (*decode_buffer_fn)(cryptstate_T *state, char_u *from, size_t len, - char_u **newptr); + char_u **newptr, int last); // Function pointers for in-place encoding and decoding, used for // crypt_*_inplace(). "from" and "to" arguments will be equal. @@ -68,9 +72,9 @@ typedef struct { // padding to files). // This method is used for swap and undo files which have a rigid format. void (*encode_inplace_fn)(cryptstate_T *state, char_u *p1, size_t len, - char_u *p2); + char_u *p2, int last); void (*decode_inplace_fn)(cryptstate_T *state, char_u *p1, size_t len, - char_u *p2); + char_u *p2, int last); } cryptmethod_T; // index is method_nr of cryptstate_T, CRYPT_M_* @@ -126,10 +130,41 @@ static cryptmethod_T cryptmethods[CRYPT_M_COUNT] = { crypt_blowfish_encode, crypt_blowfish_decode, }, + // XChaCha20 using libsodium + { + "xchacha20", + "VimCrypt~04!", +#ifdef FEAT_SODIUM + crypto_pwhash_argon2id_SALTBYTES, // 16 +#else + 16, +#endif + 8, +#ifdef CRYPT_NOT_INPLACE + FALSE, +#endif + FALSE, + NULL, + crypt_sodium_init, + crypt_sodium_encode, crypt_sodium_decode, + crypt_sodium_buffer_encode, crypt_sodium_buffer_decode, + crypt_sodium_encode, crypt_sodium_decode, + }, + // NOTE: when adding a new method, use some random bytes for the magic key, // to avoid that a text file is recognized as encrypted. }; +#ifdef FEAT_SODIUM +typedef struct { + size_t count; + unsigned char key[crypto_box_SEEDBYTES]; + // 32, same as crypto_secretstream_xchacha20poly1305_KEYBYTES + crypto_secretstream_xchacha20poly1305_state + state; +} sodium_state_T; +#endif + #define CRYPT_MAGIC_LEN 12 // cannot change static char crypt_magic_head[] = "VimCrypt~"; @@ -260,7 +295,7 @@ crypt_create( state->method_nr = method_nr; if (cryptmethods[method_nr].init_fn( - state, key, salt, salt_len, seed, seed_len) == FAIL) + state, key, salt, salt_len, seed, seed_len) == FAIL) { vim_free(state); return NULL; @@ -365,9 +400,16 @@ crypt_create_for_writing( // TODO: Should this be crypt method specific? (Probably not worth // it). sha2_seed is pretty bad for large amounts of entropy, so make // that into something which is suitable for anything. - sha2_seed(salt, salt_len, seed, seed_len); +#ifdef FEAT_SODIUM + if (sodium_init() >= 0) + { + randombytes_buf(salt, salt_len); + randombytes_buf(seed, seed_len); + } + else +#endif + sha2_seed(salt, salt_len, seed, seed_len); } - state = crypt_create(method_nr, key, salt, salt_len, seed, seed_len); if (state == NULL) VIM_CLEAR(*header); @@ -380,7 +422,15 @@ crypt_create_for_writing( void crypt_free_state(cryptstate_T *state) { - vim_free(state->method_state); +#ifdef FEAT_SODIUM + if (state->method_nr == CRYPT_M_SOD) + { + sodium_memzero(state->method_state, sizeof(sodium_state_T)); + sodium_free(state->method_state); + } + else +#endif + vim_free(state->method_state); vim_free(state); } @@ -395,21 +445,22 @@ crypt_encode_alloc( cryptstate_T *state, char_u *from, size_t len, - char_u **newptr) + char_u **newptr, + int last) { cryptmethod_T *method = &cryptmethods[state->method_nr]; if (method->encode_buffer_fn != NULL) // Has buffer function, pass through. - return method->encode_buffer_fn(state, from, len, newptr); + return method->encode_buffer_fn(state, from, len, newptr, last); if (len == 0) // Not buffering, just return EOF. return (long)len; - *newptr = alloc(len); + *newptr = alloc(len + 50); if (*newptr == NULL) return -1; - method->encode_fn(state, from, len, *newptr); + method->encode_fn(state, from, len, *newptr, last); return (long)len; } @@ -423,13 +474,14 @@ crypt_decode_alloc( cryptstate_T *state, char_u *ptr, long len, - char_u **newptr) + char_u **newptr, + int last) { cryptmethod_T *method = &cryptmethods[state->method_nr]; if (method->decode_buffer_fn != NULL) // Has buffer function, pass through. - return method->decode_buffer_fn(state, ptr, len, newptr); + return method->decode_buffer_fn(state, ptr, len, newptr, last); if (len == 0) // Not buffering, just return EOF. @@ -438,7 +490,7 @@ crypt_decode_alloc( *newptr = alloc(len); if (*newptr == NULL) return -1; - method->decode_fn(state, ptr, len, *newptr); + method->decode_fn(state, ptr, len, *newptr, last); return len; } #endif @@ -451,9 +503,10 @@ crypt_encode( cryptstate_T *state, char_u *from, size_t len, - char_u *to) + char_u *to, + int last) { - cryptmethods[state->method_nr].encode_fn(state, from, len, to); + cryptmethods[state->method_nr].encode_fn(state, from, len, to, last); } #if 0 // unused @@ -465,9 +518,10 @@ crypt_decode( cryptstate_T *state, char_u *from, size_t len, - char_u *to) + char_u *to, + int last) { - cryptmethods[state->method_nr].decode_fn(state, from, len, to); + cryptmethods[state->method_nr].decode_fn(state, from, len, to, last); } #endif @@ -478,9 +532,11 @@ crypt_decode( crypt_encode_inplace( cryptstate_T *state, char_u *buf, - size_t len) + size_t len, + int last) { - cryptmethods[state->method_nr].encode_inplace_fn(state, buf, len, buf); + cryptmethods[state->method_nr].encode_inplace_fn(state, buf, len, + buf, last); } /* @@ -490,9 +546,11 @@ crypt_encode_inplace( crypt_decode_inplace( cryptstate_T *state, char_u *buf, - size_t len) + size_t len, + int last) { - cryptmethods[state->method_nr].decode_inplace_fn(state, buf, len, buf); + cryptmethods[state->method_nr].decode_inplace_fn(state, buf, len, + buf, last); } /* @@ -523,6 +581,19 @@ crypt_check_method(int method) msg_scroll = TRUE; msg(_("Warning: Using a weak encryption method; see :help 'cm'")); } + if (method == CRYPT_M_SOD) + { + // encryption uses padding and MAC, that does not work very well with + // swap and undo files, so disable them + mf_close_file(curbuf, TRUE); // remove the swap file + set_option_value((char_u *)"swf", 0, NULL, OPT_LOCAL); +#ifdef FEAT_PERSISTENT_UNDO + set_option_value((char_u *)"udf", 0, NULL, OPT_LOCAL); +#endif + + msg_scroll = TRUE; + msg(_("Note: Encryption of swapfile not supported, disabling swap- and undofile")); + } } void @@ -610,4 +681,266 @@ crypt_append_msg( } } + int +crypt_sodium_init( + cryptstate_T *state UNUSED, + char_u *key UNUSED, + char_u *salt UNUSED, + int salt_len UNUSED, + char_u *seed UNUSED, + int seed_len UNUSED) +{ +# ifdef FEAT_SODIUM + // crypto_box_SEEDBYTES == crypto_secretstream_xchacha20poly1305_KEYBYTES + unsigned char dkey[crypto_box_SEEDBYTES]; // 32 + sodium_state_T *sd_state; + + if (sodium_init() < 0) + return FAIL; + + sd_state = (sodium_state_T *)sodium_malloc(sizeof(sodium_state_T)); + sodium_memzero(sd_state, sizeof(sodium_state_T)); + + // derive a key from the password + if (crypto_pwhash(dkey, sizeof(dkey), (const char *)key, STRLEN(key), salt, + crypto_pwhash_OPSLIMIT_INTERACTIVE, crypto_pwhash_MEMLIMIT_INTERACTIVE, + crypto_pwhash_ALG_DEFAULT) != 0) + { + // out of memory + sodium_free(sd_state); + return FAIL; + } + memcpy(sd_state->key, dkey, crypto_box_SEEDBYTES); + sd_state->count = 0; + state->method_state = sd_state; + + return OK; +# else + emsg(e_libsodium_not_built_in); + return FAIL; +# endif +} + +/* + * Encrypt "from[len]" into "to[len]". + * "from" and "to" can be equal to encrypt in place. + * Call needs to ensure that there is enough space in to (for the header) + */ + void +crypt_sodium_encode( + cryptstate_T *state UNUSED, + char_u *from UNUSED, + size_t len UNUSED, + char_u *to UNUSED, + int last UNUSED) +{ +# ifdef FEAT_SODIUM + // crypto_box_SEEDBYTES == crypto_secretstream_xchacha20poly1305_KEYBYTES + sodium_state_T *sod_st = state->method_state; + unsigned char tag = last + ? crypto_secretstream_xchacha20poly1305_TAG_FINAL : 0; + + if (sod_st->count == 0) + { + if (len <= crypto_secretstream_xchacha20poly1305_HEADERBYTES) + { + emsg(e_libsodium_cannot_encrypt_header); + return; + } + crypto_secretstream_xchacha20poly1305_init_push(&sod_st->state, + to, sod_st->key); + to += crypto_secretstream_xchacha20poly1305_HEADERBYTES; + } + + if (sod_st->count && len <= crypto_secretstream_xchacha20poly1305_ABYTES) + { + emsg(e_libsodium_cannot_encrypt_buffer); + return; + } + + crypto_secretstream_xchacha20poly1305_push(&sod_st->state, to, NULL, + from, len, NULL, 0, tag); + + sod_st->count++; +# endif +} + +/* TODO: Unused + * Decrypt "from[len]" into "to[len]". + * "from" and "to" can be equal to encrypt in place. + */ + void +crypt_sodium_decode( + cryptstate_T *state UNUSED, + char_u *from UNUSED, + size_t len UNUSED, + char_u *to UNUSED, + int last UNUSED) +{ +# ifdef FEAT_SODIUM + // crypto_box_SEEDBYTES == crypto_secretstream_xchacha20poly1305_KEYBYTES + sodium_state_T *sod_st = state->method_state; + unsigned char tag; + unsigned long long buf_len; + char_u *p1 = from; + char_u *p2 = to; + char_u *buf_out; + + if (sod_st->count == 0 + && len <= crypto_secretstream_xchacha20poly1305_HEADERBYTES) + { + emsg(e_libsodium_cannot_decrypt_header); + return; + } + + buf_out = (char_u *)alloc(len); + + if (buf_out == NULL) + { + emsg(e_libsodium_cannot_allocate_buffer); + return; + } + if (sod_st->count == 0) + { + if (crypto_secretstream_xchacha20poly1305_init_pull( + &sod_st->state, from, sod_st->key) != 0) + { + emsg(e_libsodium_decryption_failed_header_incomplete); + goto fail; + } + + from += crypto_secretstream_xchacha20poly1305_HEADERBYTES; + len -= crypto_secretstream_xchacha20poly1305_HEADERBYTES; + + if (p1 == p2) + to += crypto_secretstream_xchacha20poly1305_HEADERBYTES; + } + + if (sod_st->count && len <= crypto_secretstream_xchacha20poly1305_ABYTES) + { + emsg(e_libsodium_cannot_decrypt_buffer); + return; + } + if (crypto_secretstream_xchacha20poly1305_pull(&sod_st->state, + buf_out, &buf_len, &tag, from, len, NULL, 0) != 0) + { + emsg(e_libsodium_decription_failed); + goto fail; + } + sod_st->count++; + + if (tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL && !last) + { + emsg(e_libsodium_decyption_failed_premature); + goto fail; + } + if (p1 == p2) + mch_memmove(p2, buf_out, buf_len); + +fail: + vim_free(buf_out); +# endif +} + +/* + * Encrypt "from[len]" into "to[len]". + * "from" and "to" can be equal to encrypt in place. + */ + long +crypt_sodium_buffer_encode( + cryptstate_T *state UNUSED, + char_u *from UNUSED, + size_t len UNUSED, + char_u **buf_out UNUSED, + int last UNUSED) +{ +# ifdef FEAT_SODIUM + // crypto_box_SEEDBYTES == crypto_secretstream_xchacha20poly1305_KEYBYTES + unsigned long long out_len; + char_u *ptr; + unsigned char tag = last + ? crypto_secretstream_xchacha20poly1305_TAG_FINAL : 0; + int length; + sodium_state_T *sod_st = state->method_state; + int first = (sod_st->count == 0); + + length = len + crypto_secretstream_xchacha20poly1305_ABYTES + + (first ? crypto_secretstream_xchacha20poly1305_HEADERBYTES : 0); + *buf_out = alloc_clear(length); + if (*buf_out == NULL) + { + emsg(e_libsodium_cannot_allocate_buffer); + return -1; + } + ptr = *buf_out; + + if (first) + { + crypto_secretstream_xchacha20poly1305_init_push(&sod_st->state, + ptr, sod_st->key); + ptr += crypto_secretstream_xchacha20poly1305_HEADERBYTES; + } + + crypto_secretstream_xchacha20poly1305_push(&sod_st->state, ptr, + &out_len, from, len, NULL, 0, tag); + + sod_st->count++; + return out_len + (first + ? crypto_secretstream_xchacha20poly1305_HEADERBYTES : 0); +# else + return -1; +# endif +} + +/* + * Decrypt "from[len]" into "to[len]". + * "from" and "to" can be equal to encrypt in place. + */ + long +crypt_sodium_buffer_decode( + cryptstate_T *state UNUSED, + char_u *from UNUSED, + size_t len UNUSED, + char_u **buf_out UNUSED, + int last UNUSED) +{ +# ifdef FEAT_SODIUM + // crypto_box_SEEDBYTES == crypto_secretstream_xchacha20poly1305_KEYBYTES + sodium_state_T *sod_st = state->method_state; + unsigned char tag; + unsigned long long out_len; + *buf_out = alloc_clear(len); + if (*buf_out == NULL) + { + emsg(e_libsodium_cannot_allocate_buffer); + return -1; + } + + if (sod_st->count == 0) + { + if (crypto_secretstream_xchacha20poly1305_init_pull(&sod_st->state, + from, sod_st->key) != 0) + { + emsg(e_libsodium_decryption_failed_header_incomplete); + return -1; + } + from += crypto_secretstream_xchacha20poly1305_HEADERBYTES; + len -= crypto_secretstream_xchacha20poly1305_HEADERBYTES; + sod_st->count++; + } + if (crypto_secretstream_xchacha20poly1305_pull(&sod_st->state, + *buf_out, &out_len, &tag, from, len, NULL, 0) != 0) + { + emsg(e_libsodium_decription_failed); + return -1; + } + + if (tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL && !last) + emsg(e_libsodium_decyption_failed_premature); + return (long) out_len; +# else + return -1; +# endif +} + #endif // FEAT_CRYPT diff --git a/src/crypt_zip.c b/src/crypt_zip.c index 42abe9350b..b11d7a329f 100644 --- a/src/crypt_zip.c +++ b/src/crypt_zip.c @@ -114,7 +114,8 @@ crypt_zip_encode( cryptstate_T *state, char_u *from, size_t len, - char_u *to) + char_u *to, + int last UNUSED) { zip_state_T *zs = state->method_state; size_t i; @@ -137,7 +138,8 @@ crypt_zip_decode( cryptstate_T *state, char_u *from, size_t len, - char_u *to) + char_u *to, + int last UNUSED) { zip_state_T *zs = state->method_state; size_t i; diff --git a/src/errors.h b/src/errors.h index 63e2659f73..cb93efd907 100644 --- a/src/errors.h +++ b/src/errors.h @@ -427,3 +427,22 @@ EXTERN char e_call_to_function_that_failed_to_compile_str[] INIT(= N_("E1191: Call to function that failed to compile: %s")); EXTERN char e_empty_function_name[] INIT(= N_("E1192: Empty function name")); +// libsodium +EXTERN char e_libsodium_not_built_in[] + INIT(= N_("E1193: cryptmethod xchacha20 not built into this Vim")); +EXTERN char e_libsodium_cannot_encrypt_header[] + INIT(= N_("E1194: Cannot encrypt header, not enough space")); +EXTERN char e_libsodium_cannot_encrypt_buffer[] + INIT(= N_("E1195: Cannot encrypt buffer, not enough space")); +EXTERN char e_libsodium_cannot_decrypt_header[] + INIT(= N_("E1196: Cannot decrypt header, not enough space")); +EXTERN char e_libsodium_cannot_allocate_buffer[] + INIT(= N_("E1197: Cannot allocate_buffer for encryption")); +EXTERN char e_libsodium_decryption_failed_header_incomplete[] + INIT(= N_("E1198: Decryption failed: Header incomplete!")); +EXTERN char e_libsodium_cannot_decrypt_buffer[] + INIT(= N_("E1199: Cannot decrypt buffer, not enough space")); +EXTERN char e_libsodium_decription_failed[] + INIT(= N_("E1200: Decryption failed: corrupted chunk!")); +EXTERN char e_libsodium_decyption_failed_premature[] + INIT(= N_("E1201: Decryption failed: pre-mature end of file!")); diff --git a/src/evalfunc.c b/src/evalfunc.c index 97f7b61070..9267117daa 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -5070,6 +5070,13 @@ f_has(typval_T *argvars, typval_T *rettv) 1 #else 0 +#endif + }, + {"sodium", +#ifdef FEAT_SODIUM + 1 +#else + 0 #endif }, {"sound", diff --git a/src/feature.h b/src/feature.h index 4df73d1cb4..2ae739aa82 100644 --- a/src/feature.h +++ b/src/feature.h @@ -593,6 +593,13 @@ # define FEAT_SOUND_CANBERRA #endif +/* + * libsodium - add cryptography support + */ +#if defined(HAVE_SODIUM) && defined(FEAT_BIG) +# define FEAT_SODIUM +#endif + // There are two ways to use XPM. #if (defined(HAVE_XM_XPMP_H) && defined(FEAT_GUI_MOTIF)) \ || defined(HAVE_X11_XPM_H) diff --git a/src/fileio.c b/src/fileio.c index 91c02bfcd0..e05dfa3a5d 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -13,6 +13,10 @@ #include "vim.h" +#ifdef FEAT_SODIUM +# include +#endif + #if defined(__TANDEM) # include // for SSIZE_MAX #endif @@ -148,6 +152,8 @@ readfile( char_u *p; off_T filesize = 0; int skip_read = FALSE; + off_T filesize_disk = 0; // file size read from disk + off_T filesize_count = 0; // counter #ifdef FEAT_CRYPT char_u *cryptkey = NULL; int did_ask_for_key = FALSE; @@ -215,6 +221,7 @@ readfile( int using_b_ffname; int using_b_fname; static char *msg_is_a_directory = N_("is a directory"); + int eof; au_did_filetype = FALSE; // reset before triggering any autocommands @@ -405,6 +412,7 @@ readfile( { buf_store_time(curbuf, &st, fname); curbuf->b_mtime_read = curbuf->b_mtime; + filesize_disk = st.st_size; #ifdef UNIX /* * Use the protection bits of the original file for the swap file. @@ -1080,6 +1088,7 @@ retry: { linerest = 0; filesize = 0; + filesize_count = 0; skip_count = lines_to_skip; read_count = lines_to_read; conv_restlen = 0; @@ -1263,7 +1272,23 @@ retry: /* * Read bytes from the file. */ +# ifdef FEAT_SODIUM + // Let the crypt layer work with a buffer size of 8192 + if (filesize == 0) + // set size to 8K + Sodium Crypt Metadata + size = WRITEBUFSIZE + 36 + + crypto_secretstream_xchacha20poly1305_HEADERBYTES + + crypto_secretstream_xchacha20poly1305_ABYTES; + + else if (filesize > 0 && (curbuf->b_cryptstate != NULL && + curbuf->b_cryptstate->method_nr == CRYPT_M_SOD)) + size = WRITEBUFSIZE + crypto_secretstream_xchacha20poly1305_ABYTES; +# endif + eof = size; size = read_eintr(fd, ptr, size); + filesize_count += size; + // hit end of file + eof = (size < eof || filesize_count == filesize_disk); } #ifdef FEAT_CRYPT @@ -1285,7 +1310,8 @@ retry: if (crypt_works_inplace(curbuf->b_cryptstate)) { # endif - crypt_decode_inplace(curbuf->b_cryptstate, ptr, size); + crypt_decode_inplace(curbuf->b_cryptstate, ptr, + size, eof); # ifdef CRYPT_NOT_INPLACE } else @@ -1294,8 +1320,16 @@ retry: int decrypted_size; decrypted_size = crypt_decode_alloc( - curbuf->b_cryptstate, ptr, size, &newptr); + curbuf->b_cryptstate, ptr, size, + &newptr, eof); + if (decrypted_size < 0) + { + // error message already given + error = TRUE; + vim_free(newptr); + break; + } // If the crypt layer is buffering, not producing // anything yet, need to read more. if (decrypted_size == 0) @@ -1325,6 +1359,7 @@ retry: if (newptr != NULL) mch_memmove(new_buffer + linerest, newptr, decrypted_size); + vim_free(newptr); } if (new_buffer != NULL) @@ -1334,6 +1369,7 @@ retry: new_buffer = NULL; line_start = buffer; ptr = buffer + linerest; + real_size = size; } size = decrypted_size; } diff --git a/src/memline.c b/src/memline.c index 58582c9d69..3b09a7dd49 100644 --- a/src/memline.c +++ b/src/memline.c @@ -48,6 +48,11 @@ # include #endif +// for randombytes_buf +#ifdef FEAT_SODIUM +# include +#endif + #if defined(SASC) || defined(__amigaos4__) # include // for Open() and Close() #endif @@ -64,12 +69,14 @@ typedef struct pointer_entry PTR_EN; // block/line-count pair #define BLOCK0_ID1_C0 'c' // block 0 id 1 'cm' 0 #define BLOCK0_ID1_C1 'C' // block 0 id 1 'cm' 1 #define BLOCK0_ID1_C2 'd' // block 0 id 1 'cm' 2 +#define BLOCK0_ID1_C3 'S' // block 0 id 1 'cm' 3 - but not actually used #if defined(FEAT_CRYPT) static int id1_codes[] = { BLOCK0_ID1_C0, // CRYPT_M_ZIP BLOCK0_ID1_C1, // CRYPT_M_BF BLOCK0_ID1_C2, // CRYPT_M_BF2 + BLOCK0_ID1_C3, // CRYPT_M_SOD - Unused! }; #endif @@ -426,11 +433,15 @@ ml_set_mfp_crypt(buf_T *buf) { int method_nr = crypt_get_method_nr(buf); - if (method_nr > CRYPT_M_ZIP) + if (method_nr > CRYPT_M_ZIP && method_nr < CRYPT_M_SOD) { // Generate a seed and store it in the memfile. sha2_seed(buf->b_ml.ml_mfp->mf_seed, MF_SEED_LEN, NULL, 0); } +#ifdef FEAT_SODIUM + else if (method_nr == CRYPT_M_SOD) + randombytes_buf(buf->b_ml.ml_mfp->mf_seed, MF_SEED_LEN); + #endif } } @@ -447,7 +458,7 @@ ml_set_b0_crypt(buf_T *buf, ZERO_BL *b0p) int method_nr = crypt_get_method_nr(buf); b0p->b0_id[1] = id1_codes[method_nr]; - if (method_nr > CRYPT_M_ZIP) + if (method_nr > CRYPT_M_ZIP && method_nr < CRYPT_M_SOD) { // Generate a seed and store it in block 0 and in the memfile. sha2_seed(&b0p->b0_seed, MF_SEED_LEN, NULL, 0); @@ -482,10 +493,17 @@ ml_set_crypt_key( int top; int old_method; - if (mfp == NULL) + if (mfp == NULL || mfp->mf_fd < 0) return; // no memfile yet, nothing to do old_method = crypt_method_nr_from_name(old_cm); + if (old_method == CRYPT_M_SOD || crypt_get_method_nr(buf) == CRYPT_M_SOD) + { + // close the swapfile + mf_close_file(buf, TRUE); + buf->b_p_swf = FALSE; + return; + } // First make sure the swapfile is in a consistent state, using the old // key and method. { @@ -911,7 +929,8 @@ ml_check_b0_id(ZERO_BL *b0p) || (b0p->b0_id[1] != BLOCK0_ID1 && b0p->b0_id[1] != BLOCK0_ID1_C0 && b0p->b0_id[1] != BLOCK0_ID1_C1 - && b0p->b0_id[1] != BLOCK0_ID1_C2) + && b0p->b0_id[1] != BLOCK0_ID1_C2 + && b0p->b0_id[1] != BLOCK0_ID1_C3) ) return FAIL; return OK; @@ -2402,7 +2421,9 @@ ml_sync_all(int check_file, int check_char) FOR_ALL_BUFFERS(buf) { - if (buf->b_ml.ml_mfp == NULL || buf->b_ml.ml_mfp->mf_fname == NULL) + if (buf->b_ml.ml_mfp == NULL + || buf->b_ml.ml_mfp->mf_fname == NULL + || buf->b_ml.ml_mfp->mf_fd < 0) continue; // no file ml_flush_line(buf); // flush buffered line @@ -5320,7 +5341,8 @@ ml_encrypt_data( mch_memmove(new_data, dp, head_end - (char_u *)dp); // Encrypt the text. - crypt_encode(state, text_start, text_len, new_data + dp->db_txt_start); + crypt_encode(state, text_start, text_len, new_data + dp->db_txt_start, + FALSE); crypt_free_state(state); // Clear the gap. @@ -5360,7 +5382,7 @@ ml_decrypt_data( if (state != NULL) { // Decrypt the text in place. - crypt_decode_inplace(state, text_start, text_len); + crypt_decode_inplace(state, text_start, text_len, FALSE); crypt_free_state(state); } } @@ -5407,7 +5429,7 @@ ml_crypt_prepare(memfile_T *mfp, off_T offset, int reading) // of the block for the salt. vim_snprintf((char *)salt, sizeof(salt), "%ld", (long)offset); return crypt_create(method_nr, key, salt, (int)STRLEN(salt), - seed, MF_SEED_LEN); + seed, MF_SEED_LEN); } #endif diff --git a/src/option.c b/src/option.c index 535213c19d..07bb71e4d6 100644 --- a/src/option.c +++ b/src/option.c @@ -2713,6 +2713,10 @@ set_bool_option( || (opt_flags & OPT_GLOBAL) || opt_flags == 0) && !curbufIsChanged() && curbuf->b_ml.ml_mfp != NULL) { +#ifdef FEAT_CRYPT + if (crypt_get_method_nr(curbuf) == CRYPT_M_SOD) + continue; +#endif u_compute_hash(hash); u_read_undo(NULL, hash, curbuf->b_fname); } diff --git a/src/optionstr.c b/src/optionstr.c index f332294ed8..47aee35be3 100644 --- a/src/optionstr.c +++ b/src/optionstr.c @@ -24,7 +24,11 @@ static char *(p_bo_values[]) = {"all", "backspace", "cursor", "complete", static char *(p_nf_values[]) = {"bin", "octal", "hex", "alpha", "unsigned", NULL}; static char *(p_ff_values[]) = {FF_UNIX, FF_DOS, FF_MAC, NULL}; #ifdef FEAT_CRYPT -static char *(p_cm_values[]) = {"zip", "blowfish", "blowfish2", NULL}; +static char *(p_cm_values[]) = {"zip", "blowfish", "blowfish2", + # ifdef FEAT_SODIUM + "xchacha20", + # endif + NULL}; #endif static char *(p_cmp_values[]) = {"internal", "keepascii", NULL}; static char *(p_dy_values[]) = {"lastline", "truncate", "uhex", NULL}; diff --git a/src/proto/blowfish.pro b/src/proto/blowfish.pro index 5998fb3581..6b2c45459a 100644 --- a/src/proto/blowfish.pro +++ b/src/proto/blowfish.pro @@ -1,6 +1,6 @@ /* blowfish.c */ -void crypt_blowfish_encode(cryptstate_T *state, char_u *from, size_t len, char_u *to); -void crypt_blowfish_decode(cryptstate_T *state, char_u *from, size_t len, char_u *to); +void crypt_blowfish_encode(cryptstate_T *state, char_u *from, size_t len, char_u *to, int last); +void crypt_blowfish_decode(cryptstate_T *state, char_u *from, size_t len, char_u *to, int last); int crypt_blowfish_init(cryptstate_T *state, char_u *key, char_u *salt, int salt_len, char_u *seed, int seed_len); int blowfish_self_test(void); /* vim: set ft=c : */ diff --git a/src/proto/crypt.pro b/src/proto/crypt.pro index e8ba18897c..2e58039209 100644 --- a/src/proto/crypt.pro +++ b/src/proto/crypt.pro @@ -1,6 +1,7 @@ /* crypt.c */ int crypt_method_nr_from_name(char_u *name); int crypt_method_nr_from_magic(char *ptr, int len); +int crypt_works_inplace(cryptstate_T *state); int crypt_get_method_nr(buf_T *buf); int crypt_whole_undofile(int method_nr); int crypt_get_header_len(int method_nr); @@ -11,12 +12,19 @@ cryptstate_T *crypt_create_from_header(int method_nr, char_u *key, char_u *heade cryptstate_T *crypt_create_from_file(FILE *fp, char_u *key); cryptstate_T *crypt_create_for_writing(int method_nr, char_u *key, char_u **header, int *header_len); void crypt_free_state(cryptstate_T *state); -void crypt_encode(cryptstate_T *state, char_u *from, size_t len, char_u *to); -void crypt_encode_inplace(cryptstate_T *state, char_u *buf, size_t len); -void crypt_decode_inplace(cryptstate_T *state, char_u *buf, size_t len); +long crypt_encode_alloc(cryptstate_T *state, char_u *from, size_t len, char_u **newptr, int last); +long crypt_decode_alloc(cryptstate_T *state, char_u *ptr, long len, char_u **newptr, int last); +void crypt_encode(cryptstate_T *state, char_u *from, size_t len, char_u *to, int last); +void crypt_encode_inplace(cryptstate_T *state, char_u *buf, size_t len, int last); +void crypt_decode_inplace(cryptstate_T *state, char_u *buf, size_t len, int last); void crypt_free_key(char_u *key); void crypt_check_method(int method); void crypt_check_current_method(void); char_u *crypt_get_key(int store, int twice); void crypt_append_msg(buf_T *buf); +int crypt_sodium_init(cryptstate_T *state, char_u *key, char_u *salt, int salt_len, char_u *seed, int seed_len); +void crypt_sodium_encode(cryptstate_T *state, char_u *from, size_t len, char_u *to, int last); +void crypt_sodium_decode(cryptstate_T *state, char_u *from, size_t len, char_u *to, int last); +long crypt_sodium_buffer_encode(cryptstate_T *state, char_u *from, size_t len, char_u **buf_out, int last); +long crypt_sodium_buffer_decode(cryptstate_T *state, char_u *from, size_t len, char_u **buf_out, int last); /* vim: set ft=c : */ diff --git a/src/proto/crypt_zip.pro b/src/proto/crypt_zip.pro index 868b131f0f..626d9855bf 100644 --- a/src/proto/crypt_zip.pro +++ b/src/proto/crypt_zip.pro @@ -1,5 +1,5 @@ /* crypt_zip.c */ int crypt_zip_init(cryptstate_T *state, char_u *key, char_u *salt, int salt_len, char_u *seed, int seed_len); -void crypt_zip_encode(cryptstate_T *state, char_u *from, size_t len, char_u *to); -void crypt_zip_decode(cryptstate_T *state, char_u *from, size_t len, char_u *to); +void crypt_zip_encode(cryptstate_T *state, char_u *from, size_t len, char_u *to, int last); +void crypt_zip_decode(cryptstate_T *state, char_u *from, size_t len, char_u *to, int last); /* vim: set ft=c : */ diff --git a/src/structs.h b/src/structs.h index 26ab315feb..d62b10f371 100644 --- a/src/structs.h +++ b/src/structs.h @@ -2513,11 +2513,12 @@ typedef struct { # define CRYPT_M_ZIP 0 # define CRYPT_M_BF 1 # define CRYPT_M_BF2 2 -# define CRYPT_M_COUNT 3 // number of crypt methods +# define CRYPT_M_SOD 3 +# define CRYPT_M_COUNT 4 // number of crypt methods // Currently all crypt methods work inplace. If one is added that isn't then // define this. -// # define CRYPT_NOT_INPLACE 1 +# define CRYPT_NOT_INPLACE 1 #endif #ifdef FEAT_PROP_POPUP diff --git a/src/testdir/samples/crypt_sodium_invalid.txt b/src/testdir/samples/crypt_sodium_invalid.txt new file mode 100644 index 0000000000000000000000000000000000000000..35e31b5ac9ef929af76c482a7e131fce7611ea73 GIT binary patch literal 16504 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.txt', a:hex) + call feedkeys(":split Xtest.txt\" . a:key . "\", 'xt') + call assert_equal(a:uncrypted_text, getline(1, len(a:uncrypted_text))) + bwipe! + call delete('Xtest.txt') + set key= +endfunc + func Test_uncrypt_zip() call Uncrypt_stable('zip', "VimCrypt~01!\u0006\u001clV'\u00de}Mg\u00a0\u00ea\u00a3V\u00a9\u00e7\u0007E#3\u008e2U\u00e9\u0097", "foofoo", ["1234567890", "aábbccddeĂ«ff"]) endfunc @@ -78,10 +98,115 @@ func Test_uncrypt_blowfish() call Uncrypt_stable('blowfish', "VimCrypt~02!k)\u00be\u0017\u0097#\u0016\u00ddS\u009c\u00f5=\u00ba\u00e0\u00c8#\u00a5M\u00b4\u0086J\u00c3A\u00cd\u00a5M\u00b4\u0086!\u0080\u0015\u009b\u00f5\u000f\u00e1\u00d2\u0019\u0082\u0016\u0098\u00f7\u000d\u00da", "barbar", ["asdfasdfasdf", "0001112223333"]) endfunc -func Test_uncrypt_blowfish2() +func Test_uncrypt_blowfish2a() call Uncrypt_stable('blowfish', "VimCrypt~03!\u001e\u00d1N\u00e3;\u00d3\u00c0\u00a0^C)\u0004\u00f7\u007f.\u00b6\u00abF\u000eS\u0019\u00e0\u008b6\u00d2[T\u00cb\u00a7\u0085\u00d8\u00be9\u000b\u00812\u000bQ\u00b3\u00cc@\u0097\u000f\u00df\u009a\u00adIv\u00aa.\u00d8\u00c9\u00ee\u009e`\u00bd$\u00af%\u00d0", "barburp", ["abcdefghijklmnopqrstuvwxyz", "!@#$%^&*()_+=-`~"]) endfunc +func Test_uncrypt_blowfish2() + call Uncrypt_stable('blowfish2', "VimCrypt~03!\u001e\u00d1N\u00e3;\u00d3\u00c0\u00a0^C)\u0004\u00f7\u007f.\u00b6\u00abF\u000eS\u0019\u00e0\u008b6\u00d2[T\u00cb\u00a7\u0085\u00d8\u00be9\u000b\u00812\u000bQ\u00b3\u00cc@\u0097\u000f\u00df\u009a\u00adIv\u00aa.\u00d8\u00c9\u00ee\u009e`\u00bd$\u00af%\u00d0", "barburp", ["abcdefghijklmnopqrstuvwxyz", "!@#$%^&*()_+=-`~"]) +endfunc + +func Test_uncrypt_xchacha20() + CheckFeature sodium + let hex=['00000000: 5669 6d43 7279 7074 7e30 3421 6b7d e607 vimCrypt~04!k}..', + \ '00000010: 4ea4 e99f 923e f67f 7b59 a80d 3bca 2f06 N....>..{Y..;./.', + \ '00000020: fa11 b951 8d09 0dc9 470f e7cf 8b90 4310 ...Q....G.....C.', + \ '00000030: 653b b83b e493 378b 0390 0e38 f912 626b e;.;..7....8..bk', + \ '00000040: a02e 4697 0254 2625 2d8e 3a0b 784b e89c ..F..T&%-.:.xK..', + \ '00000050: 0c67 a975 3c17 9319 8ffd 1463 7783 a1f3 .g.u<......cw...', + \ '00000060: d917 dcb3 8b3e ecd7 c7d4 086b 6059 7ead .....>.....k`Y~.', + \ '00000070: 9b07 f96b 5c1b 4d08 cd91 f208 5221 7484 ...k\.M.....R!t.', + \ '00000080: 72be 0136 84a1 d3 r..6...'] + " the file should be in latin1 encoding, this makes sure that readfile() + " retries several times converting the multi-byte characters + call Uncrypt_stable_xxd('xchacha20', hex, "sodium_crypt", ["abcdefghijklmnopqrstuvwxyzäöü", "ZZZ_äüöÄÜÖ_!@#$%^&*()_+=-`~"]) +endfunc + +func Test_uncrypt_xchacha20_invalid() + CheckFeature sodium + " load an invalid encrypted file and verify it can be decrypted with an + " error message + try + call feedkeys(":split samples/crypt_sodium_invalid.txt\sodium\", 'xt') + call assert_false(1, 'should not happen') + catch + call assert_exception('pre-mature') + endtry + call assert_match("Note: Encryption of swapfile not supported, disabling swap- and undofile", execute(':5messages')) + + call assert_equal(0, &swapfile) + call assert_equal("xchacha20", &cryptmethod) + call assert_equal('311111111111111111111111', getline('$')) + bw! +endfunc + +func Test_uncrypt_xchacha20_2() + CheckFeature sodium + sp Xcrypt_sodium.txt + " Create a larger file, so that Vim will write in several blocks + call setline(1, range(1,4000)) + call assert_equal(1, &swapfile) + set cryptmethod=xchacha20 + call feedkeys(":X\sodium\sodium\", 'xt') + " swapfile disabled + call assert_equal(0, &swapfile) + call assert_match("Note: Encryption of swapfile not supported, disabling swap- and undofile", execute(':messages')) + w! + " encrypted using xchacha20 + call assert_match("\[xchacha20\]", execute(':messages')) + bw! + call feedkeys(":sp Xcrypt_sodium.txt\sodium\", 'xt') + " successfully decrypted + call assert_equal(range(1, 4000)->map( {_, v -> string(v)}), getline(1,'$')) + set key= + w! + " enryption removed + call assert_match('"Xcrypt_sodium.txt" 4000L, 18893B written', execute(':message')) + bw! + call delete('Xcrypt_sodium.txt') + set cryptmethod&vim +endfunc + +func Test_uncrypt_xchacha20_3_persistent_undo() + CheckFeature sodium + CheckFeature persistent_undo + sp Xcrypt_sodium_undo.txt + set cryptmethod=xchacha20 undofile + call feedkeys(":X\sodium\sodium\", 'xt') + call assert_equal(0, &undofile) + let ufile=undofile(@%) + call append(0, ['monday', 'tuesday', 'wednesday', 'thursday', 'friday']) + call cursor(1, 1) + + set undolevels=100 + normal dd + set undolevels=100 + normal dd + set undolevels=100 + normal dd + set undolevels=100 + w! + bw! + call feedkeys(":sp Xcrypt_sodium_undo.txt\sodium\", 'xt') + " should fail + norm! u + call assert_match('Already at oldest change', execute(':1mess')) + call assert_fails('verbose rundo' .. fnameescape(ufile), 'E822') + bw! + set undolevels& cryptmethod& undofile& + call delete('Xcrypt_sodium_undo.txt') +endfunc + +func Test_encrypt_xchacha20_missing() + if has("sodium") + return + endif + sp Xcrypt_sodium_undo.txt + call assert_fails(':set cryptmethod=xchacha20', 'E474') + bw! + set cm& +endfunc + func Test_uncrypt_unknown_method() split Xuncrypt_unknown.txt set bin noeol key= fenc=latin1 diff --git a/src/undo.c b/src/undo.c index 12f3e968f3..4b1158a69e 100644 --- a/src/undo.c +++ b/src/undo.c @@ -963,7 +963,9 @@ undo_flush(bufinfo_T *bi) { if (bi->bi_buffer != NULL && bi->bi_state != NULL && bi->bi_used > 0) { - crypt_encode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_used); + // Last parameter is only used for sodium encryption and that + // explicitly disables encryption of undofiles. + crypt_encode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_used, FALSE); if (fwrite(bi->bi_buffer, bi->bi_used, (size_t)1, bi->bi_fp) != 1) return FAIL; bi->bi_used = 0; @@ -995,7 +997,9 @@ fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len) if (copy == NULL) return 0; } - crypt_encode(bi->bi_state, ptr, len, copy); + // Last parameter is only used for sodium encryption and that + // explicitly disables encryption of undofiles. + crypt_encode(bi->bi_state, ptr, len, copy, TRUE); i = fwrite(copy, len, (size_t)1, bi->bi_fp); if (copy != small_buf) vim_free(copy); @@ -1129,7 +1133,7 @@ undo_read(bufinfo_T *bi, char_u *buffer, size_t size) } bi->bi_avail = n; bi->bi_used = 0; - crypt_decode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_avail); + crypt_decode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_avail, FALSE); } n = size_todo; if (n > bi->bi_avail - bi->bi_used) @@ -1176,7 +1180,7 @@ read_string_decrypt(bufinfo_T *bi, int len) ptr[len] = NUL; #ifdef FEAT_CRYPT if (bi->bi_state != NULL && bi->bi_buffer == NULL) - crypt_decode_inplace(bi->bi_state, ptr, len); + crypt_decode_inplace(bi->bi_state, ptr, len, FALSE); #endif } return ptr; diff --git a/src/version.c b/src/version.c index d217fe615b..6464816121 100644 --- a/src/version.c +++ b/src/version.c @@ -553,6 +553,11 @@ static char *(features[]) = #else "-smartindent", #endif +#ifdef FEAT_SODIUM + "+sodium", +#else + "-sodium", +#endif #ifdef FEAT_SOUND "+sound", #else @@ -750,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3022, /**/ 3021, /**/ From ca81f0e834640cf3465b3b742e60c12e8a0956f2 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 20 Jun 2021 14:41:01 +0200 Subject: [PATCH 15/28] patch 8.2.3023: Vim9: arguments for execute() not checked at compile time Problem: Vim9: arguments for execute() not checked at compile time. Solution: Add a function to check the argument types. --- src/evalfunc.c | 24 +++++++++++++++++++++++- src/testdir/test_vim9_builtin.vim | 12 ++++++++++++ src/version.c | 2 ++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/evalfunc.c b/src/evalfunc.c index 9267117daa..fdcc00fe87 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -301,6 +301,27 @@ arg_list_or_blob(type_T *type, argcontext_T *context) return FAIL; } +/* + * Check "type" is a string or a list of strings. + */ + static int +arg_string_or_list(type_T *type, argcontext_T *context) +{ + if (type->tt_type == VAR_ANY || type->tt_type == VAR_STRING) + return OK; + if (type->tt_type != VAR_LIST) + { + arg_type_mismatch(&t_string, type, context->arg_idx + 1); + return FAIL; + } + if (type->tt_member->tt_type == VAR_ANY + || type->tt_member->tt_type == VAR_STRING) + return OK; + + arg_type_mismatch(&t_list_string, type, context->arg_idx + 1); + return FAIL; +} + /* * Check "type" is a list or a dict. */ @@ -385,6 +406,7 @@ argcheck_T arg1_string[] = {arg_string}; argcheck_T arg3_string_nr_bool[] = {arg_string, arg_number, arg_bool}; argcheck_T arg1_float_or_nr[] = {arg_float_or_nr}; argcheck_T arg2_listblob_item[] = {arg_list_or_blob, arg_item_of_prev}; +argcheck_T arg2_execute[] = {arg_string_or_list, arg_string}; argcheck_T arg23_extend[] = {arg_list_or_dict, arg_same_as_prev, arg_extend3}; argcheck_T arg23_extendnew[] = {arg_list_or_dict, arg_same_struct_as_prev, arg_extend3}; argcheck_T arg3_insert[] = {arg_list_or_blob, arg_item_of_prev, arg_number}; @@ -870,7 +892,7 @@ static funcentry_T global_functions[] = ret_number_bool, f_eventhandler}, {"executable", 1, 1, FEARG_1, NULL, ret_number, f_executable}, - {"execute", 1, 2, FEARG_1, NULL, + {"execute", 1, 2, FEARG_1, arg2_execute, ret_string, f_execute}, {"exepath", 1, 1, FEARG_1, NULL, ret_string, f_exepath}, diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index b98c840ce0..d8fd079bf1 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -324,6 +324,18 @@ def Test_executable() CheckDefExecFailure(['echo executable(true)'], 'E1174:') enddef +def Test_execute() + var res = execute("echo 'hello'") + assert_equal("\nhello", res) + res = execute(["echo 'here'", "echo 'there'"]) + assert_equal("\nhere\nthere", res) + + CheckDefFailure(['echo execute(123)'], 'E1013: Argument 1: type mismatch, expected string but got number') + CheckDefFailure(['echo execute([123])'], 'E1013: Argument 1: type mismatch, expected list but got list') + CheckDefExecFailure(['echo execute(["xx", 123])'], 'E492') + CheckDefFailure(['echo execute("xx", 123)'], 'E1013: Argument 2: type mismatch, expected string but got number') +enddef + def Test_exepath() CheckDefExecFailure(['echo exepath(true)'], 'E1174:') CheckDefExecFailure(['echo exepath(v:null)'], 'E1174:') diff --git a/src/version.c b/src/version.c index 6464816121..d341ecd410 100644 --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3023, /**/ 3022, /**/ From 2fb1b89d721682585228f9a6ee7da4b6d3c6634d Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 20 Jun 2021 15:03:15 +0200 Subject: [PATCH 16/28] patch 8.2.3024: execute() function test fails Problem: execute() function test fails. Solution: Adjust test for different error. --- src/testdir/test_execute_func.vim | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_execute_func.vim b/src/testdir/test_execute_func.vim index c08b2390f2..aa73e0f317 100644 --- a/src/testdir/test_execute_func.vim +++ b/src/testdir/test_execute_func.vim @@ -40,7 +40,7 @@ func Test_execute_string() if has('float') call assert_fails('call execute(3.4)', 'E492:') call assert_equal("\nx", execute("echo \"x\"", 3.4)) - call CheckDefExecAndScriptFailure(['execute("echo \"x\"", 3.4)'], 'E806:') + call CheckDefExecAndScriptFailure2(['execute("echo \"x\"", 3.4)'], 'E1013: Argument 2: type mismatch, expected string but got float', 'E806:') endif endfunc diff --git a/src/version.c b/src/version.c index d341ecd410..ff8e46675b 100644 --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3024, /**/ 3023, /**/ From 0d5e1ec37fbe75e18acba6f650c59bf91063108c Mon Sep 17 00:00:00 2001 From: "shane.xb.qian" Date: Sun, 20 Jun 2021 16:31:00 +0200 Subject: [PATCH 17/28] patch 8.2.3025: not enough tests for quickfix end_col and end_lnum Problem: Not enough tests for quickfix end_col and end_lnum. Solution: Add a few more test cases. (Shane-XB-Qian, closes #8409) --- src/testdir/test_quickfix.vim | 37 ++++++++++++++++++++++++++++++++++- src/version.c | 2 ++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim index e82ce683ec..a6a20a8451 100644 --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -5366,7 +5366,42 @@ func Test_add_invalid_entry_with_qf_window() call setqflist(['bb'], 'a') call assert_equal(1, line('$')) call assert_equal(['Xfile1|10| aa'], getline(1, '$')) - call assert_equal([{'lnum': 10, 'end_lnum': 0, 'bufnr': bufnr('Xfile1'), 'col': 0, 'end_col': 0, 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'module': '', 'text': 'aa'}], getqflist()) + call assert_equal([{'lnum': 10 , 'end_lnum': 0 , 'bufnr': bufnr('Xfile1') , 'col': 0 , 'end_col': 0 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , getqflist()) + + call setqflist([{'lnum': 10 , 'bufnr': bufnr('Xfile1') , 'col': 0 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , 'r') + call assert_equal(1 , line('$')) + call assert_equal(['Xfile1|10| aa'] , getline(1 , '$')) + call assert_equal([{'lnum': 10 , 'end_lnum': 0 , 'bufnr': bufnr('Xfile1') , 'col': 0 , 'end_col': 0 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , getqflist()) + + call setqflist([{'lnum': 10 , 'end_lnum': 0 , 'bufnr': bufnr('Xfile1') , 'col': 0 , 'end_col': 0 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , 'r') + call assert_equal(1 , line('$')) + call assert_equal(['Xfile1|10| aa'] , getline(1 , '$')) + call assert_equal([{'lnum': 10 , 'end_lnum': 0 , 'bufnr': bufnr('Xfile1') , 'col': 0 , 'end_col': 0 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , getqflist()) + + call setqflist([{'lnum': 10 , 'end_lnum': -123 , 'bufnr': bufnr('Xfile1') , 'col': 0 , 'end_col': -456 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , 'r') + call assert_equal(1 , line('$')) + call assert_equal(['Xfile1|10| aa'] , getline(1 , '$')) + call assert_equal([{'lnum': 10 , 'end_lnum': -123 , 'bufnr': bufnr('Xfile1') , 'col': 0 , 'end_col': -456 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , getqflist()) + + call setqflist([{'lnum': 10 , 'end_lnum': -123 , 'bufnr': bufnr('Xfile1') , 'col': 666 , 'end_col': 0 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , 'r') + call assert_equal(1 , line('$')) + call assert_equal(['Xfile1|10 col 666| aa'] , getline(1 , '$')) + call assert_equal([{'lnum': 10 , 'end_lnum': -123 , 'bufnr': bufnr('Xfile1') , 'col': 666 , 'end_col': 0 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , getqflist()) + + call setqflist([{'lnum': 10 , 'end_lnum': -123 , 'bufnr': bufnr('Xfile1') , 'col': 666 , 'end_col': -456 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , 'r') + call assert_equal(1 , line('$')) + call assert_equal(['Xfile1|10 col 666| aa'] , getline(1 , '$')) + call assert_equal([{'lnum': 10 , 'end_lnum': -123 , 'bufnr': bufnr('Xfile1') , 'col': 666 , 'end_col': -456 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , getqflist()) + + call setqflist([{'lnum': 10 , 'end_lnum': -123 , 'bufnr': bufnr('Xfile1') , 'col': 666 , 'end_col': 222 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , 'r') + call assert_equal(1 , line('$')) + call assert_equal(['Xfile1|10 col 666-222| aa'] , getline(1 , '$')) + call assert_equal([{'lnum': 10 , 'end_lnum': -123 , 'bufnr': bufnr('Xfile1') , 'col': 666 , 'end_col': 222 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , getqflist()) + + call setqflist([{'lnum': 10 , 'end_lnum': 6 , 'bufnr': bufnr('Xfile1') , 'col': 666 , 'end_col': 222 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , 'r') + call assert_equal(1 , line('$')) + call assert_equal(['Xfile1|10-6 col 666-222| aa'] , getline(1 , '$')) + call assert_equal([{'lnum': 10 , 'end_lnum': 6 , 'bufnr': bufnr('Xfile1') , 'col': 666 , 'end_col': 222 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , getqflist()) cclose endfunc diff --git a/src/version.c b/src/version.c index ff8e46675b..961238bf9b 100644 --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3025, /**/ 3024, /**/ From 4f8f54280fa728b7d5a63b67d02b60a3b3dce543 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 20 Jun 2021 19:28:14 +0200 Subject: [PATCH 18/28] patch 8.2.3026: Vim9: cannot set breakpoint in compiled function Problem: Vim9: cannot set breakpoint in compiled function. Solution: Check for breakpoint when calling a function. --- src/debugger.c | 2 +- src/structs.h | 5 +++++ src/testdir/test_debugger.vim | 25 +++++++++++++++++++++++ src/version.c | 2 ++ src/vim.h | 4 ++-- src/vim9.h | 2 +- src/vim9execute.c | 37 +++++++++++++++++++++++++++++++++-- 7 files changed, 71 insertions(+), 6 deletions(-) diff --git a/src/debugger.c b/src/debugger.c index b96846310a..52a04907ef 100644 --- a/src/debugger.c +++ b/src/debugger.c @@ -606,7 +606,7 @@ dbg_parsearg( } if (bp->dbg_type == DBG_FUNC) - bp->dbg_name = vim_strsave(p); + bp->dbg_name = vim_strsave(STRNCMP(p, "g:", 2) == 0 ? p + 2 : p); else if (here) bp->dbg_name = vim_strsave(curbuf->b_ffname); else if (bp->dbg_type == DBG_EXPR) diff --git a/src/structs.h b/src/structs.h index d62b10f371..ae2c0b850c 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1629,6 +1629,11 @@ typedef struct # endif garray_T uf_lines; // function lines + + int uf_debug_tick; // when last checked for a breakpoint in this + // function. + int uf_has_breakpoint; // TRUE when a breakpoint has been set in + // this function. # ifdef FEAT_PROFILE int uf_profiling; // TRUE when func is being profiled int uf_prof_initialized; diff --git a/src/testdir/test_debugger.vim b/src/testdir/test_debugger.vim index 411909d1c7..a576a8c929 100644 --- a/src/testdir/test_debugger.vim +++ b/src/testdir/test_debugger.vim @@ -932,6 +932,31 @@ func Test_Backtrace_DefFunction() call delete('Xtest2.vim') endfunc +func Test_debug_DefFunction() + CheckCWD + let file =<< trim END + vim9script + def g:SomeFunc() + echo "here" + echo "and" + echo "there" + enddef + breakadd func 2 g:SomeFunc + END + call writefile(file, 'XtestDebug.vim') + + let buf = RunVimInTerminal('-S XtestDebug.vim', {}) + + call RunDbgCmd(buf,':call SomeFunc()', ['line 2: echo "and"']) + call RunDbgCmd(buf,'next', ['line 3: echo "there"']) + + call RunDbgCmd(buf, 'cont') + + call StopVimInTerminal(buf) + call delete('Xtest1.vim') + call delete('Xtest2.vim') +endfunc + func Test_debug_def_function() CheckCWD let file =<< trim END diff --git a/src/version.c b/src/version.c index 961238bf9b..6fe8fa4988 100644 --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3026, /**/ 3025, /**/ diff --git a/src/vim.h b/src/vim.h index 22fe8d5fed..e45a1de2e0 100644 --- a/src/vim.h +++ b/src/vim.h @@ -1804,9 +1804,9 @@ typedef enum { // Keep in sync with INSTRUCTIONS(). #ifdef FEAT_PROFILE -# define COMPILE_TYPE(ufunc) (debug_break_level > 0 ? CT_DEBUG : do_profiling == PROF_YES && (ufunc)->uf_profiling ? CT_PROFILE : CT_NONE) +# define COMPILE_TYPE(ufunc) (debug_break_level > 0 || ufunc->uf_has_breakpoint ? CT_DEBUG : do_profiling == PROF_YES && (ufunc)->uf_profiling ? CT_PROFILE : CT_NONE) #else -# define COMPILE_TYPE(ufunc) debug_break_level > 0 ? CT_DEBUG : CT_NONE +# define COMPILE_TYPE(ufunc) debug_break_level > 0 || ufunc->uf_has_breakpoint ? CT_DEBUG : CT_NONE #endif /* diff --git a/src/vim9.h b/src/vim9.h index b8c7133586..97e2132a3e 100644 --- a/src/vim9.h +++ b/src/vim9.h @@ -498,7 +498,7 @@ extern garray_T def_functions; // Keep in sync with COMPILE_TYPE() #ifdef FEAT_PROFILE # define INSTRUCTIONS(dfunc) \ - (debug_break_level > 0 \ + (debug_break_level > 0 || dfunc->df_ufunc->uf_has_breakpoint \ ? (dfunc)->df_instr_debug \ : ((do_profiling == PROF_YES && (dfunc->df_ufunc)->uf_profiling) \ ? (dfunc)->df_instr_prof \ diff --git a/src/vim9execute.c b/src/vim9execute.c index 218357a825..8b817e4505 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -147,6 +147,23 @@ exe_newlist(int count, ectx_T *ectx) return OK; } +/* + * If debug_tick changed check if "ufunc" has a breakpoint and update + * "uf_has_breakpoint". + */ + static void +update_has_breakpoint(ufunc_T *ufunc) +{ + if (ufunc->uf_debug_tick != debug_tick) + { + linenr_T breakpoint; + + ufunc->uf_debug_tick = debug_tick; + breakpoint = dbg_find_breakpoint(FALSE, ufunc->uf_name, 0); + ufunc->uf_has_breakpoint = breakpoint > 0; + } +} + /* * Call compiled function "cdf_idx" from compiled code. * This adds a stack frame and sets the instruction pointer to the start of the @@ -1444,6 +1461,20 @@ handle_debug(isn_T *iptr, ectx_T *ectx) garray_T ga; int lnum; + if (ex_nesting_level > debug_break_level) + { + linenr_T breakpoint; + + if (!ufunc->uf_has_breakpoint) + return; + + // check for the next breakpoint if needed + breakpoint = dbg_find_breakpoint(FALSE, ufunc->uf_name, + iptr->isn_lnum - 1); + if (breakpoint <= 0 || breakpoint > iptr->isn_lnum) + return; + } + SOURCING_LNUM = iptr->isn_lnum; debug_context = ectx; debug_var_count = iptr->isn_arg.number; @@ -4206,8 +4237,7 @@ exec_instructions(ectx_T *ectx) break; case ISN_DEBUG: - if (ex_nesting_level <= debug_break_level) - handle_debug(iptr, ectx); + handle_debug(iptr, ectx); break; case ISN_SHUFFLE: @@ -4383,6 +4413,9 @@ call_def_function( #undef STACK_TV_VAR #define STACK_TV_VAR(idx) (((typval_T *)ectx.ec_stack.ga_data) + ectx.ec_frame_idx + STACK_FRAME_SIZE + idx) + // Update uf_has_breakpoint if needed. + update_has_breakpoint(ufunc); + if (ufunc->uf_def_status == UF_NOT_COMPILED || ufunc->uf_def_status == UF_COMPILE_ERROR || (func_needs_compiling(ufunc, COMPILE_TYPE(ufunc)) From 2ac4b2536a40e5cd75b4ff7a3d44a282d2f4f008 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 20 Jun 2021 20:09:42 +0200 Subject: [PATCH 19/28] patch 8.2.3027: Vim9: breakpoint in compiled function not always checked Problem: Vim9: breakpoint in compiled function not always checked. Solution: Check for breakpoint when calling compiled function from compiled function. --- src/testdir/test_debugger.vim | 22 ++++++++++++++++++++++ src/version.c | 2 ++ src/vim9execute.c | 3 +++ 3 files changed, 27 insertions(+) diff --git a/src/testdir/test_debugger.vim b/src/testdir/test_debugger.vim index a576a8c929..81e86588cc 100644 --- a/src/testdir/test_debugger.vim +++ b/src/testdir/test_debugger.vim @@ -940,7 +940,22 @@ func Test_debug_DefFunction() echo "here" echo "and" echo "there" + breakadd func 2 LocalFunc + LocalFunc() enddef + + def LocalFunc() + echo "first" + echo "second" + breakadd func 1 LegacyFunc + LegacyFunc() + enddef + + func LegacyFunc() + echo "legone" + echo "legtwo" + endfunc + breakadd func 2 g:SomeFunc END call writefile(file, 'XtestDebug.vim') @@ -949,6 +964,13 @@ func Test_debug_DefFunction() call RunDbgCmd(buf,':call SomeFunc()', ['line 2: echo "and"']) call RunDbgCmd(buf,'next', ['line 3: echo "there"']) + call RunDbgCmd(buf,'next', ['line 4: breakadd func 2 LocalFunc']) + + " continue, next breakpoint is in LocalFunc() + call RunDbgCmd(buf,'cont', ['line 2: echo "second"']) + + " continue, next breakpoint is in LegacyFunc() + call RunDbgCmd(buf,'cont', ['line 1: echo "legone"']) call RunDbgCmd(buf, 'cont') diff --git a/src/version.c b/src/version.c index 6fe8fa4988..58d99e8af1 100644 --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3027, /**/ 3026, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index 8b817e4505..dfceb27d00 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -229,6 +229,9 @@ call_dfunc( } #endif + // Update uf_has_breakpoint if needed. + update_has_breakpoint(ufunc); + // When debugging and using "cont" switches to the not-debugged // instructions, may need to still compile them. if ((func_needs_compiling(ufunc, COMPILE_TYPE(ufunc)) From f1e7449d567c630601aa0cec6c663b791785a668 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Mon, 21 Jun 2021 18:44:26 +0200 Subject: [PATCH 20/28] patch 8.2.3028: GUI mouse events not tested Problem: GUI mouse events not tested. Solution: Add test_gui_mouse_event(). Add mouse tests. Also add a few viminfo tests. (Yegappan Lakshmanan, closes #8407) --- runtime/doc/eval.txt | 2 + runtime/doc/testing.txt | 26 +++- runtime/doc/usr_41.txt | 1 + src/evalfunc.c | 2 + src/proto/testing.pro | 1 + src/testdir/test_gui.vim | 224 ++++++++++++++++++++++++++++++++++- src/testdir/test_viminfo.vim | 70 ++++++++++- src/testing.c | 14 +++ src/version.c | 2 + 9 files changed, 337 insertions(+), 5 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 4620623c63..1f52d9f05b 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -3022,6 +3022,8 @@ test_feedinput({string}) none add key sequence to input buffer test_garbagecollect_now() none free memory right now for testing test_garbagecollect_soon() none free memory soon for testing test_getvalue({string}) any get value of an internal variable +test_gui_mouse_event({button}, {row}, {col}, {repeated}, {mods}) + none add a mouse event to the input buffer test_ignore_error({expr}) none ignore a specific error test_null_blob() Blob null value for testing test_null_channel() Channel null value for testing diff --git a/runtime/doc/testing.txt b/runtime/doc/testing.txt index 6fd2d45887..db505e7efc 100644 --- a/runtime/doc/testing.txt +++ b/runtime/doc/testing.txt @@ -1,4 +1,4 @@ -*testing.txt* For Vim version 8.2. Last change: 2021 Apr 02 +*testing.txt* For Vim version 8.2. Last change: 2021 Jun 21 VIM REFERENCE MANUAL by Bram Moolenaar @@ -78,6 +78,30 @@ test_getvalue({name}) *test_getvalue()* Can also be used as a |method|: > GetName()->test_getvalue() +< + *test_gui_mouse_event()* +test_gui_mouse_event({button}, {row}, {col}, {multiclick}, {modifiers}) + Inject a mouse button click event. This function works only + when GUI is running. + The supported values for {button} are: + 0 right mouse button + 1 middle mouse button + 2 left mouse button + 3 mouse button release + 4 scroll wheel down + 5 scroll wheel up + 6 scroll wheel left + 7 scroll wheel right + {row} and {col} specify the location of the mouse click. + To inject a multiclick event, set {multiclick} to 1. + The supported values for {modifiers} are: + 4 shift is pressed + 8 alt is pressed + 16 ctrl is pressed + After injecting the mouse event you probably should call + |feedkeys()| to have them processed, e.g.: > + call feedkeys("y", 'Lx!') + test_ignore_error({expr}) *test_ignore_error()* Ignore any error containing {expr}. A normal message is given diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index 09d5a39db7..de7f197078 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -1021,6 +1021,7 @@ Testing: *test-functions* test_garbagecollect_now() free memory right now test_garbagecollect_soon() set a flag to free memory soon test_getvalue() get value of an internal variable + test_gui_mouse_event() add a GUI mouse event to the input buffer test_ignore_error() ignore a specific error message test_null_blob() return a null Blob test_null_channel() return a null Channel diff --git a/src/evalfunc.c b/src/evalfunc.c index fdcc00fe87..f8af8ef812 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -1696,6 +1696,8 @@ static funcentry_T global_functions[] = ret_void, f_test_garbagecollect_soon}, {"test_getvalue", 1, 1, FEARG_1, NULL, ret_number, f_test_getvalue}, + {"test_gui_mouse_event", 5, 5, 0, NULL, + ret_void, f_test_gui_mouse_event}, {"test_ignore_error", 1, 1, FEARG_1, NULL, ret_void, f_test_ignore_error}, {"test_null_blob", 0, 0, 0, NULL, diff --git a/src/proto/testing.pro b/src/proto/testing.pro index d229030cb5..3e203f8fd6 100644 --- a/src/proto/testing.pro +++ b/src/proto/testing.pro @@ -34,5 +34,6 @@ void f_test_unknown(typval_T *argvars, typval_T *rettv); void f_test_void(typval_T *argvars, typval_T *rettv); void f_test_scrollbar(typval_T *argvars, typval_T *rettv); void f_test_setmouse(typval_T *argvars, typval_T *rettv); +void f_test_gui_mouse_event(typval_T *argvars, typval_T *rettv); void f_test_settime(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ diff --git a/src/testdir/test_gui.vim b/src/testdir/test_gui.vim index 69f140b713..3472012c4a 100644 --- a/src/testdir/test_gui.vim +++ b/src/testdir/test_gui.vim @@ -389,7 +389,7 @@ func Test_set_guifont() endif " This only works if 'renderoptions' exists and does not work for Windows XP - " and older. + " and older. if exists('+renderoptions') && windowsversion() !~ '^[345]\.' " doing this four times used to cause a crash set renderoptions=type:directx @@ -875,4 +875,226 @@ func Test_gui_recursive_mapping() nunmap a endfunc +" Test GUI mouse events +func Test_gui_mouse_event() + set mousemodel=extend + call test_override('no_query_mouse', 1) + new + call setline(1, ['one two three', 'four five six']) + + " place the cursor using left click + call cursor(1, 1) + call test_gui_mouse_event(0, 2, 4, 0, 0) + call test_gui_mouse_event(3, 2, 4, 0, 0) + call feedkeys("\", 'Lx!') + call assert_equal([0, 2, 4, 0], getpos('.')) + + " select and yank a word + let @" = '' + call test_gui_mouse_event(0, 1, 9, 0, 0) + call test_gui_mouse_event(0, 1, 9, 1, 0) + call test_gui_mouse_event(3, 1, 9, 0, 0) + call feedkeys("y", 'Lx!') + call assert_equal('three', @") + + " create visual selection using right click + let @" = '' + call test_gui_mouse_event(0, 2, 6, 0, 0) + call test_gui_mouse_event(3, 2, 6, 0, 0) + call test_gui_mouse_event(2, 2, 13, 0, 0) + call test_gui_mouse_event(3, 2, 13, 0, 0) + call feedkeys("y", 'Lx!') + call assert_equal('five six', @") + + " paste using middle mouse button + let @* = 'abc ' + call feedkeys('""', 'Lx!') + call test_gui_mouse_event(1, 1, 9, 0, 0) + call test_gui_mouse_event(3, 1, 9, 0, 0) + call feedkeys("\", 'Lx!') + call assert_equal(['one two abc three', 'four five six'], getline(1, '$')) + + " extend visual selection using right click in visual mode + let @" = '' + call cursor(1, 1) + call feedkeys('v', 'Lx!') + call test_gui_mouse_event(2, 1, 17, 0, 0) + call test_gui_mouse_event(3, 1, 17, 0, 0) + call feedkeys("y", 'Lx!') + call assert_equal('one two abc three', @") + + " extend visual selection using mouse drag + let @" = '' + call cursor(1, 1) + call test_gui_mouse_event(0, 2, 1, 0, 0) + call test_gui_mouse_event(0x43, 2, 9, 0, 0) + call test_gui_mouse_event(0x3, 2, 9, 0, 0) + call feedkeys("y", 'Lx!') + call assert_equal('four five', @") + + " select text by moving the mouse + let @" = '' + call cursor(1, 1) + redraw! + call test_gui_mouse_event(0, 1, 4, 0, 0) + call test_gui_mouse_event(0x700, 1, 9, 0, 0) + call test_gui_mouse_event(0x700, 1, 13, 0, 0) + call test_gui_mouse_event(0x3, 1, 13, 0, 0) + call feedkeys("y", 'Lx!') + call assert_equal(' two abc t', @") + + " Using mouse in insert mode + call cursor(1, 1) + call feedkeys('i', 't') + call test_gui_mouse_event(0, 2, 11, 0, 0) + call test_gui_mouse_event(3, 2, 11, 0, 0) + call feedkeys("po\", 'Lx!') + call assert_equal(['one two abc three', 'four five posix'], getline(1, '$')) + + %d _ + call setline(1, range(1, 100)) + " scroll up + call test_gui_mouse_event(0x200, 2, 1, 0, 0) + call test_gui_mouse_event(0x200, 2, 1, 0, 0) + call test_gui_mouse_event(0x200, 2, 1, 0, 0) + call feedkeys("H", 'Lx!') + call assert_equal(10, line('.')) + + " scroll down + call test_gui_mouse_event(0x100, 2, 1, 0, 0) + call test_gui_mouse_event(0x100, 2, 1, 0, 0) + call feedkeys("H", 'Lx!') + call assert_equal(4, line('.')) + + %d _ + set nowrap + call setline(1, range(10)->join('')->repeat(10)) + " scroll left + call test_gui_mouse_event(0x500, 1, 5, 0, 0) + call test_gui_mouse_event(0x500, 1, 10, 0, 0) + call test_gui_mouse_event(0x500, 1, 15, 0, 0) + call feedkeys('g0', 'Lx!') + call assert_equal(19, col('.')) + + " scroll right + call test_gui_mouse_event(0x600, 1, 15, 0, 0) + call test_gui_mouse_event(0x600, 1, 10, 0, 0) + call feedkeys('g0', 'Lx!') + call assert_equal(7, col('.')) + set wrap& + + %d _ + call setline(1, repeat([repeat('a', 60)], 10)) + + " record various mouse events + let mouseEventNames = [ + \ 'LeftMouse', 'LeftRelease', '2-LeftMouse', '3-LeftMouse', + \ 'S-LeftMouse', 'A-LeftMouse', 'C-LeftMouse', 'MiddleMouse', + \ 'MiddleRelease', '2-MiddleMouse', '3-MiddleMouse', + \ 'S-MiddleMouse', 'A-MiddleMouse', 'C-MiddleMouse', + \ 'RightMouse', 'RightRelease', '2-RightMouse', + \ '3-RightMouse', 'S-RightMouse', 'A-RightMouse', 'C-RightMouse', + \ 'X1Mouse', 'S-X1Mouse', 'A-X1Mouse', 'C-X1Mouse', 'X2Mouse', + \ 'S-X2Mouse', 'A-X2Mouse', 'C-X2Mouse' + \ ] + let mouseEventCodes = map(copy(mouseEventNames), "'<' .. v:val .. '>'") + let g:events = [] + for e in mouseEventCodes + exe 'nnoremap ' .. e .. ' call add(g:events, "' .. + \ substitute(e, '[<>]', '', 'g') .. '")' + endfor + + " Test various mouse buttons (0 - Left, 1 - Middle, 2 - Right, 0x300 - X1, + " 0x300- X2) + for button in [0, 1, 2, 0x300, 0x400] + " Single click + call test_gui_mouse_event(button, 2, 5, 0, 0) + call test_gui_mouse_event(3, 2, 5, 0, 0) + + " Double/Triple click is supported by only the Left/Middle/Right mouse + " buttons + if button <= 2 + " Double Click + call test_gui_mouse_event(button, 2, 5, 0, 0) + call test_gui_mouse_event(button, 2, 5, 1, 0) + call test_gui_mouse_event(3, 2, 5, 0, 0) + + " Triple Click + call test_gui_mouse_event(button, 2, 5, 0, 0) + call test_gui_mouse_event(button, 2, 5, 1, 0) + call test_gui_mouse_event(button, 2, 5, 1, 0) + call test_gui_mouse_event(3, 2, 5, 0, 0) + endif + + " Shift click + call test_gui_mouse_event(button, 3, 7, 0, 4) + call test_gui_mouse_event(3, 3, 7, 0, 4) + + " Alt click + call test_gui_mouse_event(button, 3, 7, 0, 8) + call test_gui_mouse_event(3, 3, 7, 0, 8) + + " Ctrl click + call test_gui_mouse_event(button, 3, 7, 0, 16) + call test_gui_mouse_event(3, 3, 7, 0, 16) + + call feedkeys("\", 'Lx!') + endfor + + call assert_equal(['LeftMouse', 'LeftRelease', 'LeftMouse', '2-LeftMouse', + \ 'LeftMouse', '2-LeftMouse', '3-LeftMouse', 'S-LeftMouse', + \ 'A-LeftMouse', 'C-LeftMouse', 'MiddleMouse', 'MiddleRelease', + \ 'MiddleMouse', '2-MiddleMouse', 'MiddleMouse', '2-MiddleMouse', + \ '3-MiddleMouse', 'S-MiddleMouse', 'A-MiddleMouse', 'C-MiddleMouse', + \ 'RightMouse', 'RightRelease', 'RightMouse', '2-RightMouse', + \ 'RightMouse', '2-RightMouse', '3-RightMouse', 'S-RightMouse', + \ 'A-RightMouse', 'C-RightMouse', 'X1Mouse', 'S-X1Mouse', 'A-X1Mouse', + \ 'C-X1Mouse', 'X2Mouse', 'S-X2Mouse', 'A-X2Mouse', 'C-X2Mouse'], + \ g:events) + + for e in mouseEventCodes + exe 'nunmap ' .. e + endfor + + " modeless selection + set mouse= + let save_guioptions = &guioptions + set guioptions+=A + %d _ + call setline(1, ['one two three', 'four five sixteen']) + call cursor(1, 1) + redraw! + " Double click should select the word and copy it to clipboard + let @* = '' + call test_gui_mouse_event(0, 2, 11, 0, 0) + call test_gui_mouse_event(0, 2, 11, 1, 0) + call test_gui_mouse_event(3, 2, 11, 0, 0) + call feedkeys("\", 'Lx!') + call assert_equal([0, 1, 1, 0], getpos('.')) + call assert_equal('sixteen', @*) + " Right click should extend the selection from cursor + call cursor(1, 6) + redraw! + let @* = '' + call test_gui_mouse_event(2, 1, 11, 0, 0) + call test_gui_mouse_event(3, 1, 11, 0, 0) + call feedkeys("\", 'Lx!') + call assert_equal([0, 1, 6, 0], getpos('.')) + call assert_equal('wo thr', @*) + " Middle click should paste the clipboard contents + call cursor(2, 1) + redraw! + call test_gui_mouse_event(1, 1, 11, 0, 0) + call test_gui_mouse_event(3, 1, 11, 0, 0) + call feedkeys("\", 'Lx!') + call assert_equal([0, 2, 7, 0], getpos('.')) + call assert_equal('wo thrfour five sixteen', getline(2)) + set mouse& + let &guioptions = save_guioptions + + bw! + call test_override('no_query_mouse', 0) + set mousemodel& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_viminfo.vim b/src/testdir/test_viminfo.vim index 0518b91d06..6c1a711668 100644 --- a/src/testdir/test_viminfo.vim +++ b/src/testdir/test_viminfo.vim @@ -63,6 +63,7 @@ func Test_global_vars() let g:MY_GLOBAL_NULL = test_null let test_none = v:none let g:MY_GLOBAL_NONE = test_none + let g:MY_GLOBAL_FUNCREF = function('min') set viminfo='100,<50,s10,h,!,nviminfo wv! Xviminfo @@ -77,6 +78,7 @@ func Test_global_vars() unlet g:MY_GLOBAL_TRUE unlet g:MY_GLOBAL_NULL unlet g:MY_GLOBAL_NONE + unlet g:MY_GLOBAL_FUNCREF rv! Xviminfo call assert_equal("Vim Editor", g:MY_GLOBAL_STRING) @@ -89,6 +91,7 @@ func Test_global_vars() call assert_equal(test_true, g:MY_GLOBAL_TRUE) call assert_equal(test_null, g:MY_GLOBAL_NULL) call assert_equal(test_none, g:MY_GLOBAL_NONE) + call assert_false(exists("g:MY_GLOBAL_FUNCREF")) " When reading global variables from viminfo, if a variable cannot be " modified, then the value should not be changed. @@ -230,7 +233,26 @@ func Test_cmdline_history() call assert_equal("echo " . long800, histget(':', -2)) call assert_equal("echo 'one'", histget(':', -3)) + " If the value for the '/' or ':' or '@' field in 'viminfo' is zero, then + " the corresponding history entries are not saved. + set viminfo='100,/0,:0,@0,<50,s10,h,!,nviminfo + call histdel('/') + call histdel(':') + call histdel('@') + call histadd('/', 'foo') + call histadd(':', 'bar') + call histadd('@', 'baz') + wviminfo! Xviminfo + call histdel('/') + call histdel(':') + call histdel('@') + rviminfo! Xviminfo + call assert_equal('', histget('/')) + call assert_equal('', histget(':')) + call assert_equal('', histget('@')) + call delete('Xviminfo') + set viminfo&vim endfunc func Test_cmdline_history_order() @@ -346,7 +368,33 @@ func Test_viminfo_registers() let len += 1 endwhile + " If the maximum number of lines saved for a register ('<' in 'viminfo') is + " zero, then register values should not be saved. + let @a = 'abc' + set viminfo='100,<0,s10,h,!,nviminfo + wviminfo Xviminfo + let @a = 'xyz' + rviminfo! Xviminfo + call assert_equal('xyz', @a) + " repeat the test with '"' instead of '<' + let @b = 'def' + set viminfo='100,\"0,s10,h,!,nviminfo + wviminfo Xviminfo + let @b = 'rst' + rviminfo! Xviminfo + call assert_equal('rst', @b) + + " If the maximum size of an item ('s' in 'viminfo') is zero, then register + " values should not be saved. + let @c = '123' + set viminfo='100,<20,s0,h,!,nviminfo + wviminfo Xviminfo + let @c = '456' + rviminfo! Xviminfo + call assert_equal('456', @c) + call delete('Xviminfo') + set viminfo&vim endfunc func Test_viminfo_marks() @@ -541,15 +589,30 @@ func Test_viminfo_bad_syntax() call delete('Xviminfo') endfunc -func Test_viminfo_bad_register_syntax() +func Test_viminfo_bad_syntax2() let lines = [] call add(lines, '|1,4') - call add(lines, '|3') " invalid number of fields for a register type - call add(lines, '|3,1,1,1,1,,1,"x"') " invalid value for the width field + + " bad viminfo syntax for history barline + call add(lines, '|2') " invalid number of fields in a history barline + call add(lines, '|2,9,1,1,"x"') " invalid value for the history type + call add(lines, '|2,0,,1,"x"') " no timestamp + call add(lines, '|2,0,1,1,10') " non-string text + + " bad viminfo syntax for register barline + call add(lines, '|3') " invalid number of fields in a register barline + call add(lines, '|3,1,1,1,1,,1,"x"') " missing width field call add(lines, '|3,0,80,1,1,1,1,"x"') " invalid register number call add(lines, '|3,0,10,5,1,1,1,"x"') " invalid register type call add(lines, '|3,0,10,1,20,1,1,"x"') " invalid line count call add(lines, '|3,0,10,1,0,1,1') " zero line count + + " bad viminfo syntax for mark barline + call add(lines, '|4') " invalid number of fields in a mark barline + call add(lines, '|4,1,1,1,1,1') " invalid value for file name + call add(lines, '|4,20,1,1,1,"x"') " invalid value for file name + call add(lines, '|4,49,0,1,1,"x"') " invalid value for line number + call writefile(lines, 'Xviminfo') rviminfo Xviminfo call delete('Xviminfo') @@ -901,6 +964,7 @@ func Test_viminfo_perm() " Try to write the viminfo to a directory call mkdir('Xdir') call assert_fails('wviminfo Xdir', 'E137:') + call assert_fails('rviminfo Xdir', 'E195:') call delete('Xdir', 'rf') endfunc diff --git a/src/testing.c b/src/testing.c index cded3c73a0..9f5a2f5acb 100644 --- a/src/testing.c +++ b/src/testing.c @@ -1215,6 +1215,20 @@ f_test_setmouse(typval_T *argvars, typval_T *rettv UNUSED) mouse_col = (time_t)tv_get_number(&argvars[1]) - 1; } + void +f_test_gui_mouse_event(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_GUI + int button = tv_get_number(&argvars[0]); + int row = tv_get_number(&argvars[1]); + int col = tv_get_number(&argvars[2]); + int repeated_click = tv_get_number(&argvars[3]); + int_u mods = tv_get_number(&argvars[4]); + + gui_send_mouse_event(button, TEXT_X(col - 1), TEXT_Y(row - 1), repeated_click, mods); +#endif +} + void f_test_settime(typval_T *argvars, typval_T *rettv UNUSED) { diff --git a/src/version.c b/src/version.c index 58d99e8af1..db2433db99 100644 --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3028, /**/ 3027, /**/ From 035bd1c99f2a8eda5ee886adde4f97ea71fb167f Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 21 Jun 2021 19:44:11 +0200 Subject: [PATCH 21/28] patch 8.2.3029: Vim9: crash when using operator and list unpack assignment Problem: Vim9: crash when using operator and list unpack assignment. (Naohiro Ono) Solution: Get variable value before operation. (closes #8416) --- src/ex_docmd.c | 13 +++++++++-- src/testdir/test_vim9_assign.vim | 23 ++++++++++++++++++++ src/testdir/test_vim9_disassemble.vim | 31 +++++++++++++++++++++++++++ src/version.c | 2 ++ src/vim9.h | 7 ++++++ src/vim9compile.c | 26 +++++++++++----------- src/vim9execute.c | 14 ++++++------ 7 files changed, 96 insertions(+), 20 deletions(-) diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 2a9983f8a8..027d139da4 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -3485,6 +3485,8 @@ find_ex_command( // can't be an assignment. if (*eap->cmd == '[') { + char_u *eq; + p = to_name_const_end(eap->cmd); if (p == eap->cmd && *p == '[') { @@ -3493,12 +3495,19 @@ find_ex_command( p = skip_var_list(eap->cmd, TRUE, &count, &semicolon, TRUE); } - if (p == NULL || p == eap->cmd || *skipwhite(p) != '=') + eq = p; + if (eq != NULL) + { + eq = skipwhite(eq); + if (vim_strchr((char_u *)"+-*/%", *eq) != NULL) + ++eq; + } + if (p == NULL || p == eap->cmd || *eq != '=') { eap->cmdidx = CMD_eval; return eap->cmd; } - if (p > eap->cmd && *skipwhite(p) == '=') + if (p > eap->cmd && *eq == '=') { eap->cmdidx = CMD_var; return eap->cmd; diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim index 1cbdcfb466..1d3c20e9fe 100644 --- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -283,6 +283,29 @@ def Test_assign_unpack() [v1, v2; _] = [1, 2, 3, 4, 5] assert_equal(1, v1) assert_equal(2, v2) + + var a = 1 + var b = 3 + [a, b] += [2, 4] + assert_equal(3, a) + assert_equal(7, b) + + [a, b] -= [1, 2] + assert_equal(2, a) + assert_equal(5, b) + + [a, b] *= [3, 2] + assert_equal(6, a) + assert_equal(10, b) + + [a, b] /= [2, 4] + assert_equal(3, a) + assert_equal(2, b) + + [a, b] = [17, 15] + [a, b] %= [5, 3] + assert_equal(2, a) + assert_equal(0, b) END CheckDefAndScriptSuccess(lines) diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim index 9d2f697d8a..7938d91f85 100644 --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -452,6 +452,37 @@ def Test_disassemble_list_assign() res) enddef +def s:ListAssignWithOp() + var a = 2 + var b = 3 + [a, b] += [4, 5] +enddef + +def Test_disassemble_list_assign_with_op() + var res = execute('disass s:ListAssignWithOp') + assert_match('\d*_ListAssignWithOp\_s*' .. + 'var a = 2\_s*' .. + '\d STORE 2 in $0\_s*' .. + 'var b = 3\_s*' .. + '\d STORE 3 in $1\_s*' .. + '\[a, b\] += \[4, 5\]\_s*' .. + '\d\+ PUSHNR 4\_s*' .. + '\d\+ PUSHNR 5\_s*' .. + '\d\+ NEWLIST size 2\_s*' .. + '\d\+ CHECKLEN 2\_s*' .. + '\d\+ LOAD $0\_s*' .. + '\d\+ ITEM 0 with op\_s*' .. + '\d\+ OPNR +\_s*' .. + '\d\+ STORE $0\_s*' .. + '\d\+ LOAD $1\_s*' .. + '\d\+ ITEM 1 with op\_s*' .. + '\d\+ OPNR +\_s*' .. + '\d\+ STORE $1\_s*' .. + '\d\+ DROP\_s*' .. + '\d\+ RETURN void', + res) +enddef + def s:ListAdd() var l: list = [] add(l, 123) diff --git a/src/version.c b/src/version.c index db2433db99..096235fc94 100644 --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3029, /**/ 3028, /**/ diff --git a/src/vim9.h b/src/vim9.h index 97e2132a3e..58d451cd11 100644 --- a/src/vim9.h +++ b/src/vim9.h @@ -209,6 +209,12 @@ typedef struct { int cuf_argcount; // number of arguments on top of stack } cufunc_T; +// arguments to ISN_GETITEM +typedef struct { + varnumber_T gi_index; + int gi_with_op; +} getitem_T; + typedef enum { JUMP_ALWAYS, JUMP_IF_FALSE, // pop and jump if false @@ -432,6 +438,7 @@ struct isn_S { isn_T *instr; tostring_T tostring; tobool_T tobool; + getitem_T getitem; } isn_arg; }; diff --git a/src/vim9compile.c b/src/vim9compile.c index e4656c99af..c5a2c2dcf2 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1240,13 +1240,16 @@ generate_PUSHFUNC(cctx_T *cctx, char_u *name, type_T *type) /* * Generate an ISN_GETITEM instruction with "index". + * "with_op" is TRUE for "+=" and other operators, the stack has the current + * value below the list with values. */ static int -generate_GETITEM(cctx_T *cctx, int index) +generate_GETITEM(cctx_T *cctx, int index, int with_op) { isn_T *isn; garray_T *stack = &cctx->ctx_type_stack; - type_T *type = ((type_T **)stack->ga_data)[stack->ga_len - 1]; + type_T *type = ((type_T **)stack->ga_data)[stack->ga_len + - (with_op ? 2 : 1)]; type_T *item_type = &t_any; RETURN_OK_IF_SKIP(cctx); @@ -1260,7 +1263,8 @@ generate_GETITEM(cctx_T *cctx, int index) item_type = type->tt_member; if ((isn = generate_instr(cctx, ISN_GETITEM)) == NULL) return FAIL; - isn->isn_arg.number = index; + isn->isn_arg.getitem.gi_index = index; + isn->isn_arg.getitem.gi_with_op = with_op; // add the item type to the type stack if (ga_grow(stack, 1) == FAIL) @@ -6746,19 +6750,17 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) int is_const = FALSE; char_u *wp; + // for "+=", "*=", "..=" etc. first load the current value + if (*op != '=' + && compile_load_lhs_with_index(&lhs, var_start, + cctx) == FAIL) + goto theend; + // For "var = expr" evaluate the expression. if (var_count == 0) { int r; - // for "+=", "*=", "..=" etc. first load the current value - if (*op != '=') - { - if (compile_load_lhs_with_index(&lhs, var_start, - cctx) == FAIL) - goto theend; - } - // Compile the expression. instr_count = instr->ga_len; if (incdec) @@ -6795,7 +6797,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) { // For "[var, var] = expr" get the "var_idx" item from the // list. - if (generate_GETITEM(cctx, var_idx) == FAIL) + if (generate_GETITEM(cctx, var_idx, *op != '=') == FAIL) goto theend; } diff --git a/src/vim9execute.c b/src/vim9execute.c index dfceb27d00..6d0da61797 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -3832,12 +3832,12 @@ exec_instructions(ectx_T *ectx) case ISN_GETITEM: { listitem_T *li; - int index = iptr->isn_arg.number; + getitem_T *gi = &iptr->isn_arg.getitem; // Get list item: list is at stack-1, push item. // List type and length is checked for when compiling. - tv = STACK_TV_BOT(-1); - li = list_find(tv->vval.v_list, index); + tv = STACK_TV_BOT(-1 - gi->gi_with_op); + li = list_find(tv->vval.v_list, gi->gi_index); if (GA_GROW(&ectx->ec_stack, 1) == FAIL) goto theend; @@ -3846,7 +3846,7 @@ exec_instructions(ectx_T *ectx) // Useful when used in unpack assignment. Reset at // ISN_DROP. - ectx->ec_where.wt_index = index + 1; + ectx->ec_where.wt_index = gi->gi_index + 1; ectx->ec_where.wt_variable = TRUE; } break; @@ -5376,8 +5376,10 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc) case ISN_ANYSLICE: smsg("%s%4d ANYSLICE", pfx, current); break; case ISN_SLICE: smsg("%s%4d SLICE %lld", pfx, current, iptr->isn_arg.number); break; - case ISN_GETITEM: smsg("%s%4d ITEM %lld", - pfx, current, iptr->isn_arg.number); break; + case ISN_GETITEM: smsg("%s%4d ITEM %lld%s", pfx, current, + iptr->isn_arg.getitem.gi_index, + iptr->isn_arg.getitem.gi_with_op ? + " with op" : ""); break; case ISN_MEMBER: smsg("%s%4d MEMBER", pfx, current); break; case ISN_STRINGMEMBER: smsg("%s%4d MEMBER %s", pfx, current, iptr->isn_arg.string); break; From cb54bc65625abad9a0af501acac5c70fba17e2cc Mon Sep 17 00:00:00 2001 From: Dominique Pelle Date: Mon, 21 Jun 2021 20:15:37 +0200 Subject: [PATCH 22/28] patch 8.2.3030: Coverity reports a memory leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Coverity reports a memory leak. Solution: Fix the leak and a few typos. (Dominique PellĂ©, closes #8418) --- src/crypt.c | 10 +++++----- src/errors.h | 4 ++-- src/version.c | 2 ++ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/crypt.c b/src/crypt.c index 35d4e14dc3..f844e4209d 100644 --- a/src/crypt.c +++ b/src/crypt.c @@ -819,19 +819,19 @@ crypt_sodium_decode( if (sod_st->count && len <= crypto_secretstream_xchacha20poly1305_ABYTES) { emsg(e_libsodium_cannot_decrypt_buffer); - return; + goto fail; } if (crypto_secretstream_xchacha20poly1305_pull(&sod_st->state, buf_out, &buf_len, &tag, from, len, NULL, 0) != 0) { - emsg(e_libsodium_decription_failed); + emsg(e_libsodium_decryption_failed); goto fail; } sod_st->count++; if (tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL && !last) { - emsg(e_libsodium_decyption_failed_premature); + emsg(e_libsodium_decryption_failed_premature); goto fail; } if (p1 == p2) @@ -931,12 +931,12 @@ crypt_sodium_buffer_decode( if (crypto_secretstream_xchacha20poly1305_pull(&sod_st->state, *buf_out, &out_len, &tag, from, len, NULL, 0) != 0) { - emsg(e_libsodium_decription_failed); + emsg(e_libsodium_decryption_failed); return -1; } if (tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL && !last) - emsg(e_libsodium_decyption_failed_premature); + emsg(e_libsodium_decryption_failed_premature); return (long) out_len; # else return -1; diff --git a/src/errors.h b/src/errors.h index cb93efd907..9663ecdd69 100644 --- a/src/errors.h +++ b/src/errors.h @@ -442,7 +442,7 @@ EXTERN char e_libsodium_decryption_failed_header_incomplete[] INIT(= N_("E1198: Decryption failed: Header incomplete!")); EXTERN char e_libsodium_cannot_decrypt_buffer[] INIT(= N_("E1199: Cannot decrypt buffer, not enough space")); -EXTERN char e_libsodium_decription_failed[] +EXTERN char e_libsodium_decryption_failed[] INIT(= N_("E1200: Decryption failed: corrupted chunk!")); -EXTERN char e_libsodium_decyption_failed_premature[] +EXTERN char e_libsodium_decryption_failed_premature[] INIT(= N_("E1201: Decryption failed: pre-mature end of file!")); diff --git a/src/version.c b/src/version.c index 096235fc94..0c3c887d13 100644 --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3030, /**/ 3029, /**/ From 22f17a29cd0b0cc3107dc6cd1d96c62eee52a7d9 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 21 Jun 2021 20:48:58 +0200 Subject: [PATCH 23/28] patch 8.2.3031: no error if a function name starts with an underscore Problem: No error if a function name starts with an underscore. (Naohiro Ono) Solution: In Vim9 script disallow a function name starting with an underscore, as is mentioned in the help. (closes #8414) --- src/testdir/test_vim9_func.vim | 18 ++++++++++++++++++ src/userfunc.c | 3 ++- src/version.c | 2 ++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 925e7c82a1..de09baaf6c 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -90,6 +90,24 @@ def Test_compile_error_in_called_function() CheckScriptFailureList(lines, ['E1012:', 'E1191:']) enddef +def Test_wrong_function_name() + var lines =<< trim END + vim9script + func _Foo() + echo 'foo' + endfunc + END + CheckScriptFailure(lines, 'E128:') + + lines =<< trim END + vim9script + def _Foo() + echo 'foo' + enddef + END + CheckScriptFailure(lines, 'E128:') +enddef + def Test_autoload_name_mismatch() var dir = 'Xdir/autoload' mkdir(dir, 'p') diff --git a/src/userfunc.c b/src/userfunc.c index 56b7df32f5..e2e745c338 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -3595,7 +3595,8 @@ trans_function_name( lead += (int)STRLEN(sid_buf); } } - else if (!(flags & TFN_INT) && builtin_function(lv.ll_name, len)) + else if (!(flags & TFN_INT) && (builtin_function(lv.ll_name, len) + || (in_vim9script() && *lv.ll_name == '_'))) { semsg(_("E128: Function name must start with a capital or \"s:\": %s"), start); diff --git a/src/version.c b/src/version.c index 0c3c887d13..77456bbbb9 100644 --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3031, /**/ 3030, /**/ From 226b28b96150e59375d2bff44e0aadd382b0c3f1 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Mon, 21 Jun 2021 21:08:08 +0200 Subject: [PATCH 24/28] patch 8.2.3032: build problems with MSVC, other crypt issues with libsodium Problem: Build problems with MSVC, other crypt issues with libsodium. Solution: Adjust MSVC makefile. Disable swap file only when 'key' is set. Adjust error message used when key is wrong. Fix Coverity issues. (Christian Brabandt, closes #8420, closes #8411) --- src/Make_mvc.mak | 14 +++++++---- src/crypt.c | 61 +++++++++++++++++++++++++++++++++++++-------- src/errors.h | 2 +- src/fileio.c | 4 ++- src/memline.c | 3 ++- src/proto/crypt.pro | 3 +-- src/version.c | 2 ++ 7 files changed, 68 insertions(+), 21 deletions(-) diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index c61bb27ab7..4d6e49b377 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -42,7 +42,11 @@ # Sound support: SOUND=yes (default is yes) # # Sodium support: SODIUM=[Path to Sodium directory] -# You need to install the msvc package from https://download.libsodium.org/libsodium/releases/ +# Dynamic built with libsodium +# You need to install the msvc package from +# https://download.libsodium.org/libsodium/releases/ +# and package the libsodium.dll with Vim +# # # DLL support (EXPERIMENTAL): VIMDLL=yes (default is no) # Creates vim{32,64}.dll, and stub gvim.exe and vim.exe. @@ -383,14 +387,14 @@ SODIUM = no ! if "$(CPU)" == "AMD64" SOD_LIB = $(SODIUM)\x64\Release\v140\dynamic ! elseif "$(CPU)" == "i386" -SOD_LIB = $(SODIUM)\x86\Release\v140\dynamic +SOD_LIB = $(SODIUM)\Win32\Release\v140\dynamic ! else SODIUM = no ! endif !endif !if "$(SODIUM)" != "no" -SOD_INC = -I $(SODIUM)\include +SOD_INC = /I "$(SODIUM)\include" SOD_DEFS = -DFEAT_SODIUM SOD_LIB = $(SOD_LIB)\libsodium.lib !endif @@ -514,7 +518,7 @@ CON_LIB = $(CON_LIB) /DELAYLOAD:comdlg32.dll /DELAYLOAD:ole32.dll DelayImp.lib CFLAGS = -c /W3 /GF /nologo $(CVARS) -I. -Iproto -DHAVE_PATHDEF -DWIN32 \ $(CSCOPE_DEFS) $(TERM_DEFS) $(SOUND_DEFS) $(NETBEANS_DEFS) $(CHANNEL_DEFS) \ - $(NBDEBUG_DEFS) $(XPM_DEFS) $(SOD_DEFS) \ + $(NBDEBUG_DEFS) $(XPM_DEFS) $(SOD_DEFS) $(SOD_INC) \ $(DEFINES) -DWINVER=$(WINVER) -D_WIN32_WINNT=$(WINVER) #>>>>> end of choices @@ -726,7 +730,7 @@ CFLAGS = $(CFLAGS) $(CFLAGS_DEPR) INCL = vim.h alloc.h ascii.h ex_cmds.h feature.h errors.h globals.h \ keymap.h macros.h option.h os_dos.h os_win32.h proto.h regexp.h \ - spell.h structs.h term.h beval.h $(NBDEBUG_INCL) $(SOD_INC) + spell.h structs.h term.h beval.h $(NBDEBUG_INCL) OBJ = \ $(OUTDIR)\arabic.obj \ diff --git a/src/crypt.c b/src/crypt.c index f844e4209d..1b2ece5db4 100644 --- a/src/crypt.c +++ b/src/crypt.c @@ -146,9 +146,9 @@ static cryptmethod_T cryptmethods[CRYPT_M_COUNT] = { FALSE, NULL, crypt_sodium_init, - crypt_sodium_encode, crypt_sodium_decode, + NULL, NULL, crypt_sodium_buffer_encode, crypt_sodium_buffer_decode, - crypt_sodium_encode, crypt_sodium_decode, + NULL, NULL, }, // NOTE: when adding a new method, use some random bytes for the magic key, @@ -250,6 +250,26 @@ crypt_get_header_len(int method_nr) + cryptmethods[method_nr].seed_len; } + +/* + * Get maximum crypt method specific length of the file header in bytes. + */ + int +crypt_get_max_header_len() +{ + int i; + int max = 0; + int temp = 0; + + for (i = 0; i < CRYPT_M_COUNT; ++i) + { + temp = crypt_get_header_len(i); + if (temp > max) + max = temp; + } + return max; +} + /* * Set the crypt method for buffer "buf" to "method_nr" using the int value as * returned by crypt_method_nr_from_name(). @@ -403,8 +423,10 @@ crypt_create_for_writing( #ifdef FEAT_SODIUM if (sodium_init() >= 0) { - randombytes_buf(salt, salt_len); - randombytes_buf(seed, seed_len); + if (salt_len > 0) + randombytes_buf(salt, salt_len); + if (seed_len > 0) + randombytes_buf(seed, seed_len); } else #endif @@ -581,6 +603,13 @@ crypt_check_method(int method) msg_scroll = TRUE; msg(_("Warning: Using a weak encryption method; see :help 'cm'")); } +} + +#ifdef FEAT_SODIUM + static void +crypt_check_swapfile_curbuf(void) +{ + int method = crypt_get_method_nr(curbuf); if (method == CRYPT_M_SOD) { // encryption uses padding and MAC, that does not work very well with @@ -590,11 +619,11 @@ crypt_check_method(int method) #ifdef FEAT_PERSISTENT_UNDO set_option_value((char_u *)"udf", 0, NULL, OPT_LOCAL); #endif - msg_scroll = TRUE; msg(_("Note: Encryption of swapfile not supported, disabling swap- and undofile")); } } +#endif void crypt_check_current_method(void) @@ -647,6 +676,9 @@ crypt_get_key( set_option_value((char_u *)"key", 0L, p1, OPT_LOCAL); crypt_free_key(p1); p1 = curbuf->b_p_key; +#ifdef FEAT_SODIUM + crypt_check_swapfile_curbuf(); +#endif } break; } @@ -654,10 +686,13 @@ crypt_get_key( } // since the user typed this, no need to wait for return - if (msg_didout) - msg_putchar('\n'); - need_wait_return = FALSE; - msg_didout = FALSE; + if (crypt_get_method_nr(curbuf) != CRYPT_M_SOD) + { + if (msg_didout) + msg_putchar('\n'); + need_wait_return = FALSE; + msg_didout = FALSE; + } crypt_free_key(p2); return p1; @@ -726,6 +761,7 @@ crypt_sodium_init( * "from" and "to" can be equal to encrypt in place. * Call needs to ensure that there is enough space in to (for the header) */ +#if 0 // Currently unused void crypt_sodium_encode( cryptstate_T *state UNUSED, @@ -764,11 +800,13 @@ crypt_sodium_encode( sod_st->count++; # endif } +#endif -/* TODO: Unused +/* * Decrypt "from[len]" into "to[len]". * "from" and "to" can be equal to encrypt in place. */ +#if 0 // Currently unused void crypt_sodium_decode( cryptstate_T *state UNUSED, @@ -841,6 +879,7 @@ fail: vim_free(buf_out); # endif } +#endif /* * Encrypt "from[len]" into "to[len]". @@ -864,7 +903,7 @@ crypt_sodium_buffer_encode( sodium_state_T *sod_st = state->method_state; int first = (sod_st->count == 0); - length = len + crypto_secretstream_xchacha20poly1305_ABYTES + length = (int)len + crypto_secretstream_xchacha20poly1305_ABYTES + (first ? crypto_secretstream_xchacha20poly1305_HEADERBYTES : 0); *buf_out = alloc_clear(length); if (*buf_out == NULL) diff --git a/src/errors.h b/src/errors.h index 9663ecdd69..8a2461b31e 100644 --- a/src/errors.h +++ b/src/errors.h @@ -443,6 +443,6 @@ EXTERN char e_libsodium_decryption_failed_header_incomplete[] EXTERN char e_libsodium_cannot_decrypt_buffer[] INIT(= N_("E1199: Cannot decrypt buffer, not enough space")); EXTERN char e_libsodium_decryption_failed[] - INIT(= N_("E1200: Decryption failed: corrupted chunk!")); + INIT(= N_("E1200: Decryption failed!")); EXTERN char e_libsodium_decryption_failed_premature[] INIT(= N_("E1201: Decryption failed: pre-mature end of file!")); diff --git a/src/fileio.c b/src/fileio.c index e05dfa3a5d..7c8f00de3a 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -1213,6 +1213,7 @@ retry: * Read bytes from curbuf. Used for converting text read * from stdin. */ + eof = FALSE; if (read_buf_lnum > from) size = 0; else @@ -1261,6 +1262,7 @@ retry: if (!curbuf->b_p_eol) --tlen; size = tlen; + eof = TRUE; break; } } @@ -1276,7 +1278,7 @@ retry: // Let the crypt layer work with a buffer size of 8192 if (filesize == 0) // set size to 8K + Sodium Crypt Metadata - size = WRITEBUFSIZE + 36 + size = WRITEBUFSIZE + crypt_get_max_header_len() + crypto_secretstream_xchacha20poly1305_HEADERBYTES + crypto_secretstream_xchacha20poly1305_ABYTES; diff --git a/src/memline.c b/src/memline.c index 3b09a7dd49..f1c2a8a524 100644 --- a/src/memline.c +++ b/src/memline.c @@ -497,7 +497,8 @@ ml_set_crypt_key( return; // no memfile yet, nothing to do old_method = crypt_method_nr_from_name(old_cm); - if (old_method == CRYPT_M_SOD || crypt_get_method_nr(buf) == CRYPT_M_SOD) + // Swapfile encryption not supported by XChaCha20 + if (crypt_get_method_nr(buf) == CRYPT_M_SOD && *buf->b_p_key != NUL) { // close the swapfile mf_close_file(buf, TRUE); diff --git a/src/proto/crypt.pro b/src/proto/crypt.pro index 2e58039209..18382a7f3e 100644 --- a/src/proto/crypt.pro +++ b/src/proto/crypt.pro @@ -5,6 +5,7 @@ int crypt_works_inplace(cryptstate_T *state); int crypt_get_method_nr(buf_T *buf); int crypt_whole_undofile(int method_nr); int crypt_get_header_len(int method_nr); +int crypt_get_max_header_len(void); void crypt_set_cm_option(buf_T *buf, int method_nr); int crypt_self_test(void); cryptstate_T *crypt_create(int method_nr, char_u *key, char_u *salt, int salt_len, char_u *seed, int seed_len); @@ -23,8 +24,6 @@ void crypt_check_current_method(void); char_u *crypt_get_key(int store, int twice); void crypt_append_msg(buf_T *buf); int crypt_sodium_init(cryptstate_T *state, char_u *key, char_u *salt, int salt_len, char_u *seed, int seed_len); -void crypt_sodium_encode(cryptstate_T *state, char_u *from, size_t len, char_u *to, int last); -void crypt_sodium_decode(cryptstate_T *state, char_u *from, size_t len, char_u *to, int last); long crypt_sodium_buffer_encode(cryptstate_T *state, char_u *from, size_t len, char_u **buf_out, int last); long crypt_sodium_buffer_decode(cryptstate_T *state, char_u *from, size_t len, char_u **buf_out, int last); /* vim: set ft=c : */ diff --git a/src/version.c b/src/version.c index 77456bbbb9..a6e483e05c 100644 --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3032, /**/ 3031, /**/ From 419a40ac9657e39646b2e0f3f71d7736b0c459d1 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 21 Jun 2021 21:55:18 +0200 Subject: [PATCH 25/28] patch 8.2.3033: no error when using alpha delimiter with :global Problem: No error when using alpha delimiter with :global. Solution: Check the delimiter like with :substitute. (closes #8415) --- src/ex_cmds.c | 21 +++++++++++++++++---- src/testdir/test_global.vim | 4 ++++ src/version.c | 2 ++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/ex_cmds.c b/src/ex_cmds.c index 614d41d69f..d8c5a12c72 100644 --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -3643,6 +3643,17 @@ skip_substitute(char_u *start, int delimiter) return p; } + static int +check_regexp_delim(int c) +{ + if (isalpha(c)) + { + emsg(_("E146: Regular expressions can't be delimited by letters")); + return FAIL; + } + return OK; +} + /* * Perform a substitution from line eap->line1 to line eap->line2 using the * command pointed to by eap->arg which should be of the form: @@ -3705,11 +3716,9 @@ ex_substitute(exarg_T *eap) && vim_strchr((char_u *)"0123456789cegriIp|\"", *cmd) == NULL) { // don't accept alphanumeric for separator - if (isalpha(*cmd)) - { - emsg(_("E146: Regular expressions can't be delimited by letters")); + if (check_regexp_delim(*cmd) == FAIL) return; - } + /* * undocumented vi feature: * "\/sub/" and "\?sub?" use last used search pattern (almost like @@ -4909,6 +4918,10 @@ ex_global(exarg_T *eap) emsg(_("E148: Regular expression missing from global")); return; } + else if (check_regexp_delim(*cmd) == FAIL) + { + return; + } else { delim = *cmd; // get the delimiter diff --git a/src/testdir/test_global.vim b/src/testdir/test_global.vim index 4a01be4194..48753cfcc8 100644 --- a/src/testdir/test_global.vim +++ b/src/testdir/test_global.vim @@ -83,4 +83,8 @@ func Test_global_newline() close! endfunc +func Test_wrong_delimiter() + call assert_fails('g x^bxd', 'E146:') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index a6e483e05c..d8d033a2e0 100644 --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3033, /**/ 3032, /**/ From ef7be8348fd830e409504a442f3f3ad0931cfbc3 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Tue, 22 Jun 2021 18:21:19 +0200 Subject: [PATCH 26/28] patch 8.2.3034: installing packages on github CI sometimes fails Problem: Installing packages on github CI sometimes fails. Solution: Update package information first. (Christian Brabandt, closes #8432) --- .github/workflows/ci.yml | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e569b42397..b821e822b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,7 +56,7 @@ jobs: - name: Install packages run: | - sudo apt-get install -y \ + sudo apt update && sudo apt install -y \ autoconf \ lcov \ gettext \ diff --git a/src/version.c b/src/version.c index d8d033a2e0..5502952b7a 100644 --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3034, /**/ 3033, /**/ From 831bdf8622fdfce7f02d48f847005e3909a055a8 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 22 Jun 2021 19:32:17 +0200 Subject: [PATCH 27/28] patch 8.2.3035: Vim9: crash when calling :def function with partial Problem: Vim9: crash when calling :def function with partial and return type is not set. Solution: When the return type is not set handle like the return type is unknown. (closes #8422) --- src/testdir/test_vim9_func.vim | 14 ++++++++++++++ src/version.c | 2 ++ src/vim9type.c | 4 ++-- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index de09baaf6c..301a55c875 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -1005,6 +1005,20 @@ def Test_pass_legacy_lambda_to_def_func() Foo() END CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + def g:TestFunc(f: func()) + enddef + legacy call g:TestFunc({-> 0}) + delfunc g:TestFunc + + def g:TestFunc(f: func(number)) + enddef + legacy call g:TestFunc({nr -> 0}) + delfunc g:TestFunc + END + CheckScriptSuccess(lines) enddef " Default arg and varargs diff --git a/src/version.c b/src/version.c index 5502952b7a..653e578b9f 100644 --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3035, /**/ 3034, /**/ diff --git a/src/vim9type.c b/src/vim9type.c index f3718e4685..91dc3fe607 100644 --- a/src/vim9type.c +++ b/src/vim9type.c @@ -171,7 +171,7 @@ alloc_func_type(type_T *ret_type, int argcount, garray_T *type_gap) if (type == NULL) return &t_any; type->tt_type = VAR_FUNC; - type->tt_member = ret_type; + type->tt_member = ret_type == NULL ? &t_unknown : ret_type; type->tt_argcount = argcount; type->tt_args = NULL; return type; @@ -188,7 +188,7 @@ get_func_type(type_T *ret_type, int argcount, garray_T *type_gap) // recognize commonly used types if (argcount <= 0) { - if (ret_type == &t_unknown) + if (ret_type == &t_unknown || ret_type == NULL) { // (argcount == 0) is not possible return &t_func_unknown; From 7237cab8f1d1a4391372cafdb57f2d97f3b32d05 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Tue, 22 Jun 2021 19:52:27 +0200 Subject: [PATCH 28/28] patch 8.2.3036: Vim9: builtin function arguments not checked at compile time Problem: Vim9: builtin function arguments not checked at compile time. Solution: Add more argument type specs. Check arguments to test_setmouse() and test_gui_mouse_event(). (Yegappan Lakshmanan, closes #8425) --- src/evalfunc.c | 132 +++++++++-------- src/testdir/test_assert.vim | 2 + src/testdir/test_gui.vim | 63 +++++++- src/testdir/test_popupwin.vim | 4 +- src/testdir/test_vim9_builtin.vim | 237 ++++++++++++++++++++++++++++++ src/testing.c | 32 +++- src/version.c | 2 + 7 files changed, 400 insertions(+), 72 deletions(-) diff --git a/src/evalfunc.c b/src/evalfunc.c index f8af8ef812..21d28afec6 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -403,12 +403,16 @@ arg_extend3(type_T *type, argcontext_T *context) * Lists of functions that check the argument types of a builtin function. */ argcheck_T arg1_string[] = {arg_string}; -argcheck_T arg3_string_nr_bool[] = {arg_string, arg_number, arg_bool}; +argcheck_T arg1_number[] = {arg_number}; argcheck_T arg1_float_or_nr[] = {arg_float_or_nr}; +argcheck_T arg2_float_or_nr[] = {arg_float_or_nr, arg_float_or_nr}; +argcheck_T arg2_number[] = {arg_number, arg_number}; argcheck_T arg2_listblob_item[] = {arg_list_or_blob, arg_item_of_prev}; argcheck_T arg2_execute[] = {arg_string_or_list, arg_string}; argcheck_T arg23_extend[] = {arg_list_or_dict, arg_same_as_prev, arg_extend3}; argcheck_T arg23_extendnew[] = {arg_list_or_dict, arg_same_struct_as_prev, arg_extend3}; +argcheck_T arg3_string[] = {arg_string, arg_string, arg_string}; +argcheck_T arg3_string_nr_bool[] = {arg_string, arg_number, arg_bool}; argcheck_T arg3_insert[] = {arg_list_or_blob, arg_item_of_prev, arg_number}; /* @@ -670,25 +674,25 @@ static funcentry_T global_functions[] = { {"abs", 1, 1, FEARG_1, arg1_float_or_nr, ret_any, FLOAT_FUNC(f_abs)}, - {"acos", 1, 1, FEARG_1, NULL, + {"acos", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_acos)}, {"add", 2, 2, FEARG_1, NULL /* arg2_listblob_item */, ret_first_arg, f_add}, - {"and", 2, 2, FEARG_1, NULL, + {"and", 2, 2, FEARG_1, arg2_number, ret_number, f_and}, {"append", 2, 2, FEARG_2, NULL, ret_number_bool, f_append}, {"appendbufline", 3, 3, FEARG_3, NULL, ret_number_bool, f_appendbufline}, - {"argc", 0, 1, 0, NULL, + {"argc", 0, 1, 0, arg1_number, ret_number, f_argc}, {"argidx", 0, 0, 0, NULL, ret_number, f_argidx}, - {"arglistid", 0, 2, 0, NULL, + {"arglistid", 0, 2, 0, arg2_number, ret_number, f_arglistid}, - {"argv", 0, 2, 0, NULL, + {"argv", 0, 2, 0, arg2_number, ret_argv, f_argv}, - {"asin", 1, 1, FEARG_1, NULL, + {"asin", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_asin)}, {"assert_beeps", 1, 2, FEARG_1, NULL, ret_number_bool, f_assert_beeps}, @@ -716,9 +720,9 @@ static funcentry_T global_functions[] = ret_number_bool, f_assert_report}, {"assert_true", 1, 2, FEARG_1, NULL, ret_number_bool, f_assert_true}, - {"atan", 1, 1, FEARG_1, NULL, + {"atan", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_atan)}, - {"atan2", 2, 2, FEARG_1, NULL, + {"atan2", 2, 2, FEARG_1, arg2_float_or_nr, ret_float, FLOAT_FUNC(f_atan2)}, {"balloon_gettext", 0, 0, 0, NULL, ret_string, @@ -780,7 +784,7 @@ static funcentry_T global_functions[] = ret_number, f_byteidxcomp}, {"call", 2, 3, FEARG_1, NULL, ret_any, f_call}, - {"ceil", 1, 1, FEARG_1, NULL, + {"ceil", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_ceil)}, {"ch_canread", 1, 1, FEARG_1, NULL, ret_number_bool, JOB_FUNC(f_ch_canread)}, @@ -832,7 +836,7 @@ static funcentry_T global_functions[] = ret_string, f_chdir}, {"cindent", 1, 1, FEARG_1, NULL, ret_number, f_cindent}, - {"clearmatches", 0, 1, FEARG_1, NULL, + {"clearmatches", 0, 1, FEARG_1, arg1_number, ret_void, f_clearmatches}, {"col", 1, 1, FEARG_1, NULL, ret_number, f_col}, @@ -848,9 +852,9 @@ static funcentry_T global_functions[] = ret_number, f_confirm}, {"copy", 1, 1, FEARG_1, NULL, ret_first_arg, f_copy}, - {"cos", 1, 1, FEARG_1, NULL, + {"cos", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_cos)}, - {"cosh", 1, 1, FEARG_1, NULL, + {"cosh", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_cosh)}, {"count", 2, 4, FEARG_1, NULL, ret_number, f_count}, @@ -858,7 +862,7 @@ static funcentry_T global_functions[] = ret_number, f_cscope_connection}, {"cursor", 1, 3, FEARG_1, NULL, ret_number, f_cursor}, - {"debugbreak", 1, 1, FEARG_1, NULL, + {"debugbreak", 1, 1, FEARG_1, arg1_number, ret_number, #ifdef MSWIN f_debugbreak @@ -898,7 +902,7 @@ static funcentry_T global_functions[] = ret_string, f_exepath}, {"exists", 1, 1, FEARG_1, NULL, ret_number_bool, f_exists}, - {"exp", 1, 1, FEARG_1, NULL, + {"exp", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_exp)}, {"expand", 1, 3, FEARG_1, NULL, ret_any, f_expand}, @@ -926,11 +930,11 @@ static funcentry_T global_functions[] = ret_list_any, f_flatten}, {"flattennew", 1, 2, FEARG_1, NULL, ret_list_any, f_flattennew}, - {"float2nr", 1, 1, FEARG_1, NULL, + {"float2nr", 1, 1, FEARG_1, arg1_float_or_nr, ret_number, FLOAT_FUNC(f_float2nr)}, - {"floor", 1, 1, FEARG_1, NULL, + {"floor", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_floor)}, - {"fmod", 2, 2, FEARG_1, NULL, + {"fmod", 2, 2, FEARG_1, arg2_float_or_nr, ret_float, FLOAT_FUNC(f_fmod)}, {"fnameescape", 1, 1, FEARG_1, NULL, ret_string, f_fnameescape}, @@ -986,11 +990,11 @@ static funcentry_T global_functions[] = ret_string, f_getcmdwintype}, {"getcompletion", 2, 3, FEARG_1, NULL, ret_list_string, f_getcompletion}, - {"getcurpos", 0, 1, FEARG_1, NULL, + {"getcurpos", 0, 1, FEARG_1, arg1_number, ret_list_number, f_getcurpos}, - {"getcursorcharpos", 0, 1, FEARG_1, NULL, + {"getcursorcharpos", 0, 1, FEARG_1, arg1_number, ret_list_number, f_getcursorcharpos}, - {"getcwd", 0, 2, FEARG_1, NULL, + {"getcwd", 0, 2, FEARG_1, arg2_number, ret_string, f_getcwd}, {"getenv", 1, 1, FEARG_1, NULL, ret_any, f_getenv}, @@ -1006,7 +1010,7 @@ static funcentry_T global_functions[] = ret_string, f_getftype}, {"getimstatus", 0, 0, 0, NULL, ret_number_bool, f_getimstatus}, - {"getjumplist", 0, 2, FEARG_1, NULL, + {"getjumplist", 0, 2, FEARG_1, arg2_number, ret_list_any, f_getjumplist}, {"getline", 1, 2, FEARG_1, NULL, ret_f_getline, f_getline}, @@ -1014,7 +1018,7 @@ static funcentry_T global_functions[] = ret_list_or_dict_1, f_getloclist}, {"getmarklist", 0, 1, FEARG_1, NULL, ret_list_dict_any, f_getmarklist}, - {"getmatches", 0, 1, 0, NULL, + {"getmatches", 0, 1, 0, arg1_number, ret_list_dict_any, f_getmatches}, {"getmousepos", 0, 0, 0, NULL, ret_dict_number, f_getmousepos}, @@ -1030,19 +1034,19 @@ static funcentry_T global_functions[] = ret_dict_any, f_getreginfo}, {"getregtype", 0, 1, FEARG_1, NULL, ret_string, f_getregtype}, - {"gettabinfo", 0, 1, FEARG_1, NULL, + {"gettabinfo", 0, 1, FEARG_1, arg1_number, ret_list_dict_any, f_gettabinfo}, {"gettabvar", 2, 3, FEARG_1, NULL, ret_any, f_gettabvar}, {"gettabwinvar", 3, 4, FEARG_1, NULL, ret_any, f_gettabwinvar}, - {"gettagstack", 0, 1, FEARG_1, NULL, + {"gettagstack", 0, 1, FEARG_1, arg1_number, ret_dict_any, f_gettagstack}, {"gettext", 1, 1, FEARG_1, NULL, ret_string, f_gettext}, - {"getwininfo", 0, 1, FEARG_1, NULL, + {"getwininfo", 0, 1, FEARG_1, arg1_number, ret_list_dict_any, f_getwininfo}, - {"getwinpos", 0, 1, FEARG_1, NULL, + {"getwinpos", 0, 1, FEARG_1, arg1_number, ret_list_number, f_getwinpos}, {"getwinposx", 0, 0, 0, NULL, ret_number, f_getwinposx}, @@ -1060,7 +1064,7 @@ static funcentry_T global_functions[] = ret_number_bool, f_has}, {"has_key", 2, 2, FEARG_1, NULL, ret_number_bool, f_has_key}, - {"haslocaldir", 0, 2, FEARG_1, NULL, + {"haslocaldir", 0, 2, FEARG_1, arg2_number, ret_number, f_haslocaldir}, {"hasmapto", 1, 3, FEARG_1, NULL, ret_number_bool, f_hasmapto}, @@ -1104,15 +1108,15 @@ static funcentry_T global_functions[] = ret_first_arg, f_insert}, {"interrupt", 0, 0, 0, NULL, ret_void, f_interrupt}, - {"invert", 1, 1, FEARG_1, NULL, + {"invert", 1, 1, FEARG_1, arg1_number, ret_number, f_invert}, {"isdirectory", 1, 1, FEARG_1, NULL, ret_number_bool, f_isdirectory}, - {"isinf", 1, 1, FEARG_1, NULL, + {"isinf", 1, 1, FEARG_1, arg1_float_or_nr, ret_number, MATH_FUNC(f_isinf)}, {"islocked", 1, 1, FEARG_1, NULL, ret_number_bool, f_islocked}, - {"isnan", 1, 1, FEARG_1, NULL, + {"isnan", 1, 1, FEARG_1, arg1_float_or_nr, ret_number_bool, MATH_FUNC(f_isnan)}, {"items", 1, 1, FEARG_1, NULL, ret_list_any, f_items}, @@ -1160,13 +1164,13 @@ static funcentry_T global_functions[] = ret_number, f_listener_add}, {"listener_flush", 0, 1, FEARG_1, NULL, ret_void, f_listener_flush}, - {"listener_remove", 1, 1, FEARG_1, NULL, + {"listener_remove", 1, 1, FEARG_1, arg1_number, ret_number_bool, f_listener_remove}, {"localtime", 0, 0, 0, NULL, ret_number, f_localtime}, - {"log", 1, 1, FEARG_1, NULL, + {"log", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_log)}, - {"log10", 1, 1, FEARG_1, NULL, + {"log10", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_log10)}, {"luaeval", 1, 2, FEARG_1, NULL, ret_any, @@ -1192,9 +1196,9 @@ static funcentry_T global_functions[] = ret_number, f_matchadd}, {"matchaddpos", 2, 5, FEARG_1, NULL, ret_number, f_matchaddpos}, - {"matcharg", 1, 1, FEARG_1, NULL, + {"matcharg", 1, 1, FEARG_1, arg1_number, ret_list_string, f_matcharg}, - {"matchdelete", 1, 2, FEARG_1, NULL, + {"matchdelete", 1, 2, FEARG_1, arg2_number, ret_number_bool, f_matchdelete}, {"matchend", 2, 4, FEARG_1, NULL, ret_number, f_matchend}, @@ -1236,7 +1240,7 @@ static funcentry_T global_functions[] = ret_number, f_nextnonblank}, {"nr2char", 1, 2, FEARG_1, NULL, ret_string, f_nr2char}, - {"or", 2, 2, FEARG_1, NULL, + {"or", 2, 2, FEARG_1, arg2_number, ret_number, f_or}, {"pathshorten", 1, 2, FEARG_1, NULL, ret_string, f_pathshorten}, @@ -1290,7 +1294,7 @@ static funcentry_T global_functions[] = ret_void, PROP_FUNC(f_popup_settext)}, {"popup_show", 1, 1, FEARG_1, NULL, ret_void, PROP_FUNC(f_popup_show)}, - {"pow", 2, 2, FEARG_1, NULL, + {"pow", 2, 2, FEARG_1, arg2_float_or_nr, ret_float, FLOAT_FUNC(f_pow)}, {"prevnonblank", 1, 1, FEARG_1, NULL, ret_number, f_prevnonblank}, @@ -1398,7 +1402,7 @@ static funcentry_T global_functions[] = ret_string, f_resolve}, {"reverse", 1, 1, FEARG_1, NULL, ret_first_arg, f_reverse}, - {"round", 1, 1, FEARG_1, NULL, + {"round", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_round)}, {"rubyeval", 1, 1, FEARG_1, NULL, ret_any, @@ -1408,11 +1412,11 @@ static funcentry_T global_functions[] = NULL #endif }, - {"screenattr", 2, 2, FEARG_1, NULL, + {"screenattr", 2, 2, FEARG_1, arg2_number, ret_number, f_screenattr}, - {"screenchar", 2, 2, FEARG_1, NULL, + {"screenchar", 2, 2, FEARG_1, arg2_number, ret_number, f_screenchar}, - {"screenchars", 2, 2, FEARG_1, NULL, + {"screenchars", 2, 2, FEARG_1, arg2_number, ret_list_number, f_screenchars}, {"screencol", 0, 0, 0, NULL, ret_number, f_screencol}, @@ -1420,7 +1424,7 @@ static funcentry_T global_functions[] = ret_dict_number, f_screenpos}, {"screenrow", 0, 0, 0, NULL, ret_number, f_screenrow}, - {"screenstring", 2, 2, FEARG_1, NULL, + {"screenstring", 2, 2, FEARG_1, arg2_number, ret_string, f_screenstring}, {"search", 1, 5, FEARG_1, NULL, ret_number, f_search}, @@ -1448,7 +1452,7 @@ static funcentry_T global_functions[] = ret_number_bool, f_setcharpos}, {"setcharsearch", 1, 1, FEARG_1, NULL, ret_void, f_setcharsearch}, - {"setcmdpos", 1, 1, FEARG_1, NULL, + {"setcmdpos", 1, 1, FEARG_1, arg1_number, ret_number_bool, f_setcmdpos}, {"setcursorcharpos", 1, 3, FEARG_1, NULL, ret_number_bool, f_setcursorcharpos}, @@ -1486,7 +1490,7 @@ static funcentry_T global_functions[] = }, {"shellescape", 1, 2, FEARG_1, NULL, ret_string, f_shellescape}, - {"shiftwidth", 0, 1, FEARG_1, NULL, + {"shiftwidth", 0, 1, FEARG_1, arg1_number, ret_number, f_shiftwidth}, {"sign_define", 1, 2, FEARG_1, NULL, ret_any, SIGN_FUNC(f_sign_define)}, @@ -1508,9 +1512,9 @@ static funcentry_T global_functions[] = ret_list_number, SIGN_FUNC(f_sign_unplacelist)}, {"simplify", 1, 1, FEARG_1, NULL, ret_string, f_simplify}, - {"sin", 1, 1, FEARG_1, NULL, + {"sin", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_sin)}, - {"sinh", 1, 1, FEARG_1, NULL, + {"sinh", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_sinh)}, {"slice", 2, 3, FEARG_1, NULL, ret_first_arg, f_slice}, @@ -1532,7 +1536,7 @@ static funcentry_T global_functions[] = ret_list_string, f_spellsuggest}, {"split", 1, 3, FEARG_1, NULL, ret_list_string, f_split}, - {"sqrt", 1, 1, FEARG_1, NULL, + {"sqrt", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_sqrt)}, {"srand", 0, 1, FEARG_1, NULL, ret_list_number, f_srand}, @@ -1616,9 +1620,9 @@ static funcentry_T global_functions[] = ret_list_string, f_tagfiles}, {"taglist", 1, 2, FEARG_1, NULL, ret_list_dict_any, f_taglist}, - {"tan", 1, 1, FEARG_1, NULL, + {"tan", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_tan)}, - {"tanh", 1, 1, FEARG_1, NULL, + {"tanh", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_tanh)}, {"tempname", 0, 0, 0, NULL, ret_string, f_tempname}, @@ -1750,15 +1754,15 @@ static funcentry_T global_functions[] = ret_void, TIMER_FUNC(f_timer_stop)}, {"timer_stopall", 0, 0, 0, NULL, ret_void, TIMER_FUNC(f_timer_stopall)}, - {"tolower", 1, 1, FEARG_1, NULL, + {"tolower", 1, 1, FEARG_1, arg1_string, ret_string, f_tolower}, - {"toupper", 1, 1, FEARG_1, NULL, + {"toupper", 1, 1, FEARG_1, arg1_string, ret_string, f_toupper}, - {"tr", 3, 3, FEARG_1, NULL, + {"tr", 3, 3, FEARG_1, arg3_string, ret_string, f_tr}, {"trim", 1, 3, FEARG_1, NULL, ret_string, f_trim}, - {"trunc", 1, 1, FEARG_1, NULL, + {"trunc", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_trunc)}, {"type", 1, 1, FEARG_1, NULL, ret_number, f_type}, @@ -1784,27 +1788,27 @@ static funcentry_T global_functions[] = ret_list_number, f_win_findbuf}, {"win_getid", 0, 2, FEARG_1, NULL, ret_number, f_win_getid}, - {"win_gettype", 0, 1, FEARG_1, NULL, + {"win_gettype", 0, 1, FEARG_1, arg1_number, ret_string, f_win_gettype}, - {"win_gotoid", 1, 1, FEARG_1, NULL, + {"win_gotoid", 1, 1, FEARG_1, arg1_number, ret_number_bool, f_win_gotoid}, - {"win_id2tabwin", 1, 1, FEARG_1, NULL, + {"win_id2tabwin", 1, 1, FEARG_1, arg1_number, ret_list_number, f_win_id2tabwin}, - {"win_id2win", 1, 1, FEARG_1, NULL, + {"win_id2win", 1, 1, FEARG_1, arg1_number, ret_number, f_win_id2win}, - {"win_screenpos", 1, 1, FEARG_1, NULL, + {"win_screenpos", 1, 1, FEARG_1, arg1_number, ret_list_number, f_win_screenpos}, {"win_splitmove", 2, 3, FEARG_1, NULL, ret_number_bool, f_win_splitmove}, - {"winbufnr", 1, 1, FEARG_1, NULL, + {"winbufnr", 1, 1, FEARG_1, arg1_number, ret_number, f_winbufnr}, {"wincol", 0, 0, 0, NULL, ret_number, f_wincol}, {"windowsversion", 0, 0, 0, NULL, ret_string, f_windowsversion}, - {"winheight", 1, 1, FEARG_1, NULL, + {"winheight", 1, 1, FEARG_1, arg1_number, ret_number, f_winheight}, - {"winlayout", 0, 1, FEARG_1, NULL, + {"winlayout", 0, 1, FEARG_1, arg1_number, ret_list_any, f_winlayout}, {"winline", 0, 0, 0, NULL, ret_number, f_winline}, @@ -1816,13 +1820,13 @@ static funcentry_T global_functions[] = ret_void, f_winrestview}, {"winsaveview", 0, 0, 0, NULL, ret_dict_number, f_winsaveview}, - {"winwidth", 1, 1, FEARG_1, NULL, + {"winwidth", 1, 1, FEARG_1, arg1_number, ret_number, f_winwidth}, {"wordcount", 0, 0, 0, NULL, ret_dict_number, f_wordcount}, {"writefile", 2, 3, FEARG_1, NULL, ret_number_bool, f_writefile}, - {"xor", 2, 2, FEARG_1, NULL, + {"xor", 2, 2, FEARG_1, arg2_number, ret_number, f_xor}, }; diff --git a/src/testdir/test_assert.vim b/src/testdir/test_assert.vim index 04bd87729f..e0dc99e7c6 100644 --- a/src/testdir/test_assert.vim +++ b/src/testdir/test_assert.vim @@ -374,6 +374,8 @@ func Test_mouse_position() call test_setmouse(5, 1) call feedkeys("\", "xt") call assert_equal([0, 2, 1, 0], getpos('.')) + call assert_fails('call test_setmouse("", 2)', 'E474:') + call assert_fails('call test_setmouse(1, "")', 'E474:') bwipe! let &mouse = save_mouse endfunc diff --git a/src/testdir/test_gui.vim b/src/testdir/test_gui.vim index 3472012c4a..e935e5fdd2 100644 --- a/src/testdir/test_gui.vim +++ b/src/testdir/test_gui.vim @@ -882,7 +882,7 @@ func Test_gui_mouse_event() new call setline(1, ['one two three', 'four five six']) - " place the cursor using left click + " place the cursor using left click in normal mode call cursor(1, 1) call test_gui_mouse_event(0, 2, 4, 0, 0) call test_gui_mouse_event(3, 2, 4, 0, 0) @@ -1092,9 +1092,70 @@ func Test_gui_mouse_event() set mouse& let &guioptions = save_guioptions + " Test invalid parameters for test_gui_mouse_event() + call assert_fails('call test_gui_mouse_event("", 1, 2, 3, 4)', 'E474:') + call assert_fails('call test_gui_mouse_event(0, "", 2, 3, 4)', 'E474:') + call assert_fails('call test_gui_mouse_event(0, 1, "", 3, 4)', 'E474:') + call assert_fails('call test_gui_mouse_event(0, 1, 2, "", 4)', 'E474:') + call assert_fails('call test_gui_mouse_event(0, 1, 2, 3, "")', 'E474:') + bw! call test_override('no_query_mouse', 0) set mousemodel& endfunc +" Test for 'guitablabel' and 'guitabtooltip' options +func TestGuiTabLabel() + call add(g:TabLabels, v:lnum + 100) + let bufnrlist = tabpagebuflist(v:lnum) + return bufname(bufnrlist[tabpagewinnr(v:lnum) - 1]) +endfunc + +func TestGuiTabToolTip() + call add(g:TabToolTips, v:lnum + 200) + let bufnrlist = tabpagebuflist(v:lnum) + return bufname(bufnrlist[tabpagewinnr(v:lnum) - 1]) +endfunc + +func Test_gui_tablabel_tooltip() + %bw! + " Removing the tabline at the end of this test, reduces the window height by + " one. Save and restore it after the test. + let save_lines = &lines + edit one + set modified + tabnew two + set modified + tabnew three + set modified + let g:TabLabels = [] + set guitablabel=%{TestGuiTabLabel()} + call test_override('starting', 1) + redrawtabline + call test_override('starting', 0) + call assert_true(index(g:TabLabels, 101) != -1) + call assert_true(index(g:TabLabels, 102) != -1) + call assert_true(index(g:TabLabels, 103) != -1) + set guitablabel& + unlet g:TabLabels + + if has('gui_gtk') + " Only on GTK+, the tooltip function is called even if the mouse is not + " on the tabline. on Win32 and Motif, the tooltip function is called only + " when the mouse pointer is over the tabline. + let g:TabToolTips = [] + set guitabtooltip=%{TestGuiTabToolTip()} + call test_override('starting', 1) + redrawtabline + call test_override('starting', 0) + call assert_true(index(g:TabToolTips, 201) != -1) + call assert_true(index(g:TabToolTips, 202) != -1) + call assert_true(index(g:TabToolTips, 203) != -1) + set guitabtooltip& + unlet g:TabToolTips + endif + %bw! + let &lines = save_lines +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim index 147a3a4ae6..68d7db87ea 100644 --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -1552,8 +1552,8 @@ func Test_popup_filter() call assert_equal(9, getcurpos()[2]) call feedkeys('0', 'xt') call assert_equal('0', g:ignored) - redraw - call assert_equal(1, getcurpos()[2]) + normal! l + call assert_equal(2, getcurpos()[2]) " x closes the popup call feedkeys('x', 'xt') diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index d8fd079bf1..c40780f4ae 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -141,6 +141,11 @@ def Test_add_blob() CheckScriptSuccess(lines) enddef +def Test_and() + CheckDefFailure(['echo and("x", 0x2)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo and(0x1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') +enddef + def Test_append() new setline(1, range(3)) @@ -155,6 +160,22 @@ def Test_append() bwipe! enddef +def Test_argc() + CheckDefFailure(['echo argc("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_arglistid() + CheckDefFailure(['echo arglistid("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo arglistid(1, "y")'], 'E1013: Argument 2: type mismatch, expected number but got string') + CheckDefFailure(['echo arglistid("x", "y")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_argv() + CheckDefFailure(['echo argv("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo argv(1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') + CheckDefFailure(['echo argv("x", "y")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + def Test_balloon_show() CheckGui CheckFeature balloon_eval @@ -256,6 +277,10 @@ def Test_chdir() assert_fails('chdir(true)', 'E1174') enddef +def Test_clearmatches() + CheckDefFailure(['echo clearmatches("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + def Test_col() new setline(1, 'asdf') @@ -311,6 +336,11 @@ def Test_cursor() CheckDefExecAndScriptFailure(lines, 'E475:') enddef +def Test_debugbreak() + CheckMSWindows + CheckDefFailure(['echo debugbreak("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + def Test_delete() var res: bool = delete('doesnotexist') assert_equal(true, res) @@ -532,6 +562,64 @@ def Test_flattennew() CheckDefAndScriptFailure(lines, 'E1158:') enddef +" Test for float functions argument type +def Test_float_funcs_args() + CheckFeature float + + # acos() + CheckDefFailure(['echo acos("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # asin() + CheckDefFailure(['echo asin("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # atan() + CheckDefFailure(['echo atan("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # atan2() + CheckDefFailure(['echo atan2("a", 1.1)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo atan2(1.2, "a")'], 'E1013: Argument 2: type mismatch, expected number but got string') + CheckDefFailure(['echo atan2(1.2)'], 'E119:') + # ceil() + CheckDefFailure(['echo ceil("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # cos() + CheckDefFailure(['echo cos("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # cosh() + CheckDefFailure(['echo cosh("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # exp() + CheckDefFailure(['echo exp("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # float2nr() + CheckDefFailure(['echo float2nr("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # floor() + CheckDefFailure(['echo floor("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # fmod() + CheckDefFailure(['echo fmod(1.1, "a")'], 'E1013: Argument 2: type mismatch, expected number but got string') + CheckDefFailure(['echo fmod("a", 1.1)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo fmod(1.1)'], 'E119:') + # isinf() + CheckDefFailure(['echo isinf("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # isnan() + CheckDefFailure(['echo isnan("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # log() + CheckDefFailure(['echo log("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # log10() + CheckDefFailure(['echo log10("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # pow() + CheckDefFailure(['echo pow("a", 1.1)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo pow(1.1, "a")'], 'E1013: Argument 2: type mismatch, expected number but got string') + CheckDefFailure(['echo pow(1.1)'], 'E119:') + # round() + CheckDefFailure(['echo round("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # sin() + CheckDefFailure(['echo sin("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # sinh() + CheckDefFailure(['echo sinh("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # sqrt() + CheckDefFailure(['echo sqrt("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # tan() + CheckDefFailure(['echo tan("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # tanh() + CheckDefFailure(['echo tanh("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # trunc() + CheckDefFailure(['echo trunc("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + def Test_fnamemodify() CheckDefSuccess(['echo fnamemodify(test_null_string(), ":p")']) CheckDefSuccess(['echo fnamemodify("", ":p")']) @@ -652,6 +740,20 @@ def Test_getcompletion() set wildignore& enddef +def Test_getcurpos() + CheckDefFailure(['echo getcursorcharpos("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_getcursorcharpos() + CheckDefFailure(['echo getcursorcharpos("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_getcwd() + CheckDefFailure(['echo getcwd("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo getcwd("x", 1)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo getcwd(1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') +enddef + def Test_getloclist_return_type() var l = getloclist(1) l->assert_equal([]) @@ -692,6 +794,16 @@ def Test_getftype() CheckDefExecFailure(['echo getftype(v:null)'], 'E1174:') enddef +def Test_getjumplist() + CheckDefFailure(['echo getjumplist("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo getjumplist("x", 1)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo getjumplist(1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') +enddef + +def Test_getmatches() + CheckDefFailure(['echo getmatches("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + def Test_getqflist_return_type() var l = getqflist() l->assert_equal([]) @@ -727,6 +839,22 @@ def Test_getregtype() assert_fails('getregtype("ab")', 'E1162:') enddef +def Test_gettabinfo() + CheckDefFailure(['echo gettabinfo("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_gettagstack() + CheckDefFailure(['echo gettagstack("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_getwininfo() + CheckDefFailure(['echo getwininfo("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_getwinpos() + CheckDefFailure(['echo getwinpos("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + def Test_glob() glob('runtest.vim', true, true, true)->assert_equal(['runtest.vim']) enddef @@ -739,6 +867,12 @@ def Test_has() has('eval', true)->assert_equal(1) enddef +def Test_haslocaldir() + CheckDefFailure(['echo haslocaldir("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo haslocaldir("x", 1)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo haslocaldir(1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') +enddef + def Test_hasmapto() hasmapto('foobar', 'i', true)->assert_equal(0) iabbrev foo foobar @@ -790,6 +924,10 @@ def Test_insert() CheckDefFailure(['insert([2, 3], 1, "x")'], 'E1013: Argument 3: type mismatch, expected number but got string', 1) enddef +def Test_invert() + CheckDefFailure(['echo invert("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + def Test_keys_return_type() const var: list = {a: 1, b: 2}->keys() var->assert_equal(['a', 'b']) @@ -812,6 +950,10 @@ def SID(): number ->str2nr() enddef +def Test_listener_remove() + CheckDefFailure(['echo listener_remove("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + def Test_map_function_arg() var lines =<< trim END def MapOne(i: number, v: string): string @@ -914,6 +1056,16 @@ def Test_map_failure() delete('Xtmpfile') enddef +def Test_matcharg() + CheckDefFailure(['echo matcharg("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_matchdelete() + CheckDefFailure(['echo matchdelete("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo matchdelete("x", 1)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo matchdelete(1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') +enddef + def Test_max() g:flag = true var l1: list = g:flag @@ -946,6 +1098,11 @@ def Test_nr2char() nr2char(97, true)->assert_equal('a') enddef +def Test_or() + CheckDefFailure(['echo or("x", 0x2)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo or(0x1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') +enddef + def Test_readdir() eval expand('sautest')->readdir((e) => e[0] !=# '.') eval expand('sautest')->readdirex((e) => e.name[0] !=# '.') @@ -995,6 +1152,26 @@ def Test_reverse_return_type() res->assert_equal(6) enddef +def Test_screenattr() + CheckDefFailure(['echo screenattr("x", 1)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo screenattr(1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') +enddef + +def Test_screenchar() + CheckDefFailure(['echo screenchar("x", 1)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo screenchar(1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') +enddef + +def Test_screenchars() + CheckDefFailure(['echo screenchars("x", 1)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo screenchars(1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') +enddef + +def Test_screenstring() + CheckDefFailure(['echo screenstring("x", 1)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo screenstring(1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') +enddef + def Test_search() new setline(1, ['foo', 'bar']) @@ -1157,6 +1334,10 @@ def Test_setbufvar() getbufvar('%', 'myvar')->assert_equal(123) enddef +def Test_setcmdpos() + CheckDefFailure(['echo setcmdpos("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + def Test_setloclist() var items = [{filename: '/tmp/file', lnum: 1, valid: true}] var what = {items: items} @@ -1172,6 +1353,10 @@ def Test_setreg() assert_fails('setreg("ab", 0)', 'E1162:') enddef +def Test_shiftwidth() + CheckDefFailure(['echo shiftwidth("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + def Test_slice() assert_equal('12345', slice('012345', 1)) assert_equal('123', slice('012345', 1, 4)) @@ -1309,6 +1494,20 @@ def Test_timer_paused() timer_stop(id) enddef +def Test_tolower() + CheckDefFailure(['echo tolower(1)'], 'E1013: Argument 1: type mismatch, expected string but got number') +enddef + +def Test_toupper() + CheckDefFailure(['echo toupper(1)'], 'E1013: Argument 1: type mismatch, expected string but got number') +enddef + +def Test_tr() + CheckDefFailure(['echo tr(1, "a", "b")'], 'E1013: Argument 1: type mismatch, expected string but got number') + CheckDefFailure(['echo tr("a", 1, "b")'], 'E1013: Argument 2: type mismatch, expected string but got number') + CheckDefFailure(['echo tr("a", "a", 1)'], 'E1013: Argument 3: type mismatch, expected string but got number') +enddef + def Test_win_execute() assert_equal("\n" .. winnr(), win_execute(win_getid(), 'echo winnr()')) assert_equal('', win_execute(342343, 'echo winnr()')) @@ -1338,7 +1537,45 @@ def Test_winsaveview() CheckDefAndScriptFailure(lines, 'E1012: Type mismatch; expected list but got dict', 1) enddef +def Test_win_gettype() + CheckDefFailure(['echo win_gettype("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef +def Test_win_gotoid() + CheckDefFailure(['echo win_gotoid("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef +def Test_win_id2tabwin() + CheckDefFailure(['echo win_id2tabwin("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_win_id2win() + CheckDefFailure(['echo win_id2win("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_win_screenpos() + CheckDefFailure(['echo win_screenpos("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_winbufnr() + CheckDefFailure(['echo winbufnr("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_winheight() + CheckDefFailure(['echo winheight("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_winlayout() + CheckDefFailure(['echo winlayout("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_winwidth() + CheckDefFailure(['echo winwidth("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_xor() + CheckDefFailure(['echo xor("x", 0x2)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo xor(0x1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') +enddef " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/testing.c b/src/testing.c index 9f5a2f5acb..5ebcefbdf0 100644 --- a/src/testing.c +++ b/src/testing.c @@ -1211,6 +1211,12 @@ f_test_scrollbar(typval_T *argvars, typval_T *rettv UNUSED) void f_test_setmouse(typval_T *argvars, typval_T *rettv UNUSED) { + if (argvars[0].v_type != VAR_NUMBER || (argvars[1].v_type) != VAR_NUMBER) + { + emsg(_(e_invarg)); + return; + } + mouse_row = (time_t)tv_get_number(&argvars[0]) - 1; mouse_col = (time_t)tv_get_number(&argvars[1]) - 1; } @@ -1219,11 +1225,27 @@ f_test_setmouse(typval_T *argvars, typval_T *rettv UNUSED) f_test_gui_mouse_event(typval_T *argvars UNUSED, typval_T *rettv UNUSED) { #ifdef FEAT_GUI - int button = tv_get_number(&argvars[0]); - int row = tv_get_number(&argvars[1]); - int col = tv_get_number(&argvars[2]); - int repeated_click = tv_get_number(&argvars[3]); - int_u mods = tv_get_number(&argvars[4]); + int button; + int row; + int col; + int repeated_click; + int_u mods; + + if (argvars[0].v_type != VAR_NUMBER + || (argvars[1].v_type) != VAR_NUMBER + || (argvars[2].v_type) != VAR_NUMBER + || (argvars[3].v_type) != VAR_NUMBER + || (argvars[4].v_type) != VAR_NUMBER) + { + emsg(_(e_invarg)); + return; + } + + button = tv_get_number(&argvars[0]); + row = tv_get_number(&argvars[1]); + col = tv_get_number(&argvars[2]); + repeated_click = tv_get_number(&argvars[3]); + mods = tv_get_number(&argvars[4]); gui_send_mouse_event(button, TEXT_X(col - 1), TEXT_Y(row - 1), repeated_click, mods); #endif diff --git a/src/version.c b/src/version.c index 653e578b9f..933dd4f798 100644 --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3036, /**/ 3035, /**/