From b7e2483655d9b68df0c7349918027d800051a28a Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 24 Jun 2020 13:37:35 +0200 Subject: [PATCH 001/105] patch 8.2.1046: insufficient tests for src/buffer.c Problem: Insufficient tests for src/buffer.c. Solution: Add more tests. Move comments related tests to a separate file. (Yegappan Lakshmanan, closes #6325) --- src/testdir/Make_all.mak | 2 + src/testdir/test_buffer.vim | 204 +++++++++++++++++++++++ src/testdir/test_cmdline.vim | 1 + src/testdir/test_comments.vim | 277 ++++++++++++++++++++++++++++++++ src/testdir/test_normal.vim | 1 + src/testdir/test_textformat.vim | 272 ------------------------------- src/version.c | 2 + 7 files changed, 487 insertions(+), 272 deletions(-) create mode 100644 src/testdir/test_comments.vim diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak index 3d143588f2..fff4fdf6ac 100644 --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -90,6 +90,7 @@ NEW_TESTS = \ test_close_count \ test_cmdline \ test_command_count \ + test_comments \ test_comparators \ test_compiler \ test_conceal \ @@ -341,6 +342,7 @@ NEW_TESTS_RES = \ test_close_count.res \ test_cmdline.res \ test_command_count.res \ + test_comments.res \ test_comparators.res \ test_conceal.res \ test_const.res \ diff --git a/src/testdir/test_buffer.vim b/src/testdir/test_buffer.vim index de163538e4..5981e247ac 100644 --- a/src/testdir/test_buffer.vim +++ b/src/testdir/test_buffer.vim @@ -1,5 +1,7 @@ " Tests for Vim buffer +source check.vim + " Test for the :bunload command with an offset func Test_bunload_with_offset() %bwipe! @@ -152,6 +154,24 @@ func Test_bdelete_cmd() set nobuflisted enew call assert_fails('bdelete ' .. bnr, 'E516:') + + " Deleting more than one buffer + new Xbuf1 + new Xbuf2 + exe 'bdel ' .. bufnr('Xbuf2') .. ' ' .. bufnr('Xbuf1') + call assert_equal(1, winnr('$')) + call assert_equal(0, getbufinfo('Xbuf1')[0].loaded) + call assert_equal(0, getbufinfo('Xbuf2')[0].loaded) + + " Deleting more than one buffer and an invalid buffer + new Xbuf1 + new Xbuf2 + let cmd = "exe 'bdel ' .. bufnr('Xbuf2') .. ' xxx ' .. bufnr('Xbuf1')" + call assert_fails(cmd, 'E94:') + call assert_equal(2, winnr('$')) + call assert_equal(1, getbufinfo('Xbuf1')[0].loaded) + call assert_equal(0, getbufinfo('Xbuf2')[0].loaded) + %bwipe! endfunc @@ -166,4 +186,188 @@ func Test_buffer_error() %bwipe endfunc +" Test for the status messages displayed when unloading, deleting or wiping +" out buffers +func Test_buffer_statusmsg() + CheckEnglish + set report=1 + new Xbuf1 + new Xbuf2 + let bnr = bufnr() + exe "normal 2\" + call assert_match('buf ' .. bnr .. ':', v:statusmsg) + bunload Xbuf1 Xbuf2 + call assert_equal('2 buffers unloaded', v:statusmsg) + bdel Xbuf1 Xbuf2 + call assert_equal('2 buffers deleted', v:statusmsg) + bwipe Xbuf1 Xbuf2 + call assert_equal('2 buffers wiped out', v:statusmsg) + set report& +endfunc + +" Test for quitting the 'swapfile exists' dialog with the split buffer +" command. +func Test_buffer_sbuf_cleanup() + call writefile([], 'Xfile') + " first open the file in a buffer + new Xfile + let bnr = bufnr() + close + " create the swap file + call writefile([], '.Xfile.swp') + " Remove the catch-all that runtest.vim adds + au! SwapExists + augroup BufTest + au! + autocmd SwapExists Xfile let v:swapchoice='q' + augroup END + exe 'sbuf ' . bnr + call assert_equal(1, winnr('$')) + call assert_equal(0, getbufinfo('Xfile')[0].loaded) + + " test for :sball + sball + call assert_equal(1, winnr('$')) + call assert_equal(0, getbufinfo('Xfile')[0].loaded) + + %bw! + set shortmess+=F + let v:statusmsg = '' + edit Xfile + call assert_equal('', v:statusmsg) + call assert_equal(1, winnr('$')) + call assert_equal(0, getbufinfo('Xfile')[0].loaded) + set shortmess& + + call delete('Xfile') + call delete('.Xfile.swp') + augroup BufTest + au! + augroup END + augroup! BufTest +endfunc + +" Test for deleting a modified buffer with :confirm +func Test_bdel_with_confirm() + CheckUnix + CheckNotGui + CheckFeature dialog_con + new + call setline(1, 'test') + call assert_fails('bdel', 'E89:') + call feedkeys('c', 'L') + confirm bdel + call assert_equal(2, winnr('$')) + call assert_equal(1, &modified) + call feedkeys('n', 'L') + confirm bdel + call assert_equal(1, winnr('$')) +endfunc + +" Test for editing another buffer from a modified buffer with :confirm +func Test_goto_buf_with_confirm() + CheckUnix + CheckNotGui + CheckFeature dialog_con + new Xfile + enew + call setline(1, 'test') + call assert_fails('b Xfile', 'E37:') + call feedkeys('c', 'L') + call assert_fails('confirm b Xfile', 'E37:') + call assert_equal(1, &modified) + call assert_equal('', @%) + call feedkeys('y', 'L') + call assert_fails('confirm b Xfile', 'E37:') + call assert_equal(1, &modified) + call assert_equal('', @%) + call feedkeys('n', 'L') + confirm b Xfile + call assert_equal('Xfile', @%) + close! +endfunc + +" Test for splitting buffer with 'switchbuf' +func Test_buffer_switchbuf() + new Xfile + wincmd w + set switchbuf=useopen + sbuf Xfile + call assert_equal(1, winnr()) + call assert_equal(2, winnr('$')) + set switchbuf=usetab + tabnew + sbuf Xfile + call assert_equal(1, tabpagenr()) + call assert_equal(2, tabpagenr('$')) + set switchbuf& + %bw +endfunc + +" Test for BufAdd autocommand wiping out the buffer +func Test_bufadd_autocmd_bwipe() + %bw! + augroup BufAdd_Wipe + au! + autocmd BufAdd Xfile %bw! + augroup END + edit Xfile + call assert_equal('', @%) + call assert_equal(0, bufexists('Xfile')) + augroup BufAdd_Wipe + au! + augroup END + augroup! BufAdd_Wipe +endfunc + +" Test for trying to load a buffer with text locked +" e in the command line is used to lock the text +func Test_load_buf_with_text_locked() + new Xfile1 + edit Xfile2 + let cmd = ":\eexecute(\"normal \\")\\" + call assert_fails("call feedkeys(cmd, 'xt')", 'E565:') + %bw! +endfunc + +" Test for using CTRL-^ to edit the alternative file keeping the cursor +" position with 'nostartofline'. Also test using the 'buf' command. +func Test_buffer_edit_altfile() + call writefile(repeat(['one two'], 50), 'Xfile1') + call writefile(repeat(['five six'], 50), 'Xfile2') + set nosol + edit Xfile1 + call cursor(25, 5) + edit Xfile2 + call cursor(30, 4) + exe "normal \" + call assert_equal([0, 25, 5, 0], getpos('.')) + exe "normal \" + call assert_equal([0, 30, 4, 0], getpos('.')) + buf Xfile1 + call assert_equal([0, 25, 5, 0], getpos('.')) + buf Xfile2 + call assert_equal([0, 30, 4, 0], getpos('.')) + set sol& + call delete('Xfile1') + call delete('Xfile2') +endfunc + +" Test for running the :sball command with a maximum window count and a +" modified buffer +func Test_sball_with_count() + %bw! + edit Xfile1 + call setline(1, ['abc']) + new Xfile2 + new Xfile3 + new Xfile4 + 2sball + call assert_equal(bufnr('Xfile4'), winbufnr(1)) + call assert_equal(bufnr('Xfile1'), winbufnr(2)) + call assert_equal(0, getbufinfo('Xfile2')[0].loaded) + call assert_equal(0, getbufinfo('Xfile3')[0].loaded) + %bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index 5d772eacdc..4dca8ea6ea 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -392,6 +392,7 @@ func Test_getcompletion() call delete('Xtags') set tags& + call assert_fails("call getcompletion('\\\\@!\\\\@=', 'buffer')", 'E871:') call assert_fails('call getcompletion("", "burp")', 'E475:') call assert_fails('call getcompletion("abc", [])', 'E475:') endfunc diff --git a/src/testdir/test_comments.vim b/src/testdir/test_comments.vim new file mode 100644 index 0000000000..c34b85c42d --- /dev/null +++ b/src/testdir/test_comments.vim @@ -0,0 +1,277 @@ +" Tests for the various flags in the 'comments' option + +" Test for the 'n' flag in 'comments' +func Test_comment_nested() + new + setlocal comments=n:> fo+=ro + exe "normal i> B\nD\ggOA\joC\Go\>>> F\nH" + exe "normal 5GOE\6GoG" + let expected =<< trim END + > A + > B + > C + > D + >>>> E + >>>> F + >>>> G + >>>> H + END + call assert_equal(expected, getline(1, '$')) + close! +endfunc + +" Test for the 'b' flag in 'comments' +func Test_comment_blank() + new + setlocal comments=b:* fo+=ro + exe "normal i* E\nF\n\G\nH\ggOC\O\B\OA\2joD" + let expected =<< trim END + A + *B + * C + * D + * E + * F + *G + H + END + call assert_equal(expected, getline(1, '$')) + close! +endfunc + +" Test for the 'f' flag in 'comments' (only the first line has a comment +" string) +func Test_comment_firstline() + new + setlocal comments=f:- fo+=ro + exe "normal i- B\nD\ggoC\ggOA\" + call assert_equal(['A', '- B', ' C', ' D'], getline(1, '$')) + %d + setlocal comments=:- + exe "normal i- B\nD\ggoC\ggOA\" + call assert_equal(['- A', '- B', '- C', '- D'], getline(1, '$')) + close! +endfunc + +" Test for the 's', 'm' and 'e' flags in 'comments' +" Test for automatically adding comment leaders in insert mode +func Test_comment_threepiece() + new + setlocal expandtab + call setline(1, ["\t/*"]) + setlocal formatoptions=croql + call cursor(1, 3) + call feedkeys("A\\/", 'tnix') + call assert_equal(["\t/*", " *", " */"], getline(1, '$')) + + " If a comment ends in a single line, then don't add it in the next line + %d + call setline(1, '/* line1 */') + call feedkeys("A\next line", 'xt') + call assert_equal(['/* line1 */', 'next line'], getline(1, '$')) + + %d + " Copy the trailing indentation from the leader comment to a new line + setlocal autoindent noexpandtab + call feedkeys("a\t/*\tone\ntwo\n/", 'xt') + call assert_equal(["\t/*\tone", "\t *\ttwo", "\t */"], getline(1, '$')) + close! +endfunc + +" Test for the 'r' flag in 'comments' (right align comment) +func Test_comment_rightalign() + new + setlocal comments=sr:/***,m:**,ex-2:******/ fo+=ro + exe "normal i=\o\t /***\nD\n/" + exe "normal 2GOA\joB\jOC\joE\GOF\joG" + let expected =<< trim END + = + A + /*** + ** B + ** C + ** D + ** E + ** F + ******/ + G + END + call assert_equal(expected, getline(1, '$')) + close! +endfunc + +" Test for the 'O' flag in 'comments' +func Test_comment_O() + new + setlocal comments=Ob:* fo+=ro + exe "normal i* B\nD\kOA\joC" + let expected =<< trim END + A + * B + * C + * D + END + call assert_equal(expected, getline(1, '$')) + close! +endfunc + +" Test for using a multibyte character as a comment leader +func Test_comment_multibyte_leader() + new + let t =<< trim END + { + X + Xa + XaY + XY + XYZ + X Y + X YZ + XX + XXa + XXY + } + END + call setline(1, t) + call cursor(2, 1) + + set tw=2 fo=cqm comments=n:X + exe "normal gqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgq" + let t =<< trim END + X + Xa + XaY + XY + XYZ + X Y + X YZ + XX + XXa + XXY + END + exe "normal o\n" . join(t, "\n") + + let expected =<< trim END + { + X + Xa + Xa + XY + XY + XY + XZ + X Y + X Y + X Z + XX + XXa + XXY + + X + Xa + Xa + XY + XY + XY + XZ + X Y + X Y + X Z + XX + XXa + XXY + } + END + call assert_equal(expected, getline(1, '$')) + + set tw& fo& comments& + close! +endfunc + +" Test for a space character in 'comments' setting +func Test_comment_space() + new + setlocal comments=b:\ > fo+=ro + exe "normal i> B\nD\ggOA\joC" + exe "normal Go > F\nH\kOE\joG" + let expected =<< trim END + A + > B + C + D + > E + > F + > G + > H + END + call assert_equal(expected, getline(1, '$')) + close! +endfunc + +" Test for formatting lines with and without comments +func Test_comment_format_lines() + new + call setline(1, ['one', '/* two */', 'three']) + normal gggqG + call assert_equal(['one', '/* two */', 'three'], getline(1, '$')) + close! +endfunc + +" Test for using 'a' in 'formatoptions' with comments +func Test_comment_autoformat() + new + setlocal formatoptions+=a + call feedkeys("a- one\n- two\n", 'xt') + call assert_equal(['- one', '- two', ''], getline(1, '$')) + + %d + call feedkeys("a\none\n", 'xt') + call assert_equal(['', 'one', ''], getline(1, '$')) + + setlocal formatoptions+=aw + %d + call feedkeys("aone \ntwo\n", 'xt') + call assert_equal(['one two', ''], getline(1, '$')) + + %d + call feedkeys("aone\ntwo\n", 'xt') + call assert_equal(['one', 'two', ''], getline(1, '$')) + + close! +endfunc + +" Test for joining lines with comments ('j' flag in 'formatoptions') +func Test_comment_join_lines_fo_j() + new + setlocal fo+=j comments=:// + call setline(1, ['i++; // comment1', ' // comment2']) + normal J + call assert_equal('i++; // comment1 comment2', getline(1)) + setlocal fo-=j + call setline(1, ['i++; // comment1', ' // comment2']) + normal J + call assert_equal('i++; // comment1 // comment2', getline(1)) + " Test with nested comments + setlocal fo+=j comments=n:>,n:) + call setline(1, ['i++; > ) > ) comment1', ' > ) comment2']) + normal J + call assert_equal('i++; > ) > ) comment1 comment2', getline(1)) + close! +endfunc + +" Test for formatting lines where only the first line has a comment. +func Test_comment_format_firstline_comment() + new + setlocal formatoptions=tcq + call setline(1, ['- one two', 'three']) + normal gggqG + call assert_equal(['- one two three'], getline(1, '$')) + + %d + call setline(1, ['- one', '- two']) + normal gggqG + call assert_equal(['- one', '- two'], getline(1, '$')) + close! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_normal.vim b/src/testdir/test_normal.vim index 2a06bb5b01..a4dc61945a 100644 --- a/src/testdir/test_normal.vim +++ b/src/testdir/test_normal.vim @@ -1267,6 +1267,7 @@ func Test_normal21_nv_hat() edit Xfoo | %bw call assert_fails(':buffer #', 'E86') call assert_fails(':execute "normal! \"', 'E23') + call assert_fails("normal i\#", 'E23:') " Test for the expected behavior when switching between two named buffers. edit Xfoo | edit Xbar diff --git a/src/testdir/test_textformat.vim b/src/testdir/test_textformat.vim index d529b8520a..a91c3da81c 100644 --- a/src/testdir/test_textformat.vim +++ b/src/testdir/test_textformat.vim @@ -784,78 +784,6 @@ func Test_tw_2_fo_tm_noai() bwipe! endfunc -func Test_tw_2_fo_cqm_com() - new - let t =<< trim END - { - X - Xa - XaY - XY - XYZ - X Y - X YZ - XX - XXa - XXY - } - END - call setline(1, t) - call cursor(2, 1) - - set tw=2 fo=cqm comments=n:X - exe "normal gqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgq" - let t =<< trim END - X - Xa - XaY - XY - XYZ - X Y - X YZ - XX - XXa - XXY - END - exe "normal o\n" . join(t, "\n") - - let expected =<< trim END - { - X - Xa - Xa - XY - XY - XY - XZ - X Y - X Y - X Z - XX - XXa - XXY - - X - Xa - Xa - XY - XY - XY - XZ - X Y - X Y - X Z - XX - XXa - XXY - } - END - call assert_equal(expected, getline(1, '$')) - - set tw& fo& comments& - bwipe! -endfunc - func Test_tw_2_fo_tm_replace() new let t =<< trim END @@ -975,140 +903,6 @@ func Test_whichwrap_multi_byte() bwipe! endfunc -" Test for automatically adding comment leaders in insert mode -func Test_threepiece_comment() - new - setlocal expandtab - call setline(1, ["\t/*"]) - setlocal formatoptions=croql - call cursor(1, 3) - call feedkeys("A\\/", 'tnix') - call assert_equal(["\t/*", " *", " */"], getline(1, '$')) - - " If a comment ends in a single line, then don't add it in the next line - %d - call setline(1, '/* line1 */') - call feedkeys("A\next line", 'xt') - call assert_equal(['/* line1 */', 'next line'], getline(1, '$')) - - %d - " Copy the trailing indentation from the leader comment to a new line - setlocal autoindent noexpandtab - call feedkeys("a\t/*\tone\ntwo\n/", 'xt') - call assert_equal(["\t/*\tone", "\t *\ttwo", "\t */"], getline(1, '$')) - close! -endfunc - -" Test for the 'f' flag in 'comments' (only the first line has the comment -" string) -func Test_firstline_comment() - new - setlocal comments=f:- fo+=ro - exe "normal i- B\nD\ggoC\ggOA\" - call assert_equal(['A', '- B', ' C', ' D'], getline(1, '$')) - %d - setlocal comments=:- - exe "normal i- B\nD\ggoC\ggOA\" - call assert_equal(['- A', '- B', '- C', '- D'], getline(1, '$')) - %bw! -endfunc - -" Test for the 'r' flag in 'comments' (right align comment) -func Test_comment_rightalign() - new - setlocal comments=sr:/***,m:**,ex-2:******/ fo+=ro - exe "normal i=\o\t /***\nD\n/" - exe "normal 2GOA\joB\jOC\joE\GOF\joG" - let expected =<< trim END - = - A - /*** - ** B - ** C - ** D - ** E - ** F - ******/ - G - END - call assert_equal(expected, getline(1, '$')) - %bw! -endfunc - -" Test for the 'b' flag in 'comments' -func Test_comment_blank() - new - setlocal comments=b:* fo+=ro - exe "normal i* E\nF\n\G\nH\ggOC\O\B\OA\2joD" - let expected =<< trim END - A - *B - * C - * D - * E - * F - *G - H - END - call assert_equal(expected, getline(1, '$')) - %bw! -endfunc - -" Test for the 'n' flag in comments -func Test_comment_nested() - new - setlocal comments=n:> fo+=ro - exe "normal i> B\nD\ggOA\joC\Go\>>> F\nH" - exe "normal 5GOE\6GoG" - let expected =<< trim END - > A - > B - > C - > D - >>>> E - >>>> F - >>>> G - >>>> H - END - call assert_equal(expected, getline(1, '$')) - %bw! -endfunc - -" Test for a space character in 'comments' setting -func Test_comment_space() - new - setlocal comments=b:\ > fo+=ro - exe "normal i> B\nD\ggOA\joC" - exe "normal Go > F\nH\kOE\joG" - let expected =<< trim END - A - > B - C - D - > E - > F - > G - > H - END - call assert_equal(expected, getline(1, '$')) - %bw! -endfunc - -" Test for the 'O' flag in 'comments' -func Test_comment_O() - new - setlocal comments=Ob:* fo+=ro - exe "normal i* B\nD\kOA\joC" - let expected =<< trim END - A - * B - * C - * D - END - call assert_equal(expected, getline(1, '$')) - %bw! -endfunc - " Test for 'a' and 'w' flags in 'formatoptions' func Test_fo_a_w() new @@ -1143,25 +937,6 @@ func Test_fo_a_w() %bw! endfunc -" Test for 'j' flag in 'formatoptions' -func Test_fo_j() - new - setlocal fo+=j comments=:// - call setline(1, ['i++; // comment1', ' // comment2']) - normal J - call assert_equal('i++; // comment1 comment2', getline(1)) - setlocal fo-=j - call setline(1, ['i++; // comment1', ' // comment2']) - normal J - call assert_equal('i++; // comment1 // comment2', getline(1)) - " Test with nested comments - setlocal fo+=j comments=n:>,n:) - call setline(1, ['i++; > ) > ) comment1', ' > ) comment2']) - normal J - call assert_equal('i++; > ) > ) comment1 comment2', getline(1)) - %bw! -endfunc - " Test for formatting lines using gq in visual mode func Test_visual_gq_format() new @@ -1296,51 +1071,4 @@ func Test_fo_2() close! endfunc -" Test for formatting lines where only the first line has a comment. -func Test_fo_gq_with_firstline_comment() - new - setlocal formatoptions=tcq - call setline(1, ['- one two', 'three']) - normal gggqG - call assert_equal(['- one two three'], getline(1, '$')) - - %d - call setline(1, ['- one', '- two']) - normal gggqG - call assert_equal(['- one', '- two'], getline(1, '$')) - close! -endfunc - -" Test for trying to join a comment line with a non-comment line -func Test_join_comments() - new - call setline(1, ['one', '/* two */', 'three']) - normal gggqG - call assert_equal(['one', '/* two */', 'three'], getline(1, '$')) - close! -endfunc - -" Test for using 'a' in 'formatoptions' with comments -func Test_autoformat_comments() - new - setlocal formatoptions+=a - call feedkeys("a- one\n- two\n", 'xt') - call assert_equal(['- one', '- two', ''], getline(1, '$')) - - %d - call feedkeys("a\none\n", 'xt') - call assert_equal(['', 'one', ''], getline(1, '$')) - - setlocal formatoptions+=aw - %d - call feedkeys("aone \ntwo\n", 'xt') - call assert_equal(['one two', ''], getline(1, '$')) - - %d - call feedkeys("aone\ntwo\n", 'xt') - call assert_equal(['one', 'two', ''], getline(1, '$')) - - close! -endfunc - " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 49f9ddf22f..1ee45307f3 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1046, /**/ 1045, /**/ From 5409f5d8c95007216ae1190565a7a8ee9ebd7100 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 24 Jun 2020 18:37:35 +0200 Subject: [PATCH 002/105] patch 8.2.1047: Vim9: script cannot use line continuation like :def function Problem: Vim9: script cannot use line continuation like in a :def function. Solution: Pass the getline function pointer to the eval() functions. Use it for addition and multiplication operators. --- src/dict.c | 11 +- src/eval.c | 274 +++++++++++++++++++++++---------- src/evalfunc.c | 2 +- src/evalvars.c | 13 +- src/ex_eval.c | 7 +- src/globals.h | 3 + src/list.c | 6 +- src/proto/eval.pro | 4 +- src/proto/scriptfile.pro | 1 + src/scriptfile.c | 9 ++ src/structs.h | 13 ++ src/testdir/test_vim9_expr.vim | 40 +++++ src/userfunc.c | 22 ++- src/version.c | 2 + src/vim.h | 4 - 15 files changed, 300 insertions(+), 111 deletions(-) diff --git a/src/dict.c b/src/dict.c index 9d94261986..caa398deba 100644 --- a/src/dict.c +++ b/src/dict.c @@ -788,12 +788,14 @@ get_literal_key(char_u **arg, typval_T *tv) /* * Allocate a variable for a Dictionary and fill it from "*arg". * "literal" is TRUE for #{key: val} + * "flags" can have EVAL_EVALUATE and other EVAL_ flags. * Return OK or FAIL. Returns NOTDONE for {expr}. */ int eval_dict(char_u **arg, typval_T *rettv, int flags, int literal) { int evaluate = flags & EVAL_EVALUATE; + evalarg_T evalarg; dict_T *d = NULL; typval_T tvkey; typval_T tv; @@ -803,6 +805,9 @@ eval_dict(char_u **arg, typval_T *rettv, int flags, int literal) char_u buf[NUMBUFLEN]; int vim9script = current_sctx.sc_version == SCRIPT_VERSION_VIM9; + CLEAR_FIELD(evalarg); + evalarg.eval_flags = flags; + /* * First check if it's not a curly-braces thing: {expr}. * Must do this without evaluating, otherwise a function may be called @@ -812,7 +817,7 @@ eval_dict(char_u **arg, typval_T *rettv, int flags, int literal) */ if (!vim9script && *start != '}') { - if (eval1(&start, &tv, 0) == FAIL) // recursive! + if (eval1(&start, &tv, NULL) == FAIL) // recursive! return FAIL; if (*start == '}') return NOTDONE; @@ -832,7 +837,7 @@ eval_dict(char_u **arg, typval_T *rettv, int flags, int literal) { if ((literal ? get_literal_key(arg, &tvkey) - : eval1(arg, &tvkey, flags)) == FAIL) // recursive! + : eval1(arg, &tvkey, &evalarg)) == FAIL) // recursive! goto failret; if (**arg != ':') @@ -854,7 +859,7 @@ eval_dict(char_u **arg, typval_T *rettv, int flags, int literal) } *arg = skipwhite(*arg + 1); - if (eval1(arg, &tv, flags) == FAIL) // recursive! + if (eval1(arg, &tv, &evalarg) == FAIL) // recursive! { if (evaluate) clear_tv(&tvkey); diff --git a/src/eval.c b/src/eval.c index 38afc24894..ffb69de8d2 100644 --- a/src/eval.c +++ b/src/eval.c @@ -45,12 +45,12 @@ typedef struct } forinfo_T; static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op); -static int eval2(char_u **arg, typval_T *rettv, int flags); -static int eval3(char_u **arg, typval_T *rettv, int flags); -static int eval4(char_u **arg, typval_T *rettv, int flags); -static int eval5(char_u **arg, typval_T *rettv, int flags); -static int eval6(char_u **arg, typval_T *rettv, int flags, int want_string); -static int eval7(char_u **arg, typval_T *rettv, int flags, int want_string); +static int eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg); +static int eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg); +static int eval4(char_u **arg, typval_T *rettv, evalarg_T *evalarg); +static int eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg); +static int eval6(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int want_string); +static int eval7(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int want_string); static int eval7_leader(typval_T *rettv, char_u *start_leader, char_u **end_leaderp); static int free_unref_items(int copyID); @@ -169,7 +169,7 @@ eval_to_bool( if (skip) ++emsg_skip; - if (eval0(arg, &tv, nextcmd, skip ? 0 : EVAL_EVALUATE) == FAIL) + if (eval0(arg, &tv, nextcmd, skip ? NULL : &EVALARG_EVALUATE) == FAIL) *error = TRUE; else { @@ -197,7 +197,7 @@ eval1_emsg(char_u **arg, typval_T *rettv, int evaluate) int did_emsg_before = did_emsg; int called_emsg_before = called_emsg; - ret = eval1(arg, rettv, evaluate ? EVAL_EVALUATE : 0); + ret = eval1(arg, rettv, evaluate ? &EVALARG_EVALUATE : NULL); if (ret == FAIL) { // Report the invalid expression unless the expression evaluation has @@ -325,7 +325,8 @@ eval_to_string_skip( if (skip) ++emsg_skip; - if (eval0(arg, &tv, nextcmd, skip ? 0 : EVAL_EVALUATE) == FAIL || skip) + if (eval0(arg, &tv, nextcmd, skip ? NULL : &EVALARG_EVALUATE) + == FAIL || skip) retval = NULL; else { @@ -348,7 +349,7 @@ skip_expr(char_u **pp) typval_T rettv; *pp = skipwhite(*pp); - return eval1(pp, &rettv, 0); + return eval1(pp, &rettv, NULL); } /* @@ -370,7 +371,7 @@ eval_to_string( char_u numbuf[NUMBUFLEN]; #endif - if (eval0(arg, &tv, nextcmd, EVAL_EVALUATE) == FAIL) + if (eval0(arg, &tv, nextcmd, &EVALARG_EVALUATE) == FAIL) retval = NULL; else { @@ -440,7 +441,7 @@ eval_to_number(char_u *expr) ++emsg_off; - if (eval1(&p, &rettv, EVAL_EVALUATE) == FAIL) + if (eval1(&p, &rettv, &EVALARG_EVALUATE) == FAIL) retval = -1; else { @@ -463,7 +464,7 @@ eval_expr(char_u *arg, char_u **nextcmd) typval_T *tv; tv = ALLOC_ONE(typval_T); - if (tv != NULL && eval0(arg, tv, nextcmd, EVAL_EVALUATE) == FAIL) + if (tv != NULL && eval0(arg, tv, nextcmd, &EVALARG_EVALUATE) == FAIL) VIM_CLEAR(tv); return tv; @@ -588,7 +589,7 @@ eval_foldexpr(char_u *arg, int *cp) ++sandbox; ++textwinlock; *cp = NUL; - if (eval0(arg, &tv, NULL, EVAL_EVALUATE) == FAIL) + if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) retval = 0; else { @@ -776,7 +777,7 @@ get_lval( else { empty1 = FALSE; - if (eval1(&p, &var1, EVAL_EVALUATE) == FAIL) // recursive! + if (eval1(&p, &var1, &EVALARG_EVALUATE) == FAIL) // recursive! return NULL; if (tv_get_string_chk(&var1) == NULL) { @@ -814,7 +815,7 @@ get_lval( { lp->ll_empty2 = FALSE; // recursive! - if (eval1(&p, &var2, EVAL_EVALUATE) == FAIL) + if (eval1(&p, &var2, &EVALARG_EVALUATE) == FAIL) { clear_tv(&var1); return NULL; @@ -1424,7 +1425,10 @@ eval_for_line( char_u *expr; typval_T tv; list_T *l; + evalarg_T evalarg; + CLEAR_FIELD(evalarg); + evalarg.eval_flags = skip ? 0 : EVAL_EVALUATE; *errp = TRUE; // default: there is an error fi = ALLOC_CLEAR_ONE(forinfo_T); @@ -1444,8 +1448,7 @@ eval_for_line( if (skip) ++emsg_skip; - if (eval0(skipwhite(expr + 2), &tv, nextcmdp, skip ? 0 : EVAL_EVALUATE) - == OK) + if (eval0(skipwhite(expr + 2), &tv, nextcmdp, &evalarg) == OK) { *errp = FALSE; if (!skip) @@ -1763,6 +1766,35 @@ eval_func( return ret; } +/* + * If inside Vim9 script, "arg" points to the end of a line (ignoring comments) + * and there is a next line, return the next line (skipping blanks) and set + * "getnext". + * Otherwise just return "arg" unmodified and set "getnext" to FALSE. + * "arg" must point somewhere inside a line, not at the start. + */ + static char_u * +eval_next_non_blank(char_u *arg, evalarg_T *evalarg, int *getnext) +{ + *getnext = FALSE; + if (current_sctx.sc_version == SCRIPT_VERSION_VIM9 + && evalarg != NULL + && evalarg->eval_cookie != NULL + && (*arg == NUL || (VIM_ISWHITE(arg[-1]) + && (*arg == '"' || *arg == '#'))) + && source_nextline(evalarg->eval_cookie) != NULL) + { + char_u *p = source_nextline(evalarg->eval_cookie); + + if (p != NULL) + { + *getnext = TRUE; + return skipwhite(p); + } + } + return arg; +} + /* * The "evaluate" argument: When FALSE, the argument is only parsed but not * executed. The function may return OK, but the rettv will be of type @@ -1774,7 +1806,7 @@ eval_func( * This calls eval1() and handles error message and nextcmd. * Put the result in "rettv" when returning OK and "evaluate" is TRUE. * Note: "rettv.v_lock" is not set. - * "flags" has EVAL_EVALUATE and similar flags. + * "evalarg" can be NULL, EVALARG_EVALUATE or a pointer. * Return OK or FAIL. */ int @@ -1782,15 +1814,16 @@ eval0( char_u *arg, typval_T *rettv, char_u **nextcmd, - int flags) + evalarg_T *evalarg) { int ret; char_u *p; int did_emsg_before = did_emsg; int called_emsg_before = called_emsg; + int flags = evalarg == NULL ? 0 : evalarg->eval_flags; p = skipwhite(arg); - ret = eval1(&p, rettv, flags); + ret = eval1(&p, rettv, evalarg); if (ret == FAIL || !ends_excmd2(arg, p)) { if (ret != FAIL) @@ -1826,23 +1859,36 @@ eval0( * Return OK or FAIL. */ int -eval1(char_u **arg, typval_T *rettv, int flags) +eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg) { - int result; - typval_T var2; - /* * Get the first variable. */ - if (eval2(arg, rettv, flags) == FAIL) + if (eval2(arg, rettv, evalarg) == FAIL) return FAIL; if ((*arg)[0] == '?') { - int evaluate = flags & EVAL_EVALUATE; + int result; + typval_T var2; + evalarg_T nested_evalarg; + int orig_flags; + + if (evalarg == NULL) + { + CLEAR_FIELD(nested_evalarg); + orig_flags = 0; + } + else + { + nested_evalarg = *evalarg; + orig_flags = evalarg->eval_flags; + } + + int evaluate = nested_evalarg.eval_flags & EVAL_EVALUATE; result = FALSE; - if (flags & EVAL_EVALUATE) + if (evaluate) { int error = FALSE; @@ -1857,7 +1903,9 @@ eval1(char_u **arg, typval_T *rettv, int flags) * Get the second variable. Recursive! */ *arg = skipwhite(*arg + 1); - if (eval1(arg, rettv, result ? flags : flags & ~EVAL_EVALUATE) == FAIL) + nested_evalarg.eval_flags = result ? orig_flags + : orig_flags & ~EVAL_EVALUATE; + if (eval1(arg, rettv, &nested_evalarg) == FAIL) return FAIL; /* @@ -1875,7 +1923,9 @@ eval1(char_u **arg, typval_T *rettv, int flags) * Get the third variable. Recursive! */ *arg = skipwhite(*arg + 1); - if (eval1(arg, &var2, !result ? flags : flags & ~EVAL_EVALUATE) == FAIL) + nested_evalarg.eval_flags = !result ? orig_flags + : orig_flags & ~EVAL_EVALUATE; + if (eval1(arg, &var2, &nested_evalarg) == FAIL) { if (evaluate && result) clear_tv(rettv); @@ -1898,7 +1948,7 @@ eval1(char_u **arg, typval_T *rettv, int flags) * Return OK or FAIL. */ static int -eval2(char_u **arg, typval_T *rettv, int flags) +eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg) { typval_T var2; long result; @@ -1908,7 +1958,7 @@ eval2(char_u **arg, typval_T *rettv, int flags) /* * Get the first variable. */ - if (eval3(arg, rettv, flags) == FAIL) + if (eval3(arg, rettv, evalarg) == FAIL) return FAIL; /* @@ -1918,7 +1968,22 @@ eval2(char_u **arg, typval_T *rettv, int flags) result = FALSE; while ((*arg)[0] == '|' && (*arg)[1] == '|') { - int evaluate = flags & EVAL_EVALUATE; + evalarg_T nested_evalarg; + int evaluate; + int orig_flags; + + if (evalarg == NULL) + { + CLEAR_FIELD(nested_evalarg); + orig_flags = 0; + evaluate = FALSE; + } + else + { + nested_evalarg = *evalarg; + orig_flags = evalarg->eval_flags; + evaluate = orig_flags & EVAL_EVALUATE; + } if (evaluate && first) { @@ -1934,8 +1999,9 @@ eval2(char_u **arg, typval_T *rettv, int flags) * Get the second variable. */ *arg = skipwhite(*arg + 2); - if (eval3(arg, &var2, !result ? flags : flags & ~EVAL_EVALUATE) - == FAIL) + nested_evalarg.eval_flags = !result ? orig_flags + : orig_flags & ~EVAL_EVALUATE; + if (eval3(arg, &var2, &nested_evalarg) == FAIL) return FAIL; /* @@ -1969,7 +2035,7 @@ eval2(char_u **arg, typval_T *rettv, int flags) * Return OK or FAIL. */ static int -eval3(char_u **arg, typval_T *rettv, int flags) +eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg) { typval_T var2; long result; @@ -1979,7 +2045,7 @@ eval3(char_u **arg, typval_T *rettv, int flags) /* * Get the first variable. */ - if (eval4(arg, rettv, flags) == FAIL) + if (eval4(arg, rettv, evalarg) == FAIL) return FAIL; /* @@ -1989,8 +2055,22 @@ eval3(char_u **arg, typval_T *rettv, int flags) result = TRUE; while ((*arg)[0] == '&' && (*arg)[1] == '&') { - int evaluate = flags & EVAL_EVALUATE; + evalarg_T nested_evalarg; + int orig_flags; + int evaluate; + if (evalarg == NULL) + { + CLEAR_FIELD(nested_evalarg); + orig_flags = 0; + evaluate = FALSE; + } + else + { + nested_evalarg = *evalarg; + orig_flags = evalarg->eval_flags; + evaluate = orig_flags & EVAL_EVALUATE; + } if (evaluate && first) { if (tv_get_number_chk(rettv, &error) == 0) @@ -2005,7 +2085,9 @@ eval3(char_u **arg, typval_T *rettv, int flags) * Get the second variable. */ *arg = skipwhite(*arg + 2); - if (eval4(arg, &var2, result ? flags : flags & ~EVAL_EVALUATE) == FAIL) + nested_evalarg.eval_flags = result ? orig_flags + : orig_flags & ~EVAL_EVALUATE; + if (eval4(arg, &var2, &nested_evalarg) == FAIL) return FAIL; /* @@ -2048,7 +2130,7 @@ eval3(char_u **arg, typval_T *rettv, int flags) * Return OK or FAIL. */ static int -eval4(char_u **arg, typval_T *rettv, int flags) +eval4(char_u **arg, typval_T *rettv, evalarg_T *evalarg) { typval_T var2; char_u *p; @@ -2060,7 +2142,7 @@ eval4(char_u **arg, typval_T *rettv, int flags) /* * Get the first variable. */ - if (eval5(arg, rettv, flags) == FAIL) + if (eval5(arg, rettv, evalarg) == FAIL) return FAIL; p = *arg; @@ -2128,12 +2210,12 @@ eval4(char_u **arg, typval_T *rettv, int flags) * Get the second variable. */ *arg = skipwhite(p + len); - if (eval5(arg, &var2, flags) == FAIL) + if (eval5(arg, &var2, evalarg) == FAIL) { clear_tv(rettv); return FAIL; } - if (flags & EVAL_EVALUATE) + if (evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE)) { int ret = typval_compare(rettv, &var2, type, ic); @@ -2195,23 +2277,14 @@ eval_addlist(typval_T *tv1, typval_T *tv2) * Return OK or FAIL. */ static int -eval5(char_u **arg, typval_T *rettv, int flags) +eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg) { - typval_T var2; - int op; - varnumber_T n1, n2; -#ifdef FEAT_FLOAT - float_T f1 = 0, f2 = 0; -#endif - char_u *s1, *s2; - char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; - char_u *p; - int concat; + int evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE); /* * Get the first variable. */ - if (eval6(arg, rettv, flags, FALSE) == FAIL) + if (eval6(arg, rettv, evalarg, FALSE) == FAIL) return FAIL; /* @@ -2219,12 +2292,20 @@ eval5(char_u **arg, typval_T *rettv, int flags) */ for (;;) { + int getnext; + char_u *p; + int op; + int concat; + typval_T var2; + // "." is only string concatenation when scriptversion is 1 - op = **arg; - concat = op == '.' - && (*(*arg + 1) == '.' || current_sctx.sc_version < 2); + p = eval_next_non_blank(*arg, evalarg, &getnext); + op = *p; + concat = op == '.' && (*(p + 1) == '.' || current_sctx.sc_version < 2); if (op != '+' && op != '-' && !concat) break; + if (getnext) + *arg = skipwhite(getsourceline(0, evalarg->eval_cookie, 0, TRUE)); if ((op != '+' || (rettv->v_type != VAR_LIST && rettv->v_type != VAR_BLOB)) @@ -2240,7 +2321,7 @@ eval5(char_u **arg, typval_T *rettv, int flags) // we know that the first operand needs to be a string or number // without evaluating the 2nd operand. So check before to avoid // side effects after an error. - if ((flags & EVAL_EVALUATE) && tv_get_string_chk(rettv) == NULL) + if (evaluate && tv_get_string_chk(rettv) == NULL) { clear_tv(rettv); return FAIL; @@ -2253,21 +2334,23 @@ eval5(char_u **arg, typval_T *rettv, int flags) if (op == '.' && *(*arg + 1) == '.') // .. string concatenation ++*arg; *arg = skipwhite(*arg + 1); - if (eval6(arg, &var2, flags, op == '.') == FAIL) + if (eval6(arg, &var2, evalarg, op == '.') == FAIL) { clear_tv(rettv); return FAIL; } - if (flags & EVAL_EVALUATE) + if (evaluate) { /* * Compute the result. */ if (op == '.') { - s1 = tv_get_string_buf(rettv, buf1); // already checked - s2 = tv_get_string_buf_chk(&var2, buf2); + char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; + char_u *s1 = tv_get_string_buf(rettv, buf1); + char_u *s2 = tv_get_string_buf_chk(&var2, buf2); + if (s2 == NULL) // type error ? { clear_tv(rettv); @@ -2290,9 +2373,11 @@ eval5(char_u **arg, typval_T *rettv, int flags) } else { - int error = FALSE; - + int error = FALSE; + varnumber_T n1, n2; #ifdef FEAT_FLOAT + float_T f1 = 0, f2 = 0; + if (rettv->v_type == VAR_FLOAT) { f1 = rettv->vval.v_float; @@ -2381,7 +2466,7 @@ eval5(char_u **arg, typval_T *rettv, int flags) eval6( char_u **arg, typval_T *rettv, - int flags, + evalarg_T *evalarg, int want_string) // after "." operator { typval_T var2; @@ -2396,7 +2481,7 @@ eval6( /* * Get the first variable. */ - if (eval7(arg, rettv, flags, want_string) == FAIL) + if (eval7(arg, rettv, evalarg, want_string) == FAIL) return FAIL; /* @@ -2404,11 +2489,17 @@ eval6( */ for (;;) { - op = **arg; + int evaluate = evalarg == NULL ? 0 + : (evalarg->eval_flags & EVAL_EVALUATE); + int getnext; + + op = *eval_next_non_blank(*arg, evalarg, &getnext); if (op != '*' && op != '/' && op != '%') break; + if (getnext) + *arg = skipwhite(getsourceline(0, evalarg->eval_cookie, 0, TRUE)); - if (flags & EVAL_EVALUATE) + if (evaluate) { #ifdef FEAT_FLOAT if (rettv->v_type == VAR_FLOAT) @@ -2431,10 +2522,10 @@ eval6( * Get the second variable. */ *arg = skipwhite(*arg + 1); - if (eval7(arg, &var2, flags, FALSE) == FAIL) + if (eval7(arg, &var2, evalarg, FALSE) == FAIL) return FAIL; - if (flags & EVAL_EVALUATE) + if (evaluate) { #ifdef FEAT_FLOAT if (var2.v_type == VAR_FLOAT) @@ -2551,10 +2642,12 @@ eval6( eval7( char_u **arg, typval_T *rettv, - int flags, + evalarg_T *evalarg, int want_string) // after "." operator { - int evaluate = flags & EVAL_EVALUATE; + int flags = evalarg == NULL ? 0 : evalarg->eval_flags; + int evaluate = evalarg != NULL + && (evalarg->eval_flags & EVAL_EVALUATE); int len; char_u *s; char_u *start_leader, *end_leader; @@ -2672,15 +2765,17 @@ eval7( /* * nested expression: (expression). */ - case '(': *arg = skipwhite(*arg + 1); - ret = eval1(arg, rettv, flags); // recursive! - if (**arg == ')') - ++*arg; - else if (ret == OK) - { - emsg(_(e_missing_close)); - clear_tv(rettv); - ret = FAIL; + case '(': { + *arg = skipwhite(*arg + 1); + ret = eval1(arg, rettv, evalarg); // recursive! + if (**arg == ')') + ++*arg; + else if (ret == OK) + { + emsg(_(e_missing_close)); + clear_tv(rettv); + ret = FAIL; + } } break; @@ -3030,6 +3125,11 @@ eval_index( } else { + evalarg_T evalarg; + + CLEAR_FIELD(evalarg); + evalarg.eval_flags = flags; + /* * something[idx] * @@ -3038,7 +3138,7 @@ eval_index( *arg = skipwhite(*arg + 1); if (**arg == ':') empty1 = TRUE; - else if (eval1(arg, &var1, flags) == FAIL) // recursive! + else if (eval1(arg, &var1, &evalarg) == FAIL) // recursive! return FAIL; else if (evaluate && tv_get_string_chk(&var1) == NULL) { @@ -3056,7 +3156,7 @@ eval_index( *arg = skipwhite(*arg + 1); if (**arg == ']') empty2 = TRUE; - else if (eval1(arg, &var2, flags) == FAIL) // recursive! + else if (eval1(arg, &var2, &evalarg) == FAIL) // recursive! { if (!empty1) clear_tv(&var1); @@ -4947,6 +5047,10 @@ ex_echo(exarg_T *eap) int atstart = TRUE; int did_emsg_before = did_emsg; int called_emsg_before = called_emsg; + evalarg_T evalarg; + + CLEAR_FIELD(evalarg); + evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE; if (eap->skip) ++emsg_skip; @@ -4957,7 +5061,7 @@ ex_echo(exarg_T *eap) need_clr_eos = needclr; p = arg; - if (eval1(&arg, &rettv, eap->skip ? 0 : EVAL_EVALUATE) == FAIL) + if (eval1(&arg, &rettv, &evalarg) == FAIL) { /* * Report the invalid expression unless the expression evaluation diff --git a/src/evalfunc.c b/src/evalfunc.c index c4ae951b60..53584dc46a 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -2084,7 +2084,7 @@ f_eval(typval_T *argvars, typval_T *rettv) s = skipwhite(s); p = s; - if (s == NULL || eval1(&s, rettv, EVAL_EVALUATE) == FAIL) + if (s == NULL || eval1(&s, rettv, &EVALARG_EVALUATE) == FAIL) { if (p != NULL && !aborting()) semsg(_(e_invexpr2), p); diff --git a/src/evalvars.c b/src/evalvars.c index b3e89150ad..35c9dcb73e 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -435,7 +435,7 @@ eval_spell_expr(char_u *badword, char_u *expr) if (p_verbose == 0) ++emsg_off; - if (eval1(&p, &rettv, EVAL_EVALUATE) == OK) + if (eval1(&p, &rettv, &EVALARG_EVALUATE) == OK) { if (rettv.v_type != VAR_LIST) clear_tv(&rettv); @@ -774,7 +774,7 @@ ex_let(exarg_T *eap) } else { - int eval_flags; + evalarg_T evalarg; rettv.v_type = VAR_UNKNOWN; i = FAIL; @@ -797,14 +797,17 @@ ex_let(exarg_T *eap) if (eap->skip) ++emsg_skip; - eval_flags = eap->skip ? 0 : EVAL_EVALUATE; - i = eval0(expr, &rettv, &eap->nextcmd, eval_flags); + evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE; + evalarg.eval_cookie = eap->getline == getsourceline + ? eap->cookie : NULL; + i = eval0(expr, &rettv, &eap->nextcmd, &evalarg); + if (eap->skip) + --emsg_skip; } if (eap->skip) { if (i != FAIL) clear_tv(&rettv); - --emsg_skip; } else if (i != FAIL) { diff --git a/src/ex_eval.c b/src/ex_eval.c index a13f844b14..cb32bd0791 100644 --- a/src/ex_eval.c +++ b/src/ex_eval.c @@ -895,9 +895,12 @@ report_discard_pending(int pending, void *value) ex_eval(exarg_T *eap) { typval_T tv; + evalarg_T evalarg; - if (eval0(eap->arg, &tv, &eap->nextcmd, eap->skip ? 0 : EVAL_EVALUATE) - == OK) + evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE; + evalarg.eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL; + + if (eval0(eap->arg, &tv, &eap->nextcmd, &evalarg) == OK) clear_tv(&tv); } diff --git a/src/globals.h b/src/globals.h index cd15491173..8601e2b1e5 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1880,6 +1880,9 @@ EXTERN char windowsVersion[20] INIT(= {0}); // Used for lv_first in a non-materialized range() list. EXTERN listitem_T range_list_item; + +// Passed to an eval() function to enable evaluation. +EXTERN evalarg_T EVALARG_EVALUATE INIT2(EVAL_EVALUATE, NULL); #endif #ifdef MSWIN diff --git a/src/list.c b/src/list.c index 130ab25251..c624003a21 100644 --- a/src/list.c +++ b/src/list.c @@ -1165,6 +1165,10 @@ get_list_tv(char_u **arg, typval_T *rettv, int flags, int do_error) list_T *l = NULL; typval_T tv; listitem_T *item; + evalarg_T evalarg; + + CLEAR_FIELD(evalarg); + evalarg.eval_flags = flags; if (evaluate) { @@ -1176,7 +1180,7 @@ get_list_tv(char_u **arg, typval_T *rettv, int flags, int do_error) *arg = skipwhite(*arg + 1); while (**arg != ']' && **arg != NUL) { - if (eval1(arg, &tv, flags) == FAIL) // recursive! + if (eval1(arg, &tv, &evalarg) == FAIL) // recursive! goto failret; if (evaluate) { diff --git a/src/proto/eval.pro b/src/proto/eval.pro index 57dd8387cd..4fb04eb1fc 100644 --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -26,8 +26,8 @@ int next_for_item(void *fi_void, char_u *arg); void free_for_info(void *fi_void); void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx); int pattern_match(char_u *pat, char_u *text, int ic); -int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int flags); -int eval1(char_u **arg, typval_T *rettv, int flags); +int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, evalarg_T *evalarg); +int eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg_in); void eval_addblob(typval_T *tv1, typval_T *tv2); int eval_addlist(typval_T *tv1, typval_T *tv2); char_u *partial_name(partial_T *pt); diff --git a/src/proto/scriptfile.pro b/src/proto/scriptfile.pro index e2a267141a..b5b8aae936 100644 --- a/src/proto/scriptfile.pro +++ b/src/proto/scriptfile.pro @@ -22,6 +22,7 @@ void ex_options(exarg_T *eap); linenr_T *source_breakpoint(void *cookie); int *source_dbg_tick(void *cookie); int source_level(void *cookie); +char_u *source_nextline(void *cookie); int do_source(char_u *fname, int check_other, int is_vimrc, int *ret_sid); void ex_scriptnames(exarg_T *eap); void scriptnames_slash_adjust(void); diff --git a/src/scriptfile.c b/src/scriptfile.c index caeab19285..9ffc66c1b1 100644 --- a/src/scriptfile.c +++ b/src/scriptfile.c @@ -1050,6 +1050,15 @@ source_level(void *cookie) { return ((struct source_cookie *)cookie)->level; } + +/* + * Return the readahead line. + */ + char_u * +source_nextline(void *cookie) +{ + return ((struct source_cookie *)cookie)->nextline; +} #endif #if (defined(MSWIN) && defined(FEAT_CSCOPE)) || defined(HAVE_FD_CLOEXEC) diff --git a/src/structs.h b/src/structs.h index ce9dfbcbfd..3a9bbd33c6 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1746,6 +1746,19 @@ typedef struct # endif } scriptitem_T; +// Struct passed through eval() functions. +// See EVALARG_EVALUATE for a fixed value with eval_flags set to EVAL_EVALUATE. +typedef struct { + int eval_flags; // EVAL_ flag values below + + // copied from exarg_T when "getline" is "getsourceline". Can be NULL. + void *eval_cookie; // argument for getline() +} evalarg_T; + +// Flags for expression evaluation. +#define EVAL_EVALUATE 1 // when missing don't actually evaluate +#define EVAL_CONSTANT 2 // when not a constant return FAIL + # ifdef FEAT_PROFILE /* * Struct used in sn_prl_ga for every line of a script. diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index 46034fb30a..b37f92fb6a 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -570,6 +570,26 @@ def Test_expr5() assert_equal(0z01ab01ab, g:ablob + g:ablob) enddef +def Test_expr5_vim9script() + " only checks line continuation + let lines =<< trim END + vim9script + let var = 11 + + 77 + - 22 + assert_equal(66, var) + END + CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + let var = 'one' + .. 'two' + assert_equal('onetwo', var) + END + CheckScriptSuccess(lines) +enddef + def Test_expr5_float() if !has('float') MissingFeature 'float' @@ -661,6 +681,26 @@ def Test_expr6() call CheckDefFailure(["let x = 6 * xxx"], 'E1001') enddef +def Test_expr6_vim9script() + " only checks line continuation + let lines =<< trim END + vim9script + let var = 11 + * 22 + / 3 + assert_equal(80, var) + END + CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + let var = 25 + % 10 + assert_equal(5, var) + END + CheckScriptSuccess(lines) +enddef + def Test_expr6_float() if !has('float') MissingFeature 'float' diff --git a/src/userfunc.c b/src/userfunc.c index 8a1b9edda8..a27c68d041 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -239,7 +239,7 @@ get_function_args( whitep = p; p = skipwhite(p); expr = p; - if (eval1(&p, &rettv, 0) != FAIL) + if (eval1(&p, &rettv, NULL) != FAIL) { if (ga_grow(default_args, 1) == FAIL) goto err_ret; @@ -561,6 +561,10 @@ get_func_tv( int ret = OK; typval_T argvars[MAX_FUNC_ARGS + 1]; // vars for arguments int argcount = 0; // number of arguments found + evalarg_T evalarg; + + CLEAR_FIELD(evalarg); + evalarg.eval_flags = funcexe->evaluate ? EVAL_EVALUATE : 0; /* * Get the arguments. @@ -572,8 +576,7 @@ get_func_tv( argp = skipwhite(argp + 1); // skip the '(' or ',' if (*argp == ')' || *argp == ',' || *argp == NUL) break; - if (eval1(&argp, &argvars[argcount], - funcexe->evaluate ? EVAL_EVALUATE : 0) == FAIL) + if (eval1(&argp, &argvars[argcount], &evalarg) == FAIL) { ret = FAIL; break; @@ -1249,7 +1252,7 @@ call_user_func( default_expr = ((char_u **)(fp->uf_def_args.ga_data)) [ai + fp->uf_def_args.ga_len]; - if (eval1(&default_expr, &def_rettv, EVAL_EVALUATE) == FAIL) + if (eval1(&default_expr, &def_rettv, &EVALARG_EVALUATE) == FAIL) { default_arg_err = 1; break; @@ -1394,7 +1397,7 @@ call_user_func( // A Lambda always has the command "return {expr}". It is much faster // to evaluate {expr} directly. ++ex_nesting_level; - (void)eval1(&p, rettv, EVAL_EVALUATE); + (void)eval1(&p, rettv, &EVALARG_EVALUATE); --ex_nesting_level; } else @@ -3697,6 +3700,7 @@ ex_return(exarg_T *eap) char_u *arg = eap->arg; typval_T rettv; int returning = FALSE; + evalarg_T evalarg; if (current_funccal == NULL) { @@ -3704,13 +3708,15 @@ ex_return(exarg_T *eap) return; } + CLEAR_FIELD(evalarg); + evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE; + if (eap->skip) ++emsg_skip; eap->nextcmd = NULL; if ((*arg != NUL && *arg != '|' && *arg != '\n') - && eval0(arg, &rettv, &eap->nextcmd, eap->skip ? 0 : EVAL_EVALUATE) - != FAIL) + && eval0(arg, &rettv, &eap->nextcmd, &evalarg) != FAIL) { if (!eap->skip) returning = do_return(eap, FALSE, TRUE, &rettv); @@ -3767,7 +3773,7 @@ ex_call(exarg_T *eap) // instead to skip to any following command, e.g. for: // :if 0 | call dict.foo().bar() | endif ++emsg_skip; - if (eval0(eap->arg, &rettv, &eap->nextcmd, 0) != FAIL) + if (eval0(eap->arg, &rettv, &eap->nextcmd, NULL) != FAIL) clear_tv(&rettv); --emsg_skip; return; diff --git a/src/version.c b/src/version.c index 1ee45307f3..e383c1843e 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1047, /**/ 1046, /**/ diff --git a/src/vim.h b/src/vim.h index 2c2848cc7c..a0fa629eac 100644 --- a/src/vim.h +++ b/src/vim.h @@ -2665,10 +2665,6 @@ long elapsed(DWORD start_tick); #define REPTERM_SPECIAL 4 #define REPTERM_NO_SIMPLIFY 8 -// Flags for expression evaluation. -#define EVAL_EVALUATE 1 // when missing don't actually evaluate -#define EVAL_CONSTANT 2 // when not a constant return FAIL - // Flags for find_special_key() #define FSK_KEYCODE 0x01 // prefer key code, e.g. K_DEL instead of DEL #define FSK_KEEP_X_KEY 0x02 // don't translate xHome to Home key From 9d40c63c7dc8c3eb3886c58dcd334bc7f37eceba Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 24 Jun 2020 19:05:29 +0200 Subject: [PATCH 003/105] patch 8.2.1048: build failure without the eval feature Problem: Build failure without the eval feature. Solution: Add dummy typedef. --- src/structs.h | 4 ++++ src/version.c | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/structs.h b/src/structs.h index 3a9bbd33c6..0ac486406b 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1794,6 +1794,10 @@ typedef struct { int dummy; } scriptitem_T; +typedef struct +{ + int dummy; +} evalarg_T; #endif // Struct passed between functions dealing with function call execution. diff --git a/src/version.c b/src/version.c index e383c1843e..4e2c553799 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1048, /**/ 1047, /**/ From b171fb179053fa631fec74911b5fb9374cb6a8a1 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 24 Jun 2020 20:34:03 +0200 Subject: [PATCH 004/105] patch 8.2.1049: Vim9: leaking memory when using continuation line Problem: Vim9: leaking memory when using continuation line. Solution: Keep a pointer to the continuation line in evalarg_T. Centralize checking for a next command. --- src/beval.c | 2 +- src/buffer.c | 4 +-- src/clientserver.c | 2 +- src/eval.c | 70 +++++++++++++++++++++++++++++++++------------- src/evalvars.c | 2 +- src/ex_docmd.c | 3 +- src/ex_eval.c | 12 ++++---- src/filepath.c | 2 +- src/findfile.c | 2 +- src/fold.c | 2 +- src/globals.h | 2 +- src/if_ole.cpp | 2 +- src/if_perl.xs | 3 +- src/if_tcl.c | 2 +- src/map.c | 2 +- src/proto/eval.pro | 16 +++++------ src/quickfix.c | 2 +- src/regexp.c | 2 +- src/register.c | 2 +- src/screen.c | 2 +- src/structs.h | 3 ++ src/userfunc.c | 4 +-- src/version.c | 2 ++ 23 files changed, 90 insertions(+), 55 deletions(-) diff --git a/src/beval.c b/src/beval.c index b7d9226e70..dd7bc3cdc7 100644 --- a/src/beval.c +++ b/src/beval.c @@ -285,7 +285,7 @@ general_beval_cb(BalloonEval *beval, int state UNUSED) ++textwinlock; vim_free(result); - result = eval_to_string(bexpr, NULL, TRUE); + result = eval_to_string(bexpr, TRUE); // Remove one trailing newline, it is added when the result was a // list and it's hardly ever useful. If the user really wants a diff --git a/src/buffer.c b/src/buffer.c index f928412e85..40ca25dfa5 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -4094,7 +4094,7 @@ build_stl_str_hl( tv.vval.v_number = wp->w_id; set_var((char_u *)"g:statusline_winid", &tv, FALSE); - usefmt = eval_to_string_safe(fmt + 2, NULL, use_sandbox); + usefmt = eval_to_string_safe(fmt + 2, use_sandbox); if (usefmt == NULL) usefmt = fmt; @@ -4434,7 +4434,7 @@ build_stl_str_hl( if (curwin != save_curwin) VIsual_active = FALSE; - str = eval_to_string_safe(p, &t, use_sandbox); + str = eval_to_string_safe(p, use_sandbox); curwin = save_curwin; curbuf = save_curbuf; diff --git a/src/clientserver.c b/src/clientserver.c index cf35c8149f..fbbeb6a4bf 100644 --- a/src/clientserver.c +++ b/src/clientserver.c @@ -86,7 +86,7 @@ eval_client_expr_to_string(char_u *expr) // to be typed. Do generate errors so that try/catch works. ++emsg_silent; - res = eval_to_string(expr, NULL, TRUE); + res = eval_to_string(expr, TRUE); debug_break_level = save_dbl; redir_off = save_ro; diff --git a/src/eval.c b/src/eval.c index ffb69de8d2..79944c8ca5 100644 --- a/src/eval.c +++ b/src/eval.c @@ -161,7 +161,7 @@ eval_clear(void) eval_to_bool( char_u *arg, int *error, - char_u **nextcmd, + exarg_T *eap, int skip) // only parse, don't execute { typval_T tv; @@ -169,7 +169,7 @@ eval_to_bool( if (skip) ++emsg_skip; - if (eval0(arg, &tv, nextcmd, skip ? NULL : &EVALARG_EVALUATE) == FAIL) + if (eval0(arg, &tv, eap, skip ? NULL : &EVALARG_EVALUATE) == FAIL) *error = TRUE; else { @@ -317,7 +317,7 @@ eval_expr_to_bool(typval_T *expr, int *error) char_u * eval_to_string_skip( char_u *arg, - char_u **nextcmd, + exarg_T *eap, int skip) // only parse, don't execute { typval_T tv; @@ -325,7 +325,7 @@ eval_to_string_skip( if (skip) ++emsg_skip; - if (eval0(arg, &tv, nextcmd, skip ? NULL : &EVALARG_EVALUATE) + if (eval0(arg, &tv, eap, skip ? NULL : &EVALARG_EVALUATE) == FAIL || skip) retval = NULL; else @@ -361,7 +361,6 @@ skip_expr(char_u **pp) char_u * eval_to_string( char_u *arg, - char_u **nextcmd, int convert) { typval_T tv; @@ -371,7 +370,7 @@ eval_to_string( char_u numbuf[NUMBUFLEN]; #endif - if (eval0(arg, &tv, nextcmd, &EVALARG_EVALUATE) == FAIL) + if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) retval = NULL; else { @@ -409,7 +408,6 @@ eval_to_string( char_u * eval_to_string_safe( char_u *arg, - char_u **nextcmd, int use_sandbox) { char_u *retval; @@ -419,7 +417,7 @@ eval_to_string_safe( if (use_sandbox) ++sandbox; ++textwinlock; - retval = eval_to_string(arg, nextcmd, FALSE); + retval = eval_to_string(arg, FALSE); if (use_sandbox) --sandbox; --textwinlock; @@ -459,12 +457,12 @@ eval_to_number(char_u *expr) * Returns NULL when there is an error. */ typval_T * -eval_expr(char_u *arg, char_u **nextcmd) +eval_expr(char_u *arg, exarg_T *eap) { typval_T *tv; tv = ALLOC_ONE(typval_T); - if (tv != NULL && eval0(arg, tv, nextcmd, &EVALARG_EVALUATE) == FAIL) + if (tv != NULL && eval0(arg, tv, eap, &EVALARG_EVALUATE) == FAIL) VIM_CLEAR(tv); return tv; @@ -1418,7 +1416,7 @@ tv_op(typval_T *tv1, typval_T *tv2, char_u *op) eval_for_line( char_u *arg, int *errp, - char_u **nextcmdp, + exarg_T *eap, int skip) { forinfo_T *fi; @@ -1448,7 +1446,7 @@ eval_for_line( if (skip) ++emsg_skip; - if (eval0(skipwhite(expr + 2), &tv, nextcmdp, &evalarg) == OK) + if (eval0(skipwhite(expr + 2), &tv, eap, &evalarg) == OK) { *errp = FALSE; if (!skip) @@ -1795,6 +1793,17 @@ eval_next_non_blank(char_u *arg, evalarg_T *evalarg, int *getnext) return arg; } +/* + * To be called when eval_next_non_blank() sets "getnext" to TRUE. + */ + static char_u * +eval_next_line(evalarg_T *evalarg) +{ + vim_free(evalarg->eval_tofree); + evalarg->eval_tofree = getsourceline(0, evalarg->eval_cookie, 0, TRUE); + return skipwhite(evalarg->eval_tofree); +} + /* * The "evaluate" argument: When FALSE, the argument is only parsed but not * executed. The function may return OK, but the rettv will be of type @@ -1813,7 +1822,7 @@ eval_next_non_blank(char_u *arg, evalarg_T *evalarg, int *getnext) eval0( char_u *arg, typval_T *rettv, - char_u **nextcmd, + exarg_T *eap, evalarg_T *evalarg) { int ret; @@ -1822,8 +1831,11 @@ eval0( int called_emsg_before = called_emsg; int flags = evalarg == NULL ? 0 : evalarg->eval_flags; + if (evalarg != NULL) + evalarg->eval_tofree = NULL; p = skipwhite(arg); ret = eval1(&p, rettv, evalarg); + if (ret == FAIL || !ends_excmd2(arg, p)) { if (ret != FAIL) @@ -1841,8 +1853,27 @@ eval0( semsg(_(e_invexpr2), arg); ret = FAIL; } - if (nextcmd != NULL) - *nextcmd = check_nextcmd(p); + + if (eap != NULL) + eap->nextcmd = check_nextcmd(p); + + if (evalarg != NULL) + { + if (eap != NULL) + { + if (evalarg->eval_tofree != NULL) + { + // We may need to keep the original command line, e.g. for + // ":let" it has the variable names. But we may also need the + // new one, "nextcmd" points into it. Keep both. + vim_free(eap->cmdline_tofree); + eap->cmdline_tofree = *eap->cmdlinep; + *eap->cmdlinep = evalarg->eval_tofree; + } + } + else + vim_free(evalarg->eval_tofree); + } return ret; } @@ -2305,7 +2336,7 @@ eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg) if (op != '+' && op != '-' && !concat) break; if (getnext) - *arg = skipwhite(getsourceline(0, evalarg->eval_cookie, 0, TRUE)); + *arg = eval_next_line(evalarg); if ((op != '+' || (rettv->v_type != VAR_LIST && rettv->v_type != VAR_BLOB)) @@ -2497,7 +2528,7 @@ eval6( if (op != '*' && op != '/' && op != '%') break; if (getnext) - *arg = skipwhite(getsourceline(0, evalarg->eval_cookie, 0, TRUE)); + *arg = eval_next_line(evalarg); if (evaluate) { @@ -4734,7 +4765,6 @@ make_expanded_name( char_u c1; char_u *retval = NULL; char_u *temp_result; - char_u *nextcmd = NULL; if (expr_end == NULL || in_end == NULL) return NULL; @@ -4743,8 +4773,8 @@ make_expanded_name( c1 = *in_end; *in_end = NUL; - temp_result = eval_to_string(expr_start + 1, &nextcmd, FALSE); - if (temp_result != NULL && nextcmd == NULL) + temp_result = eval_to_string(expr_start + 1, FALSE); + if (temp_result != NULL) { retval = alloc(STRLEN(temp_result) + (expr_start - in_start) + (in_end - expr_end) + 1); diff --git a/src/evalvars.c b/src/evalvars.c index 35c9dcb73e..e64b44ed13 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -800,7 +800,7 @@ ex_let(exarg_T *eap) evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE; evalarg.eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL; - i = eval0(expr, &rettv, &eap->nextcmd, &evalarg); + i = eval0(expr, &rettv, eap, &evalarg); if (eap->skip) --emsg_skip; } diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 4755a01b48..6c887fae1c 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -2609,6 +2609,7 @@ doend: #ifdef FEAT_EVAL --ex_nesting_level; + vim_free(ea.cmdline_tofree); #endif return ea.nextcmd; @@ -4912,7 +4913,7 @@ ex_colorscheme(exarg_T *eap) if (expr != NULL) { ++emsg_off; - p = eval_to_string(expr, NULL, FALSE); + p = eval_to_string(expr, FALSE); --emsg_off; vim_free(expr); } diff --git a/src/ex_eval.c b/src/ex_eval.c index cb32bd0791..8b8a256957 100644 --- a/src/ex_eval.c +++ b/src/ex_eval.c @@ -900,7 +900,7 @@ ex_eval(exarg_T *eap) evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE; evalarg.eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL; - if (eval0(eap->arg, &tv, &eap->nextcmd, &evalarg) == OK) + if (eval0(eap->arg, &tv, eap, &evalarg) == OK) clear_tv(&tv); } @@ -929,7 +929,7 @@ ex_if(exarg_T *eap) skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0 && !(cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE)); - result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip); + result = eval_to_bool(eap->arg, &error, eap, skip); if (!skip && !error) { @@ -1041,7 +1041,7 @@ ex_else(exarg_T *eap) if (eap->cmdidx == CMD_elseif) { - result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip); + result = eval_to_bool(eap->arg, &error, eap, skip); // When throwing error exceptions, we want to throw always the first // of several errors in a row. This is what actually happens when @@ -1103,7 +1103,7 @@ ex_while(exarg_T *eap) /* * ":while bool-expr" */ - result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip); + result = eval_to_bool(eap->arg, &error, eap, skip); } else { @@ -1122,7 +1122,7 @@ ex_while(exarg_T *eap) else { // Evaluate the argument and get the info in a structure. - fi = eval_for_line(eap->arg, &error, &eap->nextcmd, skip); + fi = eval_for_line(eap->arg, &error, eap, skip); cstack->cs_forinfo[cstack->cs_idx] = fi; } @@ -1322,7 +1322,7 @@ ex_throw(exarg_T *eap) char_u *value; if (*arg != NUL && *arg != '|' && *arg != '\n') - value = eval_to_string_skip(arg, &eap->nextcmd, eap->skip); + value = eval_to_string_skip(arg, eap, eap->skip); else { emsg(_(e_argreq)); diff --git a/src/filepath.c b/src/filepath.c index 1fe757e850..6644e9939d 100644 --- a/src/filepath.c +++ b/src/filepath.c @@ -3083,7 +3083,7 @@ expand_backtick( #ifdef FEAT_EVAL if (*cmd == '=') // `={expr}`: Expand expression - buffer = eval_to_string(cmd + 1, &p, TRUE); + buffer = eval_to_string(cmd + 1, TRUE); else #endif buffer = get_cmd_output(cmd, NULL, diff --git a/src/findfile.c b/src/findfile.c index b153ed010c..190fc69dc6 100644 --- a/src/findfile.c +++ b/src/findfile.c @@ -2079,7 +2079,7 @@ eval_includeexpr(char_u *ptr, int len) char_u *res; set_vim_var_string(VV_FNAME, ptr, len); - res = eval_to_string_safe(curbuf->b_p_inex, NULL, + res = eval_to_string_safe(curbuf->b_p_inex, was_set_insecurely((char_u *)"includeexpr", OPT_LOCAL)); set_vim_var_string(VV_FNAME, NULL, 0); return res; diff --git a/src/fold.c b/src/fold.c index d91203c75c..043037fa64 100644 --- a/src/fold.c +++ b/src/fold.c @@ -1928,7 +1928,7 @@ get_foldtext( curbuf = wp->w_buffer; ++emsg_silent; // handle exceptions, but don't display errors - text = eval_to_string_safe(wp->w_p_fdt, NULL, + text = eval_to_string_safe(wp->w_p_fdt, was_set_insecurely((char_u *)"foldtext", OPT_LOCAL)); --emsg_silent; diff --git a/src/globals.h b/src/globals.h index 8601e2b1e5..c7f9794ab9 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1882,7 +1882,7 @@ EXTERN char windowsVersion[20] INIT(= {0}); EXTERN listitem_T range_list_item; // Passed to an eval() function to enable evaluation. -EXTERN evalarg_T EVALARG_EVALUATE INIT2(EVAL_EVALUATE, NULL); +EXTERN evalarg_T EVALARG_EVALUATE INIT3(EVAL_EVALUATE, NULL, NULL); #endif #ifdef MSWIN diff --git a/src/if_ole.cpp b/src/if_ole.cpp index 34ce232660..e415a1924d 100644 --- a/src/if_ole.cpp +++ b/src/if_ole.cpp @@ -388,7 +388,7 @@ CVim::Eval(BSTR expr, BSTR *result) /* Evaluate the expression */ ++emsg_skip; - str = (char *)eval_to_string((char_u *)buffer, NULL, TRUE); + str = (char *)eval_to_string((char_u *)buffer, TRUE); --emsg_skip; vim_free(buffer); if (str == NULL) diff --git a/src/if_perl.xs b/src/if_perl.xs index bf269cbcb0..cad571c5c7 100644 --- a/src/if_perl.xs +++ b/src/if_perl.xs @@ -832,7 +832,6 @@ msg_split( char_u * eval_to_string( char_u *arg UNUSED, - char_u **nextcmd UNUSED, int dolist UNUSED) { return NULL; @@ -1562,7 +1561,7 @@ Eval(str) PREINIT: char_u *value; PPCODE: - value = eval_to_string((char_u *)str, (char_u **)0, TRUE); + value = eval_to_string((char_u *)str, TRUE); if (value == NULL) { XPUSHs(sv_2mortal(newSViv(0))); diff --git a/src/if_tcl.c b/src/if_tcl.c index c274b26750..2775221f46 100644 --- a/src/if_tcl.c +++ b/src/if_tcl.c @@ -1373,7 +1373,7 @@ tclvimexpr( #ifdef FEAT_EVAL expr = Tcl_GetStringFromObj(objv[objn], NULL); - str = (char *)eval_to_string((char_u *)expr, NULL, TRUE); + str = (char *)eval_to_string((char_u *)expr, TRUE); if (str == NULL) Tcl_SetResult(interp, _("invalid expression"), TCL_STATIC); else diff --git a/src/map.c b/src/map.c index 4f0c8709af..26fce68d8c 100644 --- a/src/map.c +++ b/src/map.c @@ -1614,7 +1614,7 @@ eval_map_expr( save_cursor = curwin->w_cursor; save_msg_col = msg_col; save_msg_row = msg_row; - p = eval_to_string(expr, NULL, FALSE); + p = eval_to_string(expr, FALSE); --textwinlock; --ex_normal_lock; curwin->w_cursor = save_cursor; diff --git a/src/proto/eval.pro b/src/proto/eval.pro index 4fb04eb1fc..87e447808b 100644 --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -3,16 +3,16 @@ varnumber_T num_divide(varnumber_T n1, varnumber_T n2); varnumber_T num_modulus(varnumber_T n1, varnumber_T n2); void eval_init(void); void eval_clear(void); -int eval_to_bool(char_u *arg, int *error, char_u **nextcmd, int skip); +int eval_to_bool(char_u *arg, int *error, exarg_T *eap, int skip); int eval_expr_valid_arg(typval_T *tv); int eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv); int eval_expr_to_bool(typval_T *expr, int *error); -char_u *eval_to_string_skip(char_u *arg, char_u **nextcmd, int skip); +char_u *eval_to_string_skip(char_u *arg, exarg_T *eap, int skip); int skip_expr(char_u **pp); -char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert); -char_u *eval_to_string_safe(char_u *arg, char_u **nextcmd, int use_sandbox); +char_u *eval_to_string(char_u *arg, int convert); +char_u *eval_to_string_safe(char_u *arg, int use_sandbox); varnumber_T eval_to_number(char_u *expr); -typval_T *eval_expr(char_u *arg, char_u **nextcmd); +typval_T *eval_expr(char_u *arg, exarg_T *eap); int call_vim_function(char_u *func, int argc, typval_T *argv, typval_T *rettv); varnumber_T call_func_retnr(char_u *func, int argc, typval_T *argv); void *call_func_retstr(char_u *func, int argc, typval_T *argv); @@ -21,13 +21,13 @@ int eval_foldexpr(char_u *arg, int *cp); char_u *get_lval(char_u *name, typval_T *rettv, lval_T *lp, int unlet, int skip, int flags, int fne_flags); void clear_lval(lval_T *lp); void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, int flags, char_u *op); -void *eval_for_line(char_u *arg, int *errp, char_u **nextcmdp, int skip); +void *eval_for_line(char_u *arg, int *errp, exarg_T *eap, int skip); int next_for_item(void *fi_void, char_u *arg); void free_for_info(void *fi_void); void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx); int pattern_match(char_u *pat, char_u *text, int ic); -int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, evalarg_T *evalarg); -int eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg_in); +int eval0(char_u *arg, typval_T *rettv, exarg_T *eap, evalarg_T *evalarg); +int eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg); void eval_addblob(typval_T *tv1, typval_T *tv2); int eval_addlist(typval_T *tv1, typval_T *tv2); char_u *partial_name(partial_T *pt); diff --git a/src/quickfix.c b/src/quickfix.c index ba54fab9c7..3bd0f75e24 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -7680,7 +7680,7 @@ ex_cexpr(exarg_T *eap) // Evaluate the expression. When the result is a string or a list we can // use it to fill the errorlist. - tv = eval_expr(eap->arg, &eap->nextcmd); + tv = eval_expr(eap->arg, eap); if (tv != NULL) { if ((tv->v_type == VAR_STRING && tv->vval.v_string != NULL) diff --git a/src/regexp.c b/src/regexp.c index de0b0fad43..229f6ef2b7 100644 --- a/src/regexp.c +++ b/src/regexp.c @@ -2066,7 +2066,7 @@ vim_regsub_both( clear_tv(&rettv); } else - eval_result = eval_to_string(source + 2, NULL, TRUE); + eval_result = eval_to_string(source + 2, TRUE); if (eval_result != NULL) { diff --git a/src/register.c b/src/register.c index 49f7a7c86a..66dd0cca65 100644 --- a/src/register.c +++ b/src/register.c @@ -136,7 +136,7 @@ get_expr_line(void) return expr_copy; ++nested; - rv = eval_to_string(expr_copy, NULL, TRUE); + rv = eval_to_string(expr_copy, TRUE); --nested; vim_free(expr_copy); return rv; diff --git a/src/screen.c b/src/screen.c index ea7aaa67f8..01d6257b7c 100644 --- a/src/screen.c +++ b/src/screen.c @@ -1148,7 +1148,7 @@ get_keymap_str( curwin = wp; STRCPY(buf, "b:keymap_name"); // must be writable ++emsg_skip; - s = p = eval_to_string(buf, NULL, FALSE); + s = p = eval_to_string(buf, FALSE); --emsg_skip; curbuf = old_curbuf; curwin = old_curwin; diff --git a/src/structs.h b/src/structs.h index 0ac486406b..3950887355 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1753,6 +1753,9 @@ typedef struct { // copied from exarg_T when "getline" is "getsourceline". Can be NULL. void *eval_cookie; // argument for getline() + + // pointer to the line obtained with getsourceline() + char_u *eval_tofree; } evalarg_T; // Flags for expression evaluation. diff --git a/src/userfunc.c b/src/userfunc.c index a27c68d041..c6a8a8cd3d 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -3716,7 +3716,7 @@ ex_return(exarg_T *eap) eap->nextcmd = NULL; if ((*arg != NUL && *arg != '|' && *arg != '\n') - && eval0(arg, &rettv, &eap->nextcmd, &evalarg) != FAIL) + && eval0(arg, &rettv, eap, &evalarg) != FAIL) { if (!eap->skip) returning = do_return(eap, FALSE, TRUE, &rettv); @@ -3773,7 +3773,7 @@ ex_call(exarg_T *eap) // instead to skip to any following command, e.g. for: // :if 0 | call dict.foo().bar() | endif ++emsg_skip; - if (eval0(eap->arg, &rettv, &eap->nextcmd, NULL) != FAIL) + if (eval0(eap->arg, &rettv, eap, NULL) != FAIL) clear_tv(&rettv); --emsg_skip; return; diff --git a/src/version.c b/src/version.c index 4e2c553799..d39c1b07c0 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1049, /**/ 1048, /**/ From 65a8ed37f7bc61fbe5c612a7b0eb0dfc16ad3e11 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 24 Jun 2020 21:00:25 +0200 Subject: [PATCH 005/105] patch 8.2.1050: missing change in struct Problem: Missing change in struct. Solution: Add missing change. --- src/ex_cmds.h | 3 +++ src/version.c | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/ex_cmds.h b/src/ex_cmds.h index f68c4fd0b2..1c1956239c 100644 --- a/src/ex_cmds.h +++ b/src/ex_cmds.h @@ -1841,6 +1841,9 @@ struct exarg char_u *nextcmd; // next command (NULL if none) char_u *cmd; // the name of the command (except for :make) char_u **cmdlinep; // pointer to pointer of allocated cmdline +#ifdef FEAT_EVAL + char_u *cmdline_tofree; // free later +#endif cmdidx_T cmdidx; // the index for the command long argt; // flags for the command int skip; // don't execute the command, only parse it diff --git a/src/version.c b/src/version.c index d39c1b07c0..ba3e621550 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1050, /**/ 1049, /**/ From ca275a05d8b79f6a9101604fdede2373d0dea44e Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 24 Jun 2020 22:07:46 +0200 Subject: [PATCH 006/105] patch 8.2.1051: crash when changing a list while using reduce() on it Problem: Crash when changing a list while using reduce() on it. Solution: Lock the list. (closes #6330) --- src/list.c | 8 ++++++-- src/testdir/test_listdict.vim | 9 +++++++++ src/version.c | 2 ++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/list.c b/src/list.c index c624003a21..cf0c99f131 100644 --- a/src/list.c +++ b/src/list.c @@ -2461,6 +2461,8 @@ f_reduce(typval_T *argvars, typval_T *rettv) list_T *l = argvars[0].vval.v_list; listitem_T *li = NULL; int r; + int prev_locked = l->lv_lock; + int called_emsg_start = called_emsg; CHECK_LIST_MATERIALIZE(l); if (argvars[2].v_type == VAR_UNKNOWN) @@ -2480,6 +2482,7 @@ f_reduce(typval_T *argvars, typval_T *rettv) li = l->lv_first; } + l->lv_lock = VAR_FIXED; // disallow the list changing here copy_tv(&initial, rettv); for ( ; li != NULL; li = li->li_next) { @@ -2488,9 +2491,10 @@ f_reduce(typval_T *argvars, typval_T *rettv) rettv->v_type = VAR_UNKNOWN; r = call_func(func_name, -1, rettv, 2, argv, &funcexe); clear_tv(&argv[0]); - if (r == FAIL) - return; + if (r == FAIL || called_emsg != called_emsg_start) + break; } + l->lv_lock = prev_locked; } else { diff --git a/src/testdir/test_listdict.vim b/src/testdir/test_listdict.vim index b1af87ea9d..26b0e91e03 100644 --- a/src/testdir/test_listdict.vim +++ b/src/testdir/test_listdict.vim @@ -709,6 +709,15 @@ func Test_reduce() call assert_fails("call reduce({}, { acc, val -> acc + val }, 1)", 'E897:') call assert_fails("call reduce(0, { acc, val -> acc + val }, 1)", 'E897:') call assert_fails("call reduce('', { acc, val -> acc + val }, 1)", 'E897:') + + let g:lut = [1, 2, 3, 4] + func EvilRemove() + call remove(g:lut, 1) + return 1 + endfunc + call assert_fails("call reduce(g:lut, { acc, val -> EvilRemove() }, 1)", 'E742:') + unlet g:lut + delfunc EvilRemove endfunc " splitting a string to a List using split() diff --git a/src/version.c b/src/version.c index ba3e621550..36da620019 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1051, /**/ 1050, /**/ From 7acde51832f383f9a6d2e740cd0420b433ea841a Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 24 Jun 2020 23:02:40 +0200 Subject: [PATCH 007/105] patch 8.2.1052: build failure with older compilers Problem: Build failure with older compilers. Solution: Move declaration to start of block. --- src/eval.c | 4 ++-- src/version.c | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/eval.c b/src/eval.c index 79944c8ca5..0402b36385 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1904,6 +1904,7 @@ eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg) typval_T var2; evalarg_T nested_evalarg; int orig_flags; + int evaluate; if (evalarg == NULL) { @@ -1916,8 +1917,7 @@ eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg) orig_flags = evalarg->eval_flags; } - int evaluate = nested_evalarg.eval_flags & EVAL_EVALUATE; - + evaluate = nested_evalarg.eval_flags & EVAL_EVALUATE; result = FALSE; if (evaluate) { diff --git a/src/version.c b/src/version.c index 36da620019..b85dc2936d 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1052, /**/ 1051, /**/ From 832adf9bb8cd39d8e982d8a35ed8a6d39b974494 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 25 Jun 2020 19:01:36 +0200 Subject: [PATCH 008/105] patch 8.2.1053: insufficient testing for 'statusline' and 'tabline' Problem: Insufficient testing for 'statusline' and 'tabline'. Solution: Add more tests. (Yegappan Lakshmanan, closes #6333) --- src/testdir/test_autocmd.vim | 25 ++++++++++++++++--- src/testdir/test_statusline.vim | 17 ++++++++++--- src/testdir/test_tabline.vim | 43 +++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 4 files changed, 81 insertions(+), 6 deletions(-) diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim index e8f513a055..d7328c3e0a 100644 --- a/src/testdir/test_autocmd.vim +++ b/src/testdir/test_autocmd.vim @@ -2585,7 +2585,7 @@ func Test_autocmd_window() edit one.txt tabnew two.txt let g:blist = [] - augroup aucmd_win_test + augroup aucmd_win_test1 au! au BufEnter * call add(g:blist, [expand(''), \ win_gettype(bufwinnr(expand('')))]) @@ -2594,10 +2594,29 @@ func Test_autocmd_window() doautoall BufEnter call assert_equal([['one.txt', 'autocmd'], ['two.txt', '']], g:blist) - augroup aucmd_win_test + augroup aucmd_win_test1 au! augroup END - augroup! aucmd_win_test + augroup! aucmd_win_test1 + %bw! +endfunc + +" Test for trying to close the temporary window used for executing an autocmd +func Test_close_autocmd_window() + %bw! + edit one.txt + tabnew two.txt + augroup aucmd_win_test2 + au! + au BufEnter * if expand('') == 'one.txt' | 1close | endif + augroup END + + call assert_fails('doautoall BufEnter', 'E813:') + + augroup aucmd_win_test2 + au! + augroup END + augroup! aucmd_win_test2 %bw! endfunc diff --git a/src/testdir/test_statusline.vim b/src/testdir/test_statusline.vim index 708686b0be..f73998a670 100644 --- a/src/testdir/test_statusline.vim +++ b/src/testdir/test_statusline.vim @@ -1,10 +1,7 @@ " Test 'statusline' " " Not tested yet: -" %a " %N -" %T -" %X source view_util.vim source check.vim @@ -105,6 +102,18 @@ func Test_statusline() set statusline=%F call assert_match('/testdir/Xstatusline\s*$', s:get_statusline()) + " Test for min and max width with %(. For some reason, if this test is moved + " after the below test for the help buffer flag, then the code to truncate + " the string is not executed. + set statusline=%015(%f%) + call assert_match('^ Xstatusline\s*$', s:get_statusline()) + set statusline=%.6(%f%) + call assert_match('^3\s*$', s:get_statusline()) + " %h: Help buffer flag, text is "[help]". " %H: Help buffer flag, text is ",HLP". set statusline=%h,%H @@ -423,3 +432,5 @@ func Test_statusline_removed_group() call StopVimInTerminal(buf) call delete('XTest_statusline') endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_tabline.vim b/src/testdir/test_tabline.vim index 383d23984c..eff95082cc 100644 --- a/src/testdir/test_tabline.vim +++ b/src/testdir/test_tabline.vim @@ -70,3 +70,46 @@ func Test_redrawtabline() let &showtabline = showtabline_save au! Bufadd endfunc + +" Test for the "%T" and "%X" flags in the 'tabline' option +func MyTabLine() + let s = '' + for i in range(tabpagenr('$')) + " set the tab page number (for mouse clicks) + let s .= '%' . (i + 1) . 'T' + + " the label is made by MyTabLabel() + let s .= ' %{MyTabLabel(' . (i + 1) . ')} ' + endfor + + " after the last tab fill with TabLineFill and reset tab page nr + let s .= '%T' + + " right-align the label to close the current tab page + if tabpagenr('$') > 1 + let s .= '%=%Xclose' + endif + + return s +endfunc + +func MyTabLabel(n) + let buflist = tabpagebuflist(a:n) + let winnr = tabpagewinnr(a:n) + return bufname(buflist[winnr - 1]) +endfunc + +func Test_tabline_flags() + if has('gui') + set guioptions-=e + endif + set tabline=%!MyTabLine() + edit Xtabline1 + tabnew Xtabline2 + redrawtabline + call assert_match('^ Xtabline1 Xtabline2\s\+close$', Screenline(1)) + set tabline= + %bw! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index b85dc2936d..c1957e41a6 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1053, /**/ 1052, /**/ From 801ab069341c8652680d63c174530fd4feb2911e Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 25 Jun 2020 19:27:56 +0200 Subject: [PATCH 009/105] patch 8.2.1054: not so easy to pass a lua function to Vim Problem: Not so easy to pass a lua function to Vim. Solution: Convert a Lua function and closure to a Vim funcref. (Prabir Shrestha, closes #6246) --- runtime/doc/if_lua.txt | 8 ++++ src/if_lua.c | 101 ++++++++++++++++++++++++++++++++++++++- src/proto/userfunc.pro | 1 + src/structs.h | 9 ++++ src/testdir/test_lua.vim | 29 +++++++++++ src/userfunc.c | 64 +++++++++++++++++++++++++ src/version.c | 2 + 7 files changed, 213 insertions(+), 1 deletion(-) diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt index 63e227d303..170f861ff0 100644 --- a/runtime/doc/if_lua.txt +++ b/runtime/doc/if_lua.txt @@ -333,6 +333,14 @@ Examples: :lua l = d.len -- assign d as 'self' :lua print(l()) < +Lua functions and closures are automatically converted to a Vim |Funcref| and +can be accessed in Vim scripts. Example: +> + lua <vval.v_number = (varnumber_T) lua_tointeger(L, pos); #endif break; + case LUA_TFUNCTION: + { + char_u *name; + lua_pushvalue(L, pos); + luaV_CFuncState *state = ALLOC_CLEAR_ONE(luaV_CFuncState); + state->lua_funcref = luaL_ref(L, LUA_REGISTRYINDEX); + state->L = L; + state->lua_tableref = LUA_NOREF; + name = register_cfunc(&luaV_call_lua_func, + &luaV_call_lua_func_free, state); + tv->v_type = VAR_FUNC; + tv->vval.v_string = vim_strsave(name); + break; + } + case LUA_TTABLE: + { + lua_pushvalue(L, pos); + int lua_tableref = luaL_ref(L, LUA_REGISTRYINDEX); + if (lua_getmetatable(L, pos)) { + lua_getfield(L, -1, LUA___CALL); + if (lua_isfunction(L, -1)) { + char_u *name; + int lua_funcref = luaL_ref(L, LUA_REGISTRYINDEX); + luaV_CFuncState *state = ALLOC_CLEAR_ONE(luaV_CFuncState); + state->lua_funcref = lua_funcref; + state->L = L; + state->lua_tableref = lua_tableref; + name = register_cfunc(&luaV_call_lua_func, + &luaV_call_lua_func_free, state); + tv->v_type = VAR_FUNC; + tv->vval.v_string = vim_strsave(name); + break; + } + } + tv->v_type = VAR_NUMBER; + tv->vval.v_number = 0; + status = FAIL; + break; + } case LUA_TUSERDATA: { void *p = lua_touserdata(L, pos); @@ -2415,4 +2465,53 @@ update_package_paths_in_lua() } } +/* + * Native C function callback + */ + static int +luaV_call_lua_func( + int argcount, + typval_T *argvars, + typval_T *rettv, + void *state) +{ + int i; + int luaargcount = argcount; + luaV_CFuncState *funcstate = (luaV_CFuncState*)state; + lua_rawgeti(funcstate->L, LUA_REGISTRYINDEX, funcstate->lua_funcref); + + if (funcstate->lua_tableref != LUA_NOREF) + { + // First arg for metatable __call method is a table + luaargcount += 1; + lua_rawgeti(funcstate->L, LUA_REGISTRYINDEX, funcstate->lua_tableref); + } + + for (i = 0; i < argcount; ++i) + luaV_pushtypval(funcstate->L, &argvars[i]); + + if (lua_pcall(funcstate->L, luaargcount, 1, 0)) + { + luaV_emsg(funcstate->L); + return FCERR_OTHER; + } + + luaV_checktypval(funcstate->L, -1, rettv, "get return value"); + return FCERR_NONE; +} + +/* + * Free up any lua references held by the func state. + */ + static void +luaV_call_lua_func_free(void *state) +{ + luaV_CFuncState *funcstate = (luaV_CFuncState*)state; + luaL_unref(L, LUA_REGISTRYINDEX, funcstate->lua_funcref); + funcstate->L = NULL; + if (funcstate->lua_tableref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, funcstate->lua_tableref); + VIM_CLEAR(funcstate); +} + #endif diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro index 6ed79ba035..340ef57f1c 100644 --- a/src/proto/userfunc.pro +++ b/src/proto/userfunc.pro @@ -4,6 +4,7 @@ hashtab_T *func_tbl_get(void); int get_function_args(char_u **argp, char_u endchar, garray_T *newargs, garray_T *argtypes, int *varargs, garray_T *default_args, int skip, exarg_T *eap, char_u **line_to_free); char_u *get_lambda_name(void); int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate); +char_u *register_cfunc(cfunc_T cb, cfunc_free_T free_cb, void *state); char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload); void emsg_funcname(char *ermsg, char_u *name); int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, funcexe_T *funcexe); diff --git a/src/structs.h b/src/structs.h index 3950887355..e308ff448d 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1529,6 +1529,9 @@ struct blobvar_S char bv_lock; // zero, VAR_LOCKED, VAR_FIXED }; +typedef int (*cfunc_T)(int argcount, typval_T *argvars, typval_T *rettv, void *state); +typedef void (*cfunc_free_T)(void *state); + #if defined(FEAT_EVAL) || defined(PROTO) typedef struct funccall_S funccall_T; @@ -1562,6 +1565,11 @@ typedef struct char_u *uf_va_name; // name from "...name" or NULL type_T *uf_va_type; // type from "...name: type" or NULL type_T *uf_func_type; // type of the function, &t_func_any if unknown +# if defined(FEAT_LUA) + cfunc_T uf_cb; // callback function for cfunc + cfunc_free_T uf_cb_free; // callback function to free cfunc + void *uf_cb_state; // state of uf_cb +# endif garray_T uf_lines; // function lines # ifdef FEAT_PROFILE @@ -1607,6 +1615,7 @@ typedef struct #define FC_EXPORT 0x100 // "export def Func()" #define FC_NOARGS 0x200 // no a: variables in lambda #define FC_VIM9 0x400 // defined in vim9 script file +#define FC_CFUNC 0x800 // defined as Lua C func #define MAX_FUNC_ARGS 20 // maximum number of function arguments #define VAR_SHORT_LEN 20 // short variable name length diff --git a/src/testdir/test_lua.vim b/src/testdir/test_lua.vim index bd50bce564..826a7bce80 100644 --- a/src/testdir/test_lua.vim +++ b/src/testdir/test_lua.vim @@ -541,6 +541,35 @@ func Test_update_package_paths() call assert_equal("hello from lua", luaeval("require('testluaplugin').hello()")) endfunc +func Vim_func_call_lua_callback(Concat, Cb) + let l:message = a:Concat("hello", "vim") + call a:Cb(l:message) +endfunc + +func Test_pass_lua_callback_to_vim_from_lua() + lua pass_lua_callback_to_vim_from_lua_result = "" + call assert_equal("", luaeval("pass_lua_callback_to_vim_from_lua_result")) + lua <uf_dfunc_idx = UF_NOT_COMPILED; + fp->uf_refcount = 1; + fp->uf_varargs = TRUE; + fp->uf_flags = FC_CFUNC; + fp->uf_calls = 0; + fp->uf_script_ctx = current_sctx; + fp->uf_lines = newlines; + fp->uf_args = newargs; + fp->uf_cb = cb; + fp->uf_cb_free = cb_free; + fp->uf_cb_state = state; + + set_ufunc_name(fp, name); + hash_add(&func_hashtab, UF2HIKEY(fp)); + + return name; + +errret: + ga_clear_strings(&newargs); + ga_clear_strings(&newlines); + vim_free(fp); + return NULL; +} +#endif + /* * Parse a lambda expression and get a Funcref from "*arg". * Return OK or FAIL. Returns NOTDONE for dict or {expr}. @@ -1027,6 +1072,17 @@ func_clear_items(ufunc_T *fp) vim_free(((type_T **)fp->uf_type_list.ga_data) [--fp->uf_type_list.ga_len]); ga_clear(&fp->uf_type_list); + +#ifdef FEAT_LUA + if (fp->uf_cb_free != NULL) + { + fp->uf_cb_free(fp->uf_cb_state); + fp->uf_cb_free = NULL; + } + + fp->uf_cb_state = NULL; + fp->uf_cb = NULL; +#endif #ifdef FEAT_PROFILE VIM_CLEAR(fp->uf_tml_count); VIM_CLEAR(fp->uf_tml_total); @@ -1973,6 +2029,14 @@ call_func( if (fp != NULL && (fp->uf_flags & FC_DELETED)) error = FCERR_DELETED; +#ifdef FEAT_LUA + else if (fp != NULL && (fp->uf_flags & FC_CFUNC)) + { + cfunc_T cb = fp->uf_cb; + + error = (*cb)(argcount, argvars, rettv, fp->uf_cb_state); + } +#endif else if (fp != NULL) { if (funcexe->argv_func != NULL) diff --git a/src/version.c b/src/version.c index c1957e41a6..536d27d44c 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1054, /**/ 1053, /**/ From 73b4465ba7f170c5a1701ad908144970e758b1f5 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 25 Jun 2020 19:53:24 +0200 Subject: [PATCH 010/105] patch 8.2.1055: no filetype set for pacman config files Problem: No filetype set for pacman config files. Solution: Recognize pacman.conf and *.hook. (Guido Cella, closes #6335) --- runtime/filetype.vim | 11 ++++++++++- src/testdir/test_filetype.vim | 20 +++++++++++++++++++- src/version.c | 2 ++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/runtime/filetype.vim b/runtime/filetype.vim index cd1c509d6e..a6c8956542 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1134,8 +1134,17 @@ au BufNewFile,BufRead *.ora setf ora " Packet filter conf au BufNewFile,BufRead pf.conf setf pf +" Pacman Config (close enough to dosini) +au BufNewFile,BufRead */etc/pacman.conf setf dosini + +" Pacman hooks +au BufNewFile,BufRead *.hook + \ if getline(1) == '[Trigger]' | + \ setf dosini | + \ endif + " Pam conf -au BufNewFile,BufRead */etc/pam.conf setf pamconf +au BufNewFile,BufRead */etc/pam.conf setf pamconf " Pam environment au BufNewFile,BufRead pam_env.conf,.pam_environment setf pamenv diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index 6d245ae12f..38339aefec 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -139,7 +139,7 @@ let s:filename_checks = { \ 'dnsmasq': ['/etc/dnsmasq.conf'], \ 'dockerfile': ['Containerfile', 'Dockerfile', 'file.Dockerfile'], \ 'dosbatch': ['file.bat', 'file.sys'], - \ 'dosini': ['.editorconfig', '/etc/yum.conf', 'file.ini'], + \ 'dosini': ['.editorconfig', '/etc/pacman.conf', '/etc/yum.conf', 'file.ini'], \ 'dot': ['file.dot', 'file.gv'], \ 'dracula': ['file.drac', 'file.drc', 'filelvs', 'filelpe'], \ 'dsl': ['file.dsl'], @@ -665,4 +665,22 @@ func Test_filetype_indent_off() close endfunc +func Test_hook_file() + filetype on + + call writefile(['[Trigger]', 'this is pacman config'], 'Xfile.hook') + split Xfile.hook + call assert_equal('dosini', &filetype) + bwipe! + + call writefile(['not pacman'], 'Xfile.hook') + split Xfile.hook + call assert_notequal('dosini', &filetype) + bwipe! + + call delete('Xfile.hook') + filetype off +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 536d27d44c..11ec7637e6 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1055, /**/ 1054, /**/ From 211dd3fd82216ca879fe7f917ea345b3ae366ce1 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 25 Jun 2020 20:07:04 +0200 Subject: [PATCH 011/105] patch 8.2.1056: wrong display when mixing match conceal and syntax conceal Problem: Wrong display when mixing match conceal and syntax conceal. Solution: Adjust how conceal flags are used. (closes #6327, closes #6303) --- src/drawline.c | 9 +-- src/highlight.c | 12 ++-- src/testdir/test_matchadd_conceal.vim | 97 +++++++++++++++++++++------ src/version.c | 2 + 4 files changed, 90 insertions(+), 30 deletions(-) diff --git a/src/drawline.c b/src/drawline.c index 6d14bda7b6..fad0645fe8 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -967,7 +967,7 @@ win_line( for (;;) { #if defined(FEAT_CONCEAL) || defined(FEAT_SEARCH_EXTRA) - int has_match_conc = 0; // match wants to conceal + int has_match_conc = 0; // match wants to conceal #endif #ifdef FEAT_CONCEAL int did_decrement_ptr = FALSE; @@ -2353,13 +2353,14 @@ win_line( { char_attr = conceal_attr; if ((prev_syntax_id != syntax_seqnr || has_match_conc > 1) - && (syn_get_sub_char() != NUL || match_conc - || wp->w_p_cole == 1) + && (syn_get_sub_char() != NUL + || (has_match_conc && match_conc) + || wp->w_p_cole == 1) && wp->w_p_cole != 3) { // First time at this concealed item: display one // character. - if (match_conc) + if (has_match_conc && match_conc) c = match_conc; else if (syn_get_sub_char() != NUL) c = syn_get_sub_char(); diff --git a/src/highlight.c b/src/highlight.c index dacce5c497..8c41c38fc8 100644 --- a/src/highlight.c +++ b/src/highlight.c @@ -4419,9 +4419,8 @@ update_search_hl( while (cur != NULL || shl_flag == FALSE) { if (shl_flag == FALSE - && ((cur != NULL - && cur->priority > SEARCH_HL_PRIORITY) - || cur == NULL)) + && (cur == NULL + || cur->priority > SEARCH_HL_PRIORITY)) { shl = search_hl; shl_flag = TRUE; @@ -4453,7 +4452,7 @@ update_search_hl( *match_conc = cur->conceal_char; } else - *has_match_conc = *match_conc = 0; + *has_match_conc = 0; # endif } else if (col == shl->endcol) @@ -4503,9 +4502,8 @@ update_search_hl( while (cur != NULL || shl_flag == FALSE) { if (shl_flag == FALSE - && ((cur != NULL - && cur->priority > SEARCH_HL_PRIORITY) - || cur == NULL)) + && (cur == NULL || + cur->priority > SEARCH_HL_PRIORITY)) { shl = search_hl; shl_flag = TRUE; diff --git a/src/testdir/test_matchadd_conceal.vim b/src/testdir/test_matchadd_conceal.vim index be154643e6..6f6e21694c 100644 --- a/src/testdir/test_matchadd_conceal.vim +++ b/src/testdir/test_matchadd_conceal.vim @@ -63,9 +63,9 @@ func Test_matchadd_and_conceallevel_3() setlocal filetype=conf syntax on - 1put='# This is a Test' - " 1234567890123456 - let expect = '#ThisisaTest' + 1put='# This is a Test $' + " 1234567890123 + let expect = '#ThisisaTest$' call cursor(1, 1) call matchadd('Conceal', '\%2l ', 10, -1, {'conceal': 'X'}) @@ -73,22 +73,25 @@ func Test_matchadd_and_conceallevel_3() let lnum = 2 call assert_equal(expect, Screenline(lnum)) call assert_equal(screenattr(lnum, 1), screenattr(lnum, 2)) - call assert_equal(screenattr(lnum, 2), screenattr(lnum, 7)) - call assert_equal(screenattr(lnum, 2), screenattr(lnum, 10)) - call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 7)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 10)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 12)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 13)) + call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 14)) call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 16)) " more matchadd() - " 1234567890123456 - let expect = '#Thisisa Test' + " 12345678901234 + let expect = '#Thisisa Test$' call matchadd('ErrorMsg', '\%2l Test', 20, -1, {'conceal': 'X'}) redraw! call assert_equal(expect, Screenline(lnum)) call assert_equal(screenattr(lnum, 1) , screenattr(lnum, 2)) - call assert_equal(screenattr(lnum, 2) , screenattr(lnum, 7)) + call assert_equal(screenattr(lnum, 1) , screenattr(lnum, 7)) call assert_notequal(screenattr(lnum, 1) , screenattr(lnum, 10)) - call assert_equal(screenattr(lnum, 10), screenattr(lnum, 12)) + call assert_equal(screenattr(lnum, 10), screenattr(lnum, 13)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 14)) call assert_notequal(screenattr(lnum, 1) , screenattr(lnum, 16)) call assert_notequal(screenattr(lnum, 10), screenattr(lnum, 16)) @@ -136,15 +139,18 @@ func Test_syn_and_match_conceal() new setlocal concealcursor=n conceallevel=1 - 1put='# This is a Test' - " 1234567890123456 - let expect = '#ZThisZisZaZTest' + 1put='# This is a Test ' - call cursor(1, 1) - call matchadd('Conceal', '\%2l ', 10, -1, {'conceal': 'Z'}) - syntax match MyConceal /\%2l / conceal containedin=ALL cchar=* - redraw! let lnum = 2 + call cursor(1, 1) + + " 123456789012345678 + let expect = '#ZThisZisZaZTestZZ' + call matchadd('Conceal', '\%2l ', 10, -1, {'conceal': 'Z'}) + syntax match MyConceal /\%2l / conceal containedin=ALL + hi MyConceal ctermbg=4 ctermfg=2 + redraw! + call assert_equal(expect, Screenline(lnum)) call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 2)) call assert_equal(screenattr(lnum, 2), screenattr(lnum, 7)) @@ -152,8 +158,19 @@ func Test_syn_and_match_conceal() call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12)) call assert_equal(screenattr(lnum, 1), screenattr(lnum, 16)) - " 1234567890123456 - let expect = '#*This*is*a*Test' + syntax clear MyConceal + syntax match MyConceal /\%2l / conceal containedin=ALL cchar=* + redraw! + + call assert_equal(expect, Screenline(lnum)) + call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 2)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 7)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 10)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 16)) + + " 123456789012345678 + let expect = '#*This*is*a*Test**' call clearmatches() redraw! @@ -164,6 +181,48 @@ func Test_syn_and_match_conceal() call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12)) call assert_equal(screenattr(lnum, 1), screenattr(lnum, 16)) + " 123456789012345678 + let expect = '#*ThisXis*a*Test**' + call matchadd('Conceal', '\%2l\%7c ', 10, -1, {'conceal': 'X'}) + redraw! + + call assert_equal(expect, Screenline(lnum)) + call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 2)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 7)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 10)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 16)) + + " 123456789012345678 + let expect = '#*ThisXis*a*Test**' + call matchadd('ErrorMsg', '\%2l Test', 20, -1) + redraw! + + call assert_equal(expect, Screenline(lnum)) + call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 2)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12)) + call assert_notequal(screenattr(lnum, 12), screenattr(lnum, 13)) + call assert_equal(screenattr(lnum, 13), screenattr(lnum, 16)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 17)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 18)) + call assert_notequal(screenattr(lnum, 18), screenattr(lnum, 19)) + + " 123456789012345678 + let expect = '# ThisXis a Test' + syntax clear MyConceal + syntax match MyConceal /\%2l / conceal containedin=ALL + redraw! + + call assert_equal(expect, Screenline(lnum)) + call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 2)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12)) + call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 12)) + call assert_notequal(screenattr(lnum, 12), screenattr(lnum, 13)) + call assert_equal(screenattr(lnum, 13), screenattr(lnum, 16)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 17)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 18)) + call assert_notequal(screenattr(lnum, 18), screenattr(lnum, 19)) + syntax off quit! endfunc diff --git a/src/version.c b/src/version.c index 11ec7637e6..90a523e3f5 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1056, /**/ 1055, /**/ From 1e4c7d0ed272201fa3a7cf34a462abb139170759 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 25 Jun 2020 20:56:42 +0200 Subject: [PATCH 012/105] patch 8.2.1057: cannot build with dynamic Lua Problem: Cannot build with dynamic Lua. Solution: Add dll variables. --- src/if_lua.c | 10 ++++++++++ src/version.c | 2 ++ 2 files changed, 12 insertions(+) diff --git a/src/if_lua.c b/src/if_lua.c index ce0901a20e..4cab3f4b9f 100644 --- a/src/if_lua.c +++ b/src/if_lua.c @@ -131,6 +131,8 @@ static void luaV_call_lua_func_free(void *state); #define luaL_addlstring dll_luaL_addlstring #define luaL_pushresult dll_luaL_pushresult #define luaL_loadstring dll_luaL_loadstring +#define luaL_ref dll_luaL_ref +#define luaL_unref dll_luaL_unref // lua #if LUA_VERSION_NUM <= 501 #define lua_tonumber dll_lua_tonumber @@ -226,6 +228,12 @@ void (*dll_luaL_buffinit) (lua_State *L, luaL_Buffer *B); void (*dll_luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l); void (*dll_luaL_pushresult) (luaL_Buffer *B); int (*dll_luaL_loadstring) (lua_State *L, const char *s); +int (*dll_luaL_ref) (lua_State *L, int idx); +#if LUA_VERSION_NUM <= 502 +void (*dll_luaL_unref) (lua_State *L, int idx, int n); +#else +void (*dll_luaL_unref) (lua_State *L, int idx, lua_Integer n); +#endif // lua #if LUA_VERSION_NUM <= 501 lua_Number (*dll_lua_tonumber) (lua_State *L, int idx); @@ -339,6 +347,8 @@ static const luaV_Reg luaV_dll[] = { {"luaL_addlstring", (luaV_function) &dll_luaL_addlstring}, {"luaL_pushresult", (luaV_function) &dll_luaL_pushresult}, {"luaL_loadstring", (luaV_function) &dll_luaL_loadstring}, + {"luaL_ref", (luaV_function) &dll_luaL_ref}, + {"luaL_unref", (luaV_function) &dll_luaL_unref}, // lua #if LUA_VERSION_NUM <= 501 {"lua_tonumber", (luaV_function) &dll_lua_tonumber}, diff --git a/src/version.c b/src/version.c index 90a523e3f5..144e57bd22 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1057, /**/ 1056, /**/ From fc838d6cb0f22c77a6ee2befd034b593e1c5ea06 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 25 Jun 2020 22:23:48 +0200 Subject: [PATCH 013/105] patch 8.2.1058: multiline conceal causes display errors Problem: Multiline conceal causes display errors. Solution: Do not allow conceal cross over EOL. (closes #6326, closes #4854, closes #6302) --- src/drawline.c | 5 +++++ src/testdir/test_conceal.vim | 22 ++++++++++++++++++++++ src/testdir/test_diffmode.vim | 12 ++++++++++++ src/version.c | 2 ++ 4 files changed, 41 insertions(+) diff --git a/src/drawline.c b/src/drawline.c index fad0645fe8..fa2596510a 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -1334,6 +1334,11 @@ win_line( &screen_search_hl, &has_match_conc, &match_conc, did_line_attr, lcs_eol_one); ptr = line + v; // "line" may have been changed + + // Do not allow a conceal over EOL otherwise EOL will be missed + // and bad things happen. + if (*ptr == NUL) + has_match_conc = 0; } #endif diff --git a/src/testdir/test_conceal.vim b/src/testdir/test_conceal.vim index bf942b7880..b555682d2d 100644 --- a/src/testdir/test_conceal.vim +++ b/src/testdir/test_conceal.vim @@ -254,4 +254,26 @@ func Test_conceal_cursor_pos() call delete('XTest_conceal_curpos') endfunc +func Test_conceal_eol() + new! + setlocal concealcursor=n conceallevel=1 + call setline(1, ["x", ""]) + call matchaddpos('Conceal', [[2, 1, 1]], 2, -1, {'conceal': 1}) + redraw! + + call assert_notequal(screenchar(1, 1), screenchar(2, 2)) + call assert_equal(screenattr(1, 1), screenattr(1, 2)) + call assert_equal(screenattr(1, 2), screenattr(2, 2)) + call assert_equal(screenattr(2, 1), screenattr(2, 2)) + + set list + redraw! + + call assert_equal(screenattr(1, 1), screenattr(2, 2)) + call assert_notequal(screenattr(1, 1), screenattr(1, 2)) + call assert_notequal(screenattr(1, 2), screenattr(2, 1)) + + set nolist +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_diffmode.vim b/src/testdir/test_diffmode.vim index 4544834b9d..a3f6d5867b 100644 --- a/src/testdir/test_diffmode.vim +++ b/src/testdir/test_diffmode.vim @@ -1118,4 +1118,16 @@ func Test_diff_rnu() call delete('Xtest_diff_rnu') endfunc +func Test_diff_multilineconceal() + new + diffthis + + new + call matchadd('Conceal', 'a\nb', 9, -1, {'conceal': 'Y'}) + set cole=2 cocu=n + call setline(1, ["a", "b"]) + diffthis + redraw +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 144e57bd22..de8807a528 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1058, /**/ 1057, /**/ From cf8441704d6e517bda1899f4afa82c6b4eecbaec Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 26 Jun 2020 19:44:06 +0200 Subject: [PATCH 014/105] patch 8.2.1059: crash when using :tabonly in an autocommand Problem: Crash when using :tabonly in an autocommand. (Yegappan Lakshmanan) Solution: Do not allow the autocommand window to be closed. --- src/ex_docmd.c | 7 +++++++ src/globals.h | 1 + src/testdir/test_autocmd.vim | 22 +++++++++++++++++++++- src/version.c | 2 ++ src/window.c | 2 +- 5 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 6c887fae1c..2469df341c 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -5178,6 +5178,13 @@ ex_win_close( int need_hide; buf_T *buf = win->w_buffer; + // Never close the autocommand window. + if (win == aucmd_win) + { + emsg(_(e_autocmd_close)); + return; + } + need_hide = (bufIsChanged(buf) && buf->b_nwindows <= 1); if (need_hide && !buf_hide(buf) && !forceit) { diff --git a/src/globals.h b/src/globals.h index c7f9794ab9..03a6937561 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1765,6 +1765,7 @@ EXTERN char e_float_as_string[] INIT(= N_("E806: using Float as a String")); #endif EXTERN char e_dirnotf[] INIT(= N_("E919: Directory not found in '%s': \"%s\"")); EXTERN char e_au_recursive[] INIT(= N_("E952: Autocommand caused recursive behavior")); +EXTERN char e_autocmd_close[] INIT(= N_("E813: Cannot close autocmd or popup window")); #ifdef FEAT_MENU EXTERN char e_menuothermode[] INIT(= N_("E328: Menu only exists in another mode")); #endif diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim index d7328c3e0a..446d22bd71 100644 --- a/src/testdir/test_autocmd.vim +++ b/src/testdir/test_autocmd.vim @@ -2617,7 +2617,27 @@ func Test_close_autocmd_window() au! augroup END augroup! aucmd_win_test2 - %bw! + %bwipe! +endfunc + +" Test for trying to close the tab that has the temporary window for exeucing +" an autocmd. +func Test_close_autocmd_tab() + edit one.txt + tabnew two.txt + augroup aucmd_win_test + au! + au BufEnter * if expand('') == 'one.txt' | tabfirst | tabonly | endif + augroup END + + call assert_fails('doautoall BufEnter', 'E813:') + + tabonly + augroup aucmd_win_test + au! + augroup END + augroup! aucmd_win_test + %bwipe! endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index de8807a528..aff4583e94 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1059, /**/ 1058, /**/ diff --git a/src/window.c b/src/window.c index 095eabed2b..e2adc0cb9f 100644 --- a/src/window.c +++ b/src/window.c @@ -2461,7 +2461,7 @@ win_close(win_T *win, int free_buf) return FAIL; // window is already being closed if (win_unlisted(win)) { - emsg(_("E813: Cannot close autocmd or popup window")); + emsg(_(e_autocmd_close)); return FAIL; } if ((firstwin == aucmd_win || lastwin == aucmd_win) && one_window()) From 5f36d5fbb836e6fdeb9e3b2c26edb88e45150db4 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 26 Jun 2020 20:23:45 +0200 Subject: [PATCH 015/105] patch 8.2.1060: not all elinks files are recognized Problem: Not all elinks files are recognized. Solution: Just check for "elinks.conf". (Guido Cella, closes #6337) --- runtime/filetype.vim | 2 +- src/testdir/test_filetype.vim | 2 +- src/version.c | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/runtime/filetype.vim b/runtime/filetype.vim index a6c8956542..3778fe5ff8 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -542,7 +542,7 @@ au BufNewFile,BufRead *.ecd setf ecd au BufNewFile,BufRead *.e,*.E call dist#ft#FTe() " Elinks configuration -au BufNewFile,BufRead */etc/elinks.conf,*/.elinks/elinks.conf setf elinks +au BufNewFile,BufRead elinks.conf setf elinks " ERicsson LANGuage; Yaws is erlang too au BufNewFile,BufRead *.erl,*.hrl,*.yaws setf erlang diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index 38339aefec..8ec8355c24 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -150,7 +150,7 @@ let s:filename_checks = { \ 'dylanlid': ['file.lid'], \ 'ecd': ['file.ecd'], \ 'edif': ['file.edf', 'file.edif', 'file.edo'], - \ 'elinks': ['/etc/elinks.conf', '/.elinks/elinks.conf'], + \ 'elinks': ['elinks.conf'], \ 'elm': ['file.elm'], \ 'elmfilt': ['filter-rules'], \ 'erlang': ['file.erl', 'file.hrl', 'file.yaws'], diff --git a/src/version.c b/src/version.c index aff4583e94..ca08dff59b 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1060, /**/ 1059, /**/ From 5d3c9f8c2a0fc29ba4ac8e0f052378b64d9e3dd3 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 26 Jun 2020 20:41:39 +0200 Subject: [PATCH 016/105] patch 8.2.1061: insufficient testing for src/window.c Problem: Insufficient testing for src/window.c. Solution: Add more tests. (Yegappan Lakshmanan, closes #6345) --- src/testdir/test_excmd.vim | 5 + src/testdir/test_gf.vim | 11 +- src/testdir/test_options.vim | 20 ++++ src/testdir/test_popupwin.vim | 2 + src/testdir/test_quickfix.vim | 17 +++ src/testdir/test_tabpage.vim | 65 +++++++++++ src/testdir/test_tagjump.vim | 45 ++++++++ src/testdir/test_window_cmd.vim | 186 +++++++++++++++++++++++++++++++- src/version.c | 2 + src/window.c | 4 +- 10 files changed, 352 insertions(+), 5 deletions(-) diff --git a/src/testdir/test_excmd.vim b/src/testdir/test_excmd.vim index 868ac6fc40..2428e9d02d 100644 --- a/src/testdir/test_excmd.vim +++ b/src/testdir/test_excmd.vim @@ -371,6 +371,11 @@ func Test_run_excmd_with_text_locked() close call assert_fails("call feedkeys(\":\=execute('bnext')\\", 'xt')", 'E565:') + + " :tabfirst + tabnew + call assert_fails("call feedkeys(\":\=execute('tabfirst')\\", 'xt')", 'E565:') + tabclose endfunc " Test for the :verbose command diff --git a/src/testdir/test_gf.vim b/src/testdir/test_gf.vim index 3fa85113f1..eaa8c0fc95 100644 --- a/src/testdir/test_gf.vim +++ b/src/testdir/test_gf.vim @@ -74,11 +74,18 @@ func Test_gF() call assert_equal('Xfile', bufname('%')) call assert_equal(2, getcurpos()[1]) + " jumping to the file/line with CTRL-W_F + %bw! + edit Xfile1 + call setline(1, ['one', 'Xfile:4', 'three']) + exe "normal 2G\F" + call assert_equal('Xfile', bufname('%')) + call assert_equal(4, getcurpos()[1]) + set isfname& call delete('Xfile') call delete('Xfile2') - bwipe Xfile - bwipe Xfile2 + %bw! endfunc " Test for invoking 'gf' on a ${VAR} variable diff --git a/src/testdir/test_options.vim b/src/testdir/test_options.vim index a57f3c74ab..8f27d9c4ac 100644 --- a/src/testdir/test_options.vim +++ b/src/testdir/test_options.vim @@ -953,4 +953,24 @@ func Test_window_opt() set window& endfunc +" Test for the 'winminheight' option +func Test_opt_winminheight() + only! + let &winheight = &lines + 4 + call assert_fails('let &winminheight = &lines + 2', 'E36:') + call assert_true(&winminheight <= &lines) + set winminheight& + set winheight& +endfunc + +" Test for the 'winminwidth' option +func Test_opt_winminwidth() + only! + let &winwidth = &columns + 4 + call assert_fails('let &winminwidth = &columns + 2', 'E36:') + call assert_true(&winminwidth <= &columns) + set winminwidth& + set winwidth& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim index 9fb51abd76..8631c71e7f 100644 --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -945,6 +945,8 @@ func Test_win_execute_not_allowed() call assert_fails('call win_execute(winid, "blast")', 'E994:') call assert_fails('call win_execute(winid, "edit")', 'E994:') call assert_fails('call win_execute(winid, "enew")', 'E994:') + call assert_fails('call win_execute(winid, "help")', 'E994:') + call assert_fails('call win_execute(winid, "1only")', 'E994:') call assert_fails('call win_execute(winid, "wincmd x")', 'E994:') call assert_fails('call win_execute(winid, "wincmd w")', 'E994:') call assert_fails('call win_execute(winid, "wincmd t")', 'E994:') diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim index 669060a518..608a9553b8 100644 --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -286,6 +286,23 @@ func XwindowTests(cchar) call assert_equal(12, winwidth(0)) Xclose + " Horizontally or vertically splitting the quickfix window should create a + " normal window/buffer + Xopen + wincmd s + call assert_equal(0, getwininfo(win_getid())[0].quickfix) + call assert_equal(0, getwininfo(win_getid())[0].loclist) + call assert_notequal('quickfix', &buftype) + close + Xopen + wincmd v + call assert_equal(0, getwininfo(win_getid())[0].quickfix) + call assert_equal(0, getwininfo(win_getid())[0].loclist) + call assert_notequal('quickfix', &buftype) + close + Xopen + Xclose + if a:cchar == 'c' " Opening the quickfix window in multiple tab pages should reuse the " quickfix buffer diff --git a/src/testdir/test_tabpage.vim b/src/testdir/test_tabpage.vim index d1bea2f3ca..895716501e 100644 --- a/src/testdir/test_tabpage.vim +++ b/src/testdir/test_tabpage.vim @@ -143,6 +143,8 @@ function Test_tabpage() call assert_fails("tabmove $3", 'E474:') call assert_fails("%tabonly", 'E16:') 1tabonly! + tabmove 1 + call assert_equal(1, tabpagenr()) tabnew call assert_fails("-2tabmove", 'E474:') tabonly! @@ -712,4 +714,67 @@ func Test_tabline_tabmenu() %bw! endfunc +" Test for changing the current tab page from an autocmd when closing a tab +" page. +func Test_tabpage_switchtab_on_close() + only + tabnew + tabnew + " Test for BufLeave + augroup T1 + au! + au BufLeave * tabfirst + augroup END + tabclose + call assert_equal(1, tabpagenr()) + augroup T1 + au! + augroup END + + " Test for WinLeave + $tabnew + augroup T1 + au! + au WinLeave * tabfirst + augroup END + tabclose + call assert_equal(1, tabpagenr()) + augroup T1 + au! + augroup END + + " Test for TabLeave + $tabnew + augroup T1 + au! + au TabLeave * tabfirst + augroup END + tabclose + call assert_equal(1, tabpagenr()) + augroup T1 + au! + augroup END + augroup! T1 + tabonly +endfunc + +" Test for closing the destination tabpage when jumping from one to another. +func Test_tabpage_close_on_switch() + tabnew + tabnew + edit Xfile + augroup T2 + au! + au BufLeave Xfile 1tabclose + augroup END + tabfirst + call assert_equal(2, tabpagenr()) + call assert_equal('Xfile', @%) + augroup T2 + au! + augroup END + augroup! T2 + %bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_tagjump.vim b/src/testdir/test_tagjump.vim index a26ba9a0fc..e5a5768a57 100644 --- a/src/testdir/test_tagjump.vim +++ b/src/testdir/test_tagjump.vim @@ -12,6 +12,47 @@ func Test_ptag_with_notagstack() set tagstack&vim endfunc +func Test_ptjump() + CheckFeature quickfix + + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "one\tXfile\t1", + \ "three\tXfile\t3", + \ "two\tXfile\t2"], + \ 'Xtags') + call writefile(['one', 'two', 'three'], 'Xfile') + + %bw! + ptjump two + call assert_equal(2, winnr()) + wincmd p + call assert_equal(1, &previewwindow) + call assert_equal('Xfile', expand("%:p:t")) + call assert_equal(2, line('.')) + call assert_equal(2, winnr('$')) + call assert_equal(1, winnr()) + close + call setline(1, ['one', 'two', 'three']) + exe "normal 3G\g}" + call assert_equal(2, winnr()) + wincmd p + call assert_equal(1, &previewwindow) + call assert_equal('Xfile', expand("%:p:t")) + call assert_equal(3, line('.')) + call assert_equal(2, winnr('$')) + call assert_equal(1, winnr()) + close + exe "normal 3G5\\}" + wincmd p + call assert_equal(5, winheight(0)) + close + + call delete('Xtags') + call delete('Xfile') + set tags& +endfunc + func Test_cancel_ptjump() CheckFeature quickfix @@ -1267,6 +1308,10 @@ func Test_macro_search() close call assert_fails('3wincmd d', 'E387:') call assert_fails('6wincmd d', 'E388:') + new + call assert_fails("normal \d", 'E349:') + call assert_fails("normal \\", 'E349:') + close " Test for :dsplit dsplit FOO diff --git a/src/testdir/test_window_cmd.vim b/src/testdir/test_window_cmd.vim index e20bfa5f9e..aa619cd7cc 100644 --- a/src/testdir/test_window_cmd.vim +++ b/src/testdir/test_window_cmd.vim @@ -36,7 +36,16 @@ func Test_window_cmd_cmdwin_with_vsp() set ls&vim endfunc -function Test_window_cmd_wincmd_gf() +" Test for jumping to windows +func Test_window_jump() + new + " jumping to a window with a count greater than the max windows + exe "normal 4\w" + call assert_equal(2, winnr()) + only +endfunc + +func Test_window_cmd_wincmd_gf() let fname = 'test_gf.txt' let swp_fname = '.' . fname . '.swp' call writefile([], fname) @@ -1099,4 +1108,179 @@ func Test_wincmd_fails() call assert_beeps("normal \2gt") endfunc +" Test for adjusting the window width when a window is closed with some +" windows using 'winfixwidth' +func Test_window_width_adjust() + only + " Three vertical windows. Windows 1 and 2 have 'winfixwidth' set and close + " window 2. + wincmd v + vert resize 10 + set winfixwidth + wincmd v + set winfixwidth + wincmd c + call assert_inrange(10, 12, winwidth(1)) + " Three vertical windows. Windows 2 and 3 have 'winfixwidth' set and close + " window 3. + only + set winfixwidth + wincmd v + vert resize 10 + set winfixwidth + wincmd v + set nowinfixwidth + wincmd b + wincmd c + call assert_inrange(10, 12, winwidth(2)) + + new | only +endfunc + +" Test for jumping to a vertical/horizontal neighbor window based on the +" current cursor position +func Test_window_goto_neightbor() + %bw! + + " Vertical window movement + + " create the following window layout: + " +--+--+ + " |w1|w3| + " +--+ | + " |w2| | + " +--+--+ + " |w4 | + " +-----+ + new + vsplit + split + " vertically jump from w4 + wincmd b + call setline(1, repeat(' ', &columns)) + call cursor(1, 1) + wincmd k + call assert_equal(2, winnr()) + wincmd b + call cursor(1, &columns) + redraw! + wincmd k + call assert_equal(3, winnr()) + %bw! + + " create the following window layout: + " +--+--+--+ + " |w1|w2|w3| + " +--+--+--+ + " |w4 | + " +--------+ + new + vsplit + vsplit + wincmd b + call setline(1, repeat(' ', &columns)) + call cursor(1, 1) + wincmd k + call assert_equal(1, winnr()) + wincmd b + call cursor(1, &columns / 2) + redraw! + wincmd k + call assert_equal(2, winnr()) + wincmd b + call cursor(1, &columns) + redraw! + wincmd k + call assert_equal(3, winnr()) + %bw! + + " Horizontal window movement + + " create the following window layout: + " +--+--+--+ + " |w1|w2|w4| + " +--+--+ | + " |w3 | | + " +-----+--+ + vsplit + split + vsplit + 4wincmd l + call setline(1, repeat([' '], &lines)) + call cursor(1, 1) + redraw! + wincmd h + call assert_equal(2, winnr()) + 4wincmd l + call cursor(&lines, 1) + redraw! + wincmd h + call assert_equal(3, winnr()) + %bw! + + " create the following window layout: + " +--+--+ + " |w1|w4| + " +--+ + + " |w2| | + " +--+ + + " |w3| | + " +--+--+ + vsplit + split + split + wincmd l + call setline(1, repeat([' '], &lines)) + call cursor(1, 1) + redraw! + wincmd h + call assert_equal(1, winnr()) + wincmd l + call cursor(&lines / 2, 1) + redraw! + wincmd h + call assert_equal(2, winnr()) + wincmd l + call cursor(&lines, 1) + redraw! + wincmd h + call assert_equal(3, winnr()) + %bw! +endfunc + +" Test for an autocmd closing the destination window when jumping from one +" window to another. +func Test_close_dest_window() + split + edit Xfile + + " Test for BufLeave + augroup T1 + au! + au BufLeave Xfile $wincmd c + augroup END + wincmd b + call assert_equal(1, winnr('$')) + call assert_equal('Xfile', @%) + augroup T1 + au! + augroup END + + " Test for WinLeave + new + wincmd p + augroup T1 + au! + au WinLeave * 1wincmd c + augroup END + wincmd t + call assert_equal(1, winnr('$')) + call assert_equal('Xfile', @%) + augroup T1 + au! + augroup END + augroup! T1 + %bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index ca08dff59b..aef2fe9e97 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1061, /**/ 1060, /**/ diff --git a/src/window.c b/src/window.c index e2adc0cb9f..4b2ff4b523 100644 --- a/src/window.c +++ b/src/window.c @@ -1810,8 +1810,8 @@ win_move_after(win_T *win1, win_T *win2) return; } - // may need move the status line/vertical separator of the last window - // + // may need to move the status line/vertical separator of the last + // window if (win1 == lastwin) { height = win1->w_prev->w_status_height; From 793648fb563359396a23740c72a6e04cb64df3a9 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 26 Jun 2020 21:28:25 +0200 Subject: [PATCH 017/105] patch 8.2.1062: Vim9: no line break allowed inside "cond ? val1 : val2" Problem: Vim9: no line break allowed inside "cond ? val1 : val2". Solution: Check for operator after line break. --- src/eval.c | 14 ++++++++++++-- src/testdir/test_vim9_expr.vim | 21 +++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/eval.c b/src/eval.c index 0402b36385..fe53632df9 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1892,13 +1892,17 @@ eval0( int eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg) { + char_u *p; + int getnext; + /* * Get the first variable. */ if (eval2(arg, rettv, evalarg) == FAIL) return FAIL; - if ((*arg)[0] == '?') + p = eval_next_non_blank(*arg, evalarg, &getnext); + if (*p == '?') { int result; typval_T var2; @@ -1906,6 +1910,9 @@ eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg) int orig_flags; int evaluate; + if (getnext) + *arg = eval_next_line(evalarg); + if (evalarg == NULL) { CLEAR_FIELD(nested_evalarg); @@ -1942,13 +1949,16 @@ eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg) /* * Check for the ":". */ - if ((*arg)[0] != ':') + p = eval_next_non_blank(*arg, evalarg, &getnext); + if (*p != ':') { emsg(_(e_missing_colon)); if (evaluate && result) clear_tv(rettv); return FAIL; } + if (getnext) + *arg = eval_next_line(evalarg); /* * Get the third variable. Recursive! diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index b37f92fb6a..566af673ff 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -45,6 +45,27 @@ def Test_expr1() assert_equal(function('len'), RetThat) enddef +def Test_expr1_vimscript() + " only checks line continuation + let lines =<< trim END + vim9script + let var = 1 + ? 'yes' + : 'no' + assert_equal('yes', var) + END + CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + let var = v:false + ? 'yes' + : 'no' + assert_equal('no', var) + END + CheckScriptSuccess(lines) +enddef + func Test_expr1_fails() call CheckDefFailure(["let x = 1 ? 'one'"], "Missing ':' after '?'") call CheckDefFailure(["let x = 1 ? 'one' : xxx"], "E1001:") diff --git a/src/version.c b/src/version.c index aef2fe9e97..a97b40acef 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1062, /**/ 1061, /**/ From be7ee488761a5582a5605197c3951a17f20d072e Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 26 Jun 2020 21:38:51 +0200 Subject: [PATCH 018/105] patch 8.2.1063: Vim9: no line break allowed before || or && Problem: Vim9: no line break allowed before || or &&. Solution: Check for operator after line break. --- src/eval.c | 20 +++++++++++++++-- src/testdir/test_vim9_expr.vim | 40 ++++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/src/eval.c b/src/eval.c index fe53632df9..9f06fd8d71 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1991,6 +1991,8 @@ eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg) static int eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg) { + char_u *p; + int getnext; typval_T var2; long result; int first; @@ -2007,12 +2009,16 @@ eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg) */ first = TRUE; result = FALSE; - while ((*arg)[0] == '|' && (*arg)[1] == '|') + p = eval_next_non_blank(*arg, evalarg, &getnext); + while (p[0] == '|' && p[1] == '|') { evalarg_T nested_evalarg; int evaluate; int orig_flags; + if (getnext) + *arg = eval_next_line(evalarg); + if (evalarg == NULL) { CLEAR_FIELD(nested_evalarg); @@ -2061,6 +2067,8 @@ eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg) rettv->v_type = VAR_NUMBER; rettv->vval.v_number = result; } + + p = eval_next_non_blank(*arg, evalarg, &getnext); } return OK; @@ -2078,6 +2086,8 @@ eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg) static int eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg) { + char_u *p; + int getnext; typval_T var2; long result; int first; @@ -2094,12 +2104,16 @@ eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg) */ first = TRUE; result = TRUE; - while ((*arg)[0] == '&' && (*arg)[1] == '&') + p = eval_next_non_blank(*arg, evalarg, &getnext); + while (p[0] == '&' && p[1] == '&') { evalarg_T nested_evalarg; int orig_flags; int evaluate; + if (getnext) + *arg = eval_next_line(evalarg); + if (evalarg == NULL) { CLEAR_FIELD(nested_evalarg); @@ -2147,6 +2161,8 @@ eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg) rettv->v_type = VAR_NUMBER; rettv->vval.v_number = result; } + + p = eval_next_non_blank(*arg, evalarg, &getnext); } return OK; diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index 566af673ff..dcdaeb2403 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -117,6 +117,26 @@ def Test_expr2() assert_equal([[], '', 0], g:vals) enddef +def Test_expr2_vimscript() + " only checks line continuation + let lines =<< trim END + vim9script + let var = 0 + || 1 + assert_equal(1, var) + END + CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + let var = v:false + || v:true + || v:false + assert_equal(1, var) + END + CheckScriptSuccess(lines) +enddef + func Test_expr2_fails() let msg = "white space required before and after '||'" call CheckDefFailure(["let x = 1||2"], msg) @@ -160,6 +180,26 @@ def Test_expr3() assert_equal([[1], 'z', 0], g:vals) enddef +def Test_expr3_vimscript() + " only checks line continuation + let lines =<< trim END + vim9script + let var = 0 + && 1 + assert_equal(0, var) + END + CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + let var = v:true + && v:true + && v:true + assert_equal(1, var) + END + CheckScriptSuccess(lines) +enddef + func Test_expr3_fails() let msg = "white space required before and after '&&'" call CheckDefFailure(["let x = 1&&2"], msg) diff --git a/src/version.c b/src/version.c index a97b40acef..bdb43fcc57 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1063, /**/ 1062, /**/ From e6536aa766e95b6c64489678eb029e6909ee6a35 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 26 Jun 2020 22:00:38 +0200 Subject: [PATCH 019/105] patch 8.2.1064: Vim9: no line break allowed before comperators Problem: Vim9: no line break allowed before comperators. Solution: Check for comperator after line break. --- src/eval.c | 6 +++++- src/testdir/test_vim9_expr.vim | 37 ++++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/eval.c b/src/eval.c index 9f06fd8d71..1bfe808f54 100644 --- a/src/eval.c +++ b/src/eval.c @@ -2191,6 +2191,7 @@ eval4(char_u **arg, typval_T *rettv, evalarg_T *evalarg) { typval_T var2; char_u *p; + int getnext; int i; exptype_T type = EXPR_UNKNOWN; int len = 2; @@ -2202,7 +2203,7 @@ eval4(char_u **arg, typval_T *rettv, evalarg_T *evalarg) if (eval5(arg, rettv, evalarg) == FAIL) return FAIL; - p = *arg; + p = eval_next_non_blank(*arg, evalarg, &getnext); switch (p[0]) { case '=': if (p[1] == '=') @@ -2247,6 +2248,9 @@ eval4(char_u **arg, typval_T *rettv, evalarg_T *evalarg) */ if (type != EXPR_UNKNOWN) { + if (getnext) + *arg = eval_next_line(evalarg); + // extra question mark appended: ignore case if (p[len] == '?') { diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index dcdaeb2403..de4d741659 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -529,6 +529,43 @@ def RetVoid() let x = 1 enddef +def Test_expr4_vimscript() + " only checks line continuation + let lines =<< trim END + vim9script + let var = 0 + < 1 + assert_equal(1, var) + END + CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + let var = 123 + != 123 + assert_equal(0, var) + END + CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + let list = [1, 2, 3] + let var = list + is list + assert_equal(1, var) + END + CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + let myblob = 0z1234 + let var = myblob + isnot 0z11 + assert_equal(1, var) + END + CheckScriptSuccess(lines) +enddef + func Test_expr4_fails() let msg = "white space required before and after '>'" call CheckDefFailure(["let x = 1>2"], msg) diff --git a/src/version.c b/src/version.c index bdb43fcc57..eb0bf8dcdc 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1064, /**/ 1063, /**/ From 7147820cb978f5b179cfec2f9d8b7774e28d43e0 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 26 Jun 2020 22:46:27 +0200 Subject: [PATCH 020/105] patch 8.2.1065: Vim9: no line break allowed inside a list Problem: Vim9: no line break allowed inside a list. Solution: Handle line break inside a list in Vim9 script. --- src/eval.c | 6 +++--- src/list.c | 30 +++++++++++++++++++++--------- src/proto/eval.pro | 2 ++ src/proto/list.pro | 2 +- src/testdir/test_arglist.vim | 33 ++++++++++++++++++--------------- src/testdir/test_vim9_expr.vim | 22 +++++++++++++++++++++- src/version.c | 2 ++ src/vim9compile.c | 2 +- 8 files changed, 69 insertions(+), 30 deletions(-) diff --git a/src/eval.c b/src/eval.c index 1bfe808f54..99003ced2c 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1771,7 +1771,7 @@ eval_func( * Otherwise just return "arg" unmodified and set "getnext" to FALSE. * "arg" must point somewhere inside a line, not at the start. */ - static char_u * + char_u * eval_next_non_blank(char_u *arg, evalarg_T *evalarg, int *getnext) { *getnext = FALSE; @@ -1796,7 +1796,7 @@ eval_next_non_blank(char_u *arg, evalarg_T *evalarg, int *getnext) /* * To be called when eval_next_non_blank() sets "getnext" to TRUE. */ - static char_u * + char_u * eval_next_line(evalarg_T *evalarg) { vim_free(evalarg->eval_tofree); @@ -2773,7 +2773,7 @@ eval7( /* * List: [expr, expr] */ - case '[': ret = get_list_tv(arg, rettv, flags, TRUE); + case '[': ret = get_list_tv(arg, rettv, evalarg, TRUE); break; /* diff --git a/src/list.c b/src/list.c index cf0c99f131..611171d1dc 100644 --- a/src/list.c +++ b/src/list.c @@ -1156,19 +1156,19 @@ f_join(typval_T *argvars, typval_T *rettv) /* * Allocate a variable for a List and fill it from "*arg". + * "*arg" points to the "[". * Return OK or FAIL. */ int -get_list_tv(char_u **arg, typval_T *rettv, int flags, int do_error) +get_list_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int do_error) { - int evaluate = flags & EVAL_EVALUATE; + int evaluate = evalarg == NULL ? FALSE + : evalarg->eval_flags & EVAL_EVALUATE; + int getnext; list_T *l = NULL; typval_T tv; listitem_T *item; - evalarg_T evalarg; - - CLEAR_FIELD(evalarg); - evalarg.eval_flags = flags; + int had_comma; if (evaluate) { @@ -1178,9 +1178,12 @@ get_list_tv(char_u **arg, typval_T *rettv, int flags, int do_error) } *arg = skipwhite(*arg + 1); + eval_next_non_blank(*arg, evalarg, &getnext); + if (getnext) + *arg = eval_next_line(evalarg); while (**arg != ']' && **arg != NUL) { - if (eval1(arg, &tv, &evalarg) == FAIL) // recursive! + if (eval1(arg, &tv, evalarg) == FAIL) // recursive! goto failret; if (evaluate) { @@ -1195,15 +1198,24 @@ get_list_tv(char_u **arg, typval_T *rettv, int flags, int do_error) clear_tv(&tv); } + // the comma must comma after the value + had_comma = **arg == ','; + if (had_comma) + *arg = skipwhite(*arg + 1); + + // the "]" can be on the next line + eval_next_non_blank(*arg, evalarg, &getnext); + if (getnext) + *arg = eval_next_line(evalarg); if (**arg == ']') break; - if (**arg != ',') + + if (!had_comma) { if (do_error) semsg(_("E696: Missing comma in List: %s"), *arg); goto failret; } - *arg = skipwhite(*arg + 1); } if (**arg != ']') diff --git a/src/proto/eval.pro b/src/proto/eval.pro index 87e447808b..6ae770ce25 100644 --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -26,6 +26,8 @@ int next_for_item(void *fi_void, char_u *arg); void free_for_info(void *fi_void); void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx); int pattern_match(char_u *pat, char_u *text, int ic); +char_u *eval_next_non_blank(char_u *arg, evalarg_T *evalarg, int *getnext); +char_u *eval_next_line(evalarg_T *evalarg); int eval0(char_u *arg, typval_T *rettv, exarg_T *eap, evalarg_T *evalarg); int eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg); void eval_addblob(typval_T *tv1, typval_T *tv2); diff --git a/src/proto/list.pro b/src/proto/list.pro index 07dd4e2b1d..ddf26a5dd6 100644 --- a/src/proto/list.pro +++ b/src/proto/list.pro @@ -39,7 +39,7 @@ void vimlist_remove(list_T *l, listitem_T *item, listitem_T *item2); char_u *list2string(typval_T *tv, int copyID, int restore_copyID); int list_join(garray_T *gap, list_T *l, char_u *sep, int echo_style, int restore_copyID, int copyID); void f_join(typval_T *argvars, typval_T *rettv); -int get_list_tv(char_u **arg, typval_T *rettv, int flags, int do_error); +int get_list_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int do_error); int write_list(FILE *fd, list_T *list, int binary); void init_static_list(staticList10_T *sl); void f_list2str(typval_T *argvars, typval_T *rettv); diff --git a/src/testdir/test_arglist.vim b/src/testdir/test_arglist.vim index f9995a4bc8..198ca989a0 100644 --- a/src/testdir/test_arglist.vim +++ b/src/testdir/test_arglist.vim @@ -175,22 +175,25 @@ func Test_argument() let save_columns = &columns let &columns = 79 - exe 'args ' .. join(range(1, 81)) - call assert_equal(join([ - \ '', - \ '[1] 6 11 16 21 26 31 36 41 46 51 56 61 66 71 76 81 ', - \ '2 7 12 17 22 27 32 37 42 47 52 57 62 67 72 77 ', - \ '3 8 13 18 23 28 33 38 43 48 53 58 63 68 73 78 ', - \ '4 9 14 19 24 29 34 39 44 49 54 59 64 69 74 79 ', - \ '5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 ', - \ ], "\n"), - \ execute('args')) + try + exe 'args ' .. join(range(1, 81)) + call assert_equal(join([ + \ '', + \ '[1] 6 11 16 21 26 31 36 41 46 51 56 61 66 71 76 81 ', + \ '2 7 12 17 22 27 32 37 42 47 52 57 62 67 72 77 ', + \ '3 8 13 18 23 28 33 38 43 48 53 58 63 68 73 78 ', + \ '4 9 14 19 24 29 34 39 44 49 54 59 64 69 74 79 ', + \ '5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 ', + \ ], "\n"), + \ execute('args')) - " No trailing newline with one item per row. - let long_arg = repeat('X', 81) - exe 'args ' .. long_arg - call assert_equal("\n[".long_arg.']', execute('args')) - let &columns = save_columns + " No trailing newline with one item per row. + let long_arg = repeat('X', 81) + exe 'args ' .. long_arg + call assert_equal("\n[".long_arg.']', execute('args')) + finally + let &columns = save_columns + endtry " Setting argument list should fail when the current buffer has unsaved " changes diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index de4d741659..33a282b061 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -974,7 +974,7 @@ def Test_expr7_list() " list assert_equal(g:list_empty, []) assert_equal(g:list_empty, [ ]) - assert_equal(g:list_mixed, [1, 'b', false]) + assert_equal(g:list_mixed, [1, 'b', false,]) assert_equal('b', g:list_mixed[1]) call CheckDefExecFailure(["let x = g:anint[3]"], 'E714:') @@ -984,6 +984,26 @@ def Test_expr7_list() call CheckDefExecFailure(["let x = g:list_empty[3]"], 'E684:') enddef +def Test_expr7_list_vim9script() + let lines =<< trim END + vim9script + let l = [ + 11, + 22, + ] + assert_equal([11, 22], l) + END + CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + let l = [11, + 22] + assert_equal([11, 22], l) + END + CheckScriptSuccess(lines) +enddef + def Test_expr7_lambda() " lambda let La = { -> 'result'} diff --git a/src/version.c b/src/version.c index eb0bf8dcdc..0743833644 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1065, /**/ 1064, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 054eaa63d5..f133a94b73 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2989,7 +2989,7 @@ to_name_const_end(char_u *arg) { // Can be "[1, 2, 3]->Func()". - if (get_list_tv(&p, &rettv, 0, FALSE) == FAIL) + if (get_list_tv(&p, &rettv, NULL, FALSE) == FAIL) p = arg; } else if (p == arg && *arg == '#' && arg[1] == '{') From bd84617d1a6766efd59c94aabebb044bef805b99 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 27 Jun 2020 12:32:57 +0200 Subject: [PATCH 021/105] patch 8.2.1066: Lua arrays are zero based Problem: Lua arrays are zero based. Solution: Make Lua arrays one based. (Prabir Shrestha, closes #6347) Note: this is not backwards compatible. --- runtime/doc/if_lua.txt | 12 ++++++++---- src/if_lua.c | 12 +++++++++++- src/testdir/test_lua.vim | 22 +++++++++++----------- src/version.c | 2 ++ 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt index 170f861ff0..f32be7c379 100644 --- a/runtime/doc/if_lua.txt +++ b/runtime/doc/if_lua.txt @@ -217,11 +217,15 @@ Vim's syntax for lists. Since lists are objects, changes in list references in Lua are reflected in Vim and vice-versa. A list "l" has the following properties and methods: +NOTE: In patch 8.2.1066 array indexes were changed from zero-based to +one-based. You can check with: > + if has("patch-8.2.1066") + Properties ---------- o "#l" is the number of items in list "l", equivalent to "len(l)" in Vim. - o "l[k]" returns the k-th item in "l"; "l" is zero-indexed, as in Vim. + o "l[k]" returns the k-th item in "l"; "l" is one-indexed, as in Lua. To modify the k-th item, simply do "l[k] = newitem"; in particular, "l[k] = nil" removes the k-th item from "l". o "l()" returns an iterator for "l". @@ -237,11 +241,11 @@ Examples: :let l = [1, 'item'] :lua l = vim.eval('l') -- same 'l' :lua l:add(vim.list()) - :lua l[0] = math.pi + :lua l[1] = math.pi :echo l[0] " 3.141593 - :lua l[0] = nil -- remove first item + :lua l[1] = nil -- remove first item :lua l:insert(true, 1) - :lua print(l, #l, l[0], l[1], l[-1]) + :lua print(l, #l, l[1], l[2]) :lua for item in l() do print(item) end < diff --git a/src/if_lua.c b/src/if_lua.c index 4cab3f4b9f..2d02f7c58d 100644 --- a/src/if_lua.c +++ b/src/if_lua.c @@ -871,7 +871,13 @@ luaV_list_index(lua_State *L) list_T *l = luaV_unbox(L, luaV_List, 1); if (lua_isnumber(L, 2)) // list item? { - listitem_T *li = list_find(l, (long) luaL_checkinteger(L, 2)); + long n = (long) luaL_checkinteger(L, 2); + listitem_T *li; + + // Lua array index starts with 1 while Vim uses 0, subtract 1 to + // normalize. + n -= 1; + li = list_find(l, n); if (li == NULL) lua_pushnil(L); else @@ -900,6 +906,10 @@ luaV_list_newindex(lua_State *L) list_T *l = luaV_unbox(L, luaV_List, 1); long n = (long) luaL_checkinteger(L, 2); listitem_T *li; + + // Lua array index starts with 1 while Vim uses 0, subtract 1 to normalize. + n -= 1; + if (l->lv_lock) luaL_error(L, "list is locked"); li = list_find(l, n); diff --git a/src/testdir/test_lua.vim b/src/testdir/test_lua.vim index 826a7bce80..9d5ad6864e 100644 --- a/src/testdir/test_lua.vim +++ b/src/testdir/test_lua.vim @@ -327,8 +327,8 @@ func Test_lua_list() call assert_equal(7, luaeval('#l')) call assert_match('^list: \%(0x\)\?\x\+$', luaeval('tostring(l)')) - lua l[0] = 124 - lua l[5] = nil + lua l[1] = 124 + lua l[6] = nil lua l:insert('first') lua l:insert('xx', 3) call assert_equal(['first', 124, 'abc', 'xx', v:true, v:false, v:null, {'a': 1, 'b': 2, 'c': 3}], l) @@ -367,22 +367,22 @@ func Test_lua_recursive_list() lua l = vim.list():add(1):add(2) lua l = l:add(l) - call assert_equal(1, luaeval('l[0]')) - call assert_equal(2, luaeval('l[1]')) + call assert_equal(1, luaeval('l[1]')) + call assert_equal(2, luaeval('l[2]')) - call assert_equal(1, luaeval('l[2][0]')) - call assert_equal(2, luaeval('l[2][1]')) + call assert_equal(1, luaeval('l[3][1]')) + call assert_equal(2, luaeval('l[3][2]')) - call assert_equal(1, luaeval('l[2][2][0]')) - call assert_equal(2, luaeval('l[2][2][1]')) + call assert_equal(1, luaeval('l[3][3][1]')) + call assert_equal(2, luaeval('l[3][3][2]')) call assert_equal('[1, 2, [...]]', string(luaeval('l'))) call assert_match('^list: \%(0x\)\?\x\+$', luaeval('tostring(l)')) - call assert_equal(luaeval('tostring(l)'), luaeval('tostring(l[2])')) + call assert_equal(luaeval('tostring(l)'), luaeval('tostring(l[3])')) - call assert_equal(luaeval('l'), luaeval('l[2]')) - call assert_equal(luaeval('l'), luaeval('l[2][2]')) + call assert_equal(luaeval('l'), luaeval('l[3]')) + call assert_equal(luaeval('l'), luaeval('l[3][3]')) lua l = nil endfunc diff --git a/src/version.c b/src/version.c index 0743833644..d152f9e7a1 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1066, /**/ 1065, /**/ From 0b1cd52ff6bf690388f892be686a91721a082e55 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 27 Jun 2020 13:11:50 +0200 Subject: [PATCH 022/105] patch 8.2.1067: expression "!expr->func()" does not work Problem: Expression "!expr->func()" does not work. Solution: Apply plus and minus earlier. (closes #6348) --- src/eval.c | 32 ++++++++++++++++++++------------ src/evalvars.c | 7 +++---- src/proto/eval.pro | 2 +- src/testdir/test_expr.vim | 7 +++++++ src/testdir/test_vim9_expr.vim | 4 ++++ src/userfunc.c | 2 +- src/version.c | 2 ++ 7 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/eval.c b/src/eval.c index 99003ced2c..4bbf65b036 100644 --- a/src/eval.c +++ b/src/eval.c @@ -51,7 +51,7 @@ static int eval4(char_u **arg, typval_T *rettv, evalarg_T *evalarg); static int eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg); static int eval6(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int want_string); static int eval7(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int want_string); -static int eval7_leader(typval_T *rettv, char_u *start_leader, char_u **end_leaderp); +static int eval7_leader(typval_T *rettv, int numeric_only, char_u *start_leader, char_u **end_leaderp); static int free_unref_items(int copyID); static char_u *make_expanded_name(char_u *in_start, char_u *expr_start, char_u *expr_end, char_u *in_end); @@ -2756,6 +2756,11 @@ eval7( case '8': case '9': case '.': ret = get_number_tv(arg, rettv, evaluate, want_string); + + // Apply prefixed "-" and "+" now. Matters especially when + // "->" follows. + if (ret == OK && evaluate && end_leader > start_leader) + ret = eval7_leader(rettv, TRUE, start_leader, &end_leader); break; /* @@ -2879,23 +2884,27 @@ eval7( // Handle following '[', '(' and '.' for expr[expr], expr.name, // expr(expr), expr->name(expr) if (ret == OK) - ret = handle_subscript(arg, rettv, flags, TRUE, - start_leader, &end_leader); + ret = handle_subscript(arg, rettv, flags, TRUE); /* * Apply logical NOT and unary '-', from right to left, ignore '+'. */ if (ret == OK && evaluate && end_leader > start_leader) - ret = eval7_leader(rettv, start_leader, &end_leader); + ret = eval7_leader(rettv, FALSE, start_leader, &end_leader); return ret; } /* * Apply the leading "!" and "-" before an eval7 expression to "rettv". + * When "numeric_only" is TRUE only handle "+" and "-". * Adjusts "end_leaderp" until it is at "start_leader". */ static int -eval7_leader(typval_T *rettv, char_u *start_leader, char_u **end_leaderp) +eval7_leader( + typval_T *rettv, + int numeric_only, + char_u *start_leader, + char_u **end_leaderp) { char_u *end_leader = *end_leaderp; int ret = OK; @@ -2921,6 +2930,11 @@ eval7_leader(typval_T *rettv, char_u *start_leader, char_u **end_leaderp) --end_leader; if (*end_leader == '!') { + if (numeric_only) + { + ++end_leader; + break; + } #ifdef FEAT_FLOAT if (rettv->v_type == VAR_FLOAT) f = !f; @@ -4871,9 +4885,7 @@ handle_subscript( char_u **arg, typval_T *rettv, int flags, // do more than finding the end - int verbose, // give error messages - char_u *start_leader, // start of '!' and '-' prefixes - char_u **end_leaderp) // end of '!' and '-' prefixes + int verbose) // give error messages { int evaluate = flags & EVAL_EVALUATE; int ret = OK; @@ -4910,10 +4922,6 @@ handle_subscript( } else if (**arg == '-') { - // Expression "-1.0->method()" applies the leader "-" before - // applying ->. - if (evaluate && *end_leaderp > start_leader) - ret = eval7_leader(rettv, start_leader, end_leaderp); if (ret == OK) { if ((*arg)[2] == '{') diff --git a/src/evalvars.c b/src/evalvars.c index e64b44ed13..7de7c1d1ef 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -1125,8 +1125,8 @@ list_arg_vars(exarg_T *eap, char_u *arg, int *first) { // handle d.key, l[idx], f(expr) arg_subsc = arg; - if (handle_subscript(&arg, &tv, EVAL_EVALUATE, TRUE, - name, &name) == FAIL) + if (handle_subscript(&arg, &tv, EVAL_EVALUATE, TRUE) + == FAIL) error = TRUE; else { @@ -3341,8 +3341,7 @@ var_exists(char_u *var) if (n) { // handle d.key, l[idx], f(expr) - n = (handle_subscript(&var, &tv, EVAL_EVALUATE, - FALSE, name, &name) == OK); + n = (handle_subscript(&var, &tv, EVAL_EVALUATE, FALSE) == OK); if (n) clear_tv(&tv); } diff --git a/src/proto/eval.pro b/src/proto/eval.pro index 6ae770ce25..16ba95b823 100644 --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -53,7 +53,7 @@ int get_name_len(char_u **arg, char_u **alias, int evaluate, int verbose); char_u *find_name_end(char_u *arg, char_u **expr_start, char_u **expr_end, int flags); int eval_isnamec(int c); int eval_isnamec1(int c); -int handle_subscript(char_u **arg, typval_T *rettv, int flags, int verbose, char_u *start_leader, char_u **end_leaderp); +int handle_subscript(char_u **arg, typval_T *rettv, int flags, int verbose); int item_copy(typval_T *from, typval_T *to, int deep, int copyID); void echo_one(typval_T *rettv, int with_space, int *atstart, int *needclr); void ex_echo(exarg_T *eap); diff --git a/src/testdir/test_expr.vim b/src/testdir/test_expr.vim index ec73aeaae4..c5dd1f37c7 100644 --- a/src/testdir/test_expr.vim +++ b/src/testdir/test_expr.vim @@ -110,6 +110,13 @@ func Test_special_char() call assert_fails('echo "\') endfunc +func Test_method_with_prefix() + call assert_equal(1, !range(5)->empty()) + call assert_equal([0, 1, 2], --3->range()) + call assert_equal(0, !-3) + call assert_equal(1, !+-+0) +endfunc + func Test_option_value() " boolean set bri diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index 33a282b061..0976d9cdb1 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -1081,6 +1081,8 @@ def Test_expr7_parens() assert_equal(6, --6) assert_equal(6, -+-6) assert_equal(-6, ---6) + assert_equal(false, !-3) + assert_equal(true, !+-+0) enddef def Test_expr7_negate() @@ -1102,6 +1104,8 @@ enddef def Test_expr7_call() assert_equal('yes', 'yes'->Echo()) assert_equal('yes', 'yes'->s:EchoArg()) + assert_equal(1, !range(5)->empty()) + assert_equal([0, 1, 2], --3->range()) call CheckDefFailure(["let x = 'yes'->Echo"], 'E107:') enddef diff --git a/src/userfunc.c b/src/userfunc.c index 691c55c4fd..537c9ccd0f 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -3926,7 +3926,7 @@ ex_call(exarg_T *eap) // Handle a function returning a Funcref, Dictionary or List. if (handle_subscript(&arg, &rettv, eap->skip ? 0 : EVAL_EVALUATE, - TRUE, name, &name) == FAIL) + TRUE) == FAIL) { failed = TRUE; break; diff --git a/src/version.c b/src/version.c index d152f9e7a1..0ab2b6ab43 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1067, /**/ 1066, /**/ From 8ea9390b78da9e34a20e7418712921397c0c1989 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 27 Jun 2020 14:11:53 +0200 Subject: [PATCH 023/105] patch 8.2.1068: Vim9: no line break allowed inside a dict Problem: Vim9: no line break allowed inside a dict. Solution: Handle line break inside a dict in Vim9 script. --- src/dict.c | 45 ++++++++++++++++++++++++++-------- src/eval.c | 4 +-- src/proto/dict.pro | 2 +- src/testdir/test_vim9_expr.vim | 40 ++++++++++++++++++++++++++++++ src/version.c | 2 ++ src/vim9compile.c | 4 +-- 6 files changed, 82 insertions(+), 15 deletions(-) diff --git a/src/dict.c b/src/dict.c index caa398deba..748a9917eb 100644 --- a/src/dict.c +++ b/src/dict.c @@ -792,10 +792,10 @@ get_literal_key(char_u **arg, typval_T *tv) * Return OK or FAIL. Returns NOTDONE for {expr}. */ int -eval_dict(char_u **arg, typval_T *rettv, int flags, int literal) +eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal) { - int evaluate = flags & EVAL_EVALUATE; - evalarg_T evalarg; + int evaluate = evalarg == NULL ? FALSE + : evalarg->eval_flags & EVAL_EVALUATE; dict_T *d = NULL; typval_T tvkey; typval_T tv; @@ -804,9 +804,8 @@ eval_dict(char_u **arg, typval_T *rettv, int flags, int literal) char_u *start = skipwhite(*arg + 1); char_u buf[NUMBUFLEN]; int vim9script = current_sctx.sc_version == SCRIPT_VERSION_VIM9; - - CLEAR_FIELD(evalarg); - evalarg.eval_flags = flags; + int had_comma; + int getnext; /* * First check if it's not a curly-braces thing: {expr}. @@ -833,11 +832,14 @@ eval_dict(char_u **arg, typval_T *rettv, int flags, int literal) tv.v_type = VAR_UNKNOWN; *arg = skipwhite(*arg + 1); + eval_next_non_blank(*arg, evalarg, &getnext); + if (getnext) + *arg = eval_next_line(evalarg); while (**arg != '}' && **arg != NUL) { if ((literal ? get_literal_key(arg, &tvkey) - : eval1(arg, &tvkey, &evalarg)) == FAIL) // recursive! + : eval1(arg, &tvkey, evalarg)) == FAIL) // recursive! goto failret; if (**arg != ':') @@ -857,9 +859,17 @@ eval_dict(char_u **arg, typval_T *rettv, int flags, int literal) goto failret; } } + if (vim9script && (*arg)[1] != NUL && !VIM_ISWHITE((*arg)[1])) + { + semsg(_(e_white_after), ":"); + goto failret; + } *arg = skipwhite(*arg + 1); - if (eval1(arg, &tv, &evalarg) == FAIL) // recursive! + eval_next_non_blank(*arg, evalarg, &getnext); + if (getnext) + *arg = eval_next_line(evalarg); + if (eval1(arg, &tv, evalarg) == FAIL) // recursive! { if (evaluate) clear_tv(&tvkey); @@ -887,15 +897,30 @@ eval_dict(char_u **arg, typval_T *rettv, int flags, int literal) } clear_tv(&tvkey); + // the comma must come after the value + had_comma = **arg == ','; + if (had_comma) + { + if (vim9script && (*arg)[1] != NUL && !VIM_ISWHITE((*arg)[1])) + { + semsg(_(e_white_after), ","); + goto failret; + } + *arg = skipwhite(*arg + 1); + } + + // the "}" can be on the next line + eval_next_non_blank(*arg, evalarg, &getnext); + if (getnext) + *arg = eval_next_line(evalarg); if (**arg == '}') break; - if (**arg != ',') + if (!had_comma) { if (evaluate) semsg(_(e_missing_dict_comma), *arg); goto failret; } - *arg = skipwhite(*arg + 1); } if (**arg != '}') diff --git a/src/eval.c b/src/eval.c index 4bbf65b036..0c8ab49c7f 100644 --- a/src/eval.c +++ b/src/eval.c @@ -2787,7 +2787,7 @@ eval7( case '#': if ((*arg)[1] == '{') { ++*arg; - ret = eval_dict(arg, rettv, flags, TRUE); + ret = eval_dict(arg, rettv, evalarg, TRUE); } else ret = NOTDONE; @@ -2799,7 +2799,7 @@ eval7( */ case '{': ret = get_lambda_tv(arg, rettv, evaluate); if (ret == NOTDONE) - ret = eval_dict(arg, rettv, flags, FALSE); + ret = eval_dict(arg, rettv, evalarg, FALSE); break; /* diff --git a/src/proto/dict.pro b/src/proto/dict.pro index 45bcfbf3a9..093139f429 100644 --- a/src/proto/dict.pro +++ b/src/proto/dict.pro @@ -32,7 +32,7 @@ varnumber_T dict_get_number(dict_T *d, char_u *key); varnumber_T dict_get_number_def(dict_T *d, char_u *key, int def); varnumber_T dict_get_number_check(dict_T *d, char_u *key); char_u *dict2string(typval_T *tv, int copyID, int restore_copyID); -int eval_dict(char_u **arg, typval_T *rettv, int evaluate, int literal); +int eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal); void dict_extend(dict_T *d1, dict_T *d2, char_u *action); dictitem_T *dict_lookup(hashitem_T *hi); int dict_equal(dict_T *d1, dict_T *d2, int ic, int recursive); diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index 0976d9cdb1..30cf1b995b 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -1002,6 +1002,12 @@ def Test_expr7_list_vim9script() assert_equal([11, 22], l) END CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + let l = [11,22] + END + CheckScriptFailure(lines, 'E1069:') enddef def Test_expr7_lambda() @@ -1034,6 +1040,40 @@ def Test_expr7_dict() call CheckDefExecFailure(["let x = g:dict_empty.member"], 'E716:') enddef +def Test_expr7_dict_vim9script() + let lines =<< trim END + vim9script + let d = { + 'one': + 1, + 'two': 2, + } + assert_equal({'one': 1, 'two': 2}, d) + END + CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + let d = #{one: 1, + two: 2, + } + assert_equal({'one': 1, 'two': 2}, d) + END + CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + let d = #{one:1, two: 2} + END + CheckScriptFailure(lines, 'E1069:') + + lines =<< trim END + vim9script + let d = #{one: 1,two: 2} + END + CheckScriptFailure(lines, 'E1069:') +enddef + def Test_expr_member() assert_equal(1, g:dict_one.one) let d: dict = g:dict_one diff --git a/src/version.c b/src/version.c index 0ab2b6ab43..f73afd8a1a 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1068, /**/ 1067, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index f133a94b73..5d03e0f92c 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2996,7 +2996,7 @@ to_name_const_end(char_u *arg) { // Can be "#{a: 1}->Func()". ++p; - if (eval_dict(&p, &rettv, 0, TRUE) == FAIL) + if (eval_dict(&p, &rettv, NULL, TRUE) == FAIL) p = arg; } else if (p == arg && *arg == '{') @@ -3006,7 +3006,7 @@ to_name_const_end(char_u *arg) // Can be "{x -> ret}()". // Can be "{'a': 1}->Func()". if (ret == NOTDONE) - ret = eval_dict(&p, &rettv, 0, FALSE); + ret = eval_dict(&p, &rettv, NULL, FALSE); if (ret != OK) p = arg; } From e6e031739c9d0c4140e371031b58a249db0eb572 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 27 Jun 2020 16:36:05 +0200 Subject: [PATCH 024/105] patch 8.2.1069: Vim9: fail to check for white space in list Problem: Vim9: fail to check for white space in list. Solution: Add check for white space. --- src/list.c | 10 +++++++++- src/version.c | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/list.c b/src/list.c index 611171d1dc..c2c485652f 100644 --- a/src/list.c +++ b/src/list.c @@ -1168,6 +1168,7 @@ get_list_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int do_error) list_T *l = NULL; typval_T tv; listitem_T *item; + int vim9script = current_sctx.sc_version == SCRIPT_VERSION_VIM9; int had_comma; if (evaluate) @@ -1198,10 +1199,17 @@ get_list_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int do_error) clear_tv(&tv); } - // the comma must comma after the value + // the comma must come after the value had_comma = **arg == ','; if (had_comma) + { + if (vim9script && (*arg)[1] != NUL && !VIM_ISWHITE((*arg)[1])) + { + semsg(_(e_white_after), ","); + goto failret; + } *arg = skipwhite(*arg + 1); + } // the "]" can be on the next line eval_next_non_blank(*arg, evalarg, &getnext); diff --git a/src/version.c b/src/version.c index f73afd8a1a..8cef53ef76 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1069, /**/ 1068, /**/ From ab19d495fd880b25a38d58cbeb5b21e4d0ee5835 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 27 Jun 2020 17:04:05 +0200 Subject: [PATCH 025/105] patch 8.2.1070: Vim9: leaking memory when lacking white space in dict Problem: Vim9: leaking memory when lacking white space in dict. Solution: Clear the typval. --- src/dict.c | 1 + src/version.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/dict.c b/src/dict.c index 748a9917eb..be3282d37d 100644 --- a/src/dict.c +++ b/src/dict.c @@ -862,6 +862,7 @@ eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal) if (vim9script && (*arg)[1] != NUL && !VIM_ISWHITE((*arg)[1])) { semsg(_(e_white_after), ":"); + clear_tv(&tvkey); goto failret; } diff --git a/src/version.c b/src/version.c index 8cef53ef76..a2fadfed64 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1070, /**/ 1069, /**/ From e40fbc2ca9fda07332a4da5af1fcaba91bed865b Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 27 Jun 2020 18:06:45 +0200 Subject: [PATCH 026/105] patch 8.2.1071: Vim9: no line break allowed inside a lambda Problem: Vim9: no line break allowed inside a lambda. Solution: Handle line break inside a lambda in Vim9 script. --- src/eval.c | 137 +++++++++++++++++++++++---------- src/evalvars.c | 6 +- src/ex_eval.c | 1 + src/globals.h | 6 +- src/popupwin.c | 2 +- src/proto/eval.pro | 3 +- src/proto/userfunc.pro | 4 +- src/structs.h | 5 ++ src/testdir/test_vim9_expr.vim | 12 +++ src/userfunc.c | 32 ++++++-- src/version.c | 2 + src/vim9compile.c | 6 +- 12 files changed, 160 insertions(+), 56 deletions(-) diff --git a/src/eval.c b/src/eval.c index 0c8ab49c7f..e1a33df608 100644 --- a/src/eval.c +++ b/src/eval.c @@ -325,8 +325,7 @@ eval_to_string_skip( if (skip) ++emsg_skip; - if (eval0(arg, &tv, eap, skip ? NULL : &EVALARG_EVALUATE) - == FAIL || skip) + if (eval0(arg, &tv, eap, skip ? NULL : &EVALARG_EVALUATE) == FAIL || skip) retval = NULL; else { @@ -352,6 +351,61 @@ skip_expr(char_u **pp) return eval1(pp, &rettv, NULL); } +/* + * Skip over an expression at "*pp". + * If in Vim9 script and line breaks are encountered, the lines are + * concatenated. "evalarg->eval_tofree" will be set accordingly. + * Return FAIL for an error, OK otherwise. + */ + int +skip_expr_concatenate(char_u **start, char_u **end, evalarg_T *evalarg) +{ + typval_T rettv; + int res; + int vim9script = current_sctx.sc_version == SCRIPT_VERSION_VIM9; + garray_T *gap = &evalarg->eval_ga; + int save_flags = evalarg == NULL ? 0 : evalarg->eval_flags; + + if (vim9script && evalarg->eval_cookie != NULL) + { + ga_init2(gap, sizeof(char_u *), 10); + if (ga_grow(gap, 1) == OK) + // leave room for "start" + ++gap->ga_len; + } + + // Don't evaluate the expression. + if (evalarg != NULL) + evalarg->eval_flags &= ~EVAL_EVALUATE; + *end = skipwhite(*end); + res = eval1(end, &rettv, evalarg); + if (evalarg != NULL) + evalarg->eval_flags = save_flags; + + if (vim9script && evalarg->eval_cookie != NULL + && evalarg->eval_ga.ga_len > 1) + { + char_u *p; + size_t endoff = STRLEN(*end); + + // Line breaks encountered, concatenate all the lines. + *((char_u **)gap->ga_data) = *start; + p = ga_concat_strings(gap, ""); + *((char_u **)gap->ga_data) = NULL; + ga_clear_strings(gap); + gap->ga_itemsize = 0; + if (p == NULL) + return FAIL; + *start = p; + vim_free(evalarg->eval_tofree); + evalarg->eval_tofree = p; + // Compute "end" relative to the end. + *end = *start + STRLEN(*start) - endoff; + } + + return res; +} + /* * Top level evaluation function, returning a string. * When "convert" is TRUE convert a List into a sequence of lines and convert @@ -1794,14 +1848,27 @@ eval_next_non_blank(char_u *arg, evalarg_T *evalarg, int *getnext) } /* - * To be called when eval_next_non_blank() sets "getnext" to TRUE. + * To be called after eval_next_non_blank() sets "getnext" to TRUE. */ char_u * eval_next_line(evalarg_T *evalarg) { - vim_free(evalarg->eval_tofree); - evalarg->eval_tofree = getsourceline(0, evalarg->eval_cookie, 0, TRUE); - return skipwhite(evalarg->eval_tofree); + garray_T *gap = &evalarg->eval_ga; + char_u *line; + + line = getsourceline(0, evalarg->eval_cookie, 0, TRUE); + if (gap->ga_itemsize > 0 && ga_grow(gap, 1) == OK) + { + // Going to concatenate the lines after parsing. + ((char_u **)gap->ga_data)[gap->ga_len] = line; + ++gap->ga_len; + } + else + { + vim_free(evalarg->eval_tofree); + evalarg->eval_tofree = line; + } + return skipwhite(line); } /* @@ -1831,8 +1898,6 @@ eval0( int called_emsg_before = called_emsg; int flags = evalarg == NULL ? 0 : evalarg->eval_flags; - if (evalarg != NULL) - evalarg->eval_tofree = NULL; p = skipwhite(arg); ret = eval1(&p, rettv, evalarg); @@ -1857,22 +1922,15 @@ eval0( if (eap != NULL) eap->nextcmd = check_nextcmd(p); - if (evalarg != NULL) + if (evalarg != NULL && eap != NULL && evalarg->eval_tofree != NULL) { - if (eap != NULL) - { - if (evalarg->eval_tofree != NULL) - { - // We may need to keep the original command line, e.g. for - // ":let" it has the variable names. But we may also need the - // new one, "nextcmd" points into it. Keep both. - vim_free(eap->cmdline_tofree); - eap->cmdline_tofree = *eap->cmdlinep; - *eap->cmdlinep = evalarg->eval_tofree; - } - } - else - vim_free(evalarg->eval_tofree); + // We may need to keep the original command line, e.g. for + // ":let" it has the variable names. But we may also need the + // new one, "nextcmd" points into it. Keep both. + vim_free(eap->cmdline_tofree); + eap->cmdline_tofree = *eap->cmdlinep; + *eap->cmdlinep = evalarg->eval_tofree; + evalarg->eval_tofree = NULL; } return ret; @@ -2797,7 +2855,7 @@ eval7( * Lambda: {arg, arg -> expr} * Dictionary: {'key': val, 'key': val} */ - case '{': ret = get_lambda_tv(arg, rettv, evaluate); + case '{': ret = get_lambda_tv(arg, rettv, evalarg); if (ret == NOTDONE) ret = eval_dict(arg, rettv, evalarg, FALSE); break; @@ -2884,7 +2942,7 @@ eval7( // Handle following '[', '(' and '.' for expr[expr], expr.name, // expr(expr), expr->name(expr) if (ret == OK) - ret = handle_subscript(arg, rettv, flags, TRUE); + ret = handle_subscript(arg, rettv, evalarg, TRUE); /* * Apply logical NOT and unary '-', from right to left, ignore '+'. @@ -3031,9 +3089,11 @@ call_func_rettv( eval_lambda( char_u **arg, typval_T *rettv, - int evaluate, + evalarg_T *evalarg, int verbose) // give error messages { + int evaluate = evalarg != NULL + && (evalarg->eval_flags & EVAL_EVALUATE); typval_T base = *rettv; int ret; @@ -3041,7 +3101,7 @@ eval_lambda( *arg += 2; rettv->v_type = VAR_UNKNOWN; - ret = get_lambda_tv(arg, rettv, evaluate); + ret = get_lambda_tv(arg, rettv, evalarg); if (ret != OK) return FAIL; else if (**arg != '(') @@ -3136,10 +3196,11 @@ eval_method( eval_index( char_u **arg, typval_T *rettv, - int flags, + evalarg_T *evalarg, int verbose) // give error messages { - int evaluate = flags & EVAL_EVALUATE; + int evaluate = evalarg != NULL + && (evalarg->eval_flags & EVAL_EVALUATE); int empty1 = FALSE, empty2 = FALSE; typval_T var1, var2; long i; @@ -3200,11 +3261,6 @@ eval_index( } else { - evalarg_T evalarg; - - CLEAR_FIELD(evalarg); - evalarg.eval_flags = flags; - /* * something[idx] * @@ -3213,7 +3269,7 @@ eval_index( *arg = skipwhite(*arg + 1); if (**arg == ':') empty1 = TRUE; - else if (eval1(arg, &var1, &evalarg) == FAIL) // recursive! + else if (eval1(arg, &var1, evalarg) == FAIL) // recursive! return FAIL; else if (evaluate && tv_get_string_chk(&var1) == NULL) { @@ -3231,7 +3287,7 @@ eval_index( *arg = skipwhite(*arg + 1); if (**arg == ']') empty2 = TRUE; - else if (eval1(arg, &var2, &evalarg) == FAIL) // recursive! + else if (eval1(arg, &var2, evalarg) == FAIL) // recursive! { if (!empty1) clear_tv(&var1); @@ -4884,10 +4940,11 @@ eval_isnamec1(int c) handle_subscript( char_u **arg, typval_T *rettv, - int flags, // do more than finding the end + evalarg_T *evalarg, int verbose) // give error messages { - int evaluate = flags & EVAL_EVALUATE; + int evaluate = evalarg != NULL + && (evalarg->eval_flags & EVAL_EVALUATE); int ret = OK; dict_T *selfdict = NULL; @@ -4926,7 +4983,7 @@ handle_subscript( { if ((*arg)[2] == '{') // expr->{lambda}() - ret = eval_lambda(arg, rettv, evaluate, verbose); + ret = eval_lambda(arg, rettv, evalarg, verbose); else // expr->name() ret = eval_method(arg, rettv, evaluate, verbose); @@ -4943,7 +5000,7 @@ handle_subscript( } else selfdict = NULL; - if (eval_index(arg, rettv, flags, verbose) == FAIL) + if (eval_index(arg, rettv, evalarg, verbose) == FAIL) { clear_tv(rettv); ret = FAIL; diff --git a/src/evalvars.c b/src/evalvars.c index 7de7c1d1ef..0acbd7bcf1 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -797,12 +797,14 @@ ex_let(exarg_T *eap) if (eap->skip) ++emsg_skip; + CLEAR_FIELD(evalarg); evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE; evalarg.eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL; i = eval0(expr, &rettv, eap, &evalarg); if (eap->skip) --emsg_skip; + vim_free(evalarg.eval_tofree); } if (eap->skip) { @@ -1125,7 +1127,7 @@ list_arg_vars(exarg_T *eap, char_u *arg, int *first) { // handle d.key, l[idx], f(expr) arg_subsc = arg; - if (handle_subscript(&arg, &tv, EVAL_EVALUATE, TRUE) + if (handle_subscript(&arg, &tv, &EVALARG_EVALUATE, TRUE) == FAIL) error = TRUE; else @@ -3341,7 +3343,7 @@ var_exists(char_u *var) if (n) { // handle d.key, l[idx], f(expr) - n = (handle_subscript(&var, &tv, EVAL_EVALUATE, FALSE) == OK); + n = (handle_subscript(&var, &tv, &EVALARG_EVALUATE, FALSE) == OK); if (n) clear_tv(&tv); } diff --git a/src/ex_eval.c b/src/ex_eval.c index 8b8a256957..6f6b8c2189 100644 --- a/src/ex_eval.c +++ b/src/ex_eval.c @@ -897,6 +897,7 @@ ex_eval(exarg_T *eap) typval_T tv; evalarg_T evalarg; + CLEAR_FIELD(evalarg); evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE; evalarg.eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL; diff --git a/src/globals.h b/src/globals.h index 03a6937561..81903818b0 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1883,7 +1883,11 @@ EXTERN char windowsVersion[20] INIT(= {0}); EXTERN listitem_T range_list_item; // Passed to an eval() function to enable evaluation. -EXTERN evalarg_T EVALARG_EVALUATE INIT3(EVAL_EVALUATE, NULL, NULL); +EXTERN evalarg_T EVALARG_EVALUATE +# ifdef DO_INIT + = {EVAL_EVALUATE, NULL, {0, 0, 0, 0, NULL}, NULL} +# endif + ; #endif #ifdef MSWIN diff --git a/src/popupwin.c b/src/popupwin.c index d144be73fe..3461313ec0 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -384,7 +384,7 @@ popup_add_timeout(win_T *wp, int time) vim_snprintf((char *)cbbuf, sizeof(cbbuf), "{_ -> popup_close(%d)}", wp->w_id); - if (get_lambda_tv(&ptr, &tv, TRUE) == OK) + if (get_lambda_tv(&ptr, &tv, &EVALARG_EVALUATE) == OK) { wp->w_popup_timer = create_timer(time, 0); wp->w_popup_timer->tr_callback = get_callback(&tv); diff --git a/src/proto/eval.pro b/src/proto/eval.pro index 16ba95b823..c66e331ce2 100644 --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -9,6 +9,7 @@ int eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv); int eval_expr_to_bool(typval_T *expr, int *error); char_u *eval_to_string_skip(char_u *arg, exarg_T *eap, int skip); int skip_expr(char_u **pp); +int skip_expr_concatenate(char_u **start, char_u **end, evalarg_T *evalarg); char_u *eval_to_string(char_u *arg, int convert); char_u *eval_to_string_safe(char_u *arg, int use_sandbox); varnumber_T eval_to_number(char_u *expr); @@ -53,7 +54,7 @@ int get_name_len(char_u **arg, char_u **alias, int evaluate, int verbose); char_u *find_name_end(char_u *arg, char_u **expr_start, char_u **expr_end, int flags); int eval_isnamec(int c); int eval_isnamec1(int c); -int handle_subscript(char_u **arg, typval_T *rettv, int flags, int verbose); +int handle_subscript(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose); int item_copy(typval_T *from, typval_T *to, int deep, int copyID); void echo_one(typval_T *rettv, int with_space, int *atstart, int *needclr); void ex_echo(exarg_T *eap); diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro index 340ef57f1c..f69f77d6a4 100644 --- a/src/proto/userfunc.pro +++ b/src/proto/userfunc.pro @@ -3,8 +3,8 @@ void func_init(void); hashtab_T *func_tbl_get(void); int get_function_args(char_u **argp, char_u endchar, garray_T *newargs, garray_T *argtypes, int *varargs, garray_T *default_args, int skip, exarg_T *eap, char_u **line_to_free); char_u *get_lambda_name(void); -int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate); -char_u *register_cfunc(cfunc_T cb, cfunc_free_T free_cb, void *state); +char_u *register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state); +int get_lambda_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg); char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload); void emsg_funcname(char *ermsg, char_u *name); int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, funcexe_T *funcexe); diff --git a/src/structs.h b/src/structs.h index e308ff448d..d68bc0184e 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1763,6 +1763,11 @@ typedef struct { // copied from exarg_T when "getline" is "getsourceline". Can be NULL. void *eval_cookie; // argument for getline() + // Used to collect lines while parsing them, so that they can be + // concatenated later. Used when "eval_ga.ga_itemsize" is not zero. + // "eval_ga.ga_data" is a list of pointers to lines. + garray_T eval_ga; + // pointer to the line obtained with getsourceline() char_u *eval_tofree; } evalarg_T; diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index 30cf1b995b..0a883f9306 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -1017,6 +1017,18 @@ def Test_expr7_lambda() assert_equal([1, 3, 5], [1, 2, 3]->map({key, val -> key + val})) enddef +def Test_expr7_lambda_vim9script() + let lines =<< trim END + vim9script + let v = 10->{a -> + a + + 2 + }() + assert_equal(12, v) + END + CheckScriptSuccess(lines) +enddef + def Test_expr7_dict() " dictionary assert_equal(g:dict_empty, {}) diff --git a/src/userfunc.c b/src/userfunc.c index 537c9ccd0f..b358ecb381 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -391,8 +391,10 @@ errret: * Return OK or FAIL. Returns NOTDONE for dict or {expr}. */ int -get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate) +get_lambda_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg) { + int evaluate = evalarg != NULL + && (evalarg->eval_flags & EVAL_EVALUATE); garray_T newargs; garray_T newlines; garray_T *pnewargs; @@ -404,6 +406,8 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate) char_u *s, *e; int *old_eval_lavars = eval_lavars_used; int eval_lavars = FALSE; + int getnext; + char_u *tofree = NULL; ga_init(&newargs); ga_init(&newlines); @@ -432,12 +436,25 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate) // Get the start and the end of the expression. *arg = skipwhite(*arg + 1); + eval_next_non_blank(*arg, evalarg, &getnext); + if (getnext) + *arg = eval_next_line(evalarg); s = *arg; - ret = skip_expr(arg); + ret = skip_expr_concatenate(&s, arg, evalarg); if (ret == FAIL) goto errret; + if (evalarg != NULL) + { + // avoid that the expression gets freed when another line break follows + tofree = evalarg->eval_tofree; + evalarg->eval_tofree = NULL; + } + e = *arg; *arg = skipwhite(*arg); + eval_next_non_blank(*arg, evalarg, &getnext); + if (getnext) + *arg = eval_next_line(evalarg); if (**arg != '}') { semsg(_("E451: Expected }: %s"), *arg); @@ -447,7 +464,8 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate) if (evaluate) { - int len, flags = 0; + int len; + int flags = 0; char_u *p; char_u *name = get_lambda_name(); @@ -464,7 +482,7 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate) goto errret; // Add "return " before the expression. - len = 7 + e - s + 1; + len = 7 + (int)(e - s) + 1; p = alloc(len); if (p == NULL) goto errret; @@ -510,6 +528,7 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate) } eval_lavars_used = old_eval_lavars; + vim_free(tofree); return OK; errret: @@ -517,6 +536,7 @@ errret: ga_clear_strings(&newlines); vim_free(fp); vim_free(pt); + vim_free(tofree); eval_lavars_used = old_eval_lavars; return FAIL; } @@ -3925,8 +3945,8 @@ ex_call(exarg_T *eap) dbg_check_breakpoint(eap); // Handle a function returning a Funcref, Dictionary or List. - if (handle_subscript(&arg, &rettv, eap->skip ? 0 : EVAL_EVALUATE, - TRUE) == FAIL) + if (handle_subscript(&arg, &rettv, + eap->skip ? NULL : &EVALARG_EVALUATE, TRUE) == FAIL) { failed = TRUE; break; diff --git a/src/version.c b/src/version.c index a2fadfed64..6c8e5e0418 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1071, /**/ 1070, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 5d03e0f92c..d37c5c95d0 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -3001,7 +3001,7 @@ to_name_const_end(char_u *arg) } else if (p == arg && *arg == '{') { - int ret = get_lambda_tv(&p, &rettv, FALSE); + int ret = get_lambda_tv(&p, &rettv, NULL); // Can be "{x -> ret}()". // Can be "{'a': 1}->Func()". @@ -3065,7 +3065,7 @@ compile_lambda(char_u **arg, cctx_T *cctx) ufunc_T *ufunc; // Get the funcref in "rettv". - if (get_lambda_tv(arg, &rettv, TRUE) != OK) + if (get_lambda_tv(arg, &rettv, &EVALARG_EVALUATE) != OK) return FAIL; ufunc = rettv.vval.v_partial->pt_func; @@ -3095,7 +3095,7 @@ compile_lambda_call(char_u **arg, cctx_T *cctx) int ret = FAIL; // Get the funcref in "rettv". - if (get_lambda_tv(arg, &rettv, TRUE) == FAIL) + if (get_lambda_tv(arg, &rettv, &EVALARG_EVALUATE) == FAIL) return FAIL; if (**arg != '(') From a0a0c4147fd966e0cd923a2bbfb84b14b15a297e Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 27 Jun 2020 18:13:10 +0200 Subject: [PATCH 027/105] patch 8.2.1072: missing libvterm test Problem: Missing libvterm test. Solution: Sync with libvterm revision 768. --- src/libvterm/src/state.c | 8 ++++---- src/libvterm/t/63screen_resize.test | 16 ++++++++++++++++ src/version.c | 2 ++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/libvterm/src/state.c b/src/libvterm/src/state.c index 30438efe82..4fe3d7d20b 100644 --- a/src/libvterm/src/state.c +++ b/src/libvterm/src/state.c @@ -1844,14 +1844,14 @@ static int on_resize(int rows, int cols, void *user) state->pos.col++; } - if(state->pos.row >= rows) - state->pos.row = rows - 1; if(state->pos.row < 0) state->pos.row = 0; - if(state->pos.col >= cols) - state->pos.col = cols - 1; + if(state->pos.row >= rows) + state->pos.row = rows - 1; if(state->pos.col < 0) state->pos.col = 0; + if(state->pos.col >= cols) + state->pos.col = cols - 1; updatecursor(state, &oldpos, 1); diff --git a/src/libvterm/t/63screen_resize.test b/src/libvterm/t/63screen_resize.test index 87932f66d4..87b88d64e0 100644 --- a/src/libvterm/t/63screen_resize.test +++ b/src/libvterm/t/63screen_resize.test @@ -69,6 +69,22 @@ RESIZE 24,80 ?screen_chars 22,0,23,10 = "Line 25" ?cursor = 23,0 +!Resize shorter does not send the cursor to a negative row +# See also https://github.com/vim/vim/pull/6141 +RESET +WANTSCREEN -b +RESIZE 25,80 +WANTSCREEN b +PUSH "\e[24HLine 24\r\nLine 25\e[H" + ?cursor = 0,0 +RESIZE 20,80 + sb_pushline 80 = + sb_pushline 80 = + sb_pushline 80 = + sb_pushline 80 = + sb_pushline 80 = + ?cursor = 0,0 + !Resize taller attempts to pop scrollback RESET WANTSCREEN -b diff --git a/src/version.c b/src/version.c index 6c8e5e0418..51a93365f1 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1072, /**/ 1071, /**/ From 7a4981b93642b5b62018cd8150b3fb0dfa2417d4 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 27 Jun 2020 20:46:29 +0200 Subject: [PATCH 028/105] patch 8.2.1073: Vim9: no line break allowed in () expression Problem: Vim9: no line break allowed in () expression. Solution: Skip a line break. --- src/eval.c | 13 +++++++++++++ src/testdir/test_vim9_expr.vim | 13 +++++++++++++ src/version.c | 2 ++ 3 files changed, 28 insertions(+) diff --git a/src/eval.c b/src/eval.c index e1a33df608..86d81fa09e 100644 --- a/src/eval.c +++ b/src/eval.c @@ -2453,6 +2453,9 @@ eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg) if (op == '.' && *(*arg + 1) == '.') // .. string concatenation ++*arg; *arg = skipwhite(*arg + 1); + eval_next_non_blank(*arg, evalarg, &getnext); + if (getnext) + *arg = eval_next_line(evalarg); if (eval6(arg, &var2, evalarg, op == '.') == FAIL) { clear_tv(rettv); @@ -2890,8 +2893,18 @@ eval7( * nested expression: (expression). */ case '(': { + int getnext; + *arg = skipwhite(*arg + 1); + eval_next_non_blank(*arg, evalarg, &getnext); + if (getnext) + *arg = eval_next_line(evalarg); + ret = eval1(arg, rettv, evalarg); // recursive! + + eval_next_non_blank(*arg, evalarg, &getnext); + if (getnext) + *arg = eval_next_line(evalarg); if (**arg == ')') ++*arg; else if (ret == OK) diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index 0a883f9306..0d2a2b0bfb 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -1137,6 +1137,19 @@ def Test_expr7_parens() assert_equal(true, !+-+0) enddef +def Test_expr7_parens_vim9script() + let lines =<< trim END + vim9script + let s = ( + 'one' + .. + 'two' + ) + assert_equal('onetwo', s) + END + CheckScriptSuccess(lines) +enddef + def Test_expr7_negate() assert_equal(-99, -99) assert_equal(99, --99) diff --git a/src/version.c b/src/version.c index 51a93365f1..4184a69f35 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1073, /**/ 1072, /**/ From 9215f01218b2ed2cfe49c1f43fcf342bd9ffdded Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 27 Jun 2020 21:18:00 +0200 Subject: [PATCH 029/105] patch 8.2.1074: Vim9: no line break allowed after some operators Problem: Vim9: no line break allowed after some operators. Solution: Skip a line break after the operator. Add eval_may_get_next_line() to simplify checking for a line break. --- src/dict.c | 15 +++---------- src/eval.c | 39 +++++++++++++++++----------------- src/list.c | 10 ++------- src/proto/eval.pro | 1 + src/testdir/test_vim9_expr.vim | 35 ++++++++++++++++++++++++++++++ src/userfunc.c | 14 ++++-------- src/version.c | 2 ++ 7 files changed, 67 insertions(+), 49 deletions(-) diff --git a/src/dict.c b/src/dict.c index be3282d37d..079eb3e11e 100644 --- a/src/dict.c +++ b/src/dict.c @@ -805,7 +805,6 @@ eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal) char_u buf[NUMBUFLEN]; int vim9script = current_sctx.sc_version == SCRIPT_VERSION_VIM9; int had_comma; - int getnext; /* * First check if it's not a curly-braces thing: {expr}. @@ -831,10 +830,7 @@ eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal) tvkey.v_type = VAR_UNKNOWN; tv.v_type = VAR_UNKNOWN; - *arg = skipwhite(*arg + 1); - eval_next_non_blank(*arg, evalarg, &getnext); - if (getnext) - *arg = eval_next_line(evalarg); + *arg = skipwhite_and_linebreak(*arg + 1, evalarg); while (**arg != '}' && **arg != NUL) { if ((literal @@ -866,10 +862,7 @@ eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal) goto failret; } - *arg = skipwhite(*arg + 1); - eval_next_non_blank(*arg, evalarg, &getnext); - if (getnext) - *arg = eval_next_line(evalarg); + *arg = skipwhite_and_linebreak(*arg + 1, evalarg); if (eval1(arg, &tv, evalarg) == FAIL) // recursive! { if (evaluate) @@ -911,9 +904,7 @@ eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal) } // the "}" can be on the next line - eval_next_non_blank(*arg, evalarg, &getnext); - if (getnext) - *arg = eval_next_line(evalarg); + *arg = skipwhite_and_linebreak(*arg, evalarg); if (**arg == '}') break; if (!had_comma) diff --git a/src/eval.c b/src/eval.c index 86d81fa09e..ca98d2eb02 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1871,6 +1871,18 @@ eval_next_line(evalarg_T *evalarg) return skipwhite(line); } + char_u * +skipwhite_and_linebreak(char_u *arg, evalarg_T *evalarg) +{ + int getnext; + char_u *p = skipwhite(arg); + + eval_next_non_blank(p, evalarg, &getnext); + if (getnext) + return eval_next_line(evalarg); + return p; +} + /* * The "evaluate" argument: When FALSE, the argument is only parsed but not * executed. The function may return OK, but the rettv will be of type @@ -1998,7 +2010,7 @@ eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg) /* * Get the second variable. Recursive! */ - *arg = skipwhite(*arg + 1); + *arg = skipwhite_and_linebreak(*arg + 1, evalarg); nested_evalarg.eval_flags = result ? orig_flags : orig_flags & ~EVAL_EVALUATE; if (eval1(arg, rettv, &nested_evalarg) == FAIL) @@ -2021,7 +2033,7 @@ eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg) /* * Get the third variable. Recursive! */ - *arg = skipwhite(*arg + 1); + *arg = skipwhite_and_linebreak(*arg + 1, evalarg); nested_evalarg.eval_flags = !result ? orig_flags : orig_flags & ~EVAL_EVALUATE; if (eval1(arg, &var2, &nested_evalarg) == FAIL) @@ -2103,7 +2115,7 @@ eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg) /* * Get the second variable. */ - *arg = skipwhite(*arg + 2); + *arg = skipwhite_and_linebreak(*arg + 2, evalarg); nested_evalarg.eval_flags = !result ? orig_flags : orig_flags & ~EVAL_EVALUATE; if (eval3(arg, &var2, &nested_evalarg) == FAIL) @@ -2197,7 +2209,7 @@ eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg) /* * Get the second variable. */ - *arg = skipwhite(*arg + 2); + *arg = skipwhite_and_linebreak(*arg + 2, evalarg); nested_evalarg.eval_flags = result ? orig_flags : orig_flags & ~EVAL_EVALUATE; if (eval4(arg, &var2, &nested_evalarg) == FAIL) @@ -2328,7 +2340,7 @@ eval4(char_u **arg, typval_T *rettv, evalarg_T *evalarg) /* * Get the second variable. */ - *arg = skipwhite(p + len); + *arg = skipwhite_and_linebreak(p + len, evalarg); if (eval5(arg, &var2, evalarg) == FAIL) { clear_tv(rettv); @@ -2452,10 +2464,7 @@ eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg) */ if (op == '.' && *(*arg + 1) == '.') // .. string concatenation ++*arg; - *arg = skipwhite(*arg + 1); - eval_next_non_blank(*arg, evalarg, &getnext); - if (getnext) - *arg = eval_next_line(evalarg); + *arg = skipwhite_and_linebreak(*arg + 1, evalarg); if (eval6(arg, &var2, evalarg, op == '.') == FAIL) { clear_tv(rettv); @@ -2893,18 +2902,10 @@ eval7( * nested expression: (expression). */ case '(': { - int getnext; - - *arg = skipwhite(*arg + 1); - eval_next_non_blank(*arg, evalarg, &getnext); - if (getnext) - *arg = eval_next_line(evalarg); - + *arg = skipwhite_and_linebreak(*arg + 1, evalarg); ret = eval1(arg, rettv, evalarg); // recursive! - eval_next_non_blank(*arg, evalarg, &getnext); - if (getnext) - *arg = eval_next_line(evalarg); + *arg = skipwhite_and_linebreak(*arg, evalarg); if (**arg == ')') ++*arg; else if (ret == OK) diff --git a/src/list.c b/src/list.c index c2c485652f..56ed5fcdb6 100644 --- a/src/list.c +++ b/src/list.c @@ -1164,7 +1164,6 @@ get_list_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int do_error) { int evaluate = evalarg == NULL ? FALSE : evalarg->eval_flags & EVAL_EVALUATE; - int getnext; list_T *l = NULL; typval_T tv; listitem_T *item; @@ -1178,10 +1177,7 @@ get_list_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int do_error) return FAIL; } - *arg = skipwhite(*arg + 1); - eval_next_non_blank(*arg, evalarg, &getnext); - if (getnext) - *arg = eval_next_line(evalarg); + *arg = skipwhite_and_linebreak(*arg + 1, evalarg); while (**arg != ']' && **arg != NUL) { if (eval1(arg, &tv, evalarg) == FAIL) // recursive! @@ -1212,9 +1208,7 @@ get_list_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int do_error) } // the "]" can be on the next line - eval_next_non_blank(*arg, evalarg, &getnext); - if (getnext) - *arg = eval_next_line(evalarg); + *arg = skipwhite_and_linebreak(*arg, evalarg); if (**arg == ']') break; diff --git a/src/proto/eval.pro b/src/proto/eval.pro index c66e331ce2..9170385918 100644 --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -29,6 +29,7 @@ void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx); int pattern_match(char_u *pat, char_u *text, int ic); char_u *eval_next_non_blank(char_u *arg, evalarg_T *evalarg, int *getnext); char_u *eval_next_line(evalarg_T *evalarg); +char_u *skipwhite_and_linebreak(char_u *arg, evalarg_T *evalarg); int eval0(char_u *arg, typval_T *rettv, exarg_T *eap, evalarg_T *evalarg); int eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg); void eval_addblob(typval_T *tv1, typval_T *tv2); diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index 0d2a2b0bfb..bf4f7d2eaf 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -64,6 +64,15 @@ def Test_expr1_vimscript() assert_equal('no', var) END CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + let var = v:false ? + 'yes' : + 'no' + assert_equal('no', var) + END + CheckScriptSuccess(lines) enddef func Test_expr1_fails() @@ -135,6 +144,15 @@ def Test_expr2_vimscript() assert_equal(1, var) END CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + let var = v:false || + v:true || + v:false + assert_equal(1, var) + END + CheckScriptSuccess(lines) enddef func Test_expr2_fails() @@ -198,6 +216,15 @@ def Test_expr3_vimscript() assert_equal(1, var) END CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + let var = v:true && + v:true && + v:true + assert_equal(1, var) + END + CheckScriptSuccess(lines) enddef func Test_expr3_fails() @@ -547,6 +574,14 @@ def Test_expr4_vimscript() END CheckScriptSuccess(lines) + lines =<< trim END + vim9script + let var = 123 == + 123 + assert_equal(1, var) + END + CheckScriptSuccess(lines) + lines =<< trim END vim9script let list = [1, 2, 3] diff --git a/src/userfunc.c b/src/userfunc.c index b358ecb381..51c437f208 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -402,17 +402,17 @@ get_lambda_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg) partial_T *pt = NULL; int varargs; int ret; - char_u *start = skipwhite(*arg + 1); + char_u *start; char_u *s, *e; int *old_eval_lavars = eval_lavars_used; int eval_lavars = FALSE; - int getnext; char_u *tofree = NULL; ga_init(&newargs); ga_init(&newlines); // First, check if this is a lambda expression. "->" must exist. + start = skipwhite(*arg + 1); ret = get_function_args(&start, '-', NULL, NULL, NULL, NULL, TRUE, NULL, NULL); if (ret == FAIL || *start != '>') @@ -435,10 +435,7 @@ get_lambda_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg) eval_lavars_used = &eval_lavars; // Get the start and the end of the expression. - *arg = skipwhite(*arg + 1); - eval_next_non_blank(*arg, evalarg, &getnext); - if (getnext) - *arg = eval_next_line(evalarg); + *arg = skipwhite_and_linebreak(*arg + 1, evalarg); s = *arg; ret = skip_expr_concatenate(&s, arg, evalarg); if (ret == FAIL) @@ -451,10 +448,7 @@ get_lambda_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg) } e = *arg; - *arg = skipwhite(*arg); - eval_next_non_blank(*arg, evalarg, &getnext); - if (getnext) - *arg = eval_next_line(evalarg); + *arg = skipwhite_and_linebreak(*arg, evalarg); if (**arg != '}') { semsg(_("E451: Expected }: %s"), *arg); diff --git a/src/version.c b/src/version.c index 4184a69f35..dc05521890 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1074, /**/ 1073, /**/ From 7e8967fdcdf45caf08753bb791dc3779e78b34c8 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 27 Jun 2020 21:56:17 +0200 Subject: [PATCH 030/105] patch 8.2.1075: Vim9: no line break allowed in :echo expression Problem: Vim9: no line break allowed in :echo expression. Solution: Skip linebreak. --- src/eval.c | 1 + src/testdir/test_vim9_cmd.vim | 23 +++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 26 insertions(+) diff --git a/src/eval.c b/src/eval.c index ca98d2eb02..736023930b 100644 --- a/src/eval.c +++ b/src/eval.c @@ -5190,6 +5190,7 @@ ex_echo(exarg_T *eap) CLEAR_FIELD(evalarg); evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE; + evalarg.eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL; if (eap->skip) ++emsg_skip; diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim index 55d756a676..2d5bf4516e 100644 --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -78,5 +78,28 @@ def Test_assign_dict() assert_equal({'0': 0, '1': 1, '2': 2}, nrd) enddef +def Test_echo_linebreak() + let lines =<< trim END + vim9script + redir @a + echo 'one' + .. 'two' + redir END + assert_equal("\nonetwo", @a) + END + CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + redir @a + echo 11 + + 77 + - 22 + redir END + assert_equal("\n66", @a) + END + CheckScriptSuccess(lines) +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index dc05521890..c304fe5ccf 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1075, /**/ 1074, /**/ From faf8626b79e380fe81e7ae2439a535ed7619d27b Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 27 Jun 2020 23:07:36 +0200 Subject: [PATCH 031/105] patch 8.2.1076: Vim9: no line break allowed in :if expression Problem: Vim9: no line break allowed in :if expression. Solution: Skip linebreak. --- src/eval.c | 39 +++++++++++++++++++++++---------- src/evalvars.c | 2 +- src/proto/eval.pro | 1 + src/testdir/test_vim9_cmd.vim | 41 +++++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 5 files changed, 73 insertions(+), 12 deletions(-) diff --git a/src/eval.c b/src/eval.c index 736023930b..6aaa3a6ef0 100644 --- a/src/eval.c +++ b/src/eval.c @@ -166,10 +166,16 @@ eval_to_bool( { typval_T tv; varnumber_T retval = FALSE; + evalarg_T evalarg; + + CLEAR_FIELD(evalarg); + evalarg.eval_flags = skip ? 0 : EVAL_EVALUATE; + evalarg.eval_cookie = eap != NULL && eap->getline == getsourceline + ? eap->cookie : NULL; if (skip) ++emsg_skip; - if (eval0(arg, &tv, eap, skip ? NULL : &EVALARG_EVALUATE) == FAIL) + if (eval0(arg, &tv, eap, &evalarg) == FAIL) *error = TRUE; else { @@ -182,6 +188,7 @@ eval_to_bool( } if (skip) --emsg_skip; + clear_evalarg(&evalarg, eap); return (int)retval; } @@ -1883,6 +1890,24 @@ skipwhite_and_linebreak(char_u *arg, evalarg_T *evalarg) return p; } +/* + * After using "evalarg" filled from "eap" free the memory. + */ + void +clear_evalarg(evalarg_T *evalarg, exarg_T *eap) +{ + if (evalarg != NULL && eap != NULL && evalarg->eval_tofree != NULL) + { + // We may need to keep the original command line, e.g. for + // ":let" it has the variable names. But we may also need the + // new one, "nextcmd" points into it. Keep both. + vim_free(eap->cmdline_tofree); + eap->cmdline_tofree = *eap->cmdlinep; + *eap->cmdlinep = evalarg->eval_tofree; + evalarg->eval_tofree = NULL; + } +} + /* * The "evaluate" argument: When FALSE, the argument is only parsed but not * executed. The function may return OK, but the rettv will be of type @@ -1934,16 +1959,7 @@ eval0( if (eap != NULL) eap->nextcmd = check_nextcmd(p); - if (evalarg != NULL && eap != NULL && evalarg->eval_tofree != NULL) - { - // We may need to keep the original command line, e.g. for - // ":let" it has the variable names. But we may also need the - // new one, "nextcmd" points into it. Keep both. - vim_free(eap->cmdline_tofree); - eap->cmdline_tofree = *eap->cmdlinep; - *eap->cmdlinep = evalarg->eval_tofree; - evalarg->eval_tofree = NULL; - } + clear_evalarg(evalarg, eap); return ret; } @@ -5223,6 +5239,7 @@ ex_echo(exarg_T *eap) arg = skipwhite(arg); } eap->nextcmd = check_nextcmd(arg); + clear_evalarg(&evalarg, eap); if (eap->skip) --emsg_skip; diff --git a/src/evalvars.c b/src/evalvars.c index 0acbd7bcf1..1d6172ac7c 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -804,7 +804,7 @@ ex_let(exarg_T *eap) i = eval0(expr, &rettv, eap, &evalarg); if (eap->skip) --emsg_skip; - vim_free(evalarg.eval_tofree); + clear_evalarg(&evalarg, eap); } if (eap->skip) { diff --git a/src/proto/eval.pro b/src/proto/eval.pro index 9170385918..88dd8a7538 100644 --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -30,6 +30,7 @@ int pattern_match(char_u *pat, char_u *text, int ic); char_u *eval_next_non_blank(char_u *arg, evalarg_T *evalarg, int *getnext); char_u *eval_next_line(evalarg_T *evalarg); char_u *skipwhite_and_linebreak(char_u *arg, evalarg_T *evalarg); +void clear_evalarg(evalarg_T *evalarg, exarg_T *eap); int eval0(char_u *arg, typval_T *rettv, exarg_T *eap, evalarg_T *evalarg); int eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg); void eval_addblob(typval_T *tv1, typval_T *tv2); diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim index 2d5bf4516e..a818d14084 100644 --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -101,5 +101,46 @@ def Test_echo_linebreak() CheckScriptSuccess(lines) enddef +def Test_if_linebreak() + let lines =<< trim END + vim9script + if 1 && + 2 + || 3 + g:res = 42 + endif + assert_equal(42, g:res) + END + CheckScriptSuccess(lines) + unlet g:res + + lines =<< trim END + vim9script + if 1 && + 0 + g:res = 0 + elseif 0 || + 0 + || 1 + g:res = 12 + endif + assert_equal(12, g:res) + END + CheckScriptSuccess(lines) + unlet g:res +enddef + +def Test_while_linebreak() + " TODO: line break in :while expression doesn't work yet + let lines =<< trim END + vim9script + let nr = 0 + while nr < 10 + 3 + nr = nr + 4 + endwhile + assert_equal(16, nr) + END + CheckScriptSuccess(lines) +enddef " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index c304fe5ccf..948f561e25 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1076, /**/ 1075, /**/ From 75e15670b8749845cde2962ddb738dd5c6e73191 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 28 Jun 2020 13:10:22 +0200 Subject: [PATCH 032/105] patch 8.2.1077: no enough test coverage for highlighting Problem: No enough test coverage for highlighting. Solution: Add more tests. (Yegappan Lakshmanan, closes #6351) --- runtime/doc/syntax.txt | 2 +- src/testdir/test_cmdline.vim | 12 ++++++++ src/testdir/test_highlight.vim | 50 +++++++++++++++++++++++++++++++++- src/testdir/test_match.vim | 36 ++++++++++++++++++++++++ src/version.c | 2 ++ 5 files changed, 100 insertions(+), 2 deletions(-) diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index dbcaad2beb..8c2d35342e 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -5018,7 +5018,7 @@ ctermul={color-nr} *highlight-ctermul* console. Example, for reverse video: > :highlight Visual ctermfg=bg ctermbg=fg < Note that the colors are used that are valid at the moment this - command are given. If the Normal group colors are changed later, the + command is given. If the Normal group colors are changed later, the "fg" and "bg" colors will not be adjusted. diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index 4dca8ea6ea..2a3112a734 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -191,6 +191,10 @@ func Test_highlight_completion() call assert_equal('"hi default', getreg(':')) call feedkeys(":hi c\\\"\", 'xt') call assert_equal('"hi clear', getreg(':')) + call feedkeys(":hi clear Aardig Aard\\\"\", 'xt') + call assert_equal('"hi clear Aardig Aardig', getreg(':')) + call feedkeys(":hi Aardig \\\"\", 'xt') + call assert_equal("\"hi Aardig \t", getreg(':')) " A cleared group does not show up in completions. hi Anders ctermfg=green @@ -201,6 +205,14 @@ func Test_highlight_completion() call assert_equal([], getcompletion('A', 'highlight')) endfunc +" Test for command-line expansion of "hi Ni " (easter egg) +func Test_highlight_easter_egg() + call test_override('ui_delay', 1) + call feedkeys(":hi Ni \\\"\", 'xt') + call assert_equal("\"hi Ni \", @:) + call test_override('ALL', 0) +endfunc + func Test_getcompletion() if !has('cmdline_compl') return diff --git a/src/testdir/test_highlight.vim b/src/testdir/test_highlight.vim index 73b72289e6..723cb84767 100644 --- a/src/testdir/test_highlight.vim +++ b/src/testdir/test_highlight.vim @@ -701,8 +701,9 @@ endfunc func Test_highlight_cmd_errors() if has('gui_running') " This test doesn't fail in the MS-Windows console version. - call assert_fails('hi Xcomment ctermfg=fg', 'E419:') + call assert_fails('hi Xcomment ctermbg=fg', 'E419:') call assert_fails('hi Xcomment ctermfg=bg', 'E420:') + call assert_fails('hi Xcomment ctermfg=ul', 'E453:') endif " Try using a very long terminal code. Define a dummy terminal code for this @@ -713,4 +714,51 @@ func Test_highlight_cmd_errors() let &t_fo = "" endfunc +" Test for 'highlight' option +func Test_highlight_opt() + let save_hl = &highlight + call assert_fails('set highlight=j:b', 'E474:') + set highlight=f\ r + call assert_equal('f r', &highlight) + set highlight=fb + call assert_equal('fb', &highlight) + set highlight=fi + call assert_equal('fi', &highlight) + set highlight=f- + call assert_equal('f-', &highlight) + set highlight=fr + call assert_equal('fr', &highlight) + set highlight=fs + call assert_equal('fs', &highlight) + set highlight=fu + call assert_equal('fu', &highlight) + set highlight=fc + call assert_equal('fc', &highlight) + set highlight=ft + call assert_equal('ft', &highlight) + call assert_fails('set highlight=fr:Search', 'E474:') + set highlight=f:$# + call assert_match('W18:', v:statusmsg) + let &highlight = save_hl +endfunc + +" Test for User group highlighting used in the statusline +func Test_highlight_User() + CheckNotGui + hi User1 ctermfg=12 + redraw! + call assert_equal('12', synIDattr(synIDtrans(hlID('User1')), 'fg')) + hi clear +endfunc + +" Test for using RGB color values in a highlight group +func Test_highlight_RGB_color() + CheckGui + hi MySearch guifg=#110000 guibg=#001100 guisp=#000011 + call assert_equal('#110000', synIDattr(synIDtrans(hlID('MySearch')), 'fg#')) + call assert_equal('#001100', synIDattr(synIDtrans(hlID('MySearch')), 'bg#')) + call assert_equal('#000011', synIDattr(synIDtrans(hlID('MySearch')), 'sp#')) + hi clear +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_match.vim b/src/testdir/test_match.vim index dafe3ceb65..e34c9f2790 100644 --- a/src/testdir/test_match.vim +++ b/src/testdir/test_match.vim @@ -98,6 +98,7 @@ function Test_match() call assert_fails('call setmatches(0)', 'E714:') call assert_fails('call setmatches([0])', 'E474:') call assert_fails("call setmatches([{'wrong key': 'wrong value'}])", 'E474:') + call assert_equal(-1, setmatches([{'group' : 'Search', 'priority' : 10, 'id' : 5, 'pos1' : {}}])) call setline(1, 'abcdefghijklmnopq') call matchaddpos("MyGroup1", [[1, 5], [1, 8, 3]], 10, 3) @@ -164,6 +165,8 @@ func Test_matchadd_error() call assert_fails("call matchadd('Error', 'XXX', 1, 3)", 'E798:') call assert_fails("call matchadd('Error', 'XXX', 1, 0)", 'E799:') call assert_fails("call matchadd('Error', 'XXX', [], 0)", 'E745:') + call assert_equal(-1, matchadd('', 'pat')) + call assert_equal(-1, matchadd('Search', '')) endfunc func Test_matchaddpos() @@ -202,6 +205,14 @@ func Test_matchaddpos() call assert_equal(screenattr(2,2), screenattr(1,10)) call assert_notequal(screenattr(2,2), screenattr(1,11)) + " matchaddpos() with line number as 0 + call clearmatches() + let id = matchaddpos('Search', [[0], [3], [0]]) + call assert_equal([{'group' : 'Search', 'priority' : 10, 'id' : id, 'pos1' : [3]}], getmatches()) + call clearmatches() + let id = matchaddpos('Search', [0, 3, 0]) + call assert_equal([{'group' : 'Search', 'priority' : 10, 'id' : id, 'pos1' : [3]}], getmatches()) + nohl call clearmatches() syntax off @@ -233,6 +244,7 @@ func Test_matchaddpos_otherwin() eval winid->clearmatches() call assert_equal([], getmatches(winid)) + call assert_fails('echo getmatches(-1)', 'E957:') call setmatches(savematches, winid) call assert_equal(expect, savematches) @@ -281,6 +293,10 @@ func Test_matchaddpos_error() call assert_fails("call matchaddpos('Error', [{}])", 'E290:') call assert_equal(-1, matchaddpos('Error', test_null_list())) call assert_fails("call matchaddpos('Error', [1], [], 1)", 'E745:') + call assert_equal(-1, matchaddpos('Search', [[]])) + call assert_fails("call matchaddpos('Search', [[{}]])", 'E728:') + call assert_fails("call matchaddpos('Search', [[2, {}]])", 'E728:') + call assert_fails("call matchaddpos('Search', [[3, 4, {}]])", 'E728:') endfunc func OtherWindowCommon() @@ -333,4 +349,24 @@ func Test_matchadd_other_window() call delete('XscriptMatchCommon') endfunc +" Test for deleting matches outside of the screen redraw top/bottom lines +" This should cause a redraw of those lines. +func Test_matchdelete_redraw() + new + call setline(1, range(1, 500)) + call cursor(250, 1) + let m1 = matchaddpos('Search', [[250]]) + let m2 = matchaddpos('Search', [[10], [450]]) + redraw! + let m3 = matchaddpos('Search', [[240], [260]]) + call matchdelete(m2) + let m = getmatches() + call assert_equal(2, len(m)) + call assert_equal([250], m[0].pos1) + redraw! + call matchdelete(m1) + call assert_equal(1, len(getmatches())) + bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 948f561e25..d9a36e233b 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1077, /**/ 1076, /**/ From 06cf97e714fd8bf9b35ff5f8a6f2302c79acdd03 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 28 Jun 2020 13:17:26 +0200 Subject: [PATCH 033/105] patch 8.2.1078: highlight and match functionality together in one file Problem: Highlight and match functionality together in one file. Solution: Move match functionality to a separate file. (Yegappan Lakshmanan, closes #6352) --- Filelist | 2 + src/Make_cyg_ming.mak | 1 + src/Make_morph.mak | 1 + src/Make_mvc.mak | 4 + src/Make_vms.mms | 5 + src/Makefile | 10 + src/README.md | 3 +- src/highlight.c | 1343 -------------------------------------- src/match.c | 1351 +++++++++++++++++++++++++++++++++++++++ src/proto.h | 1 + src/proto/highlight.pro | 15 - src/proto/match.pro | 17 + src/version.c | 2 + 13 files changed, 1396 insertions(+), 1359 deletions(-) create mode 100644 src/match.c create mode 100644 src/proto/match.pro diff --git a/Filelist b/Filelist index 2dbb2d3432..1f75741573 100644 --- a/Filelist +++ b/Filelist @@ -80,6 +80,7 @@ SRC_ALL = \ src/main.c \ src/map.c \ src/mark.c \ + src/match.c \ src/mbyte.c \ src/memfile.c \ src/memfile_test.c \ @@ -247,6 +248,7 @@ SRC_ALL = \ src/proto/main.pro \ src/proto/map.pro \ src/proto/mark.pro \ + src/proto/match.pro \ src/proto/mbyte.pro \ src/proto/memfile.pro \ src/proto/memline.pro \ diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak index 890c9568e0..157dc94956 100644 --- a/src/Make_cyg_ming.mak +++ b/src/Make_cyg_ming.mak @@ -753,6 +753,7 @@ OBJ = \ $(OUTDIR)/main.o \ $(OUTDIR)/map.o \ $(OUTDIR)/mark.o \ + $(OUTDIR)/match.o \ $(OUTDIR)/memfile.o \ $(OUTDIR)/memline.o \ $(OUTDIR)/menu.o \ diff --git a/src/Make_morph.mak b/src/Make_morph.mak index ce615b2e37..bd95115a39 100644 --- a/src/Make_morph.mak +++ b/src/Make_morph.mak @@ -72,6 +72,7 @@ SRC = arabic.c \ main.c \ map.c \ mark.c \ + match.c \ mbyte.c \ memfile.c \ memline.c \ diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index 3239974c9f..b1bb8836e0 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -775,6 +775,7 @@ OBJ = \ $(OUTDIR)\main.obj \ $(OUTDIR)\map.obj \ $(OUTDIR)\mark.obj \ + $(OUTDIR)\match.obj \ $(OUTDIR)\mbyte.obj \ $(OUTDIR)\memfile.obj \ $(OUTDIR)\memline.obj \ @@ -1671,6 +1672,8 @@ $(OUTDIR)/map.obj: $(OUTDIR) map.c $(INCL) $(OUTDIR)/mark.obj: $(OUTDIR) mark.c $(INCL) +$(OUTDIR)/match.obj: $(OUTDIR) match.c $(INCL) + $(OUTDIR)/memfile.obj: $(OUTDIR) memfile.c $(INCL) $(OUTDIR)/memline.obj: $(OUTDIR) memline.c $(INCL) @@ -1935,6 +1938,7 @@ proto.h: \ proto/main.pro \ proto/map.pro \ proto/mark.pro \ + proto/match.pro \ proto/memfile.pro \ proto/memline.pro \ proto/menu.pro \ diff --git a/src/Make_vms.mms b/src/Make_vms.mms index 1194acdd43..78f9dab5db 100644 --- a/src/Make_vms.mms +++ b/src/Make_vms.mms @@ -347,6 +347,7 @@ SRC = \ main.c \ map.c \ mark.c \ + match.c \ mbyte.c \ memfile.c \ memline.c \ @@ -460,6 +461,7 @@ OBJ = \ main.obj \ map.obj \ mark.obj \ + match.obj \ mbyte.obj \ memfile.obj \ memline.obj \ @@ -867,6 +869,9 @@ map.obj : map.c vim.h [.auto]config.h feature.h os_unix.h \ mark.obj : mark.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h +match.obj : match.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h memfile.obj : memfile.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h \ gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ diff --git a/src/Makefile b/src/Makefile index 354c13faf7..105820a93e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1649,6 +1649,7 @@ BASIC_SRC = \ main.c \ map.c \ mark.c \ + match.c \ mbyte.c \ memfile.c \ memline.c \ @@ -1797,6 +1798,7 @@ OBJ_COMMON = \ objects/list.o \ objects/map.o \ objects/mark.o \ + objects/match.o \ objects/mbyte.o \ objects/memline.o \ objects/menu.o \ @@ -1971,6 +1973,7 @@ PRO_AUTO = \ main.pro \ map.pro \ mark.pro \ + match.pro \ mbyte.pro \ memfile.pro \ memline.pro \ @@ -3379,6 +3382,9 @@ objects/map.o: map.c objects/mark.o: mark.c $(CCC) -o $@ mark.c +objects/match.o: match.c + $(CCC) -o $@ match.c + objects/memfile.o: memfile.c $(CCC) -o $@ memfile.c @@ -3965,6 +3971,10 @@ objects/mark.o: mark.c vim.h protodef.h auto/config.h feature.h os_unix.h \ auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ proto.h globals.h +objects/match.o: match.c vim.h protodef.h auto/config.h feature.h \ + os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h objects/mbyte.o: mbyte.c vim.h protodef.h auto/config.h feature.h os_unix.h \ auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ diff --git a/src/README.md b/src/README.md index 6cc0704727..a196f2b954 100644 --- a/src/README.md +++ b/src/README.md @@ -51,8 +51,9 @@ getchar.c | getting characters and key mapping highlight.c | syntax highlighting indent.c | text indentation insexpand.c | Insert mode completion -mark.c | marks map.c | mapping and abbreviations +mark.c | marks +match.c | highlight matching mbyte.c | multi-byte character handling memfile.c | storing lines for buffers in a swapfile memline.c | storing lines for buffers in memory diff --git a/src/highlight.c b/src/highlight.c index 8c41c38fc8..6ddd44a827 100644 --- a/src/highlight.c +++ b/src/highlight.c @@ -9,7 +9,6 @@ /* * Highlighting stuff. - * Includes highlighting matches. */ #include "vim.h" @@ -3694,1345 +3693,3 @@ free_highlight_fonts(void) # endif } #endif - - -#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO) - -# define SEARCH_HL_PRIORITY 0 - -/* - * Add match to the match list of window 'wp'. The pattern 'pat' will be - * highlighted with the group 'grp' with priority 'prio'. - * Optionally, a desired ID 'id' can be specified (greater than or equal to 1). - * If no particular ID is desired, -1 must be specified for 'id'. - * Return ID of added match, -1 on failure. - */ - static int -match_add( - win_T *wp, - char_u *grp, - char_u *pat, - int prio, - int id, - list_T *pos_list, - char_u *conceal_char UNUSED) // pointer to conceal replacement char -{ - matchitem_T *cur; - matchitem_T *prev; - matchitem_T *m; - int hlg_id; - regprog_T *regprog = NULL; - int rtype = SOME_VALID; - - if (*grp == NUL || (pat != NULL && *pat == NUL)) - return -1; - if (id < -1 || id == 0) - { - semsg(_("E799: Invalid ID: %d (must be greater than or equal to 1)"), - id); - return -1; - } - if (id != -1) - { - cur = wp->w_match_head; - while (cur != NULL) - { - if (cur->id == id) - { - semsg(_("E801: ID already taken: %d"), id); - return -1; - } - cur = cur->next; - } - } - if ((hlg_id = syn_namen2id(grp, (int)STRLEN(grp))) == 0) - { - semsg(_(e_nogroup), grp); - return -1; - } - if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL) - { - semsg(_(e_invarg2), pat); - return -1; - } - - // Find available match ID. - while (id == -1) - { - cur = wp->w_match_head; - while (cur != NULL && cur->id != wp->w_next_match_id) - cur = cur->next; - if (cur == NULL) - id = wp->w_next_match_id; - wp->w_next_match_id++; - } - - // Build new match. - m = ALLOC_CLEAR_ONE(matchitem_T); - m->id = id; - m->priority = prio; - m->pattern = pat == NULL ? NULL : vim_strsave(pat); - m->hlg_id = hlg_id; - m->match.regprog = regprog; - m->match.rmm_ic = FALSE; - m->match.rmm_maxcol = 0; -# if defined(FEAT_CONCEAL) - m->conceal_char = 0; - if (conceal_char != NULL) - m->conceal_char = (*mb_ptr2char)(conceal_char); -# endif - - // Set up position matches - if (pos_list != NULL) - { - linenr_T toplnum = 0; - linenr_T botlnum = 0; - listitem_T *li; - int i; - - CHECK_LIST_MATERIALIZE(pos_list); - for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH; - i++, li = li->li_next) - { - linenr_T lnum = 0; - colnr_T col = 0; - int len = 1; - list_T *subl; - listitem_T *subli; - int error = FALSE; - - if (li->li_tv.v_type == VAR_LIST) - { - subl = li->li_tv.vval.v_list; - if (subl == NULL) - goto fail; - subli = subl->lv_first; - if (subli == NULL) - goto fail; - lnum = tv_get_number_chk(&subli->li_tv, &error); - if (error == TRUE) - goto fail; - if (lnum == 0) - { - --i; - continue; - } - m->pos.pos[i].lnum = lnum; - subli = subli->li_next; - if (subli != NULL) - { - col = tv_get_number_chk(&subli->li_tv, &error); - if (error == TRUE) - goto fail; - subli = subli->li_next; - if (subli != NULL) - { - len = tv_get_number_chk(&subli->li_tv, &error); - if (error == TRUE) - goto fail; - } - } - m->pos.pos[i].col = col; - m->pos.pos[i].len = len; - } - else if (li->li_tv.v_type == VAR_NUMBER) - { - if (li->li_tv.vval.v_number == 0) - { - --i; - continue; - } - m->pos.pos[i].lnum = li->li_tv.vval.v_number; - m->pos.pos[i].col = 0; - m->pos.pos[i].len = 0; - } - else - { - emsg(_("E290: List or number required")); - goto fail; - } - if (toplnum == 0 || lnum < toplnum) - toplnum = lnum; - if (botlnum == 0 || lnum >= botlnum) - botlnum = lnum + 1; - } - - // Calculate top and bottom lines for redrawing area - if (toplnum != 0) - { - if (wp->w_buffer->b_mod_set) - { - if (wp->w_buffer->b_mod_top > toplnum) - wp->w_buffer->b_mod_top = toplnum; - if (wp->w_buffer->b_mod_bot < botlnum) - wp->w_buffer->b_mod_bot = botlnum; - } - else - { - wp->w_buffer->b_mod_set = TRUE; - wp->w_buffer->b_mod_top = toplnum; - wp->w_buffer->b_mod_bot = botlnum; - wp->w_buffer->b_mod_xlines = 0; - } - m->pos.toplnum = toplnum; - m->pos.botlnum = botlnum; - rtype = VALID; - } - } - - // Insert new match. The match list is in ascending order with regard to - // the match priorities. - cur = wp->w_match_head; - prev = cur; - while (cur != NULL && prio >= cur->priority) - { - prev = cur; - cur = cur->next; - } - if (cur == prev) - wp->w_match_head = m; - else - prev->next = m; - m->next = cur; - - redraw_win_later(wp, rtype); - return id; - -fail: - vim_free(m); - return -1; -} - -/* - * Delete match with ID 'id' in the match list of window 'wp'. - * Print error messages if 'perr' is TRUE. - */ - static int -match_delete(win_T *wp, int id, int perr) -{ - matchitem_T *cur = wp->w_match_head; - matchitem_T *prev = cur; - int rtype = SOME_VALID; - - if (id < 1) - { - if (perr == TRUE) - semsg(_("E802: Invalid ID: %d (must be greater than or equal to 1)"), - id); - return -1; - } - while (cur != NULL && cur->id != id) - { - prev = cur; - cur = cur->next; - } - if (cur == NULL) - { - if (perr == TRUE) - semsg(_("E803: ID not found: %d"), id); - return -1; - } - if (cur == prev) - wp->w_match_head = cur->next; - else - prev->next = cur->next; - vim_regfree(cur->match.regprog); - vim_free(cur->pattern); - if (cur->pos.toplnum != 0) - { - if (wp->w_buffer->b_mod_set) - { - if (wp->w_buffer->b_mod_top > cur->pos.toplnum) - wp->w_buffer->b_mod_top = cur->pos.toplnum; - if (wp->w_buffer->b_mod_bot < cur->pos.botlnum) - wp->w_buffer->b_mod_bot = cur->pos.botlnum; - } - else - { - wp->w_buffer->b_mod_set = TRUE; - wp->w_buffer->b_mod_top = cur->pos.toplnum; - wp->w_buffer->b_mod_bot = cur->pos.botlnum; - wp->w_buffer->b_mod_xlines = 0; - } - rtype = VALID; - } - vim_free(cur); - redraw_win_later(wp, rtype); - return 0; -} - -/* - * Delete all matches in the match list of window 'wp'. - */ - void -clear_matches(win_T *wp) -{ - matchitem_T *m; - - while (wp->w_match_head != NULL) - { - m = wp->w_match_head->next; - vim_regfree(wp->w_match_head->match.regprog); - vim_free(wp->w_match_head->pattern); - vim_free(wp->w_match_head); - wp->w_match_head = m; - } - redraw_win_later(wp, SOME_VALID); -} - -/* - * Get match from ID 'id' in window 'wp'. - * Return NULL if match not found. - */ - static matchitem_T * -get_match(win_T *wp, int id) -{ - matchitem_T *cur = wp->w_match_head; - - while (cur != NULL && cur->id != id) - cur = cur->next; - return cur; -} - -/* - * Init for calling prepare_search_hl(). - */ - void -init_search_hl(win_T *wp, match_T *search_hl) -{ - matchitem_T *cur; - - // Setup for match and 'hlsearch' highlighting. Disable any previous - // match - cur = wp->w_match_head; - while (cur != NULL) - { - cur->hl.rm = cur->match; - if (cur->hlg_id == 0) - cur->hl.attr = 0; - else - cur->hl.attr = syn_id2attr(cur->hlg_id); - cur->hl.buf = wp->w_buffer; - cur->hl.lnum = 0; - cur->hl.first_lnum = 0; -# ifdef FEAT_RELTIME - // Set the time limit to 'redrawtime'. - profile_setlimit(p_rdt, &(cur->hl.tm)); -# endif - cur = cur->next; - } - search_hl->buf = wp->w_buffer; - search_hl->lnum = 0; - search_hl->first_lnum = 0; - // time limit is set at the toplevel, for all windows -} - -/* - * If there is a match fill "shl" and return one. - * Return zero otherwise. - */ - static int -next_search_hl_pos( - match_T *shl, // points to a match - linenr_T lnum, - posmatch_T *posmatch, // match positions - colnr_T mincol) // minimal column for a match -{ - int i; - int found = -1; - - for (i = posmatch->cur; i < MAXPOSMATCH; i++) - { - llpos_T *pos = &posmatch->pos[i]; - - if (pos->lnum == 0) - break; - if (pos->len == 0 && pos->col < mincol) - continue; - if (pos->lnum == lnum) - { - if (found >= 0) - { - // if this match comes before the one at "found" then swap - // them - if (pos->col < posmatch->pos[found].col) - { - llpos_T tmp = *pos; - - *pos = posmatch->pos[found]; - posmatch->pos[found] = tmp; - } - } - else - found = i; - } - } - posmatch->cur = 0; - if (found >= 0) - { - colnr_T start = posmatch->pos[found].col == 0 - ? 0 : posmatch->pos[found].col - 1; - colnr_T end = posmatch->pos[found].col == 0 - ? MAXCOL : start + posmatch->pos[found].len; - - shl->lnum = lnum; - shl->rm.startpos[0].lnum = 0; - shl->rm.startpos[0].col = start; - shl->rm.endpos[0].lnum = 0; - shl->rm.endpos[0].col = end; - shl->is_addpos = TRUE; - posmatch->cur = found + 1; - return 1; - } - return 0; -} - -/* - * Search for a next 'hlsearch' or match. - * Uses shl->buf. - * Sets shl->lnum and shl->rm contents. - * Note: Assumes a previous match is always before "lnum", unless - * shl->lnum is zero. - * Careful: Any pointers for buffer lines will become invalid. - */ - static void -next_search_hl( - win_T *win, - match_T *search_hl, - match_T *shl, // points to search_hl or a match - linenr_T lnum, - colnr_T mincol, // minimal column for a match - matchitem_T *cur) // to retrieve match positions if any -{ - linenr_T l; - colnr_T matchcol; - long nmatched; - int called_emsg_before = called_emsg; - - // for :{range}s/pat only highlight inside the range - if (lnum < search_first_line || lnum > search_last_line) - { - shl->lnum = 0; - return; - } - - if (shl->lnum != 0) - { - // Check for three situations: - // 1. If the "lnum" is below a previous match, start a new search. - // 2. If the previous match includes "mincol", use it. - // 3. Continue after the previous match. - l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum; - if (lnum > l) - shl->lnum = 0; - else if (lnum < l || shl->rm.endpos[0].col > mincol) - return; - } - - /* - * Repeat searching for a match until one is found that includes "mincol" - * or none is found in this line. - */ - for (;;) - { -# ifdef FEAT_RELTIME - // Stop searching after passing the time limit. - if (profile_passed_limit(&(shl->tm))) - { - shl->lnum = 0; // no match found in time - break; - } -# endif - // Three situations: - // 1. No useful previous match: search from start of line. - // 2. Not Vi compatible or empty match: continue at next character. - // Break the loop if this is beyond the end of the line. - // 3. Vi compatible searching: continue at end of previous match. - if (shl->lnum == 0) - matchcol = 0; - else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL - || (shl->rm.endpos[0].lnum == 0 - && shl->rm.endpos[0].col <= shl->rm.startpos[0].col)) - { - char_u *ml; - - matchcol = shl->rm.startpos[0].col; - ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol; - if (*ml == NUL) - { - ++matchcol; - shl->lnum = 0; - break; - } - if (has_mbyte) - matchcol += mb_ptr2len(ml); - else - ++matchcol; - } - else - matchcol = shl->rm.endpos[0].col; - - shl->lnum = lnum; - if (shl->rm.regprog != NULL) - { - // Remember whether shl->rm is using a copy of the regprog in - // cur->match. - int regprog_is_copy = (shl != search_hl && cur != NULL - && shl == &cur->hl - && cur->match.regprog == cur->hl.rm.regprog); - int timed_out = FALSE; - - nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, - matchcol, -#ifdef FEAT_RELTIME - &(shl->tm), &timed_out -#else - NULL, NULL -#endif - ); - // Copy the regprog, in case it got freed and recompiled. - if (regprog_is_copy) - cur->match.regprog = cur->hl.rm.regprog; - - if (called_emsg > called_emsg_before || got_int || timed_out) - { - // Error while handling regexp: stop using this regexp. - if (shl == search_hl) - { - // don't free regprog in the match list, it's a copy - vim_regfree(shl->rm.regprog); - set_no_hlsearch(TRUE); - } - shl->rm.regprog = NULL; - shl->lnum = 0; - got_int = FALSE; // avoid the "Type :quit to exit Vim" message - break; - } - } - else if (cur != NULL) - nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol); - else - nmatched = 0; - if (nmatched == 0) - { - shl->lnum = 0; // no match found - break; - } - if (shl->rm.startpos[0].lnum > 0 - || shl->rm.startpos[0].col >= mincol - || nmatched > 1 - || shl->rm.endpos[0].col > mincol) - { - shl->lnum += shl->rm.startpos[0].lnum; - break; // useful match found - } - } -} - -/* - * Advance to the match in window "wp" line "lnum" or past it. - */ - void -prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum) -{ - matchitem_T *cur; // points to the match list - match_T *shl; // points to search_hl or a match - int shl_flag; // flag to indicate whether search_hl - // has been processed or not - int pos_inprogress; // marks that position match search is - // in progress - int n; - - // When using a multi-line pattern, start searching at the top - // of the window or just after a closed fold. - // Do this both for search_hl and the match list. - cur = wp->w_match_head; - shl_flag = WIN_IS_POPUP(wp); // skip search_hl in a popup window - while (cur != NULL || shl_flag == FALSE) - { - if (shl_flag == FALSE) - { - shl = search_hl; - shl_flag = TRUE; - } - else - shl = &cur->hl; - if (shl->rm.regprog != NULL - && shl->lnum == 0 - && re_multiline(shl->rm.regprog)) - { - if (shl->first_lnum == 0) - { -# ifdef FEAT_FOLDING - for (shl->first_lnum = lnum; - shl->first_lnum > wp->w_topline; --shl->first_lnum) - if (hasFoldingWin(wp, shl->first_lnum - 1, - NULL, NULL, TRUE, NULL)) - break; -# else - shl->first_lnum = wp->w_topline; -# endif - } - if (cur != NULL) - cur->pos.cur = 0; - pos_inprogress = TRUE; - n = 0; - while (shl->first_lnum < lnum && (shl->rm.regprog != NULL - || (cur != NULL && pos_inprogress))) - { - next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n, - shl == search_hl ? NULL : cur); - pos_inprogress = cur == NULL || cur->pos.cur == 0 - ? FALSE : TRUE; - if (shl->lnum != 0) - { - shl->first_lnum = shl->lnum - + shl->rm.endpos[0].lnum - - shl->rm.startpos[0].lnum; - n = shl->rm.endpos[0].col; - } - else - { - ++shl->first_lnum; - n = 0; - } - } - } - if (shl != search_hl && cur != NULL) - cur = cur->next; - } -} - -/* - * Prepare for 'hlsearch' and match highlighting in one window line. - * Return TRUE if there is such highlighting and set "search_attr" to the - * current highlight attribute. - */ - int -prepare_search_hl_line( - win_T *wp, - linenr_T lnum, - colnr_T mincol, - char_u **line, - match_T *search_hl, - int *search_attr) -{ - matchitem_T *cur; // points to the match list - match_T *shl; // points to search_hl or a match - int shl_flag; // flag to indicate whether search_hl - // has been processed or not - int area_highlighting = FALSE; - - /* - * Handle highlighting the last used search pattern and matches. - * Do this for both search_hl and the match list. - * Do not use search_hl in a popup window. - */ - cur = wp->w_match_head; - shl_flag = WIN_IS_POPUP(wp); - while (cur != NULL || shl_flag == FALSE) - { - if (shl_flag == FALSE) - { - shl = search_hl; - shl_flag = TRUE; - } - else - shl = &cur->hl; - shl->startcol = MAXCOL; - shl->endcol = MAXCOL; - shl->attr_cur = 0; - shl->is_addpos = FALSE; - if (cur != NULL) - cur->pos.cur = 0; - next_search_hl(wp, search_hl, shl, lnum, mincol, - shl == search_hl ? NULL : cur); - - // Need to get the line again, a multi-line regexp may have made it - // invalid. - *line = ml_get_buf(wp->w_buffer, lnum, FALSE); - - if (shl->lnum != 0 && shl->lnum <= lnum) - { - if (shl->lnum == lnum) - shl->startcol = shl->rm.startpos[0].col; - else - shl->startcol = 0; - if (lnum == shl->lnum + shl->rm.endpos[0].lnum - - shl->rm.startpos[0].lnum) - shl->endcol = shl->rm.endpos[0].col; - else - shl->endcol = MAXCOL; - // Highlight one character for an empty match. - if (shl->startcol == shl->endcol) - { - if (has_mbyte && (*line)[shl->endcol] != NUL) - shl->endcol += (*mb_ptr2len)((*line) + shl->endcol); - else - ++shl->endcol; - } - if ((long)shl->startcol < mincol) // match at leftcol - { - shl->attr_cur = shl->attr; - *search_attr = shl->attr; - } - area_highlighting = TRUE; - } - if (shl != search_hl && cur != NULL) - cur = cur->next; - } - return area_highlighting; -} - -/* - * For a position in a line: Check for start/end of 'hlsearch' and other - * matches. - * After end, check for start/end of next match. - * When another match, have to check for start again. - * Watch out for matching an empty string! - * Return the updated search_attr. - */ - int -update_search_hl( - win_T *wp, - linenr_T lnum, - colnr_T col, - char_u **line, - match_T *search_hl, - int *has_match_conc UNUSED, - int *match_conc UNUSED, - int did_line_attr, - int lcs_eol_one) -{ - matchitem_T *cur; // points to the match list - match_T *shl; // points to search_hl or a match - int shl_flag; // flag to indicate whether search_hl - // has been processed or not - int pos_inprogress; // marks that position match search is in - // progress - int search_attr = 0; - - - // Do this for 'search_hl' and the match list (ordered by priority). - cur = wp->w_match_head; - shl_flag = WIN_IS_POPUP(wp); - while (cur != NULL || shl_flag == FALSE) - { - if (shl_flag == FALSE - && (cur == NULL - || cur->priority > SEARCH_HL_PRIORITY)) - { - shl = search_hl; - shl_flag = TRUE; - } - else - shl = &cur->hl; - if (cur != NULL) - cur->pos.cur = 0; - pos_inprogress = TRUE; - while (shl->rm.regprog != NULL || (cur != NULL && pos_inprogress)) - { - if (shl->startcol != MAXCOL - && col >= shl->startcol - && col < shl->endcol) - { - int next_col = col + mb_ptr2len(*line + col); - - if (shl->endcol < next_col) - shl->endcol = next_col; - shl->attr_cur = shl->attr; -# ifdef FEAT_CONCEAL - // Match with the "Conceal" group results in hiding - // the match. - if (cur != NULL - && shl != search_hl - && syn_name2id((char_u *)"Conceal") == cur->hlg_id) - { - *has_match_conc = col == shl->startcol ? 2 : 1; - *match_conc = cur->conceal_char; - } - else - *has_match_conc = 0; -# endif - } - else if (col == shl->endcol) - { - shl->attr_cur = 0; - next_search_hl(wp, search_hl, shl, lnum, col, - shl == search_hl ? NULL : cur); - pos_inprogress = !(cur == NULL || cur->pos.cur == 0); - - // Need to get the line again, a multi-line regexp may have - // made it invalid. - *line = ml_get_buf(wp->w_buffer, lnum, FALSE); - - if (shl->lnum == lnum) - { - shl->startcol = shl->rm.startpos[0].col; - if (shl->rm.endpos[0].lnum == 0) - shl->endcol = shl->rm.endpos[0].col; - else - shl->endcol = MAXCOL; - - if (shl->startcol == shl->endcol) - { - // highlight empty match, try again after - // it - if (has_mbyte) - shl->endcol += (*mb_ptr2len)(*line + shl->endcol); - else - ++shl->endcol; - } - - // Loop to check if the match starts at the - // current position - continue; - } - } - break; - } - if (shl != search_hl && cur != NULL) - cur = cur->next; - } - - // Use attributes from match with highest priority among 'search_hl' and - // the match list. - cur = wp->w_match_head; - shl_flag = WIN_IS_POPUP(wp); - while (cur != NULL || shl_flag == FALSE) - { - if (shl_flag == FALSE - && (cur == NULL || - cur->priority > SEARCH_HL_PRIORITY)) - { - shl = search_hl; - shl_flag = TRUE; - } - else - shl = &cur->hl; - if (shl->attr_cur != 0) - search_attr = shl->attr_cur; - if (shl != search_hl && cur != NULL) - cur = cur->next; - } - // Only highlight one character after the last column. - if (*(*line + col) == NUL && (did_line_attr >= 1 - || (wp->w_p_list && lcs_eol_one == -1))) - search_attr = 0; - return search_attr; -} - - int -get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol) -{ - long prevcol = curcol; - int prevcol_hl_flag = FALSE; - matchitem_T *cur; // points to the match list - - // we're not really at that column when skipping some text - if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol) - ++prevcol; - - if (!search_hl->is_addpos && prevcol == (long)search_hl->startcol) - prevcol_hl_flag = TRUE; - else - { - cur = wp->w_match_head; - while (cur != NULL) - { - if (!cur->hl.is_addpos && prevcol == (long)cur->hl.startcol) - { - prevcol_hl_flag = TRUE; - break; - } - cur = cur->next; - } - } - return prevcol_hl_flag; -} - -/* - * Get highlighting for the char after the text in "char_attr" from 'hlsearch' - * or match highlighting. - */ - void -get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr) -{ - matchitem_T *cur; // points to the match list - match_T *shl; // points to search_hl or a match - int shl_flag; // flag to indicate whether search_hl - // has been processed or not - - cur = wp->w_match_head; - shl_flag = WIN_IS_POPUP(wp); - while (cur != NULL || shl_flag == FALSE) - { - if (shl_flag == FALSE - && ((cur != NULL - && cur->priority > SEARCH_HL_PRIORITY) - || cur == NULL)) - { - shl = search_hl; - shl_flag = TRUE; - } - else - shl = &cur->hl; - if (col - 1 == (long)shl->startcol - && (shl == search_hl || !shl->is_addpos)) - *char_attr = shl->attr; - if (shl != search_hl && cur != NULL) - cur = cur->next; - } -} - -#endif // FEAT_SEARCH_EXTRA - -#if defined(FEAT_EVAL) || defined(PROTO) -# ifdef FEAT_SEARCH_EXTRA - static int -matchadd_dict_arg(typval_T *tv, char_u **conceal_char, win_T **win) -{ - dictitem_T *di; - - if (tv->v_type != VAR_DICT) - { - emsg(_(e_dictreq)); - return FAIL; - } - - if (dict_find(tv->vval.v_dict, (char_u *)"conceal", -1) != NULL) - *conceal_char = dict_get_string(tv->vval.v_dict, - (char_u *)"conceal", FALSE); - - if ((di = dict_find(tv->vval.v_dict, (char_u *)"window", -1)) != NULL) - { - *win = find_win_by_nr_or_id(&di->di_tv); - if (*win == NULL) - { - emsg(_(e_invalwindow)); - return FAIL; - } - } - - return OK; -} -#endif - -/* - * "clearmatches()" function - */ - void -f_clearmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -#ifdef FEAT_SEARCH_EXTRA - win_T *win = get_optional_window(argvars, 0); - - if (win != NULL) - clear_matches(win); -#endif -} - -/* - * "getmatches()" function - */ - void -f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -# ifdef FEAT_SEARCH_EXTRA - dict_T *dict; - matchitem_T *cur; - int i; - win_T *win = get_optional_window(argvars, 0); - - if (rettv_list_alloc(rettv) == FAIL || win == NULL) - return; - - cur = win->w_match_head; - while (cur != NULL) - { - dict = dict_alloc(); - if (dict == NULL) - return; - if (cur->match.regprog == NULL) - { - // match added with matchaddpos() - for (i = 0; i < MAXPOSMATCH; ++i) - { - llpos_T *llpos; - char buf[30]; // use 30 to avoid compiler warning - list_T *l; - - llpos = &cur->pos.pos[i]; - if (llpos->lnum == 0) - break; - l = list_alloc(); - if (l == NULL) - break; - list_append_number(l, (varnumber_T)llpos->lnum); - if (llpos->col > 0) - { - list_append_number(l, (varnumber_T)llpos->col); - list_append_number(l, (varnumber_T)llpos->len); - } - sprintf(buf, "pos%d", i + 1); - dict_add_list(dict, buf, l); - } - } - else - { - dict_add_string(dict, "pattern", cur->pattern); - } - dict_add_string(dict, "group", syn_id2name(cur->hlg_id)); - dict_add_number(dict, "priority", (long)cur->priority); - dict_add_number(dict, "id", (long)cur->id); -# if defined(FEAT_CONCEAL) - if (cur->conceal_char) - { - char_u buf[MB_MAXBYTES + 1]; - - buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL; - dict_add_string(dict, "conceal", (char_u *)&buf); - } -# endif - list_append_dict(rettv->vval.v_list, dict); - cur = cur->next; - } -# endif -} - -/* - * "setmatches()" function - */ - void -f_setmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -#ifdef FEAT_SEARCH_EXTRA - list_T *l; - listitem_T *li; - dict_T *d; - list_T *s = NULL; - win_T *win = get_optional_window(argvars, 1); - - rettv->vval.v_number = -1; - if (argvars[0].v_type != VAR_LIST) - { - emsg(_(e_listreq)); - return; - } - if (win == NULL) - return; - - if ((l = argvars[0].vval.v_list) != NULL) - { - // To some extent make sure that we are dealing with a list from - // "getmatches()". - li = l->lv_first; - while (li != NULL) - { - if (li->li_tv.v_type != VAR_DICT - || (d = li->li_tv.vval.v_dict) == NULL) - { - emsg(_(e_invarg)); - return; - } - if (!(dict_find(d, (char_u *)"group", -1) != NULL - && (dict_find(d, (char_u *)"pattern", -1) != NULL - || dict_find(d, (char_u *)"pos1", -1) != NULL) - && dict_find(d, (char_u *)"priority", -1) != NULL - && dict_find(d, (char_u *)"id", -1) != NULL)) - { - emsg(_(e_invarg)); - return; - } - li = li->li_next; - } - - clear_matches(win); - li = l->lv_first; - while (li != NULL) - { - int i = 0; - char buf[30]; // use 30 to avoid compiler warning - dictitem_T *di; - char_u *group; - int priority; - int id; - char_u *conceal; - - d = li->li_tv.vval.v_dict; - if (dict_find(d, (char_u *)"pattern", -1) == NULL) - { - if (s == NULL) - { - s = list_alloc(); - if (s == NULL) - return; - } - - // match from matchaddpos() - for (i = 1; i < 9; i++) - { - sprintf((char *)buf, (char *)"pos%d", i); - if ((di = dict_find(d, (char_u *)buf, -1)) != NULL) - { - if (di->di_tv.v_type != VAR_LIST) - return; - - list_append_tv(s, &di->di_tv); - s->lv_refcount++; - } - else - break; - } - } - - group = dict_get_string(d, (char_u *)"group", TRUE); - priority = (int)dict_get_number(d, (char_u *)"priority"); - id = (int)dict_get_number(d, (char_u *)"id"); - conceal = dict_find(d, (char_u *)"conceal", -1) != NULL - ? dict_get_string(d, (char_u *)"conceal", TRUE) - : NULL; - if (i == 0) - { - match_add(win, group, - dict_get_string(d, (char_u *)"pattern", FALSE), - priority, id, NULL, conceal); - } - else - { - match_add(win, group, NULL, priority, id, s, conceal); - list_unref(s); - s = NULL; - } - vim_free(group); - vim_free(conceal); - - li = li->li_next; - } - rettv->vval.v_number = 0; - } -#endif -} - -/* - * "matchadd()" function - */ - void -f_matchadd(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -# ifdef FEAT_SEARCH_EXTRA - char_u buf[NUMBUFLEN]; - char_u *grp = tv_get_string_buf_chk(&argvars[0], buf); // group - char_u *pat = tv_get_string_buf_chk(&argvars[1], buf); // pattern - int prio = 10; // default priority - int id = -1; - int error = FALSE; - char_u *conceal_char = NULL; - win_T *win = curwin; - - rettv->vval.v_number = -1; - - if (grp == NULL || pat == NULL) - return; - if (argvars[2].v_type != VAR_UNKNOWN) - { - prio = (int)tv_get_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) - { - id = (int)tv_get_number_chk(&argvars[3], &error); - if (argvars[4].v_type != VAR_UNKNOWN - && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL) - return; - } - } - if (error == TRUE) - return; - if (id >= 1 && id <= 3) - { - semsg(_("E798: ID is reserved for \":match\": %d"), id); - return; - } - - rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL, - conceal_char); -# endif -} - -/* - * "matchaddpos()" function - */ - void -f_matchaddpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -# ifdef FEAT_SEARCH_EXTRA - char_u buf[NUMBUFLEN]; - char_u *group; - int prio = 10; - int id = -1; - int error = FALSE; - list_T *l; - char_u *conceal_char = NULL; - win_T *win = curwin; - - rettv->vval.v_number = -1; - - group = tv_get_string_buf_chk(&argvars[0], buf); - if (group == NULL) - return; - - if (argvars[1].v_type != VAR_LIST) - { - semsg(_(e_listarg), "matchaddpos()"); - return; - } - l = argvars[1].vval.v_list; - if (l == NULL) - return; - - if (argvars[2].v_type != VAR_UNKNOWN) - { - prio = (int)tv_get_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) - { - id = (int)tv_get_number_chk(&argvars[3], &error); - - if (argvars[4].v_type != VAR_UNKNOWN - && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL) - return; - } - } - if (error == TRUE) - return; - - // id == 3 is ok because matchaddpos() is supposed to substitute :3match - if (id == 1 || id == 2) - { - semsg(_("E798: ID is reserved for \":match\": %d"), id); - return; - } - - rettv->vval.v_number = match_add(win, group, NULL, prio, id, l, - conceal_char); -# endif -} - -/* - * "matcharg()" function - */ - void -f_matcharg(typval_T *argvars UNUSED, typval_T *rettv) -{ - if (rettv_list_alloc(rettv) == OK) - { -# ifdef FEAT_SEARCH_EXTRA - int id = (int)tv_get_number(&argvars[0]); - matchitem_T *m; - - if (id >= 1 && id <= 3) - { - if ((m = (matchitem_T *)get_match(curwin, id)) != NULL) - { - list_append_string(rettv->vval.v_list, - syn_id2name(m->hlg_id), -1); - list_append_string(rettv->vval.v_list, m->pattern, -1); - } - else - { - list_append_string(rettv->vval.v_list, NULL, -1); - list_append_string(rettv->vval.v_list, NULL, -1); - } - } -# endif - } -} - -/* - * "matchdelete()" function - */ - void -f_matchdelete(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -# ifdef FEAT_SEARCH_EXTRA - win_T *win = get_optional_window(argvars, 1); - - if (win == NULL) - rettv->vval.v_number = -1; - else - rettv->vval.v_number = match_delete(win, - (int)tv_get_number(&argvars[0]), TRUE); -# endif -} -#endif - -#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO) -/* - * ":[N]match {group} {pattern}" - * Sets nextcmd to the start of the next command, if any. Also called when - * skipping commands to find the next command. - */ - void -ex_match(exarg_T *eap) -{ - char_u *p; - char_u *g = NULL; - char_u *end; - int c; - int id; - - if (eap->line2 <= 3) - id = eap->line2; - else - { - emsg(_(e_invcmd)); - return; - } - - // First clear any old pattern. - if (!eap->skip) - match_delete(curwin, id, FALSE); - - if (ends_excmd2(eap->cmd, eap->arg)) - end = eap->arg; - else if ((STRNICMP(eap->arg, "none", 4) == 0 - && (VIM_ISWHITE(eap->arg[4]) - || ends_excmd2(eap->arg, eap->arg + 4)))) - end = eap->arg + 4; - else - { - p = skiptowhite(eap->arg); - if (!eap->skip) - g = vim_strnsave(eap->arg, p - eap->arg); - p = skipwhite(p); - if (*p == NUL) - { - // There must be two arguments. - vim_free(g); - semsg(_(e_invarg2), eap->arg); - return; - } - end = skip_regexp(p + 1, *p, TRUE); - if (!eap->skip) - { - if (*end != NUL && !ends_excmd2(end, skipwhite(end + 1))) - { - vim_free(g); - eap->errmsg = e_trailing; - return; - } - if (*end != *p) - { - vim_free(g); - semsg(_(e_invarg2), p); - return; - } - - c = *end; - *end = NUL; - match_add(curwin, g, p + 1, 10, id, NULL, NULL); - vim_free(g); - *end = c; - } - } - eap->nextcmd = find_nextcmd(end); -} -#endif diff --git a/src/match.c b/src/match.c new file mode 100644 index 0000000000..024ba374f0 --- /dev/null +++ b/src/match.c @@ -0,0 +1,1351 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + +/* + * match.c: functions for highlighting matches + */ + +#include "vim.h" + +#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO) + +# define SEARCH_HL_PRIORITY 0 + +/* + * Add match to the match list of window 'wp'. The pattern 'pat' will be + * highlighted with the group 'grp' with priority 'prio'. + * Optionally, a desired ID 'id' can be specified (greater than or equal to 1). + * If no particular ID is desired, -1 must be specified for 'id'. + * Return ID of added match, -1 on failure. + */ + static int +match_add( + win_T *wp, + char_u *grp, + char_u *pat, + int prio, + int id, + list_T *pos_list, + char_u *conceal_char UNUSED) // pointer to conceal replacement char +{ + matchitem_T *cur; + matchitem_T *prev; + matchitem_T *m; + int hlg_id; + regprog_T *regprog = NULL; + int rtype = SOME_VALID; + + if (*grp == NUL || (pat != NULL && *pat == NUL)) + return -1; + if (id < -1 || id == 0) + { + semsg(_("E799: Invalid ID: %d (must be greater than or equal to 1)"), + id); + return -1; + } + if (id != -1) + { + cur = wp->w_match_head; + while (cur != NULL) + { + if (cur->id == id) + { + semsg(_("E801: ID already taken: %d"), id); + return -1; + } + cur = cur->next; + } + } + if ((hlg_id = syn_namen2id(grp, (int)STRLEN(grp))) == 0) + { + semsg(_(e_nogroup), grp); + return -1; + } + if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL) + { + semsg(_(e_invarg2), pat); + return -1; + } + + // Find available match ID. + while (id == -1) + { + cur = wp->w_match_head; + while (cur != NULL && cur->id != wp->w_next_match_id) + cur = cur->next; + if (cur == NULL) + id = wp->w_next_match_id; + wp->w_next_match_id++; + } + + // Build new match. + m = ALLOC_CLEAR_ONE(matchitem_T); + m->id = id; + m->priority = prio; + m->pattern = pat == NULL ? NULL : vim_strsave(pat); + m->hlg_id = hlg_id; + m->match.regprog = regprog; + m->match.rmm_ic = FALSE; + m->match.rmm_maxcol = 0; +# if defined(FEAT_CONCEAL) + m->conceal_char = 0; + if (conceal_char != NULL) + m->conceal_char = (*mb_ptr2char)(conceal_char); +# endif + + // Set up position matches + if (pos_list != NULL) + { + linenr_T toplnum = 0; + linenr_T botlnum = 0; + listitem_T *li; + int i; + + CHECK_LIST_MATERIALIZE(pos_list); + for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH; + i++, li = li->li_next) + { + linenr_T lnum = 0; + colnr_T col = 0; + int len = 1; + list_T *subl; + listitem_T *subli; + int error = FALSE; + + if (li->li_tv.v_type == VAR_LIST) + { + subl = li->li_tv.vval.v_list; + if (subl == NULL) + goto fail; + subli = subl->lv_first; + if (subli == NULL) + goto fail; + lnum = tv_get_number_chk(&subli->li_tv, &error); + if (error == TRUE) + goto fail; + if (lnum == 0) + { + --i; + continue; + } + m->pos.pos[i].lnum = lnum; + subli = subli->li_next; + if (subli != NULL) + { + col = tv_get_number_chk(&subli->li_tv, &error); + if (error == TRUE) + goto fail; + subli = subli->li_next; + if (subli != NULL) + { + len = tv_get_number_chk(&subli->li_tv, &error); + if (error == TRUE) + goto fail; + } + } + m->pos.pos[i].col = col; + m->pos.pos[i].len = len; + } + else if (li->li_tv.v_type == VAR_NUMBER) + { + if (li->li_tv.vval.v_number == 0) + { + --i; + continue; + } + m->pos.pos[i].lnum = li->li_tv.vval.v_number; + m->pos.pos[i].col = 0; + m->pos.pos[i].len = 0; + } + else + { + emsg(_("E290: List or number required")); + goto fail; + } + if (toplnum == 0 || lnum < toplnum) + toplnum = lnum; + if (botlnum == 0 || lnum >= botlnum) + botlnum = lnum + 1; + } + + // Calculate top and bottom lines for redrawing area + if (toplnum != 0) + { + if (wp->w_buffer->b_mod_set) + { + if (wp->w_buffer->b_mod_top > toplnum) + wp->w_buffer->b_mod_top = toplnum; + if (wp->w_buffer->b_mod_bot < botlnum) + wp->w_buffer->b_mod_bot = botlnum; + } + else + { + wp->w_buffer->b_mod_set = TRUE; + wp->w_buffer->b_mod_top = toplnum; + wp->w_buffer->b_mod_bot = botlnum; + wp->w_buffer->b_mod_xlines = 0; + } + m->pos.toplnum = toplnum; + m->pos.botlnum = botlnum; + rtype = VALID; + } + } + + // Insert new match. The match list is in ascending order with regard to + // the match priorities. + cur = wp->w_match_head; + prev = cur; + while (cur != NULL && prio >= cur->priority) + { + prev = cur; + cur = cur->next; + } + if (cur == prev) + wp->w_match_head = m; + else + prev->next = m; + m->next = cur; + + redraw_win_later(wp, rtype); + return id; + +fail: + vim_free(m); + return -1; +} + +/* + * Delete match with ID 'id' in the match list of window 'wp'. + * Print error messages if 'perr' is TRUE. + */ + static int +match_delete(win_T *wp, int id, int perr) +{ + matchitem_T *cur = wp->w_match_head; + matchitem_T *prev = cur; + int rtype = SOME_VALID; + + if (id < 1) + { + if (perr == TRUE) + semsg(_("E802: Invalid ID: %d (must be greater than or equal to 1)"), + id); + return -1; + } + while (cur != NULL && cur->id != id) + { + prev = cur; + cur = cur->next; + } + if (cur == NULL) + { + if (perr == TRUE) + semsg(_("E803: ID not found: %d"), id); + return -1; + } + if (cur == prev) + wp->w_match_head = cur->next; + else + prev->next = cur->next; + vim_regfree(cur->match.regprog); + vim_free(cur->pattern); + if (cur->pos.toplnum != 0) + { + if (wp->w_buffer->b_mod_set) + { + if (wp->w_buffer->b_mod_top > cur->pos.toplnum) + wp->w_buffer->b_mod_top = cur->pos.toplnum; + if (wp->w_buffer->b_mod_bot < cur->pos.botlnum) + wp->w_buffer->b_mod_bot = cur->pos.botlnum; + } + else + { + wp->w_buffer->b_mod_set = TRUE; + wp->w_buffer->b_mod_top = cur->pos.toplnum; + wp->w_buffer->b_mod_bot = cur->pos.botlnum; + wp->w_buffer->b_mod_xlines = 0; + } + rtype = VALID; + } + vim_free(cur); + redraw_win_later(wp, rtype); + return 0; +} + +/* + * Delete all matches in the match list of window 'wp'. + */ + void +clear_matches(win_T *wp) +{ + matchitem_T *m; + + while (wp->w_match_head != NULL) + { + m = wp->w_match_head->next; + vim_regfree(wp->w_match_head->match.regprog); + vim_free(wp->w_match_head->pattern); + vim_free(wp->w_match_head); + wp->w_match_head = m; + } + redraw_win_later(wp, SOME_VALID); +} + +/* + * Get match from ID 'id' in window 'wp'. + * Return NULL if match not found. + */ + static matchitem_T * +get_match(win_T *wp, int id) +{ + matchitem_T *cur = wp->w_match_head; + + while (cur != NULL && cur->id != id) + cur = cur->next; + return cur; +} + +/* + * Init for calling prepare_search_hl(). + */ + void +init_search_hl(win_T *wp, match_T *search_hl) +{ + matchitem_T *cur; + + // Setup for match and 'hlsearch' highlighting. Disable any previous + // match + cur = wp->w_match_head; + while (cur != NULL) + { + cur->hl.rm = cur->match; + if (cur->hlg_id == 0) + cur->hl.attr = 0; + else + cur->hl.attr = syn_id2attr(cur->hlg_id); + cur->hl.buf = wp->w_buffer; + cur->hl.lnum = 0; + cur->hl.first_lnum = 0; +# ifdef FEAT_RELTIME + // Set the time limit to 'redrawtime'. + profile_setlimit(p_rdt, &(cur->hl.tm)); +# endif + cur = cur->next; + } + search_hl->buf = wp->w_buffer; + search_hl->lnum = 0; + search_hl->first_lnum = 0; + // time limit is set at the toplevel, for all windows +} + +/* + * If there is a match fill "shl" and return one. + * Return zero otherwise. + */ + static int +next_search_hl_pos( + match_T *shl, // points to a match + linenr_T lnum, + posmatch_T *posmatch, // match positions + colnr_T mincol) // minimal column for a match +{ + int i; + int found = -1; + + for (i = posmatch->cur; i < MAXPOSMATCH; i++) + { + llpos_T *pos = &posmatch->pos[i]; + + if (pos->lnum == 0) + break; + if (pos->len == 0 && pos->col < mincol) + continue; + if (pos->lnum == lnum) + { + if (found >= 0) + { + // if this match comes before the one at "found" then swap + // them + if (pos->col < posmatch->pos[found].col) + { + llpos_T tmp = *pos; + + *pos = posmatch->pos[found]; + posmatch->pos[found] = tmp; + } + } + else + found = i; + } + } + posmatch->cur = 0; + if (found >= 0) + { + colnr_T start = posmatch->pos[found].col == 0 + ? 0 : posmatch->pos[found].col - 1; + colnr_T end = posmatch->pos[found].col == 0 + ? MAXCOL : start + posmatch->pos[found].len; + + shl->lnum = lnum; + shl->rm.startpos[0].lnum = 0; + shl->rm.startpos[0].col = start; + shl->rm.endpos[0].lnum = 0; + shl->rm.endpos[0].col = end; + shl->is_addpos = TRUE; + posmatch->cur = found + 1; + return 1; + } + return 0; +} + +/* + * Search for a next 'hlsearch' or match. + * Uses shl->buf. + * Sets shl->lnum and shl->rm contents. + * Note: Assumes a previous match is always before "lnum", unless + * shl->lnum is zero. + * Careful: Any pointers for buffer lines will become invalid. + */ + static void +next_search_hl( + win_T *win, + match_T *search_hl, + match_T *shl, // points to search_hl or a match + linenr_T lnum, + colnr_T mincol, // minimal column for a match + matchitem_T *cur) // to retrieve match positions if any +{ + linenr_T l; + colnr_T matchcol; + long nmatched; + int called_emsg_before = called_emsg; + + // for :{range}s/pat only highlight inside the range + if (lnum < search_first_line || lnum > search_last_line) + { + shl->lnum = 0; + return; + } + + if (shl->lnum != 0) + { + // Check for three situations: + // 1. If the "lnum" is below a previous match, start a new search. + // 2. If the previous match includes "mincol", use it. + // 3. Continue after the previous match. + l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum; + if (lnum > l) + shl->lnum = 0; + else if (lnum < l || shl->rm.endpos[0].col > mincol) + return; + } + + // Repeat searching for a match until one is found that includes "mincol" + // or none is found in this line. + for (;;) + { +# ifdef FEAT_RELTIME + // Stop searching after passing the time limit. + if (profile_passed_limit(&(shl->tm))) + { + shl->lnum = 0; // no match found in time + break; + } +# endif + // Three situations: + // 1. No useful previous match: search from start of line. + // 2. Not Vi compatible or empty match: continue at next character. + // Break the loop if this is beyond the end of the line. + // 3. Vi compatible searching: continue at end of previous match. + if (shl->lnum == 0) + matchcol = 0; + else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL + || (shl->rm.endpos[0].lnum == 0 + && shl->rm.endpos[0].col <= shl->rm.startpos[0].col)) + { + char_u *ml; + + matchcol = shl->rm.startpos[0].col; + ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol; + if (*ml == NUL) + { + ++matchcol; + shl->lnum = 0; + break; + } + if (has_mbyte) + matchcol += mb_ptr2len(ml); + else + ++matchcol; + } + else + matchcol = shl->rm.endpos[0].col; + + shl->lnum = lnum; + if (shl->rm.regprog != NULL) + { + // Remember whether shl->rm is using a copy of the regprog in + // cur->match. + int regprog_is_copy = (shl != search_hl && cur != NULL + && shl == &cur->hl + && cur->match.regprog == cur->hl.rm.regprog); + int timed_out = FALSE; + + nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, + matchcol, +#ifdef FEAT_RELTIME + &(shl->tm), &timed_out +#else + NULL, NULL +#endif + ); + // Copy the regprog, in case it got freed and recompiled. + if (regprog_is_copy) + cur->match.regprog = cur->hl.rm.regprog; + + if (called_emsg > called_emsg_before || got_int || timed_out) + { + // Error while handling regexp: stop using this regexp. + if (shl == search_hl) + { + // don't free regprog in the match list, it's a copy + vim_regfree(shl->rm.regprog); + set_no_hlsearch(TRUE); + } + shl->rm.regprog = NULL; + shl->lnum = 0; + got_int = FALSE; // avoid the "Type :quit to exit Vim" message + break; + } + } + else if (cur != NULL) + nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol); + else + nmatched = 0; + if (nmatched == 0) + { + shl->lnum = 0; // no match found + break; + } + if (shl->rm.startpos[0].lnum > 0 + || shl->rm.startpos[0].col >= mincol + || nmatched > 1 + || shl->rm.endpos[0].col > mincol) + { + shl->lnum += shl->rm.startpos[0].lnum; + break; // useful match found + } + } +} + +/* + * Advance to the match in window "wp" line "lnum" or past it. + */ + void +prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum) +{ + matchitem_T *cur; // points to the match list + match_T *shl; // points to search_hl or a match + int shl_flag; // flag to indicate whether search_hl + // has been processed or not + int pos_inprogress; // marks that position match search is + // in progress + int n; + + // When using a multi-line pattern, start searching at the top + // of the window or just after a closed fold. + // Do this both for search_hl and the match list. + cur = wp->w_match_head; + shl_flag = WIN_IS_POPUP(wp); // skip search_hl in a popup window + while (cur != NULL || shl_flag == FALSE) + { + if (shl_flag == FALSE) + { + shl = search_hl; + shl_flag = TRUE; + } + else + shl = &cur->hl; + if (shl->rm.regprog != NULL + && shl->lnum == 0 + && re_multiline(shl->rm.regprog)) + { + if (shl->first_lnum == 0) + { +# ifdef FEAT_FOLDING + for (shl->first_lnum = lnum; + shl->first_lnum > wp->w_topline; --shl->first_lnum) + if (hasFoldingWin(wp, shl->first_lnum - 1, + NULL, NULL, TRUE, NULL)) + break; +# else + shl->first_lnum = wp->w_topline; +# endif + } + if (cur != NULL) + cur->pos.cur = 0; + pos_inprogress = TRUE; + n = 0; + while (shl->first_lnum < lnum && (shl->rm.regprog != NULL + || (cur != NULL && pos_inprogress))) + { + next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n, + shl == search_hl ? NULL : cur); + pos_inprogress = cur == NULL || cur->pos.cur == 0 + ? FALSE : TRUE; + if (shl->lnum != 0) + { + shl->first_lnum = shl->lnum + + shl->rm.endpos[0].lnum + - shl->rm.startpos[0].lnum; + n = shl->rm.endpos[0].col; + } + else + { + ++shl->first_lnum; + n = 0; + } + } + } + if (shl != search_hl && cur != NULL) + cur = cur->next; + } +} + +/* + * Prepare for 'hlsearch' and match highlighting in one window line. + * Return TRUE if there is such highlighting and set "search_attr" to the + * current highlight attribute. + */ + int +prepare_search_hl_line( + win_T *wp, + linenr_T lnum, + colnr_T mincol, + char_u **line, + match_T *search_hl, + int *search_attr) +{ + matchitem_T *cur; // points to the match list + match_T *shl; // points to search_hl or a match + int shl_flag; // flag to indicate whether search_hl + // has been processed or not + int area_highlighting = FALSE; + + // Handle highlighting the last used search pattern and matches. + // Do this for both search_hl and the match list. + // Do not use search_hl in a popup window. + cur = wp->w_match_head; + shl_flag = WIN_IS_POPUP(wp); + while (cur != NULL || shl_flag == FALSE) + { + if (shl_flag == FALSE) + { + shl = search_hl; + shl_flag = TRUE; + } + else + shl = &cur->hl; + shl->startcol = MAXCOL; + shl->endcol = MAXCOL; + shl->attr_cur = 0; + shl->is_addpos = FALSE; + if (cur != NULL) + cur->pos.cur = 0; + next_search_hl(wp, search_hl, shl, lnum, mincol, + shl == search_hl ? NULL : cur); + + // Need to get the line again, a multi-line regexp may have made it + // invalid. + *line = ml_get_buf(wp->w_buffer, lnum, FALSE); + + if (shl->lnum != 0 && shl->lnum <= lnum) + { + if (shl->lnum == lnum) + shl->startcol = shl->rm.startpos[0].col; + else + shl->startcol = 0; + if (lnum == shl->lnum + shl->rm.endpos[0].lnum + - shl->rm.startpos[0].lnum) + shl->endcol = shl->rm.endpos[0].col; + else + shl->endcol = MAXCOL; + // Highlight one character for an empty match. + if (shl->startcol == shl->endcol) + { + if (has_mbyte && (*line)[shl->endcol] != NUL) + shl->endcol += (*mb_ptr2len)((*line) + shl->endcol); + else + ++shl->endcol; + } + if ((long)shl->startcol < mincol) // match at leftcol + { + shl->attr_cur = shl->attr; + *search_attr = shl->attr; + } + area_highlighting = TRUE; + } + if (shl != search_hl && cur != NULL) + cur = cur->next; + } + return area_highlighting; +} + +/* + * For a position in a line: Check for start/end of 'hlsearch' and other + * matches. + * After end, check for start/end of next match. + * When another match, have to check for start again. + * Watch out for matching an empty string! + * Return the updated search_attr. + */ + int +update_search_hl( + win_T *wp, + linenr_T lnum, + colnr_T col, + char_u **line, + match_T *search_hl, + int *has_match_conc UNUSED, + int *match_conc UNUSED, + int did_line_attr, + int lcs_eol_one) +{ + matchitem_T *cur; // points to the match list + match_T *shl; // points to search_hl or a match + int shl_flag; // flag to indicate whether search_hl + // has been processed or not + int pos_inprogress; // marks that position match search is in + // progress + int search_attr = 0; + + + // Do this for 'search_hl' and the match list (ordered by priority). + cur = wp->w_match_head; + shl_flag = WIN_IS_POPUP(wp); + while (cur != NULL || shl_flag == FALSE) + { + if (shl_flag == FALSE + && (cur == NULL + || cur->priority > SEARCH_HL_PRIORITY)) + { + shl = search_hl; + shl_flag = TRUE; + } + else + shl = &cur->hl; + if (cur != NULL) + cur->pos.cur = 0; + pos_inprogress = TRUE; + while (shl->rm.regprog != NULL || (cur != NULL && pos_inprogress)) + { + if (shl->startcol != MAXCOL + && col >= shl->startcol + && col < shl->endcol) + { + int next_col = col + mb_ptr2len(*line + col); + + if (shl->endcol < next_col) + shl->endcol = next_col; + shl->attr_cur = shl->attr; +# ifdef FEAT_CONCEAL + // Match with the "Conceal" group results in hiding + // the match. + if (cur != NULL + && shl != search_hl + && syn_name2id((char_u *)"Conceal") == cur->hlg_id) + { + *has_match_conc = col == shl->startcol ? 2 : 1; + *match_conc = cur->conceal_char; + } + else + *has_match_conc = 0; +# endif + } + else if (col == shl->endcol) + { + shl->attr_cur = 0; + next_search_hl(wp, search_hl, shl, lnum, col, + shl == search_hl ? NULL : cur); + pos_inprogress = !(cur == NULL || cur->pos.cur == 0); + + // Need to get the line again, a multi-line regexp may have + // made it invalid. + *line = ml_get_buf(wp->w_buffer, lnum, FALSE); + + if (shl->lnum == lnum) + { + shl->startcol = shl->rm.startpos[0].col; + if (shl->rm.endpos[0].lnum == 0) + shl->endcol = shl->rm.endpos[0].col; + else + shl->endcol = MAXCOL; + + if (shl->startcol == shl->endcol) + { + // highlight empty match, try again after + // it + if (has_mbyte) + shl->endcol += (*mb_ptr2len)(*line + shl->endcol); + else + ++shl->endcol; + } + + // Loop to check if the match starts at the + // current position + continue; + } + } + break; + } + if (shl != search_hl && cur != NULL) + cur = cur->next; + } + + // Use attributes from match with highest priority among 'search_hl' and + // the match list. + cur = wp->w_match_head; + shl_flag = WIN_IS_POPUP(wp); + while (cur != NULL || shl_flag == FALSE) + { + if (shl_flag == FALSE + && (cur == NULL || + cur->priority > SEARCH_HL_PRIORITY)) + { + shl = search_hl; + shl_flag = TRUE; + } + else + shl = &cur->hl; + if (shl->attr_cur != 0) + search_attr = shl->attr_cur; + if (shl != search_hl && cur != NULL) + cur = cur->next; + } + // Only highlight one character after the last column. + if (*(*line + col) == NUL && (did_line_attr >= 1 + || (wp->w_p_list && lcs_eol_one == -1))) + search_attr = 0; + return search_attr; +} + + int +get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol) +{ + long prevcol = curcol; + int prevcol_hl_flag = FALSE; + matchitem_T *cur; // points to the match list + + // we're not really at that column when skipping some text + if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol) + ++prevcol; + + if (!search_hl->is_addpos && prevcol == (long)search_hl->startcol) + prevcol_hl_flag = TRUE; + else + { + cur = wp->w_match_head; + while (cur != NULL) + { + if (!cur->hl.is_addpos && prevcol == (long)cur->hl.startcol) + { + prevcol_hl_flag = TRUE; + break; + } + cur = cur->next; + } + } + return prevcol_hl_flag; +} + +/* + * Get highlighting for the char after the text in "char_attr" from 'hlsearch' + * or match highlighting. + */ + void +get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr) +{ + matchitem_T *cur; // points to the match list + match_T *shl; // points to search_hl or a match + int shl_flag; // flag to indicate whether search_hl + // has been processed or not + + cur = wp->w_match_head; + shl_flag = WIN_IS_POPUP(wp); + while (cur != NULL || shl_flag == FALSE) + { + if (shl_flag == FALSE + && ((cur != NULL + && cur->priority > SEARCH_HL_PRIORITY) + || cur == NULL)) + { + shl = search_hl; + shl_flag = TRUE; + } + else + shl = &cur->hl; + if (col - 1 == (long)shl->startcol + && (shl == search_hl || !shl->is_addpos)) + *char_attr = shl->attr; + if (shl != search_hl && cur != NULL) + cur = cur->next; + } +} + +#endif // FEAT_SEARCH_EXTRA + +#if defined(FEAT_EVAL) || defined(PROTO) +# ifdef FEAT_SEARCH_EXTRA + static int +matchadd_dict_arg(typval_T *tv, char_u **conceal_char, win_T **win) +{ + dictitem_T *di; + + if (tv->v_type != VAR_DICT) + { + emsg(_(e_dictreq)); + return FAIL; + } + + if (dict_find(tv->vval.v_dict, (char_u *)"conceal", -1) != NULL) + *conceal_char = dict_get_string(tv->vval.v_dict, + (char_u *)"conceal", FALSE); + + if ((di = dict_find(tv->vval.v_dict, (char_u *)"window", -1)) != NULL) + { + *win = find_win_by_nr_or_id(&di->di_tv); + if (*win == NULL) + { + emsg(_(e_invalwindow)); + return FAIL; + } + } + + return OK; +} +#endif + +/* + * "clearmatches()" function + */ + void +f_clearmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_SEARCH_EXTRA + win_T *win = get_optional_window(argvars, 0); + + if (win != NULL) + clear_matches(win); +#endif +} + +/* + * "getmatches()" function + */ + void +f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +# ifdef FEAT_SEARCH_EXTRA + dict_T *dict; + matchitem_T *cur; + int i; + win_T *win = get_optional_window(argvars, 0); + + if (rettv_list_alloc(rettv) == FAIL || win == NULL) + return; + + cur = win->w_match_head; + while (cur != NULL) + { + dict = dict_alloc(); + if (dict == NULL) + return; + if (cur->match.regprog == NULL) + { + // match added with matchaddpos() + for (i = 0; i < MAXPOSMATCH; ++i) + { + llpos_T *llpos; + char buf[30]; // use 30 to avoid compiler warning + list_T *l; + + llpos = &cur->pos.pos[i]; + if (llpos->lnum == 0) + break; + l = list_alloc(); + if (l == NULL) + break; + list_append_number(l, (varnumber_T)llpos->lnum); + if (llpos->col > 0) + { + list_append_number(l, (varnumber_T)llpos->col); + list_append_number(l, (varnumber_T)llpos->len); + } + sprintf(buf, "pos%d", i + 1); + dict_add_list(dict, buf, l); + } + } + else + { + dict_add_string(dict, "pattern", cur->pattern); + } + dict_add_string(dict, "group", syn_id2name(cur->hlg_id)); + dict_add_number(dict, "priority", (long)cur->priority); + dict_add_number(dict, "id", (long)cur->id); +# if defined(FEAT_CONCEAL) + if (cur->conceal_char) + { + char_u buf[MB_MAXBYTES + 1]; + + buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL; + dict_add_string(dict, "conceal", (char_u *)&buf); + } +# endif + list_append_dict(rettv->vval.v_list, dict); + cur = cur->next; + } +# endif +} + +/* + * "setmatches()" function + */ + void +f_setmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_SEARCH_EXTRA + list_T *l; + listitem_T *li; + dict_T *d; + list_T *s = NULL; + win_T *win = get_optional_window(argvars, 1); + + rettv->vval.v_number = -1; + if (argvars[0].v_type != VAR_LIST) + { + emsg(_(e_listreq)); + return; + } + if (win == NULL) + return; + + if ((l = argvars[0].vval.v_list) != NULL) + { + // To some extent make sure that we are dealing with a list from + // "getmatches()". + li = l->lv_first; + while (li != NULL) + { + if (li->li_tv.v_type != VAR_DICT + || (d = li->li_tv.vval.v_dict) == NULL) + { + emsg(_(e_invarg)); + return; + } + if (!(dict_find(d, (char_u *)"group", -1) != NULL + && (dict_find(d, (char_u *)"pattern", -1) != NULL + || dict_find(d, (char_u *)"pos1", -1) != NULL) + && dict_find(d, (char_u *)"priority", -1) != NULL + && dict_find(d, (char_u *)"id", -1) != NULL)) + { + emsg(_(e_invarg)); + return; + } + li = li->li_next; + } + + clear_matches(win); + li = l->lv_first; + while (li != NULL) + { + int i = 0; + char buf[30]; // use 30 to avoid compiler warning + dictitem_T *di; + char_u *group; + int priority; + int id; + char_u *conceal; + + d = li->li_tv.vval.v_dict; + if (dict_find(d, (char_u *)"pattern", -1) == NULL) + { + if (s == NULL) + { + s = list_alloc(); + if (s == NULL) + return; + } + + // match from matchaddpos() + for (i = 1; i < 9; i++) + { + sprintf((char *)buf, (char *)"pos%d", i); + if ((di = dict_find(d, (char_u *)buf, -1)) != NULL) + { + if (di->di_tv.v_type != VAR_LIST) + return; + + list_append_tv(s, &di->di_tv); + s->lv_refcount++; + } + else + break; + } + } + + group = dict_get_string(d, (char_u *)"group", TRUE); + priority = (int)dict_get_number(d, (char_u *)"priority"); + id = (int)dict_get_number(d, (char_u *)"id"); + conceal = dict_find(d, (char_u *)"conceal", -1) != NULL + ? dict_get_string(d, (char_u *)"conceal", TRUE) + : NULL; + if (i == 0) + { + match_add(win, group, + dict_get_string(d, (char_u *)"pattern", FALSE), + priority, id, NULL, conceal); + } + else + { + match_add(win, group, NULL, priority, id, s, conceal); + list_unref(s); + s = NULL; + } + vim_free(group); + vim_free(conceal); + + li = li->li_next; + } + rettv->vval.v_number = 0; + } +#endif +} + +/* + * "matchadd()" function + */ + void +f_matchadd(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +# ifdef FEAT_SEARCH_EXTRA + char_u buf[NUMBUFLEN]; + char_u *grp = tv_get_string_buf_chk(&argvars[0], buf); // group + char_u *pat = tv_get_string_buf_chk(&argvars[1], buf); // pattern + int prio = 10; // default priority + int id = -1; + int error = FALSE; + char_u *conceal_char = NULL; + win_T *win = curwin; + + rettv->vval.v_number = -1; + + if (grp == NULL || pat == NULL) + return; + if (argvars[2].v_type != VAR_UNKNOWN) + { + prio = (int)tv_get_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) + { + id = (int)tv_get_number_chk(&argvars[3], &error); + if (argvars[4].v_type != VAR_UNKNOWN + && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL) + return; + } + } + if (error == TRUE) + return; + if (id >= 1 && id <= 3) + { + semsg(_("E798: ID is reserved for \":match\": %d"), id); + return; + } + + rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL, + conceal_char); +# endif +} + +/* + * "matchaddpos()" function + */ + void +f_matchaddpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +# ifdef FEAT_SEARCH_EXTRA + char_u buf[NUMBUFLEN]; + char_u *group; + int prio = 10; + int id = -1; + int error = FALSE; + list_T *l; + char_u *conceal_char = NULL; + win_T *win = curwin; + + rettv->vval.v_number = -1; + + group = tv_get_string_buf_chk(&argvars[0], buf); + if (group == NULL) + return; + + if (argvars[1].v_type != VAR_LIST) + { + semsg(_(e_listarg), "matchaddpos()"); + return; + } + l = argvars[1].vval.v_list; + if (l == NULL) + return; + + if (argvars[2].v_type != VAR_UNKNOWN) + { + prio = (int)tv_get_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) + { + id = (int)tv_get_number_chk(&argvars[3], &error); + + if (argvars[4].v_type != VAR_UNKNOWN + && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL) + return; + } + } + if (error == TRUE) + return; + + // id == 3 is ok because matchaddpos() is supposed to substitute :3match + if (id == 1 || id == 2) + { + semsg(_("E798: ID is reserved for \":match\": %d"), id); + return; + } + + rettv->vval.v_number = match_add(win, group, NULL, prio, id, l, + conceal_char); +# endif +} + +/* + * "matcharg()" function + */ + void +f_matcharg(typval_T *argvars UNUSED, typval_T *rettv) +{ + if (rettv_list_alloc(rettv) == OK) + { +# ifdef FEAT_SEARCH_EXTRA + int id = (int)tv_get_number(&argvars[0]); + matchitem_T *m; + + if (id >= 1 && id <= 3) + { + if ((m = (matchitem_T *)get_match(curwin, id)) != NULL) + { + list_append_string(rettv->vval.v_list, + syn_id2name(m->hlg_id), -1); + list_append_string(rettv->vval.v_list, m->pattern, -1); + } + else + { + list_append_string(rettv->vval.v_list, NULL, -1); + list_append_string(rettv->vval.v_list, NULL, -1); + } + } +# endif + } +} + +/* + * "matchdelete()" function + */ + void +f_matchdelete(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +# ifdef FEAT_SEARCH_EXTRA + win_T *win = get_optional_window(argvars, 1); + + if (win == NULL) + rettv->vval.v_number = -1; + else + rettv->vval.v_number = match_delete(win, + (int)tv_get_number(&argvars[0]), TRUE); +# endif +} +#endif + +#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO) +/* + * ":[N]match {group} {pattern}" + * Sets nextcmd to the start of the next command, if any. Also called when + * skipping commands to find the next command. + */ + void +ex_match(exarg_T *eap) +{ + char_u *p; + char_u *g = NULL; + char_u *end; + int c; + int id; + + if (eap->line2 <= 3) + id = eap->line2; + else + { + emsg(_(e_invcmd)); + return; + } + + // First clear any old pattern. + if (!eap->skip) + match_delete(curwin, id, FALSE); + + if (ends_excmd2(eap->cmd, eap->arg)) + end = eap->arg; + else if ((STRNICMP(eap->arg, "none", 4) == 0 + && (VIM_ISWHITE(eap->arg[4]) + || ends_excmd2(eap->arg, eap->arg + 4)))) + end = eap->arg + 4; + else + { + p = skiptowhite(eap->arg); + if (!eap->skip) + g = vim_strnsave(eap->arg, p - eap->arg); + p = skipwhite(p); + if (*p == NUL) + { + // There must be two arguments. + vim_free(g); + semsg(_(e_invarg2), eap->arg); + return; + } + end = skip_regexp(p + 1, *p, TRUE); + if (!eap->skip) + { + if (*end != NUL && !ends_excmd2(end, skipwhite(end + 1))) + { + vim_free(g); + eap->errmsg = e_trailing; + return; + } + if (*end != *p) + { + vim_free(g); + semsg(_(e_invarg2), p); + return; + } + + c = *end; + *end = NUL; + match_add(curwin, g, p + 1, 10, id, NULL, NULL); + vim_free(g); + *end = c; + } + } + eap->nextcmd = find_nextcmd(end); +} +#endif diff --git a/src/proto.h b/src/proto.h index eec7ad530a..8c768fc00e 100644 --- a/src/proto.h +++ b/src/proto.h @@ -104,6 +104,7 @@ extern int _stricoll(char *a, char *b); # include "main.pro" # include "map.pro" # include "mark.pro" +# include "match.pro" # include "memfile.pro" # include "memline.pro" # ifdef FEAT_MENU diff --git a/src/proto/highlight.pro b/src/proto/highlight.pro index 4a8ea3cca8..ca4498140c 100644 --- a/src/proto/highlight.pro +++ b/src/proto/highlight.pro @@ -43,19 +43,4 @@ void set_context_in_highlight_cmd(expand_T *xp, char_u *arg); char_u *get_highlight_name(expand_T *xp, int idx); char_u *get_highlight_name_ext(expand_T *xp, int idx, int skip_cleared); void free_highlight_fonts(void); -void clear_matches(win_T *wp); -void init_search_hl(win_T *wp, match_T *search_hl); -void prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum); -int prepare_search_hl_line(win_T *wp, linenr_T lnum, colnr_T mincol, char_u **line, match_T *search_hl, int *search_attr); -int update_search_hl(win_T *wp, linenr_T lnum, colnr_T col, char_u **line, match_T *search_hl, int *has_match_conc, int *match_conc, int did_line_attr, int lcs_eol_one); -int get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol); -void get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr); -void f_clearmatches(typval_T *argvars, typval_T *rettv); -void f_getmatches(typval_T *argvars, typval_T *rettv); -void f_setmatches(typval_T *argvars, typval_T *rettv); -void f_matchadd(typval_T *argvars, typval_T *rettv); -void f_matchaddpos(typval_T *argvars, typval_T *rettv); -void f_matcharg(typval_T *argvars, typval_T *rettv); -void f_matchdelete(typval_T *argvars, typval_T *rettv); -void ex_match(exarg_T *eap); /* vim: set ft=c : */ diff --git a/src/proto/match.pro b/src/proto/match.pro new file mode 100644 index 0000000000..37c21da7a6 --- /dev/null +++ b/src/proto/match.pro @@ -0,0 +1,17 @@ +/* match.c */ +void clear_matches(win_T *wp); +void init_search_hl(win_T *wp, match_T *search_hl); +void prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum); +int prepare_search_hl_line(win_T *wp, linenr_T lnum, colnr_T mincol, char_u **line, match_T *search_hl, int *search_attr); +int update_search_hl(win_T *wp, linenr_T lnum, colnr_T col, char_u **line, match_T *search_hl, int *has_match_conc, int *match_conc, int did_line_attr, int lcs_eol_one); +int get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol); +void get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr); +void f_clearmatches(typval_T *argvars, typval_T *rettv); +void f_getmatches(typval_T *argvars, typval_T *rettv); +void f_setmatches(typval_T *argvars, typval_T *rettv); +void f_matchadd(typval_T *argvars, typval_T *rettv); +void f_matchaddpos(typval_T *argvars, typval_T *rettv); +void f_matcharg(typval_T *argvars, typval_T *rettv); +void f_matchdelete(typval_T *argvars, typval_T *rettv); +void ex_match(exarg_T *eap); +/* vim: set ft=c : */ diff --git a/src/version.c b/src/version.c index d9a36e233b..4394d52b34 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1078, /**/ 1077, /**/ From d5053d015a957b343ad9c9e45e0abd2978f10cf0 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 28 Jun 2020 15:51:16 +0200 Subject: [PATCH 034/105] patch 8.2.1079: Vim9: no line break allowed in a while loop Problem: Vim9: no line break allowed in a while loop. Solution: Update stored loop lines when finding line breaks. --- src/eval.c | 20 ++++--- src/evalvars.c | 7 ++- src/ex_docmd.c | 98 ++++++++++++++++++++++++----------- src/globals.h | 2 +- src/proto/ex_docmd.pro | 3 +- src/structs.h | 3 +- src/testdir/test_vim9_cmd.vim | 23 ++++++-- src/version.c | 2 + 8 files changed, 113 insertions(+), 45 deletions(-) diff --git a/src/eval.c b/src/eval.c index 6aaa3a6ef0..82045675d0 100644 --- a/src/eval.c +++ b/src/eval.c @@ -170,8 +170,11 @@ eval_to_bool( CLEAR_FIELD(evalarg); evalarg.eval_flags = skip ? 0 : EVAL_EVALUATE; - evalarg.eval_cookie = eap != NULL && eap->getline == getsourceline - ? eap->cookie : NULL; + if (eap != NULL && getline_equal(eap->getline, eap->cookie, getsourceline)) + { + evalarg.eval_getline = eap->getline; + evalarg.eval_cookie = eap->cookie; + } if (skip) ++emsg_skip; @@ -1840,10 +1843,9 @@ eval_next_non_blank(char_u *arg, evalarg_T *evalarg, int *getnext) && evalarg != NULL && evalarg->eval_cookie != NULL && (*arg == NUL || (VIM_ISWHITE(arg[-1]) - && (*arg == '"' || *arg == '#'))) - && source_nextline(evalarg->eval_cookie) != NULL) + && (*arg == '"' || *arg == '#')))) { - char_u *p = source_nextline(evalarg->eval_cookie); + char_u *p = getline_peek(evalarg->eval_getline, evalarg->eval_cookie); if (p != NULL) { @@ -1863,7 +1865,7 @@ eval_next_line(evalarg_T *evalarg) garray_T *gap = &evalarg->eval_ga; char_u *line; - line = getsourceline(0, evalarg->eval_cookie, 0, TRUE); + line = evalarg->eval_getline(0, evalarg->eval_cookie, 0, TRUE); if (gap->ga_itemsize > 0 && ga_grow(gap, 1) == OK) { // Going to concatenate the lines after parsing. @@ -5206,7 +5208,11 @@ ex_echo(exarg_T *eap) CLEAR_FIELD(evalarg); evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE; - evalarg.eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL; + if (getline_equal(eap->getline, eap->cookie, getsourceline)) + { + evalarg.eval_getline = eap->getline; + evalarg.eval_cookie = eap->cookie; + } if (eap->skip) ++emsg_skip; diff --git a/src/evalvars.c b/src/evalvars.c index 1d6172ac7c..901d406a00 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -799,8 +799,11 @@ ex_let(exarg_T *eap) ++emsg_skip; CLEAR_FIELD(evalarg); evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE; - evalarg.eval_cookie = eap->getline == getsourceline - ? eap->cookie : NULL; + if (getline_equal(eap->getline, eap->cookie, getsourceline)) + { + evalarg.eval_getline = eap->getline; + evalarg.eval_cookie = eap->cookie; + } i = eval0(expr, &rettv, eap, &evalarg); if (eap->skip) --emsg_skip; diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 2469df341c..0ec63e24e4 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -629,6 +629,7 @@ do_cmdline( cstack_T cstack; // conditional stack garray_T lines_ga; // keep lines for ":while"/":for" int current_line = 0; // active line in lines_ga + int current_line_before = 0; char_u *fname = NULL; // function or script name linenr_T *breakpoint = NULL; // ptr to breakpoint field in cookie int *dbg_tick = NULL; // ptr to dbg_tick field in cookie @@ -851,27 +852,6 @@ do_cmdline( } # endif } - - if (cstack.cs_looplevel > 0) - { - // Inside a while/for loop we need to store the lines and use them - // again. Pass a different "fgetline" function to do_one_cmd() - // below, so that it stores lines in or reads them from - // "lines_ga". Makes it possible to define a function inside a - // while/for loop. - cmd_getline = get_loop_line; - cmd_cookie = (void *)&cmd_loop_cookie; - cmd_loop_cookie.lines_gap = &lines_ga; - cmd_loop_cookie.current_line = current_line; - cmd_loop_cookie.getline = fgetline; - cmd_loop_cookie.cookie = cookie; - cmd_loop_cookie.repeating = (current_line < lines_ga.ga_len); - } - else - { - cmd_getline = fgetline; - cmd_cookie = cookie; - } #endif // 2. If no line given, get an allocated line with fgetline(). @@ -929,21 +909,44 @@ do_cmdline( #ifdef FEAT_EVAL /* - * Save the current line when inside a ":while" or ":for", and when - * the command looks like a ":while" or ":for", because we may need it - * later. When there is a '|' and another command, it is stored - * separately, because we need to be able to jump back to it from an + * Inside a while/for loop, and when the command looks like a ":while" + * or ":for", the line is stored, because we may need it later when + * looping. + * + * When there is a '|' and another command, it is stored separately, + * because we need to be able to jump back to it from an * :endwhile/:endfor. + * + * Pass a different "fgetline" function to do_one_cmd() below, + * that it stores lines in or reads them from "lines_ga". Makes it + * possible to define a function inside a while/for loop and handles + * line continuation. */ - if (current_line == lines_ga.ga_len - && (cstack.cs_looplevel || has_loop_cmd(next_cmdline))) + if ((cstack.cs_looplevel > 0 || has_loop_cmd(next_cmdline))) { - if (store_loop_line(&lines_ga, next_cmdline) == FAIL) + cmd_getline = get_loop_line; + cmd_cookie = (void *)&cmd_loop_cookie; + cmd_loop_cookie.lines_gap = &lines_ga; + cmd_loop_cookie.current_line = current_line; + cmd_loop_cookie.getline = fgetline; + cmd_loop_cookie.cookie = cookie; + cmd_loop_cookie.repeating = (current_line < lines_ga.ga_len); + + // Save the current line when encountering it the first time. + if (current_line == lines_ga.ga_len + && store_loop_line(&lines_ga, next_cmdline) == FAIL) { retval = FAIL; break; } + current_line_before = current_line; } + else + { + cmd_getline = fgetline; + cmd_cookie = cookie; + } + did_endif = FALSE; #endif @@ -1078,7 +1081,7 @@ do_cmdline( else if (cstack.cs_lflags & CSL_HAD_LOOP) { cstack.cs_lflags &= ~CSL_HAD_LOOP; - cstack.cs_line[cstack.cs_idx] = current_line - 1; + cstack.cs_line[cstack.cs_idx] = current_line_before; } } @@ -1515,7 +1518,7 @@ getline_cookie( { #ifdef FEAT_EVAL char_u *(*gp)(int, void *, int, int); - struct loop_cookie *cp; + struct loop_cookie *cp; // When "fgetline" is "get_loop_line()" use the "cookie" to find the // cookie that's originally used to obtain the lines. This may be nested @@ -1533,6 +1536,41 @@ getline_cookie( #endif } +#if defined(FEAT_EVAL) || defined(PROT) +/* + * Get the next line source line without advancing. + */ + char_u * +getline_peek( + char_u *(*fgetline)(int, void *, int, int) UNUSED, + void *cookie) // argument for fgetline() +{ + char_u *(*gp)(int, void *, int, int); + struct loop_cookie *cp; + wcmd_T *wp; + + // When "fgetline" is "get_loop_line()" use the "cookie" to find the + // cookie that's originally used to obtain the lines. This may be nested + // several levels. + gp = fgetline; + cp = (struct loop_cookie *)cookie; + while (gp == get_loop_line) + { + if (cp->current_line + 1 < cp->lines_gap->ga_len) + { + // executing lines a second time, use the stored copy + wp = (wcmd_T *)(cp->lines_gap->ga_data) + cp->current_line + 1; + return wp->line; + } + gp = cp->getline; + cp = cp->cookie; + } + if (gp == getsourceline) + return source_nextline(cp); + return NULL; +} +#endif + /* * Helper function to apply an offset for buffer commands, i.e. ":bdelete", diff --git a/src/globals.h b/src/globals.h index 81903818b0..19c6837ce2 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1885,7 +1885,7 @@ EXTERN listitem_T range_list_item; // Passed to an eval() function to enable evaluation. EXTERN evalarg_T EVALARG_EVALUATE # ifdef DO_INIT - = {EVAL_EVALUATE, NULL, {0, 0, 0, 0, NULL}, NULL} + = {EVAL_EVALUATE, NULL, NULL, {0, 0, 0, 0, NULL}, NULL} # endif ; #endif diff --git a/src/proto/ex_docmd.pro b/src/proto/ex_docmd.pro index 7f5c691282..8f5ac10d4e 100644 --- a/src/proto/ex_docmd.pro +++ b/src/proto/ex_docmd.pro @@ -4,10 +4,11 @@ int do_cmdline_cmd(char_u *cmd); int do_cmdline(char_u *cmdline, char_u *(*fgetline)(int, void *, int, int), void *cookie, int flags); int getline_equal(char_u *(*fgetline)(int, void *, int, int), void *cookie, char_u *(*func)(int, void *, int, int)); void *getline_cookie(char_u *(*fgetline)(int, void *, int, int), void *cookie); +char_u *getline_peek(char_u *(*fgetline)(int, void *, int, int), void *cookie); int parse_command_modifiers(exarg_T *eap, char **errormsg, int skip_only); int parse_cmd_address(exarg_T *eap, char **errormsg, int silent); int checkforcmd(char_u **pp, char *cmd, int len); -char_u *find_ex_command(exarg_T *eap, int *full, void *((*lookup)(char_u *, size_t, cctx_T *)), cctx_T *cctx); +char_u *find_ex_command(exarg_T *eap, int *full, void *(*lookup)(char_u *, size_t, cctx_T *), cctx_T *cctx); int modifier_len(char_u *cmd); int cmd_exists(char_u *name); cmdidx_T excmd_get_cmdidx(char_u *cmd, int len); diff --git a/src/structs.h b/src/structs.h index d68bc0184e..fd5ae2aa21 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1761,7 +1761,8 @@ typedef struct { int eval_flags; // EVAL_ flag values below // copied from exarg_T when "getline" is "getsourceline". Can be NULL. - void *eval_cookie; // argument for getline() + char_u *(*eval_getline)(int, void *, int, int); + void *eval_cookie; // argument for eval_getline() // Used to collect lines while parsing them, so that they can be // concatenated later. Used when "eval_ga.ga_itemsize" is not zero. diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim index a818d14084..c0a4358d8d 100644 --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -131,12 +131,29 @@ def Test_if_linebreak() enddef def Test_while_linebreak() - " TODO: line break in :while expression doesn't work yet let lines =<< trim END vim9script let nr = 0 - while nr < 10 + 3 - nr = nr + 4 + while nr < + 10 + 3 + nr = nr + + 4 + endwhile + assert_equal(16, nr) + END + CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + let nr = 0 + while nr + < + 10 + + + 3 + nr = nr + + + 4 endwhile assert_equal(16, nr) END diff --git a/src/version.c b/src/version.c index 4394d52b34..8047cbe68a 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1079, /**/ 1078, /**/ From b7a78f7a6713f07d2fcad0b27dea22925c7b1cdf Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 28 Jun 2020 18:43:40 +0200 Subject: [PATCH 035/105] patch 8.2.1080: Vim9: no line break allowed in a for loop Problem: Vim9: no line break allowed in a for loop. Solution: Skip line breaks in for command. --- src/eval.c | 55 +++++++++++++++++++++++++---------- src/ex_eval.c | 25 ++++++++++++++-- src/globals.h | 2 +- src/proto/eval.pro | 3 +- src/structs.h | 5 ++-- src/testdir/test_vim9_cmd.vim | 31 ++++++++++++++++++++ src/userfunc.c | 1 + src/version.c | 2 ++ 8 files changed, 101 insertions(+), 23 deletions(-) diff --git a/src/eval.c b/src/eval.c index 82045675d0..141a97315b 100644 --- a/src/eval.c +++ b/src/eval.c @@ -38,6 +38,7 @@ typedef struct { int fi_semicolon; // TRUE if ending in '; var]' int fi_varcount; // nr of variables in the list + int fi_break_count; // nr of line breaks encountered listwatch_T fi_lw; // keep an eye on the item used. list_T *fi_list; // list being used int fi_bi; // index of blob @@ -344,6 +345,7 @@ eval_to_string_skip( } if (skip) --emsg_skip; + clear_evalarg(&EVALARG_EVALUATE, eap); return retval; } @@ -461,6 +463,7 @@ eval_to_string( retval = vim_strsave(tv_get_string(&tv)); clear_tv(&tv); } + clear_evalarg(&EVALARG_EVALUATE, NULL); return retval; } @@ -528,6 +531,7 @@ eval_expr(char_u *arg, exarg_T *eap) tv = ALLOC_ONE(typval_T); if (tv != NULL && eval0(arg, tv, eap, &EVALARG_EVALUATE) == FAIL) VIM_CLEAR(tv); + clear_evalarg(&EVALARG_EVALUATE, eap); return tv; } @@ -675,6 +679,7 @@ eval_foldexpr(char_u *arg, int *cp) if (use_sandbox) --sandbox; --textwinlock; + clear_evalarg(&EVALARG_EVALUATE, NULL); return (int)retval; } @@ -1481,16 +1486,14 @@ eval_for_line( char_u *arg, int *errp, exarg_T *eap, - int skip) + evalarg_T *evalarg) { forinfo_T *fi; char_u *expr; typval_T tv; list_T *l; - evalarg_T evalarg; + int skip = !(evalarg->eval_flags & EVAL_EVALUATE); - CLEAR_FIELD(evalarg); - evalarg.eval_flags = skip ? 0 : EVAL_EVALUATE; *errp = TRUE; // default: there is an error fi = ALLOC_CLEAR_ONE(forinfo_T); @@ -1501,8 +1504,9 @@ eval_for_line( if (expr == NULL) return fi; - expr = skipwhite(expr); - if (expr[0] != 'i' || expr[1] != 'n' || !VIM_ISWHITE(expr[2])) + expr = skipwhite_and_linebreak(expr, evalarg); + if (expr[0] != 'i' || expr[1] != 'n' + || !(expr[2] == NUL || VIM_ISWHITE(expr[2]))) { emsg(_(e_missing_in)); return fi; @@ -1510,7 +1514,8 @@ eval_for_line( if (skip) ++emsg_skip; - if (eval0(skipwhite(expr + 2), &tv, eap, &evalarg) == OK) + expr = skipwhite_and_linebreak(expr + 2, evalarg); + if (eval0(expr, &tv, eap, evalarg) == OK) { *errp = FALSE; if (!skip) @@ -1558,10 +1563,24 @@ eval_for_line( } if (skip) --emsg_skip; + fi->fi_break_count = evalarg->eval_break_count; return fi; } +/* + * Used when looping over a :for line, skip the "in expr" part. + */ + void +skip_for_lines(void *fi_void, evalarg_T *evalarg) +{ + forinfo_T *fi = (forinfo_T *)fi_void; + int i; + + for (i = 0; i < fi->fi_break_count; ++i) + eval_next_line(evalarg); +} + /* * Use the first item in a ":for" list. Advance to the next. * Assign the values to the variable (list). "arg" points to the first one. @@ -1866,6 +1885,7 @@ eval_next_line(evalarg_T *evalarg) char_u *line; line = evalarg->eval_getline(0, evalarg->eval_cookie, 0, TRUE); + ++evalarg->eval_break_count; if (gap->ga_itemsize > 0 && ga_grow(gap, 1) == OK) { // Going to concatenate the lines after parsing. @@ -1898,14 +1918,19 @@ skipwhite_and_linebreak(char_u *arg, evalarg_T *evalarg) void clear_evalarg(evalarg_T *evalarg, exarg_T *eap) { - if (evalarg != NULL && eap != NULL && evalarg->eval_tofree != NULL) + if (evalarg != NULL && evalarg->eval_tofree != NULL) { - // We may need to keep the original command line, e.g. for - // ":let" it has the variable names. But we may also need the - // new one, "nextcmd" points into it. Keep both. - vim_free(eap->cmdline_tofree); - eap->cmdline_tofree = *eap->cmdlinep; - *eap->cmdlinep = evalarg->eval_tofree; + if (eap != NULL) + { + // We may need to keep the original command line, e.g. for + // ":let" it has the variable names. But we may also need the + // new one, "nextcmd" points into it. Keep both. + vim_free(eap->cmdline_tofree); + eap->cmdline_tofree = *eap->cmdlinep; + *eap->cmdlinep = evalarg->eval_tofree; + } + else + vim_free(evalarg->eval_tofree); evalarg->eval_tofree = NULL; } } @@ -1961,8 +1986,6 @@ eval0( if (eap != NULL) eap->nextcmd = check_nextcmd(p); - clear_evalarg(evalarg, eap); - return ret; } diff --git a/src/ex_eval.c b/src/ex_eval.c index 6f6b8c2189..fea8c7fd6b 100644 --- a/src/ex_eval.c +++ b/src/ex_eval.c @@ -899,10 +899,16 @@ ex_eval(exarg_T *eap) CLEAR_FIELD(evalarg); evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE; - evalarg.eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL; + if (getline_equal(eap->getline, eap->cookie, getsourceline)) + { + evalarg.eval_getline = eap->getline; + evalarg.eval_cookie = eap->cookie; + } if (eval0(eap->arg, &tv, eap, &evalarg) == OK) clear_tv(&tv); + + clear_evalarg(&evalarg, eap); } /* @@ -1108,7 +1114,16 @@ ex_while(exarg_T *eap) } else { - void *fi; + void *fi; + evalarg_T evalarg; + + CLEAR_FIELD(evalarg); + evalarg.eval_flags = skip ? 0 : EVAL_EVALUATE; + if (getline_equal(eap->getline, eap->cookie, getsourceline)) + { + evalarg.eval_getline = eap->getline; + evalarg.eval_cookie = eap->cookie; + } /* * ":for var in list-expr" @@ -1119,11 +1134,14 @@ ex_while(exarg_T *eap) // previously evaluated list. fi = cstack->cs_forinfo[cstack->cs_idx]; error = FALSE; + + // the "in expr" is not used, skip over it + skip_for_lines(fi, &evalarg); } else { // Evaluate the argument and get the info in a structure. - fi = eval_for_line(eap->arg, &error, eap, skip); + fi = eval_for_line(eap->arg, &error, eap, &evalarg); cstack->cs_forinfo[cstack->cs_idx] = fi; } @@ -1138,6 +1156,7 @@ ex_while(exarg_T *eap) free_for_info(fi); cstack->cs_forinfo[cstack->cs_idx] = NULL; } + clear_evalarg(&evalarg, eap); } /* diff --git a/src/globals.h b/src/globals.h index 19c6837ce2..41f9781c10 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1885,7 +1885,7 @@ EXTERN listitem_T range_list_item; // Passed to an eval() function to enable evaluation. EXTERN evalarg_T EVALARG_EVALUATE # ifdef DO_INIT - = {EVAL_EVALUATE, NULL, NULL, {0, 0, 0, 0, NULL}, NULL} + = {EVAL_EVALUATE, 0, NULL, NULL, {0, 0, 0, 0, NULL}, NULL} # endif ; #endif diff --git a/src/proto/eval.pro b/src/proto/eval.pro index 88dd8a7538..0e9ffeb808 100644 --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -22,7 +22,8 @@ int eval_foldexpr(char_u *arg, int *cp); char_u *get_lval(char_u *name, typval_T *rettv, lval_T *lp, int unlet, int skip, int flags, int fne_flags); void clear_lval(lval_T *lp); void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, int flags, char_u *op); -void *eval_for_line(char_u *arg, int *errp, exarg_T *eap, int skip); +void *eval_for_line(char_u *arg, int *errp, exarg_T *eap, evalarg_T *evalarg); +void skip_for_lines(void *fi_void, evalarg_T *evalarg); int next_for_item(void *fi_void, char_u *arg); void free_for_info(void *fi_void); void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx); diff --git a/src/structs.h b/src/structs.h index fd5ae2aa21..cab6885e41 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1758,11 +1758,12 @@ typedef struct // Struct passed through eval() functions. // See EVALARG_EVALUATE for a fixed value with eval_flags set to EVAL_EVALUATE. typedef struct { - int eval_flags; // EVAL_ flag values below + int eval_flags; // EVAL_ flag values below + int eval_break_count; // nr of line breaks consumed // copied from exarg_T when "getline" is "getsourceline". Can be NULL. char_u *(*eval_getline)(int, void *, int, int); - void *eval_cookie; // argument for eval_getline() + void *eval_cookie; // argument for eval_getline() // Used to collect lines while parsing them, so that they can be // concatenated later. Used when "eval_ga.ga_itemsize" is not zero. diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim index c0a4358d8d..6fedf1fe9c 100644 --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -160,4 +160,35 @@ def Test_while_linebreak() CheckScriptSuccess(lines) enddef +def Test_for_linebreak() + let lines =<< trim END + vim9script + let nr = 0 + for x + in + [1, 2, 3, 4] + nr = nr + x + endfor + assert_equal(10, nr) + END + CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + let nr = 0 + for x + in + [1, 2, + 3, 4 + ] + nr = nr + + + x + endfor + assert_equal(10, nr) + END + CheckScriptSuccess(lines) +enddef + + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/userfunc.c b/src/userfunc.c index 51c437f208..05d3fda34c 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -3825,6 +3825,7 @@ ex_return(exarg_T *eap) if (eap->skip) --emsg_skip; + clear_evalarg(&evalarg, eap); } /* diff --git a/src/version.c b/src/version.c index 8047cbe68a..0c24515bc8 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1080, /**/ 1079, /**/ From a1f9f8666ed3a462eb8a518e78418c57dc41bbd4 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 28 Jun 2020 22:41:26 +0200 Subject: [PATCH 036/105] patch 8.2.1081: Lua: cannot use table.insert() and table.remove() Problem: Lua: cannot use table.insert() and table.remove(). Solution: Add the list functions. (Prabir Shrestha, closes #6353) --- runtime/doc/if_lua.txt | 16 ++++++++++++++-- src/if_lua.c | 31 ++++++++++++++++++++++--------- src/testdir/test_lua.vim | 26 ++++++++++++++++++++++++++ src/version.c | 2 ++ 4 files changed, 64 insertions(+), 11 deletions(-) diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt index f32be7c379..214179641d 100644 --- a/runtime/doc/if_lua.txt +++ b/runtime/doc/if_lua.txt @@ -227,8 +227,17 @@ Properties in Vim. o "l[k]" returns the k-th item in "l"; "l" is one-indexed, as in Lua. To modify the k-th item, simply do "l[k] = newitem"; in - particular, "l[k] = nil" removes the k-th item from "l". + particular, "l[k] = nil" removes the k-th item from "l". Item can + be added to the end of the list by "l[#l + 1] = newitem" o "l()" returns an iterator for "l". + o "table.insert(l, newitem)" inserts an item at the end of the list. + (only Lua 5.3 and later) + o "table.insert(l, position, newitem)" inserts an item at the + specified position. "position" is one-indexed. (only Lua 5.3 and + later) + o "table.remove(l, position)" removes an item at the specified + position. "position" is one-indexed. + Methods ------- @@ -246,8 +255,11 @@ Examples: :lua l[1] = nil -- remove first item :lua l:insert(true, 1) :lua print(l, #l, l[1], l[2]) + :lua l[#l + 1] = 'value' + :lua table.insert(l, 100) + :lua table.insert(l, 2, 200) + :lua table.remove(l, 1) :lua for item in l() do print(item) end -< ============================================================================== 4. Dict userdata *lua-dict* diff --git a/src/if_lua.c b/src/if_lua.c index 2d02f7c58d..c5fc683212 100644 --- a/src/if_lua.c +++ b/src/if_lua.c @@ -913,19 +913,32 @@ luaV_list_newindex(lua_State *L) if (l->lv_lock) luaL_error(L, "list is locked"); li = list_find(l, n); - if (li == NULL) return 0; - if (lua_isnil(L, 3)) // remove? + if (li == NULL) { - vimlist_remove(l, li, li); - listitem_free(l, li); + if (!lua_isnil(L, 3)) + { + typval_T v; + luaV_checktypval(L, 3, &v, "inserting list item"); + if (list_insert_tv(l, &v, li) == FAIL) + luaL_error(L, "failed to add item to list"); + clear_tv(&v); + } } else { - typval_T v; - luaV_checktypval(L, 3, &v, "setting list item"); - clear_tv(&li->li_tv); - copy_tv(&v, &li->li_tv); - clear_tv(&v); + if (lua_isnil(L, 3)) // remove? + { + vimlist_remove(l, li, li); + listitem_free(l, li); + } + else + { + typval_T v; + luaV_checktypval(L, 3, &v, "setting list item"); + clear_tv(&li->li_tv); + copy_tv(&v, &li->li_tv); + clear_tv(&v); + } } return 0; } diff --git a/src/testdir/test_lua.vim b/src/testdir/test_lua.vim index 9d5ad6864e..e1ad5004be 100644 --- a/src/testdir/test_lua.vim +++ b/src/testdir/test_lua.vim @@ -353,6 +353,32 @@ func Test_lua_list_table() call assert_fails('lua vim.list(true)', '[string "vim chunk"]:1: table expected, got boolean') endfunc +func Test_lua_list_table_insert_remove() + let luaver = split(split(luaeval('_VERSION'), ' ')[1], '\.') + let major = str2nr(luaver[0]) + let minor = str2nr(luaver[1]) + + if major < 5 || (major == 5 && minor < 3) + throw 'Skipped: Lua version < 5.3' + endif + + let l = [1, 2] + lua t = vim.eval('l') + lua table.insert(t, 10) + lua t[#t + 1] = 20 + lua table.insert(t, 2, 30) + call assert_equal(l, [1, 30, 2, 10, 20]) + lua table.remove(t, 2) + call assert_equal(l, [1, 2, 10, 20]) + lua t[3] = nil + call assert_equal(l, [1, 2, 20]) + lua removed_value = table.remove(t, 3) + call assert_equal(luaeval('removed_value'), 20) + lua t = nil + lua removed_value = nil + unlet l +endfunc + " Test l() i.e. iterator on list func Test_lua_list_iter() lua l = vim.list():add('foo'):add('bar') diff --git a/src/version.c b/src/version.c index 0c24515bc8..69c89ff443 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1081, /**/ 1080, /**/ From 91639195eff7b29213a0a3c279ac46e46ac76edd Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 29 Jun 2020 19:55:58 +0200 Subject: [PATCH 037/105] patch 8.2.1082: Coverity complains about ignoring dict_add() return value Problem: Coverity complains about ignoring dict_add() return value. Solution: Add (void). --- src/evalfunc.c | 8 ++++---- src/version.c | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/evalfunc.c b/src/evalfunc.c index 53584dc46a..6ca450306d 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -6272,7 +6272,7 @@ f_getreginfo(typval_T *argvars, typval_T *rettv) list = (list_T *)get_reg_contents(regname, GREG_EXPR_SRC | GREG_LIST); if (list == NULL) return; - dict_add_list(dict, "regcontents", list); + (void)dict_add_list(dict, "regcontents", list); buf[0] = NUL; buf[1] = NUL; @@ -6285,12 +6285,12 @@ f_getreginfo(typval_T *argvars, typval_T *rettv) reglen + 1); break; } - dict_add_string(dict, (char *)"regtype", buf); + (void)dict_add_string(dict, (char *)"regtype", buf); buf[0] = get_register_name(get_unname_register()); buf[1] = NUL; if (regname == '"') - dict_add_string(dict, (char *)"points_to", buf); + (void)dict_add_string(dict, (char *)"points_to", buf); else { dictitem_T *item = dictitem_alloc((char_u *)"isunnamed"); @@ -6300,7 +6300,7 @@ f_getreginfo(typval_T *argvars, typval_T *rettv) item->di_tv.v_type = VAR_SPECIAL; item->di_tv.vval.v_number = regname == buf[0] ? VVAL_TRUE : VVAL_FALSE; - dict_add(dict, item); + (void)dict_add(dict, item); } } } diff --git a/src/version.c b/src/version.c index 69c89ff443..9806b91f6a 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1082, /**/ 1081, /**/ From fda20c4cc59008264676a6deb6a3095ed0c248e0 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 29 Jun 2020 20:09:36 +0200 Subject: [PATCH 038/105] patch 8.2.1083: crash when using reduce() on a NULL list Problem: Crash when using reduce() on a NULL list. Solution: Only access the list when not NULL. --- src/list.c | 31 ++++++++++++++++++------------- src/testdir/test_listdict.vim | 3 +++ src/version.c | 2 ++ 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/list.c b/src/list.c index 56ed5fcdb6..ffcffa9f0c 100644 --- a/src/list.c +++ b/src/list.c @@ -2475,10 +2475,10 @@ f_reduce(typval_T *argvars, typval_T *rettv) list_T *l = argvars[0].vval.v_list; listitem_T *li = NULL; int r; - int prev_locked = l->lv_lock; int called_emsg_start = called_emsg; - CHECK_LIST_MATERIALIZE(l); + if (l != NULL) + CHECK_LIST_MATERIALIZE(l); if (argvars[2].v_type == VAR_UNKNOWN) { if (l == NULL || l->lv_first == NULL) @@ -2495,20 +2495,25 @@ f_reduce(typval_T *argvars, typval_T *rettv) if (l != NULL) li = l->lv_first; } - - l->lv_lock = VAR_FIXED; // disallow the list changing here copy_tv(&initial, rettv); - for ( ; li != NULL; li = li->li_next) + + if (l != NULL) { - argv[0] = *rettv; - argv[1] = li->li_tv; - rettv->v_type = VAR_UNKNOWN; - r = call_func(func_name, -1, rettv, 2, argv, &funcexe); - clear_tv(&argv[0]); - if (r == FAIL || called_emsg != called_emsg_start) - break; + int prev_locked = l->lv_lock; + + l->lv_lock = VAR_FIXED; // disallow the list changing here + for ( ; li != NULL; li = li->li_next) + { + argv[0] = *rettv; + argv[1] = li->li_tv; + rettv->v_type = VAR_UNKNOWN; + r = call_func(func_name, -1, rettv, 2, argv, &funcexe); + clear_tv(&argv[0]); + if (r == FAIL || called_emsg != called_emsg_start) + break; + } + l->lv_lock = prev_locked; } - l->lv_lock = prev_locked; } else { diff --git a/src/testdir/test_listdict.vim b/src/testdir/test_listdict.vim index 26b0e91e03..8a8d353289 100644 --- a/src/testdir/test_listdict.vim +++ b/src/testdir/test_listdict.vim @@ -718,6 +718,9 @@ func Test_reduce() call assert_fails("call reduce(g:lut, { acc, val -> EvilRemove() }, 1)", 'E742:') unlet g:lut delfunc EvilRemove + + call assert_equal(42, reduce(test_null_list(), function('add'), 42)) + call assert_equal(42, reduce(test_null_blob(), function('add'), 42)) endfunc " splitting a string to a List using split() diff --git a/src/version.c b/src/version.c index 9806b91f6a..2ffa111479 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1083, /**/ 1082, /**/ From 7d2ac92ebc36049f9ce2f4ce08b8a80ca212ace2 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 29 Jun 2020 20:20:33 +0200 Subject: [PATCH 039/105] patch 8.2.1084: Lua: registering function has useless code Problem: Lua: registering function has useless code. Solution: Remove clearing grow arrays. --- src/userfunc.c | 17 ++--------------- src/version.c | 2 ++ 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/src/userfunc.c b/src/userfunc.c index 05d3fda34c..724d1247d9 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -350,16 +350,11 @@ get_lambda_name(void) register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state) { char_u *name = get_lambda_name(); - ufunc_T *fp = NULL; - garray_T newargs; - garray_T newlines; - - ga_init(&newargs); - ga_init(&newlines); + ufunc_T *fp; fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1); if (fp == NULL) - goto errret; + return NULL; fp->uf_dfunc_idx = UF_NOT_COMPILED; fp->uf_refcount = 1; @@ -367,8 +362,6 @@ register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state) fp->uf_flags = FC_CFUNC; fp->uf_calls = 0; fp->uf_script_ctx = current_sctx; - fp->uf_lines = newlines; - fp->uf_args = newargs; fp->uf_cb = cb; fp->uf_cb_free = cb_free; fp->uf_cb_state = state; @@ -377,12 +370,6 @@ register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state) hash_add(&func_hashtab, UF2HIKEY(fp)); return name; - -errret: - ga_clear_strings(&newargs); - ga_clear_strings(&newlines); - vim_free(fp); - return NULL; } #endif diff --git a/src/version.c b/src/version.c index 2ffa111479..b3d0a9354e 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1084, /**/ 1083, /**/ From 6d90c61c5a6437ff5058b6c5874ba71bff574e60 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 29 Jun 2020 20:23:32 +0200 Subject: [PATCH 040/105] patch 8.2.1085: Coverity complains about ignoring dict_add() return value Problem: Coverity complains about ignoring dict_add() return value. Solution: Add (void). --- src/register.c | 10 +++++----- src/version.c | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/register.c b/src/register.c index 66dd0cca65..9d4354aff2 100644 --- a/src/register.c +++ b/src/register.c @@ -989,16 +989,16 @@ yank_do_autocmd(oparg_T *oap, yankreg_T *reg) for (n = 0; n < reg->y_size; n++) list_append_string(list, reg->y_array[n], -1); list->lv_lock = VAR_FIXED; - dict_add_list(v_event, "regcontents", list); + (void)dict_add_list(v_event, "regcontents", list); buf[0] = (char_u)oap->regname; buf[1] = NUL; - dict_add_string(v_event, "regname", buf); + (void)dict_add_string(v_event, "regname", buf); buf[0] = get_op_char(oap->op_type); buf[1] = get_extra_op_char(oap->op_type); buf[2] = NUL; - dict_add_string(v_event, "operator", buf); + (void)dict_add_string(v_event, "operator", buf); buf[0] = NUL; buf[1] = NUL; @@ -1011,9 +1011,9 @@ yank_do_autocmd(oparg_T *oap, yankreg_T *reg) reglen + 1); break; } - dict_add_string(v_event, "regtype", buf); + (void)dict_add_string(v_event, "regtype", buf); - dict_add_bool(v_event, "visual", oap->is_VIsual); + (void)dict_add_bool(v_event, "visual", oap->is_VIsual); // Lock the dictionary and its keys dict_set_items_ro(v_event); diff --git a/src/version.c b/src/version.c index b3d0a9354e..65dffff6b6 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1085, /**/ 1084, /**/ From cf30643ae607ae1a97b50e19c622dc8303723fa2 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 29 Jun 2020 20:40:37 +0200 Subject: [PATCH 041/105] patch 8.2.1086: possibly using freed memory when text properties used Problem: Possibly using freed memory when text properties used when changing indent of a line. Solution: Compute the offset before calling ml_replace(). --- src/indent.c | 16 ++++++++++------ src/version.c | 2 ++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/indent.c b/src/indent.c index a1d4d3628e..d786f26f26 100644 --- a/src/indent.c +++ b/src/indent.c @@ -757,6 +757,10 @@ set_indent( // Replace the line (unless undo fails). if (!(flags & SIN_UNDO) || u_savesub(curwin->w_cursor.lnum) == OK) { + colnr_T old_offset = (colnr_T)(p - oldline); + colnr_T new_offset = (colnr_T)(s - newline); + + // this may free "newline" ml_replace(curwin->w_cursor.lnum, newline, FALSE); if (flags & SIN_CHANGED) changed_bytes(curwin->w_cursor.lnum, 0); @@ -764,24 +768,24 @@ set_indent( // Correct saved cursor position if it is in this line. if (saved_cursor.lnum == curwin->w_cursor.lnum) { - if (saved_cursor.col >= (colnr_T)(p - oldline)) + if (saved_cursor.col >= old_offset) // cursor was after the indent, adjust for the number of // bytes added/removed - saved_cursor.col += ind_len - (colnr_T)(p - oldline); - else if (saved_cursor.col >= (colnr_T)(s - newline)) + saved_cursor.col += ind_len - old_offset; + else if (saved_cursor.col >= new_offset) // cursor was in the indent, and is now after it, put it back // at the start of the indent (replacing spaces with TAB) - saved_cursor.col = (colnr_T)(s - newline); + saved_cursor.col = new_offset; } #ifdef FEAT_PROP_POPUP { - int added = ind_len - (colnr_T)(p - oldline); + int added = ind_len - old_offset; // When increasing indent this behaves like spaces were inserted at // the old indent, when decreasing indent it behaves like spaces // were deleted at the new indent. adjust_prop_columns(curwin->w_cursor.lnum, - (colnr_T)(added > 0 ? (p - oldline) : ind_len), added, 0); + added > 0 ? old_offset : (colnr_T)ind_len, added, 0); } #endif retval = TRUE; diff --git a/src/version.c b/src/version.c index 65dffff6b6..6f6e4810b8 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1086, /**/ 1085, /**/ From 566cc8c72bb8036f015a435800f28ef9f6a9a3b6 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 29 Jun 2020 21:14:51 +0200 Subject: [PATCH 042/105] patch 8.2.1087: possible memory leak when file expansion fails Problem: Possible memory leak when file expansion fails. Solution: Clear the grow array when returning FAIL. Use an error message instead of an empty string. --- src/filepath.c | 7 ++++++- src/version.c | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/filepath.c b/src/filepath.c index 6644e9939d..a1e281b0ee 100644 --- a/src/filepath.c +++ b/src/filepath.c @@ -3813,8 +3813,13 @@ gen_expand_wildcards( vim_free(p); } + // When returning FAIL the array must be freed here. + if (retval == FAIL) + ga_clear(&ga); + *num_file = ga.ga_len; - *file = (ga.ga_data != NULL) ? (char_u **)ga.ga_data : (char_u **)""; + *file = (ga.ga_data != NULL) ? (char_u **)ga.ga_data + : (char_u **)_("no matches"); recursive = FALSE; diff --git a/src/version.c b/src/version.c index 6f6e4810b8..f38c978b2c 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1087, /**/ 1086, /**/ From 6378b21d6dd38cc0f80aa6d31d747db6c287483b Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 29 Jun 2020 21:32:06 +0200 Subject: [PATCH 043/105] patch 8.2.1088: a very long translation might cause a buffer overflow Problem: A very long translation might cause a buffer overflow. Solution: Trunctate the message if needed. --- src/fileio.c | 10 +++++++--- src/version.c | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/fileio.c b/src/fileio.c index 395e54b6a4..81a5026652 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -52,10 +52,14 @@ filemess( if (msg_silent != 0) return; msg_add_fname(buf, name); // put file name in IObuff with quotes + // If it's extremely long, truncate it. - if (STRLEN(IObuff) > IOSIZE - 80) - IObuff[IOSIZE - 80] = NUL; - STRCAT(IObuff, s); + if (STRLEN(IObuff) > IOSIZE - 100) + IObuff[IOSIZE - 100] = NUL; + + // Avoid an over-long translation to cause trouble. + STRNCAT(IObuff, s, 99); + /* * For the first message may have to start a new line. * For further ones overwrite the previous one, reset msg_scroll before diff --git a/src/version.c b/src/version.c index f38c978b2c..48ff321cab 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1088, /**/ 1087, /**/ From 927b7dd0fe9a0a82b39d600779edb4390ecdeda6 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 29 Jun 2020 22:24:56 +0200 Subject: [PATCH 044/105] patch 8.2.1089: Coverity warns for pointer computation Problem: Coverity warns for pointer computation. Solution: Avoid computing a pointer to invalid memory. --- src/spellfile.c | 5 ++--- src/version.c | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/spellfile.c b/src/spellfile.c index d8cf3d4849..fc365e2a03 100644 --- a/src/spellfile.c +++ b/src/spellfile.c @@ -5908,7 +5908,8 @@ mkspell( spin.si_newcompID = 127; // start compound ID at first maximum // default: fnames[0] is output file, following are input files - innames = &fnames[1]; + // When "fcount" is 1 there is only one file. + innames = &fnames[fcount == 1 ? 0 : 1]; incount = fcount - 1; wfname = alloc(MAXPATHL); @@ -5922,14 +5923,12 @@ mkspell( { // For ":mkspell path/en.latin1.add" output file is // "path/en.latin1.add.spl". - innames = &fnames[0]; incount = 1; vim_snprintf((char *)wfname, MAXPATHL, "%s.spl", fnames[0]); } else if (fcount == 1) { // For ":mkspell path/vim" output file is "path/vim.latin1.spl". - innames = &fnames[0]; incount = 1; vim_snprintf((char *)wfname, MAXPATHL, SPL_FNAME_TMPL, fnames[0], spin.si_ascii ? (char_u *)"ascii" : spell_enc()); diff --git a/src/version.c b/src/version.c index 48ff321cab..fdf0504f07 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1089, /**/ 1088, /**/ From 9b5384b97e832958573ffdcd3c1e230635b434e4 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 29 Jun 2020 22:31:36 +0200 Subject: [PATCH 045/105] patch 8.2.1090: may use NULL pointer when skipping over name Problem: May use NULL pointer when skipping over name. Solution: Always set ll_name_end. --- src/eval.c | 4 +++- src/version.c | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/eval.c b/src/eval.c index 141a97315b..5b69dc736d 100644 --- a/src/eval.c +++ b/src/eval.c @@ -734,7 +734,9 @@ get_lval( { // When skipping just find the end of the name. lp->ll_name = name; - return find_name_end(name, NULL, NULL, FNE_INCL_BR | fne_flags); + lp->ll_name_end = find_name_end(name, NULL, NULL, + FNE_INCL_BR | fne_flags); + return lp->ll_name_end; } // Find the end of the name. diff --git a/src/version.c b/src/version.c index fdf0504f07..e4d8694484 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1090, /**/ 1089, /**/ From d034220c54c63daaa2841e97b653842a47f5e90e Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 29 Jun 2020 22:40:42 +0200 Subject: [PATCH 046/105] patch 8.2.1091: no check if opening a pty works Problem: No check if opening a pty works. Solution: Check for invalid file descriptor. --- src/os_unix.c | 2 ++ src/version.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/os_unix.c b/src/os_unix.c index 095e3a78e2..a82256c676 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -5922,6 +5922,8 @@ mch_create_pty_channel(job_T *job, jobopt_T *options) channel_T *channel; open_pty(&pty_master_fd, &pty_slave_fd, &job->jv_tty_out, &job->jv_tty_in); + if (pty_master_fd < 0 || pty_slave_fd < 0) + return FAIL; close(pty_slave_fd); channel = add_channel(); diff --git a/src/version.c b/src/version.c index e4d8694484..82eb99036f 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1091, /**/ 1090, /**/ From cf070112ca2a6ac9ec5466be1cdc667f6abe8fd0 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 29 Jun 2020 23:02:21 +0200 Subject: [PATCH 047/105] patch 8.2.1092: not checking if saving for undo succeeds Problem: Not checking if saving for undo succeeds. Solution: Bail out if u_savesub() returns FAIL. --- src/textprop.c | 5 +++-- src/version.c | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/textprop.c b/src/textprop.c index 2145db262f..0645e1fd70 100644 --- a/src/textprop.c +++ b/src/textprop.c @@ -1350,8 +1350,9 @@ adjust_prop_columns( if (res.dirty) { // Save for undo if requested and not done yet. - if ((flags & APC_SAVE_FOR_UNDO) && !dirty) - u_savesub(lnum); + if ((flags & APC_SAVE_FOR_UNDO) && !dirty + && u_savesub(lnum) == FAIL) + return FALSE; dirty = TRUE; } if (res.can_drop) diff --git a/src/version.c b/src/version.c index 82eb99036f..cac669e425 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1092, /**/ 1091, /**/ From de19b745eee06a8a204988ae9989d97143caece9 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 29 Jun 2020 23:07:44 +0200 Subject: [PATCH 048/105] patch 8.2.1093: Python: double free when adding item to dict fails Problem: Python: double free when adding item to dict fails. Solution: Remove vim_free() call. --- src/if_py_both.h | 1 - src/version.c | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/if_py_both.h b/src/if_py_both.h index e262d1f75f..44b4baffe3 100644 --- a/src/if_py_both.h +++ b/src/if_py_both.h @@ -1913,7 +1913,6 @@ DictionaryAssItem( if (dict_add(dict, di) == FAIL) { - vim_free(di); dictitem_free(di); RAISE_KEY_ADD_FAIL(key); Py_XDECREF(todecref); diff --git a/src/version.c b/src/version.c index cac669e425..3697248306 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1093, /**/ 1092, /**/ From a53618dd1dd91c7bb67b5dfc83dc29371b47f55b Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 29 Jun 2020 23:14:02 +0200 Subject: [PATCH 049/105] patch 8.2.1094: dead code in libvterm Problem: Dead code in libvterm. Solution: Remove condition that is always true. --- src/libvterm/src/pen.c | 3 +-- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libvterm/src/pen.c b/src/libvterm/src/pen.c index 173f64f682..c5a0b71c5d 100644 --- a/src/libvterm/src/pen.c +++ b/src/libvterm/src/pen.c @@ -106,8 +106,7 @@ static int lookup_colour(const VTermState *state, int palette, const long args[] } lookup_colour_palette(state, args[0], col); - - return argcount ? 1 : 0; + return 1; default: DEBUG_LOG1("Unrecognised colour palette %d\n", palette); diff --git a/src/version.c b/src/version.c index 3697248306..83f5c2efe5 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1094, /**/ 1093, /**/ From 6b949615edac2dd33d5e865be8328561f296b045 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 29 Jun 2020 23:18:42 +0200 Subject: [PATCH 050/105] patch 8.2.1095: may use pointer after freeing it Problem: May use pointer after freeing it when text properties are used. Solution: Update redo buffer before calling ml_replace(). --- src/spellsuggest.c | 7 ++++--- src/version.c | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/spellsuggest.c b/src/spellsuggest.c index c03233f52e..6f9a756984 100644 --- a/src/spellsuggest.c +++ b/src/spellsuggest.c @@ -676,8 +676,6 @@ spell_suggest(int count) mch_memmove(p, line, c); STRCPY(p + c, stp->st_word); STRCAT(p, sug.su_badptr + stp->st_orglen); - ml_replace(curwin->w_cursor.lnum, p, FALSE); - curwin->w_cursor.col = c; // For redo we use a change-word command. ResetRedobuff(); @@ -686,7 +684,10 @@ spell_suggest(int count) stp->st_wordlen + sug.su_badlen - stp->st_orglen); AppendCharToRedobuff(ESC); - // After this "p" may be invalid. + // "p" may be freed here + ml_replace(curwin->w_cursor.lnum, p, FALSE); + curwin->w_cursor.col = c; + changed_bytes(curwin->w_cursor.lnum, c); } } diff --git a/src/version.c b/src/version.c index 83f5c2efe5..4406488bbd 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1095, /**/ 1094, /**/ From f151ad1c70825a91afb112e611db5c712e2656ef Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 30 Jun 2020 13:38:01 +0200 Subject: [PATCH 051/105] patch 8.2.1096: Vim9: return type of getqflist() is wrong Problem: Vim9: return type of getqflist() is wrong. Solution: Let the return type depend on the arguments. Also for getloclist(). (closes #6367) --- src/evalfunc.c | 28 ++++++++++++++++++++++++++-- src/testdir/test_vim9_func.vim | 16 ++++++++++++++++ src/version.c | 2 ++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/evalfunc.c b/src/evalfunc.c index 6ca450306d..b8f1c2c38e 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -347,6 +347,30 @@ ret_first_arg(int argcount, type_T **argtypes) return &t_void; } +/* + * Used for getqflist(): returns list if there is no argument, dict if there is + * one. + */ + static type_T * +ret_list_or_dict_0(int argcount, type_T **argtypes UNUSED) +{ + if (argcount > 0) + return &t_dict_any; + return &t_list_dict_any; +} + +/* + * Used for getloclist(): returns list if there is one argument, dict if there + * are two. + */ + static type_T * +ret_list_or_dict_1(int argcount, type_T **argtypes UNUSED) +{ + if (argcount > 1) + return &t_dict_any; + return &t_list_dict_any; +} + static type_T *ret_f_function(int argcount, type_T **argtypes); /* @@ -588,13 +612,13 @@ static funcentry_T global_functions[] = {"getimstatus", 0, 0, 0, ret_number, f_getimstatus}, {"getjumplist", 0, 2, FEARG_1, ret_list_any, f_getjumplist}, {"getline", 1, 2, FEARG_1, ret_f_getline, f_getline}, - {"getloclist", 1, 2, 0, ret_list_dict_any, f_getloclist}, + {"getloclist", 1, 2, 0, ret_list_or_dict_1, f_getloclist}, {"getmarklist", 0, 1, FEARG_1, ret_list_dict_any, f_getmarklist}, {"getmatches", 0, 1, 0, ret_list_dict_any, f_getmatches}, {"getmousepos", 0, 0, 0, ret_dict_number, f_getmousepos}, {"getpid", 0, 0, 0, ret_number, f_getpid}, {"getpos", 1, 1, FEARG_1, ret_list_number, f_getpos}, - {"getqflist", 0, 1, 0, ret_list_dict_any, f_getqflist}, + {"getqflist", 0, 1, 0, ret_list_or_dict_0, f_getqflist}, {"getreg", 0, 3, FEARG_1, ret_string, f_getreg}, {"getreginfo", 0, 1, FEARG_1, ret_dict_any, f_getreginfo}, {"getregtype", 0, 1, FEARG_1, ret_string, f_getregtype}, diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index fbe73f7dcf..1fc13c088d 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -837,6 +837,22 @@ def Test_sort_return_type() res = [1, 2, 3]->sort() enddef +def Test_getqflist_return_type() + let l = getqflist() + assert_equal([], l) + + let d = getqflist(#{items: 0}) + assert_equal(#{items: []}, d) +enddef + +def Test_getloclist_return_type() + let l = getloclist(1) + assert_equal([], l) + + let d = getloclist(1, #{items: 0}) + assert_equal(#{items: []}, d) +enddef + def Line_continuation_in_def(dir: string = ''): string let path: string = empty(dir) \ ? 'empty' diff --git a/src/version.c b/src/version.c index 4406488bbd..f0ed380301 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1096, /**/ 1095, /**/ From de8f0f47f653ff10bd8cc12b3e0817ed5bdf64ea Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 30 Jun 2020 18:45:43 +0200 Subject: [PATCH 052/105] patch 8.2.1097: highlight code not sufficiently tested Problem: Highlight code not sufficiently tested. Solution: Add a few more tests. (Yegappan Lakshmanan, closes #6359) --- src/testdir/test_filter_cmd.vim | 5 ++++ src/testdir/test_highlight.vim | 43 +++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 50 insertions(+) diff --git a/src/testdir/test_filter_cmd.vim b/src/testdir/test_filter_cmd.vim index de9a5d9046..278e7e4b6f 100644 --- a/src/testdir/test_filter_cmd.vim +++ b/src/testdir/test_filter_cmd.vim @@ -146,6 +146,11 @@ func Test_filter_commands() let res = split(execute("filter /\.c$/ marks"), "\n")[1:] call assert_equal([" A 1 0 file.c"], res) + " Test filtering :highlight command + highlight MyHlGroup ctermfg=10 + let res = split(execute("filter /MyHlGroup/ highlight"), "\n") + call assert_equal(["MyHlGroup xxx ctermfg=10"], res) + call setline(1, ['one', 'two', 'three']) 1mark a 2mark b diff --git a/src/testdir/test_highlight.vim b/src/testdir/test_highlight.vim index 723cb84767..3f84c12e69 100644 --- a/src/testdir/test_highlight.vim +++ b/src/testdir/test_highlight.vim @@ -761,4 +761,47 @@ func Test_highlight_RGB_color() hi clear endfunc +" Test for using default highlighting group +func Test_highlight_default() + highlight MySearch ctermfg=7 + highlight default MySearch ctermfg=5 + let hlSearch = HighlightArgs('MySearch') + call assert_match('ctermfg=7', hlSearch) + + highlight default QFName ctermfg=3 + call assert_match('ctermfg=3', HighlightArgs('QFName')) + hi clear +endfunc + +" Test for 'ctermul in a highlight group +func Test_highlight_ctermul() + CheckNotGui + call assert_notmatch('ctermul=', HighlightArgs('Normal')) + highlight Normal ctermul=3 + call assert_match('ctermul=3', HighlightArgs('Normal')) + highlight Normal ctermul=NONE +endfunc + +" Test for specifying 'start' and 'stop' in a highlight group +func Test_highlight_start_stop() + hi HlGrp1 start=[27h;[r; + call assert_match("start=^[[27h;^[[ r;", HighlightArgs('HlGrp1')) + hi HlGrp1 start=NONE + call assert_notmatch("start=", HighlightArgs('HlGrp1')) + hi HlGrp2 stop=[27h;[r; + call assert_match("stop=^[[27h;^[[ r;", HighlightArgs('HlGrp2')) + hi HlGrp2 stop=NONE + call assert_notmatch("stop=", HighlightArgs('HlGrp2')) + hi clear +endfunc + +" Test for setting various 'term' attributes +func Test_highlight_term_attr() + hi HlGrp3 term=bold,underline,undercurl,strikethrough,reverse,italic,standout + call assert_equal('hi HlGrp3 term=bold,standout,underline,undercurl,italic,reverse,strikethrough', HighlightArgs('HlGrp3')) + hi HlGrp3 term=NONE + call assert_equal('hi HlGrp3 cleared', HighlightArgs('HlGrp3')) + hi clear +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index f0ed380301..c9815bc639 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1097, /**/ 1096, /**/ From e46a4405056276b4cbdacee76b11f85c8ea1830b Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 30 Jun 2020 20:38:27 +0200 Subject: [PATCH 053/105] Runtime file updates --- runtime/doc/eval.txt | 98 +-- runtime/doc/if_lua.txt | 2 +- runtime/doc/tags | 2 + runtime/doc/todo.txt | 124 ++-- runtime/doc/vim9.txt | 45 +- runtime/filetype.vim | 2 +- runtime/syntax/neomuttrc.vim | 1250 ++++++++++++++-------------------- 7 files changed, 667 insertions(+), 856 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 6057b57c63..10b57b7b87 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 8.2. Last change: 2020 Jun 17 +*eval.txt* For Vim version 8.2. Last change: 2020 Jun 30 VIM REFERENCE MANUAL by Bram Moolenaar @@ -157,7 +157,7 @@ You will not get an error if you try to change the type of a variable. 1.2 Function references ~ - *Funcref* *E695* *E718* + *Funcref* *E695* *E718* A Funcref variable is obtained with the |function()| function, the |funcref()| function or created with the lambda expression |expr-lambda|. It can be used in an expression in the place of a function name, before the parenthesis @@ -2511,13 +2511,15 @@ getjumplist([{winnr} [, {tabnr}]]) List list of jump list items getline({lnum}) String line {lnum} of current buffer getline({lnum}, {end}) List lines {lnum} to {end} of current buffer -getloclist({nr} [, {what}]) List list of location list items +getloclist({nr}) List list of location list items +getloclist({nr}, {what}) Dict get specific location list properties getmarklist([{expr}]) List list of global/local marks getmatches([{win}]) List list of current matches getmousepos() Dict last known mouse position getpid() Number process ID of Vim getpos({expr}) List position of cursor, mark, etc. -getqflist([{what}]) List list of quickfix items +getqflist() List list of quickfix items +getqflist({what}) Dict get specific quickfix list properties getreg([{regname} [, 1 [, {list}]]]) String or List contents of a register getreginfo([{regname}]) Dict information about a register @@ -2756,12 +2758,15 @@ setcmdpos({pos}) Number set cursor position in command-line setenv({name}, {val}) none set environment variable setfperm({fname}, {mode}) Number set {fname} file permissions to {mode} setline({lnum}, {line}) Number set line {lnum} to {line} -setloclist({nr}, {list} [, {action} [, {what}]]) - Number modify location list using {list} +setloclist({nr}, {list} [, {action}]) + Number modify location list using {list} +setloclist({nr}, {list}, {action}, {what}) + Number modify specific location list props setmatches({list} [, {win}]) Number restore a list of matches setpos({expr}, {list}) Number set the {expr} position to {list} -setqflist({list} [, {action} [, {what}]]) - Number modify quickfix list using {list} +setqflist({list} [, {action}]) Number modify quickfix list using {list} +setqflist({list}, {action}, {what}) + Number modify specific quickfix list props setreg({n}, {v} [, {opt}]) Number set register to value and type settabvar({nr}, {varname}, {val}) none set {varname} in tab page {nr} to {val} settabwinvar({tabnr}, {winnr}, {varname}, {val}) @@ -3635,7 +3640,7 @@ complete_check() *complete_check()* *complete_info()* complete_info([{what}]) - Returns a Dictionary with information about Insert mode + Returns a |Dictionary| with information about Insert mode completion. See |ins-completion|. The items are: mode Current completion mode name string. @@ -4862,7 +4867,7 @@ getbufinfo([{dict}]) Without an argument information about all the buffers is returned. - When the argument is a Dictionary only the buffers matching + When the argument is a |Dictionary| only the buffers matching the specified criteria are returned. The following keys can be specified in {dict}: buflisted include only listed buffers. @@ -5393,7 +5398,7 @@ getline({lnum} [, {end}]) < To get lines from another buffer see |getbufline()| getloclist({nr} [, {what}]) *getloclist()* - Returns a list with all the entries in the location list for + Returns a |List| with all the entries in the location list for window {nr}. {nr} can be the window number or the |window-ID|. When {nr} is zero the current window is used. @@ -5415,6 +5420,14 @@ getloclist({nr} [, {what}]) *getloclist()* |location-list-file-window| for more details. + Returns an empty Dictionary if there is no location list for + the window {nr} or the window is not present. + + Examples (See also |getqflist-examples|): > + :echo getloclist(3, {'all': 0}) + :echo getloclist(5, {'filewinid': 0}) + + getmarklist([{expr}] *getmarklist()* Without the {expr} argument returns a |List| with information about all the global marks. |mark| @@ -5459,7 +5472,7 @@ getmatches([{win}]) *getmatches()* :unlet m < getmousepos() *getmousepos()* - Returns a Dictionary with the last known position of the + Returns a |Dictionary| with the last known position of the mouse. This can be used in a mapping for a mouse click or in a filter of a popup window. The items are: screenrow screen row @@ -5679,12 +5692,12 @@ getregtype([{regname}]) *getregtype()* gettabinfo([{arg}]) *gettabinfo()* If {arg} is not specified, then information about all the tab - pages is returned as a List. Each List item is a Dictionary. + pages is returned as a |List|. Each List item is a |Dictionary|. Otherwise, {arg} specifies the tab page number and information about that one is returned. If the tab page does not exist an empty List is returned. - Each List item is a Dictionary with the following entries: + Each List item is a |Dictionary| with the following entries: tabnr tab page number. variables a reference to the dictionary with tabpage-local variables @@ -5712,7 +5725,7 @@ gettabwinvar({tabnr}, {winnr}, {varname} [, {def}]) *gettabwinvar()* When {varname} is empty a dictionary with all window-local variables is returned. When {varname} is equal to "&" get the values of all - window-local options in a Dictionary. + window-local options in a |Dictionary|. Otherwise, when {varname} starts with "&" get the value of a window-local option. Note that {varname} must be the name without "w:". @@ -5767,16 +5780,16 @@ gettagstack([{nr}]) *gettagstack()* GetWinnr()->gettagstack() getwininfo([{winid}]) *getwininfo()* - Returns information about windows as a List with Dictionaries. + Returns information about windows as a |List| with Dictionaries. If {winid} is given Information about the window with that ID - is returned, as a List with one item. If the window does not + is returned, as a |List| with one item. If the window does not exist the result is an empty list. Without {winid} information about all the windows in all the tab pages is returned. - Each List item is a Dictionary with the following entries: + Each List item is a |Dictionary| with the following entries: botline last displayed buffer line bufnr number of buffer in the window height window height (excluding winbar) @@ -5804,7 +5817,7 @@ getwininfo([{winid}]) *getwininfo()* GetWinnr()->getwininfo() getwinpos([{timeout}]) *getwinpos()* - The result is a List with two numbers, the result of + The result is a |List| with two numbers, the result of |getwinposx()| and |getwinposy()| combined: [x-pos, y-pos] {timeout} can be used to specify how long to wait in msec for @@ -5859,7 +5872,7 @@ glob({expr} [, {nosuf} [, {list} [, {alllinks}]]]) *glob()* 'suffixes' affect the ordering of matches. 'wildignorecase' always applies. - When {list} is present and it is |TRUE| the result is a List + When {list} is present and it is |TRUE| the result is a |List| with all matching files. The advantage of using a List is, you also get filenames containing newlines correctly. Otherwise the result is a String and when there are several @@ -5922,7 +5935,7 @@ globpath({path}, {expr} [, {nosuf} [, {list} [, {alllinks}]]]) one of the patterns in 'wildignore' will be skipped and 'suffixes' affect the ordering of matches. - When {list} is present and it is |TRUE| the result is a List + When {list} is present and it is |TRUE| the result is a |List| with all matching files. The advantage of using a List is, you also get filenames containing newlines correctly. Otherwise the result is a String and when there are several matches, @@ -7281,7 +7294,7 @@ matchstrpos({expr}, {pat} [, {start} [, {count}]]) *matchstrpos()* *max()* max({expr}) Return the maximum value of all items in {expr}. - {expr} can be a List or a Dictionary. For a Dictionary, + {expr} can be a |List| or a |Dictionary|. For a Dictionary, it returns the maximum of all values in the Dictionary. If {expr} is neither a List nor a Dictionary, or one of the items in {expr} cannot be used as a Number this results in @@ -7352,7 +7365,7 @@ menu_info({name} [, {mode}]) *menu_info()* < *min()* min({expr}) Return the minimum value of all items in {expr}. - {expr} can be a List or a Dictionary. For a Dictionary, + {expr} can be a |List| or a |Dictionary|. For a Dictionary, it returns the minimum of all values in the Dictionary. If {expr} is neither a List nor a Dictionary, or one of the items in {expr} cannot be used as a Number this results in @@ -8014,7 +8027,7 @@ readdirex({directory} [, {expr} [, {dict}]]) *readdirex()* If {expr} results in 1 then this entry will be added to the list. The entries "." and ".." are always excluded. - Each time {expr} is evaluated |v:val| is set to a Dictionary + Each time {expr} is evaluated |v:val| is set to a |Dictionary| of the entry. When {expr} is a function the entry is passed as the argument. For example, to get a list of files ending in ".txt": > @@ -8263,7 +8276,7 @@ remove({list}, {idx} [, {end}]) *remove()* Without {end}: Remove the item at {idx} from |List| {list} and return the item. With {end}: Remove items from {idx} to {end} (inclusive) and - return a List with these items. When {idx} points to the same + return a |List| with these items. When {idx} points to the same item as {end} a list with one item is returned. When {end} points to an item before {idx} this is an error. See |list-index| for possible values of {idx} and {end}. @@ -8400,7 +8413,7 @@ screenchar({row}, {col}) *screenchar()* GetRow()->screenchar(col) screenchars({row}, {col}) *screenchars()* - The result is a List of Numbers. The first number is the same + The result is a |List| of Numbers. The first number is the same as what |screenchar()| returns. Further numbers are composing characters on top of the base character. This is mainly to be used for testing. @@ -8565,7 +8578,7 @@ searchcount([{options}]) *searchcount()* without the "S" flag in 'shortmess'. This works even if 'shortmess' does contain the "S" flag. - This returns a Dictionary. The dictionary is empty if the + This returns a |Dictionary|. The dictionary is empty if the previous pattern was not set and "pattern" was not specified. key type meaning ~ @@ -8647,7 +8660,7 @@ searchcount([{options}]) *searchcount()* " search again call searchcount() < - {options} must be a Dictionary. It can contain: + {options} must be a |Dictionary|. It can contain: key type meaning ~ recompute |Boolean| if |TRUE|, recompute the count like |n| or |N| was executed. @@ -9186,10 +9199,12 @@ setqflist({list} [, {action} [, {what}]]) *setqflist()* setreg({regname}, {value} [, {options}]) Set the register {regname} to {value}. If {regname} is "" or "@", the unnamed register '"' is used. + {value} may be any value returned by |getreg()| or |getreginfo()|, including a |List| or |Dict|. If {options} contains "a" or {regname} is upper case, then the value is appended. + {options} can also contain a register type specification: "c" or "v" |characterwise| mode "l" or "V" |linewise| mode @@ -9220,7 +9235,7 @@ setreg({regname}, {value} [, {options}]) register: > :let var_a = getreginfo() :call setreg('a', var_a) -< or: +< or: > :let var_a = getreg('a', 1, 1) :let var_amode = getregtype('a') .... @@ -9697,13 +9712,13 @@ state([{what}]) *state()* something is busy: m halfway a mapping, :normal command, feedkeys() or stuffed command - o operator pending or waiting for a command argument, - e.g. after |f| + o operator pending, e.g. after |d| a Insert mode autocomplete active x executing an autocommand w blocked on waiting, e.g. ch_evalexpr(), ch_read() and - ch_readraw() when reading json. - S not triggering SafeState or SafeStateAgain + ch_readraw() when reading json + S not triggering SafeState or SafeStateAgain, e.g. after + |f| or a count c callback invoked, including timer (repeats for recursiveness up to "ccc") s screen has scrolled for messages @@ -9884,7 +9899,7 @@ string({expr}) Return {expr} converted to a String. If {expr} is a Number, List [item, item] Dictionary {key: value, key: value} - When a List or Dictionary has a recursive reference it is + When a |List| or |Dictionary| has a recursive reference it is replaced by "[...]" or "{...}". Using eval() on the result will then fail. @@ -10175,7 +10190,7 @@ synIDtrans({synID}) *synIDtrans()* :echo synID(line("."), col("."), 1)->synIDtrans()->synIDattr("fg") synconcealed({lnum}, {col}) *synconcealed()* - The result is a List with currently three items: + The result is a |List| with currently three items: 1. The first item in the list is 0 if the character at the position {lnum} and {col} is not part of a concealable region, 1 if it is. @@ -10220,7 +10235,7 @@ synstack({lnum}, {col}) *synstack()* system({expr} [, {input}]) *system()* *E677* Get the output of the shell command {expr} as a string. See - |systemlist()| to get the output as a List. + |systemlist()| to get the output as a |List|. When {input} is given and is a string this string is written to a file and passed as stdin to the command. The string is @@ -10482,7 +10497,7 @@ timer_info([{id}]) returned. When {id} is omitted information about all timers is returned. - For each timer the information is stored in a Dictionary with + For each timer the information is stored in a |Dictionary| with these items: "id" the timer ID "time" time the timer was started with @@ -10716,7 +10731,7 @@ undotree() *undotree()* undo blocks. The first item in the "entries" list is the oldest undo item. - Each List item is a Dictionary with these items: + Each List item is a |Dictionary| with these items: "seq" Undo sequence number. Same as what appears in |:undolist|. "time" Timestamp when the change happened. Use @@ -10928,7 +10943,7 @@ win_splitmove({nr}, {target} [, {options}]) *win_splitmove()* Returns zero for success, non-zero for failure. - {options} is a Dictionary with the following optional entries: + {options} is a |Dictionary| with the following optional entries: "vertical" When TRUE, the split is created vertically, like with |:vsplit|. "rightbelow" When TRUE, the split is made below or to the @@ -12093,8 +12108,9 @@ An assignment leaves out the `:let` command. |vim9-declaration| text... text... {endmarker} - Set internal variable {var-name} to a List containing - the lines of text bounded by the string {endmarker}. + Set internal variable {var-name} to a |List| + containing the lines of text bounded by the string + {endmarker}. {endmarker} must not contain white space. {endmarker} cannot start with a lower case character. The last line should end only with the {endmarker} diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt index 214179641d..65a2d60430 100644 --- a/runtime/doc/if_lua.txt +++ b/runtime/doc/if_lua.txt @@ -1,4 +1,4 @@ -*if_lua.txt* For Vim version 8.2. Last change: 2020 May 17 +*if_lua.txt* For Vim version 8.2. Last change: 2020 Jun 28 VIM REFERENCE MANUAL by Luis Carvalho diff --git a/runtime/doc/tags b/runtime/doc/tags index bdd63b2826..cfb8824f65 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -3894,6 +3894,7 @@ E103 diff.txt /*E103* E104 digraph.txt /*E104* E1042 vim9.txt /*E1042* E105 mbyte.txt /*E105* +E1050 vim9.txt /*E1050* E107 eval.txt /*E107* E108 eval.txt /*E108* E109 eval.txt /*E109* @@ -10002,6 +10003,7 @@ vim9-declaration vim9.txt /*vim9-declaration* vim9-declarations usr_46.txt /*vim9-declarations* vim9-differences vim9.txt /*vim9-differences* vim9-export vim9.txt /*vim9-export* +vim9-gotchas vim9.txt /*vim9-gotchas* vim9-import vim9.txt /*vim9-import* vim9-rationale vim9.txt /*vim9-rationale* vim9-scopes vim9.txt /*vim9-scopes* diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt index c9716beb25..95cf7a40e9 100644 --- a/runtime/doc/todo.txt +++ b/runtime/doc/todo.txt @@ -1,4 +1,4 @@ -*todo.txt* For Vim version 8.2. Last change: 2020 Jun 21 +*todo.txt* For Vim version 8.2. Last change: 2020 Jun 28 VIM REFERENCE MANUAL by Bram Moolenaar @@ -40,12 +40,31 @@ browser use: https://github.com/vim/vim/issues/1234 Include src/po/vim.pot ? -See if resizing a terminal can be fixed. - Vim9 script: +- line continuation at script level: + eval_to_string_skip(), test with :throw + eval1_emsg(), pass "eap", test with :execute, :echomsg, :echoerr + handle_subscript() - call_func_rettv() - get_func_tv() + func( + args arg) + callers of get_func_tv(): + eval_func() + ex_call() + function arguments, test assert_equal() with lambda, test :function + :import: + others + eval_index() +- test: + [1, + 2, + 3]->Func() + Making everything work: +- "nr += 4" gives "already defined" error. - Error for "g:var: string = 'value'" - Make func()->append('$') work - value is last argument, not first. #6305 +- in Vim9 script expressions are evaluated differently, not using a type. + e.g. "'' == 0" does not give an error and evaluates to true. - possible memory leak in test_vim9_func through compile_nested_function. - memory leaks in test_vim9_expr - memory leaks in test_vim9_script @@ -134,12 +153,6 @@ Further improvements: - compile "expr" and "call" expression of a channel in channel_exe_cmd()? Popup windows: -- With some sequence get get hidden finished terminal buffer. (#5768) - Cannot close popup terminal (#5744) - Buffer can't be wiped, gets status "aF". (#5764) - Is buf->nwindows incorrect? -- popup_clear() and popup_close() should close the terminal popup, and - make the buffer hidden. #5745 - Cursor not updated before a redraw, making it jump. (#5943) - With terminal in popup, allow for popup_hide() to temporarily hide it.? - Fire some autocommand event after a new popup window was created and @@ -274,6 +287,13 @@ The buffer list and windows are locked, no changes possible How about removing Atari MiNT support? src/Make_mint.mak, src/os_mint.h, matches with __MINT__ +Add the <=> (spaceship) operator and "cond ?< expr ?= expr ?> expr" + replace this: + let left = GetLeftFunc() + let right = GetRightFunc() + let res = left < right ? lower : left == right ? equal : upper + by: + let res = GetLeftFunc() <=> GetRightFunc() ?< lower ?= equal ?> upper Patch to make :q work with local arglist. (Christian Brabandt, #6286) Patch to fix drawing error with DirectX. (James Grant, #5688) @@ -495,9 +515,6 @@ window 2. User expects 10 to be added to size of window 2. (Daniel Steinberg, Would be nice to set tab-local values for 'diffexpr' and 'diffopt'. Use t:diffexpr_option t:diffopt_option? (#4782) -v:register isn't reset early enough, may be used by next command. -(Andy Massimino, #5294, possible fix in #5305) - Internal diff doesn't handle binary file like external diff does. (Mike Williams, 2018 Oct 30) @@ -580,9 +597,6 @@ buffer didn't change at all. Line numbers in profile are off when function was defined with ":execute". (Daniel Hahler, #4511) -Add a way to create an empty, hidden buffer. Like doing ":new|hide". -":let buf = bufcreate('name') - Session file contains absolute paths when "curdir" is removed form 'sessionoptions', making it impossible to have a session with a relative path. (#4450) @@ -1199,8 +1213,8 @@ Make a function to check for function-like type? Screen updated delayed when using CTRL-O u in Insert mode. (Barlik, #1191) Perhaps because status message? -Implement named arguments for functions: - func Foo(start, count = 1 all = 1) +Implement named arguments for functions with optional arguments: + func Foo(start, count = 1, all = 1) call Foo(12, all = 0) Add a command to take a range of lines, filter them and put the output @@ -1393,8 +1407,6 @@ Did maintainer reply? ml_get errors when reloading file. (Chris Desjardins, 2016 Apr 19) Also with latest version. -Cannot delete a file with square brackets with delete(). (#696) - Completion for input() does not expand environment variables. (chdiza, 2016 Jul 25, #948) @@ -1403,8 +1415,6 @@ names, shell commands and the like. (Kikuchan, 2010 Oct 14) Assume the system converts between the actual encoding of the filesystem to the system encoding (usually utf-8). -'hlsearch' interferes with a Conceal match. (Rom Grk, 2016 Aug 9) - MS-Windows: use WS_HIDE instead of SW_SHOWMINNOACTIVE in os_win32.c? Otherwise task flickers in taskbar. @@ -1416,8 +1426,6 @@ Have a way to get the call stack, in a function and from an exception. Second problem in #966: ins_compl_add_tv() uses get_dict_string() multiple times, overwrites the one buffer. (Nikolay Pavlov, 2016 Aug 5) -Possibly wrong value for seq_cur. (Florent Fayolle, 2016 May 15, #806) - Filetype plugin for awk. (Doug Kearns, 2016 Sep 5) Patch to improve map documentation. Issue #799. @@ -1436,8 +1444,6 @@ Reject the value? #710. When doing "vi buf.md" a BufNew autocommand for *.md is not triggered. Because of using the initial buffer? (Dun Peal, 2016 May 12) -Add redrawtabline command. (Naruhiko Nishino, 2016 Jun 11) - Neovim patch for utfc_ptr2char_len() https://github.com/neovim/neovim/pull/4574 No test, needs some work to include. @@ -1507,20 +1513,10 @@ If ":bd" also closes a Tab page then the " mark is not set. (Harm te Hennepe, Patch to avoid redrawing tabline when the popup menu is visible. (Christian Brabandt, 2016 Jan 28) -Patch to show search statistics. (Christian Brabandt, 2016 Jul 22) - When the CursorMovedI event triggers, and CTRL-X was typed, a script cannot restore the mode properly. (Andrew Stewart, 2016 Apr 20) Do not trigger the event? -Using ":windo" to set options in all windows has the side effect that it -changes the window layout and the current window. Make a variant that saves -and restores. Use in the matchparen plugin. -Perhaps we can use ":windo {cmd}"? -Patch to add to :windo, :bufdo, etc. (Christian Brabandt, 2015 Jan -6, 2nd message) -Alternative: ":keeppos" command modifier: ":keeppos windo {cmd}". - Patch to fix display of listchars on the cursorline. (Nayuri Aohime, 2013) Update suggested by Yasuhiro Matsumoto, 2014 Nov 25: https://gist.github.com/presuku/d3d6b230b9b6dcfc0477 @@ -1549,10 +1545,6 @@ Python: ":py raw_input('prompt')" doesn't work. (Manu Hack) Comparing nested structures with "==" uses a different comparator than when comparing individual items. -Also, "'' == 0" evaluates to true, which isn't nice. -Add "===" to have a strict comparison (type and value match). -Add "==*" (?) to have a value match, but no automatic conversion, and v:true -equals 1 and 1.0, v:false equals 0 and 0.0.? Using uninitialized memory. (Dominique Pelle, 2015 Nov 4) @@ -1723,8 +1715,6 @@ arguments. Problem with transparent and matchgroup. Issue #475 -Patch to add :arglocal and :arglists. (Marcin Szamotulski, 2014 Aug 6) - Spell files use a latin single quote. Unicode also has another single quote: 0x2019. (Ron Aaron, 2014 Apr 4) New OpenOffice spell files support this with ICONV. But they are not @@ -1751,8 +1741,11 @@ from? Problem with upwards search on Windows (works OK on Linux). (Brett Stahlman, 2014 Jun 8) -Include a plugin manager with Vim? Neobundle seems to be the best currently. +Include a plugin manager with Vim? vim-plug seems to be the best currently: +https://github.com/junegunn/vim-plug. Also Vundle: https://github.com/gmarik/vundle +Or minpac: https://github.com/k-takata/minpac, since it leverages the builtin +package feature. Long message about this from ZyX, 2014 Mar 23. And following replies. Also see http://vim-wiki.mawercer.de/wiki/topic/vim%20plugin%20managment.html User view: @@ -1807,6 +1800,7 @@ instead. (Samuel Ferencik, 2013 Sep 28) Patch for XDG base directory support. (Jean François Bignolles, 2014 Mar 4) Remark on the docs. Should not be a compile time feature. But then what? +Also see #2034. Completion of ":e" is ":earlier", should be ":edit". Complete to the matching command instead of doing this alphabetically. (Mikel Jorgensen) @@ -1868,6 +1862,10 @@ Patch to add {lhs} to :mapclear: clear all maps starting with {lhs}. Exception caused by argument of return is not caught by try/catch. (David Barnett, 2013 Nov 19) +Bug in try/catch: return with invalid compare throws error that isn't caught. +(ZyX, 2011 Jan 26) +try/catch not working for argument of return. (Matt Wozniski, 2008 Sep 15) +try/catch not working when inside a for loop. (ZyX, 2011 Jan 25) Patch to fix that 'cedit' is recognized after :normal. (Christian Brabandt, 2013 Mar 19, later message) @@ -1903,6 +1901,8 @@ process that is running. It might actually be some other program, e.g. after a reboot. patch to add "combine" flag to syntax commands. (so8res, 2012 Dec 6) +Patch to add "combine" to :syntax, combines highlight attributes. (Nate +Soares, 2012 Dec 3) Syntax update problem in one buffer opened in two windows, bottom window is not correctly updated. (Paul Harris, 2012 Feb 27) @@ -2090,9 +2090,6 @@ doesn't jump to the correct line with :cfirst. (ZyX, 2011 Sep 18) Behavior of i" and a" text objects isn't logical. (Ben Fritz, 2013 Nov 19) -Bug in try/catch: return with invalid compare throws error that isn't caught. -(ZyX, 2011 Jan 26) - When setting a local option value from the global value, add a script ID that indicates this, so that ":verbose set" can give a hint. Check with options in the help file. @@ -2281,9 +2278,6 @@ Add local time at start of --startuptime output. Requires configure check for localtime(). Use format year-month-day hr:min:sec. -Patch to add "combine" to :syntax, combines highlight attributes. (Nate -Soares, 2012 Dec 3) - Patch to make ":hi link" also take arguments. (Nate Soares, 2012 Dec 4) Shell not recognized properly if it ends in "csh -f". (James Vega, 2009 Nov 3) @@ -2478,6 +2472,9 @@ Sergey Khorev) Consider making YankRing or something else that keeps a list of yanked text part of standard Vim. The "1 to "9 registers are not sufficient. +6 When yanking into the unnamed registers several times, somehow make the + previous contents also available (like it's done for deleting). What + register names to use? g"1, g"2, etc.? After doing "su" $HOME can be the old user's home, thus ~root/file is not correct. Don't use it in the swap file. @@ -2684,10 +2681,6 @@ Problem with 'ts' set to 9 and 'showbreak' to ">>>". (Matthew Winn, 2007 Oct In the swapfile dialog, add a H(elp) option that gives more info about what each choice does. Similar to ":help swap-exists-choices" -try/catch not working for argument of return. (Matt Wozniski, 2008 Sep 15) - -try/catch not working when inside a for loop. (ZyX, 2011 Jan 25) - ":tab help" always opens a new tab, while ":help" re-uses an existing window. Would be more consistent when an existing tab is re-used. (Tony Mechelynck) @@ -3257,7 +3250,7 @@ Quickfix/Location List: ":grep" and ":helpgrep". More generic solution: support a filter (e.g., by calling a function). 7 Add a command that goes back to the position from before jumping to the - first quickfix location. ":cbefore"? + first quickfix location. Vi incompatibility: - Try new POSIX tests, made after my comments. (Geoff Clare, 2005 April 7) @@ -3294,7 +3287,6 @@ Vi incompatibility: 7 The ":map" command output overwrites the command. Perhaps it should keep the ":map" when it's used without arguments? 7 CTRL-L is not the end of a section? It is for Posix! Make it an option. -7 Implement 'prompt' option. Init to off when stdin is not a tty. 7 Add a way to send an email for a crashed edit session. Create a file when making changes (containing name of the swap file), delete it when writing the file. Supply a program that can check for crashed sessions (either @@ -3472,8 +3464,6 @@ GUI: Solaris 2.6. (Marley) 9 On Solaris: Using a "-geometry" argument, bigger than the window where Vim is started from, causes empty lines below the cmdline. (raf) -8 X11 GUI: When menu is disabled by excluding 'm' from 'guioptions', ALT key - should not be used to trigger a menu (like the Win32 version). 8 When setting 'langmenu', it should be effective immediately. Store both the English and the translated text in the menu structure. Re-generate the translation when 'langmenu' has changed. @@ -3512,10 +3502,6 @@ GUI: When the "+0+0" is omitted it works. 8 When starting an external command, and 'guipty' set, BS and DEL are mixed up. Set erase character somehow? -8 A dead circumflex followed by a space should give the '^' character - (Rommel). Look how xterm does this. - Also: Bednar has some code for dead key handling. - Also: Nedit 5.0.2 with USE_XMIM does it right. (Gaya) 8 The compose key doesn't work properly (Cepas). Both for Win32 and X11. 7 The cursor in an inactive window should be hollow. Currently it's not visible. @@ -3612,17 +3598,8 @@ Macintosh: "Small" problems: -- Can't disable terminal flow control, to enable the use of CTRL-S and - CTRL-Q. Add an option for it? - When using e_secure in do_one_cmd() mention the command being executed, otherwise it's not clear where it comes from. -- When the quickfix window is open and executing ":echo 'hello'" using the - Command-line window, the text is immediately removed by the redrawing. - (Michael Henry, 2008 Nov 1) - Generic solution: When redrawing while there is a message on the - cmdline, don't erase the display but draw over the existing text. - Other solution, redraw after closing the cmdline window, before executing - the command. 9 For Turkish vim_tolower() and vim_toupper() also need to use utf_ functions for characters below 0x80. (Sertacyildiz) 9 When the last edited file is a help file, using '0 in a new Vim doesn't @@ -4082,8 +4059,6 @@ Spell checking: - Considering Hunspell 1.1.4: What does MAXNGRAMSUGS do? Is COMPLEXPREFIXES necessary when we have flags for affixes? -- Support spelling words in CamelCase as if they were two separate words. - Requires some option to enable it. (Timothy Knox) - There is no Finnish spell checking file. For openoffice Voikko is now used, which is based on Malaga: http://home.arcor.de/bjoern-beutel/malaga/ (Teemu Likonen) @@ -4458,8 +4433,6 @@ Vim script language: 7 Execute a function with standard option values. No need to save and restore option values. Especially useful for new options. Problem: how to avoid a performance penalty (esp. for string options)? -8 Add referring to key options with "&t_xx". Both for "echo &t_xx" and - ":let &t_xx =". Useful for making portable mappings. - range for ":exec", pass it on to the executed command. (Webb) 8 ":{range}source": source the lines from the current file. You can already yank lines and use :@" to execute them. @@ -4690,8 +4663,6 @@ Messages: - Delete message after new command has been entered and have waited for key. Perhaps after ten seconds? - Make message history available in "msg" variables: msg1, msg2, .. msg9. -8 When reading from stdin allow suppressing the "reading from stdin" - message. 9 Check handling of overwriting of messages and delays: Very wrong: errors while redrawing cause endless loop. When switching to another file and screen scrolls because of the long @@ -5979,9 +5950,6 @@ Registers: 8 Add put command that overwrites existing text. Should also work for blocks. Useful to move text around in a table. Works like using "R ^R r" for every line. -6 When yanking into the unnamed registers several times, somehow make the - previous contents also available (like it's done for deleting). What - register names to use? g"1, g"2, etc.? - When appending to a register, also report the total resulting number of lines. Or just say "99 more lines yanked", add the "more". - When inserting a register in Insert mode with CTRL-R, don't insert comment diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt index 21bc542a17..b14ff0b101 100644 --- a/runtime/doc/vim9.txt +++ b/runtime/doc/vim9.txt @@ -1,4 +1,4 @@ -*vim9.txt* For Vim version 8.2. Last change: 2020 Jun 22 +*vim9.txt* For Vim version 8.2. Last change: 2020 Jun 24 VIM REFERENCE MANUAL by Bram Moolenaar @@ -378,6 +378,49 @@ string. > In Vim9 script one can use "true" for v:true and "false" for v:false. +What to watch out for ~ + *vim9-gotchas* +Vim9 was designed to be closer to often used programming languages, but at the +same time tries to support the legacy Vim commands. Some compromises had to +be made. Here is a summary of what might be unexpected. + +Ex command ranges need to be prefixed with a colon. > + -> " legacy Vim: shifts the previous line to the right + ->func() " Vim9: method call + :-> " Vim9: shifts the previous line to the right + + %s/a/b " legacy Vim: substitute on all lines + x = alongname + % another " Vim9: line continuation without a backslash + :%s/a/b " Vim9: substitute on all lines + +Functions defined with `:def` compile the whole function. Legacy functions +can bail out, and the following lines are not parsed: > + func Maybe() + if !has('feature') + return + endif + use-feature + endfunc +Vim9 functions are compiled as a whole: > + def Maybe() + if !has('feature') + return + endif + use-feature " May give compilation error + enddef +For a workaround, split it in two functions: > + func Maybe() + if has('feature') + call MaybyInner() + endif + endfunc + if has('feature') + def MaybeInner() + use-feature + enddef + endif + ============================================================================== 3. New style functions *fast-functions* diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 3778fe5ff8..7c76b12ee3 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1,7 +1,7 @@ " Vim support file to detect file types " " Maintainer: Bram Moolenaar -" Last Change: 2020 Jun 15 +" Last Change: 2020 Jun 25 " Listen very carefully, I will say this only once if exists("did_load_filetypes") diff --git a/runtime/syntax/neomuttrc.vim b/runtime/syntax/neomuttrc.vim index 14852c1e1d..bd73de49ea 100644 --- a/runtime/syntax/neomuttrc.vim +++ b/runtime/syntax/neomuttrc.vim @@ -2,10 +2,10 @@ " Language: NeoMutt setup files " Maintainer: Richard Russon " Previous Maintainer: Guillaume Brogi -" Last Change: 2019-11-18 +" Last Change: 2020-06-21 " Original version based on syntax/muttrc.vim -" This file covers NeoMutt 2019-11-02 +" This file covers NeoMutt 2020-06-19 " quit when a syntax file was already loaded if exists("b:current_syntax") @@ -19,23 +19,22 @@ set cpo&vim setlocal isk=@,48-57,_,- " handling optional variables -syntax match muttrcComment "^# .*$" contains=@Spell -syntax match muttrcComment "^#[^ ].*$" -syntax match muttrcComment "^#$" -syntax match muttrcComment "[^\\]#.*$"lc=1 +syntax match muttrcComment "^# .*$" contains=@Spell +syntax match muttrcComment "^#[^ ].*$" +syntax match muttrcComment "^#$" +syntax match muttrcComment "[^\\]#.*$"lc=1 " Escape sequences (back-tick and pipe goes here too) -syntax match muttrcEscape +\\[#tnr"'Cc ]+ -syntax match muttrcEscape +[`|]+ -syntax match muttrcEscape +\\$+ +syntax match muttrcEscape +\\[#tnr"'Cc ]+ +syntax match muttrcEscape +[`|]+ +syntax match muttrcEscape +\\$+ " The variables takes the following arguments -"syn match muttrcString contained "=\s*[^ #"'`]\+"lc=1 contains=muttrcEscape -syntax region muttrcString contained keepend start=+"+ms=e skip=+\\"+ end=+"+ contains=muttrcEscape,muttrcCommand,muttrcAction,muttrcShellString -syntax region muttrcString contained keepend start=+'+ms=e skip=+\\'+ end=+'+ contains=muttrcEscape,muttrcCommand,muttrcAction +syntax region muttrcString contained keepend start=+"+ms=e skip=+\\"+ end=+"+ contains=muttrcEscape,muttrcCommand,muttrcAction,muttrcShellString +syntax region muttrcString contained keepend start=+'+ms=e skip=+\\'+ end=+'+ contains=muttrcEscape,muttrcCommand,muttrcAction syntax match muttrcStringNL contained skipwhite skipnl "\s*\\$" nextgroup=muttrcString,muttrcStringNL -syntax region muttrcShellString matchgroup=muttrcEscape keepend start=+`+ skip=+\\`+ end=+`+ contains=muttrcVarStr,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcCommand,muttrcVarDeprecatedStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad +syntax region muttrcShellString matchgroup=muttrcEscape keepend start=+`+ skip=+\\`+ end=+`+ contains=muttrcVarStr,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcCommand syntax match muttrcRXChars contained /[^\\][][.*?+]\+/hs=s+1 syntax match muttrcRXChars contained /[][|()][.*?+]*/ @@ -45,54 +44,53 @@ syntax match muttrcRXChars contained /\\/ " Why does muttrcRXString2 work with one \ when muttrcRXString requires two? syntax region muttrcRXString contained skipwhite start=+'+ skip=+\\'+ end=+'+ contains=muttrcRXChars syntax region muttrcRXString contained skipwhite start=+"+ skip=+\\"+ end=+"+ contains=muttrcRXChars -syntax region muttrcRXString contained skipwhite start=+[^ "'^]+ skip=+\\\s+ end=+\s+re=e-1 contains=muttrcRXChars +syntax region muttrcRXString contained skipwhite start=+[^ "'^]+ skip=+\\\s+ end=+\s+re=e-1 contains=muttrcRXChars " For some reason, skip refuses to match backslashes here... syntax region muttrcRXString contained matchgroup=muttrcRXChars skipwhite start=+\^+ end=+[^\\]\s+re=e-1 contains=muttrcRXChars syntax region muttrcRXString contained matchgroup=muttrcRXChars skipwhite start=+\^+ end=+$\s+ contains=muttrcRXChars syntax region muttrcRXString2 contained skipwhite start=+'+ skip=+\'+ end=+'+ contains=muttrcRXChars syntax region muttrcRXString2 contained skipwhite start=+"+ skip=+\"+ end=+"+ contains=muttrcRXChars -" these must be kept synchronized with muttrcRXString, but are intended for -" muttrcRXHooks +" these must be kept synchronized with muttrcRXString, but are intended for muttrcRXHooks syntax region muttrcRXHookString contained keepend skipwhite start=+'+ skip=+\\'+ end=+'+ contains=muttrcRXString nextgroup=muttrcString,muttrcStringNL syntax region muttrcRXHookString contained keepend skipwhite start=+"+ skip=+\\"+ end=+"+ contains=muttrcRXString nextgroup=muttrcString,muttrcStringNL -syntax region muttrcRXHookString contained keepend skipwhite start=+[^ "'^]+ skip=+\\\s+ end=+\s+re=e-1 contains=muttrcRXString nextgroup=muttrcString,muttrcStringNL +syntax region muttrcRXHookString contained keepend skipwhite start=+[^ "'^]+ skip=+\\\s+ end=+\s+re=e-1 contains=muttrcRXString nextgroup=muttrcString,muttrcStringNL syntax region muttrcRXHookString contained keepend skipwhite start=+\^+ end=+[^\\]\s+re=e-1 contains=muttrcRXString nextgroup=muttrcString,muttrcStringNL syntax region muttrcRXHookString contained keepend matchgroup=muttrcRXChars skipwhite start=+\^+ end=+$\s+ contains=muttrcRXString nextgroup=muttrcString,muttrcStringNL -syntax match muttrcRXHookStringNL contained skipwhite skipnl "\s*\\$" nextgroup=muttrcRXHookString,muttrcRXHookStringNL +syntax match muttrcRXHookStringNL contained skipwhite skipnl "\s*\\$" nextgroup=muttrcRXHookString,muttrcRXHookStringNL " these are exclusively for args lists (e.g. -rx pat pat pat ...) -syntax region muttrcRXPat contained keepend skipwhite start=+'+ skip=+\\'+ end=+'\s*+ contains=muttrcRXString nextgroup=muttrcRXPat -syntax region muttrcRXPat contained keepend skipwhite start=+"+ skip=+\\"+ end=+"\s*+ contains=muttrcRXString nextgroup=muttrcRXPat -syntax match muttrcRXPat contained /[^-'"#!]\S\+/ skipwhite contains=muttrcRXChars nextgroup=muttrcRXPat -syntax match muttrcRXDef contained "-rx\s\+" skipwhite nextgroup=muttrcRXPat +syntax region muttrcRXPat contained keepend skipwhite start=+'+ skip=+\\'+ end=+'\s*+ contains=muttrcRXString nextgroup=muttrcRXPat +syntax region muttrcRXPat contained keepend skipwhite start=+"+ skip=+\\"+ end=+"\s*+ contains=muttrcRXString nextgroup=muttrcRXPat +syntax match muttrcRXPat contained /[^-'"#!]\S\+/ skipwhite contains=muttrcRXChars nextgroup=muttrcRXPat +syntax match muttrcRXDef contained "-rx\s\+" skipwhite nextgroup=muttrcRXPat -syntax match muttrcSpecial +\(['"]\)!\1+ +syntax match muttrcSpecial +\(['"]\)!\1+ -syntax match muttrcSetStrAssignment contained skipwhite /=\s*\%(\\\?\$\)\?[0-9A-Za-z_-]\+/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr contains=muttrcVariable,muttrcEscapedVariable -syntax region muttrcSetStrAssignment contained skipwhite keepend start=+=\s*"+hs=s+1 end=+"+ skip=+\\"+ nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr contains=muttrcString -syntax region muttrcSetStrAssignment contained skipwhite keepend start=+=\s*'+hs=s+1 end=+'+ skip=+\\'+ nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr contains=muttrcString -syntax match muttrcSetBoolAssignment contained skipwhite /=\s*\\\?\$\w\+/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr contains=muttrcVariable,muttrcEscapedVariable -syntax match muttrcSetBoolAssignment contained skipwhite /=\s*\%(yes\|no\)/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax match muttrcSetBoolAssignment contained skipwhite /=\s*"\%(yes\|no\)"/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax match muttrcSetBoolAssignment contained skipwhite /=\s*'\%(yes\|no\)'/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax match muttrcSetQuadAssignment contained skipwhite /=\s*\\\?\$\w\+/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr contains=muttrcVariable,muttrcEscapedVariable -syntax match muttrcSetQuadAssignment contained skipwhite /=\s*\%(ask-\)\?\%(yes\|no\)/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax match muttrcSetQuadAssignment contained skipwhite /=\s*"\%(ask-\)\?\%(yes\|no\)"/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax match muttrcSetQuadAssignment contained skipwhite /=\s*'\%(ask-\)\?\%(yes\|no\)'/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax match muttrcSetNumAssignment contained skipwhite /=\s*\\\?\$\w\+/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr contains=muttrcVariable,muttrcEscapedVariable -syntax match muttrcSetNumAssignment contained skipwhite /=\s*\d\+/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax match muttrcSetNumAssignment contained skipwhite /=\s*"\d\+"/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax match muttrcSetNumAssignment contained skipwhite /=\s*'\d\+'/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr +syntax match muttrcSetStrAssignment contained skipwhite /=\s*\%(\\\?\$\)\?[0-9A-Za-z_-]\+/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr contains=muttrcVariable,muttrcEscapedVariable +syntax region muttrcSetStrAssignment contained skipwhite keepend start=+=\s*"+hs=s+1 end=+"+ skip=+\\"+ nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr contains=muttrcString +syntax region muttrcSetStrAssignment contained skipwhite keepend start=+=\s*'+hs=s+1 end=+'+ skip=+\\'+ nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr contains=muttrcString +syntax match muttrcSetBoolAssignment contained skipwhite /=\s*\\\?\$\w\+/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr contains=muttrcVariable,muttrcEscapedVariable +syntax match muttrcSetBoolAssignment contained skipwhite /=\s*\%(yes\|no\)/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax match muttrcSetBoolAssignment contained skipwhite /=\s*"\%(yes\|no\)"/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax match muttrcSetBoolAssignment contained skipwhite /=\s*'\%(yes\|no\)'/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax match muttrcSetQuadAssignment contained skipwhite /=\s*\\\?\$\w\+/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr contains=muttrcVariable,muttrcEscapedVariable +syntax match muttrcSetQuadAssignment contained skipwhite /=\s*\%(ask-\)\?\%(yes\|no\)/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax match muttrcSetQuadAssignment contained skipwhite /=\s*"\%(ask-\)\?\%(yes\|no\)"/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax match muttrcSetQuadAssignment contained skipwhite /=\s*'\%(ask-\)\?\%(yes\|no\)'/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax match muttrcSetNumAssignment contained skipwhite /=\s*\\\?\$\w\+/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr contains=muttrcVariable,muttrcEscapedVariable +syntax match muttrcSetNumAssignment contained skipwhite /=\s*\d\+/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax match muttrcSetNumAssignment contained skipwhite /=\s*"\d\+"/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax match muttrcSetNumAssignment contained skipwhite /=\s*'\d\+'/hs=s+1 nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr " Now catch some email addresses and headers (purified version from mail.vim) -syntax match muttrcEmail "[a-zA-Z0-9._-]\+@[a-zA-Z0-9./-]\+" -syntax match muttrcHeader "\<\c\%(From\|To\|C[Cc]\|B[Cc][Cc]\|Reply-To\|Subject\|Return-Path\|Received\|Date\|Replied\|Attach\)\>:\=" +syntax match muttrcEmail "[a-zA-Z0-9._-]\+@[a-zA-Z0-9./-]\+" +syntax match muttrcHeader "\<\c\%(From\|To\|C[Cc]\|B[Cc][Cc]\|Reply-To\|Subject\|Return-Path\|Received\|Date\|Replied\|Attach\)\>:\=" syntax match muttrcKeySpecial contained +\%(\\[Cc'"]\|\^\|\\[01]\d\{2}\)+ -syntax match muttrcKey contained "\S\+" contains=muttrcKeySpecial,muttrcKeyName -syntax region muttrcKey contained start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=muttrcKeySpecial,muttrcKeyName -syntax region muttrcKey contained start=+'+ skip=+\\\\\|\\'+ end=+'+ contains=muttrcKeySpecial,muttrcKeyName +syntax match muttrcKey contained "\S\+" contains=muttrcKeySpecial,muttrcKeyName +syntax region muttrcKey contained start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=muttrcKeySpecial,muttrcKeyName +syntax region muttrcKey contained start=+'+ skip=+\\\\\|\\'+ end=+'+ contains=muttrcKeySpecial,muttrcKeyName syntax match muttrcKeyName contained "\\[trne]" syntax match muttrcKeyName contained "\c<\%(BackSpace\|BackTab\|Delete\|Down\|End\|Enter\|Esc\|Home\|Insert\|Left\|Next\|PageDown\|PageUp\|Return\|Right\|Space\|Tab\|Up\)>" syntax match muttrcKeyName contained "\c" @@ -103,90 +101,84 @@ syntax match muttrcStrftimeEscapes contained /%[AaBbCcDdeFGgHhIjklMmnpRrSsTtUuVv syntax match muttrcStrftimeEscapes contained /%E[cCxXyY]/ syntax match muttrcStrftimeEscapes contained /%O[BdeHImMSuUVwWy]/ -syntax region muttrcIndexFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcIndexFormatEscapes,muttrcIndexFormatConditionals,muttrcFormatErrors,muttrcTimeEscapes nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcIndexFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcIndexFormatEscapes,muttrcIndexFormatConditionals,muttrcFormatErrors,muttrcTimeEscapes nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcGroupIndexFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcGroupIndexFormatEscapes,muttrcGroupIndexFormatConditionals,muttrcFormatErrors,muttrcTimeEscapes nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcGroupIndexFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcGroupIndexFormatEscapes,muttrcGroupIndexFormatConditionals,muttrcFormatErrors,muttrcTimeEscapes nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcSidebarFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcSidebarFormatEscapes,muttrcSidebarFormatConditionals,muttrcFormatErrors,muttrcTimeEscapes nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcSidebarFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcSidebarFormatEscapes,muttrcSidebarFormatConditionals,muttrcFormatErrors,muttrcTimeEscapes nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcQueryFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcQueryFormatEscapes,muttrcQueryFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcAliasFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcAliasFormatEscapes,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcAliasFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcAliasFormatEscapes,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcAttachFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcAttachFormatEscapes,muttrcAttachFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcAttachFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcAttachFormatEscapes,muttrcAttachFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcComposeFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcComposeFormatEscapes,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcComposeFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcComposeFormatEscapes,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcFolderFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcFolderFormatEscapes,muttrcFolderFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcFolderFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcFolderFormatEscapes,muttrcFolderFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcMixFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcMixFormatEscapes,muttrcMixFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcMixFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcMixFormatEscapes,muttrcMixFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcPGPFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcPGPFormatEscapes,muttrcPGPFormatConditionals,muttrcFormatErrors,muttrcPGPTimeEscapes nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcPGPFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcPGPFormatEscapes,muttrcPGPFormatConditionals,muttrcFormatErrors,muttrcPGPTimeEscapes nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcPGPCmdFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcPGPCmdFormatEscapes,muttrcPGPCmdFormatConditionals,muttrcVariable,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcPGPCmdFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcPGPCmdFormatEscapes,muttrcPGPCmdFormatConditionals,muttrcVariable,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcStatusFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcStatusFormatEscapes,muttrcStatusFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcStatusFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcStatusFormatEscapes,muttrcStatusFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcPGPGetKeysFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcPGPGetKeysFormatEscapes,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcPGPGetKeysFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcPGPGetKeysFormatEscapes,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcSmimeFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcSmimeFormatEscapes,muttrcSmimeFormatConditionals,muttrcVariable,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcSmimeFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcSmimeFormatEscapes,muttrcSmimeFormatConditionals,muttrcVariable,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcStrftimeFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcStrftimeEscapes,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -syntax region muttrcStrftimeFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcStrftimeEscapes,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr +syntax region muttrcAliasFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcAliasFormatEscapes,muttrcAliasFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcAliasFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcAliasFormatEscapes,muttrcAliasFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcAttachFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcAttachFormatEscapes,muttrcAttachFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcAttachFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcAttachFormatEscapes,muttrcAttachFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcComposeFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcComposeFormatEscapes,muttrcComposeFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcComposeFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcComposeFormatEscapes,muttrcComposeFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcFolderFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcFolderFormatEscapes,muttrcFolderFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcFolderFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcFolderFormatEscapes,muttrcFolderFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcGroupIndexFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcGroupIndexFormatEscapes,muttrcGroupIndexFormatConditionals,muttrcFormatErrors,muttrcTimeEscapes nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcGroupIndexFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcGroupIndexFormatEscapes,muttrcGroupIndexFormatConditionals,muttrcFormatErrors,muttrcTimeEscapes nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcIndexFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcIndexFormatEscapes,muttrcIndexFormatConditionals,muttrcFormatErrors,muttrcTimeEscapes nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcIndexFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcIndexFormatEscapes,muttrcIndexFormatConditionals,muttrcFormatErrors,muttrcTimeEscapes nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcMixFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcMixFormatEscapes,muttrcMixFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcMixFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcMixFormatEscapes,muttrcMixFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcPGPCmdFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcPGPCmdFormatEscapes,muttrcPGPCmdFormatConditionals,muttrcVariable,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcPGPCmdFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcPGPCmdFormatEscapes,muttrcPGPCmdFormatConditionals,muttrcVariable,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcPGPFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcPGPFormatEscapes,muttrcPGPFormatConditionals,muttrcFormatErrors,muttrcPGPTimeEscapes nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcPGPFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcPGPFormatEscapes,muttrcPGPFormatConditionals,muttrcFormatErrors,muttrcPGPTimeEscapes nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcQueryFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcQueryFormatEscapes,muttrcQueryFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcQueryFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcQueryFormatEscapes,muttrcQueryFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcSidebarFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcSidebarFormatEscapes,muttrcSidebarFormatConditionals,muttrcFormatErrors,muttrcTimeEscapes nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcSidebarFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcSidebarFormatEscapes,muttrcSidebarFormatConditionals,muttrcFormatErrors,muttrcTimeEscapes nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcSmimeFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcSmimeFormatEscapes,muttrcSmimeFormatConditionals,muttrcVariable,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcSmimeFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcSmimeFormatEscapes,muttrcSmimeFormatConditionals,muttrcVariable,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcStatusFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcStatusFormatEscapes,muttrcStatusFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcStatusFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcStatusFormatEscapes,muttrcStatusFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcStrftimeFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcStrftimeEscapes,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax region muttrcStrftimeFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcStrftimeEscapes,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr " Format escapes and conditionals syntax match muttrcFormatConditionals2 contained /[^?]*?/ -function! s:escapesConditionals(baseName, sequence, alignment, secondary) +function! s:escapesConditionals(baseName, sequence, padding, conditional) exec 'syntax match muttrc' . a:baseName . 'Escapes contained /%\%(\%(-\?[0-9]\+\)\?\%(\.[0-9]\+\)\?\)\?[:_]\?\%(' . a:sequence . '\|%\)/' - if a:alignment + if a:padding exec 'syntax match muttrc' . a:baseName . 'Escapes contained /%[>|*]./' endif - if a:secondary + if a:conditional exec 'syntax match muttrc' . a:baseName . 'Conditionals contained /%?\%(' . a:sequence . '\)?/ nextgroup=muttrcFormatConditionals2' else exec 'syntax match muttrc' . a:baseName . 'Conditionals contained /%?\%(' . a:sequence . '\)?/' endif endfunction -" CHECKED 2019-11-18 -" Ref: index_format_str() in hdrline.c -call s:escapesConditionals('IndexFormat', '[AaBbCDdEeFfgHIiJKLlMmNnOPqRrSsTtuvWXxYyZ(<[{]\|G[a-zA-Z]\+\|Fp\=\|z[cst]\|cr\=', 1, 1) -" Ref: alias_format_str() in addrbook.c -syntax match muttrcAliasFormatEscapes contained /%\%(\%(-\?[0-9]\+\)\?\%(\.[0-9]\+\)\?\)\?[:_]\?[afnrt%]/ -" Ref: group_index_format_str() in browser.c -call s:escapesConditionals('GroupIndexFormat', '[CdfMNns]', 1, 1) -" Ref: sidebar_format_str() in sidebar.c -call s:escapesConditionals('SidebarFormat', '[!BDdFLNnSt]', 1, 1) -" Ref: query_format_str() in query.c -call s:escapesConditionals('QueryFormat', '[acent]', 0, 1) +" CHECKED 2020-06-21 +" Ref: alias_format_str() in alias/dlgalias.c +call s:escapesConditionals('AliasFormat', '[acfnrt]', 1, 0) " Ref: attach_format_str() in recvattach.c call s:escapesConditionals('AttachFormat', '[CcDdeFfIMmnQsTtuX]', 1, 1) " Ref: compose_format_str() in compose.c -syntax match muttrcComposeFormatEscapes contained /%\%(\%(-\?[0-9]\+\)\?\%(\.[0-9]\+\)\?\)\?[:_]\?[ahlv%]/ -syntax match muttrcComposeFormatEscapes contained /%[>|*]./ +call s:escapesConditionals('ComposeFormat', '[ahlv]', 1, 1) " Ref: folder_format_str() in browser.c call s:escapesConditionals('FolderFormat', '[CDdFfgilmNnstu]', 1, 0) +" Ref: group_index_format_str() in browser.c +call s:escapesConditionals('GroupIndexFormat', '[CdfMNns]', 1, 1) +" Ref: index_format_str() in hdrline.c +call s:escapesConditionals('IndexFormat', '[AaBbCDdEefgHIiJKLlMmNnOPqRrSsTtuvWXxYyZ(<[{]\|@\i\+@\|G[a-zA-Z]\+\|Fp\=\|z[cst]\|cr\=', 1, 1) " Ref: mix_format_str() in remailer.c -call s:escapesConditionals('MixFormat', '[acns]', 0, 0) -" Ref: status_format_str() in status.c -call s:escapesConditionals('StatusFormat', '[bDdFfhLlMmnoPpRrSstuVv]', 1, 1) -" Ref: fmt_smime_command() in ncrypt/smime.c -call s:escapesConditionals('SmimeFormat', '[aCcdfiks]', 0, 1) +call s:escapesConditionals('MixFormat', '[acns]', 1, 0) +" Ref: pgp_command_format_str() in ncrypt/pgpinvoke.c +call s:escapesConditionals('PGPCmdFormat', '[afprs]', 0, 1) " Ref: crypt_format_str() in ncrypt/crypt_gpgme.c -" Ref: pgp_entry_fmt() in ncrypt/pgpkey.c +" Ref: pgp_entry_format_str() in ncrypt/pgpkey.c " Note: crypt_format_str() supports 'p', but pgp_entry_fmt() does not call s:escapesConditionals('PGPFormat', '[AaCcFfKkLlnptu[]', 0, 0) -" Ref: fmt_pgp_command() ncrypt/pgpinvoke.c -call s:escapesConditionals('PGPCmdFormat', '[afprs]', 0, 1) +" Ref: query_format_str() in alias/dlgquery.c +call s:escapesConditionals('QueryFormat', '[acent]', 1, 1) +" Ref: sidebar_format_str() in sidebar.c +call s:escapesConditionals('SidebarFormat', '[!BDdFLNnorStZ]', 1, 1) +" Ref: smime_command_format_str() in ncrypt/smime.c +call s:escapesConditionals('SmimeFormat', '[aCcdfiks]', 0, 1) +" Ref: status_format_str() in status.c +call s:escapesConditionals('StatusFormat', '[bDdFfhLlMmnoPpRrSstuVv]', 1, 1) -" This matches the documentation, but directly contradicts the code -" (according to the code, this should be identical to the muttrcPGPCmdFormatEscapes -syntax match muttrcPGPGetKeysFormatEscapes contained /%\%(\%(-\?[0-9]\+\)\?\%(\.[0-9]\+\)\?\)\?[:_]\?[acfklntu[%]/ - -syntax region muttrcTimeEscapes contained start=+%{+ end=+}+ contains=muttrcStrftimeEscapes -syntax region muttrcTimeEscapes contained start=+%\[+ end=+\]+ contains=muttrcStrftimeEscapes -syntax region muttrcTimeEscapes contained start=+%(+ end=+)+ contains=muttrcStrftimeEscapes -syntax region muttrcTimeEscapes contained start=+%<+ end=+>+ contains=muttrcStrftimeEscapes syntax region muttrcPGPTimeEscapes contained start=+%\[+ end=+\]+ contains=muttrcStrftimeEscapes +syntax region muttrcTimeEscapes contained start=+%(+ end=+)+ contains=muttrcStrftimeEscapes +syntax region muttrcTimeEscapes contained start=+%<+ end=+>+ contains=muttrcStrftimeEscapes +syntax region muttrcTimeEscapes contained start=+%\[+ end=+\]+ contains=muttrcStrftimeEscapes +syntax region muttrcTimeEscapes contained start=+%{+ end=+}+ contains=muttrcStrftimeEscapes syntax match muttrcVarEqualsAliasFmt contained skipwhite "=" nextgroup=muttrcAliasFormatStr syntax match muttrcVarEqualsAttachFmt contained skipwhite "=" nextgroup=muttrcAttachFormatStr @@ -197,29 +189,20 @@ syntax match muttrcVarEqualsIdxFmt contained skipwhite "=" nextgroup=mutt syntax match muttrcVarEqualsMixFmt contained skipwhite "=" nextgroup=muttrcMixFormatStr syntax match muttrcVarEqualsPGPCmdFmt contained skipwhite "=" nextgroup=muttrcPGPCmdFormatStr syntax match muttrcVarEqualsPGPFmt contained skipwhite "=" nextgroup=muttrcPGPFormatStr -syntax match muttrcVarEqualsPGPGetKeysFmt contained skipwhite "=" nextgroup=muttrcPGPGetKeysFormatStr syntax match muttrcVarEqualsQueryFmt contained skipwhite "=" nextgroup=muttrcQueryFormatStr syntax match muttrcVarEqualsSdbFmt contained skipwhite "=" nextgroup=muttrcSidebarFormatStr syntax match muttrcVarEqualsSmimeFmt contained skipwhite "=" nextgroup=muttrcSmimeFormatStr syntax match muttrcVarEqualsStatusFmt contained skipwhite "=" nextgroup=muttrcStatusFormatStr syntax match muttrcVarEqualsStrftimeFmt contained skipwhite "=" nextgroup=muttrcStrftimeFormatStr -syntax match muttrcVPrefix contained /[?&]/ nextgroup=muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr +syntax match muttrcVPrefix contained /[?&]/ nextgroup=muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr -" CHECKED 2019-11-02 +" CHECKED 2020-06-21 " List of the different screens in mutt (see Menus in keymap.c) syntax keyword muttrcMenu contained alias attach browser compose editor generic index key_select_pgp key_select_smime mix pager pgp postpone query smime syntax match muttrcMenuList "\S\+" contained contains=muttrcMenu syntax match muttrcMenuCommas /,/ contained -" CHECKED 2019-11-02 -" List of hooks in Commands in init.h -syntax keyword muttrcHooks contained skipwhite - \ account-hook append-hook close-hook crypt-hook fcc-hook fcc-save-hook - \ folder-hook iconv-hook index-format-hook mbox-hook message-hook open-hook pgp-hook - \ reply-hook save-hook send-hook send2-hook -syntax keyword muttrcHooks skipwhite shutdown-hook startup-hook timeout-hook nextgroup=muttrcCommand - syntax region muttrcSpamPattern contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcPattern nextgroup=muttrcString,muttrcStringNL syntax region muttrcSpamPattern contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcPattern nextgroup=muttrcString,muttrcStringNL @@ -251,15 +234,19 @@ syntax match muttrcEscapedVariable contained "\\\$[a-zA-Z_-]\+" syntax match muttrcBadAction contained "[^<>]\+" contains=muttrcEmail syntax match muttrcAction contained "<[^>]\{-}>" contains=muttrcBadAction,muttrcFunction,muttrcKeyName +" CHECKED 2020-06-21 " First, functions that take regular expressions: syntax match muttrcRXHookNot contained /!\s*/ skipwhite nextgroup=muttrcRXHookString,muttrcRXHookStringNL syntax match muttrcRXHooks /\<\%(account\|append\|close\|crypt\|folder\|mbox\|open\|pgp\)-hook\>/ skipwhite nextgroup=muttrcRXHookNot,muttrcRXHookString,muttrcRXHookStringNL " Now, functions that take patterns syntax match muttrcPatHookNot contained /!\s*/ skipwhite nextgroup=muttrcPattern -syntax match muttrcPatHooks /\<\%(charset\|iconv\)-hook\>/ skipwhite nextgroup=muttrcPatHookNot,muttrcPattern +syntax match muttrcPatHooks /\<\%(charset\|iconv\|index-format\)-hook\>/ skipwhite nextgroup=muttrcPatHookNot,muttrcPattern syntax match muttrcPatHooks /\<\%(message\|reply\|send\|send2\|save\|fcc\|fcc-save\)-hook\>/ skipwhite nextgroup=muttrcPatHookNot,muttrcOptPattern +" Global hooks that take a command +syntax keyword muttrcHooks skipwhite shutdown-hook startup-hook timeout-hook nextgroup=muttrcCommand + syntax match muttrcBindFunction contained /\S\+\>/ skipwhite contains=muttrcFunction syntax match muttrcBindFunctionNL contained /\s*\\$/ skipwhite skipnl nextgroup=muttrcBindFunction,muttrcBindFunctionNL syntax match muttrcBindKey contained /\S\+/ skipwhite contains=muttrcKey nextgroup=muttrcBindFunction,muttrcBindFunctionNL @@ -272,8 +259,8 @@ syntax region muttrcMacroDescr contained keepend skipwhite start=+'+ms=e skip=+\ syntax region muttrcMacroDescr contained keepend skipwhite start=+"+ms=e skip=+\\"+ end=+"+me=s syntax match muttrcMacroDescrNL contained /\s*\\$/ skipwhite skipnl nextgroup=muttrcMacroDescr,muttrcMacroDescrNL syntax region muttrcMacroBody contained skipwhite start="\S" skip='\\ \|\\$' end=' \|$' contains=muttrcEscape,muttrcSet,muttrcUnset,muttrcReset,muttrcToggle,muttrcCommand,muttrcAction nextgroup=muttrcMacroDescr,muttrcMacroDescrNL -syntax region muttrcMacroBody matchgroup=Type contained skipwhite start=+'+ms=e skip=+\\'+ end=+'\|\%(\%(\\\\\)\@-][0-9]\+[kM]\?\|[0-9]\+[kM]\?[-]\%([0-9]\+[kM]\?\)\?\)" +" Parameter: date syntax match muttrcSimplePat contained "!\?\^\?[~][dr]\s*\%(\%(-\?[0-9]\{1,2}\%(/[0-9]\{1,2}\%(/[0-9]\{2}\%([0-9]\{2}\)\?\)\?\)\?\%([+*-][0-9]\+[ymwd]\)*\)\|\%(\%([0-9]\{1,2}\%(/[0-9]\{1,2}\%(/[0-9]\{2}\%([0-9]\{2}\)\?\)\?\)\?\%([+*-][0-9]\+[ymwd]\)*\)-\%([0-9]\{1,2}\%(/[0-9]\{1,2}\%(/[0-9]\{2}\%([0-9]\{2}\)\?\)\?\)\?\%([+*-][0-9]\+[ymwd]\)\?\)\?\)\|\%([<>=][0-9]\+[ymwd]\)\|\%(`[^`]\+`\)\|\%(\$[a-zA-Z0-9_-]\+\)\)" contains=muttrcShellString,muttrcVariable -syntax match muttrcSimplePat contained "!\?\^\?[~][bBcCefhHiLstxy]\s*" nextgroup=muttrcSimplePatRXContainer +" Parameter: regex +syntax match muttrcSimplePat contained "!\?\^\?[~][BbCcefHhIiLMstwxYy]\s*" nextgroup=muttrcSimplePatRXContainer +" Parameter: pattern syntax match muttrcSimplePat contained "!\?\^\?[%][bBcCefhHiLstxy]\s*" nextgroup=muttrcSimplePatString +" Parameter: pattern syntax match muttrcSimplePat contained "!\?\^\?[=][bcCefhHiLstxy]\s*" nextgroup=muttrcSimplePatString syntax region muttrcSimplePat contained keepend start=+!\?\^\?[~](+ end=+)+ contains=muttrcSimplePat + "syn match muttrcSimplePat contained /'[^~=%][^']*/ contains=muttrcRXString syntax region muttrcSimplePatString contained keepend start=+"+ end=+"+ skip=+\\"+ syntax region muttrcSimplePatString contained keepend start=+'+ end=+'+ skip=+\\'+ -syntax region muttrcSimplePatString contained keepend start=+[^ "']+ skip=+\\ + end=+\s+re=e-1 +syntax region muttrcSimplePatString contained keepend start=+[^ "']+ skip=+\\ + end=+\s+re=e-1 syntax region muttrcSimplePatRXContainer contained keepend start=+"+ end=+"+ skip=+\\"+ contains=muttrcRXString syntax region muttrcSimplePatRXContainer contained keepend start=+'+ end=+'+ skip=+\\'+ contains=muttrcRXString -syntax region muttrcSimplePatRXContainer contained keepend start=+[^ "']+ skip=+\\ + end=+\s+re=e-1 contains=muttrcRXString +syntax region muttrcSimplePatRXContainer contained keepend start=+[^ "']+ skip=+\\ + end=+\s+re=e-1 contains=muttrcRXString syntax match muttrcSimplePatMetas contained /[(|)]/ syntax match muttrcOptSimplePat contained skipwhite /[~=%!(^].*/ contains=muttrcSimplePat,muttrcSimplePatMetas @@ -350,12 +346,12 @@ syntax keyword muttrcColor contained brightblack brightblue brightcyan brightdef syntax match muttrcColor contained "\<\%(bright\)\=color\d\{1,3}\>" " Now for the structure of the color line syntax match muttrcColorRXNL contained skipnl "\s*\\$" nextgroup=muttrcColorRXPat,muttrcColorRXNL -syntax match muttrcColorBG contained /\s*[$]\?\w\+/ contains=muttrcColor,muttrcVariable,muttrcUnHighlightSpace nextgroup=muttrcColorRXPat,muttrcColorRXNL +syntax match muttrcColorBG contained /\s*[$]\?\w\+/ contains=muttrcColor,muttrcVariable,muttrcUnHighlightSpace nextgroup=muttrcColorRXPat,muttrcColorRXNL syntax match muttrcColorBGNL contained skipnl "\s*\\$" nextgroup=muttrcColorBG,muttrcColorBGNL -syntax match muttrcColorFG contained /\s*[$]\?\w\+/ contains=muttrcColor,muttrcVariable,muttrcUnHighlightSpace nextgroup=muttrcColorBG,muttrcColorBGNL +syntax match muttrcColorFG contained /\s*[$]\?\w\+/ contains=muttrcColor,muttrcVariable,muttrcUnHighlightSpace nextgroup=muttrcColorBG,muttrcColorBGNL syntax match muttrcColorFGNL contained skipnl "\s*\\$" nextgroup=muttrcColorFG,muttrcColorFGNL -syntax match muttrcColorContext contained /\s*[$]\?\w\+/ contains=muttrcColorField,muttrcVariable,muttrcUnHighlightSpace,muttrcColorCompose nextgroup=muttrcColorFG,muttrcColorFGNL -syntax match muttrcColorNL contained skipnl "\s*\\$" nextgroup=muttrcColorContext,muttrcColorNL,muttrcColorCompose +syntax match muttrcColorContext contained /\s*[$]\?\w\+/ contains=muttrcColorField,muttrcVariable,muttrcUnHighlightSpace,muttrcColorCompose nextgroup=muttrcColorFG,muttrcColorFGNL +syntax match muttrcColorNL contained skipnl "\s*\\$" nextgroup=muttrcColorContext,muttrcColorNL,muttrcColorCompose syntax match muttrcColorKeyword contained /^\s*color\s\+/ nextgroup=muttrcColorContext,muttrcColorNL,muttrcColorCompose " And now color's brother: syntax region muttrcUnColorPatterns contained skipwhite start=+\s*'+ end=+'+ skip=+\\'+ contains=muttrcPattern nextgroup=muttrcUnColorPatterns,muttrcUnColorPatNL @@ -373,25 +369,25 @@ syntax keyword muttrcMonoAttrib contained bold none normal reverse standout unde syntax keyword muttrcMono contained mono skipwhite nextgroup=muttrcColorField,muttrcColorCompose syntax match muttrcMonoLine "^\s*mono\s\+\S\+" skipwhite nextgroup=muttrcMonoAttrib contains=muttrcMono -" CHECKED 2019-11-02 +" CHECKED 2020-06-21 " List of fields in Fields in color.c syntax keyword muttrcColorField skipwhite contained - \ attachment attach_headers body bold error hdrdefault header index - \ index_author index_collapsed index_date index_flags index_label - \ index_number index_size index_subject index_tag index_tags indicator - \ markers message normal options progress prompt quoted search sidebar_divider - \ sidebar_flagged sidebar_highlight sidebar_indicator sidebar_new - \ sidebar_ordinary sidebar_spoolfile sidebar_unread signature status tilde tree - \ underline warning nextgroup=muttrcColor + \ attachment attach_headers body bold error hdrdefault header index index_author + \ index_collapsed index_date index_flags index_label index_number index_size index_subject + \ index_tag index_tags indicator markers message normal options progress prompt quoted + \ search sidebar_divider sidebar_flagged sidebar_highlight sidebar_indicator sidebar_new + \ sidebar_ordinary sidebar_spoolfile sidebar_unread signature status tilde tree underline + \ warning nextgroup=muttrcColor + syntax match muttrcColorField contained "\" syntax match muttrcColorCompose skipwhite contained /\s*compose\s*/ nextgroup=muttrcColorComposeField -" CHECKED 2019-11-02 +" CHECKED 2020-06-21 " List of fields in ComposeFields in color.c syntax keyword muttrcColorComposeField skipwhite contained - \ header security_both security_encrypt security_none security_sign - \ nextgroup=muttrcColorFG,muttrcColorFGNL + \ header security_both security_encrypt security_none security_sign + \ nextgroup=muttrcColorFG,muttrcColorFGNL syntax region muttrcColorLine keepend start=/^\s*color\s\+/ skip=+\\$+ end=+$+ contains=muttrcColorKeyword,muttrcComment,muttrcUnHighlightSpace function! s:boolQuadGen(type, vars, deprecated) @@ -403,674 +399,460 @@ function! s:boolQuadGen(type, vars, deprecated) let l:orig_type = copy(a:type) if a:deprecated let l:type = 'Deprecated' . a:type + exec 'syntax keyword muttrcVar' . l:type . ' ' . join(a:vars) + exec 'syntax keyword muttrcVar' . l:type . ' ' . join(l:novars) + exec 'syntax keyword muttrcVar' . l:type . ' ' . join(l:invvars) else let l:type = a:type + exec 'syntax keyword muttrcVar' . l:type . ' skipwhite contained ' . join(a:vars) . ' nextgroup=muttrcSet' . l:orig_type . 'Assignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr' + exec 'syntax keyword muttrcVar' . l:type . ' skipwhite contained ' . join(l:novars) . ' nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr' + exec 'syntax keyword muttrcVar' . l:type . ' skipwhite contained ' . join(l:invvars) . ' nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr' endif - exec 'syntax keyword muttrcVar' . l:type . ' skipwhite contained ' . join(a:vars) . ' nextgroup=muttrcSet' . l:orig_type . 'Assignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr' - exec 'syntax keyword muttrcVar' . l:type . ' skipwhite contained ' . join(l:novars) . ' nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr' - exec 'syntax keyword muttrcVar' . l:type . ' skipwhite contained ' . join(l:invvars) . ' nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr' endfunction -" CHECKED 2019-11-02 -" List of DT_BOOL in MuttVars in init.h +" CHECKED 2020-06-21 +" List of DT_BOOL in MuttVars in mutt_config.c call s:boolQuadGen('Bool', [ - \ 'allow_8bit', 'allow_ansi', 'arrow_cursor', 'ascii_chars', 'askbcc', - \ 'askcc', 'ask_follow_up', 'ask_x_comment_to', 'attach_split', 'autoedit', - \ 'auto_tag', 'beep', 'beep_new', 'bounce_delivered', 'braille_friendly', - \ 'change_folder_next', 'check_mbox_size', 'check_new', 'collapse_all', - \ 'collapse_flagged', 'collapse_unread', 'confirmappend', 'confirmcreate', - \ 'crypt_autoencrypt', 'crypt_autopgp', 'crypt_autosign', 'crypt_autosmime', - \ 'crypt_confirmhook', 'crypt_opportunistic_encrypt', 'crypt_replyencrypt', - \ 'crypt_replysign', 'crypt_replysignencrypted', 'crypt_timestamp', - \ 'crypt_use_gpgme', 'crypt_use_pka', 'delete_untag', 'digest_collapse', - \ 'duplicate_threads', 'edit_headers', 'encode_from', 'fast_reply', - \ 'fcc_clear', 'flag_safe', 'followup_to', 'force_name', 'forward_decode', - \ 'forward_decrypt', 'forward_quote', 'forward_references', 'hdrs', - \ 'header', 'header_cache_compress', 'header_color_partial', 'help', - \ 'hidden_host', 'hide_limited', 'hide_missing', 'hide_thread_subject', - \ 'hide_top_limited', 'hide_top_missing', 'history_remove_dups', - \ 'honor_disposition', 'idn_decode', 'idn_encode', 'ignore_list_reply_to', - \ 'imap_check_subscribed', 'imap_idle', 'imap_list_subscribed', - \ 'imap_passive', 'imap_peek', 'imap_servernoise', 'implicit_autoview', - \ 'include_onlyfirst', 'keep_flagged', 'mailcap_sanitize', - \ 'maildir_check_cur', 'maildir_header_cache_verify', 'maildir_trash', - \ 'mail_check_recent', 'mail_check_stats', 'markers', 'mark_old', - \ 'menu_move_off', 'menu_scroll', 'message_cache_clean', 'meta_key', - \ 'metoo', 'mh_purge', 'mime_forward_decode', 'mime_subject', - \ 'mime_type_query_first', 'narrow_tree', 'nm_record', 'nntp_listgroup', - \ 'nntp_load_description', 'pager_stop', 'pgp_autoinline', - \ 'pgp_auto_decode', 'pgp_check_exit', 'pgp_ignore_subkeys', 'pgp_long_ids', - \ 'pgp_replyinline', 'pgp_retainable_sigs', 'pgp_self_encrypt', - \ 'pgp_show_unusable', 'pgp_strict_enc', 'pgp_use_gpg_agent', 'pipe_decode', - \ 'pipe_split', 'pop_auth_try_all', 'pop_last', 'postpone_encrypt', - \ 'print_decode', 'print_split', 'prompt_after', 'read_only', - \ 'reflow_space_quotes', 'reflow_text', 'reply_self', 'reply_with_xorig', - \ 'resolve', 'resume_draft_files', 'resume_edited_draft_files', - \ 'reverse_alias', 'reverse_name', 'reverse_realname', 'rfc2047_parameters', - \ 'save_address', 'save_empty', 'save_name', 'save_unsubscribed', 'score', - \ 'show_new_news', 'show_only_unread', 'sidebar_folder_indent', - \ 'sidebar_new_mail_only', 'sidebar_next_new_wrap', 'sidebar_on_right', - \ 'sidebar_short_path', 'sidebar_visible', 'sig_dashes', 'sig_on_top', - \ 'smart_wrap', 'smime_ask_cert_label', 'smime_decrypt_use_default_key', - \ 'smime_is_default', 'smime_self_encrypt', 'sort_re', 'ssl_force_tls', - \ 'ssl_usesystemcerts', 'ssl_use_sslv2', 'ssl_use_sslv3', 'ssl_use_tlsv1', - \ 'ssl_use_tlsv1_1', 'ssl_use_tlsv1_2', 'ssl_verify_dates', - \ 'ssl_verify_host', 'ssl_verify_partial_chains', 'status_on_top', - \ 'strict_threads', 'suspend', 'text_flowed', 'thorough_search', - \ 'thread_received', 'tilde', 'ts_enabled', 'uncollapse_jump', - \ 'uncollapse_new', 'user_agent', 'use_8bitmime', 'use_domain', - \ 'use_envelope_from', 'use_from', 'use_ipv6', 'virtual_spoolfile', - \ 'wait_key', 'weed', 'wrap_search', 'write_bcc', 'x_comment_to', - \ 'attach_save_without_prompting', 'autocrypt', 'autocrypt_reply', - \ 'auto_subscribe', 'browser_abbreviate_mailboxes', - \ 'crypt_protected_headers_read', 'crypt_protected_headers_save', - \ 'crypt_protected_headers_write', 'fcc_before_send', 'imap_condstore', - \ 'imap_qresync', 'imap_rfc5161', 'include_encrypted', - \ 'pgp_check_gpg_decrypt_status_fd', 'sidebar_non_empty_mailbox_only', - \ 'size_show_bytes', 'size_show_fractions', 'size_show_mb', - \ 'size_units_on_left', 'ssl_use_tlsv1_3' - \ ], 0) + \ 'abort_backspace', 'allow_8bit', 'allow_ansi', 'arrow_cursor', 'ascii_chars', 'askbcc', + \ 'askcc', 'ask_follow_up', 'ask_x_comment_to', 'attach_save_without_prompting', + \ 'attach_split', 'autocrypt', 'autocrypt_reply', 'autoedit', 'auto_subscribe', 'auto_tag', + \ 'beep', 'beep_new', 'bounce_delivered', 'braille_friendly', + \ 'browser_abbreviate_mailboxes', 'change_folder_next', 'check_mbox_size', 'check_new', + \ 'collapse_all', 'collapse_flagged', 'collapse_unread', 'confirmappend', 'confirmcreate', + \ 'crypt_autoencrypt', 'crypt_autopgp', 'crypt_autosign', 'crypt_autosmime', + \ 'crypt_confirmhook', 'crypt_opportunistic_encrypt', + \ 'crypt_opportunistic_encrypt_strong_keys', 'crypt_protected_headers_read', + \ 'crypt_protected_headers_save', 'crypt_protected_headers_write', 'crypt_replyencrypt', + \ 'crypt_replysign', 'crypt_replysignencrypted', 'crypt_timestamp', 'crypt_use_gpgme', + \ 'crypt_use_pka', 'delete_untag', 'digest_collapse', 'duplicate_threads', 'edit_headers', + \ 'encode_from', 'fast_reply', 'fcc_before_send', 'fcc_clear', 'flag_safe', 'followup_to', + \ 'force_name', 'forward_decode', 'forward_decrypt', 'forward_quote', 'forward_references', + \ 'hdrs', 'header', 'header_color_partial', 'help', 'hidden_host', 'hide_limited', + \ 'hide_missing', 'hide_thread_subject', 'hide_top_limited', 'hide_top_missing', + \ 'history_remove_dups', 'honor_disposition', 'idn_decode', 'idn_encode', + \ 'ignore_list_reply_to', 'imap_check_subscribed', 'imap_condstore', 'imap_deflate', + \ 'imap_idle', 'imap_list_subscribed', 'imap_passive', 'imap_peek', 'imap_qresync', + \ 'imap_rfc5161', 'imap_servernoise', 'implicit_autoview', 'include_encrypted', + \ 'include_onlyfirst', 'keep_flagged', 'mailcap_sanitize', 'maildir_check_cur', + \ 'maildir_header_cache_verify', 'maildir_trash', 'mail_check_recent', 'mail_check_stats', + \ 'markers', 'mark_old', 'menu_move_off', 'menu_scroll', 'message_cache_clean', 'meta_key', + \ 'metoo', 'mh_purge', 'mime_forward_decode', 'mime_subject', 'mime_type_query_first', + \ 'narrow_tree', 'nm_record', 'nntp_listgroup', 'nntp_load_description', 'pager_stop', + \ 'pgp_autoinline', 'pgp_auto_decode', 'pgp_check_exit', 'pgp_check_gpg_decrypt_status_fd', + \ 'pgp_ignore_subkeys', 'pgp_long_ids', 'pgp_replyinline', 'pgp_retainable_sigs', + \ 'pgp_self_encrypt', 'pgp_show_unusable', 'pgp_strict_enc', 'pgp_use_gpg_agent', + \ 'pipe_decode', 'pipe_split', 'pop_auth_try_all', 'pop_last', 'postpone_encrypt', + \ 'print_decode', 'print_split', 'prompt_after', 'read_only', 'reflow_space_quotes', + \ 'reflow_text', 'reply_self', 'reply_with_xorig', 'resolve', 'resume_draft_files', + \ 'resume_edited_draft_files', 'reverse_alias', 'reverse_name', 'reverse_realname', + \ 'rfc2047_parameters', 'save_address', 'save_empty', 'save_name', 'save_unsubscribed', + \ 'score', 'show_new_news', 'show_only_unread', 'sidebar_folder_indent', + \ 'sidebar_new_mail_only', 'sidebar_next_new_wrap', 'sidebar_non_empty_mailbox_only', + \ 'sidebar_on_right', 'sidebar_short_path', 'sidebar_visible', 'sig_dashes', 'sig_on_top', + \ 'size_show_bytes', 'size_show_fractions', 'size_show_mb', 'size_units_on_left', + \ 'smart_wrap', 'smime_ask_cert_label', 'smime_decrypt_use_default_key', 'smime_is_default', + \ 'smime_self_encrypt', 'sort_re', 'ssl_force_tls', 'ssl_usesystemcerts', 'ssl_use_sslv2', + \ 'ssl_use_sslv3', 'ssl_use_tlsv1', 'ssl_use_tlsv1_1', 'ssl_use_tlsv1_2', 'ssl_use_tlsv1_3', + \ 'ssl_verify_dates', 'ssl_verify_host', 'ssl_verify_partial_chains', 'status_on_top', + \ 'strict_threads', 'suspend', 'text_flowed', 'thorough_search', 'thread_received', 'tilde', + \ 'ts_enabled', 'uncollapse_jump', 'uncollapse_new', 'user_agent', 'use_8bitmime', + \ 'use_domain', 'use_envelope_from', 'use_from', 'use_ipv6', 'virtual_spoolfile', + \ 'wait_key', 'weed', 'wrap_search', 'write_bcc', 'x_comment_to' + \ ], 0) -" CHECKED 2019-11-02 +" CHECKED 2020-06-21 " Deprecated Bools -" List of DT_SYNONYM synonyms of Bools in MuttVars in init.h +" List of DT_SYNONYM or DT_DEPRECATED Bools in MuttVars in mutt_config.c call s:boolQuadGen('Bool', [ - \ 'edit_hdrs', 'envelope_from', 'forw_decode', 'forw_decrypt', - \ 'forw_quote', 'ignore_linear_white_space', 'pgp_autoencrypt', - \ 'pgp_autosign', 'pgp_auto_traditional', 'pgp_create_traditional', - \ 'pgp_replyencrypt', 'pgp_replysign', 'pgp_replysignencrypted', - \ 'xterm_set_titles' - \ ], 1) + \ 'edit_hdrs', 'envelope_from', 'forw_decode', 'forw_decrypt', 'forw_quote', + \ 'header_cache_compress', 'ignore_linear_white_space', 'pgp_autoencrypt', 'pgp_autosign', + \ 'pgp_auto_traditional', 'pgp_create_traditional', 'pgp_replyencrypt', 'pgp_replysign', + \ 'pgp_replysignencrypted', 'xterm_set_titles' + \ ], 1) -" CHECKED 2019-11-02 -" List of DT_QUAD in MuttVars in init.h +" CHECKED 2020-06-21 +" List of DT_QUAD in MuttVars in mutt_config.c call s:boolQuadGen('Quad', [ - \ 'abort_noattach', 'abort_nosubject', 'abort_unmodified', 'bounce', - \ 'catchup_newsgroup', 'copy', 'crypt_verify_sig', 'delete', 'fcc_attach', - \ 'followup_to_poster', 'forward_edit', 'honor_followup_to', 'include', - \ 'mime_forward', 'mime_forward_rest', 'move', 'pgp_mime_auto', - \ 'pop_delete', 'pop_reconnect', 'postpone', 'post_moderated', 'print', - \ 'quit', 'recall', 'reply_to', 'ssl_starttls', 'forward_attachments' - \ ], 0) + \ 'abort_noattach', 'abort_nosubject', 'abort_unmodified', 'bounce', 'catchup_newsgroup', + \ 'copy', 'crypt_verify_sig', 'delete', 'fcc_attach', 'followup_to_poster', + \ 'forward_attachments', 'forward_edit', 'honor_followup_to', 'include', 'mime_forward', + \ 'mime_forward_rest', 'move', 'pgp_mime_auto', 'pop_delete', 'pop_reconnect', 'postpone', + \ 'post_moderated', 'print', 'quit', 'recall', 'reply_to', 'ssl_starttls', + \ ], 0) -" CHECKED 2019-11-02 +" CHECKED 2020-06-21 " Deprecated Quads -" List of DT_SYNONYM synonyms of Quads in MuttVars in init.h +" List of DT_SYNONYM or DT_DEPRECATED Quads in MuttVars in mutt_config.c call s:boolQuadGen('Quad', [ - \ 'mime_fwd', 'pgp_encrypt_self', 'pgp_verify_sig', 'smime_encrypt_self' - \ ], 1) + \ 'mime_fwd', 'pgp_encrypt_self', 'pgp_verify_sig', 'smime_encrypt_self' + \ ], 1) -" CHECKED 2019-11-02 -" List of DT_NUMBER or DT_LONG in MuttVars in init.h +" CHECKED 2020-06-21 +" List of DT_NUMBER or DT_LONG in MuttVars in mutt_config.c syntax keyword muttrcVarNum skipwhite contained - \ connect_timeout debug_level history imap_keepalive imap_pipeline_depth - \ imap_poll_timeout mail_check mail_check_stats_interval menu_context - \ net_inc nm_db_limit nm_open_timeout nm_query_window_current_position - \ nm_query_window_duration nntp_context nntp_poll pager_context - \ pager_index_lines pgp_timeout pop_checkinterval read_inc reflow_wrap - \ save_history score_threshold_delete score_threshold_flag - \ score_threshold_read search_context sendmail_wait sidebar_component_depth - \ sidebar_width skip_quoted_offset sleep_time smime_timeout - \ ssl_min_dh_prime_bits timeout time_inc wrap wrap_headers write_inc - \ header_cache_pagesize imap_fetch_chunk_size toggle_quoted_show_levels - \ nextgroup=muttrcSetNumAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr + \ connect_timeout debug_level header_cache_compress_level history + \ imap_fetch_chunk_size imap_keepalive imap_pipeline_depth imap_poll_timeout mail_check + \ mail_check_stats_interval menu_context net_inc nm_db_limit nm_open_timeout + \ nm_query_window_current_position nm_query_window_duration nntp_context nntp_poll + \ pager_context pager_index_lines pgp_timeout pop_checkinterval read_inc reflow_wrap + \ save_history score_threshold_delete score_threshold_flag score_threshold_read + \ search_context sendmail_wait sidebar_component_depth sidebar_width skip_quoted_offset + \ sleep_time smime_timeout ssl_min_dh_prime_bits timeout time_inc toggle_quoted_show_levels + \ wrap wrap_headers write_inc + \ nextgroup=muttrcSetNumAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr syntax keyword muttrcVarDeprecatedNum contained skipwhite - \ wrapmargin - \ nextgroup=muttrcSetNumAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr + \ header_cache_pagesize wrapmargin + \ nextgroup=muttrcSetNumAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr -" CHECKED 2019-11-02 -" List of DT_STRING in MuttVars in init.h +" CHECKED 2020-06-21 +" List of DT_STRING in MuttVars in mutt_config.c " Special cases first, and all the rest at the end " Formats themselves must be updated in their respective groups " See s:escapesConditionals -syntax match muttrcVarStr contained skipwhite 'my_[a-zA-Z0-9_]\+' nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr +syntax match muttrcVarStr contained skipwhite 'my_[a-zA-Z0-9_]\+' nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr syntax keyword muttrcVarStr contained skipwhite alias_format nextgroup=muttrcVarEqualsAliasFmt syntax keyword muttrcVarStr contained skipwhite attach_format nextgroup=muttrcVarEqualsAttachFmt syntax keyword muttrcVarStr contained skipwhite compose_format nextgroup=muttrcVarEqualsComposeFmt syntax keyword muttrcVarStr contained skipwhite folder_format vfolder_format nextgroup=muttrcVarEqualsFolderFmt -syntax keyword muttrcVarStr contained skipwhite attribution index_format message_format pager_format nextgroup=muttrcVarEqualsIdxFmt -" Deprecated format -syntax keyword muttrcVarDeprecatedStr contained skipwhite hdr_format msg_format nextgroup=muttrcVarEqualsIdxFmt +syntax keyword muttrcVarStr contained skipwhite attribution forward_format index_format message_format pager_format nextgroup=muttrcVarEqualsIdxFmt syntax keyword muttrcVarStr contained skipwhite mix_entry_format nextgroup=muttrcVarEqualsMixFmt syntax keyword muttrcVarStr contained skipwhite - \ pgp_clearsign_command pgp_decode_command pgp_decrypt_command - \ pgp_encrypt_only_command pgp_encrypt_sign_command pgp_export_command - \ pgp_import_command pgp_list_pubring_command pgp_list_secring_command - \ pgp_sign_command pgp_verify_command pgp_verify_key_command - \ nextgroup=muttrcVarEqualsPGPCmdFmt + \ pgp_clearsign_command pgp_decode_command pgp_decrypt_command + \ pgp_encrypt_only_command pgp_encrypt_sign_command pgp_export_command pgp_getkeys_command + \ pgp_import_command pgp_list_pubring_command pgp_list_secring_command + \ pgp_sign_command pgp_verify_command pgp_verify_key_command + \ nextgroup=muttrcVarEqualsPGPCmdFmt syntax keyword muttrcVarStr contained skipwhite pgp_entry_format nextgroup=muttrcVarEqualsPGPFmt -syntax keyword muttrcVarStr contained skipwhite pgp_getkeys_command nextgroup=muttrcVarEqualsPGPGetKeysFmt syntax keyword muttrcVarStr contained skipwhite query_format nextgroup=muttrcVarEqualsQueryFmt syntax keyword muttrcVarStr contained skipwhite - \ smime_decrypt_command smime_encrypt_command smime_get_cert_command - \ smime_get_cert_email_command smime_get_signer_cert_command - \ smime_import_cert_command smime_pk7out_command smime_sign_command - \ smime_verify_command smime_verify_opaque_command - \ nextgroup=muttrcVarEqualsSmimeFmt -syntax keyword muttrcVarStr contained skipwhite ts_icon_format ts_status_format status_format nextgroup=muttrcVarEqualsStatusFmt -" Deprecated format -syntax keyword muttrcVarDeprecatedStr contained skipwhite xterm_icon xterm_title nextgroup=muttrcVarEqualsStatusFmt + \ smime_decrypt_command smime_encrypt_command smime_get_cert_command + \ smime_get_cert_email_command smime_get_signer_cert_command + \ smime_import_cert_command smime_pk7out_command smime_sign_command + \ smime_verify_command smime_verify_opaque_command + \ nextgroup=muttrcVarEqualsSmimeFmt +syntax keyword muttrcVarStr contained skipwhite status_format ts_icon_format ts_status_format nextgroup=muttrcVarEqualsStatusFmt syntax keyword muttrcVarStr contained skipwhite date_format nextgroup=muttrcVarEqualsStrftimeFmt syntax keyword muttrcVarStr contained skipwhite group_index_format nextgroup=muttrcVarEqualsGrpIdxFmt syntax keyword muttrcVarStr contained skipwhite sidebar_format nextgroup=muttrcVarEqualsSdbFmt syntax keyword muttrcVarStr contained skipwhite - \ assumed_charset attach_charset attach_sep attribution_locale charset - \ config_charset content_type default_hook dsn_notify dsn_return - \ empty_subject escape forward_attribution_intro forward_attribution_trailer - \ forward_format hidden_tags hostname - \ imap_authenticators imap_delim_chars imap_headers imap_login imap_pass - \ imap_user indent_string mailcap_path mark_macro_prefix mh_seq_flagged - \ mh_seq_replied mh_seq_unseen newsgroups_charset - \ news_server nm_default_uri nm_exclude_tags nm_query_type - \ nm_query_window_current_search nm_query_window_timebase nm_record_tags - \ nm_unread_tag nntp_authenticators nntp_pass nntp_user pgp_default_key - \ pgp_sign_as pipe_sep pop_authenticators pop_host pop_pass pop_user - \ postpone_encrypt_as post_indent_string preconnect realname send_charset - \ show_multipart_alternative sidebar_delim_chars sidebar_divider_char - \ sidebar_indent_string simple_search smime_default_key smime_encrypt_with - \ smime_sign_as smime_sign_digest_alg smtp_authenticators smtp_pass smtp_url - \ spam_separator ssl_ciphers autocrypt_acct_format - \ crypt_protected_headers_subject header_cache_backend nm_flagged_tag - \ nm_replied_tag preferred_languages - \ nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr + \ abort_key arrow_string assumed_charset attach_charset attach_sep attribution_locale + \ autocrypt_acct_format charset config_charset content_type crypt_protected_headers_subject + \ default_hook dsn_notify dsn_return empty_subject escape forward_attribution_intro + \ forward_attribution_trailer header_cache_backend header_cache_compress_method hidden_tags + \ hostname imap_authenticators imap_delim_chars imap_headers imap_login imap_pass imap_user + \ indent_string mailcap_path mark_macro_prefix mh_seq_flagged mh_seq_replied mh_seq_unseen + \ newsgroups_charset news_server nm_default_url nm_exclude_tags nm_flagged_tag nm_query_type + \ nm_query_window_current_search nm_query_window_timebase nm_record_tags nm_replied_tag + \ nm_unread_tag nntp_authenticators nntp_pass nntp_user pgp_default_key pgp_sign_as pipe_sep + \ pop_authenticators pop_host pop_pass pop_user postpone_encrypt_as post_indent_string + \ preconnect preferred_languages realname send_charset show_multipart_alternative + \ sidebar_delim_chars sidebar_divider_char sidebar_indent_string simple_search + \ smime_default_key smime_encrypt_with smime_sign_as smime_sign_digest_alg + \ smtp_authenticators smtp_pass smtp_url smtp_user spam_separator ssl_ciphers + \ nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr + " Deprecated strings -syntax keyword muttrcVarDeprecatedStr contained skipwhite - \ forw_format indent_str pgp_self_encrypt_as post_indent_str - \ smime_self_encrypt_as - \ nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr +syntax keyword muttrcVarDeprecatedStr + \ abort_noattach_regexp attach_keyword forw_format hdr_format indent_str msg_format + \ nm_default_uri pgp_self_encrypt_as post_indent_str print_cmd quote_regexp reply_regexp + \ smime_self_encrypt_as xterm_icon xterm_title -" CHECKED 2019-11-02 +" CHECKED 2020-06-21 " List of DT_ADDRESS -syntax keyword muttrcVarStr contained skipwhite envelope_from_address from nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr +syntax keyword muttrcVarStr contained skipwhite envelope_from_address from nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr " List of DT_ENUM -syntax keyword muttrcVarStr contained skipwhite mbox_type nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr +syntax keyword muttrcVarStr contained skipwhite mbox_type nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr " List of DT_MBTABLE -syntax keyword muttrcVarStr contained skipwhite crypt_chars flag_chars from_chars status_chars to_chars nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr +syntax keyword muttrcVarStr contained skipwhite crypt_chars flag_chars from_chars status_chars to_chars nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr -" CHECKED 2019-11-02 +" CHECKED 2020-06-21 " List of DT_PATH syntax keyword muttrcVarStr contained skipwhite - \ alias_file attach_save_dir autocrypt_dir certificate_file debug_file - \ entropy_file folder header_cache history_file mbox message_cachedir newsrc - \ news_cache_dir postponed record signature smime_ca_location - \ smime_certificates smime_keys spoolfile ssl_ca_certificates_file - \ ssl_client_cert tmpdir trash - \ nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr + \ alias_file attach_save_dir autocrypt_dir certificate_file debug_file + \ entropy_file folder header_cache history_file mbox message_cachedir newsrc + \ news_cache_dir postponed record signature smime_ca_location + \ smime_certificates smime_keys spoolfile ssl_ca_certificates_file + \ ssl_client_cert tmpdir trash + \ nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr " List of DT_COMMAND (excluding pgp_*_command and smime_*_command) syntax keyword muttrcVarStr contained skipwhite - \ display_filter editor inews ispell mixmaster new_mail_command pager - \ print_command query_command sendmail shell visual external_search_command - \ imap_oauth_refresh_command pop_oauth_refresh_command - \ mime_type_query_command smtp_oauth_refresh_command tunnel - \ nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr + \ display_filter editor inews ispell mixmaster new_mail_command pager + \ print_command query_command sendmail shell visual external_search_command + \ imap_oauth_refresh_command pop_oauth_refresh_command + \ mime_type_query_command smtp_oauth_refresh_command tunnel + \ nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr -" CHECKED 2019-11-02 +" CHECKED 2020-06-21 " List of DT_REGEX syntax keyword muttrcVarStr contained skipwhite - \ abort_noattach_regex gecos_mask mask pgp_decryption_okay pgp_good_sign - \ quote_regex reply_regex smileys - \ nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -" List of deprecated DT_STRING|DT_COMMAND -syntax keyword muttrcVarDeprecatedStr contained skipwhite print_cmd nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr -" List of deprecated DT_REGEX -syntax keyword muttrcVarDeprecatedStr contained skipwhite abort_noattach_regexp attach_keyword quote_regexp reply_regexp nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr + \ abort_noattach_regex gecos_mask mask pgp_decryption_okay pgp_good_sign + \ quote_regex reply_regex smileys + \ nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr " List of DT_SORT syntax keyword muttrcVarStr contained skipwhite - \ pgp_sort_keys sidebar_sort_method sort sort_alias sort_aux sort_browser - \ nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr + \ pgp_sort_keys sidebar_sort_method sort sort_alias sort_aux sort_browser + \ nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr -" CHECKED 2019-11-02 -" List of commands in Commands in init.h +" CHECKED 2020-06-21 +" List of commands in Commands in mutt_config.c " Remember to remove hooks, they have already been dealt with -syntax keyword muttrcCommand skipwhite charset-hook nextgroup=muttrcRXString -syntax keyword muttrcCommand skipwhite unhook nextgroup=muttrcHooks -syntax keyword muttrcCommand skipwhite spam nextgroup=muttrcSpamPattern -syntax keyword muttrcCommand skipwhite nospam nextgroup=muttrcNoSpamPattern -syntax keyword muttrcCommand skipwhite bind nextgroup=muttrcBindMenuList,muttrcBindMenuListNL -syntax keyword muttrcCommand skipwhite macro nextgroup=muttrcMacroMenuList,muttrcMacroMenuListNL syntax keyword muttrcCommand skipwhite alias nextgroup=muttrcAliasGroupDef,muttrcAliasKey,muttrcAliasNL -syntax keyword muttrcCommand skipwhite unalias nextgroup=muttrcUnAliasKey,muttrcUnAliasNL -syntax keyword muttrcCommand skipwhite set unset reset toggle nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr,muttrcVarDeprecatedBool,muttrcVarDeprecatedQuad,muttrcVarDeprecatedStr +syntax keyword muttrcCommand skipwhite bind nextgroup=muttrcBindMenuList,muttrcBindMenuListNL syntax keyword muttrcCommand skipwhite exec nextgroup=muttrcFunction +syntax keyword muttrcCommand skipwhite macro nextgroup=muttrcMacroMenuList,muttrcMacroMenuListNL +syntax keyword muttrcCommand skipwhite nospam nextgroup=muttrcNoSpamPattern +syntax keyword muttrcCommand skipwhite set unset reset toggle nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax keyword muttrcCommand skipwhite spam nextgroup=muttrcSpamPattern +syntax keyword muttrcCommand skipwhite unalias nextgroup=muttrcUnAliasKey,muttrcUnAliasNL +syntax keyword muttrcCommand skipwhite unhook nextgroup=muttrcHooks syntax keyword muttrcCommand skipwhite - \ alternative_order attachments auto_view finish hdr_order ifdef ifndef - \ ignore lua lua-source mailboxes mailto_allow mime_lookup my_hdr push score - \ setenv sidebar_whitelist source subjectrx subscribe-to tag-formats - \ tag-transforms unalternative_order unattachments unauto_view uncolor - \ unhdr_order unignore unmailboxes unmailto_allow unmime_lookup unmono - \ unmy_hdr unscore unsetenv unsidebar_whitelist unsubjectrx unsubscribe-from - \ unvirtual-mailboxes virtual-mailboxes named-mailboxes - \ echo unbind unmacro + \ alternative_order attachments auto_view finish hdr_order ifdef ifndef + \ ignore lua lua-source mailboxes mailto_allow mime_lookup my_hdr push score + \ setenv sidebar_whitelist source subjectrx subscribe-to tag-formats + \ tag-transforms unalternative_order unattachments unauto_view uncolor + \ unhdr_order unignore unmailboxes unmailto_allow unmime_lookup unmono + \ unmy_hdr unscore unsetenv unsidebar_whitelist unsubjectrx unsubscribe-from + \ unvirtual-mailboxes virtual-mailboxes named-mailboxes + \ echo unbind unmacro -" CHECKED 2019-11-02 -" List of functions in functions.h -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" -syntax match muttrcFunction contained "\" +function! s:genFunctions(functions) + for f in a:functions + exec 'syntax match muttrcFunction contained "\<' . l:f . '\>"' + endfor +endfunction + +" CHECKED 2020-06-21 +" List of functions in functions.c +" Note: 'noop' is included but is elsewhere in the source +call s:genFunctions(['noop', + \ 'accept', 'append', 'attach-file', 'attach-key', 'attach-message', 'attach-news-message', + \ 'autocrypt-acct-menu', 'autocrypt-menu', 'backspace', 'backward-char', 'backward-word', + \ 'bol', 'bottom-page', 'bottom', 'bounce-message', 'break-thread', 'buffy-cycle', + \ 'buffy-list', 'capitalize-word', 'catchup', 'chain-next', 'chain-prev', 'change-dir', + \ 'change-folder-readonly', 'change-folder', 'change-newsgroup-readonly', + \ 'change-newsgroup', 'change-vfolder', 'check-new', 'check-stats', + \ 'check-traditional-pgp', 'clear-flag', 'collapse-all', 'collapse-parts', + \ 'collapse-thread', 'complete-query', 'complete', 'compose-to-sender', 'copy-file', + \ 'copy-message', 'create-account', 'create-alias', 'create-mailbox', 'current-bottom', + \ 'current-middle', 'current-top', 'decode-copy', 'decode-save', 'decrypt-copy', + \ 'decrypt-save', 'delete-account', 'delete-char', 'delete-entry', 'delete-mailbox', + \ 'delete-message', 'delete-pattern', 'delete-subthread', 'delete-thread', 'delete', + \ 'descend-directory', 'detach-file', 'display-address', 'display-filename', + \ 'display-message', 'display-toggle-weed', 'downcase-word', 'edit-bcc', 'edit-cc', + \ 'edit-description', 'edit-encoding', 'edit-fcc', 'edit-file', 'edit-followup-to', + \ 'edit-from', 'edit-headers', 'edit-label', 'edit-language', 'edit-message', 'edit-mime', + \ 'edit-newsgroups', 'edit-or-view-raw-message', 'edit-raw-message', 'edit-reply-to', + \ 'edit-subject', 'edit-to', 'edit-type', 'edit-x-comment-to', 'edit', 'end-cond', + \ 'enter-command', 'enter-mask', 'entire-thread', 'eol', 'exit', 'extract-keys', + \ 'fetch-mail', 'filter-entry', 'first-entry', 'flag-message', 'followup-message', + \ 'forget-passphrase', 'forward-char', 'forward-message', 'forward-to-group', + \ 'forward-word', 'get-attachment', 'get-children', 'get-message', 'get-parent', + \ 'goto-folder', 'goto-parent', 'group-alternatives', 'group-chat-reply', + \ 'group-multilingual', 'group-reply', 'half-down', 'half-up', 'help', 'history-down', + \ 'history-search', 'history-up', 'imap-fetch-mail', 'imap-logout-all', 'insert', 'ispell', + \ 'jump', 'kill-eol', 'kill-eow', 'kill-line', 'kill-word', 'last-entry', + \ 'limit-current-thread', 'limit', 'link-threads', 'list-reply', 'mail-key', + \ 'mailbox-cycle', 'mailbox-list', 'mail', 'mark-as-new', 'mark-message', 'middle-page', + \ 'mix', 'modify-labels-then-hide', 'modify-labels', 'modify-tags-then-hide', + \ 'modify-tags', 'move-down', 'move-up', 'new-mime', 'next-entry', 'next-line', + \ 'next-new-then-unread', 'next-new', 'next-page', 'next-subthread', 'next-thread', + \ 'next-undeleted', 'next-unread-mailbox', 'next-unread', 'parent-message', 'pgp-menu', + \ 'pipe-entry', 'pipe-message', 'post-message', 'postpone-message', 'previous-entry', + \ 'previous-line', 'previous-new-then-unread', 'previous-new', 'previous-page', + \ 'previous-subthread', 'previous-thread', 'previous-undeleted', 'previous-unread', + \ 'print-entry', 'print-message', 'purge-message', 'purge-thread', 'quasi-delete', + \ 'query-append', 'query', 'quit', 'quote-char', 'read-subthread', 'read-thread', + \ 'recall-message', 'reconstruct-thread', 'redraw-screen', 'refresh', 'reload-active', + \ 'rename-attachment', 'rename-file', 'rename-mailbox', 'reply', 'resend-message', + \ 'root-message', 'save-entry', 'save-message', 'search-next', 'search-opposite', + \ 'search-reverse', 'search-toggle', 'search', 'select-entry', 'select-new', + \ 'send-message', 'set-flag', 'shell-escape', 'show-limit', 'show-log-messages', + \ 'show-version', 'sidebar-next-new', 'sidebar-first', 'sidebar-last', 'sidebar-next', + \ 'sidebar-open', 'sidebar-page-down', 'sidebar-page-up', 'sidebar-prev-new', + \ 'sidebar-prev', 'sidebar-toggle-virtual', 'sidebar-toggle-visible', 'skip-quoted', + \ 'smime-menu', 'sort-mailbox', 'sort-reverse', 'sort', 'subscribe-pattern', + \ 'sync-mailbox', 'tag-entry', 'tag-message', 'tag-pattern', 'tag-prefix-cond', + \ 'tag-prefix', 'tag-subthread', 'tag-thread', 'toggle-active', 'toggle-disposition', + \ 'toggle-mailboxes', 'toggle-new', 'toggle-prefer-encrypt', 'toggle-quoted', + \ 'toggle-read', 'toggle-recode', 'toggle-subscribed', 'toggle-unlink', 'toggle-write', + \ 'top-page', 'top', 'transpose-chars', 'uncatchup', 'undelete-entry', 'undelete-message', + \ 'undelete-pattern', 'undelete-subthread', 'undelete-thread', 'unsubscribe-pattern', + \ 'untag-pattern', 'upcase-word', 'update-encoding', 'verify-key', + \ 'vfolder-from-query-readonly', 'vfolder-from-query', 'vfolder-window-backward', + \ 'vfolder-window-forward', 'view-attachments', 'view-attach', 'view-file', 'view-mailcap', + \ 'view-name', 'view-raw-message', 'view-text', 'what-key', 'write-fcc' + \ ]) " Define the default highlighting. " Only when an item doesn't have highlighting yet -highlight def link muttrcComment Comment -highlight def link muttrcEscape SpecialChar -highlight def link muttrcRXChars SpecialChar -highlight def link muttrcString String -highlight def link muttrcRXString String -highlight def link muttrcRXString2 String -highlight def link muttrcSpecial Special -highlight def link muttrcHooks Type -highlight def link muttrcGroupFlag Type -highlight def link muttrcGroupDef Macro -highlight def link muttrcAddrDef muttrcGroupFlag -highlight def link muttrcRXDef muttrcGroupFlag -highlight def link muttrcRXPat String -highlight def link muttrcAliasGroupName Macro -highlight def link muttrcAliasKey Identifier -highlight def link muttrcUnAliasKey Identifier -highlight def link muttrcAliasEncEmail Identifier -highlight def link muttrcAliasParens Type -highlight def link muttrcSetNumAssignment Number highlight def link muttrcSetBoolAssignment Boolean highlight def link muttrcSetQuadAssignment Boolean -highlight def link muttrcSetStrAssignment String -highlight def link muttrcEmail Special -highlight def link muttrcVariableInner Special -highlight def link muttrcEscapedVariable String -highlight def link muttrcHeader Type -highlight def link muttrcKeySpecial SpecialChar -highlight def link muttrcKey Type -highlight def link muttrcKeyName SpecialChar -highlight def link muttrcVarBool Identifier -highlight def link muttrcVarQuad Identifier -highlight def link muttrcVarNum Identifier -highlight def link muttrcVarStr Identifier -highlight def link muttrcMenu Identifier -highlight def link muttrcCommand Keyword -highlight def link muttrcMacroDescr String -highlight def link muttrcAction Macro + +highlight def link muttrcComment Comment + +highlight def link muttrcAlternatesLine Error highlight def link muttrcBadAction Error highlight def link muttrcBindFunction Error highlight def link muttrcBindMenuList Error -highlight def link muttrcFunction Macro -highlight def link muttrcGroupKeyword muttrcCommand -highlight def link muttrcGroupLine Error -highlight def link muttrcSubscribeKeyword muttrcCommand -highlight def link muttrcSubscribeLine Error -highlight def link muttrcListsKeyword muttrcCommand -highlight def link muttrcListsLine Error -highlight def link muttrcAlternateKeyword muttrcCommand -highlight def link muttrcAlternatesLine Error -highlight def link muttrcAttachmentsLine muttrcCommand -highlight def link muttrcAttachmentsFlag Type -highlight def link muttrcAttachmentsMimeType String -highlight def link muttrcColorLine Error -highlight def link muttrcColorContext Error -highlight def link muttrcColorContextI Identifier -highlight def link muttrcColorContextH Identifier -highlight def link muttrcColorKeyword muttrcCommand -highlight def link muttrcColorField Identifier -highlight def link muttrcColorCompose Identifier -highlight def link muttrcColorComposeField Identifier -highlight def link muttrcColor Type -highlight def link muttrcColorFG Error -highlight def link muttrcColorFGI Error -highlight def link muttrcColorFGH Error highlight def link muttrcColorBG Error -highlight def link muttrcColorBGI Error highlight def link muttrcColorBGH Error -highlight def link muttrcMonoAttrib muttrcColor -highlight def link muttrcMono muttrcCommand -highlight def link muttrcSimplePat Identifier -highlight def link muttrcSimplePatString Macro -highlight def link muttrcSimplePatMetas Special -highlight def link muttrcPattern Error -highlight def link muttrcUnColorLine Error -highlight def link muttrcUnColorKeyword muttrcCommand -highlight def link muttrcUnColorIndex Identifier -highlight def link muttrcShellString muttrcEscape -highlight def link muttrcRXHooks muttrcCommand -highlight def link muttrcRXHookNot Type -highlight def link muttrcPatHooks muttrcCommand -highlight def link muttrcPatHookNot Type -highlight def link muttrcFormatConditionals2 Type -highlight def link muttrcIndexFormatStr muttrcString -highlight def link muttrcIndexFormatEscapes muttrcEscape -highlight def link muttrcIndexFormatConditionals muttrcFormatConditionals2 -highlight def link muttrcAliasFormatStr muttrcString -highlight def link muttrcAliasFormatEscapes muttrcEscape -highlight def link muttrcAttachFormatStr muttrcString -highlight def link muttrcAttachFormatEscapes muttrcEscape -highlight def link muttrcAttachFormatConditionals muttrcFormatConditionals2 -highlight def link muttrcComposeFormatStr muttrcString -highlight def link muttrcComposeFormatEscapes muttrcEscape -highlight def link muttrcFolderFormatStr muttrcString -highlight def link muttrcFolderFormatEscapes muttrcEscape -highlight def link muttrcFolderFormatConditionals muttrcFormatConditionals2 -highlight def link muttrcMixFormatStr muttrcString -highlight def link muttrcMixFormatEscapes muttrcEscape -highlight def link muttrcMixFormatConditionals muttrcFormatConditionals2 -highlight def link muttrcPGPFormatStr muttrcString -highlight def link muttrcPGPFormatEscapes muttrcEscape -highlight def link muttrcPGPFormatConditionals muttrcFormatConditionals2 -highlight def link muttrcPGPCmdFormatStr muttrcString -highlight def link muttrcPGPCmdFormatEscapes muttrcEscape -highlight def link muttrcPGPCmdFormatConditionals muttrcFormatConditionals2 -highlight def link muttrcStatusFormatStr muttrcString -highlight def link muttrcStatusFormatEscapes muttrcEscape -highlight def link muttrcStatusFormatConditionals muttrcFormatConditionals2 -highlight def link muttrcPGPGetKeysFormatStr muttrcString -highlight def link muttrcPGPGetKeysFormatEscapes muttrcEscape -highlight def link muttrcSmimeFormatStr muttrcString -highlight def link muttrcSmimeFormatEscapes muttrcEscape -highlight def link muttrcSmimeFormatConditionals muttrcFormatConditionals2 -highlight def link muttrcTimeEscapes muttrcEscape -highlight def link muttrcPGPTimeEscapes muttrcEscape -highlight def link muttrcStrftimeEscapes Type -highlight def link muttrcStrftimeFormatStr muttrcString +highlight def link muttrcColorBGI Error +highlight def link muttrcColorContext Error +highlight def link muttrcColorFG Error +highlight def link muttrcColorFGH Error +highlight def link muttrcColorFGI Error +highlight def link muttrcColorLine Error highlight def link muttrcFormatErrors Error - -highlight def link muttrcBindFunctionNL SpecialChar -highlight def link muttrcBindKeyNL SpecialChar -highlight def link muttrcBindMenuListNL SpecialChar -highlight def link muttrcMacroDescrNL SpecialChar -highlight def link muttrcMacroBodyNL SpecialChar -highlight def link muttrcMacroKeyNL SpecialChar -highlight def link muttrcMacroMenuListNL SpecialChar -highlight def link muttrcColorMatchCountNL SpecialChar -highlight def link muttrcColorNL SpecialChar -highlight def link muttrcColorRXNL SpecialChar -highlight def link muttrcColorBGNL SpecialChar -highlight def link muttrcColorFGNL SpecialChar -highlight def link muttrcAliasNameNL SpecialChar -highlight def link muttrcAliasENNL SpecialChar -highlight def link muttrcAliasNL SpecialChar -highlight def link muttrcUnAliasNL SpecialChar -highlight def link muttrcAliasGroupDefNL SpecialChar -highlight def link muttrcAliasEncEmailNL SpecialChar -highlight def link muttrcPatternNL SpecialChar -highlight def link muttrcUnColorPatNL SpecialChar -highlight def link muttrcUnColorAPNL SpecialChar -highlight def link muttrcUnColorIndexNL SpecialChar -highlight def link muttrcStringNL SpecialChar - +highlight def link muttrcGroupLine Error +highlight def link muttrcListsLine Error +highlight def link muttrcPattern Error +highlight def link muttrcSubscribeLine Error +highlight def link muttrcUnColorLine Error highlight def link muttrcVarDeprecatedBool Error highlight def link muttrcVarDeprecatedQuad Error highlight def link muttrcVarDeprecatedStr Error +highlight def link muttrcAliasEncEmail Identifier +highlight def link muttrcAliasKey Identifier +highlight def link muttrcColorCompose Identifier +highlight def link muttrcColorComposeField Identifier +highlight def link muttrcColorContextH Identifier +highlight def link muttrcColorContextI Identifier +highlight def link muttrcColorField Identifier +highlight def link muttrcMenu Identifier +highlight def link muttrcSimplePat Identifier +highlight def link muttrcUnAliasKey Identifier +highlight def link muttrcUnColorIndex Identifier +highlight def link muttrcVarBool Identifier +highlight def link muttrcVarNum Identifier +highlight def link muttrcVarQuad Identifier +highlight def link muttrcVarStr Identifier + +highlight def link muttrcCommand Keyword + +highlight def link muttrcAction Macro +highlight def link muttrcAliasGroupName Macro +highlight def link muttrcFunction Macro +highlight def link muttrcGroupDef Macro +highlight def link muttrcSimplePatString Macro + +highlight def link muttrcMonoAttrib muttrcColor + +highlight def link muttrcAlternateKeyword muttrcCommand +highlight def link muttrcAttachmentsLine muttrcCommand +highlight def link muttrcColorKeyword muttrcCommand +highlight def link muttrcGroupKeyword muttrcCommand +highlight def link muttrcListsKeyword muttrcCommand +highlight def link muttrcMono muttrcCommand +highlight def link muttrcPatHooks muttrcCommand +highlight def link muttrcRXHooks muttrcCommand +highlight def link muttrcSubscribeKeyword muttrcCommand +highlight def link muttrcUnColorKeyword muttrcCommand + +highlight def link muttrcAliasFormatEscapes muttrcEscape +highlight def link muttrcAttachFormatEscapes muttrcEscape +highlight def link muttrcComposeFormatEscapes muttrcEscape +highlight def link muttrcFolderFormatEscapes muttrcEscape +highlight def link muttrcGroupIndexFormatEscapes muttrcEscape +highlight def link muttrcIndexFormatEscapes muttrcEscape +highlight def link muttrcMixFormatEscapes muttrcEscape +highlight def link muttrcPGPCmdFormatEscapes muttrcEscape +highlight def link muttrcPGPFormatEscapes muttrcEscape +highlight def link muttrcPGPTimeEscapes muttrcEscape +highlight def link muttrcQueryFormatEscapes muttrcEscape +highlight def link muttrcShellString muttrcEscape +highlight def link muttrcSidebarFormatEscapes muttrcEscape +highlight def link muttrcSmimeFormatEscapes muttrcEscape +highlight def link muttrcStatusFormatEscapes muttrcEscape +highlight def link muttrcTimeEscapes muttrcEscape + +highlight def link muttrcAliasFormatConditionals muttrcFormatConditionals2 +highlight def link muttrcAttachFormatConditionals muttrcFormatConditionals2 +highlight def link muttrcComposeFormatConditionals muttrcFormatConditionals2 +highlight def link muttrcFolderFormatConditionals muttrcFormatConditionals2 +highlight def link muttrcIndexFormatConditionals muttrcFormatConditionals2 +highlight def link muttrcMixFormatConditionals muttrcFormatConditionals2 +highlight def link muttrcPGPCmdFormatConditionals muttrcFormatConditionals2 +highlight def link muttrcPGPFormatConditionals muttrcFormatConditionals2 +highlight def link muttrcSmimeFormatConditionals muttrcFormatConditionals2 +highlight def link muttrcStatusFormatConditionals muttrcFormatConditionals2 + +highlight def link muttrcAddrDef muttrcGroupFlag +highlight def link muttrcRXDef muttrcGroupFlag + +highlight def link muttrcAliasFormatStr muttrcString +highlight def link muttrcAttachFormatStr muttrcString +highlight def link muttrcComposeFormatStr muttrcString +highlight def link muttrcFolderFormatStr muttrcString +highlight def link muttrcGroupIndexFormatStr muttrcString +highlight def link muttrcIndexFormatStr muttrcString +highlight def link muttrcMixFormatStr muttrcString +highlight def link muttrcPGPCmdFormatStr muttrcString +highlight def link muttrcPGPFormatStr muttrcString +highlight def link muttrcQueryFormatStr muttrcString +highlight def link muttrcSidebarFormatStr muttrcString +highlight def link muttrcSmimeFormatStr muttrcString +highlight def link muttrcStatusFormatStr muttrcString +highlight def link muttrcStrftimeFormatStr muttrcString + +highlight def link muttrcSetNumAssignment Number + +highlight def link muttrcEmail Special +highlight def link muttrcSimplePatMetas Special +highlight def link muttrcSpecial Special +highlight def link muttrcVariableInner Special + +highlight def link muttrcAliasEncEmailNL SpecialChar +highlight def link muttrcAliasENNL SpecialChar +highlight def link muttrcAliasGroupDefNL SpecialChar +highlight def link muttrcAliasNameNL SpecialChar +highlight def link muttrcAliasNL SpecialChar +highlight def link muttrcBindFunctionNL SpecialChar +highlight def link muttrcBindKeyNL SpecialChar +highlight def link muttrcBindMenuListNL SpecialChar +highlight def link muttrcColorBGNL SpecialChar +highlight def link muttrcColorFGNL SpecialChar +highlight def link muttrcColorMatchCountNL SpecialChar +highlight def link muttrcColorNL SpecialChar +highlight def link muttrcColorRXNL SpecialChar +highlight def link muttrcEscape SpecialChar +highlight def link muttrcKeyName SpecialChar +highlight def link muttrcKeySpecial SpecialChar +highlight def link muttrcMacroBodyNL SpecialChar +highlight def link muttrcMacroDescrNL SpecialChar +highlight def link muttrcMacroKeyNL SpecialChar +highlight def link muttrcMacroMenuListNL SpecialChar +highlight def link muttrcPatternNL SpecialChar +highlight def link muttrcRXChars SpecialChar +highlight def link muttrcStringNL SpecialChar +highlight def link muttrcUnAliasNL SpecialChar +highlight def link muttrcUnColorAPNL SpecialChar +highlight def link muttrcUnColorIndexNL SpecialChar +highlight def link muttrcUnColorPatNL SpecialChar + +highlight def link muttrcAttachmentsMimeType String +highlight def link muttrcEscapedVariable String +highlight def link muttrcMacroDescr String +highlight def link muttrcRXPat String +highlight def link muttrcRXString String +highlight def link muttrcRXString2 String +highlight def link muttrcSetStrAssignment String +highlight def link muttrcString String + +highlight def link muttrcAliasParens Type +highlight def link muttrcAttachmentsFlag Type +highlight def link muttrcColor Type +highlight def link muttrcFormatConditionals2 Type +highlight def link muttrcGroupFlag Type +highlight def link muttrcHeader Type +highlight def link muttrcHooks Type +highlight def link muttrcKey Type +highlight def link muttrcPatHookNot Type +highlight def link muttrcRXHookNot Type +highlight def link muttrcStrftimeEscapes Type + let b:current_syntax = "neomuttrc" let &cpo = s:cpo_save From 006ad48b8a15c3bace741d8caaf3195e592fbe78 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 30 Jun 2020 20:55:15 +0200 Subject: [PATCH 054/105] patch 8.2.1098: Vim9: cannot use line break in :throw argument Problem: Vim9: cannot use line break in :throw argument. Solution: Check for line break. --- src/eval.c | 12 ++++++++++-- src/testdir/test_vim9_script.vim | 14 ++++++++++++++ src/version.c | 2 ++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/eval.c b/src/eval.c index 5b69dc736d..a98eea34be 100644 --- a/src/eval.c +++ b/src/eval.c @@ -333,10 +333,18 @@ eval_to_string_skip( { typval_T tv; char_u *retval; + evalarg_T evalarg; + CLEAR_FIELD(evalarg); + evalarg.eval_flags = skip ? 0 : EVAL_EVALUATE; + if (eap != NULL && getline_equal(eap->getline, eap->cookie, getsourceline)) + { + evalarg.eval_getline = eap->getline; + evalarg.eval_cookie = eap->cookie; + } if (skip) ++emsg_skip; - if (eval0(arg, &tv, eap, skip ? NULL : &EVALARG_EVALUATE) == FAIL || skip) + if (eval0(arg, &tv, eap, &evalarg) == FAIL || skip) retval = NULL; else { @@ -345,7 +353,7 @@ eval_to_string_skip( } if (skip) --emsg_skip; - clear_evalarg(&EVALARG_EVALUATE, eap); + clear_evalarg(&evalarg, eap); return retval; } diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index ff1b134227..031b31917c 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -585,6 +585,20 @@ def Test_try_catch_fails() call CheckDefFailure(['throw xxx'], 'E1001:') enddef +def Test_throw_vimscript() + " only checks line continuation + let lines =<< trim END + vim9script + try + throw 'one' + .. 'two' + catch + assert_equal('onetwo', v:exception) + endtry + END + CheckScriptSuccess(lines) +enddef + if has('channel') let someJob = test_null_job() diff --git a/src/version.c b/src/version.c index c9815bc639..5854c37fcf 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1098, /**/ 1097, /**/ From 37c837119579ff70b005a4e54c2e26ca42b74022 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 30 Jun 2020 21:18:36 +0200 Subject: [PATCH 055/105] patch 8.2.1099: Vim9: cannot use line break in :cexpr argument Problem: Vim9: cannot use line break in :cexpr argument. Solution: Check for line break. --- src/eval.c | 43 +++++++++++++++----------------- src/testdir/test_vim9_script.vim | 14 +++++++++++ src/version.c | 2 ++ 3 files changed, 36 insertions(+), 23 deletions(-) diff --git a/src/eval.c b/src/eval.c index a98eea34be..060fd0c7d8 100644 --- a/src/eval.c +++ b/src/eval.c @@ -153,6 +153,18 @@ eval_clear(void) } #endif + static void +fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, int skip) +{ + CLEAR_FIELD(*evalarg); + evalarg->eval_flags = skip ? 0 : EVAL_EVALUATE; + if (eap != NULL && getline_equal(eap->getline, eap->cookie, getsourceline)) + { + evalarg->eval_getline = eap->getline; + evalarg->eval_cookie = eap->cookie; + } +} + /* * Top level evaluation function, returning a boolean. * Sets "error" to TRUE if there was an error. @@ -169,13 +181,7 @@ eval_to_bool( varnumber_T retval = FALSE; evalarg_T evalarg; - CLEAR_FIELD(evalarg); - evalarg.eval_flags = skip ? 0 : EVAL_EVALUATE; - if (eap != NULL && getline_equal(eap->getline, eap->cookie, getsourceline)) - { - evalarg.eval_getline = eap->getline; - evalarg.eval_cookie = eap->cookie; - } + fill_evalarg_from_eap(&evalarg, eap, skip); if (skip) ++emsg_skip; @@ -335,13 +341,7 @@ eval_to_string_skip( char_u *retval; evalarg_T evalarg; - CLEAR_FIELD(evalarg); - evalarg.eval_flags = skip ? 0 : EVAL_EVALUATE; - if (eap != NULL && getline_equal(eap->getline, eap->cookie, getsourceline)) - { - evalarg.eval_getline = eap->getline; - evalarg.eval_cookie = eap->cookie; - } + fill_evalarg_from_eap(&evalarg, eap, skip); if (skip) ++emsg_skip; if (eval0(arg, &tv, eap, &evalarg) == FAIL || skip) @@ -535,12 +535,15 @@ eval_to_number(char_u *expr) eval_expr(char_u *arg, exarg_T *eap) { typval_T *tv; + evalarg_T evalarg; + + fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip); tv = ALLOC_ONE(typval_T); - if (tv != NULL && eval0(arg, tv, eap, &EVALARG_EVALUATE) == FAIL) + if (tv != NULL && eval0(arg, tv, eap, &evalarg) == FAIL) VIM_CLEAR(tv); - clear_evalarg(&EVALARG_EVALUATE, eap); + clear_evalarg(&evalarg, eap); return tv; } @@ -5239,13 +5242,7 @@ ex_echo(exarg_T *eap) int called_emsg_before = called_emsg; evalarg_T evalarg; - CLEAR_FIELD(evalarg); - evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE; - if (getline_equal(eap->getline, eap->cookie, getsourceline)) - { - evalarg.eval_getline = eap->getline; - evalarg.eval_cookie = eap->cookie; - } + fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip); if (eap->skip) ++emsg_skip; diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index 031b31917c..447ea68b66 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -599,6 +599,20 @@ def Test_throw_vimscript() CheckScriptSuccess(lines) enddef +def Test_cexpr_vimscript() + " only checks line continuation + set errorformat=File\ %f\ line\ %l + let lines =<< trim END + vim9script + cexpr 'File' + .. ' someFile' .. + ' line 19' + assert_equal(19, getqflist()[0].lnum) + END + CheckScriptSuccess(lines) + set errorformat& +enddef + if has('channel') let someJob = test_null_job() diff --git a/src/version.c b/src/version.c index 5854c37fcf..0b85aaa755 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1099, /**/ 1098, /**/ From 47e880d6c13c3ec2888398fd9ba1f5a7180d791a Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 30 Jun 2020 22:02:02 +0200 Subject: [PATCH 056/105] patch 8.2.1100: Vim9: cannot use line break in :execute argument Problem: Vim9: cannot use line break in :execute, :echomsg and :echoerr argument. Solution: Check for line break. --- src/eval.c | 12 ++++++---- src/testdir/test_vim9_script.vim | 40 ++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/eval.c b/src/eval.c index 060fd0c7d8..e6843cdd05 100644 --- a/src/eval.c +++ b/src/eval.c @@ -207,14 +207,17 @@ eval_to_bool( * Call eval1() and give an error message if not done at a lower level. */ static int -eval1_emsg(char_u **arg, typval_T *rettv, int evaluate) +eval1_emsg(char_u **arg, typval_T *rettv, exarg_T *eap) { char_u *start = *arg; int ret; int did_emsg_before = did_emsg; int called_emsg_before = called_emsg; + evalarg_T evalarg; - ret = eval1(arg, rettv, evaluate ? &EVALARG_EVALUATE : NULL); + fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip); + + ret = eval1(arg, rettv, &evalarg); if (ret == FAIL) { // Report the invalid expression unless the expression evaluation has @@ -225,6 +228,7 @@ eval1_emsg(char_u **arg, typval_T *rettv, int evaluate) && called_emsg == called_emsg_before) semsg(_(e_invexpr2), start); } + clear_evalarg(&evalarg, eap); return ret; } @@ -294,7 +298,7 @@ eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv) if (s == NULL) return FAIL; s = skipwhite(s); - if (eval1_emsg(&s, rettv, TRUE) == FAIL) + if (eval1_emsg(&s, rettv, NULL) == FAIL) return FAIL; if (*s != NUL) // check for trailing chars after expr { @@ -5330,7 +5334,7 @@ ex_execute(exarg_T *eap) ++emsg_skip; while (!ends_excmd2(eap->cmd, arg) || *arg == '"') { - ret = eval1_emsg(&arg, &rettv, !eap->skip); + ret = eval1_emsg(&arg, &rettv, eap); if (ret == FAIL) break; diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index 447ea68b66..010fdcd2fc 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -1294,6 +1294,19 @@ def Test_execute_cmd() call CheckDefFailure(['execute "cmd"# comment'], 'E488:') enddef +def Test_execute_cmd_vimscript() + " only checks line continuation + let lines =<< trim END + vim9script + execute 'g:someVar' + .. ' = ' .. + '28' + assert_equal(28, g:someVar) + unlet g:someVar + END + CheckScriptSuccess(lines) +enddef + def Test_echo_cmd() echo 'some' # comment echon 'thing' @@ -1321,6 +1334,18 @@ def Test_echomsg_cmd() call CheckDefFailure(['echomsg "xxx"# comment'], 'E488:') enddef +def Test_echomsg_cmd_vimscript() + " only checks line continuation + let lines =<< trim END + vim9script + echomsg 'here' + .. ' is ' .. + 'a message' + assert_match('^here is a message$', Screenline(&lines)) + END + CheckScriptSuccess(lines) +enddef + def Test_echoerr_cmd() try echoerr 'something' 'wrong' # comment @@ -1329,6 +1354,21 @@ def Test_echoerr_cmd() endtry enddef +def Test_echoerr_cmd_vimscript() + " only checks line continuation + let lines =<< trim END + vim9script + try + echoerr 'this' + .. ' is ' .. + 'wrong' + catch + assert_match('this is wrong', v:exception) + endtry + END + CheckScriptSuccess(lines) +enddef + def Test_for_outside_of_function() let lines =<< trim END vim9script diff --git a/src/version.c b/src/version.c index 0b85aaa755..9da21730d2 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1100, /**/ 1099, /**/ From be7a50c22f63478a6e64fe6b932a847830191b95 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 30 Jun 2020 22:11:44 +0200 Subject: [PATCH 057/105] patch 8.2.1101: no error when using wrong arguments for setqflist() Problem: No error when using wrong arguments for setqflist() or setloclist(). Solution: Check for the error. --- src/quickfix.c | 8 ++++++++ src/testdir/test_quickfix.vim | 3 +++ src/version.c | 2 ++ 3 files changed, 13 insertions(+) diff --git a/src/quickfix.c b/src/quickfix.c index 3bd0f75e24..091246b764 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -7400,6 +7400,14 @@ set_errorlist( return OK; } + // A dict argument cannot be specified with a non-empty list argument + if (list != NULL && list->lv_len != 0 && what != NULL) + { + semsg(_(e_invarg2), + _("cannot have both a list and a \"what\" argument")); + return FAIL; + } + incr_quickfix_busy(); if (what != NULL) diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim index 608a9553b8..6d119fe3e3 100644 --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -2360,6 +2360,9 @@ func Xproperty_tests(cchar) call assert_equal(['Colors'], newl2.context) call assert_equal('Line10', newl2.items[0].text) call g:Xsetlist([], 'f') + + " Cannot specify both a non-empty list argument and a dict argument + call assert_fails("call g:Xsetlist([{}], ' ', {})", 'E475:') endfunc func Test_qf_property() diff --git a/src/version.c b/src/version.c index 9da21730d2..f1b0be6f69 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1101, /**/ 1100, /**/ From 90049492215aa458b90215b8e0fc50f04d5ad270 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 1 Jul 2020 13:04:05 +0200 Subject: [PATCH 058/105] patch 8.2.1102: Coverity gets confused by an unnecessary NULL check Problem: Coverity gets confused by an unnecessary NULL check. Solution: Remove the check for NULL. --- src/quickfix.c | 14 ++++++++------ src/version.c | 2 ++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/quickfix.c b/src/quickfix.c index 091246b764..a7d211822a 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -7374,6 +7374,7 @@ qf_free_stack(win_T *wp, qf_info_T *qi) * Populate the quickfix list with the items supplied in the list * of dictionaries. "title" will be copied to w:quickfix_title. * "action" is 'a' for add, 'r' for replace. Otherwise create a new list. + * When "what" is not NULL then only set some properties. */ int set_errorlist( @@ -7401,7 +7402,7 @@ set_errorlist( } // A dict argument cannot be specified with a non-empty list argument - if (list != NULL && list->lv_len != 0 && what != NULL) + if (list->lv_len != 0 && what != NULL) { semsg(_(e_invarg2), _("cannot have both a list and a \"what\" argument")); @@ -8106,7 +8107,7 @@ set_qf_ll_list( else { list_T *l = list_arg->vval.v_list; - dict_T *d = NULL; + dict_T *what = NULL; int valid_dict = TRUE; if (action_arg->v_type == VAR_STRING) @@ -8128,8 +8129,8 @@ set_qf_ll_list( if (action_arg->v_type != VAR_UNKNOWN && what_arg->v_type != VAR_UNKNOWN) { - if (what_arg->v_type == VAR_DICT) - d = what_arg->vval.v_dict; + if (what_arg->v_type == VAR_DICT && what_arg->vval.v_dict != NULL) + what = what_arg->vval.v_dict; else { emsg(_(e_dictreq)); @@ -8138,9 +8139,10 @@ set_qf_ll_list( } ++recursive; - if (l != NULL && action && valid_dict && set_errorlist(wp, l, action, + if (l != NULL && action && valid_dict + && set_errorlist(wp, l, action, (char_u *)(wp == NULL ? ":setqflist()" : ":setloclist()"), - d) == OK) + what) == OK) rettv->vval.v_number = 0; --recursive; } diff --git a/src/version.c b/src/version.c index f1b0be6f69..26eb42a405 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1102, /**/ 1101, /**/ From e707c882b23a53d2c1f0d1f7fc3a7be247aca614 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 1 Jul 2020 13:07:37 +0200 Subject: [PATCH 059/105] patch 8.2.1103: Coverity reports an unnecessary NULL check Problem: Coverity reports an unnecessary NULL check. Solution: Remove the check for NULL. --- src/eval.c | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/eval.c b/src/eval.c index e6843cdd05..5a0aef2c27 100644 --- a/src/eval.c +++ b/src/eval.c @@ -5246,7 +5246,7 @@ ex_echo(exarg_T *eap) int called_emsg_before = called_emsg; evalarg_T evalarg; - fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip); + fill_evalarg_from_eap(&evalarg, eap, eap->skip); if (eap->skip) ++emsg_skip; diff --git a/src/version.c b/src/version.c index 26eb42a405..53bb772a90 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1103, /**/ 1102, /**/ From 11b6600c88165c70d9ccbbdd4d9c96e8266e365f Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 1 Jul 2020 13:15:24 +0200 Subject: [PATCH 060/105] patch 8.2.1104: Coverity warnts for possible NULL pointer use Problem: Coverity warnts for possible NULL pointer use. Solution: Check "pbyts" is not NULL. --- src/spellsuggest.c | 3 ++- src/version.c | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/spellsuggest.c b/src/spellsuggest.c index 6f9a756984..0821dc62cb 100644 --- a/src/spellsuggest.c +++ b/src/spellsuggest.c @@ -1406,7 +1406,8 @@ suggest_trie_walk( tword[sp->ts_twordlen] = NUL; if (sp->ts_prefixdepth <= PFD_NOTSPECIAL - && (sp->ts_flags & TSF_PREFIXOK) == 0) + && (sp->ts_flags & TSF_PREFIXOK) == 0 + && pbyts != NULL) { // There was a prefix before the word. Check that the prefix // can be used with this word. diff --git a/src/version.c b/src/version.c index 53bb772a90..4c3ab9639c 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1104, /**/ 1103, /**/ From e49b8e8d7589e85e75aedefab7ce97da47adbf74 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 1 Jul 2020 13:52:55 +0200 Subject: [PATCH 061/105] patch 8.2.1105: insufficient test coverage for Lua Problem: Insufficient test coverage for Lua. Solution: Add tests. (Yegappan Lakshmanan, closes #6368) Fix uncovered memory leak. Avoid unnecessary copy/free. --- src/if_lua.c | 29 +++---- src/testdir/test_lua.vim | 158 +++++++++++++++++++++++++++++++++++++-- src/version.c | 2 + 3 files changed, 171 insertions(+), 18 deletions(-) diff --git a/src/if_lua.c b/src/if_lua.c index c5fc683212..20cdbbcebc 100644 --- a/src/if_lua.c +++ b/src/if_lua.c @@ -936,8 +936,7 @@ luaV_list_newindex(lua_State *L) typval_T v; luaV_checktypval(L, 3, &v, "setting list item"); clear_tv(&li->li_tv); - copy_tv(&v, &li->li_tv); - clear_tv(&v); + li->li_tv = v; } } return 0; @@ -1084,7 +1083,7 @@ luaV_dict_newindex(lua_State *L) dict_T *d = luaV_unbox(L, luaV_Dict, 1); char_u *key = (char_u *) luaL_checkstring(L, 2); dictitem_T *di; - typval_T v; + typval_T tv; if (d->dv_lock) luaL_error(L, "dict is locked"); @@ -1094,9 +1093,12 @@ luaV_dict_newindex(lua_State *L) luaL_error(L, "empty key"); if (!lua_isnil(L, 3)) // read value? { - luaV_checktypval(L, 3, &v, "setting dict item"); - if (d->dv_scope == VAR_DEF_SCOPE && v.v_type == VAR_FUNC) + luaV_checktypval(L, 3, &tv, "setting dict item"); + if (d->dv_scope == VAR_DEF_SCOPE && tv.v_type == VAR_FUNC) + { + clear_tv(&tv); luaL_error(L, "cannot assign funcref to builtin scope"); + } } di = dict_find(d, key, -1); if (di == NULL) // non-existing key? @@ -1105,10 +1107,14 @@ luaV_dict_newindex(lua_State *L) return 0; di = dictitem_alloc(key); if (di == NULL) + { + clear_tv(&tv); return 0; + } if (dict_add(d, di) == FAIL) { vim_free(di); + clear_tv(&tv); return 0; } } @@ -1121,10 +1127,7 @@ luaV_dict_newindex(lua_State *L) dictitem_free(di); } else - { - copy_tv(&v, &di->di_tv); - clear_tv(&v); - } + di->di_tv = tv; return 0; } @@ -1441,7 +1444,8 @@ luaV_buffer_newindex(lua_State *L) curwin->w_cursor.lnum -= 1; check_cursor_col(); } - else check_cursor(); + else + check_cursor(); changed_cline_bef_curs(); } invalidate_botline(); @@ -1842,8 +1846,7 @@ luaV_dict(lua_State *L) lua_pushnil(L); return 1; } - copy_tv(&v, &di->di_tv); - clear_tv(&v); + di->di_tv = v; lua_pop(L, 2); // key copy and value } } @@ -2398,7 +2401,7 @@ ex_luado(exarg_T *eap) lua_replace(L, -2); // function -> body for (l = eap->line1; l <= eap->line2; l++) { - // Check the line number, the command my have deleted lines. + // Check the line number, the command may have deleted lines. if (l > curbuf->b_ml.ml_line_count) break; diff --git a/src/testdir/test_lua.vim b/src/testdir/test_lua.vim index e1ad5004be..38ff87205c 100644 --- a/src/testdir/test_lua.vim +++ b/src/testdir/test_lua.vim @@ -4,6 +4,15 @@ source check.vim CheckFeature lua CheckFeature float +let s:luaver = split(split(luaeval('_VERSION'), ' ')[1], '\.') +let s:major = str2nr(s:luaver[0]) +let s:minor = str2nr(s:luaver[1]) +if s:major < 5 || (s:major == 5 && s:minor < 3) + let s:lua_53_or_later = 0 +else + let s:lua_53_or_later = 1 +endif + func TearDown() " Run garbage collection after each test to exercise luaV_setref(). call test_garbagecollect_now() @@ -28,6 +37,23 @@ func Test_lua_command() bwipe! endfunc +func Test_lua_luado() + new + call setline(1, ['one', 'two']) + luado return(linenr) + call assert_equal(['1', '2'], getline(1, '$')) + close! + + " Error cases + call assert_fails('luado string.format()', + \ "[string \"vim chunk\"]:1: bad argument #1 to 'format' (string expected, got no value)") + call assert_fails('luado func()', + \ s:lua_53_or_later + \ ? "[string \"vim chunk\"]:1: attempt to call a nil value (global 'func')" + \ : "[string \"vim chunk\"]:1: attempt to call global 'func' (a nil value)") + call assert_fails('luado error("failed")', "[string \"vim chunk\"]:1: failed") +endfunc + " Test vim.eval() func Test_lua_eval() " lua.eval with a number @@ -55,6 +81,24 @@ func Test_lua_eval() call assert_equal('blob', luaeval('vim.type(v)')) call assert_equal(0z00112233.deadbeef, luaeval('v')) + " lua.eval with a float + lua v = vim.eval('3.14') + call assert_equal('number', luaeval('vim.type(v)')) + call assert_equal(3.14, luaeval('v')) + + " lua.eval with a bool + lua v = vim.eval('v:true') + call assert_equal('number', luaeval('vim.type(v)')) + call assert_equal(1, luaeval('v')) + lua v = vim.eval('v:false') + call assert_equal('number', luaeval('vim.type(v)')) + call assert_equal(0, luaeval('v')) + + " lua.eval with a null + lua v = vim.eval('v:null') + call assert_equal('nil', luaeval('vim.type(v)')) + call assert_equal(v:null, luaeval('v')) + call assert_fails('lua v = vim.eval(nil)', \ "[string \"vim chunk\"]:1: bad argument #1 to 'eval' (string expected, got nil)") call assert_fails('lua v = vim.eval(true)', @@ -82,6 +126,13 @@ func Test_lua_window() " Window 3 does not exist so vim.window(3) should return nil call assert_equal('nil', luaeval('tostring(vim.window(3))')) + call assert_fails("let n = luaeval('vim.window().xyz()')", + \ s:lua_53_or_later + \ ? "[string \"luaeval\"]:1: attempt to call a nil value (field 'xyz')" + \ : "[string \"luaeval\"]:1: attempt to call field 'xyz' (a nil value)") + call assert_fails('lua vim.window().xyz = 1', + \ "[string \"vim chunk\"]:1: invalid window property: `xyz'") + %bwipe! endfunc @@ -125,6 +176,15 @@ endfunc func Test_lua_call() call assert_equal(has('lua'), luaeval('vim.call("has", "lua")')) call assert_equal(printf("Hello %s", "vim"), luaeval('vim.call("printf", "Hello %s", "vim")')) + + " Error cases + call assert_fails("call luaeval('vim.call(\"min\", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21)')", + \ '[string "luaeval"]:1: Function called with too many arguments') + lua co = coroutine.create(function () print("hi") end) + call assert_fails("call luaeval('vim.call(\"type\", co)')", + \ '[string "luaeval"]:1: lua: cannot convert value') + lua co = nil + call assert_fails("call luaeval('vim.call(\"abc\")')", '[string "luaeval"]:1: lua: call_vim_function failed') endfunc " Test vim.fn.* @@ -245,6 +305,15 @@ func Test_lua_buffer_insert() lua vim.buffer():insert('4', 10) call assert_equal(['1', '2', '3', '4'], getline(1, '$')) + call assert_equal('4', luaeval('vim.buffer()[4]')) + call assert_equal(v:null, luaeval('vim.buffer()[5]')) + call assert_equal(v:null, luaeval('vim.buffer()[{}]')) + call assert_fails('lua vim.buffer():xyz()', + \ s:lua_53_or_later + \ ? "[string \"vim chunk\"]:1: attempt to call a nil value (method 'xyz')" + \ : "[string \"vim chunk\"]:1: attempt to call method 'xyz' (a nil value)") + call assert_fails('lua vim.buffer()[1] = {}', + \ '[string "vim chunk"]:1: wrong argument to change') bwipe! endfunc @@ -252,6 +321,7 @@ endfunc func Test_lua_buffer_delete() new call setline(1, ['1', '2', '3']) + call cursor(3, 1) lua vim.buffer()[2] = nil call assert_equal(['1', '3'], getline(1, '$')) @@ -331,10 +401,26 @@ func Test_lua_list() lua l[6] = nil lua l:insert('first') lua l:insert('xx', 3) + call assert_fails('lua l:insert("xx", -20)', + \ '[string "vim chunk"]:1: invalid position') call assert_equal(['first', 124, 'abc', 'xx', v:true, v:false, v:null, {'a': 1, 'b': 2, 'c': 3}], l) lockvar 1 l call assert_fails('lua l:add("x")', '[string "vim chunk"]:1: list is locked') + call assert_fails('lua l:insert(2)', '[string "vim chunk"]:1: list is locked') + call assert_fails('lua l[9] = 1', '[string "vim chunk"]:1: list is locked') + + unlockvar l + let l = [1, 2] + lua ll = vim.eval('l') + let x = luaeval("ll[3]") + call assert_equal(v:null, x) + call assert_fails('let x = luaeval("ll:xyz(3)")', + \ s:lua_53_or_later + \ ? "[string \"luaeval\"]:1: attempt to call a nil value (method 'xyz')" + \ : "[string \"luaeval\"]:1: attempt to call method 'xyz' (a nil value)") + let y = luaeval("ll[{}]") + call assert_equal(v:null, y) lua l = nil endfunc @@ -354,11 +440,7 @@ func Test_lua_list_table() endfunc func Test_lua_list_table_insert_remove() - let luaver = split(split(luaeval('_VERSION'), ' ')[1], '\.') - let major = str2nr(luaver[0]) - let minor = str2nr(luaver[1]) - - if major < 5 || (major == 5 && minor < 3) + if !s:lua_53_or_later throw 'Skipped: Lua version < 5.3' endif @@ -429,6 +511,7 @@ func Test_lua_dict() call assert_match('^dict: \%(0x\)\?\x\+$', luaeval('tostring(d)')) call assert_equal('abc', luaeval('d[1]')) + call assert_equal(v:null, luaeval('d[22]')) lua d[0] = 124 lua d[4] = nil @@ -436,6 +519,23 @@ func Test_lua_dict() lockvar 1 d call assert_fails('lua d[6] = 1', '[string "vim chunk"]:1: dict is locked') + unlockvar d + + " Error case + lua d = {} + lua d[''] = 10 + call assert_fails("let t = luaeval('vim.dict(d)')", + \ '[string "luaeval"]:1: table has empty key') + let d = {} + lua x = vim.eval('d') + call assert_fails("lua x[''] = 10", '[string "vim chunk"]:1: empty key') + lua x['a'] = nil + call assert_equal({}, d) + + " cannot assign funcrefs in the global scope + lua x = vim.eval('g:') + call assert_fails("lua x['min'] = vim.funcref('max')", + \ '[string "vim chunk"]:1: cannot assign funcref to builtin scope') lua d = nil endfunc @@ -493,6 +593,28 @@ func Test_lua_blob() call assert_fails('lua b:add(true)', '[string "vim chunk"]:1: string expected, got boolean') call assert_fails('lua b:add({})', '[string "vim chunk"]:1: string expected, got table') lua b = nil + + let b = 0z0102 + lua lb = vim.eval('b') + let n = luaeval('lb[1]') + call assert_equal(2, n) + let n = luaeval('lb[6]') + call assert_equal(v:null, n) + call assert_fails('let x = luaeval("lb:xyz(3)")', + \ s:lua_53_or_later + \ ? "[string \"luaeval\"]:1: attempt to call a nil value (method 'xyz')" + \ : "[string \"luaeval\"]:1: attempt to call method 'xyz' (a nil value)") + let y = luaeval("lb[{}]") + call assert_equal(v:null, y) + + lockvar b + call assert_fails('lua lb[1] = 2', '[string "vim chunk"]:1: blob is locked') + call assert_fails('lua lb:add("12")', '[string "vim chunk"]:1: blob is locked') + + " Error cases + lua t = {} + call assert_fails('lua b = vim.blob(t)', + \ '[string "vim chunk"]:1: string expected, got table') endfunc func Test_lua_funcref() @@ -506,6 +628,17 @@ func Test_lua_funcref() lua msg = vim.funcref"tr"(msg, "|", " ") call assert_equal("funcref test OK", luaeval('msg')) + " Error cases + call assert_fails('lua f1 = vim.funcref("")', + \ '[string "vim chunk"]:1: invalid function name: ') + call assert_fails('lua f1 = vim.funcref("10")', + \ '[string "vim chunk"]:1: invalid function name: 10') + let fname = test_null_string() + call assert_fails('lua f1 = vim.funcref(fname)', + \ "[string \"vim chunk\"]:1: bad argument #1 to 'funcref' (string expected, got nil)") + call assert_fails('lua vim.funcref("abc")()', + \ '[string "vim chunk"]:1: cannot call funcref') + " dict funcref function Mylen() dict return len(self.data) @@ -619,6 +752,10 @@ func Test_luaeval_error() \ '[string "luaeval"]:1: attempt to perform arithmetic on a nil value') call assert_fails("call luaeval(']')", \ "[string \"luaeval\"]:1: unexpected symbol near ']'") + lua co = coroutine.create(function () print("hi") end) + call assert_fails('let i = luaeval("co")', 'luaeval: cannot convert value') + lua co = nil + call assert_fails('let m = luaeval("{}")', 'luaeval: cannot convert value') endfunc " Test :luafile foo.lua @@ -664,6 +801,17 @@ func Test_luafile_error() bwipe! endfunc +" Test for dealing with strings containing newlines and null character +func Test_lua_string_with_newline() + let x = execute('lua print("Hello\nWorld")') + call assert_equal("\nHello\nWorld", x) + new + lua k = vim.buffer(vim.eval('bufnr()')) + lua k:insert("Hello\0World", 0) + call assert_equal(["Hello\nWorld", ''], getline(1, '$')) + close! +endfunc + func Test_lua_set_cursor() " Check that setting the cursor position works. new diff --git a/src/version.c b/src/version.c index 4c3ab9639c..a3dd0fb330 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1105, /**/ 1104, /**/ From 8e6cbb72324b6fb25d1a9abd6cc4d102d0e5f14e Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 1 Jul 2020 14:38:12 +0200 Subject: [PATCH 062/105] patch 8.2.1106: crash when trying to use s: variable in typed command Problem: Crash when trying to use s: variable in typed command. Solution: Don't use the script index when not set. (Ken Takata, closes #6366) --- src/testdir/test_vimscript.vim | 17 ++++++++++++++++- src/version.c | 2 ++ src/vim9compile.c | 3 +++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_vimscript.vim b/src/testdir/test_vimscript.vim index 2ca4e752e4..0a03445a08 100644 --- a/src/testdir/test_vimscript.vim +++ b/src/testdir/test_vimscript.vim @@ -2007,6 +2007,7 @@ func Test_float_conversion_errors() endif endfunc +" invalid function names {{{1 func Test_invalid_function_names() " function name not starting with capital let caught_e128 = 0 @@ -2067,7 +2068,7 @@ func Test_invalid_function_names() call delete('Xscript') endfunc -" substring and variable name +" substring and variable name {{{1 func Test_substring_var() let str = 'abcdef' let n = 3 @@ -2087,6 +2088,20 @@ func Test_substring_var() unlet b:nn endfunc +" Test using s: with a typed command {{{1 +func Test_typed_script_var() + CheckRunVimInTerminal + + let buf = RunVimInTerminal('', {'rows': 6}) + + " Deep nesting of if ... endif + call term_sendkeys(buf, ":echo get(s:, 'foo', 'x')\n") + call TermWait(buf) + call WaitForAssert({-> assert_match('^E116:', term_getline(buf, 5))}) + + call StopVimInTerminal(buf) +endfunc + "------------------------------------------------------------------------------- " Modelines {{{1 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index a3dd0fb330..8f08cc5cf9 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1106, /**/ 1105, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index d37c5c95d0..fdb422bf28 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2339,6 +2339,9 @@ find_imported(char_u *name, size_t len, cctx_T *cctx) scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); int idx; + if (current_sctx.sc_sid <= 0) + return NULL; + si = SCRIPT_ITEM(current_sctx.sc_sid); if (cctx != NULL) for (idx = 0; idx < cctx->ctx_imports.ga_len; ++idx) { From ef8c617b9c4dc262ed34598d7e382237696c3d61 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 1 Jul 2020 15:12:44 +0200 Subject: [PATCH 063/105] patch 8.2.1107: 'imactivatefunc' and 'imstatusfunc' are not used in the GUI Problem: 'imactivatefunc' and 'imstatusfunc' are not used in the GUI. Solution: Adjust the #ifdefs. (closes #6367) --- runtime/doc/options.txt | 4 ++-- src/gui_xim.c | 2 +- src/testdir/test_iminsert.vim | 7 ++----- src/version.c | 2 ++ 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 4e78a2fa68..bd6cc62d62 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -4133,7 +4133,7 @@ A jump table for the options with a short description can be found at |Q_op|. global This option specifies a function that will be called to activate or deactivate the Input Method. - It is not used in the GUI. + It is not used in the MS-Windows GUI version. The expression will be evaluated in the |sandbox| when set from a modeline, see |sandbox-option|. @@ -4242,7 +4242,7 @@ A jump table for the options with a short description can be found at |Q_op|. global This option specifies a function that is called to obtain the status of Input Method. It must return a positive number when IME is active. - It is not used in the GUI. + It is not used in the MS-Windows GUI version. Example: > function ImStatusFunc() diff --git a/src/gui_xim.c b/src/gui_xim.c index b5875ee2cd..ee92301337 100644 --- a/src/gui_xim.c +++ b/src/gui_xim.c @@ -57,7 +57,7 @@ xim_log(char *s, ...) } #endif -#ifdef FEAT_GUI +#if defined(FEAT_GUI_MSWIN) # define USE_IMACTIVATEFUNC (!gui.in_use && *p_imaf != NUL) # define USE_IMSTATUSFUNC (!gui.in_use && *p_imsf != NUL) #else diff --git a/src/testdir/test_iminsert.vim b/src/testdir/test_iminsert.vim index 04c8a15995..1f19412789 100644 --- a/src/testdir/test_iminsert.vim +++ b/src/testdir/test_iminsert.vim @@ -27,7 +27,7 @@ func Test_iminsert2() set imactivatefunc= set imstatusfunc= - let expected = has('gui_running') ? 0 : 1 + let expected = has('gui_win32') ? 0 : 1 call assert_equal(expected, s:imactivatefunc_called) call assert_equal(expected, s:imstatusfunc_called) endfunc @@ -38,10 +38,7 @@ func Test_getimstatus() elseif !has('gui_mac') CheckFeature xim endif - if has('gui_running') - if !has('win32') - throw 'Skipped: running in the GUI, only works on MS-Windows' - endif + if has('gui_win32') set imactivatefunc= set imstatusfunc= else diff --git a/src/version.c b/src/version.c index 8f08cc5cf9..97c3c4ca62 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1107, /**/ 1106, /**/ From d58d4f90aeb381045000ea46493b5bd9b9d1fa23 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 1 Jul 2020 15:49:29 +0200 Subject: [PATCH 064/105] patch 8.2.1108: mouse left-right scroll is not supported in terminal window Problem: Mouse left-right scroll is not supported in terminal window. Solution: Implement mouse codes 6 and 7. (Trygve Aaberge, closes #6363) --- src/libvterm/src/mouse.c | 4 +-- src/mouse.c | 57 ++++++++++++++++++++++++---------- src/terminal.c | 6 ++-- src/testdir/mouse.vim | 16 ++++++++++ src/testdir/test_termcodes.vim | 26 ++++++++++++++-- src/version.c | 2 ++ 6 files changed, 89 insertions(+), 22 deletions(-) diff --git a/src/libvterm/src/mouse.c b/src/libvterm/src/mouse.c index 4e363134eb..ed60b0147d 100644 --- a/src/libvterm/src/mouse.c +++ b/src/libvterm/src/mouse.c @@ -83,7 +83,7 @@ void vterm_mouse_button(VTerm *vt, int button, int pressed, VTermModifier mod) state->mouse_buttons &= ~(1 << (button-1)); } - /* Most of the time we don't get button releases from 4/5 */ + /* Most of the time we don't get button releases from 4/5/6/7 */ if(state->mouse_buttons == old_buttons && button < 4) return; if (!(state->mouse_flags & MOUSE_WANT_CLICK)) @@ -92,7 +92,7 @@ void vterm_mouse_button(VTerm *vt, int button, int pressed, VTermModifier mod) if(button < 4) { output_mouse(state, button-1, pressed, mod, state->mouse_col, state->mouse_row); } - else if(button < 6) { + else if(button < 8) { output_mouse(state, button-4 + 0x40, pressed, mod, state->mouse_col, state->mouse_row); } } diff --git a/src/mouse.c b/src/mouse.c index 5abb94221c..143bcf2f67 100644 --- a/src/mouse.c +++ b/src/mouse.c @@ -2119,6 +2119,7 @@ check_termcode_mouse( # endif int mouse_code = 0; // init for GCC int is_click, is_drag; + int is_release, release_is_ambiguous; int wheel_code = 0; int current_button; static int held_button = MOUSE_RELEASE; @@ -2133,7 +2134,7 @@ check_termcode_mouse( long timediff; // elapsed time in msec # endif - is_click = is_drag = FALSE; + is_click = is_drag = is_release = release_is_ambiguous = FALSE; # if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \ || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE) @@ -2256,9 +2257,6 @@ check_termcode_mouse( || key_name[0] == KS_SGR_MOUSE_RELEASE) mouse_code += 32; - if (key_name[0] == KS_SGR_MOUSE_RELEASE) - mouse_code |= MOUSE_RELEASE; - mouse_col = getdigits(&p) - 1; if (*p++ != ';') return -1; @@ -2270,6 +2268,19 @@ check_termcode_mouse( *modifiers = 0; } + if (key_name[0] == KS_SGR_MOUSE + || key_name[0] == KS_SGR_MOUSE_RELEASE) + { + if (key_name[0] == KS_SGR_MOUSE_RELEASE) + is_release = TRUE; + } + else + { + release_is_ambiguous = TRUE; + if ((mouse_code & MOUSE_RELEASE) == MOUSE_RELEASE) + is_release = TRUE; + } + if (key_name[0] == KS_MOUSE # ifdef FEAT_MOUSE_GPM || key_name[0] == KS_GPM_MOUSE @@ -2331,7 +2342,7 @@ check_termcode_mouse( # ifdef FEAT_XCLIPBOARD else if (!(mouse_code & MOUSE_DRAG & ~MOUSE_CLICK_MASK)) { - if ((mouse_code & MOUSE_RELEASE) == MOUSE_RELEASE) + if (is_release) stop_xterm_trace(); else start_xterm_trace(mouse_code); @@ -2469,12 +2480,13 @@ check_termcode_mouse( if (button & 16) mouse_code |= MOUSE_CTRL; break; case 'u': // Button Up + is_release = TRUE; if (button & 1) - mouse_code |= MOUSE_LEFT | MOUSE_RELEASE; + mouse_code |= MOUSE_LEFT; if (button & 2) - mouse_code |= MOUSE_MIDDLE | MOUSE_RELEASE; + mouse_code |= MOUSE_MIDDLE; if (button & 4) - mouse_code |= MOUSE_RIGHT | MOUSE_RELEASE; + mouse_code |= MOUSE_RIGHT; if (button & 8) mouse_code |= MOUSE_SHIFT; if (button & 16) @@ -2598,17 +2610,20 @@ check_termcode_mouse( case 2: mouse_code = MOUSE_LEFT; WantQueryMouse = TRUE; break; - case 3: mouse_code = MOUSE_RELEASE | MOUSE_LEFT; + case 3: mouse_code = MOUSE_LEFT; + is_release = TRUE; break; case 4: mouse_code = MOUSE_MIDDLE; WantQueryMouse = TRUE; break; - case 5: mouse_code = MOUSE_RELEASE | MOUSE_MIDDLE; + case 5: mouse_code = MOUSE_MIDDLE; + is_release = TRUE; break; case 6: mouse_code = MOUSE_RIGHT; WantQueryMouse = TRUE; break; - case 7: mouse_code = MOUSE_RELEASE | MOUSE_RIGHT; + case 7: mouse_code = MOUSE_RIGHT; + is_release = TRUE; break; case 8: return -1; // fourth button down case 9: return -1; // fourth button up @@ -2661,7 +2676,7 @@ check_termcode_mouse( break; case 32: // Release - mouse_code |= MOUSE_RELEASE; + is_release = TRUE; break; case 33: // Drag @@ -2682,6 +2697,9 @@ check_termcode_mouse( // Interpret the mouse code current_button = (mouse_code & MOUSE_CLICK_MASK); + if (is_release) + current_button |= MOUSE_RELEASE; + if (current_button == MOUSE_RELEASE # ifdef FEAT_MOUSE_XTERM && wheel_code == 0 @@ -2786,15 +2804,22 @@ check_termcode_mouse( // Work out our pseudo mouse event. Note that MOUSE_RELEASE gets // added, then it's not mouse up/down. key_name[0] = KS_EXTRA; - if (wheel_code != 0 - && (wheel_code & MOUSE_RELEASE) != MOUSE_RELEASE) + if (wheel_code != 0 && (!is_release || release_is_ambiguous)) { if (wheel_code & MOUSE_CTRL) *modifiers |= MOD_MASK_CTRL; if (wheel_code & MOUSE_ALT) *modifiers |= MOD_MASK_ALT; - key_name[1] = (wheel_code & 1) - ? (int)KE_MOUSEUP : (int)KE_MOUSEDOWN; + + if (wheel_code & 1 && wheel_code & 2) + key_name[1] = (int)KE_MOUSELEFT; + else if (wheel_code & 2) + key_name[1] = (int)KE_MOUSERIGHT; + else if (wheel_code & 1) + key_name[1] = (int)KE_MOUSEUP; + else + key_name[1] = (int)KE_MOUSEDOWN; + held_button = MOUSE_RELEASE; } else diff --git a/src/terminal.c b/src/terminal.c index 5424574868..bd5fd41ee7 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -1389,8 +1389,8 @@ term_convert_key(term_T *term, int c, int modmask, char *buf) case K_MOUSEUP: other = term_send_mouse(vterm, 5, 1); break; case K_MOUSEDOWN: other = term_send_mouse(vterm, 4, 1); break; - case K_MOUSELEFT: /* TODO */ return 0; - case K_MOUSERIGHT: /* TODO */ return 0; + case K_MOUSELEFT: other = term_send_mouse(vterm, 7, 1); break; + case K_MOUSERIGHT: other = term_send_mouse(vterm, 6, 1); break; case K_LEFTMOUSE: case K_LEFTMOUSE_NM: @@ -2474,6 +2474,8 @@ terminal_loop(int blocking) restore_cursor = TRUE; raw_c = term_vgetc(); +if (raw_c > 0) + ch_log(NULL, "terminal_loop() got %d", raw_c); if (!term_use_loop_check(TRUE) || in_terminal_loop != curbuf->b_term) { // Job finished while waiting for a character. Push back the diff --git a/src/testdir/mouse.vim b/src/testdir/mouse.vim index a28a439c19..5eab1de48f 100644 --- a/src/testdir/mouse.vim +++ b/src/testdir/mouse.vim @@ -169,4 +169,20 @@ func MouseWheelDown(row, col) call feedkeys(MouseWheelDownCode(a:row, a:col), 'Lx!') endfunc +func MouseWheelLeftCode(row, col) + return TerminalEscapeCode(0x42, a:row, a:col, 'M') +endfunc + +func MouseWheelLeft(row, col) + call feedkeys(MouseWheelLeftCode(a:row, a:col), 'Lx!') +endfunc + +func MouseWheelRightCode(row, col) + return TerminalEscapeCode(0x43, a:row, a:col, 'M') +endfunc + +func MouseWheelRight(row, col) + call feedkeys(MouseWheelRightCode(a:row, a:col), 'Lx!') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_termcodes.vim b/src/testdir/test_termcodes.vim index 27c2a18cf8..a267696cc1 100644 --- a/src/testdir/test_termcodes.vim +++ b/src/testdir/test_termcodes.vim @@ -194,9 +194,10 @@ func Test_1xterm_mouse_wheel() new let save_mouse = &mouse let save_term = &term + let save_wrap = &wrap let save_ttymouse = &ttymouse - set mouse=a term=xterm - call setline(1, range(1, 100)) + set mouse=a term=xterm nowrap + call setline(1, range(100000000000000, 100000000000100)) for ttymouse_val in g:Ttymouse_values let msg = 'ttymouse=' .. ttymouse_val @@ -220,10 +221,31 @@ func Test_1xterm_mouse_wheel() call MouseWheelUp(1, 1) call assert_equal(1, line('w0'), msg) call assert_equal([0, 7, 1, 0], getpos('.'), msg) + + if has('gui') + " Horizontal wheel scrolling currently only works when vim is + " compiled with gui enabled. + call MouseWheelRight(1, 1) + call assert_equal(7, 1 + virtcol(".") - wincol(), msg) + call assert_equal([0, 7, 7, 0], getpos('.'), msg) + + call MouseWheelRight(1, 1) + call assert_equal(13, 1 + virtcol(".") - wincol(), msg) + call assert_equal([0, 7, 13, 0], getpos('.'), msg) + + call MouseWheelLeft(1, 1) + call assert_equal(7, 1 + virtcol(".") - wincol(), msg) + call assert_equal([0, 7, 13, 0], getpos('.'), msg) + + call MouseWheelLeft(1, 1) + call assert_equal(1, 1 + virtcol(".") - wincol(), msg) + call assert_equal([0, 7, 13, 0], getpos('.'), msg) + endif endfor let &mouse = save_mouse let &term = save_term + let &wrap = save_wrap let &ttymouse = save_ttymouse bwipe! endfunc diff --git a/src/version.c b/src/version.c index 97c3c4ca62..2691996ad8 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1108, /**/ 1107, /**/ From 086eb18ba16164ca5258068cff9c4b2db742f2ef Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 1 Jul 2020 16:00:44 +0200 Subject: [PATCH 065/105] patch 8.2.1109: still crashing when using s:variable Problem: Still crashing when using s:variable. Solution: Remove assignment. (Ken Takata) --- src/version.c | 2 ++ src/vim9compile.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/version.c b/src/version.c index 2691996ad8..128fe6c404 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1109, /**/ 1108, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index fdb422bf28..b3ed77d75d 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2336,7 +2336,7 @@ get_script_item_idx(int sid, char_u *name, int check_writable) imported_T * find_imported(char_u *name, size_t len, cctx_T *cctx) { - scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); + scriptitem_T *si; int idx; if (current_sctx.sc_sid <= 0) From e6b5324e3a3d354363f3c48e784c42ce3e77453f Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 1 Jul 2020 17:28:33 +0200 Subject: [PATCH 066/105] patch 8.2.1110: Vim9: line continuation does not work in function arguments Problem: Vim9: line continuation does not work in function arguments. Solution: Pass "evalarg" to get_func_tv(). Fix seeing double quoted string as comment. --- src/dict.c | 6 ++--- src/eval.c | 40 ++++++++++++++++++++++++++-------- src/ex_eval.c | 8 +------ src/list.c | 9 ++++---- src/proto/eval.pro | 2 ++ src/proto/userfunc.pro | 2 +- src/testdir/test_vim9_expr.vim | 7 ++++++ src/testdir/test_vim9_func.vim | 16 ++++++++++++++ src/userfunc.c | 20 ++++++++++------- src/version.c | 2 ++ 10 files changed, 80 insertions(+), 32 deletions(-) diff --git a/src/dict.c b/src/dict.c index 079eb3e11e..53824f75d6 100644 --- a/src/dict.c +++ b/src/dict.c @@ -830,7 +830,7 @@ eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal) tvkey.v_type = VAR_UNKNOWN; tv.v_type = VAR_UNKNOWN; - *arg = skipwhite_and_linebreak(*arg + 1, evalarg); + *arg = skipwhite_and_linebreak_keep_string(*arg + 1, evalarg); while (**arg != '}' && **arg != NUL) { if ((literal @@ -862,7 +862,7 @@ eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal) goto failret; } - *arg = skipwhite_and_linebreak(*arg + 1, evalarg); + *arg = skipwhite_and_linebreak_keep_string(*arg + 1, evalarg); if (eval1(arg, &tv, evalarg) == FAIL) // recursive! { if (evaluate) @@ -904,7 +904,7 @@ eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal) } // the "}" can be on the next line - *arg = skipwhite_and_linebreak(*arg, evalarg); + *arg = skipwhite_and_linebreak_keep_string(*arg, evalarg); if (**arg == '}') break; if (!had_comma) diff --git a/src/eval.c b/src/eval.c index 5a0aef2c27..ce255e2b96 100644 --- a/src/eval.c +++ b/src/eval.c @@ -153,7 +153,7 @@ eval_clear(void) } #endif - static void + void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, int skip) { CLEAR_FIELD(*evalarg); @@ -1804,6 +1804,7 @@ pattern_match(char_u *pat, char_u *text, int ic) static int eval_func( char_u **arg, // points to "(", will be advanced + evalarg_T *evalarg, char_u *name, int name_len, typval_T *rettv, @@ -1839,7 +1840,7 @@ eval_func( funcexe.evaluate = evaluate; funcexe.partial = partial; funcexe.basetv = basetv; - ret = get_func_tv(s, len, rettv, arg, &funcexe); + ret = get_func_tv(s, len, rettv, arg, evalarg, &funcexe); } vim_free(s); @@ -1917,6 +1918,9 @@ eval_next_line(evalarg_T *evalarg) return skipwhite(line); } +/* + * Call eval_next_non_blank() and get the next line if needed. + */ char_u * skipwhite_and_linebreak(char_u *arg, evalarg_T *evalarg) { @@ -1929,6 +1933,20 @@ skipwhite_and_linebreak(char_u *arg, evalarg_T *evalarg) return p; } +/* + * Call eval_next_non_blank() and get the next line if needed, but not when a + * double quote follows. Used inside an expression. + */ + char_u * +skipwhite_and_linebreak_keep_string(char_u *arg, evalarg_T *evalarg) +{ + char_u *p = skipwhite(arg); + + if (*p == '"') + return p; + return skipwhite_and_linebreak(arg, evalarg); +} + /* * After using "evalarg" filled from "eap" free the memory. */ @@ -2995,7 +3013,7 @@ eval7( else { if (**arg == '(') // recursive! - ret = eval_func(arg, s, len, rettv, flags, NULL); + ret = eval_func(arg, evalarg, s, len, rettv, flags, NULL); else if (flags & EVAL_CONSTANT) ret = FAIL; else if (evaluate) @@ -3106,6 +3124,7 @@ eval7_leader( static int call_func_rettv( char_u **arg, + evalarg_T *evalarg, typval_T *rettv, int evaluate, dict_T *selfdict, @@ -3142,7 +3161,7 @@ call_func_rettv( funcexe.partial = pt; funcexe.selfdict = selfdict; funcexe.basetv = basetv; - ret = get_func_tv(s, -1, rettv, arg, &funcexe); + ret = get_func_tv(s, -1, rettv, arg, evalarg, &funcexe); // Clear the funcref afterwards, so that deleting it while // evaluating the arguments is possible (see test55). @@ -3189,7 +3208,7 @@ eval_lambda( ret = FAIL; } else - ret = call_func_rettv(arg, rettv, evaluate, NULL, &base); + ret = call_func_rettv(arg, evalarg, rettv, evaluate, NULL, &base); // Clear the funcref afterwards, so that deleting it while // evaluating the arguments is possible (see test55). @@ -3208,7 +3227,7 @@ eval_lambda( eval_method( char_u **arg, typval_T *rettv, - int evaluate, + evalarg_T *evalarg, int verbose) // give error messages { char_u *name; @@ -3216,6 +3235,8 @@ eval_method( char_u *alias; typval_T base = *rettv; int ret; + int evaluate = evalarg != NULL + && (evalarg->eval_flags & EVAL_EVALUATE); // Skip over the ->. *arg += 2; @@ -3247,7 +3268,7 @@ eval_method( ret = FAIL; } else - ret = eval_func(arg, name, len, rettv, + ret = eval_func(arg, evalarg, name, len, rettv, evaluate ? EVAL_EVALUATE : 0, &base); } @@ -5035,7 +5056,8 @@ handle_subscript( { if (**arg == '(') { - ret = call_func_rettv(arg, rettv, evaluate, selfdict, NULL); + ret = call_func_rettv(arg, evalarg, rettv, evaluate, + selfdict, NULL); // Stop the expression evaluation when immediately aborting on // error, or when an interrupt occurred or an exception was thrown @@ -5058,7 +5080,7 @@ handle_subscript( ret = eval_lambda(arg, rettv, evalarg, verbose); else // expr->name() - ret = eval_method(arg, rettv, evaluate, verbose); + ret = eval_method(arg, rettv, evalarg, verbose); } } else // **arg == '[' || **arg == '.' diff --git a/src/ex_eval.c b/src/ex_eval.c index fea8c7fd6b..061bd1a649 100644 --- a/src/ex_eval.c +++ b/src/ex_eval.c @@ -897,13 +897,7 @@ ex_eval(exarg_T *eap) typval_T tv; evalarg_T evalarg; - CLEAR_FIELD(evalarg); - evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE; - if (getline_equal(eap->getline, eap->cookie, getsourceline)) - { - evalarg.eval_getline = eap->getline; - evalarg.eval_cookie = eap->cookie; - } + fill_evalarg_from_eap(&evalarg, eap, eap->skip); if (eval0(eap->arg, &tv, eap, &evalarg) == OK) clear_tv(&tv); diff --git a/src/list.c b/src/list.c index ffcffa9f0c..2bea55e02f 100644 --- a/src/list.c +++ b/src/list.c @@ -1177,7 +1177,7 @@ get_list_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int do_error) return FAIL; } - *arg = skipwhite_and_linebreak(*arg + 1, evalarg); + *arg = skipwhite_and_linebreak_keep_string(*arg + 1, evalarg); while (**arg != ']' && **arg != NUL) { if (eval1(arg, &tv, evalarg) == FAIL) // recursive! @@ -1207,8 +1207,9 @@ get_list_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int do_error) *arg = skipwhite(*arg + 1); } - // the "]" can be on the next line - *arg = skipwhite_and_linebreak(*arg, evalarg); + // The "]" can be on the next line. But a double quoted string may + // follow, not a comment. + *arg = skipwhite_and_linebreak_keep_string(*arg, evalarg); if (**arg == ']') break; @@ -2356,7 +2357,7 @@ f_insert(typval_T *argvars, typval_T *rettv) } if (l != NULL) { - list_insert_tv(l, &argvars[1], item); + (void)list_insert_tv(l, &argvars[1], item); copy_tv(&argvars[0], rettv); } } diff --git a/src/proto/eval.pro b/src/proto/eval.pro index 0e9ffeb808..910fdfd180 100644 --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -3,6 +3,7 @@ varnumber_T num_divide(varnumber_T n1, varnumber_T n2); varnumber_T num_modulus(varnumber_T n1, varnumber_T n2); void eval_init(void); void eval_clear(void); +void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, int skip); int eval_to_bool(char_u *arg, int *error, exarg_T *eap, int skip); int eval_expr_valid_arg(typval_T *tv); int eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv); @@ -31,6 +32,7 @@ int pattern_match(char_u *pat, char_u *text, int ic); char_u *eval_next_non_blank(char_u *arg, evalarg_T *evalarg, int *getnext); char_u *eval_next_line(evalarg_T *evalarg); char_u *skipwhite_and_linebreak(char_u *arg, evalarg_T *evalarg); +char_u *skipwhite_and_linebreak_keep_string(char_u *arg, evalarg_T *evalarg); void clear_evalarg(evalarg_T *evalarg, exarg_T *eap); int eval0(char_u *arg, typval_T *rettv, exarg_T *eap, evalarg_T *evalarg); int eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg); diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro index f69f77d6a4..75a1562570 100644 --- a/src/proto/userfunc.pro +++ b/src/proto/userfunc.pro @@ -7,7 +7,7 @@ char_u *register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state); int get_lambda_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg); char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload); void emsg_funcname(char *ermsg, char_u *name); -int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, funcexe_T *funcexe); +int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, evalarg_T *evalarg, funcexe_T *funcexe); char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error); ufunc_T *find_func(char_u *name, int is_global, cctx_T *cctx); int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, funcexe_T *funcexe, dict_T *selfdict); diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index bf4f7d2eaf..1f4b254980 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -1099,6 +1099,13 @@ def Test_expr7_dict_vim9script() END CheckScriptSuccess(lines) + lines =<< trim END + vim9script + let d = { "one": "one", "two": "two", } + assert_equal({'one': 'one', 'two': 'two'}, d) + END + CheckScriptSuccess(lines) + lines =<< trim END vim9script let d = #{one: 1, diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 1fc13c088d..6a03eddc1d 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -353,6 +353,22 @@ def Test_vim9script_call() assert_equal('text', var) ("some")->MyFunc() assert_equal('some', var) + + MyFunc( + 'continued' + ) + assert_equal('continued', + var + ) + + call MyFunc( + 'more' + .. + 'lines' + ) + assert_equal( + 'morelines', + var) END writefile(lines, 'Xcall.vim') source Xcall.vim diff --git a/src/userfunc.c b/src/userfunc.c index 724d1247d9..121079149a 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -601,16 +601,13 @@ get_func_tv( int len, // length of "name" or -1 to use strlen() typval_T *rettv, char_u **arg, // argument, pointing to the '(' + evalarg_T *evalarg, // for line continuation funcexe_T *funcexe) // various values { char_u *argp; int ret = OK; typval_T argvars[MAX_FUNC_ARGS + 1]; // vars for arguments int argcount = 0; // number of arguments found - evalarg_T evalarg; - - CLEAR_FIELD(evalarg); - evalarg.eval_flags = funcexe->evaluate ? EVAL_EVALUATE : 0; /* * Get the arguments. @@ -619,10 +616,12 @@ get_func_tv( while (argcount < MAX_FUNC_ARGS - (funcexe->partial == NULL ? 0 : funcexe->partial->pt_argc)) { - argp = skipwhite(argp + 1); // skip the '(' or ',' + // skip the '(' or ',' and possibly line breaks + argp = skipwhite_and_linebreak(argp + 1, evalarg); + if (*argp == ')' || *argp == ',' || *argp == NUL) break; - if (eval1(&argp, &argvars[argcount], &evalarg) == FAIL) + if (eval1(&argp, &argvars[argcount], evalarg) == FAIL) { ret = FAIL; break; @@ -631,6 +630,7 @@ get_func_tv( if (*argp != ',') break; } + argp = skipwhite_and_linebreak(argp, evalarg); if (*argp == ')') ++argp; else @@ -3832,16 +3832,19 @@ ex_call(exarg_T *eap) int failed = FALSE; funcdict_T fudi; partial_T *partial = NULL; + evalarg_T evalarg; + fill_evalarg_from_eap(&evalarg, eap, eap->skip); if (eap->skip) { // trans_function_name() doesn't work well when skipping, use eval0() // instead to skip to any following command, e.g. for: // :if 0 | call dict.foo().bar() | endif ++emsg_skip; - if (eval0(eap->arg, &rettv, eap, NULL) != FAIL) + if (eval0(eap->arg, &rettv, eap, &evalarg) != FAIL) clear_tv(&rettv); --emsg_skip; + clear_evalarg(&evalarg, eap); return; } @@ -3918,7 +3921,7 @@ ex_call(exarg_T *eap) funcexe.evaluate = !eap->skip; funcexe.partial = partial; funcexe.selfdict = fudi.fd_dict; - if (get_func_tv(name, -1, &rettv, &arg, &funcexe) == FAIL) + if (get_func_tv(name, -1, &rettv, &arg, &evalarg, &funcexe) == FAIL) { failed = TRUE; break; @@ -3947,6 +3950,7 @@ ex_call(exarg_T *eap) } if (eap->skip) --emsg_skip; + clear_evalarg(&evalarg, eap); // When inside :try we need to check for following "| catch". if (!failed || eap->cstack->cs_trylevel > 0) diff --git a/src/version.c b/src/version.c index 128fe6c404..e7dc813ce2 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1110, /**/ 1109, /**/ From 9a78e6df17033223ebdf499f2b02b2538601c52d Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 1 Jul 2020 18:29:55 +0200 Subject: [PATCH 067/105] patch 8.2.1111: inconsistent naming of get_list_tv() and eval_dict() Problem: Inconsistent naming of get_list_tv() and eval_dict(). Solution: Rename get_list_tv() to eval_list(). Similarly for eval_number(), eval_string(), eval_lit_string() and a few others. --- src/eval.c | 21 ++++++++++++--------- src/evalfunc.c | 2 +- src/evalvars.c | 10 +++++----- src/list.c | 2 +- src/proto/evalvars.pro | 2 +- src/proto/list.pro | 2 +- src/proto/typval.pro | 26 +++++++++++++------------- src/typval.c | 12 ++++++------ src/version.c | 2 ++ src/vim9compile.c | 18 +++++++++--------- src/vim9execute.c | 4 ++-- src/vim9script.c | 4 ++-- 12 files changed, 55 insertions(+), 50 deletions(-) diff --git a/src/eval.c b/src/eval.c index ce255e2b96..9a2fdf6540 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1224,7 +1224,7 @@ set_var_lval( // handle +=, -=, *=, /=, %= and .= di = NULL; - if (get_var_tv(lp->ll_name, (int)STRLEN(lp->ll_name), + if (eval_variable(lp->ll_name, (int)STRLEN(lp->ll_name), &tv, &di, TRUE, FALSE) == OK) { if ((di == NULL @@ -2901,7 +2901,7 @@ eval7( case '7': case '8': case '9': - case '.': ret = get_number_tv(arg, rettv, evaluate, want_string); + case '.': ret = eval_number(arg, rettv, evaluate, want_string); // Apply prefixed "-" and "+" now. Matters especially when // "->" follows. @@ -2912,19 +2912,19 @@ eval7( /* * String constant: "string". */ - case '"': ret = get_string_tv(arg, rettv, evaluate); + case '"': ret = eval_string(arg, rettv, evaluate); break; /* * Literal string constant: 'str''ing'. */ - case '\'': ret = get_lit_string_tv(arg, rettv, evaluate); + case '\'': ret = eval_lit_string(arg, rettv, evaluate); break; /* * List: [expr, expr] */ - case '[': ret = get_list_tv(arg, rettv, evalarg, TRUE); + case '[': ret = eval_list(arg, rettv, evalarg, TRUE); break; /* @@ -2951,13 +2951,13 @@ eval7( /* * Option value: &name */ - case '&': ret = get_option_tv(arg, rettv, evaluate); + case '&': ret = eval_option(arg, rettv, evaluate); break; /* * Environment variable: $VAR. */ - case '$': ret = get_env_tv(arg, rettv, evaluate); + case '$': ret = eval_env_var(arg, rettv, evaluate); break; /* @@ -3012,14 +3012,17 @@ eval7( ret = FAIL; else { - if (**arg == '(') // recursive! + if (**arg == '(') + // "name(..." recursive! ret = eval_func(arg, evalarg, s, len, rettv, flags, NULL); else if (flags & EVAL_CONSTANT) ret = FAIL; else if (evaluate) - ret = get_var_tv(s, len, rettv, NULL, TRUE, FALSE); + // get value of variable + ret = eval_variable(s, len, rettv, NULL, TRUE, FALSE); else { + // skip the name check_vars(s, len); ret = OK; } diff --git a/src/evalfunc.c b/src/evalfunc.c index b8f1c2c38e..8d81f15123 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -2324,7 +2324,7 @@ f_exists(typval_T *argvars, typval_T *rettv) } else if (*p == '&' || *p == '+') // option { - n = (get_option_tv(&p, NULL, TRUE) == OK); + n = (eval_option(&p, NULL, TRUE) == OK); if (*skipwhite(p) != NUL) n = FALSE; // trailing garbage } diff --git a/src/evalvars.c b/src/evalvars.c index 901d406a00..131486750c 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -1124,7 +1124,7 @@ list_arg_vars(exarg_T *eap, char_u *arg, int *first) { if (tofree != NULL) name = tofree; - if (get_var_tv(name, len, &tv, NULL, TRUE, FALSE) == FAIL) + if (eval_variable(name, len, &tv, NULL, TRUE, FALSE) == FAIL) error = TRUE; else { @@ -2365,7 +2365,7 @@ set_cmdarg(exarg_T *eap, char_u *oldarg) * Return OK or FAIL. If OK is returned "rettv" must be cleared. */ int -get_var_tv( +eval_variable( char_u *name, int len, // length of "name" typval_T *rettv, // NULL when only checking existence @@ -3206,7 +3206,7 @@ getwinvar( done = TRUE; } } - else if (get_option_tv(&varname, rettv, 1) == OK) + else if (eval_option(&varname, rettv, 1) == OK) // window-local-option done = TRUE; } @@ -3342,7 +3342,7 @@ var_exists(char_u *var) { if (tofree != NULL) name = tofree; - n = (get_var_tv(name, len, &tv, NULL, FALSE, TRUE) == OK); + n = (eval_variable(name, len, &tv, NULL, FALSE, TRUE) == OK); if (n) { // handle d.key, l[idx], f(expr) @@ -3609,7 +3609,7 @@ f_getbufvar(typval_T *argvars, typval_T *rettv) done = TRUE; } } - else if (get_option_tv(&varname, rettv, TRUE) == OK) + else if (eval_option(&varname, rettv, TRUE) == OK) // buffer-local-option done = TRUE; diff --git a/src/list.c b/src/list.c index 2bea55e02f..9475ef99e2 100644 --- a/src/list.c +++ b/src/list.c @@ -1160,7 +1160,7 @@ f_join(typval_T *argvars, typval_T *rettv) * Return OK or FAIL. */ int -get_list_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int do_error) +eval_list(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int do_error) { int evaluate = evalarg == NULL ? FALSE : evalarg->eval_flags & EVAL_EVALUATE; diff --git a/src/proto/evalvars.pro b/src/proto/evalvars.pro index eab82c8cfa..a1083100dc 100644 --- a/src/proto/evalvars.pro +++ b/src/proto/evalvars.pro @@ -52,7 +52,7 @@ void set_reg_var(int c); char_u *v_exception(char_u *oldval); char_u *v_throwpoint(char_u *oldval); char_u *set_cmdarg(exarg_T *eap, char_u *oldarg); -int get_var_tv(char_u *name, int len, typval_T *rettv, dictitem_T **dip, int verbose, int no_autoload); +int eval_variable(char_u *name, int len, typval_T *rettv, dictitem_T **dip, int verbose, int no_autoload); void check_vars(char_u *name, int len); dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload); dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, int no_autoload); diff --git a/src/proto/list.pro b/src/proto/list.pro index ddf26a5dd6..53502ae723 100644 --- a/src/proto/list.pro +++ b/src/proto/list.pro @@ -39,7 +39,7 @@ void vimlist_remove(list_T *l, listitem_T *item, listitem_T *item2); char_u *list2string(typval_T *tv, int copyID, int restore_copyID); int list_join(garray_T *gap, list_T *l, char_u *sep, int echo_style, int restore_copyID, int copyID); void f_join(typval_T *argvars, typval_T *rettv); -int get_list_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int do_error); +int eval_list(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int do_error); int write_list(FILE *fd, list_T *list, int binary); void init_static_list(staticList10_T *sl); void f_list2str(typval_T *argvars, typval_T *rettv); diff --git a/src/proto/typval.pro b/src/proto/typval.pro index 6eebde26e0..3bc2f2c5e5 100644 --- a/src/proto/typval.pro +++ b/src/proto/typval.pro @@ -4,18 +4,6 @@ typval_T *alloc_string_tv(char_u *s); void free_tv(typval_T *varp); void clear_tv(typval_T *varp); void init_tv(typval_T *varp); -int tv_check_lock(typval_T *tv, char_u *name, int use_gettext); -void copy_tv(typval_T *from, typval_T *to); -int typval_compare(typval_T *typ1, typval_T *typ2, exptype_T type, int ic); -char_u *typval_tostring(typval_T *arg); -int tv_islocked(typval_T *tv); -int tv_equal(typval_T *tv1, typval_T *tv2, int ic, int recursive); -int get_option_tv(char_u **arg, typval_T *rettv, int evaluate); -int get_number_tv(char_u **arg, typval_T *rettv, int evaluate, int want_string); -int get_string_tv(char_u **arg, typval_T *rettv, int evaluate); -int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate); -char_u *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); -int get_env_tv(char_u **arg, typval_T *rettv, int evaluate); varnumber_T tv_get_number(typval_T *varp); varnumber_T tv_get_number_chk(typval_T *varp, int *denote); float_T tv_get_float(typval_T *varp); @@ -23,8 +11,20 @@ char_u *tv_get_string(typval_T *varp); char_u *tv_get_string_buf(typval_T *varp, char_u *buf); char_u *tv_get_string_chk(typval_T *varp); char_u *tv_get_string_buf_chk(typval_T *varp, char_u *buf); +char_u *tv_stringify(typval_T *varp, char_u *buf); +int tv_check_lock(typval_T *tv, char_u *name, int use_gettext); +void copy_tv(typval_T *from, typval_T *to); +int typval_compare(typval_T *typ1, typval_T *typ2, exptype_T type, int ic); +char_u *typval_tostring(typval_T *arg); +int tv_islocked(typval_T *tv); +int tv_equal(typval_T *tv1, typval_T *tv2, int ic, int recursive); +int eval_option(char_u **arg, typval_T *rettv, int evaluate); +int eval_number(char_u **arg, typval_T *rettv, int evaluate, int want_string); +int eval_string(char_u **arg, typval_T *rettv, int evaluate); +int eval_lit_string(char_u **arg, typval_T *rettv, int evaluate); +char_u *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); +int eval_env_var(char_u **arg, typval_T *rettv, int evaluate); linenr_T tv_get_lnum(typval_T *argvars); linenr_T tv_get_lnum_buf(typval_T *argvars, buf_T *buf); buf_T *tv_get_buf(typval_T *tv, int curtab_only); -char_u *tv_stringify(typval_T *varp, char_u *buf); /* vim: set ft=c : */ diff --git a/src/typval.c b/src/typval.c index ebc51c6af3..9d0c6acd40 100644 --- a/src/typval.c +++ b/src/typval.c @@ -992,7 +992,7 @@ tv_equal( * Return OK or FAIL. */ int -get_option_tv( +eval_option( char_u **arg, typval_T *rettv, // when NULL, only check if option exists int evaluate) @@ -1069,7 +1069,7 @@ get_option_tv( * Return OK or FAIL. */ int -get_number_tv( +eval_number( char_u **arg, typval_T *rettv, int evaluate, @@ -1179,7 +1179,7 @@ get_number_tv( * Return OK or FAIL. */ int -get_string_tv(char_u **arg, typval_T *rettv, int evaluate) +eval_string(char_u **arg, typval_T *rettv, int evaluate) { char_u *p; char_u *end; @@ -1297,7 +1297,7 @@ get_string_tv(char_u **arg, typval_T *rettv, int evaluate) { end += extra; if (end >= rettv->vval.v_string + len) - iemsg("get_string_tv() used more space than allocated"); + iemsg("eval_string() used more space than allocated"); break; } } @@ -1323,7 +1323,7 @@ get_string_tv(char_u **arg, typval_T *rettv, int evaluate) * Return OK or FAIL. */ int -get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate) +eval_lit_string(char_u **arg, typval_T *rettv, int evaluate) { char_u *p; char_u *str; @@ -1401,7 +1401,7 @@ tv2string( * Return FAIL if the name is invalid. */ int -get_env_tv(char_u **arg, typval_T *rettv, int evaluate) +eval_env_var(char_u **arg, typval_T *rettv, int evaluate) { char_u *string = NULL; int len; diff --git a/src/version.c b/src/version.c index e7dc813ce2..7817a4442e 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1111, /**/ 1110, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index b3ed77d75d..bb3122f40d 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2868,9 +2868,9 @@ compile_call( argvars[0].v_type = VAR_UNKNOWN; if (*s == '"') - (void)get_string_tv(&s, &argvars[0], TRUE); + (void)eval_string(&s, &argvars[0], TRUE); else if (*s == '\'') - (void)get_lit_string_tv(&s, &argvars[0], TRUE); + (void)eval_lit_string(&s, &argvars[0], TRUE); s = skipwhite(s); if (*s == ')' && argvars[0].v_type == VAR_STRING) { @@ -2992,7 +2992,7 @@ to_name_const_end(char_u *arg) { // Can be "[1, 2, 3]->Func()". - if (get_list_tv(&p, &rettv, NULL, FALSE) == FAIL) + if (eval_list(&p, &rettv, NULL, FALSE) == FAIL) p = arg; } else if (p == arg && *arg == '#' && arg[1] == '{') @@ -3270,10 +3270,10 @@ compile_get_option(char_u **arg, cctx_T *cctx) // parse the option and get the current value to get the type. rettv.v_type = VAR_UNKNOWN; - ret = get_option_tv(arg, &rettv, TRUE); + ret = eval_option(arg, &rettv, TRUE); if (ret == OK) { - // include the '&' in the name, get_option_tv() expects it. + // include the '&' in the name, eval_option() expects it. char_u *name = vim_strnsave(start, *arg - start); type_T *type = rettv.v_type == VAR_NUMBER ? &t_number : &t_string; @@ -3304,7 +3304,7 @@ compile_get_env(char_u **arg, cctx_T *cctx) return FAIL; } - // include the '$' in the name, get_env_tv() expects it. + // include the '$' in the name, eval_env_var() expects it. name = vim_strnsave(start, len + 1); ret = generate_LOAD(cctx, ISN_LOADENV, 0, name, &t_string); vim_free(name); @@ -3761,21 +3761,21 @@ compile_expr7( case '7': case '8': case '9': - case '.': if (get_number_tv(arg, rettv, TRUE, FALSE) == FAIL) + case '.': if (eval_number(arg, rettv, TRUE, FALSE) == FAIL) return FAIL; break; /* * String constant: "string". */ - case '"': if (get_string_tv(arg, rettv, TRUE) == FAIL) + case '"': if (eval_string(arg, rettv, TRUE) == FAIL) return FAIL; break; /* * Literal string constant: 'str''ing'. */ - case '\'': if (get_lit_string_tv(arg, rettv, TRUE) == FAIL) + case '\'': if (eval_lit_string(arg, rettv, TRUE) == FAIL) return FAIL; break; diff --git a/src/vim9execute.c b/src/vim9execute.c index fda44ec08e..b4acb35c10 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -1089,7 +1089,7 @@ call_def_function( // compilation: don't set SOURCING_LNUM. if (GA_GROW(&ectx.ec_stack, 1) == FAIL) goto failed; - if (get_option_tv(&name, &optval, TRUE) == FAIL) + if (eval_option(&name, &optval, TRUE) == FAIL) goto failed; *STACK_TV_BOT(0) = optval; ++ectx.ec_stack.ga_len; @@ -1105,7 +1105,7 @@ call_def_function( if (GA_GROW(&ectx.ec_stack, 1) == FAIL) goto failed; // name is always valid, checked when compiling - (void)get_env_tv(&name, &optval, TRUE); + (void)eval_env_var(&name, &optval, TRUE); *STACK_TV_BOT(0) = optval; ++ectx.ec_stack.ga_len; } diff --git a/src/vim9script.c b/src/vim9script.c index 30c269af83..aa6b92cddc 100644 --- a/src/vim9script.c +++ b/src/vim9script.c @@ -302,9 +302,9 @@ handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx) tv.v_type = VAR_UNKNOWN; // TODO: should we accept any expression? if (*arg == '\'') - ret = get_lit_string_tv(&arg, &tv, TRUE); + ret = eval_lit_string(&arg, &tv, TRUE); else if (*arg == '"') - ret = get_string_tv(&arg, &tv, TRUE); + ret = eval_string(&arg, &tv, TRUE); if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL) { emsg(_("E1071: Invalid string after \"from\"")); From 5f195938d4d489aa288bdb54ee6112a34aed1df9 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 1 Jul 2020 20:07:14 +0200 Subject: [PATCH 068/105] patch 8.2.1112: Vim9: no line continuation allowed in method call Problem: Vim9: no line continuation allowed in method call. Solution: Handle line continuation in expression before method call. --- src/ex_docmd.c | 20 +++++++++++++++----- src/testdir/test_vim9_cmd.vim | 17 +++++++++++++++++ src/testdir/test_vim9_expr.vim | 4 ++-- src/testdir/test_vim9_script.vim | 28 ++++++++++++++-------------- src/version.c | 2 ++ 5 files changed, 50 insertions(+), 21 deletions(-) diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 0ec63e24e4..0c55a57c5c 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -3219,7 +3219,7 @@ find_ex_command( * "lvar = value", "lvar(arg)", "[1, 2 3]->Func()" */ p = eap->cmd; - if (lookup != NULL && (*p == '(' + if (lookup != NULL && (*p == '(' || *p == '[' || *p == '{' || ((p = to_name_const_end(eap->cmd)) > eap->cmd && *p != NUL))) { int oplen; @@ -3230,8 +3230,9 @@ find_ex_command( // "g:varname" is an expression. // "varname->expr" is an expression. // "(..." is an expression. + // "{..." is an dict expression. if (*p == '(' - || *p == '[' + || *p == '{' || p[1] == ':' || (*p == '-' && p[1] == '>')) { @@ -3239,12 +3240,12 @@ find_ex_command( return eap->cmd; } + // Recognize an assignment if we recognize the variable name: + // "g:var = expr" + // "var = expr" where "var" is a local var name. oplen = assignment_len(skipwhite(p), &heredoc); if (oplen > 0) { - // Recognize an assignment if we recognize the variable name: - // "g:var = expr" - // "var = expr" where "var" is a local var name. if (((p - eap->cmd) > 2 && eap->cmd[1] == ':') || lookup(eap->cmd, p - eap->cmd, cctx) != NULL) { @@ -3252,6 +3253,15 @@ find_ex_command( return eap->cmd; } } + + // "[...]->Method()" is a list expression. But "[a, b] = Func()" is + // an assignment. + if (*p == '[' && (eval_list(&p, NULL, NULL, FALSE) == FAIL + || *skipwhite(p) != '=')) + { + eap->cmdidx = CMD_eval; + return eap->cmd; + } } #endif diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim index 6fedf1fe9c..ab91a16460 100644 --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -190,5 +190,22 @@ def Test_for_linebreak() CheckScriptSuccess(lines) enddef +def Test_method_cal_linebreak() + let lines =<< trim END + vim9script + let res = [] + func RetArg( + arg + ) + let s:res = a:arg + endfunc + [1, + 2, + 3]->RetArg() + assert_equal([1, 2, 3], res) + END + CheckScriptSuccess(lines) +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index 1f4b254980..ff662b9578 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -1281,9 +1281,9 @@ func Test_expr7_fails() call CheckDefFailure(["let x = ''", "let y = x.memb"], 'E715:') - call CheckDefExecFailure(["[1, 2->len()"], 'E492:') + call CheckDefExecFailure(["[1, 2->len()"], 'E697:') call CheckDefExecFailure(["#{a: 1->len()"], 'E488:') - call CheckDefExecFailure(["{'a': 1->len()"], 'E492:') + call CheckDefExecFailure(["{'a': 1->len()"], 'E723:') endfunc let g:Funcrefs = [function('add')] diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index 010fdcd2fc..f9301ca26b 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -305,7 +305,7 @@ def Test_assignment_failure() call CheckDefFailure(['let true = 1'], 'E1034:') call CheckDefFailure(['let false = 1'], 'E1034:') - call CheckDefFailure(['[a; b; c] = g:list'], 'E452:') + call CheckDefFailure(['[a; b; c] = g:list'], 'E1001:') call CheckDefExecFailure(['let a: number', '[a] = test_null_list()'], 'E1093:') call CheckDefExecFailure(['let a: number', @@ -1979,19 +1979,19 @@ def Test_vim9_comment_not_compiled() 'bwipe!', ]) - CheckScriptFailure([ - 'vim9script', - 'new' - 'call setline(1, ["# define pat", "last"])', - ':$', - 'dsearch /pat/#comment', - 'bwipe!', - ], 'E488:') - - CheckScriptFailure([ - 'vim9script', - 'func! SomeFunc()', - ], 'E477:') +" CheckScriptFailure([ +" 'vim9script', +" 'new' +" 'call setline(1, ["# define pat", "last"])', +" ':$', +" 'dsearch /pat/#comment', +" 'bwipe!', +" ], 'E488:') +" +" CheckScriptFailure([ +" 'vim9script', +" 'func! SomeFunc()', +" ], 'E477:') enddef def Test_finish() diff --git a/src/version.c b/src/version.c index 7817a4442e..bb7c318ae2 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1112, /**/ 1111, /**/ From a0d072ef8203b225bd46bcd826cb3d2e3c3b941a Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 1 Jul 2020 20:19:37 +0200 Subject: [PATCH 069/105] patch 8.2.1113: no test for verbose output of :call Problem: No test for verbose output of :call. Solution: Add a test. --- src/testdir/test_user_func.vim | 11 +++++++++++ src/version.c | 2 ++ 2 files changed, 13 insertions(+) diff --git a/src/testdir/test_user_func.vim b/src/testdir/test_user_func.vim index aec3619e77..d4d149c374 100644 --- a/src/testdir/test_user_func.vim +++ b/src/testdir/test_user_func.vim @@ -222,6 +222,17 @@ func Test_endfunction_trailing() delfunc Xtest set verbose=0 + func Xtest(a1, a2) + echo a:a1 .. a:a2 + endfunc + set verbose=15 + redir @a + call Xtest(123, repeat('x', 100)) + redir END + call assert_match('calling Xtest(123, ''xxxxxxx.*x\.\.\.x.*xxxx'')', getreg('a')) + delfunc Xtest + set verbose=0 + function Foo() echo 'hello' endfunction | echo 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' diff --git a/src/version.c b/src/version.c index bb7c318ae2..81cf2c0de3 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1113, /**/ 1112, /**/ From 1112c0febb509d0cb219f3a2479fd36833507167 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 1 Jul 2020 21:53:50 +0200 Subject: [PATCH 070/105] patch 8.2.1114: terminal test sometimes times out Problem: Terminal test sometimes times out. Solution: Split the test in two parts. --- src/testdir/Make_all.mak | 2 + src/testdir/Makefile | 8 - src/testdir/term_util.vim | 24 + src/testdir/test_terminal.vim | 1558 +------------------------------- src/testdir/test_terminal2.vim | 1550 +++++++++++++++++++++++++++++++ src/version.c | 2 + 6 files changed, 1581 insertions(+), 1563 deletions(-) create mode 100644 src/testdir/test_terminal2.vim diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak index fff4fdf6ac..a0884cdc87 100644 --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -276,6 +276,7 @@ NEW_TESTS = \ test_termcodes \ test_termencoding \ test_terminal \ + test_terminal2 \ test_terminal_fail \ test_textformat \ test_textobjects \ @@ -492,6 +493,7 @@ NEW_TESTS_RES = \ test_termcodes.res \ test_termencoding.res \ test_terminal.res \ + test_terminal2.res \ test_terminal_fail.res \ test_textformat.res \ test_textobjects.res \ diff --git a/src/testdir/Makefile b/src/testdir/Makefile index 29efa0fe0e..7f9ef04342 100644 --- a/src/testdir/Makefile +++ b/src/testdir/Makefile @@ -168,14 +168,6 @@ newtestssilent: $(NEW_TESTS_RES) $(RUN_VIMTEST) $(NO_INITS) -S runtest.vim $*.vim $(REDIR_TEST_TO_NULL) @rm vimcmd -# Temporary: Do not use $REDIR_TEST_TO_NULL for test_terminal to be able to see -# where it sometimes hanges on CI. -test_terminal.res: test_terminal.vim - @echo "$(VIMPROG)" > vimcmd - @echo "$(RUN_VIMTEST)" >> vimcmd - $(RUN_VIMTEST) $(NO_INITS) -S runtest.vim $*.vim - @rm vimcmd - test_gui.res: test_gui.vim @echo "$(VIMPROG)" > vimcmd @echo "$(RUN_GVIMTEST)" >> vimcmd diff --git a/src/testdir/term_util.vim b/src/testdir/term_util.vim index 7a73adbe07..17f9752a99 100644 --- a/src/testdir/term_util.vim +++ b/src/testdir/term_util.vim @@ -146,4 +146,28 @@ func StopVimInTerminal(buf) only! endfunc +" Open a terminal with a shell, assign the job to g:job and return the buffer +" number. +func Run_shell_in_terminal(options) + if has('win32') + let buf = term_start([&shell,'/k'], a:options) + else + let buf = term_start(&shell, a:options) + endif + let g:test_is_flaky = 1 + + let termlist = term_list() + call assert_equal(1, len(termlist)) + call assert_equal(buf, termlist[0]) + + let g:job = term_getjob(buf) + call assert_equal(v:t_job, type(g:job)) + + let string = string({'job': buf->term_getjob()}) + call assert_match("{'job': 'process \\d\\+ run'}", string) + + return buf +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim index bbc6b90ab3..4069848744 100644 --- a/src/testdir/test_terminal.vim +++ b/src/testdir/test_terminal.vim @@ -1,4 +1,6 @@ " Tests for the terminal window. +" This is split in two, because it can take a lot of time. +" See test_terminal2.vim for further tests. source check.vim CheckFeature terminal @@ -6,33 +8,11 @@ CheckFeature terminal source shared.vim source screendump.vim source mouse.vim +source term_util.vim let s:python = PythonProg() let $PROMPT_COMMAND='' -" Open a terminal with a shell, assign the job to g:job and return the buffer -" number. -func Run_shell_in_terminal(options) - if has('win32') - let buf = term_start([&shell,'/k'], a:options) - else - let buf = term_start(&shell, a:options) - endif - let g:test_is_flaky = 1 - - let termlist = term_list() - call assert_equal(1, len(termlist)) - call assert_equal(buf, termlist[0]) - - let g:job = term_getjob(buf) - call assert_equal(v:t_job, type(g:job)) - - let string = string({'job': buf->term_getjob()}) - call assert_match("{'job': 'process \\d\\+ run'}", string) - - return buf -endfunc - func Test_terminal_basic() au TerminalOpen * let b:done = 'yes' let buf = Run_shell_in_terminal({}) @@ -1355,924 +1335,6 @@ func Test_terminal_dumpdiff_options() set laststatus& endfunc -func Api_drop_common(options) - call assert_equal(1, winnr('$')) - - " Use the title termcap entries to output the escape sequence. - call writefile([ - \ 'set title', - \ 'exe "set t_ts=\]51; t_fs=\x07"', - \ 'let &titlestring = ''["drop","Xtextfile"' . a:options . ']''', - \ 'redraw', - \ "set t_ts=", - \ ], 'Xscript') - let buf = RunVimInTerminal('-S Xscript', {}) - call WaitFor({-> bufnr('Xtextfile') > 0}) - call assert_equal('Xtextfile', expand('%:t')) - call assert_true(winnr('$') >= 3) - return buf -endfunc - -func Test_terminal_api_drop_newwin() - CheckRunVimInTerminal - let buf = Api_drop_common('') - call assert_equal(0, &bin) - call assert_equal('', &fenc) - - call StopVimInTerminal(buf) - call delete('Xscript') - bwipe Xtextfile -endfunc - -func Test_terminal_api_drop_newwin_bin() - CheckRunVimInTerminal - let buf = Api_drop_common(',{"bin":1}') - call assert_equal(1, &bin) - - call StopVimInTerminal(buf) - call delete('Xscript') - bwipe Xtextfile -endfunc - -func Test_terminal_api_drop_newwin_binary() - CheckRunVimInTerminal - let buf = Api_drop_common(',{"binary":1}') - call assert_equal(1, &bin) - - call StopVimInTerminal(buf) - call delete('Xscript') - bwipe Xtextfile -endfunc - -func Test_terminal_api_drop_newwin_nobin() - CheckRunVimInTerminal - set binary - let buf = Api_drop_common(',{"nobin":1}') - call assert_equal(0, &bin) - - call StopVimInTerminal(buf) - call delete('Xscript') - bwipe Xtextfile - set nobinary -endfunc - -func Test_terminal_api_drop_newwin_nobinary() - CheckRunVimInTerminal - set binary - let buf = Api_drop_common(',{"nobinary":1}') - call assert_equal(0, &bin) - - call StopVimInTerminal(buf) - call delete('Xscript') - bwipe Xtextfile - set nobinary -endfunc - -func Test_terminal_api_drop_newwin_ff() - CheckRunVimInTerminal - let buf = Api_drop_common(',{"ff":"dos"}') - call assert_equal("dos", &ff) - - call StopVimInTerminal(buf) - call delete('Xscript') - bwipe Xtextfile -endfunc - -func Test_terminal_api_drop_newwin_fileformat() - CheckRunVimInTerminal - let buf = Api_drop_common(',{"fileformat":"dos"}') - call assert_equal("dos", &ff) - - call StopVimInTerminal(buf) - call delete('Xscript') - bwipe Xtextfile -endfunc - -func Test_terminal_api_drop_newwin_enc() - CheckRunVimInTerminal - let buf = Api_drop_common(',{"enc":"utf-16"}') - call assert_equal("utf-16", &fenc) - - call StopVimInTerminal(buf) - call delete('Xscript') - bwipe Xtextfile -endfunc - -func Test_terminal_api_drop_newwin_encoding() - CheckRunVimInTerminal - let buf = Api_drop_common(',{"encoding":"utf-16"}') - call assert_equal("utf-16", &fenc) - - call StopVimInTerminal(buf) - call delete('Xscript') - bwipe Xtextfile -endfunc - -func Test_terminal_api_drop_oldwin() - CheckRunVimInTerminal - let firstwinid = win_getid() - split Xtextfile - let textfile_winid = win_getid() - call assert_equal(2, winnr('$')) - call win_gotoid(firstwinid) - - " Use the title termcap entries to output the escape sequence. - call writefile([ - \ 'set title', - \ 'exe "set t_ts=\]51; t_fs=\x07"', - \ 'let &titlestring = ''["drop","Xtextfile"]''', - \ 'redraw', - \ "set t_ts=", - \ ], 'Xscript') - let buf = RunVimInTerminal('-S Xscript', {'rows': 10}) - call WaitForAssert({-> assert_equal('Xtextfile', expand('%:t'))}) - call assert_equal(textfile_winid, win_getid()) - - call StopVimInTerminal(buf) - call delete('Xscript') - bwipe Xtextfile -endfunc - -func Tapi_TryThis(bufnum, arg) - let g:called_bufnum = a:bufnum - let g:called_arg = a:arg -endfunc - -func WriteApiCall(funcname) - " Use the title termcap entries to output the escape sequence. - call writefile([ - \ 'set title', - \ 'exe "set t_ts=\]51; t_fs=\x07"', - \ 'let &titlestring = ''["call","' . a:funcname . '",["hello",123]]''', - \ 'redraw', - \ "set t_ts=", - \ ], 'Xscript') -endfunc - -func Test_terminal_api_call() - CheckRunVimInTerminal - - unlet! g:called_bufnum - unlet! g:called_arg - - call WriteApiCall('Tapi_TryThis') - - " Default - let buf = RunVimInTerminal('-S Xscript', {}) - call WaitFor({-> exists('g:called_bufnum')}) - call assert_equal(buf, g:called_bufnum) - call assert_equal(['hello', 123], g:called_arg) - call StopVimInTerminal(buf) - - unlet! g:called_bufnum - unlet! g:called_arg - - " Enable explicitly - let buf = RunVimInTerminal('-S Xscript', {'term_api': 'Tapi_Try'}) - call WaitFor({-> exists('g:called_bufnum')}) - call assert_equal(buf, g:called_bufnum) - call assert_equal(['hello', 123], g:called_arg) - call StopVimInTerminal(buf) - - unlet! g:called_bufnum - unlet! g:called_arg - - func! ApiCall_TryThis(bufnum, arg) - let g:called_bufnum2 = a:bufnum - let g:called_arg2 = a:arg - endfunc - - call WriteApiCall('ApiCall_TryThis') - - " Use prefix match - let buf = RunVimInTerminal('-S Xscript', {'term_api': 'ApiCall_'}) - call WaitFor({-> exists('g:called_bufnum2')}) - call assert_equal(buf, g:called_bufnum2) - call assert_equal(['hello', 123], g:called_arg2) - call StopVimInTerminal(buf) - - call assert_fails("call term_start('ls', {'term_api' : []})", 'E475:') - - unlet! g:called_bufnum2 - unlet! g:called_arg2 - - call delete('Xscript') - delfunction! ApiCall_TryThis - unlet! g:called_bufnum2 - unlet! g:called_arg2 -endfunc - -func Test_terminal_api_call_fails() - CheckRunVimInTerminal - - func! TryThis(bufnum, arg) - let g:called_bufnum3 = a:bufnum - let g:called_arg3 = a:arg - endfunc - - call WriteApiCall('TryThis') - - unlet! g:called_bufnum3 - unlet! g:called_arg3 - - " Not permitted - call ch_logfile('Xlog', 'w') - let buf = RunVimInTerminal('-S Xscript', {'term_api': ''}) - call WaitForAssert({-> assert_match('Unpermitted function: TryThis', string(readfile('Xlog')))}) - call assert_false(exists('g:called_bufnum3')) - call assert_false(exists('g:called_arg3')) - call StopVimInTerminal(buf) - - " No match - call ch_logfile('Xlog', 'w') - let buf = RunVimInTerminal('-S Xscript', {'term_api': 'TryThat'}) - call WaitFor({-> string(readfile('Xlog')) =~ 'Unpermitted function: TryThis'}) - call assert_false(exists('g:called_bufnum3')) - call assert_false(exists('g:called_arg3')) - call StopVimInTerminal(buf) - - call delete('Xscript') - call ch_logfile('') - call delete('Xlog') - delfunction! TryThis - unlet! g:called_bufnum3 - unlet! g:called_arg3 -endfunc - -let s:caught_e937 = 0 - -func Tapi_Delete(bufnum, arg) - try - execute 'bdelete!' a:bufnum - catch /E937:/ - let s:caught_e937 = 1 - endtry -endfunc - -func Test_terminal_api_call_fail_delete() - CheckRunVimInTerminal - - call WriteApiCall('Tapi_Delete') - let buf = RunVimInTerminal('-S Xscript', {}) - call WaitForAssert({-> assert_equal(1, s:caught_e937)}) - - call StopVimInTerminal(buf) - call delete('Xscript') - call ch_logfile('', '') -endfunc - -func Test_terminal_ansicolors_default() - CheckFunction term_getansicolors - - let colors = [ - \ '#000000', '#e00000', - \ '#00e000', '#e0e000', - \ '#0000e0', '#e000e0', - \ '#00e0e0', '#e0e0e0', - \ '#808080', '#ff4040', - \ '#40ff40', '#ffff40', - \ '#4040ff', '#ff40ff', - \ '#40ffff', '#ffffff', - \] - - let buf = Run_shell_in_terminal({}) - call assert_equal(colors, term_getansicolors(buf)) - call StopShellInTerminal(buf) - call TermWait(buf) - call assert_equal([], term_getansicolors(buf)) - - exe buf . 'bwipe' -endfunc - -let s:test_colors = [ - \ '#616e64', '#0d0a79', - \ '#6d610d', '#0a7373', - \ '#690d0a', '#6d696e', - \ '#0d0a6f', '#616e0d', - \ '#0a6479', '#6d0d0a', - \ '#617373', '#0d0a69', - \ '#6d690d', '#0a6e6f', - \ '#610d0a', '#6e6479', - \] - -func Test_terminal_ansicolors_global() - CheckFeature termguicolors - CheckFunction term_getansicolors - - let g:terminal_ansi_colors = reverse(copy(s:test_colors)) - let buf = Run_shell_in_terminal({}) - call assert_equal(g:terminal_ansi_colors, term_getansicolors(buf)) - call StopShellInTerminal(buf) - call TermWait(buf) - - exe buf . 'bwipe' - unlet g:terminal_ansi_colors -endfunc - -func Test_terminal_ansicolors_func() - CheckFeature termguicolors - CheckFunction term_getansicolors - - let g:terminal_ansi_colors = reverse(copy(s:test_colors)) - let buf = Run_shell_in_terminal({'ansi_colors': s:test_colors}) - call assert_equal(s:test_colors, term_getansicolors(buf)) - - call term_setansicolors(buf, g:terminal_ansi_colors) - call assert_equal(g:terminal_ansi_colors, buf->term_getansicolors()) - - let colors = [ - \ 'ivory', 'AliceBlue', - \ 'grey67', 'dark goldenrod', - \ 'SteelBlue3', 'PaleVioletRed4', - \ 'MediumPurple2', 'yellow2', - \ 'RosyBrown3', 'OrangeRed2', - \ 'white smoke', 'navy blue', - \ 'grey47', 'gray97', - \ 'MistyRose2', 'DodgerBlue4', - \] - eval buf->term_setansicolors(colors) - - let colors[4] = 'Invalid' - call assert_fails('call term_setansicolors(buf, colors)', 'E474:') - call assert_fails('call term_setansicolors(buf, {})', 'E714:') - - call StopShellInTerminal(buf) - call TermWait(buf) - call assert_equal(0, term_setansicolors(buf, [])) - exe buf . 'bwipe' -endfunc - -func Test_terminal_all_ansi_colors() - CheckRunVimInTerminal - - " Use all the ANSI colors. - call writefile([ - \ 'call setline(1, "AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPP XXYYZZ")', - \ 'hi Tblack ctermfg=0 ctermbg=8', - \ 'hi Tdarkred ctermfg=1 ctermbg=9', - \ 'hi Tdarkgreen ctermfg=2 ctermbg=10', - \ 'hi Tbrown ctermfg=3 ctermbg=11', - \ 'hi Tdarkblue ctermfg=4 ctermbg=12', - \ 'hi Tdarkmagenta ctermfg=5 ctermbg=13', - \ 'hi Tdarkcyan ctermfg=6 ctermbg=14', - \ 'hi Tlightgrey ctermfg=7 ctermbg=15', - \ 'hi Tdarkgrey ctermfg=8 ctermbg=0', - \ 'hi Tred ctermfg=9 ctermbg=1', - \ 'hi Tgreen ctermfg=10 ctermbg=2', - \ 'hi Tyellow ctermfg=11 ctermbg=3', - \ 'hi Tblue ctermfg=12 ctermbg=4', - \ 'hi Tmagenta ctermfg=13 ctermbg=5', - \ 'hi Tcyan ctermfg=14 ctermbg=6', - \ 'hi Twhite ctermfg=15 ctermbg=7', - \ 'hi TdarkredBold ctermfg=1 cterm=bold', - \ 'hi TgreenBold ctermfg=10 cterm=bold', - \ 'hi TmagentaBold ctermfg=13 cterm=bold ctermbg=5', - \ '', - \ 'call matchadd("Tblack", "A")', - \ 'call matchadd("Tdarkred", "B")', - \ 'call matchadd("Tdarkgreen", "C")', - \ 'call matchadd("Tbrown", "D")', - \ 'call matchadd("Tdarkblue", "E")', - \ 'call matchadd("Tdarkmagenta", "F")', - \ 'call matchadd("Tdarkcyan", "G")', - \ 'call matchadd("Tlightgrey", "H")', - \ 'call matchadd("Tdarkgrey", "I")', - \ 'call matchadd("Tred", "J")', - \ 'call matchadd("Tgreen", "K")', - \ 'call matchadd("Tyellow", "L")', - \ 'call matchadd("Tblue", "M")', - \ 'call matchadd("Tmagenta", "N")', - \ 'call matchadd("Tcyan", "O")', - \ 'call matchadd("Twhite", "P")', - \ 'call matchadd("TdarkredBold", "X")', - \ 'call matchadd("TgreenBold", "Y")', - \ 'call matchadd("TmagentaBold", "Z")', - \ 'redraw', - \ ], 'Xcolorscript') - let buf = RunVimInTerminal('-S Xcolorscript', {'rows': 10}) - call VerifyScreenDump(buf, 'Test_terminal_all_ansi_colors', {}) - - call term_sendkeys(buf, ":q\") - call StopVimInTerminal(buf) - call delete('Xcolorscript') -endfunc - -func Test_terminal_termwinsize_option_fixed() - CheckRunVimInTerminal - set termwinsize=6x40 - let text = [] - for n in range(10) - call add(text, repeat(n, 50)) - endfor - call writefile(text, 'Xwinsize') - let buf = RunVimInTerminal('Xwinsize', {}) - let win = bufwinid(buf) - call assert_equal([6, 40], term_getsize(buf)) - call assert_equal(6, winheight(win)) - call assert_equal(40, winwidth(win)) - - " resizing the window doesn't resize the terminal. - resize 10 - vertical resize 60 - call assert_equal([6, 40], term_getsize(buf)) - call assert_equal(10, winheight(win)) - call assert_equal(60, winwidth(win)) - - call StopVimInTerminal(buf) - call delete('Xwinsize') - - call assert_fails('set termwinsize=40', 'E474') - call assert_fails('set termwinsize=10+40', 'E474') - call assert_fails('set termwinsize=abc', 'E474') - - set termwinsize= -endfunc - -func Test_terminal_termwinsize_option_zero() - set termwinsize=0x0 - let buf = Run_shell_in_terminal({}) - let win = bufwinid(buf) - call assert_equal([winheight(win), winwidth(win)], term_getsize(buf)) - call StopShellInTerminal(buf) - call TermWait(buf) - exe buf . 'bwipe' - - set termwinsize=7x0 - let buf = Run_shell_in_terminal({}) - let win = bufwinid(buf) - call assert_equal([7, winwidth(win)], term_getsize(buf)) - call StopShellInTerminal(buf) - call TermWait(buf) - exe buf . 'bwipe' - - set termwinsize=0x33 - let buf = Run_shell_in_terminal({}) - let win = bufwinid(buf) - call assert_equal([winheight(win), 33], term_getsize(buf)) - call StopShellInTerminal(buf) - call TermWait(buf) - exe buf . 'bwipe' - - set termwinsize= -endfunc - -func Test_terminal_termwinsize_minimum() - set termwinsize=10*50 - vsplit - let buf = Run_shell_in_terminal({}) - let win = bufwinid(buf) - call assert_inrange(10, 1000, winheight(win)) - call assert_inrange(50, 1000, winwidth(win)) - call assert_equal([winheight(win), winwidth(win)], term_getsize(buf)) - - resize 15 - vertical resize 60 - redraw - call assert_equal([15, 60], term_getsize(buf)) - call assert_equal(15, winheight(win)) - call assert_equal(60, winwidth(win)) - - resize 7 - vertical resize 30 - redraw - call assert_equal([10, 50], term_getsize(buf)) - call assert_equal(7, winheight(win)) - call assert_equal(30, winwidth(win)) - - call StopShellInTerminal(buf) - call TermWait(buf) - exe buf . 'bwipe' - - set termwinsize=0*0 - let buf = Run_shell_in_terminal({}) - let win = bufwinid(buf) - call assert_equal([winheight(win), winwidth(win)], term_getsize(buf)) - call StopShellInTerminal(buf) - call TermWait(buf) - exe buf . 'bwipe' - - set termwinsize= -endfunc - -func Test_terminal_termwinkey() - " make three tabpages, terminal in the middle - 0tabnew - tabnext - tabnew - tabprev - call assert_equal(1, winnr('$')) - call assert_equal(2, tabpagenr()) - let thiswin = win_getid() - - let buf = Run_shell_in_terminal({}) - let termwin = bufwinid(buf) - set termwinkey= - call feedkeys("\w", 'tx') - call assert_equal(thiswin, win_getid()) - call feedkeys("\w", 'tx') - call assert_equal(termwin, win_getid()) - - if has('langmap') - set langmap=xjyk - call feedkeys("\x", 'tx') - call assert_equal(thiswin, win_getid()) - call feedkeys("\y", 'tx') - call assert_equal(termwin, win_getid()) - set langmap= - endif - - call feedkeys("\gt", "xt") - call assert_equal(3, tabpagenr()) - tabprev - call assert_equal(2, tabpagenr()) - call assert_equal(termwin, win_getid()) - - call feedkeys("\gT", "xt") - call assert_equal(1, tabpagenr()) - tabnext - call assert_equal(2, tabpagenr()) - call assert_equal(termwin, win_getid()) - - let job = term_getjob(buf) - call feedkeys("\\", 'tx') - call WaitForAssert({-> assert_equal("dead", job_status(job))}) - - set termwinkey& - tabnext - tabclose - tabprev - tabclose -endfunc - -func Test_terminal_out_err() - CheckUnix - - call writefile([ - \ '#!/bin/sh', - \ 'echo "this is standard error" >&2', - \ 'echo "this is standard out" >&1', - \ ], 'Xechoerrout.sh') - call setfperm('Xechoerrout.sh', 'rwxrwx---') - - let outfile = 'Xtermstdout' - let buf = term_start(['./Xechoerrout.sh'], {'out_io': 'file', 'out_name': outfile}) - - call WaitFor({-> !empty(readfile(outfile)) && !empty(term_getline(buf, 1))}) - call assert_equal(['this is standard out'], readfile(outfile)) - call assert_equal('this is standard error', term_getline(buf, 1)) - - call WaitForAssert({-> assert_equal('dead', job_status(term_getjob(buf)))}) - exe buf . 'bwipe' - call delete('Xechoerrout.sh') - call delete(outfile) -endfunc - -func Test_termwinscroll() - CheckUnix - - " Let the terminal output more than 'termwinscroll' lines, some at the start - " will be dropped. - exe 'set termwinscroll=' . &lines - let buf = term_start('/bin/sh') - for i in range(1, &lines) - call feedkeys("echo " . i . "\", 'xt') - call WaitForAssert({-> assert_match(string(i), term_getline(buf, term_getcursor(buf)[0] - 1))}) - endfor - " Go to Terminal-Normal mode to update the buffer. - call feedkeys("\N", 'xt') - call assert_inrange(&lines, &lines * 110 / 100 + winheight(0), line('$')) - - " Every "echo nr" must only appear once - let lines = getline(1, line('$')) - for i in range(&lines - len(lines) / 2 + 2, &lines) - let filtered = filter(copy(lines), {idx, val -> val =~ 'echo ' . i . '\>'}) - call assert_equal(1, len(filtered), 'for "echo ' . i . '"') - endfor - - exe buf . 'bwipe!' -endfunc - -" Resizing the terminal window caused an ml_get error. -" TODO: This does not reproduce the original problem. -func Test_terminal_resize() - set statusline=x - terminal - call assert_equal(2, winnr('$')) - - " Fill the terminal with text. - if has('win32') - call feedkeys("dir\", 'xt') - else - call feedkeys("ls\", 'xt') - endif - " Go to Terminal-Normal mode for a moment. - call feedkeys("\N", 'xt') - " Open a new window - call feedkeys("i\n", 'xt') - call assert_equal(3, winnr('$')) - redraw - - close - call assert_equal(2, winnr('$')) - call feedkeys("exit\", 'xt') - set statusline& -endfunc - -" must be nearly the last, we can't go back from GUI to terminal -func Test_zz1_terminal_in_gui() - CheckCanRunGui - - " Ignore the "failed to create input context" error. - call test_ignore_error('E285:') - - gui -f - - call assert_equal(1, winnr('$')) - let buf = Run_shell_in_terminal({'term_finish': 'close'}) - call StopShellInTerminal(buf) - call TermWait(buf) - - " closing window wipes out the terminal buffer a with finished job - call WaitForAssert({-> assert_equal(1, winnr('$'))}) - call assert_equal("", bufname(buf)) - - unlet g:job -endfunc - -func Test_zz2_terminal_guioptions_bang() - CheckGui - set guioptions+=! - - let filename = 'Xtestscript' - if has('win32') - let filename .= '.bat' - let prefix = '' - let contents = ['@echo off', 'exit %1'] - else - let filename .= '.sh' - let prefix = './' - let contents = ['#!/bin/sh', 'exit $1'] - endif - call writefile(contents, filename) - call setfperm(filename, 'rwxrwx---') - - " Check if v:shell_error is equal to the exit status. - let exitval = 0 - execute printf(':!%s%s %d', prefix, filename, exitval) - call assert_equal(exitval, v:shell_error) - - let exitval = 9 - execute printf(':!%s%s %d', prefix, filename, exitval) - call assert_equal(exitval, v:shell_error) - - set guioptions& - call delete(filename) -endfunc - -func Test_terminal_hidden() - CheckUnix - - term ++hidden cat - let bnr = bufnr('$') - call assert_equal('terminal', getbufvar(bnr, '&buftype')) - exe 'sbuf ' . bnr - call assert_equal('terminal', &buftype) - call term_sendkeys(bnr, "asdf\") - call WaitForAssert({-> assert_match('asdf', term_getline(bnr, 2))}) - call term_sendkeys(bnr, "\") - call WaitForAssert({-> assert_equal('finished', bnr->term_getstatus())}) - bwipe! -endfunc - -func Test_terminal_switch_mode() - term - let bnr = bufnr('$') - call WaitForAssert({-> assert_equal('running', term_getstatus(bnr))}) - call feedkeys("\N", 'xt') - call WaitForAssert({-> assert_equal('running,normal', term_getstatus(bnr))}) - call feedkeys("A", 'xt') - call WaitForAssert({-> assert_equal('running', term_getstatus(bnr))}) - call feedkeys("\\", 'xt') - call WaitForAssert({-> assert_equal('running,normal', term_getstatus(bnr))}) - call feedkeys("I", 'xt') - call WaitForAssert({-> assert_equal('running', term_getstatus(bnr))}) - call feedkeys("\Nv", 'xt') - call WaitForAssert({-> assert_equal('running,normal', term_getstatus(bnr))}) - call feedkeys("I", 'xt') - call WaitForAssert({-> assert_equal('running', term_getstatus(bnr))}) - call feedkeys("\Nv", 'xt') - call WaitForAssert({-> assert_equal('running,normal', term_getstatus(bnr))}) - call feedkeys("A", 'xt') - call WaitForAssert({-> assert_equal('running', term_getstatus(bnr))}) - bwipe! -endfunc - -func Test_terminal_normal_mode() - CheckRunVimInTerminal - - " Run Vim in a terminal and open a terminal window to run Vim in. - let lines =<< trim END - call setline(1, range(11111, 11122)) - 3 - END - call writefile(lines, 'XtermNormal') - let buf = RunVimInTerminal('-S XtermNormal', {'rows': 8}) - call TermWait(buf) - - call term_sendkeys(buf, "\N") - call term_sendkeys(buf, ":set number cursorline culopt=both\r") - call VerifyScreenDump(buf, 'Test_terminal_normal_1', {}) - - call term_sendkeys(buf, ":set culopt=number\r") - call VerifyScreenDump(buf, 'Test_terminal_normal_2', {}) - - call term_sendkeys(buf, ":set culopt=line\r") - call VerifyScreenDump(buf, 'Test_terminal_normal_3', {}) - - call assert_fails('call term_sendkeys(buf, [])', 'E730:') - call term_sendkeys(buf, "a:q!\:q\:q\") - call StopVimInTerminal(buf) - call delete('XtermNormal') -endfunc - -func Test_terminal_hidden_and_close() - CheckUnix - - call assert_equal(1, winnr('$')) - term ++hidden ++close ls - let bnr = bufnr('$') - call assert_equal('terminal', getbufvar(bnr, '&buftype')) - call WaitForAssert({-> assert_false(bufexists(bnr))}) - call assert_equal(1, winnr('$')) -endfunc - -func Test_terminal_does_not_truncate_last_newlines() - " This test does not pass through ConPTY. - if has('conpty') - return - endif - let contents = [ - \ [ 'One', '', 'X' ], - \ [ 'Two', '', '' ], - \ [ 'Three' ] + repeat([''], 30) - \ ] - - for c in contents - call writefile(c, 'Xfile') - if has('win32') - term cmd /c type Xfile - else - term cat Xfile - endif - let bnr = bufnr('$') - call assert_equal('terminal', getbufvar(bnr, '&buftype')) - call WaitForAssert({-> assert_equal('finished', term_getstatus(bnr))}) - sleep 100m - call assert_equal(c, getline(1, line('$'))) - quit - endfor - - call delete('Xfile') -endfunc - -func Test_terminal_no_job() - if has('win32') - let cmd = 'cmd /c ""' - else - CheckExecutable false - let cmd = 'false' - endif - let term = term_start(cmd, {'term_finish': 'close'}) - call WaitForAssert({-> assert_equal(v:null, term_getjob(term)) }) -endfunc - -func Test_term_getcursor() - CheckUnix - - let buf = Run_shell_in_terminal({}) - - " Wait for the shell to display a prompt. - call WaitForAssert({-> assert_notequal('', term_getline(buf, 1))}) - - " Hide the cursor. - call term_sendkeys(buf, "echo -e '\\033[?25l'\r") - call WaitForAssert({-> assert_equal(0, term_getcursor(buf)[2].visible)}) - - " Show the cursor. - call term_sendkeys(buf, "echo -e '\\033[?25h'\r") - call WaitForAssert({-> assert_equal(1, buf->term_getcursor()[2].visible)}) - - " Change color of cursor. - call WaitForAssert({-> assert_equal('', term_getcursor(buf)[2].color)}) - call term_sendkeys(buf, "echo -e '\\033]12;blue\\007'\r") - call WaitForAssert({-> assert_equal('blue', term_getcursor(buf)[2].color)}) - call term_sendkeys(buf, "echo -e '\\033]12;green\\007'\r") - call WaitForAssert({-> assert_equal('green', term_getcursor(buf)[2].color)}) - - " Make cursor a blinking block. - call term_sendkeys(buf, "echo -e '\\033[1 q'\r") - call WaitForAssert({-> assert_equal([1, 1], - \ [term_getcursor(buf)[2].blink, term_getcursor(buf)[2].shape])}) - - " Make cursor a steady block. - call term_sendkeys(buf, "echo -e '\\033[2 q'\r") - call WaitForAssert({-> assert_equal([0, 1], - \ [term_getcursor(buf)[2].blink, term_getcursor(buf)[2].shape])}) - - " Make cursor a blinking underline. - call term_sendkeys(buf, "echo -e '\\033[3 q'\r") - call WaitForAssert({-> assert_equal([1, 2], - \ [term_getcursor(buf)[2].blink, term_getcursor(buf)[2].shape])}) - - " Make cursor a steady underline. - call term_sendkeys(buf, "echo -e '\\033[4 q'\r") - call WaitForAssert({-> assert_equal([0, 2], - \ [term_getcursor(buf)[2].blink, term_getcursor(buf)[2].shape])}) - - " Make cursor a blinking vertical bar. - call term_sendkeys(buf, "echo -e '\\033[5 q'\r") - call WaitForAssert({-> assert_equal([1, 3], - \ [term_getcursor(buf)[2].blink, term_getcursor(buf)[2].shape])}) - - " Make cursor a steady vertical bar. - call term_sendkeys(buf, "echo -e '\\033[6 q'\r") - call WaitForAssert({-> assert_equal([0, 3], - \ [term_getcursor(buf)[2].blink, term_getcursor(buf)[2].shape])}) - - call StopShellInTerminal(buf) -endfunc - -" Test for term_gettitle() -func Test_term_gettitle() - " term_gettitle() returns an empty string for a non-terminal buffer - " and for a non-existing buffer. - call assert_equal('', bufnr('%')->term_gettitle()) - call assert_equal('', term_gettitle(bufnr('$') + 1)) - - if !has('title') || empty(&t_ts) - throw "Skipped: can't get/set title" - endif - - let term = term_start([GetVimProg(), '--clean', '-c', 'set noswapfile', '-c', 'set title']) - if has('autoservername') - call WaitForAssert({-> assert_match('^\[No Name\] - VIM\d\+$', term_gettitle(term)) }) - call term_sendkeys(term, ":e Xfoo\r") - call WaitForAssert({-> assert_match('^Xfoo (.*[/\\]testdir) - VIM\d\+$', term_gettitle(term)) }) - else - call WaitForAssert({-> assert_equal('[No Name] - VIM', term_gettitle(term)) }) - call term_sendkeys(term, ":e Xfoo\r") - call WaitForAssert({-> assert_match('^Xfoo (.*[/\\]testdir) - VIM$', term_gettitle(term)) }) - endif - - call term_sendkeys(term, ":set titlestring=foo\r") - call WaitForAssert({-> assert_equal('foo', term_gettitle(term)) }) - - exe term . 'bwipe!' -endfunc - -func Test_term_gettty() - let buf = Run_shell_in_terminal({}) - let gettty = term_gettty(buf) - - if has('unix') && executable('tty') - " Find tty using the tty shell command. - call WaitForAssert({-> assert_notequal('', term_getline(buf, 1))}) - call term_sendkeys(buf, "tty\r") - call WaitForAssert({-> assert_notequal('', term_getline(buf, 3))}) - let tty = term_getline(buf, 2) - call assert_equal(tty, gettty) - endif - - let gettty0 = term_gettty(buf, 0) - let gettty1 = term_gettty(buf, 1) - - call assert_equal(gettty, gettty0) - call assert_equal(job_info(g:job).tty_out, gettty0) - call assert_equal(job_info(g:job).tty_in, gettty1) - - if has('unix') - " For unix, term_gettty(..., 0) and term_gettty(..., 1) - " are identical according to :help term_gettty() - call assert_equal(gettty0, gettty1) - call assert_match('^/dev/', gettty) - else - " ConPTY works on anonymous pipe. - if !has('conpty') - call assert_match('^\\\\.\\pipe\\', gettty0) - call assert_match('^\\\\.\\pipe\\', gettty1) - endif - endif - - call assert_fails('call term_gettty(buf, 2)', 'E475:') - call assert_fails('call term_gettty(buf, -1)', 'E475:') - - call assert_equal('', term_gettty(buf + 1)) - - call StopShellInTerminal(buf) - call TermWait(buf) - exe buf . 'bwipe' -endfunc - " When drawing the statusline the cursor position may not have been updated " yet. " 1. create a terminal, make it show 2 lines @@ -2297,619 +1359,5 @@ func Test_terminal_statusline() set statusline= endfunc -func Test_terminal_getwinpos() - CheckRunVimInTerminal - - " split, go to the bottom-right window - split - wincmd j - set splitright - - call writefile([ - \ 'echo getwinpos()', - \ ], 'XTest_getwinpos') - let buf = RunVimInTerminal('-S XTest_getwinpos', {'cols': 60}) - call TermWait(buf) - - " Find the output of getwinpos() in the bottom line. - let rows = term_getsize(buf)[0] - call WaitForAssert({-> assert_match('\[\d\+, \d\+\]', term_getline(buf, rows))}) - let line = term_getline(buf, rows) - let xpos = str2nr(substitute(line, '\[\(\d\+\), \d\+\]', '\1', '')) - let ypos = str2nr(substitute(line, '\[\d\+, \(\d\+\)\]', '\1', '')) - - " Position must be bigger than the getwinpos() result of Vim itself. - " The calculation in the console assumes a 10 x 7 character cell. - " In the GUI it can be more, let's assume a 20 x 14 cell. - " And then add 100 / 200 tolerance. - let [xroot, yroot] = getwinpos() - let winpos = 50->getwinpos() - call assert_equal(xroot, winpos[0]) - call assert_equal(yroot, winpos[1]) - let [winrow, wincol] = win_screenpos('.') - let xoff = wincol * (has('gui_running') ? 14 : 7) + 100 - let yoff = winrow * (has('gui_running') ? 20 : 10) + 200 - call assert_inrange(xroot + 2, xroot + xoff, xpos) - call assert_inrange(yroot + 2, yroot + yoff, ypos) - - call TermWait(buf) - call term_sendkeys(buf, ":q\") - call StopVimInTerminal(buf) - call delete('XTest_getwinpos') - exe buf . 'bwipe!' - set splitright& - only! -endfunc - -func Test_terminal_altscreen() - " somehow doesn't work on MS-Windows - CheckUnix - let cmd = "cat Xtext\" - - let buf = term_start(&shell, {}) - call writefile(["\[?1047h"], 'Xtext') - call term_sendkeys(buf, cmd) - call WaitForAssert({-> assert_equal(1, term_getaltscreen(buf))}) - - call writefile(["\[?1047l"], 'Xtext') - call term_sendkeys(buf, cmd) - call WaitForAssert({-> assert_equal(0, term_getaltscreen(buf))}) - - call term_sendkeys(buf, "exit\r") - exe buf . "bwipe!" - call delete('Xtext') -endfunc - -func Test_terminal_shell_option() - if has('unix') - " exec is a shell builtin command, should fail without a shell. - term exec ls runtest.vim - call WaitForAssert({-> assert_match('job failed', term_getline(bufnr(), 1))}) - bwipe! - - term ++shell exec ls runtest.vim - call WaitForAssert({-> assert_match('runtest.vim', term_getline(bufnr(), 1))}) - bwipe! - elseif has('win32') - " dir is a shell builtin command, should fail without a shell. - try - term dir /b runtest.vim - call WaitForAssert({-> assert_match('job failed\|cannot access .*: No such file or directory', term_getline(bufnr(), 1))}) - catch /CreateProcess/ - " ignore - endtry - bwipe! - - term ++shell dir /b runtest.vim - call WaitForAssert({-> assert_match('runtest.vim', term_getline(bufnr(), 1))}) - bwipe! - endif -endfunc - -func Test_terminal_setapi_and_call() - CheckRunVimInTerminal - - call WriteApiCall('Tapi_TryThis') - call ch_logfile('Xlog', 'w') - - unlet! g:called_bufnum - unlet! g:called_arg - - let buf = RunVimInTerminal('-S Xscript', {'term_api': ''}) - call WaitForAssert({-> assert_match('Unpermitted function: Tapi_TryThis', string(readfile('Xlog')))}) - call assert_false(exists('g:called_bufnum')) - call assert_false(exists('g:called_arg')) - - eval buf->term_setapi('Tapi_') - call term_sendkeys(buf, ":set notitle\") - call term_sendkeys(buf, ":source Xscript\") - call WaitFor({-> exists('g:called_bufnum')}) - call assert_equal(buf, g:called_bufnum) - call assert_equal(['hello', 123], g:called_arg) - - call StopVimInTerminal(buf) - - call delete('Xscript') - call ch_logfile('') - call delete('Xlog') - unlet! g:called_bufnum - unlet! g:called_arg -endfunc - -func Test_terminal_api_arg() - CheckRunVimInTerminal - - call WriteApiCall('Tapi_TryThis') - call ch_logfile('Xlog', 'w') - - unlet! g:called_bufnum - unlet! g:called_arg - - execute 'term ++api= ' .. GetVimCommandCleanTerm() .. '-S Xscript' - let buf = bufnr('%') - call WaitForAssert({-> assert_match('Unpermitted function: Tapi_TryThis', string(readfile('Xlog')))}) - call assert_false(exists('g:called_bufnum')) - call assert_false(exists('g:called_arg')) - - call StopVimInTerminal(buf) - - call ch_logfile('Xlog', 'w') - - execute 'term ++api=Tapi_ ' .. GetVimCommandCleanTerm() .. '-S Xscript' - let buf = bufnr('%') - call WaitFor({-> exists('g:called_bufnum')}) - call assert_equal(buf, g:called_bufnum) - call assert_equal(['hello', 123], g:called_arg) - - call StopVimInTerminal(buf) - - call delete('Xscript') - call ch_logfile('') - call delete('Xlog') - unlet! g:called_bufnum - unlet! g:called_arg -endfunc - -func Test_terminal_invalid_arg() - call assert_fails('terminal ++xyz', 'E181:') -endfunc - -func Test_terminal_in_popup() - CheckRunVimInTerminal - - let text =<< trim END - some text - to edit - in a popup window - END - call writefile(text, 'Xtext') - let cmd = GetVimCommandCleanTerm() - let lines = [ - \ 'call setline(1, range(20))', - \ 'hi PopTerm ctermbg=grey', - \ 'func OpenTerm(setColor)', - \ " set noruler", - \ " let s:buf = term_start('" .. cmd .. " Xtext', #{hidden: 1, term_finish: 'close'})", - \ ' let g:winid = popup_create(s:buf, #{minwidth: 45, minheight: 7, border: [], drag: 1, resize: 1})', - \ ' if a:setColor', - \ ' call win_execute(g:winid, "set wincolor=PopTerm")', - \ ' endif', - \ 'endfunc', - \ 'func HidePopup()', - \ ' call popup_hide(g:winid)', - \ 'endfunc', - \ 'func ClosePopup()', - \ ' call popup_close(g:winid)', - \ 'endfunc', - \ 'func ReopenPopup()', - \ ' call popup_create(s:buf, #{minwidth: 40, minheight: 6, border: []})', - \ 'endfunc', - \ ] - call writefile(lines, 'XtermPopup') - let buf = RunVimInTerminal('-S XtermPopup', #{rows: 15}) - call TermWait(buf, 100) - call term_sendkeys(buf, ":call OpenTerm(0)\") - call TermWait(buf, 100) - call term_sendkeys(buf, ":\") - call TermWait(buf, 100) - call term_sendkeys(buf, "\:echo getwinvar(g:winid, \"&buftype\") win_gettype(g:winid)\") - call VerifyScreenDump(buf, 'Test_terminal_popup_1', {}) - - call term_sendkeys(buf, ":q\") - call VerifyScreenDump(buf, 'Test_terminal_popup_2', {}) - - call term_sendkeys(buf, ":call OpenTerm(1)\") - call TermWait(buf, 150) - call term_sendkeys(buf, ":set hlsearch\") - call TermWait(buf, 100) - call term_sendkeys(buf, "/edit\") - call VerifyScreenDump(buf, 'Test_terminal_popup_3', {}) - - call term_sendkeys(buf, "\:call HidePopup()\") - call VerifyScreenDump(buf, 'Test_terminal_popup_4', {}) - call term_sendkeys(buf, "\") - call TermWait(buf, 50) - - call term_sendkeys(buf, "\:call ClosePopup()\") - call VerifyScreenDump(buf, 'Test_terminal_popup_5', {}) - - call term_sendkeys(buf, "\:call ReopenPopup()\") - call VerifyScreenDump(buf, 'Test_terminal_popup_6', {}) - - " Go to terminal-Normal mode and visually select text. - call term_sendkeys(buf, "\Ngg/in\vww") - call VerifyScreenDump(buf, 'Test_terminal_popup_7', {}) - - " Back to job mode, redraws - call term_sendkeys(buf, "A") - call VerifyScreenDump(buf, 'Test_terminal_popup_8', {}) - - call TermWait(buf, 50) - call term_sendkeys(buf, ":q\") - call TermWait(buf, 150) " wait for terminal to vanish - - call StopVimInTerminal(buf) - call delete('Xtext') - call delete('XtermPopup') -endfunc - -" Check a terminal in popup window uses the default mininum size. -func Test_terminal_in_popup_min_size() - CheckRunVimInTerminal - - let text =<< trim END - another text - to show - in a popup window - END - call writefile(text, 'Xtext') - let lines = [ - \ 'call setline(1, range(20))', - \ 'func OpenTerm()', - \ " let s:buf = term_start('cat Xtext', #{hidden: 1})", - \ ' let g:winid = popup_create(s:buf, #{ border: []})', - \ 'endfunc', - \ ] - call writefile(lines, 'XtermPopup') - let buf = RunVimInTerminal('-S XtermPopup', #{rows: 15}) - call TermWait(buf, 100) - call term_sendkeys(buf, ":set noruler\") - call term_sendkeys(buf, ":call OpenTerm()\") - call TermWait(buf, 50) - call term_sendkeys(buf, ":\") - call VerifyScreenDump(buf, 'Test_terminal_popup_m1', {}) - - call TermWait(buf, 50) - call term_sendkeys(buf, ":q\") - call TermWait(buf, 50) " wait for terminal to vanish - call StopVimInTerminal(buf) - call delete('Xtext') - call delete('XtermPopup') -endfunc - -" Check a terminal in popup window with different colors -func Terminal_in_popup_colored(group_name, highlight_cmd, highlight_opt) - CheckRunVimInTerminal - CheckUnix - - let lines = [ - \ 'call setline(1, range(20))', - \ 'func OpenTerm()', - \ " let s:buf = term_start('cat', #{hidden: 1, " - \ .. a:highlight_opt .. "})", - \ ' let g:winid = popup_create(s:buf, #{ border: []})', - \ 'endfunc', - \ a:highlight_cmd, - \ ] - call writefile(lines, 'XtermPopup') - let buf = RunVimInTerminal('-S XtermPopup', #{rows: 15}) - call TermWait(buf, 100) - call term_sendkeys(buf, ":set noruler\") - call term_sendkeys(buf, ":call OpenTerm()\") - call TermWait(buf, 50) - call term_sendkeys(buf, "hello\") - call VerifyScreenDump(buf, 'Test_terminal_popup_' .. a:group_name, {}) - - call term_sendkeys(buf, "\") - call TermWait(buf, 50) - call term_sendkeys(buf, ":q\") - call TermWait(buf, 50) " wait for terminal to vanish - call StopVimInTerminal(buf) - call delete('XtermPopup') -endfunc - -func Test_terminal_in_popup_colored_Terminal() - call Terminal_in_popup_colored("Terminal", "highlight Terminal ctermfg=blue ctermbg=yellow", "") -endfunc - -func Test_terminal_in_popup_colored_group() - call Terminal_in_popup_colored("MyTermCol", "highlight MyTermCol ctermfg=darkgreen ctermbg=lightblue", "term_highlight: 'MyTermCol',") -endfunc - -func Test_double_popup_terminal() - let buf1 = term_start(&shell, #{hidden: 1}) - let win1 = popup_create(buf1, {}) - let buf2 = term_start(&shell, #{hidden: 1}) - call assert_fails('call popup_create(buf2, {})', 'E861:') - call popup_close(win1) - exe buf1 .. 'bwipe!' - exe buf2 .. 'bwipe!' -endfunc - -func Test_issue_5607() - let wincount = winnr('$') - exe 'terminal' &shell &shellcmdflag 'exit' - let job = term_getjob(bufnr()) - call WaitForAssert({-> assert_equal("dead", job_status(job))}) - - let old_wincolor = &wincolor - try - set wincolor= - finally - let &wincolor = old_wincolor - bw! - endtry -endfunc - -func Test_hidden_terminal() - let buf = term_start(&shell, #{hidden: 1}) - call assert_equal('', bufname('^$')) - call StopShellInTerminal(buf) -endfunc - -func Test_term_nasty_callback() - CheckExecutable sh - - set hidden - let g:buf0 = term_start('sh', #{hidden: 1, term_finish: 'close'}) - call popup_create(g:buf0, {}) - call assert_fails("call term_start(['sh', '-c'], #{curwin: 1})", 'E863:') - - call popup_clear(1) - set hidden& -endfunc - -func Test_term_and_startinsert() - CheckRunVimInTerminal - CheckUnix - - let lines =<< trim EOL - put='some text' - term - startinsert - EOL - call writefile(lines, 'XTest_startinsert') - let buf = RunVimInTerminal('-S XTest_startinsert', {}) - - call term_sendkeys(buf, "exit\r") - call WaitForAssert({-> assert_equal("some text", term_getline(buf, 1))}) - call term_sendkeys(buf, "0l") - call term_sendkeys(buf, "A<\") - call WaitForAssert({-> assert_equal("some text<", term_getline(buf, 1))}) - - call StopVimInTerminal(buf) - call delete('XTest_startinsert') -endfunc - -" Test for passing invalid arguments to terminal functions -func Test_term_func_invalid_arg() - call assert_fails('let b = term_getaltscreen([])', 'E745:') - call assert_fails('let a = term_getattr(1, [])', 'E730:') - call assert_fails('let c = term_getcursor([])', 'E745:') - call assert_fails('let l = term_getline([], 1)', 'E745:') - call assert_fails('let l = term_getscrolled([])', 'E745:') - call assert_fails('let s = term_getsize([])', 'E745:') - call assert_fails('let s = term_getstatus([])', 'E745:') - call assert_fails('let s = term_scrape([], 1)', 'E745:') - call assert_fails('call term_sendkeys([], "a")', 'E745:') - call assert_fails('call term_setapi([], "")', 'E745:') - call assert_fails('call term_setrestore([], "")', 'E745:') - call assert_fails('call term_setkill([], "")', 'E745:') - if has('gui') || has('termguicolors') - call assert_fails('let p = term_getansicolors([])', 'E745:') - call assert_fails('call term_setansicolors([], [])', 'E745:') - endif -endfunc - -" Test for sending various special keycodes to a terminal -func Test_term_keycode_translation() - CheckRunVimInTerminal - - let buf = RunVimInTerminal('', {}) - call term_sendkeys(buf, ":set nocompatible\") - - let keys = ["\", "\", "\", "\", "\", "\", "\", - \ "\", "\", "\", "\", "\", "\", - \ "\", "\", "\", "\", "\", - \ "\", "\", "\", "\", "\", "\", - \ "\", "\", "\", "\", "\", - \ "\"] - let output = ['', '', '', '', '', '', '', - \ '', '', '', '', '', '', '', - \ '', '', '', '', '', '', - \ '', '', '', '', '', - \ '', '', '', '', ''] - - call term_sendkeys(buf, "i") - for i in range(len(keys)) - call term_sendkeys(buf, "\\" .. keys[i]) - call WaitForAssert({-> assert_equal(output[i], term_getline(buf, 1))}) - endfor - - let keypad_keys = ["\", "\", "\", "\", "\", "\", - \ "\", "\", "\", "\", "\", "\", - \ "\", "\", "\"] - let keypad_output = ['0', '1', '2', '3', '4', '5', - \ '6', '7', '8', '9', '.', '+', - \ '-', '*', '/'] - for i in range(len(keypad_keys)) - " TODO: Mysteriously keypad 3 and 9 do not work on some systems. - if keypad_output[i] == '3' || keypad_output[i] == '9' - continue - endif - call term_sendkeys(buf, "\" .. keypad_keys[i]) - call WaitForAssert({-> assert_equal(keypad_output[i], term_getline(buf, 1))}) - endfor - - call feedkeys("\\\one\.two", 'xt') - call WaitForAssert({-> assert_equal('two', term_getline(buf, 1))}) - - call StopVimInTerminal(buf) -endfunc - -" Test for using the mouse in a terminal -func Test_term_mouse() - CheckNotGui - CheckRunVimInTerminal - - let save_mouse = &mouse - let save_term = &term - let save_ttymouse = &ttymouse - let save_clipboard = &clipboard - set mouse=a term=xterm ttymouse=sgr mousetime=200 clipboard= - - let lines =<< trim END - one two three four five - red green yellow red blue - vim emacs sublime nano - END - call writefile(lines, 'Xtest_mouse') - - " Create a terminal window running Vim for the test with mouse enabled - let prev_win = win_getid() - let buf = RunVimInTerminal('Xtest_mouse -n', {}) - call term_sendkeys(buf, ":set nocompatible\") - call term_sendkeys(buf, ":set mouse=a term=xterm ttymouse=sgr\") - call term_sendkeys(buf, ":set clipboard=\") - call term_sendkeys(buf, ":set mousemodel=extend\") - call term_wait(buf) - redraw! - - " Use the mouse to enter the terminal window - call win_gotoid(prev_win) - call feedkeys(MouseLeftClickCode(1, 1), 'x') - call feedkeys(MouseLeftReleaseCode(1, 1), 'x') - call assert_equal(1, getwininfo(win_getid())[0].terminal) - - " Test for click/release - call test_setmouse(2, 5) - call feedkeys("\\", 'xt') - call test_setmouse(3, 8) - call term_sendkeys(buf, "\\") - call term_wait(buf, 50) - call term_sendkeys(buf, ":call writefile([json_encode(getpos('.'))], 'Xbuf')\") - call term_wait(buf, 50) - let pos = json_decode(readfile('Xbuf')[0]) - call assert_equal([3, 8], pos[1:2]) - - " Test for selecting text using mouse - call delete('Xbuf') - call test_setmouse(2, 11) - call term_sendkeys(buf, "\") - call test_setmouse(2, 16) - call term_sendkeys(buf, "\y") - call term_wait(buf, 50) - call term_sendkeys(buf, ":call writefile([@\"], 'Xbuf')\") - call term_wait(buf, 50) - call assert_equal('yellow', readfile('Xbuf')[0]) - - " Test for selecting text using doubleclick - call delete('Xbuf') - call test_setmouse(1, 11) - call term_sendkeys(buf, "\\\") - call test_setmouse(1, 17) - call term_sendkeys(buf, "\y") - call term_wait(buf, 50) - call term_sendkeys(buf, ":call writefile([@\"], 'Xbuf')\") - call term_wait(buf, 50) - call assert_equal('three four', readfile('Xbuf')[0]) - - " Test for selecting a line using triple click - call delete('Xbuf') - call test_setmouse(3, 2) - call term_sendkeys(buf, "\\\\\\y") - call term_wait(buf, 50) - call term_sendkeys(buf, ":call writefile([@\"], 'Xbuf')\") - call term_wait(buf, 50) - call assert_equal("vim emacs sublime nano\n", readfile('Xbuf')[0]) - - " Test for selecting a block using qudraple click - call delete('Xbuf') - call test_setmouse(1, 11) - call term_sendkeys(buf, "\\\\\\\") - call test_setmouse(3, 13) - call term_sendkeys(buf, "\y") - call term_wait(buf, 50) - call term_sendkeys(buf, ":call writefile([@\"], 'Xbuf')\") - call term_wait(buf, 50) - call assert_equal("ree\nyel\nsub", readfile('Xbuf')[0]) - - " Test for extending a selection using right click - call delete('Xbuf') - call test_setmouse(2, 9) - call term_sendkeys(buf, "\\") - call test_setmouse(2, 16) - call term_sendkeys(buf, "\\y") - call term_wait(buf, 50) - call term_sendkeys(buf, ":call writefile([@\"], 'Xbuf')\") - call term_wait(buf, 50) - call assert_equal("n yellow", readfile('Xbuf')[0]) - - " Test for pasting text using middle click - call delete('Xbuf') - call term_sendkeys(buf, ":let @r='bright '\") - call test_setmouse(2, 22) - call term_sendkeys(buf, "\"r\\") - call term_wait(buf, 50) - call term_sendkeys(buf, ":call writefile([getline(2)], 'Xbuf')\") - call term_wait(buf, 50) - call assert_equal("red bright blue", readfile('Xbuf')[0][-15:]) - - " cleanup - call term_wait(buf) - call StopVimInTerminal(buf) - let &mouse = save_mouse - let &term = save_term - let &ttymouse = save_ttymouse - let &clipboard = save_clipboard - set mousetime& - call delete('Xtest_mouse') - call delete('Xbuf') -endfunc - -" Test for modeless selection in a terminal -func Test_term_modeless_selection() - CheckUnix - CheckNotGui - CheckRunVimInTerminal - CheckFeature clipboard_working - - let save_mouse = &mouse - let save_term = &term - let save_ttymouse = &ttymouse - set mouse=a term=xterm ttymouse=sgr mousetime=200 - set clipboard=autoselectml - - let lines =<< trim END - one two three four five - red green yellow red blue - vim emacs sublime nano - END - call writefile(lines, 'Xtest_modeless') - - " Create a terminal window running Vim for the test with mouse disabled - let prev_win = win_getid() - let buf = RunVimInTerminal('Xtest_modeless -n', {}) - call term_sendkeys(buf, ":set nocompatible\") - call term_sendkeys(buf, ":set mouse=\") - call term_wait(buf) - redraw! - - " Use the mouse to enter the terminal window - call win_gotoid(prev_win) - call feedkeys(MouseLeftClickCode(1, 1), 'x') - call feedkeys(MouseLeftReleaseCode(1, 1), 'x') - call term_wait(buf) - call assert_equal(1, getwininfo(win_getid())[0].terminal) - - " Test for copying a modeless selection to clipboard - let @* = 'clean' - " communicating with X server may take a little time - sleep 100m - call feedkeys(MouseLeftClickCode(2, 3), 'x') - call feedkeys(MouseLeftDragCode(2, 11), 'x') - call feedkeys(MouseLeftReleaseCode(2, 11), 'x') - call assert_equal("d green y", @*) - - " cleanup - call term_wait(buf) - call StopVimInTerminal(buf) - let &mouse = save_mouse - let &term = save_term - let &ttymouse = save_ttymouse - set mousetime& clipboard& - call delete('Xtest_modeless') - new | only! -endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_terminal2.vim b/src/testdir/test_terminal2.vim new file mode 100644 index 0000000000..bd8995793d --- /dev/null +++ b/src/testdir/test_terminal2.vim @@ -0,0 +1,1550 @@ +" Tests for the terminal window. +" This is split in two, because it can take a lot of time. +" See test_terminal2.vim for further tests. + +source check.vim +CheckFeature terminal + +source shared.vim +source screendump.vim +source mouse.vim +source term_util.vim + +let s:python = PythonProg() +let $PROMPT_COMMAND='' + +func Api_drop_common(options) + call assert_equal(1, winnr('$')) + + " Use the title termcap entries to output the escape sequence. + call writefile([ + \ 'set title', + \ 'exe "set t_ts=\]51; t_fs=\x07"', + \ 'let &titlestring = ''["drop","Xtextfile"' . a:options . ']''', + \ 'redraw', + \ "set t_ts=", + \ ], 'Xscript') + let buf = RunVimInTerminal('-S Xscript', {}) + call WaitFor({-> bufnr('Xtextfile') > 0}) + call assert_equal('Xtextfile', expand('%:t')) + call assert_true(winnr('$') >= 3) + return buf +endfunc + +func Test_terminal_api_drop_newwin() + CheckRunVimInTerminal + let buf = Api_drop_common('') + call assert_equal(0, &bin) + call assert_equal('', &fenc) + + call StopVimInTerminal(buf) + call delete('Xscript') + bwipe Xtextfile +endfunc + +func Test_terminal_api_drop_newwin_bin() + CheckRunVimInTerminal + let buf = Api_drop_common(',{"bin":1}') + call assert_equal(1, &bin) + + call StopVimInTerminal(buf) + call delete('Xscript') + bwipe Xtextfile +endfunc + +func Test_terminal_api_drop_newwin_binary() + CheckRunVimInTerminal + let buf = Api_drop_common(',{"binary":1}') + call assert_equal(1, &bin) + + call StopVimInTerminal(buf) + call delete('Xscript') + bwipe Xtextfile +endfunc + +func Test_terminal_api_drop_newwin_nobin() + CheckRunVimInTerminal + set binary + let buf = Api_drop_common(',{"nobin":1}') + call assert_equal(0, &bin) + + call StopVimInTerminal(buf) + call delete('Xscript') + bwipe Xtextfile + set nobinary +endfunc + +func Test_terminal_api_drop_newwin_nobinary() + CheckRunVimInTerminal + set binary + let buf = Api_drop_common(',{"nobinary":1}') + call assert_equal(0, &bin) + + call StopVimInTerminal(buf) + call delete('Xscript') + bwipe Xtextfile + set nobinary +endfunc + +func Test_terminal_api_drop_newwin_ff() + CheckRunVimInTerminal + let buf = Api_drop_common(',{"ff":"dos"}') + call assert_equal("dos", &ff) + + call StopVimInTerminal(buf) + call delete('Xscript') + bwipe Xtextfile +endfunc + +func Test_terminal_api_drop_newwin_fileformat() + CheckRunVimInTerminal + let buf = Api_drop_common(',{"fileformat":"dos"}') + call assert_equal("dos", &ff) + + call StopVimInTerminal(buf) + call delete('Xscript') + bwipe Xtextfile +endfunc + +func Test_terminal_api_drop_newwin_enc() + CheckRunVimInTerminal + let buf = Api_drop_common(',{"enc":"utf-16"}') + call assert_equal("utf-16", &fenc) + + call StopVimInTerminal(buf) + call delete('Xscript') + bwipe Xtextfile +endfunc + +func Test_terminal_api_drop_newwin_encoding() + CheckRunVimInTerminal + let buf = Api_drop_common(',{"encoding":"utf-16"}') + call assert_equal("utf-16", &fenc) + + call StopVimInTerminal(buf) + call delete('Xscript') + bwipe Xtextfile +endfunc + +func Test_terminal_api_drop_oldwin() + CheckRunVimInTerminal + let firstwinid = win_getid() + split Xtextfile + let textfile_winid = win_getid() + call assert_equal(2, winnr('$')) + call win_gotoid(firstwinid) + + " Use the title termcap entries to output the escape sequence. + call writefile([ + \ 'set title', + \ 'exe "set t_ts=\]51; t_fs=\x07"', + \ 'let &titlestring = ''["drop","Xtextfile"]''', + \ 'redraw', + \ "set t_ts=", + \ ], 'Xscript') + let buf = RunVimInTerminal('-S Xscript', {'rows': 10}) + call WaitForAssert({-> assert_equal('Xtextfile', expand('%:t'))}) + call assert_equal(textfile_winid, win_getid()) + + call StopVimInTerminal(buf) + call delete('Xscript') + bwipe Xtextfile +endfunc + +func Tapi_TryThis(bufnum, arg) + let g:called_bufnum = a:bufnum + let g:called_arg = a:arg +endfunc + +func WriteApiCall(funcname) + " Use the title termcap entries to output the escape sequence. + call writefile([ + \ 'set title', + \ 'exe "set t_ts=\]51; t_fs=\x07"', + \ 'let &titlestring = ''["call","' . a:funcname . '",["hello",123]]''', + \ 'redraw', + \ "set t_ts=", + \ ], 'Xscript') +endfunc + +func Test_terminal_api_call() + CheckRunVimInTerminal + + unlet! g:called_bufnum + unlet! g:called_arg + + call WriteApiCall('Tapi_TryThis') + + " Default + let buf = RunVimInTerminal('-S Xscript', {}) + call WaitFor({-> exists('g:called_bufnum')}) + call assert_equal(buf, g:called_bufnum) + call assert_equal(['hello', 123], g:called_arg) + call StopVimInTerminal(buf) + + unlet! g:called_bufnum + unlet! g:called_arg + + " Enable explicitly + let buf = RunVimInTerminal('-S Xscript', {'term_api': 'Tapi_Try'}) + call WaitFor({-> exists('g:called_bufnum')}) + call assert_equal(buf, g:called_bufnum) + call assert_equal(['hello', 123], g:called_arg) + call StopVimInTerminal(buf) + + unlet! g:called_bufnum + unlet! g:called_arg + + func! ApiCall_TryThis(bufnum, arg) + let g:called_bufnum2 = a:bufnum + let g:called_arg2 = a:arg + endfunc + + call WriteApiCall('ApiCall_TryThis') + + " Use prefix match + let buf = RunVimInTerminal('-S Xscript', {'term_api': 'ApiCall_'}) + call WaitFor({-> exists('g:called_bufnum2')}) + call assert_equal(buf, g:called_bufnum2) + call assert_equal(['hello', 123], g:called_arg2) + call StopVimInTerminal(buf) + + call assert_fails("call term_start('ls', {'term_api' : []})", 'E475:') + + unlet! g:called_bufnum2 + unlet! g:called_arg2 + + call delete('Xscript') + delfunction! ApiCall_TryThis + unlet! g:called_bufnum2 + unlet! g:called_arg2 +endfunc + +func Test_terminal_api_call_fails() + CheckRunVimInTerminal + + func! TryThis(bufnum, arg) + let g:called_bufnum3 = a:bufnum + let g:called_arg3 = a:arg + endfunc + + call WriteApiCall('TryThis') + + unlet! g:called_bufnum3 + unlet! g:called_arg3 + + " Not permitted + call ch_logfile('Xlog', 'w') + let buf = RunVimInTerminal('-S Xscript', {'term_api': ''}) + call WaitForAssert({-> assert_match('Unpermitted function: TryThis', string(readfile('Xlog')))}) + call assert_false(exists('g:called_bufnum3')) + call assert_false(exists('g:called_arg3')) + call StopVimInTerminal(buf) + + " No match + call ch_logfile('Xlog', 'w') + let buf = RunVimInTerminal('-S Xscript', {'term_api': 'TryThat'}) + call WaitFor({-> string(readfile('Xlog')) =~ 'Unpermitted function: TryThis'}) + call assert_false(exists('g:called_bufnum3')) + call assert_false(exists('g:called_arg3')) + call StopVimInTerminal(buf) + + call delete('Xscript') + call ch_logfile('') + call delete('Xlog') + delfunction! TryThis + unlet! g:called_bufnum3 + unlet! g:called_arg3 +endfunc + +let s:caught_e937 = 0 + +func Tapi_Delete(bufnum, arg) + try + execute 'bdelete!' a:bufnum + catch /E937:/ + let s:caught_e937 = 1 + endtry +endfunc + +func Test_terminal_api_call_fail_delete() + CheckRunVimInTerminal + + call WriteApiCall('Tapi_Delete') + let buf = RunVimInTerminal('-S Xscript', {}) + call WaitForAssert({-> assert_equal(1, s:caught_e937)}) + + call StopVimInTerminal(buf) + call delete('Xscript') + call ch_logfile('', '') +endfunc + +func Test_terminal_ansicolors_default() + CheckFunction term_getansicolors + + let colors = [ + \ '#000000', '#e00000', + \ '#00e000', '#e0e000', + \ '#0000e0', '#e000e0', + \ '#00e0e0', '#e0e0e0', + \ '#808080', '#ff4040', + \ '#40ff40', '#ffff40', + \ '#4040ff', '#ff40ff', + \ '#40ffff', '#ffffff', + \] + + let buf = Run_shell_in_terminal({}) + call assert_equal(colors, term_getansicolors(buf)) + call StopShellInTerminal(buf) + call TermWait(buf) + call assert_equal([], term_getansicolors(buf)) + + exe buf . 'bwipe' +endfunc + +let s:test_colors = [ + \ '#616e64', '#0d0a79', + \ '#6d610d', '#0a7373', + \ '#690d0a', '#6d696e', + \ '#0d0a6f', '#616e0d', + \ '#0a6479', '#6d0d0a', + \ '#617373', '#0d0a69', + \ '#6d690d', '#0a6e6f', + \ '#610d0a', '#6e6479', + \] + +func Test_terminal_ansicolors_global() + CheckFeature termguicolors + CheckFunction term_getansicolors + + let g:terminal_ansi_colors = reverse(copy(s:test_colors)) + let buf = Run_shell_in_terminal({}) + call assert_equal(g:terminal_ansi_colors, term_getansicolors(buf)) + call StopShellInTerminal(buf) + call TermWait(buf) + + exe buf . 'bwipe' + unlet g:terminal_ansi_colors +endfunc + +func Test_terminal_ansicolors_func() + CheckFeature termguicolors + CheckFunction term_getansicolors + + let g:terminal_ansi_colors = reverse(copy(s:test_colors)) + let buf = Run_shell_in_terminal({'ansi_colors': s:test_colors}) + call assert_equal(s:test_colors, term_getansicolors(buf)) + + call term_setansicolors(buf, g:terminal_ansi_colors) + call assert_equal(g:terminal_ansi_colors, buf->term_getansicolors()) + + let colors = [ + \ 'ivory', 'AliceBlue', + \ 'grey67', 'dark goldenrod', + \ 'SteelBlue3', 'PaleVioletRed4', + \ 'MediumPurple2', 'yellow2', + \ 'RosyBrown3', 'OrangeRed2', + \ 'white smoke', 'navy blue', + \ 'grey47', 'gray97', + \ 'MistyRose2', 'DodgerBlue4', + \] + eval buf->term_setansicolors(colors) + + let colors[4] = 'Invalid' + call assert_fails('call term_setansicolors(buf, colors)', 'E474:') + call assert_fails('call term_setansicolors(buf, {})', 'E714:') + + call StopShellInTerminal(buf) + call TermWait(buf) + call assert_equal(0, term_setansicolors(buf, [])) + exe buf . 'bwipe' +endfunc + +func Test_terminal_all_ansi_colors() + CheckRunVimInTerminal + + " Use all the ANSI colors. + call writefile([ + \ 'call setline(1, "AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPP XXYYZZ")', + \ 'hi Tblack ctermfg=0 ctermbg=8', + \ 'hi Tdarkred ctermfg=1 ctermbg=9', + \ 'hi Tdarkgreen ctermfg=2 ctermbg=10', + \ 'hi Tbrown ctermfg=3 ctermbg=11', + \ 'hi Tdarkblue ctermfg=4 ctermbg=12', + \ 'hi Tdarkmagenta ctermfg=5 ctermbg=13', + \ 'hi Tdarkcyan ctermfg=6 ctermbg=14', + \ 'hi Tlightgrey ctermfg=7 ctermbg=15', + \ 'hi Tdarkgrey ctermfg=8 ctermbg=0', + \ 'hi Tred ctermfg=9 ctermbg=1', + \ 'hi Tgreen ctermfg=10 ctermbg=2', + \ 'hi Tyellow ctermfg=11 ctermbg=3', + \ 'hi Tblue ctermfg=12 ctermbg=4', + \ 'hi Tmagenta ctermfg=13 ctermbg=5', + \ 'hi Tcyan ctermfg=14 ctermbg=6', + \ 'hi Twhite ctermfg=15 ctermbg=7', + \ 'hi TdarkredBold ctermfg=1 cterm=bold', + \ 'hi TgreenBold ctermfg=10 cterm=bold', + \ 'hi TmagentaBold ctermfg=13 cterm=bold ctermbg=5', + \ '', + \ 'call matchadd("Tblack", "A")', + \ 'call matchadd("Tdarkred", "B")', + \ 'call matchadd("Tdarkgreen", "C")', + \ 'call matchadd("Tbrown", "D")', + \ 'call matchadd("Tdarkblue", "E")', + \ 'call matchadd("Tdarkmagenta", "F")', + \ 'call matchadd("Tdarkcyan", "G")', + \ 'call matchadd("Tlightgrey", "H")', + \ 'call matchadd("Tdarkgrey", "I")', + \ 'call matchadd("Tred", "J")', + \ 'call matchadd("Tgreen", "K")', + \ 'call matchadd("Tyellow", "L")', + \ 'call matchadd("Tblue", "M")', + \ 'call matchadd("Tmagenta", "N")', + \ 'call matchadd("Tcyan", "O")', + \ 'call matchadd("Twhite", "P")', + \ 'call matchadd("TdarkredBold", "X")', + \ 'call matchadd("TgreenBold", "Y")', + \ 'call matchadd("TmagentaBold", "Z")', + \ 'redraw', + \ ], 'Xcolorscript') + let buf = RunVimInTerminal('-S Xcolorscript', {'rows': 10}) + call VerifyScreenDump(buf, 'Test_terminal_all_ansi_colors', {}) + + call term_sendkeys(buf, ":q\") + call StopVimInTerminal(buf) + call delete('Xcolorscript') +endfunc + +func Test_terminal_termwinsize_option_fixed() + CheckRunVimInTerminal + set termwinsize=6x40 + let text = [] + for n in range(10) + call add(text, repeat(n, 50)) + endfor + call writefile(text, 'Xwinsize') + let buf = RunVimInTerminal('Xwinsize', {}) + let win = bufwinid(buf) + call assert_equal([6, 40], term_getsize(buf)) + call assert_equal(6, winheight(win)) + call assert_equal(40, winwidth(win)) + + " resizing the window doesn't resize the terminal. + resize 10 + vertical resize 60 + call assert_equal([6, 40], term_getsize(buf)) + call assert_equal(10, winheight(win)) + call assert_equal(60, winwidth(win)) + + call StopVimInTerminal(buf) + call delete('Xwinsize') + + call assert_fails('set termwinsize=40', 'E474') + call assert_fails('set termwinsize=10+40', 'E474') + call assert_fails('set termwinsize=abc', 'E474') + + set termwinsize= +endfunc + +func Test_terminal_termwinsize_option_zero() + set termwinsize=0x0 + let buf = Run_shell_in_terminal({}) + let win = bufwinid(buf) + call assert_equal([winheight(win), winwidth(win)], term_getsize(buf)) + call StopShellInTerminal(buf) + call TermWait(buf) + exe buf . 'bwipe' + + set termwinsize=7x0 + let buf = Run_shell_in_terminal({}) + let win = bufwinid(buf) + call assert_equal([7, winwidth(win)], term_getsize(buf)) + call StopShellInTerminal(buf) + call TermWait(buf) + exe buf . 'bwipe' + + set termwinsize=0x33 + let buf = Run_shell_in_terminal({}) + let win = bufwinid(buf) + call assert_equal([winheight(win), 33], term_getsize(buf)) + call StopShellInTerminal(buf) + call TermWait(buf) + exe buf . 'bwipe' + + set termwinsize= +endfunc + +func Test_terminal_termwinsize_minimum() + set termwinsize=10*50 + vsplit + let buf = Run_shell_in_terminal({}) + let win = bufwinid(buf) + call assert_inrange(10, 1000, winheight(win)) + call assert_inrange(50, 1000, winwidth(win)) + call assert_equal([winheight(win), winwidth(win)], term_getsize(buf)) + + resize 15 + vertical resize 60 + redraw + call assert_equal([15, 60], term_getsize(buf)) + call assert_equal(15, winheight(win)) + call assert_equal(60, winwidth(win)) + + resize 7 + vertical resize 30 + redraw + call assert_equal([10, 50], term_getsize(buf)) + call assert_equal(7, winheight(win)) + call assert_equal(30, winwidth(win)) + + call StopShellInTerminal(buf) + call TermWait(buf) + exe buf . 'bwipe' + + set termwinsize=0*0 + let buf = Run_shell_in_terminal({}) + let win = bufwinid(buf) + call assert_equal([winheight(win), winwidth(win)], term_getsize(buf)) + call StopShellInTerminal(buf) + call TermWait(buf) + exe buf . 'bwipe' + + set termwinsize= +endfunc + +func Test_terminal_termwinkey() + " make three tabpages, terminal in the middle + 0tabnew + tabnext + tabnew + tabprev + call assert_equal(1, winnr('$')) + call assert_equal(2, tabpagenr()) + let thiswin = win_getid() + + let buf = Run_shell_in_terminal({}) + let termwin = bufwinid(buf) + set termwinkey= + call feedkeys("\w", 'tx') + call assert_equal(thiswin, win_getid()) + call feedkeys("\w", 'tx') + call assert_equal(termwin, win_getid()) + + if has('langmap') + set langmap=xjyk + call feedkeys("\x", 'tx') + call assert_equal(thiswin, win_getid()) + call feedkeys("\y", 'tx') + call assert_equal(termwin, win_getid()) + set langmap= + endif + + call feedkeys("\gt", "xt") + call assert_equal(3, tabpagenr()) + tabprev + call assert_equal(2, tabpagenr()) + call assert_equal(termwin, win_getid()) + + call feedkeys("\gT", "xt") + call assert_equal(1, tabpagenr()) + tabnext + call assert_equal(2, tabpagenr()) + call assert_equal(termwin, win_getid()) + + let job = term_getjob(buf) + call feedkeys("\\", 'tx') + call WaitForAssert({-> assert_equal("dead", job_status(job))}) + + set termwinkey& + tabnext + tabclose + tabprev + tabclose +endfunc + +func Test_terminal_out_err() + CheckUnix + + call writefile([ + \ '#!/bin/sh', + \ 'echo "this is standard error" >&2', + \ 'echo "this is standard out" >&1', + \ ], 'Xechoerrout.sh') + call setfperm('Xechoerrout.sh', 'rwxrwx---') + + let outfile = 'Xtermstdout' + let buf = term_start(['./Xechoerrout.sh'], {'out_io': 'file', 'out_name': outfile}) + + call WaitFor({-> !empty(readfile(outfile)) && !empty(term_getline(buf, 1))}) + call assert_equal(['this is standard out'], readfile(outfile)) + call assert_equal('this is standard error', term_getline(buf, 1)) + + call WaitForAssert({-> assert_equal('dead', job_status(term_getjob(buf)))}) + exe buf . 'bwipe' + call delete('Xechoerrout.sh') + call delete(outfile) +endfunc + +func Test_termwinscroll() + CheckUnix + + " Let the terminal output more than 'termwinscroll' lines, some at the start + " will be dropped. + exe 'set termwinscroll=' . &lines + let buf = term_start('/bin/sh') + for i in range(1, &lines) + call feedkeys("echo " . i . "\", 'xt') + call WaitForAssert({-> assert_match(string(i), term_getline(buf, term_getcursor(buf)[0] - 1))}) + endfor + " Go to Terminal-Normal mode to update the buffer. + call feedkeys("\N", 'xt') + call assert_inrange(&lines, &lines * 110 / 100 + winheight(0), line('$')) + + " Every "echo nr" must only appear once + let lines = getline(1, line('$')) + for i in range(&lines - len(lines) / 2 + 2, &lines) + let filtered = filter(copy(lines), {idx, val -> val =~ 'echo ' . i . '\>'}) + call assert_equal(1, len(filtered), 'for "echo ' . i . '"') + endfor + + exe buf . 'bwipe!' +endfunc + +" Resizing the terminal window caused an ml_get error. +" TODO: This does not reproduce the original problem. +func Test_terminal_resize() + set statusline=x + terminal + call assert_equal(2, winnr('$')) + + " Fill the terminal with text. + if has('win32') + call feedkeys("dir\", 'xt') + else + call feedkeys("ls\", 'xt') + endif + " Go to Terminal-Normal mode for a moment. + call feedkeys("\N", 'xt') + " Open a new window + call feedkeys("i\n", 'xt') + call assert_equal(3, winnr('$')) + redraw + + close + call assert_equal(2, winnr('$')) + call feedkeys("exit\", 'xt') + set statusline& +endfunc + +" must be nearly the last, we can't go back from GUI to terminal +func Test_zz1_terminal_in_gui() + CheckCanRunGui + + " Ignore the "failed to create input context" error. + call test_ignore_error('E285:') + + gui -f + + call assert_equal(1, winnr('$')) + let buf = Run_shell_in_terminal({'term_finish': 'close'}) + call StopShellInTerminal(buf) + call TermWait(buf) + + " closing window wipes out the terminal buffer a with finished job + call WaitForAssert({-> assert_equal(1, winnr('$'))}) + call assert_equal("", bufname(buf)) + + unlet g:job +endfunc + +func Test_zz2_terminal_guioptions_bang() + CheckGui + set guioptions+=! + + let filename = 'Xtestscript' + if has('win32') + let filename .= '.bat' + let prefix = '' + let contents = ['@echo off', 'exit %1'] + else + let filename .= '.sh' + let prefix = './' + let contents = ['#!/bin/sh', 'exit $1'] + endif + call writefile(contents, filename) + call setfperm(filename, 'rwxrwx---') + + " Check if v:shell_error is equal to the exit status. + let exitval = 0 + execute printf(':!%s%s %d', prefix, filename, exitval) + call assert_equal(exitval, v:shell_error) + + let exitval = 9 + execute printf(':!%s%s %d', prefix, filename, exitval) + call assert_equal(exitval, v:shell_error) + + set guioptions& + call delete(filename) +endfunc + +func Test_terminal_hidden() + CheckUnix + + term ++hidden cat + let bnr = bufnr('$') + call assert_equal('terminal', getbufvar(bnr, '&buftype')) + exe 'sbuf ' . bnr + call assert_equal('terminal', &buftype) + call term_sendkeys(bnr, "asdf\") + call WaitForAssert({-> assert_match('asdf', term_getline(bnr, 2))}) + call term_sendkeys(bnr, "\") + call WaitForAssert({-> assert_equal('finished', bnr->term_getstatus())}) + bwipe! +endfunc + +func Test_terminal_switch_mode() + term + let bnr = bufnr('$') + call WaitForAssert({-> assert_equal('running', term_getstatus(bnr))}) + call feedkeys("\N", 'xt') + call WaitForAssert({-> assert_equal('running,normal', term_getstatus(bnr))}) + call feedkeys("A", 'xt') + call WaitForAssert({-> assert_equal('running', term_getstatus(bnr))}) + call feedkeys("\\", 'xt') + call WaitForAssert({-> assert_equal('running,normal', term_getstatus(bnr))}) + call feedkeys("I", 'xt') + call WaitForAssert({-> assert_equal('running', term_getstatus(bnr))}) + call feedkeys("\Nv", 'xt') + call WaitForAssert({-> assert_equal('running,normal', term_getstatus(bnr))}) + call feedkeys("I", 'xt') + call WaitForAssert({-> assert_equal('running', term_getstatus(bnr))}) + call feedkeys("\Nv", 'xt') + call WaitForAssert({-> assert_equal('running,normal', term_getstatus(bnr))}) + call feedkeys("A", 'xt') + call WaitForAssert({-> assert_equal('running', term_getstatus(bnr))}) + bwipe! +endfunc + +func Test_terminal_normal_mode() + CheckRunVimInTerminal + + " Run Vim in a terminal and open a terminal window to run Vim in. + let lines =<< trim END + call setline(1, range(11111, 11122)) + 3 + END + call writefile(lines, 'XtermNormal') + let buf = RunVimInTerminal('-S XtermNormal', {'rows': 8}) + call TermWait(buf) + + call term_sendkeys(buf, "\N") + call term_sendkeys(buf, ":set number cursorline culopt=both\r") + call VerifyScreenDump(buf, 'Test_terminal_normal_1', {}) + + call term_sendkeys(buf, ":set culopt=number\r") + call VerifyScreenDump(buf, 'Test_terminal_normal_2', {}) + + call term_sendkeys(buf, ":set culopt=line\r") + call VerifyScreenDump(buf, 'Test_terminal_normal_3', {}) + + call assert_fails('call term_sendkeys(buf, [])', 'E730:') + call term_sendkeys(buf, "a:q!\:q\:q\") + call StopVimInTerminal(buf) + call delete('XtermNormal') +endfunc + +func Test_terminal_hidden_and_close() + CheckUnix + + call assert_equal(1, winnr('$')) + term ++hidden ++close ls + let bnr = bufnr('$') + call assert_equal('terminal', getbufvar(bnr, '&buftype')) + call WaitForAssert({-> assert_false(bufexists(bnr))}) + call assert_equal(1, winnr('$')) +endfunc + +func Test_terminal_does_not_truncate_last_newlines() + " This test does not pass through ConPTY. + if has('conpty') + return + endif + let contents = [ + \ [ 'One', '', 'X' ], + \ [ 'Two', '', '' ], + \ [ 'Three' ] + repeat([''], 30) + \ ] + + for c in contents + call writefile(c, 'Xfile') + if has('win32') + term cmd /c type Xfile + else + term cat Xfile + endif + let bnr = bufnr('$') + call assert_equal('terminal', getbufvar(bnr, '&buftype')) + call WaitForAssert({-> assert_equal('finished', term_getstatus(bnr))}) + sleep 100m + call assert_equal(c, getline(1, line('$'))) + quit + endfor + + call delete('Xfile') +endfunc + +func Test_terminal_no_job() + if has('win32') + let cmd = 'cmd /c ""' + else + CheckExecutable false + let cmd = 'false' + endif + let term = term_start(cmd, {'term_finish': 'close'}) + call WaitForAssert({-> assert_equal(v:null, term_getjob(term)) }) +endfunc + +func Test_term_getcursor() + CheckUnix + + let buf = Run_shell_in_terminal({}) + + " Wait for the shell to display a prompt. + call WaitForAssert({-> assert_notequal('', term_getline(buf, 1))}) + + " Hide the cursor. + call term_sendkeys(buf, "echo -e '\\033[?25l'\r") + call WaitForAssert({-> assert_equal(0, term_getcursor(buf)[2].visible)}) + + " Show the cursor. + call term_sendkeys(buf, "echo -e '\\033[?25h'\r") + call WaitForAssert({-> assert_equal(1, buf->term_getcursor()[2].visible)}) + + " Change color of cursor. + call WaitForAssert({-> assert_equal('', term_getcursor(buf)[2].color)}) + call term_sendkeys(buf, "echo -e '\\033]12;blue\\007'\r") + call WaitForAssert({-> assert_equal('blue', term_getcursor(buf)[2].color)}) + call term_sendkeys(buf, "echo -e '\\033]12;green\\007'\r") + call WaitForAssert({-> assert_equal('green', term_getcursor(buf)[2].color)}) + + " Make cursor a blinking block. + call term_sendkeys(buf, "echo -e '\\033[1 q'\r") + call WaitForAssert({-> assert_equal([1, 1], + \ [term_getcursor(buf)[2].blink, term_getcursor(buf)[2].shape])}) + + " Make cursor a steady block. + call term_sendkeys(buf, "echo -e '\\033[2 q'\r") + call WaitForAssert({-> assert_equal([0, 1], + \ [term_getcursor(buf)[2].blink, term_getcursor(buf)[2].shape])}) + + " Make cursor a blinking underline. + call term_sendkeys(buf, "echo -e '\\033[3 q'\r") + call WaitForAssert({-> assert_equal([1, 2], + \ [term_getcursor(buf)[2].blink, term_getcursor(buf)[2].shape])}) + + " Make cursor a steady underline. + call term_sendkeys(buf, "echo -e '\\033[4 q'\r") + call WaitForAssert({-> assert_equal([0, 2], + \ [term_getcursor(buf)[2].blink, term_getcursor(buf)[2].shape])}) + + " Make cursor a blinking vertical bar. + call term_sendkeys(buf, "echo -e '\\033[5 q'\r") + call WaitForAssert({-> assert_equal([1, 3], + \ [term_getcursor(buf)[2].blink, term_getcursor(buf)[2].shape])}) + + " Make cursor a steady vertical bar. + call term_sendkeys(buf, "echo -e '\\033[6 q'\r") + call WaitForAssert({-> assert_equal([0, 3], + \ [term_getcursor(buf)[2].blink, term_getcursor(buf)[2].shape])}) + + call StopShellInTerminal(buf) +endfunc + +" Test for term_gettitle() +func Test_term_gettitle() + " term_gettitle() returns an empty string for a non-terminal buffer + " and for a non-existing buffer. + call assert_equal('', bufnr('%')->term_gettitle()) + call assert_equal('', term_gettitle(bufnr('$') + 1)) + + if !has('title') || empty(&t_ts) + throw "Skipped: can't get/set title" + endif + + let term = term_start([GetVimProg(), '--clean', '-c', 'set noswapfile', '-c', 'set title']) + if has('autoservername') + call WaitForAssert({-> assert_match('^\[No Name\] - VIM\d\+$', term_gettitle(term)) }) + call term_sendkeys(term, ":e Xfoo\r") + call WaitForAssert({-> assert_match('^Xfoo (.*[/\\]testdir) - VIM\d\+$', term_gettitle(term)) }) + else + call WaitForAssert({-> assert_equal('[No Name] - VIM', term_gettitle(term)) }) + call term_sendkeys(term, ":e Xfoo\r") + call WaitForAssert({-> assert_match('^Xfoo (.*[/\\]testdir) - VIM$', term_gettitle(term)) }) + endif + + call term_sendkeys(term, ":set titlestring=foo\r") + call WaitForAssert({-> assert_equal('foo', term_gettitle(term)) }) + + exe term . 'bwipe!' +endfunc + +func Test_term_gettty() + let buf = Run_shell_in_terminal({}) + let gettty = term_gettty(buf) + + if has('unix') && executable('tty') + " Find tty using the tty shell command. + call WaitForAssert({-> assert_notequal('', term_getline(buf, 1))}) + call term_sendkeys(buf, "tty\r") + call WaitForAssert({-> assert_notequal('', term_getline(buf, 3))}) + let tty = term_getline(buf, 2) + call assert_equal(tty, gettty) + endif + + let gettty0 = term_gettty(buf, 0) + let gettty1 = term_gettty(buf, 1) + + call assert_equal(gettty, gettty0) + call assert_equal(job_info(g:job).tty_out, gettty0) + call assert_equal(job_info(g:job).tty_in, gettty1) + + if has('unix') + " For unix, term_gettty(..., 0) and term_gettty(..., 1) + " are identical according to :help term_gettty() + call assert_equal(gettty0, gettty1) + call assert_match('^/dev/', gettty) + else + " ConPTY works on anonymous pipe. + if !has('conpty') + call assert_match('^\\\\.\\pipe\\', gettty0) + call assert_match('^\\\\.\\pipe\\', gettty1) + endif + endif + + call assert_fails('call term_gettty(buf, 2)', 'E475:') + call assert_fails('call term_gettty(buf, -1)', 'E475:') + + call assert_equal('', term_gettty(buf + 1)) + + call StopShellInTerminal(buf) + call TermWait(buf) + exe buf . 'bwipe' +endfunc + +func Test_terminal_getwinpos() + CheckRunVimInTerminal + + " split, go to the bottom-right window + split + wincmd j + set splitright + + call writefile([ + \ 'echo getwinpos()', + \ ], 'XTest_getwinpos') + let buf = RunVimInTerminal('-S XTest_getwinpos', {'cols': 60}) + call TermWait(buf) + + " Find the output of getwinpos() in the bottom line. + let rows = term_getsize(buf)[0] + call WaitForAssert({-> assert_match('\[\d\+, \d\+\]', term_getline(buf, rows))}) + let line = term_getline(buf, rows) + let xpos = str2nr(substitute(line, '\[\(\d\+\), \d\+\]', '\1', '')) + let ypos = str2nr(substitute(line, '\[\d\+, \(\d\+\)\]', '\1', '')) + + " Position must be bigger than the getwinpos() result of Vim itself. + " The calculation in the console assumes a 10 x 7 character cell. + " In the GUI it can be more, let's assume a 20 x 14 cell. + " And then add 100 / 200 tolerance. + let [xroot, yroot] = getwinpos() + let winpos = 50->getwinpos() + call assert_equal(xroot, winpos[0]) + call assert_equal(yroot, winpos[1]) + let [winrow, wincol] = win_screenpos('.') + let xoff = wincol * (has('gui_running') ? 14 : 7) + 100 + let yoff = winrow * (has('gui_running') ? 20 : 10) + 200 + call assert_inrange(xroot + 2, xroot + xoff, xpos) + call assert_inrange(yroot + 2, yroot + yoff, ypos) + + call TermWait(buf) + call term_sendkeys(buf, ":q\") + call StopVimInTerminal(buf) + call delete('XTest_getwinpos') + exe buf . 'bwipe!' + set splitright& + only! +endfunc + +func Test_terminal_altscreen() + " somehow doesn't work on MS-Windows + CheckUnix + let cmd = "cat Xtext\" + + let buf = term_start(&shell, {}) + call writefile(["\[?1047h"], 'Xtext') + call term_sendkeys(buf, cmd) + call WaitForAssert({-> assert_equal(1, term_getaltscreen(buf))}) + + call writefile(["\[?1047l"], 'Xtext') + call term_sendkeys(buf, cmd) + call WaitForAssert({-> assert_equal(0, term_getaltscreen(buf))}) + + call term_sendkeys(buf, "exit\r") + exe buf . "bwipe!" + call delete('Xtext') +endfunc + +func Test_terminal_shell_option() + if has('unix') + " exec is a shell builtin command, should fail without a shell. + term exec ls runtest.vim + call WaitForAssert({-> assert_match('job failed', term_getline(bufnr(), 1))}) + bwipe! + + term ++shell exec ls runtest.vim + call WaitForAssert({-> assert_match('runtest.vim', term_getline(bufnr(), 1))}) + bwipe! + elseif has('win32') + " dir is a shell builtin command, should fail without a shell. + try + term dir /b runtest.vim + call WaitForAssert({-> assert_match('job failed\|cannot access .*: No such file or directory', term_getline(bufnr(), 1))}) + catch /CreateProcess/ + " ignore + endtry + bwipe! + + term ++shell dir /b runtest.vim + call WaitForAssert({-> assert_match('runtest.vim', term_getline(bufnr(), 1))}) + bwipe! + endif +endfunc + +func Test_terminal_setapi_and_call() + CheckRunVimInTerminal + + call WriteApiCall('Tapi_TryThis') + call ch_logfile('Xlog', 'w') + + unlet! g:called_bufnum + unlet! g:called_arg + + let buf = RunVimInTerminal('-S Xscript', {'term_api': ''}) + call WaitForAssert({-> assert_match('Unpermitted function: Tapi_TryThis', string(readfile('Xlog')))}) + call assert_false(exists('g:called_bufnum')) + call assert_false(exists('g:called_arg')) + + eval buf->term_setapi('Tapi_') + call term_sendkeys(buf, ":set notitle\") + call term_sendkeys(buf, ":source Xscript\") + call WaitFor({-> exists('g:called_bufnum')}) + call assert_equal(buf, g:called_bufnum) + call assert_equal(['hello', 123], g:called_arg) + + call StopVimInTerminal(buf) + + call delete('Xscript') + call ch_logfile('') + call delete('Xlog') + unlet! g:called_bufnum + unlet! g:called_arg +endfunc + +func Test_terminal_api_arg() + CheckRunVimInTerminal + + call WriteApiCall('Tapi_TryThis') + call ch_logfile('Xlog', 'w') + + unlet! g:called_bufnum + unlet! g:called_arg + + execute 'term ++api= ' .. GetVimCommandCleanTerm() .. '-S Xscript' + let buf = bufnr('%') + call WaitForAssert({-> assert_match('Unpermitted function: Tapi_TryThis', string(readfile('Xlog')))}) + call assert_false(exists('g:called_bufnum')) + call assert_false(exists('g:called_arg')) + + call StopVimInTerminal(buf) + + call ch_logfile('Xlog', 'w') + + execute 'term ++api=Tapi_ ' .. GetVimCommandCleanTerm() .. '-S Xscript' + let buf = bufnr('%') + call WaitFor({-> exists('g:called_bufnum')}) + call assert_equal(buf, g:called_bufnum) + call assert_equal(['hello', 123], g:called_arg) + + call StopVimInTerminal(buf) + + call delete('Xscript') + call ch_logfile('') + call delete('Xlog') + unlet! g:called_bufnum + unlet! g:called_arg +endfunc + +func Test_terminal_invalid_arg() + call assert_fails('terminal ++xyz', 'E181:') +endfunc + +func Test_terminal_in_popup() + CheckRunVimInTerminal + + let text =<< trim END + some text + to edit + in a popup window + END + call writefile(text, 'Xtext') + let cmd = GetVimCommandCleanTerm() + let lines = [ + \ 'call setline(1, range(20))', + \ 'hi PopTerm ctermbg=grey', + \ 'func OpenTerm(setColor)', + \ " set noruler", + \ " let s:buf = term_start('" .. cmd .. " Xtext', #{hidden: 1, term_finish: 'close'})", + \ ' let g:winid = popup_create(s:buf, #{minwidth: 45, minheight: 7, border: [], drag: 1, resize: 1})', + \ ' if a:setColor', + \ ' call win_execute(g:winid, "set wincolor=PopTerm")', + \ ' endif', + \ 'endfunc', + \ 'func HidePopup()', + \ ' call popup_hide(g:winid)', + \ 'endfunc', + \ 'func ClosePopup()', + \ ' call popup_close(g:winid)', + \ 'endfunc', + \ 'func ReopenPopup()', + \ ' call popup_create(s:buf, #{minwidth: 40, minheight: 6, border: []})', + \ 'endfunc', + \ ] + call writefile(lines, 'XtermPopup') + let buf = RunVimInTerminal('-S XtermPopup', #{rows: 15}) + call TermWait(buf, 100) + call term_sendkeys(buf, ":call OpenTerm(0)\") + call TermWait(buf, 100) + call term_sendkeys(buf, ":\") + call TermWait(buf, 100) + call term_sendkeys(buf, "\:echo getwinvar(g:winid, \"&buftype\") win_gettype(g:winid)\") + call VerifyScreenDump(buf, 'Test_terminal_popup_1', {}) + + call term_sendkeys(buf, ":q\") + call VerifyScreenDump(buf, 'Test_terminal_popup_2', {}) + + call term_sendkeys(buf, ":call OpenTerm(1)\") + call TermWait(buf, 150) + call term_sendkeys(buf, ":set hlsearch\") + call TermWait(buf, 100) + call term_sendkeys(buf, "/edit\") + call VerifyScreenDump(buf, 'Test_terminal_popup_3', {}) + + call term_sendkeys(buf, "\:call HidePopup()\") + call VerifyScreenDump(buf, 'Test_terminal_popup_4', {}) + call term_sendkeys(buf, "\") + call TermWait(buf, 50) + + call term_sendkeys(buf, "\:call ClosePopup()\") + call VerifyScreenDump(buf, 'Test_terminal_popup_5', {}) + + call term_sendkeys(buf, "\:call ReopenPopup()\") + call VerifyScreenDump(buf, 'Test_terminal_popup_6', {}) + + " Go to terminal-Normal mode and visually select text. + call term_sendkeys(buf, "\Ngg/in\vww") + call VerifyScreenDump(buf, 'Test_terminal_popup_7', {}) + + " Back to job mode, redraws + call term_sendkeys(buf, "A") + call VerifyScreenDump(buf, 'Test_terminal_popup_8', {}) + + call TermWait(buf, 50) + call term_sendkeys(buf, ":q\") + call TermWait(buf, 150) " wait for terminal to vanish + + call StopVimInTerminal(buf) + call delete('Xtext') + call delete('XtermPopup') +endfunc + +" Check a terminal in popup window uses the default mininum size. +func Test_terminal_in_popup_min_size() + CheckRunVimInTerminal + + let text =<< trim END + another text + to show + in a popup window + END + call writefile(text, 'Xtext') + let lines = [ + \ 'call setline(1, range(20))', + \ 'func OpenTerm()', + \ " let s:buf = term_start('cat Xtext', #{hidden: 1})", + \ ' let g:winid = popup_create(s:buf, #{ border: []})', + \ 'endfunc', + \ ] + call writefile(lines, 'XtermPopup') + let buf = RunVimInTerminal('-S XtermPopup', #{rows: 15}) + call TermWait(buf, 100) + call term_sendkeys(buf, ":set noruler\") + call term_sendkeys(buf, ":call OpenTerm()\") + call TermWait(buf, 50) + call term_sendkeys(buf, ":\") + call VerifyScreenDump(buf, 'Test_terminal_popup_m1', {}) + + call TermWait(buf, 50) + call term_sendkeys(buf, ":q\") + call TermWait(buf, 50) " wait for terminal to vanish + call StopVimInTerminal(buf) + call delete('Xtext') + call delete('XtermPopup') +endfunc + +" Check a terminal in popup window with different colors +func Terminal_in_popup_colored(group_name, highlight_cmd, highlight_opt) + CheckRunVimInTerminal + CheckUnix + + let lines = [ + \ 'call setline(1, range(20))', + \ 'func OpenTerm()', + \ " let s:buf = term_start('cat', #{hidden: 1, " + \ .. a:highlight_opt .. "})", + \ ' let g:winid = popup_create(s:buf, #{ border: []})', + \ 'endfunc', + \ a:highlight_cmd, + \ ] + call writefile(lines, 'XtermPopup') + let buf = RunVimInTerminal('-S XtermPopup', #{rows: 15}) + call TermWait(buf, 100) + call term_sendkeys(buf, ":set noruler\") + call term_sendkeys(buf, ":call OpenTerm()\") + call TermWait(buf, 50) + call term_sendkeys(buf, "hello\") + call VerifyScreenDump(buf, 'Test_terminal_popup_' .. a:group_name, {}) + + call term_sendkeys(buf, "\") + call TermWait(buf, 50) + call term_sendkeys(buf, ":q\") + call TermWait(buf, 50) " wait for terminal to vanish + call StopVimInTerminal(buf) + call delete('XtermPopup') +endfunc + +func Test_terminal_in_popup_colored_Terminal() + call Terminal_in_popup_colored("Terminal", "highlight Terminal ctermfg=blue ctermbg=yellow", "") +endfunc + +func Test_terminal_in_popup_colored_group() + call Terminal_in_popup_colored("MyTermCol", "highlight MyTermCol ctermfg=darkgreen ctermbg=lightblue", "term_highlight: 'MyTermCol',") +endfunc + +func Test_double_popup_terminal() + let buf1 = term_start(&shell, #{hidden: 1}) + let win1 = popup_create(buf1, {}) + let buf2 = term_start(&shell, #{hidden: 1}) + call assert_fails('call popup_create(buf2, {})', 'E861:') + call popup_close(win1) + exe buf1 .. 'bwipe!' + exe buf2 .. 'bwipe!' +endfunc + +func Test_issue_5607() + let wincount = winnr('$') + exe 'terminal' &shell &shellcmdflag 'exit' + let job = term_getjob(bufnr()) + call WaitForAssert({-> assert_equal("dead", job_status(job))}) + + let old_wincolor = &wincolor + try + set wincolor= + finally + let &wincolor = old_wincolor + bw! + endtry +endfunc + +func Test_hidden_terminal() + let buf = term_start(&shell, #{hidden: 1}) + call assert_equal('', bufname('^$')) + call StopShellInTerminal(buf) +endfunc + +func Test_term_nasty_callback() + CheckExecutable sh + + set hidden + let g:buf0 = term_start('sh', #{hidden: 1, term_finish: 'close'}) + call popup_create(g:buf0, {}) + call assert_fails("call term_start(['sh', '-c'], #{curwin: 1})", 'E863:') + + call popup_clear(1) + set hidden& +endfunc + +func Test_term_and_startinsert() + CheckRunVimInTerminal + CheckUnix + + let lines =<< trim EOL + put='some text' + term + startinsert + EOL + call writefile(lines, 'XTest_startinsert') + let buf = RunVimInTerminal('-S XTest_startinsert', {}) + + call term_sendkeys(buf, "exit\r") + call WaitForAssert({-> assert_equal("some text", term_getline(buf, 1))}) + call term_sendkeys(buf, "0l") + call term_sendkeys(buf, "A<\") + call WaitForAssert({-> assert_equal("some text<", term_getline(buf, 1))}) + + call StopVimInTerminal(buf) + call delete('XTest_startinsert') +endfunc + +" Test for passing invalid arguments to terminal functions +func Test_term_func_invalid_arg() + call assert_fails('let b = term_getaltscreen([])', 'E745:') + call assert_fails('let a = term_getattr(1, [])', 'E730:') + call assert_fails('let c = term_getcursor([])', 'E745:') + call assert_fails('let l = term_getline([], 1)', 'E745:') + call assert_fails('let l = term_getscrolled([])', 'E745:') + call assert_fails('let s = term_getsize([])', 'E745:') + call assert_fails('let s = term_getstatus([])', 'E745:') + call assert_fails('let s = term_scrape([], 1)', 'E745:') + call assert_fails('call term_sendkeys([], "a")', 'E745:') + call assert_fails('call term_setapi([], "")', 'E745:') + call assert_fails('call term_setrestore([], "")', 'E745:') + call assert_fails('call term_setkill([], "")', 'E745:') + if has('gui') || has('termguicolors') + call assert_fails('let p = term_getansicolors([])', 'E745:') + call assert_fails('call term_setansicolors([], [])', 'E745:') + endif +endfunc + +" Test for sending various special keycodes to a terminal +func Test_term_keycode_translation() + CheckRunVimInTerminal + + let buf = RunVimInTerminal('', {}) + call term_sendkeys(buf, ":set nocompatible\") + + let keys = ["\", "\", "\", "\", "\", "\", "\", + \ "\", "\", "\", "\", "\", "\", + \ "\", "\", "\", "\", "\", + \ "\", "\", "\", "\", "\", "\", + \ "\", "\", "\", "\", "\", + \ "\"] + let output = ['', '', '', '', '', '', '', + \ '', '', '', '', '', '', '', + \ '', '', '', '', '', '', + \ '', '', '', '', '', + \ '', '', '', '', ''] + + call term_sendkeys(buf, "i") + for i in range(len(keys)) + call term_sendkeys(buf, "\\" .. keys[i]) + call WaitForAssert({-> assert_equal(output[i], term_getline(buf, 1))}) + endfor + + let keypad_keys = ["\", "\", "\", "\", "\", "\", + \ "\", "\", "\", "\", "\", "\", + \ "\", "\", "\"] + let keypad_output = ['0', '1', '2', '3', '4', '5', + \ '6', '7', '8', '9', '.', '+', + \ '-', '*', '/'] + for i in range(len(keypad_keys)) + " TODO: Mysteriously keypad 3 and 9 do not work on some systems. + if keypad_output[i] == '3' || keypad_output[i] == '9' + continue + endif + call term_sendkeys(buf, "\" .. keypad_keys[i]) + call WaitForAssert({-> assert_equal(keypad_output[i], term_getline(buf, 1))}) + endfor + + call feedkeys("\\\one\.two", 'xt') + call WaitForAssert({-> assert_equal('two', term_getline(buf, 1))}) + + call StopVimInTerminal(buf) +endfunc + +" Test for using the mouse in a terminal +func Test_term_mouse() + CheckNotGui + CheckRunVimInTerminal + + let save_mouse = &mouse + let save_term = &term + let save_ttymouse = &ttymouse + let save_clipboard = &clipboard + set mouse=a term=xterm ttymouse=sgr mousetime=200 clipboard= + + let lines =<< trim END + one two three four five + red green yellow red blue + vim emacs sublime nano + END + call writefile(lines, 'Xtest_mouse') + + " Create a terminal window running Vim for the test with mouse enabled + let prev_win = win_getid() + let buf = RunVimInTerminal('Xtest_mouse -n', {}) + call term_sendkeys(buf, ":set nocompatible\") + call term_sendkeys(buf, ":set mouse=a term=xterm ttymouse=sgr\") + call term_sendkeys(buf, ":set clipboard=\") + call term_sendkeys(buf, ":set mousemodel=extend\") + call term_wait(buf) + redraw! + + " Use the mouse to enter the terminal window + call win_gotoid(prev_win) + call feedkeys(MouseLeftClickCode(1, 1), 'x') + call feedkeys(MouseLeftReleaseCode(1, 1), 'x') + call assert_equal(1, getwininfo(win_getid())[0].terminal) + + " Test for click/release + call test_setmouse(2, 5) + call feedkeys("\\", 'xt') + call test_setmouse(3, 8) + call term_sendkeys(buf, "\\") + call term_wait(buf, 50) + call term_sendkeys(buf, ":call writefile([json_encode(getpos('.'))], 'Xbuf')\") + call term_wait(buf, 50) + let pos = json_decode(readfile('Xbuf')[0]) + call assert_equal([3, 8], pos[1:2]) + + " Test for selecting text using mouse + call delete('Xbuf') + call test_setmouse(2, 11) + call term_sendkeys(buf, "\") + call test_setmouse(2, 16) + call term_sendkeys(buf, "\y") + call term_wait(buf, 50) + call term_sendkeys(buf, ":call writefile([@\"], 'Xbuf')\") + call term_wait(buf, 50) + call assert_equal('yellow', readfile('Xbuf')[0]) + + " Test for selecting text using doubleclick + call delete('Xbuf') + call test_setmouse(1, 11) + call term_sendkeys(buf, "\\\") + call test_setmouse(1, 17) + call term_sendkeys(buf, "\y") + call term_wait(buf, 50) + call term_sendkeys(buf, ":call writefile([@\"], 'Xbuf')\") + call term_wait(buf, 50) + call assert_equal('three four', readfile('Xbuf')[0]) + + " Test for selecting a line using triple click + call delete('Xbuf') + call test_setmouse(3, 2) + call term_sendkeys(buf, "\\\\\\y") + call term_wait(buf, 50) + call term_sendkeys(buf, ":call writefile([@\"], 'Xbuf')\") + call term_wait(buf, 50) + call assert_equal("vim emacs sublime nano\n", readfile('Xbuf')[0]) + + " Test for selecting a block using qudraple click + call delete('Xbuf') + call test_setmouse(1, 11) + call term_sendkeys(buf, "\\\\\\\") + call test_setmouse(3, 13) + call term_sendkeys(buf, "\y") + call term_wait(buf, 50) + call term_sendkeys(buf, ":call writefile([@\"], 'Xbuf')\") + call term_wait(buf, 50) + call assert_equal("ree\nyel\nsub", readfile('Xbuf')[0]) + + " Test for extending a selection using right click + call delete('Xbuf') + call test_setmouse(2, 9) + call term_sendkeys(buf, "\\") + call test_setmouse(2, 16) + call term_sendkeys(buf, "\\y") + call term_wait(buf, 50) + call term_sendkeys(buf, ":call writefile([@\"], 'Xbuf')\") + call term_wait(buf, 50) + call assert_equal("n yellow", readfile('Xbuf')[0]) + + " Test for pasting text using middle click + call delete('Xbuf') + call term_sendkeys(buf, ":let @r='bright '\") + call test_setmouse(2, 22) + call term_sendkeys(buf, "\"r\\") + call term_wait(buf, 50) + call term_sendkeys(buf, ":call writefile([getline(2)], 'Xbuf')\") + call term_wait(buf, 50) + call assert_equal("red bright blue", readfile('Xbuf')[0][-15:]) + + " cleanup + call term_wait(buf) + call StopVimInTerminal(buf) + let &mouse = save_mouse + let &term = save_term + let &ttymouse = save_ttymouse + let &clipboard = save_clipboard + set mousetime& + call delete('Xtest_mouse') + call delete('Xbuf') +endfunc + +" Test for modeless selection in a terminal +func Test_term_modeless_selection() + CheckUnix + CheckNotGui + CheckRunVimInTerminal + CheckFeature clipboard_working + + let save_mouse = &mouse + let save_term = &term + let save_ttymouse = &ttymouse + set mouse=a term=xterm ttymouse=sgr mousetime=200 + set clipboard=autoselectml + + let lines =<< trim END + one two three four five + red green yellow red blue + vim emacs sublime nano + END + call writefile(lines, 'Xtest_modeless') + + " Create a terminal window running Vim for the test with mouse disabled + let prev_win = win_getid() + let buf = RunVimInTerminal('Xtest_modeless -n', {}) + call term_sendkeys(buf, ":set nocompatible\") + call term_sendkeys(buf, ":set mouse=\") + call term_wait(buf) + redraw! + + " Use the mouse to enter the terminal window + call win_gotoid(prev_win) + call feedkeys(MouseLeftClickCode(1, 1), 'x') + call feedkeys(MouseLeftReleaseCode(1, 1), 'x') + call term_wait(buf) + call assert_equal(1, getwininfo(win_getid())[0].terminal) + + " Test for copying a modeless selection to clipboard + let @* = 'clean' + " communicating with X server may take a little time + sleep 100m + call feedkeys(MouseLeftClickCode(2, 3), 'x') + call feedkeys(MouseLeftDragCode(2, 11), 'x') + call feedkeys(MouseLeftReleaseCode(2, 11), 'x') + call assert_equal("d green y", @*) + + " cleanup + call term_wait(buf) + call StopVimInTerminal(buf) + let &mouse = save_mouse + let &term = save_term + let &ttymouse = save_ttymouse + set mousetime& clipboard& + call delete('Xtest_modeless') + new | only! +endfunc + + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 81cf2c0de3..06d1bed4eb 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1114, /**/ 1113, /**/ From c19fd917be656b127c5619080f866a23ce1fe755 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 2 Jul 2020 20:59:05 +0200 Subject: [PATCH 071/105] patch 8.2.1115: iminsert test fails when compiled with VIMDLL Problem: Iminsert test fails when compiled with VIMDLL. Solution: Change condition. (Ken Takata, closes #6376) --- src/testdir/test_iminsert.vim | 4 ++-- src/version.c | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/testdir/test_iminsert.vim b/src/testdir/test_iminsert.vim index 1f19412789..80088c3c07 100644 --- a/src/testdir/test_iminsert.vim +++ b/src/testdir/test_iminsert.vim @@ -27,7 +27,7 @@ func Test_iminsert2() set imactivatefunc= set imstatusfunc= - let expected = has('gui_win32') ? 0 : 1 + let expected = (has('win32') && has('gui_running')) ? 0 : 1 call assert_equal(expected, s:imactivatefunc_called) call assert_equal(expected, s:imstatusfunc_called) endfunc @@ -38,7 +38,7 @@ func Test_getimstatus() elseif !has('gui_mac') CheckFeature xim endif - if has('gui_win32') + if has('win32') && has('gui_running') set imactivatefunc= set imstatusfunc= else diff --git a/src/version.c b/src/version.c index 06d1bed4eb..fa8c63955c 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1115, /**/ 1114, /**/ From d2ef6b320bf2e2f3fcc0bb858b16898e6f74b4d9 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 2 Jul 2020 21:11:34 +0200 Subject: [PATCH 072/105] patch 8.2.1116: Vim9: parsing command checks for list twice Problem: Vim9: parsing command checks for list twice. Solution: Adjust how a command is parsed. --- src/ex_docmd.c | 27 ++++++++++++++++----------- src/testdir/test_vim9_cmd.vim | 2 +- src/version.c | 2 ++ 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 0c55a57c5c..a5562cc28a 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -3219,8 +3219,9 @@ find_ex_command( * "lvar = value", "lvar(arg)", "[1, 2 3]->Func()" */ p = eap->cmd; - if (lookup != NULL && (*p == '(' || *p == '[' || *p == '{' - || ((p = to_name_const_end(eap->cmd)) > eap->cmd && *p != NUL))) + if (lookup != NULL && (*p == '(' || *p == '{' + || ((p = to_name_const_end(eap->cmd)) > eap->cmd && *p != NUL) + || *p == '[')) { int oplen; int heredoc; @@ -3233,6 +3234,7 @@ find_ex_command( // "{..." is an dict expression. if (*p == '(' || *p == '{' + || (*p == '[' && p > eap->cmd) || p[1] == ':' || (*p == '-' && p[1] == '>')) { @@ -3240,6 +3242,18 @@ find_ex_command( return eap->cmd; } + // "[...]->Method()" is a list expression, but "[a, b] = Func()" is + // an assignment. + // If there is no line break inside the "[...]" then "p" is advanced to + // after the "]" by to_name_const_end(): check if a "=" follows. + // If "[...]" has a line break "p" still points at the "[" and it can't + // be an assignment. + if (*eap->cmd == '[' && (p == eap->cmd || *skipwhite(p) != '=')) + { + eap->cmdidx = CMD_eval; + return eap->cmd; + } + // Recognize an assignment if we recognize the variable name: // "g:var = expr" // "var = expr" where "var" is a local var name. @@ -3253,15 +3267,6 @@ find_ex_command( return eap->cmd; } } - - // "[...]->Method()" is a list expression. But "[a, b] = Func()" is - // an assignment. - if (*p == '[' && (eval_list(&p, NULL, NULL, FALSE) == FAIL - || *skipwhite(p) != '=')) - { - eap->cmdidx = CMD_eval; - return eap->cmd; - } } #endif diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim index ab91a16460..27d2b3a7cd 100644 --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -190,7 +190,7 @@ def Test_for_linebreak() CheckScriptSuccess(lines) enddef -def Test_method_cal_linebreak() +def Test_method_call_linebreak() let lines =<< trim END vim9script let res = [] diff --git a/src/version.c b/src/version.c index fa8c63955c..54469ffc2b 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1116, /**/ 1115, /**/ From a9a8e5f0dc22aaa9e53578b5b2d7569279e6cb94 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 2 Jul 2020 21:17:57 +0200 Subject: [PATCH 073/105] patch 8.2.1117: Coverity warns for unsing unitialized field Problem: Coverity warns for unsing unitialized field. Solution: Initialize v_lock. --- src/if_lua.c | 2 ++ src/version.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/if_lua.c b/src/if_lua.c index 20cdbbcebc..9852c618e8 100644 --- a/src/if_lua.c +++ b/src/if_lua.c @@ -576,6 +576,8 @@ luaV_totypval(lua_State *L, int pos, typval_T *tv) { int status = OK; + tv->v_lock = 0; + switch (lua_type(L, pos)) { case LUA_TBOOLEAN: diff --git a/src/version.c b/src/version.c index 54469ffc2b..2d9455929e 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1117, /**/ 1116, /**/ From 810af5ea460eab820cc5899892067d8c242be688 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 2 Jul 2020 21:23:42 +0200 Subject: [PATCH 074/105] patch 8.2.1118: condition can never be true, dead code Problem: Condition can never be true, dead code. Solution: Remove the dead code. --- src/move.c | 2 -- src/version.c | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/move.c b/src/move.c index 5b3fb35fb4..ec1ad38ad7 100644 --- a/src/move.c +++ b/src/move.c @@ -2681,8 +2681,6 @@ halfpage(int flag, linenr_T Prenum) if (curwin->w_topfill > 0) { i = 1; - if (--n < 0 && scrolled > 0) - break; --curwin->w_topfill; } else diff --git a/src/version.c b/src/version.c index 2d9455929e..c334ec686c 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1118, /**/ 1117, /**/ From 5289783e0b07cfc3f92ee933261ca4c4acdca007 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 2 Jul 2020 22:50:37 +0200 Subject: [PATCH 075/105] patch 8.2.1119: configure fails with Xcode 12 beta Problem: Configure fails with Xcode 12 beta. Solution: use "return" instead of "exit()". (Nico Weber, closes #6381) --- src/auto/configure | 4 ++-- src/configure.ac | 4 ++-- src/version.c | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/auto/configure b/src/auto/configure index f642a0b5f9..9c9a719d81 100755 --- a/src/auto/configure +++ b/src/auto/configure @@ -14143,8 +14143,8 @@ else main() { uint32_t nr1 = (uint32_t)-1; uint32_t nr2 = (uint32_t)0xffffffffUL; - if (sizeof(uint32_t) != 4 || nr1 != 0xffffffffUL || nr2 + 1 != 0) exit(1); - exit(0); + if (sizeof(uint32_t) != 4 || nr1 != 0xffffffffUL || nr2 + 1 != 0) return 1; + return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : diff --git a/src/configure.ac b/src/configure.ac index 0543918820..19ce4c7a5f 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -4151,8 +4151,8 @@ AC_TRY_RUN([ main() { uint32_t nr1 = (uint32_t)-1; uint32_t nr2 = (uint32_t)0xffffffffUL; - if (sizeof(uint32_t) != 4 || nr1 != 0xffffffffUL || nr2 + 1 != 0) exit(1); - exit(0); + if (sizeof(uint32_t) != 4 || nr1 != 0xffffffffUL || nr2 + 1 != 0) return 1; + return 0; }], AC_MSG_RESULT(ok), AC_MSG_ERROR([WRONG! uint32_t not defined correctly.]), diff --git a/src/version.c b/src/version.c index c334ec686c..ae2a95f465 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1119, /**/ 1118, /**/ From 92fdd1e75db3048516a93a18cc53ed984813c9e5 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 3 Jul 2020 18:00:05 +0200 Subject: [PATCH 076/105] patch 8.2.1120: Python code not tested properly Problem: Python code not tested properly. Solution: Add more tests and convert old-style test into new-style test. (Yegappan Lakshmanan, closes #6370) --- src/Makefile | 2 +- src/testdir/Make_all.mak | 1 - src/testdir/Make_vms.mms | 2 +- src/testdir/test86.in | 1711 ----------------- src/testdir/test86.ok | 1445 --------------- src/testdir/test_python2.vim | 3372 +++++++++++++++++++++++++++++++++- src/version.c | 2 + 7 files changed, 3368 insertions(+), 3167 deletions(-) delete mode 100644 src/testdir/test86.in delete mode 100644 src/testdir/test86.ok diff --git a/src/Makefile b/src/Makefile index 105820a93e..9166662970 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2308,7 +2308,7 @@ test1 \ test42 test44 test49 \ test52 test59 \ test70 \ - test86 test87 \ + test87 \ test99: cd testdir; rm -f $@.out; $(MAKE) -f Makefile $@.out VIMPROG=../$(VIMTESTTARGET) $(GUI_TESTARG) SCRIPTSOURCE=../$(SCRIPTSOURCE) diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak index a0884cdc87..14062d8396 100644 --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -21,7 +21,6 @@ SCRIPTS_ALL = \ # Tests that run on most systems, but not on Amiga. SCRIPTS_MORE1 = \ test52.out \ - test86.out \ test87.out diff --git a/src/testdir/Make_vms.mms b/src/testdir/Make_vms.mms index a89bb244fd..ff6deb4657 100644 --- a/src/testdir/Make_vms.mms +++ b/src/testdir/Make_vms.mms @@ -116,7 +116,7 @@ SCRIPT_GDIFF = test47.out .ENDIF .IFDEF HAVE_PYTHON -SCRIPT_PYTHON = test86.out test87.out +SCRIPT_PYTHON = test87.out .ENDIF .in.out : diff --git a/src/testdir/test86.in b/src/testdir/test86.in deleted file mode 100644 index e24c13d1ca..0000000000 --- a/src/testdir/test86.in +++ /dev/null @@ -1,1711 +0,0 @@ -Tests for various python features. vim: set ft=vim : - -NOTE: This will cause errors when run under valgrind. -This would require recompiling Python with: - ./configure --without-pymalloc -See http://svn.python.org/view/python/trunk/Misc/README.valgrind?view=markup - -STARTTEST -:so small.vim -:set encoding=latin1 -:set noswapfile -:if !has('python') || !has('quickfix') | e! test.ok | wq! test.out | endif -:lang C -:fun Test() -:py import vim -:py cb = vim.current.buffer -:let l = [] -:py l=vim.bindeval('l') -:py f=vim.bindeval('function("strlen")') -:" Extending List directly with different types -:py l.extend([1, "as'd", [1, 2, f, {'a': 1}]]) -:$put =string(l) -:$put =string(l[-1]) -:try -: $put =string(l[-4]) -:catch -: $put =v:exception[:13] -:endtry -:" List assignment -:py l[0]=0 -:$put =string(l) -:py l[-2]=f -:$put =string(l) -:" -:" Extending Dictionary directly with different types -:let d = {} -:fun d.f() -: return 1 -:endfun -py << trim EOF - d=vim.bindeval('d') - d['1']='asd' - d.update() # Must not do anything, including throwing errors - d.update(b=[1, 2, f]) - d.update((('-1', {'a': 1}),)) - d.update({'0': -1}) - dk = d.keys() - dv = d.values() - di = d.items() - cmpfun = lambda a, b: cmp(repr(a), repr(b)) - dk.sort(cmpfun) - dv.sort(cmpfun) - di.sort(cmpfun) -EOF -:$put =pyeval('d[''f''](self={})') -:$put =pyeval('repr(dk)') -:$put =substitute(pyeval('repr(dv)'),'0x\x\+','','g') -:$put =substitute(pyeval('repr(di)'),'0x\x\+','','g') -:for [key, Val] in sort(items(d)) -: $put =string(key) . ' : ' . string(Val) -: unlet key Val -:endfor -:py del dk -:py del di -:py del dv -:" -:" removing items with del -:py del l[2] -:$put =string(l) -:let l = range(8) -:py l=vim.bindeval('l') -:try -: py del l[:3] -: py del l[1:] -:catch -: $put =v:exception -:endtry -:$put =string(l) -:" -:py del d['-1'] -:py del d['f'] -:$put =string(pyeval('d.get(''b'', 1)')) -:$put =string(pyeval('d.pop(''b'')')) -:$put =string(pyeval('d.get(''b'', 1)')) -:$put =string(pyeval('d.pop(''1'', 2)')) -:$put =string(pyeval('d.pop(''1'', 2)')) -:$put =pyeval('repr(d.has_key(''0''))') -:$put =pyeval('repr(d.has_key(''1''))') -:$put =pyeval('repr(''0'' in d)') -:$put =pyeval('repr(''1'' in d)') -:$put =pyeval('repr(list(iter(d)))') -:$put =string(d) -:$put =pyeval('repr(d.popitem())') -:$put =pyeval('repr(d.get(''0''))') -:$put =pyeval('repr(list(iter(d)))') -:" -:" removing items out of range: silently skip items that don't exist -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:" The following two ranges delete nothing as they match empty list: -:py del l[2:1] -:$put =string(l) -:py del l[2:2] -:$put =string(l) -:py del l[2:3] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py del l[2:4] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py del l[2:5] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py del l[2:6] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:" The following two ranges delete nothing as they match empty list: -:py del l[-1:2] -:$put =string(l) -:py del l[-2:2] -:$put =string(l) -:py del l[-3:2] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py del l[-4:2] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py del l[-5:2] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py del l[-6:2] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py del l[::2] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py del l[3:0:-2] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py del l[2:4:-2] -:$put =string(l) -:" -:" Slice assignment to a list -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py l[0:0]=['a'] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py l[1:2]=['b'] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py l[2:4]=['c'] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py l[4:4]=['d'] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py l[-1:2]=['e'] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py l[-10:2]=['f'] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py l[2:-10]=['g'] -:$put =string(l) -:let l = [] -:py l=vim.bindeval('l') -:py l[0:0]=['h'] -:$put =string(l) -:let l = range(8) -:py l=vim.bindeval('l') -:py l[2:6:2] = [10, 20] -:$put =string(l) -:let l = range(8) -:py l=vim.bindeval('l') -:py l[6:2:-2] = [10, 20] -:$put =string(l) -:let l = range(8) -:py l=vim.bindeval('l') -:py l[6:2] = () -:$put =string(l) -:let l = range(8) -:py l=vim.bindeval('l') -:py l[6:2:1] = () -:$put =string(l) -:let l = range(8) -:py l=vim.bindeval('l') -:py l[2:2:1] = () -:$put =string(l) -:" -:" Locked variables -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:lockvar! l -py << trim EOF - def emsg(ei): - return ei[0].__name__ + ':' + repr(ei[1].args) - - try: - l[2]='i' - except vim.error: - cb.append('l[2] threw vim.error: ' + emsg(sys.exc_info())) -EOF -:$put =string(l) -:unlockvar! l -:" -:" Function calls -py << trim EOF - import sys - def ee(expr, g=globals(), l=locals()): - try: - exec(expr, g, l) - except: - ei = sys.exc_info() - msg = emsg(ei) - msg = msg.replace('TypeError:(\'argument 1 ', 'TypeError:(\'') - if expr.find('None') > -1: - msg = msg.replace('TypeError:(\'iteration over non-sequence\',)', - 'TypeError:("\'NoneType\' object is not iterable",)') - if expr.find('FailingNumber') > -1: - msg = msg.replace(', not \'FailingNumber\'', '').replace('"', '\'') - msg = msg.replace('TypeError:(\'iteration over non-sequence\',)', - 'TypeError:("\'FailingNumber\' object is not iterable",)') - if msg.find('(\'\'') > -1 or msg.find('(\'can\'t') > -1: - msg = msg.replace('(\'', '("').replace('\',)', '",)') - # Some Python versions say can't, others cannot. - if msg.find('can\'t') > -1: - msg = msg.replace('can\'t', 'cannot') - # Some Python versions use single quote, some double quote - if msg.find('"cannot ') > -1: - msg = msg.replace('"cannot ', '\'cannot ') - if msg.find(' attributes"') > -1: - msg = msg.replace(' attributes"', ' attributes\'') - if expr == 'fd(self=[])': - # HACK: PyMapping_Check changed meaning - msg = msg.replace('AttributeError:(\'keys\',)', - 'TypeError:(\'unable to convert list to vim dictionary\',)') - vim.current.buffer.append(expr + ':' + msg) - else: - vim.current.buffer.append(expr + ':NOT FAILED') -EOF -:fun New(...) -: return ['NewStart']+a:000+['NewEnd'] -:endfun -:fun DictNew(...) dict -: return ['DictNewStart']+a:000+['DictNewEnd', self] -:endfun -:let l=[function('New'), function('DictNew')] -:py l=vim.bindeval('l') -:py l.extend(list(l[0](1, 2, 3))) -:$put =string(l) -:py l.extend(list(l[1](1, 2, 3, self={'a': 'b'}))) -:$put =string(l) -:py l.extend([l[0].name]) -:$put =string(l) -:py ee('l[1](1, 2, 3)') -:py f=l[0] -:delfunction New -:py ee('f(1, 2, 3)') -:if has('float') -: let l=[0.0] -: py l=vim.bindeval('l') -: py l.extend([0.0]) -: $put =string(l) -:else -: $put ='[0.0, 0.0]' -:endif -:let messages=[] -:delfunction DictNew -py << trim EOF - d=vim.bindeval('{}') - m=vim.bindeval('messages') - def em(expr, g=globals(), l=locals()): - try: - exec(expr, g, l) - except: - m.extend([sys.exc_type.__name__]) - - em('d["abc1"]') - em('d["abc1"]="\\0"') - em('d["abc1"]=vim') - em('d[""]=1') - em('d["a\\0b"]=1') - em('d[u"a\\0b"]=1') - - em('d.pop("abc1")') - em('d.popitem()') - del em - del m -EOF -:$put =messages -:unlet messages -:" locked and scope attributes -:let d={} | let dl={} | lockvar dl -:for s in split("d dl v: g:") -: let name=tr(s, ':', 's') -: execute 'py '.name.'=vim.bindeval("'.s.'")' -: let toput=s.' : '.join(map(['locked', 'scope'], 'v:val.":".pyeval(name.".".v:val)'), ';') -: $put =toput -:endfor -:silent! let d.abc2=1 -:silent! let dl.abc3=1 -:py d.locked=True -:py dl.locked=False -:silent! let d.def=1 -:silent! let dl.def=1 -:put ='d:'.string(d) -:put ='dl:'.string(dl) -:unlet d dl -: -:let l=[] | let ll=[] | lockvar ll -:for s in split("l ll") -: let name=tr(s, ':', 's') -: execute 'py '.name.'=vim.bindeval("'.s.'")' -: let toput=s.' : locked:'.pyeval(name.'.locked') -: $put =toput -:endfor -:silent! call extend(l, [0]) -:silent! call extend(ll, [0]) -:py l.locked=True -:py ll.locked=False -:silent! call extend(l, [1]) -:silent! call extend(ll, [1]) -:put ='l:'.string(l) -:put ='ll:'.string(ll) -:unlet l ll -:" -:" pyeval() -:let l=pyeval('range(3)') -:$put =string(l) -:let d=pyeval('{"a": "b", "c": 1, "d": ["e"]}') -:$put =sort(items(d)) -:let v:errmsg = '' -:$put ='pyeval(\"None\") = ' . pyeval('None') . v:errmsg -:if has('float') -: let f=pyeval('0.0') -: $put =string(f) -:else -: $put ='0.0' -:endif -:" Invalid values: -:for e in ['"\0"', '{"\0": 1}', 'undefined_name', 'vim'] -: try -: let v=pyeval(e) -: catch -: let toput=e.":\t".v:exception[:13] -: $put =toput -: endtry -:endfor -:" -:" threading -:let l = [0] -:py l=vim.bindeval('l') -py << trim EOF - import threading - import time - - class T(threading.Thread): - def __init__(self): - threading.Thread.__init__(self) - self.t = 0 - self.running = True - - def run(self): - while self.running: - self.t += 1 - time.sleep(0.1) - - t = T() - del T - t.start() -EOF -:sleep 1 -:py t.running = False -:py t.join() -:" Check if the background thread is working. Count should be 10, but on a -:" busy system (AppVeyor) it can be much lower. -:py l[0] = t.t > 4 -:py del time -:py del threading -:py del t -:$put =string(l) -:" -:" settrace -:let l = [] -:py l=vim.bindeval('l') -py << trim EOF - import sys - - def traceit(frame, event, arg): - global l - if event == "line": - l.extend([frame.f_lineno]) - return traceit - - def trace_main(): - for i in range(5): - pass -EOF -:py sys.settrace(traceit) -:py trace_main() -:py sys.settrace(None) -:py del traceit -:py del trace_main -:$put =string(l) -:" -:" Slice -:py ll = vim.bindeval('[0, 1, 2, 3, 4, 5]') -:py l = ll[:4] -:$put =string(pyeval('l')) -:py l = ll[2:] -:$put =string(pyeval('l')) -:py l = ll[:-4] -:$put =string(pyeval('l')) -:py l = ll[-2:] -:$put =string(pyeval('l')) -:py l = ll[2:4] -:$put =string(pyeval('l')) -:py l = ll[4:2] -:$put =string(pyeval('l')) -:py l = ll[-4:-2] -:$put =string(pyeval('l')) -:py l = ll[-2:-4] -:$put =string(pyeval('l')) -:py l = ll[:] -:$put =string(pyeval('l')) -:py l = ll[0:6] -:$put =string(pyeval('l')) -:py l = ll[-10:10] -:$put =string(pyeval('l')) -:py l = ll[4:2:-1] -:$put =string(pyeval('l')) -:py l = ll[::2] -:$put =string(pyeval('l')) -:py l = ll[4:2:1] -:$put =string(pyeval('l')) -:py del l -:" -:" Vars -:let g:foo = 'bac' -:let w:abc3 = 'def' -:let b:baz = 'bar' -:let t:bar = 'jkl' -:try -: throw "Abc" -:catch -: put =pyeval('vim.vvars[''exception'']') -:endtry -:put =pyeval('vim.vars[''foo'']') -:put =pyeval('vim.current.window.vars[''abc3'']') -:put =pyeval('vim.current.buffer.vars[''baz'']') -:put =pyeval('vim.current.tabpage.vars[''bar'']') -:" -:" Options -:" paste: boolean, global -:" previewheight number, global -:" operatorfunc: string, global -:" number: boolean, window-local -:" numberwidth: number, window-local -:" colorcolumn: string, window-local -:" statusline: string, window-local/global -:" autoindent: boolean, buffer-local -:" shiftwidth: number, buffer-local -:" omnifunc: string, buffer-local -:" preserveindent: boolean, buffer-local/global -:" path: string, buffer-local/global -:let g:bufs=[bufnr('%')] -:new -:let g:bufs+=[bufnr('%')] -:vnew -:let g:bufs+=[bufnr('%')] -:wincmd j -:vnew -:let g:bufs+=[bufnr('%')] -:wincmd l -:fun RecVars(opt) -: let gval =string(eval('&g:'.a:opt)) -: let wvals=join(map(range(1, 4), 'v:val.":".string(getwinvar(v:val, "&".a:opt))')) -: let bvals=join(map(copy(g:bufs), 'v:val.":".string(getbufvar(v:val, "&".a:opt))')) -: put =' G: '.gval -: put =' W: '.wvals -: put =' B: '.wvals -:endfun -py << trim EOF - def e(s, g=globals(), l=locals()): - try: - exec(s, g, l) - except: - vim.command('return ' + repr(sys.exc_type.__name__)) - - def ev(s, g=globals(), l=locals()): - try: - return eval(s, g, l) - except: - vim.command('let exc=' + repr(sys.exc_type.__name__)) - return 0 -EOF -:fun E(s) -: python e(vim.eval('a:s')) -:endfun -:fun Ev(s) -: let r=pyeval('ev(vim.eval("a:s"))') -: if exists('exc') -: throw exc -: endif -: return r -:endfun -:py gopts1=vim.options -:py wopts1=vim.windows[2].options -:py wopts2=vim.windows[0].options -:py wopts3=vim.windows[1].options -:py bopts1=vim.buffers[vim.bindeval("g:bufs")[2]].options -:py bopts2=vim.buffers[vim.bindeval("g:bufs")[1]].options -:py bopts3=vim.buffers[vim.bindeval("g:bufs")[0]].options -:$put ='wopts iters equal: '.pyeval('list(wopts1) == list(wopts2)') -:$put ='bopts iters equal: '.pyeval('list(bopts1) == list(bopts2)') -:py gset=set(iter(gopts1)) -:py wset=set(iter(wopts1)) -:py bset=set(iter(bopts1)) -:set path=.,..,, -:let lst=[] -:let lst+=[['paste', 1, 0, 1, 2, 1, 1, 0 ]] -:let lst+=[['previewheight', 5, 1, 6, 'a', 0, 1, 0 ]] -:let lst+=[['operatorfunc', 'A', 'B', 'C', 2, 0, 1, 0 ]] -:let lst+=[['number', 0, 1, 1, 0, 1, 0, 1 ]] -:let lst+=[['numberwidth', 2, 3, 5, -100, 0, 0, 1 ]] -:let lst+=[['colorcolumn', '+1', '+2', '+3', 'abc4', 0, 0, 1 ]] -:let lst+=[['statusline', '1', '2', '4', 0, 0, 1, 1 ]] -:let lst+=[['autoindent', 0, 1, 1, 2, 1, 0, 2 ]] -:let lst+=[['shiftwidth', 0, 2, 1, 3, 0, 0, 2 ]] -:let lst+=[['omnifunc', 'A', 'B', 'C', 1, 0, 0, 2 ]] -:let lst+=[['preserveindent', 0, 1, 1, 2, 1, 1, 2 ]] -:let lst+=[['path', '.,,', ',,', '.', 0, 0, 1, 2 ]] -:for [oname, oval1, oval2, oval3, invval, bool, global, local] in lst -: py oname=vim.eval('oname') -: py oval1=vim.bindeval('oval1') -: py oval2=vim.bindeval('oval2') -: py oval3=vim.bindeval('oval3') -: if invval is 0 || invval is 1 -: py invval=bool(vim.bindeval('invval')) -: else -: py invval=vim.bindeval('invval') -: endif -: if bool -: py oval1=bool(oval1) -: py oval2=bool(oval2) -: py oval3=bool(oval3) -: endif -: put ='>>> '.oname -: $put =' g/w/b:'.pyeval('oname in gset').'/'.pyeval('oname in wset').'/'.pyeval('oname in bset') -: $put =' g/w/b (in):'.pyeval('oname in gopts1').'/'.pyeval('oname in wopts1').'/'.pyeval('oname in bopts1') -: for v in ['gopts1', 'wopts1', 'bopts1'] -: try -: put =' p/'.v.': '.Ev('repr('.v.'['''.oname.'''])') -: catch -: put =' p/'.v.'! '.v:exception -: endtry -: let r=E(v.'['''.oname.''']=invval') -: if r isnot 0 -: put =' inv: '.string(invval).'! '.r -: endif -: for vv in (v is# 'gopts1' ? [v] : [v, v[:-2].'2', v[:-2].'3']) -: let val=substitute(vv, '^.opts', 'oval', '') -: let r=E(vv.'['''.oname.''']='.val) -: if r isnot 0 -: put =' '.vv.'! '.r -: endif -: endfor -: endfor -: call RecVars(oname) -: for v in ['wopts3', 'bopts3'] -: let r=E('del '.v.'["'.oname.'"]') -: if r isnot 0 -: put =' del '.v.'! '.r -: endif -: endfor -: call RecVars(oname) -:endfor -:delfunction RecVars -:delfunction E -:delfunction Ev -:py del ev -:py del e -:only -:for buf in g:bufs[1:] -: execute 'bwipeout!' buf -:endfor -:py del gopts1 -:py del wopts1 -:py del wopts2 -:py del wopts3 -:py del bopts1 -:py del bopts2 -:py del bopts3 -:py del oval1 -:py del oval2 -:py del oval3 -:py del oname -:py del invval -:" -:" Test buffer object -:vnew -:put ='First line' -:put ='Second line' -:put ='Third line' -:1 delete _ -:py b=vim.current.buffer -:wincmd w -:mark a -:augroup BUFS -: autocmd BufFilePost * python cb.append(vim.eval('expand("")') + ':BufFilePost:' + vim.eval('bufnr("%")')) -: autocmd BufFilePre * python cb.append(vim.eval('expand("")') + ':BufFilePre:' + vim.eval('bufnr("%")')) -:augroup END -py << trim EOF - # Tests BufferAppend and BufferItem - cb.append(b[0]) - # Tests BufferSlice and BufferAssSlice - cb.append('abc5') # Will be overwritten - cb[-1:] = b[:-2] - # Test BufferLength and BufferAssSlice - cb.append('def') # Will not be overwritten - cb[len(cb):] = b[:] - # Test BufferAssItem and BufferMark - cb.append('ghi') # Will be overwritten - cb[-1] = repr((len(cb) - cb.mark('a')[0], cb.mark('a')[1])) - # Test BufferRepr - cb.append(repr(cb) + repr(b)) - # Modify foreign buffer - b.append('foo') - b[0]='bar' - b[0:0]=['baz'] - vim.command('call append("$", getbufline(%i, 1, "$"))' % b.number) - # Test assigning to name property - import os - old_name = cb.name - cb.name = 'foo' - cb.append(cb.name[-11:].replace(os.path.sep, '/')) - b.name = 'bar' - cb.append(b.name[-11:].replace(os.path.sep, '/')) - cb.name = old_name - cb.append(cb.name[-17:].replace(os.path.sep, '/')) - del old_name - # Test CheckBuffer - for _b in vim.buffers: - if _b is not cb: - vim.command('bwipeout! ' + str(_b.number)) - del _b - cb.append('valid: b:%s, cb:%s' % (repr(b.valid), repr(cb.valid))) - for expr in ('b[1]','b[:] = ["A", "B"]','b[:]','b.append("abc6")', 'b.name = "!"'): - try: - exec(expr) - except vim.error: - pass - else: - # Usually a SEGV here - # Should not happen in any case - cb.append('No exception for ' + expr) - vim.command('cd .') - del b -EOF -:augroup BUFS -: autocmd! -:augroup END -:augroup! BUFS -:" -:" Test vim.buffers object -:set hidden -:edit a -:buffer # -:edit b -:buffer # -:edit c -:buffer # -py << trim EOF - try: - from __builtin__ import next - except ImportError: - next = lambda o: o.next() - # Check GCing iterator that was not fully exhausted - i = iter(vim.buffers) - cb.append('i:' + str(next(i))) - # and also check creating more than one iterator at a time - i2 = iter(vim.buffers) - cb.append('i2:' + str(next(i2))) - cb.append('i:' + str(next(i))) - # The following should trigger GC and not cause any problems - del i - del i2 - i3 = iter(vim.buffers) - cb.append('i3:' + str(next(i3))) - del i3 - - prevnum = 0 - for b in vim.buffers: - # Check buffer order - if prevnum >= b.number: - cb.append('!!! Buffer numbers not in strictly ascending order') - # Check indexing: vim.buffers[number].number == number - cb.append(str(b.number) + ':' + repr(vim.buffers[b.number]) + '=' + repr(b)) - prevnum = b.number - del prevnum - - cb.append(str(len(vim.buffers))) - - bnums = list(map(lambda b: b.number, vim.buffers))[1:] - - # Test wiping out buffer with existing iterator - i4 = iter(vim.buffers) - cb.append('i4:' + str(next(i4))) - vim.command('bwipeout! ' + str(bnums.pop(0))) - try: - next(i4) - except vim.error: - pass - else: - cb.append('!!!! No vim.error') - i4 = iter(vim.buffers) - vim.command('bwipeout! ' + str(bnums.pop(-1))) - vim.command('bwipeout! ' + str(bnums.pop(-1))) - cb.append('i4:' + str(next(i4))) - try: - next(i4) - except StopIteration: - cb.append('StopIteration') - del i4 - del bnums -EOF -:" -:" Test vim.{tabpage,window}list and vim.{tabpage,window} objects -:tabnew 0 -:tabnew 1 -:vnew a.1 -:tabnew 2 -:vnew a.2 -:vnew b.2 -:vnew c.2 -py << trim EOF - cb.append('Number of tabs: ' + str(len(vim.tabpages))) - cb.append('Current tab pages:') - def W(w): - if repr(w).find('(unknown)') != -1: - return '' - else: - return repr(w) - - start = len(cb) - - def Cursor(w): - if w.buffer is cb: - return repr((start - w.cursor[0], w.cursor[1])) - else: - return repr(w.cursor) - - for t in vim.tabpages: - cb.append(' ' + repr(t) + '(' + str(t.number) + ')' + ': ' + str(len(t.windows)) + ' windows, current is ' + W(t.window)) - cb.append(' Windows:') - for w in t.windows: - cb.append(' ' + W(w) + '(' + str(w.number) + ')' + ': displays buffer ' + repr(w.buffer) + '; cursor is at ' + Cursor(w)) - # Other values depend on the size of the terminal, so they are checked partly: - for attr in ('height', 'row', 'width', 'col'): - try: - aval = getattr(w, attr) - if type(aval) is not long: - raise TypeError - if aval < 0: - raise ValueError - except Exception: - cb.append('!!!!!! Error while getting attribute ' + attr + ': ' + sys.exc_type.__name__) - del aval - del attr - w.cursor = (len(w.buffer), 0) - del W - del Cursor - cb.append('Number of windows in current tab page: ' + str(len(vim.windows))) - if list(vim.windows) != list(vim.current.tabpage.windows): - cb.append('!!!!!! Windows differ') -EOF -:" -:" Test vim.current -py << trim EOF - def H(o): - return repr(o) - cb.append('Current tab page: ' + repr(vim.current.tabpage)) - cb.append('Current window: ' + repr(vim.current.window) + ': ' + H(vim.current.window) + ' is ' + H(vim.current.tabpage.window)) - cb.append('Current buffer: ' + repr(vim.current.buffer) + ': ' + H(vim.current.buffer) + ' is ' + H(vim.current.window.buffer)+ ' is ' + H(vim.current.tabpage.window.buffer)) - del H - # Assigning: fails - try: - vim.current.window = vim.tabpages[0].window - except ValueError: - cb.append('ValueError at assigning foreign tab window') - - for attr in ('window', 'tabpage', 'buffer'): - try: - setattr(vim.current, attr, None) - except TypeError: - cb.append('Type error at assigning None to vim.current.' + attr) - del attr - - # Assigning: success - vim.current.tabpage = vim.tabpages[-2] - vim.current.buffer = cb - vim.current.window = vim.windows[0] - vim.current.window.cursor = (len(vim.current.buffer), 0) - cb.append('Current tab page: ' + repr(vim.current.tabpage)) - cb.append('Current window: ' + repr(vim.current.window)) - cb.append('Current buffer: ' + repr(vim.current.buffer)) - cb.append('Current line: ' + repr(vim.current.line)) - ws = list(vim.windows) - ts = list(vim.tabpages) - for b in vim.buffers: - if b is not cb: - vim.command('bwipeout! ' + str(b.number)) - del b - cb.append('w.valid: ' + repr([w.valid for w in ws])) - cb.append('t.valid: ' + repr([t.valid for t in ts])) - del w - del t - del ts - del ws -EOF -:tabonly! -:only! -:" -:" Test types -py << trim EOF - for expr, attr in ( - ('vim.vars', 'Dictionary'), - ('vim.options', 'Options'), - ('vim.bindeval("{}")', 'Dictionary'), - ('vim.bindeval("[]")', 'List'), - ('vim.bindeval("function(\'tr\')")', 'Function'), - ('vim.current.buffer', 'Buffer'), - ('vim.current.range', 'Range'), - ('vim.current.window', 'Window'), - ('vim.current.tabpage', 'TabPage'), - ): - cb.append(expr + ':' + attr + ':' + repr(type(eval(expr)) is getattr(vim, attr))) - del expr - del attr -EOF -:" -:" Test __dir__() method -py << trim EOF - for name, o in ( - ('current', vim.current), - ('buffer', vim.current.buffer), - ('window', vim.current.window), - ('tabpage', vim.current.tabpage), - ('range', vim.current.range), - ('dictionary', vim.bindeval('{}')), - ('list', vim.bindeval('[]')), - ('function', vim.bindeval('function("tr")')), - ('output', sys.stdout), - ): - cb.append(name + ':' + ','.join(dir(o))) - del name - del o -EOF -:" -:" Test vim.*.__new__ -:$put =string(pyeval('vim.Dictionary({})')) -:$put =string(pyeval('vim.Dictionary(a=1)')) -:$put =string(pyeval('vim.Dictionary(((''a'', 1),))')) -:$put =string(pyeval('vim.List()')) -:$put =string(pyeval('vim.List(iter(''abc7''))')) -:$put =string(pyeval('vim.Function(''tr'')')) -:$put =string(pyeval('vim.Function(''tr'', args=[123, 3, 4])')) -:$put =string(pyeval('vim.Function(''tr'', args=[])')) -:$put =string(pyeval('vim.Function(''tr'', self={})')) -:$put =string(pyeval('vim.Function(''tr'', args=[123, 3, 4], self={})')) -:$put ='auto_rebind' -:$put =string(pyeval('vim.Function(''tr'', auto_rebind=False)')) -:$put =string(pyeval('vim.Function(''tr'', args=[123, 3, 4], auto_rebind=False)')) -:$put =string(pyeval('vim.Function(''tr'', args=[], auto_rebind=False)')) -:$put =string(pyeval('vim.Function(''tr'', self={}, auto_rebind=False)')) -:$put =string(pyeval('vim.Function(''tr'', args=[123, 3, 4], self={}, auto_rebind=False)')) -:" -:" Test vim.Function -:function Args(...) -: return a:000 -:endfunction -:function SelfArgs(...) dict -: return [a:000, self] -:endfunction -:" The following four lines should not crash -:let Pt = function('tr', [[]], {'l': []}) -:py Pt = vim.bindeval('Pt') -:unlet Pt -:py del Pt -py << trim EOF - def ecall(out_prefix, func, *args, **kwargs): - line = out_prefix + ': ' - try: - ret = func(*args, **kwargs) - except Exception: - line += '!exception: ' + emsg(sys.exc_info()) - else: - line += '!result: ' + vim.Function('string')(ret) - cb.append(line) - a = vim.Function('Args') - pa1 = vim.Function('Args', args=['abcArgsPA1']) - pa2 = vim.Function('Args', args=[]) - pa3 = vim.Function('Args', args=['abcArgsPA3'], self={'abcSelfPA3': 'abcSelfPA3Val'}) - pa4 = vim.Function('Args', self={'abcSelfPA4': 'abcSelfPA4Val'}) - cb.append('a: ' + repr(a)) - cb.append('pa1: ' + repr(pa1)) - cb.append('pa2: ' + repr(pa2)) - cb.append('pa3: ' + repr(pa3)) - cb.append('pa4: ' + repr(pa4)) - sa = vim.Function('SelfArgs') - psa1 = vim.Function('SelfArgs', args=['abcArgsPSA1']) - psa2 = vim.Function('SelfArgs', args=[]) - psa3 = vim.Function('SelfArgs', args=['abcArgsPSA3'], self={'abcSelfPSA3': 'abcSelfPSA3Val'}) - psa4 = vim.Function('SelfArgs', self={'abcSelfPSA4': 'abcSelfPSA4Val'}) - psa5 = vim.Function('SelfArgs', self={'abcSelfPSA5': 'abcSelfPSA5Val'}, auto_rebind=0) - psa6 = vim.Function('SelfArgs', args=['abcArgsPSA6'], self={'abcSelfPSA6': 'abcSelfPSA6Val'}, auto_rebind=()) - psa7 = vim.Function('SelfArgs', args=['abcArgsPSA7'], auto_rebind=[]) - psa8 = vim.Function('SelfArgs', auto_rebind=False) - psa9 = vim.Function('SelfArgs', self={'abcSelfPSA9': 'abcSelfPSA9Val'}, auto_rebind=True) - psaA = vim.Function('SelfArgs', args=['abcArgsPSAA'], self={'abcSelfPSAA': 'abcSelfPSAAVal'}, auto_rebind=1) - psaB = vim.Function('SelfArgs', args=['abcArgsPSAB'], auto_rebind={'abcARPSAB': 'abcARPSABVal'}) - psaC = vim.Function('SelfArgs', auto_rebind=['abcARPSAC']) - cb.append('sa: ' + repr(sa)) - cb.append('psa1: ' + repr(psa1)) - cb.append('psa2: ' + repr(psa2)) - cb.append('psa3: ' + repr(psa3)) - cb.append('psa4: ' + repr(psa4)) - cb.append('psa5: ' + repr(psa5)) - cb.append('psa6: ' + repr(psa6)) - cb.append('psa7: ' + repr(psa7)) - cb.append('psa8: ' + repr(psa8)) - cb.append('psa9: ' + repr(psa9)) - cb.append('psaA: ' + repr(psaA)) - cb.append('psaB: ' + repr(psaB)) - cb.append('psaC: ' + repr(psaC)) - - psar = vim.Function('SelfArgs', args=[{'abcArgsPSAr': 'abcArgsPSArVal'}], self={'abcSelfPSAr': 'abcSelfPSArVal'}) - psar.args[0]['abcArgsPSAr2'] = [psar.self, psar.args[0]] - psar.self['rec'] = psar - psar.self['self'] = psar.self - psar.self['args'] = psar.args - - try: - cb.append('psar: ' + repr(psar)) - except Exception: - cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info())) -EOF -:$put ='s(a): '.string(pyeval('a')) -:$put ='s(pa1): '.string(pyeval('pa1')) -:$put ='s(pa2): '.string(pyeval('pa2')) -:$put ='s(pa3): '.string(pyeval('pa3')) -:$put ='s(pa4): '.string(pyeval('pa4')) -:$put ='s(sa): '.string(pyeval('sa')) -:$put ='s(psa1): '.string(pyeval('psa1')) -:$put ='s(psa2): '.string(pyeval('psa2')) -:$put ='s(psa3): '.string(pyeval('psa3')) -:$put ='s(psa4): '.string(pyeval('psa4')) -:$put ='s(psa5): '.string(pyeval('psa5')) -:$put ='s(psa6): '.string(pyeval('psa6')) -:$put ='s(psa7): '.string(pyeval('psa7')) -:$put ='s(psa8): '.string(pyeval('psa8')) -:$put ='s(psa9): '.string(pyeval('psa9')) -:$put ='s(psaA): '.string(pyeval('psaA')) -:$put ='s(psaB): '.string(pyeval('psaB')) -:$put ='s(psaC): '.string(pyeval('psaC')) -: -:for v in ['sa', 'psa1', 'psa2', 'psa3', 'psa4', 'psa5', 'psa6', 'psa7', 'psa8', 'psa9', 'psaA', 'psaB', 'psaC'] -: let d = {'f': pyeval(v)} -: $put ='d.'.v.'(): '.string(d.f()) -:endfor -: -:py ecall('a()', a, ) -:py ecall('pa1()', pa1, ) -:py ecall('pa2()', pa2, ) -:py ecall('pa3()', pa3, ) -:py ecall('pa4()', pa4, ) -:py ecall('sa()', sa, ) -:py ecall('psa1()', psa1, ) -:py ecall('psa2()', psa2, ) -:py ecall('psa3()', psa3, ) -:py ecall('psa4()', psa4, ) -: -:py ecall('a(42, 43)', a, 42, 43) -:py ecall('pa1(42, 43)', pa1, 42, 43) -:py ecall('pa2(42, 43)', pa2, 42, 43) -:py ecall('pa3(42, 43)', pa3, 42, 43) -:py ecall('pa4(42, 43)', pa4, 42, 43) -:py ecall('sa(42, 43)', sa, 42, 43) -:py ecall('psa1(42, 43)', psa1, 42, 43) -:py ecall('psa2(42, 43)', psa2, 42, 43) -:py ecall('psa3(42, 43)', psa3, 42, 43) -:py ecall('psa4(42, 43)', psa4, 42, 43) -: -:py ecall('a(42, self={"20": 1})', a, 42, self={'20': 1}) -:py ecall('pa1(42, self={"20": 1})', pa1, 42, self={'20': 1}) -:py ecall('pa2(42, self={"20": 1})', pa2, 42, self={'20': 1}) -:py ecall('pa3(42, self={"20": 1})', pa3, 42, self={'20': 1}) -:py ecall('pa4(42, self={"20": 1})', pa4, 42, self={'20': 1}) -:py ecall('sa(42, self={"20": 1})', sa, 42, self={'20': 1}) -:py ecall('psa1(42, self={"20": 1})', psa1, 42, self={'20': 1}) -:py ecall('psa2(42, self={"20": 1})', psa2, 42, self={'20': 1}) -:py ecall('psa3(42, self={"20": 1})', psa3, 42, self={'20': 1}) -:py ecall('psa4(42, self={"20": 1})', psa4, 42, self={'20': 1}) -: -:py ecall('a(self={"20": 1})', a, self={'20': 1}) -:py ecall('pa1(self={"20": 1})', pa1, self={'20': 1}) -:py ecall('pa2(self={"20": 1})', pa2, self={'20': 1}) -:py ecall('pa3(self={"20": 1})', pa3, self={'20': 1}) -:py ecall('pa4(self={"20": 1})', pa4, self={'20': 1}) -:py ecall('sa(self={"20": 1})', sa, self={'20': 1}) -:py ecall('psa1(self={"20": 1})', psa1, self={'20': 1}) -:py ecall('psa2(self={"20": 1})', psa2, self={'20': 1}) -:py ecall('psa3(self={"20": 1})', psa3, self={'20': 1}) -:py ecall('psa4(self={"20": 1})', psa4, self={'20': 1}) -py << trim EOF - def s(v): - if v is None: - return repr(v) - else: - return vim.Function('string')(v) - - cb.append('a.args: ' + s(a.args)) - cb.append('pa1.args: ' + s(pa1.args)) - cb.append('pa2.args: ' + s(pa2.args)) - cb.append('pa3.args: ' + s(pa3.args)) - cb.append('pa4.args: ' + s(pa4.args)) - cb.append('sa.args: ' + s(sa.args)) - cb.append('psa1.args: ' + s(psa1.args)) - cb.append('psa2.args: ' + s(psa2.args)) - cb.append('psa3.args: ' + s(psa3.args)) - cb.append('psa4.args: ' + s(psa4.args)) - - cb.append('a.self: ' + s(a.self)) - cb.append('pa1.self: ' + s(pa1.self)) - cb.append('pa2.self: ' + s(pa2.self)) - cb.append('pa3.self: ' + s(pa3.self)) - cb.append('pa4.self: ' + s(pa4.self)) - cb.append('sa.self: ' + s(sa.self)) - cb.append('psa1.self: ' + s(psa1.self)) - cb.append('psa2.self: ' + s(psa2.self)) - cb.append('psa3.self: ' + s(psa3.self)) - cb.append('psa4.self: ' + s(psa4.self)) - - cb.append('a.name: ' + s(a.name)) - cb.append('pa1.name: ' + s(pa1.name)) - cb.append('pa2.name: ' + s(pa2.name)) - cb.append('pa3.name: ' + s(pa3.name)) - cb.append('pa4.name: ' + s(pa4.name)) - cb.append('sa.name: ' + s(sa.name)) - cb.append('psa1.name: ' + s(psa1.name)) - cb.append('psa2.name: ' + s(psa2.name)) - cb.append('psa3.name: ' + s(psa3.name)) - cb.append('psa4.name: ' + s(psa4.name)) - - cb.append('a.auto_rebind: ' + s(a.auto_rebind)) - cb.append('pa1.auto_rebind: ' + s(pa1.auto_rebind)) - cb.append('pa2.auto_rebind: ' + s(pa2.auto_rebind)) - cb.append('pa3.auto_rebind: ' + s(pa3.auto_rebind)) - cb.append('pa4.auto_rebind: ' + s(pa4.auto_rebind)) - cb.append('sa.auto_rebind: ' + s(sa.auto_rebind)) - cb.append('psa1.auto_rebind: ' + s(psa1.auto_rebind)) - cb.append('psa2.auto_rebind: ' + s(psa2.auto_rebind)) - cb.append('psa3.auto_rebind: ' + s(psa3.auto_rebind)) - cb.append('psa4.auto_rebind: ' + s(psa4.auto_rebind)) - cb.append('psa5.auto_rebind: ' + s(psa5.auto_rebind)) - cb.append('psa6.auto_rebind: ' + s(psa6.auto_rebind)) - cb.append('psa7.auto_rebind: ' + s(psa7.auto_rebind)) - cb.append('psa8.auto_rebind: ' + s(psa8.auto_rebind)) - cb.append('psa9.auto_rebind: ' + s(psa9.auto_rebind)) - cb.append('psaA.auto_rebind: ' + s(psaA.auto_rebind)) - cb.append('psaB.auto_rebind: ' + s(psaB.auto_rebind)) - cb.append('psaC.auto_rebind: ' + s(psaC.auto_rebind)) - - del s - - del a - del pa1 - del pa2 - del pa3 - del pa4 - del sa - del psa1 - del psa2 - del psa3 - del psa4 - del psa5 - del psa6 - del psa7 - del psa8 - del psa9 - del psaA - del psaB - del psaC - del psar - - del ecall -EOF -:" -:" Test stdout/stderr -:redir => messages -:py sys.stdout.write('abc8') ; sys.stdout.write('def') -:py sys.stderr.write('abc9') ; sys.stderr.write('def') -:py sys.stdout.writelines(iter('abcA')) -:py sys.stderr.writelines(iter('abcB')) -:redir END -:$put =string(substitute(messages, '\d\+', '', 'g')) -:" Test subclassing -:fun Put(...) -: $put =string(a:000) -: return a:000 -:endfun -py << trim EOF - class DupDict(vim.Dictionary): - def __setitem__(self, key, value): - super(DupDict, self).__setitem__(key, value) - super(DupDict, self).__setitem__('dup_' + key, value) - dd = DupDict() - dd['a'] = 'b' - - class DupList(vim.List): - def __getitem__(self, idx): - return [super(DupList, self).__getitem__(idx)] * 2 - - dl = DupList() - dl2 = DupList(iter('abcC')) - dl.extend(dl2[0]) - - class DupFun(vim.Function): - def __call__(self, arg): - return super(DupFun, self).__call__(arg, arg) - - df = DupFun('Put') -EOF -:$put =string(sort(keys(pyeval('dd')))) -:$put =string(pyeval('dl')) -:$put =string(pyeval('dl2')) -:$put =string(pyeval('df(2)')) -:$put =string(pyeval('dl') is# pyeval('dl')) -:$put =string(pyeval('dd') is# pyeval('dd')) -:$put =string(pyeval('df')) -:delfunction Put -py << trim - del DupDict - del DupList - del DupFun - del dd - del dl - del dl2 - del df -. -:" -:" Test chdir -py << trim EOF - import os - fnamemodify = vim.Function('fnamemodify') - cb.append(fnamemodify('.', ':p:h:t')) - cb.append(vim.eval('@%')) - os.chdir('..') - path = fnamemodify('.', ':p:h:t') - if path != 'src': - # Running tests from a shadow directory, so move up another level - # This will result in @% looking like shadow/testdir/test86.in, hence the - # extra fnamemodify - os.chdir('..') - cb.append(fnamemodify('.', ':p:h:t')) - cb.append(fnamemodify(vim.eval('@%'), ':s?^%s.??' % path).replace(os.path.sep, '/')) - os.chdir(path) - del path - else: - cb.append(fnamemodify('.', ':p:h:t')) - cb.append(vim.eval('@%').replace(os.path.sep, '/')) - os.chdir('testdir') - cb.append(fnamemodify('.', ':p:h:t')) - cb.append(vim.eval('@%')) - del fnamemodify -EOF -:" -:" Test errors -:fun F() dict -:endfun -:fun D() -:endfun -py << trim EOF - d = vim.Dictionary() - ned = vim.Dictionary(foo='bar', baz='abcD') - dl = vim.Dictionary(a=1) - dl.locked = True - l = vim.List() - ll = vim.List('abcE') - ll.locked = True - nel = vim.List('abcO') - f = vim.Function('string') - fd = vim.Function('F') - fdel = vim.Function('D') - vim.command('delfunction D') - - def subexpr_test(expr, name, subexprs): - cb.append('>>> Testing %s using %s' % (name, expr)) - for subexpr in subexprs: - ee(expr % subexpr) - cb.append('<<< Finished') - - def stringtochars_test(expr): - return subexpr_test(expr, 'StringToChars', ( - '1', # Fail type checks - 'u"\\0"', # Fail PyString_AsStringAndSize(bytes, , NULL) check - '"\\0"', # Fail PyString_AsStringAndSize(object, , NULL) check - )) - - class Mapping(object): - def __init__(self, d): - self.d = d - - def __getitem__(self, key): - return self.d[key] - - def keys(self): - return self.d.keys() - - def items(self): - return self.d.items() - - def convertfrompyobject_test(expr, recurse=True): - # pydict_to_tv - stringtochars_test(expr % '{%s : 1}') - if recurse: - convertfrompyobject_test(expr % '{"abcF" : %s}', False) - # pymap_to_tv - stringtochars_test(expr % 'Mapping({%s : 1})') - if recurse: - convertfrompyobject_test(expr % 'Mapping({"abcG" : %s})', False) - # pyseq_to_tv - iter_test(expr) - return subexpr_test(expr, 'ConvertFromPyObject', ( - 'None', # Not conversible - '{"": 1}', # Empty key not allowed - '{u"": 1}', # Same, but with unicode object - 'FailingMapping()', # - 'FailingMappingKey()', # - 'FailingNumber()', # - )) - - def convertfrompymapping_test(expr): - convertfrompyobject_test(expr) - return subexpr_test(expr, 'ConvertFromPyMapping', ( - '[]', - )) - - def iter_test(expr): - return subexpr_test(expr, '*Iter*', ( - 'FailingIter()', - 'FailingIterNext()', - )) - - def number_test(expr, natural=False, unsigned=False): - if natural: - unsigned = True - return subexpr_test(expr, 'NumberToLong', ( - '[]', - 'None', - ) + (unsigned and ('-1',) or ()) - + (natural and ('0',) or ())) - - class FailingTrue(object): - def __nonzero__(self): - raise NotImplementedError('bool') - - class FailingIter(object): - def __iter__(self): - raise NotImplementedError('iter') - - class FailingIterNext(object): - def __iter__(self): - return self - - def next(self): - raise NotImplementedError('next') - - class FailingIterNextN(object): - def __init__(self, n): - self.n = n - - def __iter__(self): - return self - - def next(self): - if self.n: - self.n -= 1 - return 1 - else: - raise NotImplementedError('next N') - - class FailingMappingKey(object): - def __getitem__(self, item): - raise NotImplementedError('getitem:mappingkey') - - def keys(self): - return list("abcH") - - class FailingMapping(object): - def __getitem__(self): - raise NotImplementedError('getitem:mapping') - - def keys(self): - raise NotImplementedError('keys') - - class FailingList(list): - def __getitem__(self, idx): - if i == 2: - raise NotImplementedError('getitem:list') - else: - return super(FailingList, self).__getitem__(idx) - - class NoArgsCall(object): - def __call__(self): - pass - - class FailingCall(object): - def __call__(self, path): - raise NotImplementedError('call') - - class FailingNumber(object): - def __int__(self): - raise NotImplementedError('int') - - cb.append("> Output") - cb.append(">> OutputSetattr") - ee('del sys.stdout.softspace') - number_test('sys.stdout.softspace = %s', unsigned=True) - number_test('sys.stderr.softspace = %s', unsigned=True) - ee('assert sys.stdout.isatty()==False') - ee('assert sys.stdout.seekable()==False') - ee('sys.stdout.close()') - ee('sys.stdout.flush()') - ee('assert sys.stderr.isatty()==False') - ee('assert sys.stderr.seekable()==False') - ee('sys.stderr.close()') - ee('sys.stderr.flush()') - ee('sys.stdout.attr = None') - cb.append(">> OutputWrite") - ee('assert sys.stdout.writable()==True') - ee('assert sys.stdout.readable()==False') - ee('assert sys.stderr.writable()==True') - ee('assert sys.stderr.readable()==False') - ee('assert sys.stdout.closed()==False') - ee('assert sys.stderr.closed()==False') - ee('assert sys.stdout.errors=="strict"') - ee('assert sys.stderr.errors=="strict"') - ee('assert sys.stdout.encoding==sys.stderr.encoding') - ee('sys.stdout.write(None)') - cb.append(">> OutputWriteLines") - ee('sys.stdout.writelines(None)') - ee('sys.stdout.writelines([1])') - iter_test('sys.stdout.writelines(%s)') - cb.append("> VimCommand") - stringtochars_test('vim.command(%s)') - ee('vim.command("", 2)') - #! Not checked: vim->python exceptions translating: checked later - cb.append("> VimToPython") - #! Not checked: everything: needs errors in internal python functions - cb.append("> VimEval") - stringtochars_test('vim.eval(%s)') - ee('vim.eval("", FailingTrue())') - #! Not checked: everything: needs errors in internal python functions - cb.append("> VimEvalPy") - stringtochars_test('vim.bindeval(%s)') - ee('vim.eval("", 2)') - #! Not checked: vim->python exceptions translating: checked later - cb.append("> VimStrwidth") - stringtochars_test('vim.strwidth(%s)') - cb.append("> VimForeachRTP") - ee('vim.foreach_rtp(None)') - ee('vim.foreach_rtp(NoArgsCall())') - ee('vim.foreach_rtp(FailingCall())') - ee('vim.foreach_rtp(int, 2)') - cb.append('> import') - old_rtp = vim.options['rtp'] - vim.options['rtp'] = os.getcwd().replace('\\', '\\\\').replace(',', '\\,') - ee('import xxx_no_such_module_xxx') - ee('import failing_import') - ee('import failing') - vim.options['rtp'] = old_rtp - del old_rtp - cb.append("> Options") - cb.append(">> OptionsItem") - ee('vim.options["abcQ"]') - ee('vim.options[""]') - stringtochars_test('vim.options[%s]') - cb.append(">> OptionsContains") - stringtochars_test('%s in vim.options') - cb.append("> Dictionary") - cb.append(">> DictionaryConstructor") - ee('vim.Dictionary("abcI")') - ##! Not checked: py_dict_alloc failure - cb.append(">> DictionarySetattr") - ee('del d.locked') - ee('d.locked = FailingTrue()') - ee('vim.vvars.locked = False') - ee('d.scope = True') - ee('d.xxx = True') - cb.append(">> _DictionaryItem") - ee('d.get("a", 2, 3)') - stringtochars_test('d.get(%s)') - ee('d.pop("a")') - ee('dl.pop("a")') - cb.append(">> DictionaryContains") - ee('"" in d') - ee('0 in d') - cb.append(">> DictionaryIterNext") - ee('for i in ned: ned["a"] = 1') - del i - cb.append(">> DictionaryAssItem") - ee('dl["b"] = 1') - stringtochars_test('d[%s] = 1') - convertfrompyobject_test('d["a"] = %s') - cb.append(">> DictionaryUpdate") - cb.append(">>> kwargs") - cb.append(">>> iter") - ee('d.update(FailingMapping())') - ee('d.update([FailingIterNext()])') - ee('d.update([FailingIterNextN(1)])') - iter_test('d.update(%s)') - convertfrompyobject_test('d.update(%s)') - stringtochars_test('d.update(((%s, 0),))') - convertfrompyobject_test('d.update((("a", %s),))') - cb.append(">> DictionaryPopItem") - ee('d.popitem(1, 2)') - cb.append(">> DictionaryHasKey") - ee('d.has_key()') - cb.append("> List") - cb.append(">> ListConstructor") - ee('vim.List(1, 2)') - ee('vim.List(a=1)') - iter_test('vim.List(%s)') - convertfrompyobject_test('vim.List([%s])') - cb.append(">> ListItem") - ee('l[1000]') - cb.append(">> ListAssItem") - ee('ll[1] = 2') - ee('l[1000] = 3') - cb.append(">> ListAssSlice") - ee('ll[1:100] = "abcJ"') - iter_test('l[:] = %s') - ee('nel[1:10:2] = "abcK"') - cb.append(repr(tuple(nel))) - ee('nel[1:10:2] = "a"') - cb.append(repr(tuple(nel))) - ee('nel[1:1:-1] = "a"') - cb.append(repr(tuple(nel))) - ee('nel[:] = FailingIterNextN(2)') - cb.append(repr(tuple(nel))) - convertfrompyobject_test('l[:] = [%s]') - cb.append(">> ListConcatInPlace") - iter_test('l.extend(%s)') - convertfrompyobject_test('l.extend([%s])') - cb.append(">> ListSetattr") - ee('del l.locked') - ee('l.locked = FailingTrue()') - ee('l.xxx = True') - cb.append("> Function") - cb.append(">> FunctionConstructor") - cb.append(">>> FunctionConstructor") - ee('vim.Function("123")') - ee('vim.Function("xxx_non_existent_function_xxx")') - ee('vim.Function("xxx#non#existent#function#xxx")') - ee('vim.Function("xxx_non_existent_function_xxx2", args=[])') - ee('vim.Function("xxx_non_existent_function_xxx3", self={})') - ee('vim.Function("xxx_non_existent_function_xxx4", args=[], self={})') - cb.append(">>> FunctionNew") - ee('vim.Function("tr", self="abcFuncSelf")') - ee('vim.Function("tr", args=427423)') - ee('vim.Function("tr", self="abcFuncSelf2", args="abcFuncArgs2")') - ee('vim.Function(self="abcFuncSelf2", args="abcFuncArgs2")') - ee('vim.Function("tr", "", self="abcFuncSelf2", args="abcFuncArgs2")') - ee('vim.Function("tr", "")') - cb.append(">> FunctionCall") - convertfrompyobject_test('f(%s)') - convertfrompymapping_test('fd(self=%s)') - cb.append("> TabPage") - cb.append(">> TabPageAttr") - ee('vim.current.tabpage.xxx') - cb.append("> TabList") - cb.append(">> TabListItem") - ee('vim.tabpages[1000]') - cb.append("> Window") - cb.append(">> WindowAttr") - ee('vim.current.window.xxx') - cb.append(">> WindowSetattr") - ee('vim.current.window.buffer = 0') - ee('vim.current.window.cursor = (100000000, 100000000)') - ee('vim.current.window.cursor = True') - number_test('vim.current.window.height = %s', unsigned=True) - number_test('vim.current.window.width = %s', unsigned=True) - ee('vim.current.window.xxxxxx = True') - cb.append("> WinList") - cb.append(">> WinListItem") - ee('vim.windows[1000]') - cb.append("> Buffer") - cb.append(">> StringToLine (indirect)") - ee('vim.current.buffer[0] = u"\\na"') - ee('vim.current.buffer[0] = "\\na"') - cb.append(">> SetBufferLine (indirect)") - ee('vim.current.buffer[0] = True') - cb.append(">> SetBufferLineList (indirect)") - ee('vim.current.buffer[:] = True') - ee('vim.current.buffer[:] = ["\\na", "bc"]') - cb.append(">> InsertBufferLines (indirect)") - ee('vim.current.buffer.append(None)') - ee('vim.current.buffer.append(["\\na", "bc"])') - ee('vim.current.buffer.append("\\nbc")') - cb.append(">> RBItem") - ee('vim.current.buffer[100000000]') - cb.append(">> RBAsItem") - ee('vim.current.buffer[100000000] = ""') - cb.append(">> BufferAttr") - ee('vim.current.buffer.xxx') - cb.append(">> BufferSetattr") - ee('vim.current.buffer.name = True') - ee('vim.current.buffer.xxx = True') - cb.append(">> BufferMark") - ee('vim.current.buffer.mark(0)') - ee('vim.current.buffer.mark("abcM")') - ee('vim.current.buffer.mark("!")') - cb.append(">> BufferRange") - ee('vim.current.buffer.range(1, 2, 3)') - cb.append("> BufMap") - cb.append(">> BufMapItem") - ee('vim.buffers[100000000]') - number_test('vim.buffers[%s]', natural=True) - cb.append("> Current") - cb.append(">> CurrentGetattr") - ee('vim.current.xxx') - cb.append(">> CurrentSetattr") - ee('vim.current.line = True') - ee('vim.current.buffer = True') - ee('vim.current.window = True') - ee('vim.current.tabpage = True') - ee('vim.current.xxx = True') - del d - del ned - del dl - del l - del ll - del nel - del f - del fd - del fdel - del subexpr_test - del stringtochars_test - del Mapping - del convertfrompyobject_test - del convertfrompymapping_test - del iter_test - del number_test - del FailingTrue - del FailingIter - del FailingIterNext - del FailingIterNextN - del FailingMapping - del FailingMappingKey - del FailingList - del NoArgsCall - del FailingCall - del FailingNumber -EOF -:delfunction F -:" -:" Test import -py << trim EOF - sys.path.insert(0, os.path.join(os.getcwd(), 'python_before')) - sys.path.append(os.path.join(os.getcwd(), 'python_after')) - vim.options['rtp'] = os.getcwd().replace(',', '\\,').replace('\\', '\\\\') - l = [] - def callback(path): - l.append(path[-len('/testdir'):].replace(os.path.sep, '/')) - vim.foreach_rtp(callback) - cb.append(repr(l)) - del l - def callback(path): - return path[-len('/testdir'):].replace(os.path.sep, '/') - cb.append(repr(vim.foreach_rtp(callback))) - del callback - from module import dir as d - from modulex import ddir - cb.append(d + ',' + ddir) - import before - cb.append(before.dir) - import after - cb.append(after.dir) - import topmodule as tm - import topmodule.submodule as tms - import topmodule.submodule.subsubmodule.subsubsubmodule as tmsss - cb.append(tm.__file__.replace('.pyc', '.py').replace(os.path.sep, '/')[-len('modulex/topmodule/__init__.py'):]) - cb.append(tms.__file__.replace('.pyc', '.py').replace(os.path.sep, '/')[-len('modulex/topmodule/submodule/__init__.py'):]) - cb.append(tmsss.__file__.replace('.pyc', '.py').replace(os.path.sep, '/')[-len('modulex/topmodule/submodule/subsubmodule/subsubsubmodule.py'):]) - del before - del after - del d - del ddir - del tm - del tms - del tmsss -EOF -:" -:" Test exceptions -:fun Exe(e) -: execute a:e -:endfun -py << trim EOF - Exe = vim.bindeval('function("Exe")') - ee('vim.command("throw \'abcN\'")') - ee('Exe("throw \'def\'")') - ee('vim.eval("Exe(\'throw \'\'ghi\'\'\')")') - ee('vim.eval("Exe(\'echoerr \'\'jkl\'\'\')")') - ee('vim.eval("Exe(\'xxx_non_existent_command_xxx\')")') - ee('vim.eval("xxx_unknown_function_xxx()")') - ee('vim.bindeval("Exe(\'xxx_non_existent_command_xxx\')")') - del Exe -EOF -:delfunction Exe -:" -:" Regression: interrupting vim.command propagates to next vim.command -py << trim EOF - def test_keyboard_interrupt(): - try: - vim.command('while 1 | endwhile') - except KeyboardInterrupt: - cb.append('Caught KeyboardInterrupt') - except Exception: - cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info())) - else: - cb.append('!!!!!!!! No exception') - try: - vim.command('$ put =\'Running :put\'') - except KeyboardInterrupt: - cb.append('!!!!!!!! Caught KeyboardInterrupt') - except Exception: - cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info())) - else: - cb.append('No exception') -EOF -:debuggreedy -:call inputsave() -:call feedkeys("s\ns\ns\ns\nq\n") -:redir => output -:debug silent! py test_keyboard_interrupt() -:redir END -:0 debuggreedy -:call inputrestore() -:silent $put =output -:unlet output -:py del test_keyboard_interrupt -:" -:" Cleanup -py << trim EOF - del cb - del ee - del emsg - del sys - del os - del vim -EOF -:endfun -:" -:fun RunTest() -:let checkrefs = !empty($PYTHONDUMPREFS) -:let start = getline(1, '$') -:for i in range(checkrefs ? 10 : 1) -: if i != 0 -: %d _ -: call setline(1, start) -: endif -: call Test() -: if i == 0 -: let result = getline(1, '$') -: endif -:endfor -:if checkrefs -: %d _ -: call setline(1, result) -:endif -:endfun -:" -:call RunTest() -:delfunction RunTest -:delfunction Test -:call garbagecollect(1) -:" -:/^start:/,$wq! test.out -:" vim: et ts=4 isk-=\: -:while getchar(0) isnot 0|endwhile -ENDTEST - -start: diff --git a/src/testdir/test86.ok b/src/testdir/test86.ok deleted file mode 100644 index 4f6d4b3060..0000000000 --- a/src/testdir/test86.ok +++ /dev/null @@ -1,1445 +0,0 @@ -start: -[1, 'as''d', [1, 2, function('strlen'), {'a': 1}]] -[1, 2, function('strlen'), {'a': 1}] -Vim(put):E684: -[0, 'as''d', [1, 2, function('strlen'), {'a': 1}]] -[0, function('strlen'), [1, 2, function('strlen'), {'a': 1}]] -1 -['-1', '0', '1', 'b', 'f'] -['asd', -1L, , , ] -[('-1', ), ('0', -1L), ('1', 'asd'), ('b', ), ('f', )] -'-1' : {'a': 1} -'0' : -1 -'1' : 'asd' -'b' : [1, 2, function('strlen')] -'f' : function('1') -[0, function('strlen')] -[3] -[1, 2, function('strlen')] -[1, 2, function('strlen')] -1 -'asd' -2 -True -False -True -False -['0'] -{'0': -1} -('0', -1L) -None -[] -[0, 1, 2, 3] -[0, 1, 2, 3] -[0, 1, 3] -[0, 1] -[0, 1] -[0, 1] -[0, 1, 2, 3] -[0, 1, 2, 3] -[0, 2, 3] -[2, 3] -[2, 3] -[2, 3] -[1, 3] -[0, 2] -[0, 1, 2, 3] -['a', 0, 1, 2, 3] -[0, 'b', 2, 3] -[0, 1, 'c'] -[0, 1, 2, 3, 'd'] -[0, 1, 2, 'e', 3] -['f', 2, 3] -[0, 1, 'g', 2, 3] -['h'] -[0, 1, 10, 3, 20, 5, 6, 7] -[0, 1, 2, 3, 20, 5, 10, 7] -[0, 1, 2, 3, 4, 5, 6, 7] -[0, 1, 2, 3, 4, 5, 6, 7] -[0, 1, 2, 3, 4, 5, 6, 7] -l[2] threw vim.error: error:('list is locked',) -[0, 1, 2, 3] -[function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd'] -[function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}] -[function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}, 'New'] -l[1](1, 2, 3):error:('Vim:E725: Calling dict function without Dictionary: DictNew',) -f(1, 2, 3):error:('Vim:E117: Unknown function: New',) -[0.0, 0.0] -KeyError -TypeError -TypeError -ValueError -TypeError -TypeError -KeyError -KeyError -d : locked:0;scope:0 -dl : locked:1;scope:0 -v: : locked:2;scope:1 -g: : locked:0;scope:2 -d:{'abc2': 1} -dl:{'def': 1} -l : locked:0 -ll : locked:1 -l:[0] -ll:[1] -[0, 1, 2] -['a', 'b'] -['c', 1] -['d', ['e']] -pyeval("None") = v:none -0.0 -"\0": Vim(let):E859: -{"\0": 1}: Vim(let):E859: -undefined_name: Vim(let):NameE -vim: Vim(let):E859: -[1] -[1, 10, 11, 10, 11, 10, 11, 10, 11, 10, 11, 10, 1] -[0, 1, 2, 3] -[2, 3, 4, 5] -[0, 1] -[4, 5] -[2, 3] -[] -[2, 3] -[] -[0, 1, 2, 3, 4, 5] -[0, 1, 2, 3, 4, 5] -[0, 1, 2, 3, 4, 5] -[4, 3] -[0, 2, 4] -[] -Abc -bac -def -bar -jkl -wopts iters equal: 1 -bopts iters equal: 1 ->>> paste - g/w/b:1/0/0 - g/w/b (in):1/0/0 - p/gopts1: False - p/wopts1! KeyError - inv: 2! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1! KeyError - inv: 2! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: 1 - W: 1:1 2:1 3:1 4:1 - B: 1:1 2:1 3:1 4:1 - del wopts3! KeyError - del bopts3! KeyError - G: 1 - W: 1:1 2:1 3:1 4:1 - B: 1:1 2:1 3:1 4:1 ->>> previewheight - g/w/b:1/0/0 - g/w/b (in):1/0/0 - p/gopts1: 12 - inv: 'a'! TypeError - p/wopts1! KeyError - inv: 'a'! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1! KeyError - inv: 'a'! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: 5 - W: 1:5 2:5 3:5 4:5 - B: 1:5 2:5 3:5 4:5 - del wopts3! KeyError - del bopts3! KeyError - G: 5 - W: 1:5 2:5 3:5 4:5 - B: 1:5 2:5 3:5 4:5 ->>> operatorfunc - g/w/b:1/0/0 - g/w/b (in):1/0/0 - p/gopts1: '' - inv: 2! TypeError - p/wopts1! KeyError - inv: 2! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1! KeyError - inv: 2! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: 'A' - W: 1:'A' 2:'A' 3:'A' 4:'A' - B: 1:'A' 2:'A' 3:'A' 4:'A' - del wopts3! KeyError - del bopts3! KeyError - G: 'A' - W: 1:'A' 2:'A' 3:'A' 4:'A' - B: 1:'A' 2:'A' 3:'A' 4:'A' ->>> number - g/w/b:0/1/0 - g/w/b (in):0/1/0 - p/gopts1! KeyError - inv: 0! KeyError - gopts1! KeyError - p/wopts1: False - p/bopts1! KeyError - inv: 0! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: 0 - W: 1:1 2:1 3:0 4:0 - B: 1:1 2:1 3:0 4:0 - del wopts3! ValueError - del bopts3! KeyError - G: 0 - W: 1:1 2:1 3:0 4:0 - B: 1:1 2:1 3:0 4:0 ->>> numberwidth - g/w/b:0/1/0 - g/w/b (in):0/1/0 - p/gopts1! KeyError - inv: -100! KeyError - gopts1! KeyError - p/wopts1: 8 - inv: -100! error - p/bopts1! KeyError - inv: -100! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: 8 - W: 1:3 2:5 3:2 4:8 - B: 1:3 2:5 3:2 4:8 - del wopts3! ValueError - del bopts3! KeyError - G: 8 - W: 1:3 2:5 3:2 4:8 - B: 1:3 2:5 3:2 4:8 ->>> colorcolumn - g/w/b:0/1/0 - g/w/b (in):0/1/0 - p/gopts1! KeyError - inv: 'abc4'! KeyError - gopts1! KeyError - p/wopts1: '' - inv: 'abc4'! error - p/bopts1! KeyError - inv: 'abc4'! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: '' - W: 1:'+2' 2:'+3' 3:'+1' 4:'' - B: 1:'+2' 2:'+3' 3:'+1' 4:'' - del wopts3! ValueError - del bopts3! KeyError - G: '' - W: 1:'+2' 2:'+3' 3:'+1' 4:'' - B: 1:'+2' 2:'+3' 3:'+1' 4:'' ->>> statusline - g/w/b:1/1/0 - g/w/b (in):1/1/0 - p/gopts1: '' - inv: 0! TypeError - p/wopts1: None - inv: 0! TypeError - p/bopts1! KeyError - inv: 0! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: '1' - W: 1:'2' 2:'4' 3:'1' 4:'1' - B: 1:'2' 2:'4' 3:'1' 4:'1' - del bopts3! KeyError - G: '1' - W: 1:'2' 2:'1' 3:'1' 4:'1' - B: 1:'2' 2:'1' 3:'1' 4:'1' ->>> autoindent - g/w/b:0/0/1 - g/w/b (in):0/0/1 - p/gopts1! KeyError - inv: 2! KeyError - gopts1! KeyError - p/wopts1! KeyError - inv: 2! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1: False - G: 0 - W: 1:0 2:1 3:0 4:1 - B: 1:0 2:1 3:0 4:1 - del wopts3! KeyError - del bopts3! ValueError - G: 0 - W: 1:0 2:1 3:0 4:1 - B: 1:0 2:1 3:0 4:1 ->>> shiftwidth - g/w/b:0/0/1 - g/w/b (in):0/0/1 - p/gopts1! KeyError - inv: 3! KeyError - gopts1! KeyError - p/wopts1! KeyError - inv: 3! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1: 8 - G: 8 - W: 1:0 2:2 3:8 4:1 - B: 1:0 2:2 3:8 4:1 - del wopts3! KeyError - del bopts3! ValueError - G: 8 - W: 1:0 2:2 3:8 4:1 - B: 1:0 2:2 3:8 4:1 ->>> omnifunc - g/w/b:0/0/1 - g/w/b (in):0/0/1 - p/gopts1! KeyError - inv: 1! KeyError - gopts1! KeyError - p/wopts1! KeyError - inv: 1! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1: '' - inv: 1! TypeError - G: '' - W: 1:'A' 2:'B' 3:'' 4:'C' - B: 1:'A' 2:'B' 3:'' 4:'C' - del wopts3! KeyError - del bopts3! ValueError - G: '' - W: 1:'A' 2:'B' 3:'' 4:'C' - B: 1:'A' 2:'B' 3:'' 4:'C' ->>> preserveindent - g/w/b:0/0/1 - g/w/b (in):0/0/1 - p/gopts1! KeyError - inv: 2! KeyError - gopts1! KeyError - p/wopts1! KeyError - inv: 2! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1: False - G: 0 - W: 1:0 2:1 3:0 4:1 - B: 1:0 2:1 3:0 4:1 - del wopts3! KeyError - del bopts3! ValueError - G: 0 - W: 1:0 2:1 3:0 4:1 - B: 1:0 2:1 3:0 4:1 ->>> path - g/w/b:1/0/1 - g/w/b (in):1/0/1 - p/gopts1: '.,..,,' - inv: 0! TypeError - p/wopts1! KeyError - inv: 0! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1: None - inv: 0! TypeError - G: '.,,' - W: 1:'.,,' 2:',,' 3:'.,,' 4:'.' - B: 1:'.,,' 2:',,' 3:'.,,' 4:'.' - del wopts3! KeyError - G: '.,,' - W: 1:'.,,' 2:',,' 3:'.,,' 4:'.,,' - B: 1:'.,,' 2:',,' 3:'.,,' 4:'.,,' -First line -First line -def -First line -Second line -Third line -(7, 2) - -baz -bar -Second line -Third line -foo -1:BufFilePre:1 -1:BufFilePost:1 -testdir/foo -5:BufFilePre:5 -5:BufFilePost:5 -testdir/bar -1:BufFilePre:1 -1:BufFilePost:1 -testdir/test86.in -valid: b:False, cb:True -i: -i2: -i: -i3: -1:= -8:= -9:= -10:= -4 -i4: -i4: -StopIteration -Number of tabs: 4 -Current tab pages: - (1): 1 windows, current is - Windows: - (1): displays buffer ; cursor is at (37, 0) - (2): 1 windows, current is - Windows: - (1): displays buffer ; cursor is at (1, 0) - (3): 2 windows, current is - Windows: - (1): displays buffer ; cursor is at (1, 0) - (2): displays buffer ; cursor is at (1, 0) - (4): 4 windows, current is - Windows: - (1): displays buffer ; cursor is at (1, 0) - (2): displays buffer ; cursor is at (1, 0) - (3): displays buffer ; cursor is at (1, 0) - (4): displays buffer ; cursor is at (1, 0) -Number of windows in current tab page: 4 -Current tab page: -Current window: : is -Current buffer: : is is -ValueError at assigning foreign tab window -Type error at assigning None to vim.current.window -Type error at assigning None to vim.current.tabpage -Type error at assigning None to vim.current.buffer -Current tab page: -Current window: -Current buffer: -Current line: 'Type error at assigning None to vim.current.buffer' -w.valid: [True, False] -t.valid: [True, False, True, False] -vim.vars:Dictionary:True -vim.options:Options:True -vim.bindeval("{}"):Dictionary:True -vim.bindeval("[]"):List:True -vim.bindeval("function('tr')"):Function:True -vim.current.buffer:Buffer:True -vim.current.range:Range:True -vim.current.window:Window:True -vim.current.tabpage:TabPage:True -current:__dir__,__members__,buffer,line,range,tabpage,window -buffer:__dir__,__members__,append,mark,name,number,options,range,valid,vars -window:__dir__,__members__,buffer,col,cursor,height,number,options,row,tabpage,valid,vars,width -tabpage:__dir__,__members__,number,valid,vars,window,windows -range:__dir__,__members__,append,end,start -dictionary:__dir__,__members__,get,has_key,items,keys,locked,pop,popitem,scope,update,values -list:__dir__,__members__,extend,locked -function:__dir__,__members__,args,auto_rebind,self,softspace -output:__dir__,__members__,close,closed,flush,isatty,readable,seekable,softspace,writable,write,writelines -{} -{'a': 1} -{'a': 1} -[] -['a', 'b', 'c', '7'] -function('tr') -function('tr', [123, 3, 4]) -function('tr') -function('tr', {}) -function('tr', [123, 3, 4], {}) -auto_rebind -function('tr') -function('tr', [123, 3, 4]) -function('tr') -function('tr', {}) -function('tr', [123, 3, 4], {}) -a: -pa1: -pa2: -pa3: -pa4: -sa: -psa1: -psa2: -psa3: -psa4: -psa5: -psa6: -psa7: -psa8: -psa9: -psaA: -psaB: -psaC: -psar: -s(a): function('Args') -s(pa1): function('Args', ['abcArgsPA1']) -s(pa2): function('Args') -s(pa3): function('Args', ['abcArgsPA3'], {'abcSelfPA3': 'abcSelfPA3Val'}) -s(pa4): function('Args', {'abcSelfPA4': 'abcSelfPA4Val'}) -s(sa): function('SelfArgs') -s(psa1): function('SelfArgs', ['abcArgsPSA1']) -s(psa2): function('SelfArgs') -s(psa3): function('SelfArgs', ['abcArgsPSA3'], {'abcSelfPSA3': 'abcSelfPSA3Val'}) -s(psa4): function('SelfArgs', {'abcSelfPSA4': 'abcSelfPSA4Val'}) -s(psa5): function('SelfArgs', {'abcSelfPSA5': 'abcSelfPSA5Val'}) -s(psa6): function('SelfArgs', ['abcArgsPSA6'], {'abcSelfPSA6': 'abcSelfPSA6Val'}) -s(psa7): function('SelfArgs', ['abcArgsPSA7']) -s(psa8): function('SelfArgs') -s(psa9): function('SelfArgs', {'abcSelfPSA9': 'abcSelfPSA9Val'}) -s(psaA): function('SelfArgs', ['abcArgsPSAA'], {'abcSelfPSAA': 'abcSelfPSAAVal'}) -s(psaB): function('SelfArgs', ['abcArgsPSAB']) -s(psaC): function('SelfArgs') -d.sa(): [[], {'f': function('SelfArgs')}] -d.psa1(): [['abcArgsPSA1'], {'f': function('SelfArgs', ['abcArgsPSA1'])}] -d.psa2(): [[], {'f': function('SelfArgs')}] -d.psa3(): [['abcArgsPSA3'], {'abcSelfPSA3': 'abcSelfPSA3Val'}] -d.psa4(): [[], {'abcSelfPSA4': 'abcSelfPSA4Val'}] -d.psa5(): [[], {'abcSelfPSA5': 'abcSelfPSA5Val'}] -d.psa6(): [['abcArgsPSA6'], {'abcSelfPSA6': 'abcSelfPSA6Val'}] -d.psa7(): [['abcArgsPSA7'], {'f': function('SelfArgs', ['abcArgsPSA7'])}] -d.psa8(): [[], {'f': function('SelfArgs')}] -d.psa9(): [[], {'f': function('SelfArgs', {'abcSelfPSA9': 'abcSelfPSA9Val'})}] -d.psaA(): [['abcArgsPSAA'], {'f': function('SelfArgs', ['abcArgsPSAA'], {'abcSelfPSAA': 'abcSelfPSAAVal'})}] -d.psaB(): [['abcArgsPSAB'], {'f': function('SelfArgs', ['abcArgsPSAB'])}] -d.psaC(): [[], {'f': function('SelfArgs')}] -a(): !result: [] -pa1(): !result: ['abcArgsPA1'] -pa2(): !result: [] -pa3(): !result: ['abcArgsPA3'] -pa4(): !result: [] -sa(): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) -psa1(): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) -psa2(): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) -psa3(): !result: [['abcArgsPSA3'], {'abcSelfPSA3': 'abcSelfPSA3Val'}] -psa4(): !result: [[], {'abcSelfPSA4': 'abcSelfPSA4Val'}] -a(42, 43): !result: [42, 43] -pa1(42, 43): !result: ['abcArgsPA1', 42, 43] -pa2(42, 43): !result: [42, 43] -pa3(42, 43): !result: ['abcArgsPA3', 42, 43] -pa4(42, 43): !result: [42, 43] -sa(42, 43): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) -psa1(42, 43): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) -psa2(42, 43): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) -psa3(42, 43): !result: [['abcArgsPSA3', 42, 43], {'abcSelfPSA3': 'abcSelfPSA3Val'}] -psa4(42, 43): !result: [[42, 43], {'abcSelfPSA4': 'abcSelfPSA4Val'}] -a(42, self={"20": 1}): !result: [42] -pa1(42, self={"20": 1}): !result: ['abcArgsPA1', 42] -pa2(42, self={"20": 1}): !result: [42] -pa3(42, self={"20": 1}): !result: ['abcArgsPA3', 42] -pa4(42, self={"20": 1}): !result: [42] -sa(42, self={"20": 1}): !result: [[42], {'20': 1}] -psa1(42, self={"20": 1}): !result: [['abcArgsPSA1', 42], {'20': 1}] -psa2(42, self={"20": 1}): !result: [[42], {'20': 1}] -psa3(42, self={"20": 1}): !result: [['abcArgsPSA3', 42], {'20': 1}] -psa4(42, self={"20": 1}): !result: [[42], {'20': 1}] -a(self={"20": 1}): !result: [] -pa1(self={"20": 1}): !result: ['abcArgsPA1'] -pa2(self={"20": 1}): !result: [] -pa3(self={"20": 1}): !result: ['abcArgsPA3'] -pa4(self={"20": 1}): !result: [] -sa(self={"20": 1}): !result: [[], {'20': 1}] -psa1(self={"20": 1}): !result: [['abcArgsPSA1'], {'20': 1}] -psa2(self={"20": 1}): !result: [[], {'20': 1}] -psa3(self={"20": 1}): !result: [['abcArgsPSA3'], {'20': 1}] -psa4(self={"20": 1}): !result: [[], {'20': 1}] -a.args: None -pa1.args: ['abcArgsPA1'] -pa2.args: None -pa3.args: ['abcArgsPA3'] -pa4.args: None -sa.args: None -psa1.args: ['abcArgsPSA1'] -psa2.args: None -psa3.args: ['abcArgsPSA3'] -psa4.args: None -a.self: None -pa1.self: None -pa2.self: None -pa3.self: {'abcSelfPA3': 'abcSelfPA3Val'} -pa4.self: {'abcSelfPA4': 'abcSelfPA4Val'} -sa.self: None -psa1.self: None -psa2.self: None -psa3.self: {'abcSelfPSA3': 'abcSelfPSA3Val'} -psa4.self: {'abcSelfPSA4': 'abcSelfPSA4Val'} -a.name: 'Args' -pa1.name: 'Args' -pa2.name: 'Args' -pa3.name: 'Args' -pa4.name: 'Args' -sa.name: 'SelfArgs' -psa1.name: 'SelfArgs' -psa2.name: 'SelfArgs' -psa3.name: 'SelfArgs' -psa4.name: 'SelfArgs' -a.auto_rebind: 1 -pa1.auto_rebind: 1 -pa2.auto_rebind: 1 -pa3.auto_rebind: 0 -pa4.auto_rebind: 0 -sa.auto_rebind: 1 -psa1.auto_rebind: 1 -psa2.auto_rebind: 1 -psa3.auto_rebind: 0 -psa4.auto_rebind: 0 -psa5.auto_rebind: 0 -psa6.auto_rebind: 0 -psa7.auto_rebind: 1 -psa8.auto_rebind: 1 -psa9.auto_rebind: 1 -psaA.auto_rebind: 1 -psaB.auto_rebind: 1 -psaC.auto_rebind: 1 -' -abcdef -Error detected while processing function RunTest[]..Test: -line : -abcdef -abcA -line : -abcB' -['a', 'dup_a'] -['a', 'a'] -['a', 'b', 'c', 'C'] -[2, 2] -[2, 2] -1 -1 -function('Put') -testdir -test86.in -src -testdir/test86.in -testdir -test86.in -> Output ->> OutputSetattr -del sys.stdout.softspace:AttributeError:('cannot delete OutputObject attributes',) ->>> Testing NumberToLong using sys.stdout.softspace = %s -sys.stdout.softspace = []:TypeError:('expected int(), long() or something supporting coercing to long(), but got list',) -sys.stdout.softspace = None:TypeError:('expected int(), long() or something supporting coercing to long(), but got NoneType',) -sys.stdout.softspace = -1:ValueError:('number must be greater or equal to zero',) -<<< Finished ->>> Testing NumberToLong using sys.stderr.softspace = %s -sys.stderr.softspace = []:TypeError:('expected int(), long() or something supporting coercing to long(), but got list',) -sys.stderr.softspace = None:TypeError:('expected int(), long() or something supporting coercing to long(), but got NoneType',) -sys.stderr.softspace = -1:ValueError:('number must be greater or equal to zero',) -<<< Finished -assert sys.stdout.isatty()==False:NOT FAILED -assert sys.stdout.seekable()==False:NOT FAILED -sys.stdout.close():NOT FAILED -sys.stdout.flush():NOT FAILED -assert sys.stderr.isatty()==False:NOT FAILED -assert sys.stderr.seekable()==False:NOT FAILED -sys.stderr.close():NOT FAILED -sys.stderr.flush():NOT FAILED -sys.stdout.attr = None:AttributeError:('invalid attribute: attr',) ->> OutputWrite -assert sys.stdout.writable()==True:NOT FAILED -assert sys.stdout.readable()==False:NOT FAILED -assert sys.stderr.writable()==True:NOT FAILED -assert sys.stderr.readable()==False:NOT FAILED -assert sys.stdout.closed()==False:NOT FAILED -assert sys.stderr.closed()==False:NOT FAILED -assert sys.stdout.errors=="strict":NOT FAILED -assert sys.stderr.errors=="strict":NOT FAILED -assert sys.stdout.encoding==sys.stderr.encoding:NOT FAILED -sys.stdout.write(None):TypeError:('coercing to Unicode: need string or buffer, NoneType found',) ->> OutputWriteLines -sys.stdout.writelines(None):TypeError:("'NoneType' object is not iterable",) -sys.stdout.writelines([1]):TypeError:('coercing to Unicode: need string or buffer, int found',) ->>> Testing *Iter* using sys.stdout.writelines(%s) -sys.stdout.writelines(FailingIter()):NotImplementedError:('iter',) -sys.stdout.writelines(FailingIterNext()):NotImplementedError:('next',) -<<< Finished -> VimCommand ->>> Testing StringToChars using vim.command(%s) -vim.command(1):TypeError:('expected str() or unicode() instance, but got int',) -vim.command(u"\0"):TypeError:('expected string without null bytes',) -vim.command("\0"):TypeError:('expected string without null bytes',) -<<< Finished -vim.command("", 2):TypeError:('command() takes exactly one argument (2 given)',) -> VimToPython -> VimEval ->>> Testing StringToChars using vim.eval(%s) -vim.eval(1):TypeError:('expected str() or unicode() instance, but got int',) -vim.eval(u"\0"):TypeError:('expected string without null bytes',) -vim.eval("\0"):TypeError:('expected string without null bytes',) -<<< Finished -vim.eval("", FailingTrue()):TypeError:('function takes exactly 1 argument (2 given)',) -> VimEvalPy ->>> Testing StringToChars using vim.bindeval(%s) -vim.bindeval(1):TypeError:('expected str() or unicode() instance, but got int',) -vim.bindeval(u"\0"):TypeError:('expected string without null bytes',) -vim.bindeval("\0"):TypeError:('expected string without null bytes',) -<<< Finished -vim.eval("", 2):TypeError:('function takes exactly 1 argument (2 given)',) -> VimStrwidth ->>> Testing StringToChars using vim.strwidth(%s) -vim.strwidth(1):TypeError:('expected str() or unicode() instance, but got int',) -vim.strwidth(u"\0"):TypeError:('expected string without null bytes',) -vim.strwidth("\0"):TypeError:('expected string without null bytes',) -<<< Finished -> VimForeachRTP -vim.foreach_rtp(None):TypeError:("'NoneType' object is not callable",) -vim.foreach_rtp(NoArgsCall()):TypeError:('__call__() takes exactly 1 argument (2 given)',) -vim.foreach_rtp(FailingCall()):NotImplementedError:('call',) -vim.foreach_rtp(int, 2):TypeError:('foreach_rtp() takes exactly one argument (2 given)',) -> import -import xxx_no_such_module_xxx:ImportError:('No module named xxx_no_such_module_xxx',) -import failing_import:ImportError:() -import failing:NotImplementedError:() -> Options ->> OptionsItem -vim.options["abcQ"]:KeyError:('abcQ',) -vim.options[""]:ValueError:('empty keys are not allowed',) ->>> Testing StringToChars using vim.options[%s] -vim.options[1]:TypeError:('expected str() or unicode() instance, but got int',) -vim.options[u"\0"]:TypeError:('expected string without null bytes',) -vim.options["\0"]:TypeError:('expected string without null bytes',) -<<< Finished ->> OptionsContains ->>> Testing StringToChars using %s in vim.options -1 in vim.options:TypeError:('expected str() or unicode() instance, but got int',) -u"\0" in vim.options:TypeError:('expected string without null bytes',) -"\0" in vim.options:TypeError:('expected string without null bytes',) -<<< Finished -> Dictionary ->> DictionaryConstructor -vim.Dictionary("abcI"):ValueError:('expected sequence element of size 2, but got sequence of size 1',) ->> DictionarySetattr -del d.locked:AttributeError:('cannot delete vim.Dictionary attributes',) -d.locked = FailingTrue():NotImplementedError:('bool',) -vim.vvars.locked = False:TypeError:('cannot modify fixed dictionary',) -d.scope = True:AttributeError:('cannot set attribute scope',) -d.xxx = True:AttributeError:('cannot set attribute xxx',) ->> _DictionaryItem -d.get("a", 2, 3):TypeError:('function takes at most 2 arguments (3 given)',) ->>> Testing StringToChars using d.get(%s) -d.get(1):TypeError:('expected str() or unicode() instance, but got int',) -d.get(u"\0"):TypeError:('expected string without null bytes',) -d.get("\0"):TypeError:('expected string without null bytes',) -<<< Finished -d.pop("a"):KeyError:('a',) -dl.pop("a"):error:('dictionary is locked',) ->> DictionaryContains -"" in d:ValueError:('empty keys are not allowed',) -0 in d:TypeError:('expected str() or unicode() instance, but got int',) ->> DictionaryIterNext -for i in ned: ned["a"] = 1:RuntimeError:('hashtab changed during iteration',) ->> DictionaryAssItem -dl["b"] = 1:error:('dictionary is locked',) ->>> Testing StringToChars using d[%s] = 1 -d[1] = 1:TypeError:('expected str() or unicode() instance, but got int',) -d[u"\0"] = 1:TypeError:('expected string without null bytes',) -d["\0"] = 1:TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d["a"] = {%s : 1} -d["a"] = {1 : 1}:TypeError:('expected str() or unicode() instance, but got int',) -d["a"] = {u"\0" : 1}:TypeError:('expected string without null bytes',) -d["a"] = {"\0" : 1}:TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d["a"] = {"abcF" : {%s : 1}} -d["a"] = {"abcF" : {1 : 1}}:TypeError:('expected str() or unicode() instance, but got int',) -d["a"] = {"abcF" : {u"\0" : 1}}:TypeError:('expected string without null bytes',) -d["a"] = {"abcF" : {"\0" : 1}}:TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d["a"] = {"abcF" : Mapping({%s : 1})} -d["a"] = {"abcF" : Mapping({1 : 1})}:TypeError:('expected str() or unicode() instance, but got int',) -d["a"] = {"abcF" : Mapping({u"\0" : 1})}:TypeError:('expected string without null bytes',) -d["a"] = {"abcF" : Mapping({"\0" : 1})}:TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using d["a"] = {"abcF" : %s} -d["a"] = {"abcF" : FailingIter()}:TypeError:('unable to convert FailingIter to a Vim structure',) -d["a"] = {"abcF" : FailingIterNext()}:NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using d["a"] = {"abcF" : %s} -d["a"] = {"abcF" : None}:NOT FAILED -d["a"] = {"abcF" : {"": 1}}:ValueError:('empty keys are not allowed',) -d["a"] = {"abcF" : {u"": 1}}:ValueError:('empty keys are not allowed',) -d["a"] = {"abcF" : FailingMapping()}:NotImplementedError:('keys',) -d["a"] = {"abcF" : FailingMappingKey()}:NotImplementedError:('getitem:mappingkey',) -d["a"] = {"abcF" : FailingNumber()}:TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing StringToChars using d["a"] = Mapping({%s : 1}) -d["a"] = Mapping({1 : 1}):TypeError:('expected str() or unicode() instance, but got int',) -d["a"] = Mapping({u"\0" : 1}):TypeError:('expected string without null bytes',) -d["a"] = Mapping({"\0" : 1}):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d["a"] = Mapping({"abcG" : {%s : 1}}) -d["a"] = Mapping({"abcG" : {1 : 1}}):TypeError:('expected str() or unicode() instance, but got int',) -d["a"] = Mapping({"abcG" : {u"\0" : 1}}):TypeError:('expected string without null bytes',) -d["a"] = Mapping({"abcG" : {"\0" : 1}}):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d["a"] = Mapping({"abcG" : Mapping({%s : 1})}) -d["a"] = Mapping({"abcG" : Mapping({1 : 1})}):TypeError:('expected str() or unicode() instance, but got int',) -d["a"] = Mapping({"abcG" : Mapping({u"\0" : 1})}):TypeError:('expected string without null bytes',) -d["a"] = Mapping({"abcG" : Mapping({"\0" : 1})}):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using d["a"] = Mapping({"abcG" : %s}) -d["a"] = Mapping({"abcG" : FailingIter()}):TypeError:('unable to convert FailingIter to a Vim structure',) -d["a"] = Mapping({"abcG" : FailingIterNext()}):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using d["a"] = Mapping({"abcG" : %s}) -d["a"] = Mapping({"abcG" : None}):NOT FAILED -d["a"] = Mapping({"abcG" : {"": 1}}):ValueError:('empty keys are not allowed',) -d["a"] = Mapping({"abcG" : {u"": 1}}):ValueError:('empty keys are not allowed',) -d["a"] = Mapping({"abcG" : FailingMapping()}):NotImplementedError:('keys',) -d["a"] = Mapping({"abcG" : FailingMappingKey()}):NotImplementedError:('getitem:mappingkey',) -d["a"] = Mapping({"abcG" : FailingNumber()}):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing *Iter* using d["a"] = %s -d["a"] = FailingIter():TypeError:('unable to convert FailingIter to a Vim structure',) -d["a"] = FailingIterNext():NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using d["a"] = %s -d["a"] = None:NOT FAILED -d["a"] = {"": 1}:ValueError:('empty keys are not allowed',) -d["a"] = {u"": 1}:ValueError:('empty keys are not allowed',) -d["a"] = FailingMapping():NotImplementedError:('keys',) -d["a"] = FailingMappingKey():NotImplementedError:('getitem:mappingkey',) -d["a"] = FailingNumber():TypeError:('long() argument must be a string or a number',) -<<< Finished ->> DictionaryUpdate ->>> kwargs ->>> iter -d.update(FailingMapping()):NotImplementedError:('keys',) -d.update([FailingIterNext()]):NotImplementedError:('next',) -d.update([FailingIterNextN(1)]):NotImplementedError:('next N',) ->>> Testing *Iter* using d.update(%s) -d.update(FailingIter()):NotImplementedError:('iter',) -d.update(FailingIterNext()):NotImplementedError:('next',) -<<< Finished ->>> Testing StringToChars using d.update({%s : 1}) -d.update({1 : 1}):TypeError:('expected str() or unicode() instance, but got int',) -d.update({u"\0" : 1}):TypeError:('expected string without null bytes',) -d.update({"\0" : 1}):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d.update({"abcF" : {%s : 1}}) -d.update({"abcF" : {1 : 1}}):TypeError:('expected str() or unicode() instance, but got int',) -d.update({"abcF" : {u"\0" : 1}}):TypeError:('expected string without null bytes',) -d.update({"abcF" : {"\0" : 1}}):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d.update({"abcF" : Mapping({%s : 1})}) -d.update({"abcF" : Mapping({1 : 1})}):TypeError:('expected str() or unicode() instance, but got int',) -d.update({"abcF" : Mapping({u"\0" : 1})}):TypeError:('expected string without null bytes',) -d.update({"abcF" : Mapping({"\0" : 1})}):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using d.update({"abcF" : %s}) -d.update({"abcF" : FailingIter()}):TypeError:('unable to convert FailingIter to a Vim structure',) -d.update({"abcF" : FailingIterNext()}):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using d.update({"abcF" : %s}) -d.update({"abcF" : None}):NOT FAILED -d.update({"abcF" : {"": 1}}):ValueError:('empty keys are not allowed',) -d.update({"abcF" : {u"": 1}}):ValueError:('empty keys are not allowed',) -d.update({"abcF" : FailingMapping()}):NotImplementedError:('keys',) -d.update({"abcF" : FailingMappingKey()}):NotImplementedError:('getitem:mappingkey',) -d.update({"abcF" : FailingNumber()}):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing StringToChars using d.update(Mapping({%s : 1})) -d.update(Mapping({1 : 1})):TypeError:('expected str() or unicode() instance, but got int',) -d.update(Mapping({u"\0" : 1})):TypeError:('expected string without null bytes',) -d.update(Mapping({"\0" : 1})):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d.update(Mapping({"abcG" : {%s : 1}})) -d.update(Mapping({"abcG" : {1 : 1}})):TypeError:('expected str() or unicode() instance, but got int',) -d.update(Mapping({"abcG" : {u"\0" : 1}})):TypeError:('expected string without null bytes',) -d.update(Mapping({"abcG" : {"\0" : 1}})):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d.update(Mapping({"abcG" : Mapping({%s : 1})})) -d.update(Mapping({"abcG" : Mapping({1 : 1})})):TypeError:('expected str() or unicode() instance, but got int',) -d.update(Mapping({"abcG" : Mapping({u"\0" : 1})})):TypeError:('expected string without null bytes',) -d.update(Mapping({"abcG" : Mapping({"\0" : 1})})):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using d.update(Mapping({"abcG" : %s})) -d.update(Mapping({"abcG" : FailingIter()})):TypeError:('unable to convert FailingIter to a Vim structure',) -d.update(Mapping({"abcG" : FailingIterNext()})):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using d.update(Mapping({"abcG" : %s})) -d.update(Mapping({"abcG" : None})):NOT FAILED -d.update(Mapping({"abcG" : {"": 1}})):ValueError:('empty keys are not allowed',) -d.update(Mapping({"abcG" : {u"": 1}})):ValueError:('empty keys are not allowed',) -d.update(Mapping({"abcG" : FailingMapping()})):NotImplementedError:('keys',) -d.update(Mapping({"abcG" : FailingMappingKey()})):NotImplementedError:('getitem:mappingkey',) -d.update(Mapping({"abcG" : FailingNumber()})):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing *Iter* using d.update(%s) -d.update(FailingIter()):NotImplementedError:('iter',) -d.update(FailingIterNext()):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using d.update(%s) -d.update(None):TypeError:("'NoneType' object is not iterable",) -d.update({"": 1}):ValueError:('empty keys are not allowed',) -d.update({u"": 1}):ValueError:('empty keys are not allowed',) -d.update(FailingMapping()):NotImplementedError:('keys',) -d.update(FailingMappingKey()):NotImplementedError:('getitem:mappingkey',) -d.update(FailingNumber()):TypeError:("'FailingNumber' object is not iterable",) -<<< Finished ->>> Testing StringToChars using d.update(((%s, 0),)) -d.update(((1, 0),)):TypeError:('expected str() or unicode() instance, but got int',) -d.update(((u"\0", 0),)):TypeError:('expected string without null bytes',) -d.update((("\0", 0),)):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d.update((("a", {%s : 1}),)) -d.update((("a", {1 : 1}),)):TypeError:('expected str() or unicode() instance, but got int',) -d.update((("a", {u"\0" : 1}),)):TypeError:('expected string without null bytes',) -d.update((("a", {"\0" : 1}),)):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d.update((("a", {"abcF" : {%s : 1}}),)) -d.update((("a", {"abcF" : {1 : 1}}),)):TypeError:('expected str() or unicode() instance, but got int',) -d.update((("a", {"abcF" : {u"\0" : 1}}),)):TypeError:('expected string without null bytes',) -d.update((("a", {"abcF" : {"\0" : 1}}),)):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d.update((("a", {"abcF" : Mapping({%s : 1})}),)) -d.update((("a", {"abcF" : Mapping({1 : 1})}),)):TypeError:('expected str() or unicode() instance, but got int',) -d.update((("a", {"abcF" : Mapping({u"\0" : 1})}),)):TypeError:('expected string without null bytes',) -d.update((("a", {"abcF" : Mapping({"\0" : 1})}),)):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using d.update((("a", {"abcF" : %s}),)) -d.update((("a", {"abcF" : FailingIter()}),)):TypeError:('unable to convert FailingIter to a Vim structure',) -d.update((("a", {"abcF" : FailingIterNext()}),)):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using d.update((("a", {"abcF" : %s}),)) -d.update((("a", {"abcF" : None}),)):error:("failed to add key 'a' to dictionary",) -d.update((("a", {"abcF" : {"": 1}}),)):ValueError:('empty keys are not allowed',) -d.update((("a", {"abcF" : {u"": 1}}),)):ValueError:('empty keys are not allowed',) -d.update((("a", {"abcF" : FailingMapping()}),)):NotImplementedError:('keys',) -d.update((("a", {"abcF" : FailingMappingKey()}),)):NotImplementedError:('getitem:mappingkey',) -d.update((("a", {"abcF" : FailingNumber()}),)):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing StringToChars using d.update((("a", Mapping({%s : 1})),)) -d.update((("a", Mapping({1 : 1})),)):TypeError:('expected str() or unicode() instance, but got int',) -d.update((("a", Mapping({u"\0" : 1})),)):TypeError:('expected string without null bytes',) -d.update((("a", Mapping({"\0" : 1})),)):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d.update((("a", Mapping({"abcG" : {%s : 1}})),)) -d.update((("a", Mapping({"abcG" : {1 : 1}})),)):TypeError:('expected str() or unicode() instance, but got int',) -d.update((("a", Mapping({"abcG" : {u"\0" : 1}})),)):TypeError:('expected string without null bytes',) -d.update((("a", Mapping({"abcG" : {"\0" : 1}})),)):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d.update((("a", Mapping({"abcG" : Mapping({%s : 1})})),)) -d.update((("a", Mapping({"abcG" : Mapping({1 : 1})})),)):TypeError:('expected str() or unicode() instance, but got int',) -d.update((("a", Mapping({"abcG" : Mapping({u"\0" : 1})})),)):TypeError:('expected string without null bytes',) -d.update((("a", Mapping({"abcG" : Mapping({"\0" : 1})})),)):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using d.update((("a", Mapping({"abcG" : %s})),)) -d.update((("a", Mapping({"abcG" : FailingIter()})),)):TypeError:('unable to convert FailingIter to a Vim structure',) -d.update((("a", Mapping({"abcG" : FailingIterNext()})),)):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using d.update((("a", Mapping({"abcG" : %s})),)) -d.update((("a", Mapping({"abcG" : None})),)):error:("failed to add key 'a' to dictionary",) -d.update((("a", Mapping({"abcG" : {"": 1}})),)):ValueError:('empty keys are not allowed',) -d.update((("a", Mapping({"abcG" : {u"": 1}})),)):ValueError:('empty keys are not allowed',) -d.update((("a", Mapping({"abcG" : FailingMapping()})),)):NotImplementedError:('keys',) -d.update((("a", Mapping({"abcG" : FailingMappingKey()})),)):NotImplementedError:('getitem:mappingkey',) -d.update((("a", Mapping({"abcG" : FailingNumber()})),)):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing *Iter* using d.update((("a", %s),)) -d.update((("a", FailingIter()),)):TypeError:('unable to convert FailingIter to a Vim structure',) -d.update((("a", FailingIterNext()),)):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using d.update((("a", %s),)) -d.update((("a", None),)):error:("failed to add key 'a' to dictionary",) -d.update((("a", {"": 1}),)):ValueError:('empty keys are not allowed',) -d.update((("a", {u"": 1}),)):ValueError:('empty keys are not allowed',) -d.update((("a", FailingMapping()),)):NotImplementedError:('keys',) -d.update((("a", FailingMappingKey()),)):NotImplementedError:('getitem:mappingkey',) -d.update((("a", FailingNumber()),)):TypeError:('long() argument must be a string or a number',) -<<< Finished ->> DictionaryPopItem -d.popitem(1, 2):TypeError:('popitem() takes no arguments (2 given)',) ->> DictionaryHasKey -d.has_key():TypeError:('has_key() takes exactly one argument (0 given)',) -> List ->> ListConstructor -vim.List(1, 2):TypeError:('function takes at most 1 argument (2 given)',) -vim.List(a=1):TypeError:('list constructor does not accept keyword arguments',) ->>> Testing *Iter* using vim.List(%s) -vim.List(FailingIter()):NotImplementedError:('iter',) -vim.List(FailingIterNext()):NotImplementedError:('next',) -<<< Finished ->>> Testing StringToChars using vim.List([{%s : 1}]) -vim.List([{1 : 1}]):TypeError:('expected str() or unicode() instance, but got int',) -vim.List([{u"\0" : 1}]):TypeError:('expected string without null bytes',) -vim.List([{"\0" : 1}]):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using vim.List([{"abcF" : {%s : 1}}]) -vim.List([{"abcF" : {1 : 1}}]):TypeError:('expected str() or unicode() instance, but got int',) -vim.List([{"abcF" : {u"\0" : 1}}]):TypeError:('expected string without null bytes',) -vim.List([{"abcF" : {"\0" : 1}}]):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using vim.List([{"abcF" : Mapping({%s : 1})}]) -vim.List([{"abcF" : Mapping({1 : 1})}]):TypeError:('expected str() or unicode() instance, but got int',) -vim.List([{"abcF" : Mapping({u"\0" : 1})}]):TypeError:('expected string without null bytes',) -vim.List([{"abcF" : Mapping({"\0" : 1})}]):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using vim.List([{"abcF" : %s}]) -vim.List([{"abcF" : FailingIter()}]):TypeError:('unable to convert FailingIter to a Vim structure',) -vim.List([{"abcF" : FailingIterNext()}]):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using vim.List([{"abcF" : %s}]) -vim.List([{"abcF" : None}]):NOT FAILED -vim.List([{"abcF" : {"": 1}}]):ValueError:('empty keys are not allowed',) -vim.List([{"abcF" : {u"": 1}}]):ValueError:('empty keys are not allowed',) -vim.List([{"abcF" : FailingMapping()}]):NotImplementedError:('keys',) -vim.List([{"abcF" : FailingMappingKey()}]):NotImplementedError:('getitem:mappingkey',) -vim.List([{"abcF" : FailingNumber()}]):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing StringToChars using vim.List([Mapping({%s : 1})]) -vim.List([Mapping({1 : 1})]):TypeError:('expected str() or unicode() instance, but got int',) -vim.List([Mapping({u"\0" : 1})]):TypeError:('expected string without null bytes',) -vim.List([Mapping({"\0" : 1})]):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using vim.List([Mapping({"abcG" : {%s : 1}})]) -vim.List([Mapping({"abcG" : {1 : 1}})]):TypeError:('expected str() or unicode() instance, but got int',) -vim.List([Mapping({"abcG" : {u"\0" : 1}})]):TypeError:('expected string without null bytes',) -vim.List([Mapping({"abcG" : {"\0" : 1}})]):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using vim.List([Mapping({"abcG" : Mapping({%s : 1})})]) -vim.List([Mapping({"abcG" : Mapping({1 : 1})})]):TypeError:('expected str() or unicode() instance, but got int',) -vim.List([Mapping({"abcG" : Mapping({u"\0" : 1})})]):TypeError:('expected string without null bytes',) -vim.List([Mapping({"abcG" : Mapping({"\0" : 1})})]):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using vim.List([Mapping({"abcG" : %s})]) -vim.List([Mapping({"abcG" : FailingIter()})]):TypeError:('unable to convert FailingIter to a Vim structure',) -vim.List([Mapping({"abcG" : FailingIterNext()})]):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using vim.List([Mapping({"abcG" : %s})]) -vim.List([Mapping({"abcG" : None})]):NOT FAILED -vim.List([Mapping({"abcG" : {"": 1}})]):ValueError:('empty keys are not allowed',) -vim.List([Mapping({"abcG" : {u"": 1}})]):ValueError:('empty keys are not allowed',) -vim.List([Mapping({"abcG" : FailingMapping()})]):NotImplementedError:('keys',) -vim.List([Mapping({"abcG" : FailingMappingKey()})]):NotImplementedError:('getitem:mappingkey',) -vim.List([Mapping({"abcG" : FailingNumber()})]):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing *Iter* using vim.List([%s]) -vim.List([FailingIter()]):TypeError:('unable to convert FailingIter to a Vim structure',) -vim.List([FailingIterNext()]):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using vim.List([%s]) -vim.List([None]):NOT FAILED -vim.List([{"": 1}]):ValueError:('empty keys are not allowed',) -vim.List([{u"": 1}]):ValueError:('empty keys are not allowed',) -vim.List([FailingMapping()]):NotImplementedError:('keys',) -vim.List([FailingMappingKey()]):NotImplementedError:('getitem:mappingkey',) -vim.List([FailingNumber()]):TypeError:('long() argument must be a string or a number',) -<<< Finished ->> ListItem -l[1000]:IndexError:('list index out of range',) ->> ListAssItem -ll[1] = 2:error:('list is locked',) -l[1000] = 3:IndexError:('list index out of range',) ->> ListAssSlice -ll[1:100] = "abcJ":error:('list is locked',) ->>> Testing *Iter* using l[:] = %s -l[:] = FailingIter():NotImplementedError:('iter',) -l[:] = FailingIterNext():NotImplementedError:('next',) -<<< Finished -nel[1:10:2] = "abcK":ValueError:('attempt to assign sequence of size greater than 2 to extended slice',) -('a', 'b', 'c', 'O') -nel[1:10:2] = "a":ValueError:('attempt to assign sequence of size 1 to extended slice of size 2',) -('a', 'b', 'c', 'O') -nel[1:1:-1] = "a":ValueError:('attempt to assign sequence of size greater than 0 to extended slice',) -('a', 'b', 'c', 'O') -nel[:] = FailingIterNextN(2):NotImplementedError:('next N',) -('a', 'b', 'c', 'O') ->>> Testing StringToChars using l[:] = [{%s : 1}] -l[:] = [{1 : 1}]:TypeError:('expected str() or unicode() instance, but got int',) -l[:] = [{u"\0" : 1}]:TypeError:('expected string without null bytes',) -l[:] = [{"\0" : 1}]:TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using l[:] = [{"abcF" : {%s : 1}}] -l[:] = [{"abcF" : {1 : 1}}]:TypeError:('expected str() or unicode() instance, but got int',) -l[:] = [{"abcF" : {u"\0" : 1}}]:TypeError:('expected string without null bytes',) -l[:] = [{"abcF" : {"\0" : 1}}]:TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using l[:] = [{"abcF" : Mapping({%s : 1})}] -l[:] = [{"abcF" : Mapping({1 : 1})}]:TypeError:('expected str() or unicode() instance, but got int',) -l[:] = [{"abcF" : Mapping({u"\0" : 1})}]:TypeError:('expected string without null bytes',) -l[:] = [{"abcF" : Mapping({"\0" : 1})}]:TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using l[:] = [{"abcF" : %s}] -l[:] = [{"abcF" : FailingIter()}]:TypeError:('unable to convert FailingIter to a Vim structure',) -l[:] = [{"abcF" : FailingIterNext()}]:NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using l[:] = [{"abcF" : %s}] -l[:] = [{"abcF" : None}]:NOT FAILED -l[:] = [{"abcF" : {"": 1}}]:ValueError:('empty keys are not allowed',) -l[:] = [{"abcF" : {u"": 1}}]:ValueError:('empty keys are not allowed',) -l[:] = [{"abcF" : FailingMapping()}]:NotImplementedError:('keys',) -l[:] = [{"abcF" : FailingMappingKey()}]:NotImplementedError:('getitem:mappingkey',) -l[:] = [{"abcF" : FailingNumber()}]:TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing StringToChars using l[:] = [Mapping({%s : 1})] -l[:] = [Mapping({1 : 1})]:TypeError:('expected str() or unicode() instance, but got int',) -l[:] = [Mapping({u"\0" : 1})]:TypeError:('expected string without null bytes',) -l[:] = [Mapping({"\0" : 1})]:TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using l[:] = [Mapping({"abcG" : {%s : 1}})] -l[:] = [Mapping({"abcG" : {1 : 1}})]:TypeError:('expected str() or unicode() instance, but got int',) -l[:] = [Mapping({"abcG" : {u"\0" : 1}})]:TypeError:('expected string without null bytes',) -l[:] = [Mapping({"abcG" : {"\0" : 1}})]:TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using l[:] = [Mapping({"abcG" : Mapping({%s : 1})})] -l[:] = [Mapping({"abcG" : Mapping({1 : 1})})]:TypeError:('expected str() or unicode() instance, but got int',) -l[:] = [Mapping({"abcG" : Mapping({u"\0" : 1})})]:TypeError:('expected string without null bytes',) -l[:] = [Mapping({"abcG" : Mapping({"\0" : 1})})]:TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using l[:] = [Mapping({"abcG" : %s})] -l[:] = [Mapping({"abcG" : FailingIter()})]:TypeError:('unable to convert FailingIter to a Vim structure',) -l[:] = [Mapping({"abcG" : FailingIterNext()})]:NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using l[:] = [Mapping({"abcG" : %s})] -l[:] = [Mapping({"abcG" : None})]:NOT FAILED -l[:] = [Mapping({"abcG" : {"": 1}})]:ValueError:('empty keys are not allowed',) -l[:] = [Mapping({"abcG" : {u"": 1}})]:ValueError:('empty keys are not allowed',) -l[:] = [Mapping({"abcG" : FailingMapping()})]:NotImplementedError:('keys',) -l[:] = [Mapping({"abcG" : FailingMappingKey()})]:NotImplementedError:('getitem:mappingkey',) -l[:] = [Mapping({"abcG" : FailingNumber()})]:TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing *Iter* using l[:] = [%s] -l[:] = [FailingIter()]:TypeError:('unable to convert FailingIter to a Vim structure',) -l[:] = [FailingIterNext()]:NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using l[:] = [%s] -l[:] = [None]:NOT FAILED -l[:] = [{"": 1}]:ValueError:('empty keys are not allowed',) -l[:] = [{u"": 1}]:ValueError:('empty keys are not allowed',) -l[:] = [FailingMapping()]:NotImplementedError:('keys',) -l[:] = [FailingMappingKey()]:NotImplementedError:('getitem:mappingkey',) -l[:] = [FailingNumber()]:TypeError:('long() argument must be a string or a number',) -<<< Finished ->> ListConcatInPlace ->>> Testing *Iter* using l.extend(%s) -l.extend(FailingIter()):NotImplementedError:('iter',) -l.extend(FailingIterNext()):NotImplementedError:('next',) -<<< Finished ->>> Testing StringToChars using l.extend([{%s : 1}]) -l.extend([{1 : 1}]):TypeError:('expected str() or unicode() instance, but got int',) -l.extend([{u"\0" : 1}]):TypeError:('expected string without null bytes',) -l.extend([{"\0" : 1}]):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using l.extend([{"abcF" : {%s : 1}}]) -l.extend([{"abcF" : {1 : 1}}]):TypeError:('expected str() or unicode() instance, but got int',) -l.extend([{"abcF" : {u"\0" : 1}}]):TypeError:('expected string without null bytes',) -l.extend([{"abcF" : {"\0" : 1}}]):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using l.extend([{"abcF" : Mapping({%s : 1})}]) -l.extend([{"abcF" : Mapping({1 : 1})}]):TypeError:('expected str() or unicode() instance, but got int',) -l.extend([{"abcF" : Mapping({u"\0" : 1})}]):TypeError:('expected string without null bytes',) -l.extend([{"abcF" : Mapping({"\0" : 1})}]):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using l.extend([{"abcF" : %s}]) -l.extend([{"abcF" : FailingIter()}]):TypeError:('unable to convert FailingIter to a Vim structure',) -l.extend([{"abcF" : FailingIterNext()}]):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using l.extend([{"abcF" : %s}]) -l.extend([{"abcF" : None}]):NOT FAILED -l.extend([{"abcF" : {"": 1}}]):ValueError:('empty keys are not allowed',) -l.extend([{"abcF" : {u"": 1}}]):ValueError:('empty keys are not allowed',) -l.extend([{"abcF" : FailingMapping()}]):NotImplementedError:('keys',) -l.extend([{"abcF" : FailingMappingKey()}]):NotImplementedError:('getitem:mappingkey',) -l.extend([{"abcF" : FailingNumber()}]):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing StringToChars using l.extend([Mapping({%s : 1})]) -l.extend([Mapping({1 : 1})]):TypeError:('expected str() or unicode() instance, but got int',) -l.extend([Mapping({u"\0" : 1})]):TypeError:('expected string without null bytes',) -l.extend([Mapping({"\0" : 1})]):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using l.extend([Mapping({"abcG" : {%s : 1}})]) -l.extend([Mapping({"abcG" : {1 : 1}})]):TypeError:('expected str() or unicode() instance, but got int',) -l.extend([Mapping({"abcG" : {u"\0" : 1}})]):TypeError:('expected string without null bytes',) -l.extend([Mapping({"abcG" : {"\0" : 1}})]):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using l.extend([Mapping({"abcG" : Mapping({%s : 1})})]) -l.extend([Mapping({"abcG" : Mapping({1 : 1})})]):TypeError:('expected str() or unicode() instance, but got int',) -l.extend([Mapping({"abcG" : Mapping({u"\0" : 1})})]):TypeError:('expected string without null bytes',) -l.extend([Mapping({"abcG" : Mapping({"\0" : 1})})]):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using l.extend([Mapping({"abcG" : %s})]) -l.extend([Mapping({"abcG" : FailingIter()})]):TypeError:('unable to convert FailingIter to a Vim structure',) -l.extend([Mapping({"abcG" : FailingIterNext()})]):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using l.extend([Mapping({"abcG" : %s})]) -l.extend([Mapping({"abcG" : None})]):NOT FAILED -l.extend([Mapping({"abcG" : {"": 1}})]):ValueError:('empty keys are not allowed',) -l.extend([Mapping({"abcG" : {u"": 1}})]):ValueError:('empty keys are not allowed',) -l.extend([Mapping({"abcG" : FailingMapping()})]):NotImplementedError:('keys',) -l.extend([Mapping({"abcG" : FailingMappingKey()})]):NotImplementedError:('getitem:mappingkey',) -l.extend([Mapping({"abcG" : FailingNumber()})]):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing *Iter* using l.extend([%s]) -l.extend([FailingIter()]):TypeError:('unable to convert FailingIter to a Vim structure',) -l.extend([FailingIterNext()]):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using l.extend([%s]) -l.extend([None]):NOT FAILED -l.extend([{"": 1}]):ValueError:('empty keys are not allowed',) -l.extend([{u"": 1}]):ValueError:('empty keys are not allowed',) -l.extend([FailingMapping()]):NotImplementedError:('keys',) -l.extend([FailingMappingKey()]):NotImplementedError:('getitem:mappingkey',) -l.extend([FailingNumber()]):TypeError:('long() argument must be a string or a number',) -<<< Finished ->> ListSetattr -del l.locked:AttributeError:('cannot delete vim.List attributes',) -l.locked = FailingTrue():NotImplementedError:('bool',) -l.xxx = True:AttributeError:('cannot set attribute xxx',) -> Function ->> FunctionConstructor ->>> FunctionConstructor -vim.Function("123"):ValueError:('unnamed function 123 does not exist',) -vim.Function("xxx_non_existent_function_xxx"):ValueError:('function xxx_non_existent_function_xxx does not exist',) -vim.Function("xxx#non#existent#function#xxx"):NOT FAILED -vim.Function("xxx_non_existent_function_xxx2", args=[]):ValueError:('function xxx_non_existent_function_xxx2 does not exist',) -vim.Function("xxx_non_existent_function_xxx3", self={}):ValueError:('function xxx_non_existent_function_xxx3 does not exist',) -vim.Function("xxx_non_existent_function_xxx4", args=[], self={}):ValueError:('function xxx_non_existent_function_xxx4 does not exist',) ->>> FunctionNew -vim.Function("tr", self="abcFuncSelf"):TypeError:('unable to convert str to a Vim dictionary',) -vim.Function("tr", args=427423):TypeError:('unable to convert int to a Vim list',) -vim.Function("tr", self="abcFuncSelf2", args="abcFuncArgs2"):TypeError:('unable to convert str to a Vim dictionary',) -vim.Function(self="abcFuncSelf2", args="abcFuncArgs2"):TypeError:('unable to convert str to a Vim dictionary',) -vim.Function("tr", "", self="abcFuncSelf2", args="abcFuncArgs2"):TypeError:('unable to convert str to a Vim dictionary',) -vim.Function("tr", ""):TypeError:('function takes exactly 1 argument (2 given)',) ->> FunctionCall ->>> Testing StringToChars using f({%s : 1}) -f({1 : 1}):TypeError:('expected str() or unicode() instance, but got int',) -f({u"\0" : 1}):TypeError:('expected string without null bytes',) -f({"\0" : 1}):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using f({"abcF" : {%s : 1}}) -f({"abcF" : {1 : 1}}):TypeError:('expected str() or unicode() instance, but got int',) -f({"abcF" : {u"\0" : 1}}):TypeError:('expected string without null bytes',) -f({"abcF" : {"\0" : 1}}):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using f({"abcF" : Mapping({%s : 1})}) -f({"abcF" : Mapping({1 : 1})}):TypeError:('expected str() or unicode() instance, but got int',) -f({"abcF" : Mapping({u"\0" : 1})}):TypeError:('expected string without null bytes',) -f({"abcF" : Mapping({"\0" : 1})}):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using f({"abcF" : %s}) -f({"abcF" : FailingIter()}):TypeError:('unable to convert FailingIter to a Vim structure',) -f({"abcF" : FailingIterNext()}):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using f({"abcF" : %s}) -f({"abcF" : None}):NOT FAILED -f({"abcF" : {"": 1}}):ValueError:('empty keys are not allowed',) -f({"abcF" : {u"": 1}}):ValueError:('empty keys are not allowed',) -f({"abcF" : FailingMapping()}):NotImplementedError:('keys',) -f({"abcF" : FailingMappingKey()}):NotImplementedError:('getitem:mappingkey',) -f({"abcF" : FailingNumber()}):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing StringToChars using f(Mapping({%s : 1})) -f(Mapping({1 : 1})):TypeError:('expected str() or unicode() instance, but got int',) -f(Mapping({u"\0" : 1})):TypeError:('expected string without null bytes',) -f(Mapping({"\0" : 1})):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using f(Mapping({"abcG" : {%s : 1}})) -f(Mapping({"abcG" : {1 : 1}})):TypeError:('expected str() or unicode() instance, but got int',) -f(Mapping({"abcG" : {u"\0" : 1}})):TypeError:('expected string without null bytes',) -f(Mapping({"abcG" : {"\0" : 1}})):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using f(Mapping({"abcG" : Mapping({%s : 1})})) -f(Mapping({"abcG" : Mapping({1 : 1})})):TypeError:('expected str() or unicode() instance, but got int',) -f(Mapping({"abcG" : Mapping({u"\0" : 1})})):TypeError:('expected string without null bytes',) -f(Mapping({"abcG" : Mapping({"\0" : 1})})):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using f(Mapping({"abcG" : %s})) -f(Mapping({"abcG" : FailingIter()})):TypeError:('unable to convert FailingIter to a Vim structure',) -f(Mapping({"abcG" : FailingIterNext()})):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using f(Mapping({"abcG" : %s})) -f(Mapping({"abcG" : None})):NOT FAILED -f(Mapping({"abcG" : {"": 1}})):ValueError:('empty keys are not allowed',) -f(Mapping({"abcG" : {u"": 1}})):ValueError:('empty keys are not allowed',) -f(Mapping({"abcG" : FailingMapping()})):NotImplementedError:('keys',) -f(Mapping({"abcG" : FailingMappingKey()})):NotImplementedError:('getitem:mappingkey',) -f(Mapping({"abcG" : FailingNumber()})):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing *Iter* using f(%s) -f(FailingIter()):TypeError:('unable to convert FailingIter to a Vim structure',) -f(FailingIterNext()):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using f(%s) -f(None):NOT FAILED -f({"": 1}):ValueError:('empty keys are not allowed',) -f({u"": 1}):ValueError:('empty keys are not allowed',) -f(FailingMapping()):NotImplementedError:('keys',) -f(FailingMappingKey()):NotImplementedError:('getitem:mappingkey',) -f(FailingNumber()):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing StringToChars using fd(self={%s : 1}) -fd(self={1 : 1}):TypeError:('expected str() or unicode() instance, but got int',) -fd(self={u"\0" : 1}):TypeError:('expected string without null bytes',) -fd(self={"\0" : 1}):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using fd(self={"abcF" : {%s : 1}}) -fd(self={"abcF" : {1 : 1}}):TypeError:('expected str() or unicode() instance, but got int',) -fd(self={"abcF" : {u"\0" : 1}}):TypeError:('expected string without null bytes',) -fd(self={"abcF" : {"\0" : 1}}):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using fd(self={"abcF" : Mapping({%s : 1})}) -fd(self={"abcF" : Mapping({1 : 1})}):TypeError:('expected str() or unicode() instance, but got int',) -fd(self={"abcF" : Mapping({u"\0" : 1})}):TypeError:('expected string without null bytes',) -fd(self={"abcF" : Mapping({"\0" : 1})}):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using fd(self={"abcF" : %s}) -fd(self={"abcF" : FailingIter()}):TypeError:('unable to convert FailingIter to a Vim structure',) -fd(self={"abcF" : FailingIterNext()}):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using fd(self={"abcF" : %s}) -fd(self={"abcF" : None}):NOT FAILED -fd(self={"abcF" : {"": 1}}):ValueError:('empty keys are not allowed',) -fd(self={"abcF" : {u"": 1}}):ValueError:('empty keys are not allowed',) -fd(self={"abcF" : FailingMapping()}):NotImplementedError:('keys',) -fd(self={"abcF" : FailingMappingKey()}):NotImplementedError:('getitem:mappingkey',) -fd(self={"abcF" : FailingNumber()}):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing StringToChars using fd(self=Mapping({%s : 1})) -fd(self=Mapping({1 : 1})):TypeError:('expected str() or unicode() instance, but got int',) -fd(self=Mapping({u"\0" : 1})):TypeError:('expected string without null bytes',) -fd(self=Mapping({"\0" : 1})):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using fd(self=Mapping({"abcG" : {%s : 1}})) -fd(self=Mapping({"abcG" : {1 : 1}})):TypeError:('expected str() or unicode() instance, but got int',) -fd(self=Mapping({"abcG" : {u"\0" : 1}})):TypeError:('expected string without null bytes',) -fd(self=Mapping({"abcG" : {"\0" : 1}})):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using fd(self=Mapping({"abcG" : Mapping({%s : 1})})) -fd(self=Mapping({"abcG" : Mapping({1 : 1})})):TypeError:('expected str() or unicode() instance, but got int',) -fd(self=Mapping({"abcG" : Mapping({u"\0" : 1})})):TypeError:('expected string without null bytes',) -fd(self=Mapping({"abcG" : Mapping({"\0" : 1})})):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using fd(self=Mapping({"abcG" : %s})) -fd(self=Mapping({"abcG" : FailingIter()})):TypeError:('unable to convert FailingIter to a Vim structure',) -fd(self=Mapping({"abcG" : FailingIterNext()})):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using fd(self=Mapping({"abcG" : %s})) -fd(self=Mapping({"abcG" : None})):NOT FAILED -fd(self=Mapping({"abcG" : {"": 1}})):ValueError:('empty keys are not allowed',) -fd(self=Mapping({"abcG" : {u"": 1}})):ValueError:('empty keys are not allowed',) -fd(self=Mapping({"abcG" : FailingMapping()})):NotImplementedError:('keys',) -fd(self=Mapping({"abcG" : FailingMappingKey()})):NotImplementedError:('getitem:mappingkey',) -fd(self=Mapping({"abcG" : FailingNumber()})):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing *Iter* using fd(self=%s) -fd(self=FailingIter()):TypeError:('unable to convert FailingIter to a Vim dictionary',) -fd(self=FailingIterNext()):TypeError:('unable to convert FailingIterNext to a Vim dictionary',) -<<< Finished ->>> Testing ConvertFromPyObject using fd(self=%s) -fd(self=None):TypeError:('unable to convert NoneType to a Vim dictionary',) -fd(self={"": 1}):ValueError:('empty keys are not allowed',) -fd(self={u"": 1}):ValueError:('empty keys are not allowed',) -fd(self=FailingMapping()):NotImplementedError:('keys',) -fd(self=FailingMappingKey()):NotImplementedError:('getitem:mappingkey',) -fd(self=FailingNumber()):TypeError:('unable to convert FailingNumber to a Vim dictionary',) -<<< Finished ->>> Testing ConvertFromPyMapping using fd(self=%s) -fd(self=[]):TypeError:('unable to convert list to a Vim dictionary',) -<<< Finished -> TabPage ->> TabPageAttr -vim.current.tabpage.xxx:AttributeError:('xxx',) -> TabList ->> TabListItem -vim.tabpages[1000]:IndexError:('no such tab page',) -> Window ->> WindowAttr -vim.current.window.xxx:AttributeError:('xxx',) ->> WindowSetattr -vim.current.window.buffer = 0:TypeError:('readonly attribute: buffer',) -vim.current.window.cursor = (100000000, 100000000):error:('cursor position outside buffer',) -vim.current.window.cursor = True:TypeError:('argument must be 2-item sequence, not bool',) ->>> Testing NumberToLong using vim.current.window.height = %s -vim.current.window.height = []:TypeError:('expected int(), long() or something supporting coercing to long(), but got list',) -vim.current.window.height = None:TypeError:('expected int(), long() or something supporting coercing to long(), but got NoneType',) -vim.current.window.height = -1:ValueError:('number must be greater or equal to zero',) -<<< Finished ->>> Testing NumberToLong using vim.current.window.width = %s -vim.current.window.width = []:TypeError:('expected int(), long() or something supporting coercing to long(), but got list',) -vim.current.window.width = None:TypeError:('expected int(), long() or something supporting coercing to long(), but got NoneType',) -vim.current.window.width = -1:ValueError:('number must be greater or equal to zero',) -<<< Finished -vim.current.window.xxxxxx = True:AttributeError:('xxxxxx',) -> WinList ->> WinListItem -vim.windows[1000]:IndexError:('no such window',) -> Buffer ->> StringToLine (indirect) -vim.current.buffer[0] = u"\na":error:('string cannot contain newlines',) -vim.current.buffer[0] = "\na":error:('string cannot contain newlines',) ->> SetBufferLine (indirect) -vim.current.buffer[0] = True:TypeError:('bad argument type for built-in operation',) ->> SetBufferLineList (indirect) -vim.current.buffer[:] = True:TypeError:('bad argument type for built-in operation',) -vim.current.buffer[:] = ["\na", "bc"]:error:('string cannot contain newlines',) ->> InsertBufferLines (indirect) -vim.current.buffer.append(None):TypeError:('bad argument type for built-in operation',) -vim.current.buffer.append(["\na", "bc"]):error:('string cannot contain newlines',) -vim.current.buffer.append("\nbc"):error:('string cannot contain newlines',) ->> RBItem -vim.current.buffer[100000000]:IndexError:('line number out of range',) ->> RBAsItem -vim.current.buffer[100000000] = "":IndexError:('line number out of range',) ->> BufferAttr -vim.current.buffer.xxx:AttributeError:('xxx',) ->> BufferSetattr -vim.current.buffer.name = True:TypeError:('expected str() or unicode() instance, but got bool',) -vim.current.buffer.xxx = True:AttributeError:('xxx',) ->> BufferMark -vim.current.buffer.mark(0):TypeError:('expected str() or unicode() instance, but got int',) -vim.current.buffer.mark("abcM"):ValueError:('mark name must be a single character',) -vim.current.buffer.mark("!"):error:('invalid mark name',) ->> BufferRange -vim.current.buffer.range(1, 2, 3):TypeError:('function takes exactly 2 arguments (3 given)',) -> BufMap ->> BufMapItem -vim.buffers[100000000]:KeyError:(100000000,) ->>> Testing NumberToLong using vim.buffers[%s] -vim.buffers[[]]:TypeError:('expected int(), long() or something supporting coercing to long(), but got list',) -vim.buffers[None]:TypeError:('expected int(), long() or something supporting coercing to long(), but got NoneType',) -vim.buffers[-1]:ValueError:('number must be greater than zero',) -vim.buffers[0]:ValueError:('number must be greater than zero',) -<<< Finished -> Current ->> CurrentGetattr -vim.current.xxx:AttributeError:('xxx',) ->> CurrentSetattr -vim.current.line = True:TypeError:('bad argument type for built-in operation',) -vim.current.buffer = True:TypeError:('expected vim.Buffer object, but got bool',) -vim.current.window = True:TypeError:('expected vim.Window object, but got bool',) -vim.current.tabpage = True:TypeError:('expected vim.TabPage object, but got bool',) -vim.current.xxx = True:AttributeError:('xxx',) -['/testdir'] -'/testdir' -2,xx -before -after -pythonx/topmodule/__init__.py -pythonx/topmodule/submodule/__init__.py -pythonx/topmodule/submodule/subsubmodule/subsubsubmodule.py -vim.command("throw 'abcN'"):error:('abcN',) -Exe("throw 'def'"):error:('def',) -vim.eval("Exe('throw ''ghi''')"):error:('ghi',) -vim.eval("Exe('echoerr ''jkl''')"):error:('Vim(echoerr):jkl',) -vim.eval("Exe('xxx_non_existent_command_xxx')"):error:('Vim:E492: Not an editor command: xxx_non_existent_command_xxx',) -vim.eval("xxx_unknown_function_xxx()"):error:('Vim:E117: Unknown function: xxx_unknown_function_xxx',) -vim.bindeval("Exe('xxx_non_existent_command_xxx')"):error:('Vim:E492: Not an editor command: xxx_non_existent_command_xxx',) -Caught KeyboardInterrupt -Running :put -No exception - diff --git a/src/testdir/test_python2.vim b/src/testdir/test_python2.vim index ae76fe6e89..dae2739d05 100644 --- a/src/testdir/test_python2.vim +++ b/src/testdir/test_python2.vim @@ -1,12 +1,61 @@ " Test for python 2 commands. -" TODO: move tests from test86.in here. source check.vim CheckFeature python +CheckFeature quickfix + +" NOTE: This will cause errors when run under valgrind. +" This would require recompiling Python with: +" ./configure --without-pymalloc +" See http://svn.python.org/view/python/trunk/Misc/README.valgrind?view=markup +" + +" This function should be called first. This sets up python functions used by +" the other tests. +func Test_AAA_python_setup() + py << trim EOF + import vim + import sys + + def emsg(ei): + return ei[0].__name__ + ':' + repr(ei[1].args) + + def ee(expr, g=globals(), l=locals()): + try: + exec(expr, g, l) + except: + ei = sys.exc_info() + msg = emsg(ei) + msg = msg.replace('TypeError:(\'argument 1 ', 'TypeError:(\'') + if expr.find('None') > -1: + msg = msg.replace('TypeError:(\'iteration over non-sequence\',)', + 'TypeError:("\'NoneType\' object is not iterable",)') + if expr.find('FailingNumber') > -1: + msg = msg.replace(', not \'FailingNumber\'', '').replace('"', '\'') + msg = msg.replace('TypeError:(\'iteration over non-sequence\',)', + 'TypeError:("\'FailingNumber\' object is not iterable",)') + if msg.find('(\'\'') > -1 or msg.find('(\'can\'t') > -1: + msg = msg.replace('(\'', '("').replace('\',)', '",)') + # Some Python versions say can't, others cannot. + if msg.find('can\'t') > -1: + msg = msg.replace('can\'t', 'cannot') + # Some Python versions use single quote, some double quote + if msg.find('"cannot ') > -1: + msg = msg.replace('"cannot ', '\'cannot ') + if msg.find(' attributes"') > -1: + msg = msg.replace(' attributes"', ' attributes\'') + if expr == 'fd(self=[])': + # HACK: PyMapping_Check changed meaning + msg = msg.replace('AttributeError:(\'keys\',)', + 'TypeError:(\'unable to convert list to vim dictionary\',)') + vim.current.buffer.append(expr + ':' + msg) + else: + vim.current.buffer.append(expr + ':NOT FAILED') + EOF +endfunc func Test_pydo() " Check deleting lines does not trigger ml_get error. - py import vim new call setline(1, ['one', 'two', 'three']) pydo vim.command("%d_") @@ -24,7 +73,6 @@ endfunc func Test_set_cursor() " Check that setting the cursor position works. - py import vim new call setline(1, ['first line', 'second line']) normal gg @@ -38,7 +86,6 @@ endfunc func Test_vim_function() " Check creating vim.Function object - py import vim func s:foo() return matchstr(expand(''), '\zs\d\+_foo$') @@ -59,6 +106,15 @@ func Test_vim_function() call assert_false(v:exception) endtry + let caught_vim_err = v:false + try + let x = pyeval('f.abc') + catch + call assert_match('AttributeError: abc', v:exception) + let caught_vim_err = v:true + endtry + call assert_equal(v:true, caught_vim_err) + py del f delfunc s:foo endfunc @@ -72,7 +128,6 @@ func Test_skipped_python_command_does_not_affect_pyxversion() endfunc func _SetUpHiddenBuffer() - py import vim new edit hidden setlocal bufhidden=hide @@ -122,7 +177,6 @@ func Test_Write_To_HiddenBuffer_Does_Not_Fix_Cursor_ClearLine() endfunc func _SetUpVisibleBuffer() - py import vim new let lnum = 0 while lnum < 10 @@ -149,7 +203,7 @@ func Test_Write_To_Current_Buffer_Fixes_Cursor_List() call assert_equal( line( '.' ), 1 ) bwipe! -endfunction +endfunc func Test_Write_To_Current_Buffer_Fixes_Cursor_Str() call _SetUpVisibleBuffer() @@ -158,7 +212,7 @@ func Test_Write_To_Current_Buffer_Fixes_Cursor_Str() call assert_equal( line( '.' ), 10 ) bwipe! -endfunction +endfunc func Test_Catch_Exception_Message() try @@ -188,4 +242,3306 @@ s+='B' call assert_equal('ABCDE', pyxeval('s')) endfunc +" Test for the buffer range object +func Test_python_range() + new + call setline(1, ['one', 'two', 'three']) + py b = vim.current.buffer + py r = b.range(1, 3) + call assert_equal(0, pyeval('r.start')) + call assert_equal(2, pyeval('r.end')) + call assert_equal(['two', 'three'], pyeval('r[1:]')) + py r[0] = 'green' + call assert_equal(['green', 'two', 'three'], getline(1, '$')) + py r[0:2] = ['red', 'blue'] + call assert_equal(['red', 'blue', 'three'], getline(1, '$')) + call assert_equal(['start', 'end', '__members__'], pyeval('r.__members__')) + + let caught_vim_err = v:false + try + let x = pyeval('r.abc') + catch + call assert_match('AttributeError: abc', v:exception) + let caught_vim_err = v:true + endtry + call assert_equal(v:true, caught_vim_err) + + close! +endfunc + +" Test for the python tabpage object +func Test_python_tabpage() + tabnew + py t = vim.tabpages[1] + tabclose + let caught_vim_err = v:false + try + let n = pyeval('t.number') + catch + call assert_match('vim.error: attempt to refer to deleted tab page', + \ v:exception) + let caught_vim_err = v:true + endtry + call assert_equal(v:true, caught_vim_err) + %bw! +endfunc + +" Test for the python window object +func Test_python_window() + new + py w = vim.current.window + close + let caught_vim_err = v:false + try + let n = pyeval('w.number') + catch + call assert_match('vim.error: attempt to refer to deleted window', + \ v:exception) + let caught_vim_err = v:true + endtry + call assert_equal(v:true, caught_vim_err) +endfunc + +" Test for the python List object +func Test_python_list() + let l = [1, 2] + py pl = vim.bindeval('l') + call assert_equal(['locked', '__members__'], pyeval('pl.__members__')) + + let l = [] + py l = vim.bindeval('l') + py f = vim.bindeval('function("strlen")') + " Extending List directly with different types + py l.extend([1, "as'd", [1, 2, f, {'a': 1}]]) + call assert_equal([1, "as'd", [1, 2, function("strlen"), {'a': 1}]], l) + call assert_equal([1, 2, function("strlen"), {'a': 1}], l[-1]) + call assert_fails('echo l[-4]', 'E684:') + + " List assignment + py l[0] = 0 + call assert_equal([0, "as'd", [1, 2, function("strlen"), {'a': 1}]], l) + py l[-2] = f + call assert_equal([0, function("strlen"), [1, 2, function("strlen"), {'a': 1}]], l) +endfunc + +" Test for the python Dict object +func Test_python_dict() + let d = {} + py pd = vim.bindeval('d') + call assert_equal(['locked', 'scope', '__members__'], + \ pyeval('pd.__members__')) +endfunc + +" Extending Dictionary directly with different types +func Test_python_dict_extend() + let d = {} + func d.f() + return 1 + endfunc + + py f = vim.bindeval('function("strlen")') + py << trim EOF + d = vim.bindeval('d') + d['1'] = 'asd' + d.update() # Must not do anything, including throwing errors + d.update(b = [1, 2, f]) + d.update((('-1', {'a': 1}),)) + d.update({'0': -1}) + dk = d.keys() + dv = d.values() + di = d.items() + cmpfun = lambda a, b: cmp(repr(a), repr(b)) + dk.sort(cmpfun) + dv.sort(cmpfun) + di.sort(cmpfun) + EOF + + call assert_equal(1, pyeval("d['f'](self={})")) + call assert_equal("['-1', '0', '1', 'b', 'f']", pyeval('repr(dk)')) + call assert_equal("['asd', -1L, , , ]", substitute(pyeval('repr(dv)'),'0x\x\+','','g')) + call assert_equal("[('-1', ), ('0', -1L), ('1', 'asd'), ('b', ), ('f', )]", substitute(pyeval('repr(di)'),'0x\x\+','','g')) + call assert_equal(['0', '1', 'b', 'f', '-1'], keys(d)) + call assert_equal("[-1, 'asd', [1, 2, function('strlen')], function('1'), {'a': 1}]", string(values(d))) + py del dk + py del di + py del dv +endfunc + +func Test_python_list_del_items() + " removing items with del + let l = [0, function("strlen"), [1, 2, function("strlen"), {'a': 1}]] + py l = vim.bindeval('l') + py del l[2] + call assert_equal("[0, function('strlen')]", string(l)) + + let l = range(8) + py l = vim.bindeval('l') + py del l[:3] + py del l[1:] + call assert_equal([3], l) + + " removing items out of range: silently skip items that don't exist + + " The following two ranges delete nothing as they match empty list: + let l = [0, 1, 2, 3] + py l = vim.bindeval('l') + py del l[2:1] + call assert_equal([0, 1, 2, 3], l) + py del l[2:2] + call assert_equal([0, 1, 2, 3], l) + py del l[2:3] + call assert_equal([0, 1, 3], l) + + let l = [0, 1, 2, 3] + py l = vim.bindeval('l') + py del l[2:4] + call assert_equal([0, 1], l) + + let l = [0, 1, 2, 3] + py l = vim.bindeval('l') + py del l[2:5] + call assert_equal([0, 1], l) + + let l = [0, 1, 2, 3] + py l = vim.bindeval('l') + py del l[2:6] + call assert_equal([0, 1], l) + + " The following two ranges delete nothing as they match empty list: + let l = [0, 1, 2, 3] + py l = vim.bindeval('l') + py del l[-1:2] + call assert_equal([0, 1, 2, 3], l) + py del l[-2:2] + call assert_equal([0, 1, 2, 3], l) + py del l[-3:2] + call assert_equal([0, 2, 3], l) + + let l = [0, 1, 2, 3] + py l = vim.bindeval('l') + py del l[-4:2] + call assert_equal([2, 3], l) + + let l = [0, 1, 2, 3] + py l = vim.bindeval('l') + py del l[-5:2] + call assert_equal([2, 3], l) + + let l = [0, 1, 2, 3] + py l = vim.bindeval('l') + py del l[-6:2] + call assert_equal([2, 3], l) + + let l = [0, 1, 2, 3] + py l = vim.bindeval('l') + py del l[::2] + call assert_equal([1, 3], l) + + let l = [0, 1, 2, 3] + py l = vim.bindeval('l') + py del l[3:0:-2] + call assert_equal([0, 2], l) + + let l = [0, 1, 2, 3] + py l = vim.bindeval('l') + py del l[2:4:-2] + let l = [0, 1, 2, 3] +endfunc + +func Test_python_dict_del_items() + let d = eval("{'0' : -1, '1' : 'asd', 'b' : [1, 2, function('strlen')], 'f' : function('min'), '-1' : {'a': 1}}") + py d = vim.bindeval('d') + py del d['-1'] + py del d['f'] + call assert_equal([1, 2, function('strlen')], pyeval('d.get(''b'', 1)')) + call assert_equal([1, 2, function('strlen')], pyeval('d.pop(''b'')')) + call assert_equal(1, pyeval('d.get(''b'', 1)')) + call assert_equal('asd', pyeval('d.pop(''1'', 2)')) + call assert_equal(2, pyeval('d.pop(''1'', 2)')) + call assert_equal('True', pyeval('repr(d.has_key(''0''))')) + call assert_equal('False', pyeval('repr(d.has_key(''1''))')) + call assert_equal('True', pyeval('repr(''0'' in d)')) + call assert_equal('False', pyeval('repr(''1'' in d)')) + call assert_equal("['0']", pyeval('repr(list(iter(d)))')) + call assert_equal({'0' : -1}, d) + call assert_equal("('0', -1L)", pyeval('repr(d.popitem())')) + call assert_equal('None', pyeval('repr(d.get(''0''))')) + call assert_equal('[]', pyeval('repr(list(iter(d)))')) +endfunc + +" Slice assignment to a list +func Test_python_slice_assignment() + let l = [0, 1, 2, 3] + py l = vim.bindeval('l') + py l[0:0] = ['a'] + call assert_equal(['a', 0, 1, 2, 3], l) + + let l = [0, 1, 2, 3] + py l = vim.bindeval('l') + py l[1:2] = ['b'] + call assert_equal([0, 'b', 2, 3], l) + + let l = [0, 1, 2, 3] + py l = vim.bindeval('l') + py l[2:4] = ['c'] + call assert_equal([0, 1, 'c'], l) + + let l = [0, 1, 2, 3] + py l = vim.bindeval('l') + py l[4:4] = ['d'] + call assert_equal([0, 1, 2, 3, 'd'], l) + + let l = [0, 1, 2, 3] + py l = vim.bindeval('l') + py l[-1:2] = ['e'] + call assert_equal([0, 1, 2, 'e', 3], l) + + let l = [0, 1, 2, 3] + py l = vim.bindeval('l') + py l[-10:2] = ['f'] + call assert_equal(['f', 2, 3], l) + + let l = [0, 1, 2, 3] + py l = vim.bindeval('l') + py l[2:-10] = ['g'] + call assert_equal([0, 1, 'g', 2, 3], l) + + let l = [] + py l = vim.bindeval('l') + py l[0:0] = ['h'] + call assert_equal(['h'], l) + + let l = range(8) + py l = vim.bindeval('l') + py l[2:6:2] = [10, 20] + call assert_equal([0, 1, 10, 3, 20, 5, 6, 7], l) + + let l = range(8) + py l = vim.bindeval('l') + py l[6:2:-2] = [10, 20] + call assert_equal([0, 1, 2, 3, 20, 5, 10, 7], l) + + let l = range(8) + py l = vim.bindeval('l') + py l[6:2] = () + call assert_equal([0, 1, 2, 3, 4, 5, 6, 7], l) + + let l = range(8) + py l = vim.bindeval('l') + py l[6:2:1] = () + call assert_equal([0, 1, 2, 3, 4, 5, 6, 7], l) + + let l = range(8) + py l = vim.bindeval('l') + py l[2:2:1] = () + call assert_equal([0, 1, 2, 3, 4, 5, 6, 7], l) +endfunc + +" Locked variables +func Test_python_lockedvar() + new + py cb = vim.current.buffer + let l = [0, 1, 2, 3] + py l = vim.bindeval('l') + lockvar! l + py << trim EOF + try: + l[2]='i' + except vim.error: + cb.append('l[2] threw vim.error: ' + emsg(sys.exc_info())) + EOF + call assert_equal(['', "l[2] threw vim.error: error:('list is locked',)"], + \ getline(1, '$')) + call assert_equal([0, 1, 2, 3], l) + unlockvar! l + close! +endfunc + +" Test for calling a function +func Test_python_function_call() + func New(...) + return ['NewStart'] + a:000 + ['NewEnd'] + endfunc + + func DictNew(...) dict + return ['DictNewStart'] + a:000 + ['DictNewEnd', self] + endfunc + + new + let l = [function('New'), function('DictNew')] + py l = vim.bindeval('l') + py l.extend(list(l[0](1, 2, 3))) + call assert_equal([function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd'], l) + py l.extend(list(l[1](1, 2, 3, self={'a': 'b'}))) + call assert_equal([function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}], l) + py l.extend([l[0].name]) + call assert_equal([function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}, 'New'], l) + py ee('l[1](1, 2, 3)') + call assert_equal("l[1](1, 2, 3):error:('Vim:E725: Calling dict function without Dictionary: DictNew',)", getline(2)) + %d + py f = l[0] + delfunction New + py ee('f(1, 2, 3)') + call assert_equal("f(1, 2, 3):error:('Vim:E117: Unknown function: New',)", getline(2)) + close! + delfunction DictNew +endfunc + +func Test_python_float() + CheckFeature float + let l = [0.0] + py l = vim.bindeval('l') + py l.extend([0.0]) + call assert_equal([0.0, 0.0], l) +endfunc + +" Test for Dict key errors +func Test_python_dict_key_error() + let messages = [] + py << trim EOF + d = vim.bindeval('{}') + m = vim.bindeval('messages') + def em(expr, g=globals(), l=locals()): + try: + exec(expr, g, l) + except: + m.extend([sys.exc_type.__name__]) + + em('d["abc1"]') + em('d["abc1"]="\\0"') + em('d["abc1"]=vim') + em('d[""]=1') + em('d["a\\0b"]=1') + em('d[u"a\\0b"]=1') + em('d.pop("abc1")') + em('d.popitem()') + del em + del m + EOF + + call assert_equal(['KeyError', 'TypeError', 'TypeError', 'ValueError', + \ 'TypeError', 'TypeError', 'KeyError', 'KeyError'], messages) + unlet messages +endfunc + +" Test for locked and scope attributes +func Test_python_lock_scope_attr() + let d = {} | let dl = {} | lockvar dl + let res = [] + for s in split("d dl v: g:") + let name = tr(s, ':', 's') + execute 'py ' .. name .. ' = vim.bindeval("' .. s .. '")' + call add(res, s .. ' : ' .. join(map(['locked', 'scope'], + \ 'v:val .. ":" .. pyeval(name .. "." .. v:val)'), ';')) + endfor + call assert_equal(['d : locked:0;scope:0', 'dl : locked:1;scope:0', + \ 'v: : locked:2;scope:1', 'g: : locked:0;scope:2'], res) + + silent! let d.abc2 = 1 + silent! let dl.abc3 = 1 + py d.locked = True + py dl.locked = False + silent! let d.def = 1 + silent! let dl.def = 1 + call assert_equal({'abc2': 1}, d) + call assert_equal({'def': 1}, dl) + unlet d dl + + let l = [] | let ll = [] | lockvar ll + let res = [] + for s in split("l ll") + let name = tr(s, ':', 's') + execute 'py ' .. name .. '=vim.bindeval("' .. s .. '")' + call add(res, s .. ' : locked:' .. pyeval(name .. '.locked')) + endfor + call assert_equal(['l : locked:0', 'll : locked:1'], res) + + silent! call extend(l, [0]) + silent! call extend(ll, [0]) + py l.locked = True + py ll.locked = False + silent! call extend(l, [1]) + silent! call extend(ll, [1]) + call assert_equal([0], l) + call assert_equal([1], ll) + unlet l ll +endfunc + +" Test for pyeval() +func Test_python_pyeval() + let l = pyeval('range(3)') + call assert_equal([0, 1, 2], l) + + let d = pyeval('{"a": "b", "c": 1, "d": ["e"]}') + call assert_equal([['a', 'b'], ['c', 1], ['d', ['e']]], sort(items(d))) + + let v:errmsg = '' + call assert_equal(v:none, pyeval('None')) + call assert_equal('', v:errmsg) + + if has('float') + call assert_equal(0.0, pyeval('0.0')) + endif + + " Invalid values: + let caught_859 = 0 + try + let v = pyeval('"\0"') + catch /E859:/ + let caught_859 = 1 + endtry + call assert_equal(1, caught_859) + + let caught_859 = 0 + try + let v = pyeval('{"\0" : 1}') + catch /E859:/ + let caught_859 = 1 + endtry + call assert_equal(1, caught_859) + + let caught_nameerr = 0 + try + let v = pyeval("undefined_name") + catch /NameError: name 'undefined_name'/ + let caught_nameerr = 1 + endtry + call assert_equal(1, caught_nameerr) + + let caught_859 = 0 + try + let v = pyeval("vim") + catch /E859:/ + let caught_859 = 1 + endtry + call assert_equal(1, caught_859) +endfunc + +" threading +" Running pydo command (Test_pydo) before this test, stops the python thread +" from running. So this test should be run before the pydo test +func Test_aaa_python_threading() + let l = [0] + py l = vim.bindeval('l') + py << trim EOF + import threading + import time + + class T(threading.Thread): + def __init__(self): + threading.Thread.__init__(self) + self.t = 0 + self.running = True + + def run(self): + while self.running: + self.t += 1 + time.sleep(0.1) + + t = T() + del T + t.start() + EOF + + sleep 1 + py t.running = False + py t.join() + + " Check if the background thread is working. Count should be 10, but on a + " busy system (AppVeyor) it can be much lower. + py l[0] = t.t > 4 + py del time + py del threading + py del t + call assert_equal([1], l) +endfunc + +" settrace +func Test_python_settrace() + let l = [] + py l = vim.bindeval('l') + py << trim EOF + import sys + + def traceit(frame, event, arg): + global l + if event == "line": + l.extend([frame.f_lineno]) + return traceit + + def trace_main(): + for i in range(5): + pass + EOF + py sys.settrace(traceit) + py trace_main() + py sys.settrace(None) + py del traceit + py del trace_main + call assert_equal([1, 10, 11, 10, 11, 10, 11, 10, 11, 10, 11, 10, 1], l) +endfunc + +" Slice +func Test_python_list_slice() + py ll = vim.bindeval('[0, 1, 2, 3, 4, 5]') + py l = ll[:4] + call assert_equal([0, 1, 2, 3], pyeval('l')) + py l = ll[2:] + call assert_equal([2, 3, 4, 5], pyeval('l')) + py l = ll[:-4] + call assert_equal([0, 1], pyeval('l')) + py l = ll[-2:] + call assert_equal([4, 5], pyeval('l')) + py l = ll[2:4] + call assert_equal([2, 3], pyeval('l')) + py l = ll[4:2] + call assert_equal([], pyeval('l')) + py l = ll[-4:-2] + call assert_equal([2, 3], pyeval('l')) + py l = ll[-2:-4] + call assert_equal([], pyeval('l')) + py l = ll[:] + call assert_equal([0, 1, 2, 3, 4, 5], pyeval('l')) + py l = ll[0:6] + call assert_equal([0, 1, 2, 3, 4, 5], pyeval('l')) + py l = ll[-10:10] + call assert_equal([0, 1, 2, 3, 4, 5], pyeval('l')) + py l = ll[4:2:-1] + call assert_equal([4, 3], pyeval('l')) + py l = ll[::2] + call assert_equal([0, 2, 4], pyeval('l')) + py l = ll[4:2:1] + call assert_equal([], pyeval('l')) + py del l +endfunc + +" Vars +func Test_python_vars() + let g:foo = 'bac' + let w:abc3 = 'def' + let b:baz = 'bar' + let t:bar = 'jkl' + try + throw "Abc" + catch /Abc/ + call assert_equal('Abc', pyeval('vim.vvars[''exception'']')) + endtry + call assert_equal('bac', pyeval('vim.vars[''foo'']')) + call assert_equal('def', pyeval('vim.current.window.vars[''abc3'']')) + call assert_equal('bar', pyeval('vim.current.buffer.vars[''baz'']')) + call assert_equal('jkl', pyeval('vim.current.tabpage.vars[''bar'']')) +endfunc + +" Options +" paste: boolean, global +" previewheight number, global +" operatorfunc: string, global +" number: boolean, window-local +" numberwidth: number, window-local +" colorcolumn: string, window-local +" statusline: string, window-local/global +" autoindent: boolean, buffer-local +" shiftwidth: number, buffer-local +" omnifunc: string, buffer-local +" preserveindent: boolean, buffer-local/global +" path: string, buffer-local/global +func Test_python_opts() + let g:res = [] + let g:bufs = [bufnr('%')] + new + let g:bufs += [bufnr('%')] + vnew + let g:bufs += [bufnr('%')] + wincmd j + vnew + let g:bufs += [bufnr('%')] + wincmd l + + func RecVars(opt) + let gval = string(eval('&g:' .. a:opt)) + let wvals = join(map(range(1, 4), + \ 'v:val .. ":" .. string(getwinvar(v:val, "&" .. a:opt))')) + let bvals = join(map(copy(g:bufs), + \ 'v:val .. ":" .. string(getbufvar(v:val, "&" .. a:opt))')) + call add(g:res, ' G: ' .. gval) + call add(g:res, ' W: ' .. wvals) + call add(g:res, ' B: ' .. wvals) + endfunc + + py << trim EOF + def e(s, g=globals(), l=locals()): + try: + exec(s, g, l) + except: + vim.command('return ' + repr(sys.exc_type.__name__)) + + def ev(s, g=globals(), l=locals()): + try: + return eval(s, g, l) + except: + vim.command('let exc=' + repr(sys.exc_type.__name__)) + return 0 + EOF + + func E(s) + python e(vim.eval('a:s')) + endfunc + + func Ev(s) + let r = pyeval('ev(vim.eval("a:s"))') + if exists('exc') + throw exc + endif + return r + endfunc + + py gopts1 = vim.options + py wopts1 = vim.windows[2].options + py wopts2 = vim.windows[0].options + py wopts3 = vim.windows[1].options + py bopts1 = vim.buffers[vim.bindeval("g:bufs")[2]].options + py bopts2 = vim.buffers[vim.bindeval("g:bufs")[1]].options + py bopts3 = vim.buffers[vim.bindeval("g:bufs")[0]].options + call add(g:res, 'wopts iters equal: ' .. + \ pyeval('list(wopts1) == list(wopts2)')) + call add(g:res, 'bopts iters equal: ' .. + \ pyeval('list(bopts1) == list(bopts2)')) + py gset = set(iter(gopts1)) + py wset = set(iter(wopts1)) + py bset = set(iter(bopts1)) + + set path=.,..,, + let lst = [] + let lst += [['paste', 1, 0, 1, 2, 1, 1, 0]] + let lst += [['previewheight', 5, 1, 6, 'a', 0, 1, 0]] + let lst += [['operatorfunc', 'A', 'B', 'C', 2, 0, 1, 0]] + let lst += [['number', 0, 1, 1, 0, 1, 0, 1]] + let lst += [['numberwidth', 2, 3, 5, -100, 0, 0, 1]] + let lst += [['colorcolumn', '+1', '+2', '+3', 'abc4', 0, 0, 1]] + let lst += [['statusline', '1', '2', '4', 0, 0, 1, 1]] + let lst += [['autoindent', 0, 1, 1, 2, 1, 0, 2]] + let lst += [['shiftwidth', 0, 2, 1, 3, 0, 0, 2]] + let lst += [['omnifunc', 'A', 'B', 'C', 1, 0, 0, 2]] + let lst += [['preserveindent', 0, 1, 1, 2, 1, 1, 2]] + let lst += [['path', '.,,', ',,', '.', 0, 0, 1, 2]] + for [oname, oval1, oval2, oval3, invval, bool, global, local] in lst + py oname = vim.eval('oname') + py oval1 = vim.bindeval('oval1') + py oval2 = vim.bindeval('oval2') + py oval3 = vim.bindeval('oval3') + if invval is 0 || invval is 1 + py invval = bool(vim.bindeval('invval')) + else + py invval = vim.bindeval('invval') + endif + if bool + py oval1 = bool(oval1) + py oval2 = bool(oval2) + py oval3 = bool(oval3) + endif + call add(g:res, '>>> ' .. oname) + call add(g:res, ' g/w/b:' .. pyeval('oname in gset') .. '/' .. + \ pyeval('oname in wset') .. '/' .. pyeval('oname in bset')) + call add(g:res, ' g/w/b (in):' .. pyeval('oname in gopts1') .. '/' .. + \ pyeval('oname in wopts1') .. '/' .. pyeval('oname in bopts1')) + for v in ['gopts1', 'wopts1', 'bopts1'] + try + call add(g:res, ' p/' .. v .. ': ' .. Ev('repr(' .. v .. '[''' .. oname .. '''])')) + catch + call add(g:res, ' p/' .. v .. '! ' .. v:exception) + endtry + let r = E(v .. '[''' .. oname .. ''']=invval') + if r isnot 0 + call add(g:res, ' inv: ' .. string(invval) .. '! ' .. r) + endif + for vv in (v is# 'gopts1' ? [v] : [v, v[:-2] .. '2', v[:-2] .. '3']) + let val = substitute(vv, '^.opts', 'oval', '') + let r = E(vv .. '[''' .. oname .. ''']=' .. val) + if r isnot 0 + call add(g:res, ' ' .. vv .. '! ' .. r) + endif + endfor + endfor + call RecVars(oname) + for v in ['wopts3', 'bopts3'] + let r = E('del ' .. v .. '["' .. oname .. '"]') + if r isnot 0 + call add(g:res, ' del ' .. v .. '! ' .. r) + endif + endfor + call RecVars(oname) + endfor + delfunction RecVars + delfunction E + delfunction Ev + py del ev + py del e + only + for buf in g:bufs[1:] + execute 'bwipeout!' buf + endfor + py del gopts1 + py del wopts1 + py del wopts2 + py del wopts3 + py del bopts1 + py del bopts2 + py del bopts3 + py del oval1 + py del oval2 + py del oval3 + py del oname + py del invval + + let expected =<< trim END + wopts iters equal: 1 + bopts iters equal: 1 + >>> paste + g/w/b:1/0/0 + g/w/b (in):1/0/0 + p/gopts1: False + p/wopts1! KeyError + inv: 2! KeyError + wopts1! KeyError + wopts2! KeyError + wopts3! KeyError + p/bopts1! KeyError + inv: 2! KeyError + bopts1! KeyError + bopts2! KeyError + bopts3! KeyError + G: 1 + W: 1:1 2:1 3:1 4:1 + B: 1:1 2:1 3:1 4:1 + del wopts3! KeyError + del bopts3! KeyError + G: 1 + W: 1:1 2:1 3:1 4:1 + B: 1:1 2:1 3:1 4:1 + >>> previewheight + g/w/b:1/0/0 + g/w/b (in):1/0/0 + p/gopts1: 12 + inv: 'a'! TypeError + p/wopts1! KeyError + inv: 'a'! KeyError + wopts1! KeyError + wopts2! KeyError + wopts3! KeyError + p/bopts1! KeyError + inv: 'a'! KeyError + bopts1! KeyError + bopts2! KeyError + bopts3! KeyError + G: 5 + W: 1:5 2:5 3:5 4:5 + B: 1:5 2:5 3:5 4:5 + del wopts3! KeyError + del bopts3! KeyError + G: 5 + W: 1:5 2:5 3:5 4:5 + B: 1:5 2:5 3:5 4:5 + >>> operatorfunc + g/w/b:1/0/0 + g/w/b (in):1/0/0 + p/gopts1: '' + inv: 2! TypeError + p/wopts1! KeyError + inv: 2! KeyError + wopts1! KeyError + wopts2! KeyError + wopts3! KeyError + p/bopts1! KeyError + inv: 2! KeyError + bopts1! KeyError + bopts2! KeyError + bopts3! KeyError + G: 'A' + W: 1:'A' 2:'A' 3:'A' 4:'A' + B: 1:'A' 2:'A' 3:'A' 4:'A' + del wopts3! KeyError + del bopts3! KeyError + G: 'A' + W: 1:'A' 2:'A' 3:'A' 4:'A' + B: 1:'A' 2:'A' 3:'A' 4:'A' + >>> number + g/w/b:0/1/0 + g/w/b (in):0/1/0 + p/gopts1! KeyError + inv: 0! KeyError + gopts1! KeyError + p/wopts1: False + p/bopts1! KeyError + inv: 0! KeyError + bopts1! KeyError + bopts2! KeyError + bopts3! KeyError + G: 0 + W: 1:1 2:1 3:0 4:0 + B: 1:1 2:1 3:0 4:0 + del wopts3! ValueError + del bopts3! KeyError + G: 0 + W: 1:1 2:1 3:0 4:0 + B: 1:1 2:1 3:0 4:0 + >>> numberwidth + g/w/b:0/1/0 + g/w/b (in):0/1/0 + p/gopts1! KeyError + inv: -100! KeyError + gopts1! KeyError + p/wopts1: 4 + inv: -100! error + p/bopts1! KeyError + inv: -100! KeyError + bopts1! KeyError + bopts2! KeyError + bopts3! KeyError + G: 4 + W: 1:3 2:5 3:2 4:4 + B: 1:3 2:5 3:2 4:4 + del wopts3! ValueError + del bopts3! KeyError + G: 4 + W: 1:3 2:5 3:2 4:4 + B: 1:3 2:5 3:2 4:4 + >>> colorcolumn + g/w/b:0/1/0 + g/w/b (in):0/1/0 + p/gopts1! KeyError + inv: 'abc4'! KeyError + gopts1! KeyError + p/wopts1: '' + inv: 'abc4'! error + p/bopts1! KeyError + inv: 'abc4'! KeyError + bopts1! KeyError + bopts2! KeyError + bopts3! KeyError + G: '' + W: 1:'+2' 2:'+3' 3:'+1' 4:'' + B: 1:'+2' 2:'+3' 3:'+1' 4:'' + del wopts3! ValueError + del bopts3! KeyError + G: '' + W: 1:'+2' 2:'+3' 3:'+1' 4:'' + B: 1:'+2' 2:'+3' 3:'+1' 4:'' + >>> statusline + g/w/b:1/1/0 + g/w/b (in):1/1/0 + p/gopts1: '' + inv: 0! TypeError + p/wopts1: None + inv: 0! TypeError + p/bopts1! KeyError + inv: 0! KeyError + bopts1! KeyError + bopts2! KeyError + bopts3! KeyError + G: '1' + W: 1:'2' 2:'4' 3:'1' 4:'1' + B: 1:'2' 2:'4' 3:'1' 4:'1' + del bopts3! KeyError + G: '1' + W: 1:'2' 2:'1' 3:'1' 4:'1' + B: 1:'2' 2:'1' 3:'1' 4:'1' + >>> autoindent + g/w/b:0/0/1 + g/w/b (in):0/0/1 + p/gopts1! KeyError + inv: 2! KeyError + gopts1! KeyError + p/wopts1! KeyError + inv: 2! KeyError + wopts1! KeyError + wopts2! KeyError + wopts3! KeyError + p/bopts1: False + G: 0 + W: 1:0 2:1 3:0 4:1 + B: 1:0 2:1 3:0 4:1 + del wopts3! KeyError + del bopts3! ValueError + G: 0 + W: 1:0 2:1 3:0 4:1 + B: 1:0 2:1 3:0 4:1 + >>> shiftwidth + g/w/b:0/0/1 + g/w/b (in):0/0/1 + p/gopts1! KeyError + inv: 3! KeyError + gopts1! KeyError + p/wopts1! KeyError + inv: 3! KeyError + wopts1! KeyError + wopts2! KeyError + wopts3! KeyError + p/bopts1: 8 + G: 8 + W: 1:0 2:2 3:8 4:1 + B: 1:0 2:2 3:8 4:1 + del wopts3! KeyError + del bopts3! ValueError + G: 8 + W: 1:0 2:2 3:8 4:1 + B: 1:0 2:2 3:8 4:1 + >>> omnifunc + g/w/b:0/0/1 + g/w/b (in):0/0/1 + p/gopts1! KeyError + inv: 1! KeyError + gopts1! KeyError + p/wopts1! KeyError + inv: 1! KeyError + wopts1! KeyError + wopts2! KeyError + wopts3! KeyError + p/bopts1: '' + inv: 1! TypeError + G: '' + W: 1:'A' 2:'B' 3:'' 4:'C' + B: 1:'A' 2:'B' 3:'' 4:'C' + del wopts3! KeyError + del bopts3! ValueError + G: '' + W: 1:'A' 2:'B' 3:'' 4:'C' + B: 1:'A' 2:'B' 3:'' 4:'C' + >>> preserveindent + g/w/b:0/0/1 + g/w/b (in):0/0/1 + p/gopts1! KeyError + inv: 2! KeyError + gopts1! KeyError + p/wopts1! KeyError + inv: 2! KeyError + wopts1! KeyError + wopts2! KeyError + wopts3! KeyError + p/bopts1: False + G: 0 + W: 1:0 2:1 3:0 4:1 + B: 1:0 2:1 3:0 4:1 + del wopts3! KeyError + del bopts3! ValueError + G: 0 + W: 1:0 2:1 3:0 4:1 + B: 1:0 2:1 3:0 4:1 + >>> path + g/w/b:1/0/1 + g/w/b (in):1/0/1 + p/gopts1: '.,..,,' + inv: 0! TypeError + p/wopts1! KeyError + inv: 0! KeyError + wopts1! KeyError + wopts2! KeyError + wopts3! KeyError + p/bopts1: None + inv: 0! TypeError + G: '.,,' + W: 1:'.,,' 2:',,' 3:'.,,' 4:'.' + B: 1:'.,,' 2:',,' 3:'.,,' 4:'.' + del wopts3! KeyError + G: '.,,' + W: 1:'.,,' 2:',,' 3:'.,,' 4:'.,,' + B: 1:'.,,' 2:',,' 3:'.,,' 4:'.,,' + END + + call assert_equal(expected, g:res) + unlet g:res +endfunc + +" Test for vim.buffer object +func Test_python_buffer() + new + call setline(1, "Hello\nWorld") + call assert_fails("let x = pyeval('vim.current.buffer[0]')", 'E859:') + %bw! + + edit Xfile1 + let bnr1 = bufnr() + py cb = vim.current.buffer + vnew Xfile2 + let bnr2 = bufnr() + call setline(1, ['First line', 'Second line', 'Third line']) + py b = vim.current.buffer + wincmd w + + " Tests BufferAppend and BufferItem + py cb.append(b[0]) + call assert_equal(['First line'], getbufline(bnr1, 2)) + %d + + " Tests BufferSlice and BufferAssSlice + py cb.append('abc5') # Will be overwritten + py cb[-1:] = b[:-2] + call assert_equal(['First line'], getbufline(bnr1, 2)) + %d + + " Test BufferLength and BufferAssSlice + py cb.append('def') # Will not be overwritten + py cb[len(cb):] = b[:] + call assert_equal(['def', 'First line', 'Second line', 'Third line'], + \ getbufline(bnr1, 2, '$')) + %d + + " Test BufferAssItem and BufferMark + call setbufline(bnr1, 1, ['one', 'two', 'three']) + call cursor(1, 3) + normal ma + py cb.append('ghi') # Will be overwritten + py cb[-1] = repr((len(cb) - cb.mark('a')[0], cb.mark('a')[1])) + call assert_equal(['(3, 2)'], getbufline(bnr1, 4)) + %d + + " Test BufferRepr + py cb.append(repr(cb) + repr(b)) + call assert_equal([''], getbufline(bnr1, 2)) + %d + + " Modify foreign buffer + py << trim EOF + b.append('foo') + b[0]='bar' + b[0:0]=['baz'] + vim.command('call append("$", getbufline(%i, 1, "$"))' % b.number) + EOF + call assert_equal(['baz', 'bar', 'Second line', 'Third line', 'foo'], + \ getbufline(bnr2, 1, '$')) + %d + + " Test assigning to name property + augroup BUFS + autocmd BufFilePost * python cb.append(vim.eval('expand("")') + ':BufFilePost:' + vim.eval('bufnr("%")')) + autocmd BufFilePre * python cb.append(vim.eval('expand("")') + ':BufFilePre:' + vim.eval('bufnr("%")')) + augroup END + py << trim EOF + import os + old_name = cb.name + cb.name = 'foo' + cb.append(cb.name[-11:].replace(os.path.sep, '/')) + b.name = 'bar' + cb.append(b.name[-11:].replace(os.path.sep, '/')) + cb.name = old_name + cb.append(cb.name[-14:].replace(os.path.sep, '/')) + del old_name + EOF + call assert_equal([bnr1 .. ':BufFilePre:' .. bnr1, + \ bnr1 .. ':BufFilePost:' .. bnr1, + \ 'testdir/foo', + \ bnr2 .. ':BufFilePre:' .. bnr2, + \ bnr2 .. ':BufFilePost:' .. bnr2, + \ 'testdir/bar', + \ bnr1 .. ':BufFilePre:' .. bnr1, + \ bnr1 .. ':BufFilePost:' .. bnr1, + \ 'testdir/Xfile1'], getbufline(bnr1, 2, '$')) + %d + + " Test CheckBuffer + py << trim EOF + for _b in vim.buffers: + if _b is not cb: + vim.command('bwipeout! ' + str(_b.number)) + del _b + cb.append('valid: b:%s, cb:%s' % (repr(b.valid), repr(cb.valid))) + EOF + call assert_equal('valid: b:False, cb:True', getline(2)) + %d + + py << trim EOF + for expr in ('b[1]','b[:] = ["A", "B"]','b[:]','b.append("abc6")', 'b.name = "!"'): + try: + exec(expr) + except vim.error: + pass + else: + # Usually a SEGV here + # Should not happen in any case + cb.append('No exception for ' + expr) + vim.command('cd .') + del b + EOF + call assert_equal([''], getline(1, '$')) + + augroup BUFS + autocmd! + augroup END + augroup! BUFS + %bw! +endfunc + +" Test vim.buffers object +func Test_python_buffers() + %bw! + edit Xfile + py cb = vim.current.buffer + set hidden + edit a + buffer # + edit b + buffer # + edit c + buffer # + py << trim EOF + try: + from __builtin__ import next + except ImportError: + next = lambda o: o.next() + # Check GCing iterator that was not fully exhausted + i = iter(vim.buffers) + cb.append('i:' + str(next(i))) + # and also check creating more than one iterator at a time + i2 = iter(vim.buffers) + cb.append('i2:' + str(next(i2))) + cb.append('i:' + str(next(i))) + # The following should trigger GC and not cause any problems + del i + del i2 + i3 = iter(vim.buffers) + cb.append('i3:' + str(next(i3))) + del i3 + EOF + call assert_equal(['i:', + \ 'i2:', 'i:', 'i3:'], + \ getline(2, '$')) + %d + + py << trim EOF + prevnum = 0 + for b in vim.buffers: + # Check buffer order + if prevnum >= b.number: + cb.append('!!! Buffer numbers not in strictly ascending order') + # Check indexing: vim.buffers[number].number == number + cb.append(str(b.number) + ':' + repr(vim.buffers[b.number]) + \ + '=' + repr(b)) + prevnum = b.number + del prevnum + + cb.append(str(len(vim.buffers))) + EOF + call assert_equal([bufnr('Xfile') .. ':=', + \ bufnr('a') .. ':=', + \ bufnr('b') .. ':=', + \ bufnr('c') .. ':=', '4'], getline(2, '$')) + %d + + py << trim EOF + bnums = list(map(lambda b: b.number, vim.buffers))[1:] + + # Test wiping out buffer with existing iterator + i4 = iter(vim.buffers) + cb.append('i4:' + str(next(i4))) + vim.command('bwipeout! ' + str(bnums.pop(0))) + try: + next(i4) + except vim.error: + pass + else: + cb.append('!!!! No vim.error') + i4 = iter(vim.buffers) + vim.command('bwipeout! ' + str(bnums.pop(-1))) + vim.command('bwipeout! ' + str(bnums.pop(-1))) + cb.append('i4:' + str(next(i4))) + try: + next(i4) + except StopIteration: + cb.append('StopIteration') + del i4 + del bnums + EOF + call assert_equal(['i4:', + \ 'i4:', 'StopIteration'], getline(2, '$')) + %bw! +endfunc + +" Test vim.{tabpage,window}list and vim.{tabpage,window} objects +func Test_python_tabpage_window() + %bw + edit Xfile + py cb = vim.current.buffer + tabnew 0 + tabnew 1 + vnew a.1 + tabnew 2 + vnew a.2 + vnew b.2 + vnew c.2 + + py << trim EOF + cb.append('Number of tabs: ' + str(len(vim.tabpages))) + cb.append('Current tab pages:') + def W(w): + if repr(w).find('(unknown)') != -1: + return '' + else: + return repr(w) + + start = len(cb) + + def Cursor(w): + if w.buffer is cb: + return repr((start - w.cursor[0], w.cursor[1])) + else: + return repr(w.cursor) + + for t in vim.tabpages: + cb.append(' ' + repr(t) + '(' + str(t.number) + ')' + ': ' + \ + str(len(t.windows)) + ' windows, current is ' + W(t.window)) + cb.append(' Windows:') + for w in t.windows: + cb.append(' ' + W(w) + '(' + str(w.number) + ')' + \ + ': displays buffer ' + repr(w.buffer) + \ + '; cursor is at ' + Cursor(w)) + # Other values depend on the size of the terminal, so they are checked + # partly: + for attr in ('height', 'row', 'width', 'col'): + try: + aval = getattr(w, attr) + if type(aval) is not long: + raise TypeError + if aval < 0: + raise ValueError + except Exception: + cb.append('!!!!!! Error while getting attribute ' + attr + \ + ': ' + sys.exc_type.__name__) + del aval + del attr + w.cursor = (len(w.buffer), 0) + del W + del Cursor + cb.append('Number of windows in current tab page: ' + \ + str(len(vim.windows))) + if list(vim.windows) != list(vim.current.tabpage.windows): + cb.append('!!!!!! Windows differ') + EOF + + let expected =<< trim END + Number of tabs: 4 + Current tab pages: + (1): 1 windows, current is + Windows: + (1): displays buffer ; cursor is at (2, 0) + (2): 1 windows, current is + Windows: + (1): displays buffer ; cursor is at (1, 0) + (3): 2 windows, current is + Windows: + (1): displays buffer ; cursor is at (1, 0) + (2): displays buffer ; cursor is at (1, 0) + (4): 4 windows, current is + Windows: + (1): displays buffer ; cursor is at (1, 0) + (2): displays buffer ; cursor is at (1, 0) + (3): displays buffer ; cursor is at (1, 0) + (4): displays buffer ; cursor is at (1, 0) + Number of windows in current tab page: 4 + END + call assert_equal(expected, getbufline(bufnr('Xfile'), 2, '$')) + %bw! +endfunc + +" Test vim.current +func Test_python_vim_current() + %bw + edit Xfile + py cb = vim.current.buffer + tabnew 0 + tabnew 1 + vnew a.1 + tabnew 2 + vnew a.2 + vnew b.2 + vnew c.2 + + py << trim EOF + def H(o): + return repr(o) + cb.append('Current tab page: ' + repr(vim.current.tabpage)) + cb.append('Current window: ' + repr(vim.current.window) + ': ' + \ + H(vim.current.window) + ' is ' + H(vim.current.tabpage.window)) + cb.append('Current buffer: ' + repr(vim.current.buffer) + ': ' + \ + H(vim.current.buffer) + ' is ' + H(vim.current.window.buffer)+ \ + ' is ' + H(vim.current.tabpage.window.buffer)) + del H + EOF + let expected =<< trim END + Current tab page: + Current window: : is + Current buffer: : is is + END + call assert_equal(expected, getbufline(bufnr('Xfile'), 2, '$')) + call deletebufline(bufnr('Xfile'), 1, '$') + + " Assigning: fails + py << trim EOF + try: + vim.current.window = vim.tabpages[0].window + except ValueError: + cb.append('ValueError at assigning foreign tab window') + + for attr in ('window', 'tabpage', 'buffer'): + try: + setattr(vim.current, attr, None) + except TypeError: + cb.append('Type error at assigning None to vim.current.' + attr) + del attr + EOF + + let expected =<< trim END + ValueError at assigning foreign tab window + Type error at assigning None to vim.current.window + Type error at assigning None to vim.current.tabpage + Type error at assigning None to vim.current.buffer + END + call assert_equal(expected, getbufline(bufnr('Xfile'), 2, '$')) + call deletebufline(bufnr('Xfile'), 1, '$') + + call setbufline(bufnr('Xfile'), 1, 'python interface') + py << trim EOF + # Assigning: success + vim.current.tabpage = vim.tabpages[-2] + vim.current.buffer = cb + vim.current.window = vim.windows[0] + vim.current.window.cursor = (len(vim.current.buffer), 0) + cb.append('Current tab page: ' + repr(vim.current.tabpage)) + cb.append('Current window: ' + repr(vim.current.window)) + cb.append('Current buffer: ' + repr(vim.current.buffer)) + cb.append('Current line: ' + repr(vim.current.line)) + EOF + + let expected =<< trim END + Current tab page: + Current window: + Current buffer: + Current line: 'python interface' + END + call assert_equal(expected, getbufline(bufnr('Xfile'), 2, '$')) + call deletebufline(bufnr('Xfile'), 1, '$') + + py << trim EOF + ws = list(vim.windows) + ts = list(vim.tabpages) + for b in vim.buffers: + if b is not cb: + vim.command('bwipeout! ' + str(b.number)) + del b + cb.append('w.valid: ' + repr([w.valid for w in ws])) + cb.append('t.valid: ' + repr([t.valid for t in ts])) + del w + del t + del ts + del ws + EOF + let expected =<< trim END + w.valid: [True, False] + t.valid: [True, False, True, False] + END + call assert_equal(expected, getbufline(bufnr('Xfile'), 2, '$')) + %bw! +endfunc + +" Test types +func Test_python_types() + %d + py cb = vim.current.buffer + py << trim EOF + for expr, attr in ( + ('vim.vars', 'Dictionary'), + ('vim.options', 'Options'), + ('vim.bindeval("{}")', 'Dictionary'), + ('vim.bindeval("[]")', 'List'), + ('vim.bindeval("function(\'tr\')")', 'Function'), + ('vim.current.buffer', 'Buffer'), + ('vim.current.range', 'Range'), + ('vim.current.window', 'Window'), + ('vim.current.tabpage', 'TabPage'), + ): + cb.append(expr + ':' + attr + ':' + \ + repr(type(eval(expr)) is getattr(vim, attr))) + del expr + del attr + EOF + let expected =<< trim END + vim.vars:Dictionary:True + vim.options:Options:True + vim.bindeval("{}"):Dictionary:True + vim.bindeval("[]"):List:True + vim.bindeval("function('tr')"):Function:True + vim.current.buffer:Buffer:True + vim.current.range:Range:True + vim.current.window:Window:True + vim.current.tabpage:TabPage:True + END + call assert_equal(expected, getline(2, '$')) +endfunc + +" Test __dir__() method +func Test_python_dir_method() + %d + py cb = vim.current.buffer + py << trim EOF + for name, o in ( + ('current', vim.current), + ('buffer', vim.current.buffer), + ('window', vim.current.window), + ('tabpage', vim.current.tabpage), + ('range', vim.current.range), + ('dictionary', vim.bindeval('{}')), + ('list', vim.bindeval('[]')), + ('function', vim.bindeval('function("tr")')), + ('output', sys.stdout), + ): + cb.append(name + ':' + ','.join(dir(o))) + del name + del o + EOF + let expected =<< trim END + current:__dir__,__members__,buffer,line,range,tabpage,window + buffer:__dir__,__members__,append,mark,name,number,options,range,valid,vars + window:__dir__,__members__,buffer,col,cursor,height,number,options,row,tabpage,valid,vars,width + tabpage:__dir__,__members__,number,valid,vars,window,windows + range:__dir__,__members__,append,end,start + dictionary:__dir__,__members__,get,has_key,items,keys,locked,pop,popitem,scope,update,values + list:__dir__,__members__,extend,locked + function:__dir__,__members__,args,auto_rebind,self,softspace + output:__dir__,__members__,close,closed,flush,isatty,readable,seekable,softspace,writable,write,writelines + END + call assert_equal(expected, getline(2, '$')) +endfunc + +" Test vim.*.__new__ +func Test_python_new() + call assert_equal({}, pyeval('vim.Dictionary({})')) + call assert_equal({'a': 1}, pyeval('vim.Dictionary(a=1)')) + call assert_equal({'a': 1}, pyeval('vim.Dictionary(((''a'', 1),))')) + call assert_equal([], pyeval('vim.List()')) + call assert_equal(['a', 'b', 'c', '7'], pyeval('vim.List(iter(''abc7''))')) + call assert_equal(function('tr'), pyeval('vim.Function(''tr'')')) + call assert_equal(function('tr', [123, 3, 4]), + \ pyeval('vim.Function(''tr'', args=[123, 3, 4])')) + call assert_equal(function('tr'), pyeval('vim.Function(''tr'', args=[])')) + call assert_equal(function('tr', {}), + \ pyeval('vim.Function(''tr'', self={})')) + call assert_equal(function('tr', [123, 3, 4], {}), + \ pyeval('vim.Function(''tr'', args=[123, 3, 4], self={})')) + call assert_equal(function('tr'), + \ pyeval('vim.Function(''tr'', auto_rebind=False)')) + call assert_equal(function('tr', [123, 3, 4]), + \ pyeval('vim.Function(''tr'', args=[123, 3, 4], auto_rebind=False)')) + call assert_equal(function('tr'), + \ pyeval('vim.Function(''tr'', args=[], auto_rebind=False)')) + call assert_equal(function('tr', {}), + \ pyeval('vim.Function(''tr'', self={}, auto_rebind=False)')) + call assert_equal(function('tr', [123, 3, 4], {}), + \ pyeval('vim.Function(''tr'', args=[123, 3, 4], self={}, auto_rebind=False)')) +endfunc + +" Test vim.Function +func Test_python_vim_func() + function Args(...) + return a:000 + endfunction + + function SelfArgs(...) dict + return [a:000, self] + endfunction + + " The following four lines should not crash + let Pt = function('tr', [[]], {'l': []}) + py Pt = vim.bindeval('Pt') + unlet Pt + py del Pt + + %bw! + py cb = vim.current.buffer + py << trim EOF + def ecall(out_prefix, func, *args, **kwargs): + line = out_prefix + ': ' + try: + ret = func(*args, **kwargs) + except Exception: + line += '!exception: ' + emsg(sys.exc_info()) + else: + line += '!result: ' + vim.Function('string')(ret) + cb.append(line) + a = vim.Function('Args') + pa1 = vim.Function('Args', args=['abcArgsPA1']) + pa2 = vim.Function('Args', args=[]) + pa3 = vim.Function('Args', args=['abcArgsPA3'], self={'abcSelfPA3': 'abcSelfPA3Val'}) + pa4 = vim.Function('Args', self={'abcSelfPA4': 'abcSelfPA4Val'}) + cb.append('a: ' + repr(a)) + cb.append('pa1: ' + repr(pa1)) + cb.append('pa2: ' + repr(pa2)) + cb.append('pa3: ' + repr(pa3)) + cb.append('pa4: ' + repr(pa4)) + sa = vim.Function('SelfArgs') + psa1 = vim.Function('SelfArgs', args=['abcArgsPSA1']) + psa2 = vim.Function('SelfArgs', args=[]) + psa3 = vim.Function('SelfArgs', args=['abcArgsPSA3'], self={'abcSelfPSA3': 'abcSelfPSA3Val'}) + psa4 = vim.Function('SelfArgs', self={'abcSelfPSA4': 'abcSelfPSA4Val'}) + psa5 = vim.Function('SelfArgs', self={'abcSelfPSA5': 'abcSelfPSA5Val'}, auto_rebind=0) + psa6 = vim.Function('SelfArgs', args=['abcArgsPSA6'], self={'abcSelfPSA6': 'abcSelfPSA6Val'}, auto_rebind=()) + psa7 = vim.Function('SelfArgs', args=['abcArgsPSA7'], auto_rebind=[]) + psa8 = vim.Function('SelfArgs', auto_rebind=False) + psa9 = vim.Function('SelfArgs', self={'abcSelfPSA9': 'abcSelfPSA9Val'}, auto_rebind=True) + psaA = vim.Function('SelfArgs', args=['abcArgsPSAA'], self={'abcSelfPSAA': 'abcSelfPSAAVal'}, auto_rebind=1) + psaB = vim.Function('SelfArgs', args=['abcArgsPSAB'], auto_rebind={'abcARPSAB': 'abcARPSABVal'}) + psaC = vim.Function('SelfArgs', auto_rebind=['abcARPSAC']) + cb.append('sa: ' + repr(sa)) + cb.append('psa1: ' + repr(psa1)) + cb.append('psa2: ' + repr(psa2)) + cb.append('psa3: ' + repr(psa3)) + cb.append('psa4: ' + repr(psa4)) + cb.append('psa5: ' + repr(psa5)) + cb.append('psa6: ' + repr(psa6)) + cb.append('psa7: ' + repr(psa7)) + cb.append('psa8: ' + repr(psa8)) + cb.append('psa9: ' + repr(psa9)) + cb.append('psaA: ' + repr(psaA)) + cb.append('psaB: ' + repr(psaB)) + cb.append('psaC: ' + repr(psaC)) + + psar = vim.Function('SelfArgs', args=[{'abcArgsPSAr': 'abcArgsPSArVal'}], self={'abcSelfPSAr': 'abcSelfPSArVal'}) + psar.args[0]['abcArgsPSAr2'] = [psar.self, psar.args[0]] + psar.self['rec'] = psar + psar.self['self'] = psar.self + psar.self['args'] = psar.args + + try: + cb.append('psar: ' + repr(psar)) + except Exception: + cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info())) + EOF + + let expected =<< trim END + a: + pa1: + pa2: + pa3: + pa4: + sa: + psa1: + psa2: + psa3: + psa4: + psa5: + psa6: + psa7: + psa8: + psa9: + psaA: + psaB: + psaC: + psar: + END + call assert_equal(expected, getline(2, '$')) + %d + + call assert_equal(function('Args'), pyeval('a')) + call assert_equal(function('Args', ['abcArgsPA1']), pyeval('pa1')) + call assert_equal(function('Args'), pyeval('pa2')) + call assert_equal(function('Args', ['abcArgsPA3'], {'abcSelfPA3': 'abcSelfPA3Val'}), pyeval('pa3')) + call assert_equal(function('Args', {'abcSelfPA4': 'abcSelfPA4Val'}), pyeval('pa4')) + call assert_equal(function('SelfArgs'), pyeval('sa')) + call assert_equal(function('SelfArgs', ['abcArgsPSA1']), pyeval('psa1')) + call assert_equal(function('SelfArgs'), pyeval('psa2')) + call assert_equal(function('SelfArgs', ['abcArgsPSA3'], {'abcSelfPSA3': 'abcSelfPSA3Val'}), pyeval('psa3')) + call assert_equal(function('SelfArgs', {'abcSelfPSA4': 'abcSelfPSA4Val'}), pyeval('psa4')) + call assert_equal(function('SelfArgs', {'abcSelfPSA5': 'abcSelfPSA5Val'}), pyeval('psa5')) + call assert_equal(function('SelfArgs', ['abcArgsPSA6'], {'abcSelfPSA6': 'abcSelfPSA6Val'}), pyeval('psa6')) + call assert_equal(function('SelfArgs', ['abcArgsPSA7']), pyeval('psa7')) + call assert_equal(function('SelfArgs'), pyeval('psa8')) + call assert_equal(function('SelfArgs', {'abcSelfPSA9': 'abcSelfPSA9Val'}), pyeval('psa9')) + call assert_equal(function('SelfArgs', ['abcArgsPSAA'], {'abcSelfPSAA': 'abcSelfPSAAVal'}), pyeval('psaA')) + call assert_equal(function('SelfArgs', ['abcArgsPSAB']), pyeval('psaB')) + call assert_equal(function('SelfArgs'), pyeval('psaC')) + + let res = [] + for v in ['sa', 'psa1', 'psa2', 'psa3', 'psa4', 'psa5', 'psa6', 'psa7', + \ 'psa8', 'psa9', 'psaA', 'psaB', 'psaC'] + let d = {'f': pyeval(v)} + call add(res, 'd.' .. v .. '(): ' .. string(d.f())) + endfor + + let expected =<< trim END + d.sa(): [[], {'f': function('SelfArgs')}] + d.psa1(): [['abcArgsPSA1'], {'f': function('SelfArgs', ['abcArgsPSA1'])}] + d.psa2(): [[], {'f': function('SelfArgs')}] + d.psa3(): [['abcArgsPSA3'], {'abcSelfPSA3': 'abcSelfPSA3Val'}] + d.psa4(): [[], {'abcSelfPSA4': 'abcSelfPSA4Val'}] + d.psa5(): [[], {'abcSelfPSA5': 'abcSelfPSA5Val'}] + d.psa6(): [['abcArgsPSA6'], {'abcSelfPSA6': 'abcSelfPSA6Val'}] + d.psa7(): [['abcArgsPSA7'], {'f': function('SelfArgs', ['abcArgsPSA7'])}] + d.psa8(): [[], {'f': function('SelfArgs')}] + d.psa9(): [[], {'f': function('SelfArgs', {'abcSelfPSA9': 'abcSelfPSA9Val'})}] + d.psaA(): [['abcArgsPSAA'], {'f': function('SelfArgs', ['abcArgsPSAA'], {'abcSelfPSAA': 'abcSelfPSAAVal'})}] + d.psaB(): [['abcArgsPSAB'], {'f': function('SelfArgs', ['abcArgsPSAB'])}] + d.psaC(): [[], {'f': function('SelfArgs')}] + END + call assert_equal(expected, res) + + py ecall('a()', a, ) + py ecall('pa1()', pa1, ) + py ecall('pa2()', pa2, ) + py ecall('pa3()', pa3, ) + py ecall('pa4()', pa4, ) + py ecall('sa()', sa, ) + py ecall('psa1()', psa1, ) + py ecall('psa2()', psa2, ) + py ecall('psa3()', psa3, ) + py ecall('psa4()', psa4, ) + + py ecall('a(42, 43)', a, 42, 43) + py ecall('pa1(42, 43)', pa1, 42, 43) + py ecall('pa2(42, 43)', pa2, 42, 43) + py ecall('pa3(42, 43)', pa3, 42, 43) + py ecall('pa4(42, 43)', pa4, 42, 43) + py ecall('sa(42, 43)', sa, 42, 43) + py ecall('psa1(42, 43)', psa1, 42, 43) + py ecall('psa2(42, 43)', psa2, 42, 43) + py ecall('psa3(42, 43)', psa3, 42, 43) + py ecall('psa4(42, 43)', psa4, 42, 43) + + py ecall('a(42, self={"20": 1})', a, 42, self={'20': 1}) + py ecall('pa1(42, self={"20": 1})', pa1, 42, self={'20': 1}) + py ecall('pa2(42, self={"20": 1})', pa2, 42, self={'20': 1}) + py ecall('pa3(42, self={"20": 1})', pa3, 42, self={'20': 1}) + py ecall('pa4(42, self={"20": 1})', pa4, 42, self={'20': 1}) + py ecall('sa(42, self={"20": 1})', sa, 42, self={'20': 1}) + py ecall('psa1(42, self={"20": 1})', psa1, 42, self={'20': 1}) + py ecall('psa2(42, self={"20": 1})', psa2, 42, self={'20': 1}) + py ecall('psa3(42, self={"20": 1})', psa3, 42, self={'20': 1}) + py ecall('psa4(42, self={"20": 1})', psa4, 42, self={'20': 1}) + + py ecall('a(self={"20": 1})', a, self={'20': 1}) + py ecall('pa1(self={"20": 1})', pa1, self={'20': 1}) + py ecall('pa2(self={"20": 1})', pa2, self={'20': 1}) + py ecall('pa3(self={"20": 1})', pa3, self={'20': 1}) + py ecall('pa4(self={"20": 1})', pa4, self={'20': 1}) + py ecall('sa(self={"20": 1})', sa, self={'20': 1}) + py ecall('psa1(self={"20": 1})', psa1, self={'20': 1}) + py ecall('psa2(self={"20": 1})', psa2, self={'20': 1}) + py ecall('psa3(self={"20": 1})', psa3, self={'20': 1}) + py ecall('psa4(self={"20": 1})', psa4, self={'20': 1}) + + py << trim EOF + def s(v): + if v is None: + return repr(v) + else: + return vim.Function('string')(v) + + cb.append('a.args: ' + s(a.args)) + cb.append('pa1.args: ' + s(pa1.args)) + cb.append('pa2.args: ' + s(pa2.args)) + cb.append('pa3.args: ' + s(pa3.args)) + cb.append('pa4.args: ' + s(pa4.args)) + cb.append('sa.args: ' + s(sa.args)) + cb.append('psa1.args: ' + s(psa1.args)) + cb.append('psa2.args: ' + s(psa2.args)) + cb.append('psa3.args: ' + s(psa3.args)) + cb.append('psa4.args: ' + s(psa4.args)) + + cb.append('a.self: ' + s(a.self)) + cb.append('pa1.self: ' + s(pa1.self)) + cb.append('pa2.self: ' + s(pa2.self)) + cb.append('pa3.self: ' + s(pa3.self)) + cb.append('pa4.self: ' + s(pa4.self)) + cb.append('sa.self: ' + s(sa.self)) + cb.append('psa1.self: ' + s(psa1.self)) + cb.append('psa2.self: ' + s(psa2.self)) + cb.append('psa3.self: ' + s(psa3.self)) + cb.append('psa4.self: ' + s(psa4.self)) + + cb.append('a.name: ' + s(a.name)) + cb.append('pa1.name: ' + s(pa1.name)) + cb.append('pa2.name: ' + s(pa2.name)) + cb.append('pa3.name: ' + s(pa3.name)) + cb.append('pa4.name: ' + s(pa4.name)) + cb.append('sa.name: ' + s(sa.name)) + cb.append('psa1.name: ' + s(psa1.name)) + cb.append('psa2.name: ' + s(psa2.name)) + cb.append('psa3.name: ' + s(psa3.name)) + cb.append('psa4.name: ' + s(psa4.name)) + + cb.append('a.auto_rebind: ' + s(a.auto_rebind)) + cb.append('pa1.auto_rebind: ' + s(pa1.auto_rebind)) + cb.append('pa2.auto_rebind: ' + s(pa2.auto_rebind)) + cb.append('pa3.auto_rebind: ' + s(pa3.auto_rebind)) + cb.append('pa4.auto_rebind: ' + s(pa4.auto_rebind)) + cb.append('sa.auto_rebind: ' + s(sa.auto_rebind)) + cb.append('psa1.auto_rebind: ' + s(psa1.auto_rebind)) + cb.append('psa2.auto_rebind: ' + s(psa2.auto_rebind)) + cb.append('psa3.auto_rebind: ' + s(psa3.auto_rebind)) + cb.append('psa4.auto_rebind: ' + s(psa4.auto_rebind)) + cb.append('psa5.auto_rebind: ' + s(psa5.auto_rebind)) + cb.append('psa6.auto_rebind: ' + s(psa6.auto_rebind)) + cb.append('psa7.auto_rebind: ' + s(psa7.auto_rebind)) + cb.append('psa8.auto_rebind: ' + s(psa8.auto_rebind)) + cb.append('psa9.auto_rebind: ' + s(psa9.auto_rebind)) + cb.append('psaA.auto_rebind: ' + s(psaA.auto_rebind)) + cb.append('psaB.auto_rebind: ' + s(psaB.auto_rebind)) + cb.append('psaC.auto_rebind: ' + s(psaC.auto_rebind)) + + del s + + del a + del pa1 + del pa2 + del pa3 + del pa4 + del sa + del psa1 + del psa2 + del psa3 + del psa4 + del psa5 + del psa6 + del psa7 + del psa8 + del psa9 + del psaA + del psaB + del psaC + del psar + + del ecall + EOF + + let expected =<< trim END + a(): !result: [] + pa1(): !result: ['abcArgsPA1'] + pa2(): !result: [] + pa3(): !result: ['abcArgsPA3'] + pa4(): !result: [] + sa(): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) + psa1(): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) + psa2(): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) + psa3(): !result: [['abcArgsPSA3'], {'abcSelfPSA3': 'abcSelfPSA3Val'}] + psa4(): !result: [[], {'abcSelfPSA4': 'abcSelfPSA4Val'}] + a(42, 43): !result: [42, 43] + pa1(42, 43): !result: ['abcArgsPA1', 42, 43] + pa2(42, 43): !result: [42, 43] + pa3(42, 43): !result: ['abcArgsPA3', 42, 43] + pa4(42, 43): !result: [42, 43] + sa(42, 43): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) + psa1(42, 43): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) + psa2(42, 43): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) + psa3(42, 43): !result: [['abcArgsPSA3', 42, 43], {'abcSelfPSA3': 'abcSelfPSA3Val'}] + psa4(42, 43): !result: [[42, 43], {'abcSelfPSA4': 'abcSelfPSA4Val'}] + a(42, self={"20": 1}): !result: [42] + pa1(42, self={"20": 1}): !result: ['abcArgsPA1', 42] + pa2(42, self={"20": 1}): !result: [42] + pa3(42, self={"20": 1}): !result: ['abcArgsPA3', 42] + pa4(42, self={"20": 1}): !result: [42] + sa(42, self={"20": 1}): !result: [[42], {'20': 1}] + psa1(42, self={"20": 1}): !result: [['abcArgsPSA1', 42], {'20': 1}] + psa2(42, self={"20": 1}): !result: [[42], {'20': 1}] + psa3(42, self={"20": 1}): !result: [['abcArgsPSA3', 42], {'20': 1}] + psa4(42, self={"20": 1}): !result: [[42], {'20': 1}] + a(self={"20": 1}): !result: [] + pa1(self={"20": 1}): !result: ['abcArgsPA1'] + pa2(self={"20": 1}): !result: [] + pa3(self={"20": 1}): !result: ['abcArgsPA3'] + pa4(self={"20": 1}): !result: [] + sa(self={"20": 1}): !result: [[], {'20': 1}] + psa1(self={"20": 1}): !result: [['abcArgsPSA1'], {'20': 1}] + psa2(self={"20": 1}): !result: [[], {'20': 1}] + psa3(self={"20": 1}): !result: [['abcArgsPSA3'], {'20': 1}] + psa4(self={"20": 1}): !result: [[], {'20': 1}] + a.args: None + pa1.args: ['abcArgsPA1'] + pa2.args: None + pa3.args: ['abcArgsPA3'] + pa4.args: None + sa.args: None + psa1.args: ['abcArgsPSA1'] + psa2.args: None + psa3.args: ['abcArgsPSA3'] + psa4.args: None + a.self: None + pa1.self: None + pa2.self: None + pa3.self: {'abcSelfPA3': 'abcSelfPA3Val'} + pa4.self: {'abcSelfPA4': 'abcSelfPA4Val'} + sa.self: None + psa1.self: None + psa2.self: None + psa3.self: {'abcSelfPSA3': 'abcSelfPSA3Val'} + psa4.self: {'abcSelfPSA4': 'abcSelfPSA4Val'} + a.name: 'Args' + pa1.name: 'Args' + pa2.name: 'Args' + pa3.name: 'Args' + pa4.name: 'Args' + sa.name: 'SelfArgs' + psa1.name: 'SelfArgs' + psa2.name: 'SelfArgs' + psa3.name: 'SelfArgs' + psa4.name: 'SelfArgs' + a.auto_rebind: 1 + pa1.auto_rebind: 1 + pa2.auto_rebind: 1 + pa3.auto_rebind: 0 + pa4.auto_rebind: 0 + sa.auto_rebind: 1 + psa1.auto_rebind: 1 + psa2.auto_rebind: 1 + psa3.auto_rebind: 0 + psa4.auto_rebind: 0 + psa5.auto_rebind: 0 + psa6.auto_rebind: 0 + psa7.auto_rebind: 1 + psa8.auto_rebind: 1 + psa9.auto_rebind: 1 + psaA.auto_rebind: 1 + psaB.auto_rebind: 1 + psaC.auto_rebind: 1 + END + call assert_equal(expected, getline(2, '$')) + %bw! +endfunc + +" Test stdout/stderr +func Test_python_stdin_stderr() + let caught_writeerr = 0 + let caught_writelineerr = 0 + redir => messages + py sys.stdout.write('abc8') ; sys.stdout.write('def') + try + py sys.stderr.write('abc9') ; sys.stderr.write('def') + catch /abc9def/ + let caught_writeerr = 1 + endtry + py sys.stdout.writelines(iter('abcA')) + try + py sys.stderr.writelines(iter('abcB')) + catch /abcB/ + let caught_writelineerr = 1 + endtry + redir END + call assert_equal("\nabc8def\nabcA", messages) + call assert_equal(1, caught_writeerr) + call assert_equal(1, caught_writelineerr) +endfunc + +" Test subclassing +func Test_python_subclass() + new + fun Put(...) + return a:000 + endfun + + py << trim EOF + class DupDict(vim.Dictionary): + def __setitem__(self, key, value): + super(DupDict, self).__setitem__(key, value) + super(DupDict, self).__setitem__('dup_' + key, value) + dd = DupDict() + dd['a'] = 'b' + + class DupList(vim.List): + def __getitem__(self, idx): + return [super(DupList, self).__getitem__(idx)] * 2 + + dl = DupList() + dl2 = DupList(iter('abcC')) + dl.extend(dl2[0]) + + class DupFun(vim.Function): + def __call__(self, arg): + return super(DupFun, self).__call__(arg, arg) + + df = DupFun('Put') + EOF + + call assert_equal(['a', 'dup_a'], sort(keys(pyeval('dd')))) + call assert_equal(['a', 'a'], pyeval('dl')) + call assert_equal(['a', 'b', 'c', 'C'], pyeval('dl2')) + call assert_equal([2, 2], pyeval('df(2)')) + call assert_equal(1, pyeval('dl') is# pyeval('dl')) + call assert_equal(1, pyeval('dd') is# pyeval('dd')) + call assert_equal(function('Put'), pyeval('df')) + delfunction Put + py << trim EOF + del DupDict + del DupList + del DupFun + del dd + del dl + del dl2 + del df + EOF + close! +endfunc + +" Test chdir +func Test_python_chdir() + new Xfile + py cb = vim.current.buffer + py << trim EOF + import os + fnamemodify = vim.Function('fnamemodify') + cb.append(fnamemodify('.', ':p:h:t')) + cb.append(vim.eval('@%')) + os.chdir('..') + path = fnamemodify('.', ':p:h:t') + if path != 'src': + # Running tests from a shadow directory, so move up another level + # This will result in @% looking like shadow/testdir/Xfile, hence the + # extra fnamemodify + os.chdir('..') + cb.append(fnamemodify('.', ':p:h:t')) + cb.append(fnamemodify(vim.eval('@%'), ':s?^%s.??' % path).replace(os.path.sep, '/')) + os.chdir(path) + del path + else: + cb.append(fnamemodify('.', ':p:h:t')) + cb.append(vim.eval('@%').replace(os.path.sep, '/')) + os.chdir('testdir') + cb.append(fnamemodify('.', ':p:h:t')) + cb.append(vim.eval('@%')) + del fnamemodify + EOF + call assert_equal(['testdir', 'Xfile', 'src', 'testdir/Xfile', 'testdir', + \ 'Xfile'], getline(2, '$')) + close! +endfunc + +" Test errors +func Test_python_errors() + fun F() dict + endfun + + fun D() + endfun + + new + py cb = vim.current.buffer + + py << trim EOF + d = vim.Dictionary() + ned = vim.Dictionary(foo='bar', baz='abcD') + dl = vim.Dictionary(a=1) + dl.locked = True + l = vim.List() + ll = vim.List('abcE') + ll.locked = True + nel = vim.List('abcO') + f = vim.Function('string') + fd = vim.Function('F') + fdel = vim.Function('D') + vim.command('delfunction D') + + def subexpr_test(expr, name, subexprs): + cb.append('>>> Testing %s using %s' % (name, expr)) + for subexpr in subexprs: + ee(expr % subexpr) + cb.append('<<< Finished') + + def stringtochars_test(expr): + return subexpr_test(expr, 'StringToChars', ( + '1', # Fail type checks + 'u"\\0"', # Fail PyString_AsStringAndSize(bytes, , NULL) check + '"\\0"', # Fail PyString_AsStringAndSize(object, , NULL) check + )) + + class Mapping(object): + def __init__(self, d): + self.d = d + + def __getitem__(self, key): + return self.d[key] + + def keys(self): + return self.d.keys() + + def items(self): + return self.d.items() + + def convertfrompyobject_test(expr, recurse=True): + # pydict_to_tv + stringtochars_test(expr % '{%s : 1}') + if recurse: + convertfrompyobject_test(expr % '{"abcF" : %s}', False) + # pymap_to_tv + stringtochars_test(expr % 'Mapping({%s : 1})') + if recurse: + convertfrompyobject_test(expr % 'Mapping({"abcG" : %s})', False) + # pyseq_to_tv + iter_test(expr) + return subexpr_test(expr, 'ConvertFromPyObject', ( + 'None', # Not conversible + '{"": 1}', # Empty key not allowed + '{u"": 1}', # Same, but with unicode object + 'FailingMapping()', # + 'FailingMappingKey()', # + 'FailingNumber()', # + )) + + def convertfrompymapping_test(expr): + convertfrompyobject_test(expr) + return subexpr_test(expr, 'ConvertFromPyMapping', ( + '[]', + )) + + def iter_test(expr): + return subexpr_test(expr, '*Iter*', ( + 'FailingIter()', + 'FailingIterNext()', + )) + + def number_test(expr, natural=False, unsigned=False): + if natural: + unsigned = True + return subexpr_test(expr, 'NumberToLong', ( + '[]', + 'None', + ) + (unsigned and ('-1',) or ()) + + (natural and ('0',) or ())) + + class FailingTrue(object): + def __nonzero__(self): + raise NotImplementedError('bool') + + class FailingIter(object): + def __iter__(self): + raise NotImplementedError('iter') + + class FailingIterNext(object): + def __iter__(self): + return self + + def next(self): + raise NotImplementedError('next') + + class FailingIterNextN(object): + def __init__(self, n): + self.n = n + + def __iter__(self): + return self + + def next(self): + if self.n: + self.n -= 1 + return 1 + else: + raise NotImplementedError('next N') + + class FailingMappingKey(object): + def __getitem__(self, item): + raise NotImplementedError('getitem:mappingkey') + + def keys(self): + return list("abcH") + + class FailingMapping(object): + def __getitem__(self): + raise NotImplementedError('getitem:mapping') + + def keys(self): + raise NotImplementedError('keys') + + class FailingList(list): + def __getitem__(self, idx): + if i == 2: + raise NotImplementedError('getitem:list') + else: + return super(FailingList, self).__getitem__(idx) + + class NoArgsCall(object): + def __call__(self): + pass + + class FailingCall(object): + def __call__(self, path): + raise NotImplementedError('call') + + class FailingNumber(object): + def __int__(self): + raise NotImplementedError('int') + + cb.append("> Output") + cb.append(">> OutputSetattr") + ee('del sys.stdout.softspace') + number_test('sys.stdout.softspace = %s', unsigned=True) + number_test('sys.stderr.softspace = %s', unsigned=True) + ee('assert sys.stdout.isatty()==False') + ee('assert sys.stdout.seekable()==False') + ee('sys.stdout.close()') + ee('sys.stdout.flush()') + ee('assert sys.stderr.isatty()==False') + ee('assert sys.stderr.seekable()==False') + ee('sys.stderr.close()') + ee('sys.stderr.flush()') + ee('sys.stdout.attr = None') + cb.append(">> OutputWrite") + ee('assert sys.stdout.writable()==True') + ee('assert sys.stdout.readable()==False') + ee('assert sys.stderr.writable()==True') + ee('assert sys.stderr.readable()==False') + ee('assert sys.stdout.closed()==False') + ee('assert sys.stderr.closed()==False') + ee('assert sys.stdout.errors=="strict"') + ee('assert sys.stderr.errors=="strict"') + ee('assert sys.stdout.encoding==sys.stderr.encoding') + ee('sys.stdout.write(None)') + cb.append(">> OutputWriteLines") + ee('sys.stdout.writelines(None)') + ee('sys.stdout.writelines([1])') + iter_test('sys.stdout.writelines(%s)') + cb.append("> VimCommand") + stringtochars_test('vim.command(%s)') + ee('vim.command("", 2)') + #! Not checked: vim->python exceptions translating: checked later + cb.append("> VimToPython") + #! Not checked: everything: needs errors in internal python functions + cb.append("> VimEval") + stringtochars_test('vim.eval(%s)') + ee('vim.eval("", FailingTrue())') + #! Not checked: everything: needs errors in internal python functions + cb.append("> VimEvalPy") + stringtochars_test('vim.bindeval(%s)') + ee('vim.eval("", 2)') + #! Not checked: vim->python exceptions translating: checked later + cb.append("> VimStrwidth") + stringtochars_test('vim.strwidth(%s)') + cb.append("> VimForeachRTP") + ee('vim.foreach_rtp(None)') + ee('vim.foreach_rtp(NoArgsCall())') + ee('vim.foreach_rtp(FailingCall())') + ee('vim.foreach_rtp(int, 2)') + cb.append('> import') + old_rtp = vim.options['rtp'] + vim.options['rtp'] = os.getcwd().replace('\\', '\\\\').replace(',', '\\,') + ee('import xxx_no_such_module_xxx') + ee('import failing_import') + ee('import failing') + vim.options['rtp'] = old_rtp + del old_rtp + cb.append("> Options") + cb.append(">> OptionsItem") + ee('vim.options["abcQ"]') + ee('vim.options[""]') + stringtochars_test('vim.options[%s]') + cb.append(">> OptionsContains") + stringtochars_test('%s in vim.options') + cb.append("> Dictionary") + cb.append(">> DictionaryConstructor") + ee('vim.Dictionary("abcI")') + ##! Not checked: py_dict_alloc failure + cb.append(">> DictionarySetattr") + ee('del d.locked') + ee('d.locked = FailingTrue()') + ee('vim.vvars.locked = False') + ee('d.scope = True') + ee('d.xxx = True') + cb.append(">> _DictionaryItem") + ee('d.get("a", 2, 3)') + stringtochars_test('d.get(%s)') + ee('d.pop("a")') + ee('dl.pop("a")') + cb.append(">> DictionaryContains") + ee('"" in d') + ee('0 in d') + cb.append(">> DictionaryIterNext") + ee('for i in ned: ned["a"] = 1') + del i + cb.append(">> DictionaryAssItem") + ee('dl["b"] = 1') + stringtochars_test('d[%s] = 1') + convertfrompyobject_test('d["a"] = %s') + cb.append(">> DictionaryUpdate") + cb.append(">>> kwargs") + cb.append(">>> iter") + ee('d.update(FailingMapping())') + ee('d.update([FailingIterNext()])') + ee('d.update([FailingIterNextN(1)])') + iter_test('d.update(%s)') + convertfrompyobject_test('d.update(%s)') + stringtochars_test('d.update(((%s, 0),))') + convertfrompyobject_test('d.update((("a", %s),))') + cb.append(">> DictionaryPopItem") + ee('d.popitem(1, 2)') + cb.append(">> DictionaryHasKey") + ee('d.has_key()') + cb.append("> List") + cb.append(">> ListConstructor") + ee('vim.List(1, 2)') + ee('vim.List(a=1)') + iter_test('vim.List(%s)') + convertfrompyobject_test('vim.List([%s])') + cb.append(">> ListItem") + ee('l[1000]') + cb.append(">> ListAssItem") + ee('ll[1] = 2') + ee('l[1000] = 3') + cb.append(">> ListAssSlice") + ee('ll[1:100] = "abcJ"') + iter_test('l[:] = %s') + ee('nel[1:10:2] = "abcK"') + cb.append(repr(tuple(nel))) + ee('nel[1:10:2] = "a"') + cb.append(repr(tuple(nel))) + ee('nel[1:1:-1] = "a"') + cb.append(repr(tuple(nel))) + ee('nel[:] = FailingIterNextN(2)') + cb.append(repr(tuple(nel))) + convertfrompyobject_test('l[:] = [%s]') + cb.append(">> ListConcatInPlace") + iter_test('l.extend(%s)') + convertfrompyobject_test('l.extend([%s])') + cb.append(">> ListSetattr") + ee('del l.locked') + ee('l.locked = FailingTrue()') + ee('l.xxx = True') + cb.append("> Function") + cb.append(">> FunctionConstructor") + cb.append(">>> FunctionConstructor") + ee('vim.Function("123")') + ee('vim.Function("xxx_non_existent_function_xxx")') + ee('vim.Function("xxx#non#existent#function#xxx")') + ee('vim.Function("xxx_non_existent_function_xxx2", args=[])') + ee('vim.Function("xxx_non_existent_function_xxx3", self={})') + ee('vim.Function("xxx_non_existent_function_xxx4", args=[], self={})') + cb.append(">>> FunctionNew") + ee('vim.Function("tr", self="abcFuncSelf")') + ee('vim.Function("tr", args=427423)') + ee('vim.Function("tr", self="abcFuncSelf2", args="abcFuncArgs2")') + ee('vim.Function(self="abcFuncSelf2", args="abcFuncArgs2")') + ee('vim.Function("tr", "", self="abcFuncSelf2", args="abcFuncArgs2")') + ee('vim.Function("tr", "")') + cb.append(">> FunctionCall") + convertfrompyobject_test('f(%s)') + convertfrompymapping_test('fd(self=%s)') + cb.append("> TabPage") + cb.append(">> TabPageAttr") + ee('vim.current.tabpage.xxx') + cb.append("> TabList") + cb.append(">> TabListItem") + ee('vim.tabpages[1000]') + cb.append("> Window") + cb.append(">> WindowAttr") + ee('vim.current.window.xxx') + cb.append(">> WindowSetattr") + ee('vim.current.window.buffer = 0') + ee('vim.current.window.cursor = (100000000, 100000000)') + ee('vim.current.window.cursor = True') + number_test('vim.current.window.height = %s', unsigned=True) + number_test('vim.current.window.width = %s', unsigned=True) + ee('vim.current.window.xxxxxx = True') + cb.append("> WinList") + cb.append(">> WinListItem") + ee('vim.windows[1000]') + cb.append("> Buffer") + cb.append(">> StringToLine (indirect)") + ee('vim.current.buffer[0] = u"\\na"') + ee('vim.current.buffer[0] = "\\na"') + cb.append(">> SetBufferLine (indirect)") + ee('vim.current.buffer[0] = True') + cb.append(">> SetBufferLineList (indirect)") + ee('vim.current.buffer[:] = True') + ee('vim.current.buffer[:] = ["\\na", "bc"]') + cb.append(">> InsertBufferLines (indirect)") + ee('vim.current.buffer.append(None)') + ee('vim.current.buffer.append(["\\na", "bc"])') + ee('vim.current.buffer.append("\\nbc")') + cb.append(">> RBItem") + ee('vim.current.buffer[100000000]') + cb.append(">> RBAsItem") + ee('vim.current.buffer[100000000] = ""') + cb.append(">> BufferAttr") + ee('vim.current.buffer.xxx') + cb.append(">> BufferSetattr") + ee('vim.current.buffer.name = True') + ee('vim.current.buffer.xxx = True') + cb.append(">> BufferMark") + ee('vim.current.buffer.mark(0)') + ee('vim.current.buffer.mark("abcM")') + ee('vim.current.buffer.mark("!")') + cb.append(">> BufferRange") + ee('vim.current.buffer.range(1, 2, 3)') + cb.append("> BufMap") + cb.append(">> BufMapItem") + ee('vim.buffers[100000000]') + number_test('vim.buffers[%s]', natural=True) + cb.append("> Current") + cb.append(">> CurrentGetattr") + ee('vim.current.xxx') + cb.append(">> CurrentSetattr") + ee('vim.current.line = True') + ee('vim.current.buffer = True') + ee('vim.current.window = True') + ee('vim.current.tabpage = True') + ee('vim.current.xxx = True') + del d + del ned + del dl + del l + del ll + del nel + del f + del fd + del fdel + del subexpr_test + del stringtochars_test + del Mapping + del convertfrompyobject_test + del convertfrompymapping_test + del iter_test + del number_test + del FailingTrue + del FailingIter + del FailingIterNext + del FailingIterNextN + del FailingMapping + del FailingMappingKey + del FailingList + del NoArgsCall + del FailingCall + del FailingNumber + EOF + delfunction F + + let expected =<< trim END + > Output + >> OutputSetattr + del sys.stdout.softspace:AttributeError:('cannot delete OutputObject attributes',) + >>> Testing NumberToLong using sys.stdout.softspace = %s + sys.stdout.softspace = []:TypeError:('expected int(), long() or something supporting coercing to long(), but got list',) + sys.stdout.softspace = None:TypeError:('expected int(), long() or something supporting coercing to long(), but got NoneType',) + sys.stdout.softspace = -1:ValueError:('number must be greater or equal to zero',) + <<< Finished + >>> Testing NumberToLong using sys.stderr.softspace = %s + sys.stderr.softspace = []:TypeError:('expected int(), long() or something supporting coercing to long(), but got list',) + sys.stderr.softspace = None:TypeError:('expected int(), long() or something supporting coercing to long(), but got NoneType',) + sys.stderr.softspace = -1:ValueError:('number must be greater or equal to zero',) + <<< Finished + assert sys.stdout.isatty()==False:NOT FAILED + assert sys.stdout.seekable()==False:NOT FAILED + sys.stdout.close():NOT FAILED + sys.stdout.flush():NOT FAILED + assert sys.stderr.isatty()==False:NOT FAILED + assert sys.stderr.seekable()==False:NOT FAILED + sys.stderr.close():NOT FAILED + sys.stderr.flush():NOT FAILED + sys.stdout.attr = None:AttributeError:('invalid attribute: attr',) + >> OutputWrite + assert sys.stdout.writable()==True:NOT FAILED + assert sys.stdout.readable()==False:NOT FAILED + assert sys.stderr.writable()==True:NOT FAILED + assert sys.stderr.readable()==False:NOT FAILED + assert sys.stdout.closed()==False:NOT FAILED + assert sys.stderr.closed()==False:NOT FAILED + assert sys.stdout.errors=="strict":NOT FAILED + assert sys.stderr.errors=="strict":NOT FAILED + assert sys.stdout.encoding==sys.stderr.encoding:NOT FAILED + sys.stdout.write(None):TypeError:('coercing to Unicode: need string or buffer, NoneType found',) + >> OutputWriteLines + sys.stdout.writelines(None):TypeError:("'NoneType' object is not iterable",) + sys.stdout.writelines([1]):TypeError:('coercing to Unicode: need string or buffer, int found',) + >>> Testing *Iter* using sys.stdout.writelines(%s) + sys.stdout.writelines(FailingIter()):NotImplementedError:('iter',) + sys.stdout.writelines(FailingIterNext()):NotImplementedError:('next',) + <<< Finished + > VimCommand + >>> Testing StringToChars using vim.command(%s) + vim.command(1):TypeError:('expected str() or unicode() instance, but got int',) + vim.command(u"\0"):TypeError:('expected string without null bytes',) + vim.command("\0"):TypeError:('expected string without null bytes',) + <<< Finished + vim.command("", 2):TypeError:('command() takes exactly one argument (2 given)',) + > VimToPython + > VimEval + >>> Testing StringToChars using vim.eval(%s) + vim.eval(1):TypeError:('expected str() or unicode() instance, but got int',) + vim.eval(u"\0"):TypeError:('expected string without null bytes',) + vim.eval("\0"):TypeError:('expected string without null bytes',) + <<< Finished + vim.eval("", FailingTrue()):TypeError:('function takes exactly 1 argument (2 given)',) + > VimEvalPy + >>> Testing StringToChars using vim.bindeval(%s) + vim.bindeval(1):TypeError:('expected str() or unicode() instance, but got int',) + vim.bindeval(u"\0"):TypeError:('expected string without null bytes',) + vim.bindeval("\0"):TypeError:('expected string without null bytes',) + <<< Finished + vim.eval("", 2):TypeError:('function takes exactly 1 argument (2 given)',) + > VimStrwidth + >>> Testing StringToChars using vim.strwidth(%s) + vim.strwidth(1):TypeError:('expected str() or unicode() instance, but got int',) + vim.strwidth(u"\0"):TypeError:('expected string without null bytes',) + vim.strwidth("\0"):TypeError:('expected string without null bytes',) + <<< Finished + > VimForeachRTP + vim.foreach_rtp(None):TypeError:("'NoneType' object is not callable",) + vim.foreach_rtp(NoArgsCall()):TypeError:('__call__() takes exactly 1 argument (2 given)',) + vim.foreach_rtp(FailingCall()):NotImplementedError:('call',) + vim.foreach_rtp(int, 2):TypeError:('foreach_rtp() takes exactly one argument (2 given)',) + > import + import xxx_no_such_module_xxx:ImportError:('No module named xxx_no_such_module_xxx',) + import failing_import:ImportError:() + import failing:NotImplementedError:() + > Options + >> OptionsItem + vim.options["abcQ"]:KeyError:('abcQ',) + vim.options[""]:ValueError:('empty keys are not allowed',) + >>> Testing StringToChars using vim.options[%s] + vim.options[1]:TypeError:('expected str() or unicode() instance, but got int',) + vim.options[u"\0"]:TypeError:('expected string without null bytes',) + vim.options["\0"]:TypeError:('expected string without null bytes',) + <<< Finished + >> OptionsContains + >>> Testing StringToChars using %s in vim.options + 1 in vim.options:TypeError:('expected str() or unicode() instance, but got int',) + u"\0" in vim.options:TypeError:('expected string without null bytes',) + "\0" in vim.options:TypeError:('expected string without null bytes',) + <<< Finished + > Dictionary + >> DictionaryConstructor + vim.Dictionary("abcI"):ValueError:('expected sequence element of size 2, but got sequence of size 1',) + >> DictionarySetattr + del d.locked:AttributeError:('cannot delete vim.Dictionary attributes',) + d.locked = FailingTrue():NotImplementedError:('bool',) + vim.vvars.locked = False:TypeError:('cannot modify fixed dictionary',) + d.scope = True:AttributeError:('cannot set attribute scope',) + d.xxx = True:AttributeError:('cannot set attribute xxx',) + >> _DictionaryItem + d.get("a", 2, 3):TypeError:('function takes at most 2 arguments (3 given)',) + >>> Testing StringToChars using d.get(%s) + d.get(1):TypeError:('expected str() or unicode() instance, but got int',) + d.get(u"\0"):TypeError:('expected string without null bytes',) + d.get("\0"):TypeError:('expected string without null bytes',) + <<< Finished + d.pop("a"):KeyError:('a',) + dl.pop("a"):error:('dictionary is locked',) + >> DictionaryContains + "" in d:ValueError:('empty keys are not allowed',) + 0 in d:TypeError:('expected str() or unicode() instance, but got int',) + >> DictionaryIterNext + for i in ned: ned["a"] = 1:RuntimeError:('hashtab changed during iteration',) + >> DictionaryAssItem + dl["b"] = 1:error:('dictionary is locked',) + >>> Testing StringToChars using d[%s] = 1 + d[1] = 1:TypeError:('expected str() or unicode() instance, but got int',) + d[u"\0"] = 1:TypeError:('expected string without null bytes',) + d["\0"] = 1:TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using d["a"] = {%s : 1} + d["a"] = {1 : 1}:TypeError:('expected str() or unicode() instance, but got int',) + d["a"] = {u"\0" : 1}:TypeError:('expected string without null bytes',) + d["a"] = {"\0" : 1}:TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using d["a"] = {"abcF" : {%s : 1}} + d["a"] = {"abcF" : {1 : 1}}:TypeError:('expected str() or unicode() instance, but got int',) + d["a"] = {"abcF" : {u"\0" : 1}}:TypeError:('expected string without null bytes',) + d["a"] = {"abcF" : {"\0" : 1}}:TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using d["a"] = {"abcF" : Mapping({%s : 1})} + d["a"] = {"abcF" : Mapping({1 : 1})}:TypeError:('expected str() or unicode() instance, but got int',) + d["a"] = {"abcF" : Mapping({u"\0" : 1})}:TypeError:('expected string without null bytes',) + d["a"] = {"abcF" : Mapping({"\0" : 1})}:TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing *Iter* using d["a"] = {"abcF" : %s} + d["a"] = {"abcF" : FailingIter()}:TypeError:('unable to convert FailingIter to a Vim structure',) + d["a"] = {"abcF" : FailingIterNext()}:NotImplementedError:('next',) + <<< Finished + >>> Testing ConvertFromPyObject using d["a"] = {"abcF" : %s} + d["a"] = {"abcF" : None}:NOT FAILED + d["a"] = {"abcF" : {"": 1}}:ValueError:('empty keys are not allowed',) + d["a"] = {"abcF" : {u"": 1}}:ValueError:('empty keys are not allowed',) + d["a"] = {"abcF" : FailingMapping()}:NotImplementedError:('keys',) + d["a"] = {"abcF" : FailingMappingKey()}:NotImplementedError:('getitem:mappingkey',) + d["a"] = {"abcF" : FailingNumber()}:TypeError:('long() argument must be a string or a number',) + <<< Finished + >>> Testing StringToChars using d["a"] = Mapping({%s : 1}) + d["a"] = Mapping({1 : 1}):TypeError:('expected str() or unicode() instance, but got int',) + d["a"] = Mapping({u"\0" : 1}):TypeError:('expected string without null bytes',) + d["a"] = Mapping({"\0" : 1}):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using d["a"] = Mapping({"abcG" : {%s : 1}}) + d["a"] = Mapping({"abcG" : {1 : 1}}):TypeError:('expected str() or unicode() instance, but got int',) + d["a"] = Mapping({"abcG" : {u"\0" : 1}}):TypeError:('expected string without null bytes',) + d["a"] = Mapping({"abcG" : {"\0" : 1}}):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using d["a"] = Mapping({"abcG" : Mapping({%s : 1})}) + d["a"] = Mapping({"abcG" : Mapping({1 : 1})}):TypeError:('expected str() or unicode() instance, but got int',) + d["a"] = Mapping({"abcG" : Mapping({u"\0" : 1})}):TypeError:('expected string without null bytes',) + d["a"] = Mapping({"abcG" : Mapping({"\0" : 1})}):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing *Iter* using d["a"] = Mapping({"abcG" : %s}) + d["a"] = Mapping({"abcG" : FailingIter()}):TypeError:('unable to convert FailingIter to a Vim structure',) + d["a"] = Mapping({"abcG" : FailingIterNext()}):NotImplementedError:('next',) + <<< Finished + >>> Testing ConvertFromPyObject using d["a"] = Mapping({"abcG" : %s}) + d["a"] = Mapping({"abcG" : None}):NOT FAILED + d["a"] = Mapping({"abcG" : {"": 1}}):ValueError:('empty keys are not allowed',) + d["a"] = Mapping({"abcG" : {u"": 1}}):ValueError:('empty keys are not allowed',) + d["a"] = Mapping({"abcG" : FailingMapping()}):NotImplementedError:('keys',) + d["a"] = Mapping({"abcG" : FailingMappingKey()}):NotImplementedError:('getitem:mappingkey',) + d["a"] = Mapping({"abcG" : FailingNumber()}):TypeError:('long() argument must be a string or a number',) + <<< Finished + >>> Testing *Iter* using d["a"] = %s + d["a"] = FailingIter():TypeError:('unable to convert FailingIter to a Vim structure',) + d["a"] = FailingIterNext():NotImplementedError:('next',) + <<< Finished + >>> Testing ConvertFromPyObject using d["a"] = %s + d["a"] = None:NOT FAILED + d["a"] = {"": 1}:ValueError:('empty keys are not allowed',) + d["a"] = {u"": 1}:ValueError:('empty keys are not allowed',) + d["a"] = FailingMapping():NotImplementedError:('keys',) + d["a"] = FailingMappingKey():NotImplementedError:('getitem:mappingkey',) + d["a"] = FailingNumber():TypeError:('long() argument must be a string or a number',) + <<< Finished + >> DictionaryUpdate + >>> kwargs + >>> iter + d.update(FailingMapping()):NotImplementedError:('keys',) + d.update([FailingIterNext()]):NotImplementedError:('next',) + d.update([FailingIterNextN(1)]):NotImplementedError:('next N',) + >>> Testing *Iter* using d.update(%s) + d.update(FailingIter()):NotImplementedError:('iter',) + d.update(FailingIterNext()):NotImplementedError:('next',) + <<< Finished + >>> Testing StringToChars using d.update({%s : 1}) + d.update({1 : 1}):TypeError:('expected str() or unicode() instance, but got int',) + d.update({u"\0" : 1}):TypeError:('expected string without null bytes',) + d.update({"\0" : 1}):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using d.update({"abcF" : {%s : 1}}) + d.update({"abcF" : {1 : 1}}):TypeError:('expected str() or unicode() instance, but got int',) + d.update({"abcF" : {u"\0" : 1}}):TypeError:('expected string without null bytes',) + d.update({"abcF" : {"\0" : 1}}):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using d.update({"abcF" : Mapping({%s : 1})}) + d.update({"abcF" : Mapping({1 : 1})}):TypeError:('expected str() or unicode() instance, but got int',) + d.update({"abcF" : Mapping({u"\0" : 1})}):TypeError:('expected string without null bytes',) + d.update({"abcF" : Mapping({"\0" : 1})}):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing *Iter* using d.update({"abcF" : %s}) + d.update({"abcF" : FailingIter()}):TypeError:('unable to convert FailingIter to a Vim structure',) + d.update({"abcF" : FailingIterNext()}):NotImplementedError:('next',) + <<< Finished + >>> Testing ConvertFromPyObject using d.update({"abcF" : %s}) + d.update({"abcF" : None}):NOT FAILED + d.update({"abcF" : {"": 1}}):ValueError:('empty keys are not allowed',) + d.update({"abcF" : {u"": 1}}):ValueError:('empty keys are not allowed',) + d.update({"abcF" : FailingMapping()}):NotImplementedError:('keys',) + d.update({"abcF" : FailingMappingKey()}):NotImplementedError:('getitem:mappingkey',) + d.update({"abcF" : FailingNumber()}):TypeError:('long() argument must be a string or a number',) + <<< Finished + >>> Testing StringToChars using d.update(Mapping({%s : 1})) + d.update(Mapping({1 : 1})):TypeError:('expected str() or unicode() instance, but got int',) + d.update(Mapping({u"\0" : 1})):TypeError:('expected string without null bytes',) + d.update(Mapping({"\0" : 1})):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using d.update(Mapping({"abcG" : {%s : 1}})) + d.update(Mapping({"abcG" : {1 : 1}})):TypeError:('expected str() or unicode() instance, but got int',) + d.update(Mapping({"abcG" : {u"\0" : 1}})):TypeError:('expected string without null bytes',) + d.update(Mapping({"abcG" : {"\0" : 1}})):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using d.update(Mapping({"abcG" : Mapping({%s : 1})})) + d.update(Mapping({"abcG" : Mapping({1 : 1})})):TypeError:('expected str() or unicode() instance, but got int',) + d.update(Mapping({"abcG" : Mapping({u"\0" : 1})})):TypeError:('expected string without null bytes',) + d.update(Mapping({"abcG" : Mapping({"\0" : 1})})):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing *Iter* using d.update(Mapping({"abcG" : %s})) + d.update(Mapping({"abcG" : FailingIter()})):TypeError:('unable to convert FailingIter to a Vim structure',) + d.update(Mapping({"abcG" : FailingIterNext()})):NotImplementedError:('next',) + <<< Finished + >>> Testing ConvertFromPyObject using d.update(Mapping({"abcG" : %s})) + d.update(Mapping({"abcG" : None})):NOT FAILED + d.update(Mapping({"abcG" : {"": 1}})):ValueError:('empty keys are not allowed',) + d.update(Mapping({"abcG" : {u"": 1}})):ValueError:('empty keys are not allowed',) + d.update(Mapping({"abcG" : FailingMapping()})):NotImplementedError:('keys',) + d.update(Mapping({"abcG" : FailingMappingKey()})):NotImplementedError:('getitem:mappingkey',) + d.update(Mapping({"abcG" : FailingNumber()})):TypeError:('long() argument must be a string or a number',) + <<< Finished + >>> Testing *Iter* using d.update(%s) + d.update(FailingIter()):NotImplementedError:('iter',) + d.update(FailingIterNext()):NotImplementedError:('next',) + <<< Finished + >>> Testing ConvertFromPyObject using d.update(%s) + d.update(None):TypeError:("'NoneType' object is not iterable",) + d.update({"": 1}):ValueError:('empty keys are not allowed',) + d.update({u"": 1}):ValueError:('empty keys are not allowed',) + d.update(FailingMapping()):NotImplementedError:('keys',) + d.update(FailingMappingKey()):NotImplementedError:('getitem:mappingkey',) + d.update(FailingNumber()):TypeError:("'FailingNumber' object is not iterable",) + <<< Finished + >>> Testing StringToChars using d.update(((%s, 0),)) + d.update(((1, 0),)):TypeError:('expected str() or unicode() instance, but got int',) + d.update(((u"\0", 0),)):TypeError:('expected string without null bytes',) + d.update((("\0", 0),)):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using d.update((("a", {%s : 1}),)) + d.update((("a", {1 : 1}),)):TypeError:('expected str() or unicode() instance, but got int',) + d.update((("a", {u"\0" : 1}),)):TypeError:('expected string without null bytes',) + d.update((("a", {"\0" : 1}),)):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using d.update((("a", {"abcF" : {%s : 1}}),)) + d.update((("a", {"abcF" : {1 : 1}}),)):TypeError:('expected str() or unicode() instance, but got int',) + d.update((("a", {"abcF" : {u"\0" : 1}}),)):TypeError:('expected string without null bytes',) + d.update((("a", {"abcF" : {"\0" : 1}}),)):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using d.update((("a", {"abcF" : Mapping({%s : 1})}),)) + d.update((("a", {"abcF" : Mapping({1 : 1})}),)):TypeError:('expected str() or unicode() instance, but got int',) + d.update((("a", {"abcF" : Mapping({u"\0" : 1})}),)):TypeError:('expected string without null bytes',) + d.update((("a", {"abcF" : Mapping({"\0" : 1})}),)):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing *Iter* using d.update((("a", {"abcF" : %s}),)) + d.update((("a", {"abcF" : FailingIter()}),)):TypeError:('unable to convert FailingIter to a Vim structure',) + d.update((("a", {"abcF" : FailingIterNext()}),)):NotImplementedError:('next',) + <<< Finished + >>> Testing ConvertFromPyObject using d.update((("a", {"abcF" : %s}),)) + d.update((("a", {"abcF" : None}),)):error:("failed to add key 'a' to dictionary",) + d.update((("a", {"abcF" : {"": 1}}),)):ValueError:('empty keys are not allowed',) + d.update((("a", {"abcF" : {u"": 1}}),)):ValueError:('empty keys are not allowed',) + d.update((("a", {"abcF" : FailingMapping()}),)):NotImplementedError:('keys',) + d.update((("a", {"abcF" : FailingMappingKey()}),)):NotImplementedError:('getitem:mappingkey',) + d.update((("a", {"abcF" : FailingNumber()}),)):TypeError:('long() argument must be a string or a number',) + <<< Finished + >>> Testing StringToChars using d.update((("a", Mapping({%s : 1})),)) + d.update((("a", Mapping({1 : 1})),)):TypeError:('expected str() or unicode() instance, but got int',) + d.update((("a", Mapping({u"\0" : 1})),)):TypeError:('expected string without null bytes',) + d.update((("a", Mapping({"\0" : 1})),)):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using d.update((("a", Mapping({"abcG" : {%s : 1}})),)) + d.update((("a", Mapping({"abcG" : {1 : 1}})),)):TypeError:('expected str() or unicode() instance, but got int',) + d.update((("a", Mapping({"abcG" : {u"\0" : 1}})),)):TypeError:('expected string without null bytes',) + d.update((("a", Mapping({"abcG" : {"\0" : 1}})),)):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using d.update((("a", Mapping({"abcG" : Mapping({%s : 1})})),)) + d.update((("a", Mapping({"abcG" : Mapping({1 : 1})})),)):TypeError:('expected str() or unicode() instance, but got int',) + d.update((("a", Mapping({"abcG" : Mapping({u"\0" : 1})})),)):TypeError:('expected string without null bytes',) + d.update((("a", Mapping({"abcG" : Mapping({"\0" : 1})})),)):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing *Iter* using d.update((("a", Mapping({"abcG" : %s})),)) + d.update((("a", Mapping({"abcG" : FailingIter()})),)):TypeError:('unable to convert FailingIter to a Vim structure',) + d.update((("a", Mapping({"abcG" : FailingIterNext()})),)):NotImplementedError:('next',) + <<< Finished + >>> Testing ConvertFromPyObject using d.update((("a", Mapping({"abcG" : %s})),)) + d.update((("a", Mapping({"abcG" : None})),)):error:("failed to add key 'a' to dictionary",) + d.update((("a", Mapping({"abcG" : {"": 1}})),)):ValueError:('empty keys are not allowed',) + d.update((("a", Mapping({"abcG" : {u"": 1}})),)):ValueError:('empty keys are not allowed',) + d.update((("a", Mapping({"abcG" : FailingMapping()})),)):NotImplementedError:('keys',) + d.update((("a", Mapping({"abcG" : FailingMappingKey()})),)):NotImplementedError:('getitem:mappingkey',) + d.update((("a", Mapping({"abcG" : FailingNumber()})),)):TypeError:('long() argument must be a string or a number',) + <<< Finished + >>> Testing *Iter* using d.update((("a", %s),)) + d.update((("a", FailingIter()),)):TypeError:('unable to convert FailingIter to a Vim structure',) + d.update((("a", FailingIterNext()),)):NotImplementedError:('next',) + <<< Finished + >>> Testing ConvertFromPyObject using d.update((("a", %s),)) + d.update((("a", None),)):error:("failed to add key 'a' to dictionary",) + d.update((("a", {"": 1}),)):ValueError:('empty keys are not allowed',) + d.update((("a", {u"": 1}),)):ValueError:('empty keys are not allowed',) + d.update((("a", FailingMapping()),)):NotImplementedError:('keys',) + d.update((("a", FailingMappingKey()),)):NotImplementedError:('getitem:mappingkey',) + d.update((("a", FailingNumber()),)):TypeError:('long() argument must be a string or a number',) + <<< Finished + >> DictionaryPopItem + d.popitem(1, 2):TypeError:('popitem() takes no arguments (2 given)',) + >> DictionaryHasKey + d.has_key():TypeError:('has_key() takes exactly one argument (0 given)',) + > List + >> ListConstructor + vim.List(1, 2):TypeError:('function takes at most 1 argument (2 given)',) + vim.List(a=1):TypeError:('list constructor does not accept keyword arguments',) + >>> Testing *Iter* using vim.List(%s) + vim.List(FailingIter()):NotImplementedError:('iter',) + vim.List(FailingIterNext()):NotImplementedError:('next',) + <<< Finished + >>> Testing StringToChars using vim.List([{%s : 1}]) + vim.List([{1 : 1}]):TypeError:('expected str() or unicode() instance, but got int',) + vim.List([{u"\0" : 1}]):TypeError:('expected string without null bytes',) + vim.List([{"\0" : 1}]):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using vim.List([{"abcF" : {%s : 1}}]) + vim.List([{"abcF" : {1 : 1}}]):TypeError:('expected str() or unicode() instance, but got int',) + vim.List([{"abcF" : {u"\0" : 1}}]):TypeError:('expected string without null bytes',) + vim.List([{"abcF" : {"\0" : 1}}]):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using vim.List([{"abcF" : Mapping({%s : 1})}]) + vim.List([{"abcF" : Mapping({1 : 1})}]):TypeError:('expected str() or unicode() instance, but got int',) + vim.List([{"abcF" : Mapping({u"\0" : 1})}]):TypeError:('expected string without null bytes',) + vim.List([{"abcF" : Mapping({"\0" : 1})}]):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing *Iter* using vim.List([{"abcF" : %s}]) + vim.List([{"abcF" : FailingIter()}]):TypeError:('unable to convert FailingIter to a Vim structure',) + vim.List([{"abcF" : FailingIterNext()}]):NotImplementedError:('next',) + <<< Finished + >>> Testing ConvertFromPyObject using vim.List([{"abcF" : %s}]) + vim.List([{"abcF" : None}]):NOT FAILED + vim.List([{"abcF" : {"": 1}}]):ValueError:('empty keys are not allowed',) + vim.List([{"abcF" : {u"": 1}}]):ValueError:('empty keys are not allowed',) + vim.List([{"abcF" : FailingMapping()}]):NotImplementedError:('keys',) + vim.List([{"abcF" : FailingMappingKey()}]):NotImplementedError:('getitem:mappingkey',) + vim.List([{"abcF" : FailingNumber()}]):TypeError:('long() argument must be a string or a number',) + <<< Finished + >>> Testing StringToChars using vim.List([Mapping({%s : 1})]) + vim.List([Mapping({1 : 1})]):TypeError:('expected str() or unicode() instance, but got int',) + vim.List([Mapping({u"\0" : 1})]):TypeError:('expected string without null bytes',) + vim.List([Mapping({"\0" : 1})]):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using vim.List([Mapping({"abcG" : {%s : 1}})]) + vim.List([Mapping({"abcG" : {1 : 1}})]):TypeError:('expected str() or unicode() instance, but got int',) + vim.List([Mapping({"abcG" : {u"\0" : 1}})]):TypeError:('expected string without null bytes',) + vim.List([Mapping({"abcG" : {"\0" : 1}})]):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using vim.List([Mapping({"abcG" : Mapping({%s : 1})})]) + vim.List([Mapping({"abcG" : Mapping({1 : 1})})]):TypeError:('expected str() or unicode() instance, but got int',) + vim.List([Mapping({"abcG" : Mapping({u"\0" : 1})})]):TypeError:('expected string without null bytes',) + vim.List([Mapping({"abcG" : Mapping({"\0" : 1})})]):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing *Iter* using vim.List([Mapping({"abcG" : %s})]) + vim.List([Mapping({"abcG" : FailingIter()})]):TypeError:('unable to convert FailingIter to a Vim structure',) + vim.List([Mapping({"abcG" : FailingIterNext()})]):NotImplementedError:('next',) + <<< Finished + >>> Testing ConvertFromPyObject using vim.List([Mapping({"abcG" : %s})]) + vim.List([Mapping({"abcG" : None})]):NOT FAILED + vim.List([Mapping({"abcG" : {"": 1}})]):ValueError:('empty keys are not allowed',) + vim.List([Mapping({"abcG" : {u"": 1}})]):ValueError:('empty keys are not allowed',) + vim.List([Mapping({"abcG" : FailingMapping()})]):NotImplementedError:('keys',) + vim.List([Mapping({"abcG" : FailingMappingKey()})]):NotImplementedError:('getitem:mappingkey',) + vim.List([Mapping({"abcG" : FailingNumber()})]):TypeError:('long() argument must be a string or a number',) + <<< Finished + >>> Testing *Iter* using vim.List([%s]) + vim.List([FailingIter()]):TypeError:('unable to convert FailingIter to a Vim structure',) + vim.List([FailingIterNext()]):NotImplementedError:('next',) + <<< Finished + >>> Testing ConvertFromPyObject using vim.List([%s]) + vim.List([None]):NOT FAILED + vim.List([{"": 1}]):ValueError:('empty keys are not allowed',) + vim.List([{u"": 1}]):ValueError:('empty keys are not allowed',) + vim.List([FailingMapping()]):NotImplementedError:('keys',) + vim.List([FailingMappingKey()]):NotImplementedError:('getitem:mappingkey',) + vim.List([FailingNumber()]):TypeError:('long() argument must be a string or a number',) + <<< Finished + >> ListItem + l[1000]:IndexError:('list index out of range',) + >> ListAssItem + ll[1] = 2:error:('list is locked',) + l[1000] = 3:IndexError:('list index out of range',) + >> ListAssSlice + ll[1:100] = "abcJ":error:('list is locked',) + >>> Testing *Iter* using l[:] = %s + l[:] = FailingIter():NotImplementedError:('iter',) + l[:] = FailingIterNext():NotImplementedError:('next',) + <<< Finished + nel[1:10:2] = "abcK":ValueError:('attempt to assign sequence of size greater than 2 to extended slice',) + ('a', 'b', 'c', 'O') + nel[1:10:2] = "a":ValueError:('attempt to assign sequence of size 1 to extended slice of size 2',) + ('a', 'b', 'c', 'O') + nel[1:1:-1] = "a":ValueError:('attempt to assign sequence of size greater than 0 to extended slice',) + ('a', 'b', 'c', 'O') + nel[:] = FailingIterNextN(2):NotImplementedError:('next N',) + ('a', 'b', 'c', 'O') + >>> Testing StringToChars using l[:] = [{%s : 1}] + l[:] = [{1 : 1}]:TypeError:('expected str() or unicode() instance, but got int',) + l[:] = [{u"\0" : 1}]:TypeError:('expected string without null bytes',) + l[:] = [{"\0" : 1}]:TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using l[:] = [{"abcF" : {%s : 1}}] + l[:] = [{"abcF" : {1 : 1}}]:TypeError:('expected str() or unicode() instance, but got int',) + l[:] = [{"abcF" : {u"\0" : 1}}]:TypeError:('expected string without null bytes',) + l[:] = [{"abcF" : {"\0" : 1}}]:TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using l[:] = [{"abcF" : Mapping({%s : 1})}] + l[:] = [{"abcF" : Mapping({1 : 1})}]:TypeError:('expected str() or unicode() instance, but got int',) + l[:] = [{"abcF" : Mapping({u"\0" : 1})}]:TypeError:('expected string without null bytes',) + l[:] = [{"abcF" : Mapping({"\0" : 1})}]:TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing *Iter* using l[:] = [{"abcF" : %s}] + l[:] = [{"abcF" : FailingIter()}]:TypeError:('unable to convert FailingIter to a Vim structure',) + l[:] = [{"abcF" : FailingIterNext()}]:NotImplementedError:('next',) + <<< Finished + >>> Testing ConvertFromPyObject using l[:] = [{"abcF" : %s}] + l[:] = [{"abcF" : None}]:NOT FAILED + l[:] = [{"abcF" : {"": 1}}]:ValueError:('empty keys are not allowed',) + l[:] = [{"abcF" : {u"": 1}}]:ValueError:('empty keys are not allowed',) + l[:] = [{"abcF" : FailingMapping()}]:NotImplementedError:('keys',) + l[:] = [{"abcF" : FailingMappingKey()}]:NotImplementedError:('getitem:mappingkey',) + l[:] = [{"abcF" : FailingNumber()}]:TypeError:('long() argument must be a string or a number',) + <<< Finished + >>> Testing StringToChars using l[:] = [Mapping({%s : 1})] + l[:] = [Mapping({1 : 1})]:TypeError:('expected str() or unicode() instance, but got int',) + l[:] = [Mapping({u"\0" : 1})]:TypeError:('expected string without null bytes',) + l[:] = [Mapping({"\0" : 1})]:TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using l[:] = [Mapping({"abcG" : {%s : 1}})] + l[:] = [Mapping({"abcG" : {1 : 1}})]:TypeError:('expected str() or unicode() instance, but got int',) + l[:] = [Mapping({"abcG" : {u"\0" : 1}})]:TypeError:('expected string without null bytes',) + l[:] = [Mapping({"abcG" : {"\0" : 1}})]:TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using l[:] = [Mapping({"abcG" : Mapping({%s : 1})})] + l[:] = [Mapping({"abcG" : Mapping({1 : 1})})]:TypeError:('expected str() or unicode() instance, but got int',) + l[:] = [Mapping({"abcG" : Mapping({u"\0" : 1})})]:TypeError:('expected string without null bytes',) + l[:] = [Mapping({"abcG" : Mapping({"\0" : 1})})]:TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing *Iter* using l[:] = [Mapping({"abcG" : %s})] + l[:] = [Mapping({"abcG" : FailingIter()})]:TypeError:('unable to convert FailingIter to a Vim structure',) + l[:] = [Mapping({"abcG" : FailingIterNext()})]:NotImplementedError:('next',) + <<< Finished + >>> Testing ConvertFromPyObject using l[:] = [Mapping({"abcG" : %s})] + l[:] = [Mapping({"abcG" : None})]:NOT FAILED + l[:] = [Mapping({"abcG" : {"": 1}})]:ValueError:('empty keys are not allowed',) + l[:] = [Mapping({"abcG" : {u"": 1}})]:ValueError:('empty keys are not allowed',) + l[:] = [Mapping({"abcG" : FailingMapping()})]:NotImplementedError:('keys',) + l[:] = [Mapping({"abcG" : FailingMappingKey()})]:NotImplementedError:('getitem:mappingkey',) + l[:] = [Mapping({"abcG" : FailingNumber()})]:TypeError:('long() argument must be a string or a number',) + <<< Finished + >>> Testing *Iter* using l[:] = [%s] + l[:] = [FailingIter()]:TypeError:('unable to convert FailingIter to a Vim structure',) + l[:] = [FailingIterNext()]:NotImplementedError:('next',) + <<< Finished + >>> Testing ConvertFromPyObject using l[:] = [%s] + l[:] = [None]:NOT FAILED + l[:] = [{"": 1}]:ValueError:('empty keys are not allowed',) + l[:] = [{u"": 1}]:ValueError:('empty keys are not allowed',) + l[:] = [FailingMapping()]:NotImplementedError:('keys',) + l[:] = [FailingMappingKey()]:NotImplementedError:('getitem:mappingkey',) + l[:] = [FailingNumber()]:TypeError:('long() argument must be a string or a number',) + <<< Finished + >> ListConcatInPlace + >>> Testing *Iter* using l.extend(%s) + l.extend(FailingIter()):NotImplementedError:('iter',) + l.extend(FailingIterNext()):NotImplementedError:('next',) + <<< Finished + >>> Testing StringToChars using l.extend([{%s : 1}]) + l.extend([{1 : 1}]):TypeError:('expected str() or unicode() instance, but got int',) + l.extend([{u"\0" : 1}]):TypeError:('expected string without null bytes',) + l.extend([{"\0" : 1}]):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using l.extend([{"abcF" : {%s : 1}}]) + l.extend([{"abcF" : {1 : 1}}]):TypeError:('expected str() or unicode() instance, but got int',) + l.extend([{"abcF" : {u"\0" : 1}}]):TypeError:('expected string without null bytes',) + l.extend([{"abcF" : {"\0" : 1}}]):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using l.extend([{"abcF" : Mapping({%s : 1})}]) + l.extend([{"abcF" : Mapping({1 : 1})}]):TypeError:('expected str() or unicode() instance, but got int',) + l.extend([{"abcF" : Mapping({u"\0" : 1})}]):TypeError:('expected string without null bytes',) + l.extend([{"abcF" : Mapping({"\0" : 1})}]):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing *Iter* using l.extend([{"abcF" : %s}]) + l.extend([{"abcF" : FailingIter()}]):TypeError:('unable to convert FailingIter to a Vim structure',) + l.extend([{"abcF" : FailingIterNext()}]):NotImplementedError:('next',) + <<< Finished + >>> Testing ConvertFromPyObject using l.extend([{"abcF" : %s}]) + l.extend([{"abcF" : None}]):NOT FAILED + l.extend([{"abcF" : {"": 1}}]):ValueError:('empty keys are not allowed',) + l.extend([{"abcF" : {u"": 1}}]):ValueError:('empty keys are not allowed',) + l.extend([{"abcF" : FailingMapping()}]):NotImplementedError:('keys',) + l.extend([{"abcF" : FailingMappingKey()}]):NotImplementedError:('getitem:mappingkey',) + l.extend([{"abcF" : FailingNumber()}]):TypeError:('long() argument must be a string or a number',) + <<< Finished + >>> Testing StringToChars using l.extend([Mapping({%s : 1})]) + l.extend([Mapping({1 : 1})]):TypeError:('expected str() or unicode() instance, but got int',) + l.extend([Mapping({u"\0" : 1})]):TypeError:('expected string without null bytes',) + l.extend([Mapping({"\0" : 1})]):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using l.extend([Mapping({"abcG" : {%s : 1}})]) + l.extend([Mapping({"abcG" : {1 : 1}})]):TypeError:('expected str() or unicode() instance, but got int',) + l.extend([Mapping({"abcG" : {u"\0" : 1}})]):TypeError:('expected string without null bytes',) + l.extend([Mapping({"abcG" : {"\0" : 1}})]):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using l.extend([Mapping({"abcG" : Mapping({%s : 1})})]) + l.extend([Mapping({"abcG" : Mapping({1 : 1})})]):TypeError:('expected str() or unicode() instance, but got int',) + l.extend([Mapping({"abcG" : Mapping({u"\0" : 1})})]):TypeError:('expected string without null bytes',) + l.extend([Mapping({"abcG" : Mapping({"\0" : 1})})]):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing *Iter* using l.extend([Mapping({"abcG" : %s})]) + l.extend([Mapping({"abcG" : FailingIter()})]):TypeError:('unable to convert FailingIter to a Vim structure',) + l.extend([Mapping({"abcG" : FailingIterNext()})]):NotImplementedError:('next',) + <<< Finished + >>> Testing ConvertFromPyObject using l.extend([Mapping({"abcG" : %s})]) + l.extend([Mapping({"abcG" : None})]):NOT FAILED + l.extend([Mapping({"abcG" : {"": 1}})]):ValueError:('empty keys are not allowed',) + l.extend([Mapping({"abcG" : {u"": 1}})]):ValueError:('empty keys are not allowed',) + l.extend([Mapping({"abcG" : FailingMapping()})]):NotImplementedError:('keys',) + l.extend([Mapping({"abcG" : FailingMappingKey()})]):NotImplementedError:('getitem:mappingkey',) + l.extend([Mapping({"abcG" : FailingNumber()})]):TypeError:('long() argument must be a string or a number',) + <<< Finished + >>> Testing *Iter* using l.extend([%s]) + l.extend([FailingIter()]):TypeError:('unable to convert FailingIter to a Vim structure',) + l.extend([FailingIterNext()]):NotImplementedError:('next',) + <<< Finished + >>> Testing ConvertFromPyObject using l.extend([%s]) + l.extend([None]):NOT FAILED + l.extend([{"": 1}]):ValueError:('empty keys are not allowed',) + l.extend([{u"": 1}]):ValueError:('empty keys are not allowed',) + l.extend([FailingMapping()]):NotImplementedError:('keys',) + l.extend([FailingMappingKey()]):NotImplementedError:('getitem:mappingkey',) + l.extend([FailingNumber()]):TypeError:('long() argument must be a string or a number',) + <<< Finished + >> ListSetattr + del l.locked:AttributeError:('cannot delete vim.List attributes',) + l.locked = FailingTrue():NotImplementedError:('bool',) + l.xxx = True:AttributeError:('cannot set attribute xxx',) + > Function + >> FunctionConstructor + >>> FunctionConstructor + vim.Function("123"):ValueError:('unnamed function 123 does not exist',) + vim.Function("xxx_non_existent_function_xxx"):ValueError:('function xxx_non_existent_function_xxx does not exist',) + vim.Function("xxx#non#existent#function#xxx"):NOT FAILED + vim.Function("xxx_non_existent_function_xxx2", args=[]):ValueError:('function xxx_non_existent_function_xxx2 does not exist',) + vim.Function("xxx_non_existent_function_xxx3", self={}):ValueError:('function xxx_non_existent_function_xxx3 does not exist',) + vim.Function("xxx_non_existent_function_xxx4", args=[], self={}):ValueError:('function xxx_non_existent_function_xxx4 does not exist',) + >>> FunctionNew + vim.Function("tr", self="abcFuncSelf"):TypeError:('unable to convert str to a Vim dictionary',) + vim.Function("tr", args=427423):TypeError:('unable to convert int to a Vim list',) + vim.Function("tr", self="abcFuncSelf2", args="abcFuncArgs2"):TypeError:('unable to convert str to a Vim dictionary',) + vim.Function(self="abcFuncSelf2", args="abcFuncArgs2"):TypeError:('unable to convert str to a Vim dictionary',) + vim.Function("tr", "", self="abcFuncSelf2", args="abcFuncArgs2"):TypeError:('unable to convert str to a Vim dictionary',) + vim.Function("tr", ""):TypeError:('function takes exactly 1 argument (2 given)',) + >> FunctionCall + >>> Testing StringToChars using f({%s : 1}) + f({1 : 1}):TypeError:('expected str() or unicode() instance, but got int',) + f({u"\0" : 1}):TypeError:('expected string without null bytes',) + f({"\0" : 1}):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using f({"abcF" : {%s : 1}}) + f({"abcF" : {1 : 1}}):TypeError:('expected str() or unicode() instance, but got int',) + f({"abcF" : {u"\0" : 1}}):TypeError:('expected string without null bytes',) + f({"abcF" : {"\0" : 1}}):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using f({"abcF" : Mapping({%s : 1})}) + f({"abcF" : Mapping({1 : 1})}):TypeError:('expected str() or unicode() instance, but got int',) + f({"abcF" : Mapping({u"\0" : 1})}):TypeError:('expected string without null bytes',) + f({"abcF" : Mapping({"\0" : 1})}):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing *Iter* using f({"abcF" : %s}) + f({"abcF" : FailingIter()}):TypeError:('unable to convert FailingIter to a Vim structure',) + f({"abcF" : FailingIterNext()}):NotImplementedError:('next',) + <<< Finished + >>> Testing ConvertFromPyObject using f({"abcF" : %s}) + f({"abcF" : None}):NOT FAILED + f({"abcF" : {"": 1}}):ValueError:('empty keys are not allowed',) + f({"abcF" : {u"": 1}}):ValueError:('empty keys are not allowed',) + f({"abcF" : FailingMapping()}):NotImplementedError:('keys',) + f({"abcF" : FailingMappingKey()}):NotImplementedError:('getitem:mappingkey',) + f({"abcF" : FailingNumber()}):TypeError:('long() argument must be a string or a number',) + <<< Finished + >>> Testing StringToChars using f(Mapping({%s : 1})) + f(Mapping({1 : 1})):TypeError:('expected str() or unicode() instance, but got int',) + f(Mapping({u"\0" : 1})):TypeError:('expected string without null bytes',) + f(Mapping({"\0" : 1})):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using f(Mapping({"abcG" : {%s : 1}})) + f(Mapping({"abcG" : {1 : 1}})):TypeError:('expected str() or unicode() instance, but got int',) + f(Mapping({"abcG" : {u"\0" : 1}})):TypeError:('expected string without null bytes',) + f(Mapping({"abcG" : {"\0" : 1}})):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using f(Mapping({"abcG" : Mapping({%s : 1})})) + f(Mapping({"abcG" : Mapping({1 : 1})})):TypeError:('expected str() or unicode() instance, but got int',) + f(Mapping({"abcG" : Mapping({u"\0" : 1})})):TypeError:('expected string without null bytes',) + f(Mapping({"abcG" : Mapping({"\0" : 1})})):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing *Iter* using f(Mapping({"abcG" : %s})) + f(Mapping({"abcG" : FailingIter()})):TypeError:('unable to convert FailingIter to a Vim structure',) + f(Mapping({"abcG" : FailingIterNext()})):NotImplementedError:('next',) + <<< Finished + >>> Testing ConvertFromPyObject using f(Mapping({"abcG" : %s})) + f(Mapping({"abcG" : None})):NOT FAILED + f(Mapping({"abcG" : {"": 1}})):ValueError:('empty keys are not allowed',) + f(Mapping({"abcG" : {u"": 1}})):ValueError:('empty keys are not allowed',) + f(Mapping({"abcG" : FailingMapping()})):NotImplementedError:('keys',) + f(Mapping({"abcG" : FailingMappingKey()})):NotImplementedError:('getitem:mappingkey',) + f(Mapping({"abcG" : FailingNumber()})):TypeError:('long() argument must be a string or a number',) + <<< Finished + >>> Testing *Iter* using f(%s) + f(FailingIter()):TypeError:('unable to convert FailingIter to a Vim structure',) + f(FailingIterNext()):NotImplementedError:('next',) + <<< Finished + >>> Testing ConvertFromPyObject using f(%s) + f(None):NOT FAILED + f({"": 1}):ValueError:('empty keys are not allowed',) + f({u"": 1}):ValueError:('empty keys are not allowed',) + f(FailingMapping()):NotImplementedError:('keys',) + f(FailingMappingKey()):NotImplementedError:('getitem:mappingkey',) + f(FailingNumber()):TypeError:('long() argument must be a string or a number',) + <<< Finished + >>> Testing StringToChars using fd(self={%s : 1}) + fd(self={1 : 1}):TypeError:('expected str() or unicode() instance, but got int',) + fd(self={u"\0" : 1}):TypeError:('expected string without null bytes',) + fd(self={"\0" : 1}):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using fd(self={"abcF" : {%s : 1}}) + fd(self={"abcF" : {1 : 1}}):TypeError:('expected str() or unicode() instance, but got int',) + fd(self={"abcF" : {u"\0" : 1}}):TypeError:('expected string without null bytes',) + fd(self={"abcF" : {"\0" : 1}}):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using fd(self={"abcF" : Mapping({%s : 1})}) + fd(self={"abcF" : Mapping({1 : 1})}):TypeError:('expected str() or unicode() instance, but got int',) + fd(self={"abcF" : Mapping({u"\0" : 1})}):TypeError:('expected string without null bytes',) + fd(self={"abcF" : Mapping({"\0" : 1})}):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing *Iter* using fd(self={"abcF" : %s}) + fd(self={"abcF" : FailingIter()}):TypeError:('unable to convert FailingIter to a Vim structure',) + fd(self={"abcF" : FailingIterNext()}):NotImplementedError:('next',) + <<< Finished + >>> Testing ConvertFromPyObject using fd(self={"abcF" : %s}) + fd(self={"abcF" : None}):NOT FAILED + fd(self={"abcF" : {"": 1}}):ValueError:('empty keys are not allowed',) + fd(self={"abcF" : {u"": 1}}):ValueError:('empty keys are not allowed',) + fd(self={"abcF" : FailingMapping()}):NotImplementedError:('keys',) + fd(self={"abcF" : FailingMappingKey()}):NotImplementedError:('getitem:mappingkey',) + fd(self={"abcF" : FailingNumber()}):TypeError:('long() argument must be a string or a number',) + <<< Finished + >>> Testing StringToChars using fd(self=Mapping({%s : 1})) + fd(self=Mapping({1 : 1})):TypeError:('expected str() or unicode() instance, but got int',) + fd(self=Mapping({u"\0" : 1})):TypeError:('expected string without null bytes',) + fd(self=Mapping({"\0" : 1})):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using fd(self=Mapping({"abcG" : {%s : 1}})) + fd(self=Mapping({"abcG" : {1 : 1}})):TypeError:('expected str() or unicode() instance, but got int',) + fd(self=Mapping({"abcG" : {u"\0" : 1}})):TypeError:('expected string without null bytes',) + fd(self=Mapping({"abcG" : {"\0" : 1}})):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing StringToChars using fd(self=Mapping({"abcG" : Mapping({%s : 1})})) + fd(self=Mapping({"abcG" : Mapping({1 : 1})})):TypeError:('expected str() or unicode() instance, but got int',) + fd(self=Mapping({"abcG" : Mapping({u"\0" : 1})})):TypeError:('expected string without null bytes',) + fd(self=Mapping({"abcG" : Mapping({"\0" : 1})})):TypeError:('expected string without null bytes',) + <<< Finished + >>> Testing *Iter* using fd(self=Mapping({"abcG" : %s})) + fd(self=Mapping({"abcG" : FailingIter()})):TypeError:('unable to convert FailingIter to a Vim structure',) + fd(self=Mapping({"abcG" : FailingIterNext()})):NotImplementedError:('next',) + <<< Finished + >>> Testing ConvertFromPyObject using fd(self=Mapping({"abcG" : %s})) + fd(self=Mapping({"abcG" : None})):NOT FAILED + fd(self=Mapping({"abcG" : {"": 1}})):ValueError:('empty keys are not allowed',) + fd(self=Mapping({"abcG" : {u"": 1}})):ValueError:('empty keys are not allowed',) + fd(self=Mapping({"abcG" : FailingMapping()})):NotImplementedError:('keys',) + fd(self=Mapping({"abcG" : FailingMappingKey()})):NotImplementedError:('getitem:mappingkey',) + fd(self=Mapping({"abcG" : FailingNumber()})):TypeError:('long() argument must be a string or a number',) + <<< Finished + >>> Testing *Iter* using fd(self=%s) + fd(self=FailingIter()):TypeError:('unable to convert FailingIter to a Vim dictionary',) + fd(self=FailingIterNext()):TypeError:('unable to convert FailingIterNext to a Vim dictionary',) + <<< Finished + >>> Testing ConvertFromPyObject using fd(self=%s) + fd(self=None):TypeError:('unable to convert NoneType to a Vim dictionary',) + fd(self={"": 1}):ValueError:('empty keys are not allowed',) + fd(self={u"": 1}):ValueError:('empty keys are not allowed',) + fd(self=FailingMapping()):NotImplementedError:('keys',) + fd(self=FailingMappingKey()):NotImplementedError:('getitem:mappingkey',) + fd(self=FailingNumber()):TypeError:('unable to convert FailingNumber to a Vim dictionary',) + <<< Finished + >>> Testing ConvertFromPyMapping using fd(self=%s) + fd(self=[]):TypeError:('unable to convert list to a Vim dictionary',) + <<< Finished + > TabPage + >> TabPageAttr + vim.current.tabpage.xxx:AttributeError:('xxx',) + > TabList + >> TabListItem + vim.tabpages[1000]:IndexError:('no such tab page',) + > Window + >> WindowAttr + vim.current.window.xxx:AttributeError:('xxx',) + >> WindowSetattr + vim.current.window.buffer = 0:TypeError:('readonly attribute: buffer',) + vim.current.window.cursor = (100000000, 100000000):error:('cursor position outside buffer',) + vim.current.window.cursor = True:TypeError:('argument must be 2-item sequence, not bool',) + >>> Testing NumberToLong using vim.current.window.height = %s + vim.current.window.height = []:TypeError:('expected int(), long() or something supporting coercing to long(), but got list',) + vim.current.window.height = None:TypeError:('expected int(), long() or something supporting coercing to long(), but got NoneType',) + vim.current.window.height = -1:ValueError:('number must be greater or equal to zero',) + <<< Finished + >>> Testing NumberToLong using vim.current.window.width = %s + vim.current.window.width = []:TypeError:('expected int(), long() or something supporting coercing to long(), but got list',) + vim.current.window.width = None:TypeError:('expected int(), long() or something supporting coercing to long(), but got NoneType',) + vim.current.window.width = -1:ValueError:('number must be greater or equal to zero',) + <<< Finished + vim.current.window.xxxxxx = True:AttributeError:('xxxxxx',) + > WinList + >> WinListItem + vim.windows[1000]:IndexError:('no such window',) + > Buffer + >> StringToLine (indirect) + vim.current.buffer[0] = u"\na":error:('string cannot contain newlines',) + vim.current.buffer[0] = "\na":error:('string cannot contain newlines',) + >> SetBufferLine (indirect) + vim.current.buffer[0] = True:TypeError:('bad argument type for built-in operation',) + >> SetBufferLineList (indirect) + vim.current.buffer[:] = True:TypeError:('bad argument type for built-in operation',) + vim.current.buffer[:] = ["\na", "bc"]:error:('string cannot contain newlines',) + >> InsertBufferLines (indirect) + vim.current.buffer.append(None):TypeError:('bad argument type for built-in operation',) + vim.current.buffer.append(["\na", "bc"]):error:('string cannot contain newlines',) + vim.current.buffer.append("\nbc"):error:('string cannot contain newlines',) + >> RBItem + vim.current.buffer[100000000]:IndexError:('line number out of range',) + >> RBAsItem + vim.current.buffer[100000000] = "":IndexError:('line number out of range',) + >> BufferAttr + vim.current.buffer.xxx:AttributeError:('xxx',) + >> BufferSetattr + vim.current.buffer.name = True:TypeError:('expected str() or unicode() instance, but got bool',) + vim.current.buffer.xxx = True:AttributeError:('xxx',) + >> BufferMark + vim.current.buffer.mark(0):TypeError:('expected str() or unicode() instance, but got int',) + vim.current.buffer.mark("abcM"):ValueError:('mark name must be a single character',) + vim.current.buffer.mark("!"):error:('invalid mark name',) + >> BufferRange + vim.current.buffer.range(1, 2, 3):TypeError:('function takes exactly 2 arguments (3 given)',) + > BufMap + >> BufMapItem + vim.buffers[100000000]:KeyError:(100000000,) + >>> Testing NumberToLong using vim.buffers[%s] + vim.buffers[[]]:TypeError:('expected int(), long() or something supporting coercing to long(), but got list',) + vim.buffers[None]:TypeError:('expected int(), long() or something supporting coercing to long(), but got NoneType',) + vim.buffers[-1]:ValueError:('number must be greater than zero',) + vim.buffers[0]:ValueError:('number must be greater than zero',) + <<< Finished + > Current + >> CurrentGetattr + vim.current.xxx:AttributeError:('xxx',) + >> CurrentSetattr + vim.current.line = True:TypeError:('bad argument type for built-in operation',) + vim.current.buffer = True:TypeError:('expected vim.Buffer object, but got bool',) + vim.current.window = True:TypeError:('expected vim.Window object, but got bool',) + vim.current.tabpage = True:TypeError:('expected vim.TabPage object, but got bool',) + vim.current.xxx = True:AttributeError:('xxx',) + END + + call assert_equal(expected, getline(2, '$')) + close! +endfunc + +" Test import +func Test_python_import() + new + py cb = vim.current.buffer + + py << trim EOF + sys.path.insert(0, os.path.join(os.getcwd(), 'python_before')) + sys.path.append(os.path.join(os.getcwd(), 'python_after')) + vim.options['rtp'] = os.getcwd().replace(',', '\\,').replace('\\', '\\\\') + l = [] + def callback(path): + l.append(path[-len('/testdir'):].replace(os.path.sep, '/')) + vim.foreach_rtp(callback) + cb.append(repr(l)) + del l + def callback(path): + return path[-len('/testdir'):].replace(os.path.sep, '/') + cb.append(repr(vim.foreach_rtp(callback))) + del callback + from module import dir as d + from modulex import ddir + cb.append(d + ',' + ddir) + import before + cb.append(before.dir) + import after + cb.append(after.dir) + import topmodule as tm + import topmodule.submodule as tms + import topmodule.submodule.subsubmodule.subsubsubmodule as tmsss + cb.append(tm.__file__.replace('.pyc', '.py').replace(os.path.sep, '/')[-len('modulex/topmodule/__init__.py'):]) + cb.append(tms.__file__.replace('.pyc', '.py').replace(os.path.sep, '/')[-len('modulex/topmodule/submodule/__init__.py'):]) + cb.append(tmsss.__file__.replace('.pyc', '.py').replace(os.path.sep, '/')[-len('modulex/topmodule/submodule/subsubmodule/subsubsubmodule.py'):]) + del before + del after + del d + del ddir + del tm + del tms + del tmsss + EOF + + let expected =<< trim END + ['/testdir'] + '/testdir' + 2,xx + before + after + pythonx/topmodule/__init__.py + pythonx/topmodule/submodule/__init__.py + pythonx/topmodule/submodule/subsubmodule/subsubsubmodule.py + END + call assert_equal(expected, getline(2, '$')) + close! +endfunc + +" Test exceptions +func Test_python_exception() + fun Exe(e) + execute a:e + endfun + + new + py cb = vim.current.buffer + + py << trim EOF + Exe = vim.bindeval('function("Exe")') + ee('vim.command("throw \'abcN\'")') + ee('Exe("throw \'def\'")') + ee('vim.eval("Exe(\'throw \'\'ghi\'\'\')")') + ee('vim.eval("Exe(\'echoerr \'\'jkl\'\'\')")') + ee('vim.eval("Exe(\'xxx_non_existent_command_xxx\')")') + ee('vim.eval("xxx_unknown_function_xxx()")') + ee('vim.bindeval("Exe(\'xxx_non_existent_command_xxx\')")') + del Exe + EOF + delfunction Exe + + let expected =<< trim END + vim.command("throw 'abcN'"):error:('abcN',) + Exe("throw 'def'"):error:('def',) + vim.eval("Exe('throw ''ghi''')"):error:('ghi',) + vim.eval("Exe('echoerr ''jkl''')"):error:('Vim(echoerr):jkl',) + vim.eval("Exe('xxx_non_existent_command_xxx')"):error:('Vim:E492: Not an editor command: xxx_non_existent_command_xxx',) + vim.eval("xxx_unknown_function_xxx()"):error:('Vim:E117: Unknown function: xxx_unknown_function_xxx',) + vim.bindeval("Exe('xxx_non_existent_command_xxx')"):error:('Vim:E492: Not an editor command: xxx_non_existent_command_xxx',) + END + call assert_equal(expected, getline(2, '$')) + close! +endfunc + +" Regression: interrupting vim.command propagates to next vim.command +func Test_python_keyboard_interrupt() + new + py cb = vim.current.buffer + py << trim EOF + def test_keyboard_interrupt(): + try: + vim.command('while 1 | endwhile') + except KeyboardInterrupt: + cb.append('Caught KeyboardInterrupt') + except Exception: + cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info())) + else: + cb.append('!!!!!!!! No exception') + try: + vim.command('$ put =\'Running :put\'') + except KeyboardInterrupt: + cb.append('!!!!!!!! Caught KeyboardInterrupt') + except Exception: + cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info())) + else: + cb.append('No exception') + EOF + + debuggreedy + call inputsave() + call feedkeys("s\ns\ns\ns\nq\n") + redir => output + debug silent! py test_keyboard_interrupt() + redir END + 0 debuggreedy + call inputrestore() + py del test_keyboard_interrupt + + let expected =<< trim END + Caught KeyboardInterrupt + Running :put + No exception + END + call assert_equal(expected, getline(2, '$')) + call assert_equal('', output) + close! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index ae2a95f465..c13c6e64d1 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1120, /**/ 1119, /**/ From 743d0620203388bf87dc611cea544b485e4b9f85 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 3 Jul 2020 18:15:06 +0200 Subject: [PATCH 077/105] patch 8.2.1121: command completion not working after ++arg Problem: Command completion not working after ++arg. Solution: Move skipping up. (Christian Brabandt, closes #6382) --- src/cmdexpand.c | 17 +++++++++-------- src/testdir/test_cmdline.vim | 5 ++++- src/version.c | 2 ++ 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/cmdexpand.c b/src/cmdexpand.c index 63f0cc7b15..d92366c61e 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -1099,6 +1099,15 @@ set_one_cmd_context( arg = skipwhite(p); + // Skip over ++argopt argument + if ((ea.argt & EX_ARGOPT) && *arg != NUL && STRNCMP(arg, "++", 2) == 0) + { + p = arg; + while (*p && !vim_isspace(*p)) + MB_PTR_ADV(p); + arg = skipwhite(p); + } + if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) { if (*arg == '>') // append @@ -1146,14 +1155,6 @@ set_one_cmd_context( arg = skipwhite(arg); } - // Skip over ++argopt argument - if ((ea.argt & EX_ARGOPT) && *arg != NUL && STRNCMP(arg, "++", 2) == 0) - { - p = arg; - while (*p && !vim_isspace(*p)) - MB_PTR_ADV(p); - arg = skipwhite(p); - } // Check for '|' to separate commands and '"' to start comments. // Don't do this for ":read !cmd" and ":write !cmd". diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index 2a3112a734..eb213f1976 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -1593,8 +1593,11 @@ func Test_read_shellcmd() call feedkeys(":r! ++enc=utf-8 r\\\"\", 'tx') call assert_notmatch('^"r!.*\', @:) call assert_match('^"r!.*\', @:) + + call feedkeys(":r ++enc=utf-8 !rm\\\"\", 'tx') + call assert_notmatch('^"r.*\', @:) + call assert_match('^"r ++enc\S\+ !.*\', @:) endif endfunc - " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index c13c6e64d1..de98479adb 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1121, /**/ 1120, /**/ From 442af2f89e29fb60790bffde2def9cd74a081780 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 3 Jul 2020 21:09:52 +0200 Subject: [PATCH 078/105] patch 8.2.1122: Vim9: line continuation in dict member not recognized Problem: Vim9: line continuation in dict member not recognized. Solution: Check for line continuation. --- src/eval.c | 23 ++++++++++++++++++--- src/testdir/test_vim9_expr.vim | 37 ++++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 59 insertions(+), 3 deletions(-) diff --git a/src/eval.c b/src/eval.c index 9a2fdf6540..c569710ab5 100644 --- a/src/eval.c +++ b/src/eval.c @@ -3362,7 +3362,7 @@ eval_index( * * Get the (first) variable from inside the []. */ - *arg = skipwhite(*arg + 1); + *arg = skipwhite_and_linebreak(*arg + 1, evalarg); if (**arg == ':') empty1 = TRUE; else if (eval1(arg, &var1, evalarg) == FAIL) // recursive! @@ -3377,10 +3377,11 @@ eval_index( /* * Get the second variable from inside the [:]. */ + *arg = skipwhite_and_linebreak(*arg, evalarg); if (**arg == ':') { range = TRUE; - *arg = skipwhite(*arg + 1); + *arg = skipwhite_and_linebreak(*arg + 1, evalarg); if (**arg == ']') empty2 = TRUE; else if (eval1(arg, &var2, evalarg) == FAIL) // recursive! @@ -3400,6 +3401,7 @@ eval_index( } // Check for the ']'. + *arg = skipwhite_and_linebreak(*arg, evalarg); if (**arg != ']') { if (verbose) @@ -5043,6 +5045,21 @@ handle_subscript( && (evalarg->eval_flags & EVAL_EVALUATE); int ret = OK; dict_T *selfdict = NULL; + int check_white = TRUE; + + // When at the end of the line and ".name" follows in the next line then + // consume the line break. Only when rettv is a dict. + if (rettv->v_type == VAR_DICT) + { + int getnext; + char_u *p = eval_next_non_blank(*arg, evalarg, &getnext); + + if (getnext && *p == '.' && ASCII_ISALPHA(p[1])) + { + *arg = eval_next_line(evalarg); + check_white = FALSE; + } + } // "." is ".name" lookup when we found a dict or when evaluating and // scriptversion is at least 2, where string concatenation is "..". @@ -5054,7 +5071,7 @@ handle_subscript( && current_sctx.sc_version >= 2))) || (**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC || rettv->v_type == VAR_PARTIAL))) - && !VIM_ISWHITE(*(*arg - 1))) + && (!check_white || !VIM_ISWHITE(*(*arg - 1)))) || (**arg == '-' && (*arg)[1] == '>'))) { if (**arg == '(') diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index ff662b9578..a604de2b0b 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -1138,6 +1138,43 @@ def Test_expr_member() call CheckDefExecFailure(["let d: dict", "d = g:list_empty"], 'E1029: Expected dict but got list') enddef +def Test_expr_member_vim9script() + let lines =<< trim END + vim9script + let d = #{one: + 'one', + two: 'two'} + assert_equal('one', d.one) + assert_equal('one', d + .one) + assert_equal('one', d[ + 'one' + ]) + END + CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + let l = [1, + 2, + 3, 4 + ] + assert_equal(2, l[ + 1 + ]) + assert_equal([2, 3], l[1 : 2]) + assert_equal([1, 2, 3], l[ + : + 2 + ]) + assert_equal([3, 4], l[ + 2 + : + ]) + END + CheckScriptSuccess(lines) +enddef + def Test_expr7_option() " option set ts=11 diff --git a/src/version.c b/src/version.c index de98479adb..00aca2b1e4 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1122, /**/ 1121, /**/ From effb0cd75de647a2a0bf95cb26eaa2feee8068d9 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 3 Jul 2020 21:17:34 +0200 Subject: [PATCH 079/105] patch 8.2.1123: Python 3 test is old style Problem: Python 3 test is old style. Solution: Turn into new style test. (Yegappan Lakshmanan, closes #6385) --- src/Makefile | 1 - src/testdir/Make_all.mak | 7 +- src/testdir/Make_vms.mms | 8 +- src/testdir/test87.in | 1725 ------------------ src/testdir/test87.ok | 1445 --------------- src/testdir/test_python2.vim | 90 +- src/testdir/test_python3.vim | 3311 +++++++++++++++++++++++++++++++++- src/version.c | 2 + 8 files changed, 3358 insertions(+), 3231 deletions(-) delete mode 100644 src/testdir/test87.in delete mode 100644 src/testdir/test87.ok diff --git a/src/Makefile b/src/Makefile index 9166662970..2b10024401 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2308,7 +2308,6 @@ test1 \ test42 test44 test49 \ test52 test59 \ test70 \ - test87 \ test99: cd testdir; rm -f $@.out; $(MAKE) -f Makefile $@.out VIMPROG=../$(VIMTESTTARGET) $(GUI_TESTARG) SCRIPTSOURCE=../$(SCRIPTSOURCE) diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak index 14062d8396..12aeffe68a 100644 --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -20,15 +20,12 @@ SCRIPTS_ALL = \ # Tests that run on most systems, but not on Amiga. SCRIPTS_MORE1 = \ - test52.out \ - test87.out - + test52.out # Tests that run on most systems, but not on Amiga and DOS/Windows. SCRIPTS_MORE2 = \ test49.out - # Tests that run on most systems, but not on VMS SCRIPTS_MORE4 = \ test59.out @@ -36,7 +33,6 @@ SCRIPTS_MORE4 = \ # Tests specifically for MS-Windows. SCRIPTS_WIN32 = - # Tests for the GUI. SCRIPTS_GUI = @@ -309,7 +305,6 @@ NEW_TESTS = \ test_alot_utf8 \ test_alot - # Test targets that use runtest.vim. # Keep test_alot*.res as the last one, sort the others. # test_largefile.res is omitted, it uses too much resources to run on CI. diff --git a/src/testdir/Make_vms.mms b/src/testdir/Make_vms.mms index ff6deb4657..991a2f773b 100644 --- a/src/testdir/Make_vms.mms +++ b/src/testdir/Make_vms.mms @@ -4,7 +4,7 @@ # Authors: Zoltan Arpadffy, # Sandor Kopanyi, # -# Last change: 2019 May 31 +# Last change: 2020 Jul 03 # # This has been tested on VMS 6.2 to 8.3 on DEC Alpha, VAX and IA64. # Edit the lines in the Configuration section below to select. @@ -115,10 +115,6 @@ SCRIPT_ODS5 = test102.out SCRIPT_GDIFF = test47.out .ENDIF -.IFDEF HAVE_PYTHON -SCRIPT_PYTHON = test87.out -.ENDIF - .in.out : -@ !clean up before doing the test -@ if "''F$SEARCH("test.out.*")'" .NES. "" then delete/noconfirm/nolog test.out.* @@ -140,7 +136,7 @@ SCRIPT_PYTHON = test87.out -@ if "''F$SEARCH("Xtest.*")'" .NES. "" then delete/noconfirm/nolog Xtest.*.* all : clean nolog $(START_WITH) $(SCRIPT) $(SCRIPT_GUI) $(SCRIPT_UNIX) $(SCRIPT_WIN) $(SCRIPT_SPELL) $(SCRIPT_ODS5) \ - $(SCRIPT_GDIFF) $(SCRIPT_MZSCH) $(SCRIPT_LUA) $(SCRIPT_PYTHON) nolog + $(SCRIPT_GDIFF) $(SCRIPT_MZSCH) $(SCRIPT_LUA) nolog -@ write sys$output " " -@ write sys$output "-----------------------------------------------" -@ write sys$output " All done" diff --git a/src/testdir/test87.in b/src/testdir/test87.in deleted file mode 100644 index 187b80760f..0000000000 --- a/src/testdir/test87.in +++ /dev/null @@ -1,1725 +0,0 @@ -Tests for various python features. vim: set ft=vim : - -STARTTEST -:so small.vim -:set noswapfile -:if !has('python3') || !has('quickfix') | e! test.ok | wq! test.out | endif -:lang C -:fun Test() -:py3 import vim -:py3 cb = vim.current.buffer -:let l = [] -:py3 l=vim.bindeval('l') -:py3 f=vim.bindeval('function("strlen")') -:" Extending List directly with different types -:py3 l+=[1, "as'd", [1, 2, f, {'a': 1}]] -:$put =string(l) -:$put =string(l[-1]) -:try -: $put =string(l[-4]) -:catch -: $put =v:exception[:13] -:endtry -:" List assignment -:py3 l[0]=0 -:$put =string(l) -:py3 l[-2]=f -:$put =string(l) -:" -:" Extending Dictionary directly with different types -:let d = {} -:fun d.f() -: return 1 -:endfun -py3 << trim EOF - d=vim.bindeval('d') - d['1']='asd' - d.update() # Must not do anything, including throwing errors - d.update(b=[1, 2, f]) - d.update((('-1', {'a': 1}),)) - d.update({'0': -1}) - dk = d.keys() - dv = d.values() - di = d.items() - dk.sort(key=repr) - dv.sort(key=repr) - di.sort(key=repr) -EOF -:$put =py3eval('d[''f''](self={})') -:$put =py3eval('repr(dk)') -:$put =substitute(py3eval('repr(dv)'),'0x\x\+','','g') -:$put =substitute(py3eval('repr(di)'),'0x\x\+','','g') -:for [key, Val] in sort(items(d)) -: $put =string(key) . ' : ' . string(Val) -: unlet key Val -:endfor -:py3 del dk -:py3 del di -:py3 del dv -:" -:" removing items with del -:py3 del l[2] -:$put =string(l) -:let l = range(8) -:py3 l=vim.bindeval('l') -:try -: py3 del l[:3] -: py3 del l[1:] -:catch -: $put =v:exception -:endtry -:$put =string(l) -:" -:py3 del d['-1'] -:py3 del d['f'] -:$put =string(py3eval('d.get(''b'', 1)')) -:$put =string(py3eval('d.pop(''b'')')) -:$put =string(py3eval('d.get(''b'', 1)')) -:$put =string(py3eval('d.pop(''1'', 2)')) -:$put =string(py3eval('d.pop(''1'', 2)')) -:$put =py3eval('repr(d.has_key(''0''))') -:$put =py3eval('repr(d.has_key(''1''))') -:$put =py3eval('repr(''0'' in d)') -:$put =py3eval('repr(''1'' in d)') -:$put =py3eval('repr(list(iter(d)))') -:$put =string(d) -:$put =py3eval('repr(d.popitem())') -:$put =py3eval('repr(d.get(''0''))') -:$put =py3eval('repr(list(iter(d)))') -:" -:" removing items out of range: silently skip items that don't exist -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:" The following two ranges delete nothing as they match empty list: -:py3 del l[2:1] -:$put =string(l) -:py3 del l[2:2] -:$put =string(l) -:py3 del l[2:3] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 del l[2:4] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 del l[2:5] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 del l[2:6] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:" The following two ranges delete nothing as they match empty list: -:py3 del l[-1:2] -:$put =string(l) -:py3 del l[-2:2] -:$put =string(l) -:py3 del l[-3:2] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 del l[-4:2] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 del l[-5:2] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 del l[-6:2] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 del l[::2] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 del l[3:0:-2] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 del l[2:4:-2] -:$put =string(l) -:" -:" Slice assignment to a list -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 l[0:0]=['a'] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 l[1:2]=['b'] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 l[2:4]=['c'] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 l[4:4]=['d'] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 l[-1:2]=['e'] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 l[-10:2]=['f'] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 l[2:-10]=['g'] -:$put =string(l) -:let l = [] -:py3 l=vim.bindeval('l') -:py3 l[0:0]=['h'] -:$put =string(l) -:let l = range(8) -:py3 l=vim.bindeval('l') -:py3 l[2:6:2] = [10, 20] -:$put =string(l) -:let l = range(8) -:py3 l=vim.bindeval('l') -:py3 l[6:2:-2] = [10, 20] -:$put =string(l) -:let l = range(8) -:py3 l=vim.bindeval('l') -:py3 l[6:2] = () -:$put =string(l) -:let l = range(8) -:py3 l=vim.bindeval('l') -:py3 l[6:2:1] = () -:$put =string(l) -:let l = range(8) -:py3 l=vim.bindeval('l') -:py3 l[2:2:1] = () -:$put =string(l) -:" -:" Locked variables -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:lockvar! l -py3 << trim EOF - def emsg(ei): - return ei[0].__name__ + ':' + repr(ei[1].args) - - try: - l[2]='i' - except vim.error: - cb.append('l[2] threw vim.error: ' + emsg(sys.exc_info())) -EOF -:$put =string(l) -:unlockvar! l -:" -:" Function calls -py3 << trim EOF - import sys - import re - - py33_type_error_pattern = re.compile('^__call__\(\) takes (\d+) positional argument but (\d+) were given$') - py37_exception_repr = re.compile(r'([^\(\),])(\)+)$') - - def ee(expr, g=globals(), l=locals()): - cb = vim.current.buffer - try: - try: - exec(expr, g, l) - except Exception as e: - if sys.version_info >= (3, 3) and e.__class__ is AttributeError and str(e).find('has no attribute')>=0 and not str(e).startswith("'vim."): - msg = repr((e.__class__, AttributeError(str(e)[str(e).rfind(" '") + 2:-1]))) - elif sys.version_info >= (3, 3) and e.__class__ is ImportError and str(e).find('No module named \'') >= 0: - msg = repr((e.__class__, ImportError(str(e).replace("'", '')))) - elif sys.version_info >= (3, 6) and e.__class__ is ModuleNotFoundError: - # Python 3.6 gives ModuleNotFoundError, change it to an ImportError - msg = repr((ImportError, ImportError(str(e).replace("'", '')))) - elif sys.version_info >= (3, 3) and e.__class__ is TypeError: - m = py33_type_error_pattern.search(str(e)) - if m: - msg = '__call__() takes exactly {0} positional argument ({1} given)'.format(m.group(1), m.group(2)) - msg = repr((e.__class__, TypeError(msg))) - else: - msg = repr((e.__class__, e)) - # Messages changed with Python 3.6, change new to old. - newmsg1 = """'argument must be str, bytes or bytearray, not None'""" - oldmsg1 = '''"Can't convert 'NoneType' object to str implicitly"''' - if msg.find(newmsg1) > -1: - msg = msg.replace(newmsg1, oldmsg1) - newmsg2 = """'argument must be str, bytes or bytearray, not int'""" - oldmsg2 = '''"Can't convert 'int' object to str implicitly"''' - if msg.find(newmsg2) > -1: - msg = msg.replace(newmsg2, oldmsg2) - elif sys.version_info >= (3, 5) and e.__class__ is ValueError and str(e) == 'embedded null byte': - msg = repr((TypeError, TypeError('expected bytes with no null'))) - else: - msg = repr((e.__class__, e)) - # Some Python versions say can't, others cannot. - if msg.find('can\'t') > -1: - msg = msg.replace('can\'t', 'cannot') - # Some Python versions use single quote, some double quote - if msg.find('"cannot ') > -1: - msg = msg.replace('"cannot ', '\'cannot ') - if msg.find(' attributes"') > -1: - msg = msg.replace(' attributes"', ' attributes\'') - if sys.version_info >= (3, 7): - msg = py37_exception_repr.sub(r'\1,\2', msg) - cb.append(expr + ':' + msg) - else: - cb.append(expr + ':NOT FAILED') - except Exception as e: - msg = repr((e.__class__, e)) - if sys.version_info >= (3, 7): - msg = py37_exception_repr.sub(r'\1,\2', msg) - cb.append(expr + '::' + msg) -EOF -:fun New(...) -: return ['NewStart']+a:000+['NewEnd'] -:endfun -:fun DictNew(...) dict -: return ['DictNewStart']+a:000+['DictNewEnd', self] -:endfun -:let l=[function('New'), function('DictNew')] -:py3 l=vim.bindeval('l') -:py3 l.extend(list(l[0](1, 2, 3))) -:$put =string(l) -:py3 l.extend(list(l[1](1, 2, 3, self={'a': 'b'}))) -:$put =string(l) -:py3 l+=[l[0].name] -:$put =string(l) -:py3 ee('l[1](1, 2, 3)') -:py3 f=l[0] -:delfunction New -:py3 ee('f(1, 2, 3)') -:if has('float') -: let l=[0.0] -: py3 l=vim.bindeval('l') -: py3 l.extend([0.0]) -: $put =string(l) -:else -: $put ='[0.0, 0.0]' -:endif -:let messages=[] -:delfunction DictNew -py3 << trim EOF - import sys - d=vim.bindeval('{}') - m=vim.bindeval('messages') - def em(expr, g=globals(), l=locals()): - try: - exec(expr, g, l) - except Exception as e: - if sys.version_info >= (3, 5) and e.__class__ is ValueError and str(e) == 'embedded null byte': - m.extend([TypeError.__name__]) - else: - m.extend([e.__class__.__name__]) - - em('d["abc1"]') - em('d["abc1"]="\\0"') - em('d["abc1"]=vim') - em('d[""]=1') - em('d["a\\0b"]=1') - em('d[b"a\\0b"]=1') - - em('d.pop("abc1")') - em('d.popitem()') - del em - del m -EOF -:$put =messages -:unlet messages -:" locked and scope attributes -:let d={} | let dl={} | lockvar dl -:for s in split("d dl v: g:") -: let name=tr(s, ':', 's') -: execute 'py3 '.name.'=vim.bindeval("'.s.'")' -: let toput=s.' : '.join(map(['locked', 'scope'], 'v:val.":".py3eval(name.".".v:val)'), ';') -: $put =toput -:endfor -:silent! let d.abc2=1 -:silent! let dl.abc3=1 -:py3 d.locked=True -:py3 dl.locked=False -:silent! let d.def=1 -:silent! let dl.def=1 -:put ='d:'.string(d) -:put ='dl:'.string(dl) -:unlet d dl -: -:let l=[] | let ll=[] | lockvar ll -:for s in split("l ll") -: let name=tr(s, ':', 's') -: execute 'py3 '.name.'=vim.bindeval("'.s.'")' -: let toput=s.' : locked:'.py3eval(name.'.locked') -: $put =toput -:endfor -:silent! call extend(l, [0]) -:silent! call extend(ll, [0]) -:py3 l.locked=True -:py3 ll.locked=False -:silent! call extend(l, [1]) -:silent! call extend(ll, [1]) -:put ='l:'.string(l) -:put ='ll:'.string(ll) -:unlet l ll -:" -:" py3eval() -:let l=py3eval('[0, 1, 2]') -:$put =string(l) -:let d=py3eval('{"a": "b", "c": 1, "d": ["e"]}') -:$put =sort(items(d)) -:let v:errmsg = '' -:$put ='py3eval(\"None\") = ' . py3eval('None') . v:errmsg -:if has('float') -: let f=py3eval('0.0') -: $put =string(f) -:else -: $put ='0.0' -:endif -:" Invalid values: -:for e in ['"\0"', '{"\0": 1}', 'undefined_name', 'vim'] -: try -: let v=py3eval(e) -: catch -: let toput=e.":\t".v:exception[:13] -: $put =toput -: endtry -:endfor -:" -:" threading -:let l = [0] -:py3 l=vim.bindeval('l') -py3 << trim EOF - import threading - import time - - class T(threading.Thread): - def __init__(self): - threading.Thread.__init__(self) - self.t = 0 - self.running = True - - def run(self): - while self.running: - self.t += 1 - time.sleep(0.1) - - t = T() - del T - t.start() -EOF -:sleep 1 -:py3 t.running = False -:py3 t.join() -:" Check if the background thread is working. Count should be 10, but on a -:" busy system (AppVeyor) it can be much lower. -:py3 l[0] = t.t > 4 -:py3 del time -:py3 del threading -:py3 del t -:$put =string(l) -:" -:" settrace -:let l = [] -:py3 l=vim.bindeval('l') -py3 << trim EOF - import sys - - def traceit(frame, event, arg): - global l - if event == "line": - l += [frame.f_lineno] - return traceit - - def trace_main(): - for i in range(5): - pass -EOF -:py3 sys.settrace(traceit) -:py3 trace_main() -:py3 sys.settrace(None) -:py3 del traceit -:py3 del trace_main -:$put =string(l) -:" -:" Slice -:py3 ll = vim.bindeval('[0, 1, 2, 3, 4, 5]') -:py3 l = ll[:4] -:$put =string(py3eval('l')) -:py3 l = ll[2:] -:$put =string(py3eval('l')) -:py3 l = ll[:-4] -:$put =string(py3eval('l')) -:py3 l = ll[-2:] -:$put =string(py3eval('l')) -:py3 l = ll[2:4] -:$put =string(py3eval('l')) -:py3 l = ll[4:2] -:$put =string(py3eval('l')) -:py3 l = ll[-4:-2] -:$put =string(py3eval('l')) -:py3 l = ll[-2:-4] -:$put =string(py3eval('l')) -:py3 l = ll[:] -:$put =string(py3eval('l')) -:py3 l = ll[0:6] -:$put =string(py3eval('l')) -:py3 l = ll[-10:10] -:$put =string(py3eval('l')) -:py3 l = ll[4:2:-1] -:$put =string(py3eval('l')) -:py3 l = ll[::2] -:$put =string(py3eval('l')) -:py3 l = ll[4:2:1] -:$put =string(py3eval('l')) -:py3 del l -:" -:" Vars -:let g:foo = 'bac' -:let w:abc3 = 'def' -:let b:baz = 'bar' -:let t:bar = 'jkl' -:try -: throw "Abc" -:catch -: put =py3eval('vim.vvars[''exception'']') -:endtry -:put =py3eval('vim.vars[''foo'']') -:put =py3eval('vim.current.window.vars[''abc3'']') -:put =py3eval('vim.current.buffer.vars[''baz'']') -:put =py3eval('vim.current.tabpage.vars[''bar'']') -:" -:" Options -:" paste: boolean, global -:" previewheight number, global -:" operatorfunc: string, global -:" number: boolean, window-local -:" numberwidth: number, window-local -:" colorcolumn: string, window-local -:" statusline: string, window-local/global -:" autoindent: boolean, buffer-local -:" shiftwidth: number, buffer-local -:" omnifunc: string, buffer-local -:" preserveindent: boolean, buffer-local/global -:" path: string, buffer-local/global -:let g:bufs=[bufnr('%')] -:new -:let g:bufs+=[bufnr('%')] -:vnew -:let g:bufs+=[bufnr('%')] -:wincmd j -:vnew -:let g:bufs+=[bufnr('%')] -:wincmd l -:fun RecVars(opt) -: let gval =string(eval('&g:'.a:opt)) -: let wvals=join(map(range(1, 4), 'v:val.":".string(getwinvar(v:val, "&".a:opt))')) -: let bvals=join(map(copy(g:bufs), 'v:val.":".string(getbufvar(v:val, "&".a:opt))')) -: put =' G: '.gval -: put =' W: '.wvals -: put =' B: '.wvals -:endfun -py3 << trim EOF - def e(s, g=globals(), l=locals()): - try: - exec(s, g, l) - except Exception as e: - vim.command('return ' + repr(e.__class__.__name__)) - - def ev(s, g=globals(), l=locals()): - try: - return eval(s, g, l) - except Exception as e: - vim.command('let exc=' + repr(e.__class__.__name__)) - return 0 -EOF -:fun E(s) -: python3 e(vim.eval('a:s')) -:endfun -:fun Ev(s) -: let r=py3eval('ev(vim.eval("a:s"))') -: if exists('exc') -: throw exc -: endif -: return r -:endfun -:py3 gopts1=vim.options -:py3 wopts1=vim.windows[2].options -:py3 wopts2=vim.windows[0].options -:py3 wopts3=vim.windows[1].options -:py3 bopts1=vim.buffers[vim.bindeval("g:bufs")[2]].options -:py3 bopts2=vim.buffers[vim.bindeval("g:bufs")[1]].options -:py3 bopts3=vim.buffers[vim.bindeval("g:bufs")[0]].options -:$put ='wopts iters equal: '.py3eval('list(wopts1) == list(wopts2)') -:$put ='bopts iters equal: '.py3eval('list(bopts1) == list(bopts2)') -:py3 gset=set(iter(gopts1)) -:py3 wset=set(iter(wopts1)) -:py3 bset=set(iter(bopts1)) -:set path=.,..,, -:let lst=[] -:let lst+=[['paste', 1, 0, 1, 2, 1, 1, 0 ]] -:let lst+=[['previewheight', 5, 1, 6, 'a', 0, 1, 0 ]] -:let lst+=[['operatorfunc', 'A', 'B', 'C', 2, 0, 1, 0 ]] -:let lst+=[['number', 0, 1, 1, 0, 1, 0, 1 ]] -:let lst+=[['numberwidth', 2, 3, 5, -100, 0, 0, 1 ]] -:let lst+=[['colorcolumn', '+1', '+2', '+3', 'abc4', 0, 0, 1 ]] -:let lst+=[['statusline', '1', '2', '4', 0, 0, 1, 1 ]] -:let lst+=[['autoindent', 0, 1, 1, 2, 1, 0, 2 ]] -:let lst+=[['shiftwidth', 0, 2, 1, 3, 0, 0, 2 ]] -:let lst+=[['omnifunc', 'A', 'B', 'C', 1, 0, 0, 2 ]] -:let lst+=[['preserveindent', 0, 1, 1, 2, 1, 1, 2 ]] -:let lst+=[['path', '.,,', ',,', '.', 0, 0, 1, 2 ]] -:for [oname, oval1, oval2, oval3, invval, bool, global, local] in lst -: py3 oname=vim.eval('oname') -: py3 oval1=vim.bindeval('oval1') -: py3 oval2=vim.bindeval('oval2') -: py3 oval3=vim.bindeval('oval3') -: if invval is 0 || invval is 1 -: py3 invval=bool(vim.bindeval('invval')) -: else -: py3 invval=vim.bindeval('invval') -: endif -: if bool -: py3 oval1=bool(oval1) -: py3 oval2=bool(oval2) -: py3 oval3=bool(oval3) -: endif -: put ='>>> '.oname -: $put =' g/w/b:'.py3eval('oname in gset').'/'.py3eval('oname in wset').'/'.py3eval('oname in bset') -: $put =' g/w/b (in):'.py3eval('oname in gopts1').'/'.py3eval('oname in wopts1').'/'.py3eval('oname in bopts1') -: for v in ['gopts1', 'wopts1', 'bopts1'] -: try -: put =' p/'.v.': '.Ev('repr('.v.'['''.oname.'''])') -: catch -: put =' p/'.v.'! '.v:exception -: endtry -: let r=E(v.'['''.oname.''']=invval') -: if r isnot 0 -: put =' inv: '.string(invval).'! '.r -: endif -: for vv in (v is# 'gopts1' ? [v] : [v, v[:-2].'2', v[:-2].'3']) -: let val=substitute(vv, '^.opts', 'oval', '') -: let r=E(vv.'['''.oname.''']='.val) -: if r isnot 0 -: put =' '.vv.'! '.r -: endif -: endfor -: endfor -: call RecVars(oname) -: for v in ['wopts3', 'bopts3'] -: let r=E('del '.v.'["'.oname.'"]') -: if r isnot 0 -: put =' del '.v.'! '.r -: endif -: endfor -: call RecVars(oname) -:endfor -:delfunction RecVars -:delfunction E -:delfunction Ev -:py3 del ev -:py3 del e -:only -:for buf in g:bufs[1:] -: execute 'bwipeout!' buf -:endfor -:py3 del gopts1 -:py3 del wopts1 -:py3 del wopts2 -:py3 del wopts3 -:py3 del bopts1 -:py3 del bopts2 -:py3 del bopts3 -:py3 del oval1 -:py3 del oval2 -:py3 del oval3 -:py3 del oname -:py3 del invval -:" -:" Test buffer object -:vnew -:put ='First line' -:put ='Second line' -:put ='Third line' -:1 delete _ -:py3 b=vim.current.buffer -:wincmd w -:mark a -:augroup BUFS -: autocmd BufFilePost * python3 cb.append(vim.eval('expand("")') + ':BufFilePost:' + vim.eval('bufnr("%")')) -: autocmd BufFilePre * python3 cb.append(vim.eval('expand("")') + ':BufFilePre:' + vim.eval('bufnr("%")')) -:augroup END -py3 << trim EOF - # Tests BufferAppend and BufferItem - cb.append(b[0]) - # Tests BufferSlice and BufferAssSlice - cb.append('abc5') # Will be overwritten - cb[-1:] = b[:-2] - # Test BufferLength and BufferAssSlice - cb.append('def') # Will not be overwritten - cb[len(cb):] = b[:] - # Test BufferAssItem and BufferMark - cb.append('ghi') # Will be overwritten - cb[-1] = repr((len(cb) - cb.mark('a')[0], cb.mark('a')[1])) - # Test BufferRepr - cb.append(repr(cb) + repr(b)) - # Modify foreign buffer - b.append('foo') - b[0]='bar' - b[0:0]=['baz'] - vim.command('call append("$", getbufline(%i, 1, "$"))' % b.number) - # Test assigning to name property - import os - old_name = cb.name - cb.name = 'foo' - cb.append(cb.name[-11:].replace(os.path.sep, '/')) - b.name = 'bar' - cb.append(b.name[-11:].replace(os.path.sep, '/')) - cb.name = old_name - cb.append(cb.name[-17:].replace(os.path.sep, '/')) - del old_name - # Test CheckBuffer - for _b in vim.buffers: - if _b is not cb: - vim.command('bwipeout! ' + str(_b.number)) - del _b - cb.append('valid: b:%s, cb:%s' % (repr(b.valid), repr(cb.valid))) - for expr in ('b[1]','b[:] = ["A", "B"]','b[:]','b.append("abc6")'): - try: - exec(expr) - except vim.error: - pass - else: - # Usually a SEGV here - # Should not happen in any case - cb.append('No exception for ' + expr) - vim.command('cd .') - del b -EOF -:" -:" Test vim.buffers object -:set hidden -:edit a -:buffer # -:edit b -:buffer # -:edit c -:buffer # -py3 << trim EOF - # Check GCing iterator that was not fully exhausted - i = iter(vim.buffers) - cb.append('i:' + str(next(i))) - # and also check creating more than one iterator at a time - i2 = iter(vim.buffers) - cb.append('i2:' + str(next(i2))) - cb.append('i:' + str(next(i))) - # The following should trigger GC and not cause any problems - del i - del i2 - i3 = iter(vim.buffers) - cb.append('i3:' + str(next(i3))) - del i3 - - prevnum = 0 - for b in vim.buffers: - # Check buffer order - if prevnum >= b.number: - cb.append('!!! Buffer numbers not in strictly ascending order') - # Check indexing: vim.buffers[number].number == number - cb.append(str(b.number) + ':' + repr(vim.buffers[b.number]) + '=' + repr(b)) - prevnum = b.number - del prevnum - - cb.append(str(len(vim.buffers))) - - bnums = list(map(lambda b: b.number, vim.buffers))[1:] - - # Test wiping out buffer with existing iterator - i4 = iter(vim.buffers) - cb.append('i4:' + str(next(i4))) - vim.command('bwipeout! ' + str(bnums.pop(0))) - try: - next(i4) - except vim.error: - pass - else: - cb.append('!!!! No vim.error') - i4 = iter(vim.buffers) - vim.command('bwipeout! ' + str(bnums.pop(-1))) - vim.command('bwipeout! ' + str(bnums.pop(-1))) - cb.append('i4:' + str(next(i4))) - try: - next(i4) - except StopIteration: - cb.append('StopIteration') - del i4 - del bnums -EOF -:" -:" Test vim.{tabpage,window}list and vim.{tabpage,window} objects -:tabnew 0 -:tabnew 1 -:vnew a.1 -:tabnew 2 -:vnew a.2 -:vnew b.2 -:vnew c.2 -py3 << trim EOF - cb.append('Number of tabs: ' + str(len(vim.tabpages))) - cb.append('Current tab pages:') - - def W(w): - if '(unknown)' in repr(w): - return '' - else: - return repr(w) - - def Cursor(w, start=len(cb)): - if w.buffer is cb: - return repr((start - w.cursor[0], w.cursor[1])) - else: - return repr(w.cursor) - - for t in vim.tabpages: - cb.append(' ' + repr(t) + '(' + str(t.number) + ')' + ': ' + str(len(t.windows)) + ' windows, current is ' + W(t.window)) - cb.append(' Windows:') - for w in t.windows: - cb.append(' ' + W(w) + '(' + str(w.number) + ')' + ': displays buffer ' + repr(w.buffer) + '; cursor is at ' + Cursor(w)) - # Other values depend on the size of the terminal, so they are checked partly: - for attr in ('height', 'row', 'width', 'col'): - try: - aval = getattr(w, attr) - if type(aval) is not int: - raise TypeError - if aval < 0: - raise ValueError - except Exception as e: - cb.append('!!!!!! Error while getting attribute ' + attr + ': ' + e.__class__.__name__) - del aval - del attr - w.cursor = (len(w.buffer), 0) - del W - del Cursor - cb.append('Number of windows in current tab page: ' + str(len(vim.windows))) - if list(vim.windows) != list(vim.current.tabpage.windows): - cb.append('!!!!!! Windows differ') -EOF -:" -:" Test vim.current -py3 << trim EOF - def H(o): - return repr(o) - cb.append('Current tab page: ' + repr(vim.current.tabpage)) - cb.append('Current window: ' + repr(vim.current.window) + ': ' + H(vim.current.window) + ' is ' + H(vim.current.tabpage.window)) - cb.append('Current buffer: ' + repr(vim.current.buffer) + ': ' + H(vim.current.buffer) + ' is ' + H(vim.current.window.buffer)+ ' is ' + H(vim.current.tabpage.window.buffer)) - del H - # Assigning: fails - try: - vim.current.window = vim.tabpages[0].window - except ValueError: - cb.append('ValueError at assigning foreign tab window') - - for attr in ('window', 'tabpage', 'buffer'): - try: - setattr(vim.current, attr, None) - except TypeError: - cb.append('Type error at assigning None to vim.current.' + attr) - del attr - - # Assigning: success - vim.current.tabpage = vim.tabpages[-2] - vim.current.buffer = cb - vim.current.window = vim.windows[0] - vim.current.window.cursor = (len(vim.current.buffer), 0) - cb.append('Current tab page: ' + repr(vim.current.tabpage)) - cb.append('Current window: ' + repr(vim.current.window)) - cb.append('Current buffer: ' + repr(vim.current.buffer)) - cb.append('Current line: ' + repr(vim.current.line)) - ws = list(vim.windows) - ts = list(vim.tabpages) - for b in vim.buffers: - if b is not cb: - vim.command('bwipeout! ' + str(b.number)) - del b - cb.append('w.valid: ' + repr([w.valid for w in ws])) - cb.append('t.valid: ' + repr([t.valid for t in ts])) - del w - del t - del ts - del ws -EOF -:tabonly! -:only! -:" -:" Test types -py3 << trim EOF - for expr, attr in ( - ('vim.vars', 'Dictionary'), - ('vim.options', 'Options'), - ('vim.bindeval("{}")', 'Dictionary'), - ('vim.bindeval("[]")', 'List'), - ('vim.bindeval("function(\'tr\')")', 'Function'), - ('vim.current.buffer', 'Buffer'), - ('vim.current.range', 'Range'), - ('vim.current.window', 'Window'), - ('vim.current.tabpage', 'TabPage'), - ): - cb.append(expr + ':' + attr + ':' + repr(type(eval(expr)) is getattr(vim, attr))) - del expr - del attr -EOF -:" -:" Test __dir__() method -py3 << trim EOF - for name, o in ( - ('current', vim.current), - ('buffer', vim.current.buffer), - ('window', vim.current.window), - ('tabpage', vim.current.tabpage), - ('range', vim.current.range), - ('dictionary', vim.bindeval('{}')), - ('list', vim.bindeval('[]')), - ('function', vim.bindeval('function("tr")')), - ('output', sys.stdout), - ): - cb.append(name + ':' + ','.join(dir(o))) - del name - del o -EOF -:" -:" Test vim.*.__new__ -:$put =string(py3eval('vim.Dictionary({})')) -:$put =string(py3eval('vim.Dictionary(a=1)')) -:$put =string(py3eval('vim.Dictionary(((''a'', 1),))')) -:$put =string(py3eval('vim.List()')) -:$put =string(py3eval('vim.List(iter(''abc7''))')) -:$put =string(py3eval('vim.Function(''tr'')')) -:$put =string(py3eval('vim.Function(''tr'', args=[123, 3, 4])')) -:$put =string(py3eval('vim.Function(''tr'', args=[])')) -:$put =string(py3eval('vim.Function(''tr'', self={})')) -:$put =string(py3eval('vim.Function(''tr'', args=[123, 3, 4], self={})')) -:$put ='auto_rebind' -:$put =string(py3eval('vim.Function(''tr'', auto_rebind=False)')) -:$put =string(py3eval('vim.Function(''tr'', args=[123, 3, 4], auto_rebind=False)')) -:$put =string(py3eval('vim.Function(''tr'', args=[], auto_rebind=False)')) -:$put =string(py3eval('vim.Function(''tr'', self={}, auto_rebind=False)')) -:$put =string(py3eval('vim.Function(''tr'', args=[123, 3, 4], self={}, auto_rebind=False)')) -:" -:" Test vim.Function -:function Args(...) -: return a:000 -:endfunction -:function SelfArgs(...) dict -: return [a:000, self] -:endfunction -:" The following four lines should not crash -:let Pt = function('tr', [[]], {'l': []}) -:py3 Pt = vim.bindeval('Pt') -:unlet Pt -:py3 del Pt -py3 << trim EOF - def ecall(out_prefix, func, *args, **kwargs): - line = out_prefix + ': ' - try: - ret = func(*args, **kwargs) - except Exception: - line += '!exception: ' + emsg(sys.exc_info()) - else: - line += '!result: ' + str(vim.Function('string')(ret), 'utf-8') - cb.append(line) - a = vim.Function('Args') - pa1 = vim.Function('Args', args=['abcArgsPA1']) - pa2 = vim.Function('Args', args=[]) - pa3 = vim.Function('Args', args=['abcArgsPA3'], self={'abcSelfPA3': 'abcSelfPA3Val'}) - pa4 = vim.Function('Args', self={'abcSelfPA4': 'abcSelfPA4Val'}) - cb.append('a: ' + repr(a)) - cb.append('pa1: ' + repr(pa1)) - cb.append('pa2: ' + repr(pa2)) - cb.append('pa3: ' + repr(pa3)) - cb.append('pa4: ' + repr(pa4)) - sa = vim.Function('SelfArgs') - psa1 = vim.Function('SelfArgs', args=['abcArgsPSA1']) - psa2 = vim.Function('SelfArgs', args=[]) - psa3 = vim.Function('SelfArgs', args=['abcArgsPSA3'], self={'abcSelfPSA3': 'abcSelfPSA3Val'}) - psa4 = vim.Function('SelfArgs', self={'abcSelfPSA4': 'abcSelfPSA4Val'}) - psa5 = vim.Function('SelfArgs', self={'abcSelfPSA5': 'abcSelfPSA5Val'}, auto_rebind=0) - psa6 = vim.Function('SelfArgs', args=['abcArgsPSA6'], self={'abcSelfPSA6': 'abcSelfPSA6Val'}, auto_rebind=()) - psa7 = vim.Function('SelfArgs', args=['abcArgsPSA7'], auto_rebind=[]) - psa8 = vim.Function('SelfArgs', auto_rebind=False) - psa9 = vim.Function('SelfArgs', self={'abcSelfPSA9': 'abcSelfPSA9Val'}, auto_rebind=True) - psaA = vim.Function('SelfArgs', args=['abcArgsPSAA'], self={'abcSelfPSAA': 'abcSelfPSAAVal'}, auto_rebind=1) - psaB = vim.Function('SelfArgs', args=['abcArgsPSAB'], auto_rebind={'abcARPSAB': 'abcARPSABVal'}) - psaC = vim.Function('SelfArgs', auto_rebind=['abcARPSAC']) - cb.append('sa: ' + repr(sa)) - cb.append('psa1: ' + repr(psa1)) - cb.append('psa2: ' + repr(psa2)) - cb.append('psa3: ' + repr(psa3)) - cb.append('psa4: ' + repr(psa4)) - cb.append('psa5: ' + repr(psa5)) - cb.append('psa6: ' + repr(psa6)) - cb.append('psa7: ' + repr(psa7)) - cb.append('psa8: ' + repr(psa8)) - cb.append('psa9: ' + repr(psa9)) - cb.append('psaA: ' + repr(psaA)) - cb.append('psaB: ' + repr(psaB)) - cb.append('psaC: ' + repr(psaC)) - - psar = vim.Function('SelfArgs', args=[{'abcArgsPSAr': 'abcArgsPSArVal'}], self={'abcSelfPSAr': 'abcSelfPSArVal'}) - psar.args[0]['abcArgsPSAr2'] = [psar.self, psar.args[0]] - psar.self['rec'] = psar - psar.self['self'] = psar.self - psar.self['args'] = psar.args - - try: - cb.append('psar: ' + repr(psar)) - except Exception: - cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info())) -EOF -:$put ='s(a): '.string(py3eval('a')) -:$put ='s(pa1): '.string(py3eval('pa1')) -:$put ='s(pa2): '.string(py3eval('pa2')) -:$put ='s(pa3): '.string(py3eval('pa3')) -:$put ='s(pa4): '.string(py3eval('pa4')) -:$put ='s(sa): '.string(py3eval('sa')) -:$put ='s(psa1): '.string(py3eval('psa1')) -:$put ='s(psa2): '.string(py3eval('psa2')) -:$put ='s(psa3): '.string(py3eval('psa3')) -:$put ='s(psa4): '.string(py3eval('psa4')) -:$put ='s(psa5): '.string(py3eval('psa5')) -:$put ='s(psa6): '.string(py3eval('psa6')) -:$put ='s(psa7): '.string(py3eval('psa7')) -:$put ='s(psa8): '.string(py3eval('psa8')) -:$put ='s(psa9): '.string(py3eval('psa9')) -:$put ='s(psaA): '.string(py3eval('psaA')) -:$put ='s(psaB): '.string(py3eval('psaB')) -:$put ='s(psaC): '.string(py3eval('psaC')) -: -:for v in ['sa', 'psa1', 'psa2', 'psa3', 'psa4', 'psa5', 'psa6', 'psa7', 'psa8', 'psa9', 'psaA', 'psaB', 'psaC'] -: let d = {'f': py3eval(v)} -: $put ='d.'.v.'(): '.string(d.f()) -:endfor -: -:py3 ecall('a()', a, ) -:py3 ecall('pa1()', pa1, ) -:py3 ecall('pa2()', pa2, ) -:py3 ecall('pa3()', pa3, ) -:py3 ecall('pa4()', pa4, ) -:py3 ecall('sa()', sa, ) -:py3 ecall('psa1()', psa1, ) -:py3 ecall('psa2()', psa2, ) -:py3 ecall('psa3()', psa3, ) -:py3 ecall('psa4()', psa4, ) -: -:py3 ecall('a(42, 43)', a, 42, 43) -:py3 ecall('pa1(42, 43)', pa1, 42, 43) -:py3 ecall('pa2(42, 43)', pa2, 42, 43) -:py3 ecall('pa3(42, 43)', pa3, 42, 43) -:py3 ecall('pa4(42, 43)', pa4, 42, 43) -:py3 ecall('sa(42, 43)', sa, 42, 43) -:py3 ecall('psa1(42, 43)', psa1, 42, 43) -:py3 ecall('psa2(42, 43)', psa2, 42, 43) -:py3 ecall('psa3(42, 43)', psa3, 42, 43) -:py3 ecall('psa4(42, 43)', psa4, 42, 43) -: -:py3 ecall('a(42, self={"20": 1})', a, 42, self={'20': 1}) -:py3 ecall('pa1(42, self={"20": 1})', pa1, 42, self={'20': 1}) -:py3 ecall('pa2(42, self={"20": 1})', pa2, 42, self={'20': 1}) -:py3 ecall('pa3(42, self={"20": 1})', pa3, 42, self={'20': 1}) -:py3 ecall('pa4(42, self={"20": 1})', pa4, 42, self={'20': 1}) -:py3 ecall('sa(42, self={"20": 1})', sa, 42, self={'20': 1}) -:py3 ecall('psa1(42, self={"20": 1})', psa1, 42, self={'20': 1}) -:py3 ecall('psa2(42, self={"20": 1})', psa2, 42, self={'20': 1}) -:py3 ecall('psa3(42, self={"20": 1})', psa3, 42, self={'20': 1}) -:py3 ecall('psa4(42, self={"20": 1})', psa4, 42, self={'20': 1}) -: -:py3 ecall('a(self={"20": 1})', a, self={'20': 1}) -:py3 ecall('pa1(self={"20": 1})', pa1, self={'20': 1}) -:py3 ecall('pa2(self={"20": 1})', pa2, self={'20': 1}) -:py3 ecall('pa3(self={"20": 1})', pa3, self={'20': 1}) -:py3 ecall('pa4(self={"20": 1})', pa4, self={'20': 1}) -:py3 ecall('sa(self={"20": 1})', sa, self={'20': 1}) -:py3 ecall('psa1(self={"20": 1})', psa1, self={'20': 1}) -:py3 ecall('psa2(self={"20": 1})', psa2, self={'20': 1}) -:py3 ecall('psa3(self={"20": 1})', psa3, self={'20': 1}) -:py3 ecall('psa4(self={"20": 1})', psa4, self={'20': 1}) -py3 << trim EOF - def s(v): - if v is None: - return repr(v) - else: - return str(vim.Function('string')(v), 'utf-8') - - cb.append('a.args: ' + s(a.args)) - cb.append('pa1.args: ' + s(pa1.args)) - cb.append('pa2.args: ' + s(pa2.args)) - cb.append('pa3.args: ' + s(pa3.args)) - cb.append('pa4.args: ' + s(pa4.args)) - cb.append('sa.args: ' + s(sa.args)) - cb.append('psa1.args: ' + s(psa1.args)) - cb.append('psa2.args: ' + s(psa2.args)) - cb.append('psa3.args: ' + s(psa3.args)) - cb.append('psa4.args: ' + s(psa4.args)) - - cb.append('a.self: ' + s(a.self)) - cb.append('pa1.self: ' + s(pa1.self)) - cb.append('pa2.self: ' + s(pa2.self)) - cb.append('pa3.self: ' + s(pa3.self)) - cb.append('pa4.self: ' + s(pa4.self)) - cb.append('sa.self: ' + s(sa.self)) - cb.append('psa1.self: ' + s(psa1.self)) - cb.append('psa2.self: ' + s(psa2.self)) - cb.append('psa3.self: ' + s(psa3.self)) - cb.append('psa4.self: ' + s(psa4.self)) - - cb.append('a.name: ' + s(a.name)) - cb.append('pa1.name: ' + s(pa1.name)) - cb.append('pa2.name: ' + s(pa2.name)) - cb.append('pa3.name: ' + s(pa3.name)) - cb.append('pa4.name: ' + s(pa4.name)) - cb.append('sa.name: ' + s(sa.name)) - cb.append('psa1.name: ' + s(psa1.name)) - cb.append('psa2.name: ' + s(psa2.name)) - cb.append('psa3.name: ' + s(psa3.name)) - cb.append('psa4.name: ' + s(psa4.name)) - - cb.append('a.auto_rebind: ' + s(a.auto_rebind)) - cb.append('pa1.auto_rebind: ' + s(pa1.auto_rebind)) - cb.append('pa2.auto_rebind: ' + s(pa2.auto_rebind)) - cb.append('pa3.auto_rebind: ' + s(pa3.auto_rebind)) - cb.append('pa4.auto_rebind: ' + s(pa4.auto_rebind)) - cb.append('sa.auto_rebind: ' + s(sa.auto_rebind)) - cb.append('psa1.auto_rebind: ' + s(psa1.auto_rebind)) - cb.append('psa2.auto_rebind: ' + s(psa2.auto_rebind)) - cb.append('psa3.auto_rebind: ' + s(psa3.auto_rebind)) - cb.append('psa4.auto_rebind: ' + s(psa4.auto_rebind)) - cb.append('psa5.auto_rebind: ' + s(psa5.auto_rebind)) - cb.append('psa6.auto_rebind: ' + s(psa6.auto_rebind)) - cb.append('psa7.auto_rebind: ' + s(psa7.auto_rebind)) - cb.append('psa8.auto_rebind: ' + s(psa8.auto_rebind)) - cb.append('psa9.auto_rebind: ' + s(psa9.auto_rebind)) - cb.append('psaA.auto_rebind: ' + s(psaA.auto_rebind)) - cb.append('psaB.auto_rebind: ' + s(psaB.auto_rebind)) - cb.append('psaC.auto_rebind: ' + s(psaC.auto_rebind)) - - del s - - del a - del pa1 - del pa2 - del pa3 - del pa4 - del sa - del psa1 - del psa2 - del psa3 - del psa4 - del psa5 - del psa6 - del psa7 - del psa8 - del psa9 - del psaA - del psaB - del psaC - del psar - - del ecall -EOF -:" -:" Test stdout/stderr -:redir => messages -:py3 sys.stdout.write('abc8') ; sys.stdout.write('def') -:py3 sys.stderr.write('abc9') ; sys.stderr.write('def') -:py3 sys.stdout.writelines(iter('abcA')) -:py3 sys.stderr.writelines(iter('abcB')) -:redir END -:$put =string(substitute(messages, '\d\+', '', 'g')) -:" Test subclassing -:fun Put(...) -: $put =string(a:000) -: return a:000 -:endfun -py3 << trim EOF - class DupDict(vim.Dictionary): - def __setitem__(self, key, value): - super(DupDict, self).__setitem__(key, value) - super(DupDict, self).__setitem__('dup_' + key, value) - dd = DupDict() - dd['a'] = 'b' - - class DupList(vim.List): - def __getitem__(self, idx): - return [super(DupList, self).__getitem__(idx)] * 2 - - dl = DupList() - dl2 = DupList(iter('abcC')) - dl.extend(dl2[0]) - - class DupFun(vim.Function): - def __call__(self, arg): - return super(DupFun, self).__call__(arg, arg) - - df = DupFun('Put') -EOF -:$put =string(sort(keys(py3eval('dd')))) -:$put =string(py3eval('dl')) -:$put =string(py3eval('dl2')) -:$put =string(py3eval('df(2)')) -:$put =string(py3eval('dl') is# py3eval('dl')) -:$put =string(py3eval('dd') is# py3eval('dd')) -:$put =string(py3eval('df')) -:delfunction Put -py3 << trim EOF - del DupDict - del DupList - del DupFun - del dd - del dl - del dl2 - del df -EOF -:" -:" Test chdir -py3 << trim EOF - import os - fnamemodify = vim.Function('fnamemodify') - cb.append(str(fnamemodify('.', ':p:h:t'))) - cb.append(vim.eval('@%')) - os.chdir('..') - path = fnamemodify('.', ':p:h:t') - if path != b'src': - # Running tests from a shadow directory, so move up another level - # This will result in @% looking like shadow/testdir/test87.in, hence the - # slicing to remove the leading path and path separator - os.chdir('..') - cb.append(str(fnamemodify('.', ':p:h:t'))) - cb.append(vim.eval('@%')[len(path)+1:].replace(os.path.sep, '/')) - os.chdir(path) - else: - cb.append(str(fnamemodify('.', ':p:h:t'))) - cb.append(vim.eval('@%').replace(os.path.sep, '/')) - del path - os.chdir('testdir') - cb.append(str(fnamemodify('.', ':p:h:t'))) - cb.append(vim.eval('@%')) - del fnamemodify -EOF -:" -:" Test errors -:fun F() dict -:endfun -:fun D() -:endfun -py3 << trim EOF - d = vim.Dictionary() - ned = vim.Dictionary(foo='bar', baz='abcD') - dl = vim.Dictionary(a=1) - dl.locked = True - l = vim.List() - ll = vim.List('abcE') - ll.locked = True - nel = vim.List('abcO') - f = vim.Function('string') - fd = vim.Function('F') - fdel = vim.Function('D') - vim.command('delfunction D') - - def subexpr_test(expr, name, subexprs): - cb.append('>>> Testing %s using %s' % (name, expr)) - for subexpr in subexprs: - ee(expr % subexpr) - cb.append('<<< Finished') - - def stringtochars_test(expr): - return subexpr_test(expr, 'StringToChars', ( - '1', # Fail type checks - 'b"\\0"', # Fail PyString_AsStringAndSize(object, , NULL) check - '"\\0"', # Fail PyString_AsStringAndSize(bytes, , NULL) check - )) - - class Mapping(object): - def __init__(self, d): - self.d = d - - def __getitem__(self, key): - return self.d[key] - - def keys(self): - return self.d.keys() - - def items(self): - return self.d.items() - - def convertfrompyobject_test(expr, recurse=True): - # pydict_to_tv - stringtochars_test(expr % '{%s : 1}') - if recurse: - convertfrompyobject_test(expr % '{"abcF" : %s}', False) - # pymap_to_tv - stringtochars_test(expr % 'Mapping({%s : 1})') - if recurse: - convertfrompyobject_test(expr % 'Mapping({"abcG" : %s})', False) - # pyseq_to_tv - iter_test(expr) - return subexpr_test(expr, 'ConvertFromPyObject', ( - 'None', # Not conversible - '{b"": 1}', # Empty key not allowed - '{"": 1}', # Same, but with unicode object - 'FailingMapping()', # - 'FailingMappingKey()', # - 'FailingNumber()', # - )) - - def convertfrompymapping_test(expr): - convertfrompyobject_test(expr) - return subexpr_test(expr, 'ConvertFromPyMapping', ( - '[]', - )) - - def iter_test(expr): - return subexpr_test(expr, '*Iter*', ( - 'FailingIter()', - 'FailingIterNext()', - )) - - def number_test(expr, natural=False, unsigned=False): - if natural: - unsigned = True - return subexpr_test(expr, 'NumberToLong', ( - '[]', - 'None', - ) + (('-1',) if unsigned else ()) - + (('0',) if natural else ())) - - class FailingTrue(object): - def __bool__(self): - raise NotImplementedError('bool') - - class FailingIter(object): - def __iter__(self): - raise NotImplementedError('iter') - - class FailingIterNext(object): - def __iter__(self): - return self - - def __next__(self): - raise NotImplementedError('next') - - class FailingIterNextN(object): - def __init__(self, n): - self.n = n - - def __iter__(self): - return self - - def __next__(self): - if self.n: - self.n -= 1 - return 1 - else: - raise NotImplementedError('next N') - - class FailingMappingKey(object): - def __getitem__(self, item): - raise NotImplementedError('getitem:mappingkey') - - def keys(self): - return list("abcH") - - class FailingMapping(object): - def __getitem__(self): - raise NotImplementedError('getitem:mapping') - - def keys(self): - raise NotImplementedError('keys') - - class FailingList(list): - def __getitem__(self, idx): - if i == 2: - raise NotImplementedError('getitem:list') - else: - return super(FailingList, self).__getitem__(idx) - - class NoArgsCall(object): - def __call__(self): - pass - - class FailingCall(object): - def __call__(self, path): - raise NotImplementedError('call') - - class FailingNumber(object): - def __int__(self): - raise NotImplementedError('int') - - cb.append("> Output") - cb.append(">> OutputSetattr") - ee('del sys.stdout.softspace') - number_test('sys.stdout.softspace = %s', unsigned=True) - number_test('sys.stderr.softspace = %s', unsigned=True) - ee('assert sys.stdout.isatty()==False') - ee('assert sys.stdout.seekable()==False') - ee('sys.stdout.close()') - ee('sys.stdout.flush()') - ee('assert sys.stderr.isatty()==False') - ee('assert sys.stderr.seekable()==False') - ee('sys.stderr.close()') - ee('sys.stderr.flush()') - ee('sys.stdout.attr = None') - cb.append(">> OutputWrite") - ee('assert sys.stdout.writable()==True') - ee('assert sys.stdout.readable()==False') - ee('assert sys.stderr.writable()==True') - ee('assert sys.stderr.readable()==False') - ee('assert sys.stdout.closed()==False') - ee('assert sys.stderr.closed()==False') - ee('assert sys.stdout.errors=="strict"') - ee('assert sys.stderr.errors=="strict"') - ee('assert sys.stdout.encoding==sys.stderr.encoding') - ee('sys.stdout.write(None)') - cb.append(">> OutputWriteLines") - ee('sys.stdout.writelines(None)') - ee('sys.stdout.writelines([1])') - iter_test('sys.stdout.writelines(%s)') - cb.append("> VimCommand") - stringtochars_test('vim.command(%s)') - ee('vim.command("", 2)') - #! Not checked: vim->python exceptions translating: checked later - cb.append("> VimToPython") - #! Not checked: everything: needs errors in internal python functions - cb.append("> VimEval") - stringtochars_test('vim.eval(%s)') - ee('vim.eval("", FailingTrue())') - #! Not checked: everything: needs errors in internal python functions - cb.append("> VimEvalPy") - stringtochars_test('vim.bindeval(%s)') - ee('vim.eval("", 2)') - #! Not checked: vim->python exceptions translating: checked later - cb.append("> VimStrwidth") - stringtochars_test('vim.strwidth(%s)') - cb.append("> VimForeachRTP") - ee('vim.foreach_rtp(None)') - ee('vim.foreach_rtp(NoArgsCall())') - ee('vim.foreach_rtp(FailingCall())') - ee('vim.foreach_rtp(int, 2)') - cb.append('> import') - old_rtp = vim.options['rtp'] - vim.options['rtp'] = os.getcwd().replace('\\', '\\\\').replace(',', '\\,') - ee('import xxx_no_such_module_xxx') - ee('import failing_import') - ee('import failing') - vim.options['rtp'] = old_rtp - del old_rtp - cb.append("> Options") - cb.append(">> OptionsItem") - ee('vim.options["abcQ"]') - ee('vim.options[""]') - stringtochars_test('vim.options[%s]') - cb.append(">> OptionsContains") - stringtochars_test('%s in vim.options') - cb.append("> Dictionary") - cb.append(">> DictionaryConstructor") - ee('vim.Dictionary("abcI")') - ##! Not checked: py_dict_alloc failure - cb.append(">> DictionarySetattr") - ee('del d.locked') - ee('d.locked = FailingTrue()') - ee('vim.vvars.locked = False') - ee('d.scope = True') - ee('d.xxx = True') - cb.append(">> _DictionaryItem") - ee('d.get("a", 2, 3)') - stringtochars_test('d.get(%s)') - ee('d.pop("a")') - ee('dl.pop("a")') - cb.append(">> DictionaryContains") - ee('"" in d') - ee('0 in d') - cb.append(">> DictionaryIterNext") - ee('for i in ned: ned["a"] = 1') - del i - cb.append(">> DictionaryAssItem") - ee('dl["b"] = 1') - stringtochars_test('d[%s] = 1') - convertfrompyobject_test('d["a"] = %s') - cb.append(">> DictionaryUpdate") - cb.append(">>> kwargs") - cb.append(">>> iter") - ee('d.update(FailingMapping())') - ee('d.update([FailingIterNext()])') - ee('d.update([FailingIterNextN(1)])') - iter_test('d.update(%s)') - convertfrompyobject_test('d.update(%s)') - stringtochars_test('d.update(((%s, 0),))') - convertfrompyobject_test('d.update((("a", %s),))') - cb.append(">> DictionaryPopItem") - ee('d.popitem(1, 2)') - cb.append(">> DictionaryHasKey") - ee('d.has_key()') - cb.append("> List") - cb.append(">> ListConstructor") - ee('vim.List(1, 2)') - ee('vim.List(a=1)') - iter_test('vim.List(%s)') - convertfrompyobject_test('vim.List([%s])') - cb.append(">> ListItem") - ee('l[1000]') - cb.append(">> ListAssItem") - ee('ll[1] = 2') - ee('l[1000] = 3') - cb.append(">> ListAssSlice") - ee('ll[1:100] = "abcJ"') - iter_test('l[:] = %s') - ee('nel[1:10:2] = "abcK"') - cb.append(repr(tuple(nel))) - ee('nel[1:10:2] = "a"') - cb.append(repr(tuple(nel))) - ee('nel[1:1:-1] = "a"') - cb.append(repr(tuple(nel))) - ee('nel[:] = FailingIterNextN(2)') - cb.append(repr(tuple(nel))) - convertfrompyobject_test('l[:] = [%s]') - cb.append(">> ListConcatInPlace") - iter_test('l.extend(%s)') - convertfrompyobject_test('l.extend([%s])') - cb.append(">> ListSetattr") - ee('del l.locked') - ee('l.locked = FailingTrue()') - ee('l.xxx = True') - cb.append("> Function") - cb.append(">> FunctionConstructor") - cb.append(">>> FunctionConstructor") - ee('vim.Function("123")') - ee('vim.Function("xxx_non_existent_function_xxx")') - ee('vim.Function("xxx#non#existent#function#xxx")') - ee('vim.Function("xxx_non_existent_function_xxx2", args=[])') - ee('vim.Function("xxx_non_existent_function_xxx3", self={})') - ee('vim.Function("xxx_non_existent_function_xxx4", args=[], self={})') - cb.append(">>> FunctionNew") - ee('vim.Function("tr", self="abcFuncSelf")') - ee('vim.Function("tr", args=427423)') - ee('vim.Function("tr", self="abcFuncSelf2", args="abcFuncArgs2")') - ee('vim.Function(self="abcFuncSelf2", args="abcFuncArgs2")') - ee('vim.Function("tr", "", self="abcFuncSelf2", args="abcFuncArgs2")') - ee('vim.Function("tr", "")') - cb.append(">> FunctionCall") - convertfrompyobject_test('f(%s)') - convertfrompymapping_test('fd(self=%s)') - cb.append("> TabPage") - cb.append(">> TabPageAttr") - ee('vim.current.tabpage.xxx') - cb.append("> TabList") - cb.append(">> TabListItem") - ee('vim.tabpages[1000]') - cb.append("> Window") - cb.append(">> WindowAttr") - ee('vim.current.window.xxx') - cb.append(">> WindowSetattr") - ee('vim.current.window.buffer = 0') - ee('vim.current.window.cursor = (100000000, 100000000)') - ee('vim.current.window.cursor = True') - number_test('vim.current.window.height = %s', unsigned=True) - number_test('vim.current.window.width = %s', unsigned=True) - ee('vim.current.window.xxxxxx = True') - cb.append("> WinList") - cb.append(">> WinListItem") - ee('vim.windows[1000]') - cb.append("> Buffer") - cb.append(">> StringToLine (indirect)") - ee('vim.current.buffer[0] = "\\na"') - ee('vim.current.buffer[0] = b"\\na"') - cb.append(">> SetBufferLine (indirect)") - ee('vim.current.buffer[0] = True') - cb.append(">> SetBufferLineList (indirect)") - ee('vim.current.buffer[:] = True') - ee('vim.current.buffer[:] = ["\\na", "bc"]') - cb.append(">> InsertBufferLines (indirect)") - ee('vim.current.buffer.append(None)') - ee('vim.current.buffer.append(["\\na", "bc"])') - ee('vim.current.buffer.append("\\nbc")') - cb.append(">> RBItem") - ee('vim.current.buffer[100000000]') - cb.append(">> RBAsItem") - ee('vim.current.buffer[100000000] = ""') - cb.append(">> BufferAttr") - ee('vim.current.buffer.xxx') - cb.append(">> BufferSetattr") - ee('vim.current.buffer.name = True') - ee('vim.current.buffer.xxx = True') - cb.append(">> BufferMark") - ee('vim.current.buffer.mark(0)') - ee('vim.current.buffer.mark("abcM")') - ee('vim.current.buffer.mark("!")') - cb.append(">> BufferRange") - ee('vim.current.buffer.range(1, 2, 3)') - cb.append("> BufMap") - cb.append(">> BufMapItem") - ee('vim.buffers[100000000]') - number_test('vim.buffers[%s]', natural=True) - cb.append("> Current") - cb.append(">> CurrentGetattr") - ee('vim.current.xxx') - cb.append(">> CurrentSetattr") - ee('vim.current.line = True') - ee('vim.current.buffer = True') - ee('vim.current.window = True') - ee('vim.current.tabpage = True') - ee('vim.current.xxx = True') - del d - del ned - del dl - del l - del ll - del nel - del f - del fd - del fdel - del subexpr_test - del stringtochars_test - del Mapping - del convertfrompyobject_test - del convertfrompymapping_test - del iter_test - del number_test - del FailingTrue - del FailingIter - del FailingIterNext - del FailingIterNextN - del FailingMapping - del FailingMappingKey - del FailingList - del NoArgsCall - del FailingCall - del FailingNumber -EOF -:delfunction F -:" -:" Test import -py3 << trim EOF - sys.path.insert(0, os.path.join(os.getcwd(), 'python_before')) - sys.path.append(os.path.join(os.getcwd(), 'python_after')) - vim.options['rtp'] = os.getcwd().replace(',', '\\,').replace('\\', '\\\\') - l = [] - def callback(path): - l.append(os.path.relpath(path)) - vim.foreach_rtp(callback) - cb.append(repr(l)) - del l - def callback(path): - return os.path.relpath(path) - cb.append(repr(vim.foreach_rtp(callback))) - del callback - from module import dir as d - from modulex import ddir - cb.append(d + ',' + ddir) - import before - cb.append(before.dir) - import after - cb.append(after.dir) - import topmodule as tm - import topmodule.submodule as tms - import topmodule.submodule.subsubmodule.subsubsubmodule as tmsss - cb.append(tm.__file__.replace(os.path.sep, '/')[-len('modulex/topmodule/__init__.py'):]) - cb.append(tms.__file__.replace(os.path.sep, '/')[-len('modulex/topmodule/submodule/__init__.py'):]) - cb.append(tmsss.__file__.replace(os.path.sep, '/')[-len('modulex/topmodule/submodule/subsubmodule/subsubsubmodule.py'):]) - del before - del after - del d - del ddir - del tm - del tms - del tmsss -EOF -:" -:" Test exceptions -:fun Exe(e) -: execute a:e -:endfun -py3 << trim EOF - Exe = vim.bindeval('function("Exe")') - ee('vim.command("throw \'abcN\'")') - ee('Exe("throw \'def\'")') - ee('vim.eval("Exe(\'throw \'\'ghi\'\'\')")') - ee('vim.eval("Exe(\'echoerr \'\'jkl\'\'\')")') - ee('vim.eval("Exe(\'xxx_non_existent_command_xxx\')")') - ee('vim.eval("xxx_unknown_function_xxx()")') - ee('vim.bindeval("Exe(\'xxx_non_existent_command_xxx\')")') - del Exe -EOF -:delfunction Exe -:" -:" Regression: interrupting vim.command propagates to next vim.command -py3 << trim EOF - def test_keyboard_interrupt(): - try: - vim.command('while 1 | endwhile') - except KeyboardInterrupt: - cb.append('Caught KeyboardInterrupt') - except Exception: - cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info())) - else: - cb.append('!!!!!!!! No exception') - try: - vim.command('$ put =\'Running :put\'') - except KeyboardInterrupt: - cb.append('!!!!!!!! Caught KeyboardInterrupt') - except Exception: - cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info())) - else: - cb.append('No exception') -EOF -:debuggreedy -:call inputsave() -:call feedkeys("s\ns\ns\ns\nq\n") -:redir => output -:debug silent! py3 test_keyboard_interrupt() -:redir END -:0 debuggreedy -:call inputrestore() -:silent $put =output -:unlet output -:py3 del test_keyboard_interrupt -:" -:" Cleanup -py3 << trim EOF - del cb - del ee - del emsg - del sys - del os - del vim -EOF -:endfun -:" -:fun RunTest() -:let checkrefs = !empty($PYTHONDUMPREFS) -:let start = getline(1, '$') -:for i in range(checkrefs ? 10 : 1) -: if i != 0 -: %d _ -: call setline(1, start) -: endif -: call Test() -: if i == 0 -: let result = getline(1, '$') -: endif -:endfor -:if checkrefs -: %d _ -: call setline(1, result) -:endif -:endfun -:" -:call RunTest() -:delfunction RunTest -:delfunction Test -:call garbagecollect(1) -:" -:/^start:/,$wq! test.out -:/^start:/,$w! test.out -:" vim: et ts=4 isk-=\: -:while getchar(0) isnot 0|endwhile -ENDTEST - -start: diff --git a/src/testdir/test87.ok b/src/testdir/test87.ok deleted file mode 100644 index c42acdf386..0000000000 --- a/src/testdir/test87.ok +++ /dev/null @@ -1,1445 +0,0 @@ -start: -[1, 'as''d', [1, 2, function('strlen'), {'a': 1}]] -[1, 2, function('strlen'), {'a': 1}] -Vim(put):E684: -[0, 'as''d', [1, 2, function('strlen'), {'a': 1}]] -[0, function('strlen'), [1, 2, function('strlen'), {'a': 1}]] -1 -[b'-1', b'0', b'1', b'b', b'f'] -[-1, , , , b'asd'] -[(b'-1', ), (b'0', -1), (b'1', b'asd'), (b'b', ), (b'f', )] -'-1' : {'a': 1} -'0' : -1 -'1' : 'asd' -'b' : [1, 2, function('strlen')] -'f' : function('1') -[0, function('strlen')] -[3] -[1, 2, function('strlen')] -[1, 2, function('strlen')] -1 -'asd' -2 -True -False -True -False -[b'0'] -{'0': -1} -(b'0', -1) -None -[] -[0, 1, 2, 3] -[0, 1, 2, 3] -[0, 1, 3] -[0, 1] -[0, 1] -[0, 1] -[0, 1, 2, 3] -[0, 1, 2, 3] -[0, 2, 3] -[2, 3] -[2, 3] -[2, 3] -[1, 3] -[0, 2] -[0, 1, 2, 3] -['a', 0, 1, 2, 3] -[0, 'b', 2, 3] -[0, 1, 'c'] -[0, 1, 2, 3, 'd'] -[0, 1, 2, 'e', 3] -['f', 2, 3] -[0, 1, 'g', 2, 3] -['h'] -[0, 1, 10, 3, 20, 5, 6, 7] -[0, 1, 2, 3, 20, 5, 10, 7] -[0, 1, 2, 3, 4, 5, 6, 7] -[0, 1, 2, 3, 4, 5, 6, 7] -[0, 1, 2, 3, 4, 5, 6, 7] -l[2] threw vim.error: error:('list is locked',) -[0, 1, 2, 3] -[function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd'] -[function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}] -[function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}, 'New'] -l[1](1, 2, 3):(, error('Vim:E725: Calling dict function without Dictionary: DictNew',)) -f(1, 2, 3):(, error('Vim:E117: Unknown function: New',)) -[0.0, 0.0] -KeyError -TypeError -TypeError -ValueError -TypeError -TypeError -KeyError -KeyError -d : locked:0;scope:0 -dl : locked:1;scope:0 -v: : locked:2;scope:1 -g: : locked:0;scope:2 -d:{'abc2': 1} -dl:{'def': 1} -l : locked:0 -ll : locked:1 -l:[0] -ll:[1] -[0, 1, 2] -['a', 'b'] -['c', 1] -['d', ['e']] -py3eval("None") = v:none -0.0 -"\0": Vim(let):E859: -{"\0": 1}: Vim(let):E859: -undefined_name: Vim(let):NameE -vim: Vim(let):E859: -[1] -[1, 10, 11, 10, 11, 10, 11, 10, 11, 10, 11, 10, 1] -[0, 1, 2, 3] -[2, 3, 4, 5] -[0, 1] -[4, 5] -[2, 3] -[] -[2, 3] -[] -[0, 1, 2, 3, 4, 5] -[0, 1, 2, 3, 4, 5] -[0, 1, 2, 3, 4, 5] -[4, 3] -[0, 2, 4] -[] -Abc -bac -def -bar -jkl -wopts iters equal: 1 -bopts iters equal: 1 ->>> paste - g/w/b:1/0/0 - g/w/b (in):1/0/0 - p/gopts1: False - p/wopts1! KeyError - inv: 2! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1! KeyError - inv: 2! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: 1 - W: 1:1 2:1 3:1 4:1 - B: 1:1 2:1 3:1 4:1 - del wopts3! KeyError - del bopts3! KeyError - G: 1 - W: 1:1 2:1 3:1 4:1 - B: 1:1 2:1 3:1 4:1 ->>> previewheight - g/w/b:1/0/0 - g/w/b (in):1/0/0 - p/gopts1: 12 - inv: 'a'! TypeError - p/wopts1! KeyError - inv: 'a'! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1! KeyError - inv: 'a'! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: 5 - W: 1:5 2:5 3:5 4:5 - B: 1:5 2:5 3:5 4:5 - del wopts3! KeyError - del bopts3! KeyError - G: 5 - W: 1:5 2:5 3:5 4:5 - B: 1:5 2:5 3:5 4:5 ->>> operatorfunc - g/w/b:1/0/0 - g/w/b (in):1/0/0 - p/gopts1: b'' - inv: 2! TypeError - p/wopts1! KeyError - inv: 2! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1! KeyError - inv: 2! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: 'A' - W: 1:'A' 2:'A' 3:'A' 4:'A' - B: 1:'A' 2:'A' 3:'A' 4:'A' - del wopts3! KeyError - del bopts3! KeyError - G: 'A' - W: 1:'A' 2:'A' 3:'A' 4:'A' - B: 1:'A' 2:'A' 3:'A' 4:'A' ->>> number - g/w/b:0/1/0 - g/w/b (in):0/1/0 - p/gopts1! KeyError - inv: 0! KeyError - gopts1! KeyError - p/wopts1: False - p/bopts1! KeyError - inv: 0! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: 0 - W: 1:1 2:1 3:0 4:0 - B: 1:1 2:1 3:0 4:0 - del wopts3! ValueError - del bopts3! KeyError - G: 0 - W: 1:1 2:1 3:0 4:0 - B: 1:1 2:1 3:0 4:0 ->>> numberwidth - g/w/b:0/1/0 - g/w/b (in):0/1/0 - p/gopts1! KeyError - inv: -100! KeyError - gopts1! KeyError - p/wopts1: 8 - inv: -100! error - p/bopts1! KeyError - inv: -100! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: 8 - W: 1:3 2:5 3:2 4:8 - B: 1:3 2:5 3:2 4:8 - del wopts3! ValueError - del bopts3! KeyError - G: 8 - W: 1:3 2:5 3:2 4:8 - B: 1:3 2:5 3:2 4:8 ->>> colorcolumn - g/w/b:0/1/0 - g/w/b (in):0/1/0 - p/gopts1! KeyError - inv: 'abc4'! KeyError - gopts1! KeyError - p/wopts1: b'' - inv: 'abc4'! error - p/bopts1! KeyError - inv: 'abc4'! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: '' - W: 1:'+2' 2:'+3' 3:'+1' 4:'' - B: 1:'+2' 2:'+3' 3:'+1' 4:'' - del wopts3! ValueError - del bopts3! KeyError - G: '' - W: 1:'+2' 2:'+3' 3:'+1' 4:'' - B: 1:'+2' 2:'+3' 3:'+1' 4:'' ->>> statusline - g/w/b:1/1/0 - g/w/b (in):1/1/0 - p/gopts1: b'' - inv: 0! TypeError - p/wopts1: None - inv: 0! TypeError - p/bopts1! KeyError - inv: 0! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: '1' - W: 1:'2' 2:'4' 3:'1' 4:'1' - B: 1:'2' 2:'4' 3:'1' 4:'1' - del bopts3! KeyError - G: '1' - W: 1:'2' 2:'1' 3:'1' 4:'1' - B: 1:'2' 2:'1' 3:'1' 4:'1' ->>> autoindent - g/w/b:0/0/1 - g/w/b (in):0/0/1 - p/gopts1! KeyError - inv: 2! KeyError - gopts1! KeyError - p/wopts1! KeyError - inv: 2! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1: False - G: 0 - W: 1:0 2:1 3:0 4:1 - B: 1:0 2:1 3:0 4:1 - del wopts3! KeyError - del bopts3! ValueError - G: 0 - W: 1:0 2:1 3:0 4:1 - B: 1:0 2:1 3:0 4:1 ->>> shiftwidth - g/w/b:0/0/1 - g/w/b (in):0/0/1 - p/gopts1! KeyError - inv: 3! KeyError - gopts1! KeyError - p/wopts1! KeyError - inv: 3! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1: 8 - G: 8 - W: 1:0 2:2 3:8 4:1 - B: 1:0 2:2 3:8 4:1 - del wopts3! KeyError - del bopts3! ValueError - G: 8 - W: 1:0 2:2 3:8 4:1 - B: 1:0 2:2 3:8 4:1 ->>> omnifunc - g/w/b:0/0/1 - g/w/b (in):0/0/1 - p/gopts1! KeyError - inv: 1! KeyError - gopts1! KeyError - p/wopts1! KeyError - inv: 1! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1: b'' - inv: 1! TypeError - G: '' - W: 1:'A' 2:'B' 3:'' 4:'C' - B: 1:'A' 2:'B' 3:'' 4:'C' - del wopts3! KeyError - del bopts3! ValueError - G: '' - W: 1:'A' 2:'B' 3:'' 4:'C' - B: 1:'A' 2:'B' 3:'' 4:'C' ->>> preserveindent - g/w/b:0/0/1 - g/w/b (in):0/0/1 - p/gopts1! KeyError - inv: 2! KeyError - gopts1! KeyError - p/wopts1! KeyError - inv: 2! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1: False - G: 0 - W: 1:0 2:1 3:0 4:1 - B: 1:0 2:1 3:0 4:1 - del wopts3! KeyError - del bopts3! ValueError - G: 0 - W: 1:0 2:1 3:0 4:1 - B: 1:0 2:1 3:0 4:1 ->>> path - g/w/b:1/0/1 - g/w/b (in):1/0/1 - p/gopts1: b'.,..,,' - inv: 0! TypeError - p/wopts1! KeyError - inv: 0! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1: None - inv: 0! TypeError - G: '.,,' - W: 1:'.,,' 2:',,' 3:'.,,' 4:'.' - B: 1:'.,,' 2:',,' 3:'.,,' 4:'.' - del wopts3! KeyError - G: '.,,' - W: 1:'.,,' 2:',,' 3:'.,,' 4:'.,,' - B: 1:'.,,' 2:',,' 3:'.,,' 4:'.,,' -First line -First line -def -First line -Second line -Third line -(7, 2) - -baz -bar -Second line -Third line -foo -1:BufFilePre:1 -1:BufFilePost:1 -testdir/foo -5:BufFilePre:5 -5:BufFilePost:5 -testdir/bar -1:BufFilePre:1 -1:BufFilePost:1 -testdir/test87.in -valid: b:False, cb:True -i: -i2: -i: -i3: -1:= -8:= -9:= -10:= -4 -i4: -i4: -StopIteration -Number of tabs: 4 -Current tab pages: - (1): 1 windows, current is - Windows: - (1): displays buffer ; cursor is at (37, 0) - (2): 1 windows, current is - Windows: - (1): displays buffer ; cursor is at (1, 0) - (3): 2 windows, current is - Windows: - (1): displays buffer ; cursor is at (1, 0) - (2): displays buffer ; cursor is at (1, 0) - (4): 4 windows, current is - Windows: - (1): displays buffer ; cursor is at (1, 0) - (2): displays buffer ; cursor is at (1, 0) - (3): displays buffer ; cursor is at (1, 0) - (4): displays buffer ; cursor is at (1, 0) -Number of windows in current tab page: 4 -Current tab page: -Current window: : is -Current buffer: : is is -ValueError at assigning foreign tab window -Type error at assigning None to vim.current.window -Type error at assigning None to vim.current.tabpage -Type error at assigning None to vim.current.buffer -Current tab page: -Current window: -Current buffer: -Current line: 'Type error at assigning None to vim.current.buffer' -w.valid: [True, False] -t.valid: [True, False, True, False] -vim.vars:Dictionary:True -vim.options:Options:True -vim.bindeval("{}"):Dictionary:True -vim.bindeval("[]"):List:True -vim.bindeval("function('tr')"):Function:True -vim.current.buffer:Buffer:True -vim.current.range:Range:True -vim.current.window:Window:True -vim.current.tabpage:TabPage:True -current:__dir__,buffer,line,range,tabpage,window -buffer:__dir__,append,mark,name,number,options,range,valid,vars -window:__dir__,buffer,col,cursor,height,number,options,row,tabpage,valid,vars,width -tabpage:__dir__,number,valid,vars,window,windows -range:__dir__,append,end,start -dictionary:__dir__,get,has_key,items,keys,locked,pop,popitem,scope,update,values -list:__dir__,extend,locked -function:__dir__,args,auto_rebind,self,softspace -output:__dir__,close,closed,flush,isatty,readable,seekable,softspace,writable,write,writelines -{} -{'a': 1} -{'a': 1} -[] -['a', 'b', 'c', '7'] -function('tr') -function('tr', [123, 3, 4]) -function('tr') -function('tr', {}) -function('tr', [123, 3, 4], {}) -auto_rebind -function('tr') -function('tr', [123, 3, 4]) -function('tr') -function('tr', {}) -function('tr', [123, 3, 4], {}) -a: -pa1: -pa2: -pa3: -pa4: -sa: -psa1: -psa2: -psa3: -psa4: -psa5: -psa6: -psa7: -psa8: -psa9: -psaA: -psaB: -psaC: -psar: -s(a): function('Args') -s(pa1): function('Args', ['abcArgsPA1']) -s(pa2): function('Args') -s(pa3): function('Args', ['abcArgsPA3'], {'abcSelfPA3': 'abcSelfPA3Val'}) -s(pa4): function('Args', {'abcSelfPA4': 'abcSelfPA4Val'}) -s(sa): function('SelfArgs') -s(psa1): function('SelfArgs', ['abcArgsPSA1']) -s(psa2): function('SelfArgs') -s(psa3): function('SelfArgs', ['abcArgsPSA3'], {'abcSelfPSA3': 'abcSelfPSA3Val'}) -s(psa4): function('SelfArgs', {'abcSelfPSA4': 'abcSelfPSA4Val'}) -s(psa5): function('SelfArgs', {'abcSelfPSA5': 'abcSelfPSA5Val'}) -s(psa6): function('SelfArgs', ['abcArgsPSA6'], {'abcSelfPSA6': 'abcSelfPSA6Val'}) -s(psa7): function('SelfArgs', ['abcArgsPSA7']) -s(psa8): function('SelfArgs') -s(psa9): function('SelfArgs', {'abcSelfPSA9': 'abcSelfPSA9Val'}) -s(psaA): function('SelfArgs', ['abcArgsPSAA'], {'abcSelfPSAA': 'abcSelfPSAAVal'}) -s(psaB): function('SelfArgs', ['abcArgsPSAB']) -s(psaC): function('SelfArgs') -d.sa(): [[], {'f': function('SelfArgs')}] -d.psa1(): [['abcArgsPSA1'], {'f': function('SelfArgs', ['abcArgsPSA1'])}] -d.psa2(): [[], {'f': function('SelfArgs')}] -d.psa3(): [['abcArgsPSA3'], {'abcSelfPSA3': 'abcSelfPSA3Val'}] -d.psa4(): [[], {'abcSelfPSA4': 'abcSelfPSA4Val'}] -d.psa5(): [[], {'abcSelfPSA5': 'abcSelfPSA5Val'}] -d.psa6(): [['abcArgsPSA6'], {'abcSelfPSA6': 'abcSelfPSA6Val'}] -d.psa7(): [['abcArgsPSA7'], {'f': function('SelfArgs', ['abcArgsPSA7'])}] -d.psa8(): [[], {'f': function('SelfArgs')}] -d.psa9(): [[], {'f': function('SelfArgs', {'abcSelfPSA9': 'abcSelfPSA9Val'})}] -d.psaA(): [['abcArgsPSAA'], {'f': function('SelfArgs', ['abcArgsPSAA'], {'abcSelfPSAA': 'abcSelfPSAAVal'})}] -d.psaB(): [['abcArgsPSAB'], {'f': function('SelfArgs', ['abcArgsPSAB'])}] -d.psaC(): [[], {'f': function('SelfArgs')}] -a(): !result: [] -pa1(): !result: ['abcArgsPA1'] -pa2(): !result: [] -pa3(): !result: ['abcArgsPA3'] -pa4(): !result: [] -sa(): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) -psa1(): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) -psa2(): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) -psa3(): !result: [['abcArgsPSA3'], {'abcSelfPSA3': 'abcSelfPSA3Val'}] -psa4(): !result: [[], {'abcSelfPSA4': 'abcSelfPSA4Val'}] -a(42, 43): !result: [42, 43] -pa1(42, 43): !result: ['abcArgsPA1', 42, 43] -pa2(42, 43): !result: [42, 43] -pa3(42, 43): !result: ['abcArgsPA3', 42, 43] -pa4(42, 43): !result: [42, 43] -sa(42, 43): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) -psa1(42, 43): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) -psa2(42, 43): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) -psa3(42, 43): !result: [['abcArgsPSA3', 42, 43], {'abcSelfPSA3': 'abcSelfPSA3Val'}] -psa4(42, 43): !result: [[42, 43], {'abcSelfPSA4': 'abcSelfPSA4Val'}] -a(42, self={"20": 1}): !result: [42] -pa1(42, self={"20": 1}): !result: ['abcArgsPA1', 42] -pa2(42, self={"20": 1}): !result: [42] -pa3(42, self={"20": 1}): !result: ['abcArgsPA3', 42] -pa4(42, self={"20": 1}): !result: [42] -sa(42, self={"20": 1}): !result: [[42], {'20': 1}] -psa1(42, self={"20": 1}): !result: [['abcArgsPSA1', 42], {'20': 1}] -psa2(42, self={"20": 1}): !result: [[42], {'20': 1}] -psa3(42, self={"20": 1}): !result: [['abcArgsPSA3', 42], {'20': 1}] -psa4(42, self={"20": 1}): !result: [[42], {'20': 1}] -a(self={"20": 1}): !result: [] -pa1(self={"20": 1}): !result: ['abcArgsPA1'] -pa2(self={"20": 1}): !result: [] -pa3(self={"20": 1}): !result: ['abcArgsPA3'] -pa4(self={"20": 1}): !result: [] -sa(self={"20": 1}): !result: [[], {'20': 1}] -psa1(self={"20": 1}): !result: [['abcArgsPSA1'], {'20': 1}] -psa2(self={"20": 1}): !result: [[], {'20': 1}] -psa3(self={"20": 1}): !result: [['abcArgsPSA3'], {'20': 1}] -psa4(self={"20": 1}): !result: [[], {'20': 1}] -a.args: None -pa1.args: ['abcArgsPA1'] -pa2.args: None -pa3.args: ['abcArgsPA3'] -pa4.args: None -sa.args: None -psa1.args: ['abcArgsPSA1'] -psa2.args: None -psa3.args: ['abcArgsPSA3'] -psa4.args: None -a.self: None -pa1.self: None -pa2.self: None -pa3.self: {'abcSelfPA3': 'abcSelfPA3Val'} -pa4.self: {'abcSelfPA4': 'abcSelfPA4Val'} -sa.self: None -psa1.self: None -psa2.self: None -psa3.self: {'abcSelfPSA3': 'abcSelfPSA3Val'} -psa4.self: {'abcSelfPSA4': 'abcSelfPSA4Val'} -a.name: 'Args' -pa1.name: 'Args' -pa2.name: 'Args' -pa3.name: 'Args' -pa4.name: 'Args' -sa.name: 'SelfArgs' -psa1.name: 'SelfArgs' -psa2.name: 'SelfArgs' -psa3.name: 'SelfArgs' -psa4.name: 'SelfArgs' -a.auto_rebind: 1 -pa1.auto_rebind: 1 -pa2.auto_rebind: 1 -pa3.auto_rebind: 0 -pa4.auto_rebind: 0 -sa.auto_rebind: 1 -psa1.auto_rebind: 1 -psa2.auto_rebind: 1 -psa3.auto_rebind: 0 -psa4.auto_rebind: 0 -psa5.auto_rebind: 0 -psa6.auto_rebind: 0 -psa7.auto_rebind: 1 -psa8.auto_rebind: 1 -psa9.auto_rebind: 1 -psaA.auto_rebind: 1 -psaB.auto_rebind: 1 -psaC.auto_rebind: 1 -' -abcdef -Error detected while processing function RunTest[]..Test: -line : -abcdef -abcA -line : -abcB' -['a', 'dup_a'] -['a', 'a'] -['a', 'b', 'c', 'C'] -[2, 2] -[2, 2] -1 -1 -function('Put') -b'testdir' -test87.in -b'src' -testdir/test87.in -b'testdir' -test87.in -> Output ->> OutputSetattr -del sys.stdout.softspace:(, AttributeError('cannot delete OutputObject attributes',)) ->>> Testing NumberToLong using sys.stdout.softspace = %s -sys.stdout.softspace = []:(, TypeError('expected int() or something supporting coercing to int(), but got list',)) -sys.stdout.softspace = None:(, TypeError('expected int() or something supporting coercing to int(), but got NoneType',)) -sys.stdout.softspace = -1:(, ValueError('number must be greater or equal to zero',)) -<<< Finished ->>> Testing NumberToLong using sys.stderr.softspace = %s -sys.stderr.softspace = []:(, TypeError('expected int() or something supporting coercing to int(), but got list',)) -sys.stderr.softspace = None:(, TypeError('expected int() or something supporting coercing to int(), but got NoneType',)) -sys.stderr.softspace = -1:(, ValueError('number must be greater or equal to zero',)) -<<< Finished -assert sys.stdout.isatty()==False:NOT FAILED -assert sys.stdout.seekable()==False:NOT FAILED -sys.stdout.close():NOT FAILED -sys.stdout.flush():NOT FAILED -assert sys.stderr.isatty()==False:NOT FAILED -assert sys.stderr.seekable()==False:NOT FAILED -sys.stderr.close():NOT FAILED -sys.stderr.flush():NOT FAILED -sys.stdout.attr = None:(, AttributeError('invalid attribute: attr',)) ->> OutputWrite -assert sys.stdout.writable()==True:NOT FAILED -assert sys.stdout.readable()==False:NOT FAILED -assert sys.stderr.writable()==True:NOT FAILED -assert sys.stderr.readable()==False:NOT FAILED -assert sys.stdout.closed()==False:NOT FAILED -assert sys.stderr.closed()==False:NOT FAILED -assert sys.stdout.errors=="strict":NOT FAILED -assert sys.stderr.errors=="strict":NOT FAILED -assert sys.stdout.encoding==sys.stderr.encoding:NOT FAILED -sys.stdout.write(None):(, TypeError("Can't convert 'NoneType' object to str implicitly",)) ->> OutputWriteLines -sys.stdout.writelines(None):(, TypeError("'NoneType' object is not iterable",)) -sys.stdout.writelines([1]):(, TypeError("Can't convert 'int' object to str implicitly",)) ->>> Testing *Iter* using sys.stdout.writelines(%s) -sys.stdout.writelines(FailingIter()):(, NotImplementedError('iter',)) -sys.stdout.writelines(FailingIterNext()):(, NotImplementedError('next',)) -<<< Finished -> VimCommand ->>> Testing StringToChars using vim.command(%s) -vim.command(1):(, TypeError('expected bytes() or str() instance, but got int',)) -vim.command(b"\0"):(, TypeError('expected bytes with no null',)) -vim.command("\0"):(, TypeError('expected bytes with no null',)) -<<< Finished -vim.command("", 2):(, TypeError('command() takes exactly one argument (2 given)',)) -> VimToPython -> VimEval ->>> Testing StringToChars using vim.eval(%s) -vim.eval(1):(, TypeError('expected bytes() or str() instance, but got int',)) -vim.eval(b"\0"):(, TypeError('expected bytes with no null',)) -vim.eval("\0"):(, TypeError('expected bytes with no null',)) -<<< Finished -vim.eval("", FailingTrue()):(, TypeError('function takes exactly 1 argument (2 given)',)) -> VimEvalPy ->>> Testing StringToChars using vim.bindeval(%s) -vim.bindeval(1):(, TypeError('expected bytes() or str() instance, but got int',)) -vim.bindeval(b"\0"):(, TypeError('expected bytes with no null',)) -vim.bindeval("\0"):(, TypeError('expected bytes with no null',)) -<<< Finished -vim.eval("", 2):(, TypeError('function takes exactly 1 argument (2 given)',)) -> VimStrwidth ->>> Testing StringToChars using vim.strwidth(%s) -vim.strwidth(1):(, TypeError('expected bytes() or str() instance, but got int',)) -vim.strwidth(b"\0"):(, TypeError('expected bytes with no null',)) -vim.strwidth("\0"):(, TypeError('expected bytes with no null',)) -<<< Finished -> VimForeachRTP -vim.foreach_rtp(None):(, TypeError("'NoneType' object is not callable",)) -vim.foreach_rtp(NoArgsCall()):(, TypeError('__call__() takes exactly 1 positional argument (2 given)',)) -vim.foreach_rtp(FailingCall()):(, NotImplementedError('call',)) -vim.foreach_rtp(int, 2):(, TypeError('foreach_rtp() takes exactly one argument (2 given)',)) -> import -import xxx_no_such_module_xxx:(, ImportError('No module named xxx_no_such_module_xxx',)) -import failing_import:(, ImportError()) -import failing:(, NotImplementedError()) -> Options ->> OptionsItem -vim.options["abcQ"]:(, KeyError('abcQ',)) -vim.options[""]:(, ValueError('empty keys are not allowed',)) ->>> Testing StringToChars using vim.options[%s] -vim.options[1]:(, TypeError('expected bytes() or str() instance, but got int',)) -vim.options[b"\0"]:(, TypeError('expected bytes with no null',)) -vim.options["\0"]:(, TypeError('expected bytes with no null',)) -<<< Finished ->> OptionsContains ->>> Testing StringToChars using %s in vim.options -1 in vim.options:(, TypeError('expected bytes() or str() instance, but got int',)) -b"\0" in vim.options:(, TypeError('expected bytes with no null',)) -"\0" in vim.options:(, TypeError('expected bytes with no null',)) -<<< Finished -> Dictionary ->> DictionaryConstructor -vim.Dictionary("abcI"):(, ValueError('expected sequence element of size 2, but got sequence of size 1',)) ->> DictionarySetattr -del d.locked:(, AttributeError('cannot delete vim.Dictionary attributes',)) -d.locked = FailingTrue():(, NotImplementedError('bool',)) -vim.vvars.locked = False:(, TypeError('cannot modify fixed dictionary',)) -d.scope = True:(, AttributeError('cannot set attribute scope',)) -d.xxx = True:(, AttributeError('cannot set attribute xxx',)) ->> _DictionaryItem -d.get("a", 2, 3):(, TypeError('function takes at most 2 arguments (3 given)',)) ->>> Testing StringToChars using d.get(%s) -d.get(1):(, TypeError('expected bytes() or str() instance, but got int',)) -d.get(b"\0"):(, TypeError('expected bytes with no null',)) -d.get("\0"):(, TypeError('expected bytes with no null',)) -<<< Finished -d.pop("a"):(, KeyError('a',)) -dl.pop("a"):(, error('dictionary is locked',)) ->> DictionaryContains -"" in d:(, ValueError('empty keys are not allowed',)) -0 in d:(, TypeError('expected bytes() or str() instance, but got int',)) ->> DictionaryIterNext -for i in ned: ned["a"] = 1:(, RuntimeError('hashtab changed during iteration',)) ->> DictionaryAssItem -dl["b"] = 1:(, error('dictionary is locked',)) ->>> Testing StringToChars using d[%s] = 1 -d[1] = 1:(, TypeError('expected bytes() or str() instance, but got int',)) -d[b"\0"] = 1:(, TypeError('expected bytes with no null',)) -d["\0"] = 1:(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d["a"] = {%s : 1} -d["a"] = {1 : 1}:(, TypeError('expected bytes() or str() instance, but got int',)) -d["a"] = {b"\0" : 1}:(, TypeError('expected bytes with no null',)) -d["a"] = {"\0" : 1}:(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d["a"] = {"abcF" : {%s : 1}} -d["a"] = {"abcF" : {1 : 1}}:(, TypeError('expected bytes() or str() instance, but got int',)) -d["a"] = {"abcF" : {b"\0" : 1}}:(, TypeError('expected bytes with no null',)) -d["a"] = {"abcF" : {"\0" : 1}}:(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d["a"] = {"abcF" : Mapping({%s : 1})} -d["a"] = {"abcF" : Mapping({1 : 1})}:(, TypeError('expected bytes() or str() instance, but got int',)) -d["a"] = {"abcF" : Mapping({b"\0" : 1})}:(, TypeError('expected bytes with no null',)) -d["a"] = {"abcF" : Mapping({"\0" : 1})}:(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using d["a"] = {"abcF" : %s} -d["a"] = {"abcF" : FailingIter()}:(, TypeError('unable to convert FailingIter to a Vim structure',)) -d["a"] = {"abcF" : FailingIterNext()}:(, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using d["a"] = {"abcF" : %s} -d["a"] = {"abcF" : None}:NOT FAILED -d["a"] = {"abcF" : {b"": 1}}:(, ValueError('empty keys are not allowed',)) -d["a"] = {"abcF" : {"": 1}}:(, ValueError('empty keys are not allowed',)) -d["a"] = {"abcF" : FailingMapping()}:(, NotImplementedError('keys',)) -d["a"] = {"abcF" : FailingMappingKey()}:(, NotImplementedError('getitem:mappingkey',)) -d["a"] = {"abcF" : FailingNumber()}:(, NotImplementedError('int',)) -<<< Finished ->>> Testing StringToChars using d["a"] = Mapping({%s : 1}) -d["a"] = Mapping({1 : 1}):(, TypeError('expected bytes() or str() instance, but got int',)) -d["a"] = Mapping({b"\0" : 1}):(, TypeError('expected bytes with no null',)) -d["a"] = Mapping({"\0" : 1}):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d["a"] = Mapping({"abcG" : {%s : 1}}) -d["a"] = Mapping({"abcG" : {1 : 1}}):(, TypeError('expected bytes() or str() instance, but got int',)) -d["a"] = Mapping({"abcG" : {b"\0" : 1}}):(, TypeError('expected bytes with no null',)) -d["a"] = Mapping({"abcG" : {"\0" : 1}}):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d["a"] = Mapping({"abcG" : Mapping({%s : 1})}) -d["a"] = Mapping({"abcG" : Mapping({1 : 1})}):(, TypeError('expected bytes() or str() instance, but got int',)) -d["a"] = Mapping({"abcG" : Mapping({b"\0" : 1})}):(, TypeError('expected bytes with no null',)) -d["a"] = Mapping({"abcG" : Mapping({"\0" : 1})}):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using d["a"] = Mapping({"abcG" : %s}) -d["a"] = Mapping({"abcG" : FailingIter()}):(, TypeError('unable to convert FailingIter to a Vim structure',)) -d["a"] = Mapping({"abcG" : FailingIterNext()}):(, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using d["a"] = Mapping({"abcG" : %s}) -d["a"] = Mapping({"abcG" : None}):NOT FAILED -d["a"] = Mapping({"abcG" : {b"": 1}}):(, ValueError('empty keys are not allowed',)) -d["a"] = Mapping({"abcG" : {"": 1}}):(, ValueError('empty keys are not allowed',)) -d["a"] = Mapping({"abcG" : FailingMapping()}):(, NotImplementedError('keys',)) -d["a"] = Mapping({"abcG" : FailingMappingKey()}):(, NotImplementedError('getitem:mappingkey',)) -d["a"] = Mapping({"abcG" : FailingNumber()}):(, NotImplementedError('int',)) -<<< Finished ->>> Testing *Iter* using d["a"] = %s -d["a"] = FailingIter():(, TypeError('unable to convert FailingIter to a Vim structure',)) -d["a"] = FailingIterNext():(, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using d["a"] = %s -d["a"] = None:NOT FAILED -d["a"] = {b"": 1}:(, ValueError('empty keys are not allowed',)) -d["a"] = {"": 1}:(, ValueError('empty keys are not allowed',)) -d["a"] = FailingMapping():(, NotImplementedError('keys',)) -d["a"] = FailingMappingKey():(, NotImplementedError('getitem:mappingkey',)) -d["a"] = FailingNumber():(, NotImplementedError('int',)) -<<< Finished ->> DictionaryUpdate ->>> kwargs ->>> iter -d.update(FailingMapping()):(, NotImplementedError('keys',)) -d.update([FailingIterNext()]):(, NotImplementedError('next',)) -d.update([FailingIterNextN(1)]):(, NotImplementedError('next N',)) ->>> Testing *Iter* using d.update(%s) -d.update(FailingIter()):(, NotImplementedError('iter',)) -d.update(FailingIterNext()):(, NotImplementedError('next',)) -<<< Finished ->>> Testing StringToChars using d.update({%s : 1}) -d.update({1 : 1}):(, TypeError('expected bytes() or str() instance, but got int',)) -d.update({b"\0" : 1}):(, TypeError('expected bytes with no null',)) -d.update({"\0" : 1}):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d.update({"abcF" : {%s : 1}}) -d.update({"abcF" : {1 : 1}}):(, TypeError('expected bytes() or str() instance, but got int',)) -d.update({"abcF" : {b"\0" : 1}}):(, TypeError('expected bytes with no null',)) -d.update({"abcF" : {"\0" : 1}}):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d.update({"abcF" : Mapping({%s : 1})}) -d.update({"abcF" : Mapping({1 : 1})}):(, TypeError('expected bytes() or str() instance, but got int',)) -d.update({"abcF" : Mapping({b"\0" : 1})}):(, TypeError('expected bytes with no null',)) -d.update({"abcF" : Mapping({"\0" : 1})}):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using d.update({"abcF" : %s}) -d.update({"abcF" : FailingIter()}):(, TypeError('unable to convert FailingIter to a Vim structure',)) -d.update({"abcF" : FailingIterNext()}):(, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using d.update({"abcF" : %s}) -d.update({"abcF" : None}):NOT FAILED -d.update({"abcF" : {b"": 1}}):(, ValueError('empty keys are not allowed',)) -d.update({"abcF" : {"": 1}}):(, ValueError('empty keys are not allowed',)) -d.update({"abcF" : FailingMapping()}):(, NotImplementedError('keys',)) -d.update({"abcF" : FailingMappingKey()}):(, NotImplementedError('getitem:mappingkey',)) -d.update({"abcF" : FailingNumber()}):(, NotImplementedError('int',)) -<<< Finished ->>> Testing StringToChars using d.update(Mapping({%s : 1})) -d.update(Mapping({1 : 1})):(, TypeError('expected bytes() or str() instance, but got int',)) -d.update(Mapping({b"\0" : 1})):(, TypeError('expected bytes with no null',)) -d.update(Mapping({"\0" : 1})):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d.update(Mapping({"abcG" : {%s : 1}})) -d.update(Mapping({"abcG" : {1 : 1}})):(, TypeError('expected bytes() or str() instance, but got int',)) -d.update(Mapping({"abcG" : {b"\0" : 1}})):(, TypeError('expected bytes with no null',)) -d.update(Mapping({"abcG" : {"\0" : 1}})):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d.update(Mapping({"abcG" : Mapping({%s : 1})})) -d.update(Mapping({"abcG" : Mapping({1 : 1})})):(, TypeError('expected bytes() or str() instance, but got int',)) -d.update(Mapping({"abcG" : Mapping({b"\0" : 1})})):(, TypeError('expected bytes with no null',)) -d.update(Mapping({"abcG" : Mapping({"\0" : 1})})):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using d.update(Mapping({"abcG" : %s})) -d.update(Mapping({"abcG" : FailingIter()})):(, TypeError('unable to convert FailingIter to a Vim structure',)) -d.update(Mapping({"abcG" : FailingIterNext()})):(, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using d.update(Mapping({"abcG" : %s})) -d.update(Mapping({"abcG" : None})):NOT FAILED -d.update(Mapping({"abcG" : {b"": 1}})):(, ValueError('empty keys are not allowed',)) -d.update(Mapping({"abcG" : {"": 1}})):(, ValueError('empty keys are not allowed',)) -d.update(Mapping({"abcG" : FailingMapping()})):(, NotImplementedError('keys',)) -d.update(Mapping({"abcG" : FailingMappingKey()})):(, NotImplementedError('getitem:mappingkey',)) -d.update(Mapping({"abcG" : FailingNumber()})):(, NotImplementedError('int',)) -<<< Finished ->>> Testing *Iter* using d.update(%s) -d.update(FailingIter()):(, NotImplementedError('iter',)) -d.update(FailingIterNext()):(, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using d.update(%s) -d.update(None):(, TypeError("'NoneType' object is not iterable",)) -d.update({b"": 1}):(, ValueError('empty keys are not allowed',)) -d.update({"": 1}):(, ValueError('empty keys are not allowed',)) -d.update(FailingMapping()):(, NotImplementedError('keys',)) -d.update(FailingMappingKey()):(, NotImplementedError('getitem:mappingkey',)) -d.update(FailingNumber()):(, TypeError("'FailingNumber' object is not iterable",)) -<<< Finished ->>> Testing StringToChars using d.update(((%s, 0),)) -d.update(((1, 0),)):(, TypeError('expected bytes() or str() instance, but got int',)) -d.update(((b"\0", 0),)):(, TypeError('expected bytes with no null',)) -d.update((("\0", 0),)):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d.update((("a", {%s : 1}),)) -d.update((("a", {1 : 1}),)):(, TypeError('expected bytes() or str() instance, but got int',)) -d.update((("a", {b"\0" : 1}),)):(, TypeError('expected bytes with no null',)) -d.update((("a", {"\0" : 1}),)):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d.update((("a", {"abcF" : {%s : 1}}),)) -d.update((("a", {"abcF" : {1 : 1}}),)):(, TypeError('expected bytes() or str() instance, but got int',)) -d.update((("a", {"abcF" : {b"\0" : 1}}),)):(, TypeError('expected bytes with no null',)) -d.update((("a", {"abcF" : {"\0" : 1}}),)):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d.update((("a", {"abcF" : Mapping({%s : 1})}),)) -d.update((("a", {"abcF" : Mapping({1 : 1})}),)):(, TypeError('expected bytes() or str() instance, but got int',)) -d.update((("a", {"abcF" : Mapping({b"\0" : 1})}),)):(, TypeError('expected bytes with no null',)) -d.update((("a", {"abcF" : Mapping({"\0" : 1})}),)):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using d.update((("a", {"abcF" : %s}),)) -d.update((("a", {"abcF" : FailingIter()}),)):(, TypeError('unable to convert FailingIter to a Vim structure',)) -d.update((("a", {"abcF" : FailingIterNext()}),)):(, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using d.update((("a", {"abcF" : %s}),)) -d.update((("a", {"abcF" : None}),)):(, error("failed to add key 'a' to dictionary",)) -d.update((("a", {"abcF" : {b"": 1}}),)):(, ValueError('empty keys are not allowed',)) -d.update((("a", {"abcF" : {"": 1}}),)):(, ValueError('empty keys are not allowed',)) -d.update((("a", {"abcF" : FailingMapping()}),)):(, NotImplementedError('keys',)) -d.update((("a", {"abcF" : FailingMappingKey()}),)):(, NotImplementedError('getitem:mappingkey',)) -d.update((("a", {"abcF" : FailingNumber()}),)):(, NotImplementedError('int',)) -<<< Finished ->>> Testing StringToChars using d.update((("a", Mapping({%s : 1})),)) -d.update((("a", Mapping({1 : 1})),)):(, TypeError('expected bytes() or str() instance, but got int',)) -d.update((("a", Mapping({b"\0" : 1})),)):(, TypeError('expected bytes with no null',)) -d.update((("a", Mapping({"\0" : 1})),)):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d.update((("a", Mapping({"abcG" : {%s : 1}})),)) -d.update((("a", Mapping({"abcG" : {1 : 1}})),)):(, TypeError('expected bytes() or str() instance, but got int',)) -d.update((("a", Mapping({"abcG" : {b"\0" : 1}})),)):(, TypeError('expected bytes with no null',)) -d.update((("a", Mapping({"abcG" : {"\0" : 1}})),)):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d.update((("a", Mapping({"abcG" : Mapping({%s : 1})})),)) -d.update((("a", Mapping({"abcG" : Mapping({1 : 1})})),)):(, TypeError('expected bytes() or str() instance, but got int',)) -d.update((("a", Mapping({"abcG" : Mapping({b"\0" : 1})})),)):(, TypeError('expected bytes with no null',)) -d.update((("a", Mapping({"abcG" : Mapping({"\0" : 1})})),)):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using d.update((("a", Mapping({"abcG" : %s})),)) -d.update((("a", Mapping({"abcG" : FailingIter()})),)):(, TypeError('unable to convert FailingIter to a Vim structure',)) -d.update((("a", Mapping({"abcG" : FailingIterNext()})),)):(, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using d.update((("a", Mapping({"abcG" : %s})),)) -d.update((("a", Mapping({"abcG" : None})),)):(, error("failed to add key 'a' to dictionary",)) -d.update((("a", Mapping({"abcG" : {b"": 1}})),)):(, ValueError('empty keys are not allowed',)) -d.update((("a", Mapping({"abcG" : {"": 1}})),)):(, ValueError('empty keys are not allowed',)) -d.update((("a", Mapping({"abcG" : FailingMapping()})),)):(, NotImplementedError('keys',)) -d.update((("a", Mapping({"abcG" : FailingMappingKey()})),)):(, NotImplementedError('getitem:mappingkey',)) -d.update((("a", Mapping({"abcG" : FailingNumber()})),)):(, NotImplementedError('int',)) -<<< Finished ->>> Testing *Iter* using d.update((("a", %s),)) -d.update((("a", FailingIter()),)):(, TypeError('unable to convert FailingIter to a Vim structure',)) -d.update((("a", FailingIterNext()),)):(, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using d.update((("a", %s),)) -d.update((("a", None),)):(, error("failed to add key 'a' to dictionary",)) -d.update((("a", {b"": 1}),)):(, ValueError('empty keys are not allowed',)) -d.update((("a", {"": 1}),)):(, ValueError('empty keys are not allowed',)) -d.update((("a", FailingMapping()),)):(, NotImplementedError('keys',)) -d.update((("a", FailingMappingKey()),)):(, NotImplementedError('getitem:mappingkey',)) -d.update((("a", FailingNumber()),)):(, NotImplementedError('int',)) -<<< Finished ->> DictionaryPopItem -d.popitem(1, 2):(, TypeError('popitem() takes no arguments (2 given)',)) ->> DictionaryHasKey -d.has_key():(, TypeError('has_key() takes exactly one argument (0 given)',)) -> List ->> ListConstructor -vim.List(1, 2):(, TypeError('function takes at most 1 argument (2 given)',)) -vim.List(a=1):(, TypeError('list constructor does not accept keyword arguments',)) ->>> Testing *Iter* using vim.List(%s) -vim.List(FailingIter()):(, NotImplementedError('iter',)) -vim.List(FailingIterNext()):(, NotImplementedError('next',)) -<<< Finished ->>> Testing StringToChars using vim.List([{%s : 1}]) -vim.List([{1 : 1}]):(, TypeError('expected bytes() or str() instance, but got int',)) -vim.List([{b"\0" : 1}]):(, TypeError('expected bytes with no null',)) -vim.List([{"\0" : 1}]):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using vim.List([{"abcF" : {%s : 1}}]) -vim.List([{"abcF" : {1 : 1}}]):(, TypeError('expected bytes() or str() instance, but got int',)) -vim.List([{"abcF" : {b"\0" : 1}}]):(, TypeError('expected bytes with no null',)) -vim.List([{"abcF" : {"\0" : 1}}]):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using vim.List([{"abcF" : Mapping({%s : 1})}]) -vim.List([{"abcF" : Mapping({1 : 1})}]):(, TypeError('expected bytes() or str() instance, but got int',)) -vim.List([{"abcF" : Mapping({b"\0" : 1})}]):(, TypeError('expected bytes with no null',)) -vim.List([{"abcF" : Mapping({"\0" : 1})}]):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using vim.List([{"abcF" : %s}]) -vim.List([{"abcF" : FailingIter()}]):(, TypeError('unable to convert FailingIter to a Vim structure',)) -vim.List([{"abcF" : FailingIterNext()}]):(, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using vim.List([{"abcF" : %s}]) -vim.List([{"abcF" : None}]):NOT FAILED -vim.List([{"abcF" : {b"": 1}}]):(, ValueError('empty keys are not allowed',)) -vim.List([{"abcF" : {"": 1}}]):(, ValueError('empty keys are not allowed',)) -vim.List([{"abcF" : FailingMapping()}]):(, NotImplementedError('keys',)) -vim.List([{"abcF" : FailingMappingKey()}]):(, NotImplementedError('getitem:mappingkey',)) -vim.List([{"abcF" : FailingNumber()}]):(, NotImplementedError('int',)) -<<< Finished ->>> Testing StringToChars using vim.List([Mapping({%s : 1})]) -vim.List([Mapping({1 : 1})]):(, TypeError('expected bytes() or str() instance, but got int',)) -vim.List([Mapping({b"\0" : 1})]):(, TypeError('expected bytes with no null',)) -vim.List([Mapping({"\0" : 1})]):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using vim.List([Mapping({"abcG" : {%s : 1}})]) -vim.List([Mapping({"abcG" : {1 : 1}})]):(, TypeError('expected bytes() or str() instance, but got int',)) -vim.List([Mapping({"abcG" : {b"\0" : 1}})]):(, TypeError('expected bytes with no null',)) -vim.List([Mapping({"abcG" : {"\0" : 1}})]):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using vim.List([Mapping({"abcG" : Mapping({%s : 1})})]) -vim.List([Mapping({"abcG" : Mapping({1 : 1})})]):(, TypeError('expected bytes() or str() instance, but got int',)) -vim.List([Mapping({"abcG" : Mapping({b"\0" : 1})})]):(, TypeError('expected bytes with no null',)) -vim.List([Mapping({"abcG" : Mapping({"\0" : 1})})]):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using vim.List([Mapping({"abcG" : %s})]) -vim.List([Mapping({"abcG" : FailingIter()})]):(, TypeError('unable to convert FailingIter to a Vim structure',)) -vim.List([Mapping({"abcG" : FailingIterNext()})]):(, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using vim.List([Mapping({"abcG" : %s})]) -vim.List([Mapping({"abcG" : None})]):NOT FAILED -vim.List([Mapping({"abcG" : {b"": 1}})]):(, ValueError('empty keys are not allowed',)) -vim.List([Mapping({"abcG" : {"": 1}})]):(, ValueError('empty keys are not allowed',)) -vim.List([Mapping({"abcG" : FailingMapping()})]):(, NotImplementedError('keys',)) -vim.List([Mapping({"abcG" : FailingMappingKey()})]):(, NotImplementedError('getitem:mappingkey',)) -vim.List([Mapping({"abcG" : FailingNumber()})]):(, NotImplementedError('int',)) -<<< Finished ->>> Testing *Iter* using vim.List([%s]) -vim.List([FailingIter()]):(, TypeError('unable to convert FailingIter to a Vim structure',)) -vim.List([FailingIterNext()]):(, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using vim.List([%s]) -vim.List([None]):NOT FAILED -vim.List([{b"": 1}]):(, ValueError('empty keys are not allowed',)) -vim.List([{"": 1}]):(, ValueError('empty keys are not allowed',)) -vim.List([FailingMapping()]):(, NotImplementedError('keys',)) -vim.List([FailingMappingKey()]):(, NotImplementedError('getitem:mappingkey',)) -vim.List([FailingNumber()]):(, NotImplementedError('int',)) -<<< Finished ->> ListItem -l[1000]:(, IndexError('list index out of range',)) ->> ListAssItem -ll[1] = 2:(, error('list is locked',)) -l[1000] = 3:(, IndexError('list index out of range',)) ->> ListAssSlice -ll[1:100] = "abcJ":(, error('list is locked',)) ->>> Testing *Iter* using l[:] = %s -l[:] = FailingIter():(, NotImplementedError('iter',)) -l[:] = FailingIterNext():(, NotImplementedError('next',)) -<<< Finished -nel[1:10:2] = "abcK":(, ValueError('attempt to assign sequence of size greater than 2 to extended slice',)) -(b'a', b'b', b'c', b'O') -nel[1:10:2] = "a":(, ValueError('attempt to assign sequence of size 1 to extended slice of size 2',)) -(b'a', b'b', b'c', b'O') -nel[1:1:-1] = "a":(, ValueError('attempt to assign sequence of size greater than 0 to extended slice',)) -(b'a', b'b', b'c', b'O') -nel[:] = FailingIterNextN(2):(, NotImplementedError('next N',)) -(b'a', b'b', b'c', b'O') ->>> Testing StringToChars using l[:] = [{%s : 1}] -l[:] = [{1 : 1}]:(, TypeError('expected bytes() or str() instance, but got int',)) -l[:] = [{b"\0" : 1}]:(, TypeError('expected bytes with no null',)) -l[:] = [{"\0" : 1}]:(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using l[:] = [{"abcF" : {%s : 1}}] -l[:] = [{"abcF" : {1 : 1}}]:(, TypeError('expected bytes() or str() instance, but got int',)) -l[:] = [{"abcF" : {b"\0" : 1}}]:(, TypeError('expected bytes with no null',)) -l[:] = [{"abcF" : {"\0" : 1}}]:(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using l[:] = [{"abcF" : Mapping({%s : 1})}] -l[:] = [{"abcF" : Mapping({1 : 1})}]:(, TypeError('expected bytes() or str() instance, but got int',)) -l[:] = [{"abcF" : Mapping({b"\0" : 1})}]:(, TypeError('expected bytes with no null',)) -l[:] = [{"abcF" : Mapping({"\0" : 1})}]:(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using l[:] = [{"abcF" : %s}] -l[:] = [{"abcF" : FailingIter()}]:(, TypeError('unable to convert FailingIter to a Vim structure',)) -l[:] = [{"abcF" : FailingIterNext()}]:(, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using l[:] = [{"abcF" : %s}] -l[:] = [{"abcF" : None}]:NOT FAILED -l[:] = [{"abcF" : {b"": 1}}]:(, ValueError('empty keys are not allowed',)) -l[:] = [{"abcF" : {"": 1}}]:(, ValueError('empty keys are not allowed',)) -l[:] = [{"abcF" : FailingMapping()}]:(, NotImplementedError('keys',)) -l[:] = [{"abcF" : FailingMappingKey()}]:(, NotImplementedError('getitem:mappingkey',)) -l[:] = [{"abcF" : FailingNumber()}]:(, NotImplementedError('int',)) -<<< Finished ->>> Testing StringToChars using l[:] = [Mapping({%s : 1})] -l[:] = [Mapping({1 : 1})]:(, TypeError('expected bytes() or str() instance, but got int',)) -l[:] = [Mapping({b"\0" : 1})]:(, TypeError('expected bytes with no null',)) -l[:] = [Mapping({"\0" : 1})]:(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using l[:] = [Mapping({"abcG" : {%s : 1}})] -l[:] = [Mapping({"abcG" : {1 : 1}})]:(, TypeError('expected bytes() or str() instance, but got int',)) -l[:] = [Mapping({"abcG" : {b"\0" : 1}})]:(, TypeError('expected bytes with no null',)) -l[:] = [Mapping({"abcG" : {"\0" : 1}})]:(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using l[:] = [Mapping({"abcG" : Mapping({%s : 1})})] -l[:] = [Mapping({"abcG" : Mapping({1 : 1})})]:(, TypeError('expected bytes() or str() instance, but got int',)) -l[:] = [Mapping({"abcG" : Mapping({b"\0" : 1})})]:(, TypeError('expected bytes with no null',)) -l[:] = [Mapping({"abcG" : Mapping({"\0" : 1})})]:(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using l[:] = [Mapping({"abcG" : %s})] -l[:] = [Mapping({"abcG" : FailingIter()})]:(, TypeError('unable to convert FailingIter to a Vim structure',)) -l[:] = [Mapping({"abcG" : FailingIterNext()})]:(, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using l[:] = [Mapping({"abcG" : %s})] -l[:] = [Mapping({"abcG" : None})]:NOT FAILED -l[:] = [Mapping({"abcG" : {b"": 1}})]:(, ValueError('empty keys are not allowed',)) -l[:] = [Mapping({"abcG" : {"": 1}})]:(, ValueError('empty keys are not allowed',)) -l[:] = [Mapping({"abcG" : FailingMapping()})]:(, NotImplementedError('keys',)) -l[:] = [Mapping({"abcG" : FailingMappingKey()})]:(, NotImplementedError('getitem:mappingkey',)) -l[:] = [Mapping({"abcG" : FailingNumber()})]:(, NotImplementedError('int',)) -<<< Finished ->>> Testing *Iter* using l[:] = [%s] -l[:] = [FailingIter()]:(, TypeError('unable to convert FailingIter to a Vim structure',)) -l[:] = [FailingIterNext()]:(, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using l[:] = [%s] -l[:] = [None]:NOT FAILED -l[:] = [{b"": 1}]:(, ValueError('empty keys are not allowed',)) -l[:] = [{"": 1}]:(, ValueError('empty keys are not allowed',)) -l[:] = [FailingMapping()]:(, NotImplementedError('keys',)) -l[:] = [FailingMappingKey()]:(, NotImplementedError('getitem:mappingkey',)) -l[:] = [FailingNumber()]:(, NotImplementedError('int',)) -<<< Finished ->> ListConcatInPlace ->>> Testing *Iter* using l.extend(%s) -l.extend(FailingIter()):(, NotImplementedError('iter',)) -l.extend(FailingIterNext()):(, NotImplementedError('next',)) -<<< Finished ->>> Testing StringToChars using l.extend([{%s : 1}]) -l.extend([{1 : 1}]):(, TypeError('expected bytes() or str() instance, but got int',)) -l.extend([{b"\0" : 1}]):(, TypeError('expected bytes with no null',)) -l.extend([{"\0" : 1}]):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using l.extend([{"abcF" : {%s : 1}}]) -l.extend([{"abcF" : {1 : 1}}]):(, TypeError('expected bytes() or str() instance, but got int',)) -l.extend([{"abcF" : {b"\0" : 1}}]):(, TypeError('expected bytes with no null',)) -l.extend([{"abcF" : {"\0" : 1}}]):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using l.extend([{"abcF" : Mapping({%s : 1})}]) -l.extend([{"abcF" : Mapping({1 : 1})}]):(, TypeError('expected bytes() or str() instance, but got int',)) -l.extend([{"abcF" : Mapping({b"\0" : 1})}]):(, TypeError('expected bytes with no null',)) -l.extend([{"abcF" : Mapping({"\0" : 1})}]):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using l.extend([{"abcF" : %s}]) -l.extend([{"abcF" : FailingIter()}]):(, TypeError('unable to convert FailingIter to a Vim structure',)) -l.extend([{"abcF" : FailingIterNext()}]):(, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using l.extend([{"abcF" : %s}]) -l.extend([{"abcF" : None}]):NOT FAILED -l.extend([{"abcF" : {b"": 1}}]):(, ValueError('empty keys are not allowed',)) -l.extend([{"abcF" : {"": 1}}]):(, ValueError('empty keys are not allowed',)) -l.extend([{"abcF" : FailingMapping()}]):(, NotImplementedError('keys',)) -l.extend([{"abcF" : FailingMappingKey()}]):(, NotImplementedError('getitem:mappingkey',)) -l.extend([{"abcF" : FailingNumber()}]):(, NotImplementedError('int',)) -<<< Finished ->>> Testing StringToChars using l.extend([Mapping({%s : 1})]) -l.extend([Mapping({1 : 1})]):(, TypeError('expected bytes() or str() instance, but got int',)) -l.extend([Mapping({b"\0" : 1})]):(, TypeError('expected bytes with no null',)) -l.extend([Mapping({"\0" : 1})]):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using l.extend([Mapping({"abcG" : {%s : 1}})]) -l.extend([Mapping({"abcG" : {1 : 1}})]):(, TypeError('expected bytes() or str() instance, but got int',)) -l.extend([Mapping({"abcG" : {b"\0" : 1}})]):(, TypeError('expected bytes with no null',)) -l.extend([Mapping({"abcG" : {"\0" : 1}})]):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using l.extend([Mapping({"abcG" : Mapping({%s : 1})})]) -l.extend([Mapping({"abcG" : Mapping({1 : 1})})]):(, TypeError('expected bytes() or str() instance, but got int',)) -l.extend([Mapping({"abcG" : Mapping({b"\0" : 1})})]):(, TypeError('expected bytes with no null',)) -l.extend([Mapping({"abcG" : Mapping({"\0" : 1})})]):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using l.extend([Mapping({"abcG" : %s})]) -l.extend([Mapping({"abcG" : FailingIter()})]):(, TypeError('unable to convert FailingIter to a Vim structure',)) -l.extend([Mapping({"abcG" : FailingIterNext()})]):(, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using l.extend([Mapping({"abcG" : %s})]) -l.extend([Mapping({"abcG" : None})]):NOT FAILED -l.extend([Mapping({"abcG" : {b"": 1}})]):(, ValueError('empty keys are not allowed',)) -l.extend([Mapping({"abcG" : {"": 1}})]):(, ValueError('empty keys are not allowed',)) -l.extend([Mapping({"abcG" : FailingMapping()})]):(, NotImplementedError('keys',)) -l.extend([Mapping({"abcG" : FailingMappingKey()})]):(, NotImplementedError('getitem:mappingkey',)) -l.extend([Mapping({"abcG" : FailingNumber()})]):(, NotImplementedError('int',)) -<<< Finished ->>> Testing *Iter* using l.extend([%s]) -l.extend([FailingIter()]):(, TypeError('unable to convert FailingIter to a Vim structure',)) -l.extend([FailingIterNext()]):(, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using l.extend([%s]) -l.extend([None]):NOT FAILED -l.extend([{b"": 1}]):(, ValueError('empty keys are not allowed',)) -l.extend([{"": 1}]):(, ValueError('empty keys are not allowed',)) -l.extend([FailingMapping()]):(, NotImplementedError('keys',)) -l.extend([FailingMappingKey()]):(, NotImplementedError('getitem:mappingkey',)) -l.extend([FailingNumber()]):(, NotImplementedError('int',)) -<<< Finished ->> ListSetattr -del l.locked:(, AttributeError('cannot delete vim.List attributes',)) -l.locked = FailingTrue():(, NotImplementedError('bool',)) -l.xxx = True:(, AttributeError('cannot set attribute xxx',)) -> Function ->> FunctionConstructor ->>> FunctionConstructor -vim.Function("123"):(, ValueError('unnamed function 123 does not exist',)) -vim.Function("xxx_non_existent_function_xxx"):(, ValueError('function xxx_non_existent_function_xxx does not exist',)) -vim.Function("xxx#non#existent#function#xxx"):NOT FAILED -vim.Function("xxx_non_existent_function_xxx2", args=[]):(, ValueError('function xxx_non_existent_function_xxx2 does not exist',)) -vim.Function("xxx_non_existent_function_xxx3", self={}):(, ValueError('function xxx_non_existent_function_xxx3 does not exist',)) -vim.Function("xxx_non_existent_function_xxx4", args=[], self={}):(, ValueError('function xxx_non_existent_function_xxx4 does not exist',)) ->>> FunctionNew -vim.Function("tr", self="abcFuncSelf"):(, AttributeError('keys',)) -vim.Function("tr", args=427423):(, TypeError('unable to convert int to a Vim list',)) -vim.Function("tr", self="abcFuncSelf2", args="abcFuncArgs2"):(, AttributeError('keys',)) -vim.Function(self="abcFuncSelf2", args="abcFuncArgs2"):(, AttributeError('keys',)) -vim.Function("tr", "", self="abcFuncSelf2", args="abcFuncArgs2"):(, AttributeError('keys',)) -vim.Function("tr", ""):(, TypeError('function takes exactly 1 argument (2 given)',)) ->> FunctionCall ->>> Testing StringToChars using f({%s : 1}) -f({1 : 1}):(, TypeError('expected bytes() or str() instance, but got int',)) -f({b"\0" : 1}):(, TypeError('expected bytes with no null',)) -f({"\0" : 1}):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using f({"abcF" : {%s : 1}}) -f({"abcF" : {1 : 1}}):(, TypeError('expected bytes() or str() instance, but got int',)) -f({"abcF" : {b"\0" : 1}}):(, TypeError('expected bytes with no null',)) -f({"abcF" : {"\0" : 1}}):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using f({"abcF" : Mapping({%s : 1})}) -f({"abcF" : Mapping({1 : 1})}):(, TypeError('expected bytes() or str() instance, but got int',)) -f({"abcF" : Mapping({b"\0" : 1})}):(, TypeError('expected bytes with no null',)) -f({"abcF" : Mapping({"\0" : 1})}):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using f({"abcF" : %s}) -f({"abcF" : FailingIter()}):(, TypeError('unable to convert FailingIter to a Vim structure',)) -f({"abcF" : FailingIterNext()}):(, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using f({"abcF" : %s}) -f({"abcF" : None}):NOT FAILED -f({"abcF" : {b"": 1}}):(, ValueError('empty keys are not allowed',)) -f({"abcF" : {"": 1}}):(, ValueError('empty keys are not allowed',)) -f({"abcF" : FailingMapping()}):(, NotImplementedError('keys',)) -f({"abcF" : FailingMappingKey()}):(, NotImplementedError('getitem:mappingkey',)) -f({"abcF" : FailingNumber()}):(, NotImplementedError('int',)) -<<< Finished ->>> Testing StringToChars using f(Mapping({%s : 1})) -f(Mapping({1 : 1})):(, TypeError('expected bytes() or str() instance, but got int',)) -f(Mapping({b"\0" : 1})):(, TypeError('expected bytes with no null',)) -f(Mapping({"\0" : 1})):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using f(Mapping({"abcG" : {%s : 1}})) -f(Mapping({"abcG" : {1 : 1}})):(, TypeError('expected bytes() or str() instance, but got int',)) -f(Mapping({"abcG" : {b"\0" : 1}})):(, TypeError('expected bytes with no null',)) -f(Mapping({"abcG" : {"\0" : 1}})):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using f(Mapping({"abcG" : Mapping({%s : 1})})) -f(Mapping({"abcG" : Mapping({1 : 1})})):(, TypeError('expected bytes() or str() instance, but got int',)) -f(Mapping({"abcG" : Mapping({b"\0" : 1})})):(, TypeError('expected bytes with no null',)) -f(Mapping({"abcG" : Mapping({"\0" : 1})})):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using f(Mapping({"abcG" : %s})) -f(Mapping({"abcG" : FailingIter()})):(, TypeError('unable to convert FailingIter to a Vim structure',)) -f(Mapping({"abcG" : FailingIterNext()})):(, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using f(Mapping({"abcG" : %s})) -f(Mapping({"abcG" : None})):NOT FAILED -f(Mapping({"abcG" : {b"": 1}})):(, ValueError('empty keys are not allowed',)) -f(Mapping({"abcG" : {"": 1}})):(, ValueError('empty keys are not allowed',)) -f(Mapping({"abcG" : FailingMapping()})):(, NotImplementedError('keys',)) -f(Mapping({"abcG" : FailingMappingKey()})):(, NotImplementedError('getitem:mappingkey',)) -f(Mapping({"abcG" : FailingNumber()})):(, NotImplementedError('int',)) -<<< Finished ->>> Testing *Iter* using f(%s) -f(FailingIter()):(, TypeError('unable to convert FailingIter to a Vim structure',)) -f(FailingIterNext()):(, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using f(%s) -f(None):NOT FAILED -f({b"": 1}):(, ValueError('empty keys are not allowed',)) -f({"": 1}):(, ValueError('empty keys are not allowed',)) -f(FailingMapping()):(, NotImplementedError('keys',)) -f(FailingMappingKey()):(, NotImplementedError('getitem:mappingkey',)) -f(FailingNumber()):(, NotImplementedError('int',)) -<<< Finished ->>> Testing StringToChars using fd(self={%s : 1}) -fd(self={1 : 1}):(, TypeError('expected bytes() or str() instance, but got int',)) -fd(self={b"\0" : 1}):(, TypeError('expected bytes with no null',)) -fd(self={"\0" : 1}):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using fd(self={"abcF" : {%s : 1}}) -fd(self={"abcF" : {1 : 1}}):(, TypeError('expected bytes() or str() instance, but got int',)) -fd(self={"abcF" : {b"\0" : 1}}):(, TypeError('expected bytes with no null',)) -fd(self={"abcF" : {"\0" : 1}}):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using fd(self={"abcF" : Mapping({%s : 1})}) -fd(self={"abcF" : Mapping({1 : 1})}):(, TypeError('expected bytes() or str() instance, but got int',)) -fd(self={"abcF" : Mapping({b"\0" : 1})}):(, TypeError('expected bytes with no null',)) -fd(self={"abcF" : Mapping({"\0" : 1})}):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using fd(self={"abcF" : %s}) -fd(self={"abcF" : FailingIter()}):(, TypeError('unable to convert FailingIter to a Vim structure',)) -fd(self={"abcF" : FailingIterNext()}):(, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using fd(self={"abcF" : %s}) -fd(self={"abcF" : None}):NOT FAILED -fd(self={"abcF" : {b"": 1}}):(, ValueError('empty keys are not allowed',)) -fd(self={"abcF" : {"": 1}}):(, ValueError('empty keys are not allowed',)) -fd(self={"abcF" : FailingMapping()}):(, NotImplementedError('keys',)) -fd(self={"abcF" : FailingMappingKey()}):(, NotImplementedError('getitem:mappingkey',)) -fd(self={"abcF" : FailingNumber()}):(, NotImplementedError('int',)) -<<< Finished ->>> Testing StringToChars using fd(self=Mapping({%s : 1})) -fd(self=Mapping({1 : 1})):(, TypeError('expected bytes() or str() instance, but got int',)) -fd(self=Mapping({b"\0" : 1})):(, TypeError('expected bytes with no null',)) -fd(self=Mapping({"\0" : 1})):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using fd(self=Mapping({"abcG" : {%s : 1}})) -fd(self=Mapping({"abcG" : {1 : 1}})):(, TypeError('expected bytes() or str() instance, but got int',)) -fd(self=Mapping({"abcG" : {b"\0" : 1}})):(, TypeError('expected bytes with no null',)) -fd(self=Mapping({"abcG" : {"\0" : 1}})):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using fd(self=Mapping({"abcG" : Mapping({%s : 1})})) -fd(self=Mapping({"abcG" : Mapping({1 : 1})})):(, TypeError('expected bytes() or str() instance, but got int',)) -fd(self=Mapping({"abcG" : Mapping({b"\0" : 1})})):(, TypeError('expected bytes with no null',)) -fd(self=Mapping({"abcG" : Mapping({"\0" : 1})})):(, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using fd(self=Mapping({"abcG" : %s})) -fd(self=Mapping({"abcG" : FailingIter()})):(, TypeError('unable to convert FailingIter to a Vim structure',)) -fd(self=Mapping({"abcG" : FailingIterNext()})):(, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using fd(self=Mapping({"abcG" : %s})) -fd(self=Mapping({"abcG" : None})):NOT FAILED -fd(self=Mapping({"abcG" : {b"": 1}})):(, ValueError('empty keys are not allowed',)) -fd(self=Mapping({"abcG" : {"": 1}})):(, ValueError('empty keys are not allowed',)) -fd(self=Mapping({"abcG" : FailingMapping()})):(, NotImplementedError('keys',)) -fd(self=Mapping({"abcG" : FailingMappingKey()})):(, NotImplementedError('getitem:mappingkey',)) -fd(self=Mapping({"abcG" : FailingNumber()})):(, NotImplementedError('int',)) -<<< Finished ->>> Testing *Iter* using fd(self=%s) -fd(self=FailingIter()):(, TypeError('unable to convert FailingIter to a Vim dictionary',)) -fd(self=FailingIterNext()):(, TypeError('unable to convert FailingIterNext to a Vim dictionary',)) -<<< Finished ->>> Testing ConvertFromPyObject using fd(self=%s) -fd(self=None):(, TypeError('unable to convert NoneType to a Vim dictionary',)) -fd(self={b"": 1}):(, ValueError('empty keys are not allowed',)) -fd(self={"": 1}):(, ValueError('empty keys are not allowed',)) -fd(self=FailingMapping()):(, NotImplementedError('keys',)) -fd(self=FailingMappingKey()):(, NotImplementedError('getitem:mappingkey',)) -fd(self=FailingNumber()):(, TypeError('unable to convert FailingNumber to a Vim dictionary',)) -<<< Finished ->>> Testing ConvertFromPyMapping using fd(self=%s) -fd(self=[]):(, AttributeError('keys',)) -<<< Finished -> TabPage ->> TabPageAttr -vim.current.tabpage.xxx:(, AttributeError("'vim.tabpage' object has no attribute 'xxx'",)) -> TabList ->> TabListItem -vim.tabpages[1000]:(, IndexError('no such tab page',)) -> Window ->> WindowAttr -vim.current.window.xxx:(, AttributeError("'vim.window' object has no attribute 'xxx'",)) ->> WindowSetattr -vim.current.window.buffer = 0:(, TypeError('readonly attribute: buffer',)) -vim.current.window.cursor = (100000000, 100000000):(, error('cursor position outside buffer',)) -vim.current.window.cursor = True:(, TypeError('argument must be 2-item sequence, not bool',)) ->>> Testing NumberToLong using vim.current.window.height = %s -vim.current.window.height = []:(, TypeError('expected int() or something supporting coercing to int(), but got list',)) -vim.current.window.height = None:(, TypeError('expected int() or something supporting coercing to int(), but got NoneType',)) -vim.current.window.height = -1:(, ValueError('number must be greater or equal to zero',)) -<<< Finished ->>> Testing NumberToLong using vim.current.window.width = %s -vim.current.window.width = []:(, TypeError('expected int() or something supporting coercing to int(), but got list',)) -vim.current.window.width = None:(, TypeError('expected int() or something supporting coercing to int(), but got NoneType',)) -vim.current.window.width = -1:(, ValueError('number must be greater or equal to zero',)) -<<< Finished -vim.current.window.xxxxxx = True:(, AttributeError('xxxxxx',)) -> WinList ->> WinListItem -vim.windows[1000]:(, IndexError('no such window',)) -> Buffer ->> StringToLine (indirect) -vim.current.buffer[0] = "\na":(, error('string cannot contain newlines',)) -vim.current.buffer[0] = b"\na":(, error('string cannot contain newlines',)) ->> SetBufferLine (indirect) -vim.current.buffer[0] = True:(, TypeError('bad argument type for built-in operation',)) ->> SetBufferLineList (indirect) -vim.current.buffer[:] = True:(, TypeError('bad argument type for built-in operation',)) -vim.current.buffer[:] = ["\na", "bc"]:(, error('string cannot contain newlines',)) ->> InsertBufferLines (indirect) -vim.current.buffer.append(None):(, TypeError('bad argument type for built-in operation',)) -vim.current.buffer.append(["\na", "bc"]):(, error('string cannot contain newlines',)) -vim.current.buffer.append("\nbc"):(, error('string cannot contain newlines',)) ->> RBItem -vim.current.buffer[100000000]:(, IndexError('line number out of range',)) ->> RBAsItem -vim.current.buffer[100000000] = "":(, IndexError('line number out of range',)) ->> BufferAttr -vim.current.buffer.xxx:(, AttributeError("'vim.buffer' object has no attribute 'xxx'",)) ->> BufferSetattr -vim.current.buffer.name = True:(, TypeError('expected bytes() or str() instance, but got bool',)) -vim.current.buffer.xxx = True:(, AttributeError('xxx',)) ->> BufferMark -vim.current.buffer.mark(0):(, TypeError('expected bytes() or str() instance, but got int',)) -vim.current.buffer.mark("abcM"):(, ValueError('mark name must be a single character',)) -vim.current.buffer.mark("!"):(, error('invalid mark name',)) ->> BufferRange -vim.current.buffer.range(1, 2, 3):(, TypeError('function takes exactly 2 arguments (3 given)',)) -> BufMap ->> BufMapItem -vim.buffers[100000000]:(, KeyError(100000000,)) ->>> Testing NumberToLong using vim.buffers[%s] -vim.buffers[[]]:(, TypeError('expected int() or something supporting coercing to int(), but got list',)) -vim.buffers[None]:(, TypeError('expected int() or something supporting coercing to int(), but got NoneType',)) -vim.buffers[-1]:(, ValueError('number must be greater than zero',)) -vim.buffers[0]:(, ValueError('number must be greater than zero',)) -<<< Finished -> Current ->> CurrentGetattr -vim.current.xxx:(, AttributeError("'vim.currentdata' object has no attribute 'xxx'",)) ->> CurrentSetattr -vim.current.line = True:(, TypeError('bad argument type for built-in operation',)) -vim.current.buffer = True:(, TypeError('expected vim.Buffer object, but got bool',)) -vim.current.window = True:(, TypeError('expected vim.Window object, but got bool',)) -vim.current.tabpage = True:(, TypeError('expected vim.TabPage object, but got bool',)) -vim.current.xxx = True:(, AttributeError('xxx',)) -['.'] -'.' -3,xx -before -after -pythonx/topmodule/__init__.py -pythonx/topmodule/submodule/__init__.py -pythonx/topmodule/submodule/subsubmodule/subsubsubmodule.py -vim.command("throw 'abcN'"):(, error('abcN',)) -Exe("throw 'def'"):(, error('def',)) -vim.eval("Exe('throw ''ghi''')"):(, error('ghi',)) -vim.eval("Exe('echoerr ''jkl''')"):(, error('Vim(echoerr):jkl',)) -vim.eval("Exe('xxx_non_existent_command_xxx')"):(, error('Vim:E492: Not an editor command: xxx_non_existent_command_xxx',)) -vim.eval("xxx_unknown_function_xxx()"):(, error('Vim:E117: Unknown function: xxx_unknown_function_xxx',)) -vim.bindeval("Exe('xxx_non_existent_command_xxx')"):(, error('Vim:E492: Not an editor command: xxx_non_existent_command_xxx',)) -Caught KeyboardInterrupt -Running :put -No exception - diff --git a/src/testdir/test_python2.vim b/src/testdir/test_python2.vim index dae2739d05..928783536d 100644 --- a/src/testdir/test_python2.vim +++ b/src/testdir/test_python2.vim @@ -560,11 +560,11 @@ endfunc " Test for calling a function func Test_python_function_call() func New(...) - return ['NewStart'] + a:000 + ['NewEnd'] + return ['NewStart'] + a:000 + ['NewEnd'] endfunc func DictNew(...) dict - return ['DictNewStart'] + a:000 + ['DictNewEnd', self] + return ['DictNewStart'] + a:000 + ['DictNewEnd', self] endfunc new @@ -1312,8 +1312,8 @@ func Test_python_buffer() " Test assigning to name property augroup BUFS - autocmd BufFilePost * python cb.append(vim.eval('expand("")') + ':BufFilePost:' + vim.eval('bufnr("%")')) - autocmd BufFilePre * python cb.append(vim.eval('expand("")') + ':BufFilePre:' + vim.eval('bufnr("%")')) + autocmd BufFilePost * python cb.append(vim.eval('expand("")') + ':BufFilePost:' + vim.eval('bufnr("%")')) + autocmd BufFilePre * python cb.append(vim.eval('expand("")') + ':BufFilePre:' + vim.eval('bufnr("%")')) augroup END py << trim EOF import os @@ -1364,7 +1364,7 @@ func Test_python_buffer() call assert_equal([''], getline(1, '$')) augroup BUFS - autocmd! + autocmd! augroup END augroup! BUFS %bw! @@ -1516,7 +1516,7 @@ func Test_python_tabpage_window() cb.append('!!!!!! Windows differ') EOF - let expected =<< trim END + let expected =<< trim END Number of tabs: 4 Current tab pages: (1): 1 windows, current is @@ -1801,13 +1801,13 @@ func Test_python_vim_func() cb.append('psaA: ' + repr(psaA)) cb.append('psaB: ' + repr(psaB)) cb.append('psaC: ' + repr(psaC)) - + psar = vim.Function('SelfArgs', args=[{'abcArgsPSAr': 'abcArgsPSArVal'}], self={'abcSelfPSAr': 'abcSelfPSArVal'}) psar.args[0]['abcArgsPSAr2'] = [psar.self, psar.args[0]] psar.self['rec'] = psar psar.self['self'] = psar.self psar.self['args'] = psar.args - + try: cb.append('psar: ' + repr(psar)) except Exception: @@ -1931,7 +1931,7 @@ func Test_python_vim_func() return repr(v) else: return vim.Function('string')(v) - + cb.append('a.args: ' + s(a.args)) cb.append('pa1.args: ' + s(pa1.args)) cb.append('pa2.args: ' + s(pa2.args)) @@ -1942,7 +1942,7 @@ func Test_python_vim_func() cb.append('psa2.args: ' + s(psa2.args)) cb.append('psa3.args: ' + s(psa3.args)) cb.append('psa4.args: ' + s(psa4.args)) - + cb.append('a.self: ' + s(a.self)) cb.append('pa1.self: ' + s(pa1.self)) cb.append('pa2.self: ' + s(pa2.self)) @@ -1953,7 +1953,7 @@ func Test_python_vim_func() cb.append('psa2.self: ' + s(psa2.self)) cb.append('psa3.self: ' + s(psa3.self)) cb.append('psa4.self: ' + s(psa4.self)) - + cb.append('a.name: ' + s(a.name)) cb.append('pa1.name: ' + s(pa1.name)) cb.append('pa2.name: ' + s(pa2.name)) @@ -1964,7 +1964,7 @@ func Test_python_vim_func() cb.append('psa2.name: ' + s(psa2.name)) cb.append('psa3.name: ' + s(psa3.name)) cb.append('psa4.name: ' + s(psa4.name)) - + cb.append('a.auto_rebind: ' + s(a.auto_rebind)) cb.append('pa1.auto_rebind: ' + s(pa1.auto_rebind)) cb.append('pa2.auto_rebind: ' + s(pa2.auto_rebind)) @@ -1983,9 +1983,9 @@ func Test_python_vim_func() cb.append('psaA.auto_rebind: ' + s(psaA.auto_rebind)) cb.append('psaB.auto_rebind: ' + s(psaB.auto_rebind)) cb.append('psaC.auto_rebind: ' + s(psaC.auto_rebind)) - + del s - + del a del pa1 del pa2 @@ -2005,7 +2005,7 @@ func Test_python_vim_func() del psaB del psaC del psar - + del ecall EOF @@ -2140,19 +2140,19 @@ func Test_python_subclass() super(DupDict, self).__setitem__('dup_' + key, value) dd = DupDict() dd['a'] = 'b' - + class DupList(vim.List): def __getitem__(self, idx): return [super(DupList, self).__getitem__(idx)] * 2 - + dl = DupList() dl2 = DupList(iter('abcC')) dl.extend(dl2[0]) - + class DupFun(vim.Function): def __call__(self, arg): return super(DupFun, self).__call__(arg, arg) - + df = DupFun('Put') EOF @@ -2233,33 +2233,33 @@ func Test_python_errors() fd = vim.Function('F') fdel = vim.Function('D') vim.command('delfunction D') - + def subexpr_test(expr, name, subexprs): cb.append('>>> Testing %s using %s' % (name, expr)) for subexpr in subexprs: ee(expr % subexpr) cb.append('<<< Finished') - + def stringtochars_test(expr): return subexpr_test(expr, 'StringToChars', ( '1', # Fail type checks 'u"\\0"', # Fail PyString_AsStringAndSize(bytes, , NULL) check '"\\0"', # Fail PyString_AsStringAndSize(object, , NULL) check )) - + class Mapping(object): def __init__(self, d): self.d = d - + def __getitem__(self, key): return self.d[key] - + def keys(self): return self.d.keys() - + def items(self): return self.d.items() - + def convertfrompyobject_test(expr, recurse=True): # pydict_to_tv stringtochars_test(expr % '{%s : 1}') @@ -2279,19 +2279,19 @@ func Test_python_errors() 'FailingMappingKey()', # 'FailingNumber()', # )) - + def convertfrompymapping_test(expr): convertfrompyobject_test(expr) return subexpr_test(expr, 'ConvertFromPyMapping', ( '[]', )) - + def iter_test(expr): return subexpr_test(expr, '*Iter*', ( 'FailingIter()', 'FailingIterNext()', )) - + def number_test(expr, natural=False, unsigned=False): if natural: unsigned = True @@ -2300,69 +2300,69 @@ func Test_python_errors() 'None', ) + (unsigned and ('-1',) or ()) + (natural and ('0',) or ())) - + class FailingTrue(object): def __nonzero__(self): raise NotImplementedError('bool') - + class FailingIter(object): def __iter__(self): raise NotImplementedError('iter') - + class FailingIterNext(object): def __iter__(self): return self - + def next(self): raise NotImplementedError('next') - + class FailingIterNextN(object): def __init__(self, n): self.n = n - + def __iter__(self): return self - + def next(self): if self.n: self.n -= 1 return 1 else: raise NotImplementedError('next N') - + class FailingMappingKey(object): def __getitem__(self, item): raise NotImplementedError('getitem:mappingkey') - + def keys(self): return list("abcH") - + class FailingMapping(object): def __getitem__(self): raise NotImplementedError('getitem:mapping') - + def keys(self): raise NotImplementedError('keys') - + class FailingList(list): def __getitem__(self, idx): if i == 2: raise NotImplementedError('getitem:list') else: return super(FailingList, self).__getitem__(idx) - + class NoArgsCall(object): def __call__(self): pass - + class FailingCall(object): def __call__(self, path): raise NotImplementedError('call') - + class FailingNumber(object): def __int__(self): raise NotImplementedError('int') - + cb.append("> Output") cb.append(">> OutputSetattr") ee('del sys.stdout.softspace') diff --git a/src/testdir/test_python3.vim b/src/testdir/test_python3.vim index b4a4a4aa7b..7edf7d055c 100644 --- a/src/testdir/test_python3.vim +++ b/src/testdir/test_python3.vim @@ -1,9 +1,76 @@ " Test for python 3 commands. -" TODO: move tests from test87.in here. source check.vim CheckFeature python3 +" This function should be called first. This sets up python functions used by +" the other tests. +func Test_AAA_python3_setup() + py3 << trim EOF + import vim + import sys + import re + + py33_type_error_pattern = re.compile('^__call__\(\) takes (\d+) positional argument but (\d+) were given$') + py37_exception_repr = re.compile(r'([^\(\),])(\)+)$') + + def emsg(ei): + return ei[0].__name__ + ':' + repr(ei[1].args) + + def ee(expr, g=globals(), l=locals()): + cb = vim.current.buffer + try: + try: + exec(expr, g, l) + except Exception as e: + if sys.version_info >= (3, 3) and e.__class__ is AttributeError and str(e).find('has no attribute')>=0 and not str(e).startswith("'vim."): + msg = repr((e.__class__, AttributeError(str(e)[str(e).rfind(" '") + 2:-1]))) + elif sys.version_info >= (3, 3) and e.__class__ is ImportError and str(e).find('No module named \'') >= 0: + msg = repr((e.__class__, ImportError(str(e).replace("'", '')))) + elif sys.version_info >= (3, 6) and e.__class__ is ModuleNotFoundError: + # Python 3.6 gives ModuleNotFoundError, change it to an ImportError + msg = repr((ImportError, ImportError(str(e).replace("'", '')))) + elif sys.version_info >= (3, 3) and e.__class__ is TypeError: + m = py33_type_error_pattern.search(str(e)) + if m: + msg = '__call__() takes exactly {0} positional argument ({1} given)'.format(m.group(1), m.group(2)) + msg = repr((e.__class__, TypeError(msg))) + else: + msg = repr((e.__class__, e)) + # Messages changed with Python 3.6, change new to old. + newmsg1 = """'argument must be str, bytes or bytearray, not None'""" + oldmsg1 = '''"Can't convert 'NoneType' object to str implicitly"''' + if msg.find(newmsg1) > -1: + msg = msg.replace(newmsg1, oldmsg1) + newmsg2 = """'argument must be str, bytes or bytearray, not int'""" + oldmsg2 = '''"Can't convert 'int' object to str implicitly"''' + if msg.find(newmsg2) > -1: + msg = msg.replace(newmsg2, oldmsg2) + elif sys.version_info >= (3, 5) and e.__class__ is ValueError and str(e) == 'embedded null byte': + msg = repr((TypeError, TypeError('expected bytes with no null'))) + else: + msg = repr((e.__class__, e)) + # Some Python versions say can't, others cannot. + if msg.find('can\'t') > -1: + msg = msg.replace('can\'t', 'cannot') + # Some Python versions use single quote, some double quote + if msg.find('"cannot ') > -1: + msg = msg.replace('"cannot ', '\'cannot ') + if msg.find(' attributes"') > -1: + msg = msg.replace(' attributes"', ' attributes\'') + if sys.version_info >= (3, 7): + msg = py37_exception_repr.sub(r'\1,\2', msg) + cb.append(expr + ':' + msg) + else: + cb.append(expr + ':NOT FAILED') + except Exception as e: + msg = repr((e.__class__, e)) + if sys.version_info >= (3, 7): + msg = py37_exception_repr.sub(r'\1,\2', msg) + cb.append(expr + '::' + msg) + EOF +endfunc + func Test_py3do() " Check deleting lines does not trigger an ml_get error. py3 import vim @@ -59,6 +126,15 @@ func Test_vim_function() call assert_false(v:exception) endtry + let caught_vim_err = v:false + try + let x = py3eval('f.abc') + catch + call assert_match("AttributeError: 'vim.function' object has no attribute 'abc'", v:exception) + let caught_vim_err = v:true + endtry + call assert_equal(v:true, caught_vim_err) + py3 del f delfunc s:foo endfunc @@ -149,7 +225,7 @@ func Test_Write_To_Current_Buffer_Fixes_Cursor_List() call assert_equal( line( '.' ), 1 ) bwipe! -endfunction +endfunc func Test_Write_To_Current_Buffer_Fixes_Cursor_Str() call _SetUpVisibleBuffer() @@ -158,7 +234,7 @@ func Test_Write_To_Current_Buffer_Fixes_Cursor_Str() call assert_equal( line( '.' ), 10 ) bwipe! -endfunction +endfunc func Test_Catch_Exception_Message() try @@ -355,4 +431,3233 @@ s+='B' call assert_equal('ABCDE', pyxeval('s')) endfunc +" Test for the python List object +func Test_python3_list() + let l = [] + py3 l = vim.bindeval('l') + py3 f = vim.bindeval('function("strlen")') + " Extending List directly with different types + py3 l += [1, "as'd", [1, 2, f, {'a': 1}]] + call assert_equal([1, "as'd", [1, 2, function("strlen"), {'a': 1}]], l) + call assert_equal([1, 2, function("strlen"), {'a': 1}], l[-1]) + call assert_fails('echo l[-4]', 'E684:') + + " List assignment + py3 l[0] = 0 + call assert_equal([0, "as'd", [1, 2, function("strlen"), {'a': 1}]], l) + py3 l[-2] = f + call assert_equal([0, function("strlen"), [1, 2, function("strlen"), {'a': 1}]], l) +endfunc + +" Extending Dictionary directly with different types +func Test_python3_dict_extend() + let d = {} + func d.f() + return 1 + endfunc + + py3 f = vim.bindeval('function("strlen")') + py3 << trim EOF + d = vim.bindeval('d') + d['1'] = 'asd' + d.update() # Must not do anything, including throwing errors + d.update(b = [1, 2, f]) + d.update((('-1', {'a': 1}),)) + d.update({'0': -1}) + dk = d.keys() + dv = d.values() + di = d.items() + dk.sort(key=repr) + dv.sort(key=repr) + di.sort(key=repr) + EOF + + call assert_equal(1, py3eval("d['f'](self={})")) + call assert_equal("[b'-1', b'0', b'1', b'b', b'f']", py3eval('repr(dk)')) + call assert_equal("[-1, , , , b'asd']", substitute(py3eval('repr(dv)'),'0x\x\+','','g')) + call assert_equal("[(b'-1', ), (b'0', -1), (b'1', b'asd'), (b'b', ), (b'f', )]", substitute(py3eval('repr(di)'),'0x\x\+','','g')) + call assert_equal(['0', '1', 'b', 'f', '-1'], keys(d)) + call assert_equal("[-1, 'asd', [1, 2, function('strlen')], function('1'), {'a': 1}]", string(values(d))) + py3 del dk + py3 del di + py3 del dv +endfunc + +func Test_python3_list_del_items() + " removing items with del + let l = [0, function("strlen"), [1, 2, function("strlen"), {'a': 1}]] + py3 l = vim.bindeval('l') + py3 del l[2] + call assert_equal("[0, function('strlen')]", string(l)) + + let l = range(8) + py3 l = vim.bindeval('l') + py3 del l[:3] + py3 del l[1:] + call assert_equal([3], l) + + " removing items out of range: silently skip items that don't exist + + " The following two ranges delete nothing as they match empty list: + let l = [0, 1, 2, 3] + py3 l = vim.bindeval('l') + py3 del l[2:1] + call assert_equal([0, 1, 2, 3], l) + py3 del l[2:2] + call assert_equal([0, 1, 2, 3], l) + py3 del l[2:3] + call assert_equal([0, 1, 3], l) + + let l = [0, 1, 2, 3] + py3 l = vim.bindeval('l') + py3 del l[2:4] + call assert_equal([0, 1], l) + + let l = [0, 1, 2, 3] + py3 l = vim.bindeval('l') + py3 del l[2:5] + call assert_equal([0, 1], l) + + let l = [0, 1, 2, 3] + py3 l = vim.bindeval('l') + py3 del l[2:6] + call assert_equal([0, 1], l) + + " The following two ranges delete nothing as they match empty list: + let l = [0, 1, 2, 3] + py3 l = vim.bindeval('l') + py3 del l[-1:2] + call assert_equal([0, 1, 2, 3], l) + py3 del l[-2:2] + call assert_equal([0, 1, 2, 3], l) + py3 del l[-3:2] + call assert_equal([0, 2, 3], l) + + let l = [0, 1, 2, 3] + py3 l = vim.bindeval('l') + py3 del l[-4:2] + call assert_equal([2, 3], l) + + let l = [0, 1, 2, 3] + py3 l = vim.bindeval('l') + py3 del l[-5:2] + call assert_equal([2, 3], l) + + let l = [0, 1, 2, 3] + py3 l = vim.bindeval('l') + py3 del l[-6:2] + call assert_equal([2, 3], l) + + let l = [0, 1, 2, 3] + py3 l = vim.bindeval('l') + py3 del l[::2] + call assert_equal([1, 3], l) + + let l = [0, 1, 2, 3] + py3 l = vim.bindeval('l') + py3 del l[3:0:-2] + call assert_equal([0, 2], l) + + let l = [0, 1, 2, 3] + py3 l = vim.bindeval('l') + py3 del l[2:4:-2] + let l = [0, 1, 2, 3] +endfunc + +func Test_python3_dict_del_items() + let d = eval("{'0' : -1, '1' : 'asd', 'b' : [1, 2, function('strlen')], 'f' : function('min'), '-1' : {'a': 1}}") + py3 d = vim.bindeval('d') + py3 del d['-1'] + py3 del d['f'] + call assert_equal([1, 2, function('strlen')], py3eval('d.get(''b'', 1)')) + call assert_equal([1, 2, function('strlen')], py3eval('d.pop(''b'')')) + call assert_equal(1, py3eval('d.get(''b'', 1)')) + call assert_equal('asd', py3eval('d.pop(''1'', 2)')) + call assert_equal(2, py3eval('d.pop(''1'', 2)')) + call assert_equal('True', py3eval('repr(d.has_key(''0''))')) + call assert_equal('False', py3eval('repr(d.has_key(''1''))')) + call assert_equal('True', py3eval('repr(''0'' in d)')) + call assert_equal('False', py3eval('repr(''1'' in d)')) + call assert_equal("[b'0']", py3eval('repr(list(iter(d)))')) + call assert_equal({'0' : -1}, d) + call assert_equal("(b'0', -1)", py3eval('repr(d.popitem())')) + call assert_equal('None', py3eval('repr(d.get(''0''))')) + call assert_equal('[]', py3eval('repr(list(iter(d)))')) +endfunc + +" Slice assignment to a list +func Test_python3_slice_assignment() + let l = [0, 1, 2, 3] + py3 l = vim.bindeval('l') + py3 l[0:0] = ['a'] + call assert_equal(['a', 0, 1, 2, 3], l) + + let l = [0, 1, 2, 3] + py3 l = vim.bindeval('l') + py3 l[1:2] = ['b'] + call assert_equal([0, 'b', 2, 3], l) + + let l = [0, 1, 2, 3] + py3 l = vim.bindeval('l') + py3 l[2:4] = ['c'] + call assert_equal([0, 1, 'c'], l) + + let l = [0, 1, 2, 3] + py3 l = vim.bindeval('l') + py3 l[4:4] = ['d'] + call assert_equal([0, 1, 2, 3, 'd'], l) + + let l = [0, 1, 2, 3] + py3 l = vim.bindeval('l') + py3 l[-1:2] = ['e'] + call assert_equal([0, 1, 2, 'e', 3], l) + + let l = [0, 1, 2, 3] + py3 l = vim.bindeval('l') + py3 l[-10:2] = ['f'] + call assert_equal(['f', 2, 3], l) + + let l = [0, 1, 2, 3] + py3 l = vim.bindeval('l') + py3 l[2:-10] = ['g'] + call assert_equal([0, 1, 'g', 2, 3], l) + + let l = [] + py3 l = vim.bindeval('l') + py3 l[0:0] = ['h'] + call assert_equal(['h'], l) + + let l = range(8) + py3 l = vim.bindeval('l') + py3 l[2:6:2] = [10, 20] + call assert_equal([0, 1, 10, 3, 20, 5, 6, 7], l) + + let l = range(8) + py3 l = vim.bindeval('l') + py3 l[6:2:-2] = [10, 20] + call assert_equal([0, 1, 2, 3, 20, 5, 10, 7], l) + + let l = range(8) + py3 l = vim.bindeval('l') + py3 l[6:2] = () + call assert_equal([0, 1, 2, 3, 4, 5, 6, 7], l) + + let l = range(8) + py3 l = vim.bindeval('l') + py3 l[6:2:1] = () + call assert_equal([0, 1, 2, 3, 4, 5, 6, 7], l) + + let l = range(8) + py3 l = vim.bindeval('l') + py3 l[2:2:1] = () + call assert_equal([0, 1, 2, 3, 4, 5, 6, 7], l) +endfunc + +" Locked variables +func Test_python3_lockedvar() + new + py3 cb = vim.current.buffer + let l = [0, 1, 2, 3] + py3 l = vim.bindeval('l') + lockvar! l + py3 << trim EOF + try: + l[2]='i' + except vim.error: + cb.append('l[2] threw vim.error: ' + emsg(sys.exc_info())) + EOF + call assert_equal(['', "l[2] threw vim.error: error:('list is locked',)"], + \ getline(1, '$')) + call assert_equal([0, 1, 2, 3], l) + unlockvar! l + close! +endfunc + +" Test for calling a function +func Test_python3_function_call() + func New(...) + return ['NewStart'] + a:000 + ['NewEnd'] + endfunc + + func DictNew(...) dict + return ['DictNewStart'] + a:000 + ['DictNewEnd', self] + endfunc + + new + let l = [function('New'), function('DictNew')] + py3 l = vim.bindeval('l') + py3 l.extend(list(l[0](1, 2, 3))) + call assert_equal([function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd'], l) + py3 l.extend(list(l[1](1, 2, 3, self={'a': 'b'}))) + call assert_equal([function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}], l) + py3 l += [[l[0].name]] + call assert_equal([function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}, ['New']], l) + py3 ee('l[1](1, 2, 3)') + call assert_equal("l[1](1, 2, 3):(, error('Vim:E725: Calling dict function without Dictionary: DictNew',))", getline(2)) + %d + py3 f = l[0] + delfunction New + py3 ee('f(1, 2, 3)') + call assert_equal("f(1, 2, 3):(, error('Vim:E117: Unknown function: New',))", getline(2)) + close! + delfunction DictNew +endfunc + +func Test_python3_float() + CheckFeature float + let l = [0.0] + py3 l = vim.bindeval('l') + py3 l.extend([0.0]) + call assert_equal([0.0, 0.0], l) +endfunc + +" Test for Dict key errors +func Test_python3_dict_key_error() + let messages = [] + py3 << trim EOF + import sys + d = vim.bindeval('{}') + m = vim.bindeval('messages') + def em(expr, g=globals(), l=locals()): + try: + exec(expr, g, l) + except Exception as e: + if sys.version_info >= (3, 5) and e.__class__ is ValueError and str(e) == 'embedded null byte': + m.extend([TypeError.__name__]) + else: + m.extend([e.__class__.__name__]) + + em('d["abc1"]') + em('d["abc1"]="\\0"') + em('d["abc1"]=vim') + em('d[""]=1') + em('d["a\\0b"]=1') + em('d[b"a\\0b"]=1') + em('d.pop("abc1")') + em('d.popitem()') + del em + del m + EOF + + call assert_equal(['KeyError', 'TypeError', 'TypeError', 'ValueError', + \ 'TypeError', 'TypeError', 'KeyError', 'KeyError'], messages) + unlet messages +endfunc + +" Test for locked and scope attributes +func Test_python3_lock_scope_attr() + let d = {} | let dl = {} | lockvar dl + let res = [] + for s in split("d dl v: g:") + let name = tr(s, ':', 's') + execute 'py3 ' .. name .. ' = vim.bindeval("' .. s .. '")' + call add(res, s .. ' : ' .. join(map(['locked', 'scope'], + \ 'v:val .. ":" .. py3eval(name .. "." .. v:val)'), ';')) + endfor + call assert_equal(['d : locked:0;scope:0', 'dl : locked:1;scope:0', + \ 'v: : locked:2;scope:1', 'g: : locked:0;scope:2'], res) + + silent! let d.abc2 = 1 + silent! let dl.abc3 = 1 + py3 d.locked = True + py3 dl.locked = False + silent! let d.def = 1 + silent! let dl.def = 1 + call assert_equal({'abc2': 1}, d) + call assert_equal({'def': 1}, dl) + unlet d dl + + let l = [] | let ll = [] | lockvar ll + let res = [] + for s in split("l ll") + let name = tr(s, ':', 's') + execute 'py3 ' .. name .. '=vim.bindeval("' .. s .. '")' + call add(res, s .. ' : locked:' .. py3eval(name .. '.locked')) + endfor + call assert_equal(['l : locked:0', 'll : locked:1'], res) + + silent! call extend(l, [0]) + silent! call extend(ll, [0]) + py3 l.locked = True + py3 ll.locked = False + silent! call extend(l, [1]) + silent! call extend(ll, [1]) + call assert_equal([0], l) + call assert_equal([1], ll) + unlet l ll +endfunc + +" Test for py3eval() +func Test_python3_pyeval() + let l = py3eval('[0, 1, 2]') + call assert_equal([0, 1, 2], l) + + let d = py3eval('{"a": "b", "c": 1, "d": ["e"]}') + call assert_equal([['a', 'b'], ['c', 1], ['d', ['e']]], sort(items(d))) + + let v:errmsg = '' + call assert_equal(v:none, py3eval('None')) + call assert_equal('', v:errmsg) + + if has('float') + call assert_equal(0.0, py3eval('0.0')) + endif + + " Invalid values: + let caught_859 = 0 + try + let v = py3eval('"\0"') + catch /E859:/ + let caught_859 = 1 + endtry + call assert_equal(1, caught_859) + + let caught_859 = 0 + try + let v = py3eval('{"\0" : 1}') + catch /E859:/ + let caught_859 = 1 + endtry + call assert_equal(1, caught_859) + + let caught_nameerr = 0 + try + let v = py3eval("undefined_name") + catch /NameError: name 'undefined_name'/ + let caught_nameerr = 1 + endtry + call assert_equal(1, caught_nameerr) + + let caught_859 = 0 + try + let v = py3eval("vim") + catch /E859:/ + let caught_859 = 1 + endtry + call assert_equal(1, caught_859) +endfunc + +" threading +" Running py3do command (Test_pydo) before this test, stops the python thread +" from running. So this test should be run before the pydo test +func Test_aaa_python_threading() + let l = [0] + py3 l = vim.bindeval('l') + py3 << trim EOF + import threading + import time + + class T(threading.Thread): + def __init__(self): + threading.Thread.__init__(self) + self.t = 0 + self.running = True + + def run(self): + while self.running: + self.t += 1 + time.sleep(0.1) + + t = T() + del T + t.start() + EOF + + sleep 1 + py3 t.running = False + py3 t.join() + + " Check if the background thread is working. Count should be 10, but on a + " busy system (AppVeyor) it can be much lower. + py3 l[0] = t.t > 4 + py3 del time + py3 del threading + py3 del t + call assert_equal([1], l) +endfunc + +" settrace +func Test_python3_settrace() + let l = [] + py3 l = vim.bindeval('l') + py3 << trim EOF + import sys + + def traceit(frame, event, arg): + global l + if event == "line": + l += [frame.f_lineno] + return traceit + + def trace_main(): + for i in range(5): + pass + EOF + py3 sys.settrace(traceit) + py3 trace_main() + py3 sys.settrace(None) + py3 del traceit + py3 del trace_main + call assert_equal([1, 10, 11, 10, 11, 10, 11, 10, 11, 10, 11, 10, 1], l) +endfunc + +" Slice +func Test_python3_list_slice() + py3 ll = vim.bindeval('[0, 1, 2, 3, 4, 5]') + py3 l = ll[:4] + call assert_equal([0, 1, 2, 3], py3eval('l')) + py3 l = ll[2:] + call assert_equal([2, 3, 4, 5], py3eval('l')) + py3 l = ll[:-4] + call assert_equal([0, 1], py3eval('l')) + py3 l = ll[-2:] + call assert_equal([4, 5], py3eval('l')) + py3 l = ll[2:4] + call assert_equal([2, 3], py3eval('l')) + py3 l = ll[4:2] + call assert_equal([], py3eval('l')) + py3 l = ll[-4:-2] + call assert_equal([2, 3], py3eval('l')) + py3 l = ll[-2:-4] + call assert_equal([], py3eval('l')) + py3 l = ll[:] + call assert_equal([0, 1, 2, 3, 4, 5], py3eval('l')) + py3 l = ll[0:6] + call assert_equal([0, 1, 2, 3, 4, 5], py3eval('l')) + py3 l = ll[-10:10] + call assert_equal([0, 1, 2, 3, 4, 5], py3eval('l')) + py3 l = ll[4:2:-1] + call assert_equal([4, 3], py3eval('l')) + py3 l = ll[::2] + call assert_equal([0, 2, 4], py3eval('l')) + py3 l = ll[4:2:1] + call assert_equal([], py3eval('l')) + py3 del l +endfunc + +" Vars +func Test_python3_vars() + let g:foo = 'bac' + let w:abc3 = 'def' + let b:baz = 'bar' + let t:bar = 'jkl' + try + throw "Abc" + catch /Abc/ + call assert_equal('Abc', py3eval('vim.vvars[''exception'']')) + endtry + call assert_equal('bac', py3eval('vim.vars[''foo'']')) + call assert_equal('def', py3eval('vim.current.window.vars[''abc3'']')) + call assert_equal('bar', py3eval('vim.current.buffer.vars[''baz'']')) + call assert_equal('jkl', py3eval('vim.current.tabpage.vars[''bar'']')) +endfunc + +" Options +" paste: boolean, global +" previewheight number, global +" operatorfunc: string, global +" number: boolean, window-local +" numberwidth: number, window-local +" colorcolumn: string, window-local +" statusline: string, window-local/global +" autoindent: boolean, buffer-local +" shiftwidth: number, buffer-local +" omnifunc: string, buffer-local +" preserveindent: boolean, buffer-local/global +" path: string, buffer-local/global +func Test_python3_opts() + let g:res = [] + let g:bufs = [bufnr('%')] + new + let g:bufs += [bufnr('%')] + vnew + let g:bufs += [bufnr('%')] + wincmd j + vnew + let g:bufs += [bufnr('%')] + wincmd l + + func RecVars(opt) + let gval = string(eval('&g:' .. a:opt)) + let wvals = join(map(range(1, 4), + \ 'v:val .. ":" .. string(getwinvar(v:val, "&" .. a:opt))')) + let bvals = join(map(copy(g:bufs), + \ 'v:val .. ":" .. string(getbufvar(v:val, "&" .. a:opt))')) + call add(g:res, ' G: ' .. gval) + call add(g:res, ' W: ' .. wvals) + call add(g:res, ' B: ' .. wvals) + endfunc + + py3 << trim EOF + def e(s, g=globals(), l=locals()): + try: + exec(s, g, l) + except Exception as e: + vim.command('return ' + repr(e.__class__.__name__)) + + def ev(s, g=globals(), l=locals()): + try: + return eval(s, g, l) + except Exception as e: + vim.command('let exc=' + repr(e.__class__.__name__)) + return 0 + EOF + + func E(s) + python3 e(vim.eval('a:s')) + endfunc + + func Ev(s) + let r = py3eval('ev(vim.eval("a:s"))') + if exists('exc') + throw exc + endif + return r + endfunc + + py3 gopts1 = vim.options + py3 wopts1 = vim.windows[2].options + py3 wopts2 = vim.windows[0].options + py3 wopts3 = vim.windows[1].options + py3 bopts1 = vim.buffers[vim.bindeval("g:bufs")[2]].options + py3 bopts2 = vim.buffers[vim.bindeval("g:bufs")[1]].options + py3 bopts3 = vim.buffers[vim.bindeval("g:bufs")[0]].options + call add(g:res, 'wopts iters equal: ' .. + \ py3eval('list(wopts1) == list(wopts2)')) + call add(g:res, 'bopts iters equal: ' .. + \ py3eval('list(bopts1) == list(bopts2)')) + py3 gset = set(iter(gopts1)) + py3 wset = set(iter(wopts1)) + py3 bset = set(iter(bopts1)) + + set path=.,..,, + let lst = [] + let lst += [['paste', 1, 0, 1, 2, 1, 1, 0]] + let lst += [['previewheight', 5, 1, 6, 'a', 0, 1, 0]] + let lst += [['operatorfunc', 'A', 'B', 'C', 2, 0, 1, 0]] + let lst += [['number', 0, 1, 1, 0, 1, 0, 1]] + let lst += [['numberwidth', 2, 3, 5, -100, 0, 0, 1]] + let lst += [['colorcolumn', '+1', '+2', '+3', 'abc4', 0, 0, 1]] + let lst += [['statusline', '1', '2', '4', 0, 0, 1, 1]] + let lst += [['autoindent', 0, 1, 1, 2, 1, 0, 2]] + let lst += [['shiftwidth', 0, 2, 1, 3, 0, 0, 2]] + let lst += [['omnifunc', 'A', 'B', 'C', 1, 0, 0, 2]] + let lst += [['preserveindent', 0, 1, 1, 2, 1, 1, 2]] + let lst += [['path', '.,,', ',,', '.', 0, 0, 1, 2]] + for [oname, oval1, oval2, oval3, invval, bool, global, local] in lst + py3 oname = vim.eval('oname') + py3 oval1 = vim.bindeval('oval1') + py3 oval2 = vim.bindeval('oval2') + py3 oval3 = vim.bindeval('oval3') + if invval is 0 || invval is 1 + py3 invval = bool(vim.bindeval('invval')) + else + py3 invval = vim.bindeval('invval') + endif + if bool + py3 oval1 = bool(oval1) + py3 oval2 = bool(oval2) + py3 oval3 = bool(oval3) + endif + call add(g:res, '>>> ' .. oname) + call add(g:res, ' g/w/b:' .. py3eval('oname in gset') .. '/' .. + \ py3eval('oname in wset') .. '/' .. py3eval('oname in bset')) + call add(g:res, ' g/w/b (in):' .. py3eval('oname in gopts1') .. '/' .. + \ py3eval('oname in wopts1') .. '/' .. py3eval('oname in bopts1')) + for v in ['gopts1', 'wopts1', 'bopts1'] + try + call add(g:res, ' p/' .. v .. ': ' .. Ev('repr(' .. v .. '[''' .. oname .. '''])')) + catch + call add(g:res, ' p/' .. v .. '! ' .. v:exception) + endtry + let r = E(v .. '[''' .. oname .. ''']=invval') + if r isnot 0 + call add(g:res, ' inv: ' .. string(invval) .. '! ' .. r) + endif + for vv in (v is# 'gopts1' ? [v] : [v, v[:-2] .. '2', v[:-2] .. '3']) + let val = substitute(vv, '^.opts', 'oval', '') + let r = E(vv .. '[''' .. oname .. ''']=' .. val) + if r isnot 0 + call add(g:res, ' ' .. vv .. '! ' .. r) + endif + endfor + endfor + call RecVars(oname) + for v in ['wopts3', 'bopts3'] + let r = E('del ' .. v .. '["' .. oname .. '"]') + if r isnot 0 + call add(g:res, ' del ' .. v .. '! ' .. r) + endif + endfor + call RecVars(oname) + endfor + delfunction RecVars + delfunction E + delfunction Ev + py3 del ev + py3 del e + only + for buf in g:bufs[1:] + execute 'bwipeout!' buf + endfor + py3 del gopts1 + py3 del wopts1 + py3 del wopts2 + py3 del wopts3 + py3 del bopts1 + py3 del bopts2 + py3 del bopts3 + py3 del oval1 + py3 del oval2 + py3 del oval3 + py3 del oname + py3 del invval + + let expected =<< trim END + wopts iters equal: 1 + bopts iters equal: 1 + >>> paste + g/w/b:1/0/0 + g/w/b (in):1/0/0 + p/gopts1: False + p/wopts1! KeyError + inv: 2! KeyError + wopts1! KeyError + wopts2! KeyError + wopts3! KeyError + p/bopts1! KeyError + inv: 2! KeyError + bopts1! KeyError + bopts2! KeyError + bopts3! KeyError + G: 1 + W: 1:1 2:1 3:1 4:1 + B: 1:1 2:1 3:1 4:1 + del wopts3! KeyError + del bopts3! KeyError + G: 1 + W: 1:1 2:1 3:1 4:1 + B: 1:1 2:1 3:1 4:1 + >>> previewheight + g/w/b:1/0/0 + g/w/b (in):1/0/0 + p/gopts1: 12 + inv: 'a'! TypeError + p/wopts1! KeyError + inv: 'a'! KeyError + wopts1! KeyError + wopts2! KeyError + wopts3! KeyError + p/bopts1! KeyError + inv: 'a'! KeyError + bopts1! KeyError + bopts2! KeyError + bopts3! KeyError + G: 5 + W: 1:5 2:5 3:5 4:5 + B: 1:5 2:5 3:5 4:5 + del wopts3! KeyError + del bopts3! KeyError + G: 5 + W: 1:5 2:5 3:5 4:5 + B: 1:5 2:5 3:5 4:5 + >>> operatorfunc + g/w/b:1/0/0 + g/w/b (in):1/0/0 + p/gopts1: b'' + inv: 2! TypeError + p/wopts1! KeyError + inv: 2! KeyError + wopts1! KeyError + wopts2! KeyError + wopts3! KeyError + p/bopts1! KeyError + inv: 2! KeyError + bopts1! KeyError + bopts2! KeyError + bopts3! KeyError + G: 'A' + W: 1:'A' 2:'A' 3:'A' 4:'A' + B: 1:'A' 2:'A' 3:'A' 4:'A' + del wopts3! KeyError + del bopts3! KeyError + G: 'A' + W: 1:'A' 2:'A' 3:'A' 4:'A' + B: 1:'A' 2:'A' 3:'A' 4:'A' + >>> number + g/w/b:0/1/0 + g/w/b (in):0/1/0 + p/gopts1! KeyError + inv: 0! KeyError + gopts1! KeyError + p/wopts1: False + p/bopts1! KeyError + inv: 0! KeyError + bopts1! KeyError + bopts2! KeyError + bopts3! KeyError + G: 0 + W: 1:1 2:1 3:0 4:0 + B: 1:1 2:1 3:0 4:0 + del wopts3! ValueError + del bopts3! KeyError + G: 0 + W: 1:1 2:1 3:0 4:0 + B: 1:1 2:1 3:0 4:0 + >>> numberwidth + g/w/b:0/1/0 + g/w/b (in):0/1/0 + p/gopts1! KeyError + inv: -100! KeyError + gopts1! KeyError + p/wopts1: 4 + inv: -100! error + p/bopts1! KeyError + inv: -100! KeyError + bopts1! KeyError + bopts2! KeyError + bopts3! KeyError + G: 4 + W: 1:3 2:5 3:2 4:4 + B: 1:3 2:5 3:2 4:4 + del wopts3! ValueError + del bopts3! KeyError + G: 4 + W: 1:3 2:5 3:2 4:4 + B: 1:3 2:5 3:2 4:4 + >>> colorcolumn + g/w/b:0/1/0 + g/w/b (in):0/1/0 + p/gopts1! KeyError + inv: 'abc4'! KeyError + gopts1! KeyError + p/wopts1: b'' + inv: 'abc4'! error + p/bopts1! KeyError + inv: 'abc4'! KeyError + bopts1! KeyError + bopts2! KeyError + bopts3! KeyError + G: '' + W: 1:'+2' 2:'+3' 3:'+1' 4:'' + B: 1:'+2' 2:'+3' 3:'+1' 4:'' + del wopts3! ValueError + del bopts3! KeyError + G: '' + W: 1:'+2' 2:'+3' 3:'+1' 4:'' + B: 1:'+2' 2:'+3' 3:'+1' 4:'' + >>> statusline + g/w/b:1/1/0 + g/w/b (in):1/1/0 + p/gopts1: b'' + inv: 0! TypeError + p/wopts1: None + inv: 0! TypeError + p/bopts1! KeyError + inv: 0! KeyError + bopts1! KeyError + bopts2! KeyError + bopts3! KeyError + G: '1' + W: 1:'2' 2:'4' 3:'1' 4:'1' + B: 1:'2' 2:'4' 3:'1' 4:'1' + del bopts3! KeyError + G: '1' + W: 1:'2' 2:'1' 3:'1' 4:'1' + B: 1:'2' 2:'1' 3:'1' 4:'1' + >>> autoindent + g/w/b:0/0/1 + g/w/b (in):0/0/1 + p/gopts1! KeyError + inv: 2! KeyError + gopts1! KeyError + p/wopts1! KeyError + inv: 2! KeyError + wopts1! KeyError + wopts2! KeyError + wopts3! KeyError + p/bopts1: False + G: 0 + W: 1:0 2:1 3:0 4:1 + B: 1:0 2:1 3:0 4:1 + del wopts3! KeyError + del bopts3! ValueError + G: 0 + W: 1:0 2:1 3:0 4:1 + B: 1:0 2:1 3:0 4:1 + >>> shiftwidth + g/w/b:0/0/1 + g/w/b (in):0/0/1 + p/gopts1! KeyError + inv: 3! KeyError + gopts1! KeyError + p/wopts1! KeyError + inv: 3! KeyError + wopts1! KeyError + wopts2! KeyError + wopts3! KeyError + p/bopts1: 8 + G: 8 + W: 1:0 2:2 3:8 4:1 + B: 1:0 2:2 3:8 4:1 + del wopts3! KeyError + del bopts3! ValueError + G: 8 + W: 1:0 2:2 3:8 4:1 + B: 1:0 2:2 3:8 4:1 + >>> omnifunc + g/w/b:0/0/1 + g/w/b (in):0/0/1 + p/gopts1! KeyError + inv: 1! KeyError + gopts1! KeyError + p/wopts1! KeyError + inv: 1! KeyError + wopts1! KeyError + wopts2! KeyError + wopts3! KeyError + p/bopts1: b'' + inv: 1! TypeError + G: '' + W: 1:'A' 2:'B' 3:'' 4:'C' + B: 1:'A' 2:'B' 3:'' 4:'C' + del wopts3! KeyError + del bopts3! ValueError + G: '' + W: 1:'A' 2:'B' 3:'' 4:'C' + B: 1:'A' 2:'B' 3:'' 4:'C' + >>> preserveindent + g/w/b:0/0/1 + g/w/b (in):0/0/1 + p/gopts1! KeyError + inv: 2! KeyError + gopts1! KeyError + p/wopts1! KeyError + inv: 2! KeyError + wopts1! KeyError + wopts2! KeyError + wopts3! KeyError + p/bopts1: False + G: 0 + W: 1:0 2:1 3:0 4:1 + B: 1:0 2:1 3:0 4:1 + del wopts3! KeyError + del bopts3! ValueError + G: 0 + W: 1:0 2:1 3:0 4:1 + B: 1:0 2:1 3:0 4:1 + >>> path + g/w/b:1/0/1 + g/w/b (in):1/0/1 + p/gopts1: b'.,..,,' + inv: 0! TypeError + p/wopts1! KeyError + inv: 0! KeyError + wopts1! KeyError + wopts2! KeyError + wopts3! KeyError + p/bopts1: None + inv: 0! TypeError + G: '.,,' + W: 1:'.,,' 2:',,' 3:'.,,' 4:'.' + B: 1:'.,,' 2:',,' 3:'.,,' 4:'.' + del wopts3! KeyError + G: '.,,' + W: 1:'.,,' 2:',,' 3:'.,,' 4:'.,,' + B: 1:'.,,' 2:',,' 3:'.,,' 4:'.,,' + END + + call assert_equal(expected, g:res) + unlet g:res +endfunc + +" Test for vim.buffer object +func Test_python3_buffer() + new + call setline(1, "Hello\nWorld") + call assert_fails("let x = py3eval('vim.current.buffer[0]')", 'E859:') + %bw! + + edit Xfile1 + let bnr1 = bufnr() + py3 cb = vim.current.buffer + vnew Xfile2 + let bnr2 = bufnr() + call setline(1, ['First line', 'Second line', 'Third line']) + py3 b = vim.current.buffer + wincmd w + + " Tests BufferAppend and BufferItem + py3 cb.append(b[0]) + call assert_equal(['First line'], getbufline(bnr1, 2)) + %d + + " Tests BufferSlice and BufferAssSlice + py3 cb.append('abc5') # Will be overwritten + py3 cb[-1:] = b[:-2] + call assert_equal(['First line'], getbufline(bnr1, 2)) + %d + + " Test BufferLength and BufferAssSlice + py3 cb.append('def') # Will not be overwritten + py3 cb[len(cb):] = b[:] + call assert_equal(['def', 'First line', 'Second line', 'Third line'], + \ getbufline(bnr1, 2, '$')) + %d + + " Test BufferAssItem and BufferMark + call setbufline(bnr1, 1, ['one', 'two', 'three']) + call cursor(1, 3) + normal ma + py3 cb.append('ghi') # Will be overwritten + py3 cb[-1] = repr((len(cb) - cb.mark('a')[0], cb.mark('a')[1])) + call assert_equal(['(3, 2)'], getbufline(bnr1, 4)) + %d + + " Test BufferRepr + py3 cb.append(repr(cb) + repr(b)) + call assert_equal([''], getbufline(bnr1, 2)) + %d + + " Modify foreign buffer + py3 << trim EOF + b.append('foo') + b[0]='bar' + b[0:0]=['baz'] + vim.command('call append("$", getbufline(%i, 1, "$"))' % b.number) + EOF + call assert_equal(['baz', 'bar', 'Second line', 'Third line', 'foo'], + \ getbufline(bnr2, 1, '$')) + %d + + " Test assigning to name property + augroup BUFS + autocmd BufFilePost * python3 cb.append(vim.eval('expand("")') + ':BufFilePost:' + vim.eval('bufnr("%")')) + autocmd BufFilePre * python3 cb.append(vim.eval('expand("")') + ':BufFilePre:' + vim.eval('bufnr("%")')) + augroup END + py3 << trim EOF + import os + old_name = cb.name + cb.name = 'foo' + cb.append(cb.name[-11:].replace(os.path.sep, '/')) + b.name = 'bar' + cb.append(b.name[-11:].replace(os.path.sep, '/')) + cb.name = old_name + cb.append(cb.name[-14:].replace(os.path.sep, '/')) + del old_name + EOF + call assert_equal([bnr1 .. ':BufFilePre:' .. bnr1, + \ bnr1 .. ':BufFilePost:' .. bnr1, + \ 'testdir/foo', + \ bnr2 .. ':BufFilePre:' .. bnr2, + \ bnr2 .. ':BufFilePost:' .. bnr2, + \ 'testdir/bar', + \ bnr1 .. ':BufFilePre:' .. bnr1, + \ bnr1 .. ':BufFilePost:' .. bnr1, + \ 'testdir/Xfile1'], getbufline(bnr1, 2, '$')) + %d + + " Test CheckBuffer + py3 << trim EOF + for _b in vim.buffers: + if _b is not cb: + vim.command('bwipeout! ' + str(_b.number)) + del _b + cb.append('valid: b:%s, cb:%s' % (repr(b.valid), repr(cb.valid))) + EOF + call assert_equal('valid: b:False, cb:True', getline(2)) + %d + + py3 << trim EOF + for expr in ('b[1]','b[:] = ["A", "B"]','b[:]','b.append("abc6")'): + try: + exec(expr) + except vim.error: + pass + else: + # Usually a SEGV here + # Should not happen in any case + cb.append('No exception for ' + expr) + vim.command('cd .') + del b + EOF + call assert_equal([''], getline(1, '$')) + + augroup BUFS + autocmd! + augroup END + augroup! BUFS + %bw! +endfunc + +" Test vim.buffers object +func Test_python3_buffers() + %bw! + edit Xfile + py3 cb = vim.current.buffer + set hidden + edit a + buffer # + edit b + buffer # + edit c + buffer # + py3 << trim EOF + # Check GCing iterator that was not fully exhausted + i = iter(vim.buffers) + cb.append('i:' + str(next(i))) + # and also check creating more than one iterator at a time + i2 = iter(vim.buffers) + cb.append('i2:' + str(next(i2))) + cb.append('i:' + str(next(i))) + # The following should trigger GC and not cause any problems + del i + del i2 + i3 = iter(vim.buffers) + cb.append('i3:' + str(next(i3))) + del i3 + EOF + call assert_equal(['i:', + \ 'i2:', 'i:', 'i3:'], + \ getline(2, '$')) + %d + + py3 << trim EOF + prevnum = 0 + for b in vim.buffers: + # Check buffer order + if prevnum >= b.number: + cb.append('!!! Buffer numbers not in strictly ascending order') + # Check indexing: vim.buffers[number].number == number + cb.append(str(b.number) + ':' + repr(vim.buffers[b.number]) + \ + '=' + repr(b)) + prevnum = b.number + del prevnum + + cb.append(str(len(vim.buffers))) + EOF + call assert_equal([bufnr('Xfile') .. ':=', + \ bufnr('a') .. ':=', + \ bufnr('b') .. ':=', + \ bufnr('c') .. ':=', '4'], getline(2, '$')) + %d + + py3 << trim EOF + bnums = list(map(lambda b: b.number, vim.buffers))[1:] + + # Test wiping out buffer with existing iterator + i4 = iter(vim.buffers) + cb.append('i4:' + str(next(i4))) + vim.command('bwipeout! ' + str(bnums.pop(0))) + try: + next(i4) + except vim.error: + pass + else: + cb.append('!!!! No vim.error') + i4 = iter(vim.buffers) + vim.command('bwipeout! ' + str(bnums.pop(-1))) + vim.command('bwipeout! ' + str(bnums.pop(-1))) + cb.append('i4:' + str(next(i4))) + try: + next(i4) + except StopIteration: + cb.append('StopIteration') + del i4 + del bnums + EOF + call assert_equal(['i4:', + \ 'i4:', 'StopIteration'], getline(2, '$')) + %bw! +endfunc + +" Test vim.{tabpage,window}list and vim.{tabpage,window} objects +func Test_python3_tabpage_window() + %bw + edit Xfile + py3 cb = vim.current.buffer + tabnew 0 + tabnew 1 + vnew a.1 + tabnew 2 + vnew a.2 + vnew b.2 + vnew c.2 + + py3 << trim EOF + cb.append('Number of tabs: ' + str(len(vim.tabpages))) + cb.append('Current tab pages:') + def W(w): + if '(unknown)' in repr(w): + return '' + else: + return repr(w) + + def Cursor(w, start=len(cb)): + if w.buffer is cb: + return repr((start - w.cursor[0], w.cursor[1])) + else: + return repr(w.cursor) + + for t in vim.tabpages: + cb.append(' ' + repr(t) + '(' + str(t.number) + ')' + ': ' + \ + str(len(t.windows)) + ' windows, current is ' + W(t.window)) + cb.append(' Windows:') + for w in t.windows: + cb.append(' ' + W(w) + '(' + str(w.number) + ')' + \ + ': displays buffer ' + repr(w.buffer) + \ + '; cursor is at ' + Cursor(w)) + # Other values depend on the size of the terminal, so they are checked + # partly: + for attr in ('height', 'row', 'width', 'col'): + try: + aval = getattr(w, attr) + if type(aval) is not int: + raise TypeError + if aval < 0: + raise ValueError + except Exception as e: + cb.append('!!!!!! Error while getting attribute ' + attr + \ + ': ' + e.__class__.__name__) + del aval + del attr + w.cursor = (len(w.buffer), 0) + del W + del Cursor + cb.append('Number of windows in current tab page: ' + \ + str(len(vim.windows))) + if list(vim.windows) != list(vim.current.tabpage.windows): + cb.append('!!!!!! Windows differ') + EOF + + let expected =<< trim END + Number of tabs: 4 + Current tab pages: + (1): 1 windows, current is + Windows: + (1): displays buffer ; cursor is at (2, 0) + (2): 1 windows, current is + Windows: + (1): displays buffer ; cursor is at (1, 0) + (3): 2 windows, current is + Windows: + (1): displays buffer ; cursor is at (1, 0) + (2): displays buffer ; cursor is at (1, 0) + (4): 4 windows, current is + Windows: + (1): displays buffer ; cursor is at (1, 0) + (2): displays buffer ; cursor is at (1, 0) + (3): displays buffer ; cursor is at (1, 0) + (4): displays buffer ; cursor is at (1, 0) + Number of windows in current tab page: 4 + END + call assert_equal(expected, getbufline(bufnr('Xfile'), 2, '$')) + %bw! +endfunc + +" Test vim.current +func Test_python3_vim_current() + %bw + edit Xfile + py3 cb = vim.current.buffer + tabnew 0 + tabnew 1 + vnew a.1 + tabnew 2 + vnew a.2 + vnew b.2 + vnew c.2 + + py3 << trim EOF + def H(o): + return repr(o) + cb.append('Current tab page: ' + repr(vim.current.tabpage)) + cb.append('Current window: ' + repr(vim.current.window) + ': ' + \ + H(vim.current.window) + ' is ' + H(vim.current.tabpage.window)) + cb.append('Current buffer: ' + repr(vim.current.buffer) + ': ' + \ + H(vim.current.buffer) + ' is ' + H(vim.current.window.buffer)+ \ + ' is ' + H(vim.current.tabpage.window.buffer)) + del H + EOF + let expected =<< trim END + Current tab page: + Current window: : is + Current buffer: : is is + END + call assert_equal(expected, getbufline(bufnr('Xfile'), 2, '$')) + call deletebufline(bufnr('Xfile'), 1, '$') + + " Assigning: fails + py3 << trim EOF + try: + vim.current.window = vim.tabpages[0].window + except ValueError: + cb.append('ValueError at assigning foreign tab window') + + for attr in ('window', 'tabpage', 'buffer'): + try: + setattr(vim.current, attr, None) + except TypeError: + cb.append('Type error at assigning None to vim.current.' + attr) + del attr + EOF + + let expected =<< trim END + ValueError at assigning foreign tab window + Type error at assigning None to vim.current.window + Type error at assigning None to vim.current.tabpage + Type error at assigning None to vim.current.buffer + END + call assert_equal(expected, getbufline(bufnr('Xfile'), 2, '$')) + call deletebufline(bufnr('Xfile'), 1, '$') + + call setbufline(bufnr('Xfile'), 1, 'python interface') + py3 << trim EOF + # Assigning: success + vim.current.tabpage = vim.tabpages[-2] + vim.current.buffer = cb + vim.current.window = vim.windows[0] + vim.current.window.cursor = (len(vim.current.buffer), 0) + cb.append('Current tab page: ' + repr(vim.current.tabpage)) + cb.append('Current window: ' + repr(vim.current.window)) + cb.append('Current buffer: ' + repr(vim.current.buffer)) + cb.append('Current line: ' + repr(vim.current.line)) + EOF + + let expected =<< trim END + Current tab page: + Current window: + Current buffer: + Current line: 'python interface' + END + call assert_equal(expected, getbufline(bufnr('Xfile'), 2, '$')) + call deletebufline(bufnr('Xfile'), 1, '$') + + py3 << trim EOF + ws = list(vim.windows) + ts = list(vim.tabpages) + for b in vim.buffers: + if b is not cb: + vim.command('bwipeout! ' + str(b.number)) + del b + cb.append('w.valid: ' + repr([w.valid for w in ws])) + cb.append('t.valid: ' + repr([t.valid for t in ts])) + del w + del t + del ts + del ws + EOF + let expected =<< trim END + w.valid: [True, False] + t.valid: [True, False, True, False] + END + call assert_equal(expected, getbufline(bufnr('Xfile'), 2, '$')) + %bw! +endfunc + +" Test types +func Test_python3_types() + %d + py3 cb = vim.current.buffer + py3 << trim EOF + for expr, attr in ( + ('vim.vars', 'Dictionary'), + ('vim.options', 'Options'), + ('vim.bindeval("{}")', 'Dictionary'), + ('vim.bindeval("[]")', 'List'), + ('vim.bindeval("function(\'tr\')")', 'Function'), + ('vim.current.buffer', 'Buffer'), + ('vim.current.range', 'Range'), + ('vim.current.window', 'Window'), + ('vim.current.tabpage', 'TabPage'), + ): + cb.append(expr + ':' + attr + ':' + \ + repr(type(eval(expr)) is getattr(vim, attr))) + del expr + del attr + EOF + let expected =<< trim END + vim.vars:Dictionary:True + vim.options:Options:True + vim.bindeval("{}"):Dictionary:True + vim.bindeval("[]"):List:True + vim.bindeval("function('tr')"):Function:True + vim.current.buffer:Buffer:True + vim.current.range:Range:True + vim.current.window:Window:True + vim.current.tabpage:TabPage:True + END + call assert_equal(expected, getline(2, '$')) +endfunc + +" Test __dir__() method +func Test_python3_dir_method() + %d + py3 cb = vim.current.buffer + py3 << trim EOF + for name, o in ( + ('current', vim.current), + ('buffer', vim.current.buffer), + ('window', vim.current.window), + ('tabpage', vim.current.tabpage), + ('range', vim.current.range), + ('dictionary', vim.bindeval('{}')), + ('list', vim.bindeval('[]')), + ('function', vim.bindeval('function("tr")')), + ('output', sys.stdout), + ): + cb.append(name + ':' + ','.join(dir(o))) + del name + del o + EOF + let expected =<< trim END + current:__dir__,buffer,line,range,tabpage,window + buffer:__dir__,append,mark,name,number,options,range,valid,vars + window:__dir__,buffer,col,cursor,height,number,options,row,tabpage,valid,vars,width + tabpage:__dir__,number,valid,vars,window,windows + range:__dir__,append,end,start + dictionary:__dir__,get,has_key,items,keys,locked,pop,popitem,scope,update,values + list:__dir__,extend,locked + function:__dir__,args,auto_rebind,self,softspace + output:__dir__,close,closed,flush,isatty,readable,seekable,softspace,writable,write,writelines + END + call assert_equal(expected, getline(2, '$')) +endfunc + +" Test vim.*.__new__ +func Test_python3_new() + call assert_equal({}, py3eval('vim.Dictionary({})')) + call assert_equal({'a': 1}, py3eval('vim.Dictionary(a=1)')) + call assert_equal({'a': 1}, py3eval('vim.Dictionary(((''a'', 1),))')) + call assert_equal([], py3eval('vim.List()')) + call assert_equal(['a', 'b', 'c', '7'], py3eval('vim.List(iter(''abc7''))')) + call assert_equal(function('tr'), py3eval('vim.Function(''tr'')')) + call assert_equal(function('tr', [123, 3, 4]), + \ py3eval('vim.Function(''tr'', args=[123, 3, 4])')) + call assert_equal(function('tr'), py3eval('vim.Function(''tr'', args=[])')) + call assert_equal(function('tr', {}), + \ py3eval('vim.Function(''tr'', self={})')) + call assert_equal(function('tr', [123, 3, 4], {}), + \ py3eval('vim.Function(''tr'', args=[123, 3, 4], self={})')) + call assert_equal(function('tr'), + \ py3eval('vim.Function(''tr'', auto_rebind=False)')) + call assert_equal(function('tr', [123, 3, 4]), + \ py3eval('vim.Function(''tr'', args=[123, 3, 4], auto_rebind=False)')) + call assert_equal(function('tr'), + \ py3eval('vim.Function(''tr'', args=[], auto_rebind=False)')) + call assert_equal(function('tr', {}), + \ py3eval('vim.Function(''tr'', self={}, auto_rebind=False)')) + call assert_equal(function('tr', [123, 3, 4], {}), + \ py3eval('vim.Function(''tr'', args=[123, 3, 4], self={}, auto_rebind=False)')) +endfunc + +" Test vim.Function +func Test_python3_vim_func() + function Args(...) + return a:000 + endfunc + + function SelfArgs(...) dict + return [a:000, self] + endfunc + + " The following four lines should not crash + let Pt = function('tr', [[]], {'l': []}) + py3 Pt = vim.bindeval('Pt') + unlet Pt + py3 del Pt + + %bw! + py3 cb = vim.current.buffer + py3 << trim EOF + def ecall(out_prefix, func, *args, **kwargs): + line = out_prefix + ': ' + try: + ret = func(*args, **kwargs) + except Exception: + line += '!exception: ' + emsg(sys.exc_info()) + else: + line += '!result: ' + str(vim.Function('string')(ret), 'utf-8') + cb.append(line) + a = vim.Function('Args') + pa1 = vim.Function('Args', args=['abcArgsPA1']) + pa2 = vim.Function('Args', args=[]) + pa3 = vim.Function('Args', args=['abcArgsPA3'], self={'abcSelfPA3': 'abcSelfPA3Val'}) + pa4 = vim.Function('Args', self={'abcSelfPA4': 'abcSelfPA4Val'}) + cb.append('a: ' + repr(a)) + cb.append('pa1: ' + repr(pa1)) + cb.append('pa2: ' + repr(pa2)) + cb.append('pa3: ' + repr(pa3)) + cb.append('pa4: ' + repr(pa4)) + sa = vim.Function('SelfArgs') + psa1 = vim.Function('SelfArgs', args=['abcArgsPSA1']) + psa2 = vim.Function('SelfArgs', args=[]) + psa3 = vim.Function('SelfArgs', args=['abcArgsPSA3'], self={'abcSelfPSA3': 'abcSelfPSA3Val'}) + psa4 = vim.Function('SelfArgs', self={'abcSelfPSA4': 'abcSelfPSA4Val'}) + psa5 = vim.Function('SelfArgs', self={'abcSelfPSA5': 'abcSelfPSA5Val'}, auto_rebind=0) + psa6 = vim.Function('SelfArgs', args=['abcArgsPSA6'], self={'abcSelfPSA6': 'abcSelfPSA6Val'}, auto_rebind=()) + psa7 = vim.Function('SelfArgs', args=['abcArgsPSA7'], auto_rebind=[]) + psa8 = vim.Function('SelfArgs', auto_rebind=False) + psa9 = vim.Function('SelfArgs', self={'abcSelfPSA9': 'abcSelfPSA9Val'}, auto_rebind=True) + psaA = vim.Function('SelfArgs', args=['abcArgsPSAA'], self={'abcSelfPSAA': 'abcSelfPSAAVal'}, auto_rebind=1) + psaB = vim.Function('SelfArgs', args=['abcArgsPSAB'], auto_rebind={'abcARPSAB': 'abcARPSABVal'}) + psaC = vim.Function('SelfArgs', auto_rebind=['abcARPSAC']) + cb.append('sa: ' + repr(sa)) + cb.append('psa1: ' + repr(psa1)) + cb.append('psa2: ' + repr(psa2)) + cb.append('psa3: ' + repr(psa3)) + cb.append('psa4: ' + repr(psa4)) + cb.append('psa5: ' + repr(psa5)) + cb.append('psa6: ' + repr(psa6)) + cb.append('psa7: ' + repr(psa7)) + cb.append('psa8: ' + repr(psa8)) + cb.append('psa9: ' + repr(psa9)) + cb.append('psaA: ' + repr(psaA)) + cb.append('psaB: ' + repr(psaB)) + cb.append('psaC: ' + repr(psaC)) + + psar = vim.Function('SelfArgs', args=[{'abcArgsPSAr': 'abcArgsPSArVal'}], self={'abcSelfPSAr': 'abcSelfPSArVal'}) + psar.args[0]['abcArgsPSAr2'] = [psar.self, psar.args[0]] + psar.self['rec'] = psar + psar.self['self'] = psar.self + psar.self['args'] = psar.args + + try: + cb.append('psar: ' + repr(psar)) + except Exception: + cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info())) + EOF + + let expected =<< trim END + a: + pa1: + pa2: + pa3: + pa4: + sa: + psa1: + psa2: + psa3: + psa4: + psa5: + psa6: + psa7: + psa8: + psa9: + psaA: + psaB: + psaC: + psar: + END + call assert_equal(expected, getline(2, '$')) + %d + + call assert_equal(function('Args'), py3eval('a')) + call assert_equal(function('Args', ['abcArgsPA1']), py3eval('pa1')) + call assert_equal(function('Args'), py3eval('pa2')) + call assert_equal(function('Args', ['abcArgsPA3'], {'abcSelfPA3': 'abcSelfPA3Val'}), py3eval('pa3')) + call assert_equal(function('Args', {'abcSelfPA4': 'abcSelfPA4Val'}), py3eval('pa4')) + call assert_equal(function('SelfArgs'), py3eval('sa')) + call assert_equal(function('SelfArgs', ['abcArgsPSA1']), py3eval('psa1')) + call assert_equal(function('SelfArgs'), py3eval('psa2')) + call assert_equal(function('SelfArgs', ['abcArgsPSA3'], {'abcSelfPSA3': 'abcSelfPSA3Val'}), py3eval('psa3')) + call assert_equal(function('SelfArgs', {'abcSelfPSA4': 'abcSelfPSA4Val'}), py3eval('psa4')) + call assert_equal(function('SelfArgs', {'abcSelfPSA5': 'abcSelfPSA5Val'}), py3eval('psa5')) + call assert_equal(function('SelfArgs', ['abcArgsPSA6'], {'abcSelfPSA6': 'abcSelfPSA6Val'}), py3eval('psa6')) + call assert_equal(function('SelfArgs', ['abcArgsPSA7']), py3eval('psa7')) + call assert_equal(function('SelfArgs'), py3eval('psa8')) + call assert_equal(function('SelfArgs', {'abcSelfPSA9': 'abcSelfPSA9Val'}), py3eval('psa9')) + call assert_equal(function('SelfArgs', ['abcArgsPSAA'], {'abcSelfPSAA': 'abcSelfPSAAVal'}), py3eval('psaA')) + call assert_equal(function('SelfArgs', ['abcArgsPSAB']), py3eval('psaB')) + call assert_equal(function('SelfArgs'), py3eval('psaC')) + + let res = [] + for v in ['sa', 'psa1', 'psa2', 'psa3', 'psa4', 'psa5', 'psa6', 'psa7', + \ 'psa8', 'psa9', 'psaA', 'psaB', 'psaC'] + let d = {'f': py3eval(v)} + call add(res, 'd.' .. v .. '(): ' .. string(d.f())) + endfor + + let expected =<< trim END + d.sa(): [[], {'f': function('SelfArgs')}] + d.psa1(): [['abcArgsPSA1'], {'f': function('SelfArgs', ['abcArgsPSA1'])}] + d.psa2(): [[], {'f': function('SelfArgs')}] + d.psa3(): [['abcArgsPSA3'], {'abcSelfPSA3': 'abcSelfPSA3Val'}] + d.psa4(): [[], {'abcSelfPSA4': 'abcSelfPSA4Val'}] + d.psa5(): [[], {'abcSelfPSA5': 'abcSelfPSA5Val'}] + d.psa6(): [['abcArgsPSA6'], {'abcSelfPSA6': 'abcSelfPSA6Val'}] + d.psa7(): [['abcArgsPSA7'], {'f': function('SelfArgs', ['abcArgsPSA7'])}] + d.psa8(): [[], {'f': function('SelfArgs')}] + d.psa9(): [[], {'f': function('SelfArgs', {'abcSelfPSA9': 'abcSelfPSA9Val'})}] + d.psaA(): [['abcArgsPSAA'], {'f': function('SelfArgs', ['abcArgsPSAA'], {'abcSelfPSAA': 'abcSelfPSAAVal'})}] + d.psaB(): [['abcArgsPSAB'], {'f': function('SelfArgs', ['abcArgsPSAB'])}] + d.psaC(): [[], {'f': function('SelfArgs')}] + END + call assert_equal(expected, res) + + py3 ecall('a()', a, ) + py3 ecall('pa1()', pa1, ) + py3 ecall('pa2()', pa2, ) + py3 ecall('pa3()', pa3, ) + py3 ecall('pa4()', pa4, ) + py3 ecall('sa()', sa, ) + py3 ecall('psa1()', psa1, ) + py3 ecall('psa2()', psa2, ) + py3 ecall('psa3()', psa3, ) + py3 ecall('psa4()', psa4, ) + + py3 ecall('a(42, 43)', a, 42, 43) + py3 ecall('pa1(42, 43)', pa1, 42, 43) + py3 ecall('pa2(42, 43)', pa2, 42, 43) + py3 ecall('pa3(42, 43)', pa3, 42, 43) + py3 ecall('pa4(42, 43)', pa4, 42, 43) + py3 ecall('sa(42, 43)', sa, 42, 43) + py3 ecall('psa1(42, 43)', psa1, 42, 43) + py3 ecall('psa2(42, 43)', psa2, 42, 43) + py3 ecall('psa3(42, 43)', psa3, 42, 43) + py3 ecall('psa4(42, 43)', psa4, 42, 43) + + py3 ecall('a(42, self={"20": 1})', a, 42, self={'20': 1}) + py3 ecall('pa1(42, self={"20": 1})', pa1, 42, self={'20': 1}) + py3 ecall('pa2(42, self={"20": 1})', pa2, 42, self={'20': 1}) + py3 ecall('pa3(42, self={"20": 1})', pa3, 42, self={'20': 1}) + py3 ecall('pa4(42, self={"20": 1})', pa4, 42, self={'20': 1}) + py3 ecall('sa(42, self={"20": 1})', sa, 42, self={'20': 1}) + py3 ecall('psa1(42, self={"20": 1})', psa1, 42, self={'20': 1}) + py3 ecall('psa2(42, self={"20": 1})', psa2, 42, self={'20': 1}) + py3 ecall('psa3(42, self={"20": 1})', psa3, 42, self={'20': 1}) + py3 ecall('psa4(42, self={"20": 1})', psa4, 42, self={'20': 1}) + + py3 ecall('a(self={"20": 1})', a, self={'20': 1}) + py3 ecall('pa1(self={"20": 1})', pa1, self={'20': 1}) + py3 ecall('pa2(self={"20": 1})', pa2, self={'20': 1}) + py3 ecall('pa3(self={"20": 1})', pa3, self={'20': 1}) + py3 ecall('pa4(self={"20": 1})', pa4, self={'20': 1}) + py3 ecall('sa(self={"20": 1})', sa, self={'20': 1}) + py3 ecall('psa1(self={"20": 1})', psa1, self={'20': 1}) + py3 ecall('psa2(self={"20": 1})', psa2, self={'20': 1}) + py3 ecall('psa3(self={"20": 1})', psa3, self={'20': 1}) + py3 ecall('psa4(self={"20": 1})', psa4, self={'20': 1}) + + py3 << trim EOF + def s(v): + if v is None: + return repr(v) + else: + return str(vim.Function('string')(v), 'utf-8') + + cb.append('a.args: ' + s(a.args)) + cb.append('pa1.args: ' + s(pa1.args)) + cb.append('pa2.args: ' + s(pa2.args)) + cb.append('pa3.args: ' + s(pa3.args)) + cb.append('pa4.args: ' + s(pa4.args)) + cb.append('sa.args: ' + s(sa.args)) + cb.append('psa1.args: ' + s(psa1.args)) + cb.append('psa2.args: ' + s(psa2.args)) + cb.append('psa3.args: ' + s(psa3.args)) + cb.append('psa4.args: ' + s(psa4.args)) + + cb.append('a.self: ' + s(a.self)) + cb.append('pa1.self: ' + s(pa1.self)) + cb.append('pa2.self: ' + s(pa2.self)) + cb.append('pa3.self: ' + s(pa3.self)) + cb.append('pa4.self: ' + s(pa4.self)) + cb.append('sa.self: ' + s(sa.self)) + cb.append('psa1.self: ' + s(psa1.self)) + cb.append('psa2.self: ' + s(psa2.self)) + cb.append('psa3.self: ' + s(psa3.self)) + cb.append('psa4.self: ' + s(psa4.self)) + + cb.append('a.name: ' + s(a.name)) + cb.append('pa1.name: ' + s(pa1.name)) + cb.append('pa2.name: ' + s(pa2.name)) + cb.append('pa3.name: ' + s(pa3.name)) + cb.append('pa4.name: ' + s(pa4.name)) + cb.append('sa.name: ' + s(sa.name)) + cb.append('psa1.name: ' + s(psa1.name)) + cb.append('psa2.name: ' + s(psa2.name)) + cb.append('psa3.name: ' + s(psa3.name)) + cb.append('psa4.name: ' + s(psa4.name)) + + cb.append('a.auto_rebind: ' + s(a.auto_rebind)) + cb.append('pa1.auto_rebind: ' + s(pa1.auto_rebind)) + cb.append('pa2.auto_rebind: ' + s(pa2.auto_rebind)) + cb.append('pa3.auto_rebind: ' + s(pa3.auto_rebind)) + cb.append('pa4.auto_rebind: ' + s(pa4.auto_rebind)) + cb.append('sa.auto_rebind: ' + s(sa.auto_rebind)) + cb.append('psa1.auto_rebind: ' + s(psa1.auto_rebind)) + cb.append('psa2.auto_rebind: ' + s(psa2.auto_rebind)) + cb.append('psa3.auto_rebind: ' + s(psa3.auto_rebind)) + cb.append('psa4.auto_rebind: ' + s(psa4.auto_rebind)) + cb.append('psa5.auto_rebind: ' + s(psa5.auto_rebind)) + cb.append('psa6.auto_rebind: ' + s(psa6.auto_rebind)) + cb.append('psa7.auto_rebind: ' + s(psa7.auto_rebind)) + cb.append('psa8.auto_rebind: ' + s(psa8.auto_rebind)) + cb.append('psa9.auto_rebind: ' + s(psa9.auto_rebind)) + cb.append('psaA.auto_rebind: ' + s(psaA.auto_rebind)) + cb.append('psaB.auto_rebind: ' + s(psaB.auto_rebind)) + cb.append('psaC.auto_rebind: ' + s(psaC.auto_rebind)) + + del s + + del a + del pa1 + del pa2 + del pa3 + del pa4 + del sa + del psa1 + del psa2 + del psa3 + del psa4 + del psa5 + del psa6 + del psa7 + del psa8 + del psa9 + del psaA + del psaB + del psaC + del psar + + del ecall + EOF + + let expected =<< trim END + a(): !result: [] + pa1(): !result: ['abcArgsPA1'] + pa2(): !result: [] + pa3(): !result: ['abcArgsPA3'] + pa4(): !result: [] + sa(): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) + psa1(): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) + psa2(): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) + psa3(): !result: [['abcArgsPSA3'], {'abcSelfPSA3': 'abcSelfPSA3Val'}] + psa4(): !result: [[], {'abcSelfPSA4': 'abcSelfPSA4Val'}] + a(42, 43): !result: [42, 43] + pa1(42, 43): !result: ['abcArgsPA1', 42, 43] + pa2(42, 43): !result: [42, 43] + pa3(42, 43): !result: ['abcArgsPA3', 42, 43] + pa4(42, 43): !result: [42, 43] + sa(42, 43): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) + psa1(42, 43): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) + psa2(42, 43): !exception: error:('Vim:E725: Calling dict function without Dictionary: SelfArgs',) + psa3(42, 43): !result: [['abcArgsPSA3', 42, 43], {'abcSelfPSA3': 'abcSelfPSA3Val'}] + psa4(42, 43): !result: [[42, 43], {'abcSelfPSA4': 'abcSelfPSA4Val'}] + a(42, self={"20": 1}): !result: [42] + pa1(42, self={"20": 1}): !result: ['abcArgsPA1', 42] + pa2(42, self={"20": 1}): !result: [42] + pa3(42, self={"20": 1}): !result: ['abcArgsPA3', 42] + pa4(42, self={"20": 1}): !result: [42] + sa(42, self={"20": 1}): !result: [[42], {'20': 1}] + psa1(42, self={"20": 1}): !result: [['abcArgsPSA1', 42], {'20': 1}] + psa2(42, self={"20": 1}): !result: [[42], {'20': 1}] + psa3(42, self={"20": 1}): !result: [['abcArgsPSA3', 42], {'20': 1}] + psa4(42, self={"20": 1}): !result: [[42], {'20': 1}] + a(self={"20": 1}): !result: [] + pa1(self={"20": 1}): !result: ['abcArgsPA1'] + pa2(self={"20": 1}): !result: [] + pa3(self={"20": 1}): !result: ['abcArgsPA3'] + pa4(self={"20": 1}): !result: [] + sa(self={"20": 1}): !result: [[], {'20': 1}] + psa1(self={"20": 1}): !result: [['abcArgsPSA1'], {'20': 1}] + psa2(self={"20": 1}): !result: [[], {'20': 1}] + psa3(self={"20": 1}): !result: [['abcArgsPSA3'], {'20': 1}] + psa4(self={"20": 1}): !result: [[], {'20': 1}] + a.args: None + pa1.args: ['abcArgsPA1'] + pa2.args: None + pa3.args: ['abcArgsPA3'] + pa4.args: None + sa.args: None + psa1.args: ['abcArgsPSA1'] + psa2.args: None + psa3.args: ['abcArgsPSA3'] + psa4.args: None + a.self: None + pa1.self: None + pa2.self: None + pa3.self: {'abcSelfPA3': 'abcSelfPA3Val'} + pa4.self: {'abcSelfPA4': 'abcSelfPA4Val'} + sa.self: None + psa1.self: None + psa2.self: None + psa3.self: {'abcSelfPSA3': 'abcSelfPSA3Val'} + psa4.self: {'abcSelfPSA4': 'abcSelfPSA4Val'} + a.name: 'Args' + pa1.name: 'Args' + pa2.name: 'Args' + pa3.name: 'Args' + pa4.name: 'Args' + sa.name: 'SelfArgs' + psa1.name: 'SelfArgs' + psa2.name: 'SelfArgs' + psa3.name: 'SelfArgs' + psa4.name: 'SelfArgs' + a.auto_rebind: 1 + pa1.auto_rebind: 1 + pa2.auto_rebind: 1 + pa3.auto_rebind: 0 + pa4.auto_rebind: 0 + sa.auto_rebind: 1 + psa1.auto_rebind: 1 + psa2.auto_rebind: 1 + psa3.auto_rebind: 0 + psa4.auto_rebind: 0 + psa5.auto_rebind: 0 + psa6.auto_rebind: 0 + psa7.auto_rebind: 1 + psa8.auto_rebind: 1 + psa9.auto_rebind: 1 + psaA.auto_rebind: 1 + psaB.auto_rebind: 1 + psaC.auto_rebind: 1 + END + call assert_equal(expected, getline(2, '$')) + %bw! +endfunc + +" Test stdout/stderr +func Test_python3_stdin_stderr() + let caught_writeerr = 0 + let caught_writelineerr = 0 + redir => messages + py3 sys.stdout.write('abc8') ; sys.stdout.write('def') + try + py3 sys.stderr.write('abc9') ; sys.stderr.write('def') + catch /abc9def/ + let caught_writeerr = 1 + endtry + py3 sys.stdout.writelines(iter('abcA')) + try + py3 sys.stderr.writelines(iter('abcB')) + catch /abcB/ + let caught_writelineerr = 1 + endtry + redir END + call assert_equal("\nabc8def\nabcA", messages) + call assert_equal(1, caught_writeerr) + call assert_equal(1, caught_writelineerr) +endfunc + +" Test subclassing +func Test_python3_subclass() + new + func Put(...) + return a:000 + endfunc + + py3 << trim EOF + class DupDict(vim.Dictionary): + def __setitem__(self, key, value): + super(DupDict, self).__setitem__(key, value) + super(DupDict, self).__setitem__('dup_' + key, value) + dd = DupDict() + dd['a'] = 'b' + + class DupList(vim.List): + def __getitem__(self, idx): + return [super(DupList, self).__getitem__(idx)] * 2 + + dl = DupList() + dl2 = DupList(iter('abcC')) + dl.extend(dl2[0]) + + class DupFun(vim.Function): + def __call__(self, arg): + return super(DupFun, self).__call__(arg, arg) + + df = DupFun('Put') + EOF + + call assert_equal(['a', 'dup_a'], sort(keys(py3eval('dd')))) + call assert_equal(['a', 'a'], py3eval('dl')) + call assert_equal(['a', 'b', 'c', 'C'], py3eval('dl2')) + call assert_equal([2, 2], py3eval('df(2)')) + call assert_equal(1, py3eval('dl') is# py3eval('dl')) + call assert_equal(1, py3eval('dd') is# py3eval('dd')) + call assert_equal(function('Put'), py3eval('df')) + delfunction Put + py3 << trim EOF + del DupDict + del DupList + del DupFun + del dd + del dl + del dl2 + del df + EOF + close! +endfunc + +" Test chdir +func Test_python3_chdir() + new Xfile + py3 cb = vim.current.buffer + py3 << trim EOF + import os + fnamemodify = vim.Function('fnamemodify') + cb.append(str(fnamemodify('.', ':p:h:t'))) + cb.append(vim.eval('@%')) + os.chdir('..') + path = fnamemodify('.', ':p:h:t') + if path != b'src': + # Running tests from a shadow directory, so move up another level + # This will result in @% looking like shadow/testdir/Xfile, hence the + # slicing to remove the leading path and path separator + os.chdir('..') + cb.append(str(fnamemodify('.', ':p:h:t'))) + cb.append(vim.eval('@%')[len(path)+1:].replace(os.path.sep, '/')) + os.chdir(path) + del path + else: + cb.append(str(fnamemodify('.', ':p:h:t'))) + cb.append(vim.eval('@%').replace(os.path.sep, '/')) + del path + os.chdir('testdir') + cb.append(str(fnamemodify('.', ':p:h:t'))) + cb.append(vim.eval('@%')) + del fnamemodify + EOF + call assert_equal(["b'testdir'", 'Xfile', "b'src'", 'testdir/Xfile', + \"b'testdir'", 'Xfile'], getline(2, '$')) + close! +endfunc + +" Test errors +func Test_python3_errors() + func F() dict + endfunc + + func D() + endfunc + + new + py3 cb = vim.current.buffer + + py3 << trim EOF + d = vim.Dictionary() + ned = vim.Dictionary(foo='bar', baz='abcD') + dl = vim.Dictionary(a=1) + dl.locked = True + l = vim.List() + ll = vim.List('abcE') + ll.locked = True + nel = vim.List('abcO') + f = vim.Function('string') + fd = vim.Function('F') + fdel = vim.Function('D') + vim.command('delfunction D') + + def subexpr_test(expr, name, subexprs): + cb.append('>>> Testing %s using %s' % (name, expr)) + for subexpr in subexprs: + ee(expr % subexpr) + cb.append('<<< Finished') + + def stringtochars_test(expr): + return subexpr_test(expr, 'StringToChars', ( + '1', # Fail type checks + 'b"\\0"', # Fail PyString_AsStringAndSize(object, , NULL) check + '"\\0"', # Fail PyString_AsStringAndSize(bytes, , NULL) check + )) + + class Mapping(object): + def __init__(self, d): + self.d = d + + def __getitem__(self, key): + return self.d[key] + + def keys(self): + return self.d.keys() + + def items(self): + return self.d.items() + + def convertfrompyobject_test(expr, recurse=True): + # pydict_to_tv + stringtochars_test(expr % '{%s : 1}') + if recurse: + convertfrompyobject_test(expr % '{"abcF" : %s}', False) + # pymap_to_tv + stringtochars_test(expr % 'Mapping({%s : 1})') + if recurse: + convertfrompyobject_test(expr % 'Mapping({"abcG" : %s})', False) + # pyseq_to_tv + iter_test(expr) + return subexpr_test(expr, 'ConvertFromPyObject', ( + 'None', # Not conversible + '{b"": 1}', # Empty key not allowed + '{"": 1}', # Same, but with unicode object + 'FailingMapping()', # + 'FailingMappingKey()', # + 'FailingNumber()', # + )) + + def convertfrompymapping_test(expr): + convertfrompyobject_test(expr) + return subexpr_test(expr, 'ConvertFromPyMapping', ( + '[]', + )) + + def iter_test(expr): + return subexpr_test(expr, '*Iter*', ( + 'FailingIter()', + 'FailingIterNext()', + )) + + def number_test(expr, natural=False, unsigned=False): + if natural: + unsigned = True + return subexpr_test(expr, 'NumberToLong', ( + '[]', + 'None', + ) + (('-1',) if unsigned else ()) + + (('0',) if natural else ())) + + class FailingTrue(object): + def __bool__(self): + raise NotImplementedError('bool') + + class FailingIter(object): + def __iter__(self): + raise NotImplementedError('iter') + + class FailingIterNext(object): + def __iter__(self): + return self + + def __next__(self): + raise NotImplementedError('next') + + class FailingIterNextN(object): + def __init__(self, n): + self.n = n + + def __iter__(self): + return self + + def __next__(self): + if self.n: + self.n -= 1 + return 1 + else: + raise NotImplementedError('next N') + + class FailingMappingKey(object): + def __getitem__(self, item): + raise NotImplementedError('getitem:mappingkey') + + def keys(self): + return list("abcH") + + class FailingMapping(object): + def __getitem__(self): + raise NotImplementedError('getitem:mapping') + + def keys(self): + raise NotImplementedError('keys') + + class FailingList(list): + def __getitem__(self, idx): + if i == 2: + raise NotImplementedError('getitem:list') + else: + return super(FailingList, self).__getitem__(idx) + + class NoArgsCall(object): + def __call__(self): + pass + + class FailingCall(object): + def __call__(self, path): + raise NotImplementedError('call') + + class FailingNumber(object): + def __int__(self): + raise NotImplementedError('int') + + cb.append("> Output") + cb.append(">> OutputSetattr") + ee('del sys.stdout.softspace') + number_test('sys.stdout.softspace = %s', unsigned=True) + number_test('sys.stderr.softspace = %s', unsigned=True) + ee('assert sys.stdout.isatty()==False') + ee('assert sys.stdout.seekable()==False') + ee('sys.stdout.close()') + ee('sys.stdout.flush()') + ee('assert sys.stderr.isatty()==False') + ee('assert sys.stderr.seekable()==False') + ee('sys.stderr.close()') + ee('sys.stderr.flush()') + ee('sys.stdout.attr = None') + cb.append(">> OutputWrite") + ee('assert sys.stdout.writable()==True') + ee('assert sys.stdout.readable()==False') + ee('assert sys.stderr.writable()==True') + ee('assert sys.stderr.readable()==False') + ee('assert sys.stdout.closed()==False') + ee('assert sys.stderr.closed()==False') + ee('assert sys.stdout.errors=="strict"') + ee('assert sys.stderr.errors=="strict"') + ee('assert sys.stdout.encoding==sys.stderr.encoding') + ee('sys.stdout.write(None)') + cb.append(">> OutputWriteLines") + ee('sys.stdout.writelines(None)') + ee('sys.stdout.writelines([1])') + iter_test('sys.stdout.writelines(%s)') + cb.append("> VimCommand") + stringtochars_test('vim.command(%s)') + ee('vim.command("", 2)') + #! Not checked: vim->python exceptions translating: checked later + cb.append("> VimToPython") + #! Not checked: everything: needs errors in internal python functions + cb.append("> VimEval") + stringtochars_test('vim.eval(%s)') + ee('vim.eval("", FailingTrue())') + #! Not checked: everything: needs errors in internal python functions + cb.append("> VimEvalPy") + stringtochars_test('vim.bindeval(%s)') + ee('vim.eval("", 2)') + #! Not checked: vim->python exceptions translating: checked later + cb.append("> VimStrwidth") + stringtochars_test('vim.strwidth(%s)') + cb.append("> VimForeachRTP") + ee('vim.foreach_rtp(None)') + ee('vim.foreach_rtp(NoArgsCall())') + ee('vim.foreach_rtp(FailingCall())') + ee('vim.foreach_rtp(int, 2)') + cb.append('> import') + old_rtp = vim.options['rtp'] + vim.options['rtp'] = os.getcwd().replace('\\', '\\\\').replace(',', '\\,') + ee('import xxx_no_such_module_xxx') + ee('import failing_import') + ee('import failing') + vim.options['rtp'] = old_rtp + del old_rtp + cb.append("> Options") + cb.append(">> OptionsItem") + ee('vim.options["abcQ"]') + ee('vim.options[""]') + stringtochars_test('vim.options[%s]') + cb.append(">> OptionsContains") + stringtochars_test('%s in vim.options') + cb.append("> Dictionary") + cb.append(">> DictionaryConstructor") + ee('vim.Dictionary("abcI")') + ##! Not checked: py_dict_alloc failure + cb.append(">> DictionarySetattr") + ee('del d.locked') + ee('d.locked = FailingTrue()') + ee('vim.vvars.locked = False') + ee('d.scope = True') + ee('d.xxx = True') + cb.append(">> _DictionaryItem") + ee('d.get("a", 2, 3)') + stringtochars_test('d.get(%s)') + ee('d.pop("a")') + ee('dl.pop("a")') + cb.append(">> DictionaryContains") + ee('"" in d') + ee('0 in d') + cb.append(">> DictionaryIterNext") + ee('for i in ned: ned["a"] = 1') + del i + cb.append(">> DictionaryAssItem") + ee('dl["b"] = 1') + stringtochars_test('d[%s] = 1') + convertfrompyobject_test('d["a"] = %s') + cb.append(">> DictionaryUpdate") + cb.append(">>> kwargs") + cb.append(">>> iter") + ee('d.update(FailingMapping())') + ee('d.update([FailingIterNext()])') + ee('d.update([FailingIterNextN(1)])') + iter_test('d.update(%s)') + convertfrompyobject_test('d.update(%s)') + stringtochars_test('d.update(((%s, 0),))') + convertfrompyobject_test('d.update((("a", %s),))') + cb.append(">> DictionaryPopItem") + ee('d.popitem(1, 2)') + cb.append(">> DictionaryHasKey") + ee('d.has_key()') + cb.append("> List") + cb.append(">> ListConstructor") + ee('vim.List(1, 2)') + ee('vim.List(a=1)') + iter_test('vim.List(%s)') + convertfrompyobject_test('vim.List([%s])') + cb.append(">> ListItem") + ee('l[1000]') + cb.append(">> ListAssItem") + ee('ll[1] = 2') + ee('l[1000] = 3') + cb.append(">> ListAssSlice") + ee('ll[1:100] = "abcJ"') + iter_test('l[:] = %s') + ee('nel[1:10:2] = "abcK"') + cb.append(repr(tuple(nel))) + ee('nel[1:10:2] = "a"') + cb.append(repr(tuple(nel))) + ee('nel[1:1:-1] = "a"') + cb.append(repr(tuple(nel))) + ee('nel[:] = FailingIterNextN(2)') + cb.append(repr(tuple(nel))) + convertfrompyobject_test('l[:] = [%s]') + cb.append(">> ListConcatInPlace") + iter_test('l.extend(%s)') + convertfrompyobject_test('l.extend([%s])') + cb.append(">> ListSetattr") + ee('del l.locked') + ee('l.locked = FailingTrue()') + ee('l.xxx = True') + cb.append("> Function") + cb.append(">> FunctionConstructor") + cb.append(">>> FunctionConstructor") + ee('vim.Function("123")') + ee('vim.Function("xxx_non_existent_function_xxx")') + ee('vim.Function("xxx#non#existent#function#xxx")') + ee('vim.Function("xxx_non_existent_function_xxx2", args=[])') + ee('vim.Function("xxx_non_existent_function_xxx3", self={})') + ee('vim.Function("xxx_non_existent_function_xxx4", args=[], self={})') + cb.append(">>> FunctionNew") + ee('vim.Function("tr", self="abcFuncSelf")') + ee('vim.Function("tr", args=427423)') + ee('vim.Function("tr", self="abcFuncSelf2", args="abcFuncArgs2")') + ee('vim.Function(self="abcFuncSelf2", args="abcFuncArgs2")') + ee('vim.Function("tr", "", self="abcFuncSelf2", args="abcFuncArgs2")') + ee('vim.Function("tr", "")') + cb.append(">> FunctionCall") + convertfrompyobject_test('f(%s)') + convertfrompymapping_test('fd(self=%s)') + cb.append("> TabPage") + cb.append(">> TabPageAttr") + ee('vim.current.tabpage.xxx') + cb.append("> TabList") + cb.append(">> TabListItem") + ee('vim.tabpages[1000]') + cb.append("> Window") + cb.append(">> WindowAttr") + ee('vim.current.window.xxx') + cb.append(">> WindowSetattr") + ee('vim.current.window.buffer = 0') + ee('vim.current.window.cursor = (100000000, 100000000)') + ee('vim.current.window.cursor = True') + number_test('vim.current.window.height = %s', unsigned=True) + number_test('vim.current.window.width = %s', unsigned=True) + ee('vim.current.window.xxxxxx = True') + cb.append("> WinList") + cb.append(">> WinListItem") + ee('vim.windows[1000]') + cb.append("> Buffer") + cb.append(">> StringToLine (indirect)") + ee('vim.current.buffer[0] = "\\na"') + ee('vim.current.buffer[0] = b"\\na"') + cb.append(">> SetBufferLine (indirect)") + ee('vim.current.buffer[0] = True') + cb.append(">> SetBufferLineList (indirect)") + ee('vim.current.buffer[:] = True') + ee('vim.current.buffer[:] = ["\\na", "bc"]') + cb.append(">> InsertBufferLines (indirect)") + ee('vim.current.buffer.append(None)') + ee('vim.current.buffer.append(["\\na", "bc"])') + ee('vim.current.buffer.append("\\nbc")') + cb.append(">> RBItem") + ee('vim.current.buffer[100000000]') + cb.append(">> RBAsItem") + ee('vim.current.buffer[100000000] = ""') + cb.append(">> BufferAttr") + ee('vim.current.buffer.xxx') + cb.append(">> BufferSetattr") + ee('vim.current.buffer.name = True') + ee('vim.current.buffer.xxx = True') + cb.append(">> BufferMark") + ee('vim.current.buffer.mark(0)') + ee('vim.current.buffer.mark("abcM")') + ee('vim.current.buffer.mark("!")') + cb.append(">> BufferRange") + ee('vim.current.buffer.range(1, 2, 3)') + cb.append("> BufMap") + cb.append(">> BufMapItem") + ee('vim.buffers[100000000]') + number_test('vim.buffers[%s]', natural=True) + cb.append("> Current") + cb.append(">> CurrentGetattr") + ee('vim.current.xxx') + cb.append(">> CurrentSetattr") + ee('vim.current.line = True') + ee('vim.current.buffer = True') + ee('vim.current.window = True') + ee('vim.current.tabpage = True') + ee('vim.current.xxx = True') + del d + del ned + del dl + del l + del ll + del nel + del f + del fd + del fdel + del subexpr_test + del stringtochars_test + del Mapping + del convertfrompyobject_test + del convertfrompymapping_test + del iter_test + del number_test + del FailingTrue + del FailingIter + del FailingIterNext + del FailingIterNextN + del FailingMapping + del FailingMappingKey + del FailingList + del NoArgsCall + del FailingCall + del FailingNumber + EOF + delfunction F + + let expected =<< trim END + > Output + >> OutputSetattr + del sys.stdout.softspace:(, AttributeError('cannot delete OutputObject attributes',)) + >>> Testing NumberToLong using sys.stdout.softspace = %s + sys.stdout.softspace = []:(, TypeError('expected int() or something supporting coercing to int(), but got list',)) + sys.stdout.softspace = None:(, TypeError('expected int() or something supporting coercing to int(), but got NoneType',)) + sys.stdout.softspace = -1:(, ValueError('number must be greater or equal to zero',)) + <<< Finished + >>> Testing NumberToLong using sys.stderr.softspace = %s + sys.stderr.softspace = []:(, TypeError('expected int() or something supporting coercing to int(), but got list',)) + sys.stderr.softspace = None:(, TypeError('expected int() or something supporting coercing to int(), but got NoneType',)) + sys.stderr.softspace = -1:(, ValueError('number must be greater or equal to zero',)) + <<< Finished + assert sys.stdout.isatty()==False:NOT FAILED + assert sys.stdout.seekable()==False:NOT FAILED + sys.stdout.close():NOT FAILED + sys.stdout.flush():NOT FAILED + assert sys.stderr.isatty()==False:NOT FAILED + assert sys.stderr.seekable()==False:NOT FAILED + sys.stderr.close():NOT FAILED + sys.stderr.flush():NOT FAILED + sys.stdout.attr = None:(, AttributeError('invalid attribute: attr',)) + >> OutputWrite + assert sys.stdout.writable()==True:NOT FAILED + assert sys.stdout.readable()==False:NOT FAILED + assert sys.stderr.writable()==True:NOT FAILED + assert sys.stderr.readable()==False:NOT FAILED + assert sys.stdout.closed()==False:NOT FAILED + assert sys.stderr.closed()==False:NOT FAILED + assert sys.stdout.errors=="strict":NOT FAILED + assert sys.stderr.errors=="strict":NOT FAILED + assert sys.stdout.encoding==sys.stderr.encoding:NOT FAILED + sys.stdout.write(None):(, TypeError("Can't convert 'NoneType' object to str implicitly",)) + >> OutputWriteLines + sys.stdout.writelines(None):(, TypeError("'NoneType' object is not iterable",)) + sys.stdout.writelines([1]):(, TypeError("Can't convert 'int' object to str implicitly",)) + >>> Testing *Iter* using sys.stdout.writelines(%s) + sys.stdout.writelines(FailingIter()):(, NotImplementedError('iter',)) + sys.stdout.writelines(FailingIterNext()):(, NotImplementedError('next',)) + <<< Finished + > VimCommand + >>> Testing StringToChars using vim.command(%s) + vim.command(1):(, TypeError('expected bytes() or str() instance, but got int',)) + vim.command(b"\0"):(, TypeError('expected bytes with no null',)) + vim.command("\0"):(, TypeError('expected bytes with no null',)) + <<< Finished + vim.command("", 2):(, TypeError('command() takes exactly one argument (2 given)',)) + > VimToPython + > VimEval + >>> Testing StringToChars using vim.eval(%s) + vim.eval(1):(, TypeError('expected bytes() or str() instance, but got int',)) + vim.eval(b"\0"):(, TypeError('expected bytes with no null',)) + vim.eval("\0"):(, TypeError('expected bytes with no null',)) + <<< Finished + vim.eval("", FailingTrue()):(, TypeError('function takes exactly 1 argument (2 given)',)) + > VimEvalPy + >>> Testing StringToChars using vim.bindeval(%s) + vim.bindeval(1):(, TypeError('expected bytes() or str() instance, but got int',)) + vim.bindeval(b"\0"):(, TypeError('expected bytes with no null',)) + vim.bindeval("\0"):(, TypeError('expected bytes with no null',)) + <<< Finished + vim.eval("", 2):(, TypeError('function takes exactly 1 argument (2 given)',)) + > VimStrwidth + >>> Testing StringToChars using vim.strwidth(%s) + vim.strwidth(1):(, TypeError('expected bytes() or str() instance, but got int',)) + vim.strwidth(b"\0"):(, TypeError('expected bytes with no null',)) + vim.strwidth("\0"):(, TypeError('expected bytes with no null',)) + <<< Finished + > VimForeachRTP + vim.foreach_rtp(None):(, TypeError("'NoneType' object is not callable",)) + vim.foreach_rtp(NoArgsCall()):(, TypeError('__call__() takes exactly 1 positional argument (2 given)',)) + vim.foreach_rtp(FailingCall()):(, NotImplementedError('call',)) + vim.foreach_rtp(int, 2):(, TypeError('foreach_rtp() takes exactly one argument (2 given)',)) + > import + import xxx_no_such_module_xxx:(, ImportError('No module named xxx_no_such_module_xxx',)) + import failing_import:(, ImportError()) + import failing:(, NotImplementedError()) + > Options + >> OptionsItem + vim.options["abcQ"]:(, KeyError('abcQ',)) + vim.options[""]:(, ValueError('empty keys are not allowed',)) + >>> Testing StringToChars using vim.options[%s] + vim.options[1]:(, TypeError('expected bytes() or str() instance, but got int',)) + vim.options[b"\0"]:(, TypeError('expected bytes with no null',)) + vim.options["\0"]:(, TypeError('expected bytes with no null',)) + <<< Finished + >> OptionsContains + >>> Testing StringToChars using %s in vim.options + 1 in vim.options:(, TypeError('expected bytes() or str() instance, but got int',)) + b"\0" in vim.options:(, TypeError('expected bytes with no null',)) + "\0" in vim.options:(, TypeError('expected bytes with no null',)) + <<< Finished + > Dictionary + >> DictionaryConstructor + vim.Dictionary("abcI"):(, ValueError('expected sequence element of size 2, but got sequence of size 1',)) + >> DictionarySetattr + del d.locked:(, AttributeError('cannot delete vim.Dictionary attributes',)) + d.locked = FailingTrue():(, NotImplementedError('bool',)) + vim.vvars.locked = False:(, TypeError('cannot modify fixed dictionary',)) + d.scope = True:(, AttributeError('cannot set attribute scope',)) + d.xxx = True:(, AttributeError('cannot set attribute xxx',)) + >> _DictionaryItem + d.get("a", 2, 3):(, TypeError('function takes at most 2 arguments (3 given)',)) + >>> Testing StringToChars using d.get(%s) + d.get(1):(, TypeError('expected bytes() or str() instance, but got int',)) + d.get(b"\0"):(, TypeError('expected bytes with no null',)) + d.get("\0"):(, TypeError('expected bytes with no null',)) + <<< Finished + d.pop("a"):(, KeyError('a',)) + dl.pop("a"):(, error('dictionary is locked',)) + >> DictionaryContains + "" in d:(, ValueError('empty keys are not allowed',)) + 0 in d:(, TypeError('expected bytes() or str() instance, but got int',)) + >> DictionaryIterNext + for i in ned: ned["a"] = 1:(, RuntimeError('hashtab changed during iteration',)) + >> DictionaryAssItem + dl["b"] = 1:(, error('dictionary is locked',)) + >>> Testing StringToChars using d[%s] = 1 + d[1] = 1:(, TypeError('expected bytes() or str() instance, but got int',)) + d[b"\0"] = 1:(, TypeError('expected bytes with no null',)) + d["\0"] = 1:(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using d["a"] = {%s : 1} + d["a"] = {1 : 1}:(, TypeError('expected bytes() or str() instance, but got int',)) + d["a"] = {b"\0" : 1}:(, TypeError('expected bytes with no null',)) + d["a"] = {"\0" : 1}:(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using d["a"] = {"abcF" : {%s : 1}} + d["a"] = {"abcF" : {1 : 1}}:(, TypeError('expected bytes() or str() instance, but got int',)) + d["a"] = {"abcF" : {b"\0" : 1}}:(, TypeError('expected bytes with no null',)) + d["a"] = {"abcF" : {"\0" : 1}}:(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using d["a"] = {"abcF" : Mapping({%s : 1})} + d["a"] = {"abcF" : Mapping({1 : 1})}:(, TypeError('expected bytes() or str() instance, but got int',)) + d["a"] = {"abcF" : Mapping({b"\0" : 1})}:(, TypeError('expected bytes with no null',)) + d["a"] = {"abcF" : Mapping({"\0" : 1})}:(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing *Iter* using d["a"] = {"abcF" : %s} + d["a"] = {"abcF" : FailingIter()}:(, TypeError('unable to convert FailingIter to a Vim structure',)) + d["a"] = {"abcF" : FailingIterNext()}:(, NotImplementedError('next',)) + <<< Finished + >>> Testing ConvertFromPyObject using d["a"] = {"abcF" : %s} + d["a"] = {"abcF" : None}:NOT FAILED + d["a"] = {"abcF" : {b"": 1}}:(, ValueError('empty keys are not allowed',)) + d["a"] = {"abcF" : {"": 1}}:(, ValueError('empty keys are not allowed',)) + d["a"] = {"abcF" : FailingMapping()}:(, NotImplementedError('keys',)) + d["a"] = {"abcF" : FailingMappingKey()}:(, NotImplementedError('getitem:mappingkey',)) + d["a"] = {"abcF" : FailingNumber()}:(, NotImplementedError('int',)) + <<< Finished + >>> Testing StringToChars using d["a"] = Mapping({%s : 1}) + d["a"] = Mapping({1 : 1}):(, TypeError('expected bytes() or str() instance, but got int',)) + d["a"] = Mapping({b"\0" : 1}):(, TypeError('expected bytes with no null',)) + d["a"] = Mapping({"\0" : 1}):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using d["a"] = Mapping({"abcG" : {%s : 1}}) + d["a"] = Mapping({"abcG" : {1 : 1}}):(, TypeError('expected bytes() or str() instance, but got int',)) + d["a"] = Mapping({"abcG" : {b"\0" : 1}}):(, TypeError('expected bytes with no null',)) + d["a"] = Mapping({"abcG" : {"\0" : 1}}):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using d["a"] = Mapping({"abcG" : Mapping({%s : 1})}) + d["a"] = Mapping({"abcG" : Mapping({1 : 1})}):(, TypeError('expected bytes() or str() instance, but got int',)) + d["a"] = Mapping({"abcG" : Mapping({b"\0" : 1})}):(, TypeError('expected bytes with no null',)) + d["a"] = Mapping({"abcG" : Mapping({"\0" : 1})}):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing *Iter* using d["a"] = Mapping({"abcG" : %s}) + d["a"] = Mapping({"abcG" : FailingIter()}):(, TypeError('unable to convert FailingIter to a Vim structure',)) + d["a"] = Mapping({"abcG" : FailingIterNext()}):(, NotImplementedError('next',)) + <<< Finished + >>> Testing ConvertFromPyObject using d["a"] = Mapping({"abcG" : %s}) + d["a"] = Mapping({"abcG" : None}):NOT FAILED + d["a"] = Mapping({"abcG" : {b"": 1}}):(, ValueError('empty keys are not allowed',)) + d["a"] = Mapping({"abcG" : {"": 1}}):(, ValueError('empty keys are not allowed',)) + d["a"] = Mapping({"abcG" : FailingMapping()}):(, NotImplementedError('keys',)) + d["a"] = Mapping({"abcG" : FailingMappingKey()}):(, NotImplementedError('getitem:mappingkey',)) + d["a"] = Mapping({"abcG" : FailingNumber()}):(, NotImplementedError('int',)) + <<< Finished + >>> Testing *Iter* using d["a"] = %s + d["a"] = FailingIter():(, TypeError('unable to convert FailingIter to a Vim structure',)) + d["a"] = FailingIterNext():(, NotImplementedError('next',)) + <<< Finished + >>> Testing ConvertFromPyObject using d["a"] = %s + d["a"] = None:NOT FAILED + d["a"] = {b"": 1}:(, ValueError('empty keys are not allowed',)) + d["a"] = {"": 1}:(, ValueError('empty keys are not allowed',)) + d["a"] = FailingMapping():(, NotImplementedError('keys',)) + d["a"] = FailingMappingKey():(, NotImplementedError('getitem:mappingkey',)) + d["a"] = FailingNumber():(, NotImplementedError('int',)) + <<< Finished + >> DictionaryUpdate + >>> kwargs + >>> iter + d.update(FailingMapping()):(, NotImplementedError('keys',)) + d.update([FailingIterNext()]):(, NotImplementedError('next',)) + d.update([FailingIterNextN(1)]):(, NotImplementedError('next N',)) + >>> Testing *Iter* using d.update(%s) + d.update(FailingIter()):(, NotImplementedError('iter',)) + d.update(FailingIterNext()):(, NotImplementedError('next',)) + <<< Finished + >>> Testing StringToChars using d.update({%s : 1}) + d.update({1 : 1}):(, TypeError('expected bytes() or str() instance, but got int',)) + d.update({b"\0" : 1}):(, TypeError('expected bytes with no null',)) + d.update({"\0" : 1}):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using d.update({"abcF" : {%s : 1}}) + d.update({"abcF" : {1 : 1}}):(, TypeError('expected bytes() or str() instance, but got int',)) + d.update({"abcF" : {b"\0" : 1}}):(, TypeError('expected bytes with no null',)) + d.update({"abcF" : {"\0" : 1}}):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using d.update({"abcF" : Mapping({%s : 1})}) + d.update({"abcF" : Mapping({1 : 1})}):(, TypeError('expected bytes() or str() instance, but got int',)) + d.update({"abcF" : Mapping({b"\0" : 1})}):(, TypeError('expected bytes with no null',)) + d.update({"abcF" : Mapping({"\0" : 1})}):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing *Iter* using d.update({"abcF" : %s}) + d.update({"abcF" : FailingIter()}):(, TypeError('unable to convert FailingIter to a Vim structure',)) + d.update({"abcF" : FailingIterNext()}):(, NotImplementedError('next',)) + <<< Finished + >>> Testing ConvertFromPyObject using d.update({"abcF" : %s}) + d.update({"abcF" : None}):NOT FAILED + d.update({"abcF" : {b"": 1}}):(, ValueError('empty keys are not allowed',)) + d.update({"abcF" : {"": 1}}):(, ValueError('empty keys are not allowed',)) + d.update({"abcF" : FailingMapping()}):(, NotImplementedError('keys',)) + d.update({"abcF" : FailingMappingKey()}):(, NotImplementedError('getitem:mappingkey',)) + d.update({"abcF" : FailingNumber()}):(, NotImplementedError('int',)) + <<< Finished + >>> Testing StringToChars using d.update(Mapping({%s : 1})) + d.update(Mapping({1 : 1})):(, TypeError('expected bytes() or str() instance, but got int',)) + d.update(Mapping({b"\0" : 1})):(, TypeError('expected bytes with no null',)) + d.update(Mapping({"\0" : 1})):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using d.update(Mapping({"abcG" : {%s : 1}})) + d.update(Mapping({"abcG" : {1 : 1}})):(, TypeError('expected bytes() or str() instance, but got int',)) + d.update(Mapping({"abcG" : {b"\0" : 1}})):(, TypeError('expected bytes with no null',)) + d.update(Mapping({"abcG" : {"\0" : 1}})):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using d.update(Mapping({"abcG" : Mapping({%s : 1})})) + d.update(Mapping({"abcG" : Mapping({1 : 1})})):(, TypeError('expected bytes() or str() instance, but got int',)) + d.update(Mapping({"abcG" : Mapping({b"\0" : 1})})):(, TypeError('expected bytes with no null',)) + d.update(Mapping({"abcG" : Mapping({"\0" : 1})})):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing *Iter* using d.update(Mapping({"abcG" : %s})) + d.update(Mapping({"abcG" : FailingIter()})):(, TypeError('unable to convert FailingIter to a Vim structure',)) + d.update(Mapping({"abcG" : FailingIterNext()})):(, NotImplementedError('next',)) + <<< Finished + >>> Testing ConvertFromPyObject using d.update(Mapping({"abcG" : %s})) + d.update(Mapping({"abcG" : None})):NOT FAILED + d.update(Mapping({"abcG" : {b"": 1}})):(, ValueError('empty keys are not allowed',)) + d.update(Mapping({"abcG" : {"": 1}})):(, ValueError('empty keys are not allowed',)) + d.update(Mapping({"abcG" : FailingMapping()})):(, NotImplementedError('keys',)) + d.update(Mapping({"abcG" : FailingMappingKey()})):(, NotImplementedError('getitem:mappingkey',)) + d.update(Mapping({"abcG" : FailingNumber()})):(, NotImplementedError('int',)) + <<< Finished + >>> Testing *Iter* using d.update(%s) + d.update(FailingIter()):(, NotImplementedError('iter',)) + d.update(FailingIterNext()):(, NotImplementedError('next',)) + <<< Finished + >>> Testing ConvertFromPyObject using d.update(%s) + d.update(None):(, TypeError("'NoneType' object is not iterable",)) + d.update({b"": 1}):(, ValueError('empty keys are not allowed',)) + d.update({"": 1}):(, ValueError('empty keys are not allowed',)) + d.update(FailingMapping()):(, NotImplementedError('keys',)) + d.update(FailingMappingKey()):(, NotImplementedError('getitem:mappingkey',)) + d.update(FailingNumber()):(, TypeError("'FailingNumber' object is not iterable",)) + <<< Finished + >>> Testing StringToChars using d.update(((%s, 0),)) + d.update(((1, 0),)):(, TypeError('expected bytes() or str() instance, but got int',)) + d.update(((b"\0", 0),)):(, TypeError('expected bytes with no null',)) + d.update((("\0", 0),)):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using d.update((("a", {%s : 1}),)) + d.update((("a", {1 : 1}),)):(, TypeError('expected bytes() or str() instance, but got int',)) + d.update((("a", {b"\0" : 1}),)):(, TypeError('expected bytes with no null',)) + d.update((("a", {"\0" : 1}),)):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using d.update((("a", {"abcF" : {%s : 1}}),)) + d.update((("a", {"abcF" : {1 : 1}}),)):(, TypeError('expected bytes() or str() instance, but got int',)) + d.update((("a", {"abcF" : {b"\0" : 1}}),)):(, TypeError('expected bytes with no null',)) + d.update((("a", {"abcF" : {"\0" : 1}}),)):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using d.update((("a", {"abcF" : Mapping({%s : 1})}),)) + d.update((("a", {"abcF" : Mapping({1 : 1})}),)):(, TypeError('expected bytes() or str() instance, but got int',)) + d.update((("a", {"abcF" : Mapping({b"\0" : 1})}),)):(, TypeError('expected bytes with no null',)) + d.update((("a", {"abcF" : Mapping({"\0" : 1})}),)):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing *Iter* using d.update((("a", {"abcF" : %s}),)) + d.update((("a", {"abcF" : FailingIter()}),)):(, TypeError('unable to convert FailingIter to a Vim structure',)) + d.update((("a", {"abcF" : FailingIterNext()}),)):(, NotImplementedError('next',)) + <<< Finished + >>> Testing ConvertFromPyObject using d.update((("a", {"abcF" : %s}),)) + d.update((("a", {"abcF" : None}),)):(, error("failed to add key 'a' to dictionary",)) + d.update((("a", {"abcF" : {b"": 1}}),)):(, ValueError('empty keys are not allowed',)) + d.update((("a", {"abcF" : {"": 1}}),)):(, ValueError('empty keys are not allowed',)) + d.update((("a", {"abcF" : FailingMapping()}),)):(, NotImplementedError('keys',)) + d.update((("a", {"abcF" : FailingMappingKey()}),)):(, NotImplementedError('getitem:mappingkey',)) + d.update((("a", {"abcF" : FailingNumber()}),)):(, NotImplementedError('int',)) + <<< Finished + >>> Testing StringToChars using d.update((("a", Mapping({%s : 1})),)) + d.update((("a", Mapping({1 : 1})),)):(, TypeError('expected bytes() or str() instance, but got int',)) + d.update((("a", Mapping({b"\0" : 1})),)):(, TypeError('expected bytes with no null',)) + d.update((("a", Mapping({"\0" : 1})),)):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using d.update((("a", Mapping({"abcG" : {%s : 1}})),)) + d.update((("a", Mapping({"abcG" : {1 : 1}})),)):(, TypeError('expected bytes() or str() instance, but got int',)) + d.update((("a", Mapping({"abcG" : {b"\0" : 1}})),)):(, TypeError('expected bytes with no null',)) + d.update((("a", Mapping({"abcG" : {"\0" : 1}})),)):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using d.update((("a", Mapping({"abcG" : Mapping({%s : 1})})),)) + d.update((("a", Mapping({"abcG" : Mapping({1 : 1})})),)):(, TypeError('expected bytes() or str() instance, but got int',)) + d.update((("a", Mapping({"abcG" : Mapping({b"\0" : 1})})),)):(, TypeError('expected bytes with no null',)) + d.update((("a", Mapping({"abcG" : Mapping({"\0" : 1})})),)):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing *Iter* using d.update((("a", Mapping({"abcG" : %s})),)) + d.update((("a", Mapping({"abcG" : FailingIter()})),)):(, TypeError('unable to convert FailingIter to a Vim structure',)) + d.update((("a", Mapping({"abcG" : FailingIterNext()})),)):(, NotImplementedError('next',)) + <<< Finished + >>> Testing ConvertFromPyObject using d.update((("a", Mapping({"abcG" : %s})),)) + d.update((("a", Mapping({"abcG" : None})),)):(, error("failed to add key 'a' to dictionary",)) + d.update((("a", Mapping({"abcG" : {b"": 1}})),)):(, ValueError('empty keys are not allowed',)) + d.update((("a", Mapping({"abcG" : {"": 1}})),)):(, ValueError('empty keys are not allowed',)) + d.update((("a", Mapping({"abcG" : FailingMapping()})),)):(, NotImplementedError('keys',)) + d.update((("a", Mapping({"abcG" : FailingMappingKey()})),)):(, NotImplementedError('getitem:mappingkey',)) + d.update((("a", Mapping({"abcG" : FailingNumber()})),)):(, NotImplementedError('int',)) + <<< Finished + >>> Testing *Iter* using d.update((("a", %s),)) + d.update((("a", FailingIter()),)):(, TypeError('unable to convert FailingIter to a Vim structure',)) + d.update((("a", FailingIterNext()),)):(, NotImplementedError('next',)) + <<< Finished + >>> Testing ConvertFromPyObject using d.update((("a", %s),)) + d.update((("a", None),)):(, error("failed to add key 'a' to dictionary",)) + d.update((("a", {b"": 1}),)):(, ValueError('empty keys are not allowed',)) + d.update((("a", {"": 1}),)):(, ValueError('empty keys are not allowed',)) + d.update((("a", FailingMapping()),)):(, NotImplementedError('keys',)) + d.update((("a", FailingMappingKey()),)):(, NotImplementedError('getitem:mappingkey',)) + d.update((("a", FailingNumber()),)):(, NotImplementedError('int',)) + <<< Finished + >> DictionaryPopItem + d.popitem(1, 2):(, TypeError('popitem() takes no arguments (2 given)',)) + >> DictionaryHasKey + d.has_key():(, TypeError('has_key() takes exactly one argument (0 given)',)) + > List + >> ListConstructor + vim.List(1, 2):(, TypeError('function takes at most 1 argument (2 given)',)) + vim.List(a=1):(, TypeError('list constructor does not accept keyword arguments',)) + >>> Testing *Iter* using vim.List(%s) + vim.List(FailingIter()):(, NotImplementedError('iter',)) + vim.List(FailingIterNext()):(, NotImplementedError('next',)) + <<< Finished + >>> Testing StringToChars using vim.List([{%s : 1}]) + vim.List([{1 : 1}]):(, TypeError('expected bytes() or str() instance, but got int',)) + vim.List([{b"\0" : 1}]):(, TypeError('expected bytes with no null',)) + vim.List([{"\0" : 1}]):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using vim.List([{"abcF" : {%s : 1}}]) + vim.List([{"abcF" : {1 : 1}}]):(, TypeError('expected bytes() or str() instance, but got int',)) + vim.List([{"abcF" : {b"\0" : 1}}]):(, TypeError('expected bytes with no null',)) + vim.List([{"abcF" : {"\0" : 1}}]):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using vim.List([{"abcF" : Mapping({%s : 1})}]) + vim.List([{"abcF" : Mapping({1 : 1})}]):(, TypeError('expected bytes() or str() instance, but got int',)) + vim.List([{"abcF" : Mapping({b"\0" : 1})}]):(, TypeError('expected bytes with no null',)) + vim.List([{"abcF" : Mapping({"\0" : 1})}]):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing *Iter* using vim.List([{"abcF" : %s}]) + vim.List([{"abcF" : FailingIter()}]):(, TypeError('unable to convert FailingIter to a Vim structure',)) + vim.List([{"abcF" : FailingIterNext()}]):(, NotImplementedError('next',)) + <<< Finished + >>> Testing ConvertFromPyObject using vim.List([{"abcF" : %s}]) + vim.List([{"abcF" : None}]):NOT FAILED + vim.List([{"abcF" : {b"": 1}}]):(, ValueError('empty keys are not allowed',)) + vim.List([{"abcF" : {"": 1}}]):(, ValueError('empty keys are not allowed',)) + vim.List([{"abcF" : FailingMapping()}]):(, NotImplementedError('keys',)) + vim.List([{"abcF" : FailingMappingKey()}]):(, NotImplementedError('getitem:mappingkey',)) + vim.List([{"abcF" : FailingNumber()}]):(, NotImplementedError('int',)) + <<< Finished + >>> Testing StringToChars using vim.List([Mapping({%s : 1})]) + vim.List([Mapping({1 : 1})]):(, TypeError('expected bytes() or str() instance, but got int',)) + vim.List([Mapping({b"\0" : 1})]):(, TypeError('expected bytes with no null',)) + vim.List([Mapping({"\0" : 1})]):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using vim.List([Mapping({"abcG" : {%s : 1}})]) + vim.List([Mapping({"abcG" : {1 : 1}})]):(, TypeError('expected bytes() or str() instance, but got int',)) + vim.List([Mapping({"abcG" : {b"\0" : 1}})]):(, TypeError('expected bytes with no null',)) + vim.List([Mapping({"abcG" : {"\0" : 1}})]):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using vim.List([Mapping({"abcG" : Mapping({%s : 1})})]) + vim.List([Mapping({"abcG" : Mapping({1 : 1})})]):(, TypeError('expected bytes() or str() instance, but got int',)) + vim.List([Mapping({"abcG" : Mapping({b"\0" : 1})})]):(, TypeError('expected bytes with no null',)) + vim.List([Mapping({"abcG" : Mapping({"\0" : 1})})]):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing *Iter* using vim.List([Mapping({"abcG" : %s})]) + vim.List([Mapping({"abcG" : FailingIter()})]):(, TypeError('unable to convert FailingIter to a Vim structure',)) + vim.List([Mapping({"abcG" : FailingIterNext()})]):(, NotImplementedError('next',)) + <<< Finished + >>> Testing ConvertFromPyObject using vim.List([Mapping({"abcG" : %s})]) + vim.List([Mapping({"abcG" : None})]):NOT FAILED + vim.List([Mapping({"abcG" : {b"": 1}})]):(, ValueError('empty keys are not allowed',)) + vim.List([Mapping({"abcG" : {"": 1}})]):(, ValueError('empty keys are not allowed',)) + vim.List([Mapping({"abcG" : FailingMapping()})]):(, NotImplementedError('keys',)) + vim.List([Mapping({"abcG" : FailingMappingKey()})]):(, NotImplementedError('getitem:mappingkey',)) + vim.List([Mapping({"abcG" : FailingNumber()})]):(, NotImplementedError('int',)) + <<< Finished + >>> Testing *Iter* using vim.List([%s]) + vim.List([FailingIter()]):(, TypeError('unable to convert FailingIter to a Vim structure',)) + vim.List([FailingIterNext()]):(, NotImplementedError('next',)) + <<< Finished + >>> Testing ConvertFromPyObject using vim.List([%s]) + vim.List([None]):NOT FAILED + vim.List([{b"": 1}]):(, ValueError('empty keys are not allowed',)) + vim.List([{"": 1}]):(, ValueError('empty keys are not allowed',)) + vim.List([FailingMapping()]):(, NotImplementedError('keys',)) + vim.List([FailingMappingKey()]):(, NotImplementedError('getitem:mappingkey',)) + vim.List([FailingNumber()]):(, NotImplementedError('int',)) + <<< Finished + >> ListItem + l[1000]:(, IndexError('list index out of range',)) + >> ListAssItem + ll[1] = 2:(, error('list is locked',)) + l[1000] = 3:(, IndexError('list index out of range',)) + >> ListAssSlice + ll[1:100] = "abcJ":(, error('list is locked',)) + >>> Testing *Iter* using l[:] = %s + l[:] = FailingIter():(, NotImplementedError('iter',)) + l[:] = FailingIterNext():(, NotImplementedError('next',)) + <<< Finished + nel[1:10:2] = "abcK":(, ValueError('attempt to assign sequence of size greater than 2 to extended slice',)) + (b'a', b'b', b'c', b'O') + nel[1:10:2] = "a":(, ValueError('attempt to assign sequence of size 1 to extended slice of size 2',)) + (b'a', b'b', b'c', b'O') + nel[1:1:-1] = "a":(, ValueError('attempt to assign sequence of size greater than 0 to extended slice',)) + (b'a', b'b', b'c', b'O') + nel[:] = FailingIterNextN(2):(, NotImplementedError('next N',)) + (b'a', b'b', b'c', b'O') + >>> Testing StringToChars using l[:] = [{%s : 1}] + l[:] = [{1 : 1}]:(, TypeError('expected bytes() or str() instance, but got int',)) + l[:] = [{b"\0" : 1}]:(, TypeError('expected bytes with no null',)) + l[:] = [{"\0" : 1}]:(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using l[:] = [{"abcF" : {%s : 1}}] + l[:] = [{"abcF" : {1 : 1}}]:(, TypeError('expected bytes() or str() instance, but got int',)) + l[:] = [{"abcF" : {b"\0" : 1}}]:(, TypeError('expected bytes with no null',)) + l[:] = [{"abcF" : {"\0" : 1}}]:(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using l[:] = [{"abcF" : Mapping({%s : 1})}] + l[:] = [{"abcF" : Mapping({1 : 1})}]:(, TypeError('expected bytes() or str() instance, but got int',)) + l[:] = [{"abcF" : Mapping({b"\0" : 1})}]:(, TypeError('expected bytes with no null',)) + l[:] = [{"abcF" : Mapping({"\0" : 1})}]:(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing *Iter* using l[:] = [{"abcF" : %s}] + l[:] = [{"abcF" : FailingIter()}]:(, TypeError('unable to convert FailingIter to a Vim structure',)) + l[:] = [{"abcF" : FailingIterNext()}]:(, NotImplementedError('next',)) + <<< Finished + >>> Testing ConvertFromPyObject using l[:] = [{"abcF" : %s}] + l[:] = [{"abcF" : None}]:NOT FAILED + l[:] = [{"abcF" : {b"": 1}}]:(, ValueError('empty keys are not allowed',)) + l[:] = [{"abcF" : {"": 1}}]:(, ValueError('empty keys are not allowed',)) + l[:] = [{"abcF" : FailingMapping()}]:(, NotImplementedError('keys',)) + l[:] = [{"abcF" : FailingMappingKey()}]:(, NotImplementedError('getitem:mappingkey',)) + l[:] = [{"abcF" : FailingNumber()}]:(, NotImplementedError('int',)) + <<< Finished + >>> Testing StringToChars using l[:] = [Mapping({%s : 1})] + l[:] = [Mapping({1 : 1})]:(, TypeError('expected bytes() or str() instance, but got int',)) + l[:] = [Mapping({b"\0" : 1})]:(, TypeError('expected bytes with no null',)) + l[:] = [Mapping({"\0" : 1})]:(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using l[:] = [Mapping({"abcG" : {%s : 1}})] + l[:] = [Mapping({"abcG" : {1 : 1}})]:(, TypeError('expected bytes() or str() instance, but got int',)) + l[:] = [Mapping({"abcG" : {b"\0" : 1}})]:(, TypeError('expected bytes with no null',)) + l[:] = [Mapping({"abcG" : {"\0" : 1}})]:(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using l[:] = [Mapping({"abcG" : Mapping({%s : 1})})] + l[:] = [Mapping({"abcG" : Mapping({1 : 1})})]:(, TypeError('expected bytes() or str() instance, but got int',)) + l[:] = [Mapping({"abcG" : Mapping({b"\0" : 1})})]:(, TypeError('expected bytes with no null',)) + l[:] = [Mapping({"abcG" : Mapping({"\0" : 1})})]:(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing *Iter* using l[:] = [Mapping({"abcG" : %s})] + l[:] = [Mapping({"abcG" : FailingIter()})]:(, TypeError('unable to convert FailingIter to a Vim structure',)) + l[:] = [Mapping({"abcG" : FailingIterNext()})]:(, NotImplementedError('next',)) + <<< Finished + >>> Testing ConvertFromPyObject using l[:] = [Mapping({"abcG" : %s})] + l[:] = [Mapping({"abcG" : None})]:NOT FAILED + l[:] = [Mapping({"abcG" : {b"": 1}})]:(, ValueError('empty keys are not allowed',)) + l[:] = [Mapping({"abcG" : {"": 1}})]:(, ValueError('empty keys are not allowed',)) + l[:] = [Mapping({"abcG" : FailingMapping()})]:(, NotImplementedError('keys',)) + l[:] = [Mapping({"abcG" : FailingMappingKey()})]:(, NotImplementedError('getitem:mappingkey',)) + l[:] = [Mapping({"abcG" : FailingNumber()})]:(, NotImplementedError('int',)) + <<< Finished + >>> Testing *Iter* using l[:] = [%s] + l[:] = [FailingIter()]:(, TypeError('unable to convert FailingIter to a Vim structure',)) + l[:] = [FailingIterNext()]:(, NotImplementedError('next',)) + <<< Finished + >>> Testing ConvertFromPyObject using l[:] = [%s] + l[:] = [None]:NOT FAILED + l[:] = [{b"": 1}]:(, ValueError('empty keys are not allowed',)) + l[:] = [{"": 1}]:(, ValueError('empty keys are not allowed',)) + l[:] = [FailingMapping()]:(, NotImplementedError('keys',)) + l[:] = [FailingMappingKey()]:(, NotImplementedError('getitem:mappingkey',)) + l[:] = [FailingNumber()]:(, NotImplementedError('int',)) + <<< Finished + >> ListConcatInPlace + >>> Testing *Iter* using l.extend(%s) + l.extend(FailingIter()):(, NotImplementedError('iter',)) + l.extend(FailingIterNext()):(, NotImplementedError('next',)) + <<< Finished + >>> Testing StringToChars using l.extend([{%s : 1}]) + l.extend([{1 : 1}]):(, TypeError('expected bytes() or str() instance, but got int',)) + l.extend([{b"\0" : 1}]):(, TypeError('expected bytes with no null',)) + l.extend([{"\0" : 1}]):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using l.extend([{"abcF" : {%s : 1}}]) + l.extend([{"abcF" : {1 : 1}}]):(, TypeError('expected bytes() or str() instance, but got int',)) + l.extend([{"abcF" : {b"\0" : 1}}]):(, TypeError('expected bytes with no null',)) + l.extend([{"abcF" : {"\0" : 1}}]):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using l.extend([{"abcF" : Mapping({%s : 1})}]) + l.extend([{"abcF" : Mapping({1 : 1})}]):(, TypeError('expected bytes() or str() instance, but got int',)) + l.extend([{"abcF" : Mapping({b"\0" : 1})}]):(, TypeError('expected bytes with no null',)) + l.extend([{"abcF" : Mapping({"\0" : 1})}]):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing *Iter* using l.extend([{"abcF" : %s}]) + l.extend([{"abcF" : FailingIter()}]):(, TypeError('unable to convert FailingIter to a Vim structure',)) + l.extend([{"abcF" : FailingIterNext()}]):(, NotImplementedError('next',)) + <<< Finished + >>> Testing ConvertFromPyObject using l.extend([{"abcF" : %s}]) + l.extend([{"abcF" : None}]):NOT FAILED + l.extend([{"abcF" : {b"": 1}}]):(, ValueError('empty keys are not allowed',)) + l.extend([{"abcF" : {"": 1}}]):(, ValueError('empty keys are not allowed',)) + l.extend([{"abcF" : FailingMapping()}]):(, NotImplementedError('keys',)) + l.extend([{"abcF" : FailingMappingKey()}]):(, NotImplementedError('getitem:mappingkey',)) + l.extend([{"abcF" : FailingNumber()}]):(, NotImplementedError('int',)) + <<< Finished + >>> Testing StringToChars using l.extend([Mapping({%s : 1})]) + l.extend([Mapping({1 : 1})]):(, TypeError('expected bytes() or str() instance, but got int',)) + l.extend([Mapping({b"\0" : 1})]):(, TypeError('expected bytes with no null',)) + l.extend([Mapping({"\0" : 1})]):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using l.extend([Mapping({"abcG" : {%s : 1}})]) + l.extend([Mapping({"abcG" : {1 : 1}})]):(, TypeError('expected bytes() or str() instance, but got int',)) + l.extend([Mapping({"abcG" : {b"\0" : 1}})]):(, TypeError('expected bytes with no null',)) + l.extend([Mapping({"abcG" : {"\0" : 1}})]):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using l.extend([Mapping({"abcG" : Mapping({%s : 1})})]) + l.extend([Mapping({"abcG" : Mapping({1 : 1})})]):(, TypeError('expected bytes() or str() instance, but got int',)) + l.extend([Mapping({"abcG" : Mapping({b"\0" : 1})})]):(, TypeError('expected bytes with no null',)) + l.extend([Mapping({"abcG" : Mapping({"\0" : 1})})]):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing *Iter* using l.extend([Mapping({"abcG" : %s})]) + l.extend([Mapping({"abcG" : FailingIter()})]):(, TypeError('unable to convert FailingIter to a Vim structure',)) + l.extend([Mapping({"abcG" : FailingIterNext()})]):(, NotImplementedError('next',)) + <<< Finished + >>> Testing ConvertFromPyObject using l.extend([Mapping({"abcG" : %s})]) + l.extend([Mapping({"abcG" : None})]):NOT FAILED + l.extend([Mapping({"abcG" : {b"": 1}})]):(, ValueError('empty keys are not allowed',)) + l.extend([Mapping({"abcG" : {"": 1}})]):(, ValueError('empty keys are not allowed',)) + l.extend([Mapping({"abcG" : FailingMapping()})]):(, NotImplementedError('keys',)) + l.extend([Mapping({"abcG" : FailingMappingKey()})]):(, NotImplementedError('getitem:mappingkey',)) + l.extend([Mapping({"abcG" : FailingNumber()})]):(, NotImplementedError('int',)) + <<< Finished + >>> Testing *Iter* using l.extend([%s]) + l.extend([FailingIter()]):(, TypeError('unable to convert FailingIter to a Vim structure',)) + l.extend([FailingIterNext()]):(, NotImplementedError('next',)) + <<< Finished + >>> Testing ConvertFromPyObject using l.extend([%s]) + l.extend([None]):NOT FAILED + l.extend([{b"": 1}]):(, ValueError('empty keys are not allowed',)) + l.extend([{"": 1}]):(, ValueError('empty keys are not allowed',)) + l.extend([FailingMapping()]):(, NotImplementedError('keys',)) + l.extend([FailingMappingKey()]):(, NotImplementedError('getitem:mappingkey',)) + l.extend([FailingNumber()]):(, NotImplementedError('int',)) + <<< Finished + >> ListSetattr + del l.locked:(, AttributeError('cannot delete vim.List attributes',)) + l.locked = FailingTrue():(, NotImplementedError('bool',)) + l.xxx = True:(, AttributeError('cannot set attribute xxx',)) + > Function + >> FunctionConstructor + >>> FunctionConstructor + vim.Function("123"):(, ValueError('unnamed function 123 does not exist',)) + vim.Function("xxx_non_existent_function_xxx"):(, ValueError('function xxx_non_existent_function_xxx does not exist',)) + vim.Function("xxx#non#existent#function#xxx"):NOT FAILED + vim.Function("xxx_non_existent_function_xxx2", args=[]):(, ValueError('function xxx_non_existent_function_xxx2 does not exist',)) + vim.Function("xxx_non_existent_function_xxx3", self={}):(, ValueError('function xxx_non_existent_function_xxx3 does not exist',)) + vim.Function("xxx_non_existent_function_xxx4", args=[], self={}):(, ValueError('function xxx_non_existent_function_xxx4 does not exist',)) + >>> FunctionNew + vim.Function("tr", self="abcFuncSelf"):(, AttributeError('keys',)) + vim.Function("tr", args=427423):(, TypeError('unable to convert int to a Vim list',)) + vim.Function("tr", self="abcFuncSelf2", args="abcFuncArgs2"):(, AttributeError('keys',)) + vim.Function(self="abcFuncSelf2", args="abcFuncArgs2"):(, AttributeError('keys',)) + vim.Function("tr", "", self="abcFuncSelf2", args="abcFuncArgs2"):(, AttributeError('keys',)) + vim.Function("tr", ""):(, TypeError('function takes exactly 1 argument (2 given)',)) + >> FunctionCall + >>> Testing StringToChars using f({%s : 1}) + f({1 : 1}):(, TypeError('expected bytes() or str() instance, but got int',)) + f({b"\0" : 1}):(, TypeError('expected bytes with no null',)) + f({"\0" : 1}):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using f({"abcF" : {%s : 1}}) + f({"abcF" : {1 : 1}}):(, TypeError('expected bytes() or str() instance, but got int',)) + f({"abcF" : {b"\0" : 1}}):(, TypeError('expected bytes with no null',)) + f({"abcF" : {"\0" : 1}}):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using f({"abcF" : Mapping({%s : 1})}) + f({"abcF" : Mapping({1 : 1})}):(, TypeError('expected bytes() or str() instance, but got int',)) + f({"abcF" : Mapping({b"\0" : 1})}):(, TypeError('expected bytes with no null',)) + f({"abcF" : Mapping({"\0" : 1})}):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing *Iter* using f({"abcF" : %s}) + f({"abcF" : FailingIter()}):(, TypeError('unable to convert FailingIter to a Vim structure',)) + f({"abcF" : FailingIterNext()}):(, NotImplementedError('next',)) + <<< Finished + >>> Testing ConvertFromPyObject using f({"abcF" : %s}) + f({"abcF" : None}):NOT FAILED + f({"abcF" : {b"": 1}}):(, ValueError('empty keys are not allowed',)) + f({"abcF" : {"": 1}}):(, ValueError('empty keys are not allowed',)) + f({"abcF" : FailingMapping()}):(, NotImplementedError('keys',)) + f({"abcF" : FailingMappingKey()}):(, NotImplementedError('getitem:mappingkey',)) + f({"abcF" : FailingNumber()}):(, NotImplementedError('int',)) + <<< Finished + >>> Testing StringToChars using f(Mapping({%s : 1})) + f(Mapping({1 : 1})):(, TypeError('expected bytes() or str() instance, but got int',)) + f(Mapping({b"\0" : 1})):(, TypeError('expected bytes with no null',)) + f(Mapping({"\0" : 1})):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using f(Mapping({"abcG" : {%s : 1}})) + f(Mapping({"abcG" : {1 : 1}})):(, TypeError('expected bytes() or str() instance, but got int',)) + f(Mapping({"abcG" : {b"\0" : 1}})):(, TypeError('expected bytes with no null',)) + f(Mapping({"abcG" : {"\0" : 1}})):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using f(Mapping({"abcG" : Mapping({%s : 1})})) + f(Mapping({"abcG" : Mapping({1 : 1})})):(, TypeError('expected bytes() or str() instance, but got int',)) + f(Mapping({"abcG" : Mapping({b"\0" : 1})})):(, TypeError('expected bytes with no null',)) + f(Mapping({"abcG" : Mapping({"\0" : 1})})):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing *Iter* using f(Mapping({"abcG" : %s})) + f(Mapping({"abcG" : FailingIter()})):(, TypeError('unable to convert FailingIter to a Vim structure',)) + f(Mapping({"abcG" : FailingIterNext()})):(, NotImplementedError('next',)) + <<< Finished + >>> Testing ConvertFromPyObject using f(Mapping({"abcG" : %s})) + f(Mapping({"abcG" : None})):NOT FAILED + f(Mapping({"abcG" : {b"": 1}})):(, ValueError('empty keys are not allowed',)) + f(Mapping({"abcG" : {"": 1}})):(, ValueError('empty keys are not allowed',)) + f(Mapping({"abcG" : FailingMapping()})):(, NotImplementedError('keys',)) + f(Mapping({"abcG" : FailingMappingKey()})):(, NotImplementedError('getitem:mappingkey',)) + f(Mapping({"abcG" : FailingNumber()})):(, NotImplementedError('int',)) + <<< Finished + >>> Testing *Iter* using f(%s) + f(FailingIter()):(, TypeError('unable to convert FailingIter to a Vim structure',)) + f(FailingIterNext()):(, NotImplementedError('next',)) + <<< Finished + >>> Testing ConvertFromPyObject using f(%s) + f(None):NOT FAILED + f({b"": 1}):(, ValueError('empty keys are not allowed',)) + f({"": 1}):(, ValueError('empty keys are not allowed',)) + f(FailingMapping()):(, NotImplementedError('keys',)) + f(FailingMappingKey()):(, NotImplementedError('getitem:mappingkey',)) + f(FailingNumber()):(, NotImplementedError('int',)) + <<< Finished + >>> Testing StringToChars using fd(self={%s : 1}) + fd(self={1 : 1}):(, TypeError('expected bytes() or str() instance, but got int',)) + fd(self={b"\0" : 1}):(, TypeError('expected bytes with no null',)) + fd(self={"\0" : 1}):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using fd(self={"abcF" : {%s : 1}}) + fd(self={"abcF" : {1 : 1}}):(, TypeError('expected bytes() or str() instance, but got int',)) + fd(self={"abcF" : {b"\0" : 1}}):(, TypeError('expected bytes with no null',)) + fd(self={"abcF" : {"\0" : 1}}):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using fd(self={"abcF" : Mapping({%s : 1})}) + fd(self={"abcF" : Mapping({1 : 1})}):(, TypeError('expected bytes() or str() instance, but got int',)) + fd(self={"abcF" : Mapping({b"\0" : 1})}):(, TypeError('expected bytes with no null',)) + fd(self={"abcF" : Mapping({"\0" : 1})}):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing *Iter* using fd(self={"abcF" : %s}) + fd(self={"abcF" : FailingIter()}):(, TypeError('unable to convert FailingIter to a Vim structure',)) + fd(self={"abcF" : FailingIterNext()}):(, NotImplementedError('next',)) + <<< Finished + >>> Testing ConvertFromPyObject using fd(self={"abcF" : %s}) + fd(self={"abcF" : None}):NOT FAILED + fd(self={"abcF" : {b"": 1}}):(, ValueError('empty keys are not allowed',)) + fd(self={"abcF" : {"": 1}}):(, ValueError('empty keys are not allowed',)) + fd(self={"abcF" : FailingMapping()}):(, NotImplementedError('keys',)) + fd(self={"abcF" : FailingMappingKey()}):(, NotImplementedError('getitem:mappingkey',)) + fd(self={"abcF" : FailingNumber()}):(, NotImplementedError('int',)) + <<< Finished + >>> Testing StringToChars using fd(self=Mapping({%s : 1})) + fd(self=Mapping({1 : 1})):(, TypeError('expected bytes() or str() instance, but got int',)) + fd(self=Mapping({b"\0" : 1})):(, TypeError('expected bytes with no null',)) + fd(self=Mapping({"\0" : 1})):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using fd(self=Mapping({"abcG" : {%s : 1}})) + fd(self=Mapping({"abcG" : {1 : 1}})):(, TypeError('expected bytes() or str() instance, but got int',)) + fd(self=Mapping({"abcG" : {b"\0" : 1}})):(, TypeError('expected bytes with no null',)) + fd(self=Mapping({"abcG" : {"\0" : 1}})):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing StringToChars using fd(self=Mapping({"abcG" : Mapping({%s : 1})})) + fd(self=Mapping({"abcG" : Mapping({1 : 1})})):(, TypeError('expected bytes() or str() instance, but got int',)) + fd(self=Mapping({"abcG" : Mapping({b"\0" : 1})})):(, TypeError('expected bytes with no null',)) + fd(self=Mapping({"abcG" : Mapping({"\0" : 1})})):(, TypeError('expected bytes with no null',)) + <<< Finished + >>> Testing *Iter* using fd(self=Mapping({"abcG" : %s})) + fd(self=Mapping({"abcG" : FailingIter()})):(, TypeError('unable to convert FailingIter to a Vim structure',)) + fd(self=Mapping({"abcG" : FailingIterNext()})):(, NotImplementedError('next',)) + <<< Finished + >>> Testing ConvertFromPyObject using fd(self=Mapping({"abcG" : %s})) + fd(self=Mapping({"abcG" : None})):NOT FAILED + fd(self=Mapping({"abcG" : {b"": 1}})):(, ValueError('empty keys are not allowed',)) + fd(self=Mapping({"abcG" : {"": 1}})):(, ValueError('empty keys are not allowed',)) + fd(self=Mapping({"abcG" : FailingMapping()})):(, NotImplementedError('keys',)) + fd(self=Mapping({"abcG" : FailingMappingKey()})):(, NotImplementedError('getitem:mappingkey',)) + fd(self=Mapping({"abcG" : FailingNumber()})):(, NotImplementedError('int',)) + <<< Finished + >>> Testing *Iter* using fd(self=%s) + fd(self=FailingIter()):(, TypeError('unable to convert FailingIter to a Vim dictionary',)) + fd(self=FailingIterNext()):(, TypeError('unable to convert FailingIterNext to a Vim dictionary',)) + <<< Finished + >>> Testing ConvertFromPyObject using fd(self=%s) + fd(self=None):(, TypeError('unable to convert NoneType to a Vim dictionary',)) + fd(self={b"": 1}):(, ValueError('empty keys are not allowed',)) + fd(self={"": 1}):(, ValueError('empty keys are not allowed',)) + fd(self=FailingMapping()):(, NotImplementedError('keys',)) + fd(self=FailingMappingKey()):(, NotImplementedError('getitem:mappingkey',)) + fd(self=FailingNumber()):(, TypeError('unable to convert FailingNumber to a Vim dictionary',)) + <<< Finished + >>> Testing ConvertFromPyMapping using fd(self=%s) + fd(self=[]):(, AttributeError('keys',)) + <<< Finished + > TabPage + >> TabPageAttr + vim.current.tabpage.xxx:(, AttributeError("'vim.tabpage' object has no attribute 'xxx'",)) + > TabList + >> TabListItem + vim.tabpages[1000]:(, IndexError('no such tab page',)) + > Window + >> WindowAttr + vim.current.window.xxx:(, AttributeError("'vim.window' object has no attribute 'xxx'",)) + >> WindowSetattr + vim.current.window.buffer = 0:(, TypeError('readonly attribute: buffer',)) + vim.current.window.cursor = (100000000, 100000000):(, error('cursor position outside buffer',)) + vim.current.window.cursor = True:(, TypeError('argument must be 2-item sequence, not bool',)) + >>> Testing NumberToLong using vim.current.window.height = %s + vim.current.window.height = []:(, TypeError('expected int() or something supporting coercing to int(), but got list',)) + vim.current.window.height = None:(, TypeError('expected int() or something supporting coercing to int(), but got NoneType',)) + vim.current.window.height = -1:(, ValueError('number must be greater or equal to zero',)) + <<< Finished + >>> Testing NumberToLong using vim.current.window.width = %s + vim.current.window.width = []:(, TypeError('expected int() or something supporting coercing to int(), but got list',)) + vim.current.window.width = None:(, TypeError('expected int() or something supporting coercing to int(), but got NoneType',)) + vim.current.window.width = -1:(, ValueError('number must be greater or equal to zero',)) + <<< Finished + vim.current.window.xxxxxx = True:(, AttributeError('xxxxxx',)) + > WinList + >> WinListItem + vim.windows[1000]:(, IndexError('no such window',)) + > Buffer + >> StringToLine (indirect) + vim.current.buffer[0] = "\na":(, error('string cannot contain newlines',)) + vim.current.buffer[0] = b"\na":(, error('string cannot contain newlines',)) + >> SetBufferLine (indirect) + vim.current.buffer[0] = True:(, TypeError('bad argument type for built-in operation',)) + >> SetBufferLineList (indirect) + vim.current.buffer[:] = True:(, TypeError('bad argument type for built-in operation',)) + vim.current.buffer[:] = ["\na", "bc"]:(, error('string cannot contain newlines',)) + >> InsertBufferLines (indirect) + vim.current.buffer.append(None):(, TypeError('bad argument type for built-in operation',)) + vim.current.buffer.append(["\na", "bc"]):(, error('string cannot contain newlines',)) + vim.current.buffer.append("\nbc"):(, error('string cannot contain newlines',)) + >> RBItem + vim.current.buffer[100000000]:(, IndexError('line number out of range',)) + >> RBAsItem + vim.current.buffer[100000000] = "":(, IndexError('line number out of range',)) + >> BufferAttr + vim.current.buffer.xxx:(, AttributeError("'vim.buffer' object has no attribute 'xxx'",)) + >> BufferSetattr + vim.current.buffer.name = True:(, TypeError('expected bytes() or str() instance, but got bool',)) + vim.current.buffer.xxx = True:(, AttributeError('xxx',)) + >> BufferMark + vim.current.buffer.mark(0):(, TypeError('expected bytes() or str() instance, but got int',)) + vim.current.buffer.mark("abcM"):(, ValueError('mark name must be a single character',)) + vim.current.buffer.mark("!"):(, error('invalid mark name',)) + >> BufferRange + vim.current.buffer.range(1, 2, 3):(, TypeError('function takes exactly 2 arguments (3 given)',)) + > BufMap + >> BufMapItem + vim.buffers[100000000]:(, KeyError(100000000,)) + >>> Testing NumberToLong using vim.buffers[%s] + vim.buffers[[]]:(, TypeError('expected int() or something supporting coercing to int(), but got list',)) + vim.buffers[None]:(, TypeError('expected int() or something supporting coercing to int(), but got NoneType',)) + vim.buffers[-1]:(, ValueError('number must be greater than zero',)) + vim.buffers[0]:(, ValueError('number must be greater than zero',)) + <<< Finished + > Current + >> CurrentGetattr + vim.current.xxx:(, AttributeError("'vim.currentdata' object has no attribute 'xxx'",)) + >> CurrentSetattr + vim.current.line = True:(, TypeError('bad argument type for built-in operation',)) + vim.current.buffer = True:(, TypeError('expected vim.Buffer object, but got bool',)) + vim.current.window = True:(, TypeError('expected vim.Window object, but got bool',)) + vim.current.tabpage = True:(, TypeError('expected vim.TabPage object, but got bool',)) + vim.current.xxx = True:(, AttributeError('xxx',)) + END + + call assert_equal(expected, getline(2, '$')) + close! +endfunc + +" Test import +func Test_python3_import() + new + py3 cb = vim.current.buffer + + py3 << trim EOF + sys.path.insert(0, os.path.join(os.getcwd(), 'python_before')) + sys.path.append(os.path.join(os.getcwd(), 'python_after')) + vim.options['rtp'] = os.getcwd().replace(',', '\\,').replace('\\', '\\\\') + l = [] + def callback(path): + l.append(os.path.relpath(path)) + vim.foreach_rtp(callback) + cb.append(repr(l)) + del l + def callback(path): + return os.path.relpath(path) + cb.append(repr(vim.foreach_rtp(callback))) + del callback + from module import dir as d + from modulex import ddir + cb.append(d + ',' + ddir) + import before + cb.append(before.dir) + import after + cb.append(after.dir) + import topmodule as tm + import topmodule.submodule as tms + import topmodule.submodule.subsubmodule.subsubsubmodule as tmsss + cb.append(tm.__file__.replace(os.path.sep, '/')[-len('modulex/topmodule/__init__.py'):]) + cb.append(tms.__file__.replace(os.path.sep, '/')[-len('modulex/topmodule/submodule/__init__.py'):]) + cb.append(tmsss.__file__.replace(os.path.sep, '/')[-len('modulex/topmodule/submodule/subsubmodule/subsubsubmodule.py'):]) + + del before + del after + del d + del ddir + del tm + del tms + del tmsss + EOF + + let expected =<< trim END + ['.'] + '.' + 3,xx + before + after + pythonx/topmodule/__init__.py + pythonx/topmodule/submodule/__init__.py + pythonx/topmodule/submodule/subsubmodule/subsubsubmodule.py + END + call assert_equal(expected, getline(2, '$')) + close! +endfunc + +" Test exceptions +func Test_python3_exception() + func Exe(e) + execute a:e + endfunc + + new + py3 cb = vim.current.buffer + + py3 << trim EOF + Exe = vim.bindeval('function("Exe")') + ee('vim.command("throw \'abcN\'")') + ee('Exe("throw \'def\'")') + ee('vim.eval("Exe(\'throw \'\'ghi\'\'\')")') + ee('vim.eval("Exe(\'echoerr \'\'jkl\'\'\')")') + ee('vim.eval("Exe(\'xxx_non_existent_command_xxx\')")') + ee('vim.eval("xxx_unknown_function_xxx()")') + ee('vim.bindeval("Exe(\'xxx_non_existent_command_xxx\')")') + del Exe + EOF + delfunction Exe + + let expected =<< trim END + vim.command("throw 'abcN'"):(, error('abcN',)) + Exe("throw 'def'"):(, error('def',)) + vim.eval("Exe('throw ''ghi''')"):(, error('ghi',)) + vim.eval("Exe('echoerr ''jkl''')"):(, error('Vim(echoerr):jkl',)) + vim.eval("Exe('xxx_non_existent_command_xxx')"):(, error('Vim:E492: Not an editor command: xxx_non_existent_command_xxx',)) + vim.eval("xxx_unknown_function_xxx()"):(, error('Vim:E117: Unknown function: xxx_unknown_function_xxx',)) + vim.bindeval("Exe('xxx_non_existent_command_xxx')"):(, error('Vim:E492: Not an editor command: xxx_non_existent_command_xxx',)) + END + call assert_equal(expected, getline(2, '$')) + close! +endfunc + +" Regression: interrupting vim.command propagates to next vim.command +func Test_python3_keyboard_interrupt() + new + py3 cb = vim.current.buffer + py3 << trim EOF + def test_keyboard_interrupt(): + try: + vim.command('while 1 | endwhile') + except KeyboardInterrupt: + cb.append('Caught KeyboardInterrupt') + except Exception: + cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info())) + else: + cb.append('!!!!!!!! No exception') + try: + vim.command('$ put =\'Running :put\'') + except KeyboardInterrupt: + cb.append('!!!!!!!! Caught KeyboardInterrupt') + except Exception: + cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info())) + else: + cb.append('No exception') + EOF + + debuggreedy + call inputsave() + call feedkeys("s\ns\ns\ns\nq\n") + redir => output + debug silent! py3 test_keyboard_interrupt() + redir END + 0 debuggreedy + call inputrestore() + py3 del test_keyboard_interrupt + + let expected =<< trim END + Caught KeyboardInterrupt + Running :put + No exception + END + call assert_equal(expected, getline(2, '$')) + call assert_equal('', output) + close! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 00aca2b1e4..6908662bc6 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1123, /**/ 1122, /**/ From 1c991144c502ade477e1a32fdfd0f78b6299fdc7 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 4 Jul 2020 13:15:31 +0200 Subject: [PATCH 080/105] patch 8.2.1124: Vim9: no line break allowed in :import command Problem: Vim9: no line break allowed in :import command. Solution: Skip over line breaks. --- src/proto/vim9script.pro | 4 +- src/testdir/test_vim9_script.vim | 44 +++++++ src/version.c | 2 + src/vim9compile.c | 18 ++- src/vim9script.c | 202 ++++++++++++++++++------------- 5 files changed, 179 insertions(+), 91 deletions(-) diff --git a/src/proto/vim9script.pro b/src/proto/vim9script.pro index a11f6af7a5..6a32abdcac 100644 --- a/src/proto/vim9script.pro +++ b/src/proto/vim9script.pro @@ -4,8 +4,8 @@ void ex_vim9script(exarg_T *eap); void ex_export(exarg_T *eap); void free_imports(int sid); void ex_import(exarg_T *eap); -int find_exported(int sid, char_u **argp, int *name_len, ufunc_T **ufunc, type_T **type); -char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx); +int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type); +char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid, evalarg_T *evalarg, void *cctx); char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg); int check_script_var_type(typval_T *dest, typval_T *value, char_u *name); /* vim: set ft=c : */ diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index f9301ca26b..8411621419 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -686,6 +686,35 @@ def Test_vim9_import_export() unlet g:imported_name g:imported_name_appended delete('Ximport.vim') + # similar, with line breaks + let import_line_break_script_lines =<< trim END + vim9script + import { + exported, + Exported, + } + from + './Xexport.vim' + g:imported = exported + exported += 5 + g:imported_added = exported + g:imported_func = Exported() + END + writefile(import_line_break_script_lines, 'Ximport_lbr.vim') + source Ximport_lbr.vim + + assert_equal(9876, g:imported) + assert_equal(9881, g:imported_added) + assert_equal('Exported', g:imported_func) + + # exported script not sourced again + assert_false(exists('g:result')) + unlet g:imported + unlet g:imported_added + unlet g:imported_func + delete('Ximport_lbr.vim') + + # import inside :def function let import_in_def_lines =<< trim END vim9script def ImportInDef() @@ -751,6 +780,21 @@ def Test_vim9_import_export() writefile(import_star_as_lines_missing_name, 'Ximport.vim') assert_fails('source Ximport.vim', 'E1048:') + let import_star_as_lbr_lines =<< trim END + vim9script + import * + as Export + from + './Xexport.vim' + def UseExport() + g:imported = Export.exported + enddef + UseExport() + END + writefile(import_star_as_lbr_lines, 'Ximport.vim') + source Ximport.vim + assert_equal(9883, g:imported) + let import_star_lines =<< trim END vim9script import * from './Xexport.vim' diff --git a/src/version.c b/src/version.c index 6908662bc6..2f4bb6bd55 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1124, /**/ 1123, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index bb3122f40d..9c2eaf4329 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2613,7 +2613,8 @@ compile_load_scriptvar( if (import->imp_all) { char_u *p = skipwhite(*end); - int name_len; + char_u *exp_name; + int cc; ufunc_T *ufunc; type_T *type; @@ -2630,7 +2631,17 @@ compile_load_scriptvar( return FAIL; } - idx = find_exported(import->imp_sid, &p, &name_len, &ufunc, &type); + // isolate one name + exp_name = p; + while (eval_isnamec(*p)) + ++p; + cc = *p; + *p = NUL; + + idx = find_exported(import->imp_sid, exp_name, &ufunc, &type); + *p = cc; + p = skipwhite(p); + // TODO: what if it is a function? if (idx < 0) return FAIL; @@ -2981,6 +2992,7 @@ to_name_end(char_u *arg, int namespace) /* * Like to_name_end() but also skip over a list or dict constant. + * This intentionally does not handle line continuation. */ char_u * to_name_const_end(char_u *arg) @@ -5632,7 +5644,7 @@ compile_unletlock(char_u *arg, exarg_T *eap, cctx_T *cctx) static char_u * compile_import(char_u *arg, cctx_T *cctx) { - return handle_import(arg, &cctx->ctx_imports, 0, cctx); + return handle_import(arg, &cctx->ctx_imports, 0, NULL, cctx); } /* diff --git a/src/vim9script.c b/src/vim9script.c index aa6b92cddc..14586f9b8a 100644 --- a/src/vim9script.c +++ b/src/vim9script.c @@ -142,17 +142,21 @@ free_imports(int sid) void ex_import(exarg_T *eap) { - char_u *cmd_end; + char_u *cmd_end; + evalarg_T evalarg; if (!getline_equal(eap->getline, eap->cookie, getsourceline)) { emsg(_("E1094: import can only be used in a script")); return; } + fill_evalarg_from_eap(&evalarg, eap, eap->skip); - cmd_end = handle_import(eap->arg, NULL, current_sctx.sc_sid, NULL); + cmd_end = handle_import(eap->arg, NULL, current_sctx.sc_sid, + &evalarg, NULL); if (cmd_end != NULL) eap->nextcmd = check_nextcmd(cmd_end); + clear_evalarg(&evalarg, eap); } /* @@ -164,27 +168,16 @@ ex_import(exarg_T *eap) int find_exported( int sid, - char_u **argp, - int *name_len, + char_u *name, ufunc_T **ufunc, type_T **type) { - char_u *name = *argp; - char_u *arg = *argp; - int cc; int idx = -1; svar_T *sv; scriptitem_T *script = SCRIPT_ITEM(sid); - // isolate one name - while (eval_isnamec(*arg)) - ++arg; - *name_len = (int)(arg - name); - // find name in "script" // TODO: also find script-local user function - cc = *arg; - *arg = NUL; idx = get_script_item_idx(sid, name, FALSE); if (idx >= 0) { @@ -192,7 +185,6 @@ find_exported( if (!sv->sv_export) { semsg(_("E1049: Item not exported in script: %s"), name); - *arg = cc; return -1; } *type = sv->sv_type; @@ -210,10 +202,7 @@ find_exported( { funcname = alloc(STRLEN(name) + 10); if (funcname == NULL) - { - *arg = cc; return -1; - } } funcname[0] = K_SPECIAL; funcname[1] = KS_EXTRA; @@ -226,13 +215,9 @@ find_exported( if (*ufunc == NULL) { semsg(_("E1048: Item not found in script: %s"), name); - *arg = cc; return -1; } } - *arg = cc; - arg = skipwhite(arg); - *argp = arg; return idx; } @@ -243,62 +228,121 @@ find_exported( * Returns a pointer to after the command or NULL in case of failure */ char_u * -handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx) +handle_import( + char_u *arg_start, + garray_T *gap, + int import_sid, + evalarg_T *evalarg, + void *cctx) { char_u *arg = arg_start; - char_u *cmd_end; - char_u *as_ptr = NULL; - char_u *from_ptr; - int as_len = 0; + char_u *cmd_end = NULL; + char_u *as_name = NULL; int ret = FAIL; typval_T tv; int sid = -1; int res; + garray_T names; + static char e_import_syntax[] = N_("E1047: syntax error in import"); + ga_init2(&names, sizeof(char_u *), 10); if (*arg == '{') { - // skip over {item} list - while (*arg != NUL && *arg != '}') - ++arg; - if (*arg == '}') - arg = skipwhite(arg + 1); + // "import {item, item} from ..." + arg = skipwhite_and_linebreak(arg + 1, evalarg); + for (;;) + { + char_u *p = arg; + int had_comma = FALSE; + + while (eval_isnamec(*arg)) + ++arg; + if (p == arg) + break; + if (ga_grow(&names, 1) == FAIL) + goto erret; + ((char_u **)names.ga_data)[names.ga_len] = + vim_strnsave(p, arg - p); + ++names.ga_len; + if (*arg == ',') + { + had_comma = TRUE; + ++arg; + } + arg = skipwhite_and_linebreak(arg, evalarg); + if (*arg == '}') + { + arg = skipwhite_and_linebreak(arg + 1, evalarg); + break; + } + if (!had_comma) + { + emsg(_("E1046: Missing comma in import")); + goto erret; + } + } + if (names.ga_len == 0) + { + emsg(_(e_import_syntax)); + goto erret; + } } else { - if (*arg == '*') - arg = skipwhite(arg + 1); + // "import Name from ..." + // "import * as Name from ..." + // "import item [as Name] from ..." + arg = skipwhite_and_linebreak(arg, evalarg); + if (arg[0] == '*' && IS_WHITE_OR_NUL(arg[1])) + arg = skipwhite_and_linebreak(arg + 1, evalarg); else if (eval_isnamec1(*arg)) { + char_u *p = arg; + while (eval_isnamec(*arg)) ++arg; - arg = skipwhite(arg); + if (ga_grow(&names, 1) == FAIL) + goto erret; + ((char_u **)names.ga_data)[names.ga_len] = + vim_strnsave(p, arg - p); + ++names.ga_len; + arg = skipwhite_and_linebreak(arg, evalarg); } - if (STRNCMP("as", arg, 2) == 0 && VIM_ISWHITE(arg[2])) + else { - // skip over "as Name " + emsg(_(e_import_syntax)); + goto erret; + } + + if (STRNCMP("as", arg, 2) == 0 && IS_WHITE_OR_NUL(arg[2])) + { + char_u *p; + + // skip over "as Name "; no line break allowed after "as" arg = skipwhite(arg + 2); - as_ptr = arg; + p = arg; if (eval_isnamec1(*arg)) while (eval_isnamec(*arg)) ++arg; - as_len = (int)(arg - as_ptr); - arg = skipwhite(arg); - if (check_defined(as_ptr, as_len, cctx) == FAIL) - return NULL; + if (check_defined(p, (int)(arg - p), cctx) == FAIL) + goto erret; + as_name = vim_strnsave(p, arg - p); + arg = skipwhite_and_linebreak(arg, evalarg); } else if (*arg_start == '*') { emsg(_("E1045: Missing \"as\" after *")); - return NULL; + goto erret; } } - if (STRNCMP("from", arg, 4) != 0 || !VIM_ISWHITE(arg[4])) + + if (STRNCMP("from", arg, 4) != 0 || !IS_WHITE_OR_NUL(arg[4])) { emsg(_("E1070: Missing \"from\"")); - return NULL; + goto erret; } - from_ptr = arg; - arg = skipwhite(arg + 4); + + arg = skipwhite_and_linebreak_keep_string(arg + 4, evalarg); tv.v_type = VAR_UNKNOWN; // TODO: should we accept any expression? if (*arg == '\'') @@ -308,11 +352,13 @@ handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx) if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL) { emsg(_("E1071: Invalid string after \"from\"")); - return NULL; + goto erret; } cmd_end = arg; - // find script tv.vval.v_string + /* + * find script file + */ if (*tv.vval.v_string == '.') { size_t len; @@ -326,7 +372,7 @@ handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx) if (from_name == NULL) { clear_tv(&tv); - return NULL; + goto erret; } vim_strncpy(from_name, si->sn_name, tail - si->sn_name); add_pathsep(from_name); @@ -351,7 +397,7 @@ handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx) if (from_name == NULL) { clear_tv(&tv); - return NULL; + goto erret; } vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string); res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid); @@ -362,7 +408,7 @@ handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx) { semsg(_("E1053: Could not import \"%s\""), tv.vval.v_string); clear_tv(&tv); - return NULL; + goto erret; } clear_tv(&tv); @@ -372,41 +418,44 @@ handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx) : &SCRIPT_ITEM(import_sid)->sn_imports); if (imported == NULL) - return NULL; - imported->imp_name = vim_strnsave(as_ptr, as_len); + goto erret; + imported->imp_name = as_name; + as_name = NULL; imported->imp_sid = sid; imported->imp_all = TRUE; } else { + int i; + arg = arg_start; if (*arg == '{') arg = skipwhite(arg + 1); - for (;;) + for (i = 0; i < names.ga_len; ++i) { - char_u *name = arg; - int name_len; + char_u *name = ((char_u **)names.ga_data)[i]; int idx; imported_T *imported; ufunc_T *ufunc = NULL; type_T *type; - idx = find_exported(sid, &arg, &name_len, &ufunc, &type); + idx = find_exported(sid, name, &ufunc, &type); if (idx < 0 && ufunc == NULL) - return NULL; + goto erret; - if (check_defined(name, name_len, cctx) == FAIL) - return NULL; + if (check_defined(name, STRLEN(name), cctx) == FAIL) + goto erret; imported = new_imported(gap != NULL ? gap : &SCRIPT_ITEM(import_sid)->sn_imports); if (imported == NULL) - return NULL; + goto erret; // TODO: check for "as" following - // imported->imp_name = vim_strnsave(as_ptr, as_len); - imported->imp_name = vim_strnsave(name, name_len); + // imported->imp_name = vim_strsave(as_name); + imported->imp_name = name; + ((char_u **)names.ga_data)[i] = NULL; imported->imp_sid = sid; if (idx >= 0) { @@ -415,30 +464,11 @@ handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx) } else imported->imp_funcname = ufunc->uf_name; - - arg = skipwhite(arg); - if (*arg_start != '{') - break; - if (*arg == '}') - { - arg = skipwhite(arg + 1); - break; - } - - if (*arg != ',') - { - emsg(_("E1046: Missing comma in import")); - return NULL; - } - arg = skipwhite(arg + 1); - } - if (arg != from_ptr) - { - // cannot happen, just in case the above has a flaw - emsg(_("E1047: syntax error in import")); - return NULL; } } +erret: + ga_clear_strings(&names); + vim_free(as_name); return cmd_end; } From 962d7213194647e90f9bdc608f693d39dd07cbd5 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 4 Jul 2020 14:15:00 +0200 Subject: [PATCH 081/105] patch 8.2.1125: Vim9: double quote can be a string or a comment Problem: Vim9: double quote can be a string or a comment. Solution: Only support comments starting with # to avoid confusion. --- src/dict.c | 8 ++++---- src/eval.c | 24 ++++++------------------ src/list.c | 4 ++-- src/proto/eval.pro | 1 - src/version.c | 2 ++ src/vim9script.c | 2 +- 6 files changed, 15 insertions(+), 26 deletions(-) diff --git a/src/dict.c b/src/dict.c index 53824f75d6..a549ed71b1 100644 --- a/src/dict.c +++ b/src/dict.c @@ -787,8 +787,8 @@ get_literal_key(char_u **arg, typval_T *tv) /* * Allocate a variable for a Dictionary and fill it from "*arg". + * "*arg" points to the "{". * "literal" is TRUE for #{key: val} - * "flags" can have EVAL_EVALUATE and other EVAL_ flags. * Return OK or FAIL. Returns NOTDONE for {expr}. */ int @@ -830,7 +830,7 @@ eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal) tvkey.v_type = VAR_UNKNOWN; tv.v_type = VAR_UNKNOWN; - *arg = skipwhite_and_linebreak_keep_string(*arg + 1, evalarg); + *arg = skipwhite_and_linebreak(*arg + 1, evalarg); while (**arg != '}' && **arg != NUL) { if ((literal @@ -862,7 +862,7 @@ eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal) goto failret; } - *arg = skipwhite_and_linebreak_keep_string(*arg + 1, evalarg); + *arg = skipwhite_and_linebreak(*arg + 1, evalarg); if (eval1(arg, &tv, evalarg) == FAIL) // recursive! { if (evaluate) @@ -904,7 +904,7 @@ eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal) } // the "}" can be on the next line - *arg = skipwhite_and_linebreak_keep_string(*arg, evalarg); + *arg = skipwhite_and_linebreak(*arg, evalarg); if (**arg == '}') break; if (!had_comma) diff --git a/src/eval.c b/src/eval.c index c569710ab5..1468070ae6 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1866,9 +1866,9 @@ eval_func( } /* - * If inside Vim9 script, "arg" points to the end of a line (ignoring comments) - * and there is a next line, return the next line (skipping blanks) and set - * "getnext". + * If inside Vim9 script, "arg" points to the end of a line (ignoring a # + * comment) and there is a next line, return the next line (skipping blanks) + * and set "getnext". * Otherwise just return "arg" unmodified and set "getnext" to FALSE. * "arg" must point somewhere inside a line, not at the start. */ @@ -1880,7 +1880,7 @@ eval_next_non_blank(char_u *arg, evalarg_T *evalarg, int *getnext) && evalarg != NULL && evalarg->eval_cookie != NULL && (*arg == NUL || (VIM_ISWHITE(arg[-1]) - && (*arg == '"' || *arg == '#')))) + && *arg == '#' && arg[1] != '{'))) { char_u *p = getline_peek(evalarg->eval_getline, evalarg->eval_cookie); @@ -1927,26 +1927,14 @@ skipwhite_and_linebreak(char_u *arg, evalarg_T *evalarg) int getnext; char_u *p = skipwhite(arg); + if (evalarg == NULL) + return skipwhite(arg); eval_next_non_blank(p, evalarg, &getnext); if (getnext) return eval_next_line(evalarg); return p; } -/* - * Call eval_next_non_blank() and get the next line if needed, but not when a - * double quote follows. Used inside an expression. - */ - char_u * -skipwhite_and_linebreak_keep_string(char_u *arg, evalarg_T *evalarg) -{ - char_u *p = skipwhite(arg); - - if (*p == '"') - return p; - return skipwhite_and_linebreak(arg, evalarg); -} - /* * After using "evalarg" filled from "eap" free the memory. */ diff --git a/src/list.c b/src/list.c index 9475ef99e2..e8d5f5f546 100644 --- a/src/list.c +++ b/src/list.c @@ -1177,7 +1177,7 @@ eval_list(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int do_error) return FAIL; } - *arg = skipwhite_and_linebreak_keep_string(*arg + 1, evalarg); + *arg = skipwhite_and_linebreak(*arg + 1, evalarg); while (**arg != ']' && **arg != NUL) { if (eval1(arg, &tv, evalarg) == FAIL) // recursive! @@ -1209,7 +1209,7 @@ eval_list(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int do_error) // The "]" can be on the next line. But a double quoted string may // follow, not a comment. - *arg = skipwhite_and_linebreak_keep_string(*arg, evalarg); + *arg = skipwhite_and_linebreak(*arg, evalarg); if (**arg == ']') break; diff --git a/src/proto/eval.pro b/src/proto/eval.pro index 910fdfd180..3e87907957 100644 --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -32,7 +32,6 @@ int pattern_match(char_u *pat, char_u *text, int ic); char_u *eval_next_non_blank(char_u *arg, evalarg_T *evalarg, int *getnext); char_u *eval_next_line(evalarg_T *evalarg); char_u *skipwhite_and_linebreak(char_u *arg, evalarg_T *evalarg); -char_u *skipwhite_and_linebreak_keep_string(char_u *arg, evalarg_T *evalarg); void clear_evalarg(evalarg_T *evalarg, exarg_T *eap); int eval0(char_u *arg, typval_T *rettv, exarg_T *eap, evalarg_T *evalarg); int eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg); diff --git a/src/version.c b/src/version.c index 2f4bb6bd55..b36f248f8d 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1125, /**/ 1124, /**/ diff --git a/src/vim9script.c b/src/vim9script.c index 14586f9b8a..ef447c7e35 100644 --- a/src/vim9script.c +++ b/src/vim9script.c @@ -342,7 +342,7 @@ handle_import( goto erret; } - arg = skipwhite_and_linebreak_keep_string(arg + 4, evalarg); + arg = skipwhite_and_linebreak(arg + 4, evalarg); tv.v_type = VAR_UNKNOWN; // TODO: should we accept any expression? if (*arg == '\'') From eeb27bfe28ad6f889c52628268acbe30a7584e30 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 4 Jul 2020 17:39:10 +0200 Subject: [PATCH 082/105] patch 8.2.1126: Vim9: using :copen causes an error Problem: Vim9: using :copen causes an error. Solution: Add flag LET_NO_COMMAND in set_var(). --- src/evalvars.c | 2 +- src/testdir/test_vim9_script.vim | 6 ++++++ src/version.c | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/evalvars.c b/src/evalvars.c index 131486750c..3dab22bb2e 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -2852,7 +2852,7 @@ set_var( typval_T *tv, int copy) // make copy of value in "tv" { - set_var_const(name, NULL, tv, copy, 0); + set_var_const(name, NULL, tv, copy, LET_NO_COMMAND); } /* diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index 8411621419..e548717986 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -2219,6 +2219,12 @@ def Test_source_vim9_from_legacy() delete('Xvim9_script.vim') enddef +def Test_vim9_copen() + # this was giving an error for setting w:quickfix_title + copen + quit +enddef + " Keep this last, it messes up highlighting. def Test_substitute_cmd() new diff --git a/src/version.c b/src/version.c index b36f248f8d..d1607ae15d 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1126, /**/ 1125, /**/ From fb9d5c51c8b5b44863f974e1adbee9ae330e75ff Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 4 Jul 2020 19:19:43 +0200 Subject: [PATCH 083/105] patch 8.2.1127: Vim9: getting a dict member may not work Problem: Vim9: getting a dict member may not work. Solution: Clear the dict only after copying the item. (closes #6390) --- src/testdir/test_vim9_expr.vim | 3 +++ src/version.c | 2 ++ src/vim9execute.c | 6 +++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index a604de2b0b..3bf578059f 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -1133,6 +1133,9 @@ def Test_expr_member() let d: dict = g:dict_one assert_equal(1, d['one']) + # getting the one member should clear the dict after getting the item + assert_equal('one', #{one: 'one'}.one) + call CheckDefFailure(["let x = g:dict_one.#$!"], 'E1002:') call CheckDefExecFailure(["let d: dict", "echo d['a']"], 'E716:') call CheckDefExecFailure(["let d: dict", "d = g:list_empty"], 'E1029: Expected dict but got list') diff --git a/src/version.c b/src/version.c index d1607ae15d..b406a69577 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1127, /**/ 1126, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index b4acb35c10..bcacaabaa0 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -2188,6 +2188,7 @@ call_def_function( { dict_T *dict; dictitem_T *di; + typval_T temp_tv; tv = STACK_TV_BOT(-1); if (tv->v_type != VAR_DICT || tv->vval.v_dict == NULL) @@ -2203,8 +2204,11 @@ call_def_function( semsg(_(e_dictkey), iptr->isn_arg.string); goto failed; } - clear_tv(tv); + // Clear the dict after getting the item, to avoid that it + // make the item invalid. + temp_tv = *tv; copy_tv(&di->di_tv, tv); + clear_tv(&temp_tv); } break; From 3f40ce78f5c178d15871bd784ed878c78f0b8a44 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 5 Jul 2020 14:10:13 +0200 Subject: [PATCH 084/105] patch 8.2.1128: the write message mentions characters, but it's bytes Problem: The write message mentions characters, but it's actually bytes. Solution: Change "C" to "B" and "characters" to "bytes". --- runtime/doc/options.txt | 2 +- src/fileio.c | 4 ++-- src/testdir/dumps/Test_diff_syntax_1.dump | 2 +- src/testdir/dumps/Test_display_unprintable_01.dump | 2 +- src/testdir/dumps/Test_long_file_name_1.dump | 2 +- src/testdir/dumps/Test_tselect_1.dump | 2 +- src/testdir/test_cscope.vim | 4 ++-- src/testdir/test_netbeans.vim | 4 ++-- src/version.c | 2 ++ 9 files changed, 13 insertions(+), 11 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index bd6cc62d62..109fc1614d 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -6730,7 +6730,7 @@ A jump table for the options with a short description can be found at |Q_op|. flag meaning when present ~ f use "(3 of 5)" instead of "(file 3 of 5)" i use "[noeol]" instead of "[Incomplete last line]" - l use "999L, 888C" instead of "999 lines, 888 characters" + l use "999L, 888B" instead of "999 lines, 888 bytes" m use "[+]" instead of "[Modified]" n use "[New]" instead of "[New File]" r use "[RO]" instead of "[readonly]" diff --git a/src/fileio.c b/src/fileio.c index 81a5026652..e46231029c 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -3039,13 +3039,13 @@ msg_add_lines( *p++ = ' '; if (shortmess(SHM_LINES)) vim_snprintf((char *)p, IOSIZE - (p - IObuff), - "%ldL, %lldC", lnum, (varnumber_T)nchars); + "%ldL, %lldB", lnum, (varnumber_T)nchars); else { sprintf((char *)p, NGETTEXT("%ld line, ", "%ld lines, ", lnum), lnum); p += STRLEN(p); vim_snprintf((char *)p, IOSIZE - (p - IObuff), - NGETTEXT("%lld character", "%lld characters", nchars), + NGETTEXT("%lld byte", "%lld bytes", nchars), (varnumber_T)nchars); } } diff --git a/src/testdir/dumps/Test_diff_syntax_1.dump b/src/testdir/dumps/Test_diff_syntax_1.dump index 05598ff096..9204b47ab9 100644 --- a/src/testdir/dumps/Test_diff_syntax_1.dump +++ b/src/testdir/dumps/Test_diff_syntax_1.dump @@ -17,4 +17,4 @@ |~| @73 |~| @73 |X+1#0000000&|p|r|o|g|r|a|m|1|.|c| @45|1|,|1| @11|A|l@1 -|"+0&&|X|p|r|o|g|r|a|m|2|.|c|"| |5|L|,| |7|6|C| @53 +|"+0&&|X|p|r|o|g|r|a|m|2|.|c|"| |5|L|,| |7|6|B| @53 diff --git a/src/testdir/dumps/Test_display_unprintable_01.dump b/src/testdir/dumps/Test_display_unprintable_01.dump index e4cc0d9c7f..552b40213e 100644 --- a/src/testdir/dumps/Test_display_unprintable_01.dump +++ b/src/testdir/dumps/Test_display_unprintable_01.dump @@ -6,4 +6,4 @@ |t|w|o| @46 |~+0#4040ff13&| @48 |X+1#0000000&|u|n|i|x|.|t|x|t| @22|1|,|1| @11|A|l@1 -|"+0&&|X|m|a|c|.|t|x|t|"| |[|n|o|e|o|l|]|[|m|a|c|]| |2|L|,| |9|C| @19 +|"+0&&|X|m|a|c|.|t|x|t|"| |[|n|o|e|o|l|]|[|m|a|c|]| |2|L|,| |9|B| @19 diff --git a/src/testdir/dumps/Test_long_file_name_1.dump b/src/testdir/dumps/Test_long_file_name_1.dump index 46fa9bdfbe..1dcafc315c 100644 --- a/src/testdir/dumps/Test_long_file_name_1.dump +++ b/src/testdir/dumps/Test_long_file_name_1.dump @@ -5,4 +5,4 @@ |~| @73 |~| @73 |~| @73 -|<+0#0000000&|x@64|"| |0|L|,| |0|C| +|<+0#0000000&|x@64|"| |0|L|,| |0|B| diff --git a/src/testdir/dumps/Test_tselect_1.dump b/src/testdir/dumps/Test_tselect_1.dump index cdaee45776..909012245e 100644 --- a/src/testdir/dumps/Test_tselect_1.dump +++ b/src/testdir/dumps/Test_tselect_1.dump @@ -7,4 +7,4 @@ |~| @48 |~| @48 |~| @48 -|"+0#0000000&|X|t|e|s|t|.|c|"| |2|L|,| |2|3|C| @14|1|,|1| @10|A|l@1| +|"+0#0000000&|X|t|e|s|t|.|c|"| |2|L|,| |2|3|B| @14|1|,|1| @10|A|l@1| diff --git a/src/testdir/test_cscope.vim b/src/testdir/test_cscope.vim index a1a839fcd2..9d12e2f602 100644 --- a/src/testdir/test_cscope.vim +++ b/src/testdir/test_cscope.vim @@ -103,7 +103,7 @@ func Test_cscopeWithCscopeConnections() for cmd in ['cs find f Xmemfile_test.c', 'cs find 7 Xmemfile_test.c'] enew let a = execute(cmd) - call assert_true(a =~ '"Xmemfile_test.c" \d\+L, \d\+C') + call assert_true(a =~ '"Xmemfile_test.c" \d\+L, \d\+B') call assert_equal('Xmemfile_test.c', @%) endfor @@ -113,7 +113,7 @@ func Test_cscopeWithCscopeConnections() let a = execute(cmd) let alines = split(a, '\n', 1) call assert_equal('', alines[0]) - call assert_true(alines[1] =~ '"Xmemfile_test.c" \d\+L, \d\+C') + call assert_true(alines[1] =~ '"Xmemfile_test.c" \d\+L, \d\+B') call assert_equal('(1 of 1): <> #include ', alines[2]) call assert_equal('#include ', getline('.')) endfor diff --git a/src/testdir/test_netbeans.vim b/src/testdir/test_netbeans.vim index 52c37820ef..8a35389d52 100644 --- a/src/testdir/test_netbeans.vim +++ b/src/testdir/test_netbeans.vim @@ -501,7 +501,7 @@ func Nb_basic(port) let l = readfile('Xnetbeans') call assert_equal('send: 3:insertDone!78 T F', l[-1]) sleep 1m - call assert_match('.*/Xfile4" 3L, 0C', v:statusmsg) + call assert_match('.*/Xfile4" 3L, 0B', v:statusmsg) let g:last += 3 " saveDone test @@ -511,7 +511,7 @@ func Nb_basic(port) let l = readfile('Xnetbeans') call assert_equal('send: 3:saveDone!79', l[-1]) sleep 1m - call assert_match('.*/Xfile4" 3L, 0C', v:statusmsg) + call assert_match('.*/Xfile4" 3L, 0B', v:statusmsg) let g:last += 3 " unimplemented command test diff --git a/src/version.c b/src/version.c index b406a69577..880051efa6 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1128, /**/ 1127, /**/ From e9f262bdff2defa248e5d40b6520251799581ea4 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 5 Jul 2020 14:57:51 +0200 Subject: [PATCH 085/105] patch 8.2.1129: Vim9: bar not recognized after not compiled command Problem: Vim9: bar not recognized after not compiled command. Solution: Check for bar for commands where this is possible. (closes #6391) --- src/testdir/test_vim9_cmd.vim | 34 ++++++++++++++++++++++++++++++++++ src/version.c | 2 ++ src/vim9compile.c | 32 ++++++++++++++++++++++++++++++-- 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim index 27d2b3a7cd..53d964fa8b 100644 --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -2,6 +2,7 @@ source check.vim source vim9.vim +source view_util.vim def Test_edit_wildcards() let filename = 'Xtest' @@ -207,5 +208,38 @@ def Test_method_call_linebreak() CheckScriptSuccess(lines) enddef +def Test_bar_after_command() + def RedrawAndEcho() + let x = 'did redraw' + redraw | echo x + enddef + RedrawAndEcho() + assert_match('did redraw', Screenline(&lines)) + + if has('unix') + # bar in filter write command does not start new command + def WriteToShell() + new + setline(1, 'some text') + w !cat | cat > Xoutfile + bwipe! + enddef + WriteToShell() + assert_equal(['some text'], readfile('Xoutfile')) + delete('Xoutfile') + + # bar in filter read command does not start new command + def ReadFromShell() + new + r! echo hello there | cat > Xoutfile + r !echo again | cat >> Xoutfile + bwipe! + enddef + ReadFromShell() + assert_equal(['hello there', 'again'], readfile('Xoutfile')) + delete('Xoutfile') + endif +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index 880051efa6..d079d29096 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1129, /**/ 1128, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 9c2eaf4329..5c3d08d30f 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -6562,12 +6562,33 @@ compile_exec(char_u *line, exarg_T *eap, cctx_T *cctx) { char_u *p; int has_expr = FALSE; + char_u *nextcmd = (char_u *)""; if (cctx->ctx_skip == SKIP_YES) goto theend; if (eap->cmdidx >= 0 && eap->cmdidx < CMD_SIZE) - has_expr = (excmd_get_argt(eap->cmdidx) & (EX_XFILE | EX_EXPAND)); + { + long argt = excmd_get_argt(eap->cmdidx); + int usefilter = FALSE; + + has_expr = argt & (EX_XFILE | EX_EXPAND); + + // If the command can be followed by a bar, find the bar and truncate + // it, so that the following command can be compiled. + // The '|' is overwritten with a NUL, it is put back below. + if ((eap->cmdidx == CMD_write || eap->cmdidx == CMD_read) + && *eap->arg == '!') + // :w !filter or :r !filter or :r! filter + usefilter = TRUE; + if ((argt & EX_TRLBAR) && !usefilter) + { + separate_nextcmd(eap); + if (eap->nextcmd != NULL) + nextcmd = eap->nextcmd; + } + } + if (eap->cmdidx == CMD_syntax && STRNCMP(eap->arg, "include ", 8) == 0) { // expand filename in "syntax include [@group] filename" @@ -6626,7 +6647,14 @@ compile_exec(char_u *line, exarg_T *eap, cctx_T *cctx) generate_EXEC(cctx, line); theend: - return (char_u *)""; + if (*nextcmd != NUL) + { + // the parser expects a pointer to the bar, put it back + --nextcmd; + *nextcmd = '|'; + } + + return nextcmd; } /* From 788123c00c4b7acc4e6ba3e9f1cc8b175ae9aae8 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 5 Jul 2020 15:32:17 +0200 Subject: [PATCH 086/105] patch 8.2.1130: Vim9: bar not recognized after function call Problem: Vim9: bar not recognized after function call Solution: Skip whitespace. (closes #6391) --- src/testdir/test_vim9_cmd.vim | 7 +++++++ src/version.c | 2 ++ src/vim9compile.c | 3 ++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim index 53d964fa8b..d7c01aae97 100644 --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -216,6 +216,13 @@ def Test_bar_after_command() RedrawAndEcho() assert_match('did redraw', Screenline(&lines)) + def CallAndEcho() + let x = 'did redraw' + reg_executing() | echo x + enddef + CallAndEcho() + assert_match('did redraw', Screenline(&lines)) + if has('unix') # bar in filter write command does not start new command def WriteToShell() diff --git a/src/version.c b/src/version.c index d079d29096..c94988faf0 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1130, /**/ 1129, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 5c3d08d30f..b4209a1fee 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -6985,7 +6985,8 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx) // drop the return value generate_instr_drop(&cctx, ISN_DROP, 1); - line = p; + + line = skipwhite(p); continue; } // CMD_let cannot happen, compile_assignment() above is used From 05a5551a86e013e35d1dfa10fd0d811c587f9c88 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 5 Jul 2020 15:52:19 +0200 Subject: [PATCH 087/105] patch 8.2.1131: Vim9: error message for returning a value is not clear Problem: Vim9: error message for returning a value in a function that does not return anything is not clear. Solution: Add a specific message. --- src/testdir/test_vim9_func.vim | 6 +++--- src/version.c | 2 ++ src/vim9compile.c | 12 +++++++++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 6a03eddc1d..583a232287 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -286,14 +286,14 @@ enddef def Test_error_in_nested_function() " Error in called function requires unwinding the call stack. - assert_fails('call FuncWithForwardCall()', 'E1013') + assert_fails('call FuncWithForwardCall()', 'E1096') enddef def Test_return_type_wrong() CheckScriptFailure(['def Func(): number', 'return "a"', 'enddef', 'defcompile'], 'expected number but got string') CheckScriptFailure(['def Func(): string', 'return 1', 'enddef', 'defcompile'], 'expected string but got number') - CheckScriptFailure(['def Func(): void', 'return "a"', 'enddef', 'defcompile'], 'expected void but got string') - CheckScriptFailure(['def Func()', 'return "a"', 'enddef', 'defcompile'], 'expected void but got string') + CheckScriptFailure(['def Func(): void', 'return "a"', 'enddef', 'defcompile'], 'E1096: Returning a value in a function without a return type') + CheckScriptFailure(['def Func()', 'return "a"', 'enddef', 'defcompile'], 'E1096: Returning a value in a function without a return type') CheckScriptFailure(['def Func(): number', 'return', 'enddef', 'defcompile'], 'E1003:') diff --git a/src/version.c b/src/version.c index c94988faf0..8f85bb7bf0 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1131, /**/ 1130, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index b4209a1fee..e5a801a28e 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -4568,9 +4568,19 @@ compile_return(char_u *arg, int set_return_type, cctx_T *cctx) stack_type = ((type_T **)stack->ga_data)[stack->ga_len - 1]; if (set_return_type) cctx->ctx_ufunc->uf_ret_type = stack_type; - else if (need_type(stack_type, cctx->ctx_ufunc->uf_ret_type, -1, cctx) + else + { + if (cctx->ctx_ufunc->uf_ret_type->tt_type == VAR_VOID + && stack_type->tt_type != VAR_VOID + && stack_type->tt_type != VAR_UNKNOWN) + { + emsg(_("E1096: Returning a value in a function without a return type")); + return NULL; + } + if (need_type(stack_type, cctx->ctx_ufunc->uf_ret_type, -1, cctx) == FAIL) return NULL; + } } else { From 9978d473e32cee928d6f901a5eb9a297a2970132 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 5 Jul 2020 16:01:56 +0200 Subject: [PATCH 088/105] patch 8.2.1132: Vim9: return type of repeat() is not specific enough Problem: Vim9: return type of repeat() is not specific enough. Solution: Return the type of the first argument. (closes #6395) --- src/evalfunc.c | 4 ++-- src/testdir/test_vim9_func.vim | 8 ++++++++ src/version.c | 2 ++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/evalfunc.c b/src/evalfunc.c index 8d81f15123..2cdf186be3 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -815,10 +815,10 @@ static funcentry_T global_functions[] = {"remote_peek", 1, 2, FEARG_1, ret_number, f_remote_peek}, {"remote_read", 1, 2, FEARG_1, ret_string, f_remote_read}, {"remote_send", 2, 3, FEARG_1, ret_string, f_remote_send}, - {"remote_startserver", 1, 1, FEARG_1, ret_void, f_remote_startserver}, + {"remote_startserver", 1, 1, FEARG_1, ret_void, f_remote_startserver}, {"remove", 2, 3, FEARG_1, ret_any, f_remove}, {"rename", 2, 2, FEARG_1, ret_number, f_rename}, - {"repeat", 2, 2, FEARG_1, ret_any, f_repeat}, + {"repeat", 2, 2, FEARG_1, ret_first_arg, f_repeat}, {"resolve", 1, 1, FEARG_1, ret_string, f_resolve}, {"reverse", 1, 1, FEARG_1, ret_any, f_reverse}, {"round", 1, 1, FEARG_1, ret_float, FLOAT_FUNC(f_round)}, diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 583a232287..e8d712f0a7 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -596,6 +596,14 @@ def Test_func_type() assert_equal(13, funcResult) enddef +def Test_repeat_return_type() + let res = 0 + for n in repeat([1], 3) + res += n + endfor + assert_equal(3, res) +enddef + def Test_func_type_part() let RefVoid: func: void RefVoid = FuncNoArgNoRet diff --git a/src/version.c b/src/version.c index 8f85bb7bf0..ed3f34ad4d 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1132, /**/ 1131, /**/ From fce82b3aa7dd87f9e15a4c12eda2c65de285d99a Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 5 Jul 2020 16:07:21 +0200 Subject: [PATCH 089/105] patch 8.2.1133: Vim9: return type of add() is not specific enough Problem: Vim9: return type of add() is not specific enough. Solution: Return the type of the first argument. (closes #6395) --- src/evalfunc.c | 2 +- src/testdir/test_vim9_func.vim | 6 ++++++ src/version.c | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/evalfunc.c b/src/evalfunc.c index 2cdf186be3..c3eaef2ea5 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -441,7 +441,7 @@ static funcentry_T global_functions[] = { {"abs", 1, 1, FEARG_1, ret_any, FLOAT_FUNC(f_abs)}, {"acos", 1, 1, FEARG_1, ret_float, FLOAT_FUNC(f_acos)}, - {"add", 2, 2, FEARG_1, ret_any, f_add}, + {"add", 2, 2, FEARG_1, ret_first_arg, f_add}, {"and", 2, 2, FEARG_1, ret_number, f_and}, {"append", 2, 2, FEARG_LAST, ret_number, f_append}, {"appendbufline", 3, 3, FEARG_LAST, ret_number, f_appendbufline}, diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index e8d712f0a7..cc938a50ef 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -602,6 +602,12 @@ def Test_repeat_return_type() res += n endfor assert_equal(3, res) + + res = 0 + for n in add([1, 2], 3) + res += n + endfor + assert_equal(6, res) enddef def Test_func_type_part() diff --git a/src/version.c b/src/version.c index ed3f34ad4d..6a2295191e 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1133, /**/ 1132, /**/ From 435d89789ef4dd329938edbe17c646db9f0ea772 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 5 Jul 2020 16:42:13 +0200 Subject: [PATCH 090/105] patch 8.2.1134: Vim9: getting a list member may not work Problem: Vim9: getting a list member may not work. Solution: Clear the list only after copying the item. (closes #6393) --- src/testdir/test_vim9_expr.vim | 5 +++++ src/version.c | 2 ++ src/vim9execute.c | 9 +++++++-- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index 3bf578059f..aef1815121 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -1141,6 +1141,11 @@ def Test_expr_member() call CheckDefExecFailure(["let d: dict", "d = g:list_empty"], 'E1029: Expected dict but got list') enddef +def Test_expr_index() + # getting the one member should clear the list only after getting the item + assert_equal('bbb', ['aaa', 'bbb', 'ccc'][1]) +enddef + def Test_expr_member_vim9script() let lines =<< trim END vim9script diff --git a/src/version.c b/src/version.c index 6a2295191e..8774b2a8d9 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1134, /**/ 1133, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index bcacaabaa0..47e072e549 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -2085,6 +2085,7 @@ call_def_function( list_T *list; varnumber_T n; listitem_T *li; + typval_T temp_tv; // list index: list is at stack-2, index at stack-1 tv = STACK_TV_BOT(-2); @@ -2109,8 +2110,12 @@ call_def_function( goto failed; } --ectx.ec_stack.ga_len; - clear_tv(STACK_TV_BOT(-1)); - copy_tv(&li->li_tv, STACK_TV_BOT(-1)); + // Clear the list after getting the item, to avoid that it + // make the item invalid. + tv = STACK_TV_BOT(-1); + temp_tv = *tv; + copy_tv(&li->li_tv, tv); + clear_tv(&temp_tv); } break; From 50788ef34947aeb1729604cd3876845afbd15e3c Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 5 Jul 2020 16:51:26 +0200 Subject: [PATCH 091/105] patch 8.2.1135: Vim9: getting a dict member may not work Problem: Vim9: getting a dict member may not work. Solution: Clear the dict only after copying the item. --- src/testdir/test_vim9_expr.vim | 3 +++ src/version.c | 2 ++ src/vim9execute.c | 11 ++++++++--- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index aef1815121..b3906c1b98 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -1128,6 +1128,8 @@ def Test_expr7_dict_vim9script() CheckScriptFailure(lines, 'E1069:') enddef +let g:oneString = 'one' + def Test_expr_member() assert_equal(1, g:dict_one.one) let d: dict = g:dict_one @@ -1135,6 +1137,7 @@ def Test_expr_member() # getting the one member should clear the dict after getting the item assert_equal('one', #{one: 'one'}.one) + assert_equal('one', #{one: 'one'}[g:oneString]) call CheckDefFailure(["let x = g:dict_one.#$!"], 'E1002:') call CheckDefExecFailure(["let d: dict", "echo d['a']"], 'E716:') diff --git a/src/version.c b/src/version.c index 8774b2a8d9..82e4c6ddcb 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1135, /**/ 1134, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index 47e072e549..7afa3fc5ec 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -2166,6 +2166,7 @@ call_def_function( dict_T *dict; char_u *key; dictitem_T *di; + typval_T temp_tv; // dict member: dict is at stack-2, key at stack-1 tv = STACK_TV_BOT(-2); @@ -2181,10 +2182,14 @@ call_def_function( semsg(_(e_dictkey), key); goto failed; } - --ectx.ec_stack.ga_len; clear_tv(tv); - clear_tv(STACK_TV_BOT(-1)); - copy_tv(&di->di_tv, STACK_TV_BOT(-1)); + --ectx.ec_stack.ga_len; + // Clear the dict after getting the item, to avoid that it + // make the item invalid. + tv = STACK_TV_BOT(-1); + temp_tv = *tv; + copy_tv(&di->di_tv, tv); + clear_tv(&temp_tv); } break; From 846178a72ca0860073d47fc0dc95f98d15f1d921 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 5 Jul 2020 17:04:13 +0200 Subject: [PATCH 092/105] patch 8.2.1136: Vim9: return type of argv() is always any Problem: Vim9: return type of argv() is always any. Solution: Use list if there is no argument. --- src/evalfunc.c | 13 ++++++++++++- src/testdir/test_vim9_func.vim | 9 +++++++++ src/version.c | 2 ++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/evalfunc.c b/src/evalfunc.c index c3eaef2ea5..5215befb5a 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -371,6 +371,17 @@ ret_list_or_dict_1(int argcount, type_T **argtypes UNUSED) return &t_list_dict_any; } + static type_T * +ret_argv(int argcount, type_T **argtypes UNUSED) +{ + // argv() returns list of strings + if (argcount == 0) + return &t_list_string; + + // argv(0) returns a string, but argv(-1] returns a list + return &t_any; +} + static type_T *ret_f_function(int argcount, type_T **argtypes); /* @@ -448,7 +459,7 @@ static funcentry_T global_functions[] = {"argc", 0, 1, 0, ret_number, f_argc}, {"argidx", 0, 0, 0, ret_number, f_argidx}, {"arglistid", 0, 2, 0, ret_number, f_arglistid}, - {"argv", 0, 2, 0, ret_any, f_argv}, + {"argv", 0, 2, 0, ret_argv, f_argv}, {"asin", 1, 1, FEARG_1, ret_float, FLOAT_FUNC(f_asin)}, {"assert_beeps", 1, 2, FEARG_1, ret_number, f_assert_beeps}, {"assert_equal", 2, 3, FEARG_2, ret_number, f_assert_equal}, diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index cc938a50ef..c3236fff15 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -610,6 +610,15 @@ def Test_repeat_return_type() assert_equal(6, res) enddef +def Test_argv_return_type() + next fileone filetwo + let res = '' + for name in argv() + res ..= name + endfor + assert_equal('fileonefiletwo', res) +enddef + def Test_func_type_part() let RefVoid: func: void RefVoid = FuncNoArgNoRet diff --git a/src/version.c b/src/version.c index 82e4c6ddcb..23a94348f8 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1136, /**/ 1135, /**/ From 47e7d70b58e8bfc1daaf6d35569ef2dbd0339ddc Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 5 Jul 2020 18:18:42 +0200 Subject: [PATCH 093/105] patch 8.2.1137: Vim9: modifiers not cleared after compiling function Problem: Vim9: modifiers not cleared after compiling function. Solution: Clear command modifiers. (closes #6396) --- src/ex_docmd.c | 53 ++++++++++---------- src/proto/ex_docmd.pro | 1 + src/testdir/dumps/Test_vim9_silent_echo.dump | 6 +++ src/testdir/test_vim9_func.vim | 23 +++++++++ src/version.c | 2 + src/vim9compile.c | 3 ++ 6 files changed, 61 insertions(+), 27 deletions(-) create mode 100644 src/testdir/dumps/Test_vim9_silent_echo.dump diff --git a/src/ex_docmd.c b/src/ex_docmd.c index a5562cc28a..59778eb981 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -25,7 +25,6 @@ static char_u *do_one_cmd(char_u **, int, cstack_T *, char_u *(*fgetline)(int, v static char_u *do_one_cmd(char_u **, int, char_u *(*fgetline)(int, void *, int, int), void *cookie); static int if_level = 0; // depth in :if #endif -static void free_cmdmod(void); static void append_command(char_u *cmd); #ifndef FEAT_MENU @@ -2611,32 +2610,10 @@ doend: ? cmdnames[(int)ea.cmdidx].cmd_name : (char_u *)NULL); #endif - if (ea.verbose_save >= 0) - p_verbose = ea.verbose_save; - - free_cmdmod(); + undo_cmdmod(&ea, save_msg_scroll); cmdmod = save_cmdmod; reg_executing = save_reg_executing; - if (ea.save_msg_silent != -1) - { - // messages could be enabled for a serious error, need to check if the - // counters don't become negative - if (!did_emsg || msg_silent > ea.save_msg_silent) - msg_silent = ea.save_msg_silent; - emsg_silent -= ea.did_esilent; - if (emsg_silent < 0) - emsg_silent = 0; - // Restore msg_scroll, it's set by file I/O commands, even when no - // message is actually displayed. - msg_scroll = save_msg_scroll; - - // "silent reg" or "silent echo x" inside "redir" leaves msg_col - // somewhere in the line. Put it back in the first column. - if (redirecting()) - msg_col = 0; - } - #ifdef HAVE_SANDBOX if (ea.did_sandbox) --sandbox; @@ -2927,11 +2904,14 @@ parse_command_modifiers(exarg_T *eap, char **errormsg, int skip_only) } /* - * Free contents of "cmdmod". + * Unod and free contents of "cmdmod". */ - static void -free_cmdmod(void) + void +undo_cmdmod(exarg_T *eap, int save_msg_scroll) { + if (eap->verbose_save >= 0) + p_verbose = eap->verbose_save; + if (cmdmod.save_ei != NULL) { // Restore 'eventignore' to the value before ":noautocmd". @@ -2942,6 +2922,25 @@ free_cmdmod(void) if (cmdmod.filter_regmatch.regprog != NULL) vim_regfree(cmdmod.filter_regmatch.regprog); + + if (eap->save_msg_silent != -1) + { + // messages could be enabled for a serious error, need to check if the + // counters don't become negative + if (!did_emsg || msg_silent > eap->save_msg_silent) + msg_silent = eap->save_msg_silent; + emsg_silent -= eap->did_esilent; + if (emsg_silent < 0) + emsg_silent = 0; + // Restore msg_scroll, it's set by file I/O commands, even when no + // message is actually displayed. + msg_scroll = save_msg_scroll; + + // "silent reg" or "silent echo x" inside "redir" leaves msg_col + // somewhere in the line. Put it back in the first column. + if (redirecting()) + msg_col = 0; + } } /* diff --git a/src/proto/ex_docmd.pro b/src/proto/ex_docmd.pro index 8f5ac10d4e..85050313b6 100644 --- a/src/proto/ex_docmd.pro +++ b/src/proto/ex_docmd.pro @@ -6,6 +6,7 @@ int getline_equal(char_u *(*fgetline)(int, void *, int, int), void *cookie, char void *getline_cookie(char_u *(*fgetline)(int, void *, int, int), void *cookie); char_u *getline_peek(char_u *(*fgetline)(int, void *, int, int), void *cookie); int parse_command_modifiers(exarg_T *eap, char **errormsg, int skip_only); +void undo_cmdmod(exarg_T *eap, int save_msg_scroll); int parse_cmd_address(exarg_T *eap, char **errormsg, int silent); int checkforcmd(char_u **pp, char *cmd, int len); char_u *find_ex_command(exarg_T *eap, int *full, void *(*lookup)(char_u *, size_t, cctx_T *), cctx_T *cctx); diff --git a/src/testdir/dumps/Test_vim9_silent_echo.dump b/src/testdir/dumps/Test_vim9_silent_echo.dump new file mode 100644 index 0000000000..f5f7927859 --- /dev/null +++ b/src/testdir/dumps/Test_vim9_silent_echo.dump @@ -0,0 +1,6 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|:+0#0000000&|a|b|c> @70 diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index c3236fff15..761dd57252 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -3,6 +3,7 @@ source check.vim source view_util.vim source vim9.vim +source screendump.vim func Test_def_basic() def SomeFunc(): string @@ -903,5 +904,27 @@ def Test_line_continuation_in_def() assert_equal('full', Line_continuation_in_def('.')) enddef +def Test_silent_echo() + CheckScreendump + + let lines =<< trim END + vim9script + def EchoNothing() + silent echo '' + enddef + defcompile + END + writefile(lines, 'XTest_silent_echo') + + " Check that the balloon shows up after a mouse move + let buf = RunVimInTerminal('-S XTest_silent_echo', {'rows': 6}) + term_sendkeys(buf, ":abc") + call VerifyScreenDump(buf, 'Test_vim9_silent_echo', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('XTest_silent_echo') +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index 23a94348f8..b6ed058e13 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1137, /**/ 1136, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index e5a801a28e..ed069b740a 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -6809,6 +6809,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx) exarg_T ea; int starts_with_colon = FALSE; char_u *cmd; + int save_msg_scroll = msg_scroll; // Bail out on the first error to avoid a flood of errors and report // the right line number when inside try/catch. @@ -6897,6 +6898,8 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx) line = (char_u *)""; continue; } + // TODO: use modifiers in the command + undo_cmdmod(&ea, save_msg_scroll); // Skip ":call" to get to the function name. if (checkforcmd(&ea.cmd, "call", 3)) From a66ba01a5fbbd72375ef6982b901d6552da2414f Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 5 Jul 2020 18:41:08 +0200 Subject: [PATCH 094/105] patch 8.2.1138: Vim9: return type of copy() and deepcopy() is any Problem: Vim9: return type of copy() and deepcopy() is any. Solution: Use type of the argument. --- src/evalfunc.c | 4 ++-- src/testdir/test_vim9_func.vim | 16 ++++++++++++++++ src/version.c | 2 ++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/evalfunc.c b/src/evalfunc.c index 5215befb5a..57e707fc36 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -544,7 +544,7 @@ static funcentry_T global_functions[] = {"complete_check", 0, 0, 0, ret_number, f_complete_check}, {"complete_info", 0, 1, FEARG_1, ret_dict_any, f_complete_info}, {"confirm", 1, 4, FEARG_1, ret_number, f_confirm}, - {"copy", 1, 1, FEARG_1, ret_any, f_copy}, + {"copy", 1, 1, FEARG_1, ret_first_arg, f_copy}, {"cos", 1, 1, FEARG_1, ret_float, FLOAT_FUNC(f_cos)}, {"cosh", 1, 1, FEARG_1, ret_float, FLOAT_FUNC(f_cosh)}, {"count", 2, 4, FEARG_1, ret_number, f_count}, @@ -557,7 +557,7 @@ static funcentry_T global_functions[] = NULL #endif }, - {"deepcopy", 1, 2, FEARG_1, ret_any, f_deepcopy}, + {"deepcopy", 1, 2, FEARG_1, ret_first_arg, f_deepcopy}, {"delete", 1, 2, FEARG_1, ret_number, f_delete}, {"deletebufline", 2, 3, FEARG_1, ret_number, f_deletebufline}, {"did_filetype", 0, 0, 0, ret_number, f_did_filetype}, diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 761dd57252..94c94575f3 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -893,6 +893,22 @@ def Test_getloclist_return_type() assert_equal(#{items: []}, d) enddef +def Test_copy_return_type() + let l = copy([1, 2, 3]) + let res = 0 + for n in l + res += n + endfor + assert_equal(6, res) + + let dl = deepcopy([1, 2, 3]) + res = 0 + for n in dl + res += n + endfor + assert_equal(6, res) +enddef + def Line_continuation_in_def(dir: string = ''): string let path: string = empty(dir) \ ? 'empty' diff --git a/src/version.c b/src/version.c index b6ed058e13..950871e9a4 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1138, /**/ 1137, /**/ From 8f510afcd616fc62291fb41f9dd03ce298f1c601 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 5 Jul 2020 18:48:23 +0200 Subject: [PATCH 095/105] patch 8.2.1139: Vim9: test for silent echo fails in some environments Problem: Vim9: test for silent echo fails in some environments. Solution: Use :function instead of :def. --- src/testdir/test_vim9_func.vim | 8 ++++---- src/version.c | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 94c94575f3..d7c37b1592 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -920,7 +920,7 @@ def Test_line_continuation_in_def() assert_equal('full', Line_continuation_in_def('.')) enddef -def Test_silent_echo() +func Test_silent_echo() CheckScreendump let lines =<< trim END @@ -930,17 +930,17 @@ def Test_silent_echo() enddef defcompile END - writefile(lines, 'XTest_silent_echo') + call writefile(lines, 'XTest_silent_echo') " Check that the balloon shows up after a mouse move let buf = RunVimInTerminal('-S XTest_silent_echo', {'rows': 6}) - term_sendkeys(buf, ":abc") + call term_sendkeys(buf, ":abc") call VerifyScreenDump(buf, 'Test_vim9_silent_echo', {}) " clean up call StopVimInTerminal(buf) call delete('XTest_silent_echo') -enddef +endfunc " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index 950871e9a4..23d2d1b974 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1139, /**/ 1138, /**/ From b3c019cbc32e2e9250ac668bf77d61ebd89c13f2 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 5 Jul 2020 20:08:39 +0200 Subject: [PATCH 096/105] patch 8.2.1140: Vim9: return type of extend() is any Problem: Vim9: return type of extend() is any. Solution: Use type of the argument. --- src/evalfunc.c | 2 +- src/testdir/test_vim9_func.vim | 9 +++++++++ src/version.c | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/evalfunc.c b/src/evalfunc.c index 57e707fc36..439333184b 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -576,7 +576,7 @@ static funcentry_T global_functions[] = {"exp", 1, 1, FEARG_1, ret_float, FLOAT_FUNC(f_exp)}, {"expand", 1, 3, FEARG_1, ret_any, f_expand}, {"expandcmd", 1, 1, FEARG_1, ret_string, f_expandcmd}, - {"extend", 2, 3, FEARG_1, ret_any, f_extend}, + {"extend", 2, 3, FEARG_1, ret_first_arg, f_extend}, {"feedkeys", 1, 2, FEARG_1, ret_void, f_feedkeys}, {"file_readable", 1, 1, FEARG_1, ret_number, f_filereadable}, // obsolete {"filereadable", 1, 1, FEARG_1, ret_number, f_filereadable}, diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index d7c37b1592..525d46d4cd 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -909,6 +909,15 @@ def Test_copy_return_type() assert_equal(6, res) enddef +def Test_extend_return_type() + let l = extend([1, 2], [3]) + let res = 0 + for n in l + res += n + endfor + assert_equal(6, res) +enddef + def Line_continuation_in_def(dir: string = ''): string let path: string = empty(dir) \ ? 'empty' diff --git a/src/version.c b/src/version.c index 23d2d1b974..116cb065fe 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1140, /**/ 1139, /**/ From 0d94ad69586972e7522ec6ca1441a517af2ade7a Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 5 Jul 2020 20:16:41 +0200 Subject: [PATCH 097/105] patch 8.2.1141: Vim9: return type of filter() is any Problem: Vim9: return type of filter() is any. Solution: Use type of the argument. --- src/evalfunc.c | 2 +- src/testdir/test_vim9_func.vim | 9 +++++++++ src/version.c | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/evalfunc.c b/src/evalfunc.c index 439333184b..b015f6dad1 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -581,7 +581,7 @@ static funcentry_T global_functions[] = {"file_readable", 1, 1, FEARG_1, ret_number, f_filereadable}, // obsolete {"filereadable", 1, 1, FEARG_1, ret_number, f_filereadable}, {"filewritable", 1, 1, FEARG_1, ret_number, f_filewritable}, - {"filter", 2, 2, FEARG_1, ret_any, f_filter}, + {"filter", 2, 2, FEARG_1, ret_first_arg, f_filter}, {"finddir", 1, 3, FEARG_1, ret_string, f_finddir}, {"findfile", 1, 3, FEARG_1, ret_string, f_findfile}, {"flatten", 1, 2, FEARG_1, ret_list_any, f_flatten}, diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 525d46d4cd..8b83d77095 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -918,6 +918,15 @@ def Test_extend_return_type() assert_equal(6, res) enddef +def Test_filter_return_type() + let l = filter([1, 2, 3], {-> 1}) + let res = 0 + for n in l + res += n + endfor + assert_equal(6, res) +enddef + def Line_continuation_in_def(dir: string = ''): string let path: string = empty(dir) \ ? 'empty' diff --git a/src/version.c b/src/version.c index 116cb065fe..47d4bccd51 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1141, /**/ 1140, /**/ From 252e88a78535d239ec4b764d8e3f04aad5a94a76 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 5 Jul 2020 20:47:18 +0200 Subject: [PATCH 098/105] patch 8.2.1142: Vim9: return type of insert() is any Problem: Vim9: return type of insert() is any. Solution: Use type of the first argument. --- src/evalfunc.c | 2 +- src/testdir/test_vim9_func.vim | 9 +++++++++ src/version.c | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/evalfunc.c b/src/evalfunc.c index b015f6dad1..65c34cd6a0 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -667,7 +667,7 @@ static funcentry_T global_functions[] = {"inputrestore", 0, 0, 0, ret_number, f_inputrestore}, {"inputsave", 0, 0, 0, ret_number, f_inputsave}, {"inputsecret", 1, 2, FEARG_1, ret_string, f_inputsecret}, - {"insert", 2, 3, FEARG_1, ret_any, f_insert}, + {"insert", 2, 3, FEARG_1, ret_first_arg, f_insert}, {"interrupt", 0, 0, 0, ret_void, f_interrupt}, {"invert", 1, 1, FEARG_1, ret_number, f_invert}, {"isdirectory", 1, 1, FEARG_1, ret_number, f_isdirectory}, diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 8b83d77095..65b525706a 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -918,6 +918,15 @@ def Test_extend_return_type() assert_equal(6, res) enddef +def Test_insert_return_type() + let l = insert([2, 1], 3) + let res = 0 + for n in l + res += n + endfor + assert_equal(6, res) +enddef + def Test_filter_return_type() let l = filter([1, 2, 3], {-> 1}) let res = 0 diff --git a/src/version.c b/src/version.c index 47d4bccd51..6d4d6e93bb 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1142, /**/ 1141, /**/ From ad7c24932725b9ab37b65fe359a41f8ba3e3dfcf Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 5 Jul 2020 20:55:29 +0200 Subject: [PATCH 099/105] patch 8.2.1143: Vim9: return type of remove() is any Problem: Vim9: return type of remove() is any. Solution: Use the member type of the first argument, if known. --- src/evalfunc.c | 13 ++++++++++++- src/testdir/test_vim9_func.vim | 9 +++++++++ src/version.c | 2 ++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/evalfunc.c b/src/evalfunc.c index 65c34cd6a0..83b878b37c 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -382,6 +382,17 @@ ret_argv(int argcount, type_T **argtypes UNUSED) return &t_any; } + static type_T * +ret_remove(int argcount UNUSED, type_T **argtypes) +{ + if (argtypes[0]->tt_type == VAR_LIST + || argtypes[0]->tt_type == VAR_DICT) + return argtypes[0]->tt_member; + if (argtypes[0]->tt_type == VAR_BLOB) + return &t_number; + return &t_any; +} + static type_T *ret_f_function(int argcount, type_T **argtypes); /* @@ -827,7 +838,7 @@ static funcentry_T global_functions[] = {"remote_read", 1, 2, FEARG_1, ret_string, f_remote_read}, {"remote_send", 2, 3, FEARG_1, ret_string, f_remote_send}, {"remote_startserver", 1, 1, FEARG_1, ret_void, f_remote_startserver}, - {"remove", 2, 3, FEARG_1, ret_any, f_remove}, + {"remove", 2, 3, FEARG_1, ret_remove, f_remove}, {"rename", 2, 2, FEARG_1, ret_number, f_rename}, {"repeat", 2, 2, FEARG_1, ret_first_arg, f_repeat}, {"resolve", 1, 1, FEARG_1, ret_string, f_resolve}, diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 65b525706a..347dc819e0 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -927,6 +927,15 @@ def Test_insert_return_type() assert_equal(6, res) enddef +def Test_remove_return_type() + let l = remove(#{one: [1, 2], two: [3, 4]}, 'one') + let res = 0 + for n in l + res += n + endfor + assert_equal(3, res) +enddef + def Test_filter_return_type() let l = filter([1, 2, 3], {-> 1}) let res = 0 diff --git a/src/version.c b/src/version.c index 6d4d6e93bb..65cf985a2f 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1143, /**/ 1142, /**/ From 67627355accff4af4f2a7e727c77ea8df675636e Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 5 Jul 2020 21:10:24 +0200 Subject: [PATCH 100/105] patch 8.2.1144: Vim9: return type of reverse() is any Problem: Vim9: return type of reverse() is any. Solution: Use the type of the first argument. --- src/evalfunc.c | 2 +- src/testdir/test_vim9_func.vim | 9 +++++++++ src/version.c | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/evalfunc.c b/src/evalfunc.c index 83b878b37c..0a100748d9 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -842,7 +842,7 @@ static funcentry_T global_functions[] = {"rename", 2, 2, FEARG_1, ret_number, f_rename}, {"repeat", 2, 2, FEARG_1, ret_first_arg, f_repeat}, {"resolve", 1, 1, FEARG_1, ret_string, f_resolve}, - {"reverse", 1, 1, FEARG_1, ret_any, f_reverse}, + {"reverse", 1, 1, FEARG_1, ret_first_arg, f_reverse}, {"round", 1, 1, FEARG_1, ret_float, FLOAT_FUNC(f_round)}, {"rubyeval", 1, 1, FEARG_1, ret_any, #ifdef FEAT_RUBY diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 347dc819e0..de30a620d1 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -927,6 +927,15 @@ def Test_insert_return_type() assert_equal(6, res) enddef +def Test_reverse_return_type() + let l = reverse([1, 2, 3]) + let res = 0 + for n in l + res += n + endfor + assert_equal(6, res) +enddef + def Test_remove_return_type() let l = remove(#{one: [1, 2], two: [3, 4]}, 'one') let res = 0 diff --git a/src/version.c b/src/version.c index 65cf985a2f..f93678388b 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1144, /**/ 1143, /**/ From 0ad3e894d75236915e67dfbbcc821b6bb3c05d91 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 5 Jul 2020 21:38:11 +0200 Subject: [PATCH 101/105] patch 8.2.1145: Vim9: "for" only accepts a list at compile time Problem: Vim9: "for" only accepts a list at compile time. Solution: Also accept a list at runtime. --- src/testdir/test_vim9_disassemble.vim | 37 +++++++++++++++++++++++++++ src/testdir/test_vim9_script.vim | 8 +++++- src/version.c | 2 ++ src/vim9compile.c | 8 +++--- 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim index 2862289e40..27d16a5802 100644 --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -727,6 +727,43 @@ def Test_disassemble_for_loop() instr) enddef +def ForLoopEval(): string + let res = "" + for str in eval('["one", "two"]') + res ..= str + endfor + return res +enddef + +def Test_disassemble_for_loop_eval() + assert_equal('onetwo', ForLoopEval()) + let instr = execute('disassemble ForLoopEval') + assert_match('ForLoopEval\_s*' .. + 'let res = ""\_s*' .. + '\d PUSHS ""\_s*' .. + '\d STORE $0\_s*' .. + 'for str in eval(''\["one", "two"\]'')\_s*' .. + '\d STORE -1 in $1\_s*' .. + '\d PUSHS "\["one", "two"\]"\_s*' .. + '\d BCALL eval(argc 1)\_s*' .. + '\d CHECKTYPE list stack\[-1\]\_s*' .. + '\d FOR $1 -> \d\+\_s*' .. + '\d STORE $2\_s*' .. + 'res ..= str\_s*' .. + '\d\+ LOAD $0\_s*' .. + '\d\+ LOAD $2\_s*' .. + '\d\+ CHECKTYPE string stack\[-1\]\_s*' .. + '\d\+ CONCAT\_s*' .. + '\d\+ STORE $0\_s*' .. + 'endfor\_s*' .. + '\d\+ JUMP -> 6\_s*' .. + '\d\+ DROP\_s*' .. + 'return res\_s*' .. + '\d\+ LOAD $0\_s*' .. + '\d\+ RETURN', + instr) +enddef + let g:number = 42 def Computing() diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index e548717986..726ca9af6d 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -1440,6 +1440,12 @@ def Test_for_loop() result ..= cnt .. '_' endfor assert_equal('0_1_3_', result) + + let concat = '' + for str in eval('["one", "two"]') + concat ..= str + endfor + assert_equal('onetwo', concat) enddef def Test_for_loop_fails() @@ -1447,7 +1453,7 @@ def Test_for_loop_fails() CheckDefFailure(['for i In range(5)'], 'E690:') CheckDefFailure(['let x = 5', 'for x in range(5)'], 'E1023:') CheckScriptFailure(['def Func(arg: any)', 'for arg in range(5)', 'enddef', 'defcompile'], 'E1006:') - CheckDefFailure(['for i in "text"'], 'E1024:') + CheckDefFailure(['for i in "text"'], 'E1013:') CheckDefFailure(['for i in xxx'], 'E1001:') CheckDefFailure(['endfor'], 'E588:') CheckDefFailure(['for i in range(3)', 'echo 3'], 'E170:') diff --git a/src/version.c b/src/version.c index f93678388b..b4b8b1dd43 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1145, /**/ 1144, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index ed069b740a..db24eaead6 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -6062,15 +6062,15 @@ compile_for(char_u *arg, cctx_T *cctx) return NULL; } - // now we know the type of "var" + // Now that we know the type of "var", check that it is a list, now or at + // runtime. vartype = ((type_T **)stack->ga_data)[stack->ga_len - 1]; - if (vartype->tt_type != VAR_LIST) + if (need_type(vartype, &t_list_any, -1, cctx) == FAIL) { - emsg(_("E1024: need a List to iterate over")); drop_scope(cctx); return NULL; } - if (vartype->tt_member->tt_type != VAR_ANY) + if (vartype->tt_type == VAR_LIST && vartype->tt_member->tt_type != VAR_ANY) var_lvar->lv_type = vartype->tt_member; // "for_end" is set when ":endfor" is found From ab5894638413748fcedfe28691e6c27893924520 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 6 Jul 2020 21:03:06 +0200 Subject: [PATCH 102/105] patch 8.2.1146: not enough testing for Python Problem: Not enough testing for Python. Solution: Add more tests. Fix uncovered problems. (Yegappan Lakshmanan, closes #6392) --- src/if_py_both.h | 17 +- src/if_python3.c | 4 + src/testdir/shared.vim | 15 ++ src/testdir/test_python2.vim | 367 +++++++++++++++++++++++++++-------- src/testdir/test_python3.vim | 355 ++++++++++++++++++++++++++++----- src/version.c | 2 + 6 files changed, 630 insertions(+), 130 deletions(-) diff --git a/src/if_py_both.h b/src/if_py_both.h index 44b4baffe3..c7df93be2a 100644 --- a/src/if_py_both.h +++ b/src/if_py_both.h @@ -2250,6 +2250,9 @@ ListNew(PyTypeObject *subtype, list_T *list) { ListObject *self; + if (list == NULL) + return NULL; + self = (ListObject *) subtype->tp_alloc(subtype, 0); if (self == NULL) return NULL; @@ -2695,6 +2698,12 @@ ListAssIndex(ListObject *self, Py_ssize_t index, PyObject *obj) if (obj == NULL) { li = list_find(l, (long) index); + if (li == NULL) + { + PyErr_VIM_FORMAT(N_("internal error: failed to get Vim " + "list item %d"), (int) index); + return -1; + } vimlist_remove(l, li, li); clear_tv(&li->li_tv); vim_free(li); @@ -2716,6 +2725,12 @@ ListAssIndex(ListObject *self, Py_ssize_t index, PyObject *obj) else { li = list_find(l, (long) index); + if (li == NULL) + { + PyErr_VIM_FORMAT(N_("internal error: failed to get Vim " + "list item %d"), (int) index); + return -1; + } clear_tv(&li->li_tv); copy_tv(&tv, &li->li_tv); clear_tv(&tv); @@ -3897,7 +3912,7 @@ WindowDestructor(WindowObject *self) PyObject_GC_UnTrack((void *)(self)); if (self->win && self->win != INVALID_WINDOW_VALUE) WIN_PYTHON_REF(self->win) = NULL; - Py_XDECREF(((PyObject *)(self->tabObject))); + Py_XDECREF(((PyObject *)(self->tabObject))); PyObject_GC_Del((void *)(self)); } diff --git a/src/if_python3.c b/src/if_python3.c index ecca163b09..d540226b32 100644 --- a/src/if_python3.c +++ b/src/if_python3.c @@ -1256,6 +1256,10 @@ BufferAsSubscript(PyObject *self, PyObject* idx, PyObject* val) if (PyLong_Check(idx)) { long n = PyLong_AsLong(idx); + + if (CheckBuffer((BufferObject *) self)) + return -1; + return RBAsItem((BufferObject *)(self), n, val, 1, (Py_ssize_t)((BufferObject *)(self))->buf->b_ml.ml_line_count, NULL); diff --git a/src/testdir/shared.vim b/src/testdir/shared.vim index 3e0929c349..bbd28be1cc 100644 --- a/src/testdir/shared.vim +++ b/src/testdir/shared.vim @@ -353,4 +353,19 @@ func GetMessages() return msg_list endfunc +" Run the list of commands in 'cmds' and look for 'errstr' in exception. +" Note that assert_fails() cannot be used in some places and this function +" can be used. +func AssertException(cmds, errstr) + let save_exception = '' + try + for cmd in a:cmds + exe cmd + endfor + catch + let save_exception = v:exception + endtry + call assert_match(a:errstr, save_exception) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_python2.vim b/src/testdir/test_python2.vim index 928783536d..f0b9b06c72 100644 --- a/src/testdir/test_python2.vim +++ b/src/testdir/test_python2.vim @@ -3,6 +3,7 @@ source check.vim CheckFeature python CheckFeature quickfix +source shared.vim " NOTE: This will cause errors when run under valgrind. " This would require recompiling Python with: @@ -55,13 +56,13 @@ func Test_AAA_python_setup() endfunc func Test_pydo() - " Check deleting lines does not trigger ml_get error. + " Check deleting lines does not trigger an ml_get error. new call setline(1, ['one', 'two', 'three']) pydo vim.command("%d_") bwipe! - " Check switching to another buffer does not trigger ml_get error. + " Check switching to another buffer does not trigger an ml_get error. new let wincount = winnr('$') call setline(1, ['one', 'two', 'three']) @@ -69,6 +70,19 @@ func Test_pydo() call assert_equal(wincount + 1, winnr('$')) bwipe! bwipe! + + " Try modifying a buffer with 'nomodifiable' set + set nomodifiable + call assert_fails('pydo toupper(line)', 'cannot save undo information') + set modifiable + + " Invalid command + call AssertException(['pydo non_existing_cmd'], + \ "Vim(pydo):NameError: global name 'non_existing_cmd' is not defined") + call AssertException(["pydo raise Exception('test')"], + \ 'Vim(pydo):Exception: test') + call AssertException(["pydo {lambda}"], + \ 'Vim(pydo):SyntaxError: invalid syntax') endfunc func Test_set_cursor() @@ -106,14 +120,9 @@ func Test_vim_function() call assert_false(v:exception) endtry - let caught_vim_err = v:false - try - let x = pyeval('f.abc') - catch - call assert_match('AttributeError: abc', v:exception) - let caught_vim_err = v:true - endtry - call assert_equal(v:true, caught_vim_err) + " Non-existing function attribute + call AssertException(["let x = pyeval('f.abc')"], + \ 'Vim(let):AttributeError: abc') py del f delfunc s:foo @@ -250,6 +259,9 @@ func Test_python_range() py r = b.range(1, 3) call assert_equal(0, pyeval('r.start')) call assert_equal(2, pyeval('r.end')) + call assert_equal('one', pyeval('r[0]')) + call assert_equal('one', pyeval('r[-3]')) + call assert_equal('three', pyeval('r[-4]')) call assert_equal(['two', 'three'], pyeval('r[1:]')) py r[0] = 'green' call assert_equal(['green', 'two', 'three'], getline(1, '$')) @@ -257,14 +269,22 @@ func Test_python_range() call assert_equal(['red', 'blue', 'three'], getline(1, '$')) call assert_equal(['start', 'end', '__members__'], pyeval('r.__members__')) - let caught_vim_err = v:false - try - let x = pyeval('r.abc') - catch - call assert_match('AttributeError: abc', v:exception) - let caught_vim_err = v:true - endtry - call assert_equal(v:true, caught_vim_err) + " try different invalid start/end index for the range slice + %d + call setline(1, ['one', 'two', 'three']) + py r[-10:1] = ["a"] + py r[10:12] = ["b"] + py r[-10:-9] = ["c"] + py r[1:0] = ["d"] + call assert_equal(['c', 'd', 'a', 'two', 'three', 'b'], getline(1, '$')) + + " FIXME: The following code triggers ml_get errors + " %d + " let x = pyeval('r[:]') + + " Non-existing range attribute + call AssertException(["let x = pyeval('r.abc')"], + \ 'Vim(let):AttributeError: abc') close! endfunc @@ -273,33 +293,50 @@ endfunc func Test_python_tabpage() tabnew py t = vim.tabpages[1] + py wl = t.windows tabclose - let caught_vim_err = v:false - try - let n = pyeval('t.number') - catch - call assert_match('vim.error: attempt to refer to deleted tab page', - \ v:exception) - let caught_vim_err = v:true - endtry - call assert_equal(v:true, caught_vim_err) + " Accessing a closed tabpage + call AssertException(["let n = pyeval('t.number')"], + \ 'Vim(let):vim.error: attempt to refer to deleted tab page') + call AssertException(["let n = pyeval('len(wl)')"], + \ 'Vim(let):vim.error: attempt to refer to deleted tab page') + call AssertException(["py w = wl[0]"], + \ 'Vim(python):vim.error: attempt to refer to deleted tab page') + call AssertException(["py vim.current.tabpage = t"], + \ 'Vim(python):vim.error: attempt to refer to deleted tab page') + call assert_match(', , ]", substitute(pyeval('repr(dv)'),'0x\x\+','','g')) @@ -552,6 +640,11 @@ func Test_python_lockedvar() EOF call assert_equal(['', "l[2] threw vim.error: error:('list is locked',)"], \ getline(1, '$')) + + " Try to concatenate a locked list + call AssertException(['py l += [4, 5]'], + \ 'Vim(python):vim.error: list is locked') + call assert_equal([0, 1, 2, 3], l) unlockvar! l close! @@ -665,6 +758,11 @@ func Test_python_lock_scope_attr() call assert_equal([0], l) call assert_equal([1], ll) unlet l ll + + " Try changing an attribute of a fixed list + py a = vim.bindeval('v:argv') + call AssertException(['py a.locked = 0'], + \ 'Vim(python):TypeError: cannot modify fixed list') endfunc " Test for pyeval() @@ -679,42 +777,38 @@ func Test_python_pyeval() call assert_equal(v:none, pyeval('None')) call assert_equal('', v:errmsg) + py v = vim.eval('test_null_function()') + call assert_equal(v:none, pyeval('v')) + if has('float') call assert_equal(0.0, pyeval('0.0')) endif - " Invalid values: - let caught_859 = 0 - try - let v = pyeval('"\0"') - catch /E859:/ - let caught_859 = 1 - endtry - call assert_equal(1, caught_859) + " Evaluate an invalid values + call AssertException(['let v = pyeval(''"\0"'')'], 'E859:') + call AssertException(['let v = pyeval(''{"\0" : 1}'')'], 'E859:') + call AssertException(['let v = pyeval("undefined_name")'], + \ "Vim(let):NameError: name 'undefined_name' is not defined") + call AssertException(['let v = pyeval("vim")'], 'E859:') +endfunc - let caught_859 = 0 - try - let v = pyeval('{"\0" : 1}') - catch /E859:/ - let caught_859 = 1 - endtry - call assert_equal(1, caught_859) +" Test for vim.bindeval() +func Test_python_vim_bindeval() + " Float + let f = 3.14 + py f = vim.bindeval('f') + call assert_equal(3.14, pyeval('f')) - let caught_nameerr = 0 - try - let v = pyeval("undefined_name") - catch /NameError: name 'undefined_name'/ - let caught_nameerr = 1 - endtry - call assert_equal(1, caught_nameerr) + " Blob + let b = 0z12 + py b = vim.bindeval('b') + call assert_equal("\x12", pyeval('b')) - let caught_859 = 0 - try - let v = pyeval("vim") - catch /E859:/ - let caught_859 = 1 - endtry - call assert_equal(1, caught_859) + " Bool + call assert_equal(1, pyeval("vim.bindeval('v:true')")) + call assert_equal(0, pyeval("vim.bindeval('v:false')")) + call assert_equal(v:none, pyeval("vim.bindeval('v:null')")) + call assert_equal(v:none, pyeval("vim.bindeval('v:none')")) endfunc " threading @@ -812,7 +906,24 @@ func Test_python_list_slice() call assert_equal([0, 2, 4], pyeval('l')) py l = ll[4:2:1] call assert_equal([], pyeval('l')) + + " Error case: Use an invalid index + call AssertException(['py ll[-10] = 5'], 'Vim(python):vim.error: internal error:') + + " Use a step value of 0 + call AssertException(['py ll[0:3:0] = [1, 2, 3]'], + \ 'Vim(python):ValueError: slice step cannot be zero') + + " Error case: Invalid slice type + call AssertException(["py x = ll['abc']"], + \ 'Vim(python):TypeError: index must be int or slice, not str') py del l + + " Error case: List with a null list item + let l = [test_null_list()] + py ll = vim.bindeval('l') + call AssertException(["py x = ll[:]"], + \ 'Vim(python):SystemError: error return without exception set') endfunc " Vars @@ -1249,6 +1360,24 @@ func Test_python_opts() call assert_equal(expected, g:res) unlet g:res + + call assert_equal(0, pyeval("'' in vim.options")) + + " use an empty key to index vim.options + call AssertException(["let v = pyeval(\"vim.options['']\")"], + \ 'Vim(let):ValueError: empty keys are not allowed') + call AssertException(["py vim.current.window.options[''] = 0"], + \ 'Vim(python):ValueError: empty keys are not allowed') + call AssertException(["py vim.current.window.options[{}] = 0"], + \ 'Vim(python):TypeError: expected str() or unicode() instance, but got dict') + + " set one of the number options to a very large number + let cmd = ["py vim.options['previewheight'] = 9999999999999999"] + call AssertException(cmd, 'OverflowError:') + + " unset a global-local string option + call AssertException(["py del vim.options['errorformat']"], + \ 'Vim(python):ValueError: unable to unset global option errorformat') endfunc " Test for vim.buffer object @@ -1267,11 +1396,26 @@ func Test_python_buffer() py b = vim.current.buffer wincmd w + " Test for getting lines from the buffer using a slice + call assert_equal(['First line'], pyeval('b[-10:1]')) + call assert_equal(['Third line'], pyeval('b[2:10]')) + call assert_equal([], pyeval('b[2:0]')) + call assert_equal([], pyeval('b[10:12]')) + call assert_equal([], pyeval('b[-10:-8]')) + " Tests BufferAppend and BufferItem py cb.append(b[0]) call assert_equal(['First line'], getbufline(bnr1, 2)) %d + " Try to append using out-of-range line number + call AssertException(["py b.append('abc', 10)"], + \ 'Vim(python):IndexError: line number out of range') + + " Append a non-string item + call AssertException(["py b.append([22])"], + \ 'Vim(python):TypeError: expected str() or unicode() instance, but got int') + " Tests BufferSlice and BufferAssSlice py cb.append('abc5') # Will be overwritten py cb[-1:] = b[:-2] @@ -1363,11 +1507,62 @@ func Test_python_buffer() EOF call assert_equal([''], getline(1, '$')) + " Delete all the lines in a buffer + call setline(1, ['a', 'b', 'c']) + py vim.current.buffer[:] = [] + call assert_equal([''], getline(1, '$')) + + " Test for modifying a 'nomodifiable' buffer + setlocal nomodifiable + call AssertException(["py vim.current.buffer[0] = 'abc'"], + \ "Vim(python):vim.error: Vim:E21: Cannot make changes, 'modifiable' is off") + call AssertException(["py vim.current.buffer[0] = None"], + \ "Vim(python):vim.error: Vim:E21: Cannot make changes, 'modifiable' is off") + call AssertException(["py vim.current.buffer[:] = None"], + \ "Vim(python):vim.error: Vim:E21: Cannot make changes, 'modifiable' is off") + call AssertException(["py vim.current.buffer[:] = []"], + \ "Vim(python):vim.error: Vim:E21: Cannot make changes, 'modifiable' is off") + call AssertException(["py vim.current.buffer.append('abc')"], + \ "Vim(python):vim.error: Vim:E21: Cannot make changes, 'modifiable' is off") + call AssertException(["py vim.current.buffer.append([])"], + \ "Vim(python):vim.error: Vim:E21: Cannot make changes, 'modifiable' is off") + setlocal modifiable + augroup BUFS autocmd! augroup END augroup! BUFS %bw! + + " Range object for a deleted buffer + new Xfile + call setline(1, ['one', 'two', 'three']) + py b = vim.current.buffer + py r = vim.current.buffer.range(0, 2) + call assert_equal('', pyeval('repr(r)')) + %bw! + call AssertException(['py r[:] = []'], + \ 'Vim(python):vim.error: attempt to refer to deleted buffer') + call assert_match(' Buffer") cb.append(">> StringToLine (indirect)") - ee('vim.current.buffer[0] = u"\\na"') ee('vim.current.buffer[0] = "\\na"') + ee('vim.current.buffer[0] = u"\\na"') cb.append(">> SetBufferLine (indirect)") ee('vim.current.buffer[0] = True') cb.append(">> SetBufferLineList (indirect)") @@ -3360,8 +3561,8 @@ func Test_python_errors() vim.windows[1000]:IndexError:('no such window',) > Buffer >> StringToLine (indirect) - vim.current.buffer[0] = u"\na":error:('string cannot contain newlines',) vim.current.buffer[0] = "\na":error:('string cannot contain newlines',) + vim.current.buffer[0] = u"\na":error:('string cannot contain newlines',) >> SetBufferLine (indirect) vim.current.buffer[0] = True:TypeError:('bad argument type for built-in operation',) >> SetBufferLineList (indirect) @@ -3442,6 +3643,7 @@ func Test_python_import() cb.append(tm.__file__.replace('.pyc', '.py').replace(os.path.sep, '/')[-len('modulex/topmodule/__init__.py'):]) cb.append(tms.__file__.replace('.pyc', '.py').replace(os.path.sep, '/')[-len('modulex/topmodule/submodule/__init__.py'):]) cb.append(tmsss.__file__.replace('.pyc', '.py').replace(os.path.sep, '/')[-len('modulex/topmodule/submodule/subsubmodule/subsubsubmodule.py'):]) + del before del after del d @@ -3463,13 +3665,16 @@ func Test_python_import() END call assert_equal(expected, getline(2, '$')) close! + + " Try to import a non-existing moudle with a dot (.) + call AssertException(['py import a.b.c'], 'ImportError:') endfunc " Test exceptions func Test_python_exception() - fun Exe(e) + func Exe(e) execute a:e - endfun + endfunc new py cb = vim.current.buffer diff --git a/src/testdir/test_python3.vim b/src/testdir/test_python3.vim index 7edf7d055c..b3cfbf37c4 100644 --- a/src/testdir/test_python3.vim +++ b/src/testdir/test_python3.vim @@ -2,6 +2,7 @@ source check.vim CheckFeature python3 +source shared.vim " This function should be called first. This sets up python functions used by " the other tests. @@ -73,7 +74,6 @@ endfunc func Test_py3do() " Check deleting lines does not trigger an ml_get error. - py3 import vim new call setline(1, ['one', 'two', 'three']) py3do vim.command("%d_") @@ -87,11 +87,23 @@ func Test_py3do() call assert_equal(wincount + 1, winnr('$')) bwipe! bwipe! + + " Try modifying a buffer with 'nomodifiable' set + set nomodifiable + call assert_fails('py3do toupper(line)', 'cannot save undo information') + set modifiable + + " Invalid command + call AssertException(['py3do non_existing_cmd'], + \ "Vim(py3do):NameError: name 'non_existing_cmd' is not defined") + call AssertException(["py3do raise Exception('test')"], + \ 'Vim(py3do):Exception: test') + call AssertException(["py3do {lambda}"], + \ 'Vim(py3do):SyntaxError: invalid syntax') endfunc func Test_set_cursor() " Check that setting the cursor position works. - py3 import vim new call setline(1, ['first line', 'second line']) normal gg @@ -105,7 +117,6 @@ endfunc func Test_vim_function() " Check creating vim.Function object - py3 import vim func s:foo() return matchstr(expand(''), '\zs\d\+_foo$') @@ -126,14 +137,9 @@ func Test_vim_function() call assert_false(v:exception) endtry - let caught_vim_err = v:false - try - let x = py3eval('f.abc') - catch - call assert_match("AttributeError: 'vim.function' object has no attribute 'abc'", v:exception) - let caught_vim_err = v:true - endtry - call assert_equal(v:true, caught_vim_err) + " Non-existing function attribute + call AssertException(["let x = py3eval('f.abc')"], + \ "Vim(let):AttributeError: 'vim.function' object has no attribute 'abc'") py3 del f delfunc s:foo @@ -148,7 +154,6 @@ func Test_skipped_python3_command_does_not_affect_pyxversion() endfunc func _SetUpHiddenBuffer() - py3 import vim new edit hidden setlocal bufhidden=hide @@ -198,7 +203,6 @@ func Test_Write_To_HiddenBuffer_Does_Not_Fix_Cursor_ClearLine() endfunc func _SetUpVisibleBuffer() - py3 import vim new let lnum = 0 while lnum < 10 @@ -303,8 +307,8 @@ func Test_python3_range() call assert_fails('py3 r[3] = "x"', 'IndexError: line number out of range') call assert_fails('py3 x = r[3]', 'IndexError: line number out of range') - call assert_fails('py3 r["a"] = "x"', 'TypeError') - call assert_fails('py3 x = r["a"]', 'TypeError') + call assert_fails('py3 r["a"] = "x"', 'TypeError: index must be int or slice, not str') + call assert_fails('py3 x = r["a"]', 'TypeError: index must be int or slice, not str') py3 del r[:] call assert_equal(['1', '5', '6'], getline(1, '$')) @@ -431,8 +435,112 @@ s+='B' call assert_equal('ABCDE', pyxeval('s')) endfunc +" Test for the buffer range object +func Test_python3_range2() + new + call setline(1, ['one', 'two', 'three']) + py3 b = vim.current.buffer + py3 r = b.range(1, 3) + call assert_equal(0, py3eval('r.start')) + call assert_equal(2, py3eval('r.end')) + call assert_equal('one', py3eval('r[0]')) + call assert_equal('one', py3eval('r[-3]')) + call AssertException(["let x = py3eval('r[-4]')"], + \ 'Vim(let):IndexError: line number out of range') + call assert_equal(['two', 'three'], py3eval('r[1:]')) + py3 r[0] = 'green' + call assert_equal(['green', 'two', 'three'], getline(1, '$')) + py3 r[0:2] = ['red', 'blue'] + call assert_equal(['red', 'blue', 'three'], getline(1, '$')) + + " try different invalid start/end index for the range slice + %d + call setline(1, ['one', 'two', 'three']) + py3 r[-10:1] = ["a"] + py3 r[10:12] = ["b"] + py3 r[-10:-9] = ["c"] + py3 r[1:0] = ["d"] + call assert_equal(['c', 'd', 'a', 'two', 'three', 'b'], getline(1, '$')) + + " FIXME: The following code triggers ml_get errors + " %d + " let x = py3eval('r[:]') + + " Non-existing range attribute + call AssertException(["let x = py3eval('r.abc')"], + \ "Vim(let):AttributeError: 'vim.range' object has no attribute 'abc'") + + close! +endfunc + +" Test for the python tabpage object +func Test_python3_tabpage() + tabnew + py3 t = vim.tabpages[1] + py3 wl = t.windows + tabclose + " Accessing a closed tabpage + call AssertException(["let n = py3eval('t.number')"], + \ 'Vim(let):vim.error: attempt to refer to deleted tab page') + call AssertException(["let n = py3eval('len(wl)')"], + \ 'Vim(let):vim.error: attempt to refer to deleted tab page') + call AssertException(["py3 w = wl[0]"], + \ 'Vim(py3):vim.error: attempt to refer to deleted tab page') + call AssertException(["py3 vim.current.tabpage = t"], + \ 'Vim(py3):vim.error: attempt to refer to deleted tab page') + call assert_match(' returned NULL without setting an error') + + " Try to convert a List with a null List item + call AssertException(["py3 t = vim.eval('[test_null_list()]')"], + \ 'Vim(py3):SystemError: returned NULL without setting an error') + + " Try to bind a null List variable + let cmds =<< trim END + let l = test_null_list() + py3 ll = vim.bindeval('l') + END + call AssertException(cmds, + \ 'Vim(py3):SystemError: returned NULL without setting an error') + let l = [] py3 l = vim.bindeval('l') py3 f = vim.bindeval('function("strlen")') @@ -447,6 +555,39 @@ func Test_python3_list() call assert_equal([0, "as'd", [1, 2, function("strlen"), {'a': 1}]], l) py3 l[-2] = f call assert_equal([0, function("strlen"), [1, 2, function("strlen"), {'a': 1}]], l) + + " appending to a list + let l = [1, 2] + py3 ll = vim.bindeval('l') + py3 ll[2] = 8 + call assert_equal([1, 2, 8], l) + + " Using dict as an index + call AssertException(['py3 ll[{}] = 10'], + \ 'Vim(py3):TypeError: index must be int or slice, not dict') +endfunc + +" Test for the python Dict object +func Test_python3_dict() + " Try to convert a null Dict + call AssertException(["py3 t = vim.eval('test_null_dict()')"], + \ 'Vim(py3):SystemError: returned NULL without setting an error') + + " Try to convert a Dict with a null List value + call AssertException(["py3 t = vim.eval(\"{'a' : test_null_list()}\")"], + \ 'Vim(py3):SystemError: returned NULL without setting an error') + + " Try to convert a Dict with a null string key + py3 t = vim.eval("{test_null_string() : 10}") + call assert_fails("let d = py3eval('t')", 'E859:') + + " Dict length + let d = {'a' : 10, 'b' : 20} + py3 d = vim.bindeval('d') + call assert_equal(2, py3eval('len(d)')) + + " Deleting an non-existing key + call AssertException(["py3 del d['c']"], "Vim(py3):KeyError: 'c'") endfunc " Extending Dictionary directly with different types @@ -472,6 +613,12 @@ func Test_python3_dict_extend() di.sort(key=repr) EOF + " Try extending a locked dictionary + lockvar d + call AssertException(["py3 d.update({'b' : 20})"], + \ 'Vim(py3):vim.error: dictionary is locked') + unlockvar d + call assert_equal(1, py3eval("d['f'](self={})")) call assert_equal("[b'-1', b'0', b'1', b'b', b'f']", py3eval('repr(dk)')) call assert_equal("[-1, , , , b'asd']", substitute(py3eval('repr(dv)'),'0x\x\+','','g')) @@ -668,6 +815,10 @@ func Test_python3_lockedvar() EOF call assert_equal(['', "l[2] threw vim.error: error:('list is locked',)"], \ getline(1, '$')) + + " Try to concatenate a locked list + call AssertException(['py3 l += [4, 5]'], 'Vim(py3):vim.error: list is locked') + call assert_equal([0, 1, 2, 3], l) unlockvar! l close! @@ -785,6 +936,11 @@ func Test_python3_lock_scope_attr() call assert_equal([0], l) call assert_equal([1], ll) unlet l ll + + " Try changing an attribute of a fixed list + py3 a = vim.bindeval('v:argv') + call AssertException(['py3 a.locked = 0'], + \ 'Vim(py3):TypeError: cannot modify fixed list') endfunc " Test for py3eval() @@ -799,48 +955,44 @@ func Test_python3_pyeval() call assert_equal(v:none, py3eval('None')) call assert_equal('', v:errmsg) + py3 v = vim.eval('test_null_function()') + call assert_equal(v:none, py3eval('v')) + if has('float') call assert_equal(0.0, py3eval('0.0')) endif - " Invalid values: - let caught_859 = 0 - try - let v = py3eval('"\0"') - catch /E859:/ - let caught_859 = 1 - endtry - call assert_equal(1, caught_859) + " Evaluate an invalid values + call AssertException(['let v = py3eval(''"\0"'')'], 'E859:') + call AssertException(['let v = py3eval(''{"\0" : 1}'')'], 'E859:') + call AssertException(['let v = py3eval("undefined_name")'], + \ "Vim(let):NameError: name 'undefined_name' is not defined") + call AssertException(['let v = py3eval("vim")'], 'E859:') +endfunc - let caught_859 = 0 - try - let v = py3eval('{"\0" : 1}') - catch /E859:/ - let caught_859 = 1 - endtry - call assert_equal(1, caught_859) +" Test for vim.bindeval() +func Test_python3_vim_bindeval() + " Float + let f = 3.14 + py3 f = vim.bindeval('f') + call assert_equal(3.14, py3eval('f')) - let caught_nameerr = 0 - try - let v = py3eval("undefined_name") - catch /NameError: name 'undefined_name'/ - let caught_nameerr = 1 - endtry - call assert_equal(1, caught_nameerr) + " Blob + let b = 0z12 + py3 b = vim.bindeval('b') + call assert_equal("\x12", py3eval('b')) - let caught_859 = 0 - try - let v = py3eval("vim") - catch /E859:/ - let caught_859 = 1 - endtry - call assert_equal(1, caught_859) + " Bool + call assert_equal(1, py3eval("vim.bindeval('v:true')")) + call assert_equal(0, py3eval("vim.bindeval('v:false')")) + call assert_equal(v:none, py3eval("vim.bindeval('v:null')")) + call assert_equal(v:none, py3eval("vim.bindeval('v:none')")) endfunc " threading " Running py3do command (Test_pydo) before this test, stops the python thread " from running. So this test should be run before the pydo test -func Test_aaa_python_threading() +func Test_aaa_python3_threading() let l = [0] py3 l = vim.bindeval('l') py3 << trim EOF @@ -932,7 +1084,24 @@ func Test_python3_list_slice() call assert_equal([0, 2, 4], py3eval('l')) py3 l = ll[4:2:1] call assert_equal([], py3eval('l')) + + " Error case: Use an invalid index + call AssertException(['py3 ll[-10] = 5'], 'Vim(py3):vim.error: internal error:') + + " Use a step value of 0 + call AssertException(['py3 ll[0:3:0] = [1, 2, 3]'], + \ 'Vim(py3):ValueError: slice step cannot be zero') + + " Error case: Invalid slice type + call AssertException(["py3 x = ll['abc']"], + \ "Vim(py3):TypeError: index must be int or slice, not str") py3 del l + + " Error case: List with a null list item + let l = [test_null_list()] + py3 ll = vim.bindeval('l') + call AssertException(["py3 x = ll[:]"], + \ "Vim(py3):SystemError: error return without exception set") endfunc " Vars @@ -1369,6 +1538,24 @@ func Test_python3_opts() call assert_equal(expected, g:res) unlet g:res + + call assert_equal(0, py3eval("'' in vim.options")) + + " use an empty key to index vim.options + call AssertException(["let v = py3eval(\"vim.options['']\")"], + \ 'Vim(let):ValueError: empty keys are not allowed') + call AssertException(["py3 vim.current.window.options[''] = 0"], + \ 'Vim(py3):ValueError: empty keys are not allowed') + call AssertException(["py3 vim.current.window.options[{}] = 0"], + \ 'Vim(py3):TypeError: expected bytes() or str() instance, but got dict') + + " set one of the number options to a very large number + let cmd = ["py3 vim.options['previewheight'] = 9999999999999999"] + call AssertException(cmd, "Vim(py3):OverflowError:") + + " unset a global-local string option + call AssertException(["py3 del vim.options['errorformat']"], + \ 'Vim(py3):ValueError: unable to unset global option errorformat') endfunc " Test for vim.buffer object @@ -1387,11 +1574,26 @@ func Test_python3_buffer() py3 b = vim.current.buffer wincmd w + " Test for getting lines from the buffer using a slice + call assert_equal(['First line'], py3eval('b[-10:1]')) + call assert_equal(['Third line'], py3eval('b[2:10]')) + call assert_equal([], py3eval('b[2:0]')) + call assert_equal([], py3eval('b[10:12]')) + call assert_equal([], py3eval('b[-10:-8]')) + " Tests BufferAppend and BufferItem py3 cb.append(b[0]) call assert_equal(['First line'], getbufline(bnr1, 2)) %d + " Try to append using out-of-range line number + call AssertException(["py3 b.append('abc', 10)"], + \ 'Vim(py3):IndexError: line number out of range') + + " Append a non-string item + call AssertException(["py3 b.append([22])"], + \ 'Vim(py3):TypeError: expected bytes() or str() instance, but got int') + " Tests BufferSlice and BufferAssSlice py3 cb.append('abc5') # Will be overwritten py3 cb[-1:] = b[:-2] @@ -1483,11 +1685,62 @@ func Test_python3_buffer() EOF call assert_equal([''], getline(1, '$')) + " Delete all the lines in a buffer + call setline(1, ['a', 'b', 'c']) + py3 vim.current.buffer[:] = [] + call assert_equal([''], getline(1, '$')) + + " Test for modifying a 'nomodifiable' buffer + setlocal nomodifiable + call AssertException(["py3 vim.current.buffer[0] = 'abc'"], + \ "Vim(py3):vim.error: Vim:E21: Cannot make changes, 'modifiable' is off") + call AssertException(["py3 vim.current.buffer[0] = None"], + \ "Vim(py3):vim.error: Vim:E21: Cannot make changes, 'modifiable' is off") + call AssertException(["py3 vim.current.buffer[:] = None"], + \ "Vim(py3):vim.error: Vim:E21: Cannot make changes, 'modifiable' is off") + call AssertException(["py3 vim.current.buffer[:] = []"], + \ "Vim(py3):vim.error: Vim:E21: Cannot make changes, 'modifiable' is off") + call AssertException(["py3 vim.current.buffer.append('abc')"], + \ "Vim(py3):vim.error: Vim:E21: Cannot make changes, 'modifiable' is off") + call AssertException(["py3 vim.current.buffer.append([])"], + \ "Vim(py3):vim.error: Vim:E21: Cannot make changes, 'modifiable' is off") + setlocal modifiable + augroup BUFS autocmd! augroup END augroup! BUFS %bw! + + " Range object for a deleted buffer + new Xfile + call setline(1, ['one', 'two', 'three']) + py3 b = vim.current.buffer + py3 r = vim.current.buffer.range(0, 2) + call assert_equal('', py3eval('repr(r)')) + %bw! + call AssertException(['py3 r[:] = []'], + \ 'Vim(py3):vim.error: attempt to refer to deleted buffer') + call assert_match(' Date: Mon, 6 Jul 2020 21:24:57 +0200 Subject: [PATCH 103/105] patch 8.2.1147: :confirm may happen in cooked mode Problem: :confirm may happen in cooked mode. (Jason Franklin) Solution: Switch to raw mode before prompting. (Brandon Pfeifer) --- src/message.c | 6 ++ src/testdir/test_excmd.vim | 120 ++++++++++++++++++++++++++----------- src/version.c | 2 + 3 files changed, 93 insertions(+), 35 deletions(-) diff --git a/src/message.c b/src/message.c index 006e648fe1..a26a63798b 100644 --- a/src/message.c +++ b/src/message.c @@ -3652,6 +3652,7 @@ do_dialog( char_u *hotkeys; int c; int i; + tmode_T save_tmode; #ifndef NO_CONSOLE // Don't output anything in silent mode ("ex -s") @@ -3683,6 +3684,10 @@ do_dialog( State = CONFIRM; setmouse(); + // Ensure raw mode here. + save_tmode = cur_tmode; + settmode(TMODE_RAW); + /* * Since we wait for a keypress, don't make the * user press RETURN as well afterwards. @@ -3743,6 +3748,7 @@ do_dialog( vim_free(hotkeys); } + settmode(save_tmode); State = oldState; setmouse(); --no_wait_return; diff --git a/src/testdir/test_excmd.vim b/src/testdir/test_excmd.vim index 2428e9d02d..2c693e21d4 100644 --- a/src/testdir/test_excmd.vim +++ b/src/testdir/test_excmd.vim @@ -189,49 +189,58 @@ func Test_confirm_cmd() CheckNotGui CheckRunVimInTerminal - call writefile(['foo1'], 'foo') - call writefile(['bar1'], 'bar') + call writefile(['foo1'], 'Xfoo') + call writefile(['bar1'], 'Xbar') " Test for saving all the modified buffers - let buf = RunVimInTerminal('', {'rows': 20}) - call term_sendkeys(buf, ":set nomore\n") - call term_sendkeys(buf, ":new foo\n") - call term_sendkeys(buf, ":call setline(1, 'foo2')\n") - call term_sendkeys(buf, ":new bar\n") - call term_sendkeys(buf, ":call setline(1, 'bar2')\n") - call term_sendkeys(buf, ":wincmd b\n") + let lines =<< trim END + set nomore + new Xfoo + call setline(1, 'foo2') + new Xbar + call setline(1, 'bar2') + wincmd b + END + call writefile(lines, 'Xscript') + let buf = RunVimInTerminal('-S Xscript', {'rows': 20}) call term_sendkeys(buf, ":confirm qall\n") call WaitForAssert({-> assert_match('\[Y\]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ', term_getline(buf, 20))}, 1000) call term_sendkeys(buf, "A") call StopVimInTerminal(buf) - call assert_equal(['foo2'], readfile('foo')) - call assert_equal(['bar2'], readfile('bar')) + call assert_equal(['foo2'], readfile('Xfoo')) + call assert_equal(['bar2'], readfile('Xbar')) " Test for discarding all the changes to modified buffers - let buf = RunVimInTerminal('', {'rows': 20}) - call term_sendkeys(buf, ":set nomore\n") - call term_sendkeys(buf, ":new foo\n") - call term_sendkeys(buf, ":call setline(1, 'foo3')\n") - call term_sendkeys(buf, ":new bar\n") - call term_sendkeys(buf, ":call setline(1, 'bar3')\n") - call term_sendkeys(buf, ":wincmd b\n") + let lines =<< trim END + set nomore + new Xfoo + call setline(1, 'foo3') + new Xbar + call setline(1, 'bar3') + wincmd b + END + call writefile(lines, 'Xscript') + let buf = RunVimInTerminal('-S Xscript', {'rows': 20}) call term_sendkeys(buf, ":confirm qall\n") call WaitForAssert({-> assert_match('\[Y\]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ', term_getline(buf, 20))}, 1000) call term_sendkeys(buf, "D") call StopVimInTerminal(buf) - call assert_equal(['foo2'], readfile('foo')) - call assert_equal(['bar2'], readfile('bar')) + call assert_equal(['foo2'], readfile('Xfoo')) + call assert_equal(['bar2'], readfile('Xbar')) " Test for saving and discarding changes to some buffers - let buf = RunVimInTerminal('', {'rows': 20}) - call term_sendkeys(buf, ":set nomore\n") - call term_sendkeys(buf, ":new foo\n") - call term_sendkeys(buf, ":call setline(1, 'foo4')\n") - call term_sendkeys(buf, ":new bar\n") - call term_sendkeys(buf, ":call setline(1, 'bar4')\n") - call term_sendkeys(buf, ":wincmd b\n") + let lines =<< trim END + set nomore + new Xfoo + call setline(1, 'foo4') + new Xbar + call setline(1, 'bar4') + wincmd b + END + call writefile(lines, 'Xscript') + let buf = RunVimInTerminal('-S Xscript', {'rows': 20}) call term_sendkeys(buf, ":confirm qall\n") call WaitForAssert({-> assert_match('\[Y\]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ', term_getline(buf, 20))}, 1000) call term_sendkeys(buf, "N") @@ -239,11 +248,12 @@ func Test_confirm_cmd() call term_sendkeys(buf, "Y") call StopVimInTerminal(buf) - call assert_equal(['foo4'], readfile('foo')) - call assert_equal(['bar2'], readfile('bar')) + call assert_equal(['foo4'], readfile('Xfoo')) + call assert_equal(['bar2'], readfile('Xbar')) - call delete('foo') - call delete('bar') + call delete('Xscript') + call delete('Xfoo') + call delete('Xbar') endfunc func Test_confirm_cmd_cancel() @@ -251,10 +261,13 @@ func Test_confirm_cmd_cancel() CheckRunVimInTerminal " Test for closing a window with a modified buffer - let buf = RunVimInTerminal('', {'rows': 20}) - call term_sendkeys(buf, ":set nomore\n") - call term_sendkeys(buf, ":new\n") - call term_sendkeys(buf, ":call setline(1, 'abc')\n") + let lines =<< trim END + set nomore + new + call setline(1, 'abc') + END + call writefile(lines, 'Xscript') + let buf = RunVimInTerminal('-S Xscript', {'rows': 20}) call term_sendkeys(buf, ":confirm close\n") call WaitForAssert({-> assert_match('^\[Y\]es, (N)o, (C)ancel: *$', \ term_getline(buf, 20))}, 1000) @@ -267,6 +280,43 @@ func Test_confirm_cmd_cancel() call WaitForAssert({-> assert_match('^ *0,0-1 All$', \ term_getline(buf, 20))}, 1000) call StopVimInTerminal(buf) + call delete('Xscript') +endfunc + +" The ":confirm" prompt was sometimes used with the terminal in cooked mode. +" This test verifies that a "\" character is NOT required to respond to a +" prompt from the ":conf q" and ":conf wq" commands. +func Test_confirm_q_wq() + CheckNotGui + CheckRunVimInTerminal + + call writefile(['foo'], 'Xfoo') + + let lines =<< trim END + set hidden nomore + call setline(1, 'abc') + edit Xfoo + END + call writefile(lines, 'Xscript') + let buf = RunVimInTerminal('-S Xscript', {'rows': 20}) + call term_sendkeys(buf, ":confirm q\n") + call WaitForAssert({-> assert_match('^\[Y\]es, (N)o, (C)ancel: *$', + \ term_getline(buf, 20))}, 1000) + call term_sendkeys(buf, 'C') + call WaitForAssert({-> assert_notmatch('^\[Y\]es, (N)o, (C)ancel: C*$', + \ term_getline(buf, 20))}, 1000) + + call term_sendkeys(buf, ":edit Xfoo\n") + call term_sendkeys(buf, ":confirm wq\n") + call WaitForAssert({-> assert_match('^\[Y\]es, (N)o, (C)ancel: *$', + \ term_getline(buf, 20))}, 1000) + call term_sendkeys(buf, 'C') + call WaitForAssert({-> assert_notmatch('^\[Y\]es, (N)o, (C)ancel: C*$', + \ term_getline(buf, 20))}, 1000) + call StopVimInTerminal(buf) + + call delete('Xscript') + call delete('Xfoo') endfunc " Test for the :print command diff --git a/src/version.c b/src/version.c index 2d0cafc1cc..ec1e12e0b0 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1147, /**/ 1146, /**/ From cbb6bdcd8967edc8ad123746d27ec30ccc7c1718 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 6 Jul 2020 21:53:17 +0200 Subject: [PATCH 104/105] patch 8.2.1148: warning for using int instead of size_t Problem: Warning for using int instead of size_t. Solution: Change "len" argument to size_t. (Mike Williams) --- src/proto/vim9compile.pro | 2 +- src/version.c | 2 ++ src/vim9compile.c | 2 +- src/vim9script.c | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/proto/vim9compile.pro b/src/proto/vim9compile.pro index 040e4d7eb7..05f6d50220 100644 --- a/src/proto/vim9compile.pro +++ b/src/proto/vim9compile.pro @@ -1,5 +1,5 @@ /* vim9compile.c */ -int check_defined(char_u *p, int len, cctx_T *cctx); +int check_defined(char_u *p, size_t len, cctx_T *cctx); type_T *typval2type(typval_T *tv); int check_type(type_T *expected, type_T *actual, int give_msg); char_u *skip_type(char_u *start); diff --git a/src/version.c b/src/version.c index ec1e12e0b0..867eaf55f2 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1148, /**/ 1147, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index db24eaead6..c876da0751 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -288,7 +288,7 @@ lookup_script(char_u *name, size_t len) * Return FAIL and give an error if it defined. */ int -check_defined(char_u *p, int len, cctx_T *cctx) +check_defined(char_u *p, size_t len, cctx_T *cctx) { if (lookup_script(p, len) == OK || (cctx != NULL diff --git a/src/vim9script.c b/src/vim9script.c index ef447c7e35..77fe6b074a 100644 --- a/src/vim9script.c +++ b/src/vim9script.c @@ -324,7 +324,7 @@ handle_import( if (eval_isnamec1(*arg)) while (eval_isnamec(*arg)) ++arg; - if (check_defined(p, (int)(arg - p), cctx) == FAIL) + if (check_defined(p, arg - p, cctx) == FAIL) goto erret; as_name = vim_strnsave(p, arg - p); arg = skipwhite_and_linebreak(arg, evalarg); From 007f9d6ed597bd212acb95be9d0767c97d2a1438 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 6 Jul 2020 23:04:49 +0200 Subject: [PATCH 105/105] patch 8.2.1149: Vim9: :eval command not handled properly Problem: Vim9: :eval command not handled properly. Solution: Compile the :eval command. (closes #6408) --- src/testdir/test_vim9_cmd.vim | 15 +++++++++++++++ src/version.c | 2 ++ src/vim9compile.c | 28 ++++++++++++++-------------- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim index d7c01aae97..14af261713 100644 --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -248,5 +248,20 @@ def Test_bar_after_command() endif enddef +def Test_eval_command() + let from = 3 + let to = 5 + g:val = 111 + def Increment(nrs: list) + for nr in nrs + g:val += nr + endfor + enddef + eval range(from, to) + ->Increment() + assert_equal(111 + 3 + 4 + 5, g:val) + unlet g:val +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index 867eaf55f2..3c8b1431c5 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1149, /**/ 1148, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index c876da0751..ff6668fe46 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -3852,7 +3852,6 @@ compile_expr7( char_u *start = skipwhite(*arg + 1); // Find out what comes after the arguments. - // TODO: pass getline function ret = get_function_args(&start, '-', NULL, NULL, NULL, NULL, TRUE, NULL, NULL); if (ret != FAIL && *start == '>') @@ -6990,21 +6989,12 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx) } // Expression or function call. - if (ea.cmdidx == CMD_eval) + if (ea.cmdidx != CMD_eval) { - p = ea.cmd; - if (compile_expr0(&p, &cctx) == FAIL) - goto erret; - - // drop the return value - generate_instr_drop(&cctx, ISN_DROP, 1); - - line = skipwhite(p); - continue; + // CMD_let cannot happen, compile_assignment() above is used + iemsg("Command from find_ex_command() not handled"); + goto erret; } - // CMD_let cannot happen, compile_assignment() above is used - iemsg("Command from find_ex_command() not handled"); - goto erret; } p = skipwhite(p); @@ -7124,6 +7114,16 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx) line = compile_throw(p, &cctx); break; + case CMD_eval: + if (compile_expr0(&p, &cctx) == FAIL) + goto erret; + + // drop the return value + generate_instr_drop(&cctx, ISN_DROP, 1); + + line = skipwhite(p); + break; + case CMD_echo: case CMD_echon: case CMD_execute: