From ade51c1d898d8796093060b21289ac81ba4ecca1 Mon Sep 17 00:00:00 2001 From: RJ Miller Date: Sat, 9 Jul 2016 13:43:18 -0400 Subject: [PATCH 0001/1671] terminal.c: Handle more special keys --- src/nvim/terminal.c | 52 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index bd7b9fc58f..bfcc123161 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -762,6 +762,7 @@ static VTermKey convert_key(int key, VTermModifier *statep) switch (key) { case K_BS: return VTERM_KEY_BACKSPACE; + case K_S_TAB: *statep |= VTERM_MOD_SHIFT; case TAB: return VTERM_KEY_TAB; case Ctrl_M: return VTERM_KEY_ENTER; case ESC: return VTERM_KEY_ESCAPE; @@ -801,6 +802,57 @@ static VTermKey convert_key(int key, VTermModifier *statep) case K_KMULTIPLY: return VTERM_KEY_KP_MULT; case K_KDIVIDE: return VTERM_KEY_KP_DIVIDE; + case K_S_F1: *statep |= VTERM_MOD_SHIFT; + case K_F1: return VTERM_KEY_FUNCTION(1); + case K_S_F2: *statep |= VTERM_MOD_SHIFT; + case K_F2: return VTERM_KEY_FUNCTION(2); + case K_S_F3: *statep |= VTERM_MOD_SHIFT; + case K_F3: return VTERM_KEY_FUNCTION(3); + case K_S_F4: *statep |= VTERM_MOD_SHIFT; + case K_F4: return VTERM_KEY_FUNCTION(4); + case K_S_F5: *statep |= VTERM_MOD_SHIFT; + case K_F5: return VTERM_KEY_FUNCTION(5); + case K_S_F6: *statep |= VTERM_MOD_SHIFT; + case K_F6: return VTERM_KEY_FUNCTION(6); + case K_S_F7: *statep |= VTERM_MOD_SHIFT; + case K_F7: return VTERM_KEY_FUNCTION(7); + case K_S_F8: *statep |= VTERM_MOD_SHIFT; + case K_F8: return VTERM_KEY_FUNCTION(8); + case K_S_F9: *statep |= VTERM_MOD_SHIFT; + case K_F9: return VTERM_KEY_FUNCTION(9); + case K_S_F10: *statep |= VTERM_MOD_SHIFT; + case K_F10: return VTERM_KEY_FUNCTION(10); + case K_S_F11: *statep |= VTERM_MOD_SHIFT; + case K_F11: return VTERM_KEY_FUNCTION(11); + case K_S_F12: *statep |= VTERM_MOD_SHIFT; + case K_F12: return VTERM_KEY_FUNCTION(12); + + case K_F13: return VTERM_KEY_FUNCTION(13); + case K_F14: return VTERM_KEY_FUNCTION(14); + case K_F15: return VTERM_KEY_FUNCTION(15); + case K_F16: return VTERM_KEY_FUNCTION(16); + case K_F17: return VTERM_KEY_FUNCTION(17); + case K_F18: return VTERM_KEY_FUNCTION(18); + case K_F19: return VTERM_KEY_FUNCTION(19); + case K_F20: return VTERM_KEY_FUNCTION(20); + case K_F21: return VTERM_KEY_FUNCTION(21); + case K_F22: return VTERM_KEY_FUNCTION(22); + case K_F23: return VTERM_KEY_FUNCTION(23); + case K_F24: return VTERM_KEY_FUNCTION(24); + case K_F25: return VTERM_KEY_FUNCTION(25); + case K_F26: return VTERM_KEY_FUNCTION(26); + case K_F27: return VTERM_KEY_FUNCTION(27); + case K_F28: return VTERM_KEY_FUNCTION(28); + case K_F29: return VTERM_KEY_FUNCTION(29); + case K_F30: return VTERM_KEY_FUNCTION(30); + case K_F31: return VTERM_KEY_FUNCTION(31); + case K_F32: return VTERM_KEY_FUNCTION(32); + case K_F33: return VTERM_KEY_FUNCTION(33); + case K_F34: return VTERM_KEY_FUNCTION(34); + case K_F35: return VTERM_KEY_FUNCTION(35); + case K_F36: return VTERM_KEY_FUNCTION(36); + case K_F37: return VTERM_KEY_FUNCTION(37); + default: return VTERM_KEY_NONE; } } From d23403a1df624792077559badc1f5f1037d81f5d Mon Sep 17 00:00:00 2001 From: RJ Miller Date: Sat, 9 Jul 2016 14:36:20 -0400 Subject: [PATCH 0002/1671] terminal.c: move mod logic into convert_modifiers --- src/nvim/terminal.c | 59 +++++++++++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index bfcc123161..94c8ef0858 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -749,27 +749,56 @@ static int term_sb_pop(int cols, VTermScreenCell *cells, void *data) // }}} // input handling {{{ -static void convert_modifiers(VTermModifier *statep) +static void convert_modifiers(int key, VTermModifier *statep) { if (mod_mask & MOD_MASK_SHIFT) { *statep |= VTERM_MOD_SHIFT; } if (mod_mask & MOD_MASK_CTRL) { *statep |= VTERM_MOD_CTRL; } if (mod_mask & MOD_MASK_ALT) { *statep |= VTERM_MOD_ALT; } + + switch(key) { + case K_S_TAB: + case K_S_LEFT: + case K_S_RIGHT: + case K_S_F1: + case K_S_F2: + case K_S_F3: + case K_S_F4: + case K_S_F5: + case K_S_F6: + case K_S_F7: + case K_S_F8: + case K_S_F9: + case K_S_F10: + case K_S_F11: + case K_S_F12: + *statep |= VTERM_MOD_SHIFT; + break; + + case K_C_LEFT: + case K_C_RIGHT: + *statep |= VTERM_MOD_CTRL; + break; + } } static VTermKey convert_key(int key, VTermModifier *statep) { - convert_modifiers(statep); + convert_modifiers(key, statep); switch (key) { case K_BS: return VTERM_KEY_BACKSPACE; - case K_S_TAB: *statep |= VTERM_MOD_SHIFT; + case K_S_TAB: case TAB: return VTERM_KEY_TAB; case Ctrl_M: return VTERM_KEY_ENTER; case ESC: return VTERM_KEY_ESCAPE; case K_UP: return VTERM_KEY_UP; case K_DOWN: return VTERM_KEY_DOWN; + case K_S_LEFT: + case K_C_LEFT: case K_LEFT: return VTERM_KEY_LEFT; + case K_S_RIGHT: + case K_C_RIGHT: case K_RIGHT: return VTERM_KEY_RIGHT; case K_INS: return VTERM_KEY_INS; @@ -802,29 +831,29 @@ static VTermKey convert_key(int key, VTermModifier *statep) case K_KMULTIPLY: return VTERM_KEY_KP_MULT; case K_KDIVIDE: return VTERM_KEY_KP_DIVIDE; - case K_S_F1: *statep |= VTERM_MOD_SHIFT; + case K_S_F1: case K_F1: return VTERM_KEY_FUNCTION(1); - case K_S_F2: *statep |= VTERM_MOD_SHIFT; + case K_S_F2: case K_F2: return VTERM_KEY_FUNCTION(2); - case K_S_F3: *statep |= VTERM_MOD_SHIFT; + case K_S_F3: case K_F3: return VTERM_KEY_FUNCTION(3); - case K_S_F4: *statep |= VTERM_MOD_SHIFT; + case K_S_F4: case K_F4: return VTERM_KEY_FUNCTION(4); - case K_S_F5: *statep |= VTERM_MOD_SHIFT; + case K_S_F5: case K_F5: return VTERM_KEY_FUNCTION(5); - case K_S_F6: *statep |= VTERM_MOD_SHIFT; + case K_S_F6: case K_F6: return VTERM_KEY_FUNCTION(6); - case K_S_F7: *statep |= VTERM_MOD_SHIFT; + case K_S_F7: case K_F7: return VTERM_KEY_FUNCTION(7); - case K_S_F8: *statep |= VTERM_MOD_SHIFT; + case K_S_F8: case K_F8: return VTERM_KEY_FUNCTION(8); - case K_S_F9: *statep |= VTERM_MOD_SHIFT; + case K_S_F9: case K_F9: return VTERM_KEY_FUNCTION(9); - case K_S_F10: *statep |= VTERM_MOD_SHIFT; + case K_S_F10: case K_F10: return VTERM_KEY_FUNCTION(10); - case K_S_F11: *statep |= VTERM_MOD_SHIFT; + case K_S_F11: case K_F11: return VTERM_KEY_FUNCTION(11); - case K_S_F12: *statep |= VTERM_MOD_SHIFT; + case K_S_F12: case K_F12: return VTERM_KEY_FUNCTION(12); case K_F13: return VTERM_KEY_FUNCTION(13); From 4fd4f66514da4dfcd5d84c709be89d57616be753 Mon Sep 17 00:00:00 2001 From: RJ Miller Date: Sun, 10 Jul 2016 14:57:35 -0400 Subject: [PATCH 0003/1671] terminal.c: add more arrow key support --- src/nvim/terminal.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 94c8ef0858..df15921f41 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -755,8 +755,10 @@ static void convert_modifiers(int key, VTermModifier *statep) if (mod_mask & MOD_MASK_CTRL) { *statep |= VTERM_MOD_CTRL; } if (mod_mask & MOD_MASK_ALT) { *statep |= VTERM_MOD_ALT; } - switch(key) { + switch (key) { case K_S_TAB: + case K_S_UP: + case K_S_DOWN: case K_S_LEFT: case K_S_RIGHT: case K_S_F1: @@ -792,7 +794,9 @@ static VTermKey convert_key(int key, VTermModifier *statep) case Ctrl_M: return VTERM_KEY_ENTER; case ESC: return VTERM_KEY_ESCAPE; + case K_S_UP: case K_UP: return VTERM_KEY_UP; + case K_S_DOWN: case K_DOWN: return VTERM_KEY_DOWN; case K_S_LEFT: case K_C_LEFT: From 5ffa01c8b78292bef0646168da856c726c2e4bc7 Mon Sep 17 00:00:00 2001 From: RJ Miller Date: Sun, 10 Jul 2016 20:04:09 -0400 Subject: [PATCH 0004/1671] terminal.c: handle ctrl+space and ctrl+@ --- src/nvim/terminal.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index df15921f41..c4a8a52816 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -512,6 +512,12 @@ void terminal_send(Terminal *term, char *data, size_t size) void terminal_send_key(Terminal *term, int c) { VTermModifier mod = VTERM_MOD_NONE; + + // Convert K_ZERO back to ASCII + if (c == K_ZERO) { + c = Ctrl_AT; + } + VTermKey key = convert_key(c, &mod); if (key) { From a1d545f8e521e0f58633c42407e24d533111eb6c Mon Sep 17 00:00:00 2001 From: RJ Miller Date: Mon, 14 Nov 2016 18:33:28 -0500 Subject: [PATCH 0005/1671] terminal.c: label fallthrough on big switch --- src/nvim/terminal.c | 50 ++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index c4a8a52816..30556a3835 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -795,20 +795,20 @@ static VTermKey convert_key(int key, VTermModifier *statep) switch (key) { case K_BS: return VTERM_KEY_BACKSPACE; - case K_S_TAB: + case K_S_TAB: // FALLTHROUGH case TAB: return VTERM_KEY_TAB; case Ctrl_M: return VTERM_KEY_ENTER; case ESC: return VTERM_KEY_ESCAPE; - case K_S_UP: + case K_S_UP: // FALLTHROUGH case K_UP: return VTERM_KEY_UP; - case K_S_DOWN: + case K_S_DOWN: // FALLTHROUGH case K_DOWN: return VTERM_KEY_DOWN; - case K_S_LEFT: - case K_C_LEFT: + case K_S_LEFT: // FALLTHROUGH + case K_C_LEFT: // FALLTHROUGH case K_LEFT: return VTERM_KEY_LEFT; - case K_S_RIGHT: - case K_C_RIGHT: + case K_S_RIGHT: // FALLTHROUGH + case K_C_RIGHT: // FALLTHROUGH case K_RIGHT: return VTERM_KEY_RIGHT; case K_INS: return VTERM_KEY_INS; @@ -818,22 +818,22 @@ static VTermKey convert_key(int key, VTermModifier *statep) case K_PAGEUP: return VTERM_KEY_PAGEUP; case K_PAGEDOWN: return VTERM_KEY_PAGEDOWN; - case K_K0: + case K_K0: // FALLTHROUGH case K_KINS: return VTERM_KEY_KP_0; - case K_K1: + case K_K1: // FALLTHROUGH case K_KEND: return VTERM_KEY_KP_1; case K_K2: return VTERM_KEY_KP_2; - case K_K3: + case K_K3: // FALLTHROUGH case K_KPAGEDOWN: return VTERM_KEY_KP_3; case K_K4: return VTERM_KEY_KP_4; case K_K5: return VTERM_KEY_KP_5; case K_K6: return VTERM_KEY_KP_6; - case K_K7: + case K_K7: // FALLTHROUGH case K_KHOME: return VTERM_KEY_KP_7; case K_K8: return VTERM_KEY_KP_8; - case K_K9: + case K_K9: // FALLTHROUGH case K_KPAGEUP: return VTERM_KEY_KP_9; - case K_KDEL: + case K_KDEL: // FALLTHROUGH case K_KPOINT: return VTERM_KEY_KP_PERIOD; case K_KENTER: return VTERM_KEY_KP_ENTER; case K_KPLUS: return VTERM_KEY_KP_PLUS; @@ -841,29 +841,29 @@ static VTermKey convert_key(int key, VTermModifier *statep) case K_KMULTIPLY: return VTERM_KEY_KP_MULT; case K_KDIVIDE: return VTERM_KEY_KP_DIVIDE; - case K_S_F1: + case K_S_F1: // FALLTHROUGH case K_F1: return VTERM_KEY_FUNCTION(1); - case K_S_F2: + case K_S_F2: // FALLTHROUGH case K_F2: return VTERM_KEY_FUNCTION(2); - case K_S_F3: + case K_S_F3: // FALLTHROUGH case K_F3: return VTERM_KEY_FUNCTION(3); - case K_S_F4: + case K_S_F4: // FALLTHROUGH case K_F4: return VTERM_KEY_FUNCTION(4); - case K_S_F5: + case K_S_F5: // FALLTHROUGH case K_F5: return VTERM_KEY_FUNCTION(5); - case K_S_F6: + case K_S_F6: // FALLTHROUGH case K_F6: return VTERM_KEY_FUNCTION(6); - case K_S_F7: + case K_S_F7: // FALLTHROUGH case K_F7: return VTERM_KEY_FUNCTION(7); - case K_S_F8: + case K_S_F8: // FALLTHROUGH case K_F8: return VTERM_KEY_FUNCTION(8); - case K_S_F9: + case K_S_F9: // FALLTHROUGH case K_F9: return VTERM_KEY_FUNCTION(9); - case K_S_F10: + case K_S_F10: // FALLTHROUGH case K_F10: return VTERM_KEY_FUNCTION(10); - case K_S_F11: + case K_S_F11: // FALLTHROUGH case K_F11: return VTERM_KEY_FUNCTION(11); - case K_S_F12: + case K_S_F12: // FALLTHROUGH case K_F12: return VTERM_KEY_FUNCTION(12); case K_F13: return VTERM_KEY_FUNCTION(13); From a5481957c65985fc1f885464ae9b43f92d0dc519 Mon Sep 17 00:00:00 2001 From: lonerover Date: Fri, 17 Mar 2017 22:48:42 +0800 Subject: [PATCH 0006/1671] vim-patch:7.4.2255 Problem: The script that checks translations can't handle plurals. Solution: Check for plural msgid and msgstr entries. Leave the cursor on the first error. https://github.com/vim/vim/commit/ec42059b78c1932a44f2bf36ac982109884dc7c7 --- src/nvim/po/check.vim | 76 +++++++++++++++++++++++++++++++------------ src/nvim/version.c | 2 +- 2 files changed, 56 insertions(+), 22 deletions(-) diff --git a/src/nvim/po/check.vim b/src/nvim/po/check.vim index 1622741da6..b323174550 100644 --- a/src/nvim/po/check.vim +++ b/src/nvim/po/check.vim @@ -33,36 +33,66 @@ func! GetMline() return substitute(idline, '[^%]*\(%[-+ #''.0-9*]*l\=[dsuxXpoc%]\)\=', '\1', 'g') endfunc -" This only works when 'wrapscan' is set. +" This only works when 'wrapscan' is not set. let s:save_wrapscan = &wrapscan -set wrapscan +set nowrapscan " Start at the first "msgid" line. 1 -/^msgid -let startline = line('.') +/^msgid\> + +" When an error is detected this is set to the line number. +" Note: this is used in the Makefile. let error = 0 while 1 if getline(line('.') - 1) !~ "no-c-format" - let fromline = GetMline() + " go over the "msgid" and "msgid_plural" lines + let prevfromline = 'foobar' + while 1 + let fromline = GetMline() + if prevfromline != 'foobar' && prevfromline != fromline + echomsg 'Mismatching % in line ' . (line('.') - 1) + echomsg 'msgid: ' . prevfromline + echomsg 'msgid ' . fromline + if error == 0 + let error = line('.') + endif + endif + if getline('.') !~ 'msgid_plural' + break + endif + let prevfromline = fromline + endwhile + if getline('.') !~ '^msgstr' - echo 'Missing "msgstr" in line ' . line('.') - let error = 1 - endif - let toline = GetMline() - if fromline != toline - echo 'Mismatching % in line ' . (line('.') - 1) - echo 'msgid: ' . fromline - echo 'msgstr: ' . toline - let error = 1 + echomsg 'Missing "msgstr" in line ' . line('.') + if error == 0 + let error = line('.') + endif endif + + " check all the 'msgstr' lines + while getline('.') =~ '^msgstr' + let toline = GetMline() + if fromline != toline + echomsg 'Mismatching % in line ' . (line('.') - 1) + echomsg 'msgid: ' . fromline + echomsg 'msgstr: ' . toline + if error == 0 + let error = line('.') + endif + endif + if line('.') == line('$') + break + endif + endwhile endif - " Find next msgid. - " Wrap around at the end of the file, quit when back at the first one. - /^msgid - if line('.') == startline + " Find next msgid. Quit when there is no more. + let lnum = line('.') + silent! /^msgid\> + if line('.') == lnum break endif endwhile @@ -77,12 +107,16 @@ endwhile " 1 if search('msgid "\("\n"\)\?\([EW][0-9]\+:\).*\nmsgstr "\("\n"\)\?[^"]\@=\2\@!') > 0 - echo 'Mismatching error/warning code in line ' . line('.') - let error = 1 + echomsg 'Mismatching error/warning code in line ' . line('.') + if error == 0 + let error = line('.') + endif endif if error == 0 - echo "OK" + echomsg "OK" +else + exe error endif redir END diff --git a/src/nvim/version.c b/src/nvim/version.c index 5b3a7398d5..b9286c2a4e 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -186,7 +186,7 @@ static int included_patches[] = { // 2258 NA // 2257 NA // 2256, - // 2255, + 2255, // 2254 NA // 2253 NA // 2252 NA From 934137fb489f0c99c32abe32e32f683e0ba3d40f Mon Sep 17 00:00:00 2001 From: James McCoy Date: Sun, 19 Mar 2017 13:14:49 -0400 Subject: [PATCH 0007/1671] vim-patch:8.0.0116 Problem: When reading English help and using CTRl-] the language from 'helplang' is used. Solution: Make help tag jumps keep the language. (Tatsuki, test by Hirohito Higashi, closes vim/vim#1249) https://github.com/vim/vim/commit/6dbf66aa3e2197ce41f2b1cc7602bb9c15840548 --- src/nvim/tag.c | 26 ++++++++++++++++----- src/nvim/testdir/test_help_tagjump.vim | 32 ++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 2c70f396a1..9c9f24b9c0 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -1135,6 +1135,7 @@ find_tags ( char_u *help_lang_find = NULL; /* lang to be found */ char_u help_lang[3]; /* lang of current tags file */ char_u *saved_pat = NULL; /* copy of pat[] */ + bool is_txt = false; pat_T orgpat; /* holds unconverted pattern info */ vimconv_T vimconv; @@ -1232,6 +1233,14 @@ find_tags ( * When the tag file is case-fold sorted, it is either one or the other. * Only ignore case when TAG_NOIC not used or 'ignorecase' set. */ + // Set a flag if the file extension is .txt + if ((flags & TAG_KEEP_LANG) + && help_lang_find == NULL + && curbuf->b_fname != NULL + && (i = (int)STRLEN(curbuf->b_fname)) > 4 + && STRICMP(curbuf->b_fname + i - 4, ".txt") == 0) { + is_txt = true; + } orgpat.regmatch.rm_ic = ((p_ic || !noic) && (findall || orgpat.headlen == 0 || !p_tbs)); for (round = 1; round <= 2; ++round) { @@ -1247,13 +1256,18 @@ find_tags ( fp = NULL; // avoid GCC warning } else { if (curbuf->b_help) { - /* Prefer help tags according to 'helplang'. Put the - * two-letter language name in help_lang[]. */ - i = (int)STRLEN(tag_fname); - if (i > 3 && tag_fname[i - 3] == '-') - STRCPY(help_lang, tag_fname + i - 2); - else + // Keep en if the file extension is .txt + if (is_txt) { STRCPY(help_lang, "en"); + } else { + /* Prefer help tags according to 'helplang'. Put the + * two-letter language name in help_lang[]. */ + i = (int)STRLEN(tag_fname); + if (i > 3 && tag_fname[i - 3] == '-') + STRCPY(help_lang, tag_fname + i - 2); + else + STRCPY(help_lang, "en"); + } /* When searching for a specific language skip tags files * for other languages. */ diff --git a/src/nvim/testdir/test_help_tagjump.vim b/src/nvim/testdir/test_help_tagjump.vim index aabfe40537..1ca0f722cf 100644 --- a/src/nvim/testdir/test_help_tagjump.vim +++ b/src/nvim/testdir/test_help_tagjump.vim @@ -162,4 +162,36 @@ func Test_help_complete() endtry endfunc +func Test_help_respect_current_file_lang() + try + let list = [] + call s:doc_config_setup() + + if has('multi_lang') + function s:check_help_file_ext(help_keyword, ext) + exec 'help ' . a:help_keyword + call assert_equal(a:ext, expand('%:e')) + call feedkeys("\", 'tx') + call assert_equal(a:ext, expand('%:e')) + pop + helpclose + endfunc + + set rtp+=Xdir1/doc-ab + set rtp+=Xdir1/doc-ja + + set helplang=ab + call s:check_help_file_ext('test-char', 'abx') + call s:check_help_file_ext('test-char@ja', 'jax') + set helplang=ab,ja + call s:check_help_file_ext('test-char@ja', 'jax') + call s:check_help_file_ext('test-char@en', 'txt') + endif + catch + call assert_exception('X') + finally + call s:doc_config_teardown() + endtry +endfunc + " vim: shiftwidth=2 sts=2 expandtab From b87cb77570a53b3c0507d5acded0d103e9ab52f9 Mon Sep 17 00:00:00 2001 From: raichoo Date: Sat, 11 Mar 2017 09:56:55 +0100 Subject: [PATCH 0008/1671] vim-patch:7.4.2315 Problem: Insufficient testing for Normal mode commands. Solution: Add a big test. (Christian Brabandt, closes vim/vim#1029) https://github.com/vim/vim/commit/87bc3f74598ae8c648957e5755000cc6cdbc89ce --- src/nvim/testdir/Makefile | 1 + src/nvim/testdir/test_normal.vim | 1994 ++++++++++++++++++++++++++++++ src/nvim/version.c | 2 +- 3 files changed, 1996 insertions(+), 1 deletion(-) create mode 100644 src/nvim/testdir/test_normal.vim diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 9f9ecbc6c9..d090ace432 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -47,6 +47,7 @@ NEW_TESTS ?= \ test_match.res \ test_matchadd_conceal.res \ test_nested_function.res \ + test_normal.res \ test_quickfix.res \ test_signs.res \ test_syntax.res \ diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim new file mode 100644 index 0000000000..1371ccfd5e --- /dev/null +++ b/src/nvim/testdir/test_normal.vim @@ -0,0 +1,1994 @@ +" Test for various Normal mode commands + +func! Setup_NewWindow() + 10new + call setline(1, range(1,100)) +endfunc + +func! MyFormatExpr() + " Adds '->$' at lines having numbers followed by trailing whitespace + for ln in range(v:lnum, v:lnum+v:count-1) + let line = getline(ln) + if getline(ln) =~# '\d\s\+$' + call setline(ln, substitute(line, '\s\+$', '', '') . '->$') + endif + endfor +endfu + +function! CountSpaces(type, ...) + " for testing operatorfunc + " will count the number of spaces + " and return the result in g:a + let sel_save = &selection + let &selection = "inclusive" + let reg_save = @@ + + if a:0 " Invoked from Visual mode, use gv command. + silent exe "normal! gvy" + elseif a:type == 'line' + silent exe "normal! '[V']y" + else + silent exe "normal! `[v`]y" + endif + let g:a=strlen(substitute(@@, '[^ ]', '', 'g')) + let &selection = sel_save + let @@ = reg_save +endfunction + +fun! Test_normal00_optrans() + " Attention: This needs to be the very first test, + " it will fail, if it runs later, don't know why! + " Test for S s and alike comamnds, that are internally handled aliased + new + call append(0, ['1 This is a simple test: abcd', '2 This is the second line', '3 this is the third line']) + 1 + exe "norm! Sfoobar\" + call assert_equal(['foobar', '2 This is the second line', '3 this is the third line', ''], getline(1,'$')) + 2 + " Test does not work + " TODO: Why does it not work? + " Adds an additional linebreak if used in visual mode... + " When run in the test, this returns: + " ,-------- + " |foobar + " |2 This is + " |the second + " |one + " |3 this is the third line + " `----------- + " instead of + " ,-------- + " |foobar + " |2 This is the second one + " |3 this is the third line + " `----------- + exe "norm! $vbsone" + call assert_equal(['foobar', '2 This is the second one', '3 this is the third line', ''], getline(1,'$')) + " When run in the test, this returns: + " ,-------- + " |foobar + " |Second line + " |here + " |3 this is the third line + " `----------- + " instead of + " ,-------- + " |foobar + " |Second line here + " |3 this is the third line + " `----------- + norm! VS Second line here + call assert_equal(['foobar', ' Second line here', '3 this is the third line', ''], getline(1, '$')) + %d + call append(0, ['4 This is a simple test: abcd', '5 This is the second line', '6 this is the third line']) + call append(0, ['1 This is a simple test: abcd', '2 This is the second line', '3 this is the third line']) + + 1 + norm! 2D + call assert_equal(['3 this is the third line', '4 This is a simple test: abcd', '5 This is the second line', '6 this is the third line', ''], getline(1,'$')) + set cpo+=# + norm! 4D + call assert_equal(['', '4 This is a simple test: abcd', '5 This is the second line', '6 this is the third line', ''], getline(1,'$')) + + " clean up + set cpo-=# + bw! +endfu + +func! Test_normal01_keymodel() + call Setup_NewWindow() + " Test 1: depending on 'keymodel' does something different + :50 + call feedkeys("V\y", 'tx') + call assert_equal(['47', '48', '49', '50'], getline("'<", "'>")) + :set keymodel=startsel + :50 + call feedkeys("V\y", 'tx') + call assert_equal(['49', '50'], getline("'<", "'>")) + " Start visual mode when keymodel = startsel + :50 + call feedkeys("\y", 'tx') + call assert_equal(['49', '5'], getreg(0, 0, 1)) + " Do not start visual mode when keymodel= + :set keymodel= + :50 + call feedkeys("\y$", 'tx') + call assert_equal(['42'], getreg(0, 0, 1)) + + " clean up + bw! +endfunc + +func! Test_normal02_selectmode() + " some basic select mode tests + call Setup_NewWindow() + 50 + norm! gHy + call assert_equal('y51', getline('.')) + call setline(1, range(1,100)) + 50 + exe ":norm! V9jo\y" + call assert_equal('y60', getline('.')) + " clean up + bw! +endfu + +func! Test_normal03_join() + " basic join test + call Setup_NewWindow() + 50 + norm! VJ + call assert_equal('50 51', getline('.')) + $ + norm! J + call assert_equal('100', getline('.')) + $ + norm! V9-gJ + call assert_equal('919293949596979899100', getline('.')) + call setline(1, range(1,100)) + $ + :j 10 + call assert_equal('100', getline('.')) + " clean up + bw! +endfu + +func! Test_normal04_filter() + " basic filter test + " only test on non windows platform + if has("win32") || has("win64") || has("win95") + return + endif + call Setup_NewWindow() + 1 + call feedkeys("!!sed -e 's/^/| /'\n", 'tx') + call assert_equal('| 1', getline('.')) + 90 + :sil :!echo one + call feedkeys('.', 'tx') + call assert_equal('| 90', getline('.')) + 95 + set cpo+=! + " 2 , 1: for executing the command, + " 2: clear hit-enter-prompt + call feedkeys("!!\n", 'tx') + call feedkeys(":!echo one\n\n", 'tx') + call feedkeys(".", 'tx') + call assert_equal('one', getline('.')) + set cpo-=! + bw! +endfu + +func! Test_normal05_formatexpr() + " basic formatexpr test + call Setup_NewWindow() + %d_ + call setline(1, ['here: 1 ', '2', 'here: 3 ', '4', 'not here: ']) + 1 + set formatexpr=MyFormatExpr() + norm! gqG + call assert_equal(['here: 1->$', '2', 'here: 3->$', '4', 'not here: '], getline(1,'$')) + set formatexpr= + bw! +endfu + +func! Test_normal06_formatprg() + " basic test for formatprg + " only test on non windows platform + if has("win32") || has("win64") || has("win95") + return + else + " uses sed to number non-empty lines + call writefile(['#!/bin/sh', 'sed ''/./=''|sed ''/./{', 'N', 's/\n/ /', '}'''], 'Xsed_format.sh') + call system('chmod +x ./Xsed_format.sh') + endif + call Setup_NewWindow() + %d + call setline(1, ['a', '', 'c', '', ' ', 'd', 'e']) + set formatprg=./Xsed_format.sh + norm! gggqG + call assert_equal(['1 a', '', '3 c', '', '5 ', '6 d', '7 e'], getline(1, '$')) + " clean up + set formatprg= + call delete('Xsed_format.sh') + bw! +endfu + +func! Test_normal07_internalfmt() + " basic test for internal formmatter to textwidth of 12 + let list=range(1,11) + call map(list, 'v:val." "') + 10new + call setline(1, list) + set tw=12 + norm! gggqG + call assert_equal(['1 2 3', '4 5 6', '7 8 9', '10 11 '], getline(1, '$')) + " clean up + set formatprg= + bw! +endfu + +func! Test_normal08_fold() + " basic tests for foldopen/folddelete + if !has("folding") + return + endif + call Setup_NewWindow() + 50 + setl foldenable fdm=marker + " First fold + norm! V4jzf + " check that folds have been created + call assert_equal(['50/*{{{*/', '51', '52', '53', '54/*}}}*/'], getline(50,54)) + " Second fold + 46 + norm! V10jzf + " check that folds have been created + call assert_equal('46/*{{{*/', getline(46)) + call assert_equal('60/*}}}*/', getline(60)) + norm! k + call assert_equal('45', getline('.')) + norm! j + call assert_equal('46/*{{{*/', getline('.')) + norm! j + call assert_equal('61', getline('.')) + norm! k + " open a fold + norm! Vzo + norm! k + call assert_equal('45', getline('.')) + norm! j + call assert_equal('46/*{{{*/', getline('.')) + norm! j + call assert_equal('47', getline('.')) + norm! k + norm! zcVzO + call assert_equal('46/*{{{*/', getline('.')) + norm! j + call assert_equal('47', getline('.')) + norm! j + call assert_equal('48', getline('.')) + norm! j + call assert_equal('49', getline('.')) + norm! j + call assert_equal('50/*{{{*/', getline('.')) + norm! j + call assert_equal('51', getline('.')) + " delete folds + :46 + " collapse fold + norm! V14jzC + " delete all folds recursively + norm! VzD + call assert_equal(['46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '60'], getline(46,60)) + + " clean up + setl nofoldenable fdm=marker + bw! +endfu + +func! Test_normal09_operatorfunc() + " Test operatorfunc + call Setup_NewWindow() + " Add some spaces for counting + 50,60s/$/ / + unlet! g:a + let g:a=0 + nmap ,, :set opfunc=CountSpacesg@ + vmap ,, :call CountSpaces(visualmode(), 1) + 50 + norm V2j,, + call assert_equal(6, g:a) + norm V,, + call assert_equal(2, g:a) + norm ,,l + call assert_equal(0, g:a) + 50 + exe "norm 0\10j2l,," + call assert_equal(11, g:a) + 50 + norm V10j,, + call assert_equal(22, g:a) + + " clean up + unmap ,, + set opfunc= + bw! +endfu + +func! Test_normal10_expand() + " Test for expand() + 10new + call setline(1, ['1', 'ifooar,,cbar']) + 2 + norm! $ + let a=expand('') + let b=expand('') + call assert_equal('cbar', a) + call assert_equal('ifooar,,cbar', b) + " clean up + bw! +endfu + +func! Test_normal11_showcmd() + " test for 'showcmd' + 10new + exe "norm! ofoobar\" + call assert_equal(2, line('$')) + set showcmd + exe "norm! ofoobar2\" + call assert_equal(3, line('$')) + exe "norm! VAfoobar3\" + call assert_equal(3, line('$')) + exe "norm! 0d3\2l" + call assert_equal('obar2foobar3', getline('.')) + bw! +endfu + +func! Test_normal12_nv_error() + " Test for nv_error + 10new + call setline(1, range(1,5)) + " should not do anything, just beep + exe "norm! " + call assert_equal(map(range(1,5), 'string(v:val)'), getline(1,'$')) + bw! +endfu + +func! Test_normal13_help() + " Test for F1 + call assert_equal(1, winnr()) + call feedkeys("\", 'txi') + call assert_match('help\.txt', bufname('%')) + call assert_equal(2, winnr('$')) + bw! +endfu + +func! Test_normal14_page() + " basic test for Ctrl-F and Ctrl-B + call Setup_NewWindow() + exe "norm! \" + call assert_equal('9', getline('.')) + exe "norm! 2\" + call assert_equal('25', getline('.')) + exe "norm! 2\" + call assert_equal('18', getline('.')) + 1 + set scrolloff=5 + exe "norm! 2\" + call assert_equal('21', getline('.')) + exe "norm! \" + call assert_equal('13', getline('.')) + 1 + set scrolloff=99 + exe "norm! \" + call assert_equal('13', getline('.')) + set scrolloff=0 + 100 + exe "norm! $\" + call assert_equal('92', getline('.')) + call assert_equal([0, 92, 1, 0, 1], getcurpos()) + 100 + set nostartofline + exe "norm! $\" + call assert_equal('92', getline('.')) + call assert_equal([0, 92, 2, 0, 2147483647], getcurpos()) + " cleanup + set startofline + bw! +endfu + +func! Test_normal15_z_scroll_vert() + " basic test for z commands that scroll the window + call Setup_NewWindow() + 100 + norm! >> + " Test for z + exe "norm! z\" + call assert_equal(' 100', getline('.')) + call assert_equal(100, winsaveview()['topline']) + call assert_equal([0, 100, 2, 0, 9], getcurpos()) + + " Test for zt + 21 + norm! >>0zt + call assert_equal(' 21', getline('.')) + call assert_equal(21, winsaveview()['topline']) + call assert_equal([0, 21, 1, 0, 8], getcurpos()) + + " Test for zb + 30 + norm! >>$ztzb + call assert_equal(' 30', getline('.')) + call assert_equal(30, winsaveview()['topline']+winheight(0)-1) + call assert_equal([0, 30, 3, 0, 2147483647], getcurpos()) + + " Test for z- + 1 + 30 + norm! 0z- + call assert_equal(' 30', getline('.')) + call assert_equal(30, winsaveview()['topline']+winheight(0)-1) + call assert_equal([0, 30, 2, 0, 9], getcurpos()) + + " Test for z{height} + call assert_equal(10, winheight(0)) + exe "norm! z12\" + call assert_equal(12, winheight(0)) + exe "norm! z10\" + call assert_equal(10, winheight(0)) + + " Test for z. + 1 + 21 + norm! 0z. + call assert_equal(' 21', getline('.')) + call assert_equal(17, winsaveview()['topline']) + call assert_equal([0, 21, 2, 0, 9], getcurpos()) + + " Test for zz + 1 + 21 + norm! 0zz + call assert_equal(' 21', getline('.')) + call assert_equal(17, winsaveview()['topline']) + call assert_equal([0, 21, 1, 0, 8], getcurpos()) + + " Test for z+ + 11 + norm! zt + norm! z+ + call assert_equal(' 21', getline('.')) + call assert_equal(21, winsaveview()['topline']) + call assert_equal([0, 21, 2, 0, 9], getcurpos()) + + " Test for [count]z+ + 1 + norm! 21z+ + call assert_equal(' 21', getline('.')) + call assert_equal(21, winsaveview()['topline']) + call assert_equal([0, 21, 2, 0, 9], getcurpos()) + + " Test for z^ + norm! 22z+0 + norm! z^ + call assert_equal(' 21', getline('.')) + call assert_equal(12, winsaveview()['topline']) + call assert_equal([0, 21, 2, 0, 9], getcurpos()) + + " Test for [count]z^ + 1 + norm! 30z^ + call assert_equal(' 21', getline('.')) + call assert_equal(12, winsaveview()['topline']) + call assert_equal([0, 21, 2, 0, 9], getcurpos()) + + " cleanup + bw! +endfu + +func! Test_normal16_z_scroll_hor() + " basic test for z commands that scroll the window + 10new + 15vsp + set nowrap listchars= + let lineA='abcdefghijklmnopqrstuvwxyz' + let lineB='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' + $put =lineA + $put =lineB + 1d + + " Test for zl + 1 + norm! 5zl + call assert_equal(lineA, getline('.')) + call assert_equal(6, col('.')) + call assert_equal(5, winsaveview()['leftcol']) + norm! yl + call assert_equal('f', @0) + + " Test for zh + norm! 2zh + call assert_equal(lineA, getline('.')) + call assert_equal(6, col('.')) + norm! yl + call assert_equal('f', @0) + call assert_equal(3, winsaveview()['leftcol']) + + " Test for zL + norm! zL + call assert_equal(11, col('.')) + norm! yl + call assert_equal('k', @0) + call assert_equal(10, winsaveview()['leftcol']) + norm! 2zL + call assert_equal(25, col('.')) + norm! yl + call assert_equal('y', @0) + call assert_equal(24, winsaveview()['leftcol']) + + " Test for zH + norm! 2zH + call assert_equal(25, col('.')) + call assert_equal(10, winsaveview()['leftcol']) + norm! yl + call assert_equal('y', @0) + + " Test for zs + norm! $zs + call assert_equal(26, col('.')) + call assert_equal(25, winsaveview()['leftcol']) + norm! yl + call assert_equal('z', @0) + + " Test for ze + norm! ze + call assert_equal(26, col('.')) + call assert_equal(11, winsaveview()['leftcol']) + norm! yl + call assert_equal('z', @0) + + " cleanup + set wrap listchars=eol:$ + bw! +endfu + +func! Test_normal17_z_scroll_hor2() + " basic test for z commands that scroll the window + " using 'sidescrolloff' setting + 10new + 20vsp + set nowrap listchars= sidescrolloff=5 + let lineA='abcdefghijklmnopqrstuvwxyz' + let lineB='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' + $put =lineA + $put =lineB + 1d + + " Test for zl + 1 + norm! 5zl + call assert_equal(lineA, getline('.')) + call assert_equal(11, col('.')) + call assert_equal(5, winsaveview()['leftcol']) + norm! yl + call assert_equal('k', @0) + + " Test for zh + norm! 2zh + call assert_equal(lineA, getline('.')) + call assert_equal(11, col('.')) + norm! yl + call assert_equal('k', @0) + call assert_equal(3, winsaveview()['leftcol']) + + " Test for zL + norm! 0zL + call assert_equal(16, col('.')) + norm! yl + call assert_equal('p', @0) + call assert_equal(10, winsaveview()['leftcol']) + norm! 2zL + call assert_equal(26, col('.')) + norm! yl + call assert_equal('z', @0) + call assert_equal(15, winsaveview()['leftcol']) + + " Test for zH + norm! 2zH + call assert_equal(15, col('.')) + call assert_equal(0, winsaveview()['leftcol']) + norm! yl + call assert_equal('o', @0) + + " Test for zs + norm! $zs + call assert_equal(26, col('.')) + call assert_equal(20, winsaveview()['leftcol']) + norm! yl + call assert_equal('z', @0) + + " Test for ze + norm! ze + call assert_equal(26, col('.')) + call assert_equal(11, winsaveview()['leftcol']) + norm! yl + call assert_equal('z', @0) + + " cleanup + set wrap listchars=eol:$ sidescrolloff=0 + bw! +endfu + +func! Test_normal18_z_fold() + " basic tests for foldopen/folddelete + if !has("folding") + return + endif + call Setup_NewWindow() + 50 + setl foldenable fdm=marker foldlevel=5 + + " Test for zF + " First fold + norm! 4zF + " check that folds have been created + call assert_equal(['50/*{{{*/', '51', '52', '53/*}}}*/'], getline(50,53)) + + " Test for zd + 51 + norm! 2zF + call assert_equal(2, foldlevel('.')) + norm! kzd + call assert_equal(['50', '51/*{{{*/', '52/*}}}*/', '53'], getline(50,53)) + norm! j + call assert_equal(1, foldlevel('.')) + + " Test for zD + " also deletes partially selected folds recursively + 51 + norm! zF + call assert_equal(2, foldlevel('.')) + norm! kV2jzD + call assert_equal(['50', '51', '52', '53'], getline(50,53)) + + " Test for zE + 85 + norm! 4zF + 86 + norm! 2zF + 90 + norm! 4zF + call assert_equal(['85/*{{{*/', '86/*{{{*/', '87/*}}}*/', '88/*}}}*/', '89', '90/*{{{*/', '91', '92', '93/*}}}*/'], getline(85,93)) + norm! zE + call assert_equal(['85', '86', '87', '88', '89', '90', '91', '92', '93'], getline(85,93)) + + " Test for zn + 50 + set foldlevel=0 + norm! 2zF + norm! zn + norm! k + call assert_equal('49', getline('.')) + norm! j + call assert_equal('50/*{{{*/', getline('.')) + norm! j + call assert_equal('51/*}}}*/', getline('.')) + norm! j + call assert_equal('52', getline('.')) + call assert_equal(0, &foldenable) + + " Test for zN + 49 + norm! zN + call assert_equal('49', getline('.')) + norm! j + call assert_equal('50/*{{{*/', getline('.')) + norm! j + call assert_equal('52', getline('.')) + call assert_equal(1, &foldenable) + + " Test for zi + norm! zi + call assert_equal(0, &foldenable) + norm! zi + call assert_equal(1, &foldenable) + norm! zi + call assert_equal(0, &foldenable) + norm! zi + call assert_equal(1, &foldenable) + + " Test for za + 50 + norm! za + norm! k + call assert_equal('49', getline('.')) + norm! j + call assert_equal('50/*{{{*/', getline('.')) + norm! j + call assert_equal('51/*}}}*/', getline('.')) + norm! j + call assert_equal('52', getline('.')) + 50 + norm! za + norm! k + call assert_equal('49', getline('.')) + norm! j + call assert_equal('50/*{{{*/', getline('.')) + norm! j + call assert_equal('52', getline('.')) + + 49 + norm! 5zF + norm! k + call assert_equal('48', getline('.')) + norm! j + call assert_equal('49/*{{{*/', getline('.')) + norm! j + call assert_equal('55', getline('.')) + 49 + norm! za + call assert_equal('49/*{{{*/', getline('.')) + norm! j + call assert_equal('50/*{{{*/', getline('.')) + norm! j + call assert_equal('52', getline('.')) + set nofoldenable + " close fold and set foldenable + norm! za + call assert_equal(1, &foldenable) + + 50 + " have to use {count}za to open all folds and make the cursor visible + norm! 2za + norm! 2k + call assert_equal('48', getline('.')) + norm! j + call assert_equal('49/*{{{*/', getline('.')) + norm! j + call assert_equal('50/*{{{*/', getline('.')) + norm! j + call assert_equal('51/*}}}*/', getline('.')) + norm! j + call assert_equal('52', getline('.')) + + " Test for zA + 49 + set foldlevel=0 + 50 + norm! zA + norm! 2k + call assert_equal('48', getline('.')) + norm! j + call assert_equal('49/*{{{*/', getline('.')) + norm! j + call assert_equal('50/*{{{*/', getline('.')) + norm! j + call assert_equal('51/*}}}*/', getline('.')) + norm! j + call assert_equal('52', getline('.')) + + " zA on a opened fold when foldenale is not set + 50 + set nofoldenable + norm! zA + call assert_equal(1, &foldenable) + norm! k + call assert_equal('48', getline('.')) + norm! j + call assert_equal('49/*{{{*/', getline('.')) + norm! j + call assert_equal('55', getline('.')) + + " Test for zc + norm! zE + 50 + norm! 2zF + 49 + norm! 5zF + set nofoldenable + 50 + " There most likely is a bug somewhere: + " https://groups.google.com/d/msg/vim_dev/v2EkfJ_KQjI/u-Cvv94uCAAJ + " TODO: Should this only close the inner most fold or both folds? + norm! zc + call assert_equal(1, &foldenable) + norm! k + call assert_equal('48', getline('.')) + norm! j + call assert_equal('49/*{{{*/', getline('.')) + norm! j + call assert_equal('55', getline('.')) + set nofoldenable + 50 + norm! Vjzc + norm! k + call assert_equal('48', getline('.')) + norm! j + call assert_equal('49/*{{{*/', getline('.')) + norm! j + call assert_equal('55', getline('.')) + + " Test for zC + set nofoldenable + 50 + norm! zCk + call assert_equal('48', getline('.')) + norm! j + call assert_equal('49/*{{{*/', getline('.')) + norm! j + call assert_equal('55', getline('.')) + + " Test for zx + " 1) close folds at line 49-54 + set nofoldenable + 48 + norm! zx + call assert_equal(1, &foldenable) + norm! j + call assert_equal('49/*{{{*/', getline('.')) + norm! j + call assert_equal('55', getline('.')) + + " 2) do not close fold under curser + 51 + set nofoldenable + norm! zx + call assert_equal(1, &foldenable) + norm! 3k + call assert_equal('48', getline('.')) + norm! j + call assert_equal('49/*{{{*/', getline('.')) + norm! j + call assert_equal('50/*{{{*/', getline('.')) + norm! j + call assert_equal('51/*}}}*/', getline('.')) + norm! j + call assert_equal('52', getline('.')) + norm! j + call assert_equal('53', getline('.')) + norm! j + call assert_equal('54/*}}}*/', getline('.')) + norm! j + call assert_equal('55', getline('.')) + + " 3) close one level of folds + 48 + set nofoldenable + set foldlevel=1 + norm! zx + call assert_equal(1, &foldenable) + call assert_equal('48', getline('.')) + norm! j + call assert_equal('49/*{{{*/', getline('.')) + norm! j + call assert_equal('50/*{{{*/', getline('.')) + norm! j + call assert_equal('52', getline('.')) + norm! j + call assert_equal('53', getline('.')) + norm! j + call assert_equal('54/*}}}*/', getline('.')) + norm! j + call assert_equal('55', getline('.')) + + " Test for zX + " Close all folds + set foldlevel=0 nofoldenable + 50 + norm! zX + call assert_equal(1, &foldenable) + norm! k + call assert_equal('48', getline('.')) + norm! j + call assert_equal('49/*{{{*/', getline('.')) + norm! j + call assert_equal('55', getline('.')) + + " Test for zm + 50 + set nofoldenable foldlevel=2 + norm! zm + call assert_equal(1, &foldenable) + call assert_equal(1, &foldlevel) + norm! zm + call assert_equal(0, &foldlevel) + norm! zm + call assert_equal(0, &foldlevel) + norm! k + call assert_equal('48', getline('.')) + norm! j + call assert_equal('49/*{{{*/', getline('.')) + norm! j + call assert_equal('55', getline('.')) + + " Test for zM + 48 + set nofoldenable foldlevel=99 + norm! zM + call assert_equal(1, &foldenable) + call assert_equal(0, &foldlevel) + call assert_equal('48', getline('.')) + norm! j + call assert_equal('49/*{{{*/', getline('.')) + norm! j + call assert_equal('55', getline('.')) + + " Test for zr + 48 + set nofoldenable foldlevel=0 + norm! zr + call assert_equal(0, &foldenable) + call assert_equal(1, &foldlevel) + set foldlevel=0 foldenable + norm! zr + call assert_equal(1, &foldenable) + call assert_equal(1, &foldlevel) + norm! zr + call assert_equal(2, &foldlevel) + call assert_equal('48', getline('.')) + norm! j + call assert_equal('49/*{{{*/', getline('.')) + norm! j + call assert_equal('50/*{{{*/', getline('.')) + norm! j + call assert_equal('51/*}}}*/', getline('.')) + norm! j + call assert_equal('52', getline('.')) + + " Test for zR + 48 + set nofoldenable foldlevel=0 + norm! zR + call assert_equal(0, &foldenable) + call assert_equal(2, &foldlevel) + set foldenable foldlevel=0 + norm! zR + call assert_equal(1, &foldenable) + call assert_equal(2, &foldlevel) + call assert_equal('48', getline('.')) + norm! j + call assert_equal('49/*{{{*/', getline('.')) + norm! j + call assert_equal('50/*{{{*/', getline('.')) + norm! j + call assert_equal('51/*}}}*/', getline('.')) + norm! j + call assert_equal('52', getline('.')) + call append(50, ['a /*{{{*/', 'b /*}}}*/']) + 48 + call assert_equal('48', getline('.')) + norm! j + call assert_equal('49/*{{{*/', getline('.')) + norm! j + call assert_equal('50/*{{{*/', getline('.')) + norm! j + call assert_equal('a /*{{{*/', getline('.')) + norm! j + call assert_equal('51/*}}}*/', getline('.')) + norm! j + call assert_equal('52', getline('.')) + 48 + norm! zR + call assert_equal(1, &foldenable) + call assert_equal(3, &foldlevel) + call assert_equal('48', getline('.')) + norm! j + call assert_equal('49/*{{{*/', getline('.')) + norm! j + call assert_equal('50/*{{{*/', getline('.')) + norm! j + call assert_equal('a /*{{{*/', getline('.')) + norm! j + call assert_equal('b /*}}}*/', getline('.')) + norm! j + call assert_equal('51/*}}}*/', getline('.')) + norm! j + call assert_equal('52', getline('.')) + + " clean up + setl nofoldenable fdm=marker foldlevel=0 + bw! +endfu + +func! Test_normal19_z_spell() + if !has("spell") || !has('syntax') + return + endif + new + call append(0, ['1 good', '2 goood', '3 goood']) + set spell spellfile=./Xspellfile.add spelllang=en + let oldlang=v:lang + lang C + + " Test for zg + 1 + norm! ]s + call assert_equal('2 goood', getline('.')) + norm! zg + 1 + let a=execute('unsilent :norm! ]s') + call assert_equal('1 good', getline('.')) + call assert_equal('search hit BOTTOM, continuing at TOP', a[1:]) + let cnt=readfile('./Xspellfile.add') + call assert_equal('goood', cnt[0]) + + " Test for zw + 2 + norm! $zw + 1 + norm! ]s + call assert_equal('2 goood', getline('.')) + let cnt=readfile('./Xspellfile.add') + call assert_equal('#oood', cnt[0]) + call assert_equal('goood/!', cnt[1]) + + " Test for zg in visual mode + let a=execute('unsilent :norm! V$zg') + call assert_equal("Word '2 goood' added to ./Xspellfile.add", a[1:]) + 1 + norm! ]s + call assert_equal('3 goood', getline('.')) + let cnt=readfile('./Xspellfile.add') + call assert_equal('2 goood', cnt[2]) + " Remove "2 good" from spellfile + 2 + let a=execute('unsilent norm! V$zw') + call assert_equal("Word '2 goood' added to ./Xspellfile.add", a[1:]) + let cnt=readfile('./Xspellfile.add') + call assert_equal('2 goood/!', cnt[3]) + + " Test for zG + let a=execute('unsilent norm! V$zG') + call assert_match("Word '2 goood' added to .*", a) + let fname=matchstr(a, 'to\s\+\zs\f\+$') + let cnt=readfile(fname) + call assert_equal('2 goood', cnt[0]) + + " Test for zW + let a=execute('unsilent norm! V$zW') + call assert_match("Word '2 goood' added to .*", a) + let cnt=readfile(fname) + call assert_equal('# goood', cnt[0]) + call assert_equal('2 goood/!', cnt[1]) + + " Test for zuW + let a=execute('unsilent norm! V$zuW') + call assert_match("Word '2 goood' removed from .*", a) + let cnt=readfile(fname) + call assert_equal('# goood', cnt[0]) + call assert_equal('# goood/!', cnt[1]) + + " Test for zuG + let a=execute('unsilent norm! $zG') + call assert_match("Word 'goood' added to .*", a) + let cnt=readfile(fname) + call assert_equal('# goood', cnt[0]) + call assert_equal('# goood/!', cnt[1]) + call assert_equal('goood', cnt[2]) + let a=execute('unsilent norm! $zuG') + let cnt=readfile(fname) + call assert_match("Word 'goood' removed from .*", a) + call assert_equal('# goood', cnt[0]) + call assert_equal('# goood/!', cnt[1]) + call assert_equal('#oood', cnt[2]) + " word not found in wordlist + let a=execute('unsilent norm! V$zuG') + let cnt=readfile(fname) + call assert_match("", a) + call assert_equal('# goood', cnt[0]) + call assert_equal('# goood/!', cnt[1]) + call assert_equal('#oood', cnt[2]) + + " Test for zug + call delete('./Xspellfile.add') + 2 + let a=execute('unsilent norm! $zg') + let cnt=readfile('./Xspellfile.add') + call assert_equal('goood', cnt[0]) + let a=execute('unsilent norm! $zug') + call assert_match("Word 'goood' removed from \./Xspellfile.add", a) + let cnt=readfile('./Xspellfile.add') + call assert_equal('#oood', cnt[0]) + " word not in wordlist + let a=execute('unsilent norm! V$zug') + call assert_match('', a) + let cnt=readfile('./Xspellfile.add') + call assert_equal('#oood', cnt[0]) + + " Test for zuw + call delete('./Xspellfile.add') + 2 + let a=execute('unsilent norm! Vzw') + let cnt=readfile('./Xspellfile.add') + call assert_equal('2 goood/!', cnt[0]) + let a=execute('unsilent norm! Vzuw') + call assert_match("Word '2 goood' removed from \./Xspellfile.add", a) + let cnt=readfile('./Xspellfile.add') + call assert_equal('# goood/!', cnt[0]) + " word not in wordlist + let a=execute('unsilent norm! $zug') + call assert_match('', a) + let cnt=readfile('./Xspellfile.add') + call assert_equal('# goood/!', cnt[0]) + + " add second entry to spellfile setting + set spellfile=./Xspellfile.add,./Xspellfile2.add + call delete('./Xspellfile.add') + 2 + let a=execute('unsilent norm! $2zg') + let cnt=readfile('./Xspellfile2.add') + call assert_match("Word 'goood' added to ./Xspellfile2.add", a) + call assert_equal('goood', cnt[0]) + + " clean up + exe "lang" oldlang + call delete("./Xspellfile.add") + call delete("./Xspellfile2.add") + + " zux -> no-op + 2 + norm! $zux + call assert_equal([], glob('Xspellfile.add',0,1)) + call assert_equal([], glob('Xspellfile2.add',0,1)) + + set spellfile= + bw! +endfu + +func! Test_normal20_exmode() + if !(has("win32") || has("win64")) + return + endif + call writefile(['1a', 'foo', 'bar', '.', 'w! Xfile2', 'q!'], 'Xscript') + call writefile(['1', '2'], 'Xfile') + call system(v:progpath .' -e -s < Xscript Xfile') + let a=readfile('Xfile2') + call assert_equal(['1', 'foo', 'bar', '2'], a) + + " clean up + for file in ['Xfile', 'Xfile2', 'Xscript'] + call delete(file) + endfor + bw! +endfu + +func! Test_normal21_nv_hat() + set hidden + e Xfoobar + e Xfile2 + call feedkeys("\", 't') + call assert_equal("Xfile2", fnamemodify(bufname('%'), ':t')) + call feedkeys("f\", 't') + call assert_equal("Xfile2", fnamemodify(bufname('%'), ':t')) + " clean up + set nohidden + bw! +endfu + +func! Test_normal22_zet() + " Test for ZZ + let shell = &shell + let &shell = 'sh' + call writefile(['1', '2'], 'Xfile') + let args = ' -u NONE -N -U NONE -i NONE --noplugins -X --not-a-term' + call system(v:progpath . args . ' -c "%d" -c ":norm! ZZ" Xfile') + let a = readfile('Xfile') + call assert_equal([], a) + " Test for ZQ + call writefile(['1', '2'], 'Xfile') + call system(v:progpath . args . ' -c "%d" -c ":norm! ZQ" Xfile') + let a = readfile('Xfile') + call assert_equal(['1', '2'], a) + + " clean up + for file in ['Xfile'] + call delete(file) + endfor + let &shell = shell +endfu + +func! Test_normal23_K() + " Test for K command + new + call append(0, ['version8.txt', 'man']) + let k = &keywordprg + set keywordprg=:help + 1 + norm! VK + call assert_equal('version8.txt', fnamemodify(bufname('%'), ':t')) + call assert_equal('help', &ft) + call assert_match('\*version8.txt\*', getline('.')) + helpclose + norm! 0K + call assert_equal('version8.txt', fnamemodify(bufname('%'), ':t')) + call assert_equal('help', &ft) + call assert_match('\*version8\.0\*', getline('.')) + helpclose + + if !(has("win32") || has("win64")) + let &keywordprg = k + bw! + return + endif + set keywordprg=man\ --pager=cat + " Test for using man + 2 + let a = execute('unsilent norm! K') + call assert_match("man --pager=cat 'man'", a) + + " clean up + let &keywordprg = k + bw! +endfu + +func! Test_normal24_rot13() + " This test uses multi byte characters + if !has("multi_byte") + return + endif + " Testing for g?? g?g? + new + call append(0, 'abcdefghijklmnopqrstuvwxyzäüö') + 1 + norm! g?? + call assert_equal('nopqrstuvwxyzabcdefghijklmäüö', getline('.')) + norm! g?g? + call assert_equal('abcdefghijklmnopqrstuvwxyzäüö', getline('.')) + + " clean up + bw! +endfu + +func! Test_normal25_tag() + " Testing for CTRL-] g CTRL-] g] + " CTRL-W g] CTRL-W CTRL-] CTRL-W g CTRL-] + h + " Test for CTRL-] + call search('\$') + exe "norm! \" + call assert_equal("change.txt", fnamemodify(bufname('%'), ':t')) + norm! yiW + call assert_equal("*x*", @0) + exe ":norm \" + + " Test for g_CTRL-] + call search('\$') + exe "norm! g\" + call assert_equal("change.txt", fnamemodify(bufname('%'), ':t')) + norm! yiW + call assert_equal("*v_u*", @0) + exe ":norm \" + + " Test for g] + call search('\$') + let a = execute(":norm! g]") + call assert_match('i_.*insert.txt', a) + + if !empty(exepath('cscope')) && has('cscope') + " setting cscopetag changes how g] works + set cst + exe "norm! g]" + call assert_equal("insert.txt", fnamemodify(bufname('%'), ':t')) + norm! yiW + call assert_equal("*i_*", @0) + exe ":norm \" + " Test for CTRL-W g] + exe "norm! \g]" + call assert_equal("insert.txt", fnamemodify(bufname('%'), ':t')) + norm! yiW + call assert_equal("*i_*", @0) + call assert_equal(3, winnr('$')) + helpclose + set nocst + endif + + " Test for CTRL-W g] + let a = execute("norm! \g]") + call assert_match('i_.*insert.txt', a) + + " Test for CTRL-W CTRL-] + exe "norm! \\" + call assert_equal("insert.txt", fnamemodify(bufname('%'), ':t')) + norm! yiW + call assert_equal("*i_*", @0) + call assert_equal(3, winnr('$')) + helpclose + + " Test for CTRL-W g CTRL-] + exe "norm! \g\" + call assert_equal("insert.txt", fnamemodify(bufname('%'), ':t')) + norm! yiW + call assert_equal("*i_*", @0) + call assert_equal(3, winnr('$')) + helpclose + + " clean up + helpclose +endfu + +func! Test_normal26_put() + " Test for ]p ]P [p and [P + new + call append(0, ['while read LINE', 'do', ' ((count++))', ' if [ $? -ne 0 ]; then', " echo 'Error writing file'", ' fi', 'done']) + 1 + /Error/y a + 2 + norm! "a]pj"a[p + call assert_equal(['do', "echo 'Error writing file'", " echo 'Error writing file'", ' ((count++))'], getline(2,5)) + 1 + /^\s\{4}/ + exe "norm! \"a]P3Eldt'" + exe "norm! j\"a[P2Eldt'" + call assert_equal([' if [ $? -ne 0 ]; then', " echo 'Error writing'", " echo 'Error'", " echo 'Error writing file'", ' fi'], getline(6,10)) + + " clean up + bw! +endfu + +func! Test_normal27_bracket() + " Test for [' [` ]' ]` + call Setup_NewWindow() + 1,21s/.\+/ & b/ + 1 + norm! $ma + 5 + norm! $mb + 10 + norm! $mc + 15 + norm! $md + 20 + norm! $me + + " Test for [' + 9 + norm! 2[' + call assert_equal(' 1 b', getline('.')) + call assert_equal(1, line('.')) + call assert_equal(3, col('.')) + + " Test for ]' + norm! ]' + call assert_equal(' 5 b', getline('.')) + call assert_equal(5, line('.')) + call assert_equal(3, col('.')) + + " No mark after line 21, cursor moves to first non blank on current line + 21 + norm! $]' + call assert_equal(' 21 b', getline('.')) + call assert_equal(21, line('.')) + call assert_equal(3, col('.')) + + " Test for [` + norm! 2[` + call assert_equal(' 15 b', getline('.')) + call assert_equal(15, line('.')) + call assert_equal(8, col('.')) + + " Test for ]` + norm! ]` + call assert_equal(' 20 b', getline('.')) + call assert_equal(20, line('.')) + call assert_equal(8, col('.')) + + " clean up + bw! +endfu + +func! Test_normal28_parenthesis() + " basic testing for ( and ) + new + call append(0, ['This is a test. With some sentences!', '', 'Even with a question? And one more. And no sentence here']) + + $ + norm! d( + call assert_equal(['This is a test. With some sentences!', '', 'Even with a question? And one more. ', ''], getline(1, '$')) + norm! 2d( + call assert_equal(['This is a test. With some sentences!', '', ' ', ''], getline(1, '$')) + 1 + norm! 0d) + call assert_equal(['With some sentences!', '', ' ', ''], getline(1, '$')) + + call append('$', ['This is a long sentence', '', 'spanning', 'over several lines. ']) + $ + norm! $d( + call assert_equal(['With some sentences!', '', ' ', '', 'This is a long sentence', ''], getline(1, '$')) + + " clean up + bw! +endfu + +fun! Test_normal29_brace() + " basic test for { and } movements + let text= ['A paragraph begins after each empty line, and also at each of a set of', + \ 'paragraph macros, specified by the pairs of characters in the ''paragraphs''', + \ 'option. The default is "IPLPPPQPP TPHPLIPpLpItpplpipbp", which corresponds to', + \ 'the macros ".IP", ".LP", etc. (These are nroff macros, so the dot must be in', + \ 'the first column). A section boundary is also a paragraph boundary.', + \ 'Note that a blank line (only containing white space) is NOT a paragraph', + \ 'boundary.', + \ '', + \ '', + \ 'Also note that this does not include a ''{'' or ''}'' in the first column. When', + \ 'the ''{'' flag is in ''cpoptions'' then ''{'' in the first column is used as a', + \ 'paragraph boundary |posix|.', + \ '{', + \ 'This is no paragaraph', + \ 'unless the ''{'' is set', + \ 'in ''cpoptions''', + \ '}', + \ '.IP', + \ 'The nroff macros IP seperates a paragraph', + \ 'That means, it must be a ''.''', + \ 'followed by IP', + \ '.LPIt does not matter, if afterwards some', + \ 'more characters follow.', + \ '.SHAlso section boundaries from the nroff', + \ 'macros terminate a paragraph. That means', + \ 'a character like this:', + \ '.NH', + \ 'End of text here'] + new + call append(0, text) + 1 + norm! 0d2} + call assert_equal(['.IP', + \ 'The nroff macros IP seperates a paragraph', 'That means, it must be a ''.''', 'followed by IP', + \ '.LPIt does not matter, if afterwards some', 'more characters follow.', '.SHAlso section boundaries from the nroff', + \ 'macros terminate a paragraph. That means', 'a character like this:', '.NH', 'End of text here', ''], getline(1,'$')) + norm! 0d} + call assert_equal(['.LPIt does not matter, if afterwards some', 'more characters follow.', + \ '.SHAlso section boundaries from the nroff', 'macros terminate a paragraph. That means', + \ 'a character like this:', '.NH', 'End of text here', ''], getline(1, '$')) + $ + norm! d{ + call assert_equal(['.LPIt does not matter, if afterwards some', 'more characters follow.', + \ '.SHAlso section boundaries from the nroff', 'macros terminate a paragraph. That means', 'a character like this:', ''], getline(1, '$')) + norm! d{ + call assert_equal(['.LPIt does not matter, if afterwards some', 'more characters follow.', ''], getline(1,'$')) + " Test with { in cpooptions + %d + call append(0, text) + set cpo+={ + 1 + norm! 0d2} + call assert_equal(['{', 'This is no paragaraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}', + \ '.IP', 'The nroff macros IP seperates a paragraph', 'That means, it must be a ''.''', + \ 'followed by IP', '.LPIt does not matter, if afterwards some', 'more characters follow.', + \ '.SHAlso section boundaries from the nroff', 'macros terminate a paragraph. That means', + \ 'a character like this:', '.NH', 'End of text here', ''], getline(1,'$')) + $ + norm! d} + call assert_equal(['{', 'This is no paragaraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}', + \ '.IP', 'The nroff macros IP seperates a paragraph', 'That means, it must be a ''.''', + \ 'followed by IP', '.LPIt does not matter, if afterwards some', 'more characters follow.', + \ '.SHAlso section boundaries from the nroff', 'macros terminate a paragraph. That means', + \ 'a character like this:', '.NH', 'End of text here', ''], getline(1,'$')) + norm! gg} + norm! d5} + call assert_equal(['{', 'This is no paragaraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}', ''], getline(1,'$')) + + " clean up + set cpo-={ + bw! +endfu + +fun! Test_normal30_changecase() + " This test uses multi byte characters + if !has("multi_byte") + return + endif + new + call append(0, 'This is a simple test: äüöß') + norm! 1ggVu + call assert_equal('this is a simple test: äüöß', getline('.')) + norm! VU + call assert_equal('THIS IS A SIMPLE TEST: ÄÜÖSS', getline('.')) + norm! guu + call assert_equal('this is a simple test: äüöss', getline('.')) + norm! gUgU + call assert_equal('THIS IS A SIMPLE TEST: ÄÜÖSS', getline('.')) + norm! gugu + call assert_equal('this is a simple test: äüöss', getline('.')) + norm! gUU + call assert_equal('THIS IS A SIMPLE TEST: ÄÜÖSS', getline('.')) + norm! 010~ + call assert_equal('this is a SIMPLE TEST: ÄÜÖSS', getline('.')) + norm! V~ + call assert_equal('THIS IS A simple test: äüöss', getline('.')) + + " clean up + bw! +endfu + +fun! Test_normal31_r_cmd() + " Test for r command + new + call append(0, 'This is a simple test: abcd') + exe "norm! 1gg$r\" + call assert_equal(['This is a simple test: abc', '', ''], getline(1,'$')) + exe "norm! 1gg2wlr\" + call assert_equal(['This is a', 'simple test: abc', '', ''], getline(1,'$')) + exe "norm! 2gg0W5r\" + call assert_equal(['This is a', 'simple ', ' abc', '', ''], getline('1', '$')) + set autoindent + call setline(2, ['simple test: abc', '']) + exe "norm! 2gg0W5r\" + call assert_equal(['This is a', 'simple ', 'abc', '', '', ''], getline('1', '$')) + exe "norm! 1ggVr\" + call assert_equal('^M^M^M^M^M^M^M^M^M', strtrans(getline(1))) + call setline(1, 'This is a') + exe "norm! 1gg05rf" + call assert_equal('fffffis a', getline(1)) + + " clean up + set noautoindent + bw! +endfu + +func! Test_normal32_g_cmd1() + " Test for g*, g# + new + call append(0, ['abc.x_foo', 'x_foobar.abc']) + 1 + norm! $g* + call assert_equal('x_foo', @/) + call assert_equal('x_foobar.abc', getline('.')) + norm! $g# + call assert_equal('abc', @/) + call assert_equal('abc.x_foo', getline('.')) + + " clean up + bw! +endfu + +fun! Test_normal33_g_cmd2() + if !has("jumplist") + return + endif + " Tests for g cmds + call Setup_NewWindow() + " Test for g` + clearjumps + norm! ma10j + let a=execute(':jumps') + " empty jumplist + call assert_equal('>', a[-1:]) + norm! g`a + call assert_equal('>', a[-1:]) + call assert_equal(1, line('.')) + call assert_equal('1', getline('.')) + + " Test for g; and g, + norm! g; + " there is only one change in the changelist + " currently, when we setup the window + call assert_equal(2, line('.')) + call assert_fails(':norm! g;', 'E662') + call assert_fails(':norm! g,', 'E663') + let &ul=&ul + call append('$', ['a', 'b', 'c', 'd']) + let &ul=&ul + call append('$', ['Z', 'Y', 'X', 'W']) + let a = execute(':changes') + call assert_match('2\s\+0\s\+2', a) + call assert_match('101\s\+0\s\+a', a) + call assert_match('105\s\+0\s\+Z', a) + norm! 3g; + call assert_equal(2, line('.')) + norm! 2g, + call assert_equal(105, line('.')) + + " Test for g& - global substitute + %d + call setline(1, range(1,10)) + call append('$', ['a', 'b', 'c', 'd']) + $s/\w/&&/g + exe "norm! /[1-8]\" + norm! g& + call assert_equal(['11', '22', '33', '44', '55', '66', '77', '88', '9', '110', 'a', 'b', 'c', 'dd'], getline(1, '$')) + + " Test for gv + %d + call append('$', repeat(['abcdefgh'], 8)) + exe "norm! 2gg02l\2j2ly" + call assert_equal(['cde', 'cde', 'cde'], getreg(0, 1, 1)) + " in visual mode, gv swaps current and last selected region + exe "norm! G0\4k4lgvd" + call assert_equal(['', 'abfgh', 'abfgh', 'abfgh', 'abcdefgh', 'abcdefgh', 'abcdefgh', 'abcdefgh', 'abcdefgh'], getline(1,'$')) + exe "norm! G0\4k4ly" + exe "norm! gvood" + call assert_equal(['', 'abfgh', 'abfgh', 'abfgh', 'fgh', 'fgh', 'fgh', 'fgh', 'fgh'], getline(1,'$')) + + " Test for gk/gj + %d + 15vsp + set wrap listchars= sbr= + let lineA='abcdefghijklmnopqrstuvwxyz' + let lineB='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' + $put =lineA + $put =lineB + + norm! 3gg0dgk + call assert_equal(['', 'abcdefghijklmno', '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'], getline(1, '$')) + set nu + norm! 3gg0gjdgj + call assert_equal(['', 'abcdefghijklmno', '0123456789AMNOPQRSTUVWXYZ'], getline(1,'$')) + + " Test for gJ + norm! 2gggJ + call assert_equal(['', 'abcdefghijklmno0123456789AMNOPQRSTUVWXYZ'], getline(1,'$')) + call assert_equal(16, col('.')) + " shouldn't do anything + norm! 10gJ + call assert_equal(1, col('.')) + + " Test for g0 g^ gm g$ + exe "norm! 2gg0gji " + call assert_equal(['', 'abcdefghijk lmno0123456789AMNOPQRSTUVWXYZ'], getline(1,'$')) + norm! g0yl + call assert_equal(12, col('.')) + call assert_equal(' ', getreg(0)) + norm! g$yl + call assert_equal(22, col('.')) + call assert_equal('3', getreg(0)) + norm! gmyl + call assert_equal(17, col('.')) + call assert_equal('n', getreg(0)) + norm! g^yl + call assert_equal(15, col('.')) + call assert_equal('l', getreg(0)) + + " Test for g Ctrl-G + let a=execute(":norm! g\") + call assert_match('Col 15 of 43; Line 2 of 2; Word 2 of 2; Byte 16 of 45', a) + + " Test for gI + norm! gIfoo + call assert_equal(['', 'fooabcdefghijk lmno0123456789AMNOPQRSTUVWXYZ'], getline(1,'$')) + + " Test for gi + wincmd c + %d + set tw=0 + call setline(1, ['foobar', 'new line']) + norm! A next word + $put ='third line' + norm! gi another word + call assert_equal(['foobar next word another word', 'new line', 'third line'], getline(1,'$')) + + " clean up + bw! +endfu + +fun! Test_normal34_g_cmd3() + if !has("multi_byte") + return + endif + " Test for g8 + new + call append(0, 'abcdefghijklmnopqrstuvwxyzäüö') + let a=execute(':norm! 1gg$g8') + call assert_equal('c3 b6 ', a[1:]) + + " Test for gp gP + call append(1, range(1,10)) + " clean up + bw! +endfu + +fun! Test_normal35_g_cmd4() + " Test for g< + " Cannot capture its output, + " probably a bug, therefore, test disabled: + return + echo "a\nb\nc\nd" + let b=execute(':norm! g<') + call assert_true(!empty(b), 'failed `execute(g<)`') +endfu + +fun! Test_normal36_g_cmd5() + new + call append(0, 'abcdefghijklmnopqrstuvwxyz') + " Test for gp gP + call append(1, range(1,10)) + 1 + norm! 1yy + 3 + norm! gp + call assert_equal([0, 5, 1, 0, 1], getcurpos()) + $ + norm! gP + call assert_equal([0, 14, 1, 0, 1], getcurpos()) + + " Test for go + norm! 26go + call assert_equal([0, 1, 26, 0, 26], getcurpos()) + norm! 27go + call assert_equal([0, 1, 26, 0, 26], getcurpos()) + norm! 28go + call assert_equal([0, 2, 1, 0, 1], getcurpos()) + set ff=dos + norm! 29go + call assert_equal([0, 2, 1, 0, 1], getcurpos()) + set ff=unix + norm! gg0 + norm! 101go + call assert_equal([0, 13, 26, 0, 26], getcurpos()) + norm! 103go + call assert_equal([0, 14, 1, 0, 1], getcurpos()) + " count > buffer content + norm! 120go + call assert_equal([0, 14, 1, 0, 2147483647], getcurpos()) + " clean up + bw! +endfu + +fun! Test_normal37_g_cmd6() + " basic test for gt and gT + tabnew 1.txt + tabnew 2.txt + tabnew 3.txt + norm! 1gt + call assert_equal(1, tabpagenr()) + norm! 3gt + call assert_equal(3, tabpagenr()) + norm! 1gT + " count gT goes not to the absolute tabpagenumber + " but, but goes to the count previous tabpagenumber + call assert_equal(2, tabpagenr()) + " wrap around + norm! 3gT + call assert_equal(3, tabpagenr()) + " gt does not wrap around + norm! 5gt + call assert_equal(3, tabpagenr()) + + for i in range(3) + tabclose + endfor + " clean up + call assert_fails(':tabclose', 'E784') +endfu + +fun! Test_normal38_nvhome() + " Test for and key + new + call setline(1, range(10)) + $ + setl et sw=2 + norm! V10>$ + " count is ignored + exe "norm! 10\" + call assert_equal(1, col('.')) + exe "norm! \" + call assert_equal([0, 10, 1, 0, 1], getcurpos()) + exe "norm! 5\" + call assert_equal([0, 5, 1, 0, 1], getcurpos()) + exe "norm! \" + call assert_equal([0, 1, 1, 0, 1], getcurpos()) + + " clean up + bw! +endfu + +fun! Test_normal39_cw() + " Test for cw and cW on whitespace + " and cpo+=w setting + new + set tw=0 + call append(0, 'here are some words') + norm! 1gg0elcwZZZ + call assert_equal('hereZZZare some words', getline('.')) + norm! 1gg0elcWYYY + call assert_equal('hereZZZareYYYsome words', getline('.')) + set cpo+=w + call setline(1, 'here are some words') + norm! 1gg0elcwZZZ + call assert_equal('hereZZZ are some words', getline('.')) + norm! 1gg2elcWYYY + call assert_equal('hereZZZ areYYY some words', getline('.')) + set cpo-=w + norm! 2gg0cwfoo + call assert_equal('foo', getline('.')) + + " clean up + bw! +endfu + +fun! Test_normal40_ctrl_bsl() + " Basic test for CTRL-\ commands + new + call append(0, 'here are some words') + exe "norm! 1gg0a\\" + call assert_equal('n', mode()) + call assert_equal(1, col('.')) + call assert_equal('', visualmode()) + exe "norm! 1gg0viw\\" + call assert_equal('n', mode()) + call assert_equal(4, col('.')) + exe "norm! 1gg0a\\" + call assert_equal('n', mode()) + call assert_equal(1, col('.')) + "imap , + set im + exe ":norm! \\dw" + set noim + call assert_equal('are some words', getline(1)) + call assert_false(&insertmode) + + " clean up + bw! +endfu + +fun! Test_normal41_insert_reg() + " Test for =, = and = + " in insert mode + new + set sts=2 sw=2 ts=8 tw=0 + call append(0, ["aaa\tbbb\tccc", '', '', '']) + let a=getline(1) + norm! 2gg0 + exe "norm! a\=a\" + norm! 3gg0 + exe "norm! a\\=a\" + norm! 4gg0 + exe "norm! a\\=a\" + call assert_equal(['aaa bbb ccc', 'aaa bbb ccc', 'aaa bbb ccc', 'aaa bbb ccc', ''], getline(1, '$')) + + " clean up + set sts=0 sw=8 ts=8 + "bw! +endfu + +func! Test_normal42_halfpage() + " basic test for Ctrl-D and Ctrl-U + call Setup_NewWindow() + call assert_equal(5, &scroll) + exe "norm! \" + call assert_equal('6', getline('.')) + exe "norm! 2\" + call assert_equal('8', getline('.')) + call assert_equal(2, &scroll) + set scroll=5 + exe "norm! \" + call assert_equal('3', getline('.')) + 1 + set scrolloff=5 + exe "norm! \" + call assert_equal('10', getline('.')) + exe "norm! \" + call assert_equal('5', getline('.')) + 1 + set scrolloff=99 + exe "norm! \" + call assert_equal('10', getline('.')) + set scrolloff=0 + 100 + exe "norm! $\" + call assert_equal('95', getline('.')) + call assert_equal([0, 95, 1, 0, 1], getcurpos()) + 100 + set nostartofline + exe "norm! $\" + call assert_equal('95', getline('.')) + call assert_equal([0, 95, 2, 0, 2147483647], getcurpos()) + " cleanup + set startofline + bw! +endfu + +fun! Test_normal43_textobject1() + " basic tests for text object aw + new + call append(0, ['foobar,eins,foobar', 'foo,zwei,foo ']) + " diw + norm! 1gg0diw + call assert_equal([',eins,foobar', 'foo,zwei,foo ', ''], getline(1,'$')) + " daw + norm! 2ggEdaw + call assert_equal([',eins,foobar', 'foo,zwei,', ''], getline(1, '$')) + %d + call append(0, ["foo\teins\tfoobar", "foo\tzwei\tfoo "]) + " diW + norm! 2ggwd2iW + call assert_equal(['foo eins foobar', 'foo foo ', ''], getline(1,'$')) + " daW + norm! 1ggd2aW + call assert_equal(['foobar', 'foo foo ', ''], getline(1,'$')) + + %d + call append(0, ["foo\teins\tfoobar", "foo\tzwei\tfoo "]) + " aw in visual line mode switches to characterwise mode + norm! 2gg$Vawd + call assert_equal(['foo eins foobar', 'foo zwei foo'], getline(1,'$')) + norm! 1gg$Viwd + call assert_equal(['foo eins ', 'foo zwei foo'], getline(1,'$')) + + " clean up + bw! +endfu + +func! Test_normal44_textobjects2() + " basic testing for is and as text objects + new + call append(0, ['This is a test. With some sentences!', '', 'Even with a question? And one more. And no sentence here']) + " Test for dis - does not remove trailing whitespace + norm! 1gg0dis + call assert_equal([' With some sentences!', '', 'Even with a question? And one more. And no sentence here', ''], getline(1,'$')) + " Test for das - removes leading whitespace + norm! 3ggf?ldas + call assert_equal([' With some sentences!', '', 'Even with a question? And no sentence here', ''], getline(1,'$')) + " when used in visual mode, is made characterwise + norm! 3gg$Visy + call assert_equal('v', visualmode()) + " reset visualmode() + norm! 3ggVy + norm! 3gg$Vasy + call assert_equal('v', visualmode()) + " basic testing for textobjects a< and at + %d + call setline(1, ['
','xyz','
', ' ']) + " a< + norm! 1gg0da< + call assert_equal([' ', 'xyz', ' ', ' '], getline(1,'$')) + norm! 1pj + call assert_equal(['
', 'xyz', '
', ' '], getline(1,'$')) + " at + norm! d2at + call assert_equal([' '], getline(1,'$')) + %d + call setline(1, ['
','xyz','
', ' ']) + " i< + norm! 1gg0di< + call assert_equal(['<> ', 'xyz', ' ', ' '], getline(1,'$')) + norm! 1Pj + call assert_equal(['
', 'xyz', '
', ' '], getline(1,'$')) + norm! d2it + call assert_equal(['
',' '], getline(1,'$')) + " basic testing for a[ and i[ text object + %d + call setline(1, [' ', '[', 'one [two]', 'thre', ']']) + norm! 3gg0di[ + call assert_equal([' ', '[', ']'], getline(1,'$')) + call setline(1, [' ', '[', 'one [two]', 'thre', ']']) + norm! 3gg0ftd2a[ + call assert_equal([' '], getline(1,'$')) + %d + " Test for i" when cursor is in front of a quoted object + call append(0, 'foo "bar"') + norm! 1gg0di" + call assert_equal(['foo ""', ''], getline(1,'$')) + + " clean up + bw! +endfu + +func! Test_normal45_drop() + if !has("dnd") + return + endif + " basic test for :drop command + " unfortunately, without a gui, we can't really test much here, + " so simply test that ~p fails (which uses the drop register) + new + call assert_fails(':norm! "~p', 'E353') + call assert_equal([], getreg('~', 1, 1)) + " the ~ register is read only + call assert_fails(':let @~="1"', 'E354') + bw! +endfu + +func! Test_normal46_ignore() + new + " How to test this? + " let's just for now test, that the buffer + " does not change + call feedkeys("\", 't') + call assert_equal([''], getline(1,'$')) + + " clean up + bw! +endfu diff --git a/src/nvim/version.c b/src/nvim/version.c index ab3ccdab3d..bef92d40dd 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -126,7 +126,7 @@ static int included_patches[] = { // 2318, // 2317, // 2316 NA - // 2315, + 2315, 2314, 2313, 2312, From afd8eacb4e0268d6ce4dbcca9f188fc8f660cb3c Mon Sep 17 00:00:00 2001 From: raichoo Date: Sat, 11 Mar 2017 10:21:14 +0100 Subject: [PATCH 0009/1671] vim-patch:7.4.2317 Problem: Normal mode tests fail on MS-Windows. Solution: Do some tests only on Unix. Set 'fileformat' to "unix". https://github.com/vim/vim/commit/0913a1089a07ac7b17abc3a1343dfa7cd25613f4 --- src/nvim/testdir/test_normal.vim | 14 +++++++++----- src/nvim/version.c | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 1371ccfd5e..ff6710218d 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -1137,7 +1137,8 @@ func! Test_normal19_z_spell() endfu func! Test_normal20_exmode() - if !(has("win32") || has("win64")) + if !has("unix") + " Reading from redirected file doesn't work on MS-Windows return endif call writefile(['1a', 'foo', 'bar', '.', 'w! Xfile2', 'q!'], 'Xscript') @@ -1168,8 +1169,8 @@ endfu func! Test_normal22_zet() " Test for ZZ - let shell = &shell - let &shell = 'sh' + " let shell = &shell + " let &shell = 'sh' call writefile(['1', '2'], 'Xfile') let args = ' -u NONE -N -U NONE -i NONE --noplugins -X --not-a-term' call system(v:progpath . args . ' -c "%d" -c ":norm! ZZ" Xfile') @@ -1185,7 +1186,7 @@ func! Test_normal22_zet() for file in ['Xfile'] call delete(file) endfor - let &shell = shell + " let &shell = shell endfu func! Test_normal23_K() @@ -1206,7 +1207,8 @@ func! Test_normal23_K() call assert_match('\*version8\.0\*', getline('.')) helpclose - if !(has("win32") || has("win64")) + " Only expect "man" to work on Unix + if !has("unix") let &keywordprg = k bw! return @@ -1642,6 +1644,7 @@ fun! Test_normal33_g_cmd2() call assert_equal('l', getreg(0)) " Test for g Ctrl-G + set ff=unix let a=execute(":norm! g\") call assert_match('Col 15 of 43; Line 2 of 2; Word 2 of 2; Byte 16 of 45', a) @@ -1692,6 +1695,7 @@ endfu fun! Test_normal36_g_cmd5() new call append(0, 'abcdefghijklmnopqrstuvwxyz') + set ff=unix " Test for gp gP call append(1, range(1,10)) 1 diff --git a/src/nvim/version.c b/src/nvim/version.c index bef92d40dd..207b561cc3 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -124,7 +124,7 @@ static int included_patches[] = { // 2320, // 2319 NA // 2318, - // 2317, + 2317, // 2316 NA 2315, 2314, From 42caeccce6e50fa3b8b25fe3076ac2fbd555b152 Mon Sep 17 00:00:00 2001 From: raichoo Date: Sat, 11 Mar 2017 10:27:15 +0100 Subject: [PATCH 0010/1671] vim-patch:7.4.2322 Problem: Access memory beyond the end of the line. (Dominique Pelle) Solution: Adjust the cursor column. https://github.com/vim/vim/commit/bc54f3f3fed4dc3556df8c46cee6739d211b0eb2 --- src/nvim/move.c | 2 +- src/nvim/testdir/test_normal.vim | 9 +++++++++ src/nvim/version.c | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/nvim/move.c b/src/nvim/move.c index 4c1b8a8411..9ba515f209 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1877,7 +1877,7 @@ int onepage(int dir, long count) } } foldAdjustCursor(); - cursor_correct(); + check_cursor_col(); if (retval == OK) beginline(BL_SOL | BL_FIX); curwin->w_valid &= ~(VALID_WCOL|VALID_WROW|VALID_VIRTCOL); diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index ff6710218d..98cb7754bb 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -398,6 +398,15 @@ func! Test_normal14_page() bw! endfu +func! Test_normal14_page_eol() + 10new + norm oxxxxxxx + exe "norm 2\" + " check with valgrind that cursor is put back in column 1 + exe "norm 2\" + bw! +endfunc + func! Test_normal15_z_scroll_vert() " basic test for z commands that scroll the window call Setup_NewWindow() diff --git a/src/nvim/version.c b/src/nvim/version.c index 207b561cc3..7f07e5a287 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -119,7 +119,7 @@ static int included_patches[] = { // 2325 NA // 2324, // 2323, - // 2322, + 2322, 2321, // 2320, // 2319 NA From e888864c28033546b827c07d9dd095348fff23bd Mon Sep 17 00:00:00 2001 From: raichoo Date: Sat, 11 Mar 2017 11:33:15 +0100 Subject: [PATCH 0011/1671] vim-patch:7.4.2326 Problem: Illegal memory access when Visual selection starts in invalid position. (Dominique Pelle) Solution: Correct position when needed. https://github.com/vim/vim/commit/d5824ce1b5491df7d2eb0b66189d366fa67b4585 --- src/nvim/cursor.c | 18 ++++++++++++++++++ src/nvim/normal.c | 1 + src/nvim/version.c | 2 +- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 3ba9da34f2..01476627de 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -294,6 +294,24 @@ linenr_T get_cursor_rel_lnum(win_T *wp, linenr_T lnum) return (lnum < cursor) ? -retval : retval; } +// Make sure "pos.lnum" and "pos.col" are valid in "buf". +// This allows for the col to be on the NUL byte. +void check_pos(buf_T *buf, pos_T *pos) { + char_u *line; + colnr_T len; + + if (pos->lnum > buf->b_ml.ml_line_count) { + pos->lnum = buf->b_ml.ml_line_count; + } + + if (pos->col > 0) { + line = ml_get_buf(buf, pos->lnum, FALSE); + len = (colnr_T)STRLEN(line); + if (pos->col > len) + pos->col = len; + } +} + /* * Make sure curwin->w_cursor.lnum is valid. */ diff --git a/src/nvim/normal.c b/src/nvim/normal.c index ee3c3f9f11..1fd03257ec 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -7831,6 +7831,7 @@ static void get_op_vcol( // prevent from moving onto a trail byte if (has_mbyte) { + check_pos(curwin->w_buffer, &oap->end); mb_adjustpos(curwin->w_buffer, &oap->end); } diff --git a/src/nvim/version.c b/src/nvim/version.c index 7f07e5a287..09fe1d029b 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -115,7 +115,7 @@ static int included_patches[] = { // 2329, // 2328, // 2327 NA - // 2326, + 2326, // 2325 NA // 2324, // 2323, From 1e33c886856abcdac13c3eb368c127bda40e1e84 Mon Sep 17 00:00:00 2001 From: raichoo Date: Sat, 11 Mar 2017 10:42:07 +0100 Subject: [PATCH 0012/1671] vim-patch:7.4.2333 Problem: Outdated comments in test. Solution: Cleanup normal mode test. (Christian Brabandt) https://github.com/vim/vim/commit/31845093b7f1b33e0c7e9e592bef65528674a1f2 --- src/nvim/ops.c | 9 ++++- src/nvim/testdir/test_normal.vim | 63 ++++++++++++++------------------ src/nvim/version.c | 4 +- 3 files changed, 37 insertions(+), 39 deletions(-) diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 1e4d392754..3a682b6f96 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3847,6 +3847,7 @@ fex_format ( int use_sandbox = was_set_insecurely((char_u *)"formatexpr", OPT_LOCAL); int r; + char_u *fex; /* * Set v:lnum to the first line number and v:count to the number of lines. @@ -3856,16 +3857,22 @@ fex_format ( set_vim_var_nr(VV_COUNT, (varnumber_T)count); set_vim_var_char(c); + // Make a copy, the option could be changed while calling it. + fex = vim_strsave(curbuf->b_p_fex); + if (fex == NULL) { + return 0; + } /* * Evaluate the function. */ if (use_sandbox) ++sandbox; - r = eval_to_number(curbuf->b_p_fex); + r = (int)eval_to_number(fex); if (use_sandbox) --sandbox; set_vim_var_string(VV_CHAR, NULL, -1); + xfree(fex); return r; } diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 98cb7754bb..9a8f8f83be 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -36,47 +36,14 @@ function! CountSpaces(type, ...) endfunction fun! Test_normal00_optrans() - " Attention: This needs to be the very first test, - " it will fail, if it runs later, don't know why! - " Test for S s and alike comamnds, that are internally handled aliased new call append(0, ['1 This is a simple test: abcd', '2 This is the second line', '3 this is the third line']) 1 exe "norm! Sfoobar\" call assert_equal(['foobar', '2 This is the second line', '3 this is the third line', ''], getline(1,'$')) 2 - " Test does not work - " TODO: Why does it not work? - " Adds an additional linebreak if used in visual mode... - " When run in the test, this returns: - " ,-------- - " |foobar - " |2 This is - " |the second - " |one - " |3 this is the third line - " `----------- - " instead of - " ,-------- - " |foobar - " |2 This is the second one - " |3 this is the third line - " `----------- exe "norm! $vbsone" call assert_equal(['foobar', '2 This is the second one', '3 this is the third line', ''], getline(1,'$')) - " When run in the test, this returns: - " ,-------- - " |foobar - " |Second line - " |here - " |3 this is the third line - " `----------- - " instead of - " ,-------- - " |foobar - " |Second line here - " |3 this is the third line - " `----------- norm! VS Second line here call assert_equal(['foobar', ' Second line here', '3 this is the third line', ''], getline(1, '$')) %d @@ -192,6 +159,30 @@ func! Test_normal05_formatexpr() bw! endfu +func Test_normal05_formatexpr_newbuf() + " Edit another buffer in the 'formatexpr' function + new + func! Format() + edit another + endfunc + set formatexpr=Format() + norm gqG + bw! + set formatexpr= +endfunc + +func Test_normal05_formatexpr_setopt() + " Change the 'formatexpr' value in the function + new + func! Format() + set formatexpr= + endfunc + set formatexpr=Format() + norm gqG + bw! + set formatexpr= +endfunc + func! Test_normal06_formatprg() " basic test for formatprg " only test on non windows platform @@ -224,7 +215,7 @@ func! Test_normal07_internalfmt() norm! gggqG call assert_equal(['1 2 3', '4 5 6', '7 8 9', '10 11 '], getline(1, '$')) " clean up - set formatprg= + set formatprg= tw=0 bw! endfu @@ -1695,7 +1686,7 @@ fun! Test_normal35_g_cmd4() " Test for g< " Cannot capture its output, " probably a bug, therefore, test disabled: - return + throw "Skipped: output of g< can't be tested currently" echo "a\nb\nc\nd" let b=execute(':norm! g<') call assert_true(!empty(b), 'failed `execute(g<)`') @@ -1853,7 +1844,7 @@ fun! Test_normal41_insert_reg() " clean up set sts=0 sw=8 ts=8 - "bw! + bw! endfu func! Test_normal42_halfpage() diff --git a/src/nvim/version.c b/src/nvim/version.c index 09fe1d029b..dd7318b697 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -108,7 +108,7 @@ static int included_patches[] = { // 2336, 2335, // 2334, - // 2333, + 2333, // 2332 NA 2331, // 2330, @@ -118,7 +118,7 @@ static int included_patches[] = { 2326, // 2325 NA // 2324, - // 2323, + 2323, 2322, 2321, // 2320, From 4500b16f15510f6091ca98b78aa4ee13e1acf2c7 Mon Sep 17 00:00:00 2001 From: raichoo Date: Sat, 11 Mar 2017 10:44:35 +0100 Subject: [PATCH 0013/1671] vim-patch:7.4.2336 Problem: Running normal mode tests leave a couple of files behind. (Yegappan Lakshmanan) Solution: Delete the files. (Christian Brabandt) https://github.com/vim/vim/commit/df0db16cf74281a83a9ea3388a2ef9aedccd013c --- src/nvim/testdir/test_normal.vim | 2 ++ src/nvim/version.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 9a8f8f83be..b894a633c4 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -1125,6 +1125,8 @@ func! Test_normal19_z_spell() exe "lang" oldlang call delete("./Xspellfile.add") call delete("./Xspellfile2.add") + call delete("./Xspellfile.add.spl") + call delete("./Xspellfile2.add.spl") " zux -> no-op 2 diff --git a/src/nvim/version.c b/src/nvim/version.c index dd7318b697..487685ea2e 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -105,7 +105,7 @@ static int included_patches[] = { // 2339, // 2338 NA 2337, - // 2336, + 2336, 2335, // 2334, 2333, From ae828982ceb9dc4c5bde58a276e6e10df6f35051 Mon Sep 17 00:00:00 2001 From: raichoo Date: Sat, 11 Mar 2017 10:46:50 +0100 Subject: [PATCH 0014/1671] vim-patch:7.4.2347 Problem: Crash when closing a buffer while Visual mode is active. (Dominique Pelle) Solution: Adjust the position before computing the number of lines. When closing the current buffer stop Visual mode. https://github.com/vim/vim/commit/c4a908e83690844b0d3a46124ba6af7d23485d69 --- src/nvim/buffer.c | 12 ++++++++++++ src/nvim/normal.c | 3 ++- src/nvim/testdir/test_normal.vim | 14 ++++++++++++++ src/nvim/version.c | 2 +- 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index f52989c74d..0a95cc9020 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -438,6 +438,13 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) /* Remember if we are closing the current buffer. Restore the number of * windows, so that autocommands in buf_freeall() don't get confused. */ bool is_curbuf = (buf == curbuf); + + // When closing the current buffer stop Visual mode before freeing + // anything. + if (is_curbuf) { + end_visual_mode(); + } + buf->b_nwindows = nwindows; buf_freeall(buf, (del_buf ? BFA_DEL : 0) + (wipe_buf ? BFA_WIPE : 0)); @@ -1075,6 +1082,11 @@ do_buffer ( } } + // When closing the current buffer stop Visual mode. + if (buf == curbuf) { + end_visual_mode(); + } + /* * If deleting the last (listed) buffer, make it empty. * The last (listed) buffer cannot be unloaded. diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 1fd03257ec..ce286042df 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1596,6 +1596,8 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) oap->start = curwin->w_cursor; } + // Just in case lines were deleted that make the position invalid. + check_pos(curwin->w_buffer, &oap->end); oap->line_count = oap->end.lnum - oap->start.lnum + 1; /* Set "virtual_op" before resetting VIsual_active. */ @@ -7831,7 +7833,6 @@ static void get_op_vcol( // prevent from moving onto a trail byte if (has_mbyte) { - check_pos(curwin->w_buffer, &oap->end); mb_adjustpos(curwin->w_buffer, &oap->end); } diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index b894a633c4..79af7b2587 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -1998,3 +1998,17 @@ func! Test_normal46_ignore() " clean up bw! endfu + +func! Test_normal47_visual_buf_wipe() + " This was causing a crash or ml_get error. + enew! + call setline(1,'xxx') + normal $ + new + call setline(1, range(1,2)) + 2 + exe "norm \$" + bw! + norm yp + set nomodified +endfu diff --git a/src/nvim/version.c b/src/nvim/version.c index 487685ea2e..b652f5fee5 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -94,7 +94,7 @@ static int included_patches[] = { // 2350, // 2349, // 2348, - // 2347, + 2347, // 2346, // 2345 NA // 2344 NA From 707aea86bbae6ff819deb64ca977cc701d39d569 Mon Sep 17 00:00:00 2001 From: raichoo Date: Sat, 11 Mar 2017 11:45:44 +0100 Subject: [PATCH 0015/1671] vim-patch:7.4.2353 Problem: Not enough test coverage for Normal mode commands. Solution: Add more tests. (Christian Brabandt) https://github.com/vim/vim/commit/2931f2a5df0d962032d41060af84d9fd2cb35c9f --- src/nvim/testdir/test_normal.vim | 311 +++++++++++++++++++++++++------ src/nvim/version.c | 2 +- 2 files changed, 254 insertions(+), 59 deletions(-) diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 79af7b2587..f896046d4b 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -13,9 +13,9 @@ func! MyFormatExpr() call setline(ln, substitute(line, '\s\+$', '', '') . '->$') endif endfor -endfu +endfunc -function! CountSpaces(type, ...) +func! CountSpaces(type, ...) " for testing operatorfunc " will count the number of spaces " and return the result in g:a @@ -33,7 +33,11 @@ function! CountSpaces(type, ...) let g:a=strlen(substitute(@@, '[^ ]', '', 'g')) let &selection = sel_save let @@ = reg_save -endfunction +endfunc + +func! IsWindows() + return has("win32") || has("win64") || has("win95") +endfunc fun! Test_normal00_optrans() new @@ -60,27 +64,37 @@ fun! Test_normal00_optrans() " clean up set cpo-=# bw! -endfu +endfunc func! Test_normal01_keymodel() call Setup_NewWindow() " Test 1: depending on 'keymodel' does something different - :50 + 50 call feedkeys("V\y", 'tx') call assert_equal(['47', '48', '49', '50'], getline("'<", "'>")) - :set keymodel=startsel - :50 + set keymodel=startsel + 50 call feedkeys("V\y", 'tx') call assert_equal(['49', '50'], getline("'<", "'>")) " Start visual mode when keymodel = startsel - :50 + 50 call feedkeys("\y", 'tx') call assert_equal(['49', '5'], getreg(0, 0, 1)) " Do not start visual mode when keymodel= - :set keymodel= - :50 + set keymodel= + 50 call feedkeys("\y$", 'tx') call assert_equal(['42'], getreg(0, 0, 1)) + " Stop visual mode when keymodel=stopsel + set keymodel=stopsel + 50 + call feedkeys("Vkk\yy", 'tx') + call assert_equal(['47'], getreg(0, 0, 1)) + + set keymodel= + 50 + call feedkeys("Vkk\yy", 'tx') + call assert_equal(['47', '48', '49', '50'], getreg(0, 0, 1)) " clean up bw! @@ -98,7 +112,17 @@ func! Test_normal02_selectmode() call assert_equal('y60', getline('.')) " clean up bw! -endfu +endfunc + +func! Test_normal02_selectmode2() + " some basic select mode tests + call Setup_NewWindow() + 50 + call feedkeys(":set im\n\gHc\:set noim\n", 'tx') + call assert_equal('c51', getline('.')) + " clean up + bw! +endfunc func! Test_normal03_join() " basic join test @@ -118,12 +142,12 @@ func! Test_normal03_join() call assert_equal('100', getline('.')) " clean up bw! -endfu +endfunc func! Test_normal04_filter() " basic filter test " only test on non windows platform - if has("win32") || has("win64") || has("win95") + if IsWindows() return endif call Setup_NewWindow() @@ -144,7 +168,7 @@ func! Test_normal04_filter() call assert_equal('one', getline('.')) set cpo-=! bw! -endfu +endfunc func! Test_normal05_formatexpr() " basic formatexpr test @@ -157,7 +181,7 @@ func! Test_normal05_formatexpr() call assert_equal(['here: 1->$', '2', 'here: 3->$', '4', 'not here: '], getline(1,'$')) set formatexpr= bw! -endfu +endfunc func Test_normal05_formatexpr_newbuf() " Edit another buffer in the 'formatexpr' function @@ -186,7 +210,7 @@ endfunc func! Test_normal06_formatprg() " basic test for formatprg " only test on non windows platform - if has("win32") || has("win64") || has("win95") + if IsWindows() return else " uses sed to number non-empty lines @@ -203,7 +227,7 @@ func! Test_normal06_formatprg() set formatprg= call delete('Xsed_format.sh') bw! -endfu +endfunc func! Test_normal07_internalfmt() " basic test for internal formmatter to textwidth of 12 @@ -217,7 +241,7 @@ func! Test_normal07_internalfmt() " clean up set formatprg= tw=0 bw! -endfu +endfunc func! Test_normal08_fold() " basic tests for foldopen/folddelete @@ -276,7 +300,7 @@ func! Test_normal08_fold() " clean up setl nofoldenable fdm=marker bw! -endfu +endfunc func! Test_normal09_operatorfunc() " Test operatorfunc @@ -305,7 +329,7 @@ func! Test_normal09_operatorfunc() unmap ,, set opfunc= bw! -endfu +endfunc func! Test_normal10_expand() " Test for expand() @@ -319,7 +343,7 @@ func! Test_normal10_expand() call assert_equal('ifooar,,cbar', b) " clean up bw! -endfu +endfunc func! Test_normal11_showcmd() " test for 'showcmd' @@ -334,7 +358,7 @@ func! Test_normal11_showcmd() exe "norm! 0d3\2l" call assert_equal('obar2foobar3', getline('.')) bw! -endfu +endfunc func! Test_normal12_nv_error() " Test for nv_error @@ -344,7 +368,7 @@ func! Test_normal12_nv_error() exe "norm! " call assert_equal(map(range(1,5), 'string(v:val)'), getline(1,'$')) bw! -endfu +endfunc func! Test_normal13_help() " Test for F1 @@ -353,7 +377,7 @@ func! Test_normal13_help() call assert_match('help\.txt', bufname('%')) call assert_equal(2, winnr('$')) bw! -endfu +endfunc func! Test_normal14_page() " basic test for Ctrl-F and Ctrl-B @@ -387,7 +411,7 @@ func! Test_normal14_page() " cleanup set startofline bw! -endfu +endfunc func! Test_normal14_page_eol() 10new @@ -485,7 +509,7 @@ func! Test_normal15_z_scroll_vert() " cleanup bw! -endfu +endfunc func! Test_normal16_z_scroll_hor() " basic test for z commands that scroll the window @@ -551,7 +575,7 @@ func! Test_normal16_z_scroll_hor() " cleanup set wrap listchars=eol:$ bw! -endfu +endfunc func! Test_normal17_z_scroll_hor2() " basic test for z commands that scroll the window @@ -618,7 +642,7 @@ func! Test_normal17_z_scroll_hor2() " cleanup set wrap listchars=eol:$ sidescrolloff=0 bw! -endfu +endfunc func! Test_normal18_z_fold() " basic tests for foldopen/folddelete @@ -989,7 +1013,7 @@ func! Test_normal18_z_fold() " clean up setl nofoldenable fdm=marker foldlevel=0 bw! -endfu +endfunc func! Test_normal19_z_spell() if !has("spell") || !has('syntax') @@ -1136,7 +1160,7 @@ func! Test_normal19_z_spell() set spellfile= bw! -endfu +endfunc func! Test_normal20_exmode() if !has("unix") @@ -1154,10 +1178,14 @@ func! Test_normal20_exmode() call delete(file) endfor bw! -endfu +endfunc func! Test_normal21_nv_hat() set hidden + new + " to many buffers opened already, will not work + "call assert_fails(":b#", 'E23') + "call assert_equal('', @#) e Xfoobar e Xfile2 call feedkeys("\", 't') @@ -1167,7 +1195,7 @@ func! Test_normal21_nv_hat() " clean up set nohidden bw! -endfu +endfunc func! Test_normal22_zet() " Test for ZZ @@ -1189,7 +1217,7 @@ func! Test_normal22_zet() call delete(file) endfor " let &shell = shell -endfu +endfunc func! Test_normal23_K() " Test for K command @@ -1224,7 +1252,7 @@ func! Test_normal23_K() " clean up let &keywordprg = k bw! -endfu +endfunc func! Test_normal24_rot13() " This test uses multi byte characters @@ -1242,7 +1270,7 @@ func! Test_normal24_rot13() " clean up bw! -endfu +endfunc func! Test_normal25_tag() " Testing for CTRL-] g CTRL-] g] @@ -1309,7 +1337,7 @@ func! Test_normal25_tag() " clean up helpclose -endfu +endfunc func! Test_normal26_put() " Test for ]p ]P [p and [P @@ -1328,7 +1356,7 @@ func! Test_normal26_put() " clean up bw! -endfu +endfunc func! Test_normal27_bracket() " Test for [' [` ]' ]` @@ -1379,7 +1407,7 @@ func! Test_normal27_bracket() " clean up bw! -endfu +endfunc func! Test_normal28_parenthesis() " basic testing for ( and ) @@ -1402,7 +1430,7 @@ func! Test_normal28_parenthesis() " clean up bw! -endfu +endfunc fun! Test_normal29_brace() " basic test for { and } movements @@ -1477,7 +1505,7 @@ fun! Test_normal29_brace() " clean up set cpo-={ bw! -endfu +endfunc fun! Test_normal30_changecase() " This test uses multi byte characters @@ -1505,7 +1533,7 @@ fun! Test_normal30_changecase() " clean up bw! -endfu +endfunc fun! Test_normal31_r_cmd() " Test for r command @@ -1530,7 +1558,7 @@ fun! Test_normal31_r_cmd() " clean up set noautoindent bw! -endfu +endfunc func! Test_normal32_g_cmd1() " Test for g*, g# @@ -1546,7 +1574,7 @@ func! Test_normal32_g_cmd1() " clean up bw! -endfu +endfunc fun! Test_normal33_g_cmd2() if !has("jumplist") @@ -1666,7 +1694,7 @@ fun! Test_normal33_g_cmd2() " clean up bw! -endfu +endfunc fun! Test_normal34_g_cmd3() if !has("multi_byte") @@ -1682,7 +1710,7 @@ fun! Test_normal34_g_cmd3() call append(1, range(1,10)) " clean up bw! -endfu +endfunc fun! Test_normal35_g_cmd4() " Test for g< @@ -1692,7 +1720,7 @@ fun! Test_normal35_g_cmd4() echo "a\nb\nc\nd" let b=execute(':norm! g<') call assert_true(!empty(b), 'failed `execute(g<)`') -endfu +endfunc fun! Test_normal36_g_cmd5() new @@ -1730,7 +1758,7 @@ fun! Test_normal36_g_cmd5() call assert_equal([0, 14, 1, 0, 2147483647], getcurpos()) " clean up bw! -endfu +endfunc fun! Test_normal37_g_cmd6() " basic test for gt and gT @@ -1757,7 +1785,7 @@ fun! Test_normal37_g_cmd6() endfor " clean up call assert_fails(':tabclose', 'E784') -endfu +endfunc fun! Test_normal38_nvhome() " Test for and key @@ -1778,7 +1806,7 @@ fun! Test_normal38_nvhome() " clean up bw! -endfu +endfunc fun! Test_normal39_cw() " Test for cw and cW on whitespace @@ -1802,7 +1830,7 @@ fun! Test_normal39_cw() " clean up bw! -endfu +endfunc fun! Test_normal40_ctrl_bsl() " Basic test for CTRL-\ commands @@ -1827,7 +1855,7 @@ fun! Test_normal40_ctrl_bsl() " clean up bw! -endfu +endfunc fun! Test_normal41_insert_reg() " Test for =, = and = @@ -1847,7 +1875,7 @@ fun! Test_normal41_insert_reg() " clean up set sts=0 sw=8 ts=8 bw! -endfu +endfunc func! Test_normal42_halfpage() " basic test for Ctrl-D and Ctrl-U @@ -1884,7 +1912,7 @@ func! Test_normal42_halfpage() " cleanup set startofline bw! -endfu +endfunc fun! Test_normal43_textobject1() " basic tests for text object aw @@ -1915,7 +1943,7 @@ fun! Test_normal43_textobject1() " clean up bw! -endfu +endfunc func! Test_normal44_textobjects2() " basic testing for is and as text objects @@ -1970,7 +1998,7 @@ func! Test_normal44_textobjects2() " clean up bw! -endfu +endfunc func! Test_normal45_drop() if !has("dnd") @@ -1985,9 +2013,14 @@ func! Test_normal45_drop() " the ~ register is read only call assert_fails(':let @~="1"', 'E354') bw! -endfu +endfunc func! Test_normal46_ignore() + " This test uses multi byte characters + if !has("multi_byte") + return + endif + new " How to test this? " let's just for now test, that the buffer @@ -1995,9 +2028,16 @@ func! Test_normal46_ignore() call feedkeys("\", 't') call assert_equal([''], getline(1,'$')) + " no valid commands + exe "norm! \" + call assert_equal([''], getline(1,'$')) + + exe "norm! ä" + call assert_equal([''], getline(1,'$')) + " clean up bw! -endfu +endfunc func! Test_normal47_visual_buf_wipe() " This was causing a crash or ml_get error. @@ -2011,4 +2051,159 @@ func! Test_normal47_visual_buf_wipe() bw! norm yp set nomodified -endfu +endfunc + +func! Test_normal47_autocmd() + " disabled, does not seem to be possible currently + throw "Skipped: not possible to test cursorhold autocmd while waiting for input in normal_cmd" + new + call append(0, repeat('-',20)) + au CursorHold * call feedkeys('2l', '') + 1 + set updatetime=20 + " should delete 12 chars (d12l) + call feedkeys('d1', '!') + call assert_equal('--------', getline(1)) + + " clean up + au! CursorHold + set updatetime=4000 + bw! +endfunc + +func! Test_normal48_wincmd() + new + exe "norm! \c" + call assert_equal(1, winnr('$')) + call assert_fails(":norm! \c", "E444") +endfunc + +func! Test_normal49_counts() + new + call setline(1, 'one two three four five six seven eight nine ten') + 1 + norm! 3d2w + call assert_equal('seven eight nine ten', getline(1)) + bw! +endfunc + +func! Test_normal50_commandline() + if !has("timers") || !has("cmdline_hist") || !has("vertsplit") + return + endif + func! DoTimerWork(id) + call assert_equal('[Command Line]', bufname('')) + " should fail, with E11, but does fail with E23? + "call feedkeys("\", 'tm') + + " should also fail with E11 + call assert_fails(":wincmd p", 'E11') + " return from commandline window + call feedkeys("\") + endfunc + + let oldlang=v:lang + lang C + set updatetime=20 + call timer_start(100, 'DoTimerWork') + try + " throws E23, for whatever reason... + call feedkeys('q:', 'x!') + catch /E23/ + " no-op + endtry + " clean up + set updatetime=4000 + exe "lang" oldlang + bw! +endfunc + +func! Test_normal51_FileChangedRO() + if !has("autocmd") + return + endif + call writefile(['foo'], 'Xreadonly.log') + new Xreadonly.log + setl ro + au FileChangedRO :call feedkeys("\", 'tix') + call assert_fails(":norm! Af", 'E788') + call assert_equal(['foo'], getline(1,'$')) + call assert_equal('Xreadonly.log', bufname('')) + + " cleanup + bw! + call delete("Xreadonly.log") +endfunc + +func! Test_normal52_rl() + if !has("rightleft") + return + endif + new + call setline(1, 'abcde fghij klmnopq') + norm! 1gg$ + set rl + call assert_equal(19, col('.')) + call feedkeys('l', 'tx') + call assert_equal(18, col('.')) + call feedkeys('h', 'tx') + call assert_equal(19, col('.')) + call feedkeys("\", 'tx') + call assert_equal(18, col('.')) + call feedkeys("\", 'tx') + call assert_equal(13, col('.')) + call feedkeys("\", 'tx') + call assert_equal(7, col('.')) + call feedkeys("\", 'tx') + call assert_equal(13, col('.')) + call feedkeys("\", 'tx') + call assert_equal(19, col('.')) + call feedkeys("<<", 'tx') + call assert_equal(' abcde fghij klmnopq',getline(1)) + call feedkeys(">>", 'tx') + call assert_equal('abcde fghij klmnopq',getline(1)) + + " cleanup + set norl + bw! +endfunc + +func! Test_normal53_digraph() + if !has('digraphs') + return + endif + new + call setline(1, 'abcdefgh|') + exe "norm! 1gg0f\!!" + call assert_equal(9, col('.')) + set cpo+=D + exe "norm! 1gg0f\!!" + call assert_equal(1, col('.')) + + set cpo-=D + bw! +endfunc + +func! Test_normal54_Ctrl_bsl() + new + call setline(1, 'abcdefghijklmn') + exe "norm! df\\" + call assert_equal(['abcdefghijklmn'], getline(1,'$')) + exe "norm! df\\" + call assert_equal(['abcdefghijklmn'], getline(1,'$')) + exe "norm! df\m" + call assert_equal(['abcdefghijklmn'], getline(1,'$')) + if !has("multi_byte") + return + endif + call setline(2, 'abcdefghijklmnāf') + norm! 2gg0 + exe "norm! df\" + call assert_equal(['abcdefghijklmn', 'f'], getline(1,'$')) + norm! 1gg0 + exe "norm! df\" + call assert_equal(['abcdefghijklmn', 'f'], getline(1,'$')) + + " clean up + bw! +endfunc diff --git a/src/nvim/version.c b/src/nvim/version.c index b652f5fee5..e79d7a0faa 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -88,7 +88,7 @@ static int included_patches[] = { // 2356, // 2355, // 2354, - // 2353, + 2353, // 2352 NA // 2351 NA // 2350, From 151605cacc5939299e0a2ac44477396a56a6e083 Mon Sep 17 00:00:00 2001 From: raichoo Date: Sat, 11 Mar 2017 11:53:56 +0100 Subject: [PATCH 0016/1671] vim-patch:8.0.0060 Problem: When using an Ex command for 'keywordprg' it is escaped as with a shell command. (Romain Lafourcade) Solution: Escape for an Ex command. (closes vim/vim#1175) https://github.com/vim/vim/commit/426f3754223c8ff8a1bc51d6ba1eba11e8982ebc --- src/nvim/normal.c | 9 +++++++-- src/nvim/testdir/test_normal.vim | 20 +++++++++++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/nvim/normal.c b/src/nvim/normal.c index ce286042df..49085a27a0 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -4763,9 +4763,14 @@ static void nv_ident(cmdarg_T *cap) * Now grab the chars in the identifier */ if (cmdchar == 'K' && !kp_ex) { - /* Escape the argument properly for a shell command */ ptr = vim_strnsave(ptr, n); - p = vim_strsave_shellescape(ptr, true, true); + if (kp_ex) { + // Escape the argument properly for an Ex command + p = vim_strsave_fnameescape(ptr, FALSE); + } else { + // Escape the argument properly for a shell command + p = vim_strsave_shellescape(ptr, TRUE, TRUE); + } xfree(ptr); char *newbuf = xrealloc(buf, STRLEN(buf) + STRLEN(p) + 1); buf = newbuf; diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index f896046d4b..20cbaa00f0 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -1222,7 +1222,7 @@ endfunc func! Test_normal23_K() " Test for K command new - call append(0, ['version8.txt', 'man']) + call append(0, ['version8.txt', 'man', 'aa%bb', 'cc|dd']) let k = &keywordprg set keywordprg=:help 1 @@ -1237,6 +1237,24 @@ func! Test_normal23_K() call assert_match('\*version8\.0\*', getline('.')) helpclose + set keywordprg=:new + set iskeyword+=% + set iskeyword+=\| + 2 + norm! K + call assert_equal('man', fnamemodify(bufname('%'), ':t')) + bwipe! + 3 + norm! K + call assert_equal('aa%bb', fnamemodify(bufname('%'), ':t')) + bwipe! + 4 + norm! K + call assert_equal('cc|dd', fnamemodify(bufname('%'), ':t')) + bwipe! + set iskeyword-=% + set iskeyword-=\| + " Only expect "man" to work on Unix if !has("unix") let &keywordprg = k From 88dd2e8a085f88bb8b3c7ded86d5d0ac1ebaf58b Mon Sep 17 00:00:00 2001 From: raichoo Date: Sat, 11 Mar 2017 11:58:57 +0100 Subject: [PATCH 0017/1671] vim-patch:8.0.0064 Problem: Normal test fails on MS-Windows. Solution: Don't try using an illegal file name. https://github.com/vim/vim/commit/eb828d01d9c91791fa1fe217ba651cdc25746d1b --- src/nvim/testdir/test_normal.vim | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 20cbaa00f0..4dafe3c105 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -1248,10 +1248,12 @@ func! Test_normal23_K() norm! K call assert_equal('aa%bb', fnamemodify(bufname('%'), ':t')) bwipe! - 4 - norm! K - call assert_equal('cc|dd', fnamemodify(bufname('%'), ':t')) - bwipe! + if !has('win32') + 4 + norm! K + call assert_equal('cc|dd', fnamemodify(bufname('%'), ':t')) + bwipe! + endif set iskeyword-=% set iskeyword-=\| From a4f20db08ce2824d3a63b5ae50aabbea14830ac2 Mon Sep 17 00:00:00 2001 From: raichoo Date: Sat, 11 Mar 2017 12:00:36 +0100 Subject: [PATCH 0018/1671] vim-patch:8.0.0066 Problem: when calling an operator function when 'linebreak' is set, it is internally reset before calling the operator function. Solution: Restore 'linebreak' before calling op_function(). (Christian Brabandt) https://github.com/vim/vim/commit/4a08b0dc4dd70334056fc1bf069b5e938f2ed7d5 --- src/nvim/normal.c | 3 ++ src/nvim/testdir/test_normal.vim | 47 +++++++++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 49085a27a0..489eec8474 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1914,6 +1914,9 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) break; case OP_FUNCTION: + // Restore linebreak, so that when the user edits it looks as + // before. + curwin->w_p_lbr = lbr_saved; op_function(oap); /* call 'operatorfunc' */ break; diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 4dafe3c105..29bd783ebc 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -35,8 +35,20 @@ func! CountSpaces(type, ...) let @@ = reg_save endfunc -func! IsWindows() - return has("win32") || has("win64") || has("win95") +func! OpfuncDummy(type, ...) + " for testing operatorfunc + let g:opt=&linebreak + + if a:0 " Invoked from Visual mode, use gv command. + silent exe "normal! gvy" + elseif a:type == 'line' + silent exe "normal! '[V']y" + else + silent exe "normal! `[v`]y" + endif + " Create a new dummy window + new + let g:bufnr=bufnr('%') endfunc fun! Test_normal00_optrans() @@ -147,7 +159,7 @@ endfunc func! Test_normal04_filter() " basic filter test " only test on non windows platform - if IsWindows() + if has('win32') return endif call Setup_NewWindow() @@ -210,7 +222,7 @@ endfunc func! Test_normal06_formatprg() " basic test for formatprg " only test on non windows platform - if IsWindows() + if has('win32') return else " uses sed to number non-empty lines @@ -328,9 +340,36 @@ func! Test_normal09_operatorfunc() " clean up unmap ,, set opfunc= + unlet! g:a bw! endfunc +func! Test_normal09a_operatorfunc() + " Test operatorfunc + call Setup_NewWindow() + " Add some spaces for counting + 50,60s/$/ / + unlet! g:opt + set linebreak + nmap ,, :set opfunc=OpfuncDummyg@ + 50 + norm ,,j + exe "bd!" g:bufnr + call assert_true(&linebreak) + call assert_equal(g:opt, &linebreak) + set nolinebreak + norm ,,j + exe "bd!" g:bufnr + call assert_false(&linebreak) + call assert_equal(g:opt, &linebreak) + + " clean up + unmap ,, + set opfunc= + bw! + unlet! g:opt +endfunc + func! Test_normal10_expand() " Test for expand() 10new From 86b1e7f5834d58eebc87735c9a531040fea1a0f7 Mon Sep 17 00:00:00 2001 From: raichoo Date: Sat, 11 Mar 2017 13:42:06 +0100 Subject: [PATCH 0019/1671] vim-patch:7.4.2362 Problem: Illegal memory access with ":1@". (Dominique Pelle) Solution: Correct cursor column after setting the line number. Also avoid calling end_visual_mode() when not in Visual mode. https://github.com/vim/vim/commit/4930a76a0357f76a829eafe4985d04cf3ce0e9e0 --- src/nvim/buffer.c | 4 ++-- src/nvim/ex_docmd.c | 1 + src/nvim/version.c | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 0a95cc9020..f97f05e697 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -441,7 +441,7 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) // When closing the current buffer stop Visual mode before freeing // anything. - if (is_curbuf) { + if (is_curbuf && VIsual_active) { end_visual_mode(); } @@ -1083,7 +1083,7 @@ do_buffer ( } // When closing the current buffer stop Visual mode. - if (buf == curbuf) { + if (buf == curbuf && VIsual_active) { end_visual_mode(); } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 1234f8e888..bd3b8c204a 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -7325,6 +7325,7 @@ static void ex_at(exarg_T *eap) int prev_len = typebuf.tb_len; curwin->w_cursor.lnum = eap->line2; + check_cursor_col(); // Get the register name. No name means use the previous one. int c = *eap->arg; diff --git a/src/nvim/version.c b/src/nvim/version.c index e79d7a0faa..7d44efb401 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -79,7 +79,7 @@ static int included_patches[] = { // 2365 NA // 2364, // 2363 NA - // 2362, + 2362, // 2361 NA // 2360, // 2359 NA From 2ad25c04663da7d08da94db84dc6ded7df11ea87 Mon Sep 17 00:00:00 2001 From: raichoo Date: Sat, 11 Mar 2017 14:41:34 +0100 Subject: [PATCH 0020/1671] linter: make changes pass the linter --- src/nvim/cursor.c | 8 +++++--- src/nvim/move.c | 3 ++- src/nvim/normal.c | 6 +++--- src/nvim/ops.c | 14 +++++++------- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 01476627de..544bcf6ede 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -296,7 +296,8 @@ linenr_T get_cursor_rel_lnum(win_T *wp, linenr_T lnum) // Make sure "pos.lnum" and "pos.col" are valid in "buf". // This allows for the col to be on the NUL byte. -void check_pos(buf_T *buf, pos_T *pos) { +void check_pos(buf_T *buf, pos_T *pos) +{ char_u *line; colnr_T len; @@ -305,10 +306,11 @@ void check_pos(buf_T *buf, pos_T *pos) { } if (pos->col > 0) { - line = ml_get_buf(buf, pos->lnum, FALSE); + line = ml_get_buf(buf, pos->lnum, false); len = (colnr_T)STRLEN(line); - if (pos->col > len) + if (pos->col > len) { pos->col = len; + } } } diff --git a/src/nvim/move.c b/src/nvim/move.c index 9ba515f209..4c3f82bc16 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1878,8 +1878,9 @@ int onepage(int dir, long count) } foldAdjustCursor(); check_cursor_col(); - if (retval == OK) + if (retval == OK) { beginline(BL_SOL | BL_FIX); + } curwin->w_valid &= ~(VALID_WCOL|VALID_WROW|VALID_VIRTCOL); /* diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 489eec8474..5a89fed207 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1917,7 +1917,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) // Restore linebreak, so that when the user edits it looks as // before. curwin->w_p_lbr = lbr_saved; - op_function(oap); /* call 'operatorfunc' */ + op_function(oap); // call 'operatorfunc' break; case OP_INSERT: @@ -4769,10 +4769,10 @@ static void nv_ident(cmdarg_T *cap) ptr = vim_strnsave(ptr, n); if (kp_ex) { // Escape the argument properly for an Ex command - p = vim_strsave_fnameescape(ptr, FALSE); + p = vim_strsave_fnameescape(ptr, false); } else { // Escape the argument properly for a shell command - p = vim_strsave_shellescape(ptr, TRUE, TRUE); + p = vim_strsave_shellescape(ptr, true, true); } xfree(ptr); char *newbuf = xrealloc(buf, STRLEN(buf) + STRLEN(p) + 1); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 3a682b6f96..c13b6f736a 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3862,14 +3862,14 @@ fex_format ( if (fex == NULL) { return 0; } - /* - * Evaluate the function. - */ - if (use_sandbox) - ++sandbox; + // Evaluate the function. + if (use_sandbox) { + sandbox++; + } r = (int)eval_to_number(fex); - if (use_sandbox) - --sandbox; + if (use_sandbox) { + sandbox--; + } set_vim_var_string(VV_CHAR, NULL, -1); xfree(fex); From 0f5c3f111ab130f5cbb5943082ea5e877c1c2f4c Mon Sep 17 00:00:00 2001 From: raichoo Date: Thu, 9 Mar 2017 17:33:51 +0100 Subject: [PATCH 0021/1671] vim-patch:8.0.0179 Problem: 'formatprg' is a global option but the value may depend on the type of buffer. (Sung Pae) Solution: Make 'formatprg' global-local. (closes vim/vim#1380) https://github.com/vim/vim/commit/9be7c04e6cd5b0facedcb56b09a5bcfc339efe03 --- runtime/doc/options.txt | 2 +- src/nvim/buffer_defs.h | 1 + src/nvim/normal.c | 22 +++++++++++++--------- src/nvim/option.c | 8 ++++++++ src/nvim/option_defs.h | 1 + src/nvim/options.lua | 2 +- src/nvim/testdir/test_normal.vim | 32 ++++++++++++++++++++++---------- 7 files changed, 47 insertions(+), 21 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 23d8287a2c..29de35805e 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2731,7 +2731,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'formatprg'* *'fp'* 'formatprg' 'fp' string (default "") - global + global or local to buffer |global-local| The name of an external program that will be used to format the lines selected with the |gq| operator. The program must take the input on stdin and produce the output on stdout. The Unix program "fmt" is diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 3e9767adde..fdd7d945c9 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -640,6 +640,7 @@ struct file_buffer { char_u *b_p_inde; ///< 'indentexpr' uint32_t b_p_inde_flags; ///< flags for 'indentexpr' char_u *b_p_indk; ///< 'indentkeys' + char_u *b_p_fp; ///< 'formatprg' char_u *b_p_fex; ///< 'formatexpr' uint32_t b_p_fex_flags; ///< flags for 'formatexpr' char_u *b_p_kp; ///< 'keywordprg' diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 5a89fed207..82fa9f5f97 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1901,12 +1901,13 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) break; case OP_FORMAT: - if (*curbuf->b_p_fex != NUL) - op_formatexpr(oap); /* use expression */ - else if (*p_fp != NUL) - op_colon(oap); /* use external command */ - else - op_format(oap, false); /* use internal function */ + if (*curbuf->b_p_fex != NUL) { + op_formatexpr(oap); // use expression + } else if (*p_fp != NUL || *curbuf->b_p_fp != NUL) { + op_colon(oap); // use external command + } else { + op_format(oap, false); // use internal function + } break; case OP_FORMAT2: @@ -2064,10 +2065,13 @@ static void op_colon(oparg_T *oap) stuffReadbuff(get_equalprg()); stuffReadbuff((char_u *)"\n"); } else if (oap->op_type == OP_FORMAT) { - if (*p_fp == NUL) - stuffReadbuff((char_u *)"fmt"); - else + if (*curbuf->b_p_fp != NUL) { + stuffReadbuff(curbuf->b_p_fp); + } else if (*p_fp != NUL) { stuffReadbuff(p_fp); + } else { + stuffReadbuff((char_u *)"fmt"); + } stuffReadbuff((char_u *)"\n']"); } diff --git a/src/nvim/option.c b/src/nvim/option.c index 2fae4aa848..2a17ca69d1 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2150,6 +2150,7 @@ void check_buf_options(buf_T *buf) check_string_option(&buf->b_p_inex); check_string_option(&buf->b_p_inde); check_string_option(&buf->b_p_indk); + check_string_option(&buf->b_p_fp); check_string_option(&buf->b_p_fex); check_string_option(&buf->b_p_kp); check_string_option(&buf->b_p_mps); @@ -5255,6 +5256,9 @@ void unset_global_local_option(char *name, void *from) case PV_TSR: clear_string_option(&buf->b_p_tsr); break; + case PV_FP: + clear_string_option(&buf->b_p_fp); + break; case PV_EFM: clear_string_option(&buf->b_p_efm); break; @@ -5288,6 +5292,7 @@ static char_u *get_varp_scope(vimoption_T *p, int opt_flags) } if ((opt_flags & OPT_LOCAL) && ((int)p->indir & PV_BOTH)) { switch ((int)p->indir) { + case PV_FP: return (char_u *)&(curbuf->b_p_fp); case PV_EFM: return (char_u *)&(curbuf->b_p_efm); case PV_GP: return (char_u *)&(curbuf->b_p_gp); case PV_MP: return (char_u *)&(curbuf->b_p_mp); @@ -5346,6 +5351,8 @@ static char_u *get_varp(vimoption_T *p) ? (char_u *)&(curbuf->b_p_dict) : p->var; case PV_TSR: return *curbuf->b_p_tsr != NUL ? (char_u *)&(curbuf->b_p_tsr) : p->var; + case PV_FP: return *curbuf->b_p_fp != NUL + ? (char_u *)&(curbuf->b_p_fp) : p->var; case PV_EFM: return *curbuf->b_p_efm != NUL ? (char_u *)&(curbuf->b_p_efm) : p->var; case PV_GP: return *curbuf->b_p_gp != NUL @@ -5694,6 +5701,7 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_s.b_p_spl = vim_strsave(p_spl); buf->b_p_inde = vim_strsave(p_inde); buf->b_p_indk = vim_strsave(p_indk); + buf->b_p_fp = empty_option; buf->b_p_fex = vim_strsave(p_fex); buf->b_p_sua = vim_strsave(p_sua); buf->b_p_keymap = vim_strsave(p_keymap); diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 1fd6dc9c91..b171b23edb 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -713,6 +713,7 @@ enum { , BV_EP , BV_ET , BV_FENC + , BV_FP , BV_BEXPR , BV_FEX , BV_FF diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 1476fdda2c..853c2b52d7 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -948,7 +948,7 @@ return { }, { full_name='formatprg', abbreviation='fp', - type='string', scope={'global'}, + type='string', scope={'global', 'buffer'}, secure=true, vi_def=true, expand=true, diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 29bd783ebc..98177851ab 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -224,21 +224,33 @@ func! Test_normal06_formatprg() " only test on non windows platform if has('win32') return - else - " uses sed to number non-empty lines - call writefile(['#!/bin/sh', 'sed ''/./=''|sed ''/./{', 'N', 's/\n/ /', '}'''], 'Xsed_format.sh') - call system('chmod +x ./Xsed_format.sh') endif - call Setup_NewWindow() - %d - call setline(1, ['a', '', 'c', '', ' ', 'd', 'e']) + + " uses sed to number non-empty lines + call writefile(['#!/bin/sh', 'sed ''/./=''|sed ''/./{', 'N', 's/\n/ /', '}'''], 'Xsed_format.sh') + call system('chmod +x ./Xsed_format.sh') + let text = ['a', '', 'c', '', ' ', 'd', 'e'] + let expected = ['1 a', '', '3 c', '', '5 ', '6 d', '7 e'] + + 10new + call setline(1, text) set formatprg=./Xsed_format.sh norm! gggqG - call assert_equal(['1 a', '', '3 c', '', '5 ', '6 d', '7 e'], getline(1, '$')) + call assert_equal(expected, getline(1, '$')) + bw! + + 10new + call setline(1, text) + set formatprg=donothing + setlocal formatprg=./Xsed_format.sh + norm! gggqG + call assert_equal(expected, getline(1, '$')) + bw! + " clean up set formatprg= + setlocal formatprg= call delete('Xsed_format.sh') - bw! endfunc func! Test_normal07_internalfmt() @@ -251,7 +263,7 @@ func! Test_normal07_internalfmt() norm! gggqG call assert_equal(['1 2 3', '4 5 6', '7 8 9', '10 11 '], getline(1, '$')) " clean up - set formatprg= tw=0 + set tw=0 bw! endfunc From 99f2dc1341c8501290e3a0b297e1700f74fd17a3 Mon Sep 17 00:00:00 2001 From: raichoo Date: Sat, 11 Mar 2017 16:43:15 +0100 Subject: [PATCH 0022/1671] vim-patch:8.0.0229 Problem: When freeing a buffer the local value of the 'formatprg' option is not cleared. Solution: Add missing change. https://github.com/vim/vim/commit/24a2d416ec261829ff7fd29f7b66739c96dd6513 --- src/nvim/buffer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index f97f05e697..ab45008962 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1679,6 +1679,7 @@ void free_buf_options(buf_T *buf, int free_p_ff) clear_string_option(&buf->b_p_inex); clear_string_option(&buf->b_p_inde); clear_string_option(&buf->b_p_indk); + clear_string_option(&buf->b_p_fp); clear_string_option(&buf->b_p_fex); clear_string_option(&buf->b_p_kp); clear_string_option(&buf->b_p_mps); From db128974fcbd5702ca724610590a5dafa2e87712 Mon Sep 17 00:00:00 2001 From: raichoo Date: Thu, 16 Mar 2017 07:40:31 +0100 Subject: [PATCH 0023/1671] vim-patch:7.4.2348 Problem: Crash on exit when EXITFREE is defined. (Dominique Pelle) Solution: Don't access curwin when exiting. https://github.com/vim/vim/commit/9a27c7fde6d453d9892b6f6baa756bce4d6d419d --- src/nvim/buffer.c | 6 +++++- src/nvim/version.c | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index ab45008962..9e781f5dff 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -441,7 +441,11 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) // When closing the current buffer stop Visual mode before freeing // anything. - if (is_curbuf && VIsual_active) { + if (is_curbuf && VIsual_active +#if defined(EXITFREE) + && !entered_free_all_mem +#endif + ) { end_visual_mode(); } diff --git a/src/nvim/version.c b/src/nvim/version.c index 7d44efb401..8fd0fce329 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -93,7 +93,7 @@ static int included_patches[] = { // 2351 NA // 2350, // 2349, - // 2348, + 2348, 2347, // 2346, // 2345 NA From 2f54d6927cc02484b528a5e8b25b64c8d6580ddd Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 19 Mar 2017 23:45:11 +0100 Subject: [PATCH 0024/1671] test/legacy: fix test_normal.vim --- src/nvim/normal.c | 6 +-- src/nvim/testdir/runtest.vim | 4 +- src/nvim/testdir/test_normal.vim | 72 +++++++++++++++++--------------- 3 files changed, 43 insertions(+), 39 deletions(-) diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 82fa9f5f97..7188e13436 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -4766,10 +4766,8 @@ static void nv_ident(cmdarg_T *cap) } } - /* - * Now grab the chars in the identifier - */ - if (cmdchar == 'K' && !kp_ex) { + // Now grab the chars in the identifier + if (cmdchar == 'K') { ptr = vim_strnsave(ptr, n); if (kp_ex) { // Escape the argument properly for an Ex command diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index 0403a53f9c..b4eb9de506 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -68,10 +68,10 @@ let $HOME = '/does/not/exist' " Prepare for calling garbagecollect_for_testing(). let v:testing = 1 -" Align with vim defaults. +" Align Nvim defaults to Vim. set directory^=. -set nohidden set backspace= +set nohidden smarttab noautoindent noautoread complete-=i noruler noshowcmd function RunTheTest(test) echo 'Executing ' . a:test diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 98177851ab..29a7c1edd8 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -69,9 +69,10 @@ fun! Test_normal00_optrans() 1 norm! 2D call assert_equal(['3 this is the third line', '4 This is a simple test: abcd', '5 This is the second line', '6 this is the third line', ''], getline(1,'$')) - set cpo+=# - norm! 4D - call assert_equal(['', '4 This is a simple test: abcd', '5 This is the second line', '6 this is the third line', ''], getline(1,'$')) + " Nvim: no "#" flag in 'cpoptions'. + " set cpo+=# + " norm! 4D + " call assert_equal(['', '4 This is a simple test: abcd', '5 This is the second line', '6 this is the third line', ''], getline(1,'$')) " clean up set cpo-=# @@ -79,6 +80,7 @@ fun! Test_normal00_optrans() endfunc func! Test_normal01_keymodel() + throw "skipped: Nvim regression: 'keymodel'" call Setup_NewWindow() " Test 1: depending on 'keymodel' does something different 50 @@ -431,6 +433,7 @@ func! Test_normal13_help() endfunc func! Test_normal14_page() + throw "skipped: Nvim regression: CTRL-F with 'scrolloff'" " basic test for Ctrl-F and Ctrl-B call Setup_NewWindow() exe "norm! \" @@ -1067,6 +1070,7 @@ func! Test_normal18_z_fold() endfunc func! Test_normal19_z_spell() + throw "skipped: Nvim 'spell' requires download" if !has("spell") || !has('syntax') return endif @@ -1253,7 +1257,7 @@ func! Test_normal22_zet() " let shell = &shell " let &shell = 'sh' call writefile(['1', '2'], 'Xfile') - let args = ' -u NONE -N -U NONE -i NONE --noplugins -X --not-a-term' + let args = ' --headless -u NONE -N -U NONE -i NONE --noplugins' call system(v:progpath . args . ' -c "%d" -c ":norm! ZZ" Xfile') let a = readfile('Xfile') call assert_equal([], a) @@ -1273,19 +1277,19 @@ endfunc func! Test_normal23_K() " Test for K command new - call append(0, ['version8.txt', 'man', 'aa%bb', 'cc|dd']) + call append(0, ['helphelp.txt', 'man', 'aa%bb', 'cc|dd']) let k = &keywordprg set keywordprg=:help 1 norm! VK - call assert_equal('version8.txt', fnamemodify(bufname('%'), ':t')) + call assert_equal('helphelp.txt', fnamemodify(bufname('%'), ':t')) call assert_equal('help', &ft) - call assert_match('\*version8.txt\*', getline('.')) + call assert_match('\*helphelp.txt\*', getline('.')) helpclose norm! 0K - call assert_equal('version8.txt', fnamemodify(bufname('%'), ':t')) + call assert_equal('helphelp.txt', fnamemodify(bufname('%'), ':t')) call assert_equal('help', &ft) - call assert_match('\*version8\.0\*', getline('.')) + call assert_match('Help on help files', getline('.')) helpclose set keywordprg=:new @@ -1554,24 +1558,25 @@ fun! Test_normal29_brace() " Test with { in cpooptions %d call append(0, text) - set cpo+={ - 1 - norm! 0d2} - call assert_equal(['{', 'This is no paragaraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}', - \ '.IP', 'The nroff macros IP seperates a paragraph', 'That means, it must be a ''.''', - \ 'followed by IP', '.LPIt does not matter, if afterwards some', 'more characters follow.', - \ '.SHAlso section boundaries from the nroff', 'macros terminate a paragraph. That means', - \ 'a character like this:', '.NH', 'End of text here', ''], getline(1,'$')) - $ - norm! d} - call assert_equal(['{', 'This is no paragaraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}', - \ '.IP', 'The nroff macros IP seperates a paragraph', 'That means, it must be a ''.''', - \ 'followed by IP', '.LPIt does not matter, if afterwards some', 'more characters follow.', - \ '.SHAlso section boundaries from the nroff', 'macros terminate a paragraph. That means', - \ 'a character like this:', '.NH', 'End of text here', ''], getline(1,'$')) - norm! gg} - norm! d5} - call assert_equal(['{', 'This is no paragaraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}', ''], getline(1,'$')) + " Nvim: no "{" flag in 'cpoptions'. + " set cpo+={ + " 1 + " norm! 0d2} + " call assert_equal(['{', 'This is no paragaraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}', + " \ '.IP', 'The nroff macros IP seperates a paragraph', 'That means, it must be a ''.''', + " \ 'followed by IP', '.LPIt does not matter, if afterwards some', 'more characters follow.', + " \ '.SHAlso section boundaries from the nroff', 'macros terminate a paragraph. That means', + " \ 'a character like this:', '.NH', 'End of text here', ''], getline(1,'$')) + " $ + " norm! d} + " call assert_equal(['{', 'This is no paragaraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}', + " \ '.IP', 'The nroff macros IP seperates a paragraph', 'That means, it must be a ''.''', + " \ 'followed by IP', '.LPIt does not matter, if afterwards some', 'more characters follow.', + " \ '.SHAlso section boundaries from the nroff', 'macros terminate a paragraph. That means', + " \ 'a character like this:', '.NH', 'End of text here', ''], getline(1,'$')) + " norm! gg} + " norm! d5} + " call assert_equal(['{', 'This is no paragaraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}', ''], getline(1,'$')) " clean up set cpo-={ @@ -1889,12 +1894,13 @@ fun! Test_normal39_cw() call assert_equal('hereZZZare some words', getline('.')) norm! 1gg0elcWYYY call assert_equal('hereZZZareYYYsome words', getline('.')) - set cpo+=w - call setline(1, 'here are some words') - norm! 1gg0elcwZZZ - call assert_equal('hereZZZ are some words', getline('.')) - norm! 1gg2elcWYYY - call assert_equal('hereZZZ areYYY some words', getline('.')) + " Nvim: no "w" flag in 'cpoptions'. + " set cpo+=w + " call setline(1, 'here are some words') + " norm! 1gg0elcwZZZ + " call assert_equal('hereZZZ are some words', getline('.')) + " norm! 1gg2elcWYYY + " call assert_equal('hereZZZ areYYY some words', getline('.')) set cpo-=w norm! 2gg0cwfoo call assert_equal('foo', getline('.')) From 058516aaf9c29b41bf27c8d4adfa2773c3896205 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Sun, 19 Mar 2017 13:24:45 -0400 Subject: [PATCH 0025/1671] vim-patch:8.0.0190 Problem: Detecting duplicate tags uses a slow linear search. Solution: Use a much faster hash table solution. (James McCoy, closes vim/vim#1046) But don't add hi_keylen, it makes hash tables 50% bigger. https://github.com/vim/vim/commit/810f9c361c83afb36b9f1cdadca2b93f1201d039 --- src/nvim/tag.c | 239 ++++++++++++++++++++++++------------------------- 1 file changed, 116 insertions(+), 123 deletions(-) diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 9c9f24b9c0..c3c2634598 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -73,9 +73,9 @@ typedef struct { } pat_T; /* - * The matching tags are first stored in ga_match[]. In which one depends on - * the priority of the match. - * At the end, the matches from ga_match[] are concatenated, to make a list + * The matching tags are first stored in one of the ht_match[] hash tables. In + * which one depends on the priority of the match. + * At the end, the matches from ht_match[] are concatenated, to make a list * sorted on priority. */ #define MT_ST_CUR 0 /* static match in current file */ @@ -1122,11 +1122,9 @@ find_tags ( int save_emsg_off; - struct match_found { - int len; /* nr of chars of match[] to be compared */ - char_u match[1]; /* actually longer */ - } *mfp, *mfp2; - garray_T ga_match[MT_COUNT]; + char_u *mfp; + hashtab_T ht_match[MT_COUNT]; + hash_T hash = 0; int match_count = 0; /* number of matches found */ char_u **matches; int mtt; @@ -1186,7 +1184,7 @@ find_tags ( lbuf = xmalloc(lbuf_size); tag_fname = xmalloc(MAXPATHL + 1); for (mtt = 0; mtt < MT_COUNT; ++mtt) - ga_init(&ga_match[mtt], (int)sizeof(struct match_found *), 100); + hash_init(&ht_match[mtt]); STRCPY(tag_fname, "from cscope"); /* for error messages */ @@ -1752,9 +1750,11 @@ parse_line: } /* - * If a match is found, add it to ga_match[]. + * If a match is found, add it to ht_match[]. */ if (match) { + int len = 0; + if (use_cscope) { /* Don't change the ordering, always use the same table. */ mtt = MT_GL_OTH; @@ -1790,116 +1790,102 @@ parse_line: } /* - * Add the found match in ga_match[mtt], avoiding duplicates. + * Add the found match in ht_match[mtt]. * Store the info we need later, which depends on the kind of * tags we are dealing with. */ - ga_grow(&ga_match[mtt], 1); - { - int len; - - if (help_only) { + if (help_only) { # define ML_EXTRA 3 - /* - * Append the help-heuristic number after the - * tagname, for sorting it later. - */ - *tagp.tagname_end = NUL; - len = (int)(tagp.tagname_end - tagp.tagname); - mfp = xmalloc(sizeof(struct match_found) + len + 10 + ML_EXTRA); - /* "len" includes the language and the NUL, but - * not the priority. */ - mfp->len = len + ML_EXTRA + 1; -#define ML_HELP_LEN 6 - p = mfp->match; - STRCPY(p, tagp.tagname); - p[len] = '@'; - STRCPY(p + len + 1, help_lang); - sprintf((char *)p + len + 1 + ML_EXTRA, "%06d", - help_heuristic(tagp.tagname, - match_re ? matchoff : 0, !match_no_ic) - + help_pri - ); + // Append the help-heuristic number after the tagname, for + // sorting it later. The heuristic is ignored for + // detecting duplicates. + // The format is {tagname}@{lang}NUL{heuristic}NUL + *tagp.tagname_end = NUL; + len = (int)(tagp.tagname_end - tagp.tagname); + mfp = xmalloc(sizeof(char_u) + len + 10 + ML_EXTRA + 1); - *tagp.tagname_end = TAB; - } else if (name_only) { - if (get_it_again) { - char_u *temp_end = tagp.command; + p = mfp; + STRCPY(p, tagp.tagname); + p[len] = '@'; + STRCPY(p + len + 1, help_lang); + sprintf((char *)p + len + 1 + ML_EXTRA, "%06d", + help_heuristic(tagp.tagname, + match_re ? matchoff : 0, !match_no_ic) + + help_pri); - if (*temp_end == '/') - while (*temp_end && *temp_end != '\r' - && *temp_end != '\n' - && *temp_end != '$') - temp_end++; + *tagp.tagname_end = TAB; + } else if (name_only) { + if (get_it_again) { + char_u *temp_end = tagp.command; - if (tagp.command + 2 < temp_end) { - len = (int)(temp_end - tagp.command - 2); - mfp = xmalloc(sizeof(struct match_found) + len); - mfp->len = len + 1; /* include the NUL */ - p = mfp->match; - STRLCPY(p, tagp.command + 2, len + 1); - } else - mfp = NULL; - get_it_again = FALSE; - } else { - len = (int)(tagp.tagname_end - tagp.tagname); - mfp = xmalloc(sizeof(struct match_found) + len); - mfp->len = len + 1; /* include the NUL */ - p = mfp->match; - STRLCPY(p, tagp.tagname, len + 1); + if (*temp_end == '/') + while (*temp_end && *temp_end != '\r' + && *temp_end != '\n' + && *temp_end != '$') + temp_end++; - /* if wanted, re-read line to get long form too */ - if (State & INSERT) - get_it_again = p_sft; - } - } else { - /* Save the tag in a buffer. - * Emacs tag: - * other tag: - * without Emacs tags: - */ - len = (int)STRLEN(tag_fname) - + (int)STRLEN(lbuf) + 3; - mfp = xmalloc(sizeof(struct match_found) + len); - mfp->len = len; - p = mfp->match; - p[0] = mtt; - STRCPY(p + 1, tag_fname); -#ifdef BACKSLASH_IN_FILENAME - /* Ignore differences in slashes, avoid adding - * both path/file and path\file. */ - slash_adjust(p + 1); -#endif - s = p + 1 + STRLEN(tag_fname) + 1; - STRCPY(s, lbuf); - } - - if (mfp != NULL) { - /* - * Don't add identical matches. - * This can take a lot of time when finding many - * matches, check for CTRL-C now and then. - * Add all cscope tags, because they are all listed. - */ - if (use_cscope) - i = -1; - else - for (i = ga_match[mtt].ga_len; --i >= 0 && !got_int; ) { - mfp2 = ((struct match_found **) - (ga_match[mtt].ga_data))[i]; - if (mfp2->len == mfp->len - && memcmp(mfp2->match, mfp->match, - (size_t)mfp->len) == 0) - break; - fast_breakcheck(); - } - if (i < 0) { - ((struct match_found **)(ga_match[mtt].ga_data)) - [ga_match[mtt].ga_len++] = mfp; - ++match_count; + if (tagp.command + 2 < temp_end) { + len = (int)(temp_end - tagp.command - 2); + mfp = xmalloc(sizeof(char_u) + len + 1); + STRLCPY(mfp, tagp.command + 2, len + 1); } else - xfree(mfp); + mfp = NULL; + get_it_again = FALSE; + } else { + len = (int)(tagp.tagname_end - tagp.tagname); + mfp = xmalloc(sizeof(char_u) + len + 1); + STRLCPY(mfp, tagp.tagname, len + 1); + + /* if wanted, re-read line to get long form too */ + if (State & INSERT) + get_it_again = p_sft; } + } else { +#define TAG_SEP 0x01 + size_t tag_fname_len = STRLEN(tag_fname); + /* Save the tag in a buffer. + * Use 0x01 to separate fields (Can't use NUL, because the + * hash key is terminated by NUL). + * Emacs tag: + * other tag: + * without Emacs tags: + */ + len = (int)tag_fname_len + (int)STRLEN(lbuf) + 3; + mfp = xmalloc(sizeof(char_u) + len + 1); + p = mfp; + p[0] = mtt; + STRCPY(p + 1, tag_fname); +#ifdef BACKSLASH_IN_FILENAME + /* Ignore differences in slashes, avoid adding + * both path/file and path\file. */ + slash_adjust(p + 1); +#endif + p[tag_fname_len + 1] = TAG_SEP; + s = p + 1 + tag_fname_len + 1; + STRCPY(s, lbuf); + } + + if (mfp != NULL) { + hashitem_T *hi; + + // Don't add identical matches. + // Add all cscope tags, because they are all listed. + // "mfp" is used as a hash key, there is a NUL byte to end + // the part matters for comparing, more bytes may follow + // after it. E.g. help tags store the priority after the + // NUL. + if (use_cscope) + hash++; + else + hash = hash_hash(mfp); + hi = hash_lookup(&ht_match[mtt], (const char *)mfp, + STRLEN(mfp), hash); + if (HASHITEM_EMPTY(hi)) { + hash_add_item(&ht_match[mtt], hi, mfp, hash); + ++match_count; + } else + // duplicate tag, drop it + xfree(mfp); } } if (use_cscope && eof) @@ -1962,7 +1948,7 @@ findtag_end: xfree(tag_fname); /* - * Move the matches from the ga_match[] arrays into one list of + * Move the matches from the ht_match[] arrays into one list of * matches. When retval == FAIL, free the matches. */ if (retval == FAIL) @@ -1974,20 +1960,27 @@ findtag_end: matches = NULL; match_count = 0; for (mtt = 0; mtt < MT_COUNT; ++mtt) { - for (int i = 0; i < ga_match[mtt].ga_len; ++i) { - mfp = ((struct match_found **)(ga_match[mtt].ga_data))[i]; - if (matches == NULL) - xfree(mfp); - else { - /* To avoid allocating memory again we turn the struct - * match_found into a string. For help the priority was not - * included in the length. */ - memmove(mfp, mfp->match, - (size_t)(mfp->len + (help_only ? ML_HELP_LEN : 0))); - matches[match_count++] = (char_u *)mfp; + hashitem_T *hi; + long todo = (long)ht_match[mtt].ht_used; + + for (hi = ht_match[mtt].ht_array; todo > 0; hi++) { + if (!HASHITEM_EMPTY(hi)) { + mfp = hi->hi_key; + if (matches == NULL) { + xfree(mfp); + } else { + // now change the TAG_SEP back to NUL + for (p = mfp; *p != NUL; p++) { + if (*p == TAG_SEP) { + *p = NUL; + } + } + matches[match_count++] = (char_u *)mfp; + } + todo--; } } - ga_clear(&ga_match[mtt]); + hash_clear(&ht_match[mtt]); } *matchesp = matches; From e1af49b425f25024a3e6361ed576cf69296a1da4 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Sun, 19 Mar 2017 14:50:00 -0400 Subject: [PATCH 0026/1671] vim-patch:8.0.0195 Problem: Jumping to a tag that is a static item in the current file fails. (Kazunobu Kuriyama) Solution: Make sure the first byte of the tag key is not NUL. (Suggested by James McCoy, closes vim/vim#1387) https://github.com/vim/vim/commit/a9d23c20879d0dcb289a4db54b3c7df060f87c3c --- src/nvim/tag.c | 22 ++++++++++++---------- src/nvim/testdir/test_tagjump.vim | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/nvim/tag.c b/src/nvim/tag.c index c3c2634598..92a535ab91 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -82,10 +82,6 @@ typedef struct { #define MT_GL_CUR 1 /* global match in current file */ #define MT_GL_OTH 2 /* global match in other file */ #define MT_ST_OTH 3 /* static match in other file */ -#define MT_IC_ST_CUR 4 /* icase static match in current file */ -#define MT_IC_GL_CUR 5 /* icase global match in current file */ -#define MT_IC_GL_OTH 6 /* icase global match in other file */ -#define MT_IC_ST_OTH 7 /* icase static match in other file */ #define MT_IC_OFF 4 /* add for icase match */ #define MT_RE_OFF 8 /* add for regexp match */ #define MT_MASK 7 /* mask for printing priority */ @@ -1826,7 +1822,7 @@ parse_line: if (tagp.command + 2 < temp_end) { len = (int)(temp_end - tagp.command - 2); - mfp = xmalloc(sizeof(char_u) + len + 1); + mfp = xmalloc(len + 2); STRLCPY(mfp, tagp.command + 2, len + 1); } else mfp = NULL; @@ -1849,11 +1845,12 @@ parse_line: * Emacs tag: * other tag: * without Emacs tags: + * Here is the "mtt" value plus 1 to avoid NUL. */ len = (int)tag_fname_len + (int)STRLEN(lbuf) + 3; mfp = xmalloc(sizeof(char_u) + len + 1); p = mfp; - p[0] = mtt; + p[0] = mtt + 1; STRCPY(p + 1, tag_fname); #ifdef BACKSLASH_IN_FILENAME /* Ignore differences in slashes, avoid adding @@ -1969,10 +1966,15 @@ findtag_end: if (matches == NULL) { xfree(mfp); } else { - // now change the TAG_SEP back to NUL - for (p = mfp; *p != NUL; p++) { - if (*p == TAG_SEP) { - *p = NUL; + if (!name_only) { + // Change mtt back to zero-based. + *mfp = *mfp - 1; + + // change the TAG_SEP back to NUL + for (p = mfp + 1; *p != NUL; p++) { + if (*p == TAG_SEP) { + *p = NUL; + } } } matches[match_count++] = (char_u *)mfp; diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim index 678ad0ada8..54b5f4afd6 100644 --- a/src/nvim/testdir/test_tagjump.vim +++ b/src/nvim/testdir/test_tagjump.vim @@ -23,4 +23,22 @@ func Test_cancel_ptjump() quit endfunc +func Test_static_tagjump() + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "one\tXfile1\t/^one/;\"\tf\tfile:\tsignature:(void)", + \ "word\tXfile2\tcmd2"], + \ 'Xtags') + new Xfile1 + call setline(1, ['empty', 'one()', 'empty']) + write + tag one + call assert_equal(2, line('.')) + + set tags& + call delete('Xtags') + call delete('Xfile1') + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab From d3f15f1e6d299796bd552896c0ba01a7cca58618 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Sun, 19 Mar 2017 15:08:26 -0400 Subject: [PATCH 0027/1671] vim-patch:8.0.0223 Problem: Coverity gets confused by the flags passed to find_tags() and warnts for an uninitialized variable. Solution: Disallow using cscope and help tags at the same time. https://github.com/vim/vim/commit/fffbf308dd98d1129ba4914d921ab47dc6a6c9b1 --- src/nvim/tag.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 92a535ab91..1cbf789270 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -1057,6 +1057,7 @@ static void prepare_pats(pat_T *pats, int has_re) * TAG_REGEXP use "pat" as a regexp * TAG_NOIC don't always ignore case * TAG_KEEP_LANG keep language + * TAG_CSCOPE use cscope results for tags */ int find_tags ( @@ -1189,6 +1190,11 @@ find_tags ( */ if (help_only) /* want tags from help file */ curbuf->b_help = true; /* will be restored later */ + else if (use_cscope) { + // Make sure we don't mix help and cscope, confuses Coverity. + help_only = false; + curbuf->b_help = false; + } orgpat.len = (int)STRLEN(pat); if (curbuf->b_help) { From 097d04ac71499f5ba0126ab6f731d4f4af0a4e84 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Sun, 19 Mar 2017 15:10:27 -0400 Subject: [PATCH 0028/1671] vim-patch:8.0.0393 Problem: When the same tag appears more than once, the order is unpredictable. (Charles Campbell) Solution: Besides using a dict for finding duplicates, use a grow array for keeping the tags in sequence. https://github.com/vim/vim/commit/98e83b295628bc29bc67bcc1adb8ae75d01b8e07 --- src/nvim/tag.c | 55 ++++++++++++++++--------------- src/nvim/testdir/test_tagjump.vim | 24 ++++++++++++++ 2 files changed, 53 insertions(+), 26 deletions(-) diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 1cbf789270..0d5008b4ce 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -73,9 +73,10 @@ typedef struct { } pat_T; /* - * The matching tags are first stored in one of the ht_match[] hash tables. In + * The matching tags are first stored in one of the hash tables. In * which one depends on the priority of the match. - * At the end, the matches from ht_match[] are concatenated, to make a list + * ht_match[] is used to find duplicates, ga_match[] to keep them in sequence. + * At the end, the matches from ga_match[] are concatenated, to make a list * sorted on priority. */ #define MT_ST_CUR 0 /* static match in current file */ @@ -1120,7 +1121,8 @@ find_tags ( char_u *mfp; - hashtab_T ht_match[MT_COUNT]; + garray_T ga_match[MT_COUNT]; // stores matches in sequence + hashtab_T ht_match[MT_COUNT]; // stores matches by key hash_T hash = 0; int match_count = 0; /* number of matches found */ char_u **matches; @@ -1180,8 +1182,10 @@ find_tags ( */ lbuf = xmalloc(lbuf_size); tag_fname = xmalloc(MAXPATHL + 1); - for (mtt = 0; mtt < MT_COUNT; ++mtt) + for (mtt = 0; mtt < MT_COUNT; ++mtt) { + ga_init(&ga_match[mtt], sizeof(char_u *), 100); hash_init(&ht_match[mtt]); + } STRCPY(tag_fname, "from cscope"); /* for error messages */ @@ -1752,7 +1756,7 @@ parse_line: } /* - * If a match is found, add it to ht_match[]. + * If a match is found, add it to ht_match[] and ga_match[]. */ if (match) { int len = 0; @@ -1792,7 +1796,7 @@ parse_line: } /* - * Add the found match in ht_match[mtt]. + * Add the found match in ht_match[mtt] and ga_match[mtt]. * Store the info we need later, which depends on the kind of * tags we are dealing with. */ @@ -1885,6 +1889,9 @@ parse_line: STRLEN(mfp), hash); if (HASHITEM_EMPTY(hi)) { hash_add_item(&ht_match[mtt], hi, mfp, hash); + ga_grow(&ga_match[mtt], 1); + ((char_u **)(ga_match[mtt].ga_data)) + [ga_match[mtt].ga_len++] = mfp; ++match_count; } else // duplicate tag, drop it @@ -1951,7 +1958,7 @@ findtag_end: xfree(tag_fname); /* - * Move the matches from the ht_match[] arrays into one list of + * Move the matches from the ga_match[] arrays into one list of * matches. When retval == FAIL, free the matches. */ if (retval == FAIL) @@ -1963,31 +1970,27 @@ findtag_end: matches = NULL; match_count = 0; for (mtt = 0; mtt < MT_COUNT; ++mtt) { - hashitem_T *hi; - long todo = (long)ht_match[mtt].ht_used; + for (i = 0; i < ga_match[mtt].ga_len; i++) { + mfp = ((char_u **)(ga_match[mtt].ga_data))[i]; + if (matches == NULL) { + xfree(mfp); + } else { + if (!name_only) { + // Change mtt back to zero-based. + *mfp = *mfp - 1; - for (hi = ht_match[mtt].ht_array; todo > 0; hi++) { - if (!HASHITEM_EMPTY(hi)) { - mfp = hi->hi_key; - if (matches == NULL) { - xfree(mfp); - } else { - if (!name_only) { - // Change mtt back to zero-based. - *mfp = *mfp - 1; - - // change the TAG_SEP back to NUL - for (p = mfp + 1; *p != NUL; p++) { - if (*p == TAG_SEP) { - *p = NUL; - } + // change the TAG_SEP back to NUL + for (p = mfp + 1; *p != NUL; p++) { + if (*p == TAG_SEP) { + *p = NUL; } } - matches[match_count++] = (char_u *)mfp; } - todo--; + matches[match_count++] = (char_u *)mfp; } } + + ga_clear(&ga_match[mtt]); hash_clear(&ht_match[mtt]); } diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim index 54b5f4afd6..2044c23f79 100644 --- a/src/nvim/testdir/test_tagjump.vim +++ b/src/nvim/testdir/test_tagjump.vim @@ -35,10 +35,34 @@ func Test_static_tagjump() tag one call assert_equal(2, line('.')) + bwipe! set tags& call delete('Xtags') call delete('Xfile1') +endfunc + +func Test_duplicate_tagjump() + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "thesame\tXfile1\t1;\"\td\tfile:", + \ "thesame\tXfile1\t2;\"\td\tfile:", + \ "thesame\tXfile1\t3;\"\td\tfile:", + \ ], + \ 'Xtags') + new Xfile1 + call setline(1, ['thesame one', 'thesame two', 'thesame three']) + write + tag thesame + call assert_equal(1, line('.')) + tnext + call assert_equal(2, line('.')) + tnext + call assert_equal(3, line('.')) + bwipe! + set tags& + call delete('Xtags') + call delete('Xfile1') endfunc " vim: shiftwidth=2 sts=2 expandtab From a56615214dbbc4b2f9cf38f65f95172a2ef06289 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Sun, 19 Mar 2017 21:00:45 -0400 Subject: [PATCH 0029/1671] lint --- src/nvim/tag.c | 118 ++++++++++++++++++++++++------------------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 0d5008b4ce..7bcaff662c 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -72,20 +72,18 @@ typedef struct { regmatch_T regmatch; /* regexp program, may be NULL */ } pat_T; -/* - * The matching tags are first stored in one of the hash tables. In - * which one depends on the priority of the match. - * ht_match[] is used to find duplicates, ga_match[] to keep them in sequence. - * At the end, the matches from ga_match[] are concatenated, to make a list - * sorted on priority. - */ -#define MT_ST_CUR 0 /* static match in current file */ -#define MT_GL_CUR 1 /* global match in current file */ -#define MT_GL_OTH 2 /* global match in other file */ -#define MT_ST_OTH 3 /* static match in other file */ -#define MT_IC_OFF 4 /* add for icase match */ -#define MT_RE_OFF 8 /* add for regexp match */ -#define MT_MASK 7 /* mask for printing priority */ +// The matching tags are first stored in one of the hash tables. In +// which one depends on the priority of the match. +// ht_match[] is used to find duplicates, ga_match[] to keep them in sequence. +// At the end, the matches from ga_match[] are concatenated, to make a list +// sorted on priority. +#define MT_ST_CUR 0 // static match in current file +#define MT_GL_CUR 1 // global match in current file +#define MT_GL_OTH 2 // global match in other file +#define MT_ST_OTH 3 // static match in other file +#define MT_IC_OFF 4 // add for icase match +#define MT_RE_OFF 8 // add for regexp match +#define MT_MASK 7 // mask for printing priority #define MT_COUNT 16 static char *mt_names[MT_COUNT/2] = @@ -1124,14 +1122,14 @@ find_tags ( garray_T ga_match[MT_COUNT]; // stores matches in sequence hashtab_T ht_match[MT_COUNT]; // stores matches by key hash_T hash = 0; - int match_count = 0; /* number of matches found */ + int match_count = 0; // number of matches found char_u **matches; int mtt; int help_save; int help_pri = 0; - char_u *help_lang_find = NULL; /* lang to be found */ - char_u help_lang[3]; /* lang of current tags file */ - char_u *saved_pat = NULL; /* copy of pat[] */ + char_u *help_lang_find = NULL; // lang to be found + char_u help_lang[3]; // lang of current tags file + char_u *saved_pat = NULL; // copy of pat[] bool is_txt = false; pat_T orgpat; /* holds unconverted pattern info */ @@ -1182,7 +1180,7 @@ find_tags ( */ lbuf = xmalloc(lbuf_size); tag_fname = xmalloc(MAXPATHL + 1); - for (mtt = 0; mtt < MT_COUNT; ++mtt) { + for (mtt = 0; mtt < MT_COUNT; mtt++) { ga_init(&ga_match[mtt], sizeof(char_u *), 100); hash_init(&ht_match[mtt]); } @@ -1192,9 +1190,9 @@ find_tags ( /* * Initialize a few variables */ - if (help_only) /* want tags from help file */ - curbuf->b_help = true; /* will be restored later */ - else if (use_cscope) { + if (help_only) { // want tags from help file + curbuf->b_help = true; // will be restored later + } else if (use_cscope) { // Make sure we don't mix help and cscope, confuses Coverity. help_only = false; curbuf->b_help = false; @@ -1264,13 +1262,14 @@ find_tags ( if (is_txt) { STRCPY(help_lang, "en"); } else { - /* Prefer help tags according to 'helplang'. Put the - * two-letter language name in help_lang[]. */ + // Prefer help tags according to 'helplang'. Put the + // two-letter language name in help_lang[]. i = (int)STRLEN(tag_fname); - if (i > 3 && tag_fname[i - 3] == '-') + if (i > 3 && tag_fname[i - 3] == '-') { STRCPY(help_lang, tag_fname + i - 2); - else + } else { STRCPY(help_lang, "en"); + } } /* When searching for a specific language skip tags files @@ -1755,9 +1754,7 @@ parse_line: match_re = TRUE; } - /* - * If a match is found, add it to ht_match[] and ga_match[]. - */ + // If a match is found, add it to ht_match[] and ga_match[]. if (match) { int len = 0; @@ -1795,11 +1792,9 @@ parse_line: mtt += MT_RE_OFF; } - /* - * Add the found match in ht_match[mtt] and ga_match[mtt]. - * Store the info we need later, which depends on the kind of - * tags we are dealing with. - */ + // Add the found match in ht_match[mtt] and ga_match[mtt]. + // Store the info we need later, which depends on the kind of + // tags we are dealing with. if (help_only) { # define ML_EXTRA 3 // Append the help-heuristic number after the tagname, for @@ -1814,57 +1809,60 @@ parse_line: STRCPY(p, tagp.tagname); p[len] = '@'; STRCPY(p + len + 1, help_lang); - sprintf((char *)p + len + 1 + ML_EXTRA, "%06d", - help_heuristic(tagp.tagname, - match_re ? matchoff : 0, !match_no_ic) - + help_pri); + snprintf((char *)p + len + 1 + ML_EXTRA, 10, "%06d", + help_heuristic(tagp.tagname, + match_re ? matchoff : 0, !match_no_ic) + + help_pri); *tagp.tagname_end = TAB; } else if (name_only) { if (get_it_again) { char_u *temp_end = tagp.command; - if (*temp_end == '/') + if (*temp_end == '/') { while (*temp_end && *temp_end != '\r' && *temp_end != '\n' - && *temp_end != '$') + && *temp_end != '$') { temp_end++; + } + } if (tagp.command + 2 < temp_end) { len = (int)(temp_end - tagp.command - 2); mfp = xmalloc(len + 2); STRLCPY(mfp, tagp.command + 2, len + 1); - } else + } else { mfp = NULL; - get_it_again = FALSE; + } + get_it_again = false; } else { len = (int)(tagp.tagname_end - tagp.tagname); mfp = xmalloc(sizeof(char_u) + len + 1); STRLCPY(mfp, tagp.tagname, len + 1); - /* if wanted, re-read line to get long form too */ - if (State & INSERT) + // if wanted, re-read line to get long form too + if (State & INSERT) { get_it_again = p_sft; + } } } else { #define TAG_SEP 0x01 size_t tag_fname_len = STRLEN(tag_fname); - /* Save the tag in a buffer. - * Use 0x01 to separate fields (Can't use NUL, because the - * hash key is terminated by NUL). - * Emacs tag: - * other tag: - * without Emacs tags: - * Here is the "mtt" value plus 1 to avoid NUL. - */ + // Save the tag in a buffer. + // Use 0x01 to separate fields (Can't use NUL, because the + // hash key is terminated by NUL). + // Emacs tag: + // other tag: + // without Emacs tags: + // Here is the "mtt" value plus 1 to avoid NUL. len = (int)tag_fname_len + (int)STRLEN(lbuf) + 3; mfp = xmalloc(sizeof(char_u) + len + 1); p = mfp; p[0] = mtt + 1; STRCPY(p + 1, tag_fname); #ifdef BACKSLASH_IN_FILENAME - /* Ignore differences in slashes, avoid adding - * both path/file and path\file. */ + // Ignore differences in slashes, avoid adding + // both path/file and path\file. slash_adjust(p + 1); #endif p[tag_fname_len + 1] = TAG_SEP; @@ -1881,10 +1879,11 @@ parse_line: // the part matters for comparing, more bytes may follow // after it. E.g. help tags store the priority after the // NUL. - if (use_cscope) + if (use_cscope) { hash++; - else + } else { hash = hash_hash(mfp); + } hi = hash_lookup(&ht_match[mtt], (const char *)mfp, STRLEN(mfp), hash); if (HASHITEM_EMPTY(hi)) { @@ -1892,10 +1891,11 @@ parse_line: ga_grow(&ga_match[mtt], 1); ((char_u **)(ga_match[mtt].ga_data)) [ga_match[mtt].ga_len++] = mfp; - ++match_count; - } else + match_count++; + } else { // duplicate tag, drop it xfree(mfp); + } } } if (use_cscope && eof) @@ -1969,7 +1969,7 @@ findtag_end: else matches = NULL; match_count = 0; - for (mtt = 0; mtt < MT_COUNT; ++mtt) { + for (mtt = 0; mtt < MT_COUNT; mtt++) { for (i = 0; i < ga_match[mtt].ga_len; i++) { mfp = ((char_u **)(ga_match[mtt].ga_data))[i]; if (matches == NULL) { From ae16e4fa1363b531972628c14bb225b4cd45dbc6 Mon Sep 17 00:00:00 2001 From: Drew Neil Date: Mon, 20 Mar 2017 12:23:19 +0000 Subject: [PATCH 0030/1671] doc: remove "only available when compiled with +timers" (#6321) --- runtime/doc/eval.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index d94b3b7a2e..e1a8ba079a 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -7538,7 +7538,6 @@ timer_start({time}, {callback} [, {options}]) \ {'repeat': 3}) < This will invoke MyHandler() three times at 500 msec intervals. - {only available when compiled with the |+timers| feature} timer_stop({timer}) *timer_stop()* Stop a timer. The timer callback will no longer be invoked. From 8924e75f3477b20f0ef4e3df64b56bf496b197ae Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Thu, 2 Mar 2017 23:04:57 +0100 Subject: [PATCH 0031/1671] vim-patch:7.4.2170 Problem: Cannot get information about timers. Solution: Add timer_info(). https://github.com/vim/vim/commit/8e97bd74b5377753597e3d98e7123d8985c7fffd --- runtime/doc/eval.txt | 31 +++++++++++++++++++++---- src/nvim/eval.c | 55 ++++++++++++++++++++++++++++++++++++++++++++ src/nvim/eval.lua | 1 + src/nvim/version.c | 2 +- 4 files changed, 83 insertions(+), 6 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index e1a8ba079a..37ce0e1b5f 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2297,6 +2297,7 @@ tan({expr}) Float tangent of {expr} tanh({expr}) Float hyperbolic tangent of {expr} tempname() String name for a temporary file test_garbagecollect_now() none free memory right now for testing +timer_info([{id}]) List information about timers timer_start({time}, {callback} [, {options}]) Number create a timer timer_stop({timer}) none stop a timer @@ -3198,8 +3199,12 @@ exepath({expr}) *exepath()* *exists()* exists({expr}) The result is a Number, which is |TRUE| if {expr} is - defined, zero otherwise. The {expr} argument is a string, - which contains one of these: + defined, zero otherwise. + + For checking for a supported feature use |has()|. + For checking if a file exists use |filereadable()|. + + The {expr} argument is a string, which contains one of these: &option-name Vim option (only checks if it exists, not if it really works) +option-name Vim option that works. @@ -3247,7 +3252,6 @@ exists({expr}) The result is a Number, which is |TRUE| if {expr} is event and pattern. ##event autocommand for this event is supported. - For checking for a supported feature use |has()|. Examples: > exists("&mouse") @@ -5329,7 +5333,8 @@ matchadd({group}, {pattern}[, {priority}[, {id} [, {dict}]]]) available from |getmatches()|. All matches can be deleted in one operation by |clearmatches()|. -matchaddpos({group}, {pos}[, {priority}[, {id}[, {dict}]]]) *matchaddpos()* + *matchaddpos()* +matchaddpos({group}, {pos}[, {priority}[, {id}[, {dict}]]]) Same as |matchadd()|, but requires a list of positions {pos} instead of a pattern. This command is faster than |matchadd()| because it does not require to handle regular expressions and @@ -7513,6 +7518,22 @@ tanh({expr}) *tanh()* < -0.761594 + *timer_info()* +timer_info([{id}]) + Return a list with information about timers. + When {id} is given only information about this timer is + returned. When timer {id} does not exist an empty list is + returned. + When {id} is omitted information about all timers is returned. + + For each timer the information is stored in a Dictionary with + these items: + "id" the timer ID + "time" time the timer was started with + "repeat" number of times the timer will still fire; + -1 means forever + "callback" the callback + *timer_start()* timer_start({time}, {callback} [, {options}]) Create a timer and return the timer ID. @@ -7542,7 +7563,7 @@ timer_start({time}, {callback} [, {options}]) timer_stop({timer}) *timer_stop()* Stop a timer. The timer callback will no longer be invoked. {timer} is an ID returned by timer_start(), thus it must be a - Number. + Number. If {timer} does not exist there is no error. tolower({expr}) *tolower()* The result is a copy of the String given, with all uppercase diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d4daffb469..537e9e696f 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -17931,6 +17931,61 @@ static bool set_ref_in_callback(Callback *callback, int copyID, return false; } +static void add_timer_info(typval_T *rettv, timer_T *timer) +{ + list_T *list = rettv->vval.v_list; + dict_T *dict = dict_alloc(); + + list_append_dict(list, dict); + dict_add_nr_str(dict, "id", (long)timer->timer_id, NULL); + dict_add_nr_str(dict, "time", timer->timeout, NULL); + + dict_add_nr_str(dict, "repeat", + (long)(timer->repeat_count < 0 ? -1 : timer->repeat_count), + NULL); + + dictitem_T *di = dictitem_alloc((char_u *)"callback"); + if (dict_add(dict, di) == FAIL) { + xfree(di); + return; + } + + if (timer->callback.type == kCallbackPartial) { + di->di_tv.v_type = VAR_PARTIAL; + di->di_tv.vval.v_partial = timer->callback.data.partial; + timer->callback.data.partial->pt_refcount++; + } else if (timer->callback.type == kCallbackFuncref) { + di->di_tv.v_type = VAR_FUNC; + di->di_tv.vval.v_string = vim_strsave(timer->callback.data.funcref); + } + di->di_tv.v_lock = 0; +} + +static void add_timer_info_all(typval_T *rettv) +{ + timer_T *timer; + map_foreach_value(timers, timer, { + add_timer_info(rettv, timer); + }) +} + +/// "timer_info([timer])" function +static void f_timer_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + rettv_list_alloc(rettv); + if (argvars[0].v_type != VAR_UNKNOWN) { + if (argvars[0].v_type != VAR_NUMBER) { + EMSG(_(e_number_exp)); + } else { + timer_T *timer = pmap_get(uint64_t)(timers, get_tv_number(&argvars[0])); + if (timer != NULL) { + add_timer_info(rettv, timer); + } + } + } else { + add_timer_info_all(rettv); + } +} /// "timer_start(timeout, callback, opts)" function static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index e3c5981b32..e923fee316 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -306,6 +306,7 @@ return { tempname={}, termopen={args={1, 2}}, test_garbagecollect_now={}, + timer_info={args={0,1}}, timer_start={args={2,3}}, timer_stop={args=1}, tolower={args=1}, diff --git a/src/nvim/version.c b/src/nvim/version.c index 8fd0fce329..b283059c7a 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -271,7 +271,7 @@ static int included_patches[] = { // 2173, // 2172, // 2171, - // 2170, + 2170, // 2169, // 2168 NA // 2167 NA From 5b8ce2feed6b528bae4bceba6f234be000949971 Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Sun, 5 Mar 2017 16:25:40 +0100 Subject: [PATCH 0032/1671] vim-patch:7.4.2180 Problem: There is no easy way to stop all timers. There is no way to temporary pause a timer. Solution: Add timer_stopall() and timer_pause(). https://github.com/vim/vim/commit/b73598e2f022a22fec512ea681c70d2775e8fd87 --- runtime/doc/eval.txt | 22 +++++++- src/nvim/eval.c | 34 +++++++++++-- src/nvim/eval.lua | 2 + src/nvim/testdir/shared.vim | 17 +++++++ src/nvim/testdir/test_timers.vim | 86 ++++++++++++++++++++++++++------ src/nvim/version.c | 2 +- 6 files changed, 141 insertions(+), 22 deletions(-) create mode 100644 src/nvim/testdir/shared.vim diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 37ce0e1b5f..8d3bd6aeb7 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1947,7 +1947,7 @@ assert_exception( {error} [, {msg}]) none assert {error} is in v:exception assert_fails( {cmd} [, {error}]) none assert {cmd} fails assert_false({actual} [, {msg}]) none assert {actual} is false assert_inrange({lower}, {upper}, {actual} [, {msg}]) - none assert {actual} is inside the range + none assert {actual} is inside the range assert_match( {pat}, {text} [, {msg}]) none assert {pat} matches {text} assert_notequal( {exp}, {act} [, {msg}]) none assert {exp} is not equal {act} assert_notmatch( {pat}, {text} [, {msg}]) none assert {pat} not matches {text} @@ -2298,9 +2298,11 @@ tanh({expr}) Float hyperbolic tangent of {expr} tempname() String name for a temporary file test_garbagecollect_now() none free memory right now for testing timer_info([{id}]) List information about timers +timer_pause({id}, {pause}) none pause or unpause a timer timer_start({time}, {callback} [, {options}]) Number create a timer timer_stop({timer}) none stop a timer +timer_stopall() none stop all timers tolower({expr}) String the String {expr} switched to lowercase toupper({expr}) String the String {expr} switched to uppercase tr({src}, {fromstr}, {tostr}) String translate chars of {src} in {fromstr} @@ -7534,6 +7536,19 @@ timer_info([{id}]) -1 means forever "callback" the callback +timer_pause({timer}, {paused}) *timer_pause()* + Pause or unpause a timer. A paused timer does not invoke its + callback, while the time it would is not changed. Unpausing a + timer may cause the callback to be invoked almost immediately + if enough time has passed. + + Pausing a timer is useful to avoid the callback to be called + for a short time. + + If {paused} evaluates to a non-zero Number or a non-empty + String, then the timer is paused, otherwise it is unpaused. + See |non-zero-arg|. + *timer_start()* timer_start({time}, {callback} [, {options}]) Create a timer and return the timer ID. @@ -7565,6 +7580,11 @@ timer_stop({timer}) *timer_stop()* {timer} is an ID returned by timer_start(), thus it must be a Number. If {timer} does not exist there is no error. +timer_stopall() *timer_stopall()* + Stop all timers. The timer callbacks will no longer be + invoked. Useful if some timers is misbehaving. If there are + no timers there is no error. + tolower({expr}) *tolower()* The result is a copy of the String given, with all uppercase characters turned into lowercase (just like applying |gu| to diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 537e9e696f..062999e73b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -446,6 +446,7 @@ typedef struct { int refcount; long timeout; bool stopped; + bool paused; Callback callback; } timer_T; @@ -17939,6 +17940,7 @@ static void add_timer_info(typval_T *rettv, timer_T *timer) list_append_dict(list, dict); dict_add_nr_str(dict, "id", (long)timer->timer_id, NULL); dict_add_nr_str(dict, "time", timer->timeout, NULL); + dict_add_nr_str(dict, "paused", (long)timer->paused, NULL); dict_add_nr_str(dict, "repeat", (long)(timer->repeat_count < 0 ? -1 : timer->repeat_count), @@ -17965,7 +17967,9 @@ static void add_timer_info_all(typval_T *rettv) { timer_T *timer; map_foreach_value(timers, timer, { - add_timer_info(rettv, timer); + if (!timer->stopped) { + add_timer_info(rettv, timer); + } }) } @@ -17978,7 +17982,7 @@ static void f_timer_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_number_exp)); } else { timer_T *timer = pmap_get(uint64_t)(timers, get_tv_number(&argvars[0])); - if (timer != NULL) { + if (timer != NULL && !timer->stopped) { add_timer_info(rettv, timer); } } @@ -17987,6 +17991,20 @@ static void f_timer_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } +/// "timer_pause(timer, paused)" function +static void f_timer_pause(typval_T *argvars, typval_T *unused, FunPtr fptr) +{ + if (argvars[0].v_type != VAR_NUMBER) { + EMSG(_(e_number_exp)); + } else { + int paused = (bool)get_tv_number(&argvars[1]); + timer_T *timer = pmap_get(uint64_t)(timers, get_tv_number(&argvars[0])); + if (timer != NULL) { + timer->paused = paused; + } + } +} + /// "timer_start(timeout, callback, opts)" function static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -18019,6 +18037,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) timer = xmalloc(sizeof *timer); timer->refcount = 1; timer->stopped = false; + timer->paused = false; timer->repeat_count = repeat; timer->timeout = timeout; timer->timer_id = last_timer_id++; @@ -18028,8 +18047,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) timer->tw.events = multiqueue_new_child(main_loop.events); // if main loop is blocked, don't queue up multiple events timer->tw.blockable = true; - time_watcher_start(&timer->tw, timer_due_cb, timeout, - timeout * (repeat != 1)); + time_watcher_start(&timer->tw, timer_due_cb, timeout, timeout); pmap_put(uint64_t)(timers, timer->timer_id, timer); rettv->vval.v_number = timer->timer_id; @@ -18053,13 +18071,19 @@ static void f_timer_stop(typval_T *argvars, typval_T *rettv, FunPtr fptr) timer_stop(timer); } +static void f_timer_stopall(typval_T *argvars, typval_T *unused, FunPtr fptr) +{ + timer_teardown(); +} + // invoked on the main loop static void timer_due_cb(TimeWatcher *tw, void *data) { timer_T *timer = (timer_T *)data; - if (timer->stopped) { + if (timer->stopped || timer->paused) { return; } + timer->refcount++; // if repeat was negative repeat forever if (timer->repeat_count >= 0 && --timer->repeat_count == 0) { diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index e923fee316..d30d34135b 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -307,8 +307,10 @@ return { termopen={args={1, 2}}, test_garbagecollect_now={}, timer_info={args={0,1}}, + timer_pause={args=2}, timer_start={args={2,3}}, timer_stop={args=1}, + timer_stopall={args=0}, tolower={args=1}, toupper={args=1}, tr={args=3}, diff --git a/src/nvim/testdir/shared.vim b/src/nvim/testdir/shared.vim new file mode 100644 index 0000000000..9573f5a9c3 --- /dev/null +++ b/src/nvim/testdir/shared.vim @@ -0,0 +1,17 @@ +" Functions shared by several tests. + +" Wait for up to a second for "expr" to become true. +" Return time slept in milliseconds. +func WaitFor(expr) + let slept = 0 + for i in range(100) + try + if eval(a:expr) + return slept + endif + catch + endtry + let slept += 10 + sleep 10m + endfor +endfunc diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index 56f9feef66..b03295bd01 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -4,8 +4,10 @@ if !has('timers') finish endif +source shared.vim + func MyHandler(timer) - let s:val += 1 + let g:val += 1 endfunc func MyHandlerWithLists(lists, timer) @@ -13,44 +15,98 @@ func MyHandlerWithLists(lists, timer) endfunc func Test_oneshot() - let s:val = 0 + let g:val = 0 let timer = timer_start(50, 'MyHandler') - sleep 200m - call assert_equal(1, s:val) + let slept = WaitFor('g:val == 1') + call assert_equal(1, g:val) + call assert_inrange(30, 100, slept) endfunc func Test_repeat_three() - let s:val = 0 + let g:val = 0 let timer = timer_start(50, 'MyHandler', {'repeat': 3}) - sleep 500m - call assert_equal(3, s:val) + let slept = WaitFor('g:val == 3') + call assert_equal(3, g:val) + call assert_inrange(100, 250, slept) endfunc func Test_repeat_many() - let s:val = 0 + let g:val = 0 let timer = timer_start(50, 'MyHandler', {'repeat': -1}) sleep 200m call timer_stop(timer) - call assert_true(s:val > 1) - call assert_true(s:val < 5) + call assert_inrange(2, 4, g:val) endfunc func Test_with_partial_callback() - let s:val = 0 + let g:val = 0 let s:meow = {} function s:meow.bite(...) - let s:val += 1 + let g:val += 1 endfunction call timer_start(50, s:meow.bite) - sleep 200m - call assert_equal(1, s:val) + let slept = WaitFor('g:val == 1') + call assert_equal(1, g:val) + call assert_inrange(30, 100, slept) endfunc func Test_retain_partial() - call timer_start(100, function('MyHandlerWithLists', [['a']])) + call timer_start(50, function('MyHandlerWithLists', [['a']])) call garbagecollect() + sleep 100m +endfunc + +func Test_info() + let id = timer_start(1000, 'MyHandler') + let info = timer_info(id) + call assert_equal(id, info[0]['id']) + call assert_equal(1000, info[0]['time']) + call assert_equal("function('MyHandler')", string(info[0]['callback'])) + + let found = 0 + for info in timer_info() + if info['id'] == id + let found += 1 + endif + endfor + call assert_equal(1, found) + + call timer_stop(id) + call assert_equal([], timer_info(id)) +endfunc + +func Test_stopall() + let id1 = timer_start(1000, 'MyHandler') + let id2 = timer_start(2000, 'MyHandler') + let info = timer_info() + call assert_equal(2, len(info)) + + call timer_stopall() + let info = timer_info() + call assert_equal(0, len(info)) +endfunc + +func Test_paused() + let g:val = 0 + + let id = timer_start(50, 'MyHandler') + let info = timer_info(id) + call assert_equal(0, info[0]['paused']) + + call timer_pause(id, 1) + let info = timer_info(id) + call assert_equal(1, info[0]['paused']) sleep 200m + call assert_equal(0, g:val) + + call timer_pause(id, 0) + let info = timer_info(id) + call assert_equal(0, info[0]['paused']) + + let slept = WaitFor('g:val == 1') + call assert_equal(1, g:val) + call assert_inrange(0, 10, slept) endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/version.c b/src/nvim/version.c index b283059c7a..4da970d43b 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -261,7 +261,7 @@ static int included_patches[] = { 2183, // 2182 NA // 2181, - // 2180, + 2180, // 2179, // 2178, // 2177, From 420a9955fa969ee0460f41798c60c1374476fd08 Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Mon, 6 Mar 2017 07:15:19 +0100 Subject: [PATCH 0033/1671] version.c: Mark 7.4.2171 and 7.4.2181 as NA. --- src/nvim/version.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nvim/version.c b/src/nvim/version.c index 4da970d43b..0bf0f10b56 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -260,7 +260,7 @@ static int included_patches[] = { // 2184, 2183, // 2182 NA - // 2181, + // 2181 NA 2180, // 2179, // 2178, @@ -270,7 +270,7 @@ static int included_patches[] = { 2174, // 2173, // 2172, - // 2171, + // 2171 NA 2170, // 2169, // 2168 NA From 5c2f1e29e3dfdfab8c2a9b31962d9cc12c171e46 Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Mon, 20 Mar 2017 20:52:54 +0100 Subject: [PATCH 0034/1671] vim-patch:7.4.2240 Problem: Tests using the sleep time can be flaky. Solution: Use reltime() if available. (Partly by Shane Harper) https://github.com/vim/vim/commit/f267f8bdf777073e392ada5b31d837c7b6090eb4 --- src/nvim/testdir/shared.vim | 21 ++++++++++++++++----- src/nvim/testdir/test_timers.vim | 24 ++++++++++++++++++++---- src/nvim/version.c | 2 +- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/nvim/testdir/shared.vim b/src/nvim/testdir/shared.vim index 9573f5a9c3..1138ceba4d 100644 --- a/src/nvim/testdir/shared.vim +++ b/src/nvim/testdir/shared.vim @@ -1,17 +1,28 @@ " Functions shared by several tests. -" Wait for up to a second for "expr" to become true. -" Return time slept in milliseconds. +" Return time slept in milliseconds. With the +reltime feature this can be +" more than the actual waiting time. Without +reltime it can also be less. func WaitFor(expr) - let slept = 0 + " using reltime() is more accurate, but not always available + if has('reltime') + let start = reltime() + else + let slept = 0 + endif for i in range(100) try if eval(a:expr) - return slept + if has('reltime') + return float2nr(reltimefloat(reltime(start)) * 1000) + endif + return slept endif catch endtry - let slept += 10 + if !has('reltime') + let slept += 10 + endif sleep 10m endfor + return 1000 endfunc diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index b03295bd01..2a5fa5c662 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -19,7 +19,11 @@ func Test_oneshot() let timer = timer_start(50, 'MyHandler') let slept = WaitFor('g:val == 1') call assert_equal(1, g:val) - call assert_inrange(30, 100, slept) + if has('reltime') + call assert_inrange(50, 100, slept) + else + call assert_inrange(20, 100, slept) + endif endfunc func Test_repeat_three() @@ -27,7 +31,11 @@ func Test_repeat_three() let timer = timer_start(50, 'MyHandler', {'repeat': 3}) let slept = WaitFor('g:val == 3') call assert_equal(3, g:val) - call assert_inrange(100, 250, slept) + if has('reltime') + call assert_inrange(150, 200, slept) + else + call assert_inrange(80, 200, slept) + endif endfunc func Test_repeat_many() @@ -48,7 +56,11 @@ func Test_with_partial_callback() call timer_start(50, s:meow.bite) let slept = WaitFor('g:val == 1') call assert_equal(1, g:val) - call assert_inrange(30, 100, slept) + if has('reltime') + call assert_inrange(50, 100, slept) + else + call assert_inrange(20, 100, slept) + endif endfunc func Test_retain_partial() @@ -106,7 +118,11 @@ func Test_paused() let slept = WaitFor('g:val == 1') call assert_equal(1, g:val) - call assert_inrange(0, 10, slept) + if has('reltime') + call assert_inrange(0, 30, slept) + else + call assert_inrange(0, 10, slept) + endif endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/version.c b/src/nvim/version.c index 0bf0f10b56..ef6c9d1454 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -201,7 +201,7 @@ static int included_patches[] = { // 2243 NA // 2242, // 2241, - // 2240, + 2240, // 2239, // 2238 NA 2237, From 3558f89d22faad613e0049592d2187ad35f5bec8 Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Mon, 20 Mar 2017 21:11:24 +0100 Subject: [PATCH 0035/1671] vim-patch:7.4.2241 Problem: Timer test sometimes fails. Solution: Increase the maximum time for repeating timer. https://github.com/vim/vim/commit/973365dcc40a41e6b72ece56f15cebfee69b1329 --- src/nvim/testdir/test_timers.vim | 2 +- src/nvim/version.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index 2a5fa5c662..8829b5de59 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -32,7 +32,7 @@ func Test_repeat_three() let slept = WaitFor('g:val == 3') call assert_equal(3, g:val) if has('reltime') - call assert_inrange(150, 200, slept) + call assert_inrange(150, 250, slept) else call assert_inrange(80, 200, slept) endif diff --git a/src/nvim/version.c b/src/nvim/version.c index ef6c9d1454..b7516e443a 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -200,7 +200,7 @@ static int included_patches[] = { // 2244, // 2243 NA // 2242, - // 2241, + 2241, 2240, // 2239, // 2238 NA From 4f69a8fb8854698adb2de8956ad0d86ff35a6f68 Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Mon, 20 Mar 2017 21:12:31 +0100 Subject: [PATCH 0036/1671] vim-patch:7.4.2242 Problem: Timer test sometimes fails. Solution: Increase the maximum time for callback timer test. https://github.com/vim/vim/commit/17f1347b867cbcc0ce380bf9a2466b4c31896f04 --- src/nvim/testdir/test_timers.vim | 2 +- src/nvim/version.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index 8829b5de59..db10f351ae 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -57,7 +57,7 @@ func Test_with_partial_callback() let slept = WaitFor('g:val == 1') call assert_equal(1, g:val) if has('reltime') - call assert_inrange(50, 100, slept) + call assert_inrange(50, 130, slept) else call assert_inrange(20, 100, slept) endif diff --git a/src/nvim/version.c b/src/nvim/version.c index b7516e443a..91c37658aa 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -199,7 +199,7 @@ static int included_patches[] = { // 2245, // 2244, // 2243 NA - // 2242, + 2242, 2241, 2240, // 2239, From b4cb5fa610903492f89a70d0ae2d4565ddcd1228 Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Tue, 7 Mar 2017 19:20:07 +0100 Subject: [PATCH 0037/1671] vim-patch:7.4.2266 Problem: printf() test fails on Windows. "-inf" is not used. Solution: Check for Windows-specific values for "nan". Add sign to "inf" when appropriate. https://github.com/vim/vim/commit/9992237a3e791fbc0c1ebf743ece1b75e1488410 --- src/nvim/strings.c | 22 ++++++++++++++++++++-- src/nvim/testdir/test_expr.vim | 8 +++----- src/nvim/version.c | 2 +- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/nvim/strings.c b/src/nvim/strings.c index d03970108b..47ee311bd4 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -1209,9 +1209,10 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, } if (fmt_spec == 'f' && abs_f > 1.0e307) { + const char inf_str[] = f < 0 ? "-inf" : "inf"; // avoid a buffer overflow - memmove(tmp, "inf", sizeof("inf")); - str_arg_l = sizeof("inf") - 1; + memmove(tmp, inf_str, sizeof(inf_str) - 1); + str_arg_l = sizeof(inf_str) - 1; } else { format[0] = '%'; int l = 1; @@ -1234,6 +1235,23 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, str_arg_l = (size_t)snprintf(tmp, sizeof(tmp), format, f); assert(str_arg_l < sizeof(tmp)); + // Be consistent: Change "1.#IND" to "nan" and + // "1.#INF" to "inf". + char *s = *tmp == '-' ? tmp + 1 : tmp; + if (STRNCMP(s, "1.#INF", 6) == 0) { + STRCPY(s, "inf"); + } else if (STRNCMP(s, "1.#IND", 6) == 0) { + STRCPY(s, "nan"); + } + // Remove sign before "nan" + if (STRNCMP(tmp, "-nan", 4) == 0) { + STRCPY(tmp, "nan"); + } + // Add sign before "inf" if needed. + if (isinf(f) == -1 && STRNCMP(tmp, "inf", 3) == 0) { + STRCPY(tmp, "-inf"); + } + if (remove_trailing_zeroes) { int i; char *tp; diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index 7f7811dc7a..fc804f3a15 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -201,12 +201,10 @@ function Test_printf_float() call assert_equal('inf', printf('%f', 1.0/0.0)) - " This prints inf but shouldn't it print -inf instead? - call assert_match('^-\?inf$', printf('%f', -1.0/0.0)) + call assert_match('^-inf$', printf('%f', -1.0/0.0)) - " This prints -nan but shouldn't it print nan instead? - call assert_match('^-\?nan$', printf('%f', sqrt(-1.0))) - call assert_match('^-\?nan$', printf('%f', 0.0/0.0)) + call assert_match('^nan$', printf('%f', sqrt(-1.0))) + call assert_match('^nan$', printf('%f', 0.0/0.0)) call assert_fails('echo printf("%f", "a")', 'E807:') endfunc diff --git a/src/nvim/version.c b/src/nvim/version.c index 8fd0fce329..5479e9f91f 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -175,7 +175,7 @@ static int included_patches[] = { // 2269, // 2268, // 2267 NA - // 2266, + 2266, 2265, 2264, // 2263, From 6ca580be9bffcccc513bfce383a6e1f9f026614c Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Tue, 7 Mar 2017 08:47:31 +0100 Subject: [PATCH 0038/1671] vim-patch:7.4.2280 Problem: printf() doesn't handle infinity float values correctly. Solution: Add a table with possible infinity values. (Dominique Pelle) https://github.com/vim/vim/commit/e999782e369999539a1783a7ebe4eadcc6da28a8 --- src/nvim/strings.c | 57 ++++++++++++--------- src/nvim/testdir/test_expr.vim | 90 ++++++++++++++++++++++++++++++---- src/nvim/version.c | 2 +- 3 files changed, 116 insertions(+), 33 deletions(-) diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 47ee311bd4..e8d70f2676 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -753,6 +753,22 @@ int vim_snprintf(char *str, size_t str_m, const char *fmt, ...) return str_l; } +// Return the representation of infinity for printf() function: +// "-inf", "inf", "+inf", " inf", "-INF", "INF", "+INF" or " INF". +static const char *infinity_str(bool positive, char fmt_spec, + int force_sign, int space_for_positive) +{ + static const char *table[] = { + "-inf", "inf", "+inf", " inf", + "-INF", "INF", "+INF", " INF" + }; + int idx = positive * (1 + force_sign + force_sign * space_for_positive); + if (ASCII_ISUPPER(fmt_spec)) { + idx += 4; + } + return table[idx]; +} + /// Write formatted value to the string /// @@ -1209,10 +1225,10 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, } if (fmt_spec == 'f' && abs_f > 1.0e307) { - const char inf_str[] = f < 0 ? "-inf" : "inf"; - // avoid a buffer overflow - memmove(tmp, inf_str, sizeof(inf_str) - 1); - str_arg_l = sizeof(inf_str) - 1; + STRCPY(tmp, infinity_str(f > 0.0, fmt_spec, + force_sign, space_for_positive)); + str_arg_l = STRLEN(tmp); + zero_padding = 0; } else { format[0] = '%'; int l = 1; @@ -1231,25 +1247,22 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, } format[l] = (char)(fmt_spec == 'F' ? 'f' : fmt_spec); format[l + 1] = NUL; - assert(l + 1 < (int)sizeof(format)); - str_arg_l = (size_t)snprintf(tmp, sizeof(tmp), format, f); - assert(str_arg_l < sizeof(tmp)); - // Be consistent: Change "1.#IND" to "nan" and - // "1.#INF" to "inf". - char *s = *tmp == '-' ? tmp + 1 : tmp; - if (STRNCMP(s, "1.#INF", 6) == 0) { - STRCPY(s, "inf"); - } else if (STRNCMP(s, "1.#IND", 6) == 0) { - STRCPY(s, "nan"); - } - // Remove sign before "nan" - if (STRNCMP(tmp, "-nan", 4) == 0) { - STRCPY(tmp, "nan"); - } - // Add sign before "inf" if needed. - if (isinf(f) == -1 && STRNCMP(tmp, "inf", 3) == 0) { - STRCPY(tmp, "-inf"); + if (isnan(f)) { + // Not a number: nan or NAN + STRCPY(tmp, ASCII_ISUPPER(fmt_spec) ? "NAN" : "nan"); + str_arg_l = 3; + zero_padding = 0; + } else if (isinf(f)) { + STRCPY(tmp, infinity_str(f > 0.0, fmt_spec, + force_sign, space_for_positive)); + str_arg_l = STRLEN(tmp); + zero_padding = 0; + } else { + // Regular float number + assert(l + 1 < (int)sizeof(format)); + str_arg_l = (size_t)snprintf(tmp, sizeof(tmp), format, f); + assert(str_arg_l < sizeof(tmp)); } if (remove_trailing_zeroes) { diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index fc804f3a15..b7eeb6d48c 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -141,7 +141,7 @@ function Test_printf_misc() call assert_equal('173', printf('%O', 123)) call assert_equal('7b', printf('%x', 123)) call assert_equal('7B', printf('%X', 123)) - call assert_equal('{', printf('%c', 123)) + call assert_equal('#', printf('%c', 123)) call assert_equal('abc', printf('%s', 'abc')) call assert_equal('abc', printf('%S', 'abc')) @@ -152,13 +152,22 @@ function Test_printf_misc() call assert_equal(' 123', printf('% d', 123)) call assert_equal('-123', printf('% d', -123)) + call assert_equal('123', printf('%2d', 123)) + call assert_equal(' 123', printf('%6d', 123)) + call assert_equal('000123', printf('%06d', 123)) + call assert_equal('+00123', printf('%+06d', 123)) + call assert_equal(' 00123', printf('% 06d', 123)) + call assert_equal(' +123', printf('%+6d', 123)) + call assert_equal(' 123', printf('% 6d', 123)) + call assert_equal(' -123', printf('% 6d', -123)) + call assert_equal('+123 ', printf('%-+6d', 123)) + call assert_equal(' 123 ', printf('%- 6d', 123)) + call assert_equal('-123 ', printf('%- 6d', -123)) + call assert_equal('00123', printf('%.*d', 5, 123)) call assert_equal(' 123', printf('% *d', 5, 123)) call assert_equal(' +123', printf('%+ *d', 5, 123)) - call assert_equal('123', printf('%2d', 123)) - call assert_equal(' 123', printf('%5d', 123)) - call assert_equal('00123', printf('%05d', 123)) call assert_equal('123 ', printf('%-5d', 123)) call assert_equal('0x7b', printf('%#x', 123)) call assert_equal('0X7B', printf('%#X', 123)) @@ -191,20 +200,81 @@ endfunc function Test_printf_float() call assert_equal('1.230000', printf('%f', 1.23)) call assert_equal('1.230000', printf('%F', 1.23)) - call assert_equal('1.23', printf('%g', 1.23)) - call assert_equal('1.23', printf('%G', 1.23)) + call assert_equal('9999999.9', printf('%g', 9999999.9)) + call assert_equal('9999999.9', printf('%G', 9999999.9)) + call assert_equal('1.00000001e7', printf('%.8g', 10000000.1)) + call assert_equal('1.00000001E7', printf('%.8G', 10000000.1)) call assert_equal('1.230000e+00', printf('%e', 1.23)) call assert_equal('1.230000E+00', printf('%E', 1.23)) call assert_equal('1.200000e-02', printf('%e', 0.012)) call assert_equal('-1.200000e-02', printf('%e', -0.012)) - call assert_equal('1.2', printf('%.1f', 1.23)) + call assert_equal('0.33', printf('%.2f', 1.0/3.0)) + call assert_equal(' 0.33', printf('%6.2f', 1.0/3.0)) + call assert_equal(' -0.33', printf('%6.2f', -1.0/3.0)) + call assert_equal('000.33', printf('%06.2f', 1.0/3.0)) + " FIXME: call assert_equal('-00.33', printf('%06.2f', -1.0/3.0)) + " FIXME: call assert_equal('-00.33', printf('%+06.2f', -1.0/3.0)) + " FIXME: call assert_equal('+00.33', printf('%+06.2f', 1.0/3.0)) + " FIXME: call assert_equal(' 00.33', printf('% 06.2f', 1.0/3.0)) + " Float infinity can be signed. call assert_equal('inf', printf('%f', 1.0/0.0)) + call assert_equal('-inf', printf('%f', -1.0/0.0)) + call assert_equal('inf', printf('%g', 1.0/0.0)) + call assert_equal('-inf', printf('%g', -1.0/0.0)) + call assert_equal('inf', printf('%e', 1.0/0.0)) + call assert_equal('-inf', printf('%e', -1.0/0.0)) + call assert_equal('INF', printf('%E', 1.0/0.0)) + call assert_equal('-INF', printf('%E', -1.0/0.0)) + call assert_equal('INF', printf('%E', 1.0/0.0)) + call assert_equal('-INF', printf('%G', -1.0/0.0)) + call assert_equal('+inf', printf('%+f', 1.0/0.0)) + call assert_equal('-inf', printf('%+f', -1.0/0.0)) + call assert_equal(' inf', printf('% f', 1.0/0.0)) + call assert_equal(' inf', printf('%6f', 1.0/0.0)) + call assert_equal(' -inf', printf('%6f', -1.0/0.0)) + call assert_equal(' inf', printf('%6g', 1.0/0.0)) + call assert_equal(' -inf', printf('%6g', -1.0/0.0)) + call assert_equal(' +inf', printf('%+6f', 1.0/0.0)) + call assert_equal(' inf', printf('% 6f', 1.0/0.0)) + call assert_equal(' +inf', printf('%+06f', 1.0/0.0)) + call assert_equal('inf ', printf('%-6f', 1.0/0.0)) + call assert_equal('-inf ', printf('%-6f', -1.0/0.0)) + call assert_equal('+inf ', printf('%-+6f', 1.0/0.0)) + call assert_equal(' inf ', printf('%- 6f', 1.0/0.0)) + call assert_equal('INF ', printf('%-6G', 1.0/0.0)) + call assert_equal('-INF ', printf('%-6G', -1.0/0.0)) + call assert_equal('INF ', printf('%-6E', 1.0/0.0)) + call assert_equal('-INF ', printf('%-6E', -1.0/0.0)) + call assert_equal("str2float('inf')", printf('%s', 1.0/0.0)) + call assert_equal("-str2float('inf')", printf('%s', -1.0/0.0)) - call assert_match('^-inf$', printf('%f', -1.0/0.0)) + " Float zero can be signed. + call assert_equal('0.000000', printf('%f', 1.0/(1.0/0.0))) + call assert_equal('-0.000000', printf('%f', 1.0/(-1.0/0.0))) + call assert_equal('0.0', printf('%s', 1.0/(1.0/0.0))) + call assert_equal('-0.0', printf('%s', 1.0/(-1.0/0.0))) + call assert_equal('0.0', printf('%S', 1.0/(1.0/0.0))) + call assert_equal('-0.0', printf('%S', 1.0/(-1.0/0.0))) - call assert_match('^nan$', printf('%f', sqrt(-1.0))) - call assert_match('^nan$', printf('%f', 0.0/0.0)) + " Float nan (not a number) has no sign. + call assert_equal('nan', printf('%f', sqrt(-1.0))) + call assert_equal('nan', printf('%f', 0.0/0.0)) + call assert_equal('nan', printf('%f', -0.0/0.0)) + call assert_equal('nan', printf('%g', 0.0/0.0)) + call assert_equal('nan', printf('%e', 0.0/0.0)) + call assert_equal('NAN', printf('%G', 0.0/0.0)) + call assert_equal('NAN', printf('%E', 0.0/0.0)) + call assert_equal('NAN', printf('%G', -0.0/0.0)) + call assert_equal('NAN', printf('%E', -0.0/0.0)) + call assert_equal(' nan', printf('%6f', 0.0/0.0)) + call assert_equal(' nan', printf('%06f', 0.0/0.0)) + call assert_equal('nan ', printf('%-6f', 0.0/0.0)) + call assert_equal('nan ', printf('%- 6f', 0.0/0.0)) + call assert_equal("str2float('nan')", printf('%s', 0.0/0.0)) + call assert_equal("str2float('nan')", printf('%s', -0.0/0.0)) + call assert_equal("str2float('nan')", printf('%S', 0.0/0.0)) + call assert_equal("str2float('nan')", printf('%S', -0.0/0.0)) call assert_fails('echo printf("%f", "a")', 'E807:') endfunc diff --git a/src/nvim/version.c b/src/nvim/version.c index 5479e9f91f..09e27d210b 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -161,7 +161,7 @@ static int included_patches[] = { // 2283, // 2282 NA // 2281 NA - // 2280, + 2280, 2279, // 2278 NA 2277, From cad9a76be28f2ee3bd2c48ae959243aa7cfdb02a Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Tue, 7 Mar 2017 21:05:02 +0100 Subject: [PATCH 0039/1671] vim-patch:7.4.2291 Problem: printf() handles floats wrong when there is a sign. Solution: Fix placing the sign. Add tests. (Dominique Pelle) https://github.com/vim/vim/commit/04186095346daa60e82e981dad114de2b641d672 --- runtime/doc/eval.txt | 12 +++--- src/nvim/strings.c | 53 ++++++++++++++----------- src/nvim/testdir/test_expr.vim | 72 +++++++++++++++++++++++++++------- src/nvim/version.c | 2 +- 4 files changed, 97 insertions(+), 42 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index e1a8ba079a..5340830947 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -5683,9 +5683,10 @@ printf({fmt}, {expr1} ...) *printf()* %04x hex number padded with zeros to at least 4 characters %X hex number using upper case letters %o octal number - %f floating point number in the form 123.456 - %e floating point number in the form 1.234e3 - %E floating point number in the form 1.234E3 + %f floating point number as 12.23, inf, -inf or nan + %F floating point number as 12.23, INF, -INF or NAN + %e floating point number as 1.23e3, inf, -inf or nan + %E floating point number as 1.23E3, INF, -INF or NAN %g floating point number, as %f or %e depending on value %G floating point number, as %f or %E depending on value %% the % character itself @@ -5810,8 +5811,9 @@ printf({fmt}, {expr1} ...) *printf()* digits after the decimal point. When the precision is zero the decimal point is omitted. When the precision is not specified 6 is used. A really big number - (out of range or dividing by zero) results in "inf". - "0.0 / 0.0" results in "nan". + (out of range or dividing by zero) results in "inf" + or "-inf" with %f (INF or -INF with %F). + "0.0 / 0.0" results in "nan" with %f (NAN with %F). Example: > echo printf("%.2f", 12.115) < 12.12 diff --git a/src/nvim/strings.c b/src/nvim/strings.c index e8d70f2676..a2052423e2 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -925,7 +925,6 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, case 'D': fmt_spec = 'd'; length_modifier = 'l'; break; case 'U': fmt_spec = 'u'; length_modifier = 'l'; break; case 'O': fmt_spec = 'o'; length_modifier = 'l'; break; - case 'F': fmt_spec = 'f'; break; default: break; } @@ -1202,6 +1201,7 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, } case 'f': + case 'F': case 'e': case 'E': case 'g': @@ -1217,37 +1217,19 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, if (fmt_spec == 'g' || fmt_spec == 'G') { // can't use %g directly, cause it prints "1.0" as "1" if ((abs_f >= 0.001 && abs_f < 10000000.0) || abs_f == 0.0) { - fmt_spec = 'f'; + fmt_spec = ASCII_ISUPPER(fmt_spec) ? 'F' : 'f'; } else { fmt_spec = fmt_spec == 'g' ? 'e' : 'E'; } remove_trailing_zeroes = true; } - if (fmt_spec == 'f' && abs_f > 1.0e307) { + if ((fmt_spec == 'f' || fmt_spec == 'F') && abs_f > 1.0e307) { STRCPY(tmp, infinity_str(f > 0.0, fmt_spec, force_sign, space_for_positive)); str_arg_l = STRLEN(tmp); zero_padding = 0; } else { - format[0] = '%'; - int l = 1; - if (precision_specified) { - size_t max_prec = TMP_LEN - 10; - - // make sure we don't get more digits than we have room for - if (fmt_spec == 'f' && abs_f > 1.0) { - max_prec -= (size_t)log10(abs_f); - } - if (precision > max_prec) { - precision = max_prec; - } - l += snprintf(format + 1, sizeof(format) - 1, ".%d", - (int)precision); - } - format[l] = (char)(fmt_spec == 'F' ? 'f' : fmt_spec); - format[l + 1] = NUL; - if (isnan(f)) { // Not a number: nan or NAN STRCPY(tmp, ASCII_ISUPPER(fmt_spec) ? "NAN" : "nan"); @@ -1259,6 +1241,27 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, str_arg_l = STRLEN(tmp); zero_padding = 0; } else { + format[0] = '%'; + int l = 1; + if (force_sign) { + format[l++] = space_for_positive ? ' ' : '+'; + } + if (precision_specified) { + size_t max_prec = TMP_LEN - 10; + + // make sure we don't get more digits than we have room for + if ((fmt_spec == 'f' || fmt_spec == 'F') && abs_f > 1.0) { + max_prec -= (size_t)log10(abs_f); + } + if (precision > max_prec) { + precision = max_prec; + } + l += snprintf(format + l, sizeof(format) - 1, ".%d", + (int)precision); + } + format[l] = (char)(fmt_spec == 'F' ? 'f' : fmt_spec); + format[l + 1] = NUL; + // Regular float number assert(l + 1 < (int)sizeof(format)); str_arg_l = (size_t)snprintf(tmp, sizeof(tmp), format, f); @@ -1270,7 +1273,7 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, char *tp; // using %g or %G: remove superfluous zeroes - if (fmt_spec == 'f') { + if (fmt_spec == 'f' || fmt_spec == 'F') { tp = tmp + str_arg_l - 1; } else { tp = (char *)vim_strchr((char_u *)tmp, @@ -1312,6 +1315,12 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, } } } + if (zero_padding && min_field_width > str_arg_l + && (tmp[0] == '-' || force_sign)) { + // Padding 0's should be inserted after the sign. + number_of_zeros_to_pad = min_field_width - str_arg_l; + zero_padding_insertion_ind = 1; + } str_arg = tmp; break; } diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index b7eeb6d48c..ccbdde8244 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -141,7 +141,7 @@ function Test_printf_misc() call assert_equal('173', printf('%O', 123)) call assert_equal('7b', printf('%x', 123)) call assert_equal('7B', printf('%X', 123)) - call assert_equal('#', printf('%c', 123)) + call assert_equal('{', printf('%c', 123)) call assert_equal('abc', printf('%s', 'abc')) call assert_equal('abc', printf('%S', 'abc')) @@ -160,21 +160,44 @@ function Test_printf_misc() call assert_equal(' +123', printf('%+6d', 123)) call assert_equal(' 123', printf('% 6d', 123)) call assert_equal(' -123', printf('% 6d', -123)) + + " Test left adjusted. + call assert_equal('123 ', printf('%-6d', 123)) call assert_equal('+123 ', printf('%-+6d', 123)) call assert_equal(' 123 ', printf('%- 6d', 123)) call assert_equal('-123 ', printf('%- 6d', -123)) + call assert_equal(' 00123', printf('%7.5d', 123)) + call assert_equal(' -00123', printf('%7.5d', -123)) + call assert_equal(' +00123', printf('%+7.5d', 123)) + " Precision field should not be used when combined with %0 + call assert_equal(' 00123', printf('%07.5d', 123)) + call assert_equal(' -00123', printf('%07.5d', -123)) + + call assert_equal(' 123', printf('%*d', 5, 123)) + call assert_equal('123 ', printf('%*d', -5, 123)) call assert_equal('00123', printf('%.*d', 5, 123)) call assert_equal(' 123', printf('% *d', 5, 123)) call assert_equal(' +123', printf('%+ *d', 5, 123)) - call assert_equal('123 ', printf('%-5d', 123)) + " Simple quote (thousand grouping char) is ignored. + call assert_equal('+00123456', printf("%+'09d", 123456)) + + " Unrecognized format specifier kept as-is. + call assert_equal('_123', printf("%_%d", 123)) + + " Test alternate forms. call assert_equal('0x7b', printf('%#x', 123)) call assert_equal('0X7B', printf('%#X', 123)) call assert_equal('0173', printf('%#o', 123)) call assert_equal('0173', printf('%#O', 123)) call assert_equal('abc', printf('%#s', 'abc')) call assert_equal('abc', printf('%#S', 'abc')) + call assert_equal(' 0173', printf('%#6o', 123)) + call assert_equal(' 00173', printf('%#6.5o', 123)) + call assert_equal(' 0173', printf('%#6.2o', 123)) + call assert_equal(' 0173', printf('%#6.2o', 123)) + call assert_equal('0173', printf('%#2.2o', 123)) call assert_equal(' 00123', printf('%6.5d', 123)) call assert_equal(' 0007b', printf('%6.5x', 123)) @@ -198,6 +221,7 @@ function Test_printf_misc() endfunc function Test_printf_float() + call assert_equal('1.000000', printf('%f', 1)) call assert_equal('1.230000', printf('%f', 1.23)) call assert_equal('1.230000', printf('%F', 1.23)) call assert_equal('9999999.9', printf('%g', 9999999.9)) @@ -212,10 +236,31 @@ function Test_printf_float() call assert_equal(' 0.33', printf('%6.2f', 1.0/3.0)) call assert_equal(' -0.33', printf('%6.2f', -1.0/3.0)) call assert_equal('000.33', printf('%06.2f', 1.0/3.0)) - " FIXME: call assert_equal('-00.33', printf('%06.2f', -1.0/3.0)) - " FIXME: call assert_equal('-00.33', printf('%+06.2f', -1.0/3.0)) - " FIXME: call assert_equal('+00.33', printf('%+06.2f', 1.0/3.0)) - " FIXME: call assert_equal(' 00.33', printf('% 06.2f', 1.0/3.0)) + call assert_equal('-00.33', printf('%06.2f', -1.0/3.0)) + call assert_equal('-00.33', printf('%+06.2f', -1.0/3.0)) + call assert_equal('+00.33', printf('%+06.2f', 1.0/3.0)) + call assert_equal(' 00.33', printf('% 06.2f', 1.0/3.0)) + call assert_equal('000.33', printf('%06.2g', 1.0/3.0)) + call assert_equal('-00.33', printf('%06.2g', -1.0/3.0)) + call assert_equal('0.33', printf('%3.2f', 1.0/3.0)) + call assert_equal('003.33e-01', printf('%010.2e', 1.0/3.0)) + call assert_equal(' 03.33e-01', printf('% 010.2e', 1.0/3.0)) + call assert_equal('+03.33e-01', printf('%+010.2e', 1.0/3.0)) + call assert_equal('-03.33e-01', printf('%010.2e', -1.0/3.0)) + + " When precision is 0, the dot should be omitted. + call assert_equal(' 2', printf('%3.f', 7.0/3.0)) + call assert_equal(' 2', printf('%3.g', 7.0/3.0)) + call assert_equal(' 2e+00', printf('%7.e', 7.0/3.0)) + + " Float zero can be signed. + call assert_equal('+0.000000', printf('%+f', 0.0)) + call assert_equal('0.000000', printf('%f', 1.0/(1.0/0.0))) + call assert_equal('-0.000000', printf('%f', 1.0/(-1.0/0.0))) + call assert_equal('0.0', printf('%s', 1.0/(1.0/0.0))) + call assert_equal('-0.0', printf('%s', 1.0/(-1.0/0.0))) + call assert_equal('0.0', printf('%S', 1.0/(1.0/0.0))) + call assert_equal('-0.0', printf('%S', 1.0/(-1.0/0.0))) " Float infinity can be signed. call assert_equal('inf', printf('%f', 1.0/0.0)) @@ -224,6 +269,8 @@ function Test_printf_float() call assert_equal('-inf', printf('%g', -1.0/0.0)) call assert_equal('inf', printf('%e', 1.0/0.0)) call assert_equal('-inf', printf('%e', -1.0/0.0)) + call assert_equal('INF', printf('%F', 1.0/0.0)) + call assert_equal('-INF', printf('%F', -1.0/0.0)) call assert_equal('INF', printf('%E', 1.0/0.0)) call assert_equal('-INF', printf('%E', -1.0/0.0)) call assert_equal('INF', printf('%E', 1.0/0.0)) @@ -242,6 +289,9 @@ function Test_printf_float() call assert_equal('-inf ', printf('%-6f', -1.0/0.0)) call assert_equal('+inf ', printf('%-+6f', 1.0/0.0)) call assert_equal(' inf ', printf('%- 6f', 1.0/0.0)) + call assert_equal('-INF ', printf('%-6F', -1.0/0.0)) + call assert_equal('+INF ', printf('%-+6F', 1.0/0.0)) + call assert_equal(' INF ', printf('%- 6F', 1.0/0.0)) call assert_equal('INF ', printf('%-6G', 1.0/0.0)) call assert_equal('-INF ', printf('%-6G', -1.0/0.0)) call assert_equal('INF ', printf('%-6E', 1.0/0.0)) @@ -249,22 +299,16 @@ function Test_printf_float() call assert_equal("str2float('inf')", printf('%s', 1.0/0.0)) call assert_equal("-str2float('inf')", printf('%s', -1.0/0.0)) - " Float zero can be signed. - call assert_equal('0.000000', printf('%f', 1.0/(1.0/0.0))) - call assert_equal('-0.000000', printf('%f', 1.0/(-1.0/0.0))) - call assert_equal('0.0', printf('%s', 1.0/(1.0/0.0))) - call assert_equal('-0.0', printf('%s', 1.0/(-1.0/0.0))) - call assert_equal('0.0', printf('%S', 1.0/(1.0/0.0))) - call assert_equal('-0.0', printf('%S', 1.0/(-1.0/0.0))) - " Float nan (not a number) has no sign. call assert_equal('nan', printf('%f', sqrt(-1.0))) call assert_equal('nan', printf('%f', 0.0/0.0)) call assert_equal('nan', printf('%f', -0.0/0.0)) call assert_equal('nan', printf('%g', 0.0/0.0)) call assert_equal('nan', printf('%e', 0.0/0.0)) + call assert_equal('NAN', printf('%F', 0.0/0.0)) call assert_equal('NAN', printf('%G', 0.0/0.0)) call assert_equal('NAN', printf('%E', 0.0/0.0)) + call assert_equal('NAN', printf('%F', -0.0/0.0)) call assert_equal('NAN', printf('%G', -0.0/0.0)) call assert_equal('NAN', printf('%E', -0.0/0.0)) call assert_equal(' nan', printf('%6f', 0.0/0.0)) diff --git a/src/nvim/version.c b/src/nvim/version.c index 09e27d210b..dd4adae213 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -150,7 +150,7 @@ static int included_patches[] = { 2294, // 2293, 2292, - // 2291, + 2291, // 2290 NA // 2289 NA // 2288 NA From c6c8e1e8cc4ea6fc98187c0bce8875cf82278ba5 Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Tue, 21 Mar 2017 07:34:08 +0100 Subject: [PATCH 0040/1671] version: Mark patch 2221 as applied. It was implemented as a part of #3916. --- src/nvim/version.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/version.c b/src/nvim/version.c index dd4adae213..4bc108dd2d 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -220,7 +220,7 @@ static int included_patches[] = { // 2224, 2223, 2222, - // 2221, + 2221, 2220, 2219, // 2218 NA From 66259e4c498dd3b5278aeea16cc3a71ed83f1b76 Mon Sep 17 00:00:00 2001 From: Jack Bracewell Date: Tue, 21 Mar 2017 10:11:32 +0000 Subject: [PATCH 0041/1671] vim-patch:7.4.2293 (#6307) The original patch makes all the modeline comments consistent, but these have been removed in the neovim source. However there as a correction of a comment included in the patch that we can use. https://github.com/vim/vim/commit/edf3f97ae2af024708ebb4ac614227327033ca47 --- src/nvim/eval.c | 2 +- src/nvim/version.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d4daffb469..7eb149476f 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4196,7 +4196,7 @@ static int eval7( // string and free a string that isn't there. rettv->v_type = VAR_UNKNOWN; - // Skip '!' and '-' characters. They are handled later. + // Skip '!', '-' and '+' characters. They are handled later. start_leader = *arg; while (**arg == '!' || **arg == '-' || **arg == '+') { *arg = skipwhite(*arg + 1); diff --git a/src/nvim/version.c b/src/nvim/version.c index 8fd0fce329..bf13e52be4 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -148,7 +148,7 @@ static int included_patches[] = { // 2296, 2295, 2294, - // 2293, + 2293, 2292, // 2291, // 2290 NA From 03a04172ec911a4334d814b87f4c9ecc805b817e Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 11:28:13 +0100 Subject: [PATCH 0042/1671] scripts/vim-patch.sh: Remove "Last change ..." lines. Also: - fix ignoring doc/tags file - use 12 chars of hash instead of 7 --- scripts/vim-patch.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh index bf4b7d9080..d02eef6b44 100755 --- a/scripts/vim-patch.sh +++ b/scripts/vim-patch.sh @@ -109,7 +109,7 @@ assign_commit_details() { local strip_commit_line=true else # Interpret parameter as commit hash. - vim_version="${1:0:7}" + vim_version="${1:0:12}" vim_commit=$(cd "${VIM_SOURCE_DIR}" \ && git log -1 --format="%H" "${vim_version}") local strip_commit_line=false @@ -136,9 +136,12 @@ preprocess_patch() { 2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/\S*\<\%('${na_src}'\)@norm! d/\v(^diff)|%$ ' +w +q "$file" # Remove channel.txt, netbeans.txt, os_*.txt, todo.txt, version*.txt, tags - local na_doc='channel\.txt\|netbeans\.txt\|os_\w\+\.txt\|todo\.txt\|version\d\.txt\|tags$' + local na_doc='channel\.txt\|netbeans\.txt\|os_\w\+\.txt\|todo\.txt\|version\d\.txt\|tags' 2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/runtime/doc/\%('${na_doc}'\)@norm! d/\v(^diff)|%$ ' +w +q "$file" + # Remove "Last change ..." changes in doc files. + 2>/dev/null $nvim --cmd 'set dir=/tmp' +'%s/^@@.*\n.*For Vim version.*Last change.*\n.*For Vim version.*Last change.*//' +w +q "$file" + # Remove some testdir/Make_*.mak files local na_src_testdir='Make_amiga.mak\|Make_dos.mak\|Make_ming.mak\|Make_vms.mms' 2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/testdir/\%('${na_src_testdir}'\)@norm! d/\v(^diff)|%$ ' +w +q "$file" From d6797e28650f7ba1c6e7ec7fc97264d0552c61ac Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 11:33:41 +0100 Subject: [PATCH 0043/1671] .gitignore: Do not ignore *.rej --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3fb3d62517..68c0e9d17c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ /.deps/ /tmp/ -*.rej *.orig *.mo .*.sw? From cc421270ee6fd3fbd6e1e7bd766f71e1db30c7b5 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 12:11:53 +0100 Subject: [PATCH 0044/1671] vim-patch:bc8801c9317eb Updated runtime files. https://github.com/vim/vim/commit/bc8801c9317eb721a2ee91322669f2dd5d136380 --- runtime/doc/autocmd.txt | 5 ++-- runtime/doc/eval.txt | 8 +++--- runtime/doc/map.txt | 8 +++--- runtime/doc/various.txt | 1 + runtime/doc/windows.txt | 2 +- runtime/syntax/php.vim | 44 +++++++++++++++--------------- runtime/syntax/sh.vim | 59 ++++++++++++++++++++++------------------- runtime/syntax/tex.vim | 34 ++++++++++++++---------- runtime/syntax/vim.vim | 4 +-- 9 files changed, 89 insertions(+), 76 deletions(-) diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 180127cd52..b865cb9bc9 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -1184,10 +1184,11 @@ name! different from existing {event} names, as this most likely will not do what you intended. - *:augroup-delete* *E367* + *:augroup-delete* *E367* *W19* :aug[roup]! {name} Delete the autocmd group {name}. Don't use this if there is still an autocommand using - this group! This is not checked. + this group! You will get a warning if doing + it anyway. To enter autocommands for a specific group, use this method: 1. Select the group with ":augroup {name}". diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index e1a8ba079a..238e1758e5 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -247,7 +247,7 @@ it. To change a list in-place see |list-modification| below. Sublist ~ - + *sublist* A part of the List can be obtained by specifying the first and last index, separated by a colon in square brackets: > :let shortlist = mylist[2:-1] " get List [3, "four"] @@ -975,10 +975,10 @@ Examples: > :let s = line(".")[4:] " from the fifth byte to the end :let s = s[:-3] " remove last two bytes < - *sublist* *slice* + *slice* If expr8 is a |List| this results in a new |List| with the items indicated by the indexes expr1a and expr1b. This works like with a String, as explained -just above, except that indexes out of range cause an error. Examples: > +just above. Also see |sublist| below. Examples: > :let l = mylist[:3] " first four items :let l = mylist[4:4] " List with one item :let l = mylist[:] " shallow copy of a List @@ -8219,7 +8219,7 @@ See |:verbose-cmd| for more information. : let x += 1 : return x : endfunction - : return function('Bar') + : return funcref('Bar') :endfunction :let F = Foo() diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index 53b152dc06..6dc8b1acc8 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -1411,9 +1411,11 @@ The valid escape sequences are The command modifiers, if specified. Otherwise, expands to nothing. Supported modifiers are |:aboveleft|, |:belowright|, |:botright|, |:browse|, |:confirm|, |:hide|, |:keepalt|, - |:keepjumps|, |:keepmarks|, |:keeppatterns|, |:lockmarks|, - |:noswapfile|, |:silent|, |:tab|, |:topleft|, |:verbose|, and - |:vertical|. + |:keepjumps|, |:keepmarks|, |:keeppatterns|, |:leftabove|, + |:lockmarks|, |:noswapfile| |:rightbelow|, |:silent|, |:tab|, + |:topleft|, |:verbose|, and |:vertical|. + Note that these are not yet supported: |:noautocmd|, + |:sandbox| and |:unsilent|. Examples: > command! -nargs=+ -complete=file MyEdit \ for f in expand(, 0, 1) | diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index 0ac294ec37..3a66becb9a 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -331,6 +331,7 @@ N *+gettext* message translations |multi-lang| N *+insert_expand* |insert_expand| Insert mode completion N *+jumplist* |jumplist| B *+keymap* |'keymap'| +N *+lambda* |lambda| and |closure| B *+langmap* |'langmap'| N *+libcall* |libcall()| N *+linebreak* |'linebreak'|, |'breakat'| and |'showbreak'| diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index 5b94626e36..25239bd7ae 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -249,7 +249,7 @@ window will appear. far left and occupies the full height of the Vim window. Doesn't work for |:execute| and |:normal|. - *:botright* + *:bo* *:botright* :bo[tright] {cmd} Execute {cmd}. If it contains a command that splits a window, it will appear at the bottom and occupy the full width of the diff --git a/runtime/syntax/php.vim b/runtime/syntax/php.vim index fc257418d0..ae3f82d64a 100644 --- a/runtime/syntax/php.vim +++ b/runtime/syntax/php.vim @@ -1,7 +1,7 @@ " Vim syntax file -" Language: php PHP 3/4/5 +" Language: php PHP 3/4/5/7 " Maintainer: Jason Woofenden -" Last Change: Apr 18, 2016 +" Last Change: Jul 27, 2016 " URL: https://jasonwoof.com/gitweb/?p=vim-syntax.git;a=blob;f=php.vim;hb=HEAD " Former Maintainers: Peter Hodge " Debian VIM Maintainers @@ -377,29 +377,29 @@ syn keyword phpTodo todo fixme xxx contained " Comment if exists("php_parent_error_open") - syn region phpComment start="/\*" end="\*/" contained contains=phpTodo + syn region phpComment start="/\*" end="\*/" contained contains=phpTodo,@Spell else - syn region phpComment start="/\*" end="\*/" contained contains=phpTodo extend + syn region phpComment start="/\*" end="\*/" contained contains=phpTodo,@Spell extend endif if version >= 600 - syn match phpComment "#.\{-}\(?>\|$\)\@=" contained contains=phpTodo - syn match phpComment "//.\{-}\(?>\|$\)\@=" contained contains=phpTodo + syn match phpComment "#.\{-}\(?>\|$\)\@=" contained contains=phpTodo,@Spell + syn match phpComment "//.\{-}\(?>\|$\)\@=" contained contains=phpTodo,@Spell else - syn match phpComment "#.\{-}$" contained contains=phpTodo - syn match phpComment "#.\{-}?>"me=e-2 contained contains=phpTodo - syn match phpComment "//.\{-}$" contained contains=phpTodo - syn match phpComment "//.\{-}?>"me=e-2 contained contains=phpTodo + syn match phpComment "#.\{-}$" contained contains=phpTodo,@Spell + syn match phpComment "#.\{-}?>"me=e-2 contained contains=phpTodo,@Spell + syn match phpComment "//.\{-}$" contained contains=phpTodo,@Spell + syn match phpComment "//.\{-}?>"me=e-2 contained contains=phpTodo,@Spell endif " String if exists("php_parent_error_open") - syn region phpStringDouble matchgroup=phpStringDouble start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=@phpAddStrings,phpBackslashSequences,phpBackslashDoubleQuote,@phpInterpDouble contained keepend + syn region phpStringDouble matchgroup=phpStringDouble start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=@phpAddStrings,phpBackslashSequences,phpBackslashDoubleQuote,@phpInterpDouble,@Spell contained keepend syn region phpBacktick matchgroup=phpBacktick start=+`+ skip=+\\\\\|\\"+ end=+`+ contains=@phpAddStrings,phpIdentifier,phpBackslashSequences,phpIdentifierSimply,phpIdentifierComplex contained keepend - syn region phpStringSingle matchgroup=phpStringSingle start=+'+ skip=+\\\\\|\\'+ end=+'+ contains=@phpAddStrings,phpBackslashSingleQuote contained keepend + syn region phpStringSingle matchgroup=phpStringSingle start=+'+ skip=+\\\\\|\\'+ end=+'+ contains=@phpAddStrings,phpBackslashSingleQuote,@Spell contained keepend else - syn region phpStringDouble matchgroup=phpStringDouble start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=@phpAddStrings,phpBackslashSequences,phpBackslashDoubleQuote,@phpInterpDouble contained extend keepend + syn region phpStringDouble matchgroup=phpStringDouble start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=@phpAddStrings,phpBackslashSequences,phpBackslashDoubleQuote,@phpInterpDouble,@Spell contained extend keepend syn region phpBacktick matchgroup=phpBacktick start=+`+ skip=+\\\\\|\\"+ end=+`+ contains=@phpAddStrings,phpIdentifier,phpBackslashSequences,phpIdentifierSimply,phpIdentifierComplex contained extend keepend - syn region phpStringSingle matchgroup=phpStringSingle start=+'+ skip=+\\\\\|\\'+ end=+'+ contains=@phpAddStrings,phpBackslashSingleQuote contained keepend extend + syn region phpStringSingle matchgroup=phpStringSingle start=+'+ skip=+\\\\\|\\'+ end=+'+ contains=@phpAddStrings,phpBackslashSingleQuote,@Spell contained keepend extend endif " HereDoc and NowDoc @@ -407,18 +407,18 @@ if version >= 600 syn case match " HereDoc - syn region phpHereDoc matchgroup=Delimiter start="\(<<<\)\@<=\(\"\=\)\z(\I\i*\)\2$" end="^\z1\(;\=$\)\@=" contained contains=phpIdentifier,phpIdentifierSimply,phpIdentifierComplex,phpBackslashSequences,phpMethodsVar keepend extend + syn region phpHereDoc matchgroup=Delimiter start="\(<<<\)\@<=\(\"\=\)\z(\I\i*\)\2$" end="^\z1\(;\=$\)\@=" contained contains=phpIdentifier,phpIdentifierSimply,phpIdentifierComplex,phpBackslashSequences,phpMethodsVar,@Spell keepend extend " including HTML,JavaScript,SQL even if not enabled via options - syn region phpHereDoc matchgroup=Delimiter start="\(<<<\)\@<=\(\"\=\)\z(\(\I\i*\)\=\(html\)\c\(\i*\)\)\2$" end="^\z1\(;\=$\)\@=" contained contains=@htmlTop,phpIdentifier,phpIdentifierSimply,phpIdentifierComplex,phpBackslashSequences,phpMethodsVar keepend extend - syn region phpHereDoc matchgroup=Delimiter start="\(<<<\)\@<=\(\"\=\)\z(\(\I\i*\)\=\(sql\)\c\(\i*\)\)\2$" end="^\z1\(;\=$\)\@=" contained contains=@sqlTop,phpIdentifier,phpIdentifierSimply,phpIdentifierComplex,phpBackslashSequences,phpMethodsVar keepend extend - syn region phpHereDoc matchgroup=Delimiter start="\(<<<\)\@<=\(\"\=\)\z(\(\I\i*\)\=\(javascript\)\c\(\i*\)\)\2$" end="^\z1\(;\=$\)\@=" contained contains=@htmlJavascript,phpIdentifierSimply,phpIdentifier,phpIdentifierComplex,phpBackslashSequences,phpMethodsVar keepend extend + syn region phpHereDoc matchgroup=Delimiter start="\(<<<\)\@<=\(\"\=\)\z(\(\I\i*\)\=\(html\)\c\(\i*\)\)\2$" end="^\z1\(;\=$\)\@=" contained contains=@htmlTop,phpIdentifier,phpIdentifierSimply,phpIdentifierComplex,phpBackslashSequences,phpMethodsVar,@Spell keepend extend + syn region phpHereDoc matchgroup=Delimiter start="\(<<<\)\@<=\(\"\=\)\z(\(\I\i*\)\=\(sql\)\c\(\i*\)\)\2$" end="^\z1\(;\=$\)\@=" contained contains=@sqlTop,phpIdentifier,phpIdentifierSimply,phpIdentifierComplex,phpBackslashSequences,phpMethodsVar,@Spell keepend extend + syn region phpHereDoc matchgroup=Delimiter start="\(<<<\)\@<=\(\"\=\)\z(\(\I\i*\)\=\(javascript\)\c\(\i*\)\)\2$" end="^\z1\(;\=$\)\@=" contained contains=@htmlJavascript,phpIdentifierSimply,phpIdentifier,phpIdentifierComplex,phpBackslashSequences,phpMethodsVar,@Spell keepend extend " NowDoc - syn region phpNowDoc matchgroup=Delimiter start="\(<<<\)\@<='\z(\I\i*\)'$" end="^\z1\(;\=$\)\@=" contained keepend extend + syn region phpNowDoc matchgroup=Delimiter start="\(<<<\)\@<='\z(\I\i*\)'$" end="^\z1\(;\=$\)\@=" contained contains=@Spell keepend extend " including HTML,JavaScript,SQL even if not enabled via options - syn region phpNowDoc matchgroup=Delimiter start="\(<<<\)\@<='\z(\(\I\i*\)\=\(html\)\c\(\i*\)\)'$" end="^\z1\(;\=$\)\@=" contained contains=@htmlTop keepend extend - syn region phpNowDoc matchgroup=Delimiter start="\(<<<\)\@<='\z(\(\I\i*\)\=\(sql\)\c\(\i*\)\)'$" end="^\z1\(;\=$\)\@=" contained contains=@sqlTop keepend extend - syn region phpNowDoc matchgroup=Delimiter start="\(<<<\)\@<='\z(\(\I\i*\)\=\(javascript\)\c\(\i*\)\)'$" end="^\z1\(;\=$\)\@=" contained contains=@htmlJavascript keepend extend + syn region phpNowDoc matchgroup=Delimiter start="\(<<<\)\@<='\z(\(\I\i*\)\=\(html\)\c\(\i*\)\)'$" end="^\z1\(;\=$\)\@=" contained contains=@htmlTop,@Spell keepend extend + syn region phpNowDoc matchgroup=Delimiter start="\(<<<\)\@<='\z(\(\I\i*\)\=\(sql\)\c\(\i*\)\)'$" end="^\z1\(;\=$\)\@=" contained contains=@sqlTop,@Spell keepend extend + syn region phpNowDoc matchgroup=Delimiter start="\(<<<\)\@<='\z(\(\I\i*\)\=\(javascript\)\c\(\i*\)\)'$" end="^\z1\(;\=$\)\@=" contained contains=@htmlJavascript,@Spell keepend extend syn case ignore endif diff --git a/runtime/syntax/sh.vim b/runtime/syntax/sh.vim index 711971ea4a..34b8ab79e4 100644 --- a/runtime/syntax/sh.vim +++ b/runtime/syntax/sh.vim @@ -2,8 +2,8 @@ " Language: shell (sh) Korn shell (ksh) bash (sh) " Maintainer: Charles E. Campbell " Previous Maintainer: Lennart Schultz -" Last Change: Jun 10, 2016 -" Version: 152 +" Last Change: Jul 29, 2016 +" Version: 155 " URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_SH " For options and settings, please use: :help ft-sh-syntax " This file includes many ideas from Eric Brunet (eric.brunet@ens.fr) @@ -131,11 +131,11 @@ syn cluster shArithParenList contains=shArithmetic,shCaseEsac,shComment,shDeref, syn cluster shArithList contains=@shArithParenList,shParenError syn cluster shCaseEsacList contains=shCaseStart,shCase,shCaseBar,shCaseIn,shComment,shDeref,shDerefSimple,shCaseCommandSub,shCaseExSingleQuote,shCaseSingleQuote,shCaseDoubleQuote,shCtrlSeq,@shErrorList,shStringSpecial,shCaseRange syn cluster shCaseList contains=@shCommandSubList,shCaseEsac,shColon,shCommandSub,shComment,shDo,shEcho,shExpr,shFor,shHereDoc,shIf,shRedir,shSetList,shSource,shStatement,shVariable,shCtrlSeq -syn cluster shCommandSubList contains=shAlias,shArithmetic,shComment,shCmdParenRegion,shCtrlSeq,shDeref,shDerefSimple,shDoubleQuote,shEcho,shEscape,shExDoubleQuote,shExpr,shExSingleQuote,shNumber,shOperator,shOption,shPosnParm,shSingleQuote,shSpecial,shStatement,shSubSh,shTest,shVariable +syn cluster shCommandSubList contains=shAlias,shArithmetic,shComment,shCmdParenRegion,shCtrlSeq,shDeref,shDerefSimple,shDoubleQuote,shEcho,shEscape,shExDoubleQuote,shExpr,shExSingleQuote,shHereDoc,shNumber,shOperator,shOption,shPosnParm,shSingleQuote,shSpecial,shStatement,shSubSh,shTest,shVariable syn cluster shCurlyList contains=shNumber,shComma,shDeref,shDerefSimple,shDerefSpecial syn cluster shDblQuoteList contains=shCommandSub,shDeref,shDerefSimple,shEscape,shPosnParm,shCtrlSeq,shSpecial syn cluster shDerefList contains=shDeref,shDerefSimple,shDerefVar,shDerefSpecial,shDerefWordError,shDerefPSR,shDerefPPS -syn cluster shDerefVarList contains=shDerefOp,shDerefVarArray,shDerefOpError +syn cluster shDerefVarList contains=shDerefOff,shDerefOp,shDerefVarArray,shDerefOpError syn cluster shEchoList contains=shArithmetic,shCommandSub,shDeref,shDerefSimple,shEscape,shExpr,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shCtrlSeq,shEchoQuote syn cluster shExprList1 contains=shCharClass,shNumber,shOperator,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shExpr,shDblBrace,shDeref,shDerefSimple,shCtrlSeq syn cluster shExprList2 contains=@shExprList1,@shCaseList,shTest @@ -332,7 +332,7 @@ endif " String And Character Constants: {{{1 "================================ syn match shNumber "\<\d\+\>#\=" -syn match shNumber "-\=\.\=\d\+\>#\=" +syn match shNumber "\<-\=\.\=\d\+\>#\=" syn match shCtrlSeq "\\\d\d\d\|\\[abcfnrtv0]" contained if exists("b:is_bash") syn match shSpecial "[^\\]\(\\\\\)*\zs\\\o\o\o\|\\x\x\x\|\\c[^"]\|\\[abefnrtv]" contained @@ -366,21 +366,21 @@ syn match shQuickComment contained "#.*$" " Here Documents: {{{1 " ========================================= -ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc01 start="<<\s*\\\=\z([^ \t|]\+\)" matchgroup=shHereDoc01 end="^\z1\s*$" contains=@shDblQuoteList -ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc02 start="<<\s*\"\z([^ \t|]\+\)\"" matchgroup=shHereDoc02 end="^\z1\s*$" -ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc03 start="<<-\s*\z([^ \t|]\+\)" matchgroup=shHereDoc03 end="^\s*\z1\s*$" contains=@shDblQuoteList -ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc04 start="<<-\s*'\z([^ \t|]\+\)'" matchgroup=shHereDoc04 end="^\s*\z1\s*$" -ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc05 start="<<\s*'\z([^ \t|]\+\)'" matchgroup=shHereDoc05 end="^\z1\s*$" -ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc06 start="<<-\s*\"\z([^ \t|]\+\)\"" matchgroup=shHereDoc06 end="^\s*\z1\s*$" -ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc07 start="<<\s*\\\_$\_s*\z([^ \t|]\+\)" matchgroup=shHereDoc07 end="^\z1\s*$" contains=@shDblQuoteList -ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc08 start="<<\s*\\\_$\_s*'\z([^ \t|]\+\)'" matchgroup=shHereDoc08 end="^\z1\s*$" -ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc09 start="<<\s*\\\_$\_s*\"\z([^ \t|]\+\)\"" matchgroup=shHereDoc09 end="^\z1\s*$" -ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc10 start="<<-\s*\\\_$\_s*\z([^ \t|]\+\)" matchgroup=shHereDoc10 end="^\s*\z1\s*$" -ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc11 start="<<-\s*\\\_$\_s*\\\z([^ \t|]\+\)" matchgroup=shHereDoc11 end="^\s*\z1\s*$" -ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc12 start="<<-\s*\\\_$\_s*'\z([^ \t|]\+\)'" matchgroup=shHereDoc12 end="^\s*\z1\s*$" -ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc13 start="<<-\s*\\\_$\_s*\"\z([^ \t|]\+\)\"" matchgroup=shHereDoc13 end="^\s*\z1\s*$" -ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc14 start="<<\\\z([^ \t|]\+\)" matchgroup=shHereDoc14 end="^\z1\s*$" -ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc15 start="<<-\s*\\\z([^ \t|]\+\)" matchgroup=shHereDoc15 end="^\s*\z1\s*$" +ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc01 start="<<\s*\\\=\z([^ \t|>]\+\)" matchgroup=shHereDoc01 end="^\z1\s*$" contains=@shDblQuoteList +ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc02 start="<<\s*\"\z([^ \t|>]\+\)\"" matchgroup=shHereDoc02 end="^\z1\s*$" +ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc03 start="<<-\s*\z([^ \t|>]\+\)" matchgroup=shHereDoc03 end="^\s*\z1\s*$" contains=@shDblQuoteList +ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc04 start="<<-\s*'\z([^ \t|>]\+\)'" matchgroup=shHereDoc04 end="^\s*\z1\s*$" +ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc05 start="<<\s*'\z([^ \t|>]\+\)'" matchgroup=shHereDoc05 end="^\z1\s*$" +ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc06 start="<<-\s*\"\z([^ \t|>]\+\)\"" matchgroup=shHereDoc06 end="^\s*\z1\s*$" +ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc07 start="<<\s*\\\_$\_s*\z([^ \t|>]\+\)" matchgroup=shHereDoc07 end="^\z1\s*$" contains=@shDblQuoteList +ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc08 start="<<\s*\\\_$\_s*'\z([^ \t|>]\+\)'" matchgroup=shHereDoc08 end="^\z1\s*$" +ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc09 start="<<\s*\\\_$\_s*\"\z([^ \t|>]\+\)\"" matchgroup=shHereDoc09 end="^\z1\s*$" +ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc10 start="<<-\s*\\\_$\_s*\z([^ \t|>]\+\)" matchgroup=shHereDoc10 end="^\s*\z1\s*$" +ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc11 start="<<-\s*\\\_$\_s*\\\z([^ \t|>]\+\)" matchgroup=shHereDoc11 end="^\s*\z1\s*$" +ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc12 start="<<-\s*\\\_$\_s*'\z([^ \t|>]\+\)'" matchgroup=shHereDoc12 end="^\s*\z1\s*$" +ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc13 start="<<-\s*\\\_$\_s*\"\z([^ \t|>]\+\)\"" matchgroup=shHereDoc13 end="^\s*\z1\s*$" +ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc14 start="<<\\\z([^ \t|>]\+\)" matchgroup=shHereDoc14 end="^\z1\s*$" +ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc15 start="<<-\s*\\\z([^ \t|>]\+\)" matchgroup=shHereDoc15 end="^\s*\z1\s*$" " Here Strings: {{{1 " ============= @@ -427,7 +427,7 @@ endif if !exists("g:sh_no_error") syn match shDerefWordError "[^}$[~]" contained endif -syn match shDerefSimple "\$\%(\k\+\|\d\)" +syn match shDerefSimple "\$\%(\h\w*\|\d\)" syn region shDeref matchgroup=PreProc start="\${" end="}" contains=@shDerefList,shDerefVarArray syn match shDerefSimple "\$[-#*@!?]" syn match shDerefSimple "\$\$" @@ -445,18 +445,18 @@ endif " bash: ${!prefix*} and ${#parameter}: {{{1 " ==================================== if exists("b:is_bash") - syn region shDeref matchgroup=PreProc start="\${!" end="\*\=}" contains=@shDerefList,shDerefOp - syn match shDerefVar contained "{\@<=!\k\+" nextgroup=@shDerefVarList + syn region shDeref matchgroup=PreProc start="\${!" end="\*\=}" contains=@shDerefList,shDerefOff + syn match shDerefVar contained "{\@<=!\h\w*" nextgroup=@shDerefVarList endif if exists("b:is_kornshell") - syn match shDerefVar contained "{\@<=!\k[[:alnum:]_.]*" nextgroup=@shDerefVarList + syn match shDerefVar contained "{\@<=!\h\w*[[:alnum:]_.]*" nextgroup=@shDerefVarList endif syn match shDerefSpecial contained "{\@<=[-*@?0]" nextgroup=shDerefOp,shDerefOpError syn match shDerefSpecial contained "\({[#!]\)\@<=[[:alnum:]*@_]\+" nextgroup=@shDerefVarList,shDerefOp -syn match shDerefVar contained "{\@<=\k\+" nextgroup=@shDerefVarList +syn match shDerefVar contained "{\@<=\h\w*" nextgroup=@shDerefVarList if exists("b:is_kornshell") - syn match shDerefVar contained "{\@<=\k[[:alnum:]_.]*" nextgroup=@shDerefVarList + syn match shDerefVar contained "{\@<=\h\w*[[:alnum:]_.]*" nextgroup=@shDerefVarList endif " sh ksh bash : ${var[... ]...} array reference: {{{1 @@ -498,8 +498,9 @@ syn match shDerefString contained "\\["']" nextgroup=shDerefPattern if exists("b:is_bash") " bash : ${parameter:offset} " bash : ${parameter:offset:length} - syn region shDerefOp contained start=":[$[:alnum:]_]"me=e-1 end=":"me=e-1 end="}"me=e-1 contains=@shCommandSubList nextgroup=shDerefPOL - syn match shDerefPOL contained ":[^}]\+" contains=@shCommandSubList + syn region shDerefOff contained start=':' end='\ze:' end='\ze}' contains=shDeref,shDerefSimple nextgroup=shDerefLen,shDeref,shDerefSimple + syn region shDerefOff contained start=':\s-' end='\ze:' end='\ze}' contains=shDeref,shDerefSimple nextgroup=shDerefLen,shDeref,shDerefSimple + syn match shDerefLen contained ":[^}]\+" contains=shDeref,shDerefSimple " bash : ${parameter//pattern/string} " bash : ${parameter//pattern} @@ -636,6 +637,8 @@ if exists("b:is_bash") hi def link bashSpecialVariables shShellVariables hi def link bashStatement shStatement hi def link shCharClass shSpecial + hi def link shDerefOff shDerefOp + hi def link shDerefLen shDerefOff endif if exists("b:is_kornshell") hi def link kshSpecialVariables shShellVariables diff --git a/runtime/syntax/tex.vim b/runtime/syntax/tex.vim index e560573e6e..c0982d84da 100644 --- a/runtime/syntax/tex.vim +++ b/runtime/syntax/tex.vim @@ -1,8 +1,8 @@ " Vim syntax file " Language: TeX " Maintainer: Charles E. Campbell -" Last Change: Jun 17, 2016 -" Version: 97 +" Last Change: Jul 05, 2016 +" Version: 98 " URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_TEX " " Notes: {{{1 @@ -132,14 +132,20 @@ endif " One may override this iskeyword setting by providing " g:tex_isk if exists("g:tex_isk") - exe "setlocal isk=".g:tex_isk -elseif !has("patch-7.4.1142") - setl isk=48-57,a-z,A-Z,192-255 + if b:tex_stylish && g:tex_isk !~ '@' + let b:tex_isk= '@,'.g:tex_isk + else + let b:tex_isk= g:tex_isk + endif +elseif b:tex_stylish + let b:tex_isk="@,48-57,a-z,A-Z,192-255" else - syn iskeyword 48-57,a-z,A-Z,192-255 + let b:tex_isk="48-57,a-z,A-Z,192-255" endif -if b:tex_stylish - setlocal isk+=@-@ +if v:version > 704 || (v:version == 704 && has("patch-7.4.1142")) + exe "syn iskeyword ".b:tex_isk +else + exe "setl isk=".b:tex_isk endif if exists("g:tex_no_error") && g:tex_no_error let s:tex_no_error= 1 @@ -159,7 +165,7 @@ endif " Clusters: {{{1 " -------- -syn cluster texCmdGroup contains=texCmdBody,texComment,texDefParm,texDelimiter,texDocType,texInput,texLength,texLigature,texMathDelim,texMathOper,texNewCmd,texNewEnv,texRefZone,texSection,texBeginEnd,texBeginEndName,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle +syn cluster texCmdGroup contains=texCmdBody,texComment,texDefParm,texDelimiter,texDocType,texInput,texLength,texLigature,texMathDelim,texMathOper,texNewCmd,texNewEnv,texRefZone,texSection,texBeginEnd,texBeginEndName,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,@texMathZones if !s:tex_no_error syn cluster texCmdGroup add=texMathError endif @@ -447,9 +453,8 @@ if !exists("g:tex_no_math") call TexNewMathZone("G","gather",1) call TexNewMathZone("H","math",1) call TexNewMathZone("I","multline",1) - call TexNewMathZone("J","subequations",0) - call TexNewMathZone("K","xalignat",1) - call TexNewMathZone("L","xxalignat",0) + call TexNewMathZone("J","xalignat",1) + call TexNewMathZone("K","xxalignat",0) " Inline Math Zones: {{{2 if s:tex_fast =~# 'M' @@ -481,8 +486,9 @@ if !exists("g:tex_no_math") " \left..something.. and \right..something.. support: {{{2 syn match texMathDelimBad contained "\S" if has("conceal") && &enc == 'utf-8' && s:tex_conceal =~# 'm' - syn match texMathDelim contained "\\left\\{\>" skipwhite nextgroup=texMathDelimSet1,texMathDelimSet2,texMathDelimBad contains=texMathSymbol cchar={ - syn match texMathDelim contained "\\right\\}\>" skipwhite nextgroup=texMathDelimSet1,texMathDelimSet2,texMathDelimBad contains=texMathSymbol cchar=} + syn match texMathDelim contained "\\left\[" + syn match texMathDelim contained "\\left\\{" skipwhite nextgroup=texMathDelimSet1,texMathDelimSet2,texMathDelimBad contains=texMathSymbol cchar={ + syn match texMathDelim contained "\\right\\}" skipwhite nextgroup=texMathDelimSet1,texMathDelimSet2,texMathDelimBad contains=texMathSymbol cchar=} let s:texMathDelimList=[ \ ['<' , '<'] , \ ['>' , '>'] , diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 63618e902e..bb3fa1f6c9 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -238,7 +238,7 @@ if !exists("g:vimsyn_noerror") && !exists("g:vimsyn_nousercmderror") endif syn case ignore syn keyword vimUserAttrbKey contained bar ban[g] cou[nt] ra[nge] com[plete] n[args] re[gister] -syn keyword vimUserAttrbCmplt contained augroup buffer color command compiler cscope dir environment event expression file file_in_path filetype function help highlight locale mapping menu option shellcmd sign syntax tag tag_listfiles var +syn keyword vimUserAttrbCmplt contained augroup buffer behave color command compiler cscope dir environment event expression file file_in_path filetype function help highlight history locale mapping menu option packadd shellcmd sign syntax syntime tag tag_listfiles user var syn keyword vimUserAttrbCmplt contained custom customlist nextgroup=vimUserAttrbCmpltFunc,vimUserCmdError syn match vimUserAttrbCmpltFunc contained ",\%([sS]:\|<[sS][iI][dD]>\)\=\%(\h\w*\%(#\h\w*\)\+\|\h\w*\)"hs=s+1 nextgroup=vimUserCmdError @@ -390,7 +390,7 @@ syn match vimNotation "\(\\\|\)\=<\([scamd]-\)\{0,4}x\=\(f\d\{1,2}\|[^ \t:]\ syn match vimNotation "\(\\\|\)\=<\([scam2-4]-\)\{0,4}\(right\|left\|middle\)\(mouse\)\=\(drag\|release\)\=>" contains=vimBracket syn match vimNotation "\(\\\|\)\=<\(bslash\|plug\|sid\|space\|bar\|nop\|nul\|lt\)>" contains=vimBracket syn match vimNotation '\(\\\|\)\=[0-9a-z"%#:.\-=]'he=e-1 contains=vimBracket -syn match vimNotation '\(\\\|\)\=<\%(q-\)\=\(line[12]\|count\|bang\|reg\|args\|f-args\|lt\)>' contains=vimBracket +syn match vimNotation '\(\\\|\)\=<\%(q-\)\=\(line[12]\|count\|bang\|reg\|args\|mods\|f-args\|f-mods\|lt\)>' contains=vimBracket syn match vimNotation "\(\\\|\)\=<\([cas]file\|abuf\|amatch\|cword\|cWORD\|client\)>" contains=vimBracket syn match vimBracket contained "[\\<>]" syn case match From 26d7757ccbc57125a3dfe3b2cad0f07a2e567df1 Mon Sep 17 00:00:00 2001 From: lonerover Date: Fri, 17 Mar 2017 23:09:15 +0800 Subject: [PATCH 0045/1671] vim-patch:7.4.2256 Problem: Coverity complains about null pointer check. Solution: Remove wrong and superfluous error check. https://github.com/vim/vim/commit/db249f26edf7a5f88d1f4468d08ec5b84f5ab7ad --- src/nvim/eval.c | 5 ++++- src/nvim/version.c | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d4daffb469..5de999f175 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4860,7 +4860,10 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate) } *name = NUL; - *arg = p + 1; + if (*p != NUL) { // just in case + p++; + } + *arg = p; return OK; } diff --git a/src/nvim/version.c b/src/nvim/version.c index b9286c2a4e..5cdd0d96fe 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -185,7 +185,7 @@ static int included_patches[] = { // 2259, // 2258 NA // 2257 NA - // 2256, + 2256, 2255, // 2254 NA // 2253 NA From 7418adc14375161b504e78c77c5c4e532016902c Mon Sep 17 00:00:00 2001 From: lonerover Date: Tue, 21 Mar 2017 21:36:31 +0800 Subject: [PATCH 0046/1671] move.c: add cursor adjustment for scrolloff (#6319) --- src/nvim/move.c | 1 + src/nvim/testdir/test_normal.vim | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/move.c b/src/nvim/move.c index 4c3f82bc16..4feabf624b 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1877,6 +1877,7 @@ int onepage(int dir, long count) } } foldAdjustCursor(); + cursor_correct(); check_cursor_col(); if (retval == OK) { beginline(BL_SOL | BL_FIX); diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 29a7c1edd8..a22dca35cc 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -433,7 +433,6 @@ func! Test_normal13_help() endfunc func! Test_normal14_page() - throw "skipped: Nvim regression: CTRL-F with 'scrolloff'" " basic test for Ctrl-F and Ctrl-B call Setup_NewWindow() exe "norm! \" From 6baa669c10518b8751904a31899feb897fb7c995 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 17:07:00 +0100 Subject: [PATCH 0047/1671] vim-patch:7.4.2164 (#6326) Problem: It is not possible to use plugins in an "after" directory to tune the behavior of a package. Solution: First load plugins from non-after directories, then packages and finally plugins in after directories. Reset 'loadplugins' before executing --cmd arguments. https://github.com/vim/vim/commit/66459b7c98c67f8a9d39de8f08e8e8f1fca0e359 vim-patch:7.4.2172 vim-patch:7.4.2169 vim-patch:7.4.2177 vim-patch:7.4.2178 vim-patch:7.4.2184 vim-patch:8.0.0050 vim-patch:8.0.0105 vim-patch:8.0.0400 vim-patch:8.0.0405 Closes #6034 --- runtime/doc/starting.txt | 9 +- src/nvim/ex_cmds2.c | 16 ++- src/nvim/main.c | 13 +- src/nvim/testdir/setup.vim | 3 +- src/nvim/testdir/shared.vim | 214 ++++++++++++++++++++++++++++++ src/nvim/testdir/test_startup.vim | 200 ++++++++++++++++++++++++++++ src/nvim/version.c | 12 +- src/nvim/vim.h | 2 + 8 files changed, 457 insertions(+), 12 deletions(-) create mode 100644 src/nvim/testdir/shared.vim create mode 100644 src/nvim/testdir/test_startup.vim diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index 2c024d3c75..b168d2cb98 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -457,6 +457,8 @@ accordingly. Vim proceeds in this order: searched for the "plugin" sub-directory and all files ending in ".vim" will be sourced (in alphabetical order per directory), also in subdirectories. + However, directories in 'runtimepath' ending in "after" are skipped + here and only loaded after packages, see below. Loading plugins won't be done when: - The 'loadplugins' option was reset in a vimrc file. - The |--noplugin| command line argument is used. @@ -464,13 +466,18 @@ accordingly. Vim proceeds in this order: - When Vim was compiled without the |+eval| feature. Note that using "-c 'set noloadplugins'" doesn't work, because the commands from the command line have not been executed yet. You can - use "--cmd 'set noloadplugins'" |--cmd|. + use "--cmd 'set noloadplugins'" or "--cmd 'set loadplugins'" |--cmd|. Packages are loaded. These are plugins, as above, but found in the "start" directory of each entry in 'packpath'. Every plugin directory found is added in 'runtimepath' and then the plugins are sourced. See |packages|. + The plugins scripts are loaded, as above, but now only the directories + ending in "after" are used. Note that 'runtimepath' will have changed + if packages have been found, but that should not add a directory + ending in "after". + 7. Set 'shellpipe' and 'shellredir' The 'shellpipe' and 'shellredir' options are set according to the value of the 'shell' option, unless they have been set before. diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 36f50ae7a2..9fc4ef2a02 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -2361,12 +2361,25 @@ int do_in_path(char_u *path, char_u *name, int flags, while (*rtp != NUL && ((flags & DIP_ALL) || !did_one)) { // Copy the path from 'runtimepath' to buf[]. copy_option_part(&rtp, buf, MAXPATHL, ","); + size_t buflen = STRLEN(buf); + + // Skip after or non-after directories. + if (flags & (DIP_NOAFTER | DIP_AFTER)) { + bool is_after = buflen >= 5 + && STRCMP(buf + buflen - 5, "after") == 0; + + if ((is_after && (flags & DIP_NOAFTER)) + || (!is_after && (flags & DIP_AFTER))) { + continue; + } + } + if (name == NULL) { (*callback)(buf, (void *)&cookie); if (!did_one) { did_one = (cookie == NULL); } - } else if (STRLEN(buf) + STRLEN(name) + 2 < MAXPATHL) { + } else if (buflen + STRLEN(name) + 2 < MAXPATHL) { add_pathsep((char *)buf); tail = buf + STRLEN(buf); @@ -2597,6 +2610,7 @@ static bool did_source_packages = false; // ":packloadall" // Find plugins in the package directories and source them. +// "eap" is NULL when invoked during startup. void ex_packloadall(exarg_T *eap) { if (!did_source_packages || (eap != NULL && eap->forceit)) { diff --git a/src/nvim/main.c b/src/nvim/main.c index 4416bd067c..7b1c912f4b 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -326,6 +326,12 @@ int main(int argc, char **argv) do_cmdline_cmd("augroup END"); #undef PROTO + // Reset 'loadplugins' for "-u NONE" before "--cmd" arguments. + // Allows for setting 'loadplugins' there. + if (params.use_vimrc != NULL && strcmp(params.use_vimrc, "NONE") == 0) { + p_lpl = false; + } + /* Execute --cmd arguments. */ exe_pre_commands(¶ms); @@ -1268,11 +1274,14 @@ static void set_window_layout(mparm_T *paramp) static void load_plugins(void) { if (p_lpl) { - source_runtime((char_u *)"plugin/**/*.vim", DIP_ALL); // NOLINT + source_runtime((char_u *)"plugin/**/*.vim", DIP_ALL | DIP_NOAFTER); // NOLINT TIME_MSG("loading plugins"); ex_packloadall(NULL); TIME_MSG("loading packages"); + + source_runtime((char_u *)"plugin/**/*.vim", DIP_ALL | DIP_AFTER); + TIME_MSG("loading after plugins"); } } @@ -1708,8 +1717,6 @@ static void source_startup_scripts(const mparm_T *const parmp) if (parmp->use_vimrc != NULL) { if (strcmp(parmp->use_vimrc, "NONE") == 0 || strcmp(parmp->use_vimrc, "NORC") == 0) { - if (parmp->use_vimrc[2] == 'N') - p_lpl = false; // don't load plugins either } else { if (do_source((char_u *)parmp->use_vimrc, FALSE, DOSO_NONE) != OK) EMSG2(_("E282: Cannot read from \"%s\""), parmp->use_vimrc); diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim index 05257d566d..06f2199214 100644 --- a/src/nvim/testdir/setup.vim +++ b/src/nvim/testdir/setup.vim @@ -4,8 +4,9 @@ set noruler set noshowcmd set belloff= -" Make sure 'runtimepath' does not include $HOME. +" Make sure 'runtimepath' and 'packpath' does not include $HOME. set rtp=$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after +let &packpath = &rtp " Make sure $HOME does not get read or written. let $HOME = '/does/not/exist' diff --git a/src/nvim/testdir/shared.vim b/src/nvim/testdir/shared.vim new file mode 100644 index 0000000000..784e4a0a02 --- /dev/null +++ b/src/nvim/testdir/shared.vim @@ -0,0 +1,214 @@ +" Functions shared by several tests. + +" Get the name of the Python executable. +" Also keeps it in s:python. +func PythonProg() + " This test requires the Python command to run the test server. + " This most likely only works on Unix and Windows. + if has('unix') + " We also need the job feature or the pkill command to make sure the server + " can be stopped. + if !(executable('python') && (has('job') || executable('pkill'))) + return '' + endif + let s:python = 'python' + elseif has('win32') + " Use Python Launcher for Windows (py.exe) if available. + if executable('py.exe') + let s:python = 'py.exe' + elseif executable('python.exe') + let s:python = 'python.exe' + else + return '' + endif + else + return '' + endif + return s:python +endfunc + +" Run "cmd". Returns the job if using a job. +func RunCommand(cmd) + let job = 0 + if has('job') + let job = job_start(a:cmd, {"stoponexit": "hup"}) + call job_setoptions(job, {"stoponexit": "kill"}) + elseif has('win32') + exe 'silent !start cmd /c start "test_channel" ' . a:cmd + else + exe 'silent !' . a:cmd . '&' + endif + return job +endfunc + +" Read the port number from the Xportnr file. +func GetPort() + let l = [] + for i in range(200) + try + let l = readfile("Xportnr") + catch + endtry + if len(l) >= 1 + break + endif + sleep 10m + endfor + call delete("Xportnr") + + if len(l) == 0 + " Can't make the connection, give up. + return 0 + endif + return l[0] +endfunc + +" Run a Python server for "cmd" and call "testfunc". +" Always kills the server before returning. +func RunServer(cmd, testfunc, args) + " The Python program writes the port number in Xportnr. + call delete("Xportnr") + + if len(a:args) == 1 + let arg = ' ' . a:args[0] + else + let arg = '' + endif + let pycmd = s:python . " " . a:cmd . arg + + try + let g:currentJob = RunCommand(pycmd) + + " Wait for up to 2 seconds for the port number to be there. + let port = GetPort() + if port == 0 + call assert_false(1, "Can't start " . a:cmd) + return + endif + + call call(function(a:testfunc), [port]) + catch + call assert_false(1, 'Caught exception: "' . v:exception . '" in ' . v:throwpoint) + finally + call s:kill_server(a:cmd) + endtry +endfunc + +func s:kill_server(cmd) + if has('job') + if exists('g:currentJob') + call job_stop(g:currentJob) + unlet g:currentJob + endif + elseif has('win32') + let cmd = substitute(a:cmd, ".py", '', '') + call system('taskkill /IM ' . s:python . ' /T /F /FI "WINDOWTITLE eq ' . cmd . '"') + else + call system("pkill -f " . a:cmd) + endif +endfunc + +" Wait for up to a second for "expr" to become true. +" Return time slept in milliseconds. With the +reltime feature this can be +" more than the actual waiting time. Without +reltime it can also be less. +func WaitFor(expr) + " using reltime() is more accurate, but not always available + if has('reltime') + let start = reltime() + else + let slept = 0 + endif + for i in range(100) + try + if eval(a:expr) + if has('reltime') + return float2nr(reltimefloat(reltime(start)) * 1000) + endif + return slept + endif + catch + endtry + if !has('reltime') + let slept += 10 + endif + sleep 10m + endfor + return 1000 +endfunc + +" Wait for up to a given milliseconds. +" With the +timers feature this waits for key-input by getchar(), Resume() +" feeds key-input and resumes process. Return time waited in milliseconds. +" Without +timers it uses simply :sleep. +func Standby(msec) + if has('timers') + let start = reltime() + let g:_standby_timer = timer_start(a:msec, function('s:feedkeys')) + call getchar() + return float2nr(reltimefloat(reltime(start)) * 1000) + else + execute 'sleep ' a:msec . 'm' + return a:msec + endif +endfunc + +func Resume() + if exists('g:_standby_timer') + call timer_stop(g:_standby_timer) + call s:feedkeys(0) + unlet g:_standby_timer + endif +endfunc + +func s:feedkeys(timer) + call feedkeys('x', 'nt') +endfunc + +" Get the command to run Vim, with -u NONE and --headless arguments. +" Returns an empty string on error. +func GetVimCommand() + let cmd = v:progpath + let cmd = substitute(cmd, '-u \f\+', '-u NONE', '') + if cmd !~ '-u NONE' + let cmd = cmd . ' -u NONE' + endif + let cmd .= ' --headless -i NONE' + let cmd = substitute(cmd, 'VIMRUNTIME=.*VIMRUNTIME;', '', '') + return cmd +endfunc + +" Run Vim, using the "vimcmd" file and "-u NORC". +" "before" is a list of Vim commands to be executed before loading plugins. +" "after" is a list of Vim commands to be executed after loading plugins. +" Plugins are not loaded, unless 'loadplugins' is set in "before". +" Return 1 if Vim could be executed. +func RunVim(before, after, arguments) + return RunVimPiped(a:before, a:after, a:arguments, '') +endfunc + +func RunVimPiped(before, after, arguments, pipecmd) + let $NVIM_LOG_FILE='Xnvim.log' + let cmd = GetVimCommand() + if cmd == '' + return 0 + endif + let args = '' + if len(a:before) > 0 + call writefile(a:before, 'Xbefore.vim') + let args .= ' --cmd "so Xbefore.vim"' + endif + if len(a:after) > 0 + call writefile(a:after, 'Xafter.vim') + let args .= ' -S Xafter.vim' + endif + + exe "silent !" . a:pipecmd . cmd . args . ' ' . a:arguments + + if len(a:before) > 0 + call delete('Xbefore.vim') + endif + if len(a:after) > 0 + call delete('Xafter.vim') + endif + return 1 +endfunc diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim new file mode 100644 index 0000000000..f78d92f3ff --- /dev/null +++ b/src/nvim/testdir/test_startup.vim @@ -0,0 +1,200 @@ +" Tests for startup. + +source shared.vim + +" Check that loading startup.vim works. +func Test_startup_script() + throw 'skipped: Nvim does not need defaults.vim' + set compatible + source $VIMRUNTIME/defaults.vim + + call assert_equal(0, &compatible) +endfunc + +" Verify the order in which plugins are loaded: +" 1. plugins in non-after directories +" 2. packages +" 3. plugins in after directories +func Test_after_comes_later() + if !has('packages') + return + endif + let before = [ + \ 'set nocp viminfo+=nviminfo', + \ 'set guioptions+=M', + \ 'let $HOME = "/does/not/exist"', + \ 'set loadplugins', + \ 'set rtp=Xhere,Xafter', + \ 'set packpath=Xhere,Xafter', + \ 'set nomore', + \ ] + let after = [ + \ 'redir! > Xtestout', + \ 'scriptnames', + \ 'redir END', + \ 'quit', + \ ] + call mkdir('Xhere/plugin', 'p') + call writefile(['let done = 1'], 'Xhere/plugin/here.vim') + call mkdir('Xhere/pack/foo/start/foobar/plugin', 'p') + call writefile(['let done = 1'], 'Xhere/pack/foo/start/foobar/plugin/foo.vim') + + call mkdir('Xafter/plugin', 'p') + call writefile(['let done = 1'], 'Xafter/plugin/later.vim') + + if RunVim(before, after, '') + + let lines = readfile('Xtestout') + let expected = ['Xbefore.vim', 'here.vim', 'foo.vim', 'later.vim', 'Xafter.vim'] + let found = [] + for line in lines + for one in expected + if line =~ one + call add(found, one) + endif + endfor + endfor + call assert_equal(expected, found) + endif + + call delete('Xtestout') + call delete('Xhere', 'rf') + call delete('Xafter', 'rf') +endfunc + +func Test_help_arg() + if !has('unix') && has('gui') + " this doesn't work with gvim on MS-Windows + return + endif + if RunVim([], [], '--help >Xtestout') + let lines = readfile('Xtestout') + call assert_true(len(lines) > 20) + call assert_match('Usage:', lines[0]) + + " check if couple of lines are there + let found = [] + for line in lines + if line =~ '-R.*Readonly mode' + call add(found, 'Readonly mode') + endif + " Watch out for a second --version line in the Gnome version. + if line =~ '--version.*Print version information and exit' + call add(found, "--version") + endif + endfor + call assert_equal(['--version'], found) + endif + call delete('Xtestout') +endfunc + +func Test_compatible_args() + throw "skipped: Nvim is always 'nocompatible'" + let after = [ + \ 'call writefile([string(&compatible)], "Xtestout")', + \ 'set viminfo+=nviminfo', + \ 'quit', + \ ] + if RunVim([], after, '-C') + let lines = readfile('Xtestout') + call assert_equal('1', lines[0]) + endif + + if RunVim([], after, '-N') + let lines = readfile('Xtestout') + call assert_equal('0', lines[0]) + endif + + call delete('Xtestout') +endfunc + +func Test_file_args() + let after = [ + \ 'call writefile(argv(), "Xtestout")', + \ 'qall', + \ ] + if RunVim([], after, '') + let lines = readfile('Xtestout') + call assert_equal(0, len(lines)) + endif + + if RunVim([], after, 'one') + let lines = readfile('Xtestout') + call assert_equal(1, len(lines)) + call assert_equal('one', lines[0]) + endif + + if RunVim([], after, 'one two three') + let lines = readfile('Xtestout') + call assert_equal(3, len(lines)) + call assert_equal('one', lines[0]) + call assert_equal('two', lines[1]) + call assert_equal('three', lines[2]) + endif + + if RunVim([], after, 'one -c echo two') + let lines = readfile('Xtestout') + call assert_equal(2, len(lines)) + call assert_equal('one', lines[0]) + call assert_equal('two', lines[1]) + endif + + if RunVim([], after, 'one -- -c echo two') + let lines = readfile('Xtestout') + call assert_equal(4, len(lines)) + call assert_equal('one', lines[0]) + call assert_equal('-c', lines[1]) + call assert_equal('echo', lines[2]) + call assert_equal('two', lines[3]) + endif + + call delete('Xtestout') +endfunc + +func Test_startuptime() + if !has('startuptime') + return + endif + let after = ['qall'] + if RunVim([], after, '--startuptime Xtestout one') + let lines = readfile('Xtestout') + let expected = ['parsing arguments', 'inits 3', 'opening buffers'] + let found = [] + for line in lines + for exp in expected + if line =~ exp + call add(found, exp) + endif + endfor + endfor + call assert_equal(expected, found) + endif + call delete('Xtestout') +endfunc + +func Test_read_stdin() + let after = [ + \ 'write Xtestout', + \ 'quit!', + \ ] + if RunVimPiped([], after, '-', 'echo something | ') + let lines = readfile('Xtestout') + " MS-Windows adds a space after the word + call assert_equal(['something'], split(lines[0])) + endif + call delete('Xtestout') +endfunc + +func Test_progpath() + " Tests normally run with "./vim" or "../vim", these must have been expanded + " to a full path. + if has('unix') + call assert_equal('/', v:progpath[0]) + elseif has('win32') + call assert_equal(':', v:progpath[1]) + call assert_match('[/\\]', v:progpath[2]) + endif + + " Only expect "vim" to appear in v:progname. + call assert_match('vim\c', v:progname) +endfunc diff --git a/src/nvim/version.c b/src/nvim/version.c index bf13e52be4..1d4239b215 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -257,27 +257,27 @@ static int included_patches[] = { 2187, // 2186 NA 2185, - // 2184, + 2184, 2183, // 2182 NA // 2181, // 2180, // 2179, - // 2178, - // 2177, + 2178, + 2177, // 2176 NA 2175, 2174, // 2173, - // 2172, + 2172, // 2171, // 2170, - // 2169, + 2169, // 2168 NA // 2167 NA // 2166 NA // 2165, - // 2164, + 2164, 2163, 2162, // 2161, diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 458d23fcad..87e9889465 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -325,6 +325,8 @@ enum { #define DIP_START 0x08 // also use "start" directory in 'packpath' #define DIP_OPT 0x10 // also use "opt" directory in 'packpath' #define DIP_NORTP 0x20 // do not use 'runtimepath' +#define DIP_NOAFTER 0x40 // skip "after" directories +#define DIP_AFTER 0x80 // only use "after" directories // Lowest number used for window ID. Cannot have this many windows per tab. #define LOWEST_WIN_ID 1000 From 82c67768fa6b56689f22b7ba5f1e4bcf620aaa7d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 17:08:19 +0100 Subject: [PATCH 0048/1671] doc: Replace "For Vim ... Last change ..." headers (#6328) --- runtime/doc/api.txt | 2 +- runtime/doc/arabic.txt | 2 +- runtime/doc/autocmd.txt | 2 +- runtime/doc/change.txt | 2 +- runtime/doc/cmdline.txt | 2 +- runtime/doc/debug.txt | 2 +- runtime/doc/deprecated.txt | 2 +- runtime/doc/diff.txt | 2 +- runtime/doc/digraph.txt | 2 +- runtime/doc/editing.txt | 2 +- runtime/doc/eval.txt | 2 +- runtime/doc/farsi.txt | 2 +- runtime/doc/filetype.txt | 2 +- runtime/doc/fold.txt | 2 +- runtime/doc/ft_ada.txt | 2 +- runtime/doc/ft_sql.txt | 2 +- runtime/doc/gui.txt | 2 +- runtime/doc/hebrew.txt | 2 +- runtime/doc/help.txt | 2 +- runtime/doc/helphelp.txt | 2 +- runtime/doc/howto.txt | 2 +- runtime/doc/if_cscop.txt | 2 +- runtime/doc/if_pyth.txt | 2 +- runtime/doc/indent.txt | 2 +- runtime/doc/index.txt | 2 +- runtime/doc/insert.txt | 2 +- runtime/doc/intro.txt | 2 +- runtime/doc/job_control.txt | 2 +- runtime/doc/map.txt | 2 +- runtime/doc/mbyte.txt | 2 +- runtime/doc/message.txt | 2 +- runtime/doc/mlang.txt | 2 +- runtime/doc/motion.txt | 2 +- runtime/doc/nvim_terminal_emulator.txt | 2 +- runtime/doc/options.txt | 2 +- runtime/doc/os_win32.txt | 2 +- runtime/doc/pattern.txt | 2 +- runtime/doc/pi_gzip.txt | 2 +- runtime/doc/pi_msgpack.txt | 2 +- runtime/doc/pi_netrw.txt | 2 +- runtime/doc/pi_paren.txt | 2 +- runtime/doc/pi_spec.txt | 2 +- runtime/doc/pi_tar.txt | 2 +- runtime/doc/pi_zip.txt | 2 +- runtime/doc/print.txt | 2 +- runtime/doc/provider.txt | 2 +- runtime/doc/quickfix.txt | 2 +- runtime/doc/quickref.txt | 2 +- runtime/doc/recover.txt | 2 +- runtime/doc/remote.txt | 2 +- runtime/doc/remote_plugin.txt | 2 +- runtime/doc/repeat.txt | 2 +- runtime/doc/rileft.txt | 2 +- runtime/doc/russian.txt | 2 +- runtime/doc/scroll.txt | 2 +- runtime/doc/sign.txt | 2 +- runtime/doc/sponsor.txt | 2 +- runtime/doc/starting.txt | 2 +- runtime/doc/syntax.txt | 2 +- runtime/doc/tabpage.txt | 2 +- runtime/doc/tagsrch.txt | 2 +- runtime/doc/term.txt | 2 +- runtime/doc/tips.txt | 2 +- runtime/doc/uganda.txt | 2 +- runtime/doc/undo.txt | 2 +- runtime/doc/usr_01.txt | 2 +- runtime/doc/usr_02.txt | 2 +- runtime/doc/usr_03.txt | 2 +- runtime/doc/usr_04.txt | 2 +- runtime/doc/usr_05.txt | 2 +- runtime/doc/usr_06.txt | 2 +- runtime/doc/usr_07.txt | 2 +- runtime/doc/usr_08.txt | 2 +- runtime/doc/usr_09.txt | 2 +- runtime/doc/usr_10.txt | 2 +- runtime/doc/usr_11.txt | 2 +- runtime/doc/usr_12.txt | 2 +- runtime/doc/usr_20.txt | 2 +- runtime/doc/usr_21.txt | 2 +- runtime/doc/usr_22.txt | 2 +- runtime/doc/usr_23.txt | 2 +- runtime/doc/usr_24.txt | 2 +- runtime/doc/usr_25.txt | 2 +- runtime/doc/usr_26.txt | 2 +- runtime/doc/usr_27.txt | 2 +- runtime/doc/usr_28.txt | 2 +- runtime/doc/usr_29.txt | 2 +- runtime/doc/usr_30.txt | 2 +- runtime/doc/usr_31.txt | 2 +- runtime/doc/usr_32.txt | 2 +- runtime/doc/usr_40.txt | 2 +- runtime/doc/usr_41.txt | 2 +- runtime/doc/usr_42.txt | 2 +- runtime/doc/usr_43.txt | 2 +- runtime/doc/usr_44.txt | 2 +- runtime/doc/usr_45.txt | 2 +- runtime/doc/usr_toc.txt | 2 +- runtime/doc/various.txt | 2 +- runtime/doc/vi_diff.txt | 2 +- runtime/doc/vim_diff.txt | 2 +- runtime/doc/visual.txt | 2 +- runtime/doc/windows.txt | 2 +- 102 files changed, 102 insertions(+), 102 deletions(-) diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 122ab37e72..2bcc996d8b 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -1,4 +1,4 @@ -*api.txt* {Nvim} +*api.txt* Nvim NVIM REFERENCE MANUAL by Thiago de Arruda diff --git a/runtime/doc/arabic.txt b/runtime/doc/arabic.txt index 6f17b38a7a..3f30d7b5bc 100644 --- a/runtime/doc/arabic.txt +++ b/runtime/doc/arabic.txt @@ -1,4 +1,4 @@ -*arabic.txt* For Vim version 7.4. Last change: 2010 Nov 13 +*arabic.txt* Nvim VIM REFERENCE MANUAL by Nadim Shaikli diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index b865cb9bc9..e7c7b0d71a 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -1,4 +1,4 @@ -*autocmd.txt* For Vim version 7.4. Last change: 2016 Jun 09 +*autocmd.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index 31a46f53bb..c8576d83e8 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -1,4 +1,4 @@ -*change.txt* For Vim version 7.4. Last change: 2016 Apr 12 +*change.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index a123ea711b..7277e1d22e 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -1,4 +1,4 @@ -*cmdline.txt* For Vim version 7.4. Last change: 2015 Dec 17 +*cmdline.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/debug.txt b/runtime/doc/debug.txt index f03116ffed..c5414e91ca 100644 --- a/runtime/doc/debug.txt +++ b/runtime/doc/debug.txt @@ -1,4 +1,4 @@ -*debug.txt* For Vim version 7.4. Last change: 2012 Feb 11 +*debug.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt index ef00143709..822019340a 100644 --- a/runtime/doc/deprecated.txt +++ b/runtime/doc/deprecated.txt @@ -1,4 +1,4 @@ -*deprecated.txt* {Nvim} +*deprecated.txt* Nvim NVIM REFERENCE MANUAL diff --git a/runtime/doc/diff.txt b/runtime/doc/diff.txt index 6b45f8552f..12f4563518 100644 --- a/runtime/doc/diff.txt +++ b/runtime/doc/diff.txt @@ -1,4 +1,4 @@ -*diff.txt* For Vim version 7.4. Last change: 2016 Aug 24 +*diff.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/digraph.txt b/runtime/doc/digraph.txt index 84024fd1b3..20fbe60602 100644 --- a/runtime/doc/digraph.txt +++ b/runtime/doc/digraph.txt @@ -1,4 +1,4 @@ -*digraph.txt* For Vim version 7.4. Last change: 2014 Jun 19 +*digraph.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt index 5bfffac1f1..57458b7b81 100644 --- a/runtime/doc/editing.txt +++ b/runtime/doc/editing.txt @@ -1,4 +1,4 @@ -*editing.txt* For Vim version 7.4. Last change: 2016 Aug 06 +*editing.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 238e1758e5..a52b3cd080 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 7.4. Last change: 2016 Aug 27 +*eval.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/farsi.txt b/runtime/doc/farsi.txt index b85c0a357c..a824c469b0 100644 --- a/runtime/doc/farsi.txt +++ b/runtime/doc/farsi.txt @@ -1,4 +1,4 @@ -*farsi.txt* For Vim version 7.4. Last change: 2010 Aug 07 +*farsi.txt* Nvim VIM REFERENCE MANUAL by Mortaza Ghassab Shiran diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index d15815191e..4c7360c88e 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -1,4 +1,4 @@ -*filetype.txt* For Vim version 7.4. Last change: 2016 Jun 20 +*filetype.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/fold.txt b/runtime/doc/fold.txt index 680e3270f2..4826a65d7b 100644 --- a/runtime/doc/fold.txt +++ b/runtime/doc/fold.txt @@ -1,4 +1,4 @@ -*fold.txt* For Vim version 7.4. Last change: 2016 Jan 02 +*fold.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/ft_ada.txt b/runtime/doc/ft_ada.txt index 1e81acff67..94d97b481a 100644 --- a/runtime/doc/ft_ada.txt +++ b/runtime/doc/ft_ada.txt @@ -1,4 +1,4 @@ -*ft_ada.txt* For Vim version 7.4. Last change: 2010 Jul 20 +*ft_ada.txt* Nvim ADA FILE TYPE PLUG-INS REFERENCE MANUAL~ diff --git a/runtime/doc/ft_sql.txt b/runtime/doc/ft_sql.txt index 75c55269ef..29268f5753 100644 --- a/runtime/doc/ft_sql.txt +++ b/runtime/doc/ft_sql.txt @@ -1,4 +1,4 @@ -*ft_sql.txt* For Vim version 7.4. Last change: 2013 May 15 +*ft_sql.txt* Nvim by David Fishburn diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt index 1e8bb408d9..12b86e5d73 100644 --- a/runtime/doc/gui.txt +++ b/runtime/doc/gui.txt @@ -1,4 +1,4 @@ -*gui.txt* For Vim version 7.4. Last change: 2014 Mar 08 +*gui.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/hebrew.txt b/runtime/doc/hebrew.txt index 7bc8f02e7f..642d80adc7 100644 --- a/runtime/doc/hebrew.txt +++ b/runtime/doc/hebrew.txt @@ -1,4 +1,4 @@ -*hebrew.txt* For Vim version 7.4. Last change: 2007 Jun 14 +*hebrew.txt* Nvim VIM REFERENCE MANUAL by Ron Aaron (and Avner Lottem) diff --git a/runtime/doc/help.txt b/runtime/doc/help.txt index b33fb50c06..f71f46bad3 100644 --- a/runtime/doc/help.txt +++ b/runtime/doc/help.txt @@ -1,4 +1,4 @@ -*help.txt* For Vim version 7.4. Last change: 2016 Mar 31 +*help.txt* Nvim VIM - main help file k diff --git a/runtime/doc/helphelp.txt b/runtime/doc/helphelp.txt index ad1611133a..6741efabd8 100644 --- a/runtime/doc/helphelp.txt +++ b/runtime/doc/helphelp.txt @@ -1,4 +1,4 @@ -*helphelp.txt* For Vim version 7.4. Last change: 2016 Apr 01 +*helphelp.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/howto.txt b/runtime/doc/howto.txt index 03ae3dbd8d..33c8552463 100644 --- a/runtime/doc/howto.txt +++ b/runtime/doc/howto.txt @@ -1,4 +1,4 @@ -*howto.txt* For Vim version 7.4. Last change: 2006 Apr 02 +*howto.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/if_cscop.txt b/runtime/doc/if_cscop.txt index 7482f5eebb..3ce1575c99 100644 --- a/runtime/doc/if_cscop.txt +++ b/runtime/doc/if_cscop.txt @@ -1,4 +1,4 @@ -*if_cscop.txt* For Vim version 7.4. Last change: 2011 Jun 12 +*if_cscop.txt* Nvim VIM REFERENCE MANUAL by Andy Kahn diff --git a/runtime/doc/if_pyth.txt b/runtime/doc/if_pyth.txt index b6fe234de4..f2a7d91bb7 100644 --- a/runtime/doc/if_pyth.txt +++ b/runtime/doc/if_pyth.txt @@ -1,4 +1,4 @@ -*if_pyth.txt* For Vim version 7.4. Last change: 2014 Jul 23 +*if_pyth.txt* Nvim VIM REFERENCE MANUAL by Paul Moore diff --git a/runtime/doc/indent.txt b/runtime/doc/indent.txt index 496ccbc703..4f4b39f559 100644 --- a/runtime/doc/indent.txt +++ b/runtime/doc/indent.txt @@ -1,4 +1,4 @@ -*indent.txt* For Vim version 7.4. Last change: 2014 Dec 06 +*indent.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index cedf0266d6..bab15bcbb6 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1,4 +1,4 @@ -*index.txt* For Vim version 7.4. Last change: 2016 Jul 16 +*index.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt index 2d3eaafe63..8b8c05c070 100644 --- a/runtime/doc/insert.txt +++ b/runtime/doc/insert.txt @@ -1,4 +1,4 @@ -*insert.txt* For Vim version 7.4. Last change: 2016 Jan 31 +*insert.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt index 786097dd74..a5f9106bb0 100644 --- a/runtime/doc/intro.txt +++ b/runtime/doc/intro.txt @@ -1,4 +1,4 @@ -*intro.txt* For Vim version 7.4. Last change: 2015 Jan 20 +*intro.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/job_control.txt b/runtime/doc/job_control.txt index 1aa7ce06d6..40fd83b52c 100644 --- a/runtime/doc/job_control.txt +++ b/runtime/doc/job_control.txt @@ -1,4 +1,4 @@ -*job_control.txt* For Nvim. {Nvim} +*job_control.txt* Nvim NVIM REFERENCE MANUAL by Thiago de Arruda diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index 6dc8b1acc8..db349eca71 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -1,4 +1,4 @@ -*map.txt* For Vim version 7.4. Last change: 2016 Jul 06 +*map.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/mbyte.txt b/runtime/doc/mbyte.txt index 355a1da423..708c05e241 100644 --- a/runtime/doc/mbyte.txt +++ b/runtime/doc/mbyte.txt @@ -1,4 +1,4 @@ -*mbyte.txt* For Vim version 7.4. Last change: 2016 Jul 21 +*mbyte.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar et al. diff --git a/runtime/doc/message.txt b/runtime/doc/message.txt index b3a4bf9d72..d0bdba41ab 100644 --- a/runtime/doc/message.txt +++ b/runtime/doc/message.txt @@ -1,4 +1,4 @@ -*message.txt* For Vim version 7.4. Last change: 2016 Jul 16 +*message.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/mlang.txt b/runtime/doc/mlang.txt index a2be3cfd49..187d8f029b 100644 --- a/runtime/doc/mlang.txt +++ b/runtime/doc/mlang.txt @@ -1,4 +1,4 @@ -*mlang.txt* For Vim version 7.4. Last change: 2016 Jan 16 +*mlang.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt index be3170cf92..a89fabe107 100644 --- a/runtime/doc/motion.txt +++ b/runtime/doc/motion.txt @@ -1,4 +1,4 @@ -*motion.txt* For Vim version 7.4. Last change: 2016 Jul 12 +*motion.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/nvim_terminal_emulator.txt b/runtime/doc/nvim_terminal_emulator.txt index 0954dcb5a7..85325d3470 100644 --- a/runtime/doc/nvim_terminal_emulator.txt +++ b/runtime/doc/nvim_terminal_emulator.txt @@ -1,4 +1,4 @@ -*terminal_emulator.txt* {Nvim} +*terminal_emulator.txt* Nvim NVIM REFERENCE MANUAL by Thiago de Arruda diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 29de35805e..1cb14f7771 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 7.4. Last change: 2016 Jul 12 +*options.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/os_win32.txt b/runtime/doc/os_win32.txt index 5dc276c9df..cd8ad3465a 100644 --- a/runtime/doc/os_win32.txt +++ b/runtime/doc/os_win32.txt @@ -1,4 +1,4 @@ -*os_win32.txt* For Vim version 7.4. Last change: 2016 Mar 05 +*os_win32.txt* Nvim VIM REFERENCE MANUAL by George Reilly diff --git a/runtime/doc/pattern.txt b/runtime/doc/pattern.txt index f3f5bcbd66..1d0f42c222 100644 --- a/runtime/doc/pattern.txt +++ b/runtime/doc/pattern.txt @@ -1,4 +1,4 @@ -*pattern.txt* For Vim version 7.4. Last change: 2016 Jun 08 +*pattern.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/pi_gzip.txt b/runtime/doc/pi_gzip.txt index df0ba5e52e..5b7a903f9c 100644 --- a/runtime/doc/pi_gzip.txt +++ b/runtime/doc/pi_gzip.txt @@ -1,4 +1,4 @@ -*pi_gzip.txt* For Vim version 7.4. Last change: 2012 Jul 19 +*pi_gzip.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/pi_msgpack.txt b/runtime/doc/pi_msgpack.txt index d695ac42cb..e9f77e70b0 100644 --- a/runtime/doc/pi_msgpack.txt +++ b/runtime/doc/pi_msgpack.txt @@ -1,4 +1,4 @@ -*pi_msgpack.txt* For NeoVim version 0.1. +*pi_msgpack.txt* Nvim Author: Nikolay Pavlov Copyright: (c) 2015 by Nikolay Pavlov diff --git a/runtime/doc/pi_netrw.txt b/runtime/doc/pi_netrw.txt index f740143c61..d0cfa70582 100644 --- a/runtime/doc/pi_netrw.txt +++ b/runtime/doc/pi_netrw.txt @@ -1,4 +1,4 @@ -*pi_netrw.txt* For Vim version 7.4. Last change: 2016 Apr 20 +*pi_netrw.txt* Nvim ------------------------------------------------ NETRW REFERENCE MANUAL by Charles E. Campbell diff --git a/runtime/doc/pi_paren.txt b/runtime/doc/pi_paren.txt index 534b3add29..4b425c6cbb 100644 --- a/runtime/doc/pi_paren.txt +++ b/runtime/doc/pi_paren.txt @@ -1,4 +1,4 @@ -*pi_paren.txt* For Vim version 7.4. Last change: 2013 May 08 +*pi_paren.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/pi_spec.txt b/runtime/doc/pi_spec.txt index 650d1d369b..f4949db2af 100644 --- a/runtime/doc/pi_spec.txt +++ b/runtime/doc/pi_spec.txt @@ -1,4 +1,4 @@ -*pi_spec.txt* For Vim version 7.4. Last change: 2006 Apr 24 +*pi_spec.txt* Nvim by Gustavo Niemeyer ~ diff --git a/runtime/doc/pi_tar.txt b/runtime/doc/pi_tar.txt index b2fab290b3..a189d006dd 100644 --- a/runtime/doc/pi_tar.txt +++ b/runtime/doc/pi_tar.txt @@ -1,4 +1,4 @@ -*pi_tar.txt* For Vim version 7.4. Last change: 2013 Apr 17 +*pi_tar.txt* Nvim +====================+ | Tar File Interface | diff --git a/runtime/doc/pi_zip.txt b/runtime/doc/pi_zip.txt index 0a081f24d3..64f0be1a08 100644 --- a/runtime/doc/pi_zip.txt +++ b/runtime/doc/pi_zip.txt @@ -1,4 +1,4 @@ -*pi_zip.txt* For Vim version 7.4. Last change: 2013 Apr 17 +*pi_zip.txt* Nvim +====================+ | Zip File Interface | diff --git a/runtime/doc/print.txt b/runtime/doc/print.txt index 7565d1e976..c5470d1469 100644 --- a/runtime/doc/print.txt +++ b/runtime/doc/print.txt @@ -1,4 +1,4 @@ -*print.txt* For Vim version 7.4. Last change: 2010 Jul 20 +*print.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/provider.txt b/runtime/doc/provider.txt index ac4d7361f4..eaa23de093 100644 --- a/runtime/doc/provider.txt +++ b/runtime/doc/provider.txt @@ -1,4 +1,4 @@ -*provider.txt* {Nvim} +*provider.txt* Nvim NVIM REFERENCE MANUAL by Thiago de Arruda diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index ec0b2cfef6..1bc5c31281 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -1,4 +1,4 @@ -*quickfix.txt* For Vim version 7.4. Last change: 2016 Jul 17 +*quickfix.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt index 2e2bec7637..7de0bba118 100644 --- a/runtime/doc/quickref.txt +++ b/runtime/doc/quickref.txt @@ -1,4 +1,4 @@ -*quickref.txt* For Vim version 7.4. Last change: 2016 Aug 12 +*quickref.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/recover.txt b/runtime/doc/recover.txt index e09138b2f5..8eded487c0 100644 --- a/runtime/doc/recover.txt +++ b/runtime/doc/recover.txt @@ -1,4 +1,4 @@ -*recover.txt* For Vim version 7.4. Last change: 2014 Mar 27 +*recover.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/remote.txt b/runtime/doc/remote.txt index 933ab3a444..b99aa6a859 100644 --- a/runtime/doc/remote.txt +++ b/runtime/doc/remote.txt @@ -1,4 +1,4 @@ -*remote.txt* For Vim version 7.4. Last change: 2015 Mar 01 +*remote.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/remote_plugin.txt b/runtime/doc/remote_plugin.txt index dddc021d68..3c682e83f3 100644 --- a/runtime/doc/remote_plugin.txt +++ b/runtime/doc/remote_plugin.txt @@ -1,4 +1,4 @@ -*remote_plugin.txt* For Nvim. {Nvim} +*remote_plugin.txt* Nvim NVIM REFERENCE MANUAL by Thiago de Arruda diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt index ef98556260..b34d081ba9 100644 --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -1,4 +1,4 @@ -*repeat.txt* For Vim version 7.4. Last change: 2016 Jul 21 +*repeat.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/rileft.txt b/runtime/doc/rileft.txt index e4e495bb18..ba9bb1eba7 100644 --- a/runtime/doc/rileft.txt +++ b/runtime/doc/rileft.txt @@ -1,4 +1,4 @@ -*rileft.txt* For Vim version 7.4. Last change: 2006 Apr 24 +*rileft.txt* Nvim VIM REFERENCE MANUAL by Avner Lottem diff --git a/runtime/doc/russian.txt b/runtime/doc/russian.txt index 36f3d0b715..7dd267ad50 100644 --- a/runtime/doc/russian.txt +++ b/runtime/doc/russian.txt @@ -1,4 +1,4 @@ -*russian.txt* For Vim version 7.4. Last change: 2006 Apr 24 +*russian.txt* Nvim VIM REFERENCE MANUAL by Vassily Ragosin diff --git a/runtime/doc/scroll.txt b/runtime/doc/scroll.txt index a9492fc169..fba5275f47 100644 --- a/runtime/doc/scroll.txt +++ b/runtime/doc/scroll.txt @@ -1,4 +1,4 @@ -*scroll.txt* For Vim version 7.4. Last change: 2006 Aug 27 +*scroll.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/sign.txt b/runtime/doc/sign.txt index fd1fe8dce3..e5a6b0be39 100644 --- a/runtime/doc/sign.txt +++ b/runtime/doc/sign.txt @@ -1,4 +1,4 @@ -*sign.txt* For Vim version 7.4. Last change: 2016 Aug 12 +*sign.txt* Nvim VIM REFERENCE MANUAL by Gordon Prieur diff --git a/runtime/doc/sponsor.txt b/runtime/doc/sponsor.txt index a99d66d843..cfdf21abea 100644 --- a/runtime/doc/sponsor.txt +++ b/runtime/doc/sponsor.txt @@ -1,4 +1,4 @@ -*sponsor.txt* For Vim version 7.4. Last change: 2008 Jun 21 +*sponsor.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index b168d2cb98..daf6ad9ca2 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -1,4 +1,4 @@ -*starting.txt* For Vim version 7.4. Last change: 2016 Jul 03 +*starting.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index e59f567826..b0b4cabd65 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -1,4 +1,4 @@ -*syntax.txt* For Vim version 7.4. Last change: 2016 Aug 10 +*syntax.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/tabpage.txt b/runtime/doc/tabpage.txt index 4ab5b3c759..c299d43927 100644 --- a/runtime/doc/tabpage.txt +++ b/runtime/doc/tabpage.txt @@ -1,4 +1,4 @@ -*tabpage.txt* For Vim version 7.4. Last change: 2015 Apr 18 +*tabpage.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/tagsrch.txt b/runtime/doc/tagsrch.txt index 047abb42cc..2c090a66fa 100644 --- a/runtime/doc/tagsrch.txt +++ b/runtime/doc/tagsrch.txt @@ -1,4 +1,4 @@ -*tagsrch.txt* For Vim version 7.4. Last change: 2013 Oct 01 +*tagsrch.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/term.txt b/runtime/doc/term.txt index 137d3a06db..56f57a3819 100644 --- a/runtime/doc/term.txt +++ b/runtime/doc/term.txt @@ -1,4 +1,4 @@ -*term.txt* For Vim version 7.4. Last change: 2015 Nov 24 +*term.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/tips.txt b/runtime/doc/tips.txt index 9d2eb3deba..e8dfbaea0b 100644 --- a/runtime/doc/tips.txt +++ b/runtime/doc/tips.txt @@ -1,4 +1,4 @@ -*tips.txt* For Vim version 7.4. Last change: 2009 Nov 07 +*tips.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/uganda.txt b/runtime/doc/uganda.txt index c228c65542..cdd677cbd5 100644 --- a/runtime/doc/uganda.txt +++ b/runtime/doc/uganda.txt @@ -1,4 +1,4 @@ -*uganda.txt* For Vim version 7.4. Last change: 2013 Jul 06 +*uganda.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/undo.txt b/runtime/doc/undo.txt index c6c70ab6d2..e40be6b250 100644 --- a/runtime/doc/undo.txt +++ b/runtime/doc/undo.txt @@ -1,4 +1,4 @@ -*undo.txt* For Vim version 7.4. Last change: 2014 May 24 +*undo.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/usr_01.txt b/runtime/doc/usr_01.txt index 2fa15331df..f3a5728d72 100644 --- a/runtime/doc/usr_01.txt +++ b/runtime/doc/usr_01.txt @@ -1,4 +1,4 @@ -*usr_01.txt* For Vim version 7.4. Last change: 2010 Nov 03 +*usr_01.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_02.txt b/runtime/doc/usr_02.txt index c10643940d..b32d84080c 100644 --- a/runtime/doc/usr_02.txt +++ b/runtime/doc/usr_02.txt @@ -1,4 +1,4 @@ -*usr_02.txt* For Vim version 7.4. Last change: 2016 Jan 16 +*usr_02.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_03.txt b/runtime/doc/usr_03.txt index 943d7b528c..f2e523e784 100644 --- a/runtime/doc/usr_03.txt +++ b/runtime/doc/usr_03.txt @@ -1,4 +1,4 @@ -*usr_03.txt* For Vim version 7.4. Last change: 2016 Jan 05 +*usr_03.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_04.txt b/runtime/doc/usr_04.txt index 59ba0b3e73..e02a50e494 100644 --- a/runtime/doc/usr_04.txt +++ b/runtime/doc/usr_04.txt @@ -1,4 +1,4 @@ -*usr_04.txt* For Vim version 7.4. Last change: 2014 Aug 29 +*usr_04.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_05.txt b/runtime/doc/usr_05.txt index f920fd4591..9e7f06ab63 100644 --- a/runtime/doc/usr_05.txt +++ b/runtime/doc/usr_05.txt @@ -1,4 +1,4 @@ -*usr_05.txt* For Vim version 7.4. Last change: 2016 Mar 28 +*usr_05.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_06.txt b/runtime/doc/usr_06.txt index b4b495ff9f..fb02fe2683 100644 --- a/runtime/doc/usr_06.txt +++ b/runtime/doc/usr_06.txt @@ -1,4 +1,4 @@ -*usr_06.txt* For Vim version 7.4. Last change: 2009 Oct 28 +*usr_06.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_07.txt b/runtime/doc/usr_07.txt index 50e4781cd7..3fc600d977 100644 --- a/runtime/doc/usr_07.txt +++ b/runtime/doc/usr_07.txt @@ -1,4 +1,4 @@ -*usr_07.txt* For Vim version 7.4. Last change: 2006 Apr 24 +*usr_07.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_08.txt b/runtime/doc/usr_08.txt index d1f3fbd49d..575921fc4f 100644 --- a/runtime/doc/usr_08.txt +++ b/runtime/doc/usr_08.txt @@ -1,4 +1,4 @@ -*usr_08.txt* For Vim version 7.4. Last change: 2014 Jul 06 +*usr_08.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_09.txt b/runtime/doc/usr_09.txt index d68d734b8f..1ff3d93329 100644 --- a/runtime/doc/usr_09.txt +++ b/runtime/doc/usr_09.txt @@ -1,4 +1,4 @@ -*usr_09.txt* For Vim version 7.4. Last change: 2006 Apr 24 +*usr_09.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_10.txt b/runtime/doc/usr_10.txt index bf7ba18222..044cb1582b 100644 --- a/runtime/doc/usr_10.txt +++ b/runtime/doc/usr_10.txt @@ -1,4 +1,4 @@ -*usr_10.txt* For Vim version 7.4. Last change: 2006 Nov 05 +*usr_10.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_11.txt b/runtime/doc/usr_11.txt index 1a72c300d7..42009519df 100644 --- a/runtime/doc/usr_11.txt +++ b/runtime/doc/usr_11.txt @@ -1,4 +1,4 @@ -*usr_11.txt* For Vim version 7.4. Last change: 2010 Jul 20 +*usr_11.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_12.txt b/runtime/doc/usr_12.txt index f64a230576..9078f4748a 100644 --- a/runtime/doc/usr_12.txt +++ b/runtime/doc/usr_12.txt @@ -1,4 +1,4 @@ -*usr_12.txt* For Vim version 7.4. Last change: 2007 May 11 +*usr_12.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_20.txt b/runtime/doc/usr_20.txt index 5f0a660187..3b8eeae175 100644 --- a/runtime/doc/usr_20.txt +++ b/runtime/doc/usr_20.txt @@ -1,4 +1,4 @@ -*usr_20.txt* For Vim version 7.4. Last change: 2006 Apr 24 +*usr_20.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_21.txt b/runtime/doc/usr_21.txt index f99c3263d0..cfb8c90027 100644 --- a/runtime/doc/usr_21.txt +++ b/runtime/doc/usr_21.txt @@ -1,4 +1,4 @@ -*usr_21.txt* For Vim version 7.4. Last change: 2012 Nov 02 +*usr_21.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_22.txt b/runtime/doc/usr_22.txt index cff8e9db1e..8d11be11b2 100644 --- a/runtime/doc/usr_22.txt +++ b/runtime/doc/usr_22.txt @@ -1,4 +1,4 @@ -*usr_22.txt* For Vim version 7.4. Last change: 2012 Nov 15 +*usr_22.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_23.txt b/runtime/doc/usr_23.txt index 4761203512..cb776d2b5c 100644 --- a/runtime/doc/usr_23.txt +++ b/runtime/doc/usr_23.txt @@ -1,4 +1,4 @@ -*usr_23.txt* For Vim version 7.4. Last change: 2006 Apr 24 +*usr_23.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_24.txt b/runtime/doc/usr_24.txt index ba9d083fe0..b74ed879e0 100644 --- a/runtime/doc/usr_24.txt +++ b/runtime/doc/usr_24.txt @@ -1,4 +1,4 @@ -*usr_24.txt* For Vim version 7.4. Last change: 2006 Jul 23 +*usr_24.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_25.txt b/runtime/doc/usr_25.txt index 24420353dd..ae3d3c4422 100644 --- a/runtime/doc/usr_25.txt +++ b/runtime/doc/usr_25.txt @@ -1,4 +1,4 @@ -*usr_25.txt* For Vim version 7.4. Last change: 2016 Mar 28 +*usr_25.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_26.txt b/runtime/doc/usr_26.txt index e381a1df12..7b0b940da2 100644 --- a/runtime/doc/usr_26.txt +++ b/runtime/doc/usr_26.txt @@ -1,4 +1,4 @@ -*usr_26.txt* For Vim version 7.4. Last change: 2006 Apr 24 +*usr_26.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_27.txt b/runtime/doc/usr_27.txt index d837610698..afea67bd0b 100644 --- a/runtime/doc/usr_27.txt +++ b/runtime/doc/usr_27.txt @@ -1,4 +1,4 @@ -*usr_27.txt* For Vim version 7.4. Last change: 2010 Mar 28 +*usr_27.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_28.txt b/runtime/doc/usr_28.txt index d29b384437..24e995d9ab 100644 --- a/runtime/doc/usr_28.txt +++ b/runtime/doc/usr_28.txt @@ -1,4 +1,4 @@ -*usr_28.txt* For Vim version 7.4. Last change: 2008 Jun 14 +*usr_28.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_29.txt b/runtime/doc/usr_29.txt index 9eb66ce83c..b0cb9d7e88 100644 --- a/runtime/doc/usr_29.txt +++ b/runtime/doc/usr_29.txt @@ -1,4 +1,4 @@ -*usr_29.txt* For Vim version 7.4. Last change: 2016 Feb 27 +*usr_29.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_30.txt b/runtime/doc/usr_30.txt index dbac440ecc..786f2d1800 100644 --- a/runtime/doc/usr_30.txt +++ b/runtime/doc/usr_30.txt @@ -1,4 +1,4 @@ -*usr_30.txt* For Vim version 7.4. Last change: 2007 Nov 10 +*usr_30.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_31.txt b/runtime/doc/usr_31.txt index 7bcf4d428a..2354e27b42 100644 --- a/runtime/doc/usr_31.txt +++ b/runtime/doc/usr_31.txt @@ -1,4 +1,4 @@ -*usr_31.txt* For Vim version 7.4. Last change: 2007 May 08 +*usr_31.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_32.txt b/runtime/doc/usr_32.txt index fd58f2d517..fca802872d 100644 --- a/runtime/doc/usr_32.txt +++ b/runtime/doc/usr_32.txt @@ -1,4 +1,4 @@ -*usr_32.txt* For Vim version 7.4. Last change: 2010 Jul 20 +*usr_32.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_40.txt b/runtime/doc/usr_40.txt index d3cc792c59..4092af703e 100644 --- a/runtime/doc/usr_40.txt +++ b/runtime/doc/usr_40.txt @@ -1,4 +1,4 @@ -*usr_40.txt* For Vim version 7.4. Last change: 2013 Aug 05 +*usr_40.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index 191b0871f4..77c238ae97 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -1,4 +1,4 @@ -*usr_41.txt* For Vim version 7.4. Last change: 2016 Aug 07 +*usr_41.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_42.txt b/runtime/doc/usr_42.txt index c8eac5f062..ad04555f8c 100644 --- a/runtime/doc/usr_42.txt +++ b/runtime/doc/usr_42.txt @@ -1,4 +1,4 @@ -*usr_42.txt* For Vim version 7.4. Last change: 2008 May 05 +*usr_42.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_43.txt b/runtime/doc/usr_43.txt index bab446af3c..765a525692 100644 --- a/runtime/doc/usr_43.txt +++ b/runtime/doc/usr_43.txt @@ -1,4 +1,4 @@ -*usr_43.txt* For Vim version 7.4. Last change: 2015 Oct 23 +*usr_43.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_44.txt b/runtime/doc/usr_44.txt index a91f92ff4e..fcd6b71219 100644 --- a/runtime/doc/usr_44.txt +++ b/runtime/doc/usr_44.txt @@ -1,4 +1,4 @@ -*usr_44.txt* For Vim version 7.4. Last change: 2008 Dec 28 +*usr_44.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_45.txt b/runtime/doc/usr_45.txt index 34b607cbd2..9bbbe82113 100644 --- a/runtime/doc/usr_45.txt +++ b/runtime/doc/usr_45.txt @@ -1,4 +1,4 @@ -*usr_45.txt* For Vim version 7.4. Last change: 2008 Nov 15 +*usr_45.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/usr_toc.txt b/runtime/doc/usr_toc.txt index e021b806e3..0dce3eac83 100644 --- a/runtime/doc/usr_toc.txt +++ b/runtime/doc/usr_toc.txt @@ -1,4 +1,4 @@ -*usr_toc.txt* For Vim version 7.4. Last change: 2016 Mar 25 +*usr_toc.txt* Nvim VIM USER MANUAL - by Bram Moolenaar diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index 3a66becb9a..fd81064d5b 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -1,4 +1,4 @@ -*various.txt* For Vim version 7.4. Last change: 2016 Jul 09 +*various.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/vi_diff.txt b/runtime/doc/vi_diff.txt index 54ef8fecb2..1a108caeaf 100644 --- a/runtime/doc/vi_diff.txt +++ b/runtime/doc/vi_diff.txt @@ -1,4 +1,4 @@ -*vi_diff.txt* For Vim version 7.4. Last change: 2016 Feb 12 +*vi_diff.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index d7d31b5853..11dde27868 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -1,4 +1,4 @@ -*vim_diff.txt* For Nvim. {Nvim} +*vim_diff.txt* Nvim NVIM REFERENCE MANUAL diff --git a/runtime/doc/visual.txt b/runtime/doc/visual.txt index 6810d50c11..9bad1a24a0 100644 --- a/runtime/doc/visual.txt +++ b/runtime/doc/visual.txt @@ -1,4 +1,4 @@ -*visual.txt* For Vim version 7.4. Last change: 2014 Mar 23 +*visual.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index 25239bd7ae..4ed7b68194 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -1,4 +1,4 @@ -*windows.txt* For Vim version 7.4. Last change: 2016 Jun 10 +*windows.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar From 3e33025133e1e77c7ad3b8670e06175b9757cf30 Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Tue, 21 Mar 2017 22:48:01 +0100 Subject: [PATCH 0049/1671] strings: Fix problems found during code review --- src/nvim/strings.c | 75 ++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 40 deletions(-) diff --git a/src/nvim/strings.c b/src/nvim/strings.c index a2052423e2..7ec2ea13f7 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -1224,49 +1224,44 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, remove_trailing_zeroes = true; } - if ((fmt_spec == 'f' || fmt_spec == 'F') && abs_f > 1.0e307) { - STRCPY(tmp, infinity_str(f > 0.0, fmt_spec, - force_sign, space_for_positive)); - str_arg_l = STRLEN(tmp); + if (isinf(f) + || (strchr("fF", fmt_spec) != NULL && abs_f > 1.0e307)) { + xstrlcpy(tmp, infinity_str(f > 0.0, fmt_spec, + force_sign, space_for_positive), + sizeof(tmp)); + str_arg_l = strlen(tmp); + zero_padding = 0; + } else if (isnan(f)) { + // Not a number: nan or NAN + memmove(tmp, ASCII_ISUPPER(fmt_spec) ? "NAN" : "nan", 4); + str_arg_l = 3; zero_padding = 0; } else { - if (isnan(f)) { - // Not a number: nan or NAN - STRCPY(tmp, ASCII_ISUPPER(fmt_spec) ? "NAN" : "nan"); - str_arg_l = 3; - zero_padding = 0; - } else if (isinf(f)) { - STRCPY(tmp, infinity_str(f > 0.0, fmt_spec, - force_sign, space_for_positive)); - str_arg_l = STRLEN(tmp); - zero_padding = 0; - } else { - format[0] = '%'; - int l = 1; - if (force_sign) { - format[l++] = space_for_positive ? ' ' : '+'; - } - if (precision_specified) { - size_t max_prec = TMP_LEN - 10; - - // make sure we don't get more digits than we have room for - if ((fmt_spec == 'f' || fmt_spec == 'F') && abs_f > 1.0) { - max_prec -= (size_t)log10(abs_f); - } - if (precision > max_prec) { - precision = max_prec; - } - l += snprintf(format + l, sizeof(format) - 1, ".%d", - (int)precision); - } - format[l] = (char)(fmt_spec == 'F' ? 'f' : fmt_spec); - format[l + 1] = NUL; - - // Regular float number - assert(l + 1 < (int)sizeof(format)); - str_arg_l = (size_t)snprintf(tmp, sizeof(tmp), format, f); - assert(str_arg_l < sizeof(tmp)); + format[0] = '%'; + size_t l = 1; + if (force_sign) { + format[l++] = space_for_positive ? ' ' : '+'; } + if (precision_specified) { + size_t max_prec = TMP_LEN - 10; + + // make sure we don't get more digits than we have room for + if ((fmt_spec == 'f' || fmt_spec == 'F') && abs_f > 1.0) { + max_prec -= (size_t)log10(abs_f); + } + if (precision > max_prec) { + precision = max_prec; + } + l += (size_t)snprintf(format + l, sizeof(format) - l, ".%d", + (int)precision); + } + format[l] = fmt_spec == 'F' ? 'f' : fmt_spec; + format[l + 1] = NUL; + + // Regular float number + assert(l + 1 < sizeof(format)); + str_arg_l = (size_t)snprintf(tmp, sizeof(tmp), format, f); + assert(str_arg_l < sizeof(tmp)); if (remove_trailing_zeroes) { int i; From 7cc4e782a0d14be0bf648b3b938b1577bbb70df6 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 22 Mar 2017 01:13:51 +0100 Subject: [PATCH 0050/1671] doc: Update missing headers. (#6330) pi_* docs are considered standalone plugins so they don't follow this convention. --- runtime/doc/develop.txt | 2 +- runtime/doc/if_ruby.txt | 2 +- runtime/doc/msgpack_rpc.txt | 2 +- runtime/doc/nvim.txt | 2 +- runtime/doc/spell.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt index b738da9bec..76ccc42546 100644 --- a/runtime/doc/develop.txt +++ b/runtime/doc/develop.txt @@ -1,4 +1,4 @@ -*develop.txt* +*develop.txt* Nvim NVIM REFERENCE MANUAL diff --git a/runtime/doc/if_ruby.txt b/runtime/doc/if_ruby.txt index fdd63501ea..2474039d82 100644 --- a/runtime/doc/if_ruby.txt +++ b/runtime/doc/if_ruby.txt @@ -1,4 +1,4 @@ -*if_ruby.txt* +*if_ruby.txt* Nvim VIM REFERENCE MANUAL by Shugo Maeda diff --git a/runtime/doc/msgpack_rpc.txt b/runtime/doc/msgpack_rpc.txt index c074eb43ff..02086e24fd 100644 --- a/runtime/doc/msgpack_rpc.txt +++ b/runtime/doc/msgpack_rpc.txt @@ -1,4 +1,4 @@ -*msgpack_rpc.txt* {Nvim} +*msgpack_rpc.txt* Nvim NVIM REFERENCE MANUAL by Thiago de Arruda diff --git a/runtime/doc/nvim.txt b/runtime/doc/nvim.txt index 7dfbbd0cf0..bd483e9949 100644 --- a/runtime/doc/nvim.txt +++ b/runtime/doc/nvim.txt @@ -1,4 +1,4 @@ -*nvim.txt* {Nvim} +*nvim.txt* Nvim NVIM REFERENCE MANUAL diff --git a/runtime/doc/spell.txt b/runtime/doc/spell.txt index 0902d5d10f..08415f72a7 100644 --- a/runtime/doc/spell.txt +++ b/runtime/doc/spell.txt @@ -1,4 +1,4 @@ -*spell.txt* +*spell.txt* Nvim VIM REFERENCE MANUAL by Bram Moolenaar From ab16c07584ab4a5bc48784dde3b9b46d30e5e4a0 Mon Sep 17 00:00:00 2001 From: lonerover Date: Wed, 22 Mar 2017 08:28:03 +0800 Subject: [PATCH 0051/1671] vim-patch:7.4.2283 Problem: Part of ":oldfiles" command isn't cleared. (Lifepillar) Solution: Clear the rest of the line. (closes 1018) https://github.com/vim/vim/commit/885c00eabe6d1fd757d4f0eb531ad3a15a35ec04 --- src/nvim/eval.c | 1 + src/nvim/version.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7eb149476f..e11b460edf 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -23628,6 +23628,7 @@ void ex_oldfiles(exarg_T *eap) msg_outnum(++nr); MSG_PUTS(": "); msg_outtrans(get_tv_string(&li->li_tv)); + msg_clr_eos(); msg_putchar('\n'); ui_flush(); /* output one line at a time */ os_breakcheck(); diff --git a/src/nvim/version.c b/src/nvim/version.c index 39886aa9d9..e060724d81 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -158,7 +158,7 @@ static int included_patches[] = { // 2286 NA // 2285 NA 2284, - // 2283, + 2283, // 2282 NA // 2281 NA 2280, From 56e400d800b9eb6c89ea8336c50d2a61cc8fd18b Mon Sep 17 00:00:00 2001 From: John Szakmeister Date: Wed, 22 Mar 2017 03:49:45 -0400 Subject: [PATCH 0052/1671] vim_vsnprintf: fix conversion warning #6333 Re-apply fix from #6311 which was accidentally regressed in #6231. --- src/nvim/strings.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 7ec2ea13f7..267832ed2d 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -1255,7 +1255,9 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, l += (size_t)snprintf(format + l, sizeof(format) - l, ".%d", (int)precision); } - format[l] = fmt_spec == 'F' ? 'f' : fmt_spec; + + // Cast to char to avoid a conversion warning on Ubuntu 12.04. + format[l] = (char)(fmt_spec == 'F' ? 'f' : fmt_spec); format[l + 1] = NUL; // Regular float number From c554b53040e9fea2dce343eb11178a3352d5e8da Mon Sep 17 00:00:00 2001 From: lonerover Date: Wed, 22 Mar 2017 08:34:04 +0800 Subject: [PATCH 0053/1671] vim-patch:7.4.2296 Problem: No tests for :undolist and "U" command. Solution: Add tests. (Dominique Pelle) https://github.com/vim/vim/commit/c628fdcd46e93c308f742efdf54248695960e290 --- src/nvim/testdir/test_undo.vim | 33 +++++++++++++++++++++++++++++++++ src/nvim/version.c | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/nvim/testdir/test_undo.vim b/src/nvim/testdir/test_undo.vim index fc61d1f223..9ff73fd870 100644 --- a/src/nvim/testdir/test_undo.vim +++ b/src/nvim/testdir/test_undo.vim @@ -131,6 +131,39 @@ func Test_undo_del_chars() close! endfunc +func Test_undolist() + new + set ul=100 + + let a=execute('undolist') + call assert_equal("\nNothing to undo", a) + + " 1 leaf (2 changes). + call feedkeys('achange1', 'xt') + call feedkeys('achange2', 'xt') + let a=execute('undolist') + call assert_match("^\nnumber changes when *saved\n *2 *2 .*$", a) + + " 2 leaves. + call feedkeys('u', 'xt') + call feedkeys('achange3\', 'xt') + let a=execute('undolist') + call assert_match("^\nnumber changes when *saved\n *2 *2 *.*\n *3 *2 .*$", a) + close! +endfunc + +func Test_U_command() + new + set ul=100 + call feedkeys("achange1\", 'xt') + call feedkeys("achange2\", 'xt') + norm! U + call assert_equal('', getline(1)) + norm! U + call assert_equal('change1change2', getline(1)) + close! +endfunc + func Test_undojoin() new call feedkeys("Goaaaa\", 'xt') diff --git a/src/nvim/version.c b/src/nvim/version.c index e060724d81..1f2c8d12f5 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -145,7 +145,7 @@ static int included_patches[] = { 2299, // 2298 NA // 2297 NA - // 2296, + 2296, 2295, 2294, 2293, From 114a18b93588c7d5238fe66a77dc0f94a2ba5500 Mon Sep 17 00:00:00 2001 From: lonerover Date: Wed, 22 Mar 2017 08:37:02 +0800 Subject: [PATCH 0054/1671] vim-patch:7.4.2303 Problem: When using "is" the mode isn't always updated. Solution: Redraw the command line. (Christian Brabandt) https://github.com/vim/vim/commit/779f2fc3a7468e273897d2fd0672315812a2e3da --- src/nvim/search.c | 3 ++- src/nvim/version.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/nvim/search.c b/src/nvim/search.c index ba6c4e6548..75862e1136 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -3028,7 +3028,8 @@ extend: ++curwin->w_cursor.col; VIsual = start_pos; VIsual_mode = 'v'; - redraw_curbuf_later(INVERTED); /* update the inversion */ + redraw_cmdline = true; // show mode later + redraw_curbuf_later(INVERTED); // update the inversion } else { /* include a newline after the sentence, if there is one */ if (incl(&curwin->w_cursor) == -1) diff --git a/src/nvim/version.c b/src/nvim/version.c index 1f2c8d12f5..ccd7fd21d9 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -138,7 +138,7 @@ static int included_patches[] = { // 2306, 2305, // 2304 NA - // 2303, + 2303, // 2302 NA // 2301 NA 2300, From a1732b46abe48541f380d6a605fa7529c9a05da1 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 22 Mar 2017 14:49:37 +0100 Subject: [PATCH 0055/1671] terminal: Avoid invalid cursor col (#6265) Patch-by: oni-link Closes #6203 https://s3.amazonaws.com/archive.travis-ci.org/jobs/206794197/log.txt References #3161 [ RUN ] ...d/neovim/neovim/test/functional/terminal/buffer_spec.lua @ 199: terminal buffer term_close() use-after-free #4393 ./test/functional/helpers.lua:187: attempt to perform arithmetic on local 'written' (a nil value) stack traceback: ./test/functional/helpers.lua:187: in function 'nvim_feed' ./test/functional/helpers.lua:329: in function 'execute' ...d/neovim/neovim/test/functional/terminal/buffer_spec.lua:206: in function <...d/neovim/neovim/test/functional/terminal/buffer_spec.lua:199> [ ERROR ] ...d/neovim/neovim/test/functional/terminal/buffer_spec.lua @ 199: terminal buffer term_close() use-after-free #4393 (199.47 ms) ==================== File /home/travis/build/neovim/neovim/build/log/ubsan.15466 ==================== = ================================================================= = ==15466==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x621000029101 at pc 0x000000ea7ba0 bp 0x7ffd5bb628c0 sp 0x7ffd5bb628b8 = READ of size 1 at 0x621000029101 thread T0 = #0 0xea7b9f in utf_head_off /home/travis/build/neovim/neovim/src/nvim/mbyte.c:1637:7 = #1 0xeaaf53 in mb_adjustpos /home/travis/build/neovim/neovim/src/nvim/mbyte.c:1840:16 = #2 0xeaab48 in mb_adjust_cursor /home/travis/build/neovim/neovim/src/nvim/mbyte.c:1825:3 = #3 0x11000d0 in normal_finish_command /home/travis/build/neovim/neovim/src/nvim/normal.c:928:5 = #4 0x1077df1 in normal_execute /home/travis/build/neovim/neovim/src/nvim/normal.c:1147:3 = #5 0x16ff943 in state_enter /home/travis/build/neovim/neovim/src/nvim/state.c:58:26 = #6 0x102d8db in normal_enter /home/travis/build/neovim/neovim/src/nvim/normal.c:463:3 = #7 0xdf3398 in main /home/travis/build/neovim/neovim/src/nvim/main.c:540:3 = #8 0x2b973e8b4f44 in __libc_start_main /build/eglibc-oGUzwX/eglibc-2.19/csu/libc-start.c:287 = #9 0x447445 in _start (/home/travis/build/neovim/neovim/build/bin/nvim+0x447445) = = 0x621000029101 is located 1 bytes to the right of 4096-byte region [0x621000028100,0x621000029100) = allocated by thread T0 here: = #0 0x4f17b8 in malloc (/home/travis/build/neovim/neovim/build/bin/nvim+0x4f17b8) = #1 0xf1f374 in try_malloc /home/travis/build/neovim/neovim/src/nvim/memory.c:84:15 = #2 0xf1f534 in xmalloc /home/travis/build/neovim/neovim/src/nvim/memory.c:118:15 = #3 0xebe6a8 in mf_alloc_bhdr /home/travis/build/neovim/neovim/src/nvim/memfile.c:646:17 = #4 0xebc394 in mf_new /home/travis/build/neovim/neovim/src/nvim/memfile.c:297:12 = #5 0xed1368 in ml_new_data /home/travis/build/neovim/neovim/src/nvim/memline.c:2704:16 = #6 0xece6ab in ml_open /home/travis/build/neovim/neovim/src/nvim/memline.c:349:8 = #7 0x6438ad in open_buffer /home/travis/build/neovim/neovim/src/nvim/buffer.c:109:7 = #8 0xa6ec8d in do_ecmd /home/travis/build/neovim/neovim/src/nvim/ex_cmds.c:2489:24 = #9 0xb5a0f9 in do_exedit /home/travis/build/neovim/neovim/src/nvim/ex_docmd.c:6723:9 = #10 0xb791f8 in ex_edit /home/travis/build/neovim/neovim/src/nvim/ex_docmd.c:6651:3 = #11 0xb28b43 in do_one_cmd /home/travis/build/neovim/neovim/src/nvim/ex_docmd.c:2198:5 = #12 0xb077a7 in do_cmdline /home/travis/build/neovim/neovim/src/nvim/ex_docmd.c:601:20 = #13 0x10905db in nv_colon /home/travis/build/neovim/neovim/src/nvim/normal.c:4495:18 = #14 0x1077de8 in normal_execute /home/travis/build/neovim/neovim/src/nvim/normal.c:1144:3 = #15 0x16ff943 in state_enter /home/travis/build/neovim/neovim/src/nvim/state.c:58:26 = #16 0x102d8db in normal_enter /home/travis/build/neovim/neovim/src/nvim/normal.c:463:3 = #17 0xdf3398 in main /home/travis/build/neovim/neovim/src/nvim/main.c:540:3 = #18 0x2b973e8b4f44 in __libc_start_main /build/eglibc-oGUzwX/eglibc-2.19/csu/libc-start.c:287 = = SUMMARY: AddressSanitizer: heap-buffer-overflow /home/travis/build/neovim/neovim/src/nvim/mbyte.c:1637:7 in utf_head_off stack traceback: ./test/helpers.lua:80: in function 'check_logs' ./test/functional/helpers.lua:639: in function <./test/functional/helpers.lua:638> [----------] 9 tests from /home/travis/build/neovim/neovim/test/functional/terminal/buffer_spec.lua (2263.12 ms total) --- src/nvim/cursor.c | 5 ++--- src/nvim/mbyte.c | 35 +++++++++++++++++++++++++++++++++++ src/nvim/normal.c | 4 +--- src/nvim/terminal.c | 1 + 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 544bcf6ede..82f1bf0a16 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -338,9 +338,8 @@ void check_cursor_col(void) check_cursor_col_win(curwin); } -/* - * Make sure win->w_cursor.col is valid. - */ +/// Make sure win->w_cursor.col is valid. Special handling of insert-mode. +/// @see mb_check_adjust_col void check_cursor_col_win(win_T *win) { colnr_T len; diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 6a87a63b8c..7a54b0c64f 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -1848,6 +1848,41 @@ void mb_adjustpos(buf_T *buf, pos_T *lp) } } +/// Checks and adjusts cursor column. Not mode-dependent. +/// @see check_cursor_col_win +/// +/// @param win Places cursor on a valid column for this window. +void mb_check_adjust_col(win_T *win) +{ + colnr_T oldcol = win->w_cursor.col; + + // Column 0 is always valid. + if (oldcol != 0) { + char_u *p = ml_get_buf(win->w_buffer, win->w_cursor.lnum, false); + colnr_T len = (colnr_T)STRLEN(p); + + // Empty line or invalid column? + if (len == 0 || oldcol < 0) { + win->w_cursor.col = 0; + } else { + // Cursor column too big for line? + if (oldcol > len) { + win->w_cursor.col = len - 1; + } + // Move the cursor to the head byte. + win->w_cursor.col -= (*mb_head_off)(p, p + win->w_cursor.col); + } + + // Reset `coladd` when the cursor would be on the right half of a + // double-wide character. + if (win->w_cursor.coladd == 1 && p[win->w_cursor.col] != TAB + && vim_isprintc((*mb_ptr2char)(p + win->w_cursor.col)) + && ptr2cells(p + win->w_cursor.col) > 1) { + win->w_cursor.coladd = 0; + } + } +} + /* * Return a pointer to the character before "*p", if there is one. */ diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 7188e13436..85dc509ee6 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -925,9 +925,7 @@ normal_end: checkpcmark(); // check if we moved since setting pcmark xfree(s->ca.searchbuf); - if (has_mbyte) { - mb_adjust_cursor(); - } + mb_check_adjust_col(curwin); // #6203 if (curwin->w_p_scb && s->toplevel) { validate_cursor(); // may need to update w_leftcol diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index bd925a8106..87ee8f410f 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -1197,6 +1197,7 @@ static void adjust_topline(Terminal *term, buf_T *buf, long added) // Ensure valid cursor for each window displaying this terminal. wp->w_cursor.lnum = MIN(wp->w_cursor.lnum, ml_end); } + mb_check_adjust_col(wp); } } } From 1d8356a807e306eb82f2c1a7e2d2802407c7fa6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Linse?= Date: Wed, 22 Mar 2017 17:16:49 +0100 Subject: [PATCH 0056/1671] mbyte: remove dead code --- src/nvim/mbyte.c | 249 ++++++---------------------------------------- src/nvim/search.c | 30 ++---- src/nvim/spell.c | 9 +- 3 files changed, 41 insertions(+), 247 deletions(-) diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 7a54b0c64f..0ab133a545 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -355,14 +355,13 @@ int bomb_size(void) */ void remove_bom(char_u *s) { - if (enc_utf8) { - char_u *p = s; + char_u *p = s; - while ((p = vim_strbyte(p, 0xef)) != NULL) { - if (p[1] == 0xbb && p[2] == 0xbf) - STRMOVE(p, p + 3); - else - ++p; + while ((p = vim_strbyte(p, 0xef)) != NULL) { + if (p[1] == 0xbb && p[2] == 0xbf) { + STRMOVE(p, p + 3); + } else { + p++; } } } @@ -388,154 +387,7 @@ int mb_get_class_buf(const char_u *p, buf_T *buf) return 2; return 1; } - if (enc_dbcs != 0 && p[0] != NUL && p[1] != NUL) - return dbcs_class(p[0], p[1]); - if (enc_utf8) - return utf_class(utf_ptr2char(p)); - return 0; -} - -/* - * Get class of a double-byte character. This always returns 3 or bigger. - * TODO: Should return 1 for punctuation. - */ -int dbcs_class(unsigned lead, unsigned trail) -{ - switch (enc_dbcs) { - /* please add classify routine for your language in here */ - - case DBCS_JPNU: /* ? */ - case DBCS_JPN: - { - /* JIS code classification */ - unsigned char lb = lead; - unsigned char tb = trail; - - /* convert process code to JIS */ - /* - * XXX: Code page identification can not use with all - * system! So, some other encoding information - * will be needed. - * In japanese: SJIS,EUC,UNICODE,(JIS) - * Note that JIS-code system don't use as - * process code in most system because it uses - * escape sequences(JIS is context depend encoding). - */ - /* assume process code is JAPANESE-EUC */ - lb &= 0x7f; - tb &= 0x7f; - /* exceptions */ - switch (lb << 8 | tb) { - case 0x2121: /* ZENKAKU space */ - return 0; - case 0x2122: /* TOU-TEN (Japanese comma) */ - case 0x2123: /* KU-TEN (Japanese period) */ - case 0x2124: /* ZENKAKU comma */ - case 0x2125: /* ZENKAKU period */ - return 1; - case 0x213c: /* prolongedsound handled as KATAKANA */ - return 13; - } - /* sieved by KU code */ - switch (lb) { - case 0x21: - case 0x22: - /* special symbols */ - return 10; - case 0x23: - /* alpha-numeric */ - return 11; - case 0x24: - /* hiragana */ - return 12; - case 0x25: - /* katakana */ - return 13; - case 0x26: - /* greek */ - return 14; - case 0x27: - /* russian */ - return 15; - case 0x28: - /* lines */ - return 16; - default: - /* kanji */ - return 17; - } - } - - case DBCS_KORU: /* ? */ - case DBCS_KOR: - { - /* KS code classification */ - unsigned char c1 = lead; - unsigned char c2 = trail; - - /* - * 20 : Hangul - * 21 : Hanja - * 22 : Symbols - * 23 : Alpha-numeric/Roman Letter (Full width) - * 24 : Hangul Letter(Alphabet) - * 25 : Roman Numeral/Greek Letter - * 26 : Box Drawings - * 27 : Unit Symbols - * 28 : Circled/Parenthesized Letter - * 29 : Hiragana/Katakana - * 30 : Cyrillic Letter - */ - - if (c1 >= 0xB0 && c1 <= 0xC8) - /* Hangul */ - return 20; - - else if (c1 >= 0xCA && c1 <= 0xFD) - /* Hanja */ - return 21; - else switch (c1) { - case 0xA1: - case 0xA2: - /* Symbols */ - return 22; - case 0xA3: - /* Alpha-numeric */ - return 23; - case 0xA4: - /* Hangul Letter(Alphabet) */ - return 24; - case 0xA5: - /* Roman Numeral/Greek Letter */ - return 25; - case 0xA6: - /* Box Drawings */ - return 26; - case 0xA7: - /* Unit Symbols */ - return 27; - case 0xA8: - case 0xA9: - if (c2 <= 0xAF) - return 25; /* Roman Letter */ - else if (c2 >= 0xF6) - return 22; /* Symbols */ - else - /* Circled/Parenthesized Letter */ - return 28; - case 0xAA: - case 0xAB: - /* Hiragana/Katakana */ - return 29; - case 0xAC: - /* Cyrillic Letter */ - return 30; - } - } - default: - break; - } - return 3; + return utf_class(utf_ptr2char(p)); } /* @@ -788,10 +640,7 @@ int mb_cptr2char_adv(char_u **pp) int c; c = (*mb_ptr2char)(*pp); - if (enc_utf8) - *pp += utf_ptr2len(*pp); - else - *pp += (*mb_ptr2len)(*pp); + *pp += utf_ptr2len(*pp); return c; } @@ -1542,36 +1391,7 @@ int utf16_to_utf8(const WCHAR *strw, char **str) */ int mb_strnicmp(char_u *s1, char_u *s2, size_t nn) { - int i, l; - int cdiff; - int n = (int)nn; - - if (enc_utf8) { - return utf_strnicmp(s1, s2, nn, nn); - } else { - for (i = 0; i < n; i += l) { - if (s1[i] == NUL && s2[i] == NUL) /* both strings end */ - return 0; - - l = (*mb_ptr2len)(s1 + i); - if (l <= 1) { - /* Single byte: first check normally, then with ignore case. */ - if (s1[i] != s2[i]) { - cdiff = vim_tolower(s1[i]) - vim_tolower(s2[i]); - if (cdiff != 0) - return cdiff; - } - } else { - /* For non-Unicode multi-byte don't ignore case. */ - if (l > n - i) - l = n - i; - cdiff = STRNCMP(s1 + i, s2 + i, l); - if (cdiff != 0) - return cdiff; - } - } - } - return 0; + return utf_strnicmp(s1, s2, nn, nn); } /* We need to call mb_stricmp() even when we aren't dealing with a multi-byte @@ -1699,27 +1519,24 @@ int mb_off_next(char_u *base, char_u *p) int i; int j; - if (enc_utf8) { - if (*p < 0x80) /* be quick for ASCII */ - return 0; - - /* Find the next character that isn't 10xx.xxxx */ - for (i = 0; (p[i] & 0xc0) == 0x80; ++i) - ; - if (i > 0) { - /* Check for illegal sequence. */ - for (j = 0; p - j > base; ++j) - if ((p[-j] & 0xc0) != 0x80) - break; - if (utf8len_tab[p[-j]] != i + j) - return 0; - } - return i; + if (*p < 0x80) { // be quick for ASCII + return 0; } - /* Only need to check if we're on a trail byte, it doesn't matter if we - * want the offset to the next or current character. */ - return (*mb_head_off)(base, p); + // Find the next character that isn't 10xx.xxxx + for (i = 0; (p[i] & 0xc0) == 0x80; i++) {} + if (i > 0) { + // Check for illegal sequence. + for (j = 0; p - j > base; j++) { + if ((p[-j] & 0xc0) != 0x80) { + break; + } + } + if (utf8len_tab[p[-j]] != i + j) { + return 0; + } + } + return i; } /* @@ -1762,10 +1579,10 @@ void utf_find_illegal(void) char_u *tofree = NULL; vimconv.vc_type = CONV_NONE; - if (enc_utf8 && (enc_canon_props(curbuf->b_p_fenc) & ENC_8BIT)) { - /* 'encoding' is "utf-8" but we are editing a 8-bit encoded file, - * possibly a utf-8 file with illegal bytes. Setup for conversion - * from utf-8 to 'fileencoding'. */ + if (enc_canon_props(curbuf->b_p_fenc) & ENC_8BIT) { + // 'encoding' is "utf-8" but we are editing a 8-bit encoded file, + // possibly a utf-8 file with illegal bytes. Setup for conversion + // from utf-8 to 'fileencoding'. convert_setup(&vimconv, p_enc, curbuf->b_p_fenc); } @@ -2279,13 +2096,7 @@ static char_u * iconv_string(vimconv_T *vcp, char_u *str, size_t slen, *to++ = '?'; if ((*mb_ptr2cells)((char_u *)from) > 1) *to++ = '?'; - if (enc_utf8) - l = utfc_ptr2len_len((const char_u *)from, (int)fromlen); - else { - l = (*mb_ptr2len)((char_u *)from); - if (l > (int)fromlen) - l = (int)fromlen; - } + l = utfc_ptr2len_len((const char_u *)from, (int)fromlen); from += l; fromlen -= l; } else if (ICONV_ERRNO != ICONV_E2BIG) { diff --git a/src/nvim/search.c b/src/nvim/search.c index 75862e1136..8c56eda7cf 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -2417,32 +2417,20 @@ static int cls(void) int c; c = gchar_cursor(); - if (p_altkeymap && c == F_BLANK) + if (p_altkeymap && c == F_BLANK) { return 0; - if (c == ' ' || c == '\t' || c == NUL) - return 0; - if (enc_dbcs != 0 && c > 0xFF) { - /* If cls_bigword, report multi-byte chars as class 1. */ - if (enc_dbcs == DBCS_KOR && cls_bigword) - return 1; - - /* process code leading/trailing bytes */ - return dbcs_class(((unsigned)c >> 8), (c & 0xFF)); } - if (enc_utf8) { - c = utf_class(c); - if (c != 0 && cls_bigword) - return 1; - return c; + if (c == ' ' || c == '\t' || c == NUL) { + return 0; } - /* If cls_bigword is TRUE, report all non-blanks as class 1. */ - if (cls_bigword) + c = utf_class(c); + + // If cls_bigword is TRUE, report all non-blanks as class 1. + if (c != 0 && cls_bigword) { return 1; - - if (vim_iswordc(c)) - return 2; - return 1; + } + return c; } /* diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 12f982106a..3b891d998f 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -2623,7 +2623,7 @@ bool spell_iswordp_nmw(char_u *p, win_T *wp) // Returns true if word class indicates a word character. // Only for characters above 255. // Unicode subscript and superscript are not considered word characters. -// See also dbcs_class() and utf_class() in mbyte.c. +// See also utf_class() in mbyte.c. static bool spell_mb_isword_class(int cl, win_T *wp) { if (wp->w_s->b_cjk) @@ -2646,12 +2646,7 @@ static bool spell_iswordp_w(int *p, win_T *wp) s = p; if (*s > 255) { - if (enc_utf8) - return spell_mb_isword_class(utf_class(*s), wp); - if (enc_dbcs) - return spell_mb_isword_class( - dbcs_class((unsigned)*s >> 8, *s & 0xff), wp); - return false; + return spell_mb_isword_class(utf_class(*s), wp); } return spelltab.st_isw[*s]; } From b9e1289819183f94afb43330a7e0d953869e2af7 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 20 Mar 2017 02:48:28 +0100 Subject: [PATCH 0057/1671] vim-patch:8.0.0486 Problem: Crash and endless loop when closing windows in a SessionLoadPost autocommand. Solution: Check for valid tabpage. (partly neovim/neovim#6308) https://github.com/vim/vim/commit/8c752bd6c4af54c0b7bac35a39acc2bf16015f85 Closes #6308 --- src/nvim/fileio.c | 6 +++ src/nvim/testdir/test_autocmd.vim | 68 +++++++++++++++++++++++++++++++ src/nvim/window.c | 58 +++++++++++++++++++++----- 3 files changed, 121 insertions(+), 11 deletions(-) diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 873c15ff4a..d948e20b32 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -6464,6 +6464,12 @@ win_found: win_remove(curwin, NULL); aucmd_win_used = false; last_status(false); // may need to remove last status line + + if (!valid_tabpage_win(curtab)) { + // no valid window in current tabpage + close_tabpage(curtab); + } + restore_snapshot(SNAP_AUCMD_IDX, false); (void)win_comp_pos(); // recompute window positions unblock_autocmds(); diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index f05a55f1aa..fe9a3dd451 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -196,3 +196,71 @@ func Test_augroup_deleted() au! VimEnter endfunc +" Closing a window might cause an endless loop +" E814 for older Vims +function Test_autocmd_bufwipe_in_SessLoadPost() + if has('win32') + throw 'Skipped: test hangs on MS-Windows' + endif + tabnew + set noswapfile + let g:bufnr=bufnr('%') + mksession! + + let content=['set nocp noswapfile', + \ 'let v:swapchoice="e"', + \ 'augroup test_autocmd_sessionload', + \ 'autocmd!', + \ 'autocmd SessionLoadPost * 4bw!', + \ 'augroup END' + \ ] + call writefile(content, 'Xvimrc') + let a=system(v:progpath. ' --headless -u Xvimrc --noplugins -S Session.vim') + call assert_match('E814', a) + + unlet! g:bufnr + set swapfile + for file in ['Session.vim', 'Xvimrc'] + call delete(file) + endfor +endfunc + +" SEGV occurs in older versions. +function Test_autocmd_bufwipe_in_SessLoadPost2() + if has('win32') + throw 'Skipped: test hangs on MS-Windows' + endif + tabnew + set noswapfile + let g:bufnr=bufnr('%') + mksession! + + let content = ['set nocp noswapfile', + \ 'function! DeleteInactiveBufs()', + \ ' tabfirst', + \ ' let tabblist = []', + \ ' for i in range(1, tabpagenr(''$''))', + \ ' call extend(tabblist, tabpagebuflist(i))', + \ ' endfor', + \ ' for b in range(1, bufnr(''$''))', + \ ' if bufexists(b) && buflisted(b) && (index(tabblist, b) == -1 || bufname(b) =~# ''^$'')', + \ ' exec ''bwipeout '' . b', + \ ' endif', + \ ' endfor', + \ 'call append("1", "SessionLoadPost DONE")', + \ 'endfunction', + \ 'au SessionLoadPost * call DeleteInactiveBufs()'] + call writefile(content, 'Xvimrc') + let a=system(v:progpath. ' --headless -u Xvimrc --noplugins -S Session.vim') + " this probably only matches on unix + if has("unix") + call assert_notmatch('Caught deadly signal SEGV', a) + endif + call assert_match('SessionLoadPost DONE', a) + + unlet! g:bufnr + set swapfile + for file in ['Session.vim', 'Xvimrc'] + call delete(file) + endfor +endfunc diff --git a/src/nvim/window.c b/src/nvim/window.c index 6c9d3554f1..eda3cd7810 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1714,14 +1714,10 @@ static void win_equal_rec( } } -/* - * close all windows for buffer 'buf' - */ -void -close_windows ( - buf_T *buf, - int keep_curwin /* don't close "curwin" */ -) +/// Closes all windows for buffer `buf`. +/// +/// @param keep_curwin don't close `curwin` +void close_windows(buf_T *buf, int keep_curwin) { tabpage_T *tp, *nexttp; int h = tabline_height(); @@ -1731,9 +1727,11 @@ close_windows ( for (win_T *wp = firstwin; wp != NULL && lastwin != firstwin; ) { if (wp->w_buffer == buf && (!keep_curwin || wp != curwin) - && !(wp->w_closing || wp->w_buffer->b_closing) - ) { - win_close(wp, FALSE); + && !(wp->w_closing || wp->w_buffer->b_closing)) { + if (win_close(wp, false) == FAIL) { + // If closing the window fails give up, to avoid looping forever. + break; + } /* Start all over, autocommands may change the window layout. */ wp = firstwin; @@ -3134,6 +3132,44 @@ bool valid_tabpage(tabpage_T *tpc) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT return false; } +/// Returns true when `tpc` is valid and at least one window is valid. +int valid_tabpage_win(tabpage_T *tpc) +{ + FOR_ALL_TABS(tp) { + if (tp == tpc) { + FOR_ALL_WINDOWS_IN_TAB(wp, tp) { + if (win_valid_any_tab(wp)) { + return true; + } + } + return false; + } + } + // shouldn't happen + return false; +} + +/// Close tabpage `tab`, assuming it has no windows in it. +/// There must be another tabpage or this will crash. +void close_tabpage(tabpage_T *tab) +{ + tabpage_T *ptp; + + if (tab == first_tabpage) { + first_tabpage = tab->tp_next; + ptp = first_tabpage; + } else { + for (ptp = first_tabpage; ptp != NULL && ptp->tp_next != tab; + ptp = ptp->tp_next) { + // do nothing + } + ptp->tp_next = tab->tp_next; + } + + goto_tabpage_tp(ptp, false, false); + free_tabpage(tab); +} + /* * Find tab page "n" (first one is 1). Returns NULL when not found. */ From ca853edb6f9ffe1d2e5d4a63bf88e4c3a059f5fb Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 20 Mar 2017 13:59:11 +0100 Subject: [PATCH 0058/1671] vim-patch:8.0.0177 Problem: When opening a buffer on a directory and inside a try/catch then the BufEnter event is not triggered. Solution: Return NOTDONE from readfile() for a directory and deal with the three possible return values. (Justin M. Keyes, closes vim/vim#1375, closes vim/vim#1353) https://github.com/vim/vim/commit/e13b9afe1283f5ae43232b5992372a0eb570666c --- src/nvim/testdir/test_autocmd.vim | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index fe9a3dd451..395c4a8596 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -196,6 +196,25 @@ func Test_augroup_deleted() au! VimEnter endfunc +func Test_BufEnter() + au! BufEnter + au Bufenter * let val = val . '+' + let g:val = '' + split NewFile + call assert_equal('+', g:val) + bwipe! + call assert_equal('++', g:val) + + " Also get BufEnter when editing a directory + call mkdir('Xdir') + split Xdir + call assert_equal('+++', g:val) + bwipe! + + call delete('Xdir', 'd') + au! BufEnter +endfunc + " Closing a window might cause an endless loop " E814 for older Vims function Test_autocmd_bufwipe_in_SessLoadPost() From 165ba3e636769c38d67285e1b8ea2966ccb00b30 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 20 Mar 2017 14:01:22 +0100 Subject: [PATCH 0059/1671] vim-patch:7.4.2324 Problem: Crash when editing a new buffer and BufUnload autocommand wipes out the new buffer. (Norio Takagi) Solution: Don't allow wiping out this buffer. (partly by Hirohito Higashi) Move old style test13 into test_autocmd. Avoid ml_get error when editing a file. https://github.com/vim/vim/commit/e0ab94e7123ca7855f45919114d948ef2bc1e8c3 --- src/nvim/buffer.c | 23 ++++--- src/nvim/buffer_defs.h | 6 +- src/nvim/ex_cmds.c | 11 ++-- src/nvim/ex_docmd.c | 6 +- src/nvim/testdir/test13.in | 63 -------------------- src/nvim/testdir/test13.ok | 31 ---------- src/nvim/testdir/test_autocmd.vim | 99 +++++++++++++++++++++++++++++++ src/nvim/version.c | 2 +- src/nvim/window.c | 13 ++-- 9 files changed, 133 insertions(+), 121 deletions(-) delete mode 100644 src/nvim/testdir/test13.in delete mode 100644 src/nvim/testdir/test13.ok diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 9e781f5dff..5edc87eab1 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -360,6 +360,13 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) wipe_buf = true; } + // Disallow deleting the buffer when it is locked (already being closed or + // halfway a command that relies on it). Unloading is allowed. + if (buf->b_locked > 0 && (del_buf || wipe_buf)) { + EMSG(_("E937: Attempt to delete a buffer that is in use")); + return; + } + if (win_valid_any_tab(win)) { // Set b_last_cursor when closing the last window for the buffer. // Remember the last cursor position and window options of the buffer. @@ -378,14 +385,14 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) /* When the buffer is no longer in a window, trigger BufWinLeave */ if (buf->b_nwindows == 1) { - buf->b_closing = true; + buf->b_locked++; if (apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, false, buf) && !bufref_valid(&bufref)) { // Autocommands deleted the buffer. EMSG(_(e_auabort)); return; } - buf->b_closing = false; + buf->b_locked--; if (abort_if_last && one_window()) { /* Autocommands made this the only window. */ EMSG(_(e_auabort)); @@ -395,14 +402,14 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) /* When the buffer becomes hidden, but is not unloaded, trigger * BufHidden */ if (!unload_buf) { - buf->b_closing = true; + buf->b_locked++; if (apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname, false, buf) && !bufref_valid(&bufref)) { // Autocommands deleted the buffer. EMSG(_(e_auabort)); return; } - buf->b_closing = false; + buf->b_locked--; if (abort_if_last && one_window()) { /* Autocommands made this the only window. */ EMSG(_(e_auabort)); @@ -559,7 +566,7 @@ void buf_freeall(buf_T *buf, int flags) tabpage_T *the_curtab = curtab; // Make sure the buffer isn't closed by autocommands. - buf->b_closing = true; + buf->b_locked++; bufref_T bufref; set_bufref(&bufref, buf); @@ -584,7 +591,7 @@ void buf_freeall(buf_T *buf, int flags) // Autocommands may delete the buffer. return; } - buf->b_closing = false; + buf->b_locked--; // If the buffer was in curwin and the window has changed, go back to that // window, if it still exists. This avoids that ":edit x" triggering a @@ -1111,7 +1118,7 @@ do_buffer ( * a window with this buffer. */ while (buf == curbuf - && !(curwin->w_closing || curwin->w_buffer->b_closing) + && !(curwin->w_closing || curwin->w_buffer->b_locked > 0) && (firstwin != lastwin || first_tabpage->tp_next != NULL)) { if (win_close(curwin, FALSE) == FAIL) break; @@ -4499,7 +4506,7 @@ void ex_buffer_all(exarg_T *eap) : wp->w_width != Columns) || (had_tab > 0 && wp != firstwin) ) && firstwin != lastwin - && !(wp->w_closing || wp->w_buffer->b_closing) + && !(wp->w_closing || wp->w_buffer->b_locked > 0) ) { win_close(wp, FALSE); wpnext = firstwin; /* just in case an autocommand does diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index fdd7d945c9..9d350c763e 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -470,9 +470,9 @@ struct file_buffer { int b_nwindows; /* nr of windows open on this buffer */ - int b_flags; /* various BF_ flags */ - bool b_closing; /* buffer is being closed, don't let - autocommands close it too. */ + int b_flags; // various BF_ flags + int b_locked; // Buffer is being closed or referenced, don't + // let autocommands wipe it out. /* * b_ffname has the full path of the file (NULL for no name). diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 1b83677807..41f97b0cef 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -2284,8 +2284,11 @@ int do_ecmd( } else { win_T *the_curwin = curwin; - // Set the w_closing flag to avoid that autocommands close the window. + // Set w_closing to avoid that autocommands close the window. + // Set b_locked for the same reason. the_curwin->w_closing = true; + buf->b_locked++; + if (curbuf == old_curbuf.br_buf) { buf_copy_options(buf, BCO_ENTER); } @@ -2298,6 +2301,7 @@ int do_ecmd( false); the_curwin->w_closing = false; + buf->b_locked--; // autocmds may abort script processing if (aborting() && curwin->w_buffer != NULL) { @@ -2444,11 +2448,6 @@ int do_ecmd( /* Assume success now */ retval = OK; - /* - * Reset cursor position, could be used by autocommands. - */ - check_cursor(); - /* * Check if we are editing the w_arg_idx file in the argument list. */ diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index bd3b8c204a..ebfbf2bf09 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5775,7 +5775,7 @@ static void ex_quit(exarg_T *eap) // Refuse to quit when locked or when the buffer in the last window is // being closed (can only happen in autocommands). if (curbuf_locked() - || (wp->w_buffer->b_nwindows == 1 && wp->w_buffer->b_closing)) { + || (wp->w_buffer->b_nwindows == 1 && wp->w_buffer->b_locked > 0)) { return; } @@ -5836,7 +5836,7 @@ static void ex_quit_all(exarg_T *eap) apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, curbuf); /* Refuse to quit when locked or when the buffer in the last window is * being closed (can only happen in autocommands). */ - if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_closing)) + if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_locked > 0)) return; exiting = true; @@ -6131,7 +6131,7 @@ static void ex_exit(exarg_T *eap) apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, curbuf); /* Refuse to quit when locked or when the buffer in the last window is * being closed (can only happen in autocommands). */ - if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_closing)) + if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_locked > 0)) return; // if more files or windows we won't exit diff --git a/src/nvim/testdir/test13.in b/src/nvim/testdir/test13.in deleted file mode 100644 index fa9ba312b7..0000000000 --- a/src/nvim/testdir/test13.in +++ /dev/null @@ -1,63 +0,0 @@ -Tests for autocommands on :close command - -Write three files and open them, each in a window. -Then go to next window, with autocommand that deletes the previous one. -Do this twice, writing the file. - -Also test deleting the buffer on a Unload event. If this goes wrong there -will be the ATTENTION prompt. - -Also test changing buffers in a BufDel autocommand. If this goes wrong there -are ml_line errors and/or a Crash. - -STARTTEST -:/^start of testfile/,/^end of testfile/w! Xtestje1 -:/^start of testfile/,/^end of testfile/w! Xtestje2 -:/^start of testfile/,/^end of testfile/w! Xtestje3 -:e Xtestje1 -otestje1 -:w -:sp Xtestje2 -otestje2 -:w -:sp Xtestje3 -otestje3 -:w - -:au WinLeave Xtestje2 bwipe - -:w! test.out -:au WinLeave Xtestje1 bwipe Xtestje3 -:close -:w >>test.out -:e Xtestje1 -:bwipe Xtestje2 Xtestje3 test.out -:au! -:au! BufUnload Xtestje1 bwipe -:e Xtestje3 -:w >>test.out -:e Xtestje2 -:sp Xtestje1 -:e -:w >>test.out -:au! -:only -:e Xtestje1 -:bwipe Xtestje2 Xtestje3 test.out test13.in -:au BufWipeout Xtestje1 buf Xtestje1 -:bwipe -:w >>test.out -:only -:new|set buftype=help -:wincmd w -:1quit -:$put ='Final line' -:$w >>test.out -:qa! -ENDTEST - -start of testfile - contents - contents - contents -end of testfile diff --git a/src/nvim/testdir/test13.ok b/src/nvim/testdir/test13.ok deleted file mode 100644 index 66ebce63f7..0000000000 --- a/src/nvim/testdir/test13.ok +++ /dev/null @@ -1,31 +0,0 @@ -start of testfile -testje1 - contents - contents - contents -end of testfile -start of testfile -testje1 - contents - contents - contents -end of testfile -start of testfile -testje3 - contents - contents - contents -end of testfile -start of testfile -testje2 - contents - contents - contents -end of testfile -start of testfile -testje1 - contents - contents - contents -end of testfile -Final line diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 395c4a8596..39cd92440e 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -77,11 +77,49 @@ function Test_autocmd_bufunload_with_tabnext() quit call assert_equal(2, tabpagenr('$')) + autocmd! test_autocmd_bufunload_with_tabnext_group augroup! test_autocmd_bufunload_with_tabnext_group tablast quit endfunc +" SEGV occurs in older versions. (At least 7.4.2321 or older) +function Test_autocmd_bufunload_avoiding_SEGV_01() + split aa.txt + let lastbuf = bufnr('$') + + augroup test_autocmd_bufunload + autocmd! + exe 'autocmd BufUnload ' . (lastbuf + 1) . 'bwipeout!' + augroup END + + call assert_fails('edit bb.txt', 'E937:') + + autocmd! test_autocmd_bufunload + augroup! test_autocmd_bufunload + bwipe! aa.txt + bwipe! bb.txt +endfunc + +" SEGV occurs in older versions. (At least 7.4.2321 or older) +function Test_autocmd_bufunload_avoiding_SEGV_02() + setlocal buftype=nowrite + let lastbuf = bufnr('$') + + augroup test_autocmd_bufunload + autocmd! + exe 'autocmd BufUnload ' . (lastbuf + 1) . 'bwipeout!' + augroup END + + normal! i1 + call assert_fails('edit a.txt', 'E517:') + call feedkeys("\") + + autocmd! test_autocmd_bufunload + augroup! test_autocmd_bufunload + bwipe! a.txt +endfunc + func Test_win_tab_autocmd() let g:record = [] @@ -196,6 +234,67 @@ func Test_augroup_deleted() au! VimEnter endfunc +" Tests for autocommands on :close command. +" This used to be in test13. +func Test_three_windows() + " Write three files and open them, each in a window. + " Then go to next window, with autocommand that deletes the previous one. + " Do this twice, writing the file. + e! Xtestje1 + call setline(1, 'testje1') + w + sp Xtestje2 + call setline(1, 'testje2') + w + sp Xtestje3 + call setline(1, 'testje3') + w + wincmd w + au WinLeave Xtestje2 bwipe + wincmd w + call assert_equal('Xtestje1', expand('%')) + + au WinLeave Xtestje1 bwipe Xtestje3 + close + call assert_equal('Xtestje1', expand('%')) + + " Test deleting the buffer on a Unload event. If this goes wrong there + " will be the ATTENTION prompt. + e Xtestje1 + au! + au! BufUnload Xtestje1 bwipe + call assert_fails('e Xtestje3', 'E937:') + call assert_equal('Xtestje3', expand('%')) + + e Xtestje2 + sp Xtestje1 + call assert_fails('e', 'E937:') + call assert_equal('Xtestje2', expand('%')) + + " Test changing buffers in a BufWipeout autocommand. If this goes wrong + " there are ml_line errors and/or a Crash. + au! + only + e Xanother + e Xtestje1 + bwipe Xtestje2 + bwipe Xtestje3 + au BufWipeout Xtestje1 buf Xtestje1 + bwipe + call assert_equal('Xanother', expand('%')) + + only + help + wincmd w + 1quit + call assert_equal('Xanother', expand('%')) + + au! + call delete('Xtestje1') + call delete('Xtestje2') + call delete('Xtestje3') +endfunc + func Test_BufEnter() au! BufEnter au Bufenter * let val = val . '+' diff --git a/src/nvim/version.c b/src/nvim/version.c index 32ef2c2fdb..8ae5e4057e 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -117,7 +117,7 @@ static int included_patches[] = { // 2327 NA 2326, // 2325 NA - // 2324, + 2324, 2323, 2322, 2321, diff --git a/src/nvim/window.c b/src/nvim/window.c index eda3cd7810..a737ffb33c 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1727,7 +1727,7 @@ void close_windows(buf_T *buf, int keep_curwin) for (win_T *wp = firstwin; wp != NULL && lastwin != firstwin; ) { if (wp->w_buffer == buf && (!keep_curwin || wp != curwin) - && !(wp->w_closing || wp->w_buffer->b_closing)) { + && !(wp->w_closing || wp->w_buffer->b_locked > 0)) { if (win_close(wp, false) == FAIL) { // If closing the window fails give up, to avoid looping forever. break; @@ -1745,8 +1745,7 @@ void close_windows(buf_T *buf, int keep_curwin) if (tp != curtab) { FOR_ALL_WINDOWS_IN_TAB(wp, tp) { if (wp->w_buffer == buf - && !(wp->w_closing || wp->w_buffer->b_closing) - ) { + && !(wp->w_closing || wp->w_buffer->b_locked > 0)) { win_close_othertab(wp, FALSE, tp); /* Start all over, the tab page may be closed and @@ -1882,8 +1881,9 @@ int win_close(win_T *win, int free_buf) return FAIL; } - if (win->w_closing || (win->w_buffer != NULL && win->w_buffer->b_closing)) - return FAIL; /* window is already being closed */ + if (win->w_closing || (win->w_buffer != NULL && win->w_buffer->b_locked > 0)) { + return FAIL; // window is already being closed + } if (win == aucmd_win) { EMSG(_("E813: Cannot close autocmd window")); return FAIL; @@ -2064,7 +2064,8 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp) // Get here with win->w_buffer == NULL when win_close() detects the tab page // changed. - if (win->w_closing || (win->w_buffer != NULL && win->w_buffer->b_closing)) { + if (win->w_closing + || (win->w_buffer != NULL && win->w_buffer->b_locked > 0)) { return; // window is already being closed } From 6a8bad03084c58d531e48b3d7f7f87f305f150d8 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 10:15:22 +0100 Subject: [PATCH 0060/1671] vim-patch:8.0.0019 Problem: Test_command_count is old style. Solution: Turn it into a new style test. (Naruhiko Nishino) Use more assert functions. --- src/nvim/testdir/Makefile | 3 +- src/nvim/testdir/test_alot.vim | 1 + src/nvim/testdir/test_autocmd.vim | 13 ++ src/nvim/testdir/test_command_count.vim | 191 ++++++++++++++++++++++++ 4 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 src/nvim/testdir/test_command_count.vim diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index d090ace432..0337ab13dd 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -1,4 +1,4 @@ -# +# vim: noet ts=8 # Makefile to run all tests for Vim # @@ -30,6 +30,7 @@ SCRIPTS ?= \ NEW_TESTS ?= \ test_bufwintabinfo.res \ test_cmdline.res \ + test_command_count.res \ test_cscope.res \ test_digraph.res \ test_diffmode.res \ diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index 8aa0f417d1..241300f688 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -3,6 +3,7 @@ source test_assign.vim source test_autocmd.vim +source test_command_count.vim source test_cursor_func.vim source test_execute_func.vim source test_ex_undo.vim diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 39cd92440e..ace9e457bb 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -1,5 +1,15 @@ " Tests for autocommands +set belloff=all + +function! s:cleanup_buffers() abort + for bnr in range(1, bufnr('$')) + if bufloaded(bnr) && bufnr('%') != bnr + execute 'bd! ' . bnr + endif + endfor +endfunction + func Test_vim_did_enter() call assert_false(v:vim_did_enter) @@ -237,6 +247,9 @@ endfunc " Tests for autocommands on :close command. " This used to be in test13. func Test_three_windows() + " Clean up buffers, because in some cases this function fails. + call s:cleanup_buffers() + " Write three files and open them, each in a window. " Then go to next window, with autocommand that deletes the previous one. " Do this twice, writing the file. diff --git a/src/nvim/testdir/test_command_count.vim b/src/nvim/testdir/test_command_count.vim new file mode 100644 index 0000000000..e438a8b077 --- /dev/null +++ b/src/nvim/testdir/test_command_count.vim @@ -0,0 +1,191 @@ +" Test for user command counts. + +func Test_command_count_0() + set hidden + set noswapfile + + split DoesNotExistEver + let lastbuf = bufnr('$') + call setline(1, 'asdf') + quit! + + command! -range -addr=loaded_buffers RangeLoadedBuffers :let lines = [, ] + command! -range=% -addr=loaded_buffers RangeLoadedBuffersAll :let lines = [, ] + command! -range -addr=buffers RangeBuffers :let lines = [, ] + command! -range=% -addr=buffers RangeBuffersAll :let lines = [, ] + + .,$RangeLoadedBuffers + call assert_equal([1, 1], lines) + %RangeLoadedBuffers + call assert_equal([1, 1], lines) + RangeLoadedBuffersAll + call assert_equal([1, 1], lines) + .,$RangeBuffers + call assert_equal([1, lastbuf], lines) + %RangeBuffers + call assert_equal([1, lastbuf], lines) + RangeBuffersAll + call assert_equal([1, lastbuf], lines) + + delcommand RangeLoadedBuffers + delcommand RangeLoadedBuffersAll + delcommand RangeBuffers + delcommand RangeBuffersAll + + set hidden& + set swapfile& +endfunc + +func Test_command_count_1() + silent! %argd + arga a b c d e + argdo echo "loading buffers" + argu 3 + command! -range -addr=arguments RangeArguments :let lines = [, ] + command! -range=% -addr=arguments RangeArgumentsAll :let lines = [, ] + .-,$-RangeArguments + call assert_equal([2, 4], lines) + %RangeArguments + call assert_equal([1, 5], lines) + RangeArgumentsAll + call assert_equal([1, 5], lines) + N + .RangeArguments + call assert_equal([2, 2], lines) + delcommand RangeArguments + delcommand RangeArgumentsAll + + split|split|split|split + 3wincmd w + command! -range -addr=windows RangeWindows :let lines = [, ] + .,$RangeWindows + call assert_equal([3, 5], lines) + %RangeWindows + call assert_equal([1, 5], lines) + delcommand RangeWindows + + command! -range=% -addr=windows RangeWindowsAll :let lines = [, ] + RangeWindowsAll + call assert_equal([1, 5], lines) + delcommand RangeWindowsAll + only + blast|bd + + tabe|tabe|tabe|tabe + normal 2gt + command! -range -addr=tabs RangeTabs :let lines = [, ] + .,$RangeTabs + call assert_equal([2, 5], lines) + %RangeTabs + call assert_equal([1, 5], lines) + delcommand RangeTabs + + command! -range=% -addr=tabs RangeTabsAll :let lines = [, ] + RangeTabsAll + call assert_equal([1, 5], lines) + delcommand RangeTabsAll + 1tabonly + + s/\n/\r\r\r\r\r/ + 2ma< + $-ma> + command! -range=% RangeLines :let lines = [, ] + '<,'>RangeLines + call assert_equal([2, 5], lines) + delcommand RangeLines + + command! -range=% -buffer LocalRangeLines :let lines = [, ] + '<,'>LocalRangeLines + call assert_equal([2, 5], lines) + delcommand LocalRangeLines +endfunc + +func Test_command_count_2() + silent! %argd + arga a b c d + call assert_fails('5argu', 'E16:') + + $argu + call assert_equal('d', expand('%:t')) + + 1argu + call assert_equal('a', expand('%:t')) + + call assert_fails('300b', 'E16:') + + split|split|split|split + 0close + + $wincmd w + $close + call assert_equal(3, winnr()) + + call assert_fails('$+close', 'E16:') + + $tabe + call assert_equal(2, tabpagenr()) + + call assert_fails('$+tabe', 'E16:') + + only! + e x + 0tabm + normal 1gt + call assert_equal('x', expand('%:t')) + + tabonly! + only! +endfunc + +func Test_command_count_3() + se nohidden + e aaa + let buf_aaa = bufnr('%') + e bbb + let buf_bbb = bufnr('%') + e ccc + let buf_ccc = bufnr('%') + buf 1 + call assert_equal([1, 1, 1], [buflisted(buf_aaa), buflisted(buf_bbb), buflisted(buf_ccc)]) + exe buf_bbb . "," . buf_ccc . "bdelete" + call assert_equal([1, 0, 0], [buflisted(buf_aaa), buflisted(buf_bbb), buflisted(buf_ccc)]) + exe buf_aaa . "bdelete" + call assert_equal([0, 0, 0], [buflisted(buf_aaa), buflisted(buf_bbb), buflisted(buf_ccc)]) +endfunc + +func Test_command_count_4() + %argd + let bufnr = bufnr('$') + 1 + arga aa bb cc dd ee ff + 3argu + let args = [] + .,$-argdo call add(args, expand('%')) + call assert_equal(['cc', 'dd', 'ee'], args) + + " create windows to get 5 + split|split|split|split + 2wincmd w + let windows = [] + .,$-windo call add(windows, winnr()) + call assert_equal([2, 3, 4], windows) + only! + + exe bufnr . 'buf' + let buffers = [] + .,$-bufdo call add(buffers, bufnr('%')) + call assert_equal([bufnr, bufnr + 1, bufnr + 2, bufnr + 3, bufnr + 4], buffers) + + exe (bufnr + 3) . 'bdel' + let buffers = [] + exe (bufnr + 2) . ',' . (bufnr + 5) . "bufdo call add(buffers, bufnr('%'))" + call assert_equal([bufnr + 2, bufnr + 4, bufnr + 5], buffers) + + " create tabpages to get 5 + tabe|tabe|tabe|tabe + normal! 2gt + let tabpages = [] + .,$-tabdo call add(tabpages, tabpagenr()) + call assert_equal([2, 3, 4], tabpages) + tabonly! + bwipe! +endfunc From 4c18670e91e22ab4fe566aa181aa6f28166e8aad Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 10:19:01 +0100 Subject: [PATCH 0061/1671] vim-patch:7.4.2346 Problem: Autocommand test fails when run directly, passes when run as part of test_alot. Solution: Add command to make the cursor move. Close a tab page. --- src/nvim/testdir/test_autocmd.vim | 3 +++ src/nvim/version.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index ace9e457bb..34cca5f612 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -23,6 +23,9 @@ if has('timers') endfunc func Test_cursorhold_insert() + " Need to move the cursor. + call feedkeys("ggG", "xt") + let g:triggered = 0 au CursorHoldI * let g:triggered += 1 set updatetime=20 diff --git a/src/nvim/version.c b/src/nvim/version.c index 8ae5e4057e..21f3a493c3 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -95,7 +95,7 @@ static int included_patches[] = { // 2349, 2348, 2347, - // 2346, + 2346, // 2345 NA // 2344 NA // 2343, From 4a2e6f460d02762d211c0f92af305ef5290a667b Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 10:24:40 +0100 Subject: [PATCH 0062/1671] vim-patch:7.4.2341 Problem: Tiny things. Test doesn't clean up properly. Solution: Adjust comment and white space. Restore option value. --- src/nvim/ex_cmds.c | 2 +- src/nvim/testdir/test_autocmd.vim | 3 +++ src/nvim/version.c | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 41f97b0cef..e368838df7 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -2294,7 +2294,7 @@ int do_ecmd( } // Close the link to the current buffer. This will set - // curwin->w_buffer to NULL. + // oldwin->w_buffer to NULL. u_sync(false); close_buffer(oldwin, curbuf, (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD, diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 34cca5f612..b5d78c183f 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -33,6 +33,7 @@ if has('timers') call feedkeys('a', 'x!') call assert_equal(1, g:triggered) au! CursorHoldI + set updatetime& endfunc func Test_cursorhold_insert_ctrl_x() @@ -44,6 +45,7 @@ if has('timers') call feedkeys("a\", 'x!') call assert_equal(0, g:triggered) au! CursorHoldI + set updatetime& endfunc endif @@ -223,6 +225,7 @@ func Test_augroup_warning() augroup Another augroup END call assert_true(match(execute('au VimEnter'), "-Deleted-.*VimEnter") >= 0) + augroup! Another " no warning for postpone aucmd delete augroup StartOK diff --git a/src/nvim/version.c b/src/nvim/version.c index 21f3a493c3..84cb8fea9b 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -100,7 +100,7 @@ static int included_patches[] = { // 2344 NA // 2343, // 2342 NA - // 2341, + 2341, // 2340 NA // 2339, // 2338 NA From 10f6624f65edbc8af84c9775e6712484be9c81a5 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 10:39:24 +0100 Subject: [PATCH 0063/1671] vim-patch:7.4.2328 Problem: Crash when BufWinLeave autocmd goes to another tab page. (Hirohito Higashi) Solution: Make close_buffer() go back to the right window. --- src/nvim/buffer.c | 14 ++++++++++++++ src/nvim/testdir/test_autocmd.vim | 11 +++++++++++ src/nvim/version.c | 2 +- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 5edc87eab1..7429e8628e 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -337,6 +337,10 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) bool del_buf = (action == DOBUF_DEL || action == DOBUF_WIPE); bool wipe_buf = (action == DOBUF_WIPE); + bool is_curwin = (curwin != NULL && curwin->w_buffer == buf); + win_T *the_curwin = curwin; + tabpage_T *the_curtab = curtab; + // Force unloading or deleting when 'bufhidden' says so, but not for terminal // buffers. // The caller must take care of NOT deleting/freeing when 'bufhidden' is @@ -419,6 +423,16 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) if (aborting()) /* autocmds may abort script processing */ return; } + + // If the buffer was in curwin and the window has changed, go back to that + // window, if it still exists. This avoids that ":edit x" triggering a + // "tabnext" BufUnload autocmd leaves a window behind without a buffer. + if (is_curwin && curwin != the_curwin && win_valid_any_tab(the_curwin)) { + block_autocmds(); + goto_tabpage_win(the_curtab, the_curwin); + unblock_autocmds(); + } + int nwindows = buf->b_nwindows; /* decrease the link count from windows (unless not in any window) */ diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index b5d78c183f..a122a62561 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -98,6 +98,17 @@ function Test_autocmd_bufunload_with_tabnext() quit endfunc +function Test_autocmd_bufwinleave_with_tabfirst() + tabedit + augroup sample + autocmd! + autocmd BufWinLeave tabfirst + augroup END + call setline(1, ['a', 'b', 'c']) + edit! a.txt + tabclose +endfunc + " SEGV occurs in older versions. (At least 7.4.2321 or older) function Test_autocmd_bufunload_avoiding_SEGV_01() split aa.txt diff --git a/src/nvim/version.c b/src/nvim/version.c index 84cb8fea9b..d1d3e5a3c9 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -113,7 +113,7 @@ static int included_patches[] = { 2331, // 2330, 2329, - // 2328, + 2328, // 2327 NA 2326, // 2325 NA From 2e9c1a9c4a6e2aaaef87c1c37b9cd14965b6d7b8 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 12:09:11 +0100 Subject: [PATCH 0064/1671] vim-patch:8.0.0178 Problem: test_command_count may fail when a previous test interferes, seen on MS-Windows. Solution: Run it separately. https://github.com/vim/vim/commit/9b73c4a215cb5f0f7df1e7f0663aea2bce1914ab --- src/nvim/testdir/test_alot.vim | 1 - 1 file changed, 1 deletion(-) diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index 241300f688..8aa0f417d1 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -3,7 +3,6 @@ source test_assign.vim source test_autocmd.vim -source test_command_count.vim source test_cursor_func.vim source test_execute_func.vim source test_ex_undo.vim From b0e34497b357462024c07d506a16426d58475497 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 10:47:46 +0100 Subject: [PATCH 0065/1671] test/legacy: fix test_autocmd.vim vim-patch:8.0.0487 --- src/nvim/testdir/runtest.vim | 2 ++ src/nvim/testdir/test_autocmd.vim | 12 +++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index b4eb9de506..1cf7ab475c 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -72,6 +72,8 @@ let v:testing = 1 set directory^=. set backspace= set nohidden smarttab noautoindent noautoread complete-=i noruler noshowcmd +" Prevent Nvim log from writing to stderr. +let $NVIM_LOG_FILE='Xnvim.log' function RunTheTest(test) echo 'Executing ' . a:test diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index a122a62561..5b5232f06e 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -359,11 +359,11 @@ function Test_autocmd_bufwipe_in_SessLoadPost() \ 'let v:swapchoice="e"', \ 'augroup test_autocmd_sessionload', \ 'autocmd!', - \ 'autocmd SessionLoadPost * 4bw!', - \ 'augroup END' + \ 'autocmd SessionLoadPost * 4bw!|qall!', + \ 'augroup END', \ ] call writefile(content, 'Xvimrc') - let a=system(v:progpath. ' --headless -u Xvimrc --noplugins -S Session.vim') + let a=system(v:progpath. ' --headless -i NONE -u Xvimrc --noplugins -S Session.vim') call assert_match('E814', a) unlet! g:bufnr @@ -395,11 +395,13 @@ function Test_autocmd_bufwipe_in_SessLoadPost2() \ ' exec ''bwipeout '' . b', \ ' endif', \ ' endfor', - \ 'call append("1", "SessionLoadPost DONE")', + \ 'redraw!', + \ 'echon "SessionLoadPost DONE"', + \ 'qall!', \ 'endfunction', \ 'au SessionLoadPost * call DeleteInactiveBufs()'] call writefile(content, 'Xvimrc') - let a=system(v:progpath. ' --headless -u Xvimrc --noplugins -S Session.vim') + let a=system(v:progpath. ' --headless -i NONE -u Xvimrc --noplugins -S Session.vim') " this probably only matches on unix if has("unix") call assert_notmatch('Caught deadly signal SEGV', a) From 53ccd07fa1c0bfcb79ca11bd873ea12f4dc12f4a Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 11:02:29 +0100 Subject: [PATCH 0066/1671] lint --- src/nvim/buffer.c | 4 ++-- src/nvim/ex_docmd.c | 18 ++++++++++-------- src/nvim/window.c | 5 +++-- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 7429e8628e..3c416c157f 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -4518,8 +4518,8 @@ void ex_buffer_all(exarg_T *eap) ? wp->w_height + wp->w_status_height < Rows - p_ch - tabline_height() : wp->w_width != Columns) - || (had_tab > 0 && wp != firstwin) - ) && firstwin != lastwin + || (had_tab > 0 && wp != firstwin)) + && firstwin != lastwin && !(wp->w_closing || wp->w_buffer->b_locked > 0) ) { win_close(wp, FALSE); diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index ebfbf2bf09..abf4371455 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5833,11 +5833,12 @@ static void ex_quit_all(exarg_T *eap) text_locked_msg(); return; } - apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, curbuf); - /* Refuse to quit when locked or when the buffer in the last window is - * being closed (can only happen in autocommands). */ - if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_locked > 0)) + apply_autocmds(EVENT_QUITPRE, NULL, NULL, false, curbuf); + // Refuse to quit when locked or when the buffer in the last window is + // being closed (can only happen in autocommands). + if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_locked > 0)) { return; + } exiting = true; if (eap->forceit || !check_changed_any(false, false)) { @@ -6128,11 +6129,12 @@ static void ex_exit(exarg_T *eap) text_locked_msg(); return; } - apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, curbuf); - /* Refuse to quit when locked or when the buffer in the last window is - * being closed (can only happen in autocommands). */ - if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_locked > 0)) + apply_autocmds(EVENT_QUITPRE, NULL, NULL, false, curbuf); + // Refuse to quit when locked or when the buffer in the last window is + // being closed (can only happen in autocommands). + if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_locked > 0)) { return; + } // if more files or windows we won't exit if (check_more(false, eap->forceit) == OK && only_one_window()) { diff --git a/src/nvim/window.c b/src/nvim/window.c index a737ffb33c..15482d9061 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1746,7 +1746,7 @@ void close_windows(buf_T *buf, int keep_curwin) FOR_ALL_WINDOWS_IN_TAB(wp, tp) { if (wp->w_buffer == buf && !(wp->w_closing || wp->w_buffer->b_locked > 0)) { - win_close_othertab(wp, FALSE, tp); + win_close_othertab(wp, false, tp); /* Start all over, the tab page may be closed and * autocommands may change the window layout. */ @@ -1881,7 +1881,8 @@ int win_close(win_T *win, int free_buf) return FAIL; } - if (win->w_closing || (win->w_buffer != NULL && win->w_buffer->b_locked > 0)) { + if (win->w_closing + || (win->w_buffer != NULL && win->w_buffer->b_locked > 0)) { return FAIL; // window is already being closed } if (win == aucmd_win) { From 929859ed8156a45f5acb491d82f8c06ef5f57902 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 13:11:10 +0100 Subject: [PATCH 0067/1671] vim-patch:7.4.2355 Problem: Regexp fails to match when using "\>\)\?". (Ramel) Solution: When a state is already in the list, but addstate_here() is used and the existing state comes later, add the new state anyway. https://github.com/vim/vim/commit/16b3578f355282846f2600ce77fb344950f0b9ce --- src/nvim/regexp_nfa.c | 61 +++++++++++++++++++-------- src/nvim/testdir/test_regexp_utf8.vim | 9 ++++ src/nvim/version.c | 2 +- 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 474f3df32a..3f4e12af4a 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -3893,21 +3893,25 @@ state_in_list ( return FALSE; } -/* - * Add "state" and possibly what follows to state list ".". - * Returns "subs_arg", possibly copied into temp_subs. - */ +// Offset used for "off" by addstate_here(). +#define ADDSTATE_HERE_OFFSET 10 +// Add "state" and possibly what follows to state list ".". +// Returns "subs_arg", possibly copied into temp_subs. static regsubs_T * addstate ( nfa_list_T *l, /* runtime state list */ nfa_state_T *state, /* state to update */ regsubs_T *subs_arg, /* pointers to subexpressions */ nfa_pim_T *pim, /* postponed look-behind match */ - int off /* byte offset, when -1 go to next line */ -) + int off_arg) /* byte offset, when -1 go to next line */ { int subidx; + int off = off_arg; + int add_here = FALSE; + int listindex = 0; + int k; + int found = FALSE; nfa_thread_T *thread; lpos_T save_lpos; int save_in_use; @@ -3920,6 +3924,12 @@ addstate ( int did_print = FALSE; #endif + if (off_arg <= -ADDSTATE_HERE_OFFSET) { + add_here = true; + off = 0; + listindex = -(off_arg + ADDSTATE_HERE_OFFSET); + } + switch (state->c) { case NFA_NCLOSE: case NFA_MCLOSE: @@ -3996,13 +4006,28 @@ addstate ( * lower position is preferred. */ if (!nfa_has_backref && pim == NULL && !l->has_pim && state->c != NFA_MATCH) { + + /* When called from addstate_here() do insert before + * existing states. */ + if (add_here) { + for (k = 0; k < l->n && k < listindex; ++k) { + if (l->t[k].state->id == state->id) { + found = TRUE; + break; + } + } + } + + if (!add_here || found) { skip_add: #ifdef REGEXP_DEBUG - nfa_set_code(state->c); - fprintf(log_fd, "> Not adding state %d to list %d. char %d: %s\n", - abs(state->id), l->id, state->c, code); + nfa_set_code(state->c); + fprintf(log_fd, "> Not adding state %d to list %d. char %d: %s pim: %s has_pim: %d found: %d\n", + abs(state->id), l->id, state->c, code, + pim == NULL ? "NULL" : "yes", l->has_pim, found); #endif return subs; + } } /* Do not add the state again when it exists with the same @@ -4058,14 +4083,14 @@ skip_add: case NFA_SPLIT: /* order matters here */ - subs = addstate(l, state->out, subs, pim, off); - subs = addstate(l, state->out1, subs, pim, off); + subs = addstate(l, state->out, subs, pim, off_arg); + subs = addstate(l, state->out1, subs, pim, off_arg); break; case NFA_EMPTY: case NFA_NOPEN: case NFA_NCLOSE: - subs = addstate(l, state->out, subs, pim, off); + subs = addstate(l, state->out, subs, pim, off_arg); break; case NFA_MOPEN: @@ -4145,7 +4170,7 @@ skip_add: sub->list.line[subidx].start = reginput + off; } - subs = addstate(l, state->out, subs, pim, off); + subs = addstate(l, state->out, subs, pim, off_arg); /* "subs" may have changed, need to set "sub" again */ if (state->c >= NFA_ZOPEN && state->c <= NFA_ZOPEN9) sub = &subs->synt; @@ -4168,7 +4193,7 @@ skip_add: ? subs->norm.list.multi[0].end_lnum >= 0 : subs->norm.list.line[0].end != NULL)) { /* Do not overwrite the position set by \ze. */ - subs = addstate(l, state->out, subs, pim, off); + subs = addstate(l, state->out, subs, pim, off_arg); break; } case NFA_MCLOSE1: @@ -4228,7 +4253,7 @@ skip_add: save_lpos.col = 0; } - subs = addstate(l, state->out, subs, pim, off); + subs = addstate(l, state->out, subs, pim, off_arg); /* "subs" may have changed, need to set "sub" again */ if (state->c >= NFA_ZCLOSE && state->c <= NFA_ZCLOSE9) sub = &subs->synt; @@ -4266,8 +4291,10 @@ addstate_here ( int count; int listidx = *ip; - /* first add the state(s) at the end, so that we know how many there are */ - addstate(l, state, subs, pim, 0); + /* First add the state(s) at the end, so that we know how many there are. + * Pass the listidx as offset (avoids adding another argument to + * addstate(). */ + addstate(l, state, subs, pim, -listidx - ADDSTATE_HERE_OFFSET); /* when "*ip" was at the end of the list, nothing to do */ if (listidx + 1 == tlen) diff --git a/src/nvim/testdir/test_regexp_utf8.vim b/src/nvim/testdir/test_regexp_utf8.vim index 9e9a3de500..7f3b31575d 100644 --- a/src/nvim/testdir/test_regexp_utf8.vim +++ b/src/nvim/testdir/test_regexp_utf8.vim @@ -97,3 +97,12 @@ func Test_recursive_substitute() call setwinvar(1, 'myvar', 1) bwipe! endfunc + +func Test_eow_with_optional() + let expected = ['abc def', 'abc', 'def', '', '', '', '', '', '', ''] + for re in range(0, 2) + exe 'set re=' . re + let actual = matchlist('abc def', '\(abc\>\)\?\s*\(def\)') + call assert_equal(expected, actual) + endfor +endfunc diff --git a/src/nvim/version.c b/src/nvim/version.c index d1d3e5a3c9..1b6e513c49 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -86,7 +86,7 @@ static int included_patches[] = { // 2358 NA // 2357, // 2356, - // 2355, + 2355, // 2354, 2353, // 2352 NA From 5ee211770d9c7999a01413f368e82588f3d0426d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 13:22:14 +0100 Subject: [PATCH 0068/1671] vim-patch:8.0.0033 Problem: Cannot use overlapping positions with matchaddpos(). Solution: Check end of match. (Ozaki Kiichi) Add a test (Hirohito Higashi) https://github.com/vim/vim/commit/a6c27ee6db2c328e0ab0e6d143e2a295a0bb9c9a --- src/nvim/testdir/test_match.vim | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/nvim/testdir/test_match.vim b/src/nvim/testdir/test_match.vim index 9398ef2f27..066bb2f6a1 100644 --- a/src/nvim/testdir/test_match.vim +++ b/src/nvim/testdir/test_match.vim @@ -198,6 +198,16 @@ func Test_matchaddpos() call assert_equal(screenattr(2,2), screenattr(1,10)) call assert_notequal(screenattr(2,2), screenattr(1,11)) + " Check overlapping pos + call clearmatches() + call setline(1, ['1234567890', 'NH']) + call matchaddpos('Error', [[1,1,5], [1,3,5], [2,2]]) + redraw! + call assert_notequal(screenattr(2,2), 0) + call assert_equal(screenattr(2,2), screenattr(1,5)) + call assert_equal(screenattr(2,2), screenattr(1,7)) + call assert_notequal(screenattr(2,2), screenattr(1,8)) + nohl call clearmatches() syntax off From cd9fc4627ecf4d675e2c221b614cc2eb40aabf9c Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 13:26:02 +0100 Subject: [PATCH 0069/1671] vim-patch:8.0.0049 Problem: When a match ends in part of concealed text highlighting, it might mess up concealing by resetting prev_syntax_id. Solution: Do not reset prev_syntax_id and add a test to verify. (Christian Brabandt, closes vim/vim#1092) https://github.com/vim/vim/commit/2f97912800e86a296c001832bbbf2fc425f1e533 --- src/nvim/screen.c | 1 - src/nvim/testdir/test_matchadd_conceal.vim | 22 ++++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/nvim/screen.c b/src/nvim/screen.c index b98e59ed06..280b8c65fe 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2922,7 +2922,6 @@ win_line ( } } else if (v == (long)shl->endcol) { shl->attr_cur = 0; - prev_syntax_id = 0; next_search_hl(wp, shl, lnum, (colnr_T)v, shl == &search_hl ? NULL : cur); diff --git a/src/nvim/testdir/test_matchadd_conceal.vim b/src/nvim/testdir/test_matchadd_conceal.vim index bc1c28d6e9..c788689e33 100644 --- a/src/nvim/testdir/test_matchadd_conceal.vim +++ b/src/nvim/testdir/test_matchadd_conceal.vim @@ -260,3 +260,25 @@ function! Test_matchadd_repeat_conceal_with_syntax_off() quit! endfunction + +function! Test_matchadd_and_syn_conceal() + new + let cnt='Inductive bool : Type := | true : bool | false : bool.' + let expect = 'Inductive - : Type := | true : - | false : -.' + 0put =cnt + " set filetype and :syntax on to change screenattr() + set cole=1 cocu=nv + hi link CheckedByCoq WarningMsg + syntax on + syntax keyword coqKwd bool conceal cchar=- + redraw! + call assert_equal(expect, s:screenline(1)) + call assert_notequal(screenattr(1, 10) , screenattr(1, 11)) + call assert_notequal(screenattr(1, 11) , screenattr(1, 12)) + call assert_equal(screenattr(1, 11) , screenattr(1, 32)) + call matchadd('CheckedByCoq', '\%<2l\%>9c\%<16c') + call assert_equal(expect, s:screenline(1)) + call assert_notequal(screenattr(1, 10) , screenattr(1, 11)) + call assert_notequal(screenattr(1, 11) , screenattr(1, 12)) + call assert_equal(screenattr(1, 11) , screenattr(1, 32)) +endfunction From 89abed7d852518528051c1c0fdf84d34d3d8c2d5 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 13:56:10 +0100 Subject: [PATCH 0070/1671] vim-patch:8.0.0032 Problem: Tests may change the input file when something goes wrong. Solution: Avoid writing the input file. https://github.com/vim/vim/commit/3e8474dd50f64c998bb665ce852f584a58dede6b --- src/nvim/testdir/test_tabpage.vim | 1 + 1 file changed, 1 insertion(+) diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim index 7c3039ba24..c2aadcea6e 100644 --- a/src/nvim/testdir/test_tabpage.vim +++ b/src/nvim/testdir/test_tabpage.vim @@ -11,6 +11,7 @@ function Test_tabpage() 0tabnew 1tabnew $tabnew + %del tabdo call append(line('$'), tabpagenr()) tabclose! 2 tabrewind From 78a4c73cf06aaf8cd496d39bbbeaef821906fc25 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 14:11:27 +0100 Subject: [PATCH 0071/1671] vim-patch:7.4.2339 Problem: Tab page test fails when run as fake root. Solution: Check 'buftype' instead of 'filetype'. (James McCoy, closes vim/vim#1042) https://github.com/vim/vim/commit/100f5c90f4d4fb40bc3aeabc35192db371f5988f --- src/nvim/testdir/test_tabpage.vim | 2 +- src/nvim/version.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim index c2aadcea6e..870cb4da13 100644 --- a/src/nvim/testdir/test_tabpage.vim +++ b/src/nvim/testdir/test_tabpage.vim @@ -205,7 +205,7 @@ function Test_tabpage_with_tab_modifier() exec 'tabnext ' . a:pre_nr exec a:cmd call assert_equal(a:post_nr, tabpagenr()) - call assert_equal('help', &filetype) + call assert_equal('help', &buftype) helpclose endfunc diff --git a/src/nvim/version.c b/src/nvim/version.c index 1b6e513c49..db3b9b51b2 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -102,7 +102,7 @@ static int included_patches[] = { // 2342 NA 2341, // 2340 NA - // 2339, + 2339, // 2338 NA 2337, 2336, From c99514c2a4ebfc35413daa57d017be16bd179e90 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 13:42:33 +0100 Subject: [PATCH 0072/1671] test/legacy: Run test_match, test_tabpage isolated These are failing when run as a batch. Most likely some Vim runtime patch fixed something, but we don't have it yet. Just isolate them for now. Also test_matchadd_conceal_utf8 (it's not there in Vim tree, either). --- src/nvim/testdir/test_alot.vim | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index 8aa0f417d1..9d1f07fc0b 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -13,8 +13,6 @@ source test_filter_map.vim source test_goto.vim source test_jumps.vim source test_lambda.vim -source test_match.vim -source test_matchadd_conceal_utf8.vim source test_menu.vim source test_mapping.vim source test_messages.vim @@ -26,7 +24,7 @@ source test_source_utf8.vim source test_statusline.vim source test_syn_attr.vim source test_tabline.vim -source test_tabpage.vim +" source test_tabpage.vim source test_tagcase.vim source test_tagjump.vim source test_true_false.vim From 386a5f379b059060808d9772cd1f150f507a5577 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 14:35:21 +0100 Subject: [PATCH 0073/1671] test/legacy: Makefile Add missing tests These tests aren't in test_alot.vim, so they need to be added to the Makefile or they won't be run. --- src/nvim/testdir/Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 0337ab13dd..d948cd0f93 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -36,6 +36,8 @@ NEW_TESTS ?= \ test_diffmode.res \ test_farsi.res \ test_filter_map.res \ + test_fold.res \ + test_glob2regpat.res \ test_gn.res \ test_hardcopy.res \ test_help_tagjump.res \ @@ -47,13 +49,16 @@ NEW_TESTS ?= \ test_marks.res \ test_match.res \ test_matchadd_conceal.res \ + test_matchadd_conceal_utf8.res \ test_nested_function.res \ test_normal.res \ test_quickfix.res \ test_signs.res \ test_syntax.res \ + test_tabpage.res \ test_textobjects.res \ test_timers.res \ + test_undo.res \ test_usercommands.res \ test_viml.res \ test_visual.res \ From 0f3afdaa1b5a75a5ed54a69dc43e32ddbb6a32ea Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 16:22:04 +0100 Subject: [PATCH 0074/1671] vim-patch:8.0.0259 Problem: Tab commands do not handle count correctly. (Ken Hamada) Solution: Add ADDR_TABS_RELATIVE. (Hirohito Higashi) https://github.com/vim/vim/commit/2f72c70657129c16e6b0e413752a775c804f02f8 --- src/nvim/ex_cmds.lua | 29 +- src/nvim/ex_cmds_defs.h | 3 +- src/nvim/ex_docmd.c | 284 ++++++++++++------ src/nvim/testdir/test_tabpage.vim | 242 ++++++++++++++- test/functional/legacy/062_tab_pages_spec.lua | 18 +- 5 files changed, 448 insertions(+), 128 deletions(-) diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index 5f81306fc1..92f0669422 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -34,7 +34,8 @@ local ADDR_ARGUMENTS = 2 local ADDR_LOADED_BUFFERS = 3 local ADDR_BUFFERS = 4 local ADDR_TABS = 5 -local ADDR_QUICKFIX = 6 +local ADDR_TABS_RELATIVE = 6 +local ADDR_QUICKFIX = 7 local ADDR_OTHER = 99 -- The following table is described in ex_cmds_defs.h file. @@ -2650,12 +2651,12 @@ return { { command='tab', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type=ADDR_TABS, func='ex_wrongmodifier', }, { command='tabclose', - flags=bit.bor(RANGE, NOTADR, COUNT, BANG, TRLBAR, CMDWIN), + flags=bit.bor(BANG, RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN), addr_type=ADDR_TABS, func='ex_tabclose', }, @@ -2680,7 +2681,7 @@ return { { command='tabfirst', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type=ADDR_TABS, func='ex_tabnext', }, { @@ -2692,13 +2693,13 @@ return { { command='tablast', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type=ADDR_TABS, func='ex_tabnext', }, { command='tabnext', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR), + addr_type=ADDR_TABS, func='ex_tabnext', }, { @@ -2709,32 +2710,32 @@ return { }, { command='tabonly', - flags=bit.bor(BANG, RANGE, NOTADR, TRLBAR, CMDWIN), + flags=bit.bor(BANG, RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN), addr_type=ADDR_TABS, func='ex_tabonly', }, { command='tabprevious', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR), + addr_type=ADDR_TABS_RELATIVE, func='ex_tabnext', }, { command='tabNext', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR), + addr_type=ADDR_TABS_RELATIVE, func='ex_tabnext', }, { command='tabrewind', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type=ADDR_TABS, func='ex_tabnext', }, { command='tabs', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type=ADDR_TABS, func='ex_tabs', }, { diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h index c6389a0c8b..133c37cef4 100644 --- a/src/nvim/ex_cmds_defs.h +++ b/src/nvim/ex_cmds_defs.h @@ -72,7 +72,8 @@ #define ADDR_LOADED_BUFFERS 3 #define ADDR_BUFFERS 4 #define ADDR_TABS 5 -#define ADDR_QUICKFIX 6 +#define ADDR_TABS_RELATIVE 6 // Tab page that only relative +#define ADDR_QUICKFIX 7 #define ADDR_OTHER 99 typedef struct exarg exarg_T; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index abf4371455..4f34312562 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1522,8 +1522,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.line2 = curwin->w_cursor.lnum; break; case ADDR_WINDOWS: - lnum = CURRENT_WIN_NR; - ea.line2 = lnum; + ea.line2 = CURRENT_WIN_NR; break; case ADDR_ARGUMENTS: ea.line2 = curwin->w_arg_idx + 1; @@ -1536,8 +1535,10 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.line2 = curbuf->b_fnum; break; case ADDR_TABS: - lnum = CURRENT_TAB_NR; - ea.line2 = lnum; + ea.line2 = CURRENT_TAB_NR; + break; + case ADDR_TABS_RELATIVE: + ea.line2 = 1; break; case ADDR_QUICKFIX: ea.line2 = qf_get_cur_valid_idx(&ea); @@ -1587,6 +1588,10 @@ static char_u * do_one_cmd(char_u **cmdlinep, goto doend; } break; + case ADDR_TABS_RELATIVE: + errormsg = (char_u *)_(e_invrange); + goto doend; + break; case ADDR_ARGUMENTS: if (ARGCOUNT == 0) { ea.line1 = ea.line2 = 0; @@ -1973,6 +1978,9 @@ static char_u * do_one_cmd(char_u **cmdlinep, case ADDR_TABS: ea.line2 = LAST_TAB_NR; break; + case ADDR_TABS_RELATIVE: + ea.line2 = 1; + break; case ADDR_ARGUMENTS: if (ARGCOUNT == 0) { ea.line1 = ea.line2 = 0; @@ -2028,11 +2036,11 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.line1 = ea.line2; ea.line2 += n - 1; ++ea.addr_count; - /* - * Be vi compatible: no error message for out of range. - */ - if (ea.line2 > curbuf->b_ml.ml_line_count) + // Be vi compatible: no error message for out of range. + if (ea.addr_type == ADDR_LINES + && ea.line2 > curbuf->b_ml.ml_line_count) { ea.line2 = curbuf->b_ml.ml_line_count; + } } } @@ -3459,6 +3467,11 @@ static linenr_T get_address(exarg_T *eap, case ADDR_TABS: lnum = CURRENT_TAB_NR; break; + case ADDR_TABS_RELATIVE: + EMSG(_(e_invrange)); + cmd = NULL; + goto error; + break; case ADDR_QUICKFIX: lnum = qf_get_cur_valid_idx(eap); break; @@ -3493,6 +3506,11 @@ static linenr_T get_address(exarg_T *eap, case ADDR_TABS: lnum = LAST_TAB_NR; break; + case ADDR_TABS_RELATIVE: + EMSG(_(e_invrange)); + cmd = NULL; + goto error; + break; case ADDR_QUICKFIX: lnum = qf_get_size(eap); if (lnum == 0) { @@ -3641,6 +3659,9 @@ static linenr_T get_address(exarg_T *eap, case ADDR_TABS: lnum = CURRENT_TAB_NR; break; + case ADDR_TABS_RELATIVE: + lnum = 1; + break; case ADDR_QUICKFIX: lnum = qf_get_cur_valid_idx(eap); break; @@ -3655,7 +3676,12 @@ static linenr_T get_address(exarg_T *eap, n = 1; else n = getdigits(&cmd); - if (addr_type == ADDR_LOADED_BUFFERS || addr_type == ADDR_BUFFERS) { + + if (addr_type == ADDR_TABS_RELATIVE) { + EMSG(_(e_invrange)); + cmd = NULL; + goto error; + } else if (addr_type == ADDR_LOADED_BUFFERS || addr_type == ADDR_BUFFERS) { lnum = compute_buffer_local_count( addr_type, lnum, (i == '-') ? -1 * n : n); } else { @@ -3777,6 +3803,9 @@ static char_u *invalid_range(exarg_T *eap) return (char_u *)_(e_invrange); } break; + case ADDR_TABS_RELATIVE: + // Do nothing + break; case ADDR_QUICKFIX: assert(eap->line2 >= 0); if (eap->line2 != 1 && (size_t)eap->line2 > qf_get_size(eap)) { @@ -4285,6 +4314,104 @@ static int getargopt(exarg_T *eap) return OK; } +/* + * Handle the argument for a tabpage related ex command. + * Returns a tabpage number. + * When an error is encountered then eap->errmsg is set. + */ + static int +get_tabpage_arg(exarg_T *eap) +{ + int tab_number; + int unaccept_arg0 = (eap->cmdidx == CMD_tabmove) ? 0 : 1; + + if (eap->arg && *eap->arg != NUL) + { + char_u *p = eap->arg; + char_u *p_save; + int relative = 0; /* argument +N/-N means: go to N places to the + * right/left relative to the current position. */ + + if (*p == '-') + { + relative = -1; + p++; + } + else if (*p == '+') + { + relative = 1; + p++; + } + + p_save = p; + tab_number = getdigits(&p); + + if (relative == 0) + { + if (STRCMP(p, "$") == 0) + tab_number = LAST_TAB_NR; + else if (p == p_save || *p_save == '-' || *p != NUL + || tab_number > LAST_TAB_NR) + { + /* No numbers as argument. */ + eap->errmsg = e_invarg; + goto theend; + } + } + else + { + if (*p_save == NUL) + tab_number = 1; + else if (p == p_save || *p_save == '-' || *p != NUL + || tab_number == 0) + { + /* No numbers as argument. */ + eap->errmsg = e_invarg; + goto theend; + } + tab_number = tab_number * relative + tabpage_index(curtab); + if (!unaccept_arg0 && relative == -1) + --tab_number; + } + if (tab_number < unaccept_arg0 || tab_number > LAST_TAB_NR) + eap->errmsg = e_invarg; + } + else if (eap->addr_count > 0) + { + if (unaccept_arg0 && eap->line2 == 0) + eap->errmsg = e_invrange; + else + { + tab_number = eap->line2; + if (!unaccept_arg0 && **eap->cmdlinep == '-') + { + --tab_number; + if (tab_number < unaccept_arg0) + eap->errmsg = e_invarg; + } + } + } + else + { + switch (eap->cmdidx) + { + case CMD_tabnext: + tab_number = tabpage_index(curtab) + 1; + if (tab_number > LAST_TAB_NR) + tab_number = 1; + break; + case CMD_tabmove: + tab_number = LAST_TAB_NR; + break; + default: + tab_number = tabpage_index(curtab); + } + } + +theend: + return tab_number; +} + /* * ":abbreviate" and friends. */ @@ -5938,8 +6065,9 @@ static void ex_tabclose(exarg_T *eap) else if (first_tabpage->tp_next == NULL) EMSG(_("E784: Cannot close last tab page")); else { - if (eap->addr_count > 0) { - tp = find_tabpage((int)eap->line2); + int tab_number = get_tabpage_arg(eap); + if (eap->errmsg == NULL) { + tp = find_tabpage(tab_number); if (tp == NULL) { beep_flush(); return; @@ -5947,44 +6075,43 @@ static void ex_tabclose(exarg_T *eap) if (tp != curtab) { tabpage_close_other(tp, eap->forceit); return; + } else if (!text_locked() && !curbuf_locked()) { + tabpage_close(eap->forceit); } } - if (!text_locked() - && !curbuf_locked() - ) - tabpage_close(eap->forceit); } } -/* - * ":tabonly": close all tab pages except the current one - */ +/// ":tabonly": close all tab pages except the current one static void ex_tabonly(exarg_T *eap) { - if (cmdwin_type != 0) + if (cmdwin_type != 0) { cmdwin_result = K_IGNORE; - else if (first_tabpage->tp_next == NULL) - MSG(_("Already only one tab page")); - else { - if (eap->addr_count > 0) - goto_tabpage(eap->line2); - /* Repeat this up to a 1000 times, because autocommands may mess - * up the lists. */ - for (int done = 0; done < 1000; ++done) { - FOR_ALL_TABS(tp) { - if (tp->tp_topframe != topframe) { - tabpage_close_other(tp, eap->forceit); - /* if we failed to close it quit */ - if (valid_tabpage(tp)) - done = 1000; - /* start over, "tp" is now invalid */ + } else if (first_tabpage->tp_next == NULL) { + MSG(_("Already only one tab page")); + } else { + int tab_number = get_tabpage_arg(eap); + if (eap->errmsg == NULL) { + goto_tabpage(tab_number); + // Repeat this up to a 1000 times, because autocommands may + // mess up the lists. + for (int done = 0; done < 1000; done++) { + FOR_ALL_TABS(tp) { + if (tp->tp_topframe != topframe) { + tabpage_close_other(tp, eap->forceit); + // if we failed to close it quit + if (valid_tabpage(tp)) { + done = 1000; + } + // start over, "tp" is now invalid + break; + } + } + assert(first_tabpage); + if (first_tabpage->tp_next == NULL) { break; } } - assert(first_tabpage); - if (first_tabpage->tp_next == NULL) { - break; - } } } } @@ -6473,6 +6600,8 @@ void tabpage_new(void) */ static void ex_tabnext(exarg_T *eap) { + int tab_number; + switch (eap->cmdidx) { case CMD_tabfirst: case CMD_tabrewind: @@ -6483,10 +6612,34 @@ static void ex_tabnext(exarg_T *eap) break; case CMD_tabprevious: case CMD_tabNext: - goto_tabpage(eap->addr_count == 0 ? -1 : -(int)eap->line2); + if (eap->arg && *eap->arg != NUL) { + char_u *p = eap->arg; + char_u *p_save = p; + + tab_number = getdigits(&p); + if (p == p_save || *p_save == '-' || *p != NUL || tab_number == 0) { + // No numbers as argument. + eap->errmsg = e_invarg; + return; + } + } else { + if (eap->addr_count == 0) { + tab_number = 1; + } else { + tab_number = eap->line2; + if (tab_number < 1) { + eap->errmsg = e_invrange; + return; + } + } + } + goto_tabpage(-tab_number); break; - default: /* CMD_tabnext */ - goto_tabpage(eap->addr_count == 0 ? 0 : (int)eap->line2); + default: // CMD_tabnext + tab_number = get_tabpage_arg(eap); + if (eap->errmsg == NULL) { + goto_tabpage(tab_number); + } break; } } @@ -6496,53 +6649,10 @@ static void ex_tabnext(exarg_T *eap) */ static void ex_tabmove(exarg_T *eap) { - int tab_number; - - if (eap->arg && *eap->arg != NUL) { - char_u *p = eap->arg; - int relative = 0; /* argument +N/-N means: move N places to the - * right/left relative to the current position. */ - - if (*eap->arg == '-') { - relative = -1; - p = eap->arg + 1; - } else if (*eap->arg == '+') { - relative = 1; - p = eap->arg + 1; - } else - p = eap->arg; - - if (relative == 0) { - if (STRCMP(p, "$") == 0) { - tab_number = LAST_TAB_NR; - } else if (p == skipdigits(p)) { - // No numbers as argument. - eap->errmsg = e_invarg; - return; - } else { - tab_number = getdigits(&p); - } - } else { - if (*p != NUL) { - tab_number = getdigits(&p); - } else { - tab_number = 1; - } - tab_number = tab_number * relative + tabpage_index(curtab); - if (relative == -1) { - --tab_number; - } - } - } else if (eap->addr_count != 0) { - tab_number = eap->line2; - if (**eap->cmdlinep == '-') { - --tab_number; - } - } else { - tab_number = LAST_TAB_NR; + int tab_number = get_tabpage_arg(eap); + if (eap->errmsg == NULL) { + tabpage_move(tab_number); } - - tabpage_move(tab_number); } /* diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim index 870cb4da13..33139fcda0 100644 --- a/src/nvim/testdir/test_tabpage.vim +++ b/src/nvim/testdir/test_tabpage.vim @@ -94,10 +94,6 @@ function Test_tabpage() call assert_equal(7, tabpagenr()) tabmove call assert_equal(10, tabpagenr()) - tabmove -20 - call assert_equal(1, tabpagenr()) - tabmove +20 - call assert_equal(10, tabpagenr()) 0tabmove call assert_equal(1, tabpagenr()) $tabmove @@ -110,7 +106,16 @@ function Test_tabpage() call assert_equal(4, tabpagenr()) 7tabmove 5 call assert_equal(5, tabpagenr()) + call assert_fails("99tabmove", 'E16:') + call assert_fails("+99tabmove", 'E16:') + call assert_fails("-99tabmove", 'E16:') call assert_fails("tabmove foo", 'E474:') + call assert_fails("tabmove 99", 'E474:') + call assert_fails("tabmove +99", 'E474:') + call assert_fails("tabmove -99", 'E474:') + call assert_fails("tabmove -3+", 'E474:') + call assert_fails("tabmove $3", 'E474:') + 1tabonly! endfunc " Test autocommands @@ -118,7 +123,6 @@ function Test_tabpage_with_autocmd() if !has('autocmd') return endif - tabonly! command -nargs=1 -bar C :call add(s:li, '=== ' . . ' ===')| augroup TestTabpageGroup au! @@ -183,8 +187,10 @@ function Test_tabpage_with_autocmd() autocmd TabDestructive TabEnter * nested :C tabnext 2 | C tabclose 3 let s:li = [] - C tabnext 3 - call assert_equal(['=== tabnext 3 ===', 'BufLeave', 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter', '=== tabnext 2 ===', 'BufLeave', 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter', '=== tabnext 2 ===', '=== tabclose 3 ===', 'BufEnter', '=== tabclose 3 ==='], s:li) + call assert_equal(3, tabpagenr('$')) + C tabnext 2 + call assert_equal(2, tabpagenr('$')) + call assert_equal(['=== tabnext 2 ===', 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter', '=== tabnext 2 ===', '=== tabclose 3 ==='], s:li) call assert_equal(['2/2'], [tabpagenr().'/'.tabpagenr('$')]) delcommand C @@ -192,8 +198,7 @@ function Test_tabpage_with_autocmd() augroup! TabDestructive autocmd! TestTabpageGroup augroup! TestTabpageGroup - tabonly! - bw! + 1tabonly! endfunction function Test_tabpage_with_tab_modifier() @@ -224,8 +229,223 @@ function Test_tabpage_with_tab_modifier() call assert_fails('-99tab help', 'E16:') delfunction s:check_tab - tabonly! - bw! + 1tabonly! +endfunction + +function Check_tab_count(pre_nr, cmd, post_nr) + exec 'tabnext' a:pre_nr + normal! G + exec a:cmd + call assert_equal(a:post_nr, tabpagenr(), a:cmd) +endfunc + +" Test for [count] of tabnext +function Test_tabpage_with_tabnext() + for n in range(4) + tabedit + call setline(1, ['', '', '3']) + endfor + + call Check_tab_count(1, 'tabnext', 2) + call Check_tab_count(1, '3tabnext', 3) + call Check_tab_count(1, '.tabnext', 1) + call Check_tab_count(1, '.+1tabnext', 2) + call Check_tab_count(2, '+tabnext', 3) + call Check_tab_count(2, '+2tabnext', 4) + call Check_tab_count(4, '-tabnext', 3) + call Check_tab_count(4, '-2tabnext', 2) + call Check_tab_count(3, '$tabnext', 5) + call assert_fails('0tabnext', 'E16:') + call assert_fails('99tabnext', 'E16:') + call assert_fails('+99tabnext', 'E16:') + call assert_fails('-99tabnext', 'E16:') + call Check_tab_count(1, 'tabnext 3', 3) + call Check_tab_count(2, 'tabnext +', 3) + call Check_tab_count(2, 'tabnext +2', 4) + call Check_tab_count(4, 'tabnext -', 3) + call Check_tab_count(4, 'tabnext -2', 2) + call Check_tab_count(3, 'tabnext $', 5) + call assert_fails('tabnext 0', 'E474:') + call assert_fails('tabnext .', 'E474:') + call assert_fails('tabnext -+', 'E474:') + call assert_fails('tabnext +2-', 'E474:') + call assert_fails('tabnext $3', 'E474:') + call assert_fails('tabnext 99', 'E474:') + call assert_fails('tabnext +99', 'E474:') + call assert_fails('tabnext -99', 'E474:') + + 1tabonly! +endfunction + +" Test for [count] of tabprevious +function Test_tabpage_with_tabprevious() + for n in range(5) + tabedit + call setline(1, ['', '', '3']) + endfor + + for cmd in ['tabNext', 'tabprevious'] + call Check_tab_count(6, cmd, 5) + call Check_tab_count(6, '3' . cmd, 3) + call Check_tab_count(6, '8' . cmd, 4) + call Check_tab_count(6, cmd . ' 3', 3) + call Check_tab_count(6, cmd . ' 8', 4) + for n in range(2) + for c in ['0', '.+3', '+', '+2' , '-', '-2' , '$', '+99', '-99'] + if n == 0 " pre count + let entire_cmd = c . cmd + let err_code = 'E16:' + else + let entire_cmd = cmd . ' ' . c + let err_code = 'E474:' + endif + call assert_fails(entire_cmd, err_code) + endfor + endfor + endfor + + 1tabonly! +endfunction + +function s:reconstruct_tabpage_for_test(nr) + let n = (a:nr > 2) ? a:nr - 2 : 1 + 1tabonly! + 0tabedit n0 + for n in range(1, n) + exec '$tabedit n' . n + if n == 1 + call setline(1, ['', '', '3']) + endif + endfor +endfunc + +" Test for [count] of tabclose +function Test_tabpage_with_tabclose() + + " pre count + call s:reconstruct_tabpage_for_test(6) + call Check_tab_count(3, 'tabclose!', 3) + call Check_tab_count(1, '3tabclose', 1) + call Check_tab_count(4, '4tabclose', 3) + call Check_tab_count(3, '1tabclose', 2) + call Check_tab_count(2, 'tabclose', 1) + call assert_equal(1, tabpagenr('$')) + call assert_equal('', bufname('')) + + call s:reconstruct_tabpage_for_test(6) + call Check_tab_count(2, '$tabclose', 2) + call Check_tab_count(4, '.tabclose', 4) + call Check_tab_count(3, '.+tabclose', 3) + call Check_tab_count(3, '.-2tabclose', 2) + call Check_tab_count(1, '.+1tabclose!', 1) + call assert_equal(1, tabpagenr('$')) + call assert_equal('', bufname('')) + + " post count + call s:reconstruct_tabpage_for_test(6) + call Check_tab_count(3, 'tabclose!', 3) + call Check_tab_count(1, 'tabclose 3', 1) + call Check_tab_count(4, 'tabclose 4', 3) + call Check_tab_count(3, 'tabclose 1', 2) + call Check_tab_count(2, 'tabclose', 1) + call assert_equal(1, tabpagenr('$')) + call assert_equal('', bufname('')) + + call s:reconstruct_tabpage_for_test(6) + call Check_tab_count(2, 'tabclose $', 2) + call Check_tab_count(4, 'tabclose', 4) + call Check_tab_count(3, 'tabclose +', 3) + call Check_tab_count(3, 'tabclose -2', 2) + call Check_tab_count(1, 'tabclose! +1', 1) + call assert_equal(1, tabpagenr('$')) + call assert_equal('', bufname('')) + + call s:reconstruct_tabpage_for_test(6) + for n in range(2) + for c in ['0', '$3', '99', '+99', '-99'] + if n == 0 " pre count + let entire_cmd = c . 'tabclose' + let err_code = 'E16:' + else + let entire_cmd = 'tabclose ' . c + let err_code = 'E474:' + endif + call assert_fails(entire_cmd, err_code) + call assert_equal(6, tabpagenr('$')) + endfor + endfor + + call assert_fails('3tabclose', 'E37:') + call assert_fails('tabclose 3', 'E37:') + call assert_fails('tabclose -+', 'E474:') + call assert_fails('tabclose +2-', 'E474:') + call assert_equal(6, tabpagenr('$')) + + 1tabonly! +endfunction + +" Test for [count] of tabonly +function Test_tabpage_with_tabonly() + + " Test for the normal behavior (pre count only) + let tc = [ [4, '.', '!'], [2, '.+', ''], [3, '.-2', '!'], [1, '.+1', '!'] ] + for c in tc + call s:reconstruct_tabpage_for_test(6) + let entire_cmd = c[1] . 'tabonly' . c[2] + call Check_tab_count(c[0], entire_cmd, 1) + call assert_equal(1, tabpagenr('$')) + endfor + + " Test for the normal behavior + let tc2 = [ [3, '', ''], [1, '3', ''], [4, '4', '!'], [3, '1', '!'], + \ [2, '', '!'], + \ [2, '$', '!'], [3, '+', '!'], [3, '-2', '!'], [3, '+1', '!'] + \ ] + for n in range(2) + for c in tc2 + call s:reconstruct_tabpage_for_test(6) + if n == 0 " pre count + let entire_cmd = c[1] . 'tabonly' . c[2] + else + let entire_cmd = 'tabonly' . c[2] . ' ' . c[1] + endif + call Check_tab_count(c[0], entire_cmd, 1) + call assert_equal(1, tabpagenr('$')) + endfor + endfor + + " Test for the error behavior + for n in range(2) + for c in ['0', '$3', '99', '+99', '-99'] + call s:reconstruct_tabpage_for_test(6) + if n == 0 " pre count + let entire_cmd = c . 'tabonly' + let err_code = 'E16:' + else + let entire_cmd = 'tabonly ' . c + let err_code = 'E474:' + endif + call assert_fails(entire_cmd, err_code) + call assert_equal(6, tabpagenr('$')) + endfor + endfor + + " Test for the error behavior (post count only) + for c in tc + call s:reconstruct_tabpage_for_test(6) + let entire_cmd = 'tabonly' . c[2] . ' ' . c[1] + let err_code = 'E474:' + call assert_fails(entire_cmd, err_code) + call assert_equal(6, tabpagenr('$')) + endfor + + call assert_fails('tabonly -+', 'E474:') + call assert_fails('tabonly +2-', 'E474:') + call assert_equal(6, tabpagenr('$')) + + 1tabonly! + new + only! endfunction func Test_tabnext_on_buf_unload1() diff --git a/test/functional/legacy/062_tab_pages_spec.lua b/test/functional/legacy/062_tab_pages_spec.lua index d5b10b160e..71a0a77354 100644 --- a/test/functional/legacy/062_tab_pages_spec.lua +++ b/test/functional/legacy/062_tab_pages_spec.lua @@ -99,10 +99,6 @@ describe('tab pages', function() eq(7, eval('tabpagenr()')) execute('tabmove') eq(10, eval('tabpagenr()')) - execute('tabmove -20') - eq(1, eval('tabpagenr()')) - execute('tabmove +20') - eq(10, eval('tabpagenr()')) execute('0tabmove') eq(1, eval('tabpagenr()')) execute('$tabmove') @@ -172,7 +168,7 @@ describe('tab pages', function() C tabnext 1 autocmd TabDestructive TabEnter * nested \ :C tabnext 2 | C tabclose 3 - C tabnext 3 + C tabnext 2 let g:r+=[tabpagenr().'/'.tabpagenr('$')] endfunction call Test() @@ -233,22 +229,14 @@ describe('tab pages', function() WinEnter TabEnter BufEnter - === tabnext 3 === - BufLeave + === tabnext 2 === WinLeave TabLeave WinEnter TabEnter === tabnext 2 === - BufLeave - WinLeave - TabLeave - WinEnter - TabEnter - === tabnext 2 === - === tabclose 3 === - BufEnter === tabclose 3 === 2/2]]) + eq(2, eval("tabpagenr('$')")) end) end) From 7e23ce6b4fa843c12e44464a20154d92e95355c6 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 17:34:25 +0100 Subject: [PATCH 0075/1671] lint --- src/nvim/ex_docmd.c | 160 ++++++++++++++++++++------------------------ 1 file changed, 72 insertions(+), 88 deletions(-) diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 4f34312562..2bd5158b33 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -4314,102 +4314,86 @@ static int getargopt(exarg_T *eap) return OK; } -/* - * Handle the argument for a tabpage related ex command. - * Returns a tabpage number. - * When an error is encountered then eap->errmsg is set. - */ - static int -get_tabpage_arg(exarg_T *eap) +/// Handle the argument for a tabpage related ex command. +/// Returns a tabpage number. +/// When an error is encountered then eap->errmsg is set. +static int get_tabpage_arg(exarg_T *eap) { - int tab_number; - int unaccept_arg0 = (eap->cmdidx == CMD_tabmove) ? 0 : 1; + int tab_number; + int unaccept_arg0 = (eap->cmdidx == CMD_tabmove) ? 0 : 1; - if (eap->arg && *eap->arg != NUL) - { - char_u *p = eap->arg; - char_u *p_save; - int relative = 0; /* argument +N/-N means: go to N places to the - * right/left relative to the current position. */ + if (eap->arg && *eap->arg != NUL) { + char_u *p = eap->arg; + char_u *p_save; + int relative = 0; // argument +N/-N means: go to N places to the + // right/left relative to the current position. - if (*p == '-') - { - relative = -1; - p++; - } - else if (*p == '+') - { - relative = 1; - p++; - } - - p_save = p; - tab_number = getdigits(&p); - - if (relative == 0) - { - if (STRCMP(p, "$") == 0) - tab_number = LAST_TAB_NR; - else if (p == p_save || *p_save == '-' || *p != NUL - || tab_number > LAST_TAB_NR) - { - /* No numbers as argument. */ - eap->errmsg = e_invarg; - goto theend; - } - } - else - { - if (*p_save == NUL) - tab_number = 1; - else if (p == p_save || *p_save == '-' || *p != NUL - || tab_number == 0) - { - /* No numbers as argument. */ - eap->errmsg = e_invarg; - goto theend; - } - tab_number = tab_number * relative + tabpage_index(curtab); - if (!unaccept_arg0 && relative == -1) - --tab_number; - } - if (tab_number < unaccept_arg0 || tab_number > LAST_TAB_NR) - eap->errmsg = e_invarg; + if (*p == '-') { + relative = -1; + p++; + } else if (*p == '+') { + relative = 1; + p++; } - else if (eap->addr_count > 0) - { - if (unaccept_arg0 && eap->line2 == 0) - eap->errmsg = e_invrange; - else - { - tab_number = eap->line2; - if (!unaccept_arg0 && **eap->cmdlinep == '-') - { - --tab_number; - if (tab_number < unaccept_arg0) - eap->errmsg = e_invarg; - } - } + + p_save = p; + tab_number = getdigits(&p); + + if (relative == 0) { + if (STRCMP(p, "$") == 0) { + tab_number = LAST_TAB_NR; + } else if (p == p_save || *p_save == '-' || *p != NUL + || tab_number > LAST_TAB_NR) { + // No numbers as argument. + eap->errmsg = e_invarg; + goto theend; + } + } else { + if (*p_save == NUL) { + tab_number = 1; + } + else if (p == p_save || *p_save == '-' || *p != NUL || tab_number == 0) { + // No numbers as argument. + eap->errmsg = e_invarg; + goto theend; + } + tab_number = tab_number * relative + tabpage_index(curtab); + if (!unaccept_arg0 && relative == -1) { + --tab_number; + } } - else - { - switch (eap->cmdidx) - { - case CMD_tabnext: - tab_number = tabpage_index(curtab) + 1; - if (tab_number > LAST_TAB_NR) - tab_number = 1; - break; - case CMD_tabmove: - tab_number = LAST_TAB_NR; - break; - default: - tab_number = tabpage_index(curtab); - } + if (tab_number < unaccept_arg0 || tab_number > LAST_TAB_NR) { + eap->errmsg = e_invarg; } + } else if (eap->addr_count > 0) { + if (unaccept_arg0 && eap->line2 == 0) { + eap->errmsg = e_invrange; + } else { + tab_number = eap->line2; + if (!unaccept_arg0 && **eap->cmdlinep == '-') { + --tab_number; + if (tab_number < unaccept_arg0) { + eap->errmsg = e_invarg; + } + } + } + } else { + switch (eap->cmdidx) { + case CMD_tabnext: + tab_number = tabpage_index(curtab) + 1; + if (tab_number > LAST_TAB_NR) + tab_number = 1; + break; + case CMD_tabmove: + tab_number = LAST_TAB_NR; + break; + default: + tab_number = tabpage_index(curtab); + } + } theend: - return tab_number; + return tab_number; } /* From a24c6cc6a8e180d201c5887935d6dfdc95ac1abf Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 17:38:30 +0100 Subject: [PATCH 0076/1671] get_tabpage_arg: satisfy clang warning vim-patch:8.0.0266 --- src/nvim/ex_docmd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 2bd5158b33..a06c6a89c8 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -4319,7 +4319,7 @@ static int getargopt(exarg_T *eap) /// When an error is encountered then eap->errmsg is set. static int get_tabpage_arg(exarg_T *eap) { - int tab_number; + int tab_number = 0; int unaccept_arg0 = (eap->cmdidx == CMD_tabmove) ? 0 : 1; if (eap->arg && *eap->arg != NUL) { @@ -4368,6 +4368,7 @@ static int get_tabpage_arg(exarg_T *eap) } else if (eap->addr_count > 0) { if (unaccept_arg0 && eap->line2 == 0) { eap->errmsg = e_invrange; + tab_number = 0; } else { tab_number = eap->line2; if (!unaccept_arg0 && **eap->cmdlinep == '-') { From f06a6913260a320ec6b8ede5860edb1829c4f8ba Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 19:14:18 +0100 Subject: [PATCH 0077/1671] vim-patch:8.0.0037 Problem: Get E924 when switching tabs. () Solution: Use win_valid_any_tab() instead of win_valid(). (Martin Vuille, closes vim/vim#1167, closes vim/vim#1171) https://github.com/vim/vim/commit/0a9046fbcb33770517ab0220b8100c4494bddab2 --- src/nvim/quickfix.c | 2 +- src/nvim/testdir/test_quickfix.vim | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 3f7975051f..8d8c20c1d0 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -1975,7 +1975,7 @@ win_found: ok = buflist_getfile(qf_ptr->qf_fnum, (linenr_T)1, GETF_SETMARK | GETF_SWITCH, forceit); - if (qi != &ql_info && !win_valid(oldwin)) { + if (qi != &ql_info && !win_valid_any_tab(oldwin)) { EMSG(_("E924: Current window was closed")); is_abort = true; opened_window = false; diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 640918b343..75ab01f013 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -599,6 +599,22 @@ function Test_locationlist_curwin_was_closed() augroup! testgroup endfunction +function Test_locationlist_cross_tab_jump() + call writefile(['loclistfoo'], 'loclistfoo') + call writefile(['loclistbar'], 'loclistbar') + set switchbuf=usetab + + edit loclistfoo + tabedit loclistbar + silent lgrep loclistfoo loclist* + call assert_equal(1, tabpagenr()) + + enew | only | tabonly + set switchbuf&vim + call delete('loclistfoo') + call delete('loclistbar') +endfunction + " More tests for 'errorformat' function! Test_efm1() if !has('unix') From 9c8540edfd52c77678eac84e48beb56171487b3f Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 19:16:26 +0100 Subject: [PATCH 0078/1671] vim-patch:8.0.0159 References #5406 Problem: Using a NULL pointer when using feedkeys() to trigger drawing a tabline. Solution: Skip drawing a tabline if TabPageIdxs is NULL. (Dominique Pelle) Also fix recursing into getcmdline() from the cmd window. https://github.com/vim/vim/commit/c695cec4698b41d7b9555efdd47dda9b1945d3ae --- src/nvim/ex_getln.c | 2 ++ src/nvim/screen.c | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index d99c8d02f7..d6e003a82f 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -698,7 +698,9 @@ static int command_line_execute(VimState *state, int key) if (s->c == cedit_key || s->c == K_CMDWIN) { if (ex_normal_busy == 0 && got_int == false) { // Open a window to edit the command line (and history). + save_cmdline(&s->save_ccline); s->c = ex_window(); + restore_cmdline(&s->save_ccline); s->some_key_typed = true; } } else { diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 280b8c65fe..baec18dd6f 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -6884,7 +6884,10 @@ static void draw_tabline(void) int use_sep_chars = (t_colors < 8 ); - redraw_tabline = FALSE; + if (ScreenLines == NULL) { + return; + } + redraw_tabline = false; if (tabline_height() < 1) From b82e3358e006264187f104bb0321104621bcc811 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 19:23:12 +0100 Subject: [PATCH 0079/1671] vim-patch:8.0.0083 Problem: Using freed memory with win_getid(). (Domenique Pelle) Solution: For the current tab use curwin. https://github.com/vim/vim/commit/8e639052638a9bb8c7dd6e3e10776b1218cec1a3 --- src/nvim/testdir/test_window_id.vim | 9 +++++++++ src/nvim/window.c | 6 +++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/nvim/testdir/test_window_id.vim b/src/nvim/testdir/test_window_id.vim index 66656e1d0a..b3b506d04d 100644 --- a/src/nvim/testdir/test_window_id.vim +++ b/src/nvim/testdir/test_window_id.vim @@ -92,3 +92,12 @@ func Test_win_getid() only! endfunc + +func Test_win_getid_curtab() + tabedit X + tabfirst + copen + only + call assert_equal(win_getid(1), win_getid(1, 1)) + tabclose! +endfunc diff --git a/src/nvim/window.c b/src/nvim/window.c index 15482d9061..ce322e1185 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5821,7 +5821,11 @@ int win_getid(typval_T *argvars) if (tp == NULL) { return -1; } - wp = tp->tp_firstwin; + if (tp == curtab) { + wp = firstwin; + } else { + wp = tp->tp_firstwin; + } } for ( ; wp != NULL; wp = wp->w_next) { if (--winnr == 0) { From 01bf78971cea43938e01439ae6d4687bc97196ea Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 23:17:10 +0100 Subject: [PATCH 0080/1671] vim-patch:8.0.0172 Problem: The command selected in the command line window is not executed. (Andrey Starodubtsev) Solution: Save and restore the command line at a lower level. (closes vim/vim#1370) https://github.com/vim/vim/commit/1d669c233c97486555a34f7d3f069068d9ebdb63 --- src/nvim/ex_getln.c | 12 ++++-------- src/nvim/testdir/test_history.vim | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index d6e003a82f..2600f484dc 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -698,9 +698,7 @@ static int command_line_execute(VimState *state, int key) if (s->c == cedit_key || s->c == K_CMDWIN) { if (ex_normal_busy == 0 && got_int == false) { // Open a window to edit the command line (and history). - save_cmdline(&s->save_ccline); s->c = ex_window(); - restore_cmdline(&s->save_ccline); s->some_key_typed = true; } } else { @@ -5229,10 +5227,8 @@ static int ex_window(void) invalidate_botline(); redraw_later(SOME_VALID); - /* Save the command line info, can be used recursively. */ - save_ccline = ccline; - ccline.cmdbuff = NULL; - ccline.cmdprompt = NULL; + // Save the command line info, can be used recursively. + save_cmdline(&save_ccline); /* No Ex mode here! */ exmode_active = 0; @@ -5266,8 +5262,8 @@ static int ex_window(void) /* Restore KeyTyped in case it is modified by autocommands */ KeyTyped = save_KeyTyped; - /* Restore the command line info. */ - ccline = save_ccline; + // Restore the command line info. + restore_cmdline(&save_ccline); cmdwin_type = 0; exmode_active = save_exmode; diff --git a/src/nvim/testdir/test_history.vim b/src/nvim/testdir/test_history.vim index ee6acfffc3..3163b344d3 100644 --- a/src/nvim/testdir/test_history.vim +++ b/src/nvim/testdir/test_history.vim @@ -63,3 +63,20 @@ function Test_History() call assert_equal(-1, histnr('abc')) call assert_fails('call histnr([])', 'E730:') endfunction + +function Test_Search_history_window() + new + call setline(1, ['a', 'b', 'a', 'b']) + 1 + call feedkeys("/a\", 'xt') + call assert_equal('a', getline('.')) + 1 + call feedkeys("/b\", 'xt') + call assert_equal('b', getline('.')) + 1 + " select the previous /a command + call feedkeys("q/kk\", 'x!') + call assert_equal('a', getline('.')) + call assert_equal('a', @/) + bwipe! +endfunc From 33858ccb9b53d79f3dafbffef8ca7273be7de119 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 23:36:27 +0100 Subject: [PATCH 0081/1671] vim-patch:8.0.0485 Problem: Not all windows commands are tested. Solution: Add more tests for windows commands. (Dominique Pelle, closes vim/vim#1575) Run test_autocmd separately, it interferes with other tests. Fix tests that depended on side effects. https://github.com/vim/vim/commit/4520d440c59034452d1450b27fcd56825c090687 --- src/nvim/testdir/Makefile | 1 + src/nvim/testdir/test_alot.vim | 1 - src/nvim/testdir/test_autocmd.vim | 2 + src/nvim/testdir/test_window_cmd.vim | 300 +++++++++++++++++++++++++++ 4 files changed, 303 insertions(+), 1 deletion(-) diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index d948cd0f93..531a07912f 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -28,6 +28,7 @@ SCRIPTS ?= \ # Tests using runtest.vim. # Keep test_alot*.res as the last one, sort the others. NEW_TESTS ?= \ + test_autocmd.res \ test_bufwintabinfo.res \ test_cmdline.res \ test_command_count.res \ diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index 9d1f07fc0b..9c7db9e397 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -2,7 +2,6 @@ " This makes testing go faster, since Vim doesn't need to restart. source test_assign.vim -source test_autocmd.vim source test_cursor_func.vim source test_execute_func.vim source test_ex_undo.vim diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 5b5232f06e..e8e563d8ad 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -320,6 +320,8 @@ func Test_three_windows() call assert_equal('Xanother', expand('%')) au! + enew + bwipe! Xtestje1 call delete('Xtestje1') call delete('Xtestje2') call delete('Xtestje3') diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index 569a78a0ed..a5f27fba8a 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -67,4 +67,304 @@ function Test_window_cmd_wincmd_gf() augroup! test_window_cmd_wincmd_gf endfunc +func Test_window_quit() + e Xa + split Xb + call assert_equal(2, winnr('$')) + call assert_equal('Xb', bufname(winbufnr(1))) + call assert_equal('Xa', bufname(winbufnr(2))) + + wincmd q + call assert_equal(1, winnr('$')) + call assert_equal('Xa', bufname(winbufnr(1))) + + bw Xa Xb +endfunc + +func Test_window_horizontal_split() + call assert_equal(1, winnr('$')) + 3wincmd s + call assert_equal(2, winnr('$')) + call assert_equal(3, winheight(0)) + call assert_equal(winwidth(1), winwidth(2)) + + call assert_fails('botright topleft wincmd s', 'E442:') + bw +endfunc + +func Test_window_vertical_split() + call assert_equal(1, winnr('$')) + 3wincmd v + call assert_equal(2, winnr('$')) + call assert_equal(3, winwidth(0)) + call assert_equal(winheight(1), winheight(2)) + + call assert_fails('botright topleft wincmd v', 'E442:') + bw +endfunc + +func Test_window_split_edit_alternate() + e Xa + e Xb + + wincmd ^ + call assert_equal('Xa', bufname(winbufnr(1))) + call assert_equal('Xb', bufname(winbufnr(2))) + + bw Xa Xb +endfunc + +func Test_window_preview() + " Open a preview window + pedit Xa + call assert_equal(2, winnr('$')) + call assert_equal(0, &previewwindow) + + " Go to the preview window + wincmd P + call assert_equal(1, &previewwindow) + + " Close preview window + wincmd z + call assert_equal(1, winnr('$')) + call assert_equal(0, &previewwindow) + + call assert_fails('wincmd P', 'E441:') +endfunc + +func Test_window_exchange() + e Xa + + " Nothing happens with window exchange when there is 1 window + wincmd x + call assert_equal(1, winnr('$')) + + split Xb + split Xc + + call assert_equal('Xc', bufname(winbufnr(1))) + call assert_equal('Xb', bufname(winbufnr(2))) + call assert_equal('Xa', bufname(winbufnr(3))) + + " Exchange current window 1 with window 3 + 3wincmd x + call assert_equal('Xa', bufname(winbufnr(1))) + call assert_equal('Xb', bufname(winbufnr(2))) + call assert_equal('Xc', bufname(winbufnr(3))) + + " Exchange window with next when at the top window + wincmd x + call assert_equal('Xb', bufname(winbufnr(1))) + call assert_equal('Xa', bufname(winbufnr(2))) + call assert_equal('Xc', bufname(winbufnr(3))) + + " Exchange window with next when at the middle window + wincmd j + wincmd x + call assert_equal('Xb', bufname(winbufnr(1))) + call assert_equal('Xc', bufname(winbufnr(2))) + call assert_equal('Xa', bufname(winbufnr(3))) + + " Exchange window with next when at the bottom window. + " When there is no next window, it exchanges with the previous window. + wincmd j + wincmd x + call assert_equal('Xb', bufname(winbufnr(1))) + call assert_equal('Xa', bufname(winbufnr(2))) + call assert_equal('Xc', bufname(winbufnr(3))) + + bw Xa Xb Xc +endfunc + +func Test_window_rotate() + e Xa + split Xb + split Xc + call assert_equal('Xc', bufname(winbufnr(1))) + call assert_equal('Xb', bufname(winbufnr(2))) + call assert_equal('Xa', bufname(winbufnr(3))) + + " Rotate downwards + wincmd r + call assert_equal('Xa', bufname(winbufnr(1))) + call assert_equal('Xc', bufname(winbufnr(2))) + call assert_equal('Xb', bufname(winbufnr(3))) + + 2wincmd r + call assert_equal('Xc', bufname(winbufnr(1))) + call assert_equal('Xb', bufname(winbufnr(2))) + call assert_equal('Xa', bufname(winbufnr(3))) + + " Rotate upwards + wincmd R + call assert_equal('Xb', bufname(winbufnr(1))) + call assert_equal('Xa', bufname(winbufnr(2))) + call assert_equal('Xc', bufname(winbufnr(3))) + + 2wincmd R + call assert_equal('Xc', bufname(winbufnr(1))) + call assert_equal('Xb', bufname(winbufnr(2))) + call assert_equal('Xa', bufname(winbufnr(3))) + + bot vsplit + call assert_fails('wincmd R', 'E443:') + + bw Xa Xb Xc +endfunc + +func Test_window_height() + e Xa + split Xb + + let [wh1, wh2] = [winheight(1), winheight(2)] + " Active window (1) should have the same height or 1 more + " than the other window. + call assert_inrange(wh2, wh2 + 1, wh1) + + wincmd - + call assert_equal(wh1 - 1, winheight(1)) + call assert_equal(wh2 + 1, winheight(2)) + + wincmd + + call assert_equal(wh1, winheight(1)) + call assert_equal(wh2, winheight(2)) + + 2wincmd _ + call assert_equal(2, winheight(1)) + call assert_equal(wh1 + wh2 - 2, winheight(2)) + + wincmd = + call assert_equal(wh1, winheight(1)) + call assert_equal(wh2, winheight(2)) + + 2wincmd _ + set winfixheight + split Xc + let [wh1, wh2, wh3] = [winheight(1), winheight(2), winheight(3)] + call assert_equal(2, winheight(2)) + call assert_inrange(wh3, wh3 + 1, wh1) + 3wincmd + + call assert_equal(2, winheight(2)) + call assert_equal(wh1 + 3, winheight(1)) + call assert_equal(wh3 - 3, winheight(3)) + wincmd = + call assert_equal(2, winheight(2)) + call assert_equal(wh1, winheight(1)) + call assert_equal(wh3, winheight(3)) + + wincmd j + set winfixheight& + + wincmd = + let [wh1, wh2, wh3] = [winheight(1), winheight(2), winheight(3)] + " Current window (2) should have the same height or 1 more + " than the other windows. + call assert_inrange(wh1, wh1 + 1, wh2) + call assert_inrange(wh3, wh3 + 1, wh2) + + bw Xa Xb Xc +endfunc + +func Test_window_width() + e Xa + vsplit Xb + + let [ww1, ww2] = [winwidth(1), winwidth(2)] + " Active window (1) should have the same width or 1 more + " than the other window. + call assert_inrange(ww2, ww2 + 1, ww1) + + wincmd < + call assert_equal(ww1 - 1, winwidth(1)) + call assert_equal(ww2 + 1, winwidth(2)) + + wincmd > + call assert_equal(ww1, winwidth(1)) + call assert_equal(ww2, winwidth(2)) + + 2wincmd | + call assert_equal(2, winwidth(1)) + call assert_equal(ww1 + ww2 - 2, winwidth(2)) + + wincmd = + call assert_equal(ww1, winwidth(1)) + call assert_equal(ww2, winwidth(2)) + + 2wincmd | + set winfixwidth + vsplit Xc + let [ww1, ww2, ww3] = [winwidth(1), winwidth(2), winwidth(3)] + " FIXME: commented out: I would expect the width of 2nd window to + " remain 2 but it's actually 1?! + "call assert_equal(2, winwidth(2)) + call assert_inrange(ww3, ww3 + 1, ww1) + 3wincmd > + " FIXME: commented out: I would expect the width of 2nd window to + " remain 2 but it's actually 1?! + "call assert_equal(2, winwidth(2)) + call assert_equal(ww1 + 3, winwidth(1)) + call assert_equal(ww3 - 3, winwidth(3)) + wincmd = + " FIXME: commented out: I would expect the width of 2nd window to + " remain 2 but it's actually 1?! + "call assert_equal(2, winwidth(2)) + call assert_equal(ww1, winwidth(1)) + call assert_equal(ww3, winwidth(3)) + + wincmd l + set winfixwidth& + + wincmd = + let [ww1, ww2, ww3] = [winwidth(1), winwidth(2), winwidth(3)] + " Current window (2) should have the same width or 1 more + " than the other windows. + call assert_inrange(ww1, ww1 + 1, ww2) + call assert_inrange(ww3, ww3 + 1, ww2) + + bw Xa Xb Xc +endfunc + +func Test_window_jump_tag() + help + /iccf + call assert_match('^|iccf|', getline('.')) + call assert_equal(2, winnr('$')) + 2wincmd } + call assert_equal(3, winnr('$')) + call assert_match('^|iccf|', getline('.')) + wincmd k + call assert_match('\*iccf\*', getline('.')) + call assert_equal(2, winheight(0)) + + wincmd z + set previewheight=4 + help + /bugs + wincmd } + wincmd k + call assert_match('\*bugs\*', getline('.')) + call assert_equal(4, winheight(0)) + set previewheight& + + %bw! +endfunc + +func Test_window_newtab() + e Xa + + call assert_equal(1, tabpagenr('$')) + call assert_equal("\nAlready only one window", execute('wincmd T')) + + split Xb + split Xc + + wincmd T + call assert_equal(2, tabpagenr('$')) + call assert_equal(['Xb', 'Xa'], map(tabpagebuflist(1), 'bufname(v:val)')) + call assert_equal(['Xc' ], map(tabpagebuflist(2), 'bufname(v:val)')) + + %bw! +endfunc + + " vim: shiftwidth=2 sts=2 expandtab From 51bc9f243a0b60205985b42bb47f174379a463c6 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 22 Mar 2017 00:22:45 +0100 Subject: [PATCH 0082/1671] ex_tabnext: Disallow "+NN" arg. Need to do this explicitly because our implementation of getdigits() is slightly different. --- src/nvim/ex_docmd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index a06c6a89c8..d815d57a60 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -6602,7 +6602,8 @@ static void ex_tabnext(exarg_T *eap) char_u *p_save = p; tab_number = getdigits(&p); - if (p == p_save || *p_save == '-' || *p != NUL || tab_number == 0) { + if (p == p_save || *p_save == '-' || *p_save == '+' || *p != NUL + || tab_number == 0) { // No numbers as argument. eap->errmsg = e_invarg; return; From 2b32053559669580f51da3cad2258f65c3adf9c4 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 22 Mar 2017 01:19:13 +0100 Subject: [PATCH 0083/1671] test/legacy: Test_three_windows: rtp for :help --- src/nvim/testdir/test_autocmd.vim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index e8e563d8ad..c4110eba94 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -314,6 +314,8 @@ func Test_three_windows() call assert_equal('Xanother', expand('%')) only + + helptags ALL help wincmd w 1quit From f58a593cea3872c7b8b862375b6982436ee88ef2 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 22 Mar 2017 01:26:32 +0100 Subject: [PATCH 0084/1671] vim-patch:8.0.0483 Problem: Illegal memory access when using :all. (Dominique Pelle) Solution: Adjust the cursor position right after setting "curwin". https://github.com/vim/vim/commit/f79225ed4f81bc579bb3360ad2eb06adc8058153 --- src/nvim/testdir/test_window_cmd.vim | 12 ++++++++++++ src/nvim/window.c | 8 +++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index a5f27fba8a..188a7ed0f3 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -67,6 +67,18 @@ function Test_window_cmd_wincmd_gf() augroup! test_window_cmd_wincmd_gf endfunc +func Test_next_split_all() + " This was causing an illegal memory access. + n x + norm axxx + split + split + s/x + s/x + all + bwipe! +endfunc + func Test_window_quit() e Xa split Xb diff --git a/src/nvim/window.c b/src/nvim/window.c index ce322e1185..82e97ea00d 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2018,10 +2018,12 @@ int win_close(win_T *win, int free_buf) } curbuf = curwin->w_buffer; close_curwin = TRUE; + + // The cursor position may be invalid if the buffer changed after last + // using the window. + check_cursor(); } - if (p_ea - && (*p_ead == 'b' || *p_ead == dir) - ) { + if (p_ea && (*p_ead == 'b' || *p_ead == dir)) { win_equal(curwin, true, dir); } else { win_comp_pos(); From 54f31187ba006049ba455a1eeea4781750a3958d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 22 Mar 2017 02:13:54 +0100 Subject: [PATCH 0085/1671] vim-patch:8.0.0482 Problem: The setbufvar() function may mess up the window layout. (Kay Z.) Solution: Do not check the window to be valid if it is NULL. https://github.com/vim/vim/commit/2c90d51123fba44a90e09aa4a4f2b7d972dadb94 --- src/nvim/testdir/test_functions.vim | 31 +++++++++++++++++++++++++++++ src/nvim/window.c | 3 ++- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 src/nvim/testdir/test_functions.vim diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim new file mode 100644 index 0000000000..81cb6314ce --- /dev/null +++ b/src/nvim/testdir/test_functions.vim @@ -0,0 +1,31 @@ +func Test_setbufvar_options() + " This tests that aucmd_prepbuf() and aucmd_restbuf() properly restore the + " window layout. + call assert_equal(1, winnr('$')) + split dummy_preview + resize 2 + set winfixheight winfixwidth + let prev_id = win_getid() + + wincmd j + let wh = winheight('.') + let dummy_buf = bufnr('dummy_buf1', v:true) + call setbufvar(dummy_buf, '&buftype', 'nofile') + execute 'belowright vertical split #' . dummy_buf + call assert_equal(wh, winheight('.')) + let dum1_id = win_getid() + + wincmd h + let wh = winheight('.') + let dummy_buf = bufnr('dummy_buf2', v:true) + call setbufvar(dummy_buf, '&buftype', 'nofile') + execute 'belowright vertical split #' . dummy_buf + call assert_equal(wh, winheight('.')) + + bwipe! + call win_gotoid(prev_id) + bwipe! + call win_gotoid(dum1_id) + bwipe! +endfunc + diff --git a/src/nvim/window.c b/src/nvim/window.c index 82e97ea00d..ebb998e3af 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5377,7 +5377,8 @@ static int check_snapshot_rec(frame_T *sn, frame_T *fr) || (sn->fr_next != NULL && check_snapshot_rec(sn->fr_next, fr->fr_next) == FAIL) || (sn->fr_child != NULL - && check_snapshot_rec(sn->fr_child, fr->fr_child) == FAIL)) + && check_snapshot_rec(sn->fr_child, fr->fr_child) == FAIL) + || (sn->fr_win != NULL && !win_valid(sn->fr_win))) return FAIL; return OK; } From 41bffeacff885b4904a1f456c6d56e016ad6f88c Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 22 Mar 2017 02:20:06 +0100 Subject: [PATCH 0086/1671] vim-patch:8.0.0331 Problem: Restoring help snapshot accesses freed memory. (Dominique Pelle) Solution: Don't restore a snapshot when the window closes. https://github.com/vim/vim/commit/343b8c042967da82f2f022afa31f2c97a264c1c8 --- src/nvim/testdir/test_help.vim | 16 ++++++++++++++++ src/nvim/window.c | 6 ++---- 2 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 src/nvim/testdir/test_help.vim diff --git a/src/nvim/testdir/test_help.vim b/src/nvim/testdir/test_help.vim new file mode 100644 index 0000000000..26edc16107 --- /dev/null +++ b/src/nvim/testdir/test_help.vim @@ -0,0 +1,16 @@ + +" Tests for :help + +func Test_help_restore_snapshot() + help + set buftype= + help + edit x + help + helpclose +endfunc + +func Test_help_errors() + call assert_fails('help doesnotexist', 'E149:') + call assert_fails('help!', 'E478:') +endfunc diff --git a/src/nvim/window.c b/src/nvim/window.c index ebb998e3af..a34daccb89 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5365,10 +5365,8 @@ restore_snapshot ( clear_snapshot(curtab, idx); } -/* - * Check if frames "sn" and "fr" have the same layout, same following frames - * and same children. - */ +/// Check if frames "sn" and "fr" have the same layout, same following frames +/// and same children. And the window pointer is valid. static int check_snapshot_rec(frame_T *sn, frame_T *fr) { if (sn->fr_layout != fr->fr_layout From 830b31683e9958130a64b9dec990f42b58efc0a7 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 22 Mar 2017 13:06:29 +0100 Subject: [PATCH 0087/1671] vim-patch:8.0.0502 Problem: Coverity complains about possible NULL pointer. Solution: Add an assert(), let's see if this works on all systems. https://github.com/vim/vim/commit/a37ffaa5e0a47e2db27bc0cc23f49e7094f47f3b --- src/nvim/window.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/nvim/window.c b/src/nvim/window.c index a34daccb89..db6c0f9048 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -3167,6 +3167,7 @@ void close_tabpage(tabpage_T *tab) ptp = ptp->tp_next) { // do nothing } + assert(ptp != NULL); ptp->tp_next = tab->tp_next; } From c398402f1225e8050856260cbc6ae929ff2f5c31 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 22 Mar 2017 15:02:19 +0100 Subject: [PATCH 0088/1671] vim-patch:8.0.0307 Problem: Asan detects a memory error when EXITFREE is defined. (Dominique Pelle) Solution: In getvcol() check for ml_get_buf() returning an empty string. Also skip adjusting the scroll position. Set "exiting" in mch_exit() for all systems. https://github.com/vim/vim/commit/955f198fc546cc30a34361932d3f454a61df0efa --- src/nvim/charset.c | 5 +++++ src/nvim/window.c | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 9e240fd38b..f31cef5886 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1160,6 +1160,11 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, // continue until the NUL posptr = NULL; } else { + // Special check for an empty line, which can happen on exit, when + // ml_get_buf() always returns an empty string. + if (*ptr == NUL) { + pos->col = 0; + } posptr = ptr + pos->col; } diff --git a/src/nvim/window.c b/src/nvim/window.c index db6c0f9048..dc0ce03794 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -4803,7 +4803,11 @@ void win_new_height(win_T *wp, int height) wp->w_height = height; wp->w_skipcol = 0; - scroll_to_fraction(wp, prev_height); + // There is no point in adjusting the scroll position when exiting. Some + // values might be invalid. + if (!exiting) { + scroll_to_fraction(wp, prev_height); + } } void scroll_to_fraction(win_T *wp, int prev_height) From 44e75eba30682007dd17f9a80805a9925a921976 Mon Sep 17 00:00:00 2001 From: raichoo Date: Wed, 22 Mar 2017 21:57:20 +0100 Subject: [PATCH 0089/1671] vim-patch:7.4.2161 (#6340) Problem: Expression test fails without conceal feature. Solution: Only check "conceal" with the conceal feature. https://github.com/vim/vim/commit/7ab6defcafe017a3ad58580a3e56dab705b1ed8b --- src/nvim/testdir/test_expr.vim | 8 ++++++-- src/nvim/version.c | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index ccbdde8244..2541d38d12 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -101,8 +101,12 @@ endfunc func Test_setmatches() hi def link 1 Comment hi def link 2 PreProc - let set = [{"group": 1, "pattern": 2, "id": 3, "priority": 4, "conceal": 5}] - let exp = [{"group": '1', "pattern": '2', "id": 3, "priority": 4, "conceal": '5'}] + let set = [{"group": 1, "pattern": 2, "id": 3, "priority": 4}] + let exp = [{"group": '1', "pattern": '2', "id": 3, "priority": 4}] + if has('conceal') + let set[0]['conceal'] = 5 + let exp[0]['conceal'] = '5' + endif call setmatches(set) call assert_equal(exp, getmatches()) endfunc diff --git a/src/nvim/version.c b/src/nvim/version.c index 398a3e6542..cbb7902985 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -280,7 +280,7 @@ static int included_patches[] = { 2164, 2163, 2162, - // 2161, + 2161, 2160, 2159, 2158, From 06ed7a189b2c1dca88f307538b9739b989776068 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jurica=20Bradari=C4=87?= Date: Thu, 23 Mar 2017 08:54:01 +0100 Subject: [PATCH 0090/1671] vim-patch:7.4.2329 (#6341) Problem: Error for min() and max() contains %s. (Nikolay Pavlov) Solution: Pass the function name. (closes vim/vim#1040) https://github.com/vim/vim/commit/26b84339fd8766898bcf6a259cbc2e0c38689726 --- src/nvim/eval.c | 5 +++-- src/nvim/testdir/test_expr.vim | 7 +++++++ src/nvim/version.c | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 0872ed37ad..8f52717252 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -13804,8 +13804,9 @@ static void max_min(typval_T *argvars, typval_T *rettv, int domax) } } } - } else - EMSG(_(e_listdictarg)); + } else { + EMSG2(_(e_listdictarg), domax ? "max()" : "min()"); + } rettv->vval.v_number = error ? 0 : n; } diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index 2541d38d12..03a9155512 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -335,6 +335,13 @@ function Test_printf_errors() call assert_fails('echo printf("%d", 1.2)', 'E805:') endfunc +function Test_max_min_errors() + call assert_fails('call max(v:true)', 'E712:') + call assert_fails('call max(v:true)', 'max()') + call assert_fails('call min(v:true)', 'E712:') + call assert_fails('call min(v:true)', 'min()') +endfunc + func Test_substitute_expr() let g:val = 'XXX' call assert_equal('XXX', substitute('yyy', 'y*', '\=g:val', '')) diff --git a/src/nvim/version.c b/src/nvim/version.c index cbb7902985..32ef2c2fdb 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -112,7 +112,7 @@ static int included_patches[] = { // 2332 NA 2331, // 2330, - // 2329, + 2329, // 2328, // 2327 NA 2326, From b2b88423aa0087e5d4aaa122e63dffb97f85222f Mon Sep 17 00:00:00 2001 From: Matthew Malcomson Date: Mon, 27 Feb 2017 09:46:50 +0000 Subject: [PATCH 0091/1671] Robustly handle folds during a :move command In order to re-order marks according to the :move command, do_move() uses mark_adjust() in a non-standard manner. The non-standard action is that it moves some marks *past* other marks. This doesn't matter for marks, but mark_adjust() calls foldMarkAdjust() which simply changes fold starts and lengths and doesn't have enough information to know that other folds have to be checked and reordered. The array of folds for each window are assumed to be in order of increasing line number, and if this gets broken some folds can get "lost". There has been a previous patch to avoid this problem by deleting and recalculating all folds in the window, but this comes at the cost of closing all folds when executing :move, and doesn't cover the case of manual folds. This patch adds a new function foldMoveRange() specifically for the :move command that handles reordering folds as well as simply moving them. Additionally, we allow calling mark_adjust_nofold() that does the same as mark_adjust() but doesn't affect any fold array. Calling mark_adjust_nofold() should be done in the same manner as calling mark_adjust(), but according changes to the fold arrays must be done seperately by the calling function. vim-patch:8.0.0457 vim-patch:8.0.0459 vim-patch:8.0.0461 vim-patch:8.0.0465 --- src/nvim/ex_cmds.c | 35 +++-- src/nvim/fold.c | 148 +++++++++++++++++++++ src/nvim/mark.c | 22 +++- src/nvim/testdir/test_fold.vim | 141 ++++++++++++++++++++ test/functional/normal/fold_spec.lua | 186 +++++++++++++++++++++++++++ 5 files changed, 510 insertions(+), 22 deletions(-) diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 1b83677807..c560ff9210 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -756,14 +756,6 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) linenr_T num_lines; // Num lines moved linenr_T last_line; // Last line in file after adding new text - // Moving lines seems to corrupt the folds, delete folding info now - // and recreate it when finished. Don't do this for manual folding, it - // would delete all folds. - bool isFolded = hasAnyFolding(curwin) && !foldmethodIsManual(curwin); - if (isFolded) { - deleteFoldRecurse(&curwin->w_folds); - } - if (dest >= line1 && dest < line2) { EMSG(_("E134: Move lines into themselves")); return FAIL; @@ -801,21 +793,29 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) * their final destination at the new text position -- webb */ last_line = curbuf->b_ml.ml_line_count; - mark_adjust(line1, line2, last_line - line2, 0L); - changed_lines(last_line - num_lines + 1, 0, last_line + 1, num_lines); + mark_adjust_nofold(line1, line2, last_line - line2, 0L); if (dest >= line2) { - mark_adjust(line2 + 1, dest, -num_lines, 0L); + mark_adjust_nofold(line2 + 1, dest, -num_lines, 0L); + FOR_ALL_TAB_WINDOWS(tab, win) { + if (win->w_buffer == curbuf) { + foldMoveRange(&win->w_folds, line1, line2, dest); + } + } curbuf->b_op_start.lnum = dest - num_lines + 1; curbuf->b_op_end.lnum = dest; } else { - mark_adjust(dest + 1, line1 - 1, num_lines, 0L); + mark_adjust_nofold(dest + 1, line1 - 1, num_lines, 0L); + FOR_ALL_TAB_WINDOWS(tab, win) { + if (win->w_buffer == curbuf) { + foldMoveRange(&win->w_folds, dest + 1, line1 - 1, line2); + } + } curbuf->b_op_start.lnum = dest + 1; curbuf->b_op_end.lnum = dest + num_lines; } curbuf->b_op_start.col = curbuf->b_op_end.col = 0; - mark_adjust(last_line - num_lines + 1, last_line, - -(last_line - dest - extra), 0L); - changed_lines(last_line - num_lines + 1, 0, last_line + 1, -extra); + mark_adjust_nofold(last_line - num_lines + 1, last_line, + -(last_line - dest - extra), 0L); /* * Now we delete the original text -- webb @@ -851,11 +851,6 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) changed_lines(dest + 1, 0, line1 + num_lines, 0L); } - // recreate folds - if (isFolded) { - foldUpdateAll(curwin); - } - return OK; } diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 7c0283971e..e2d1b017b6 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -2597,6 +2597,154 @@ static void foldRemove(garray_T *gap, linenr_T top, linenr_T bot) } } +// foldMoveRange() {{{2 +static void reverse_fold_order(garray_T *gap, size_t start, size_t end) +{ + for (; start < end; start++, end--) { + fold_T *left = (fold_T *)gap->ga_data + start; + fold_T *right = (fold_T *)gap->ga_data + end; + fold_T tmp = *left; + *left = *right; + *right = tmp; + } +} + +// Move folds within the inclusive range "line1" to "line2" to after "dest" +// require "line1" <= "line2" <= "dest" +// +// There are the following situations for the first fold at or below line1 - 1. +// 1 2 3 4 +// 1 2 3 4 +// line1 2 3 4 +// 2 3 4 5 6 7 +// line2 3 4 5 6 7 +// 3 4 6 7 8 9 +// dest 4 7 8 9 +// 4 7 8 10 +// 4 7 8 10 +// +// In the following descriptions, "moved" means moving in the buffer, *and* in +// the fold array. +// Meanwhile, "shifted" just means moving in the buffer. +// 1. not changed +// 2. truncated above line1 +// 3. length reduced by line2 - line1, folds starting between the end of 3 and +// dest are truncated and shifted up +// 4. internal folds moved (from [line1, line2] to dest) +// 5. moved to dest. +// 6. truncated below line2 and moved. +// 7. length reduced by line2 - dest, folds starting between line2 and dest are +// removed, top is moved down by move_len. +// 8. truncated below dest and shifted up. +// 9. shifted up +// 10. not changed +static void truncate_fold(fold_T *fp, linenr_T end) +{ + // I want to stop *at here*, foldRemove() stops *above* top + end += 1; + foldRemove(&fp->fd_nested, end - fp->fd_top, MAXLNUM); + fp->fd_len = end - fp->fd_top; +} + +#define fold_end(fp) ((fp)->fd_top + (fp)->fd_len - 1) +#define valid_fold(fp, gap) ((fp) < ((fold_T *)(gap)->ga_data + (gap)->ga_len)) +#define fold_index(fp, gap) ((size_t)(fp - ((fold_T *)(gap)->ga_data))) +void foldMoveRange(garray_T *gap, const linenr_T line1, const linenr_T line2, + const linenr_T dest) +{ + fold_T *fp; + const linenr_T range_len = line2 - line1 + 1; + const linenr_T move_len = dest - line2; + const bool at_start = foldFind(gap, line1 - 1, &fp); + + if (at_start) { + if (fold_end(fp) > dest) { + // Case 4 -- don't have to change this fold, but have to move nested + // folds. + foldMoveRange(&fp->fd_nested, line1 - fp->fd_top, line2 - + fp->fd_top, dest - fp->fd_top); + return; + } else if (fold_end(fp) > line2) { + // Case 3 -- Remove nested folds between line1 and line2 & reduce the + // length of fold by "range_len". + // Folds after this one must be dealt with. + foldMarkAdjustRecurse(&fp->fd_nested, line1 - fp->fd_top, + line2 - fp->fd_top, MAXLNUM, -range_len); + fp->fd_len -= range_len; + } else { + // Case 2 -- truncate fold *above* line1. + // Folds after this one must be dealt with. + truncate_fold(fp, line1 - 1); + } + // Look at the next fold, and treat that one as if it were the first after + // "line1" (because now it is). + fp = fp + 1; + } + + if (!valid_fold(fp, gap) || fp->fd_top > dest) { + // No folds after "line1" and before "dest" + // Case 10. + return; + } else if (fp->fd_top > line2) { + for (; valid_fold(fp, gap) && fold_end(fp) <= dest; fp++) { + // Case 9. (for all case 9's) -- shift up. + fp->fd_top -= range_len; + } + if (valid_fold(fp, gap) && fp->fd_top <= dest) { + // Case 8. -- ensure truncated at dest, shift up + truncate_fold(fp, dest); + fp->fd_top -= range_len; + } + return; + } else if (fold_end(fp) > dest) { + // Case 7 -- remove nested folds and shrink + foldMarkAdjustRecurse(&fp->fd_nested, line2 + 1 - fp->fd_top, + dest - fp->fd_top, MAXLNUM, -move_len); + fp->fd_len -= move_len; + fp->fd_top += move_len; + return; + } + + // Case 5 or 6: changes rely on whether there are folds between the end of + // this fold and "dest". + size_t move_start = fold_index(fp, gap); + size_t move_end = 0, dest_index = 0; + for (; valid_fold(fp, gap) && fp->fd_top <= dest; fp++) { + if (fp->fd_top <= line2) { + // 5, or 6 + if (fold_end(fp) > line2) { + // 6, truncate before moving + truncate_fold(fp, line2); + } + fp->fd_top += move_len; + continue; + } + + // Record index of the first fold after the moved range. + if (move_end == 0) { + move_end = fold_index(fp, gap); + } + + if (fold_end(fp) > dest) { + truncate_fold(fp, dest); + } + + fp->fd_top -= range_len; + } + dest_index = fold_index(fp, gap); + + // All folds are now correct, but they are not necessarily in the correct + // order. + // We have to swap folds in the range [move_end, dest_index) with those in + // the range [move_start, move_end). + reverse_fold_order(gap, move_start, dest_index - 1); + reverse_fold_order(gap, move_start, move_start + dest_index - move_end - 1); + reverse_fold_order(gap, move_start + dest_index - move_end, dest_index - 1); +} +#undef fold_end +#undef valid_fold +#undef fold_index + /* foldMerge() {{{2 */ /* * Merge two adjacent folds (and the nested ones in them). diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 4e05845eb5..de2fdd7f13 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -886,6 +886,23 @@ void ex_changes(exarg_T *eap) * or: mark_adjust(56, 55, MAXLNUM, 2); */ void mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after) +{ + mark_adjust_internal(line1, line2, amount, amount_after, true); +} + +// mark_adjust_nofold() does the same as mark_adjust() but without adjusting +// folds in any way. Folds must be adjusted manually by the caller. +// This is only useful when folds need to be moved in a way different to +// calling foldMarkAdjust() with arguments line1, line2, amount, amount_after, +// for an example of why this may be necessary, see do_move(). +void mark_adjust_nofold(linenr_T line1, linenr_T line2, long amount, + long amount_after) +{ + mark_adjust_internal(line1, line2, amount, amount_after, false); +} + +static void mark_adjust_internal(linenr_T line1, linenr_T line2, long amount, + long amount_after, bool adjust_folds) { int i; int fnum = curbuf->b_fnum; @@ -1011,8 +1028,9 @@ void mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after) } } - /* adjust folds */ - foldMarkAdjust(win, line1, line2, amount, amount_after); + if (adjust_folds) { + foldMarkAdjust(win, line1, line2, amount, amount_after); + } } } diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim index 7cb9faa75f..de07a05a2c 100644 --- a/src/nvim/testdir/test_fold.vim +++ b/src/nvim/testdir/test_fold.vim @@ -1,5 +1,9 @@ " Test for folding +func! PrepIndent(arg) + return [a:arg] + repeat(["\t".a:arg], 5) +endfu + func! Test_address_fold() new call setline(1, ['int FuncName() {/*{{{*/', 1, 2, 3, 4, 5, '}/*}}}*/', @@ -128,3 +132,140 @@ func Test_manual_fold_with_filter() call assert_equal(['1', '2', '3', '4', '5', '6', '7', '8', '9'], getline(1, '$')) bwipe! endfunc + +func! Test_move_folds_around_manual() + new + let input = PrepIndent("a") + PrepIndent("b") + PrepIndent("c") + call setline(1, PrepIndent("a") + PrepIndent("b") + PrepIndent("c")) + let folds=[-1, 2, 2, 2, 2, 2, -1, 8, 8, 8, 8, 8, -1, 14, 14, 14, 14, 14] + " all folds closed + set foldenable foldlevel=0 fdm=indent + " needs a forced redraw + redraw! + set fdm=manual + call assert_equal(folds, map(range(1, line('$')), 'foldclosed(v:val)')) + call assert_equal(input, getline(1, '$')) + 7,12m0 + call assert_equal(PrepIndent("b") + PrepIndent("a") + PrepIndent("c"), getline(1, '$')) + call assert_equal(folds, map(range(1, line('$')), 'foldclosed(v:val)')) + 10,12m0 + call assert_equal(PrepIndent("a")[1:] + PrepIndent("b") + ["a"] + PrepIndent("c"), getline(1, '$')) + call assert_equal([1, 1, 1, 1, 1, -1, 7, 7, 7, 7, 7, -1, -1, 14, 14, 14, 14, 14], map(range(1, line('$')), 'foldclosed(v:val)')) + " moving should not close the folds + %d + call setline(1, PrepIndent("a") + PrepIndent("b") + PrepIndent("c")) + set fdm=indent + redraw! + set fdm=manual + call cursor(2, 1) + %foldopen + 7,12m0 + let folds=repeat([-1], 18) + call assert_equal(PrepIndent("b") + PrepIndent("a") + PrepIndent("c"), getline(1, '$')) + call assert_equal(folds, map(range(1, line('$')), 'foldclosed(v:val)')) + norm! zM + " folds are not corrupted and all have been closed + call assert_equal([-1, 2, 2, 2, 2, 2, -1, 8, 8, 8, 8, 8, -1, 14, 14, 14, 14, 14], map(range(1, line('$')), 'foldclosed(v:val)')) + %d + call setline(1, ["a", "\tb", "\tc", "\td", "\te"]) + set fdm=indent + redraw! + set fdm=manual + %foldopen + 3m4 + %foldclose + call assert_equal(["a", "\tb", "\td", "\tc", "\te"], getline(1, '$')) + call assert_equal([-1, 5, 5, 5, 5], map(range(1, line('$')), 'foldclosedend(v:val)')) + %d + call setline(1, ["a", "\tb", "\tc", "\td", "\te", "z", "\ty", "\tx", "\tw", "\tv"]) + set fdm=indent foldlevel=0 + set fdm=manual + %foldopen + 3m1 + %foldclose + call assert_equal(["a", "\tc", "\tb", "\td", "\te", "z", "\ty", "\tx", "\tw", "\tv"], getline(1, '$')) + call assert_equal(0, foldlevel(2)) + call assert_equal(5, foldclosedend(3)) + call assert_equal([-1, -1, 3, 3, 3, -1, 7, 7, 7, 7], map(range(1, line('$')), 'foldclosed(v:val)')) + 2,6m$ + %foldclose + call assert_equal(5, foldclosedend(2)) + call assert_equal(0, foldlevel(6)) + call assert_equal(9, foldclosedend(7)) + call assert_equal([-1, 2, 2, 2, 2, -1, 7, 7, 7, -1], map(range(1, line('$')), 'foldclosed(v:val)')) + %d + " Ensure moving around the edges still works. + call setline(1, PrepIndent("a") + repeat(["a"], 3) + ["\ta"]) + set fdm=indent foldlevel=0 + set fdm=manual + %foldopen + 6m$ + " The first fold has been truncated to the 5'th line. + " Second fold has been moved up because the moved line is now below it. + call assert_equal([0, 1, 1, 1, 1, 0, 0, 0, 1, 0], map(range(1, line('$')), 'foldlevel(v:val)')) + bw! +endfunc + +func! Test_move_folds_around_indent() + new + let input = PrepIndent("a") + PrepIndent("b") + PrepIndent("c") + call setline(1, PrepIndent("a") + PrepIndent("b") + PrepIndent("c")) + let folds=[-1, 2, 2, 2, 2, 2, -1, 8, 8, 8, 8, 8, -1, 14, 14, 14, 14, 14] + " all folds closed + set fdm=indent + call assert_equal(folds, map(range(1, line('$')), 'foldclosed(v:val)')) + call assert_equal(input, getline(1, '$')) + 7,12m0 + call assert_equal(PrepIndent("b") + PrepIndent("a") + PrepIndent("c"), getline(1, '$')) + call assert_equal(folds, map(range(1, line('$')), 'foldclosed(v:val)')) + 10,12m0 + call assert_equal(PrepIndent("a")[1:] + PrepIndent("b") + ["a"] + PrepIndent("c"), getline(1, '$')) + call assert_equal([1, 1, 1, 1, 1, -1, 7, 7, 7, 7, 7, -1, -1, 14, 14, 14, 14, 14], map(range(1, line('$')), 'foldclosed(v:val)')) + " moving should not close the folds + %d + call setline(1, PrepIndent("a") + PrepIndent("b") + PrepIndent("c")) + set fdm=indent + call cursor(2, 1) + %foldopen + 7,12m0 + let folds=repeat([-1], 18) + call assert_equal(PrepIndent("b") + PrepIndent("a") + PrepIndent("c"), getline(1, '$')) + call assert_equal(folds, map(range(1, line('$')), 'foldclosed(v:val)')) + norm! zM + " folds are not corrupted and all have been closed + call assert_equal([-1, 2, 2, 2, 2, 2, -1, 8, 8, 8, 8, 8, -1, 14, 14, 14, 14, 14], map(range(1, line('$')), 'foldclosed(v:val)')) + %d + call setline(1, ["a", "\tb", "\tc", "\td", "\te"]) + set fdm=indent + %foldopen + 3m4 + %foldclose + call assert_equal(["a", "\tb", "\td", "\tc", "\te"], getline(1, '$')) + call assert_equal([-1, 5, 5, 5, 5], map(range(1, line('$')), 'foldclosedend(v:val)')) + %d + call setline(1, ["a", "\tb", "\tc", "\td", "\te", "z", "\ty", "\tx", "\tw", "\tv"]) + set fdm=indent foldlevel=0 + %foldopen + 3m1 + %foldclose + call assert_equal(["a", "\tc", "\tb", "\td", "\te", "z", "\ty", "\tx", "\tw", "\tv"], getline(1, '$')) + call assert_equal(1, foldlevel(2)) + call assert_equal(5, foldclosedend(3)) + call assert_equal([-1, 2, 2, 2, 2, -1, 7, 7, 7, 7], map(range(1, line('$')), 'foldclosed(v:val)')) + 2,6m$ + %foldclose + call assert_equal(9, foldclosedend(2)) + call assert_equal(1, foldlevel(6)) + call assert_equal(9, foldclosedend(7)) + call assert_equal([-1, 2, 2, 2, 2, 2, 2, 2, 2, -1], map(range(1, line('$')), 'foldclosed(v:val)')) + " Ensure moving around the edges still works. + %d + call setline(1, PrepIndent("a") + repeat(["a"], 3) + ["\ta"]) + set fdm=indent foldlevel=0 + %foldopen + 6m$ + " The first fold has been truncated to the 5'th line. + " Second fold has been moved up because the moved line is now below it. + call assert_equal([0, 1, 1, 1, 1, 0, 0, 0, 1, 1], map(range(1, line('$')), 'foldlevel(v:val)')) + bw! +endfunc diff --git a/test/functional/normal/fold_spec.lua b/test/functional/normal/fold_spec.lua index a2a2a35a8b..5584db20ba 100644 --- a/test/functional/normal/fold_spec.lua +++ b/test/functional/normal/fold_spec.lua @@ -5,9 +5,13 @@ local insert = helpers.insert local feed = helpers.feed local expect = helpers.expect local execute = helpers.execute +local funcs = helpers.funcs +local foldlevel, foldclosedend = funcs.foldlevel, funcs.foldclosedend +local eq = helpers.eq describe('Folds', function() clear() + before_each(function() execute('enew!') end) it('manual folding adjusts with filter', function() insert([[ 1 @@ -44,4 +48,186 @@ describe('Folds', function() 8 9]]) end) + describe('adjusting folds after :move', function() + local function manually_fold_indent() + -- setting foldmethod twice is a trick to get vim to set the folds for me + execute('set foldmethod=indent', 'set foldmethod=manual') + -- Ensure that all folds will get closed (makes it easier to test the + -- length of folds). + execute('set foldminlines=0') + -- Start with all folds open (so :move ranges aren't affected by closed + -- folds). + execute('%foldopen!') + end + + local function get_folds() + local rettab = {} + for i = 1, funcs.line('$') do + table.insert(rettab, foldlevel(i)) + end + return rettab + end + + local function test_move_indent(insert_string, move_command) + -- This test is easy because we just need to ensure that the resulting + -- fold is the same as calculated when creating folds from scratch. + insert(insert_string) + execute(move_command) + local after_move_folds = get_folds() + -- Doesn't change anything, but does call foldUpdateAll() + execute('set foldminlines=0') + eq(after_move_folds, get_folds()) + -- Set up the buffer with insert_string for the manual fold testing. + execute('enew!') + insert(insert_string) + manually_fold_indent() + execute(move_command) + end + + it('neither closes nor corrupts folds', function() + test_move_indent([[ +a + a + a + a + a + a +a + a + a + a + a + a +a + a + a + a + a + a]], '7,12m0') + expect([[ +a + a + a + a + a + a +a + a + a + a + a + a +a + a + a + a + a + a]]) + -- lines are not closed, folds are correct + for i = 1,funcs.line('$') do + eq(-1, funcs.foldclosed(i)) + if i == 1 or i == 7 or i == 13 then + eq(0, foldlevel(i)) + elseif i == 4 then + eq(2, foldlevel(i)) + else + eq(1, foldlevel(i)) + end + end + -- folds are not corrupted + feed('zM') + eq(6, foldclosedend(2)) + eq(12, foldclosedend(8)) + eq(18, foldclosedend(14)) + end) + it("doesn't split a fold when the move is within it", function() + test_move_indent([[ +a + a + a + a + a + a + a + a + a +a]], '5m6') + eq({0, 1, 1, 2, 2, 2, 2, 1, 1, 0}, get_folds()) + end) + it('truncates folds that end in the moved range', function() + test_move_indent([[ +a + a + a + a + a +a +a]], '4,5m6') + eq({0, 1, 2, 0, 0, 0, 0}, get_folds()) + end) + it('moves folds that start between moved range and destination', function() + test_move_indent([[ +a + a + a + a + a +a +a + a + a + a +a +a + a]], '3,4m$') + eq({0, 1, 1, 0, 0, 1, 2, 1, 0, 0, 1, 0, 0}, get_folds()) + end) + it('does not affect folds outside changed lines', function() + test_move_indent([[ + a + a + a +a +a +a + a + a + a]], '4m5') + eq({1, 1, 1, 0, 0, 0, 1, 1, 1}, get_folds()) + end) + it('moves and truncates folds that start in moved range', function() + test_move_indent([[ +a + a + a + a + a +a +a +a +a +a]], '1,3m7') + eq({0, 0, 0, 0, 0, 1, 2, 0, 0, 0}, get_folds()) + end) + it('breaks a fold when moving text into it', function() + test_move_indent([[ +a + a + a + a + a +a +a]], '$m4') + eq({0, 1, 2, 2, 0, 0, 0}, get_folds()) + end) + it('adjusts correctly when moving a range backwards', function() + test_move_indent([[ +a + a + a + a +a]], '2,3m0') + eq({1, 2, 0, 0, 0}, get_folds()) + end) + end) end) From 308a953e0b892a0d59467e55227e972dbe51983c Mon Sep 17 00:00:00 2001 From: Matthew Malcomson Date: Wed, 22 Mar 2017 16:41:00 +0000 Subject: [PATCH 0092/1671] Fix wrap-around in 32 bit --- src/nvim/fold.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/nvim/fold.c b/src/nvim/fold.c index e2d1b017b6..5835da2e94 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -2507,6 +2507,8 @@ static void foldSplit(garray_T *gap, int i, linenr_T top, linenr_T bot) fp = (fold_T *)gap->ga_data + i; fp[1].fd_top = bot + 1; + // check for wrap around (MAXLNUM, and 32bit) + assert(fp[1].fd_top > bot); fp[1].fd_len = fp->fd_len - (fp[1].fd_top - fp->fd_top); fp[1].fd_flags = fp->fd_flags; fp[1].fd_small = MAYBE; @@ -2555,34 +2557,35 @@ static void foldRemove(garray_T *gap, linenr_T top, linenr_T bot) { fold_T *fp = NULL; - if (bot < top) - return; /* nothing to do */ + if (bot < top) { + return; // nothing to do + } for (;; ) { - /* Find fold that includes top or a following one. */ + // Find fold that includes top or a following one. if (foldFind(gap, top, &fp) && fp->fd_top < top) { - /* 2: or 3: need to delete nested folds */ + // 2: or 3: need to delete nested folds foldRemove(&fp->fd_nested, top - fp->fd_top, bot - fp->fd_top); - if (fp->fd_top + fp->fd_len > bot + 1) { - /* 3: need to split it. */ + if (fp->fd_top + fp->fd_len - 1 > bot) { + // 3: need to split it. foldSplit(gap, (int)(fp - (fold_T *)gap->ga_data), top, bot); } else { - /* 2: truncate fold at "top". */ + // 2: truncate fold at "top". fp->fd_len = top - fp->fd_top; } - fold_changed = TRUE; + fold_changed = true; continue; } if (fp >= (fold_T *)(gap->ga_data) + gap->ga_len || fp->fd_top > bot) { - /* 6: Found a fold below bot, can stop looking. */ + // 6: Found a fold below bot, can stop looking. break; } if (fp->fd_top >= top) { - /* Found an entry below top. */ - fold_changed = TRUE; + // Found an entry below top. + fold_changed = true; if (fp->fd_top + fp->fd_len - 1 > bot) { - /* 5: Make fold that includes bot start below bot. */ + // 5: Make fold that includes bot start below bot. foldMarkAdjustRecurse(&fp->fd_nested, (linenr_T)0, (long)(bot - fp->fd_top), (linenr_T)MAXLNUM, (long)(fp->fd_top - bot - 1)); @@ -2591,8 +2594,8 @@ static void foldRemove(garray_T *gap, linenr_T top, linenr_T bot) break; } - /* 4: Delete completely contained fold. */ - deleteFoldEntry(gap, (int)(fp - (fold_T *)gap->ga_data), TRUE); + // 4: Delete completely contained fold. + deleteFoldEntry(gap, (int)(fp - (fold_T *)gap->ga_data), true); } } } From a6c9c91841a9c3a63196ec5b82c09308c8e5b78d Mon Sep 17 00:00:00 2001 From: Matthew Malcomson Date: Thu, 23 Mar 2017 14:16:21 +0000 Subject: [PATCH 0093/1671] vim-patch:8.0.0407 : filtering folds with marker method not tested Problem: Filtering folds with marker method not tested. Solution: Also set 'foldmethod' to "marker". --- src/nvim/testdir/test_fold.vim | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim index de07a05a2c..976c6b5cd1 100644 --- a/src/nvim/testdir/test_fold.vim +++ b/src/nvim/testdir/test_fold.vim @@ -120,17 +120,22 @@ func Test_manual_fold_with_filter() if !executable('cat') return endif - new - call setline(1, range(1, 20)) - 4,$fold - %foldopen - 10,$fold - %foldopen - " This filter command should not have an effect - 1,8! cat - call feedkeys('5ggzdzMGdd', 'xt') - call assert_equal(['1', '2', '3', '4', '5', '6', '7', '8', '9'], getline(1, '$')) - bwipe! + for type in ['manual', 'marker'] + exe 'set foldmethod=' . type + new + call setline(1, range(1, 20)) + 4,$fold + %foldopen + 10,$fold + %foldopen + " This filter command should not have an effect + 1,8! cat + call feedkeys('5ggzdzMGdd', 'xt') + call assert_equal(['1', '2', '3', '4', '5', '6', '7', '8', '9'], getline(1, '$')) + + bwipe! + set foldmethod& + endfor endfunc func! Test_move_folds_around_manual() From 7214d0bc846179a862e8d3061d00270a6caa0d7b Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 23 Mar 2017 14:07:51 +0100 Subject: [PATCH 0094/1671] XXX: ex_tabonly(): aucmd_win is not part of the window list. During free_all_mem, somehow ex_tabonly() may free aucmd_win. But it isn't fully destroyed (maybe autocmd_busy?). When win_free_all() tries to free aucmd_win directly, it double-frees the sub-fields. Tried unnsuccessfully to work around this by invoking `:tabonly!` with autocmds disabled: diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 58c01fbe7a12..91c845e94d22 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -565,9 +565,9 @@ void free_all_mem(void) /* Close all tabs and windows. Reset 'equalalways' to avoid redraws. */ p_ea = false; if (first_tabpage->tp_next != NULL) - do_cmdline_cmd("tabonly!"); + do_cmdline_cmd("noautocmd tabonly!"); if (firstwin != lastwin) - do_cmdline_cmd("only!"); + do_cmdline_cmd("noautocmd only!"); /* Free all spell info. */ spell_free_all(); --- src/nvim/ex_docmd.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index d815d57a60..486baaad47 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -6081,6 +6081,9 @@ static void ex_tabonly(exarg_T *eap) // Repeat this up to a 1000 times, because autocommands may // mess up the lists. for (int done = 0; done < 1000; done++) { + FOR_ALL_TAB_WINDOWS(tp, wp) { + assert(wp != aucmd_win); + } FOR_ALL_TABS(tp) { if (tp->tp_topframe != topframe) { tabpage_close_other(tp, eap->forceit); From a62ec4eb989f41ace8e6b7f085349a2bce964d68 Mon Sep 17 00:00:00 2001 From: Tommy Allen Date: Thu, 23 Mar 2017 20:38:01 -0400 Subject: [PATCH 0095/1671] health.vim: tmux: Try -qvg and -qvgs (#6348) --- runtime/autoload/health/nvim.vim | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/runtime/autoload/health/nvim.vim b/runtime/autoload/health/nvim.vim index a1bf9b21cf..ca62de84d8 100644 --- a/runtime/autoload/health/nvim.vim +++ b/runtime/autoload/health/nvim.vim @@ -118,6 +118,12 @@ function! s:check_tmux() abort let cmd = 'tmux show-option -qvg default-terminal' let out = system(cmd) let tmux_default_term = substitute(out, '\v(\s|\r|\n)', '', 'g') + if empty(tmux_default_term) + let cmd = 'tmux show-option -qvgs default-terminal' + let out = system(cmd) + let tmux_default_term = substitute(out, '\v(\s|\r|\n)', '', 'g') + endif + if v:shell_error call health#report_error('command failed: '.cmd."\n".out) elseif tmux_default_term !=# $TERM From 2a6d44ca5232d5fbfa0401451b3ebce60e93ed4d Mon Sep 17 00:00:00 2001 From: James McCoy Date: Fri, 24 Mar 2017 09:36:09 -0400 Subject: [PATCH 0096/1671] vim-patch:8.0.0250 Problem: When virtcol() gets a column that is not the first byte of a multi-byte character the result is unpredictable. (Christian Ludwig) Solution: Correct the column to the first byte of a multi-byte character. Change the utf-8 test to new style. https://github.com/vim/vim/commit/0c0590d9827cb07a33c1552cb3558b94bddcb4dc Closes #6269 --- src/nvim/charset.c | 1 + src/nvim/testdir/test_alot.vim | 1 + src/nvim/testdir/test_utf8.vim | 65 ++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 src/nvim/testdir/test_utf8.vim diff --git a/src/nvim/charset.c b/src/nvim/charset.c index f31cef5886..efe32b915f 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1166,6 +1166,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, pos->col = 0; } posptr = ptr + pos->col; + posptr -= utf_head_off(line, posptr); } // This function is used very often, do some speed optimizations. diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index 9c7db9e397..baf49b7ff7 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -28,4 +28,5 @@ source test_tagcase.vim source test_tagjump.vim source test_true_false.vim source test_unlet.vim +source test_utf8.vim source test_window_cmd.vim diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim new file mode 100644 index 0000000000..24e3db86fb --- /dev/null +++ b/src/nvim/testdir/test_utf8.vim @@ -0,0 +1,65 @@ +" Tests for Unicode manipulations +if !has('multi_byte') + finish +endif + + +" Visual block Insert adjusts for multi-byte char +func Test_visual_block_insert() + new + call setline(1, ["aaa", "あああ", "bbb"]) + exe ":norm! gg0l\jjIx\" + call assert_equal(['axaa', 'xあああ', 'bxbb'], getline(1, '$')) + bwipeout! +endfunc + +" Test for built-in function strchars() +func Test_strchars() + let inp = ["a", "あいa", "A\u20dd", "A\u20dd\u20dd", "\u20dd"] + let exp = [[1, 1, 1], [3, 3, 3], [2, 2, 1], [3, 3, 1], [1, 1, 1]] + for i in range(len(inp)) + call assert_equal(exp[i][0], strchars(inp[i])) + call assert_equal(exp[i][1], strchars(inp[i], 0)) + call assert_equal(exp[i][2], strchars(inp[i], 1)) + endfor +endfunc + +" Test for customlist completion +function! CustomComplete1(lead, line, pos) + return ['あ', 'い'] +endfunction + +function! CustomComplete2(lead, line, pos) + return ['あたし', 'あたま', 'あたりめ'] +endfunction + +function! CustomComplete3(lead, line, pos) + return ['Nこ', 'Nん', 'Nぶ'] +endfunction + +func Test_customlist_completion() + command -nargs=1 -complete=customlist,CustomComplete1 Test1 echo + call feedkeys(":Test1 \\\"\", 'itx') + call assert_equal('"Test1 ', getreg(':')) + + command -nargs=1 -complete=customlist,CustomComplete2 Test2 echo + call feedkeys(":Test2 \\\"\", 'itx') + call assert_equal('"Test2 あた', getreg(':')) + + command -nargs=1 -complete=customlist,CustomComplete3 Test3 echo + call feedkeys(":Test3 \\\"\", 'itx') + call assert_equal('"Test3 N', getreg(':')) + + call garbagecollect(1) +endfunc + +" Yank one 3 byte character and check the mark columns. +func Test_getvcol() + new + call setline(1, "x\u2500x") + normal 0lvy + call assert_equal(2, col("'[")) + call assert_equal(4, col("']")) + call assert_equal(2, virtcol("'[")) + call assert_equal(2, virtcol("']")) +endfunc From 90ac8b09f56bf5a53ca9e1ddba0a5f92e4df7fa9 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 25 Mar 2017 00:15:31 +0100 Subject: [PATCH 0097/1671] fold.c: uppercase macros --- src/nvim/fold.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 5835da2e94..36a5b0efd7 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -2649,9 +2649,9 @@ static void truncate_fold(fold_T *fp, linenr_T end) fp->fd_len = end - fp->fd_top; } -#define fold_end(fp) ((fp)->fd_top + (fp)->fd_len - 1) -#define valid_fold(fp, gap) ((fp) < ((fold_T *)(gap)->ga_data + (gap)->ga_len)) -#define fold_index(fp, gap) ((size_t)(fp - ((fold_T *)(gap)->ga_data))) +#define FOLD_END(fp) ((fp)->fd_top + (fp)->fd_len - 1) +#define VALID_FOLD(fp, gap) ((fp) < ((fold_T *)(gap)->ga_data + (gap)->ga_len)) +#define FOLD_INDEX(fp, gap) ((size_t)(fp - ((fold_T *)(gap)->ga_data))) void foldMoveRange(garray_T *gap, const linenr_T line1, const linenr_T line2, const linenr_T dest) { @@ -2661,13 +2661,13 @@ void foldMoveRange(garray_T *gap, const linenr_T line1, const linenr_T line2, const bool at_start = foldFind(gap, line1 - 1, &fp); if (at_start) { - if (fold_end(fp) > dest) { + if (FOLD_END(fp) > dest) { // Case 4 -- don't have to change this fold, but have to move nested // folds. foldMoveRange(&fp->fd_nested, line1 - fp->fd_top, line2 - fp->fd_top, dest - fp->fd_top); return; - } else if (fold_end(fp) > line2) { + } else if (FOLD_END(fp) > line2) { // Case 3 -- Remove nested folds between line1 and line2 & reduce the // length of fold by "range_len". // Folds after this one must be dealt with. @@ -2684,22 +2684,22 @@ void foldMoveRange(garray_T *gap, const linenr_T line1, const linenr_T line2, fp = fp + 1; } - if (!valid_fold(fp, gap) || fp->fd_top > dest) { + if (!VALID_FOLD(fp, gap) || fp->fd_top > dest) { // No folds after "line1" and before "dest" // Case 10. return; } else if (fp->fd_top > line2) { - for (; valid_fold(fp, gap) && fold_end(fp) <= dest; fp++) { + for (; VALID_FOLD(fp, gap) && FOLD_END(fp) <= dest; fp++) { // Case 9. (for all case 9's) -- shift up. fp->fd_top -= range_len; } - if (valid_fold(fp, gap) && fp->fd_top <= dest) { + if (VALID_FOLD(fp, gap) && fp->fd_top <= dest) { // Case 8. -- ensure truncated at dest, shift up truncate_fold(fp, dest); fp->fd_top -= range_len; } return; - } else if (fold_end(fp) > dest) { + } else if (FOLD_END(fp) > dest) { // Case 7 -- remove nested folds and shrink foldMarkAdjustRecurse(&fp->fd_nested, line2 + 1 - fp->fd_top, dest - fp->fd_top, MAXLNUM, -move_len); @@ -2710,12 +2710,12 @@ void foldMoveRange(garray_T *gap, const linenr_T line1, const linenr_T line2, // Case 5 or 6: changes rely on whether there are folds between the end of // this fold and "dest". - size_t move_start = fold_index(fp, gap); + size_t move_start = FOLD_INDEX(fp, gap); size_t move_end = 0, dest_index = 0; - for (; valid_fold(fp, gap) && fp->fd_top <= dest; fp++) { + for (; VALID_FOLD(fp, gap) && fp->fd_top <= dest; fp++) { if (fp->fd_top <= line2) { // 5, or 6 - if (fold_end(fp) > line2) { + if (FOLD_END(fp) > line2) { // 6, truncate before moving truncate_fold(fp, line2); } @@ -2725,16 +2725,16 @@ void foldMoveRange(garray_T *gap, const linenr_T line1, const linenr_T line2, // Record index of the first fold after the moved range. if (move_end == 0) { - move_end = fold_index(fp, gap); + move_end = FOLD_INDEX(fp, gap); } - if (fold_end(fp) > dest) { + if (FOLD_END(fp) > dest) { truncate_fold(fp, dest); } fp->fd_top -= range_len; } - dest_index = fold_index(fp, gap); + dest_index = FOLD_INDEX(fp, gap); // All folds are now correct, but they are not necessarily in the correct // order. @@ -2744,9 +2744,9 @@ void foldMoveRange(garray_T *gap, const linenr_T line1, const linenr_T line2, reverse_fold_order(gap, move_start, move_start + dest_index - move_end - 1); reverse_fold_order(gap, move_start + dest_index - move_end, dest_index - 1); } -#undef fold_end -#undef valid_fold -#undef fold_index +#undef FOLD_END +#undef VALID_FOLD +#undef FOLD_INDEX /* foldMerge() {{{2 */ /* From 098e91400eb06d29c31264ba973ea8a563703059 Mon Sep 17 00:00:00 2001 From: Matthew Malcomson Date: Sat, 25 Mar 2017 14:43:19 +0000 Subject: [PATCH 0098/1671] refactor: Remove allow_keys global (#6346) * The allow_keys global is unused in nvim, remove it * clint --- src/nvim/digraph.c | 4 ---- src/nvim/edit.c | 36 ++++++++++++++++-------------------- src/nvim/eval.c | 6 ++---- src/nvim/ex_cmds.c | 6 ++---- src/nvim/ex_getln.c | 12 ++++-------- src/nvim/getchar.c | 27 ++++++++++----------------- src/nvim/globals.h | 12 +++++------- src/nvim/message.c | 21 ++++++++++----------- src/nvim/misc1.c | 18 +++++++----------- src/nvim/normal.c | 38 +++++++++++++------------------------- src/nvim/window.c | 11 +++++------ 11 files changed, 74 insertions(+), 117 deletions(-) diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c index 6ba6e659a6..560205fe7d 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -1448,10 +1448,8 @@ int get_digraph(int cmdline) { int cc; no_mapping++; - allow_keys++; int c = plain_vgetc(); no_mapping--; - allow_keys--; if (c != ESC) { // ESC cancels CTRL-K @@ -1468,10 +1466,8 @@ int get_digraph(int cmdline) add_to_showcmd(c); } no_mapping++; - allow_keys++; cc = plain_vgetc(); no_mapping--; - allow_keys--; if (cc != ESC) { // ESC cancels CTRL-K diff --git a/src/nvim/edit.c b/src/nvim/edit.c index aa029c38a2..5544f0b163 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -689,11 +689,9 @@ static int insert_execute(VimState *state, int key) if (s->c == Ctrl_BSL) { // may need to redraw when no more chars available now ins_redraw(false); - ++no_mapping; - ++allow_keys; + no_mapping++; s->c = plain_vgetc(); - --no_mapping; - --allow_keys; + no_mapping--; if (s->c != Ctrl_N && s->c != Ctrl_G && s->c != Ctrl_O) { // it's something else vungetc(s->c); @@ -8303,17 +8301,16 @@ static int ins_digraph(void) } - /* don't map the digraph chars. This also prevents the - * mode message to be deleted when ESC is hit */ - ++no_mapping; - ++allow_keys; + // don't map the digraph chars. This also prevents the + // mode message to be deleted when ESC is hit + no_mapping++; c = plain_vgetc(); - --no_mapping; - --allow_keys; - if (did_putchar) - /* when the line fits in 'columns' the '?' is at the start of the next - * line and will not be removed by the redraw */ + no_mapping--; + if (did_putchar) { + // when the line fits in 'columns' the '?' is at the start of the next + // line and will not be removed by the redraw edit_unputchar(); + } if (IS_SPECIAL(c) || mod_mask) { /* special key */ clear_showcmd(); @@ -8333,15 +8330,14 @@ static int ins_digraph(void) } add_to_showcmd_c(c); } - ++no_mapping; - ++allow_keys; + no_mapping++; cc = plain_vgetc(); - --no_mapping; - --allow_keys; - if (did_putchar) - /* when the line fits in 'columns' the '?' is at the start of the - * next line and will not be removed by a redraw */ + no_mapping--; + if (did_putchar) { + // when the line fits in 'columns' the '?' is at the start of the + // next line and will not be removed by a redraw edit_unputchar(); + } if (cc != ESC) { AppendToRedobuff((char_u *)CTRL_V_STR); c = getdigraph(c, cc, TRUE); diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8f52717252..d49fcbf17e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -10596,8 +10596,7 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) varnumber_T n; int error = FALSE; - ++no_mapping; - ++allow_keys; + no_mapping++; for (;; ) { // Position the cursor. Needed after a message that ends in a space, // or if event processing caused a redraw. @@ -10630,8 +10629,7 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) continue; break; } - --no_mapping; - --allow_keys; + no_mapping--; vimvars[VV_MOUSE_WIN].vv_nr = 0; vimvars[VV_MOUSE_WINID].vv_nr = 0; diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 852d930b0a..678102daf6 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -3577,11 +3577,9 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) ui_cursor_goto(msg_row, msg_col); RedrawingDisabled = temp; - ++no_mapping; /* don't map this key */ - ++allow_keys; /* allow special keys */ + no_mapping++; // don't map this key typed = plain_vgetc(); - --allow_keys; - --no_mapping; + no_mapping--; /* clear the question */ msg_didout = FALSE; /* don't scroll up */ diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 2600f484dc..58979c0e43 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -621,11 +621,9 @@ static int command_line_execute(VimState *state, int key) // CTRL-\ CTRL-N goes to Normal mode, CTRL-\ CTRL-G goes to Insert // mode when 'insertmode' is set, CTRL-\ e prompts for an expression. if (s->c == Ctrl_BSL) { - ++no_mapping; - ++allow_keys; + no_mapping++; s->c = plain_vgetc(); - --no_mapping; - --allow_keys; + no_mapping--; // CTRL-\ e doesn't work when obtaining an expression, unless it // is in a mapping. if (s->c != Ctrl_N && s->c != Ctrl_G && (s->c != 'e' @@ -1887,8 +1885,7 @@ getexmodeline ( msg_putchar(' '); } } - ++no_mapping; - ++allow_keys; + no_mapping++; /* * Get the line, one character at a time. @@ -2078,8 +2075,7 @@ redraw: } } - --no_mapping; - --allow_keys; + no_mapping--; /* make following messages go to the next line */ msg_didout = FALSE; diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index bae8ae6d91..0c131d7b33 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1389,27 +1389,20 @@ int vgetc(void) for (;; ) { // this is done twice if there are modifiers bool did_inc = false; if (mod_mask) { // no mapping after modifier has been read - ++no_mapping; - ++allow_keys; + no_mapping++; did_inc = true; // mod_mask may change value } c = vgetorpeek(true); if (did_inc) { - --no_mapping; - --allow_keys; + no_mapping--; } - /* Get two extra bytes for special keys */ - if (c == K_SPECIAL - ) { - int save_allow_keys = allow_keys; - - ++no_mapping; - allow_keys = 0; /* make sure BS is not found */ - c2 = vgetorpeek(TRUE); /* no mapping for these chars */ - c = vgetorpeek(TRUE); - --no_mapping; - allow_keys = save_allow_keys; + // Get two extra bytes for special keys + if (c == K_SPECIAL) { + no_mapping++; + c2 = vgetorpeek(true); // no mapping for these chars + c = vgetorpeek(true); + no_mapping--; if (c2 == KS_MODIFIER) { mod_mask = c; continue; @@ -1487,7 +1480,7 @@ int vgetc(void) buf[i] = CSI; } } - --no_mapping; + no_mapping--; c = (*mb_ptr2char)(buf); } @@ -1570,7 +1563,7 @@ int char_avail(void) no_mapping++; retval = vpeekc(); - --no_mapping; + no_mapping--; return retval != NUL; } diff --git a/src/nvim/globals.h b/src/nvim/globals.h index f8c7c9d330..ad14ec4f8c 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -832,13 +832,11 @@ EXTERN int ex_no_reprint INIT(= FALSE); /* no need to print after z or p */ EXTERN int Recording INIT(= FALSE); /* TRUE when recording into a reg. */ EXTERN int Exec_reg INIT(= FALSE); /* TRUE when executing a register */ -EXTERN int no_mapping INIT(= FALSE); /* currently no mapping allowed */ -EXTERN int no_zero_mapping INIT(= 0); /* mapping zero not allowed */ -EXTERN int allow_keys INIT(= FALSE); /* allow key codes when no_mapping - * is set */ -EXTERN int no_u_sync INIT(= 0); /* Don't call u_sync() */ -EXTERN int u_sync_once INIT(= 0); /* Call u_sync() once when evaluating - an expression. */ +EXTERN int no_mapping INIT(= false); // currently no mapping allowed +EXTERN int no_zero_mapping INIT(= 0); // mapping zero not allowed +EXTERN int no_u_sync INIT(= 0); // Don't call u_sync() +EXTERN int u_sync_once INIT(= 0); // Call u_sync() once when evaluating + // an expression. EXTERN bool force_restart_edit INIT(= false); // force restart_edit after // ex_normal returns diff --git a/src/nvim/message.c b/src/nvim/message.c index 4cd0db21e8..cc9ef15f13 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -847,23 +847,22 @@ void wait_return(int redraw) * CTRL-C, but we need to loop then. */ had_got_int = got_int; - /* Don't do mappings here, we put the character back in the - * typeahead buffer. */ - ++no_mapping; - ++allow_keys; + // Don't do mappings here, we put the character back in the + // typeahead buffer. + no_mapping++; - /* Temporarily disable Recording. If Recording is active, the - * character will be recorded later, since it will be added to the - * typebuf after the loop */ + // Temporarily disable Recording. If Recording is active, the + // character will be recorded later, since it will be added to the + // typebuf after the loop save_Recording = Recording; save_scriptout = scriptout; Recording = FALSE; scriptout = NULL; c = safe_vgetc(); - if (had_got_int && !global_busy) - got_int = FALSE; - --no_mapping; - --allow_keys; + if (had_got_int && !global_busy) { + got_int = false; + } + no_mapping--; Recording = save_Recording; scriptout = save_scriptout; diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 0bb5a8468d..d751f13644 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -2225,11 +2225,10 @@ int ask_yesno(const char *str, bool direct) int r = ' '; int save_State = State; - ++no_wait_return; - State = CONFIRM; /* mouse behaves like with :confirm */ - setmouse(); /* disables mouse for xterm */ - ++no_mapping; - ++allow_keys; /* no mapping here, but recognize keys */ + no_wait_return++; + State = CONFIRM; // mouse behaves like with :confirm + setmouse(); // disables mouse for xterm + no_mapping++; while (r != 'y' && r != 'n') { /* same highlighting as for wait_return */ @@ -2247,8 +2246,7 @@ int ask_yesno(const char *str, bool direct) --no_wait_return; State = save_State; setmouse(); - --no_mapping; - --allow_keys; + no_mapping--; return r; } @@ -2398,8 +2396,7 @@ get_number ( if (msg_silent != 0) return 0; - ++no_mapping; - ++allow_keys; /* no mapping here, but recognize keys */ + no_mapping++; for (;; ) { ui_cursor_goto(msg_row, msg_col); c = safe_vgetc(); @@ -2427,8 +2424,7 @@ get_number ( } else if (c == CAR || c == NL || c == Ctrl_C || c == ESC) break; } - --no_mapping; - --allow_keys; + no_mapping--; return n; } diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 85dc509ee6..2ade9cb87d 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -642,8 +642,7 @@ static void normal_get_additional_char(NormalState *s) bool langmap_active = false; // using :lmap mappings int lang; // getting a text character - ++no_mapping; - ++allow_keys; // no mapping for nchar, but allow key codes + no_mapping++; // Don't generate a CursorHold event here, most commands can't handle // it, e.g., nv_replace(), nv_csearch(). did_cursorhold = true; @@ -681,8 +680,7 @@ static void normal_get_additional_char(NormalState *s) } if (lang && curbuf->b_p_iminsert == B_IMODE_LMAP) { // Allow mappings defined with ":lmap". - --no_mapping; - --allow_keys; + no_mapping--; if (repl) { State = LREPLACE; } else { @@ -695,8 +693,7 @@ static void normal_get_additional_char(NormalState *s) if (langmap_active) { // Undo the decrement done above - ++no_mapping; - ++allow_keys; + no_mapping++; State = NORMAL_BUSY; } State = NORMAL_BUSY; @@ -781,8 +778,7 @@ static void normal_get_additional_char(NormalState *s) } no_mapping++; } - --no_mapping; - --allow_keys; + no_mapping--; } static void normal_invert_horizontal(NormalState *s) @@ -832,8 +828,7 @@ static bool normal_get_command_count(NormalState *s) } if (s->ctrl_w) { - ++no_mapping; - ++allow_keys; // no mapping for nchar, but keys + no_mapping++; } ++no_zero_mapping; // don't map zero here @@ -841,8 +836,7 @@ static bool normal_get_command_count(NormalState *s) LANGMAP_ADJUST(s->c, true); --no_zero_mapping; if (s->ctrl_w) { - --no_mapping; - --allow_keys; + no_mapping--; } s->need_flushbuf |= add_to_showcmd(s->c); } @@ -852,12 +846,10 @@ static bool normal_get_command_count(NormalState *s) s->ctrl_w = true; s->ca.opcount = s->ca.count0; // remember first count s->ca.count0 = 0; - ++no_mapping; - ++allow_keys; // no mapping for nchar, but keys + no_mapping++; s->c = plain_vgetc(); // get next character LANGMAP_ADJUST(s->c, true); - --no_mapping; - --allow_keys; + no_mapping--; s->need_flushbuf |= add_to_showcmd(s->c); return true; } @@ -4043,12 +4035,10 @@ static void nv_zet(cmdarg_T *cap) return; n = nchar - '0'; for (;; ) { - ++no_mapping; - ++allow_keys; /* no mapping for nchar, but allow key codes */ + no_mapping++; nchar = plain_vgetc(); LANGMAP_ADJUST(nchar, true); - --no_mapping; - --allow_keys; + no_mapping--; (void)add_to_showcmd(nchar); if (nchar == K_DEL || nchar == K_KDEL) n /= 10; @@ -4376,13 +4366,11 @@ dozet: break; - case 'u': /* "zug" and "zuw": undo "zg" and "zw" */ - ++no_mapping; - ++allow_keys; /* no mapping for nchar, but allow key codes */ + case 'u': // "zug" and "zuw": undo "zg" and "zw" + no_mapping++; nchar = plain_vgetc(); LANGMAP_ADJUST(nchar, true); - --no_mapping; - --allow_keys; + no_mapping--; (void)add_to_showcmd(nchar); if (vim_strchr((char_u *)"gGwW", nchar) == NULL) { clearopbeep(cap->oap); diff --git a/src/nvim/window.c b/src/nvim/window.c index dc0ce03794..4fac730f02 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -447,13 +447,12 @@ wingotofile: case 'g': case Ctrl_G: CHECK_CMDWIN - ++ no_mapping; - ++allow_keys; /* no mapping for xchar, but allow key codes */ - if (xchar == NUL) + no_mapping++; + if (xchar == NUL) { xchar = plain_vgetc(); - LANGMAP_ADJUST(xchar, TRUE); - --no_mapping; - --allow_keys; + } + LANGMAP_ADJUST(xchar, true); + no_mapping--; (void)add_to_showcmd(xchar); switch (xchar) { case '}': From a346cb1d5893664c9ea331d3c75eca0bb0a4e511 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Sat, 25 Mar 2017 17:43:25 -0400 Subject: [PATCH 0099/1671] man.vim: call s:error in man#read_page (#6362) The comment is incorrect, s:error does need to be called. I thought the call was unnecessary because it didn't show any message for me but I had shortmess+=F which was hiding the message. --- runtime/autoload/man.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index 4352a8c782..96f03e47cc 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -79,7 +79,7 @@ function! man#read_page(ref) abort let [sect, name, path] = s:verify_exists(sect, name) let page = s:get_page(path) catch - " call to s:error() is unnecessary + call s:error(v:exception) return endtry let b:man_sect = sect From b60e5c85adb597f5a1688de47198175b2747fbd4 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Sat, 25 Mar 2017 23:02:20 +0100 Subject: [PATCH 0100/1671] cmake: USE_BUNDLED_X instead of X_USE_BUNDLED (#6357) --- cmake/FindJeMalloc.cmake | 2 +- cmake/FindLibTermkey.cmake | 2 +- cmake/FindLibUV.cmake | 2 +- cmake/FindLibVterm.cmake | 2 +- cmake/FindLuaJit.cmake | 2 +- cmake/FindMsgpack.cmake | 2 +- cmake/FindUnibilium.cmake | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cmake/FindJeMalloc.cmake b/cmake/FindJeMalloc.cmake index f36cbc6f7a..820ceeed4a 100644 --- a/cmake/FindJeMalloc.cmake +++ b/cmake/FindJeMalloc.cmake @@ -4,7 +4,7 @@ # JEMALLOC_INCLUDE_DIRS - The jemalloc include directories # JEMALLOC_LIBRARIES - The libraries needed to use jemalloc -if(NOT JEMALLOC_USE_BUNDLED) +if(NOT USE_BUNDLED_JEMALLOC) find_package(PkgConfig) if (PKG_CONFIG_FOUND) pkg_check_modules(PC_JEMALLOC QUIET jemalloc) diff --git a/cmake/FindLibTermkey.cmake b/cmake/FindLibTermkey.cmake index 144deceaae..66fd2e6c89 100644 --- a/cmake/FindLibTermkey.cmake +++ b/cmake/FindLibTermkey.cmake @@ -4,7 +4,7 @@ # LIBTERMKEY_INCLUDE_DIRS - The libtermkey include directories # LIBTERMKEY_LIBRARIES - The libraries needed to use libtermkey -if(NOT LIBTERMKEY_USE_BUNDLED) +if(NOT USE_BUNDLED_LIBTERMKEY) find_package(PkgConfig) if (PKG_CONFIG_FOUND) pkg_check_modules(PC_LIBTERMKEY QUIET termkey) diff --git a/cmake/FindLibUV.cmake b/cmake/FindLibUV.cmake index dcdd5e48b7..3e042e4c50 100644 --- a/cmake/FindLibUV.cmake +++ b/cmake/FindLibUV.cmake @@ -8,7 +8,7 @@ # Set the LIBUV_USE_STATIC variable to specify if static libraries should # be preferred to shared ones. -if(NOT LIBUV_USE_BUNDLED) +if(NOT USE_BUNDLED_LIBUV) find_package(PkgConfig) if (PKG_CONFIG_FOUND) pkg_check_modules(PC_LIBUV QUIET libuv) diff --git a/cmake/FindLibVterm.cmake b/cmake/FindLibVterm.cmake index 0d773d8896..2cbd3215c5 100644 --- a/cmake/FindLibVterm.cmake +++ b/cmake/FindLibVterm.cmake @@ -4,7 +4,7 @@ # LIBVTERM_INCLUDE_DIRS - The libvterm include directories # LIBVTERM_LIBRARIES - The libraries needed to use libvterm -if(NOT LIBVTERM_USE_BUNDLED) +if(NOT USE_BUNDLED_LIBVTERM) find_package(PkgConfig) if (PKG_CONFIG_FOUND) pkg_check_modules(PC_LIBVTERM QUIET vterm) diff --git a/cmake/FindLuaJit.cmake b/cmake/FindLuaJit.cmake index e9ff53ab62..b8eda6388b 100644 --- a/cmake/FindLuaJit.cmake +++ b/cmake/FindLuaJit.cmake @@ -4,7 +4,7 @@ # LUAJIT_INCLUDE_DIRS - The luajit include directories # LUAJIT_LIBRARIES - The libraries needed to use luajit -if(NOT LUAJIT_USE_BUNDLED) +if(NOT USE_BUNDLED_LUAJIT) find_package(PkgConfig) if (PKG_CONFIG_FOUND) pkg_check_modules(PC_LUAJIT QUIET luajit) diff --git a/cmake/FindMsgpack.cmake b/cmake/FindMsgpack.cmake index 8881a34332..6716289a98 100644 --- a/cmake/FindMsgpack.cmake +++ b/cmake/FindMsgpack.cmake @@ -4,7 +4,7 @@ # MSGPACK_INCLUDE_DIRS - The msgpack include directories # MSGPACK_LIBRARIES - The libraries needed to use msgpack -if(NOT MSGPACK_USE_BUNDLED) +if(NOT USE_BUNDLED_MSGPACK) find_package(PkgConfig) if (PKG_CONFIG_FOUND) pkg_search_module(PC_MSGPACK QUIET diff --git a/cmake/FindUnibilium.cmake b/cmake/FindUnibilium.cmake index e1e0de9b7e..cf0ccda877 100644 --- a/cmake/FindUnibilium.cmake +++ b/cmake/FindUnibilium.cmake @@ -4,7 +4,7 @@ # UNIBILIUM_INCLUDE_DIRS - The unibilium include directories # UNIBILIUM_LIBRARIES - The libraries needed to use unibilium -if(NOT UNIBILIUM_USE_BUNDLED) +if(NOT USE_BUNDLED_UNIBILIUM) find_package(PkgConfig) if (PKG_CONFIG_FOUND) pkg_check_modules(PC_UNIBILIUM QUIET unibilium) From cf202b74db91133d4861c5cc8952eb0f9511dc83 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 26 Mar 2017 00:22:15 +0100 Subject: [PATCH 0101/1671] build: Do not ref `libuv` CMake target if USE_BUNDLED_LIBUV=OFF. (#6363) CMake Warning: The dependency target "libuv" of target "luv-static" does not exist. (CMP0046) Closes #6355 --- third-party/cmake/BuildLuv.cmake | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/third-party/cmake/BuildLuv.cmake b/third-party/cmake/BuildLuv.cmake index 8ba4a0b41b..2a786dd8f3 100644 --- a/third-party/cmake/BuildLuv.cmake +++ b/third-party/cmake/BuildLuv.cmake @@ -87,5 +87,9 @@ BuildLuv(PATCH_COMMAND ${LUV_PATCH_COMMAND} INSTALL_COMMAND ${LUV_INSTALL_COMMAND}) list(APPEND THIRD_PARTY_DEPS luv-static) -add_dependencies(luv-static luajit) -add_dependencies(luv-static libuv) +if(USE_BUNDLED_LUAJIT) + add_dependencies(luv-static luajit) +endif() +if(USE_BUNDLED_LIBUV) + add_dependencies(luv-static libuv) +endif() From 43a99f77a82e3de980798383ae8a4134953ece06 Mon Sep 17 00:00:00 2001 From: Yichao Zhou Date: Sun, 26 Mar 2017 04:04:20 -0700 Subject: [PATCH 0102/1671] highlight: :match should override 'list' (#6343) Closes #4946 --- src/nvim/screen.c | 11 +++++--- test/functional/ui/highlight_spec.lua | 39 ++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/nvim/screen.c b/src/nvim/screen.c index baec18dd6f..505d04ed56 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2193,7 +2193,8 @@ win_line ( int prev_c1 = 0; /* first composing char for prev_c */ int did_line_attr = 0; - bool has_bufhl = false; // this buffer has highlight matches + bool search_attr_from_match = false; // if search_attr is from :match + bool has_bufhl = false; // this buffer has highlight matches int bufhl_attr = 0; // attributes desired by bufhl bufhl_lineinfo_T bufhl_info; // bufhl data for this line @@ -2625,6 +2626,7 @@ win_line ( if ((long)shl->startcol < v) { // match at leftcol shl->attr_cur = shl->attr; search_attr = shl->attr; + search_attr_from_match = shl != &search_hl; } area_highlighting = true; } @@ -2962,6 +2964,7 @@ win_line ( /* Use attributes from match with highest priority among * 'search_hl' and the match list. */ + search_attr_from_match = false; search_attr = search_hl.attr_cur; cur = wp->w_match_head; shl_flag = FALSE; @@ -2974,8 +2977,10 @@ win_line ( shl_flag = TRUE; } else shl = &cur->hl; - if (shl->attr_cur != 0) + if (shl->attr_cur != 0) { search_attr = shl->attr_cur; + search_attr_from_match = shl != &search_hl; + } if (shl != &search_hl && cur != NULL) cur = cur->next; } @@ -3711,7 +3716,7 @@ win_line ( } // Don't override visual selection highlighting. - if (n_attr > 0 && draw_state == WL_LINE) { + if (n_attr > 0 && draw_state == WL_LINE && !search_attr_from_match) { char_attr = hl_combine_attr(char_attr, extra_attr); } diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index 945b16ef92..7a1b8c91e7 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -401,7 +401,7 @@ describe('guisp (special/undercurl)', function() end) end) -describe("'cursorline' with 'listchars'", function() +describe("'listchars' highlight", function() local screen before_each(function() @@ -644,4 +644,41 @@ describe("'cursorline' with 'listchars'", function() | ]]) end) + + it("'cursorline' with :match", function() + screen:set_default_attr_ids({ + [0] = {bold=true, foreground=Screen.colors.Blue}, + [1] = {background=Screen.colors.Grey90}, + [2] = {foreground=Screen.colors.Red}, + [3] = {foreground=Screen.colors.Green1}, + }) + execute('highlight clear ModeMsg') + execute('highlight SpecialKey guifg=#FF0000') + execute('highlight Error guifg=#00FF00') + execute('set nowrap') + feed('ia \t bc \t ') + screen:expect([[ + a bc ^ | + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + execute('set listchars=space:.,eol:¬,tab:>-,extends:>,precedes:<,trail:* list') + screen:expect([[ + a{2:.>-----.}bc{2:*>---*^*}{0:¬} | + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + execute('match Error /\\s\\+$/') + screen:expect([[ + a{2:.>-----.}bc{3:*>---*^*}{0:¬} | + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + end) end) From f9a31e98505c0f5e83dd3ba957fbd0f4b150d30c Mon Sep 17 00:00:00 2001 From: lonerover Date: Mon, 27 Mar 2017 01:04:57 +0800 Subject: [PATCH 0103/1671] vim-patch:7.4.2349 (#6368) Problem: Valgrind reports using uninitialzed memory. (Dominique Pelle) Solution: Check the length before checking for a NUL. https://github.com/vim/vim/commit/2321ca2a78286bc026fa7f407281ddbeb04114bb --- src/nvim/message.c | 2 +- src/nvim/version.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nvim/message.c b/src/nvim/message.c index cc9ef15f13..bf54284881 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1986,7 +1986,7 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen) char buf[4]; char *p; - while (*s != NUL && (maxlen < 0 || s - str < maxlen)) { + while ((maxlen < 0 || s - str < maxlen) && *s != NUL) { if (!(silent_mode && p_verbose == 0)) { // NL --> CR NL translation (for Unix, not for "--version") p = &buf[0]; diff --git a/src/nvim/version.c b/src/nvim/version.c index db3b9b51b2..a354634218 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -92,7 +92,7 @@ static int included_patches[] = { // 2352 NA // 2351 NA // 2350, - // 2349, + 2349, 2348, 2347, 2346, From e7bbd8256b8c701205389be431bbafd8743c72a9 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 4 Mar 2016 21:55:28 +0300 Subject: [PATCH 0104/1671] eval: Add luaeval function No tests yet, no documentation update, no :lua* stuff, no vim module. converter.c should also work with typval_T, not Object. Known problem: luaeval("1", {}) results in PANIC: unprotected error in call to Lua API (attempt to index a nil value) Ref #3823 --- CMakeLists.txt | 3 + scripts/generate_vim_module.lua | 38 ++ scripts/{gendispatch.lua => genmsgpack.lua} | 145 +++++- src/nvim/CMakeLists.txt | 40 +- src/nvim/eval.c | 43 ++ src/nvim/eval.lua | 1 + src/nvim/viml/executor/converter.c | 537 ++++++++++++++++++++ src/nvim/viml/executor/converter.h | 12 + src/nvim/viml/executor/executor.c | 270 ++++++++++ src/nvim/viml/executor/executor.h | 23 + src/nvim/viml/executor/vim.lua | 2 + 11 files changed, 1100 insertions(+), 14 deletions(-) create mode 100644 scripts/generate_vim_module.lua rename scripts/{gendispatch.lua => genmsgpack.lua} (75%) create mode 100644 src/nvim/viml/executor/converter.c create mode 100644 src/nvim/viml/executor/converter.h create mode 100644 src/nvim/viml/executor/executor.c create mode 100644 src/nvim/viml/executor/executor.h create mode 100644 src/nvim/viml/executor/vim.lua diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a058f2bff..b746dd460e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -310,6 +310,9 @@ include_directories(SYSTEM ${LIBUV_INCLUDE_DIRS}) find_package(Msgpack 1.0.0 REQUIRED) include_directories(SYSTEM ${MSGPACK_INCLUDE_DIRS}) +find_package(LuaJit REQUIRED) +include_directories(SYSTEM ${LUAJIT_INCLUDE_DIRS}) + if(UNIX) option(FEAT_TUI "Enable the Terminal UI" ON) else() diff --git a/scripts/generate_vim_module.lua b/scripts/generate_vim_module.lua new file mode 100644 index 0000000000..954f1c38be --- /dev/null +++ b/scripts/generate_vim_module.lua @@ -0,0 +1,38 @@ +assert(#arg == 2) + +module_file = arg[1] +target_file = arg[2] + +module = io.open(module_file, 'r') +target = io.open(target_file, 'w') + +target:write('#include \n\n') +target:write('static const uint8_t vim_module[] = {\n') + +num_bytes = 0 +MAX_NUM_BYTES = 15 -- 78 / 5: maximum number of bytes on one line +target:write(' ') + +increase_num_bytes = function() + num_bytes = num_bytes + 1 + if num_bytes == MAX_NUM_BYTES then + num_bytes = 0 + target:write('\n ') + end +end + +for line in module:lines() do + for i = 1,string.len(line) do + byte = string.byte(line, i) + assert(byte ~= 0) + target:write(string.format(' %3u,', byte)) + increase_num_bytes() + end + target:write(string.format(' %3u,', string.byte('\n', 1))) + increase_num_bytes() +end + +target:write(' 0};\n') + +module:close() +target:close() diff --git a/scripts/gendispatch.lua b/scripts/genmsgpack.lua similarity index 75% rename from scripts/gendispatch.lua rename to scripts/genmsgpack.lua index 45a4f4a4de..dd3caab5e4 100644 --- a/scripts/gendispatch.lua +++ b/scripts/genmsgpack.lua @@ -45,7 +45,16 @@ c_proto = Ct( grammar = Ct((c_proto + c_comment + c_preproc + ws) ^ 1) -- we need at least 4 arguments since the last two are output files -assert(#arg >= 3) +if arg[1] == '--help' then + print('Usage: genmsgpack.lua args') + print('Args: 1: source directory') + print(' 2: dispatch output file (dispatch_wrappers.generated.h)') + print(' 3: functions metadata output file (funcs_metadata.generated.h)') + print(' 4: API metadata output file (api_metadata.mpack)') + print(' 5: lua C bindings output file (msgpack_lua_c_bindings.generated.c)') + print(' rest: C files where API functions are defined') +end +assert(#arg >= 4) functions = {} local nvimsrcdir = arg[1] @@ -56,17 +65,18 @@ package.path = nvimsrcdir .. '/?.lua;' .. package.path headers = {} -- output h file with generated dispatch functions -dispatch_outputf = arg[#arg-2] +dispatch_outputf = arg[2] -- output h file with packed metadata -funcs_metadata_outputf = arg[#arg-1] +funcs_metadata_outputf = arg[3] -- output metadata mpack file, for use by other build scripts -mpack_outputf = arg[#arg] +mpack_outputf = arg[4] +lua_c_bindings_outputf = arg[5] -- set of function names, used to detect duplicates function_names = {} -- read each input file, parse and append to the api metadata -for i = 2, #arg - 3 do +for i = 6, #arg do local full_path = arg[i] local parts = {} for part in string.gmatch(full_path, '[^/]+') do @@ -332,3 +342,128 @@ output:close() mpack_output = io.open(mpack_outputf, 'wb') mpack_output:write(mpack.pack(functions)) mpack_output:close() + +local function include_headers(output, headers) + for i = 1, #headers do + if headers[i]:sub(-12) ~= '.generated.h' then + output:write('\n#include "nvim/'..headers[i]..'"') + end + end +end + +local function write_shifted_output(output, str) + str = str:gsub('\n ', '\n') + str = str:gsub('^ ', '') + str = str:gsub(' +$', '') + output:write(str) +end + +-- start building lua output +output = io.open(lua_c_bindings_outputf, 'wb') + +output:write([[ +#include +#include +#include + +#include "nvim/func_attr.h" +#include "nvim/api/private/defs.h" +#include "nvim/viml/executor/converter.h" +]]) +include_headers(output, headers) +output:write('\n') + +lua_c_functions = {} + +local function process_function(fn) + lua_c_function_name = ('nlua_msgpack_%s'):format(fn.name) + write_shifted_output(output, string.format([[ + + static int %s(lua_State *lstate) + { + Error err = {.set = false}; + ]], lua_c_function_name)) + lua_c_functions[#lua_c_functions + 1] = { + binding=lua_c_function_name, + api=fn.name + } + cparams = '' + for j, param in ipairs(fn.parameters) do + cparam = string.format('arg%u', j) + if param[1]:match('^ArrayOf') then + param_type = 'Array' + else + param_type = param[1] + end + write_shifted_output(output, string.format([[ + %s %s = nlua_pop_%s(lstate, &err); + if (err.set) { + lua_pushstring(lstate, err.msg); + return lua_error(lstate); + } + ]], param[1], cparam, param_type)) + cparams = cparams .. cparam .. ', ' + end + if fn.receives_channel_id then + cparams = 'INTERNAL_CALL, ' .. cparams + end + if fn.can_fail then + cparams = cparams .. '&err' + else + cparams = cparams:gsub(', $', '') + end + local name = fn.impl_name or fn.name + if fn.return_type ~= 'void' then + if fn.return_type:match('^ArrayOf') then + return_type = 'Array' + else + return_type = fn.return_type + end + write_shifted_output(output, string.format([[ + %s ret = %s(%s); + if (err.set) { + lua_pushstring(lstate, err.msg); + return lua_error(lstate); + } + nlua_push_%s(lstate, ret); + return 1; + ]], fn.return_type, name, cparams, return_type)) + else + write_shifted_output(output, string.format([[ + %s(%s); + if (err.set) { + lua_pushstring(lstate, err.msg); + return lua_error(lstate); + } + return 0; + ]], name, cparams)) + end + write_shifted_output(output, [[ + } + ]]) +end + +for _, fn in ipairs(functions) do + if not fn.noeval then + process_function(fn) + end +end + +output:write(string.format([[ +void nlua_add_api_functions(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL +{ + lua_createtable(lstate, 0, %u); +]], #lua_c_functions)) +for _, func in ipairs(lua_c_functions) do + output:write(string.format([[ + lua_pushcfunction(lstate, &%s); + lua_setfield(lstate, -2, "%s"); + ]], func.binding, func.api)) +end +output:write([[ + lua_setfield(lstate, -2, "api"); +} +]]) + +output:close() diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 22cf1f3a3d..a47a8e49c7 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -11,11 +11,10 @@ endif() endif() set(GENERATED_DIR ${PROJECT_BINARY_DIR}/src/nvim/auto) -set(DISPATCH_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gendispatch.lua) -file(GLOB API_HEADERS api/*.h) -file(GLOB MSGPACK_RPC_HEADERS msgpack_rpc/*.h) +set(MSGPACK_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genmsgpack.lua) set(API_METADATA ${PROJECT_BINARY_DIR}/api_metadata.mpack) set(FUNCS_DATA ${PROJECT_BINARY_DIR}/funcs_data.mpack) +set(MSGPACK_LUA_C_BINDINGS ${GENERATED_DIR}/msgpack_lua_c_bindings.generated.c) set(HEADER_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gendeclarations.lua) set(GENERATED_INCLUDES_DIR ${PROJECT_BINARY_DIR}/include) set(GENERATED_API_DISPATCH ${GENERATED_DIR}/api/private/dispatch_wrappers.generated.h) @@ -37,8 +36,14 @@ set(EVAL_DEFS_FILE ${PROJECT_SOURCE_DIR}/src/nvim/eval.lua) set(OPTIONS_LIST_FILE ${PROJECT_SOURCE_DIR}/src/nvim/options.lua) set(UNICODE_TABLES_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genunicodetables.lua) set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/unicode) -file(GLOB UNICODE_FILES ${UNICODE_DIR}/*.txt) set(GENERATED_UNICODE_TABLES ${GENERATED_DIR}/unicode_tables.generated.h) +set(VIM_MODULE_FILE ${GENERATED_DIR}/viml/executor/vim_module.generated.h) +set(VIM_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/src/nvim/viml/executor/vim.lua) +set(VIM_MODULE_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/generate_vim_module.lua) + +file(GLOB API_HEADERS api/*.h) +file(GLOB MSGPACK_RPC_HEADERS msgpack_rpc/*.h) +file(GLOB UNICODE_FILES ${UNICODE_DIR}/*.txt) include_directories(${GENERATED_DIR}) include_directories(${CACHED_GENERATED_DIR}) @@ -57,6 +62,8 @@ foreach(subdir tui event eval + viml + viml/executor ) if(${subdir} MATCHES "tui" AND NOT FEAT_TUI) continue() @@ -198,18 +205,30 @@ add_custom_command(OUTPUT ${GENERATED_UNICODE_TABLES} ${UNICODE_FILES} ) -add_custom_command(OUTPUT ${GENERATED_API_DISPATCH} ${GENERATED_FUNCS_METADATA} - ${API_METADATA} - COMMAND ${LUA_PRG} ${DISPATCH_GENERATOR} ${CMAKE_CURRENT_LIST_DIR} - ${API_HEADERS} ${GENERATED_API_DISPATCH} +add_custom_command( + OUTPUT ${GENERATED_API_DISPATCH} ${GENERATED_FUNCS_METADATA} + ${API_METADATA} ${MSGPACK_LUA_C_BINDINGS} + COMMAND ${LUA_PRG} ${MSGPACK_GENERATOR} ${CMAKE_CURRENT_LIST_DIR} + ${GENERATED_API_DISPATCH} ${GENERATED_FUNCS_METADATA} ${API_METADATA} + ${MSGPACK_LUA_C_BINDINGS} + ${API_HEADERS} DEPENDS ${API_HEADERS} ${MSGPACK_RPC_HEADERS} - ${DISPATCH_GENERATOR} + ${MSGPACK_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/api/dispatch_deprecated.lua ) +add_custom_command( + OUTPUT ${VIM_MODULE_FILE} + COMMAND ${LUA_PRG} ${VIM_MODULE_GENERATOR} ${VIM_MODULE_SOURCE} + ${VIM_MODULE_FILE} + DEPENDS + ${VIM_MODULE_GENERATOR} + ${VIM_MODULE_SOURCE} +) + list(APPEND NEOVIM_GENERATED_SOURCES "${PROJECT_BINARY_DIR}/config/auto/pathdef.c" "${GENERATED_API_DISPATCH}" @@ -219,6 +238,8 @@ list(APPEND NEOVIM_GENERATED_SOURCES "${GENERATED_EVENTS_NAMES_MAP}" "${GENERATED_OPTIONS}" "${GENERATED_UNICODE_TABLES}" + "${MSGPACK_LUA_C_BINDINGS}" + "${VIM_MODULE_FILE}" ) add_custom_command(OUTPUT ${GENERATED_EX_CMDS_ENUM} ${GENERATED_EX_CMDS_DEFS} @@ -270,6 +291,7 @@ list(APPEND NVIM_LINK_LIBRARIES ${LIBTERMKEY_LIBRARIES} ${UNIBILIUM_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} + ${LUAJIT_LIBRARIES} ) if(UNIX) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d49fcbf17e..c9141fbcbf 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -95,6 +95,7 @@ #include "nvim/lib/khash.h" #include "nvim/lib/queue.h" #include "nvim/eval/typval_encode.h" +#include "nvim/viml/executor/executor.h" #define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */ @@ -13376,6 +13377,48 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) } } +/// luaeval() function implementation +static void f_luaeval(typval_T *argvars, typval_T *rettv, FunPtr fptr) + FUNC_ATTR_NONNULL_ALL +{ + char *const str = (char *) get_tv_string(&argvars[0]); + if (str == NULL) { + return; + } + + Object arg; + if (argvars[1].v_type == VAR_UNKNOWN) { + arg = NIL; + } else { + arg = vim_to_object(&argvars[1]); + } + + // TODO(ZyX-I): Create function which converts lua objects directly to VimL + // objects, not to API objects. + Error err; + String err_str; + Object ret = executor_eval_lua(cstr_as_string(str), arg, &err, &err_str); + if (err.set) { + if (err_str.size) { + EMSG3(_("E971: Failed to eval lua string: %s (%s)"), err.msg, + err_str.data); + } else { + EMSG2(_("E971: Failed to eval lua string: %s"), err.msg); + } + } + + api_free_string(err_str); + + if (!err.set) { + if (!object_to_vim(ret, rettv, &err)) { + EMSG2(_("E972: Failed to convert resulting API object to VimL: %s"), + err.msg); + } + } + + api_free_object(ret); +} + /* * "map()" function */ diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index e3c5981b32..9db90ce05d 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -193,6 +193,7 @@ return { localtime={}, log={args=1, func="float_op_wrapper", data="&log"}, log10={args=1, func="float_op_wrapper", data="&log10"}, + luaeval={args={1, 2}}, map={args=2}, maparg={args={1, 4}}, mapcheck={args={1, 3}}, diff --git a/src/nvim/viml/executor/converter.c b/src/nvim/viml/executor/converter.c new file mode 100644 index 0000000000..2105beb08a --- /dev/null +++ b/src/nvim/viml/executor/converter.c @@ -0,0 +1,537 @@ +#include +#include +#include +#include + +#include "nvim/api/private/defs.h" +#include "nvim/api/private/helpers.h" +#include "nvim/func_attr.h" +#include "nvim/memory.h" +#include "nvim/assert.h" + +#include "nvim/viml/executor/converter.h" +#include "nvim/viml/executor/executor.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "viml/executor/converter.c.generated.h" +#endif + +#define NLUA_PUSH_IDX(lstate, type, idx) \ + do { \ + STATIC_ASSERT(sizeof(type) <= sizeof(lua_Number), \ + "Number sizes do not match"); \ + const type src = idx; \ + lua_Number tgt; \ + memset(&tgt, 0, sizeof(tgt)); \ + memcpy(&tgt, &src, sizeof(src)); \ + lua_pushnumber(lstate, tgt); \ + } while (0) + +#define NLUA_POP_IDX(lstate, type, stack_idx, idx) \ + do { \ + STATIC_ASSERT(sizeof(type) <= sizeof(lua_Number), \ + "Number sizes do not match"); \ + const lua_Number src = lua_tonumber(lstate, stack_idx); \ + type tgt; \ + memcpy(&tgt, &src, sizeof(tgt)); \ + idx = tgt; \ + } while (0) + +/// Push value which is a type index +/// +/// Used for all “typed” tables: i.e. for all tables which represent VimL +/// values. +static inline void nlua_push_type_idx(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL +{ + lua_pushboolean(lstate, true); +} + +/// Push value which is a locks index +/// +/// Used for containers tables. +static inline void nlua_push_locks_idx(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL +{ + lua_pushboolean(lstate, false); +} + +/// Push value which is a value index +/// +/// Used for tables which represent scalar values, like float value. +static inline void nlua_push_val_idx(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL +{ + lua_pushnumber(lstate, (lua_Number) 0); +} + +/// Push type +/// +/// Type is a value in vim.types table. +/// +/// @param[out] lstate Lua state. +/// @param[in] type Type to push (key in vim.types table). +static inline void nlua_push_type(lua_State *lstate, const char *const type) +{ + lua_getglobal(lstate, "vim"); + lua_getfield(lstate, -1, "types"); + lua_remove(lstate, -2); + lua_getfield(lstate, -1, type); + lua_remove(lstate, -2); +} + +/// Create lua table which has an entry that determines its VimL type +/// +/// @param[out] lstate Lua state. +/// @param[in] narr Number of “array” entries to be populated later. +/// @param[in] nrec Number of “dictionary” entries to be populated later. +/// @param[in] type Type of the table. +static inline void nlua_create_typed_table(lua_State *lstate, + const size_t narr, + const size_t nrec, + const char *const type) + FUNC_ATTR_NONNULL_ALL +{ + lua_createtable(lstate, (int) narr, (int) (1 + nrec)); + nlua_push_type_idx(lstate); + nlua_push_type(lstate, type); + lua_rawset(lstate, -3); +} + + +/// Convert given String to lua string +/// +/// Leaves converted string on top of the stack. +void nlua_push_String(lua_State *lstate, const String s) + FUNC_ATTR_NONNULL_ALL +{ + lua_pushlstring(lstate, s.data, s.size); +} + +/// Convert given Integer to lua number +/// +/// Leaves converted number on top of the stack. +void nlua_push_Integer(lua_State *lstate, const Integer n) + FUNC_ATTR_NONNULL_ALL +{ + lua_pushnumber(lstate, (lua_Number) n); +} + +/// Convert given Float to lua table +/// +/// Leaves converted table on top of the stack. +void nlua_push_Float(lua_State *lstate, const Float f) + FUNC_ATTR_NONNULL_ALL +{ + nlua_create_typed_table(lstate, 0, 1, "float"); + nlua_push_val_idx(lstate); + lua_pushnumber(lstate, (lua_Number) f); + lua_rawset(lstate, -3); +} + +/// Convert given Float to lua boolean +/// +/// Leaves converted value on top of the stack. +void nlua_push_Boolean(lua_State *lstate, const Boolean b) + FUNC_ATTR_NONNULL_ALL +{ + lua_pushboolean(lstate, b); +} + +static inline void nlua_add_locks_table(lua_State *lstate) +{ + nlua_push_locks_idx(lstate); + lua_newtable(lstate); + lua_rawset(lstate, -3); +} + +/// Convert given Dictionary to lua table +/// +/// Leaves converted table on top of the stack. +void nlua_push_Dictionary(lua_State *lstate, const Dictionary dict) + FUNC_ATTR_NONNULL_ALL +{ + nlua_create_typed_table(lstate, 0, 1 + dict.size, "dict"); + nlua_add_locks_table(lstate); + for (size_t i = 0; i < dict.size; i++) { + nlua_push_String(lstate, dict.items[i].key); + nlua_push_Object(lstate, dict.items[i].value); + lua_rawset(lstate, -3); + } +} + +/// Convert given Array to lua table +/// +/// Leaves converted table on top of the stack. +void nlua_push_Array(lua_State *lstate, const Array array) + FUNC_ATTR_NONNULL_ALL +{ + nlua_create_typed_table(lstate, array.size, 1, "float"); + nlua_add_locks_table(lstate); + for (size_t i = 0; i < array.size; i++) { + nlua_push_Object(lstate, array.items[i]); + lua_rawseti(lstate, -3, (int) i + 1); + } +} + +#define GENERATE_INDEX_FUNCTION(type) \ +void nlua_push_##type(lua_State *lstate, const type item) \ + FUNC_ATTR_NONNULL_ALL \ +{ \ + NLUA_PUSH_IDX(lstate, type, item); \ +} + +GENERATE_INDEX_FUNCTION(Buffer) +GENERATE_INDEX_FUNCTION(Window) +GENERATE_INDEX_FUNCTION(Tabpage) + +#undef GENERATE_INDEX_FUNCTION + +/// Convert given Object to lua value +/// +/// Leaves converted value on top of the stack. +void nlua_push_Object(lua_State *lstate, const Object obj) + FUNC_ATTR_NONNULL_ALL +{ + switch (obj.type) { + case kObjectTypeNil: { + lua_pushnil(lstate); + break; + } +#define ADD_TYPE(type, data_key) \ + case kObjectType##type: { \ + nlua_push_##type(lstate, obj.data.data_key); \ + break; \ + } + ADD_TYPE(Boolean, boolean) + ADD_TYPE(Integer, integer) + ADD_TYPE(Float, floating) + ADD_TYPE(String, string) + ADD_TYPE(Array, array) + ADD_TYPE(Dictionary, dictionary) +#undef ADD_TYPE +#define ADD_REMOTE_TYPE(type) \ + case kObjectType##type: { \ + nlua_push_##type(lstate, (type)obj.data.integer); \ + break; \ + } + ADD_REMOTE_TYPE(Buffer) + ADD_REMOTE_TYPE(Window) + ADD_REMOTE_TYPE(Tabpage) +#undef ADD_REMOTE_TYPE + } +} + + +/// Convert lua value to string +/// +/// Always pops one value from the stack. +String nlua_pop_String(lua_State *lstate, Error *err) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + String ret; + + ret.data = (char *) lua_tolstring(lstate, -1, &(ret.size)); + + if (ret.data == NULL) { + lua_pop(lstate, 1); + set_api_error("Expected lua string", err); + return (String) { .size = 0, .data = NULL }; + } + + ret.data = xmemdupz(ret.data, ret.size); + lua_pop(lstate, 1); + + return ret; +} + +/// Convert lua value to integer +/// +/// Always pops one value from the stack. +Integer nlua_pop_Integer(lua_State *lstate, Error *err) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + Integer ret = 0; + + if (!lua_isnumber(lstate, -1)) { + lua_pop(lstate, 1); + set_api_error("Expected lua integer", err); + return ret; + } + ret = (Integer) lua_tonumber(lstate, -1); + lua_pop(lstate, 1); + + return ret; +} + +/// Convert lua value to boolean +/// +/// Always pops one value from the stack. +Boolean nlua_pop_Boolean(lua_State *lstate, Error *err) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + Boolean ret = lua_toboolean(lstate, -1); + lua_pop(lstate, 1); + return ret; +} + +static inline bool nlua_check_type(lua_State *lstate, Error *err, + const char *const type) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (lua_type(lstate, -1) != LUA_TTABLE) { + set_api_error("Expected lua table", err); + return true; + } + + nlua_push_type_idx(lstate); + lua_rawget(lstate, -2); + nlua_push_type(lstate, type); + if (!lua_rawequal(lstate, -2, -1)) { + lua_pop(lstate, 2); + set_api_error("Expected lua table with float type", err); + return true; + } + lua_pop(lstate, 2); + + return false; +} + +/// Convert lua table to float +/// +/// Always pops one value from the stack. +Float nlua_pop_Float(lua_State *lstate, Error *err) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + Float ret = 0; + + if (nlua_check_type(lstate, err, "float")) { + lua_pop(lstate, 1); + return 0; + } + + nlua_push_val_idx(lstate); + lua_rawget(lstate, -2); + + if (!lua_isnumber(lstate, -1)) { + lua_pop(lstate, 2); + set_api_error("Value field should be lua number", err); + return ret; + } + ret = lua_tonumber(lstate, -1); + lua_pop(lstate, 2); + + return ret; +} + +/// Convert lua table to array +/// +/// Always pops one value from the stack. +Array nlua_pop_Array(lua_State *lstate, Error *err) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + Array ret = { .size = 0, .items = NULL }; + + if (nlua_check_type(lstate, err, "list")) { + lua_pop(lstate, 1); + return ret; + } + + for (int i = 1; ; i++, ret.size++) { + lua_rawgeti(lstate, -1, i); + + if (lua_isnil(lstate, -1)) { + lua_pop(lstate, 1); + break; + } + lua_pop(lstate, 1); + } + + if (ret.size == 0) { + lua_pop(lstate, 1); + return ret; + } + + ret.items = xcalloc(ret.size, sizeof(*ret.items)); + for (size_t i = 1; i <= ret.size; i++) { + Object val; + + lua_rawgeti(lstate, -1, (int) i); + + val = nlua_pop_Object(lstate, err); + if (err->set) { + ret.size = i; + lua_pop(lstate, 1); + api_free_array(ret); + return (Array) { .size = 0, .items = NULL }; + } + ret.items[i - 1] = val; + } + lua_pop(lstate, 1); + + return ret; +} + +/// Convert lua table to dictionary +/// +/// Always pops one value from the stack. Does not check whether +/// `vim.is_dict(table[type_idx])` or whether topmost value on the stack is +/// a table. +Dictionary nlua_pop_Dictionary_unchecked(lua_State *lstate, Error *err) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + Dictionary ret = { .size = 0, .items = NULL }; + + lua_pushnil(lstate); + + while (lua_next(lstate, -2)) { + if (lua_type(lstate, -2) == LUA_TSTRING) { + ret.size++; + } + lua_pop(lstate, 1); + } + + if (ret.size == 0) { + lua_pop(lstate, 1); + return ret; + } + ret.items = xcalloc(ret.size, sizeof(*ret.items)); + + lua_pushnil(lstate); + for (size_t i = 0; lua_next(lstate, -2);) { + // stack: dict, key, value + + if (lua_type(lstate, -2) == LUA_TSTRING) { + lua_pushvalue(lstate, -2); + // stack: dict, key, value, key + + ret.items[i].key = nlua_pop_String(lstate, err); + // stack: dict, key, value + + if (!err->set) { + ret.items[i].value = nlua_pop_Object(lstate, err); + // stack: dict, key + } else { + lua_pop(lstate, 1); + // stack: dict, key + } + + if (err->set) { + ret.size = i; + api_free_dictionary(ret); + lua_pop(lstate, 2); + // stack: + return (Dictionary) { .size = 0, .items = NULL }; + } + i++; + } else { + lua_pop(lstate, 1); + // stack: dict, key + } + } + lua_pop(lstate, 1); + + return ret; +} + +/// Convert lua table to dictionary +/// +/// Always pops one value from the stack. +Dictionary nlua_pop_Dictionary(lua_State *lstate, Error *err) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (nlua_check_type(lstate, err, "dict")) { + lua_pop(lstate, 1); + return (Dictionary) { .size = 0, .items = NULL }; + } + + return nlua_pop_Dictionary_unchecked(lstate, err); +} + +/// Convert lua table to object +/// +/// Always pops one value from the stack. +Object nlua_pop_Object(lua_State *lstate, Error *err) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + Object ret = { .type = kObjectTypeNil }; + + switch (lua_type(lstate, -1)) { + case LUA_TNIL: { + ret.type = kObjectTypeNil; + lua_pop(lstate, 1); + break; + } + case LUA_TSTRING: { + ret.type = kObjectTypeString; + ret.data.string = nlua_pop_String(lstate, err); + break; + } + case LUA_TNUMBER: { + ret.type = kObjectTypeInteger; + ret.data.integer = nlua_pop_Integer(lstate, err); + break; + } + case LUA_TBOOLEAN: { + ret.type = kObjectTypeBoolean; + ret.data.boolean = nlua_pop_Boolean(lstate, err); + break; + } + case LUA_TTABLE: { + lua_getglobal(lstate, "vim"); + // stack: obj, vim +#define CHECK_TYPE(Type, key, vim_type) \ + lua_getfield(lstate, -1, "is_" #vim_type); \ + /* stack: obj, vim, checker */ \ + lua_pushvalue(lstate, -3); \ + /* stack: obj, vim, checker, obj */ \ + lua_call(lstate, 1, 1); \ + /* stack: obj, vim, result */ \ + if (lua_toboolean(lstate, -1)) { \ + lua_pop(lstate, 2); \ + /* stack: obj */ \ + ret.type = kObjectType##Type; \ + ret.data.key = nlua_pop_##Type(lstate, err); \ + /* stack: */ \ + break; \ + } \ + lua_pop(lstate, 1); \ + // stack: obj, vim + CHECK_TYPE(Float, floating, float) + CHECK_TYPE(Array, array, list) + CHECK_TYPE(Dictionary, dictionary, dict) +#undef CHECK_TYPE + lua_pop(lstate, 1); + // stack: obj + ret.type = kObjectTypeDictionary; + ret.data.dictionary = nlua_pop_Dictionary_unchecked(lstate, err); + break; + } + default: { + lua_pop(lstate, 1); + set_api_error("Cannot convert given lua type", err); + break; + } + } + if (err->set) { + ret.type = kObjectTypeNil; + } + + return ret; +} + +#define GENERATE_INDEX_FUNCTION(type) \ +type nlua_pop_##type(lua_State *lstate, Error *err) \ + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \ +{ \ + type ret; \ + NLUA_POP_IDX(lstate, type, -1, ret); \ + lua_pop(lstate, 1); \ + return ret; \ +} + +GENERATE_INDEX_FUNCTION(Buffer) +GENERATE_INDEX_FUNCTION(Window) +GENERATE_INDEX_FUNCTION(Tabpage) + +#undef GENERATE_INDEX_FUNCTION diff --git a/src/nvim/viml/executor/converter.h b/src/nvim/viml/executor/converter.h new file mode 100644 index 0000000000..e11d0cef19 --- /dev/null +++ b/src/nvim/viml/executor/converter.h @@ -0,0 +1,12 @@ +#ifndef NVIM_VIML_EXECUTOR_CONVERTER_H +#define NVIM_VIML_EXECUTOR_CONVERTER_H + +#include +#include +#include "nvim/api/private/defs.h" +#include "nvim/func_attr.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "viml/executor/converter.h.generated.h" +#endif +#endif // NVIM_VIML_EXECUTOR_CONVERTER_H diff --git a/src/nvim/viml/executor/executor.c b/src/nvim/viml/executor/executor.c new file mode 100644 index 0000000000..6f1e847649 --- /dev/null +++ b/src/nvim/viml/executor/executor.c @@ -0,0 +1,270 @@ +#include +#include +#include + +#include "nvim/misc1.h" +#include "nvim/getchar.h" +#include "nvim/garray.h" +#include "nvim/func_attr.h" +#include "nvim/api/private/defs.h" +#include "nvim/api/vim.h" +#include "nvim/vim.h" +#include "nvim/message.h" + +#include "nvim/viml/executor/executor.h" +#include "nvim/viml/executor/converter.h" + +typedef struct { + Error err; + String lua_err_str; +} LuaError; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "viml/executor/vim_module.generated.h" +# include "viml/executor/executor.c.generated.h" +#endif + +/// Name of the run code for use in messages +#define NLUA_EVAL_NAME "" + +/// Call C function which does not expect any arguments +/// +/// @param function Called function +/// @param numret Number of returned arguments +#define NLUA_CALL_C_FUNCTION_0(lstate, function, numret) \ + do { \ + lua_pushcfunction(lstate, &function); \ + lua_call(lstate, 0, numret); \ + } while (0) +/// Call C function which expects four arguments +/// +/// @param function Called function +/// @param numret Number of returned arguments +/// @param a… Supplied argument (should be a void* pointer) +#define NLUA_CALL_C_FUNCTION_3(lstate, function, numret, a1, a2, a3) \ + do { \ + lua_pushcfunction(lstate, &function); \ + lua_pushlightuserdata(lstate, a1); \ + lua_pushlightuserdata(lstate, a2); \ + lua_pushlightuserdata(lstate, a3); \ + lua_call(lstate, 3, numret); \ + } while (0) +/// Call C function which expects five arguments +/// +/// @param function Called function +/// @param numret Number of returned arguments +/// @param a… Supplied argument (should be a void* pointer) +#define NLUA_CALL_C_FUNCTION_4(lstate, function, numret, a1, a2, a3, a4) \ + do { \ + lua_pushcfunction(lstate, &function); \ + lua_pushlightuserdata(lstate, a1); \ + lua_pushlightuserdata(lstate, a2); \ + lua_pushlightuserdata(lstate, a3); \ + lua_pushlightuserdata(lstate, a4); \ + lua_call(lstate, 4, numret); \ + } while (0) + +static void set_lua_error(lua_State *lstate, LuaError *lerr) + FUNC_ATTR_NONNULL_ALL +{ + const char *const str = lua_tolstring(lstate, -1, &lerr->lua_err_str.size); + lerr->lua_err_str.data = xmemdupz(str, lerr->lua_err_str.size); + lua_pop(lstate, 1); + + // FIXME? More specific error? + set_api_error("Error while executing lua code", &lerr->err); +} + +/// Compare two strings, ignoring case +/// +/// Expects two values on the stack: compared strings. Returns one of the +/// following numbers: 0, -1 or 1. +/// +/// Does no error handling: never call it with non-string or with some arguments +/// omitted. +static int nlua_stricmp(lua_State *lstate) FUNC_ATTR_NONNULL_ALL +{ + const char *s1 = luaL_checklstring(lstate, 1, NULL); + const char *s2 = luaL_checklstring(lstate, 2, NULL); + const int ret = STRICMP(s1, s2); + lua_pop(lstate, 2); + lua_pushnumber(lstate, (lua_Number) ((ret > 0) - (ret < 0))); + return 1; +} + +/// Evaluate lua string +/// +/// Expects three values on the stack: string to evaluate, pointer to the +/// location where result is saved, pointer to the location where error is +/// saved. Always returns nothing (from the lua point of view). +static int nlua_exec_lua_string(lua_State *lstate) FUNC_ATTR_NONNULL_ALL +{ + String *str = (String *) lua_touserdata(lstate, 1); + Object *obj = (Object *) lua_touserdata(lstate, 2); + LuaError *lerr = (LuaError *) lua_touserdata(lstate, 3); + lua_pop(lstate, 3); + + if (luaL_loadbuffer(lstate, str->data, str->size, NLUA_EVAL_NAME)) { + set_lua_error(lstate, lerr); + return 0; + } + if (lua_pcall(lstate, 0, 1, 0)) { + set_lua_error(lstate, lerr); + return 0; + } + *obj = nlua_pop_Object(lstate, &lerr->err); + return 0; +} + +/// Initialize lua interpreter state +/// +/// Called by lua interpreter itself to initialize state. +static int nlua_state_init(lua_State *lstate) FUNC_ATTR_NONNULL_ALL +{ + lua_pushcfunction(lstate, &nlua_stricmp); + lua_setglobal(lstate, "stricmp"); + if (luaL_dostring(lstate, (char *) &vim_module[0])) { + LuaError lerr; + set_lua_error(lstate, &lerr); + return 1; + } + nlua_add_api_functions(lstate); + lua_setglobal(lstate, "vim"); + return 0; +} + +/// Initialize lua interpreter +/// +/// Crashes NeoVim if initialization fails. Should be called once per lua +/// interpreter instance. +static lua_State *init_lua(void) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT +{ + lua_State *lstate = luaL_newstate(); + if (lstate == NULL) { + EMSG(_("E970: Failed to initialize lua interpreter")); + preserve_exit(); + } + luaL_openlibs(lstate); + NLUA_CALL_C_FUNCTION_0(lstate, nlua_state_init, 0); + return lstate; +} + +static Object exec_lua_string(lua_State *lstate, String str, LuaError *lerr) + FUNC_ATTR_NONNULL_ALL +{ + Object ret = { kObjectTypeNil, { false } }; + NLUA_CALL_C_FUNCTION_3(lstate, nlua_exec_lua_string, 0, &str, &ret, lerr); + return ret; +} + +static lua_State *global_lstate = NULL; + +/// Execute lua string +/// +/// Used for :lua. +/// +/// @param[in] str String to execute. +/// @param[out] err Location where error will be saved. +/// @param[out] err_str Location where lua error string will be saved, if any. +/// +/// @return Result of the execution. +Object executor_exec_lua(String str, Error *err, String *err_str) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (global_lstate == NULL) { + global_lstate = init_lua(); + } + + LuaError lerr = { + .err = { .set = false }, + .lua_err_str = STRING_INIT, + }; + + Object ret = exec_lua_string(global_lstate, str, &lerr); + + *err = lerr.err; + *err_str = lerr.lua_err_str; + + return ret; +} + +/// Evaluate lua string +/// +/// Used for luaeval(). Expects three values on the stack: +/// +/// 1. String to evaluate. +/// 2. _A value. +/// 3. Pointer to location where result is saved. +/// 4. Pointer to location where error will be saved. +/// +/// @param[in,out] lstate Lua interpreter state. +static int nlua_eval_lua_string(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL +{ + String *str = (String *) lua_touserdata(lstate, 1); + Object *arg = (Object *) lua_touserdata(lstate, 2); + Object *ret = (Object *) lua_touserdata(lstate, 3); + LuaError *lerr = (LuaError *) lua_touserdata(lstate, 4); + + garray_T str_ga; + ga_init(&str_ga, 1, 80); +#define EVALHEADER "local _A=select(1,...) return " + ga_concat_len(&str_ga, EVALHEADER, sizeof(EVALHEADER) - 1); +#undef EVALHEADER + ga_concat_len(&str_ga, str->data, str->size); + if (luaL_loadbuffer(lstate, str_ga.ga_data, (size_t) str_ga.ga_len, + NLUA_EVAL_NAME)) { + set_lua_error(lstate, lerr); + return 0; + } + ga_clear(&str_ga); + + nlua_push_Object(lstate, *arg); + if (lua_pcall(lstate, 1, 1, 0)) { + set_lua_error(lstate, lerr); + return 0; + } + *ret = nlua_pop_Object(lstate, &lerr->err); + + return 0; +} + +static Object eval_lua_string(lua_State *lstate, String str, Object arg, + LuaError *lerr) + FUNC_ATTR_NONNULL_ALL +{ + Object ret = { kObjectTypeNil, { false } }; + NLUA_CALL_C_FUNCTION_4(lstate, nlua_eval_lua_string, 0, + &str, &arg, &ret, lerr); + return ret; +} + +/// Evaluate lua string +/// +/// Used for luaeval(). +/// +/// @param[in] str String to execute. +/// @param[out] err Location where error will be saved. +/// @param[out] err_str Location where lua error string will be saved, if any. +/// +/// @return Result of the execution. +Object executor_eval_lua(String str, Object arg, Error *err, String *err_str) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (global_lstate == NULL) { + global_lstate = init_lua(); + } + + LuaError lerr = { + .err = { .set = false }, + .lua_err_str = STRING_INIT, + }; + + Object ret = eval_lua_string(global_lstate, str, arg, &lerr); + + *err = lerr.err; + *err_str = lerr.lua_err_str; + + return ret; +} diff --git a/src/nvim/viml/executor/executor.h b/src/nvim/viml/executor/executor.h new file mode 100644 index 0000000000..85cb3550e7 --- /dev/null +++ b/src/nvim/viml/executor/executor.h @@ -0,0 +1,23 @@ +#ifndef NVIM_VIML_EXECUTOR_EXECUTOR_H +#define NVIM_VIML_EXECUTOR_EXECUTOR_H + +#include + +#include "nvim/api/private/defs.h" +#include "nvim/func_attr.h" + +// Generated by msgpack-gen.lua +void nlua_add_api_functions(lua_State *lstate) REAL_FATTR_NONNULL_ALL; + +#define set_api_error(s, err) \ + do { \ + Error *err_ = (err); \ + err_->type = kErrorTypeException; \ + err_->set = true; \ + memcpy(&err_->msg[0], s, sizeof(s)); \ + } while (0) + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "viml/executor/executor.h.generated.h" +#endif +#endif // NVIM_VIML_EXECUTOR_EXECUTOR_H diff --git a/src/nvim/viml/executor/vim.lua b/src/nvim/viml/executor/vim.lua new file mode 100644 index 0000000000..8d1c5bdf4f --- /dev/null +++ b/src/nvim/viml/executor/vim.lua @@ -0,0 +1,2 @@ +-- TODO(ZyX-I): Create compatibility layer. +return {} From f551df17f33e7f38b7e5693eb923118ca1542d27 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Jul 2016 08:03:14 +0300 Subject: [PATCH 0105/1671] viml/executor: Directly generate typval_T values Note: this will *still* crash when using API in cases similar to the one described in first commit. Just it needs different code to reproduce. --- src/nvim/eval.c | 70 +++--- src/nvim/eval/decode.c | 138 +++++----- src/nvim/viml/executor/converter.c | 388 +++++++++++++++++++++++++++++ src/nvim/viml/executor/converter.h | 3 + src/nvim/viml/executor/executor.c | 159 ++++++------ test/functional/lua_spec.lua | 143 +++++++++++ 6 files changed, 715 insertions(+), 186 deletions(-) create mode 100644 test/functional/lua_spec.lua diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c9141fbcbf..248383d2e1 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6480,28 +6480,44 @@ static void dict_free_dict(dict_T *d) { xfree(d); } -void dict_free(dict_T *d) { +void dict_free(dict_T *d) +{ if (!in_free_unref_items) { dict_free_contents(d); dict_free_dict(d); } } -/* - * Allocate a Dictionary item. - * The "key" is copied to the new item. - * Note that the value of the item "di_tv" still needs to be initialized! - */ -dictitem_T *dictitem_alloc(char_u *key) FUNC_ATTR_NONNULL_RET +/// Allocate a dictionary item +/// +/// @note that the value of the item di_tv still needs to be initialized. +/// +/// @param[in] key Item key. +/// @param[in] len Key length. +/// +/// @return [allocated] New dictionary item. +dictitem_T *dictitem_alloc_len(const char *const key, const size_t len) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT { - dictitem_T *di = xmalloc(offsetof(dictitem_T, di_key) + STRLEN(key) + 1); -#ifndef __clang_analyzer__ - STRCPY(di->di_key, key); -#endif + dictitem_T *const di = xmallocz(offsetof(dictitem_T, di_key) + len); + memcpy(di->di_key, key, len); di->di_flags = DI_FLAGS_ALLOC; return di; } +/// Allocate a dictionary item +/// +/// @note that the value of the item di_tv still needs to be initialized. +/// +/// @param[in] key Item key, NUL-terminated string. +/// +/// @return [allocated] New dictionary item. +dictitem_T *dictitem_alloc(const char_u *const key) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT +{ + return dictitem_alloc_len((const char *)key, STRLEN(key)); +} + /* * Make a copy of a Dictionary item. */ @@ -13386,37 +13402,7 @@ static void f_luaeval(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - Object arg; - if (argvars[1].v_type == VAR_UNKNOWN) { - arg = NIL; - } else { - arg = vim_to_object(&argvars[1]); - } - - // TODO(ZyX-I): Create function which converts lua objects directly to VimL - // objects, not to API objects. - Error err; - String err_str; - Object ret = executor_eval_lua(cstr_as_string(str), arg, &err, &err_str); - if (err.set) { - if (err_str.size) { - EMSG3(_("E971: Failed to eval lua string: %s (%s)"), err.msg, - err_str.data); - } else { - EMSG2(_("E971: Failed to eval lua string: %s"), err.msg); - } - } - - api_free_string(err_str); - - if (!err.set) { - if (!object_to_vim(ret, rettv, &err)) { - EMSG2(_("E972: Failed to convert resulting API object to VimL: %s"), - err.msg); - } - } - - api_free_object(ret); + executor_eval_lua(cstr_as_string(str), &argvars[1], rettv); } /* diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 43e9f76c0f..cb5a624f7b 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -7,6 +7,7 @@ #include "nvim/eval/encode.h" #include "nvim/ascii.h" #include "nvim/message.h" +#include "nvim/globals.h" #include "nvim/charset.h" // vim_str2nr #include "nvim/lib/kvec.h" #include "nvim/vim.h" // OK, FAIL @@ -218,6 +219,69 @@ static inline int json_decoder_pop(ValuesStackItem obj, } \ } while (0) +/// Create a new special dictionary that ought to represent a MAP +/// +/// @param[out] ret_tv Address where new special dictionary is saved. +/// +/// @return [allocated] list which should contain key-value pairs. Return value +/// may be safely ignored. +list_T *decode_create_map_special_dict(typval_T *const ret_tv) + FUNC_ATTR_NONNULL_ALL +{ + list_T *const list = list_alloc(); + list->lv_refcount++; + create_special_dict(ret_tv, kMPMap, ((typval_T) { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list }, + })); + return list; +} + +/// Convert char* string to typval_T +/// +/// Depending on whether string has (no) NUL bytes, it may use a special +/// dictionary or decode string to VAR_STRING. +/// +/// @param[in] s String to decode. +/// @param[in] len String length. +/// @param[in] hasnul Whether string has NUL byte, not or it was not yet +/// determined. +/// @param[in] binary If true, save special string type as kMPBinary, +/// otherwise kMPString. +/// +/// @return Decoded string. +typval_T decode_string(const char *const s, const size_t len, + const TriState hasnul, const bool binary) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + assert(s != NULL || len == 0); + const bool really_hasnul = (hasnul == kNone + ? memchr(s, NUL, len) != NULL + : (bool)hasnul); + if (really_hasnul) { + list_T *const list = list_alloc(); + list->lv_refcount++; + typval_T tv; + create_special_dict(&tv, binary ? kMPBinary : kMPString, ((typval_T) { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list }, + })); + if (encode_list_write((void *)list, s, len) == -1) { + clear_tv(&tv); + return (typval_T) { .v_type = VAR_UNKNOWN, .v_lock = VAR_UNLOCKED }; + } + return tv; + } else { + return (typval_T) { + .v_type = VAR_STRING, + .v_lock = VAR_UNLOCKED, + .vval = { .v_string = xmemdupz(s, len) }, + }; + } +} + /// Parse JSON double-quoted string /// /// @param[in] conv Defines conversion necessary to convert UTF-8 string to @@ -428,29 +492,13 @@ static inline int parse_json_string(vimconv_T *const conv, str = new_str; str_end = new_str + str_len; } - if (hasnul) { - typval_T obj; - list_T *const list = list_alloc(); - list->lv_refcount++; - create_special_dict(&obj, kMPString, ((typval_T) { - .v_type = VAR_LIST, - .v_lock = VAR_UNLOCKED, - .vval = { .v_list = list }, - })); - if (encode_list_write((void *) list, str, (size_t) (str_end - str)) - == -1) { - clear_tv(&obj); - goto parse_json_string_fail; - } - xfree(str); - POP(obj, true); - } else { - *str_end = NUL; - POP(((typval_T) { - .v_type = VAR_STRING, - .vval = { .v_string = (char_u *) str }, - }), false); + typval_T obj; + obj = decode_string(str, (size_t)(str_end - str), hasnul ? kTrue : kFalse, + false); + if (obj.v_type == VAR_UNKNOWN) { + goto parse_json_string_fail; } + POP(obj, obj.v_type != VAR_STRING); goto parse_json_string_ret; parse_json_string_fail: ret = FAIL; @@ -827,13 +875,7 @@ json_decode_string_cycle_start: list_T *val_list = NULL; if (next_map_special) { next_map_special = false; - val_list = list_alloc(); - val_list->lv_refcount++; - create_special_dict(&tv, kMPMap, ((typval_T) { - .v_type = VAR_LIST, - .v_lock = VAR_UNLOCKED, - .vval = { .v_list = val_list }, - })); + val_list = decode_create_map_special_dict(&tv); } else { dict_T *dict = dict_alloc(); dict->dv_refcount++; @@ -980,37 +1022,15 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) break; } case MSGPACK_OBJECT_STR: { - list_T *const list = list_alloc(); - list->lv_refcount++; - create_special_dict(rettv, kMPString, ((typval_T) { - .v_type = VAR_LIST, - .v_lock = VAR_UNLOCKED, - .vval = { .v_list = list }, - })); - if (encode_list_write((void *) list, mobj.via.str.ptr, mobj.via.str.size) - == -1) { + *rettv = decode_string(mobj.via.bin.ptr, mobj.via.bin.size, kTrue, false); + if (rettv->v_type == VAR_UNKNOWN) { return FAIL; } break; } case MSGPACK_OBJECT_BIN: { - if (memchr(mobj.via.bin.ptr, NUL, mobj.via.bin.size) == NULL) { - *rettv = (typval_T) { - .v_type = VAR_STRING, - .v_lock = VAR_UNLOCKED, - .vval = { .v_string = xmemdupz(mobj.via.bin.ptr, mobj.via.bin.size) }, - }; - break; - } - list_T *const list = list_alloc(); - list->lv_refcount++; - create_special_dict(rettv, kMPBinary, ((typval_T) { - .v_type = VAR_LIST, - .v_lock = VAR_UNLOCKED, - .vval = { .v_list = list }, - })); - if (encode_list_write((void *) list, mobj.via.bin.ptr, mobj.via.bin.size) - == -1) { + *rettv = decode_string(mobj.via.bin.ptr, mobj.via.bin.size, kNone, true); + if (rettv->v_type == VAR_UNKNOWN) { return FAIL; } break; @@ -1067,13 +1087,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) } break; msgpack_to_vim_generic_map: {} - list_T *const list = list_alloc(); - list->lv_refcount++; - create_special_dict(rettv, kMPMap, ((typval_T) { - .v_type = VAR_LIST, - .v_lock = VAR_UNLOCKED, - .vval = { .v_list = list }, - })); + list_T *const list = decode_create_map_special_dict(rettv); for (size_t i = 0; i < mobj.via.map.size; i++) { list_T *const kv_pair = list_alloc(); list_append_list(list, kv_pair); diff --git a/src/nvim/viml/executor/converter.c b/src/nvim/viml/executor/converter.c index 2105beb08a..123659aa5c 100644 --- a/src/nvim/viml/executor/converter.c +++ b/src/nvim/viml/executor/converter.c @@ -2,12 +2,24 @@ #include #include #include +#include +#include #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/func_attr.h" #include "nvim/memory.h" #include "nvim/assert.h" +// FIXME: vim.h is not actually needed, but otherwise it states MAXPATHL is +// redefined +#include "nvim/vim.h" +#include "nvim/globals.h" +#include "nvim/message.h" +#include "nvim/eval_defs.h" +#include "nvim/ascii.h" + +#include "nvim/lib/kvec.h" +#include "nvim/eval/decode.h" #include "nvim/viml/executor/converter.h" #include "nvim/viml/executor/executor.h" @@ -16,6 +28,382 @@ # include "viml/executor/converter.c.generated.h" #endif +/// Helper structure for nlua_pop_typval +typedef struct { + typval_T *tv; ///< Location where conversion result is saved. + bool container; ///< True if tv is a container. + bool special; ///< If true then tv is a _VAL part of special dictionary + ///< that represents mapping. +} PopStackItem; + +/// Convert lua object to VimL typval_T +/// +/// Should pop exactly one value from lua stack. +/// +/// @param lstate Lua state. +/// @param[out] ret_tv Where to put the result. +/// +/// @return `true` in case of success, `false` in case of failure. Error is +/// reported automatically. +bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) +{ + bool ret = true; +#ifndef NDEBUG + const int initial_size = lua_gettop(lstate); +#endif + kvec_t(PopStackItem) stack = KV_INITIAL_VALUE; + kv_push(stack, ((PopStackItem) { ret_tv, false, false })); + while (ret && kv_size(stack)) { + if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { + emsgf(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 3); + ret = false; + break; + } + PopStackItem cur = kv_pop(stack); + if (cur.container) { + if (cur.special || cur.tv->v_type == VAR_DICT) { + assert(cur.tv->v_type == (cur.special ? VAR_LIST : VAR_DICT)); + if (lua_next(lstate, -2)) { + assert(lua_type(lstate, -2) == LUA_TSTRING); + size_t len; + const char *s = lua_tolstring(lstate, -2, &len); + if (cur.special) { + list_T *const kv_pair = list_alloc(); + list_append_list(cur.tv->vval.v_list, kv_pair); + listitem_T *const key = listitem_alloc(); + key->li_tv = decode_string(s, len, kTrue, false); + list_append(kv_pair, key); + if (key->li_tv.v_type == VAR_UNKNOWN) { + ret = false; + list_unref(kv_pair); + continue; + } + listitem_T *const val = listitem_alloc(); + list_append(kv_pair, val); + kv_push(stack, cur); + cur = (PopStackItem) { &val->li_tv, false, false }; + } else { + dictitem_T *const di = dictitem_alloc_len(s, len); + if (dict_add(cur.tv->vval.v_dict, di) == FAIL) { + assert(false); + } + kv_push(stack, cur); + cur = (PopStackItem) { &di->di_tv, false, false }; + } + } else { + lua_pop(lstate, 1); + continue; + } + } else { + assert(cur.tv->v_type == VAR_LIST); + lua_rawgeti(lstate, -1, cur.tv->vval.v_list->lv_len + 1); + if (lua_isnil(lstate, -1)) { + lua_pop(lstate, 1); + lua_pop(lstate, 1); + continue; + } + listitem_T *li = listitem_alloc(); + list_append(cur.tv->vval.v_list, li); + kv_push(stack, cur); + cur = (PopStackItem) { &li->li_tv, false, false }; + } + } + assert(!cur.container); + memset(cur.tv, 0, sizeof(*cur.tv)); + switch (lua_type(lstate, -1)) { + case LUA_TNIL: { + cur.tv->v_type = VAR_SPECIAL; + cur.tv->vval.v_special = kSpecialVarNull; + break; + } + case LUA_TBOOLEAN: { + cur.tv->v_type = VAR_SPECIAL; + cur.tv->vval.v_special = (lua_toboolean(lstate, -1) + ? kSpecialVarTrue + : kSpecialVarFalse); + break; + } + case LUA_TSTRING: { + size_t len; + const char *s = lua_tolstring(lstate, -1, &len); + *cur.tv = decode_string(s, len, kNone, true); + if (cur.tv->v_type == VAR_UNKNOWN) { + ret = false; + } + break; + } + case LUA_TNUMBER: { + const lua_Number n = lua_tonumber(lstate, -1); + if (n > (lua_Number)VARNUMBER_MAX || n < (lua_Number)VARNUMBER_MIN + || ((lua_Number)((varnumber_T)n)) != n) { + cur.tv->v_type = VAR_FLOAT; + cur.tv->vval.v_float = (float_T)n; + } else { + cur.tv->v_type = VAR_NUMBER; + cur.tv->vval.v_number = (varnumber_T)n; + } + break; + } + case LUA_TTABLE: { + bool has_string = false; + bool has_string_with_nul = false; + bool has_other = false; + size_t maxidx = 0; + size_t tsize = 0; + lua_pushnil(lstate); + while (lua_next(lstate, -2)) { + switch (lua_type(lstate, -2)) { + case LUA_TSTRING: { + size_t len; + const char *s = lua_tolstring(lstate, -2, &len); + if (memchr(s, NUL, len) != NULL) { + has_string_with_nul = true; + } + has_string = true; + break; + } + case LUA_TNUMBER: { + const lua_Number n = lua_tonumber(lstate, -2); + if (n > (lua_Number)SIZE_MAX || n <= 0 + || ((lua_Number)((size_t)n)) != n) { + has_other = true; + } else { + const size_t idx = (size_t)n; + if (idx > maxidx) { + maxidx = idx; + } + } + break; + } + default: { + has_other = true; + break; + } + } + tsize++; + lua_pop(lstate, 1); + } + + if (tsize == 0) { + // Assuming empty list + cur.tv->v_type = VAR_LIST; + cur.tv->vval.v_list = list_alloc(); + cur.tv->vval.v_list->lv_refcount++; + } else if (tsize == maxidx && !has_other && !has_string) { + // Assuming array + cur.tv->v_type = VAR_LIST; + cur.tv->vval.v_list = list_alloc(); + cur.tv->vval.v_list->lv_refcount++; + cur.container = true; + kv_push(stack, cur); + } else if (has_string && !has_other && maxidx == 0) { + // Assuming dictionary + cur.special = has_string_with_nul; + if (has_string_with_nul) { + decode_create_map_special_dict(cur.tv); + assert(cur.tv->v_type = VAR_DICT); + dictitem_T *const val_di = dict_find(cur.tv->vval.v_dict, + (char_u *)"_VAL", 4); + assert(val_di != NULL); + cur.tv = &val_di->di_tv; + assert(cur.tv->v_type == VAR_LIST); + } else { + cur.tv->v_type = VAR_DICT; + cur.tv->vval.v_dict = dict_alloc(); + cur.tv->vval.v_dict->dv_refcount++; + } + cur.container = true; + kv_push(stack, cur); + lua_pushnil(lstate); + } else { + EMSG(_("E5100: Cannot convert given lua table: table " + "should either have a sequence of positive integer keys " + "or contain only string keys")); + ret = false; + } + break; + } + default: { + EMSG(_("E5101: Cannot convert given lua type")); + ret = false; + break; + } + } + if (!cur.container) { + lua_pop(lstate, 1); + } + } + kv_destroy(stack); + if (!ret) { + clear_tv(ret_tv); + memset(ret_tv, 0, sizeof(*ret_tv)); + lua_pop(lstate, lua_gettop(lstate) - initial_size + 1); + } + assert(lua_gettop(lstate) == initial_size - 1); + return ret; +} + +#define TYPVAL_ENCODE_ALLOW_SPECIALS true + +#define TYPVAL_ENCODE_CONV_NIL(tv) \ + lua_pushnil(lstate) + +#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \ + lua_pushboolean(lstate, (bool)(num)) + +#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \ + lua_pushnumber(lstate, (lua_Number)(num)) + +#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER TYPVAL_ENCODE_CONV_NUMBER + +#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \ + TYPVAL_ENCODE_CONV_NUMBER(tv, flt) + +#define TYPVAL_ENCODE_CONV_STRING(tv, str, len) \ + lua_pushlstring(lstate, (const char *)(str), (len)) + +#define TYPVAL_ENCODE_CONV_STR_STRING TYPVAL_ENCODE_CONV_STRING + +#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, str, len, type) \ + TYPVAL_ENCODE_CONV_NIL() + +#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ + do { \ + TYPVAL_ENCODE_CONV_NIL(tv); \ + goto typval_encode_stop_converting_one_item; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len) +#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len) +#define TYPVAL_ENCODE_CONV_FUNC_END(tv) + +#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \ + lua_createtable(lstate, 0, 0) + +#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ + TYPVAL_ENCODE_CONV_EMPTY_LIST() + +#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \ + do { \ + if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { \ + emsgf(_("E5102: Lua failed to grow stack to %i"), \ + lua_gettop(lstate) + 3); \ + return false; \ + } \ + lua_createtable(lstate, (int)(len), 0); \ + lua_pushnumber(lstate, 1); \ + } while (0) + +#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) + +#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) \ + do { \ + lua_Number idx = lua_tonumber(lstate, -2); \ + lua_rawset(lstate, -3); \ + lua_pushnumber(lstate, idx + 1); \ + } while (0) + +#define TYPVAL_ENCODE_CONV_LIST_END(tv) \ + lua_rawset(lstate, -3) + +#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \ + do { \ + if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { \ + emsgf(_("E5102: Lua failed to grow stack to %i"), \ + lua_gettop(lstate) + 3); \ + return false; \ + } \ + lua_createtable(lstate, 0, (int)(len)); \ + } while (0) + +#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, kv_pair) + +#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv) + +#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) + +#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) \ + lua_rawset(lstate, -3) + +#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \ + TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) + +#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \ + do { \ + for (size_t backref = kv_size(*mpstack); backref; backref--) { \ + const MPConvStackVal mpval = kv_A(*mpstack, backref - 1); \ + if (mpval.type == conv_type) { \ + if (conv_type == kMPConvDict \ + ? (void *) mpval.data.d.dict == (void *) (val) \ + : (void *) mpval.data.l.list == (void *) (val)) { \ + lua_pushvalue(lstate, \ + 1 - ((int)((kv_size(*mpstack) - backref + 1) * 2))); \ + break; \ + } \ + } \ + } \ + } while (0) + +#define TYPVAL_ENCODE_SCOPE static +#define TYPVAL_ENCODE_NAME lua +#define TYPVAL_ENCODE_FIRST_ARG_TYPE lua_State *const +#define TYPVAL_ENCODE_FIRST_ARG_NAME lstate +#include "nvim/eval/typval_encode.c.h" +#undef TYPVAL_ENCODE_SCOPE +#undef TYPVAL_ENCODE_NAME +#undef TYPVAL_ENCODE_FIRST_ARG_TYPE +#undef TYPVAL_ENCODE_FIRST_ARG_NAME + +#undef TYPVAL_ENCODE_CONV_STRING +#undef TYPVAL_ENCODE_CONV_STR_STRING +#undef TYPVAL_ENCODE_CONV_EXT_STRING +#undef TYPVAL_ENCODE_CONV_NUMBER +#undef TYPVAL_ENCODE_CONV_FLOAT +#undef TYPVAL_ENCODE_CONV_FUNC_START +#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS +#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF +#undef TYPVAL_ENCODE_CONV_FUNC_END +#undef TYPVAL_ENCODE_CONV_EMPTY_LIST +#undef TYPVAL_ENCODE_CONV_LIST_START +#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START +#undef TYPVAL_ENCODE_CONV_EMPTY_DICT +#undef TYPVAL_ENCODE_CONV_NIL +#undef TYPVAL_ENCODE_CONV_BOOL +#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER +#undef TYPVAL_ENCODE_CONV_DICT_START +#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START +#undef TYPVAL_ENCODE_CONV_DICT_END +#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY +#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK +#undef TYPVAL_ENCODE_CONV_LIST_END +#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_CONV_RECURSE +#undef TYPVAL_ENCODE_ALLOW_SPECIALS + +/// Convert VimL typval_T to lua value +/// +/// Should leave single value in lua stack. May only fail if lua failed to grow +/// stack. +/// +/// @param lstate Lua interpreter state. +/// @param[in] tv typval_T to convert. +/// +/// @return true in case of success, false otherwise. +bool nlua_push_typval(lua_State *lstate, typval_T *const tv) +{ + const int initial_size = lua_gettop(lstate); + if (!lua_checkstack(lstate, initial_size + 1)) { + emsgf(_("E1502: Lua failed to grow stack to %i"), initial_size + 4); + return false; + } + if (encode_vim_to_lua(lstate, tv, "nlua_push_typval argument") == FAIL) { + return false; + } + assert(lua_gettop(lstate) == initial_size + 1); + return true; +} + #define NLUA_PUSH_IDX(lstate, type, idx) \ do { \ STATIC_ASSERT(sizeof(type) <= sizeof(lua_Number), \ diff --git a/src/nvim/viml/executor/converter.h b/src/nvim/viml/executor/converter.h index e11d0cef19..dbbaaebf6b 100644 --- a/src/nvim/viml/executor/converter.h +++ b/src/nvim/viml/executor/converter.h @@ -3,8 +3,11 @@ #include #include +#include + #include "nvim/api/private/defs.h" #include "nvim/func_attr.h" +#include "nvim/eval.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "viml/executor/converter.h.generated.h" diff --git a/src/nvim/viml/executor/executor.c b/src/nvim/viml/executor/executor.c index 6f1e847649..a9d05bcd31 100644 --- a/src/nvim/viml/executor/executor.c +++ b/src/nvim/viml/executor/executor.c @@ -36,7 +36,19 @@ typedef struct { lua_pushcfunction(lstate, &function); \ lua_call(lstate, 0, numret); \ } while (0) -/// Call C function which expects four arguments +/// Call C function which expects two arguments +/// +/// @param function Called function +/// @param numret Number of returned arguments +/// @param a… Supplied argument (should be a void* pointer) +#define NLUA_CALL_C_FUNCTION_2(lstate, function, numret, a1, a2) \ + do { \ + lua_pushcfunction(lstate, &function); \ + lua_pushlightuserdata(lstate, a1); \ + lua_pushlightuserdata(lstate, a2); \ + lua_call(lstate, 2, numret); \ + } while (0) +/// Call C function which expects three arguments /// /// @param function Called function /// @param numret Number of returned arguments @@ -64,15 +76,19 @@ typedef struct { lua_call(lstate, 4, numret); \ } while (0) -static void set_lua_error(lua_State *lstate, LuaError *lerr) +/// Convert lua error into a Vim error message +/// +/// @param lstate Lua interpreter state. +/// @param[in] msg Message base, must contain one `%s`. +static void nlua_error(lua_State *const lstate, const char *const msg) FUNC_ATTR_NONNULL_ALL { - const char *const str = lua_tolstring(lstate, -1, &lerr->lua_err_str.size); - lerr->lua_err_str.data = xmemdupz(str, lerr->lua_err_str.size); - lua_pop(lstate, 1); + size_t len; + const char *const str = lua_tolstring(lstate, -1, &len); - // FIXME? More specific error? - set_api_error("Error while executing lua code", &lerr->err); + EMSG2(msg, str); + + lua_pop(lstate, 1); } /// Compare two strings, ignoring case @@ -94,25 +110,26 @@ static int nlua_stricmp(lua_State *lstate) FUNC_ATTR_NONNULL_ALL /// Evaluate lua string /// -/// Expects three values on the stack: string to evaluate, pointer to the -/// location where result is saved, pointer to the location where error is -/// saved. Always returns nothing (from the lua point of view). +/// Expects two values on the stack: string to evaluate, pointer to the +/// location where result is saved. Always returns nothing (from the lua point +/// of view). static int nlua_exec_lua_string(lua_State *lstate) FUNC_ATTR_NONNULL_ALL { - String *str = (String *) lua_touserdata(lstate, 1); - Object *obj = (Object *) lua_touserdata(lstate, 2); - LuaError *lerr = (LuaError *) lua_touserdata(lstate, 3); - lua_pop(lstate, 3); + String *str = (String *)lua_touserdata(lstate, 1); + typval_T *ret_tv = (typval_T *)lua_touserdata(lstate, 2); + lua_pop(lstate, 2); if (luaL_loadbuffer(lstate, str->data, str->size, NLUA_EVAL_NAME)) { - set_lua_error(lstate, lerr); + nlua_error(lstate, _("E5104: Error while creating lua chunk: %s")); return 0; } if (lua_pcall(lstate, 0, 1, 0)) { - set_lua_error(lstate, lerr); + nlua_error(lstate, _("E5105: Error while calling lua chunk: %s")); + return 0; + } + if (!nlua_pop_typval(lstate, ret_tv)) { return 0; } - *obj = nlua_pop_Object(lstate, &lerr->err); return 0; } @@ -124,8 +141,7 @@ static int nlua_state_init(lua_State *lstate) FUNC_ATTR_NONNULL_ALL lua_pushcfunction(lstate, &nlua_stricmp); lua_setglobal(lstate, "stricmp"); if (luaL_dostring(lstate, (char *) &vim_module[0])) { - LuaError lerr; - set_lua_error(lstate, &lerr); + nlua_error(lstate, _("E5106: Error while creating vim module: %s")); return 1; } nlua_add_api_functions(lstate); @@ -150,14 +166,6 @@ static lua_State *init_lua(void) return lstate; } -static Object exec_lua_string(lua_State *lstate, String str, LuaError *lerr) - FUNC_ATTR_NONNULL_ALL -{ - Object ret = { kObjectTypeNil, { false } }; - NLUA_CALL_C_FUNCTION_3(lstate, nlua_exec_lua_string, 0, &str, &ret, lerr); - return ret; -} - static lua_State *global_lstate = NULL; /// Execute lua string @@ -165,28 +173,17 @@ static lua_State *global_lstate = NULL; /// Used for :lua. /// /// @param[in] str String to execute. -/// @param[out] err Location where error will be saved. -/// @param[out] err_str Location where lua error string will be saved, if any. +/// @param[out] ret_tv Location where result will be saved. /// /// @return Result of the execution. -Object executor_exec_lua(String str, Error *err, String *err_str) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +void executor_exec_lua(String str, typval_T *ret_tv) + FUNC_ATTR_NONNULL_ALL { if (global_lstate == NULL) { global_lstate = init_lua(); } - LuaError lerr = { - .err = { .set = false }, - .lua_err_str = STRING_INIT, - }; - - Object ret = exec_lua_string(global_lstate, str, &lerr); - - *err = lerr.err; - *err_str = lerr.lua_err_str; - - return ret; + NLUA_CALL_C_FUNCTION_2(global_lstate, nlua_exec_lua_string, 0, &str, ret_tv); } /// Evaluate lua string @@ -196,75 +193,73 @@ Object executor_exec_lua(String str, Error *err, String *err_str) /// 1. String to evaluate. /// 2. _A value. /// 3. Pointer to location where result is saved. -/// 4. Pointer to location where error will be saved. /// /// @param[in,out] lstate Lua interpreter state. static int nlua_eval_lua_string(lua_State *lstate) FUNC_ATTR_NONNULL_ALL { - String *str = (String *) lua_touserdata(lstate, 1); - Object *arg = (Object *) lua_touserdata(lstate, 2); - Object *ret = (Object *) lua_touserdata(lstate, 3); - LuaError *lerr = (LuaError *) lua_touserdata(lstate, 4); + String *str = (String *)lua_touserdata(lstate, 1); + typval_T *arg = (typval_T *)lua_touserdata(lstate, 2); + typval_T *ret_tv = (typval_T *)lua_touserdata(lstate, 3); + lua_pop(lstate, 3); garray_T str_ga; ga_init(&str_ga, 1, 80); -#define EVALHEADER "local _A=select(1,...) return " - ga_concat_len(&str_ga, EVALHEADER, sizeof(EVALHEADER) - 1); +#define EVALHEADER "local _A=select(1,...) return (" + const size_t lcmd_len = sizeof(EVALHEADER) - 1 + str->size + 1; + char *lcmd; + if (lcmd_len < IOSIZE) { + lcmd = (char *)IObuff; + } else { + lcmd = xmalloc(lcmd_len); + } + memcpy(lcmd, EVALHEADER, sizeof(EVALHEADER) - 1); + memcpy(lcmd + sizeof(EVALHEADER) - 1, str->data, str->size); + lcmd[lcmd_len - 1] = ')'; #undef EVALHEADER - ga_concat_len(&str_ga, str->data, str->size); - if (luaL_loadbuffer(lstate, str_ga.ga_data, (size_t) str_ga.ga_len, - NLUA_EVAL_NAME)) { - set_lua_error(lstate, lerr); + if (luaL_loadbuffer(lstate, lcmd, lcmd_len, NLUA_EVAL_NAME)) { + nlua_error(lstate, + _("E5107: Error while creating lua chunk for luaeval(): %s")); return 0; } - ga_clear(&str_ga); + if (lcmd != (char *)IObuff) { + xfree(lcmd); + } - nlua_push_Object(lstate, *arg); + if (arg == NULL || arg->v_type == VAR_UNKNOWN) { + lua_pushnil(lstate); + } else { + nlua_push_typval(lstate, arg); + } if (lua_pcall(lstate, 1, 1, 0)) { - set_lua_error(lstate, lerr); + nlua_error(lstate, + _("E5108: Error while calling lua chunk for luaeval(): %s")); + return 0; + } + if (!nlua_pop_typval(lstate, ret_tv)) { return 0; } - *ret = nlua_pop_Object(lstate, &lerr->err); return 0; } -static Object eval_lua_string(lua_State *lstate, String str, Object arg, - LuaError *lerr) - FUNC_ATTR_NONNULL_ALL -{ - Object ret = { kObjectTypeNil, { false } }; - NLUA_CALL_C_FUNCTION_4(lstate, nlua_eval_lua_string, 0, - &str, &arg, &ret, lerr); - return ret; -} - /// Evaluate lua string /// /// Used for luaeval(). /// /// @param[in] str String to execute. -/// @param[out] err Location where error will be saved. -/// @param[out] err_str Location where lua error string will be saved, if any. +/// @param[in] arg Second argument to `luaeval()`. +/// @param[out] ret_tv Location where result will be saved. /// /// @return Result of the execution. -Object executor_eval_lua(String str, Object arg, Error *err, String *err_str) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +void executor_eval_lua(const String str, typval_T *const arg, + typval_T *const ret_tv) + FUNC_ATTR_NONNULL_ALL { if (global_lstate == NULL) { global_lstate = init_lua(); } - LuaError lerr = { - .err = { .set = false }, - .lua_err_str = STRING_INIT, - }; - - Object ret = eval_lua_string(global_lstate, str, arg, &lerr); - - *err = lerr.err; - *err_str = lerr.lua_err_str; - - return ret; + NLUA_CALL_C_FUNCTION_3(global_lstate, nlua_eval_lua_string, 0, + (void *)&str, arg, ret_tv); } diff --git a/test/functional/lua_spec.lua b/test/functional/lua_spec.lua new file mode 100644 index 0000000000..fdfd9b4c2d --- /dev/null +++ b/test/functional/lua_spec.lua @@ -0,0 +1,143 @@ +local helpers = require('test.functional.helpers')(after_each) + +local eq = helpers.eq +local neq = helpers.neq +local NIL = helpers.NIL +local eval = helpers.eval +local clear = helpers.clear +local funcs = helpers.funcs +local meths = helpers.meths +local exc_exec = helpers.exc_exec +local curbufmeths = helpers.curbufmeths + +local function startswith(expected, actual) + eq(expected, actual:sub(1, #expected)) +end + +before_each(clear) + +describe('luaeval() function', function() + local nested_by_level = {} + local nested = {} + local nested_s = '{}' + for i=1,100 do + if i % 2 == 0 then + nested = {nested} + nested_s = '{' .. nested_s .. '}' + else + nested = {nested=nested} + nested_s = '{nested=' .. nested_s .. '}' + end + nested_by_level[i] = {o=nested, s=nested_s} + end + + it('correctly evaluates scalars', function() + eq(1, funcs.luaeval('1')) + eq(0, eval('type(luaeval("1"))')) + + eq(1.5, funcs.luaeval('1.5')) + eq(5, eval('type(luaeval("1.5"))')) + + eq("test", funcs.luaeval('"test"')) + eq(1, eval('type(luaeval("\'test\'"))')) + + eq('', funcs.luaeval('""')) + eq({_TYPE={}, _VAL={'\n'}}, funcs.luaeval([['\0']])) + eq({_TYPE={}, _VAL={'\n', '\n'}}, funcs.luaeval([['\0\n\0']])) + eq(1, eval([[luaeval('"\0\n\0"')._TYPE is v:msgpack_types.binary]])) + + eq(true, funcs.luaeval('true')) + eq(false, funcs.luaeval('false')) + eq(NIL, funcs.luaeval('nil')) + end) + + it('correctly evaluates containers', function() + eq({}, funcs.luaeval('{}')) + eq(3, eval('type(luaeval("{}"))')) + + eq({test=1, foo=2}, funcs.luaeval('{test=1, foo=2}')) + eq(4, eval('type(luaeval("{test=1, foo=2}"))')) + + eq({4, 2}, funcs.luaeval('{4, 2}')) + eq(3, eval('type(luaeval("{4, 2}"))')) + + local level = 30 + eq(nested_by_level[level].o, funcs.luaeval(nested_by_level[level].s)) + + eq({_TYPE={}, _VAL={{{_TYPE={}, _VAL={'\n', '\n'}}, {_TYPE={}, _VAL={'\n', '\n\n'}}}}}, + funcs.luaeval([[{['\0\n\0']='\0\n\0\0'}]])) + eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._TYPE is v:msgpack_types.map]])) + eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._VAL[0][0]._TYPE is v:msgpack_types.string]])) + eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._VAL[0][1]._TYPE is v:msgpack_types.binary]])) + eq({nested={{_TYPE={}, _VAL={{{_TYPE={}, _VAL={'\n', '\n'}}, {_TYPE={}, _VAL={'\n', '\n\n'}}}}}}}, + funcs.luaeval([[{nested={{['\0\n\0']='\0\n\0\0'}}}]])) + end) + + it('correctly passes scalars as argument', function() + eq(1, funcs.luaeval('_A', 1)) + eq(1.5, funcs.luaeval('_A', 1.5)) + eq('', funcs.luaeval('_A', '')) + eq('test', funcs.luaeval('_A', 'test')) + eq(NIL, funcs.luaeval('_A', NIL)) + eq(true, funcs.luaeval('_A', true)) + eq(false, funcs.luaeval('_A', false)) + end) + + it('correctly passes containers as argument', function() + eq({}, funcs.luaeval('_A', {})) + eq({test=1}, funcs.luaeval('_A', {test=1})) + eq({4, 2}, funcs.luaeval('_A', {4, 2})) + local level = 28 + eq(nested_by_level[level].o, funcs.luaeval('_A', nested_by_level[level].o)) + end) + + local function sp(typ, val) + return ('{"_TYPE": v:msgpack_types.%s, "_VAL": %s}'):format(typ, val) + end + local function mapsp(...) + local val = '' + for i=1,(select('#', ...)/2) do + val = ('%s[%s,%s],'):format(val, select(i * 2 - 1, ...), + select(i * 2, ...)) + end + return sp('map', '[' .. val .. ']') + end + local function luaevalarg(argexpr, expr) + return eval(([=[ + [ + extend(g:, {'_ret': luaeval(%s, %s)})._ret, + type(g:_ret)==type({})&&has_key(g:_ret, '_TYPE') + ? [ + get(keys(filter(copy(v:msgpack_types), 'v:val is g:_ret._TYPE')), 0, + g:_ret._TYPE), + get(g:_ret, '_VAL', g:_ret) + ] + : [0, g:_ret]][1] + ]=]):format(expr or '"_A"', argexpr):gsub('\n', '')) + end + + it('correctly passes special dictionaries', function() + eq({'binary', {'\n', '\n'}}, luaevalarg(sp('binary', '["\\n", "\\n"]'))) + eq({'binary', {'\n', '\n'}}, luaevalarg(sp('string', '["\\n", "\\n"]'))) + eq({0, true}, luaevalarg(sp('boolean', 1))) + eq({0, false}, luaevalarg(sp('boolean', 0))) + eq({0, NIL}, luaevalarg(sp('nil', 0))) + eq({0, {[""]=""}}, luaevalarg(mapsp(sp('binary', '[""]'), '""'))) + eq({0, {[""]=""}}, luaevalarg(mapsp(sp('string', '[""]'), '""'))) + end) + + it('issues an error in some cases', function() + eq("Vim(call):E5100: Cannot convert given lua table: table should either have a sequence of positive integer keys or contain only string keys", + exc_exec('call luaeval("{1, foo=2}")')) + eq("Vim(call):E5101: Cannot convert given lua type", + exc_exec('call luaeval("vim.api.buffer_get_line_slice")')) + startswith("Vim(call):E5107: Error while creating lua chunk for luaeval(): ", + exc_exec('call luaeval("1, 2, 3")')) + startswith("Vim(call):E5108: Error while calling lua chunk for luaeval(): ", + exc_exec('call luaeval("(nil)()")')) + + -- The following should not crash: conversion error happens inside + eq("Vim(call):E5101: Cannot convert given lua type", + exc_exec('call luaeval("vim.api")')) + end) +end) From a4dc8de0739d8e9e910d786a9b6fbfbc162aee9c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 10 Jul 2016 21:49:59 +0300 Subject: [PATCH 0106/1671] *: Silence linter --- src/nvim/eval.c | 2 +- src/nvim/viml/executor/converter.c | 20 ++++++++++---------- src/nvim/viml/executor/executor.c | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 248383d2e1..c349a601c6 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -13397,7 +13397,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) static void f_luaeval(typval_T *argvars, typval_T *rettv, FunPtr fptr) FUNC_ATTR_NONNULL_ALL { - char *const str = (char *) get_tv_string(&argvars[0]); + const char *const str = (const char *)get_tv_string(&argvars[0]); if (str == NULL) { return; } diff --git a/src/nvim/viml/executor/converter.c b/src/nvim/viml/executor/converter.c index 123659aa5c..fb7bbd51eb 100644 --- a/src/nvim/viml/executor/converter.c +++ b/src/nvim/viml/executor/converter.c @@ -334,8 +334,8 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) const MPConvStackVal mpval = kv_A(*mpstack, backref - 1); \ if (mpval.type == conv_type) { \ if (conv_type == kMPConvDict \ - ? (void *) mpval.data.d.dict == (void *) (val) \ - : (void *) mpval.data.l.list == (void *) (val)) { \ + ? (void *)mpval.data.d.dict == (void *)(val) \ + : (void *)mpval.data.l.list == (void *)(val)) { \ lua_pushvalue(lstate, \ 1 - ((int)((kv_size(*mpstack) - backref + 1) * 2))); \ break; \ @@ -450,7 +450,7 @@ static inline void nlua_push_locks_idx(lua_State *lstate) static inline void nlua_push_val_idx(lua_State *lstate) FUNC_ATTR_NONNULL_ALL { - lua_pushnumber(lstate, (lua_Number) 0); + lua_pushnumber(lstate, (lua_Number)0); } /// Push type @@ -480,7 +480,7 @@ static inline void nlua_create_typed_table(lua_State *lstate, const char *const type) FUNC_ATTR_NONNULL_ALL { - lua_createtable(lstate, (int) narr, (int) (1 + nrec)); + lua_createtable(lstate, (int)narr, (int)(1 + nrec)); nlua_push_type_idx(lstate); nlua_push_type(lstate, type); lua_rawset(lstate, -3); @@ -502,7 +502,7 @@ void nlua_push_String(lua_State *lstate, const String s) void nlua_push_Integer(lua_State *lstate, const Integer n) FUNC_ATTR_NONNULL_ALL { - lua_pushnumber(lstate, (lua_Number) n); + lua_pushnumber(lstate, (lua_Number)n); } /// Convert given Float to lua table @@ -513,7 +513,7 @@ void nlua_push_Float(lua_State *lstate, const Float f) { nlua_create_typed_table(lstate, 0, 1, "float"); nlua_push_val_idx(lstate); - lua_pushnumber(lstate, (lua_Number) f); + lua_pushnumber(lstate, (lua_Number)f); lua_rawset(lstate, -3); } @@ -558,7 +558,7 @@ void nlua_push_Array(lua_State *lstate, const Array array) nlua_add_locks_table(lstate); for (size_t i = 0; i < array.size; i++) { nlua_push_Object(lstate, array.items[i]); - lua_rawseti(lstate, -3, (int) i + 1); + lua_rawseti(lstate, -3, (int)i + 1); } } @@ -619,7 +619,7 @@ String nlua_pop_String(lua_State *lstate, Error *err) { String ret; - ret.data = (char *) lua_tolstring(lstate, -1, &(ret.size)); + ret.data = (char *)lua_tolstring(lstate, -1, &(ret.size)); if (ret.data == NULL) { lua_pop(lstate, 1); @@ -646,7 +646,7 @@ Integer nlua_pop_Integer(lua_State *lstate, Error *err) set_api_error("Expected lua integer", err); return ret; } - ret = (Integer) lua_tonumber(lstate, -1); + ret = (Integer)lua_tonumber(lstate, -1); lua_pop(lstate, 1); return ret; @@ -744,7 +744,7 @@ Array nlua_pop_Array(lua_State *lstate, Error *err) for (size_t i = 1; i <= ret.size; i++) { Object val; - lua_rawgeti(lstate, -1, (int) i); + lua_rawgeti(lstate, -1, (int)i); val = nlua_pop_Object(lstate, err); if (err->set) { diff --git a/src/nvim/viml/executor/executor.c b/src/nvim/viml/executor/executor.c index a9d05bcd31..1a7b5f8915 100644 --- a/src/nvim/viml/executor/executor.c +++ b/src/nvim/viml/executor/executor.c @@ -104,7 +104,7 @@ static int nlua_stricmp(lua_State *lstate) FUNC_ATTR_NONNULL_ALL const char *s2 = luaL_checklstring(lstate, 2, NULL); const int ret = STRICMP(s1, s2); lua_pop(lstate, 2); - lua_pushnumber(lstate, (lua_Number) ((ret > 0) - (ret < 0))); + lua_pushnumber(lstate, (lua_Number)((ret > 0) - (ret < 0))); return 1; } @@ -140,7 +140,7 @@ static int nlua_state_init(lua_State *lstate) FUNC_ATTR_NONNULL_ALL { lua_pushcfunction(lstate, &nlua_stricmp); lua_setglobal(lstate, "stricmp"); - if (luaL_dostring(lstate, (char *) &vim_module[0])) { + if (luaL_dostring(lstate, (char *)&vim_module[0])) { nlua_error(lstate, _("E5106: Error while creating vim module: %s")); return 1; } From ed3115bd26047c9b125798d9cb56d09b155a243b Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Jul 2016 20:42:27 +0300 Subject: [PATCH 0107/1671] executor: Make sure it works with API values --- src/nvim/api/vim.c | 16 +- src/nvim/eval.c | 2 +- src/nvim/viml/executor/converter.c | 559 ++++++++++++++++++----------- src/nvim/viml/executor/executor.c | 1 + test/functional/lua_spec.lua | 94 ++++- 5 files changed, 461 insertions(+), 211 deletions(-) diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 413456c615..24959e9a59 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -197,7 +197,21 @@ Object nvim_eval(String expr, Error *err) return rv; } -/// Calls a VimL function with the given arguments. +/// Returns object given as argument +/// +/// This API function is used for testing. One should not rely on its presence +/// in plugins. +/// +/// @param[in] obj Object to return. +/// +/// @return its argument. +Object _vim_id(Object obj) +{ + return obj; +} + +/// Calls a VimL function with the given arguments +/// /// On VimL error: Returns a generic error; v:errmsg is not updated. /// /// @param fname Function to call diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c349a601c6..de82f8a145 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -13402,7 +13402,7 @@ static void f_luaeval(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - executor_eval_lua(cstr_as_string(str), &argvars[1], rettv); + executor_eval_lua(cstr_as_string((char *)str), &argvars[1], rettv); } /* diff --git a/src/nvim/viml/executor/converter.c b/src/nvim/viml/executor/converter.c index fb7bbd51eb..c127b87738 100644 --- a/src/nvim/viml/executor/converter.c +++ b/src/nvim/viml/executor/converter.c @@ -24,10 +24,121 @@ #include "nvim/viml/executor/converter.h" #include "nvim/viml/executor/executor.h" +/// Determine, which keys lua table contains +typedef struct { + size_t maxidx; ///< Maximum positive integral value found. + size_t string_keys_num; ///< Number of string keys. + bool has_string_with_nul; ///< True if there is string key with NUL byte. + ObjectType type; ///< If has_type_key is true then attached value. Otherwise + ///< either kObjectTypeNil, kObjectTypeDictionary or + ///< kObjectTypeArray, depending on other properties. + lua_Number val; ///< If has_val_key and val_type == LUA_TNUMBER: value. +} LuaTableProps; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "viml/executor/converter.c.generated.h" #endif +#define TYPE_IDX_VALUE true +#define VAL_IDX_VALUE false + +#define LUA_PUSH_STATIC_STRING(lstate, s) \ + lua_pushlstring(lstate, s, sizeof(s) - 1) + +static LuaTableProps nlua_traverse_table(lua_State *const lstate) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + bool has_type_key = false; // True if type key was found, + // @see nlua_push_type_idx(). + size_t tsize = 0; // Total number of keys. + int val_type = 0; // If has_val_key: lua type of the value. + bool has_val_key = false; // True if val key was found, + // @see nlua_push_val_idx(). + bool has_other = false; // True if there are keys that are not strings + // or positive integral values. + LuaTableProps ret; + memset(&ret, 0, sizeof(ret)); + if (!lua_checkstack(lstate, lua_gettop(lstate) + 2)) { + emsgf(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 2); + ret.type = kObjectTypeNil; + return ret; + } + lua_pushnil(lstate); + while (lua_next(lstate, -2)) { + switch (lua_type(lstate, -2)) { + case LUA_TSTRING: { + size_t len; + const char *s = lua_tolstring(lstate, -2, &len); + if (memchr(s, NUL, len) != NULL) { + ret.has_string_with_nul = true; + } + ret.string_keys_num++; + break; + } + case LUA_TNUMBER: { + const lua_Number n = lua_tonumber(lstate, -2); + if (n > (lua_Number)SIZE_MAX || n <= 0 + || ((lua_Number)((size_t)n)) != n) { + has_other = true; + } else { + const size_t idx = (size_t)n; + if (idx > ret.maxidx) { + ret.maxidx = idx; + } + } + break; + } + case LUA_TBOOLEAN: { + const bool b = lua_toboolean(lstate, -2); + if (b == TYPE_IDX_VALUE) { + if (lua_type(lstate, -1) == LUA_TNUMBER) { + lua_Number n = lua_tonumber(lstate, -1); + if (n == (lua_Number)kObjectTypeFloat + || n == (lua_Number)kObjectTypeArray + || n == (lua_Number)kObjectTypeDictionary) { + has_type_key = true; + ret.type = (ObjectType)n; + } else { + has_other = true; + } + } else { + has_other = true; + } + } else { + has_val_key = true; + val_type = lua_type(lstate, -1); + if (val_type == LUA_TNUMBER) { + ret.val = lua_tonumber(lstate, -1); + } + } + break; + } + default: { + has_other = true; + break; + } + } + tsize++; + lua_pop(lstate, 1); + } + if (has_type_key) { + if (ret.type == kObjectTypeFloat + && (!has_val_key || val_type != LUA_TNUMBER)) { + ret.type = kObjectTypeNil; + } + } else { + if (tsize == 0 + || (tsize == ret.maxidx && !has_other && ret.string_keys_num == 0)) { + ret.type = kObjectTypeArray; + } else if (ret.string_keys_num == tsize) { + ret.type = kObjectTypeDictionary; + } else { + ret.type = kObjectTypeNil; + } + } + return ret; +} + /// Helper structure for nlua_pop_typval typedef struct { typval_T *tv; ///< Location where conversion result is saved. @@ -63,8 +174,15 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) if (cur.container) { if (cur.special || cur.tv->v_type == VAR_DICT) { assert(cur.tv->v_type == (cur.special ? VAR_LIST : VAR_DICT)); - if (lua_next(lstate, -2)) { - assert(lua_type(lstate, -2) == LUA_TSTRING); + bool next_key_found = false; + while (lua_next(lstate, -2)) { + if (lua_type(lstate, -2) == LUA_TSTRING) { + next_key_found = true; + break; + } + lua_pop(lstate, 1); + } + if (next_key_found) { size_t len; const char *s = lua_tolstring(lstate, -2, &len); if (cur.special) { @@ -109,7 +227,11 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) } } assert(!cur.container); - memset(cur.tv, 0, sizeof(*cur.tv)); + *cur.tv = (typval_T) { + .v_type = VAR_NUMBER, + .v_lock = VAR_UNLOCKED, + .vval = { .v_number = 0 }, + }; switch (lua_type(lstate, -1)) { case LUA_TNIL: { cur.tv->v_type = VAR_SPECIAL; @@ -145,81 +267,64 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) break; } case LUA_TTABLE: { - bool has_string = false; - bool has_string_with_nul = false; - bool has_other = false; - size_t maxidx = 0; - size_t tsize = 0; - lua_pushnil(lstate); - while (lua_next(lstate, -2)) { - switch (lua_type(lstate, -2)) { - case LUA_TSTRING: { - size_t len; - const char *s = lua_tolstring(lstate, -2, &len); - if (memchr(s, NUL, len) != NULL) { - has_string_with_nul = true; - } - has_string = true; - break; - } - case LUA_TNUMBER: { - const lua_Number n = lua_tonumber(lstate, -2); - if (n > (lua_Number)SIZE_MAX || n <= 0 - || ((lua_Number)((size_t)n)) != n) { - has_other = true; - } else { - const size_t idx = (size_t)n; - if (idx > maxidx) { - maxidx = idx; - } - } - break; - } - default: { - has_other = true; - break; - } - } - tsize++; - lua_pop(lstate, 1); - } + const LuaTableProps table_props = nlua_traverse_table(lstate); - if (tsize == 0) { - // Assuming empty list - cur.tv->v_type = VAR_LIST; - cur.tv->vval.v_list = list_alloc(); - cur.tv->vval.v_list->lv_refcount++; - } else if (tsize == maxidx && !has_other && !has_string) { - // Assuming array - cur.tv->v_type = VAR_LIST; - cur.tv->vval.v_list = list_alloc(); - cur.tv->vval.v_list->lv_refcount++; - cur.container = true; - kv_push(stack, cur); - } else if (has_string && !has_other && maxidx == 0) { - // Assuming dictionary - cur.special = has_string_with_nul; - if (has_string_with_nul) { - decode_create_map_special_dict(cur.tv); - assert(cur.tv->v_type = VAR_DICT); - dictitem_T *const val_di = dict_find(cur.tv->vval.v_dict, - (char_u *)"_VAL", 4); - assert(val_di != NULL); - cur.tv = &val_di->di_tv; - assert(cur.tv->v_type == VAR_LIST); - } else { - cur.tv->v_type = VAR_DICT; - cur.tv->vval.v_dict = dict_alloc(); - cur.tv->vval.v_dict->dv_refcount++; + switch (table_props.type) { + case kObjectTypeArray: { + if (table_props.maxidx == 0) { + cur.tv->v_type = VAR_LIST; + cur.tv->vval.v_list = list_alloc(); + cur.tv->vval.v_list->lv_refcount++; + } else { + cur.tv->v_type = VAR_LIST; + cur.tv->vval.v_list = list_alloc(); + cur.tv->vval.v_list->lv_refcount++; + cur.container = true; + kv_push(stack, cur); + } + break; + } + case kObjectTypeDictionary: { + if (table_props.string_keys_num == 0) { + cur.tv->v_type = VAR_DICT; + cur.tv->vval.v_dict = dict_alloc(); + cur.tv->vval.v_dict->dv_refcount++; + } else { + cur.special = table_props.has_string_with_nul; + if (table_props.has_string_with_nul) { + decode_create_map_special_dict(cur.tv); + assert(cur.tv->v_type = VAR_DICT); + dictitem_T *const val_di = dict_find(cur.tv->vval.v_dict, + (char_u *)"_VAL", 4); + assert(val_di != NULL); + cur.tv = &val_di->di_tv; + assert(cur.tv->v_type == VAR_LIST); + } else { + cur.tv->v_type = VAR_DICT; + cur.tv->vval.v_dict = dict_alloc(); + cur.tv->vval.v_dict->dv_refcount++; + } + cur.container = true; + kv_push(stack, cur); + lua_pushnil(lstate); + } + break; + } + case kObjectTypeFloat: { + cur.tv->v_type = VAR_FLOAT; + cur.tv->vval.v_float = (float_T)table_props.val; + break; + } + case kObjectTypeNil: { + EMSG(_("E5100: Cannot convert given lua table: table " + "should either have a sequence of positive integer keys " + "or contain only string keys")); + ret = false; + break; + } + default: { + assert(false); } - cur.container = true; - kv_push(stack, cur); - lua_pushnil(lstate); - } else { - EMSG(_("E5100: Cannot convert given lua table: table " - "should either have a sequence of positive integer keys " - "or contain only string keys")); - ret = false; } break; } @@ -236,7 +341,11 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) kv_destroy(stack); if (!ret) { clear_tv(ret_tv); - memset(ret_tv, 0, sizeof(*ret_tv)); + *ret_tv = (typval_T) { + .v_type = VAR_NUMBER, + .v_lock = VAR_UNLOCKED, + .vval = { .v_number = 0 }, + }; lua_pop(lstate, lua_gettop(lstate) - initial_size + 1); } assert(lua_gettop(lstate) == initial_size - 1); @@ -281,7 +390,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) lua_createtable(lstate, 0, 0) #define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ - TYPVAL_ENCODE_CONV_EMPTY_LIST() + nlua_create_typed_table(lstate, 0, 0, kObjectTypeDictionary) #define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \ do { \ @@ -432,16 +541,7 @@ bool nlua_push_typval(lua_State *lstate, typval_T *const tv) static inline void nlua_push_type_idx(lua_State *lstate) FUNC_ATTR_NONNULL_ALL { - lua_pushboolean(lstate, true); -} - -/// Push value which is a locks index -/// -/// Used for containers tables. -static inline void nlua_push_locks_idx(lua_State *lstate) - FUNC_ATTR_NONNULL_ALL -{ - lua_pushboolean(lstate, false); + lua_pushboolean(lstate, TYPE_IDX_VALUE); } /// Push value which is a value index @@ -450,7 +550,7 @@ static inline void nlua_push_locks_idx(lua_State *lstate) static inline void nlua_push_val_idx(lua_State *lstate) FUNC_ATTR_NONNULL_ALL { - lua_pushnumber(lstate, (lua_Number)0); + lua_pushboolean(lstate, VAL_IDX_VALUE); } /// Push type @@ -458,14 +558,11 @@ static inline void nlua_push_val_idx(lua_State *lstate) /// Type is a value in vim.types table. /// /// @param[out] lstate Lua state. -/// @param[in] type Type to push (key in vim.types table). -static inline void nlua_push_type(lua_State *lstate, const char *const type) +/// @param[in] type Type to push. +static inline void nlua_push_type(lua_State *lstate, ObjectType type) + FUNC_ATTR_NONNULL_ALL { - lua_getglobal(lstate, "vim"); - lua_getfield(lstate, -1, "types"); - lua_remove(lstate, -2); - lua_getfield(lstate, -1, type); - lua_remove(lstate, -2); + lua_pushnumber(lstate, (lua_Number)type); } /// Create lua table which has an entry that determines its VimL type @@ -477,7 +574,7 @@ static inline void nlua_push_type(lua_State *lstate, const char *const type) static inline void nlua_create_typed_table(lua_State *lstate, const size_t narr, const size_t nrec, - const char *const type) + const ObjectType type) FUNC_ATTR_NONNULL_ALL { lua_createtable(lstate, (int)narr, (int)(1 + nrec)); @@ -511,7 +608,7 @@ void nlua_push_Integer(lua_State *lstate, const Integer n) void nlua_push_Float(lua_State *lstate, const Float f) FUNC_ATTR_NONNULL_ALL { - nlua_create_typed_table(lstate, 0, 1, "float"); + nlua_create_typed_table(lstate, 0, 1, kObjectTypeFloat); nlua_push_val_idx(lstate); lua_pushnumber(lstate, (lua_Number)f); lua_rawset(lstate, -3); @@ -526,21 +623,17 @@ void nlua_push_Boolean(lua_State *lstate, const Boolean b) lua_pushboolean(lstate, b); } -static inline void nlua_add_locks_table(lua_State *lstate) -{ - nlua_push_locks_idx(lstate); - lua_newtable(lstate); - lua_rawset(lstate, -3); -} - /// Convert given Dictionary to lua table /// /// Leaves converted table on top of the stack. void nlua_push_Dictionary(lua_State *lstate, const Dictionary dict) FUNC_ATTR_NONNULL_ALL { - nlua_create_typed_table(lstate, 0, 1 + dict.size, "dict"); - nlua_add_locks_table(lstate); + if (dict.size == 0) { + nlua_create_typed_table(lstate, 0, 0, kObjectTypeDictionary); + } else { + lua_createtable(lstate, 0, (int)dict.size); + } for (size_t i = 0; i < dict.size; i++) { nlua_push_String(lstate, dict.items[i].key); nlua_push_Object(lstate, dict.items[i].value); @@ -554,11 +647,10 @@ void nlua_push_Dictionary(lua_State *lstate, const Dictionary dict) void nlua_push_Array(lua_State *lstate, const Array array) FUNC_ATTR_NONNULL_ALL { - nlua_create_typed_table(lstate, array.size, 1, "float"); - nlua_add_locks_table(lstate); + lua_createtable(lstate, (int)array.size, 0); for (size_t i = 0; i < array.size; i++) { nlua_push_Object(lstate, array.items[i]); - lua_rawseti(lstate, -3, (int)i + 1); + lua_rawseti(lstate, -2, (int)i + 1); } } @@ -663,26 +755,33 @@ Boolean nlua_pop_Boolean(lua_State *lstate, Error *err) return ret; } -static inline bool nlua_check_type(lua_State *lstate, Error *err, - const char *const type) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +/// Check whether typed table on top of the stack has given type +/// +/// @param[in] lstate Lua state. +/// @param[out] err Location where error will be saved. May be NULL. +/// @param[in] type Type to check. +/// +/// @return @see nlua_traverse_table(). +static inline LuaTableProps nlua_check_type(lua_State *const lstate, + Error *const err, + const ObjectType type) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT { if (lua_type(lstate, -1) != LUA_TTABLE) { - set_api_error("Expected lua table", err); - return true; + if (err) { + set_api_error("Expected lua table", err); + } + return (LuaTableProps) { .type = kObjectTypeNil }; + } + const LuaTableProps table_props = nlua_traverse_table(lstate); + + if (table_props.type != type) { + if (err) { + set_api_error("Unexpected type", err); + } } - nlua_push_type_idx(lstate); - lua_rawget(lstate, -2); - nlua_push_type(lstate, type); - if (!lua_rawequal(lstate, -2, -1)) { - lua_pop(lstate, 2); - set_api_error("Expected lua table with float type", err); - return true; - } - lua_pop(lstate, 2); - - return false; + return table_props; } /// Convert lua table to float @@ -691,49 +790,32 @@ static inline bool nlua_check_type(lua_State *lstate, Error *err, Float nlua_pop_Float(lua_State *lstate, Error *err) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - Float ret = 0; - - if (nlua_check_type(lstate, err, "float")) { + if (lua_type(lstate, -1) == LUA_TNUMBER) { + const Float ret = (Float)lua_tonumber(lstate, -1); lua_pop(lstate, 1); - return 0; - } - - nlua_push_val_idx(lstate); - lua_rawget(lstate, -2); - - if (!lua_isnumber(lstate, -1)) { - lua_pop(lstate, 2); - set_api_error("Value field should be lua number", err); return ret; } - ret = lua_tonumber(lstate, -1); - lua_pop(lstate, 2); - return ret; + const LuaTableProps table_props = nlua_check_type(lstate, err, + kObjectTypeFloat); + lua_pop(lstate, 1); + if (table_props.type != kObjectTypeFloat) { + return 0; + } else { + return (Float)table_props.val; + } } -/// Convert lua table to array +/// Convert lua table to array without determining whether it is array /// -/// Always pops one value from the stack. -Array nlua_pop_Array(lua_State *lstate, Error *err) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +/// @param lstate Lua state. +/// @param[in] table_props nlua_traverse_table() output. +/// @param[out] err Location where error will be saved. +static Array nlua_pop_Array_unchecked(lua_State *const lstate, + const LuaTableProps table_props, + Error *const err) { - Array ret = { .size = 0, .items = NULL }; - - if (nlua_check_type(lstate, err, "list")) { - lua_pop(lstate, 1); - return ret; - } - - for (int i = 1; ; i++, ret.size++) { - lua_rawgeti(lstate, -1, i); - - if (lua_isnil(lstate, -1)) { - lua_pop(lstate, 1); - break; - } - lua_pop(lstate, 1); - } + Array ret = { .size = table_props.maxidx, .items = NULL }; if (ret.size == 0) { lua_pop(lstate, 1); @@ -748,7 +830,7 @@ Array nlua_pop_Array(lua_State *lstate, Error *err) val = nlua_pop_Object(lstate, err); if (err->set) { - ret.size = i; + ret.size = i - 1; lua_pop(lstate, 1); api_free_array(ret); return (Array) { .size = 0, .items = NULL }; @@ -760,24 +842,35 @@ Array nlua_pop_Array(lua_State *lstate, Error *err) return ret; } -/// Convert lua table to dictionary +/// Convert lua table to array /// -/// Always pops one value from the stack. Does not check whether -/// `vim.is_dict(table[type_idx])` or whether topmost value on the stack is -/// a table. -Dictionary nlua_pop_Dictionary_unchecked(lua_State *lstate, Error *err) +/// Always pops one value from the stack. +Array nlua_pop_Array(lua_State *lstate, Error *err) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - Dictionary ret = { .size = 0, .items = NULL }; - - lua_pushnil(lstate); - - while (lua_next(lstate, -2)) { - if (lua_type(lstate, -2) == LUA_TSTRING) { - ret.size++; - } - lua_pop(lstate, 1); + const LuaTableProps table_props = nlua_check_type(lstate, err, + kObjectTypeArray); + lua_pop(lstate, 1); + if (table_props.type != kObjectTypeArray) { + return (Array) { .size = 0, .items = NULL }; } + return nlua_pop_Array_unchecked(lstate, table_props, err); +} + +/// Convert lua table to dictionary +/// +/// Always pops one value from the stack. Does not check whether whether topmost +/// value on the stack is a table. +/// +/// @param lstate Lua interpreter state. +/// @param[in] table_props nlua_traverse_table() output. +/// @param[out] err Location where error will be saved. +static Dictionary nlua_pop_Dictionary_unchecked(lua_State *lstate, + const LuaTableProps table_props, + Error *err) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + Dictionary ret = { .size = table_props.string_keys_num, .items = NULL }; if (ret.size == 0) { lua_pop(lstate, 1); @@ -786,7 +879,7 @@ Dictionary nlua_pop_Dictionary_unchecked(lua_State *lstate, Error *err) ret.items = xcalloc(ret.size, sizeof(*ret.items)); lua_pushnil(lstate); - for (size_t i = 0; lua_next(lstate, -2);) { + for (size_t i = 0; lua_next(lstate, -2) && i < ret.size;) { // stack: dict, key, value if (lua_type(lstate, -2) == LUA_TSTRING) { @@ -828,12 +921,14 @@ Dictionary nlua_pop_Dictionary_unchecked(lua_State *lstate, Error *err) Dictionary nlua_pop_Dictionary(lua_State *lstate, Error *err) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - if (nlua_check_type(lstate, err, "dict")) { + const LuaTableProps table_props = nlua_check_type(lstate, err, + kObjectTypeDictionary); + if (table_props.type != kObjectTypeDictionary) { lua_pop(lstate, 1); return (Dictionary) { .size = 0, .items = NULL }; } - return nlua_pop_Dictionary_unchecked(lstate, err); + return nlua_pop_Dictionary_unchecked(lstate, table_props, err); } /// Convert lua table to object @@ -856,8 +951,16 @@ Object nlua_pop_Object(lua_State *lstate, Error *err) break; } case LUA_TNUMBER: { - ret.type = kObjectTypeInteger; - ret.data.integer = nlua_pop_Integer(lstate, err); + const lua_Number n = lua_tonumber(lstate, -1); + if (n > (lua_Number)API_INTEGER_MAX || n < (lua_Number)API_INTEGER_MIN + || ((lua_Number)((Integer)n)) != n) { + ret.type = kObjectTypeFloat; + ret.data.floating = (Float)n; + } else { + ret.type = kObjectTypeInteger; + ret.data.integer = (Integer)n; + } + lua_pop(lstate, 1); break; } case LUA_TBOOLEAN: { @@ -866,33 +969,26 @@ Object nlua_pop_Object(lua_State *lstate, Error *err) break; } case LUA_TTABLE: { - lua_getglobal(lstate, "vim"); - // stack: obj, vim -#define CHECK_TYPE(Type, key, vim_type) \ - lua_getfield(lstate, -1, "is_" #vim_type); \ - /* stack: obj, vim, checker */ \ - lua_pushvalue(lstate, -3); \ - /* stack: obj, vim, checker, obj */ \ - lua_call(lstate, 1, 1); \ - /* stack: obj, vim, result */ \ - if (lua_toboolean(lstate, -1)) { \ - lua_pop(lstate, 2); \ - /* stack: obj */ \ - ret.type = kObjectType##Type; \ - ret.data.key = nlua_pop_##Type(lstate, err); \ - /* stack: */ \ - break; \ - } \ - lua_pop(lstate, 1); \ - // stack: obj, vim - CHECK_TYPE(Float, floating, float) - CHECK_TYPE(Array, array, list) - CHECK_TYPE(Dictionary, dictionary, dict) -#undef CHECK_TYPE - lua_pop(lstate, 1); - // stack: obj - ret.type = kObjectTypeDictionary; - ret.data.dictionary = nlua_pop_Dictionary_unchecked(lstate, err); + const LuaTableProps table_props = nlua_traverse_table(lstate); + ret.type = table_props.type; + switch (table_props.type) { + case kObjectTypeArray: { + ret.data.array = nlua_pop_Array_unchecked(lstate, table_props, err); + break; + } + case kObjectTypeDictionary: { + ret.data.dictionary = nlua_pop_Dictionary_unchecked(lstate, + table_props, err); + break; + } + case kObjectTypeFloat: { + ret.data.floating = (Float)table_props.val; + break; + } + default: { + assert(false); + } + } break; } default: { @@ -923,3 +1019,50 @@ GENERATE_INDEX_FUNCTION(Window) GENERATE_INDEX_FUNCTION(Tabpage) #undef GENERATE_INDEX_FUNCTION + +/// Record some auxilary values in vim module +/// +/// Assumes that module table is on top of the stack. +/// +/// Recorded values: +/// +/// `vim.type_idx`: @see nlua_push_type_idx() +/// `vim.val_idx`: @see nlua_push_val_idx() +/// `vim.types`: table mapping possible values of `vim.type_idx` to string +/// names (i.e. `array`, `float`, `dictionary`) and back. +void nlua_init_types(lua_State *const lstate) +{ + LUA_PUSH_STATIC_STRING(lstate, "type_idx"); + nlua_push_type_idx(lstate); + lua_rawset(lstate, -3); + + LUA_PUSH_STATIC_STRING(lstate, "val_idx"); + nlua_push_val_idx(lstate); + lua_rawset(lstate, -3); + + LUA_PUSH_STATIC_STRING(lstate, "types"); + lua_createtable(lstate, 0, 3); + + LUA_PUSH_STATIC_STRING(lstate, "float"); + lua_pushnumber(lstate, (lua_Number)kObjectTypeFloat); + lua_rawset(lstate, -3); + lua_pushnumber(lstate, (lua_Number)kObjectTypeFloat); + LUA_PUSH_STATIC_STRING(lstate, "float"); + lua_rawset(lstate, -3); + + LUA_PUSH_STATIC_STRING(lstate, "array"); + lua_pushnumber(lstate, (lua_Number)kObjectTypeArray); + lua_rawset(lstate, -3); + lua_pushnumber(lstate, (lua_Number)kObjectTypeArray); + LUA_PUSH_STATIC_STRING(lstate, "array"); + lua_rawset(lstate, -3); + + LUA_PUSH_STATIC_STRING(lstate, "dictionary"); + lua_pushnumber(lstate, (lua_Number)kObjectTypeDictionary); + lua_rawset(lstate, -3); + lua_pushnumber(lstate, (lua_Number)kObjectTypeDictionary); + LUA_PUSH_STATIC_STRING(lstate, "dictionary"); + lua_rawset(lstate, -3); + + lua_rawset(lstate, -3); +} diff --git a/src/nvim/viml/executor/executor.c b/src/nvim/viml/executor/executor.c index 1a7b5f8915..33b6870ea0 100644 --- a/src/nvim/viml/executor/executor.c +++ b/src/nvim/viml/executor/executor.c @@ -145,6 +145,7 @@ static int nlua_state_init(lua_State *lstate) FUNC_ATTR_NONNULL_ALL return 1; } nlua_add_api_functions(lstate); + nlua_init_types(lstate); lua_setglobal(lstate, "vim"); return 0; } diff --git a/test/functional/lua_spec.lua b/test/functional/lua_spec.lua index fdfd9b4c2d..4f00189519 100644 --- a/test/functional/lua_spec.lua +++ b/test/functional/lua_spec.lua @@ -8,7 +8,7 @@ local clear = helpers.clear local funcs = helpers.funcs local meths = helpers.meths local exc_exec = helpers.exc_exec -local curbufmeths = helpers.curbufmeths +local redir_exec = helpers.redir_exec local function startswith(expected, actual) eq(expected, actual:sub(1, #expected)) @@ -139,5 +139,97 @@ describe('luaeval() function', function() -- The following should not crash: conversion error happens inside eq("Vim(call):E5101: Cannot convert given lua type", exc_exec('call luaeval("vim.api")')) + -- The following should not show internal error + eq("\nE5101: Cannot convert given lua type\n0", + redir_exec('echo luaeval("vim.api")')) end) + + it('correctly converts containers with type_idx', function() + eq(5, eval('type(luaeval("{[vim.type_idx]=vim.types.float, [vim.val_idx]=0}"))')) + eq(4, eval([[type(luaeval('{[vim.type_idx]=vim.types.dictionary}'))]])) + eq(3, eval([[type(luaeval('{[vim.type_idx]=vim.types.array}'))]])) + + eq({}, funcs.luaeval('{[vim.type_idx]=vim.types.array}')) + + -- Presence of type_idx makes Vim ignore some keys + eq({42}, funcs.luaeval('{[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}')) + eq({foo=2}, funcs.luaeval('{[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}')) + eq(10, funcs.luaeval('{[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}')) + + -- The following should not crash + eq({}, funcs.luaeval('{[vim.type_idx]=vim.types.dictionary}')) + end) + + it('correctly converts from API objects', function() + eq(1, funcs.luaeval('vim.api.vim_eval("1")')) + eq('1', funcs.luaeval([[vim.api.vim_eval('"1"')]])) + eq({}, funcs.luaeval('vim.api.vim_eval("[]")')) + eq({}, funcs.luaeval('vim.api.vim_eval("{}")')) + eq(1, funcs.luaeval('vim.api.vim_eval("1.0")')) + eq(true, funcs.luaeval('vim.api.vim_eval("v:true")')) + eq(false, funcs.luaeval('vim.api.vim_eval("v:false")')) + eq(NIL, funcs.luaeval('vim.api.vim_eval("v:null")')) + + eq(0, eval([[type(luaeval('vim.api.vim_eval("1")'))]])) + eq(1, eval([[type(luaeval('vim.api.vim_eval("''1''")'))]])) + eq(3, eval([[type(luaeval('vim.api.vim_eval("[]")'))]])) + eq(4, eval([[type(luaeval('vim.api.vim_eval("{}")'))]])) + eq(5, eval([[type(luaeval('vim.api.vim_eval("1.0")'))]])) + eq(6, eval([[type(luaeval('vim.api.vim_eval("v:true")'))]])) + eq(6, eval([[type(luaeval('vim.api.vim_eval("v:false")'))]])) + eq(7, eval([[type(luaeval('vim.api.vim_eval("v:null")'))]])) + + eq({foo=42}, funcs.luaeval([[vim.api.vim_eval('{"foo": 42}')]])) + eq({42}, funcs.luaeval([[vim.api.vim_eval('[42]')]])) + + eq({foo={bar=42}, baz=50}, funcs.luaeval([[vim.api.vim_eval('{"foo": {"bar": 42}, "baz": 50}')]])) + eq({{42}, {}}, funcs.luaeval([=[vim.api.vim_eval('[[42], []]')]=])) + end) + + it('correctly converts to API objects', function() + eq(1, funcs.luaeval('vim.api._vim_id(1)')) + eq('1', funcs.luaeval('vim.api._vim_id("1")')) + eq({1}, funcs.luaeval('vim.api._vim_id({1})')) + eq({foo=1}, funcs.luaeval('vim.api._vim_id({foo=1})')) + eq(1.5, funcs.luaeval('vim.api._vim_id(1.5)')) + eq(true, funcs.luaeval('vim.api._vim_id(true)')) + eq(false, funcs.luaeval('vim.api._vim_id(false)')) + eq(NIL, funcs.luaeval('vim.api._vim_id(nil)')) + + eq(0, eval([[type(luaeval('vim.api._vim_id(1)'))]])) + eq(1, eval([[type(luaeval('vim.api._vim_id("1")'))]])) + eq(3, eval([[type(luaeval('vim.api._vim_id({1})'))]])) + eq(4, eval([[type(luaeval('vim.api._vim_id({foo=1})'))]])) + eq(5, eval([[type(luaeval('vim.api._vim_id(1.5)'))]])) + eq(6, eval([[type(luaeval('vim.api._vim_id(true)'))]])) + eq(6, eval([[type(luaeval('vim.api._vim_id(false)'))]])) + eq(7, eval([[type(luaeval('vim.api._vim_id(nil)'))]])) + + eq({foo=1, bar={42, {{baz=true}, 5}}}, funcs.luaeval('vim.api._vim_id({foo=1, bar={42, {{baz=true}, 5}}})')) + end) + + it('correctly converts containers with type_idx to API objects', function() + -- TODO: Similar tests with _vim_array_id and _vim_dictionary_id, that will + -- follow slightly different code paths. + eq(5, eval('type(luaeval("vim.api._vim_id({[vim.type_idx]=vim.types.float, [vim.val_idx]=0})"))')) + eq(4, eval([[type(luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.dictionary})'))]])) + eq(3, eval([[type(luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.array})'))]])) + + eq({}, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.array})')) + + -- Presence of type_idx makes Vim ignore some keys + -- FIXME + -- eq({42}, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) + eq({foo=2}, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) + eq(10, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) + end) + -- TODO: check what happens when it errors out on second list item +--[[FIXME + [ + [ it('correctly converts self-containing containers', function() + [ meths.set_var('l', {}) + [ eval('add(l, l)') + [ eq(true, eval('luaeval("_A == _A[1]", l)')) + [ end) + ]] end) From 3fa4ca81880bc5113c32a89de965ce593e9b001f Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 12 Jul 2016 19:04:12 +0300 Subject: [PATCH 0108/1671] executor/converter: Fix conversion of self-containing containers --- src/nvim/viml/executor/converter.c | 2 +- test/functional/lua_spec.lua | 27 +++++++++++++++++++-------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/nvim/viml/executor/converter.c b/src/nvim/viml/executor/converter.c index c127b87738..319e07bdbc 100644 --- a/src/nvim/viml/executor/converter.c +++ b/src/nvim/viml/executor/converter.c @@ -446,7 +446,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) ? (void *)mpval.data.d.dict == (void *)(val) \ : (void *)mpval.data.l.list == (void *)(val)) { \ lua_pushvalue(lstate, \ - 1 - ((int)((kv_size(*mpstack) - backref + 1) * 2))); \ + -((int)((kv_size(*mpstack) - backref + 1) * 2))); \ break; \ } \ } \ diff --git a/test/functional/lua_spec.lua b/test/functional/lua_spec.lua index 4f00189519..5c6dee90a3 100644 --- a/test/functional/lua_spec.lua +++ b/test/functional/lua_spec.lua @@ -224,12 +224,23 @@ describe('luaeval() function', function() eq(10, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) end) -- TODO: check what happens when it errors out on second list item ---[[FIXME - [ - [ it('correctly converts self-containing containers', function() - [ meths.set_var('l', {}) - [ eval('add(l, l)') - [ eq(true, eval('luaeval("_A == _A[1]", l)')) - [ end) - ]] + -- TODO: check what happens if API function receives wrong number of + -- arguments. + -- TODO: check what happens if API function receives wrong argument types. + + it('correctly converts self-containing containers', function() + meths.set_var('l', {}) + eval('add(l, l)') + eq(true, eval('luaeval("_A == _A[1]", l)')) + eq(true, eval('luaeval("_A[1] == _A[1][1]", [l])')) + eq(true, eval('luaeval("_A.d == _A.d[1]", {"d": l})')) + eq(true, eval('luaeval("_A ~= _A[1]", [l])')) + + meths.set_var('d', {foo=42}) + eval('extend(d, {"d": d})') + eq(true, eval('luaeval("_A == _A.d", d)')) + eq(true, eval('luaeval("_A[1] == _A[1].d", [d])')) + eq(true, eval('luaeval("_A.d == _A.d.d", {"d": d})')) + eq(true, eval('luaeval("_A ~= _A.d", {"d": d})')) + end) end) From 9297d941e2f1576006d77bfd6391cecc3bea37b0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 12 Jul 2016 19:20:57 +0300 Subject: [PATCH 0109/1671] executor/converter: Fix how maxidx is determined --- src/nvim/viml/executor/converter.c | 37 ++++++++++++++++++++++++------ test/functional/lua_spec.lua | 4 ++-- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/nvim/viml/executor/converter.c b/src/nvim/viml/executor/converter.c index 319e07bdbc..c8d6848006 100644 --- a/src/nvim/viml/executor/converter.c +++ b/src/nvim/viml/executor/converter.c @@ -54,8 +54,8 @@ static LuaTableProps nlua_traverse_table(lua_State *const lstate) int val_type = 0; // If has_val_key: lua type of the value. bool has_val_key = false; // True if val key was found, // @see nlua_push_val_idx(). - bool has_other = false; // True if there are keys that are not strings - // or positive integral values. + size_t other_keys_num = 0; // Number of keys that are not string, integral + // or type keys. LuaTableProps ret; memset(&ret, 0, sizeof(ret)); if (!lua_checkstack(lstate, lua_gettop(lstate) + 2)) { @@ -79,7 +79,7 @@ static LuaTableProps nlua_traverse_table(lua_State *const lstate) const lua_Number n = lua_tonumber(lstate, -2); if (n > (lua_Number)SIZE_MAX || n <= 0 || ((lua_Number)((size_t)n)) != n) { - has_other = true; + other_keys_num++; } else { const size_t idx = (size_t)n; if (idx > ret.maxidx) { @@ -99,10 +99,10 @@ static LuaTableProps nlua_traverse_table(lua_State *const lstate) has_type_key = true; ret.type = (ObjectType)n; } else { - has_other = true; + other_keys_num++; } } else { - has_other = true; + other_keys_num++; } } else { has_val_key = true; @@ -114,7 +114,7 @@ static LuaTableProps nlua_traverse_table(lua_State *const lstate) break; } default: { - has_other = true; + other_keys_num++; break; } } @@ -125,10 +125,33 @@ static LuaTableProps nlua_traverse_table(lua_State *const lstate) if (ret.type == kObjectTypeFloat && (!has_val_key || val_type != LUA_TNUMBER)) { ret.type = kObjectTypeNil; + } else if (ret.type == kObjectTypeArray) { + // Determine what is the last number in a *sequence* of keys. + // This condition makes sure that Neovim will not crash when it gets table + // {[vim.type_idx]=vim.types.array, [SIZE_MAX]=1}: without it maxidx will + // be SIZE_MAX, with this condition it should be zero and [SIZE_MAX] key + // should be ignored. + if (ret.maxidx != 0 + && ret.maxidx != (tsize + - has_type_key + - other_keys_num + - has_val_key + - ret.string_keys_num)) { + for (ret.maxidx = 0;; ret.maxidx++) { + lua_rawgeti(lstate, -1, (int)ret.maxidx + 1); + if (lua_isnil(lstate, -1)) { + lua_pop(lstate, 1); + break; + } + lua_pop(lstate, 1); + } + } } } else { if (tsize == 0 - || (tsize == ret.maxidx && !has_other && ret.string_keys_num == 0)) { + || (tsize == ret.maxidx + && other_keys_num == 0 + && ret.string_keys_num == 0)) { ret.type = kObjectTypeArray; } else if (ret.string_keys_num == tsize) { ret.type = kObjectTypeDictionary; diff --git a/test/functional/lua_spec.lua b/test/functional/lua_spec.lua index 5c6dee90a3..e16f41dba2 100644 --- a/test/functional/lua_spec.lua +++ b/test/functional/lua_spec.lua @@ -218,10 +218,10 @@ describe('luaeval() function', function() eq({}, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.array})')) -- Presence of type_idx makes Vim ignore some keys - -- FIXME - -- eq({42}, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) + eq({42}, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) eq({foo=2}, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) eq(10, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) + eq({}, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2})')) end) -- TODO: check what happens when it errors out on second list item -- TODO: check what happens if API function receives wrong number of From 425d348f0f9f680a44af31fc3cecd20a07374bb5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Jul 2016 00:34:24 +0300 Subject: [PATCH 0110/1671] executor/converter: Make nlua_pop_Object not recursive --- src/nvim/api/private/defs.h | 2 +- src/nvim/api/vim.c | 54 ++++-- src/nvim/viml/executor/converter.c | 257 ++++++++++++++++++++--------- test/functional/lua_spec.lua | 37 ++++- 4 files changed, 248 insertions(+), 102 deletions(-) diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index 223aab09dc..86b549cb44 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -76,10 +76,10 @@ typedef struct { } Dictionary; typedef enum { + kObjectTypeNil = 0, kObjectTypeBuffer, kObjectTypeWindow, kObjectTypeTabpage, - kObjectTypeNil, kObjectTypeBoolean, kObjectTypeInteger, kObjectTypeFloat, diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 24959e9a59..5d862628cb 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -197,19 +197,6 @@ Object nvim_eval(String expr, Error *err) return rv; } -/// Returns object given as argument -/// -/// This API function is used for testing. One should not rely on its presence -/// in plugins. -/// -/// @param[in] obj Object to return. -/// -/// @return its argument. -Object _vim_id(Object obj) -{ - return obj; -} - /// Calls a VimL function with the given arguments /// /// On VimL error: Returns a generic error; v:errmsg is not updated. @@ -843,3 +830,44 @@ static void write_msg(String message, bool to_err) --no_wait_return; msg_end(); } + +// Functions used for testing purposes + +/// Returns object given as argument +/// +/// This API function is used for testing. One should not rely on its presence +/// in plugins. +/// +/// @param[in] obj Object to return. +/// +/// @return its argument. +Object _vim_id(Object obj) +{ + return obj; +} + +/// Returns array given as argument +/// +/// This API function is used for testing. One should not rely on its presence +/// in plugins. +/// +/// @param[in] arr Array to return. +/// +/// @return its argument. +Array _vim_id_array(Array arr) +{ + return arr; +} + +/// Returns dictionary given as argument +/// +/// This API function is used for testing. One should not rely on its presence +/// in plugins. +/// +/// @param[in] dct Dictionary to return. +/// +/// @return its argument. +Dictionary _vim_id_dictionary(Dictionary dct) +{ + return dct; +} diff --git a/src/nvim/viml/executor/converter.c b/src/nvim/viml/executor/converter.c index c8d6848006..6c9b42b38c 100644 --- a/src/nvim/viml/executor/converter.c +++ b/src/nvim/viml/executor/converter.c @@ -58,7 +58,7 @@ static LuaTableProps nlua_traverse_table(lua_State *const lstate) // or type keys. LuaTableProps ret; memset(&ret, 0, sizeof(ret)); - if (!lua_checkstack(lstate, lua_gettop(lstate) + 2)) { + if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { emsgf(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 2); ret.type = kObjectTypeNil; return ret; @@ -168,7 +168,7 @@ typedef struct { bool container; ///< True if tv is a container. bool special; ///< If true then tv is a _VAL part of special dictionary ///< that represents mapping. -} PopStackItem; +} TVPopStackItem; /// Convert lua object to VimL typval_T /// @@ -182,18 +182,16 @@ typedef struct { bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) { bool ret = true; -#ifndef NDEBUG const int initial_size = lua_gettop(lstate); -#endif - kvec_t(PopStackItem) stack = KV_INITIAL_VALUE; - kv_push(stack, ((PopStackItem) { ret_tv, false, false })); + kvec_t(TVPopStackItem) stack = KV_INITIAL_VALUE; + kv_push(stack, ((TVPopStackItem) { ret_tv, false, false })); while (ret && kv_size(stack)) { if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { emsgf(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 3); ret = false; break; } - PopStackItem cur = kv_pop(stack); + TVPopStackItem cur = kv_pop(stack); if (cur.container) { if (cur.special || cur.tv->v_type == VAR_DICT) { assert(cur.tv->v_type == (cur.special ? VAR_LIST : VAR_DICT)); @@ -222,14 +220,14 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) listitem_T *const val = listitem_alloc(); list_append(kv_pair, val); kv_push(stack, cur); - cur = (PopStackItem) { &val->li_tv, false, false }; + cur = (TVPopStackItem) { &val->li_tv, false, false }; } else { dictitem_T *const di = dictitem_alloc_len(s, len); if (dict_add(cur.tv->vval.v_dict, di) == FAIL) { assert(false); } kv_push(stack, cur); - cur = (PopStackItem) { &di->di_tv, false, false }; + cur = (TVPopStackItem) { &di->di_tv, false, false }; } } else { lua_pop(lstate, 1); @@ -239,14 +237,13 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) assert(cur.tv->v_type == VAR_LIST); lua_rawgeti(lstate, -1, cur.tv->vval.v_list->lv_len + 1); if (lua_isnil(lstate, -1)) { - lua_pop(lstate, 1); - lua_pop(lstate, 1); + lua_pop(lstate, 2); continue; } listitem_T *li = listitem_alloc(); list_append(cur.tv->vval.v_list, li); kv_push(stack, cur); - cur = (PopStackItem) { &li->li_tv, false, false }; + cur = (TVPopStackItem) { &li->li_tv, false, false }; } } assert(!cur.container); @@ -294,14 +291,10 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) switch (table_props.type) { case kObjectTypeArray: { - if (table_props.maxidx == 0) { - cur.tv->v_type = VAR_LIST; - cur.tv->vval.v_list = list_alloc(); - cur.tv->vval.v_list->lv_refcount++; - } else { - cur.tv->v_type = VAR_LIST; - cur.tv->vval.v_list = list_alloc(); - cur.tv->vval.v_list->lv_refcount++; + cur.tv->v_type = VAR_LIST; + cur.tv->vval.v_list = list_alloc(); + cur.tv->vval.v_list->lv_refcount++; + if (table_props.maxidx != 0) { cur.container = true; kv_push(stack, cur); } @@ -525,7 +518,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) bool nlua_push_typval(lua_State *lstate, typval_T *const tv) { const int initial_size = lua_gettop(lstate); - if (!lua_checkstack(lstate, initial_size + 1)) { + if (!lua_checkstack(lstate, initial_size + 2)) { emsgf(_("E1502: Lua failed to grow stack to %i"), initial_size + 4); return false; } @@ -873,7 +866,6 @@ Array nlua_pop_Array(lua_State *lstate, Error *err) { const LuaTableProps table_props = nlua_check_type(lstate, err, kObjectTypeArray); - lua_pop(lstate, 1); if (table_props.type != kObjectTypeArray) { return (Array) { .size = 0, .items = NULL }; } @@ -954,76 +946,179 @@ Dictionary nlua_pop_Dictionary(lua_State *lstate, Error *err) return nlua_pop_Dictionary_unchecked(lstate, table_props, err); } +/// Helper structure for nlua_pop_Object +typedef struct { + Object *obj; ///< Location where conversion result is saved. + bool container; ///< True if tv is a container. +} ObjPopStackItem; + /// Convert lua table to object /// /// Always pops one value from the stack. -Object nlua_pop_Object(lua_State *lstate, Error *err) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +Object nlua_pop_Object(lua_State *const lstate, Error *const err) { - Object ret = { .type = kObjectTypeNil }; - - switch (lua_type(lstate, -1)) { - case LUA_TNIL: { - ret.type = kObjectTypeNil; - lua_pop(lstate, 1); + Object ret = NIL; + const int initial_size = lua_gettop(lstate); + kvec_t(ObjPopStackItem) stack = KV_INITIAL_VALUE; + kv_push(stack, ((ObjPopStackItem) { &ret, false })); + while (!err->set && kv_size(stack)) { + if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { + api_set_error(err, Exception, "Lua failed to grow stack"); break; } - case LUA_TSTRING: { - ret.type = kObjectTypeString; - ret.data.string = nlua_pop_String(lstate, err); - break; - } - case LUA_TNUMBER: { - const lua_Number n = lua_tonumber(lstate, -1); - if (n > (lua_Number)API_INTEGER_MAX || n < (lua_Number)API_INTEGER_MIN - || ((lua_Number)((Integer)n)) != n) { - ret.type = kObjectTypeFloat; - ret.data.floating = (Float)n; + ObjPopStackItem cur = kv_pop(stack); + if (cur.container) { + if (cur.obj->type == kObjectTypeDictionary) { + // stack: …, dict, key + if (cur.obj->data.dictionary.size + == cur.obj->data.dictionary.capacity) { + lua_pop(lstate, 2); + continue; + } + bool next_key_found = false; + while (lua_next(lstate, -2)) { + // stack: …, dict, new key, val + if (lua_type(lstate, -2) == LUA_TSTRING) { + next_key_found = true; + break; + } + lua_pop(lstate, 1); + // stack: …, dict, new key + } + if (next_key_found) { + // stack: …, dict, new key, val + size_t len; + const char *s = lua_tolstring(lstate, -2, &len); + const size_t idx = cur.obj->data.dictionary.size++; + cur.obj->data.dictionary.items[idx].key = (String) { + .data = xmemdupz(s, len), + .size = len, + }; + kv_push(stack, cur); + cur = (ObjPopStackItem) { + .obj = &cur.obj->data.dictionary.items[idx].value, + .container = false, + }; + } else { + // stack: …, dict + lua_pop(lstate, 1); + // stack: … + continue; + } } else { - ret.type = kObjectTypeInteger; - ret.data.integer = (Integer)n; + if (cur.obj->data.array.size == cur.obj->data.array.capacity) { + lua_pop(lstate, 1); + continue; + } + const size_t idx = cur.obj->data.array.size++; + lua_rawgeti(lstate, -1, (int)idx + 1); + if (lua_isnil(lstate, -1)) { + lua_pop(lstate, 2); + continue; + } + kv_push(stack, cur); + cur = (ObjPopStackItem) { + .obj = &cur.obj->data.array.items[idx], + .container = false, + }; } - lua_pop(lstate, 1); - break; } - case LUA_TBOOLEAN: { - ret.type = kObjectTypeBoolean; - ret.data.boolean = nlua_pop_Boolean(lstate, err); - break; - } - case LUA_TTABLE: { - const LuaTableProps table_props = nlua_traverse_table(lstate); - ret.type = table_props.type; - switch (table_props.type) { - case kObjectTypeArray: { - ret.data.array = nlua_pop_Array_unchecked(lstate, table_props, err); - break; - } - case kObjectTypeDictionary: { - ret.data.dictionary = nlua_pop_Dictionary_unchecked(lstate, - table_props, err); - break; - } - case kObjectTypeFloat: { - ret.data.floating = (Float)table_props.val; - break; - } - default: { - assert(false); - } + assert(!cur.container); + *cur.obj = NIL; + switch (lua_type(lstate, -1)) { + case LUA_TNIL: { + break; } - break; - } - default: { - lua_pop(lstate, 1); - set_api_error("Cannot convert given lua type", err); - break; - } - } - if (err->set) { - ret.type = kObjectTypeNil; - } + case LUA_TBOOLEAN: { + *cur.obj = BOOLEAN_OBJ(lua_toboolean(lstate, -1)); + break; + } + case LUA_TSTRING: { + size_t len; + const char *s = lua_tolstring(lstate, -1, &len); + *cur.obj = STRING_OBJ(((String) { + .data = xmemdupz(s, len), + .size = len, + })); + break; + } + case LUA_TNUMBER: { + const lua_Number n = lua_tonumber(lstate, -1); + if (n > (lua_Number)API_INTEGER_MAX || n < (lua_Number)API_INTEGER_MIN + || ((lua_Number)((Integer)n)) != n) { + *cur.obj = FLOATING_OBJ((Float)n); + } else { + *cur.obj = INTEGER_OBJ((Integer)n); + } + break; + } + case LUA_TTABLE: { + const LuaTableProps table_props = nlua_traverse_table(lstate); + switch (table_props.type) { + case kObjectTypeArray: { + *cur.obj = ARRAY_OBJ(((Array) { + .items = NULL, + .size = 0, + .capacity = 0, + })); + if (table_props.maxidx != 0) { + cur.obj->data.array.items = + xcalloc(table_props.maxidx, + sizeof(cur.obj->data.array.items[0])); + cur.obj->data.array.capacity = table_props.maxidx; + cur.container = true; + kv_push(stack, cur); + } + break; + } + case kObjectTypeDictionary: { + *cur.obj = DICTIONARY_OBJ(((Dictionary) { + .items = NULL, + .size = 0, + .capacity = 0, + })); + if (table_props.string_keys_num != 0) { + cur.obj->data.dictionary.items = + xcalloc(table_props.string_keys_num, + sizeof(cur.obj->data.dictionary.items[0])); + cur.obj->data.dictionary.capacity = table_props.string_keys_num; + cur.container = true; + kv_push(stack, cur); + lua_pushnil(lstate); + } + break; + } + case kObjectTypeFloat: { + *cur.obj = FLOATING_OBJ((Float)table_props.val); + break; + } + case kObjectTypeNil: { + api_set_error(err, Validation, "Cannot convert given lua table"); + break; + } + default: { + assert(false); + } + } + break; + } + default: { + api_set_error(err, Validation, "Cannot convert given lua type"); + break; + } + } + if (!cur.container) { + lua_pop(lstate, 1); + } + } + kv_destroy(stack); + if (err->set) { + api_free_object(ret); + ret = NIL; + lua_pop(lstate, lua_gettop(lstate) - initial_size + 1); + } + assert(lua_gettop(lstate) == initial_size - 1); return ret; } diff --git a/test/functional/lua_spec.lua b/test/functional/lua_spec.lua index e16f41dba2..bd1ed51c2d 100644 --- a/test/functional/lua_spec.lua +++ b/test/functional/lua_spec.lua @@ -31,6 +31,9 @@ describe('luaeval() function', function() nested_by_level[i] = {o=nested, s=nested_s} end + -- Not checked: funcrefs converted to NIL. To be altered to something more + -- meaningful later. + it('correctly evaluates scalars', function() eq(1, funcs.luaeval('1')) eq(0, eval('type(luaeval("1"))')) @@ -135,6 +138,10 @@ describe('luaeval() function', function() exc_exec('call luaeval("1, 2, 3")')) startswith("Vim(call):E5108: Error while calling lua chunk for luaeval(): ", exc_exec('call luaeval("(nil)()")')) + eq("Vim(call):E5101: Cannot convert given lua type", + exc_exec('call luaeval("{42, vim.api}")')) + eq("Vim(call):E5101: Cannot convert given lua type", + exc_exec('call luaeval("{foo=42, baz=vim.api}")')) -- The following should not crash: conversion error happens inside eq("Vim(call):E5101: Cannot convert given lua type", @@ -208,9 +215,7 @@ describe('luaeval() function', function() eq({foo=1, bar={42, {{baz=true}, 5}}}, funcs.luaeval('vim.api._vim_id({foo=1, bar={42, {{baz=true}, 5}}})')) end) - it('correctly converts containers with type_idx to API objects', function() - -- TODO: Similar tests with _vim_array_id and _vim_dictionary_id, that will - -- follow slightly different code paths. + it('correctly converts container objects with type_idx to API objects', function() eq(5, eval('type(luaeval("vim.api._vim_id({[vim.type_idx]=vim.types.float, [vim.val_idx]=0})"))')) eq(4, eval([[type(luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.dictionary})'))]])) eq(3, eval([[type(luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.array})'))]])) @@ -223,10 +228,18 @@ describe('luaeval() function', function() eq(10, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) eq({}, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2})')) end) - -- TODO: check what happens when it errors out on second list item - -- TODO: check what happens if API function receives wrong number of - -- arguments. - -- TODO: check what happens if API function receives wrong argument types. + + it('correctly converts arrays with type_idx to API objects', function() + eq(3, eval([[type(luaeval('vim.api._vim_id_array({[vim.type_idx]=vim.types.array})'))]])) + + eq({}, funcs.luaeval('vim.api._vim_id_array({[vim.type_idx]=vim.types.array})')) + + -- Presence of type_idx makes Vim ignore some keys + eq({42}, funcs.luaeval('vim.api._vim_id_array({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) + eq({{foo=2}}, funcs.luaeval('vim.api._vim_id_array({{[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})')) + eq({10}, funcs.luaeval('vim.api._vim_id_array({{[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})')) + eq({}, funcs.luaeval('vim.api._vim_id_array({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2})')) + end) it('correctly converts self-containing containers', function() meths.set_var('l', {}) @@ -243,4 +256,14 @@ describe('luaeval() function', function() eq(true, eval('luaeval("_A.d == _A.d.d", {"d": d})')) eq(true, eval('luaeval("_A ~= _A.d", {"d": d})')) end) + + it('errors out correctly when working with API', function() + eq(0, exc_exec([[call luaeval("vim.api.id")]])) + end) + + -- TODO: check buffer/window/etc. + -- TODO: check what happens when it errors out on second list item + -- TODO: check what happens if API function receives wrong number of + -- arguments. + -- TODO: check what happens if API function receives wrong argument types. end) From 7a013e93e0364f78a2bc04eadaaeeaa689d0258a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Jul 2016 00:48:25 +0300 Subject: [PATCH 0111/1671] executor/converter: Make it possible to supply `{}` to Dictionary arg --- src/nvim/viml/executor/converter.c | 16 ++++++++++------ test/functional/lua_spec.lua | 20 +++++++++++++++++++- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/nvim/viml/executor/converter.c b/src/nvim/viml/executor/converter.c index 6c9b42b38c..a6399500f2 100644 --- a/src/nvim/viml/executor/converter.c +++ b/src/nvim/viml/executor/converter.c @@ -33,6 +33,7 @@ typedef struct { ///< either kObjectTypeNil, kObjectTypeDictionary or ///< kObjectTypeArray, depending on other properties. lua_Number val; ///< If has_val_key and val_type == LUA_TNUMBER: value. + bool has_type_key; ///< True if type key is present. } LuaTableProps; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -48,8 +49,6 @@ typedef struct { static LuaTableProps nlua_traverse_table(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - bool has_type_key = false; // True if type key was found, - // @see nlua_push_type_idx(). size_t tsize = 0; // Total number of keys. int val_type = 0; // If has_val_key: lua type of the value. bool has_val_key = false; // True if val key was found, @@ -96,7 +95,7 @@ static LuaTableProps nlua_traverse_table(lua_State *const lstate) if (n == (lua_Number)kObjectTypeFloat || n == (lua_Number)kObjectTypeArray || n == (lua_Number)kObjectTypeDictionary) { - has_type_key = true; + ret.has_type_key = true; ret.type = (ObjectType)n; } else { other_keys_num++; @@ -121,7 +120,7 @@ static LuaTableProps nlua_traverse_table(lua_State *const lstate) tsize++; lua_pop(lstate, 1); } - if (has_type_key) { + if (ret.has_type_key) { if (ret.type == kObjectTypeFloat && (!has_val_key || val_type != LUA_TNUMBER)) { ret.type = kObjectTypeNil; @@ -133,7 +132,7 @@ static LuaTableProps nlua_traverse_table(lua_State *const lstate) // should be ignored. if (ret.maxidx != 0 && ret.maxidx != (tsize - - has_type_key + - ret.has_type_key - other_keys_num - has_val_key - ret.string_keys_num)) { @@ -789,7 +788,12 @@ static inline LuaTableProps nlua_check_type(lua_State *const lstate, } return (LuaTableProps) { .type = kObjectTypeNil }; } - const LuaTableProps table_props = nlua_traverse_table(lstate); + LuaTableProps table_props = nlua_traverse_table(lstate); + + if (type == kObjectTypeDictionary && table_props.type == kObjectTypeArray + && table_props.maxidx == 0 && !table_props.has_type_key) { + table_props.type = kObjectTypeDictionary; + } if (table_props.type != type) { if (err) { diff --git a/test/functional/lua_spec.lua b/test/functional/lua_spec.lua index bd1ed51c2d..082efe4c0e 100644 --- a/test/functional/lua_spec.lua +++ b/test/functional/lua_spec.lua @@ -234,11 +234,29 @@ describe('luaeval() function', function() eq({}, funcs.luaeval('vim.api._vim_id_array({[vim.type_idx]=vim.types.array})')) - -- Presence of type_idx makes Vim ignore some keys eq({42}, funcs.luaeval('vim.api._vim_id_array({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) eq({{foo=2}}, funcs.luaeval('vim.api._vim_id_array({{[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})')) eq({10}, funcs.luaeval('vim.api._vim_id_array({{[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})')) eq({}, funcs.luaeval('vim.api._vim_id_array({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2})')) + + eq({}, funcs.luaeval('vim.api._vim_id_array({})')) + eq(3, eval([[type(luaeval('vim.api._vim_id_array({})'))]])) + end) + + it('correctly converts dictionaries with type_idx to API objects', function() + eq(4, eval([[type(luaeval('vim.api._vim_id_dictionary({[vim.type_idx]=vim.types.dictionary})'))]])) + + eq({}, funcs.luaeval('vim.api._vim_id_dictionary({[vim.type_idx]=vim.types.dictionary})')) + + eq({v={42}}, funcs.luaeval('vim.api._vim_id_dictionary({v={[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})')) + eq({foo=2}, funcs.luaeval('vim.api._vim_id_dictionary({[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) + eq({v=10}, funcs.luaeval('vim.api._vim_id_dictionary({v={[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})')) + eq({v={}}, funcs.luaeval('vim.api._vim_id_dictionary({v={[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2}})')) + + -- If API requests dictionary, then empty table will be the one. This is not + -- the case normally because empty table is an empty arrray. + eq({}, funcs.luaeval('vim.api._vim_id_dictionary({})')) + eq(4, eval([[type(luaeval('vim.api._vim_id_dictionary({})'))]])) end) it('correctly converts self-containing containers', function() From ba2f615cd40d5d809d1a141c7b098e3bd22ff7bb Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Jul 2016 02:26:04 +0300 Subject: [PATCH 0112/1671] functests: Test for error conditions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During testing found the following bugs: 1. msgpack-gen.lua script is completely unprepared for Float values either in return type or in arguments. Specifically: 1. At the time of writing relevant code FLOAT_OBJ did not exist as well as FLOATING_OBJ, but it would be used by msgpack-gen.lua should return type be Float. I added FLOATING_OBJ macros later because did not know that msgpack-gen.lua uses these _OBJ macros, otherwise it would be FLOAT_OBJ. 2. msgpack-gen.lua should use .data.floating in place of .data.float. But it did not expect that .data subattribute may have name different from lowercased type name. 2. vim_replace_termcodes returned its argument as-is if it receives an empty string (as well as _vim_id*() functions did). But if something in returned argument lives in an allocated memory such action will cause double free: once when freeing arguments, then when freeing return value. It did not cause problems yet because msgpack bindings return empty string as {NULL, 0} and nothing was actually allocated. 3. New code in msgpack-gen.lua popped arguments in reversed order, making lua bindings’ signatures be different from API ones. --- scripts/genmsgpack.lua | 58 ++++++++++++++++++++---------- src/nvim/api/private/helpers.c | 2 +- src/nvim/api/private/helpers.h | 2 +- src/nvim/api/vim.c | 21 ++++++++--- src/nvim/msgpack_rpc/helpers.c | 2 +- src/nvim/viml/executor/converter.c | 41 ++++++++++----------- test/functional/api/vim_spec.lua | 11 ++++++ test/functional/lua_spec.lua | 51 +++++++++++++++++++++++--- 8 files changed, 138 insertions(+), 50 deletions(-) diff --git a/scripts/genmsgpack.lua b/scripts/genmsgpack.lua index dd3caab5e4..d47d637548 100644 --- a/scripts/genmsgpack.lua +++ b/scripts/genmsgpack.lua @@ -216,6 +216,14 @@ local function real_type(type) return rv end +local function attr_name(rt) + if rt == 'Float' then + return 'floating' + else + return rt:lower() + end +end + -- start the handler functions. Visit each function metadata to build the -- handler function with code generated for validating arguments and calling to -- the real API. @@ -253,7 +261,7 @@ for i = 1, #functions do output:write('\n '..converted..' = (handle_T)args.items['..(j - 1)..'].data.integer;') else output:write('\n if (args.items['..(j - 1)..'].type == kObjectType'..rt..') {') - output:write('\n '..converted..' = args.items['..(j - 1)..'].data.'..rt:lower()..';') + output:write('\n '..converted..' = args.items['..(j - 1)..'].data.'..attr_name(rt)..';') end if rt:match('^Buffer$') or rt:match('^Window$') or rt:match('^Tabpage$') or rt:match('^Boolean$') then -- accept nonnegative integers for Booleans, Buffers, Windows and Tabpages @@ -368,6 +376,7 @@ output:write([[ #include "nvim/func_attr.h" #include "nvim/api/private/defs.h" +#include "nvim/api/private/helpers.h" #include "nvim/viml/executor/converter.h" ]]) include_headers(output, headers) @@ -382,27 +391,35 @@ local function process_function(fn) static int %s(lua_State *lstate) { Error err = {.set = false}; - ]], lua_c_function_name)) + if (lua_gettop(lstate) != %i) { + api_set_error(&err, Validation, "Expected %i argument%s"); + lua_pushstring(lstate, err.msg); + return lua_error(lstate); + } + ]], lua_c_function_name, #fn.parameters, #fn.parameters, + (#fn.parameters == 1) and '' or 's')) lua_c_functions[#lua_c_functions + 1] = { binding=lua_c_function_name, api=fn.name } - cparams = '' - for j, param in ipairs(fn.parameters) do + local cparams = '' + local free_code = {} + for j = #fn.parameters,1,-1 do + param = fn.parameters[j] cparam = string.format('arg%u', j) - if param[1]:match('^ArrayOf') then - param_type = 'Array' - else - param_type = param[1] - end + param_type = real_type(param[1]) + lc_param_type = param_type:lower() write_shifted_output(output, string.format([[ - %s %s = nlua_pop_%s(lstate, &err); + const %s %s = nlua_pop_%s(lstate, &err); + if (err.set) { + %s lua_pushstring(lstate, err.msg); return lua_error(lstate); } - ]], param[1], cparam, param_type)) - cparams = cparams .. cparam .. ', ' + ]], param[1], cparam, param_type, table.concat(free_code, '\n '))) + free_code[#free_code + 1] = ('api_free_%s(%s);'):format(lc_param_type, cparam) + cparams = cparam .. ', ' .. cparams end if fn.receives_channel_id then cparams = 'INTERNAL_CALL, ' .. cparams @@ -412,7 +429,7 @@ local function process_function(fn) else cparams = cparams:gsub(', $', '') end - local name = fn.impl_name or fn.name + free_at_exit_code = table.concat(free_code, '\n ') if fn.return_type ~= 'void' then if fn.return_type:match('^ArrayOf') then return_type = 'Array' @@ -420,23 +437,27 @@ local function process_function(fn) return_type = fn.return_type end write_shifted_output(output, string.format([[ - %s ret = %s(%s); + const %s ret = %s(%s); + %s if (err.set) { lua_pushstring(lstate, err.msg); return lua_error(lstate); } nlua_push_%s(lstate, ret); + api_free_%s(ret); return 1; - ]], fn.return_type, name, cparams, return_type)) + ]], fn.return_type, fn.name, cparams, free_at_exit_code, return_type, + return_type:lower())) else write_shifted_output(output, string.format([[ %s(%s); + %s if (err.set) { lua_pushstring(lstate, err.msg); return lua_error(lstate); } return 0; - ]], name, cparams)) + ]], fn.name, cparams, free_at_exit_code)) end write_shifted_output(output, [[ } @@ -457,11 +478,12 @@ void nlua_add_api_functions(lua_State *lstate) ]], #lua_c_functions)) for _, func in ipairs(lua_c_functions) do output:write(string.format([[ + lua_pushcfunction(lstate, &%s); - lua_setfield(lstate, -2, "%s"); - ]], func.binding, func.api)) + lua_setfield(lstate, -2, "%s");]], func.binding, func.api)) end output:write([[ + lua_setfield(lstate, -2, "api"); } ]]) diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 7efa086af2..23d1540e2f 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -351,7 +351,7 @@ void set_option_to(void *to, int type, String name, Object value, Error *err) #define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER TYPVAL_ENCODE_CONV_NUMBER #define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \ - kv_push(edata->stack, FLOATING_OBJ((Float)(flt))) + kv_push(edata->stack, FLOAT_OBJ((Float)(flt))) #define TYPVAL_ENCODE_CONV_STRING(tv, str, len) \ do { \ diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 9fe8c351cf..640e901fa1 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -27,7 +27,7 @@ .type = kObjectTypeInteger, \ .data.integer = i }) -#define FLOATING_OBJ(f) ((Object) { \ +#define FLOAT_OBJ(f) ((Object) { \ .type = kObjectTypeFloat, \ .data.floating = f }) diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 5d862628cb..3fd1f57ace 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -139,7 +139,7 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, { if (str.size == 0) { // Empty string - return str; + return (String) { .data = NULL, .size = 0 }; } char *ptr = NULL; @@ -843,7 +843,7 @@ static void write_msg(String message, bool to_err) /// @return its argument. Object _vim_id(Object obj) { - return obj; + return copy_object(obj); } /// Returns array given as argument @@ -856,7 +856,7 @@ Object _vim_id(Object obj) /// @return its argument. Array _vim_id_array(Array arr) { - return arr; + return copy_object(ARRAY_OBJ(arr)).data.array; } /// Returns dictionary given as argument @@ -869,5 +869,18 @@ Array _vim_id_array(Array arr) /// @return its argument. Dictionary _vim_id_dictionary(Dictionary dct) { - return dct; + return copy_object(DICTIONARY_OBJ(dct)).data.dictionary; +} + +/// Returns floating-point value given as argument +/// +/// This API function is used for testing. One should not rely on its presence +/// in plugins. +/// +/// @param[in] flt Value to return. +/// +/// @return its argument. +Float _vim_id_float(Float flt) +{ + return flt; } diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index 5137b375f0..64a018f5c3 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -117,7 +117,7 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg) case MSGPACK_OBJECT_FLOAT: { STATIC_ASSERT(sizeof(Float) == sizeof(cur.mobj->via.f64), "Msgpack floating-point size does not match API integer"); - *cur.aobj = FLOATING_OBJ(cur.mobj->via.f64); + *cur.aobj = FLOAT_OBJ(cur.mobj->via.f64); break; } #define STR_CASE(type, attr, obj, dest, conv) \ diff --git a/src/nvim/viml/executor/converter.c b/src/nvim/viml/executor/converter.c index a6399500f2..a741d3a752 100644 --- a/src/nvim/viml/executor/converter.c +++ b/src/nvim/viml/executor/converter.c @@ -724,16 +724,15 @@ void nlua_push_Object(lua_State *lstate, const Object obj) String nlua_pop_String(lua_State *lstate, Error *err) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { + if (lua_type(lstate, -1) != LUA_TSTRING) { + lua_pop(lstate, 1); + api_set_error(err, Validation, "Expected lua string"); + return (String) { .size = 0, .data = NULL }; + } String ret; ret.data = (char *)lua_tolstring(lstate, -1, &(ret.size)); - - if (ret.data == NULL) { - lua_pop(lstate, 1); - set_api_error("Expected lua string", err); - return (String) { .size = 0, .data = NULL }; - } - + assert(ret.data != NULL); ret.data = xmemdupz(ret.data, ret.size); lua_pop(lstate, 1); @@ -746,17 +745,19 @@ String nlua_pop_String(lua_State *lstate, Error *err) Integer nlua_pop_Integer(lua_State *lstate, Error *err) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - Integer ret = 0; - - if (!lua_isnumber(lstate, -1)) { + if (lua_type(lstate, -1) != LUA_TNUMBER) { lua_pop(lstate, 1); - set_api_error("Expected lua integer", err); - return ret; + api_set_error(err, Validation, "Expected lua number"); + return 0; } - ret = (Integer)lua_tonumber(lstate, -1); + const lua_Number n = lua_tonumber(lstate, -1); lua_pop(lstate, 1); - - return ret; + if (n > (lua_Number)API_INTEGER_MAX || n < (lua_Number)API_INTEGER_MIN + || ((lua_Number)((Integer)n)) != n) { + api_set_error(err, Exception, "Number is not integral"); + return 0; + } + return (Integer)n; } /// Convert lua value to boolean @@ -765,7 +766,7 @@ Integer nlua_pop_Integer(lua_State *lstate, Error *err) Boolean nlua_pop_Boolean(lua_State *lstate, Error *err) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - Boolean ret = lua_toboolean(lstate, -1); + const Boolean ret = lua_toboolean(lstate, -1); lua_pop(lstate, 1); return ret; } @@ -784,7 +785,7 @@ static inline LuaTableProps nlua_check_type(lua_State *const lstate, { if (lua_type(lstate, -1) != LUA_TTABLE) { if (err) { - set_api_error("Expected lua table", err); + api_set_error(err, Validation, "Expected lua table"); } return (LuaTableProps) { .type = kObjectTypeNil }; } @@ -797,7 +798,7 @@ static inline LuaTableProps nlua_check_type(lua_State *const lstate, if (table_props.type != type) { if (err) { - set_api_error("Unexpected type", err); + api_set_error(err, Validation, "Unexpected type"); } } @@ -1050,7 +1051,7 @@ Object nlua_pop_Object(lua_State *const lstate, Error *const err) const lua_Number n = lua_tonumber(lstate, -1); if (n > (lua_Number)API_INTEGER_MAX || n < (lua_Number)API_INTEGER_MIN || ((lua_Number)((Integer)n)) != n) { - *cur.obj = FLOATING_OBJ((Float)n); + *cur.obj = FLOAT_OBJ((Float)n); } else { *cur.obj = INTEGER_OBJ((Integer)n); } @@ -1094,7 +1095,7 @@ Object nlua_pop_Object(lua_State *const lstate, Error *const err) break; } case kObjectTypeFloat: { - *cur.obj = FLOATING_OBJ((Float)table_props.val); + *cur.obj = FLOAT_OBJ((Float)table_props.val); break; } case kObjectTypeNil: { diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 3348368a36..24ed0afe67 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -219,6 +219,17 @@ describe('api', function() eq('\128\253\44', helpers.nvim('replace_termcodes', '', true, true, true)) end) + + it('does not crash when transforming an empty string', function() + -- Actually does not test anything, because current code will use NULL for + -- an empty string. + -- + -- Problem here is that if String argument has .data in allocated memory + -- then `return str` in vim_replace_termcodes body will make Neovim free + -- `str.data` twice: once when freeing arguments, then when freeing return + -- value. + eq('', meths.replace_termcodes('', true, true, true)) + end) end) describe('nvim_feedkeys', function() diff --git a/test/functional/lua_spec.lua b/test/functional/lua_spec.lua index 082efe4c0e..8ca47718aa 100644 --- a/test/functional/lua_spec.lua +++ b/test/functional/lua_spec.lua @@ -276,12 +276,53 @@ describe('luaeval() function', function() end) it('errors out correctly when working with API', function() - eq(0, exc_exec([[call luaeval("vim.api.id")]])) + -- Conversion errors + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Cannot convert given lua type', + exc_exec([[call luaeval("vim.api._vim_id(vim.api._vim_id)")]])) + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Cannot convert given lua table', + exc_exec([[call luaeval("vim.api._vim_id({1, foo=42})")]])) + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Cannot convert given lua type', + exc_exec([[call luaeval("vim.api._vim_id({42, vim.api._vim_id})")]])) + -- Errors in number of arguments + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Expected 1 argument', + exc_exec([[call luaeval("vim.api._vim_id()")]])) + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Expected 1 argument', + exc_exec([[call luaeval("vim.api._vim_id(1, 2)")]])) + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Expected 2 arguments', + exc_exec([[call luaeval("vim.api.vim_set_var(1, 2, 3)")]])) + -- Error in argument types + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Expected lua string', + exc_exec([[call luaeval("vim.api.vim_set_var(1, 2)")]])) + + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Expected lua number', + exc_exec([[call luaeval("vim.api.buffer_get_line(0, 'test')")]])) + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Number is not integral', + exc_exec([[call luaeval("vim.api.buffer_get_line(0, 1.5)")]])) + + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Expected lua table', + exc_exec([[call luaeval("vim.api._vim_id_float('test')")]])) + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Unexpected type', + exc_exec([[call luaeval("vim.api._vim_id_float({[vim.type_idx]=vim.types.dictionary})")]])) + + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Expected lua table', + exc_exec([[call luaeval("vim.api._vim_id_array(1)")]])) + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Unexpected type', + exc_exec([[call luaeval("vim.api._vim_id_array({[vim.type_idx]=vim.types.dictionary})")]])) + + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Expected lua table', + exc_exec([[call luaeval("vim.api._vim_id_dictionary(1)")]])) + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Unexpected type', + exc_exec([[call luaeval("vim.api._vim_id_dictionary({[vim.type_idx]=vim.types.array})")]])) + -- TODO: check for errors with Tabpage argument + -- TODO: check for errors with Window argument + -- TODO: check for errors with Buffer argument + end) + + it('accepts any value as API Boolean', function() + eq('', funcs.luaeval('vim.api.vim_replace_termcodes("", vim, false, nil)')) + eq('', funcs.luaeval('vim.api.vim_replace_termcodes("", 0, 1.5, "test")')) + eq('', funcs.luaeval('vim.api.vim_replace_termcodes("", true, {}, {[vim.type_idx]=vim.types.array})')) end) -- TODO: check buffer/window/etc. - -- TODO: check what happens when it errors out on second list item - -- TODO: check what happens if API function receives wrong number of - -- arguments. - -- TODO: check what happens if API function receives wrong argument types. end) From d932693d5147ac12d181e0810a20bdcbffab2818 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 20 Jan 2017 23:00:36 +0300 Subject: [PATCH 0113/1671] executor/converter: Allow converting self-referencing lua objects --- src/nvim/viml/executor/converter.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/nvim/viml/executor/converter.c b/src/nvim/viml/executor/converter.c index a741d3a752..b7bacc8ed9 100644 --- a/src/nvim/viml/executor/converter.c +++ b/src/nvim/viml/executor/converter.c @@ -167,6 +167,7 @@ typedef struct { bool container; ///< True if tv is a container. bool special; ///< If true then tv is a _VAL part of special dictionary ///< that represents mapping. + int idx; ///< Container index (used to detect self-referencing structures). } TVPopStackItem; /// Convert lua object to VimL typval_T @@ -183,7 +184,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) bool ret = true; const int initial_size = lua_gettop(lstate); kvec_t(TVPopStackItem) stack = KV_INITIAL_VALUE; - kv_push(stack, ((TVPopStackItem) { ret_tv, false, false })); + kv_push(stack, ((TVPopStackItem) { ret_tv, false, false, 0 })); while (ret && kv_size(stack)) { if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { emsgf(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 3); @@ -219,14 +220,14 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) listitem_T *const val = listitem_alloc(); list_append(kv_pair, val); kv_push(stack, cur); - cur = (TVPopStackItem) { &val->li_tv, false, false }; + cur = (TVPopStackItem) { &val->li_tv, false, false, 0 }; } else { dictitem_T *const di = dictitem_alloc_len(s, len); if (dict_add(cur.tv->vval.v_dict, di) == FAIL) { assert(false); } kv_push(stack, cur); - cur = (TVPopStackItem) { &di->di_tv, false, false }; + cur = (TVPopStackItem) { &di->di_tv, false, false, 0 }; } } else { lua_pop(lstate, 1); @@ -242,7 +243,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) listitem_T *li = listitem_alloc(); list_append(cur.tv->vval.v_list, li); kv_push(stack, cur); - cur = (TVPopStackItem) { &li->li_tv, false, false }; + cur = (TVPopStackItem) { &li->li_tv, false, false, 0 }; } } assert(!cur.container); @@ -288,6 +289,14 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) case LUA_TTABLE: { const LuaTableProps table_props = nlua_traverse_table(lstate); + for (size_t i = 0; i < kv_size(stack); i++) { + const TVPopStackItem item = kv_A(stack, i); + if (item.container && lua_rawequal(lstate, -1, item.idx)) { + copy_tv(item.tv, cur.tv); + goto nlua_pop_typval_table_processing_end; + } + } + switch (table_props.type) { case kObjectTypeArray: { cur.tv->v_type = VAR_LIST; @@ -295,6 +304,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) cur.tv->vval.v_list->lv_refcount++; if (table_props.maxidx != 0) { cur.container = true; + cur.idx = lua_gettop(lstate); kv_push(stack, cur); } break; @@ -320,6 +330,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) cur.tv->vval.v_dict->dv_refcount++; } cur.container = true; + cur.idx = lua_gettop(lstate); kv_push(stack, cur); lua_pushnil(lstate); } @@ -341,6 +352,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) assert(false); } } +nlua_pop_typval_table_processing_end: break; } default: { From 5c1b9a0d2af86461f56f0d27ed275456921f6187 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 20 Jan 2017 23:06:22 +0300 Subject: [PATCH 0114/1671] api: Reserve more numbers for internal calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reasoning; currently INTERNAL_CALL is mostly used to determine whether it is needed to deal with NL-used-as-NUL problem. This code is useful for nvim_… API calls done from VimL, but not for API calls done from lua, yet lua needs to supply something as channel_id. --- scripts/genmsgpack.lua | 2 +- src/nvim/api/buffer.c | 4 ++-- src/nvim/api/private/defs.h | 27 +++++++++++++++++++++++++-- src/nvim/eval.c | 2 +- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/scripts/genmsgpack.lua b/scripts/genmsgpack.lua index d47d637548..aed56b29eb 100644 --- a/scripts/genmsgpack.lua +++ b/scripts/genmsgpack.lua @@ -422,7 +422,7 @@ local function process_function(fn) cparams = cparam .. ', ' .. cparams end if fn.receives_channel_id then - cparams = 'INTERNAL_CALL, ' .. cparams + cparams = 'LUA_INTERNAL_CALL, ' .. cparams end if fn.can_fail then cparams = cparams .. '&err' diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index b75a2c7211..5eda88025f 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -192,7 +192,7 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id, Object str = STRING_OBJ(cstr_to_string(bufstr)); // Vim represents NULs as NLs, but this may confuse clients. - if (channel_id != INTERNAL_CALL) { + if (channel_id != VIML_INTERNAL_CALL) { strchrsub(str.data.string.data, '\n', '\0'); } @@ -313,7 +313,7 @@ void nvim_buf_set_lines(uint64_t channel_id, // line and convert NULs to newlines to avoid truncation. lines[i] = xmallocz(l.size); for (size_t j = 0; j < l.size; j++) { - if (l.data[j] == '\n' && channel_id != INTERNAL_CALL) { + if (l.data[j] == '\n' && channel_id != VIML_INTERNAL_CALL) { api_set_error(err, Exception, _("string cannot contain newlines")); new_len = i + 1; goto end; diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index 86b549cb44..cb7ee8eb4c 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -5,6 +5,8 @@ #include #include +#include "nvim/func_attr.h" + #define ARRAY_DICT_INIT {.size = 0, .capacity = 0, .items = NULL} #define STRING_INIT {.data = NULL, .size = 0} #define OBJECT_INIT { .type = kObjectTypeNil } @@ -33,8 +35,29 @@ typedef enum { /// Used as the message ID of notifications. #define NO_RESPONSE UINT64_MAX -/// Used as channel_id when the call is local. -#define INTERNAL_CALL UINT64_MAX +/// Mask for all internal calls +#define INTERNAL_CALL_MASK (UINT64_MAX ^ (UINT64_MAX >> 1)) +// (1 << 63) in all forms produces “warning: shift count >= width of type +// [-Wshift-count-overflow]” + +/// Internal call from VimL code +#define VIML_INTERNAL_CALL INTERNAL_CALL_MASK + +/// Internal call from lua code +#define LUA_INTERNAL_CALL (VIML_INTERNAL_CALL + 1) + +static inline bool is_internal_call(uint64_t channel_id) + REAL_FATTR_ALWAYS_INLINE REAL_FATTR_CONST; + +/// Check whether call is internal +/// +/// @param[in] channel_id Channel id. +/// +/// @return true if channel_id refers to internal channel. +static inline bool is_internal_call(const uint64_t channel_id) +{ + return !!(channel_id & INTERNAL_CALL_MASK); +} typedef struct { ErrorType type; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index de82f8a145..8b6638f1d7 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7796,7 +7796,7 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr) } Error err = ERROR_INIT; - Object result = fn(INTERNAL_CALL, args, &err); + Object result = fn(VIML_INTERNAL_CALL, args, &err); if (err.set) { nvim_err_writeln(cstr_as_string(err.msg)); From 8679feb3cbffd6b175be6a2868e980ca971125f7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 21 Jan 2017 00:00:09 +0300 Subject: [PATCH 0115/1671] executor/converter: Use readable lua numbers for handles --- src/nvim/viml/executor/converter.c | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/src/nvim/viml/executor/converter.c b/src/nvim/viml/executor/converter.c index b7bacc8ed9..316a5aa93f 100644 --- a/src/nvim/viml/executor/converter.c +++ b/src/nvim/viml/executor/converter.c @@ -217,7 +217,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) list_unref(kv_pair); continue; } - listitem_T *const val = listitem_alloc(); + listitem_T *const val = listitem_alloc(); list_append(kv_pair, val); kv_push(stack, cur); cur = (TVPopStackItem) { &val->li_tv, false, false, 0 }; @@ -240,7 +240,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) lua_pop(lstate, 2); continue; } - listitem_T *li = listitem_alloc(); + listitem_T *const li = listitem_alloc(); list_append(cur.tv->vval.v_list, li); kv_push(stack, cur); cur = (TVPopStackItem) { &li->li_tv, false, false, 0 }; @@ -293,6 +293,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) const TVPopStackItem item = kv_A(stack, i); if (item.container && lua_rawequal(lstate, -1, item.idx)) { copy_tv(item.tv, cur.tv); + cur.container = false; goto nlua_pop_typval_table_processing_end; } } @@ -540,25 +541,14 @@ bool nlua_push_typval(lua_State *lstate, typval_T *const tv) return true; } -#define NLUA_PUSH_IDX(lstate, type, idx) \ +#define NLUA_PUSH_HANDLE(lstate, type, idx) \ do { \ - STATIC_ASSERT(sizeof(type) <= sizeof(lua_Number), \ - "Number sizes do not match"); \ - const type src = idx; \ - lua_Number tgt; \ - memset(&tgt, 0, sizeof(tgt)); \ - memcpy(&tgt, &src, sizeof(src)); \ - lua_pushnumber(lstate, tgt); \ + lua_pushnumber(lstate, (lua_Number)(idx)); \ } while (0) -#define NLUA_POP_IDX(lstate, type, stack_idx, idx) \ +#define NLUA_POP_HANDLE(lstate, type, stack_idx, idx) \ do { \ - STATIC_ASSERT(sizeof(type) <= sizeof(lua_Number), \ - "Number sizes do not match"); \ - const lua_Number src = lua_tonumber(lstate, stack_idx); \ - type tgt; \ - memcpy(&tgt, &src, sizeof(tgt)); \ - idx = tgt; \ + idx = (type)lua_tonumber(lstate, stack_idx); \ } while (0) /// Push value which is a type index @@ -685,7 +675,7 @@ void nlua_push_Array(lua_State *lstate, const Array array) void nlua_push_##type(lua_State *lstate, const type item) \ FUNC_ATTR_NONNULL_ALL \ { \ - NLUA_PUSH_IDX(lstate, type, item); \ + NLUA_PUSH_HANDLE(lstate, type, item); \ } GENERATE_INDEX_FUNCTION(Buffer) @@ -1144,7 +1134,7 @@ type nlua_pop_##type(lua_State *lstate, Error *err) \ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \ { \ type ret; \ - NLUA_POP_IDX(lstate, type, -1, ret); \ + NLUA_POP_HANDLE(lstate, type, -1, ret); \ lua_pop(lstate, 1); \ return ret; \ } From a3ea05c1e5965ca23b1b926c41b00597e9d0211c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 21 Jan 2017 00:00:47 +0300 Subject: [PATCH 0116/1671] functests: Add some tests --- test/functional/lua/api_spec.lua | 36 +++++++++++++++ test/functional/lua/commands_spec.lua | 4 ++ test/functional/lua/luaeval_spec.lua | 63 +++++++++++++++++++++++++++ test/helpers.lua | 9 ++++ 4 files changed, 112 insertions(+) create mode 100644 test/functional/lua/api_spec.lua create mode 100644 test/functional/lua/commands_spec.lua create mode 100644 test/functional/lua/luaeval_spec.lua diff --git a/test/functional/lua/api_spec.lua b/test/functional/lua/api_spec.lua new file mode 100644 index 0000000000..6652c60adb --- /dev/null +++ b/test/functional/lua/api_spec.lua @@ -0,0 +1,36 @@ +-- Test suite for testing interactions with API bindings +local helpers = require('test.functional.helpers')(after_each) + +local funcs = helpers.funcs +local clear = helpers.clear +local NIL = helpers.NIL +local eq = helpers.eq + +before_each(clear) + +describe('luaeval(vim.api.…)', function() + describe('with channel_id and buffer handle', function() + describe('nvim_buf_get_lines', function() + it('works', function() + funcs.setline(1, {"abc", "def", "a\nb", "ttt"}) + eq({{_TYPE={}, _VAL={'a\nb'}}}, + funcs.luaeval('vim.api.nvim_buf_get_lines(1, 2, 3, false)')) + end) + end) + describe('nvim_buf_set_lines', function() + it('works', function() + funcs.setline(1, {"abc", "def", "a\nb", "ttt"}) + eq(NIL, funcs.luaeval('vim.api.nvim_buf_set_lines(1, 1, 2, false, {"b\\0a"})')) + eq({'abc', {_TYPE={}, _VAL={'b\na'}}, {_TYPE={}, _VAL={'a\nb'}}, 'ttt'}, + funcs.luaeval('vim.api.nvim_buf_get_lines(1, 0, 4, false)')) + end) + end) + end) + describe('with errors', function() + it('transforms API errors into lua errors', function() + funcs.setline(1, {"abc", "def", "a\nb", "ttt"}) + eq({false, 'string cannot contain newlines'}, + funcs.luaeval('{pcall(vim.api.nvim_buf_set_lines, 1, 1, 2, false, {"b\\na"})}')) + end) + end) +end) diff --git a/test/functional/lua/commands_spec.lua b/test/functional/lua/commands_spec.lua new file mode 100644 index 0000000000..191504bfaa --- /dev/null +++ b/test/functional/lua/commands_spec.lua @@ -0,0 +1,4 @@ +-- Test suite for checking :lua* commands +local helpers = require('test.functional.helpers')(after_each) + +local eq = helpers.eq diff --git a/test/functional/lua/luaeval_spec.lua b/test/functional/lua/luaeval_spec.lua new file mode 100644 index 0000000000..5b7f365ef3 --- /dev/null +++ b/test/functional/lua/luaeval_spec.lua @@ -0,0 +1,63 @@ +-- Test suite for testing luaeval() function +local helpers = require('test.functional.helpers')(after_each) + +local command = helpers.command +local meths = helpers.meths +local funcs = helpers.funcs +local clear = helpers.clear +local NIL = helpers.NIL +local eq = helpers.eq + +before_each(clear) + +describe('luaeval()', function() + describe('second argument', function() + it('is successfully received', function() + local t = {t=true, f=false, --[[n=NIL,]] d={l={'string', 42, 0.42}}} + eq(t, funcs.luaeval("_A", t)) + -- Not tested: nil, funcrefs, returned object identity: behaviour will + -- most likely change. + end) + end) + describe('lua values', function() + it('are successfully transformed', function() + eq({n=1, f=1.5, s='string', l={4, 2}}, + funcs.luaeval('{n=1, f=1.5, s="string", l={4, 2}}')) + -- Not tested: nil inside containers: behaviour will most likely change. + eq(NIL, funcs.luaeval('nil')) + end) + end) + describe('recursive lua values', function() + it('are successfully transformed', function() + funcs.luaeval('rawset(_G, "d", {})') + funcs.luaeval('rawset(d, "d", d)') + eq('\n{\'d\': {...@0}}', funcs.execute('echo luaeval("d")')) + + funcs.luaeval('rawset(_G, "l", {})') + funcs.luaeval('table.insert(l, l)') + eq('\n[[...@0]]', funcs.execute('echo luaeval("l")')) + end) + end) + describe('strings', function() + it('are successfully converted to special dictionaries', function() + command([[let s = luaeval('"\0"')]]) + eq({_TYPE={}, _VAL={'\n'}}, meths.get_var('s')) + eq(1, funcs.eval('s._TYPE is v:msgpack_types.binary')) + end) + it('are successfully converted to special dictionaries in table keys', + function() + command([[let d = luaeval('{["\0"]=1}')]]) + eq({_TYPE={}, _VAL={{{_TYPE={}, _VAL={'\n'}}, 1}}}, meths.get_var('d')) + eq(1, funcs.eval('d._TYPE is v:msgpack_types.map')) + eq(1, funcs.eval('d._VAL[0][0]._TYPE is v:msgpack_types.string')) + end) + it('are successfully converted to special dictionaries from a list', + function() + command([[let l = luaeval('{"abc", "a\0b", "c\0d", "def"}')]]) + eq({'abc', {_TYPE={}, _VAL={'a\nb'}}, {_TYPE={}, _VAL={'c\nd'}}, 'def'}, + meths.get_var('l')) + eq(1, funcs.eval('l[1]._TYPE is v:msgpack_types.binary')) + eq(1, funcs.eval('l[2]._TYPE is v:msgpack_types.binary')) + end) + end) +end) diff --git a/test/helpers.lua b/test/helpers.lua index e5224349c2..18f47e950b 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -225,6 +225,14 @@ local function which(exe) end end +local function shallowcopy(orig) + local copy = {} + for orig_key, orig_value in pairs(orig) do + copy[orig_key] = orig_value + end + return copy +end + return { eq = eq, neq = neq, @@ -238,4 +246,5 @@ return { check_cores = check_cores, hasenv = hasenv, which = which, + shallowcopy = shallowcopy, } From 45feaa73d0759858a9a4454037fe4a41ea97e5b9 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 21 Jan 2017 00:16:25 +0300 Subject: [PATCH 0117/1671] eval/decode: Fix memory leak in JSON functions --- src/nvim/eval/decode.c | 24 ++++++++++++++++++------ src/nvim/viml/executor/converter.c | 4 ++-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index cb5a624f7b..d95e75170a 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -249,10 +249,14 @@ list_T *decode_create_map_special_dict(typval_T *const ret_tv) /// determined. /// @param[in] binary If true, save special string type as kMPBinary, /// otherwise kMPString. +/// @param[in] s_allocated If true, then `s` was allocated and can be saved in +/// a returned structure. If it is not saved there, it +/// will be freed. /// /// @return Decoded string. typval_T decode_string(const char *const s, const size_t len, - const TriState hasnul, const bool binary) + const TriState hasnul, const bool binary, + const bool s_allocated) FUNC_ATTR_WARN_UNUSED_RESULT { assert(s != NULL || len == 0); @@ -268,7 +272,11 @@ typval_T decode_string(const char *const s, const size_t len, .v_lock = VAR_UNLOCKED, .vval = { .v_list = list }, })); - if (encode_list_write((void *)list, s, len) == -1) { + const int elw_ret = encode_list_write((void *)list, s, len); + if (s_allocated) { + xfree((void *)s); + } + if (elw_ret == -1) { clear_tv(&tv); return (typval_T) { .v_type = VAR_UNKNOWN, .v_lock = VAR_UNLOCKED }; } @@ -277,7 +285,8 @@ typval_T decode_string(const char *const s, const size_t len, return (typval_T) { .v_type = VAR_STRING, .v_lock = VAR_UNLOCKED, - .vval = { .v_string = xmemdupz(s, len) }, + .vval = { .v_string = (char_u *)( + s_allocated ? (char *)s : xmemdupz(s, len)) }, }; } } @@ -492,9 +501,10 @@ static inline int parse_json_string(vimconv_T *const conv, str = new_str; str_end = new_str + str_len; } + *str_end = NUL; typval_T obj; obj = decode_string(str, (size_t)(str_end - str), hasnul ? kTrue : kFalse, - false); + false, true); if (obj.v_type == VAR_UNKNOWN) { goto parse_json_string_fail; } @@ -1022,14 +1032,16 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) break; } case MSGPACK_OBJECT_STR: { - *rettv = decode_string(mobj.via.bin.ptr, mobj.via.bin.size, kTrue, false); + *rettv = decode_string(mobj.via.bin.ptr, mobj.via.bin.size, kTrue, false, + false); if (rettv->v_type == VAR_UNKNOWN) { return FAIL; } break; } case MSGPACK_OBJECT_BIN: { - *rettv = decode_string(mobj.via.bin.ptr, mobj.via.bin.size, kNone, true); + *rettv = decode_string(mobj.via.bin.ptr, mobj.via.bin.size, kNone, true, + false); if (rettv->v_type == VAR_UNKNOWN) { return FAIL; } diff --git a/src/nvim/viml/executor/converter.c b/src/nvim/viml/executor/converter.c index 316a5aa93f..a39f573036 100644 --- a/src/nvim/viml/executor/converter.c +++ b/src/nvim/viml/executor/converter.c @@ -210,7 +210,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) list_T *const kv_pair = list_alloc(); list_append_list(cur.tv->vval.v_list, kv_pair); listitem_T *const key = listitem_alloc(); - key->li_tv = decode_string(s, len, kTrue, false); + key->li_tv = decode_string(s, len, kTrue, false, false); list_append(kv_pair, key); if (key->li_tv.v_type == VAR_UNKNOWN) { ret = false; @@ -268,7 +268,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) case LUA_TSTRING: { size_t len; const char *s = lua_tolstring(lstate, -1, &len); - *cur.tv = decode_string(s, len, kNone, true); + *cur.tv = decode_string(s, len, kNone, true, false); if (cur.tv->v_type == VAR_UNKNOWN) { ret = false; } From 600bee9d4fa22ab914175a9edf82bb3503f47cda Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 21 Jan 2017 00:59:13 +0300 Subject: [PATCH 0118/1671] genmsgpack: Include error source in error messages --- scripts/genmsgpack.lua | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/scripts/genmsgpack.lua b/scripts/genmsgpack.lua index aed56b29eb..2a7cb4bacf 100644 --- a/scripts/genmsgpack.lua +++ b/scripts/genmsgpack.lua @@ -393,8 +393,7 @@ local function process_function(fn) Error err = {.set = false}; if (lua_gettop(lstate) != %i) { api_set_error(&err, Validation, "Expected %i argument%s"); - lua_pushstring(lstate, err.msg); - return lua_error(lstate); + return luaL_error(lstate, "%%s", err.msg); } ]], lua_c_function_name, #fn.parameters, #fn.parameters, (#fn.parameters == 1) and '' or 's')) @@ -414,8 +413,7 @@ local function process_function(fn) if (err.set) { %s - lua_pushstring(lstate, err.msg); - return lua_error(lstate); + return luaL_error(lstate, "%%s", err.msg); } ]], param[1], cparam, param_type, table.concat(free_code, '\n '))) free_code[#free_code + 1] = ('api_free_%s(%s);'):format(lc_param_type, cparam) @@ -440,8 +438,7 @@ local function process_function(fn) const %s ret = %s(%s); %s if (err.set) { - lua_pushstring(lstate, err.msg); - return lua_error(lstate); + return luaL_error(lstate, "%%s", err.msg); } nlua_push_%s(lstate, ret); api_free_%s(ret); @@ -453,8 +450,7 @@ local function process_function(fn) %s(%s); %s if (err.set) { - lua_pushstring(lstate, err.msg); - return lua_error(lstate); + return luaL_error(lstate, "%%s", err.msg); } return 0; ]], fn.name, cparams, free_at_exit_code)) @@ -465,7 +461,7 @@ local function process_function(fn) end for _, fn in ipairs(functions) do - if not fn.noeval then + if not fn.noeval or fn.name:sub(1, 4) == '_vim' then process_function(fn) end end From f8d55266e461a0a85e17e5adfd33e8429e45d9a5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 21 Jan 2017 01:02:15 +0300 Subject: [PATCH 0119/1671] executor/executor: When reporting errors use lua string length --- src/nvim/viml/executor/executor.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/nvim/viml/executor/executor.c b/src/nvim/viml/executor/executor.c index 33b6870ea0..29ec8dc927 100644 --- a/src/nvim/viml/executor/executor.c +++ b/src/nvim/viml/executor/executor.c @@ -86,7 +86,7 @@ static void nlua_error(lua_State *const lstate, const char *const msg) size_t len; const char *const str = lua_tolstring(lstate, -1, &len); - EMSG2(msg, str); + emsgf(msg, (int)len, str); lua_pop(lstate, 1); } @@ -120,11 +120,11 @@ static int nlua_exec_lua_string(lua_State *lstate) FUNC_ATTR_NONNULL_ALL lua_pop(lstate, 2); if (luaL_loadbuffer(lstate, str->data, str->size, NLUA_EVAL_NAME)) { - nlua_error(lstate, _("E5104: Error while creating lua chunk: %s")); + nlua_error(lstate, _("E5104: Error while creating lua chunk: %.*s")); return 0; } if (lua_pcall(lstate, 0, 1, 0)) { - nlua_error(lstate, _("E5105: Error while calling lua chunk: %s")); + nlua_error(lstate, _("E5105: Error while calling lua chunk: %.*s")); return 0; } if (!nlua_pop_typval(lstate, ret_tv)) { @@ -141,7 +141,7 @@ static int nlua_state_init(lua_State *lstate) FUNC_ATTR_NONNULL_ALL lua_pushcfunction(lstate, &nlua_stricmp); lua_setglobal(lstate, "stricmp"); if (luaL_dostring(lstate, (char *)&vim_module[0])) { - nlua_error(lstate, _("E5106: Error while creating vim module: %s")); + nlua_error(lstate, _("E5106: Error while creating vim module: %.*s")); return 1; } nlua_add_api_functions(lstate); @@ -220,7 +220,7 @@ static int nlua_eval_lua_string(lua_State *lstate) #undef EVALHEADER if (luaL_loadbuffer(lstate, lcmd, lcmd_len, NLUA_EVAL_NAME)) { nlua_error(lstate, - _("E5107: Error while creating lua chunk for luaeval(): %s")); + _("E5107: Error while creating lua chunk for luaeval(): %.*s")); return 0; } if (lcmd != (char *)IObuff) { @@ -234,7 +234,7 @@ static int nlua_eval_lua_string(lua_State *lstate) } if (lua_pcall(lstate, 1, 1, 0)) { nlua_error(lstate, - _("E5108: Error while calling lua chunk for luaeval(): %s")); + _("E5108: Error while calling lua chunk for luaeval(): %.*s")); return 0; } if (!nlua_pop_typval(lstate, ret_tv)) { From bca9c2f3c4af9c132439bbfeaed028ac3171db48 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 21 Jan 2017 01:10:44 +0300 Subject: [PATCH 0120/1671] functests: Move existing tests from lua_spec to lua/*, fix them --- test/functional/lua/api_spec.lua | 143 ++++++++++++ test/functional/lua/luaeval_spec.lua | 181 +++++++++++++++ test/functional/lua_spec.lua | 328 --------------------------- 3 files changed, 324 insertions(+), 328 deletions(-) delete mode 100644 test/functional/lua_spec.lua diff --git a/test/functional/lua/api_spec.lua b/test/functional/lua/api_spec.lua index 6652c60adb..31c855c2c1 100644 --- a/test/functional/lua/api_spec.lua +++ b/test/functional/lua/api_spec.lua @@ -1,8 +1,10 @@ -- Test suite for testing interactions with API bindings local helpers = require('test.functional.helpers')(after_each) +local exc_exec = helpers.exc_exec local funcs = helpers.funcs local clear = helpers.clear +local eval = helpers.eval local NIL = helpers.NIL local eq = helpers.eq @@ -33,4 +35,145 @@ describe('luaeval(vim.api.…)', function() funcs.luaeval('{pcall(vim.api.nvim_buf_set_lines, 1, 1, 2, false, {"b\\na"})}')) end) end) + + it('correctly converts from API objects', function() + eq(1, funcs.luaeval('vim.api.nvim_eval("1")')) + eq('1', funcs.luaeval([[vim.api.nvim_eval('"1"')]])) + eq({}, funcs.luaeval('vim.api.nvim_eval("[]")')) + eq({}, funcs.luaeval('vim.api.nvim_eval("{}")')) + eq(1, funcs.luaeval('vim.api.nvim_eval("1.0")')) + eq(true, funcs.luaeval('vim.api.nvim_eval("v:true")')) + eq(false, funcs.luaeval('vim.api.nvim_eval("v:false")')) + eq(NIL, funcs.luaeval('vim.api.nvim_eval("v:null")')) + + eq(0, eval([[type(luaeval('vim.api.nvim_eval("1")'))]])) + eq(1, eval([[type(luaeval('vim.api.nvim_eval("''1''")'))]])) + eq(3, eval([[type(luaeval('vim.api.nvim_eval("[]")'))]])) + eq(4, eval([[type(luaeval('vim.api.nvim_eval("{}")'))]])) + eq(5, eval([[type(luaeval('vim.api.nvim_eval("1.0")'))]])) + eq(6, eval([[type(luaeval('vim.api.nvim_eval("v:true")'))]])) + eq(6, eval([[type(luaeval('vim.api.nvim_eval("v:false")'))]])) + eq(7, eval([[type(luaeval('vim.api.nvim_eval("v:null")'))]])) + + eq({foo=42}, funcs.luaeval([[vim.api.nvim_eval('{"foo": 42}')]])) + eq({42}, funcs.luaeval([[vim.api.nvim_eval('[42]')]])) + + eq({foo={bar=42}, baz=50}, funcs.luaeval([[vim.api.nvim_eval('{"foo": {"bar": 42}, "baz": 50}')]])) + eq({{42}, {}}, funcs.luaeval([=[vim.api.nvim_eval('[[42], []]')]=])) + end) + + it('correctly converts to API objects', function() + eq(1, funcs.luaeval('vim.api._vim_id(1)')) + eq('1', funcs.luaeval('vim.api._vim_id("1")')) + eq({1}, funcs.luaeval('vim.api._vim_id({1})')) + eq({foo=1}, funcs.luaeval('vim.api._vim_id({foo=1})')) + eq(1.5, funcs.luaeval('vim.api._vim_id(1.5)')) + eq(true, funcs.luaeval('vim.api._vim_id(true)')) + eq(false, funcs.luaeval('vim.api._vim_id(false)')) + eq(NIL, funcs.luaeval('vim.api._vim_id(nil)')) + + eq(0, eval([[type(luaeval('vim.api._vim_id(1)'))]])) + eq(1, eval([[type(luaeval('vim.api._vim_id("1")'))]])) + eq(3, eval([[type(luaeval('vim.api._vim_id({1})'))]])) + eq(4, eval([[type(luaeval('vim.api._vim_id({foo=1})'))]])) + eq(5, eval([[type(luaeval('vim.api._vim_id(1.5)'))]])) + eq(6, eval([[type(luaeval('vim.api._vim_id(true)'))]])) + eq(6, eval([[type(luaeval('vim.api._vim_id(false)'))]])) + eq(7, eval([[type(luaeval('vim.api._vim_id(nil)'))]])) + + eq({foo=1, bar={42, {{baz=true}, 5}}}, funcs.luaeval('vim.api._vim_id({foo=1, bar={42, {{baz=true}, 5}}})')) + end) + + it('correctly converts container objects with type_idx to API objects', function() + eq(5, eval('type(luaeval("vim.api._vim_id({[vim.type_idx]=vim.types.float, [vim.val_idx]=0})"))')) + eq(4, eval([[type(luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.dictionary})'))]])) + eq(3, eval([[type(luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.array})'))]])) + + eq({}, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.array})')) + + -- Presence of type_idx makes Vim ignore some keys + eq({42}, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) + eq({foo=2}, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) + eq(10, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) + eq({}, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2})')) + end) + + it('correctly converts arrays with type_idx to API objects', function() + eq(3, eval([[type(luaeval('vim.api._vim_id_array({[vim.type_idx]=vim.types.array})'))]])) + + eq({}, funcs.luaeval('vim.api._vim_id_array({[vim.type_idx]=vim.types.array})')) + + eq({42}, funcs.luaeval('vim.api._vim_id_array({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) + eq({{foo=2}}, funcs.luaeval('vim.api._vim_id_array({{[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})')) + eq({10}, funcs.luaeval('vim.api._vim_id_array({{[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})')) + eq({}, funcs.luaeval('vim.api._vim_id_array({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2})')) + + eq({}, funcs.luaeval('vim.api._vim_id_array({})')) + eq(3, eval([[type(luaeval('vim.api._vim_id_array({})'))]])) + end) + + it('correctly converts dictionaries with type_idx to API objects', function() + eq(4, eval([[type(luaeval('vim.api._vim_id_dictionary({[vim.type_idx]=vim.types.dictionary})'))]])) + + eq({}, funcs.luaeval('vim.api._vim_id_dictionary({[vim.type_idx]=vim.types.dictionary})')) + + eq({v={42}}, funcs.luaeval('vim.api._vim_id_dictionary({v={[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})')) + eq({foo=2}, funcs.luaeval('vim.api._vim_id_dictionary({[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) + eq({v=10}, funcs.luaeval('vim.api._vim_id_dictionary({v={[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})')) + eq({v={}}, funcs.luaeval('vim.api._vim_id_dictionary({v={[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2}})')) + + -- If API requests dictionary, then empty table will be the one. This is not + -- the case normally because empty table is an empty arrray. + eq({}, funcs.luaeval('vim.api._vim_id_dictionary({})')) + eq(4, eval([[type(luaeval('vim.api._vim_id_dictionary({})'))]])) + end) + + it('errors out correctly when working with API', function() + -- Conversion errors + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Cannot convert given lua type', + exc_exec([[call luaeval("vim.api._vim_id(vim.api._vim_id)")]])) + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Cannot convert given lua table', + exc_exec([[call luaeval("vim.api._vim_id({1, foo=42})")]])) + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Cannot convert given lua type', + exc_exec([[call luaeval("vim.api._vim_id({42, vim.api._vim_id})")]])) + -- Errors in number of arguments + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Expected 1 argument', + exc_exec([[call luaeval("vim.api._vim_id()")]])) + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Expected 1 argument', + exc_exec([[call luaeval("vim.api._vim_id(1, 2)")]])) + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Expected 2 arguments', + exc_exec([[call luaeval("vim.api.nvim_set_var(1, 2, 3)")]])) + -- Error in argument types + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Expected lua string', + exc_exec([[call luaeval("vim.api.nvim_set_var(1, 2)")]])) + + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Expected lua number', + exc_exec([[call luaeval("vim.api.nvim_buf_get_lines(0, 'test', 1, false)")]])) + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Number is not integral', + exc_exec([[call luaeval("vim.api.nvim_buf_get_lines(0, 1.5, 1, false)")]])) + + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Expected lua table', + exc_exec([[call luaeval("vim.api._vim_id_float('test')")]])) + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Unexpected type', + exc_exec([[call luaeval("vim.api._vim_id_float({[vim.type_idx]=vim.types.dictionary})")]])) + + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Expected lua table', + exc_exec([[call luaeval("vim.api._vim_id_array(1)")]])) + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Unexpected type', + exc_exec([[call luaeval("vim.api._vim_id_array({[vim.type_idx]=vim.types.dictionary})")]])) + + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Expected lua table', + exc_exec([[call luaeval("vim.api._vim_id_dictionary(1)")]])) + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Unexpected type', + exc_exec([[call luaeval("vim.api._vim_id_dictionary({[vim.type_idx]=vim.types.array})")]])) + -- TODO: check for errors with Tabpage argument + -- TODO: check for errors with Window argument + -- TODO: check for errors with Buffer argument + end) + + it('accepts any value as API Boolean', function() + eq('', funcs.luaeval('vim.api.nvim_replace_termcodes("", vim, false, nil)')) + eq('', funcs.luaeval('vim.api.nvim_replace_termcodes("", 0, 1.5, "test")')) + eq('', funcs.luaeval('vim.api.nvim_replace_termcodes("", true, {}, {[vim.type_idx]=vim.types.array})')) + end) end) diff --git a/test/functional/lua/luaeval_spec.lua b/test/functional/lua/luaeval_spec.lua index 5b7f365ef3..345848cfff 100644 --- a/test/functional/lua/luaeval_spec.lua +++ b/test/functional/lua/luaeval_spec.lua @@ -1,16 +1,37 @@ -- Test suite for testing luaeval() function local helpers = require('test.functional.helpers')(after_each) +local redir_exec = helpers.redir_exec +local exc_exec = helpers.exc_exec local command = helpers.command local meths = helpers.meths local funcs = helpers.funcs local clear = helpers.clear +local eval = helpers.eval local NIL = helpers.NIL local eq = helpers.eq before_each(clear) +local function startswith(expected, actual) + eq(expected, actual:sub(1, #expected)) +end + describe('luaeval()', function() + local nested_by_level = {} + local nested = {} + local nested_s = '{}' + for i=1,100 do + if i % 2 == 0 then + nested = {nested} + nested_s = '{' .. nested_s .. '}' + else + nested = {nested=nested} + nested_s = '{nested=' .. nested_s .. '}' + end + nested_by_level[i] = {o=nested, s=nested_s} + end + describe('second argument', function() it('is successfully received', function() local t = {t=true, f=false, --[[n=NIL,]] d={l={'string', 42, 0.42}}} @@ -60,4 +81,164 @@ describe('luaeval()', function() eq(1, funcs.eval('l[2]._TYPE is v:msgpack_types.binary')) end) end) + + -- Not checked: funcrefs converted to NIL. To be altered to something more + -- meaningful later. + + it('correctly evaluates scalars', function() + eq(1, funcs.luaeval('1')) + eq(0, eval('type(luaeval("1"))')) + + eq(1.5, funcs.luaeval('1.5')) + eq(5, eval('type(luaeval("1.5"))')) + + eq("test", funcs.luaeval('"test"')) + eq(1, eval('type(luaeval("\'test\'"))')) + + eq('', funcs.luaeval('""')) + eq({_TYPE={}, _VAL={'\n'}}, funcs.luaeval([['\0']])) + eq({_TYPE={}, _VAL={'\n', '\n'}}, funcs.luaeval([['\0\n\0']])) + eq(1, eval([[luaeval('"\0\n\0"')._TYPE is v:msgpack_types.binary]])) + + eq(true, funcs.luaeval('true')) + eq(false, funcs.luaeval('false')) + eq(NIL, funcs.luaeval('nil')) + end) + + it('correctly evaluates containers', function() + eq({}, funcs.luaeval('{}')) + eq(3, eval('type(luaeval("{}"))')) + + eq({test=1, foo=2}, funcs.luaeval('{test=1, foo=2}')) + eq(4, eval('type(luaeval("{test=1, foo=2}"))')) + + eq({4, 2}, funcs.luaeval('{4, 2}')) + eq(3, eval('type(luaeval("{4, 2}"))')) + + local level = 30 + eq(nested_by_level[level].o, funcs.luaeval(nested_by_level[level].s)) + + eq({_TYPE={}, _VAL={{{_TYPE={}, _VAL={'\n', '\n'}}, {_TYPE={}, _VAL={'\n', '\n\n'}}}}}, + funcs.luaeval([[{['\0\n\0']='\0\n\0\0'}]])) + eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._TYPE is v:msgpack_types.map]])) + eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._VAL[0][0]._TYPE is v:msgpack_types.string]])) + eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._VAL[0][1]._TYPE is v:msgpack_types.binary]])) + eq({nested={{_TYPE={}, _VAL={{{_TYPE={}, _VAL={'\n', '\n'}}, {_TYPE={}, _VAL={'\n', '\n\n'}}}}}}}, + funcs.luaeval([[{nested={{['\0\n\0']='\0\n\0\0'}}}]])) + end) + + it('correctly passes scalars as argument', function() + eq(1, funcs.luaeval('_A', 1)) + eq(1.5, funcs.luaeval('_A', 1.5)) + eq('', funcs.luaeval('_A', '')) + eq('test', funcs.luaeval('_A', 'test')) + eq(NIL, funcs.luaeval('_A', NIL)) + eq(true, funcs.luaeval('_A', true)) + eq(false, funcs.luaeval('_A', false)) + end) + + it('correctly passes containers as argument', function() + eq({}, funcs.luaeval('_A', {})) + eq({test=1}, funcs.luaeval('_A', {test=1})) + eq({4, 2}, funcs.luaeval('_A', {4, 2})) + local level = 28 + eq(nested_by_level[level].o, funcs.luaeval('_A', nested_by_level[level].o)) + end) + + local function sp(typ, val) + return ('{"_TYPE": v:msgpack_types.%s, "_VAL": %s}'):format(typ, val) + end + local function mapsp(...) + local val = '' + for i=1,(select('#', ...)/2) do + val = ('%s[%s,%s],'):format(val, select(i * 2 - 1, ...), + select(i * 2, ...)) + end + return sp('map', '[' .. val .. ']') + end + local function luaevalarg(argexpr, expr) + return eval(([=[ + [ + extend(g:, {'_ret': luaeval(%s, %s)})._ret, + type(g:_ret)==type({})&&has_key(g:_ret, '_TYPE') + ? [ + get(keys(filter(copy(v:msgpack_types), 'v:val is g:_ret._TYPE')), 0, + g:_ret._TYPE), + get(g:_ret, '_VAL', g:_ret) + ] + : [0, g:_ret]][1] + ]=]):format(expr or '"_A"', argexpr):gsub('\n', '')) + end + + it('correctly passes special dictionaries', function() + eq({'binary', {'\n', '\n'}}, luaevalarg(sp('binary', '["\\n", "\\n"]'))) + eq({'binary', {'\n', '\n'}}, luaevalarg(sp('string', '["\\n", "\\n"]'))) + eq({0, true}, luaevalarg(sp('boolean', 1))) + eq({0, false}, luaevalarg(sp('boolean', 0))) + eq({0, NIL}, luaevalarg(sp('nil', 0))) + eq({0, {[""]=""}}, luaevalarg(mapsp(sp('binary', '[""]'), '""'))) + eq({0, {[""]=""}}, luaevalarg(mapsp(sp('string', '[""]'), '""'))) + end) + + it('issues an error in some cases', function() + eq("Vim(call):E5100: Cannot convert given lua table: table should either have a sequence of positive integer keys or contain only string keys", + exc_exec('call luaeval("{1, foo=2}")')) + eq("Vim(call):E5101: Cannot convert given lua type", + exc_exec('call luaeval("vim.api.nvim_buf_get_lines")')) + startswith("Vim(call):E5107: Error while creating lua chunk for luaeval(): ", + exc_exec('call luaeval("1, 2, 3")')) + startswith("Vim(call):E5108: Error while calling lua chunk for luaeval(): ", + exc_exec('call luaeval("(nil)()")')) + eq("Vim(call):E5101: Cannot convert given lua type", + exc_exec('call luaeval("{42, vim.api}")')) + eq("Vim(call):E5101: Cannot convert given lua type", + exc_exec('call luaeval("{foo=42, baz=vim.api}")')) + + -- The following should not crash: conversion error happens inside + eq("Vim(call):E5101: Cannot convert given lua type", + exc_exec('call luaeval("vim.api")')) + -- The following should not show internal error + eq("\nE5101: Cannot convert given lua type\n0", + redir_exec('echo luaeval("vim.api")')) + end) + + it('correctly converts containers with type_idx', function() + eq(5, eval('type(luaeval("{[vim.type_idx]=vim.types.float, [vim.val_idx]=0}"))')) + eq(4, eval([[type(luaeval('{[vim.type_idx]=vim.types.dictionary}'))]])) + eq(3, eval([[type(luaeval('{[vim.type_idx]=vim.types.array}'))]])) + + eq({}, funcs.luaeval('{[vim.type_idx]=vim.types.array}')) + + -- Presence of type_idx makes Vim ignore some keys + eq({42}, funcs.luaeval('{[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}')) + eq({foo=2}, funcs.luaeval('{[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}')) + eq(10, funcs.luaeval('{[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}')) + + -- The following should not crash + eq({}, funcs.luaeval('{[vim.type_idx]=vim.types.dictionary}')) + end) + + it('correctly converts self-containing containers', function() + meths.set_var('l', {}) + eval('add(l, l)') + eq(true, eval('luaeval("_A == _A[1]", l)')) + eq(true, eval('luaeval("_A[1] == _A[1][1]", [l])')) + eq(true, eval('luaeval("_A.d == _A.d[1]", {"d": l})')) + eq(true, eval('luaeval("_A ~= _A[1]", [l])')) + + meths.set_var('d', {foo=42}) + eval('extend(d, {"d": d})') + eq(true, eval('luaeval("_A == _A.d", d)')) + eq(true, eval('luaeval("_A[1] == _A[1].d", [d])')) + eq(true, eval('luaeval("_A.d == _A.d.d", {"d": d})')) + eq(true, eval('luaeval("_A ~= _A.d", {"d": d})')) + end) + + it('errors out correctly when doing incorrect things in lua', function() + -- Conversion errors + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: attempt to call field \'xxx_nonexistent_key_xxx\' (a nil value)', + exc_exec([[call luaeval("vim.xxx_nonexistent_key_xxx()")]])) + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: ERROR', + exc_exec([[call luaeval("error('ERROR')")]])) + end) end) diff --git a/test/functional/lua_spec.lua b/test/functional/lua_spec.lua deleted file mode 100644 index 8ca47718aa..0000000000 --- a/test/functional/lua_spec.lua +++ /dev/null @@ -1,328 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) - -local eq = helpers.eq -local neq = helpers.neq -local NIL = helpers.NIL -local eval = helpers.eval -local clear = helpers.clear -local funcs = helpers.funcs -local meths = helpers.meths -local exc_exec = helpers.exc_exec -local redir_exec = helpers.redir_exec - -local function startswith(expected, actual) - eq(expected, actual:sub(1, #expected)) -end - -before_each(clear) - -describe('luaeval() function', function() - local nested_by_level = {} - local nested = {} - local nested_s = '{}' - for i=1,100 do - if i % 2 == 0 then - nested = {nested} - nested_s = '{' .. nested_s .. '}' - else - nested = {nested=nested} - nested_s = '{nested=' .. nested_s .. '}' - end - nested_by_level[i] = {o=nested, s=nested_s} - end - - -- Not checked: funcrefs converted to NIL. To be altered to something more - -- meaningful later. - - it('correctly evaluates scalars', function() - eq(1, funcs.luaeval('1')) - eq(0, eval('type(luaeval("1"))')) - - eq(1.5, funcs.luaeval('1.5')) - eq(5, eval('type(luaeval("1.5"))')) - - eq("test", funcs.luaeval('"test"')) - eq(1, eval('type(luaeval("\'test\'"))')) - - eq('', funcs.luaeval('""')) - eq({_TYPE={}, _VAL={'\n'}}, funcs.luaeval([['\0']])) - eq({_TYPE={}, _VAL={'\n', '\n'}}, funcs.luaeval([['\0\n\0']])) - eq(1, eval([[luaeval('"\0\n\0"')._TYPE is v:msgpack_types.binary]])) - - eq(true, funcs.luaeval('true')) - eq(false, funcs.luaeval('false')) - eq(NIL, funcs.luaeval('nil')) - end) - - it('correctly evaluates containers', function() - eq({}, funcs.luaeval('{}')) - eq(3, eval('type(luaeval("{}"))')) - - eq({test=1, foo=2}, funcs.luaeval('{test=1, foo=2}')) - eq(4, eval('type(luaeval("{test=1, foo=2}"))')) - - eq({4, 2}, funcs.luaeval('{4, 2}')) - eq(3, eval('type(luaeval("{4, 2}"))')) - - local level = 30 - eq(nested_by_level[level].o, funcs.luaeval(nested_by_level[level].s)) - - eq({_TYPE={}, _VAL={{{_TYPE={}, _VAL={'\n', '\n'}}, {_TYPE={}, _VAL={'\n', '\n\n'}}}}}, - funcs.luaeval([[{['\0\n\0']='\0\n\0\0'}]])) - eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._TYPE is v:msgpack_types.map]])) - eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._VAL[0][0]._TYPE is v:msgpack_types.string]])) - eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._VAL[0][1]._TYPE is v:msgpack_types.binary]])) - eq({nested={{_TYPE={}, _VAL={{{_TYPE={}, _VAL={'\n', '\n'}}, {_TYPE={}, _VAL={'\n', '\n\n'}}}}}}}, - funcs.luaeval([[{nested={{['\0\n\0']='\0\n\0\0'}}}]])) - end) - - it('correctly passes scalars as argument', function() - eq(1, funcs.luaeval('_A', 1)) - eq(1.5, funcs.luaeval('_A', 1.5)) - eq('', funcs.luaeval('_A', '')) - eq('test', funcs.luaeval('_A', 'test')) - eq(NIL, funcs.luaeval('_A', NIL)) - eq(true, funcs.luaeval('_A', true)) - eq(false, funcs.luaeval('_A', false)) - end) - - it('correctly passes containers as argument', function() - eq({}, funcs.luaeval('_A', {})) - eq({test=1}, funcs.luaeval('_A', {test=1})) - eq({4, 2}, funcs.luaeval('_A', {4, 2})) - local level = 28 - eq(nested_by_level[level].o, funcs.luaeval('_A', nested_by_level[level].o)) - end) - - local function sp(typ, val) - return ('{"_TYPE": v:msgpack_types.%s, "_VAL": %s}'):format(typ, val) - end - local function mapsp(...) - local val = '' - for i=1,(select('#', ...)/2) do - val = ('%s[%s,%s],'):format(val, select(i * 2 - 1, ...), - select(i * 2, ...)) - end - return sp('map', '[' .. val .. ']') - end - local function luaevalarg(argexpr, expr) - return eval(([=[ - [ - extend(g:, {'_ret': luaeval(%s, %s)})._ret, - type(g:_ret)==type({})&&has_key(g:_ret, '_TYPE') - ? [ - get(keys(filter(copy(v:msgpack_types), 'v:val is g:_ret._TYPE')), 0, - g:_ret._TYPE), - get(g:_ret, '_VAL', g:_ret) - ] - : [0, g:_ret]][1] - ]=]):format(expr or '"_A"', argexpr):gsub('\n', '')) - end - - it('correctly passes special dictionaries', function() - eq({'binary', {'\n', '\n'}}, luaevalarg(sp('binary', '["\\n", "\\n"]'))) - eq({'binary', {'\n', '\n'}}, luaevalarg(sp('string', '["\\n", "\\n"]'))) - eq({0, true}, luaevalarg(sp('boolean', 1))) - eq({0, false}, luaevalarg(sp('boolean', 0))) - eq({0, NIL}, luaevalarg(sp('nil', 0))) - eq({0, {[""]=""}}, luaevalarg(mapsp(sp('binary', '[""]'), '""'))) - eq({0, {[""]=""}}, luaevalarg(mapsp(sp('string', '[""]'), '""'))) - end) - - it('issues an error in some cases', function() - eq("Vim(call):E5100: Cannot convert given lua table: table should either have a sequence of positive integer keys or contain only string keys", - exc_exec('call luaeval("{1, foo=2}")')) - eq("Vim(call):E5101: Cannot convert given lua type", - exc_exec('call luaeval("vim.api.buffer_get_line_slice")')) - startswith("Vim(call):E5107: Error while creating lua chunk for luaeval(): ", - exc_exec('call luaeval("1, 2, 3")')) - startswith("Vim(call):E5108: Error while calling lua chunk for luaeval(): ", - exc_exec('call luaeval("(nil)()")')) - eq("Vim(call):E5101: Cannot convert given lua type", - exc_exec('call luaeval("{42, vim.api}")')) - eq("Vim(call):E5101: Cannot convert given lua type", - exc_exec('call luaeval("{foo=42, baz=vim.api}")')) - - -- The following should not crash: conversion error happens inside - eq("Vim(call):E5101: Cannot convert given lua type", - exc_exec('call luaeval("vim.api")')) - -- The following should not show internal error - eq("\nE5101: Cannot convert given lua type\n0", - redir_exec('echo luaeval("vim.api")')) - end) - - it('correctly converts containers with type_idx', function() - eq(5, eval('type(luaeval("{[vim.type_idx]=vim.types.float, [vim.val_idx]=0}"))')) - eq(4, eval([[type(luaeval('{[vim.type_idx]=vim.types.dictionary}'))]])) - eq(3, eval([[type(luaeval('{[vim.type_idx]=vim.types.array}'))]])) - - eq({}, funcs.luaeval('{[vim.type_idx]=vim.types.array}')) - - -- Presence of type_idx makes Vim ignore some keys - eq({42}, funcs.luaeval('{[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}')) - eq({foo=2}, funcs.luaeval('{[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}')) - eq(10, funcs.luaeval('{[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}')) - - -- The following should not crash - eq({}, funcs.luaeval('{[vim.type_idx]=vim.types.dictionary}')) - end) - - it('correctly converts from API objects', function() - eq(1, funcs.luaeval('vim.api.vim_eval("1")')) - eq('1', funcs.luaeval([[vim.api.vim_eval('"1"')]])) - eq({}, funcs.luaeval('vim.api.vim_eval("[]")')) - eq({}, funcs.luaeval('vim.api.vim_eval("{}")')) - eq(1, funcs.luaeval('vim.api.vim_eval("1.0")')) - eq(true, funcs.luaeval('vim.api.vim_eval("v:true")')) - eq(false, funcs.luaeval('vim.api.vim_eval("v:false")')) - eq(NIL, funcs.luaeval('vim.api.vim_eval("v:null")')) - - eq(0, eval([[type(luaeval('vim.api.vim_eval("1")'))]])) - eq(1, eval([[type(luaeval('vim.api.vim_eval("''1''")'))]])) - eq(3, eval([[type(luaeval('vim.api.vim_eval("[]")'))]])) - eq(4, eval([[type(luaeval('vim.api.vim_eval("{}")'))]])) - eq(5, eval([[type(luaeval('vim.api.vim_eval("1.0")'))]])) - eq(6, eval([[type(luaeval('vim.api.vim_eval("v:true")'))]])) - eq(6, eval([[type(luaeval('vim.api.vim_eval("v:false")'))]])) - eq(7, eval([[type(luaeval('vim.api.vim_eval("v:null")'))]])) - - eq({foo=42}, funcs.luaeval([[vim.api.vim_eval('{"foo": 42}')]])) - eq({42}, funcs.luaeval([[vim.api.vim_eval('[42]')]])) - - eq({foo={bar=42}, baz=50}, funcs.luaeval([[vim.api.vim_eval('{"foo": {"bar": 42}, "baz": 50}')]])) - eq({{42}, {}}, funcs.luaeval([=[vim.api.vim_eval('[[42], []]')]=])) - end) - - it('correctly converts to API objects', function() - eq(1, funcs.luaeval('vim.api._vim_id(1)')) - eq('1', funcs.luaeval('vim.api._vim_id("1")')) - eq({1}, funcs.luaeval('vim.api._vim_id({1})')) - eq({foo=1}, funcs.luaeval('vim.api._vim_id({foo=1})')) - eq(1.5, funcs.luaeval('vim.api._vim_id(1.5)')) - eq(true, funcs.luaeval('vim.api._vim_id(true)')) - eq(false, funcs.luaeval('vim.api._vim_id(false)')) - eq(NIL, funcs.luaeval('vim.api._vim_id(nil)')) - - eq(0, eval([[type(luaeval('vim.api._vim_id(1)'))]])) - eq(1, eval([[type(luaeval('vim.api._vim_id("1")'))]])) - eq(3, eval([[type(luaeval('vim.api._vim_id({1})'))]])) - eq(4, eval([[type(luaeval('vim.api._vim_id({foo=1})'))]])) - eq(5, eval([[type(luaeval('vim.api._vim_id(1.5)'))]])) - eq(6, eval([[type(luaeval('vim.api._vim_id(true)'))]])) - eq(6, eval([[type(luaeval('vim.api._vim_id(false)'))]])) - eq(7, eval([[type(luaeval('vim.api._vim_id(nil)'))]])) - - eq({foo=1, bar={42, {{baz=true}, 5}}}, funcs.luaeval('vim.api._vim_id({foo=1, bar={42, {{baz=true}, 5}}})')) - end) - - it('correctly converts container objects with type_idx to API objects', function() - eq(5, eval('type(luaeval("vim.api._vim_id({[vim.type_idx]=vim.types.float, [vim.val_idx]=0})"))')) - eq(4, eval([[type(luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.dictionary})'))]])) - eq(3, eval([[type(luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.array})'))]])) - - eq({}, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.array})')) - - -- Presence of type_idx makes Vim ignore some keys - eq({42}, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) - eq({foo=2}, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) - eq(10, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) - eq({}, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2})')) - end) - - it('correctly converts arrays with type_idx to API objects', function() - eq(3, eval([[type(luaeval('vim.api._vim_id_array({[vim.type_idx]=vim.types.array})'))]])) - - eq({}, funcs.luaeval('vim.api._vim_id_array({[vim.type_idx]=vim.types.array})')) - - eq({42}, funcs.luaeval('vim.api._vim_id_array({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) - eq({{foo=2}}, funcs.luaeval('vim.api._vim_id_array({{[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})')) - eq({10}, funcs.luaeval('vim.api._vim_id_array({{[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})')) - eq({}, funcs.luaeval('vim.api._vim_id_array({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2})')) - - eq({}, funcs.luaeval('vim.api._vim_id_array({})')) - eq(3, eval([[type(luaeval('vim.api._vim_id_array({})'))]])) - end) - - it('correctly converts dictionaries with type_idx to API objects', function() - eq(4, eval([[type(luaeval('vim.api._vim_id_dictionary({[vim.type_idx]=vim.types.dictionary})'))]])) - - eq({}, funcs.luaeval('vim.api._vim_id_dictionary({[vim.type_idx]=vim.types.dictionary})')) - - eq({v={42}}, funcs.luaeval('vim.api._vim_id_dictionary({v={[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})')) - eq({foo=2}, funcs.luaeval('vim.api._vim_id_dictionary({[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) - eq({v=10}, funcs.luaeval('vim.api._vim_id_dictionary({v={[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})')) - eq({v={}}, funcs.luaeval('vim.api._vim_id_dictionary({v={[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2}})')) - - -- If API requests dictionary, then empty table will be the one. This is not - -- the case normally because empty table is an empty arrray. - eq({}, funcs.luaeval('vim.api._vim_id_dictionary({})')) - eq(4, eval([[type(luaeval('vim.api._vim_id_dictionary({})'))]])) - end) - - it('correctly converts self-containing containers', function() - meths.set_var('l', {}) - eval('add(l, l)') - eq(true, eval('luaeval("_A == _A[1]", l)')) - eq(true, eval('luaeval("_A[1] == _A[1][1]", [l])')) - eq(true, eval('luaeval("_A.d == _A.d[1]", {"d": l})')) - eq(true, eval('luaeval("_A ~= _A[1]", [l])')) - - meths.set_var('d', {foo=42}) - eval('extend(d, {"d": d})') - eq(true, eval('luaeval("_A == _A.d", d)')) - eq(true, eval('luaeval("_A[1] == _A[1].d", [d])')) - eq(true, eval('luaeval("_A.d == _A.d.d", {"d": d})')) - eq(true, eval('luaeval("_A ~= _A.d", {"d": d})')) - end) - - it('errors out correctly when working with API', function() - -- Conversion errors - eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Cannot convert given lua type', - exc_exec([[call luaeval("vim.api._vim_id(vim.api._vim_id)")]])) - eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Cannot convert given lua table', - exc_exec([[call luaeval("vim.api._vim_id({1, foo=42})")]])) - eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Cannot convert given lua type', - exc_exec([[call luaeval("vim.api._vim_id({42, vim.api._vim_id})")]])) - -- Errors in number of arguments - eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Expected 1 argument', - exc_exec([[call luaeval("vim.api._vim_id()")]])) - eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Expected 1 argument', - exc_exec([[call luaeval("vim.api._vim_id(1, 2)")]])) - eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Expected 2 arguments', - exc_exec([[call luaeval("vim.api.vim_set_var(1, 2, 3)")]])) - -- Error in argument types - eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Expected lua string', - exc_exec([[call luaeval("vim.api.vim_set_var(1, 2)")]])) - - eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Expected lua number', - exc_exec([[call luaeval("vim.api.buffer_get_line(0, 'test')")]])) - eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Number is not integral', - exc_exec([[call luaeval("vim.api.buffer_get_line(0, 1.5)")]])) - - eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Expected lua table', - exc_exec([[call luaeval("vim.api._vim_id_float('test')")]])) - eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Unexpected type', - exc_exec([[call luaeval("vim.api._vim_id_float({[vim.type_idx]=vim.types.dictionary})")]])) - - eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Expected lua table', - exc_exec([[call luaeval("vim.api._vim_id_array(1)")]])) - eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Unexpected type', - exc_exec([[call luaeval("vim.api._vim_id_array({[vim.type_idx]=vim.types.dictionary})")]])) - - eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Expected lua table', - exc_exec([[call luaeval("vim.api._vim_id_dictionary(1)")]])) - eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): Unexpected type', - exc_exec([[call luaeval("vim.api._vim_id_dictionary({[vim.type_idx]=vim.types.array})")]])) - -- TODO: check for errors with Tabpage argument - -- TODO: check for errors with Window argument - -- TODO: check for errors with Buffer argument - end) - - it('accepts any value as API Boolean', function() - eq('', funcs.luaeval('vim.api.vim_replace_termcodes("", vim, false, nil)')) - eq('', funcs.luaeval('vim.api.vim_replace_termcodes("", 0, 1.5, "test")')) - eq('', funcs.luaeval('vim.api.vim_replace_termcodes("", true, {}, {[vim.type_idx]=vim.types.array})')) - end) - - -- TODO: check buffer/window/etc. -end) From 53b89c1dcf44c95827cc85a9657faee451c573c5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 21 Jan 2017 01:42:50 +0300 Subject: [PATCH 0121/1671] executor/executor: Free lcmd on error --- src/nvim/viml/executor/executor.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/nvim/viml/executor/executor.c b/src/nvim/viml/executor/executor.c index 29ec8dc927..acc375881c 100644 --- a/src/nvim/viml/executor/executor.c +++ b/src/nvim/viml/executor/executor.c @@ -221,6 +221,9 @@ static int nlua_eval_lua_string(lua_State *lstate) if (luaL_loadbuffer(lstate, lcmd, lcmd_len, NLUA_EVAL_NAME)) { nlua_error(lstate, _("E5107: Error while creating lua chunk for luaeval(): %.*s")); + if (lcmd != (char *)IObuff) { + xfree(lcmd); + } return 0; } if (lcmd != (char *)IObuff) { From 666d85d3ce80c19c9cd2acd156edbf50fd7e8741 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 21 Jan 2017 01:52:19 +0300 Subject: [PATCH 0122/1671] functests: Some more tests --- test/functional/lua/api_spec.lua | 10 ++++++++++ test/functional/lua/luaeval_spec.lua | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/test/functional/lua/api_spec.lua b/test/functional/lua/api_spec.lua index 31c855c2c1..f6f65cb741 100644 --- a/test/functional/lua/api_spec.lua +++ b/test/functional/lua/api_spec.lua @@ -36,6 +36,16 @@ describe('luaeval(vim.api.…)', function() end) end) + it('correctly evaluates API code which calls luaeval', function() + eq(1, funcs.luaeval(([===[vim.api.nvim_eval([==[ + luaeval('vim.api.nvim_eval([=[ + luaeval("vim.api.nvim_eval([[ + luaeval(1) + ]])") + ]=])') + ]==])]===]):gsub('\n', ' '))) + end) + it('correctly converts from API objects', function() eq(1, funcs.luaeval('vim.api.nvim_eval("1")')) eq('1', funcs.luaeval([[vim.api.nvim_eval('"1"')]])) diff --git a/test/functional/lua/luaeval_spec.lua b/test/functional/lua/luaeval_spec.lua index 345848cfff..b0169c3d6b 100644 --- a/test/functional/lua/luaeval_spec.lua +++ b/test/functional/lua/luaeval_spec.lua @@ -241,4 +241,11 @@ describe('luaeval()', function() eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: ERROR', exc_exec([[call luaeval("error('ERROR')")]])) end) + + it('does not leak memory when called with too long line with syntax error', + function() + local s = ('x'):rep(65536) + eq('Vim(call):E5107: Error while creating lua chunk for luaeval(): [string ""]:1: unexpected symbol near \')\'', + exc_exec([[call luaeval("(']] .. s ..[[' + )")]])) + end) end) From 1646a28173d1b7c8309188ebe7b6fb38a6ca8315 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 21 Jan 2017 02:33:09 +0300 Subject: [PATCH 0123/1671] cmake: Allow switching from luajit to lua --- CMakeLists.txt | 11 +++++++++-- src/nvim/CMakeLists.txt | 6 +++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b746dd460e..782dc8c87a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -310,8 +310,15 @@ include_directories(SYSTEM ${LIBUV_INCLUDE_DIRS}) find_package(Msgpack 1.0.0 REQUIRED) include_directories(SYSTEM ${MSGPACK_INCLUDE_DIRS}) -find_package(LuaJit REQUIRED) -include_directories(SYSTEM ${LUAJIT_INCLUDE_DIRS}) +option(PREFER_LUAJIT "Prefer LuaJIT over Lua." ON) + +if(PREFER_LUAJIT) + find_package(LuaJit REQUIRED) + include_directories(SYSTEM ${LUAJIT_INCLUDE_DIRS}) +else() + find_package(Lua REQUIRED) + include_directories(SYSTEM ${LUA_INCLUDE_DIR}) +endif() if(UNIX) option(FEAT_TUI "Enable the Terminal UI" ON) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index a47a8e49c7..7a7dbbcce6 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -291,8 +291,12 @@ list(APPEND NVIM_LINK_LIBRARIES ${LIBTERMKEY_LIBRARIES} ${UNIBILIUM_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} - ${LUAJIT_LIBRARIES} ) +if(PREFER_LUAJIT) + list(APPEND NVIM_LINK_LIBRARIES ${LUAJIT_LIBRARIES}) +else() + list(APPEND NVIM_LINK_LIBRARIES ${LUA_LIBRARIES}) +endif() if(UNIX) list(APPEND NVIM_LINK_LIBRARIES From 8fec4d53d0f6db0847050bf2a608141bbd98e768 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 22 Jan 2017 04:21:27 +0300 Subject: [PATCH 0124/1671] ci: Make ASAN build link with lua, build lua with address sanitizer --- .ci/common/build.sh | 3 ++- .travis.yml | 4 +++- third-party/cmake/BuildLua.cmake | 19 ++++++++++++++++--- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/.ci/common/build.sh b/.ci/common/build.sh index 44087513ee..64c9df00ed 100644 --- a/.ci/common/build.sh +++ b/.ci/common/build.sh @@ -2,7 +2,8 @@ build_deps() { if [[ "${BUILD_32BIT}" == ON ]]; then DEPS_CMAKE_FLAGS="${DEPS_CMAKE_FLAGS} ${CMAKE_FLAGS_32BIT}" fi - if [[ "${FUNCTIONALTEST}" == "functionaltest-lua" ]]; then + if [[ "${FUNCTIONALTEST}" == "functionaltest-lua" ]] \ + || [[ "${CLANG_SANITIZER}" == "ASAN_UBSAN" ]]; then DEPS_CMAKE_FLAGS="${DEPS_CMAKE_FLAGS} -DUSE_BUNDLED_LUA=ON" fi diff --git a/.travis.yml b/.travis.yml index d28fc9d7f1..978dede667 100644 --- a/.travis.yml +++ b/.travis.yml @@ -69,7 +69,9 @@ matrix: env: BUILD_32BIT=ON - os: linux compiler: clang-3.8 - env: CLANG_SANITIZER=ASAN_UBSAN + env: > + CLANG_SANITIZER=ASAN_UBSAN + CMAKE_FLAGS="$CMAKE_FLAGS -DPREFER_LUAJIT=false" - os: linux compiler: clang-3.8 env: CLANG_SANITIZER=TSAN diff --git a/third-party/cmake/BuildLua.cmake b/third-party/cmake/BuildLua.cmake index 1c5e2a186c..ed300710d1 100644 --- a/third-party/cmake/BuildLua.cmake +++ b/third-party/cmake/BuildLua.cmake @@ -51,19 +51,32 @@ else() endif() endif() +set(LUA_CFLAGS "-g3") +set(LUA_LDFLAGS "") + +if(CLANG_ASAN_UBSAN) + set(LUA_CFLAGS "${LUA_CFLAGS} -fsanitize=address") + set(LUA_CFLAGS "${LUA_CFLAGS} -fno-omit-frame-pointer") + set(LUA_CFLAGS "${LUA_CFLAGS} -fno-optimize-sibling-calls") + + set(LUA_LDFLAGS "${LUA_LDFLAGS} -fsanitize=address") +endif() + set(LUA_CONFIGURE_COMMAND sed -e "/^CC/s@gcc@${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ARG1}@" - -e "/^CFLAGS/s@-O2@-g3@" + -e "/^CFLAGS/s@-O2@${LUA_CFLAGS}@" + -e "/^MYLDFLAGS/s@$@${LUA_LDFLAGS}@" -e "s@-lreadline@@g" -e "s@-lhistory@@g" -e "s@-lncurses@@g" -i ${DEPS_BUILD_DIR}/src/lua/src/Makefile && sed -e "/#define LUA_USE_READLINE/d" -i ${DEPS_BUILD_DIR}/src/lua/src/luaconf.h) +set(LUA_INSTALL_TOP_ARG "INSTALL_TOP=${DEPS_INSTALL_DIR}") set(LUA_BUILD_COMMAND - ${MAKE_PRG} ${LUA_TARGET}) + ${MAKE_PRG} ${LUA_INSTALL_TOP_ARG} ${LUA_TARGET}) set(LUA_INSTALL_COMMAND - ${MAKE_PRG} INSTALL_TOP=${DEPS_INSTALL_DIR} install) + ${MAKE_PRG} ${LUA_INSTALL_TOP_ARG} install) message(STATUS "Lua target is ${LUA_TARGET}") From 6b4a51f7ea49a6ef8305831e8d96b2a7cc4ba5f3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 22 Jan 2017 04:34:26 +0300 Subject: [PATCH 0125/1671] scripts: Make generate_vim_module more generic --- ...enerate_vim_module.lua => gencharblob.lua} | 24 +++++++++++++------ src/nvim/CMakeLists.txt | 8 +++---- 2 files changed, 21 insertions(+), 11 deletions(-) rename scripts/{generate_vim_module.lua => gencharblob.lua} (56%) diff --git a/scripts/generate_vim_module.lua b/scripts/gencharblob.lua similarity index 56% rename from scripts/generate_vim_module.lua rename to scripts/gencharblob.lua index 954f1c38be..d860375e26 100644 --- a/scripts/generate_vim_module.lua +++ b/scripts/gencharblob.lua @@ -1,13 +1,23 @@ -assert(#arg == 2) +if arg[1] == '--help' then + print('Usage:') + print(' gencharblob.lua source target varname') + print('') + print('Generates C file with big uint8_t blob.') + print('Blob will be stored in a static const array named varname.') + os.exit() +end -module_file = arg[1] -target_file = arg[2] +assert(#arg == 3) -module = io.open(module_file, 'r') +local source_file = arg[1] +local target_file = arg[2] +local varname = arg[3] + +source = io.open(source_file, 'r') target = io.open(target_file, 'w') target:write('#include \n\n') -target:write('static const uint8_t vim_module[] = {\n') +target:write(('static const uint8_t %s[] = {\n'):format(varname)) num_bytes = 0 MAX_NUM_BYTES = 15 -- 78 / 5: maximum number of bytes on one line @@ -21,7 +31,7 @@ increase_num_bytes = function() end end -for line in module:lines() do +for line in source:lines() do for i = 1,string.len(line) do byte = string.byte(line, i) assert(byte ~= 0) @@ -34,5 +44,5 @@ end target:write(' 0};\n') -module:close() +source:close() target:close() diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 7a7dbbcce6..21a39b950f 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -39,7 +39,7 @@ set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/unicode) set(GENERATED_UNICODE_TABLES ${GENERATED_DIR}/unicode_tables.generated.h) set(VIM_MODULE_FILE ${GENERATED_DIR}/viml/executor/vim_module.generated.h) set(VIM_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/src/nvim/viml/executor/vim.lua) -set(VIM_MODULE_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/generate_vim_module.lua) +set(CHAR_BLOB_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gencharblob.lua) file(GLOB API_HEADERS api/*.h) file(GLOB MSGPACK_RPC_HEADERS msgpack_rpc/*.h) @@ -222,10 +222,10 @@ add_custom_command( add_custom_command( OUTPUT ${VIM_MODULE_FILE} - COMMAND ${LUA_PRG} ${VIM_MODULE_GENERATOR} ${VIM_MODULE_SOURCE} - ${VIM_MODULE_FILE} + COMMAND ${LUA_PRG} ${CHAR_BLOB_GENERATOR} ${VIM_MODULE_SOURCE} + ${VIM_MODULE_FILE} vim_module DEPENDS - ${VIM_MODULE_GENERATOR} + ${CHAR_BLOB_GENERATOR} ${VIM_MODULE_SOURCE} ) From ca4c8b7f8a7b9543ef01157cb5ab94783f624ac6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 22 Jan 2017 04:55:26 +0300 Subject: [PATCH 0126/1671] api: Allow kObjectTypeNil to be zero without breaking compatibility --- src/nvim/api/private/defs.h | 7 ++++--- src/nvim/msgpack_rpc/helpers.c | 26 +++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index cb7ee8eb4c..432ab347bc 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -100,15 +100,16 @@ typedef struct { typedef enum { kObjectTypeNil = 0, - kObjectTypeBuffer, - kObjectTypeWindow, - kObjectTypeTabpage, kObjectTypeBoolean, kObjectTypeInteger, kObjectTypeFloat, kObjectTypeString, kObjectTypeArray, kObjectTypeDictionary, + // EXT types, cannot be split or reordered, see #EXT_OBJECT_TYPE_SHIFT + kObjectTypeBuffer, + kObjectTypeWindow, + kObjectTypeTabpage, } ObjectType; struct object { diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index 64a018f5c3..972d5b3441 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -20,13 +20,20 @@ static msgpack_zone zone; static msgpack_sbuffer sbuffer; +/// Value by which objects represented as EXT type are shifted +/// +/// Subtracted when packing, added when unpacking. Used to allow moving +/// buffer/window/tabpage block inside ObjectType enum. This block yet cannot be +/// split or reordered. +#define EXT_OBJECT_TYPE_SHIFT kObjectTypeBuffer + #define HANDLE_TYPE_CONVERSION_IMPL(t, lt) \ bool msgpack_rpc_to_##lt(const msgpack_object *const obj, \ Integer *const arg) \ FUNC_ATTR_NONNULL_ALL \ { \ if (obj->type != MSGPACK_OBJECT_EXT \ - || obj->via.ext.type != kObjectType##t) { \ + || obj->via.ext.type + EXT_OBJECT_TYPE_SHIFT != kObjectType##t) { \ return false; \ } \ \ @@ -51,7 +58,8 @@ static msgpack_sbuffer sbuffer; msgpack_packer pac; \ msgpack_packer_init(&pac, &sbuffer, msgpack_sbuffer_write); \ msgpack_pack_int64(&pac, (handle_T)o); \ - msgpack_pack_ext(res, sbuffer.size, kObjectType##t); \ + msgpack_pack_ext(res, sbuffer.size, \ + kObjectType##t - EXT_OBJECT_TYPE_SHIFT); \ msgpack_pack_ext_body(res, sbuffer.data, sbuffer.size); \ msgpack_sbuffer_clear(&sbuffer); \ } @@ -211,7 +219,7 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg) break; } case MSGPACK_OBJECT_EXT: { - switch (cur.mobj->via.ext.type) { + switch ((ObjectType)(cur.mobj->via.ext.type + EXT_OBJECT_TYPE_SHIFT)) { case kObjectTypeBuffer: { cur.aobj->type = kObjectTypeBuffer; ret = msgpack_rpc_to_buffer(cur.mobj, &cur.aobj->data.integer); @@ -227,6 +235,15 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg) ret = msgpack_rpc_to_tabpage(cur.mobj, &cur.aobj->data.integer); break; } + case kObjectTypeNil: + case kObjectTypeBoolean: + case kObjectTypeInteger: + case kObjectTypeFloat: + case kObjectTypeString: + case kObjectTypeArray: + case kObjectTypeDictionary: { + break; + } } break; } @@ -350,6 +367,9 @@ void msgpack_rpc_from_object(const Object result, msgpack_packer *const res) kv_push(stack, ((APIToMPObjectStackItem) { &result, false, 0 })); while (kv_size(stack)) { APIToMPObjectStackItem cur = kv_last(stack); + STATIC_ASSERT(kObjectTypeWindow == kObjectTypeBuffer + 1 + && kObjectTypeTabpage == kObjectTypeWindow + 1, + "Buffer, window and tabpage enum items are in order"); switch (cur.aobj->type) { case kObjectTypeNil: { msgpack_pack_nil(res); From 927e6efc3d93ff9d34c532180591dc3ca326edf5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 22 Jan 2017 05:16:05 +0300 Subject: [PATCH 0127/1671] clint: Allow omitting include guards in .c.h file and func_attr.h file --- src/clint.py | 5 +++++ src/nvim/eval/typval_encode.c.h | 5 ----- src/nvim/func_attr.h | 5 ----- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/clint.py b/src/clint.py index ce31822ada..ca4dbdb1bc 100755 --- a/src/clint.py +++ b/src/clint.py @@ -182,6 +182,7 @@ _ERROR_CATEGORIES = [ 'build/include_order', 'build/printf_format', 'build/storage_class', + 'build/useless_fattr', 'readability/alt_tokens', 'readability/bool', 'readability/braces', @@ -1224,6 +1225,10 @@ def CheckForHeaderGuard(filename, lines, error): lines: An array of strings, each representing a line of the file. error: The function to call with any errors found. """ + if filename.endswith('.c.h') or FileInfo(filename).RelativePath() in set(( + 'func_attr.h', + )): + return cppvar = GetHeaderGuardCPPVariable(filename) diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index 4ff5589887..812340b9c3 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -233,10 +233,6 @@ /// /// This name will only be used by one of the above macros which are defined by /// the caller. Functions defined here do not use first argument directly. -#ifndef NVIM_EVAL_TYPVAL_ENCODE_C_H -#define NVIM_EVAL_TYPVAL_ENCODE_C_H -#undef NVIM_EVAL_TYPVAL_ENCODE_C_H - #include #include #include @@ -816,4 +812,3 @@ encode_vim_to__error_ret: // Prevent “unused label” warnings. goto typval_encode_stop_converting_one_item; } -#endif // NVIM_EVAL_TYPVAL_ENCODE_C_H diff --git a/src/nvim/func_attr.h b/src/nvim/func_attr.h index 18410445e1..7b8fcc4343 100644 --- a/src/nvim/func_attr.h +++ b/src/nvim/func_attr.h @@ -41,10 +41,6 @@ // $ gcc -E -dM - Date: Sun, 22 Jan 2017 05:35:48 +0300 Subject: [PATCH 0128/1671] msgpack_rpc: Fix #HANDLE_TYPE_CONVERSION_IMPL Function declarations generator is able to handle properly only the *first* function definition that is in macros, and only if it is the first entity in the macros. So msgpack_rpc_from_* was already really a static function, additionally its attributes were useless. This commit switches to explicit declarations and makes generated functions static. --- src/nvim/msgpack_rpc/helpers.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index 972d5b3441..94a4f5ce3e 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -28,9 +28,11 @@ static msgpack_sbuffer sbuffer; #define EXT_OBJECT_TYPE_SHIFT kObjectTypeBuffer #define HANDLE_TYPE_CONVERSION_IMPL(t, lt) \ - bool msgpack_rpc_to_##lt(const msgpack_object *const obj, \ - Integer *const arg) \ - FUNC_ATTR_NONNULL_ALL \ + static bool msgpack_rpc_to_##lt(const msgpack_object *const obj, \ + Integer *const arg) \ + REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT; \ + static bool msgpack_rpc_to_##lt(const msgpack_object *const obj, \ + Integer *const arg) \ { \ if (obj->type != MSGPACK_OBJECT_EXT \ || obj->via.ext.type + EXT_OBJECT_TYPE_SHIFT != kObjectType##t) { \ @@ -52,8 +54,9 @@ static msgpack_sbuffer sbuffer; return true; \ } \ \ - void msgpack_rpc_from_##lt(Integer o, msgpack_packer *res) \ - FUNC_ATTR_NONNULL_ARG(2) \ + static void msgpack_rpc_from_##lt(Integer o, msgpack_packer *res) \ + REAL_FATTR_NONNULL_ARG(2); \ + static void msgpack_rpc_from_##lt(Integer o, msgpack_packer *res) \ { \ msgpack_packer pac; \ msgpack_packer_init(&pac, &sbuffer, msgpack_sbuffer_write); \ From c5a2124e81b87334237916bd087c5c412862d6f2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 22 Jan 2017 06:10:52 +0300 Subject: [PATCH 0129/1671] ci: When building lua use -fPIC --- third-party/cmake/BuildLua.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/third-party/cmake/BuildLua.cmake b/third-party/cmake/BuildLua.cmake index ed300710d1..d0566f6df8 100644 --- a/third-party/cmake/BuildLua.cmake +++ b/third-party/cmake/BuildLua.cmake @@ -58,6 +58,7 @@ if(CLANG_ASAN_UBSAN) set(LUA_CFLAGS "${LUA_CFLAGS} -fsanitize=address") set(LUA_CFLAGS "${LUA_CFLAGS} -fno-omit-frame-pointer") set(LUA_CFLAGS "${LUA_CFLAGS} -fno-optimize-sibling-calls") + set(LUA_CFLAGS "${LUA_CFLAGS} -fPIC") set(LUA_LDFLAGS "${LUA_LDFLAGS} -fsanitize=address") endif() From d33b13dd6ba5fddbb0903c28fd78a69b5f0a11bd Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 22 Jan 2017 22:02:39 +0300 Subject: [PATCH 0130/1671] cmake: Try fixing ASAN nvim-test compilation --- src/nvim/CMakeLists.txt | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 21a39b950f..918a64c2b4 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -363,6 +363,23 @@ if(WIN32) add_dependencies(nvim_runtime_deps external_blobs) endif() +add_library(libnvim STATIC EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_SOURCES} + ${NEOVIM_SOURCES} ${NEOVIM_HEADERS}) +target_link_libraries(libnvim ${NVIM_LINK_LIBRARIES}) +set_target_properties(libnvim PROPERTIES + POSITION_INDEPENDENT_CODE ON + OUTPUT_NAME nvim) +set_property(TARGET libnvim + APPEND_STRING PROPERTY COMPILE_FLAGS " -DMAKE_LIB ") + +add_library(nvim-test MODULE EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_SOURCES} + ${NEOVIM_SOURCES} ${UNIT_TEST_FIXTURES} ${NEOVIM_HEADERS}) +target_link_libraries(nvim-test ${NVIM_LINK_LIBRARIES}) +set_target_properties(nvim-test PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_property(TARGET nvim-test + APPEND_STRING PROPERTY COMPILE_FLAGS " -DUNIT_TESTING ") + if(CLANG_ASAN_UBSAN) message(STATUS "Enabling Clang address sanitizer and undefined behavior sanitizer for nvim.") check_c_compiler_flag(-fno-sanitize-recover=all SANITIZE_RECOVER_ALL) @@ -374,6 +391,9 @@ if(CLANG_ASAN_UBSAN) set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "-DEXITFREE ") set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "${SANITIZE_RECOVER} -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=address -fsanitize=undefined -fsanitize-blacklist=${PROJECT_SOURCE_DIR}/src/.asan-blacklist") set_property(TARGET nvim APPEND_STRING PROPERTY LINK_FLAGS "-fsanitize=address -fsanitize=undefined ") + + set_property(TARGET nvim-test APPEND_STRING PROPERTY COMPILE_FLAGS "${SANITIZE_RECOVER} -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=address -fsanitize-blacklist=${PROJECT_SOURCE_DIR}/src/.asan-blacklist") + set_property(TARGET nvim-test APPEND_STRING PROPERTY LINK_FLAGS "-fsanitize=address ") elseif(CLANG_MSAN) message(STATUS "Enabling Clang memory sanitizer for nvim.") set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "-DEXITFREE ") @@ -387,17 +407,4 @@ elseif(CLANG_TSAN) set_property(TARGET nvim APPEND_STRING PROPERTY LINK_FLAGS "-fsanitize=thread ") endif() -add_library(libnvim STATIC EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_SOURCES} - ${NEOVIM_SOURCES} ${NEOVIM_HEADERS}) -target_link_libraries(libnvim ${NVIM_LINK_LIBRARIES}) -set_target_properties(libnvim PROPERTIES - POSITION_INDEPENDENT_CODE ON - OUTPUT_NAME nvim) -set_property(TARGET libnvim APPEND_STRING PROPERTY COMPILE_FLAGS " -DMAKE_LIB ") - -add_library(nvim-test MODULE EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_SOURCES} - ${NEOVIM_SOURCES} ${UNIT_TEST_FIXTURES} ${NEOVIM_HEADERS}) -target_link_libraries(nvim-test ${NVIM_LINK_LIBRARIES}) -set_property(TARGET nvim-test APPEND_STRING PROPERTY COMPILE_FLAGS -DUNIT_TESTING) - add_subdirectory(po) From d5228787ce62c37291018a7b0215e07ebf26946d Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 23 Jan 2017 01:30:47 +0300 Subject: [PATCH 0131/1671] deps: Always build lua with -fPIC and -O0 --- third-party/cmake/BuildLua.cmake | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/third-party/cmake/BuildLua.cmake b/third-party/cmake/BuildLua.cmake index d0566f6df8..ea1371d1d5 100644 --- a/third-party/cmake/BuildLua.cmake +++ b/third-party/cmake/BuildLua.cmake @@ -51,14 +51,13 @@ else() endif() endif() -set(LUA_CFLAGS "-g3") +set(LUA_CFLAGS "-O0 -g3 -fPIC") set(LUA_LDFLAGS "") if(CLANG_ASAN_UBSAN) set(LUA_CFLAGS "${LUA_CFLAGS} -fsanitize=address") set(LUA_CFLAGS "${LUA_CFLAGS} -fno-omit-frame-pointer") set(LUA_CFLAGS "${LUA_CFLAGS} -fno-optimize-sibling-calls") - set(LUA_CFLAGS "${LUA_CFLAGS} -fPIC") set(LUA_LDFLAGS "${LUA_LDFLAGS} -fsanitize=address") endif() From d60302d5177e2ba99e84588c8ddd70dad0cbc64a Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 27 Jan 2017 02:55:34 +0300 Subject: [PATCH 0132/1671] cmake: Link libnvim-test with luajit in place of lua, disable ASAN Reasoning: luajit is not being compiled with sanitizers, lua is. Given that linking with sanitized libraries requires sanitizers enabled, it is needed to either compile libnvim-test with sanitizers or link it with lua compiled without sanitizers. Most easy way to do the latter is just use luajit which is compiled without sanitizers (as they do not work well with luajit). --- CMakeLists.txt | 10 ++++------ src/nvim/CMakeLists.txt | 27 +++++++++++++++++---------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 782dc8c87a..53582a6f59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -310,14 +310,12 @@ include_directories(SYSTEM ${LIBUV_INCLUDE_DIRS}) find_package(Msgpack 1.0.0 REQUIRED) include_directories(SYSTEM ${MSGPACK_INCLUDE_DIRS}) -option(PREFER_LUAJIT "Prefer LuaJIT over Lua." ON) +option(PREFER_LUAJIT "Prefer LuaJIT over Lua when compiling executable. Test library always uses luajit." ON) -if(PREFER_LUAJIT) - find_package(LuaJit REQUIRED) - include_directories(SYSTEM ${LUAJIT_INCLUDE_DIRS}) -else() +find_package(LuaJit REQUIRED) + +if(NOT PREFER_LUAJIT) find_package(Lua REQUIRED) - include_directories(SYSTEM ${LUA_INCLUDE_DIR}) endif() if(UNIX) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 918a64c2b4..467af5bc00 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -292,11 +292,6 @@ list(APPEND NVIM_LINK_LIBRARIES ${UNIBILIUM_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) -if(PREFER_LUAJIT) - list(APPEND NVIM_LINK_LIBRARIES ${LUAJIT_LIBRARIES}) -else() - list(APPEND NVIM_LINK_LIBRARIES ${LUA_LIBRARIES}) -endif() if(UNIX) list(APPEND NVIM_LINK_LIBRARIES @@ -306,6 +301,14 @@ if(UNIX) endif() set(NVIM_EXEC_LINK_LIBRARIES ${NVIM_LINK_LIBRARIES}) +set(NVIM_TEST_LINK_LIBRARIES ${NVIM_LINK_LIBRARIES}) + +if(PREFER_LUAJIT) + list(APPEND NVIM_EXEC_LINK_LIBRARIES ${LUAJIT_LIBRARIES}) +else() + list(APPEND NVIM_EXEC_LINK_LIBRARIES ${LUA_LIBRARIES}) +endif() +list(APPEND NVIM_TEST_LINK_LIBRARIES ${LUAJIT_LIBRARIES}) # Don't use jemalloc in the unit test library. if(JEMALLOC_FOUND) @@ -317,6 +320,12 @@ add_executable(nvim ${NEOVIM_GENERATED_SOURCES} ${NEOVIM_SOURCES} target_link_libraries(nvim ${NVIM_EXEC_LINK_LIBRARIES}) install_helper(TARGETS nvim) +if(PREFER_LUAJIT) + target_include_directories(nvim SYSTEM PRIVATE ${LUAJIT_INCLUDE_DIRS}) +else() + target_include_directories(nvim SYSTEM PRIVATE ${LUA_INCLUDE_DIRS}) +endif() + if(WIN32) # Copy DLLs and third-party tools to bin/ and install them along with nvim add_custom_target(nvim_runtime_deps ALL @@ -365,7 +374,7 @@ endif() add_library(libnvim STATIC EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_SOURCES} ${NEOVIM_SOURCES} ${NEOVIM_HEADERS}) -target_link_libraries(libnvim ${NVIM_LINK_LIBRARIES}) +target_link_libraries(libnvim ${NVIM_TEST_LINK_LIBRARIES}) set_target_properties(libnvim PROPERTIES POSITION_INDEPENDENT_CODE ON OUTPUT_NAME nvim) @@ -374,7 +383,8 @@ set_property(TARGET libnvim add_library(nvim-test MODULE EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_SOURCES} ${NEOVIM_SOURCES} ${UNIT_TEST_FIXTURES} ${NEOVIM_HEADERS}) -target_link_libraries(nvim-test ${NVIM_LINK_LIBRARIES}) +target_link_libraries(nvim-test ${NVIM_TEST_LINK_LIBRARIES}) +target_include_directories(nvim-test SYSTEM PRIVATE ${LUAJIT_INCLUDE_DIRS}) set_target_properties(nvim-test PROPERTIES POSITION_INDEPENDENT_CODE ON) set_property(TARGET nvim-test @@ -391,9 +401,6 @@ if(CLANG_ASAN_UBSAN) set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "-DEXITFREE ") set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "${SANITIZE_RECOVER} -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=address -fsanitize=undefined -fsanitize-blacklist=${PROJECT_SOURCE_DIR}/src/.asan-blacklist") set_property(TARGET nvim APPEND_STRING PROPERTY LINK_FLAGS "-fsanitize=address -fsanitize=undefined ") - - set_property(TARGET nvim-test APPEND_STRING PROPERTY COMPILE_FLAGS "${SANITIZE_RECOVER} -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=address -fsanitize-blacklist=${PROJECT_SOURCE_DIR}/src/.asan-blacklist") - set_property(TARGET nvim-test APPEND_STRING PROPERTY LINK_FLAGS "-fsanitize=address ") elseif(CLANG_MSAN) message(STATUS "Enabling Clang memory sanitizer for nvim.") set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "-DEXITFREE ") From 9c743df2d532b3adc04b532fa6fc9c7838b31353 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 27 Jan 2017 03:12:53 +0300 Subject: [PATCH 0133/1671] cmake: Use LuaJIT include directory for declarations generator --- src/nvim/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 467af5bc00..be3c8d1b07 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -151,7 +151,7 @@ if(CLANG_ASAN_UBSAN OR CLANG_MSAN OR CLANG_TSAN) endif() get_directory_property(gen_includes INCLUDE_DIRECTORIES) -foreach(gen_include ${gen_includes}) +foreach(gen_include ${gen_includes} ${LUAJIT_INCLUDE_DIRS}) list(APPEND gen_cflags "-I${gen_include}") endforeach() string(TOUPPER "${CMAKE_BUILD_TYPE}" build_type) From f74322b9a5695d2a3bf31e1da05197d700b94c76 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 28 Jan 2017 00:46:58 +0300 Subject: [PATCH 0134/1671] gendeclarations: Save where declaration is comping from --- scripts/gendeclarations.lua | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/scripts/gendeclarations.lua b/scripts/gendeclarations.lua index ff69b18ae4..6358ea4778 100755 --- a/scripts/gendeclarations.lua +++ b/scripts/gendeclarations.lua @@ -188,23 +188,29 @@ local footer = [[ local non_static = header local static = header -local filepattern = '^#%a* %d+ "[^"]-/?([^"/]+)"' +local filepattern = '^#%a* (%d+) "[^"]-/?([^"/]+)"' local curfile init = 0 curfile = nil neededfile = fname:match('[^/]+$') +local declline = 0 +local declendpos = 0 while init ~= nil do init = text:find('\n', init) if init == nil then break end init = init + 1 + declline = declline + 1 if text:sub(init, init) == '#' then - file = text:match(filepattern, init) + local line, file = text:match(filepattern, init) if file ~= nil then curfile = file end + declline = tonumber(line) - 1 + elseif init < declendpos then + -- Skipping over declaration elseif curfile == neededfile then s = init e = pattern:match(text, init) @@ -225,13 +231,15 @@ while init ~= nil do declaration = declaration:gsub(' ?(%*+) ?', ' %1') declaration = declaration:gsub(' ?(FUNC_ATTR_)', ' %1') declaration = declaration:gsub(' $', '') - declaration = declaration .. ';\n' + declaration = declaration .. ';' + declaration = declaration .. ' // ' .. curfile .. ':' .. declline + declaration = declaration .. '\n' if text:sub(s, s + 5) == 'static' then static = static .. declaration else non_static = non_static .. declaration end - init = e + declendpos = e end end end From c470fc32a8c96fb153b779489c22b8e86003e9f0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 28 Jan 2017 00:52:27 +0300 Subject: [PATCH 0135/1671] gendeclarations: Also save information about directory --- scripts/gendeclarations.lua | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/scripts/gendeclarations.lua b/scripts/gendeclarations.lua index 6358ea4778..5d5939f7d1 100755 --- a/scripts/gendeclarations.lua +++ b/scripts/gendeclarations.lua @@ -188,14 +188,15 @@ local footer = [[ local non_static = header local static = header -local filepattern = '^#%a* (%d+) "[^"]-/?([^"/]+)"' +local filepattern = '^#%a* (%d+) "([^"]-)/?([^"/]+)"' local curfile -init = 0 -curfile = nil -neededfile = fname:match('[^/]+$') +local init = 0 +local curfile = nil +local neededfile = fname:match('[^/]+$') local declline = 0 local declendpos = 0 +local curdir = nil while init ~= nil do init = text:find('\n', init) if init == nil then @@ -204,11 +205,17 @@ while init ~= nil do init = init + 1 declline = declline + 1 if text:sub(init, init) == '#' then - local line, file = text:match(filepattern, init) + local line, dir, file = text:match(filepattern, init) if file ~= nil then curfile = file end declline = tonumber(line) - 1 + local curdir_start = dir:find('src/nvim/') + if curdir_start ~= nil then + curdir = dir:sub(curdir_start + #('src/nvim/')) + else + curdir = dir + end elseif init < declendpos then -- Skipping over declaration elseif curfile == neededfile then @@ -232,7 +239,8 @@ while init ~= nil do declaration = declaration:gsub(' ?(FUNC_ATTR_)', ' %1') declaration = declaration:gsub(' $', '') declaration = declaration .. ';' - declaration = declaration .. ' // ' .. curfile .. ':' .. declline + declaration = declaration .. (' // %s/%s:%u'):format( + curdir, curfile, declline) declaration = declaration .. '\n' if text:sub(s, s + 5) == 'static' then static = static .. declaration From 52c7066f4b546419a1838b41e68a5d1650ac498e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 28 Jan 2017 01:07:18 +0300 Subject: [PATCH 0136/1671] gendeclarations: Handle case when text did not match --- scripts/gendeclarations.lua | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/scripts/gendeclarations.lua b/scripts/gendeclarations.lua index 5d5939f7d1..3f948b91df 100755 --- a/scripts/gendeclarations.lua +++ b/scripts/gendeclarations.lua @@ -208,13 +208,15 @@ while init ~= nil do local line, dir, file = text:match(filepattern, init) if file ~= nil then curfile = file - end - declline = tonumber(line) - 1 - local curdir_start = dir:find('src/nvim/') - if curdir_start ~= nil then - curdir = dir:sub(curdir_start + #('src/nvim/')) + declline = tonumber(line) - 1 + local curdir_start = dir:find('src/nvim/') + if curdir_start ~= nil then + curdir = dir:sub(curdir_start + #('src/nvim/')) + else + curdir = dir + end else - curdir = dir + declline = declline - 1 end elseif init < declendpos then -- Skipping over declaration From ae4adcc70735a89bffb110bcf9d5a993b0786c4d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 28 Jan 2017 03:47:15 +0300 Subject: [PATCH 0137/1671] gendeclarations: Make declarations generator work with macros funcs Now it checks functions also after every semicolon and closing figure brace, possibly preceded by whitespaces (tabs and spaces). This should make messing with declarations in macros not needed. --- scripts/gendeclarations.lua | 34 +++++++++++++++++++++------------- src/nvim/msgpack_rpc/helpers.c | 7 ++----- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/scripts/gendeclarations.lua b/scripts/gendeclarations.lua index 3f948b91df..e999e53e4a 100755 --- a/scripts/gendeclarations.lua +++ b/scripts/gendeclarations.lua @@ -69,17 +69,18 @@ local word = branch( right_word ) ) +local inline_comment = concat( + lit('/*'), + any_amount(concat( + neg_look_ahead(lit('*/')), + any_character + )), + lit('*/') +) local spaces = any_amount(branch( s, -- Comments are really handled by preprocessor, so the following is not needed - concat( - lit('/*'), - any_amount(concat( - neg_look_ahead(lit('*/')), - any_character - )), - lit('*/') - ), + inline_comment, concat( lit('//'), any_amount(concat( @@ -110,6 +111,7 @@ local typ = one_or_more(typ_part) local typ_id = two_or_more(typ_part) local arg = typ_id -- argument name is swallowed by typ local pattern = concat( + any_amount(branch(set(' ', '\t'), inline_comment)), typ_id, -- return type with function name spaces, lit('('), @@ -197,17 +199,22 @@ local neededfile = fname:match('[^/]+$') local declline = 0 local declendpos = 0 local curdir = nil +local is_needed_file = false while init ~= nil do - init = text:find('\n', init) + init = text:find('[\n;}]', init) if init == nil then break end + local init_is_nl = text:sub(init, init) == '\n' init = init + 1 - declline = declline + 1 - if text:sub(init, init) == '#' then + if init_is_nl and is_needed_file then + declline = declline + 1 + end + if init_is_nl and text:sub(init, init) == '#' then local line, dir, file = text:match(filepattern, init) if file ~= nil then curfile = file + is_needed_file = (curfile == neededfile) declline = tonumber(line) - 1 local curdir_start = dir:find('src/nvim/') if curdir_start ~= nil then @@ -220,7 +227,7 @@ while init ~= nil do end elseif init < declendpos then -- Skipping over declaration - elseif curfile == neededfile then + elseif is_needed_file then s = init e = pattern:match(text, init) if e ~= nil then @@ -240,11 +247,12 @@ while init ~= nil do declaration = declaration:gsub(' ?(%*+) ?', ' %1') declaration = declaration:gsub(' ?(FUNC_ATTR_)', ' %1') declaration = declaration:gsub(' $', '') + declaration = declaration:gsub('^ ', '') declaration = declaration .. ';' declaration = declaration .. (' // %s/%s:%u'):format( curdir, curfile, declline) declaration = declaration .. '\n' - if text:sub(s, s + 5) == 'static' then + if declaration:sub(1, 6) == 'static' then static = static .. declaration else non_static = non_static .. declaration diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index 94a4f5ce3e..ec35d56978 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -30,9 +30,7 @@ static msgpack_sbuffer sbuffer; #define HANDLE_TYPE_CONVERSION_IMPL(t, lt) \ static bool msgpack_rpc_to_##lt(const msgpack_object *const obj, \ Integer *const arg) \ - REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT; \ - static bool msgpack_rpc_to_##lt(const msgpack_object *const obj, \ - Integer *const arg) \ + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \ { \ if (obj->type != MSGPACK_OBJECT_EXT \ || obj->via.ext.type + EXT_OBJECT_TYPE_SHIFT != kObjectType##t) { \ @@ -55,8 +53,7 @@ static msgpack_sbuffer sbuffer; } \ \ static void msgpack_rpc_from_##lt(Integer o, msgpack_packer *res) \ - REAL_FATTR_NONNULL_ARG(2); \ - static void msgpack_rpc_from_##lt(Integer o, msgpack_packer *res) \ + FUNC_ATTR_NONNULL_ARG(2) \ { \ msgpack_packer pac; \ msgpack_packer_init(&pac, &sbuffer, msgpack_sbuffer_write); \ From d836464cd2a6cdb4f4b797677794a376d5a12a45 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 28 Jan 2017 22:16:24 +0300 Subject: [PATCH 0138/1671] cmake: Also include luajit directories for libnvim target --- src/nvim/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index be3c8d1b07..0cf331206e 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -374,6 +374,7 @@ endif() add_library(libnvim STATIC EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_SOURCES} ${NEOVIM_SOURCES} ${NEOVIM_HEADERS}) +target_include_directories(libnvim SYSTEM PRIVATE ${LUAJIT_INCLUDE_DIRS}) target_link_libraries(libnvim ${NVIM_TEST_LINK_LIBRARIES}) set_target_properties(libnvim PROPERTIES POSITION_INDEPENDENT_CODE ON From 140cd0da1d0b10cf19a501696971ca2e20eff75b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Jan 2017 00:52:48 +0300 Subject: [PATCH 0139/1671] =?UTF-8?q?functests:=20Fix=20=E2=80=9Cfunction?= =?UTF-8?q?=20has=20more=20then=2060=20upvalues=E2=80=9D=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/functional/helpers.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 13a0cff137..a62c05128b 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -570,7 +570,7 @@ local curbufmeths = create_callindex(curbuf) local curwinmeths = create_callindex(curwin) local curtabmeths = create_callindex(curtab) -local M = { +local module = { prepend_argv = prepend_argv, clear = clear, connect = connect, @@ -644,5 +644,5 @@ return function(after_each) check_cores('build/bin/nvim') end) end - return M + return module end From 62fde319360e27a86c0deba0053ff230f80ca772 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Jan 2017 01:29:51 +0300 Subject: [PATCH 0140/1671] api: Also shift numbers in api_metadata output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes problem introduced by “api: Allow kObjectTypeNil to be zero without breaking compatibility”: apparently there are clients which use metadata and there are which aren’t. For the first that commit would not be needed, for the second that commit misses this critical piece. --- src/nvim/api/private/helpers.c | 9 ++++++--- src/nvim/msgpack_rpc/helpers.c | 7 ------- src/nvim/msgpack_rpc/helpers.h | 7 +++++++ 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 23d1540e2f..373e509120 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -851,15 +851,18 @@ static void init_type_metadata(Dictionary *metadata) Dictionary types = ARRAY_DICT_INIT; Dictionary buffer_metadata = ARRAY_DICT_INIT; - PUT(buffer_metadata, "id", INTEGER_OBJ(kObjectTypeBuffer)); + PUT(buffer_metadata, "id", + INTEGER_OBJ(kObjectTypeBuffer - EXT_OBJECT_TYPE_SHIFT)); PUT(buffer_metadata, "prefix", STRING_OBJ(cstr_to_string("nvim_buf_"))); Dictionary window_metadata = ARRAY_DICT_INIT; - PUT(window_metadata, "id", INTEGER_OBJ(kObjectTypeWindow)); + PUT(window_metadata, "id", + INTEGER_OBJ(kObjectTypeWindow - EXT_OBJECT_TYPE_SHIFT)); PUT(window_metadata, "prefix", STRING_OBJ(cstr_to_string("nvim_win_"))); Dictionary tabpage_metadata = ARRAY_DICT_INIT; - PUT(tabpage_metadata, "id", INTEGER_OBJ(kObjectTypeTabpage)); + PUT(tabpage_metadata, "id", + INTEGER_OBJ(kObjectTypeTabpage - EXT_OBJECT_TYPE_SHIFT)); PUT(tabpage_metadata, "prefix", STRING_OBJ(cstr_to_string("nvim_tabpage_"))); PUT(types, "Buffer", DICTIONARY_OBJ(buffer_metadata)); diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index ec35d56978..21acd6a394 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -20,13 +20,6 @@ static msgpack_zone zone; static msgpack_sbuffer sbuffer; -/// Value by which objects represented as EXT type are shifted -/// -/// Subtracted when packing, added when unpacking. Used to allow moving -/// buffer/window/tabpage block inside ObjectType enum. This block yet cannot be -/// split or reordered. -#define EXT_OBJECT_TYPE_SHIFT kObjectTypeBuffer - #define HANDLE_TYPE_CONVERSION_IMPL(t, lt) \ static bool msgpack_rpc_to_##lt(const msgpack_object *const obj, \ Integer *const arg) \ diff --git a/src/nvim/msgpack_rpc/helpers.h b/src/nvim/msgpack_rpc/helpers.h index 7d9f114140..0e4cd1be6d 100644 --- a/src/nvim/msgpack_rpc/helpers.h +++ b/src/nvim/msgpack_rpc/helpers.h @@ -9,6 +9,13 @@ #include "nvim/event/wstream.h" #include "nvim/api/private/defs.h" +/// Value by which objects represented as EXT type are shifted +/// +/// Subtracted when packing, added when unpacking. Used to allow moving +/// buffer/window/tabpage block inside ObjectType enum. This block yet cannot be +/// split or reordered. +#define EXT_OBJECT_TYPE_SHIFT kObjectTypeBuffer + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "msgpack_rpc/helpers.h.generated.h" #endif From 872a909150506828f72a63636e1cfd6b72f1b306 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Jan 2017 02:41:37 +0300 Subject: [PATCH 0141/1671] executor: Add :lua command Does not work currently. --- src/nvim/ex_cmds.lua | 2 +- src/nvim/ex_docmd.c | 1 + src/nvim/viml/executor/executor.c | 19 +++++++++++++++++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index 92f0669422..b34975f00e 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -1542,7 +1542,7 @@ return { command='lua', flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN), addr_type=ADDR_LINES, - func='ex_script_ni', + func='ex_lua', }, { command='luado', diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 486baaad47..aa43b71a0d 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -67,6 +67,7 @@ #include "nvim/event/rstream.h" #include "nvim/event/wstream.h" #include "nvim/shada.h" +#include "nvim/viml/executor/executor.h" static int quitmore = 0; static int ex_pressedreturn = FALSE; diff --git a/src/nvim/viml/executor/executor.c b/src/nvim/viml/executor/executor.c index acc375881c..8da218270a 100644 --- a/src/nvim/viml/executor/executor.c +++ b/src/nvim/viml/executor/executor.c @@ -7,8 +7,10 @@ #include "nvim/garray.h" #include "nvim/func_attr.h" #include "nvim/api/private/defs.h" +#include "nvim/api/private/helpers.h" #include "nvim/api/vim.h" #include "nvim/vim.h" +#include "nvim/ex_getln.h" #include "nvim/message.h" #include "nvim/viml/executor/executor.h" @@ -171,8 +173,6 @@ static lua_State *global_lstate = NULL; /// Execute lua string /// -/// Used for :lua. -/// /// @param[in] str String to execute. /// @param[out] ret_tv Location where result will be saved. /// @@ -267,3 +267,18 @@ void executor_eval_lua(const String str, typval_T *const arg, NLUA_CALL_C_FUNCTION_3(global_lstate, nlua_eval_lua_string, 0, (void *)&str, arg, ret_tv); } + +/// Run lua string +/// +/// Used for :lua. +/// +/// @param eap VimL command being run. +void ex_lua(exarg_T *const eap) + FUNC_ATTR_NONNULL_ALL +{ + char *const code = (char *)script_get(eap, eap->arg); + typval_T tv = { .v_type = VAR_UNKNOWN }; + executor_exec_lua(cstr_as_string(code), &tv); + clear_tv(&tv); + xfree(code); +} From 3531d8c8eaa6783637f510dbc5dbd58c9d435b61 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Jan 2017 03:08:56 +0300 Subject: [PATCH 0142/1671] executor: Add some const qualifiers --- src/nvim/viml/executor/executor.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/nvim/viml/executor/executor.c b/src/nvim/viml/executor/executor.c index 8da218270a..38e3cf1c6d 100644 --- a/src/nvim/viml/executor/executor.c +++ b/src/nvim/viml/executor/executor.c @@ -100,7 +100,7 @@ static void nlua_error(lua_State *const lstate, const char *const msg) /// /// Does no error handling: never call it with non-string or with some arguments /// omitted. -static int nlua_stricmp(lua_State *lstate) FUNC_ATTR_NONNULL_ALL +static int nlua_stricmp(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL { const char *s1 = luaL_checklstring(lstate, 1, NULL); const char *s2 = luaL_checklstring(lstate, 2, NULL); @@ -115,10 +115,10 @@ static int nlua_stricmp(lua_State *lstate) FUNC_ATTR_NONNULL_ALL /// Expects two values on the stack: string to evaluate, pointer to the /// location where result is saved. Always returns nothing (from the lua point /// of view). -static int nlua_exec_lua_string(lua_State *lstate) FUNC_ATTR_NONNULL_ALL +static int nlua_exec_lua_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL { - String *str = (String *)lua_touserdata(lstate, 1); - typval_T *ret_tv = (typval_T *)lua_touserdata(lstate, 2); + const String *const str = (String *)lua_touserdata(lstate, 1); + typval_T *const ret_tv = (typval_T *)lua_touserdata(lstate, 2); lua_pop(lstate, 2); if (luaL_loadbuffer(lstate, str->data, str->size, NLUA_EVAL_NAME)) { @@ -138,7 +138,7 @@ static int nlua_exec_lua_string(lua_State *lstate) FUNC_ATTR_NONNULL_ALL /// Initialize lua interpreter state /// /// Called by lua interpreter itself to initialize state. -static int nlua_state_init(lua_State *lstate) FUNC_ATTR_NONNULL_ALL +static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL { lua_pushcfunction(lstate, &nlua_stricmp); lua_setglobal(lstate, "stricmp"); @@ -177,14 +177,15 @@ static lua_State *global_lstate = NULL; /// @param[out] ret_tv Location where result will be saved. /// /// @return Result of the execution. -void executor_exec_lua(String str, typval_T *ret_tv) +void executor_exec_lua(const String str, typval_T *const ret_tv) FUNC_ATTR_NONNULL_ALL { if (global_lstate == NULL) { global_lstate = init_lua(); } - NLUA_CALL_C_FUNCTION_2(global_lstate, nlua_exec_lua_string, 0, &str, ret_tv); + NLUA_CALL_C_FUNCTION_2(global_lstate, nlua_exec_lua_string, 0, + (void *)&str, ret_tv); } /// Evaluate lua string @@ -196,12 +197,12 @@ void executor_exec_lua(String str, typval_T *ret_tv) /// 3. Pointer to location where result is saved. /// /// @param[in,out] lstate Lua interpreter state. -static int nlua_eval_lua_string(lua_State *lstate) +static int nlua_eval_lua_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL { - String *str = (String *)lua_touserdata(lstate, 1); - typval_T *arg = (typval_T *)lua_touserdata(lstate, 2); - typval_T *ret_tv = (typval_T *)lua_touserdata(lstate, 3); + const String *const str = (String *)lua_touserdata(lstate, 1); + typval_T *const arg = (typval_T *)lua_touserdata(lstate, 2); + typval_T *const ret_tv = (typval_T *)lua_touserdata(lstate, 3); lua_pop(lstate, 3); garray_T str_ga; From 3d48c35d6bfa83ba3ae621fa9ec3f512da199f59 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Jan 2017 03:36:47 +0300 Subject: [PATCH 0143/1671] ex_getln: Refactor script_get() 1. Use `char *` for strings. 2. Add `const` qualifiers. 3. Add attributes and documentation. 4. Handle skipping *inside*. 5. Handle non-heredoc argument also inside: deferring this to the caller is pointless because all callers need the same thing. Though new ex_lua caller may live without allocations in this case, allocating nevertheless produces cleaner code. 6. Note that all callers call script_get with `eap` and `eap->arg`. Thus second argument is useless in practice: it is one and the same always and can be reached through the first argument. --- src/nvim/ex_cmds2.c | 11 +++-- src/nvim/ex_docmd.c | 8 ++-- src/nvim/ex_getln.c | 68 +++++++++++++++++++------------ src/nvim/viml/executor/executor.c | 9 +++- 4 files changed, 58 insertions(+), 38 deletions(-) diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 9fc4ef2a02..092bb38dd0 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -3695,19 +3695,18 @@ char_u *get_locales(expand_T *xp, int idx) static void script_host_execute(char *name, exarg_T *eap) { - uint8_t *script = script_get(eap, eap->arg); + size_t len; + char *const script = script_get(eap, &len); - if (!eap->skip) { - list_T *args = list_alloc(); + if (script != NULL) { + list_T *const args = list_alloc(); // script - list_append_string(args, script ? script : eap->arg, -1); + list_append_allocated_string(args, script); // current range list_append_number(args, (int)eap->line1); list_append_number(args, (int)eap->line2); (void)eval_call_provider(name, "execute", args); } - - xfree(script); } static void script_host_execute_file(char *name, exarg_T *eap) diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index aa43b71a0d..74bb6177c6 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -3734,10 +3734,12 @@ void ex_ni(exarg_T *eap) /// Skips over ":perl <skip) + if (!eap->skip) { ex_ni(eap); - else - xfree(script_get(eap, eap->arg)); + } else { + size_t len; + xfree(script_get(eap, &len)); + } } /* diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 58979c0e43..f7e10f3787 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -5343,47 +5343,61 @@ static int ex_window(void) return cmdwin_result; } -/* - * Used for commands that either take a simple command string argument, or: - * cmd << endmarker - * {script} - * endmarker - * Returns a pointer to allocated memory with {script} or NULL. - */ -char_u *script_get(exarg_T *eap, char_u *cmd) +/// Get script string +/// +/// Used for commands which accept either `:command script` or +/// +/// :command << endmarker +/// script +/// endmarker +/// +/// @param eap Command being run. +/// @param[out] lenp Location where length of resulting string is saved. Will +/// be set to zero when skipping. +/// +/// @return [allocated] NULL or script. Does not show any error messages. +/// NULL is returned when skipping and on error. +char *script_get(exarg_T *const eap, size_t *const lenp) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { - char_u *theline; - char *end_pattern = NULL; - char dot[] = "."; - garray_T ga; + const char *const cmd = (const char *)eap->arg; - if (cmd[0] != '<' || cmd[1] != '<' || eap->getline == NULL) - return NULL; + if (cmd[0] != '<' || cmd[1] != '<' || eap->getline == NULL) { + *lenp = STRLEN(eap->arg); + return xmemdupz(eap->arg, *lenp); + } - ga_init(&ga, 1, 0x400); + garray_T ga = { .ga_data = NULL, .ga_len = 0 }; + if (!eap->skip) { + ga_init(&ga, 1, 0x400); + } - if (cmd[2] != NUL) - end_pattern = (char *)skipwhite(cmd + 2); - else - end_pattern = dot; - - for (;; ) { - theline = eap->getline( + const char *const end_pattern = ( + cmd[2] != NUL + ? (const char *)skipwhite((const char_u *)cmd + 2) + : "."); + for (;;) { + char *const theline = (char *)eap->getline( eap->cstack->cs_looplevel > 0 ? -1 : NUL, eap->cookie, 0); - if (theline == NULL || STRCMP(end_pattern, theline) == 0) { + if (theline == NULL || strcmp(end_pattern, theline) == 0) { xfree(theline); break; } - ga_concat(&ga, theline); - ga_append(&ga, '\n'); + if (!eap->skip) { + ga_concat(&ga, (const char_u *)theline); + ga_append(&ga, '\n'); + } xfree(theline); } - ga_append(&ga, NUL); + *lenp = (size_t)ga.ga_len; // Set length without trailing NUL. + if (!eap->skip) { + ga_append(&ga, NUL); + } - return (char_u *)ga.ga_data; + return (char *)ga.ga_data; } /// Iterate over history items diff --git a/src/nvim/viml/executor/executor.c b/src/nvim/viml/executor/executor.c index 38e3cf1c6d..fb39716f5c 100644 --- a/src/nvim/viml/executor/executor.c +++ b/src/nvim/viml/executor/executor.c @@ -277,9 +277,14 @@ void executor_eval_lua(const String str, typval_T *const arg, void ex_lua(exarg_T *const eap) FUNC_ATTR_NONNULL_ALL { - char *const code = (char *)script_get(eap, eap->arg); + size_t len; + char *const code = script_get(eap, &len); + if (eap->skip) { + xfree(code); + return; + } typval_T tv = { .v_type = VAR_UNKNOWN }; - executor_exec_lua(cstr_as_string(code), &tv); + executor_exec_lua((String) { .data = code, .size = len }, &tv); clear_tv(&tv); xfree(code); } From 7a5646d594ef4ed97f1969103a1cee32733bfa2d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Jan 2017 04:11:18 +0300 Subject: [PATCH 0144/1671] functests: Add tests for :lua --- test/functional/lua/commands_spec.lua | 53 +++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/test/functional/lua/commands_spec.lua b/test/functional/lua/commands_spec.lua index 191504bfaa..6a37d1fd1b 100644 --- a/test/functional/lua/commands_spec.lua +++ b/test/functional/lua/commands_spec.lua @@ -2,3 +2,56 @@ local helpers = require('test.functional.helpers')(after_each) local eq = helpers.eq +local clear = helpers.clear +local meths = helpers.meths +local source = helpers.source +local dedent = helpers.dedent +local exc_exec = helpers.exc_exec +local redir_exec = helpers.redir_exec +local curbufmeths = helpers.curbufmeths + +before_each(clear) + +describe(':lua command', function() + it('works', function() + eq('', redir_exec( + 'lua vim.api.nvim_buf_set_lines(1, 1, 2, false, {"TEST"})')) + eq({'', 'TEST'}, curbufmeths.get_lines(0, 100, false)) + source(dedent([[ + lua << EOF + vim.api.nvim_buf_set_lines(1, 1, 2, false, {"TSET"}) + EOF]])) + eq({'', 'TSET'}, curbufmeths.get_lines(0, 100, false)) + source(dedent([[ + lua << EOF + vim.api.nvim_buf_set_lines(1, 1, 2, false, {"SETT"})]])) + eq({'', 'SETT'}, curbufmeths.get_lines(0, 100, false)) + source(dedent([[ + lua << EOF + vim.api.nvim_buf_set_lines(1, 1, 2, false, {"ETTS"}) + vim.api.nvim_buf_set_lines(1, 2, 3, false, {"TTSE"}) + vim.api.nvim_buf_set_lines(1, 3, 4, false, {"STTE"}) + EOF]])) + eq({'', 'ETTS', 'TTSE', 'STTE'}, curbufmeths.get_lines(0, 100, false)) + end) + it('throws catchable errors', function() + eq([[Vim(lua):E5104: Error while creating lua chunk: [string ""]:1: unexpected symbol near ')']], + exc_exec('lua ()')) + eq([[Vim(lua):E5105: Error while calling lua chunk: [string ""]:1: TEST]], + exc_exec('lua error("TEST")')) + eq([[Vim(lua):E5105: Error while calling lua chunk: [string ""]:1: Invalid buffer id]], + exc_exec('lua vim.api.nvim_buf_set_lines(-10, 1, 1, false, {"TEST"})')) + eq({''}, curbufmeths.get_lines(0, 100, false)) + end) + it('accepts embedded NLs without heredoc', function() + -- Such code is usually used for `:execute 'lua' {generated_string}`: + -- heredocs do not work in this case. + meths.command([[ + lua + vim.api.nvim_buf_set_lines(1, 1, 2, false, {"ETTS"}) + vim.api.nvim_buf_set_lines(1, 2, 3, false, {"TTSE"}) + vim.api.nvim_buf_set_lines(1, 3, 4, false, {"STTE"}) + ]]) + eq({'', 'ETTS', 'TTSE', 'STTE'}, curbufmeths.get_lines(0, 100, false)) + end) +end) From b4e2860c69a4e96fa305b535ce0dbdde37632fe1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Jan 2017 05:09:54 +0300 Subject: [PATCH 0145/1671] doc,functests: Add documentation Missing: updates to various lists. --- runtime/doc/if_lua.txt | 173 ++++++++++++++++++++++++++ test/functional/lua/commands_spec.lua | 8 ++ test/functional/lua/luaeval_spec.lua | 1 + 3 files changed, 182 insertions(+) create mode 100644 runtime/doc/if_lua.txt diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt new file mode 100644 index 0000000000..0660ffc73b --- /dev/null +++ b/runtime/doc/if_lua.txt @@ -0,0 +1,173 @@ +*if_lua.txt* For Neovim + + + VIM REFERENCE MANUAL by Luis Carvalho + + +The Lua Interface to Vim *lua* *Lua* + +1. Commands |lua-commands| +2. The vim module |lua-vim| +3. The luaeval function |lua-luaeval| + +============================================================================== +1. Commands *lua-commands* + + *:lua* +:[range]lua {chunk} + Execute Lua chunk {chunk}. {not in Vi} + +Examples: +> + :lua vim.api.nvim_command('echo "Hello, Neovim!"') +< + +:[range]lua << {endmarker} +{script} +{endmarker} + Execute Lua script {script}. {not in Vi} + Note: This command doesn't work when the Lua + feature wasn't compiled in. To avoid errors, see + |script-here|. + +{endmarker} must NOT be preceded by any white space. If {endmarker} is +omitted from after the "<<", a dot '.' must be used after {script}, like +for the |:append| and |:insert| commands. +This form of the |:lua| command is mainly useful for including Lua code +in Vim scripts. + +Example: +> + function! CurrentLineInfo() + lua << EOF + local linenr = vim.api.nvim_win_get_cursor(0)[1] + local curline = vim.api.nvim_buf_get_lines( + 0, linenr, linenr + 1, false)[1] + print(string.format("Current line [%d] has %d bytes", + linenr, #curline)) + EOF + endfunction + +Note that in example variables are prefixed with local: they will disappear +when block finishes. This is not the case for globals. + +To see what version of Lua you have: > + :lua print(_VERSION) + +If you use LuaJIT you can also use this: > + :lua print(jit.version) +< + + *:luado* +:[range]luado {body} Execute Lua function "function (line, linenr) {body} + end" for each line in the [range], with the function + argument being set to the text of each line in turn, + without a trailing , and the current line number. + If the value returned by the function is a string it + becomes the text of the line in the current turn. The + default for [range] is the whole file: "1,$". + {not in Vi} + +Examples: +> + :luado return string.format("%s\t%d", line:reverse(), #line) + + :lua require"lpeg" + :lua -- balanced parenthesis grammar: + :lua bp = lpeg.P{ "(" * ((1 - lpeg.S"()") + lpeg.V(1))^0 * ")" } + :luado if bp:match(line) then return "-->\t" .. line end +< + + *:luafile* +:[range]luafile {file} + Execute Lua script in {file}. {not in Vi} + The whole argument is used as a single file name. + +Examples: +> + :luafile script.lua + :luafile % +< + +All these commands execute a Lua chunk from either the command line (:lua and +:luado) or a file (:luafile) with the given line [range]. Similarly to the Lua +interpreter, each chunk has its own scope and so only global variables are +shared between command calls. All Lua default libraries are available. In +addition, Lua "print" function has its output redirected to the Vim message +area, with arguments separated by a white space instead of a tab. + +Lua uses the "vim" module (see |lua-vim|) to issue commands to Neovim +and manage buffers (|lua-buffer|) and windows (|lua-window|). However, +procedures that alter buffer content, open new buffers, and change cursor +position are restricted when the command is executed in the |sandbox|. + + +============================================================================== +2. The vim module *lua-vim* + +Lua interfaces Vim through the "vim" module. Currently it only has `api` +submodule which is a table with all API functions. Descriptions of these +functions may be found in |api-funcs.txt|. + +============================================================================== +3. The luaeval function *lua-luaeval* *lua-eval* + *luaeval()* + +The (dual) equivalent of "vim.eval" for passing Lua values to Vim is +"luaeval". "luaeval" takes an expression string and an optional argument and +returns the result of the expression. It is semantically equivalent in Lua to: +> + local chunkheader = "local _A = select(1, ...) return " + function luaeval (expstr, arg) + local chunk = assert(loadstring(chunkheader .. expstr, "luaeval")) + return chunk(arg) -- return typval + end + +Note that "_A" receives the argument to "luaeval". Lua nils, numbers, strings, +tables and booleans are converted to their Vim respective types. An error is +thrown if conversion of any of the remaining Lua types is attempted. + +Note 2: lua tables are used as both dictionaries and lists, thus making it +impossible to determine whether empty table is meant to be empty list or empty +dictionary. To distinguish between these cases there is the following +agreement: + +0. Empty table is empty list. +1. Table with N incrementally growing integral numbers, starting from 1 and + ending with N is considered to be a list. +2. Table with string keys, none of which contains NUL byte, is considered to + be a dictionary. +3. Table with string keys, at least one of which contains NUL byte, is also + considered to be a dictionary, but this time it is converted to + a |msgpack-special-map|. +4. Table with `vim.type_idx` key may be a dictionary, a list or floating-point + value: + - `{[vim.type_idx]=vim.types.float, [vim.val_idx]=1}` is converted to + a floating-point 1.0. Note that by default integral lua numbers are + converted to |Number|s, non-integral are converted to |Float|s. This + variant allows integral |Float|s. + - `{[vim.type_idx]=vim.types.dictionary}` is converted to an empty + dictionary, `{[vim.type_idx]=vim.types.dictionary, [42]=1, a=2}` is + converted to a dictionary `{'a': 42}`: non-string keys are ignored. + Without `vim.type_idx` key tables with keys not fitting in 1., 2. or 3. + are errors. + - `{[vim.type_idx]=vim.types.list}` is converted to an empty list. As well + as `{[vim.type_idx]=vim.types.list, [42]=1}`: integral keys that do not + form a 1-step sequence from 1 to N are ignored, as well as all + non-integral keys. + +Examples: > + + :echo luaeval('math.pi') + :function Rand(x,y) " random uniform between x and y + : return luaeval('(_A.y-_A.x)*math.random()+_A.x', {'x':a:x,'y':a:y}) + : endfunction + :echo Rand(1,10) + +Note that currently second argument to `luaeval` undergoes VimL to lua +conversion, so changing resulting containers in lua do not affect values in +VimL. Return value is also always converted. When converting, +|msgpack-special-dict|s are treated specially. + +============================================================================== + vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/test/functional/lua/commands_spec.lua b/test/functional/lua/commands_spec.lua index 6a37d1fd1b..c607889edb 100644 --- a/test/functional/lua/commands_spec.lua +++ b/test/functional/lua/commands_spec.lua @@ -2,8 +2,10 @@ local helpers = require('test.functional.helpers')(after_each) local eq = helpers.eq +local NIL = helpers.NIL local clear = helpers.clear local meths = helpers.meths +local funcs = helpers.funcs local source = helpers.source local dedent = helpers.dedent local exc_exec = helpers.exc_exec @@ -54,4 +56,10 @@ describe(':lua command', function() ]]) eq({'', 'ETTS', 'TTSE', 'STTE'}, curbufmeths.get_lines(0, 100, false)) end) + it('preserves global and not preserves local variables', function() + eq('', redir_exec('lua gvar = 42')) + eq('', redir_exec('lua local lvar = 100500')) + eq(NIL, funcs.luaeval('lvar')) + eq(42, funcs.luaeval('gvar')) + end) end) diff --git a/test/functional/lua/luaeval_spec.lua b/test/functional/lua/luaeval_spec.lua index b0169c3d6b..6132d44bee 100644 --- a/test/functional/lua/luaeval_spec.lua +++ b/test/functional/lua/luaeval_spec.lua @@ -46,6 +46,7 @@ describe('luaeval()', function() funcs.luaeval('{n=1, f=1.5, s="string", l={4, 2}}')) -- Not tested: nil inside containers: behaviour will most likely change. eq(NIL, funcs.luaeval('nil')) + eq({['']=1}, funcs.luaeval('{[""]=1}')) end) end) describe('recursive lua values', function() From 9114d9be778b07f4a49edc078f1c159aa51320d8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Jan 2017 18:40:39 +0300 Subject: [PATCH 0146/1671] executor: Add :luado command --- src/nvim/ex_cmds.lua | 2 +- src/nvim/macros.h | 9 ++ src/nvim/viml/executor/executor.c | 121 ++++++++++++++++++++++++++- test/functional/lua/luaeval_spec.lua | 2 +- 4 files changed, 129 insertions(+), 5 deletions(-) diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index b34975f00e..25e74ebf9b 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -1548,7 +1548,7 @@ return { command='luado', flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN), addr_type=ADDR_LINES, - func='ex_ni', + func='ex_luado', }, { command='luafile', diff --git a/src/nvim/macros.h b/src/nvim/macros.h index 650bf76156..5042663041 100644 --- a/src/nvim/macros.h +++ b/src/nvim/macros.h @@ -19,6 +19,15 @@ # define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) #endif +/// String with length +/// +/// For use in functions which accept (char *s, size_t len) pair in arguments. +/// +/// @param[in] s Static string. +/// +/// @return `s, sizeof(s) - 1` +#define S_LEN(s) (s), (sizeof(s) - 1) + /* * Position comparisons */ diff --git a/src/nvim/viml/executor/executor.c b/src/nvim/viml/executor/executor.c index fb39716f5c..80f2651afc 100644 --- a/src/nvim/viml/executor/executor.c +++ b/src/nvim/viml/executor/executor.c @@ -12,6 +12,13 @@ #include "nvim/vim.h" #include "nvim/ex_getln.h" #include "nvim/message.h" +#include "nvim/memline.h" +#include "nvim/buffer_defs.h" +#include "nvim/macros.h" +#include "nvim/screen.h" +#include "nvim/cursor.h" +#include "nvim/undo.h" +#include "nvim/ascii.h" #include "nvim/viml/executor/executor.h" #include "nvim/viml/executor/converter.h" @@ -38,6 +45,17 @@ typedef struct { lua_pushcfunction(lstate, &function); \ lua_call(lstate, 0, numret); \ } while (0) +/// Call C function which expects one argument +/// +/// @param function Called function +/// @param numret Number of returned arguments +/// @param a… Supplied argument (should be a void* pointer) +#define NLUA_CALL_C_FUNCTION_1(lstate, function, numret, a1) \ + do { \ + lua_pushcfunction(lstate, &function); \ + lua_pushlightuserdata(lstate, a1); \ + lua_call(lstate, 1, numret); \ + } while (0) /// Call C function which expects two arguments /// /// @param function Called function @@ -117,7 +135,7 @@ static int nlua_stricmp(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL /// of view). static int nlua_exec_lua_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL { - const String *const str = (String *)lua_touserdata(lstate, 1); + const String *const str = (const String *)lua_touserdata(lstate, 1); typval_T *const ret_tv = (typval_T *)lua_touserdata(lstate, 2); lua_pop(lstate, 2); @@ -135,6 +153,79 @@ static int nlua_exec_lua_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL return 0; } +/// Evaluate lua string for each line in range +/// +/// Expects two values on the stack: string to evaluate and pointer to integer +/// array with line range. Always returns nothing (from the lua point of view). +static int nlua_exec_luado_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL +{ + const String *const str = (const String *)lua_touserdata(lstate, 1); + const linenr_T *const range = (const linenr_T *)lua_touserdata(lstate, 1); + lua_pop(lstate, 1); + +#define DOSTART "return function(line, linenr) " +#define DOEND " end" + const size_t lcmd_len = str->size + (sizeof(DOSTART) - 1) + (sizeof(DOEND) - 1); + char *lcmd; + if (lcmd_len < IOSIZE) { + lcmd = (char *)IObuff; + } else { + lcmd = xmalloc(lcmd_len); + } + memcpy(lcmd, S_LEN(DOSTART)); + memcpy(lcmd + sizeof(DOSTART) - 1, str->data, str->size); + memcpy(lcmd + sizeof(DOSTART) - 1 + str->size, S_LEN(DOEND)); +#undef DOSTART +#undef DOEND + + if (luaL_loadbuffer(lstate, lcmd, lcmd_len, NLUA_EVAL_NAME)) { + nlua_error(lstate, _("E5109: Error while creating lua chunk: %.*s")); + return 0; + } + if (lua_pcall(lstate, 0, 1, 0)) { + nlua_error(lstate, _("E5110: Error while creating lua function: %.*s")); + return 0; + } + for (linenr_T l = range[0]; l < range[1]; l++) { + if (l > curbuf->b_ml.ml_line_count) { + break; + } + lua_pushvalue(lstate, -1); + lua_pushstring(lstate, (const char *)ml_get_buf(curbuf, l, false)); + lua_pushnumber(lstate, (lua_Number)l); + if (lua_pcall(lstate, 2, 1, 0)) { + nlua_error(lstate, _("E5111: Error while calling lua function: %.*s")); + break; + } + if (lua_isstring(lstate, -1)) { + if (sandbox) { + EMSG(_("E5112: Not allowed in sandbox")); + lua_pop(lstate, 1); + break; + } + size_t new_line_len; + const char *new_line = lua_tolstring(lstate, -1, &new_line_len); + char *const new_line_transformed = ( + new_line_len < IOSIZE + ? memcpy(IObuff, new_line, new_line_len) + : xmemdupz(new_line, new_line_len)); + new_line_transformed[new_line_len] = NUL; + for (size_t i = 0; i < new_line_len; i++) { + if (new_line_transformed[new_line_len] == NUL) { + new_line_transformed[new_line_len] = '\n'; + } + } + ml_replace(l, (char_u *)new_line_transformed, true); + changed_bytes(l, 0); + } + lua_pop(lstate, 1); + } + lua_pop(lstate, 1); + check_cursor(); + update_screen(NOT_VALID); + return 0; +} + /// Initialize lua interpreter state /// /// Called by lua interpreter itself to initialize state. @@ -200,7 +291,7 @@ void executor_exec_lua(const String str, typval_T *const ret_tv) static int nlua_eval_lua_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL { - const String *const str = (String *)lua_touserdata(lstate, 1); + const String *const str = (const String *)lua_touserdata(lstate, 1); typval_T *const arg = (typval_T *)lua_touserdata(lstate, 2); typval_T *const ret_tv = (typval_T *)lua_touserdata(lstate, 3); lua_pop(lstate, 3); @@ -215,7 +306,7 @@ static int nlua_eval_lua_string(lua_State *const lstate) } else { lcmd = xmalloc(lcmd_len); } - memcpy(lcmd, EVALHEADER, sizeof(EVALHEADER) - 1); + memcpy(lcmd, S_LEN(EVALHEADER)); memcpy(lcmd + sizeof(EVALHEADER) - 1, str->data, str->size); lcmd[lcmd_len - 1] = ')'; #undef EVALHEADER @@ -288,3 +379,27 @@ void ex_lua(exarg_T *const eap) clear_tv(&tv); xfree(code); } + +/// Run lua string for each line in range +/// +/// Used for :luado. +/// +/// @param eap VimL command being run. +void ex_luado(exarg_T *const eap) + FUNC_ATTR_NONNULL_ALL +{ + if (global_lstate == NULL) { + global_lstate = init_lua(); + } + if (u_save(eap->line1 - 1, eap->line2 + 1) == FAIL) { + EMSG(_("cannot save undo information")); + return; + } + const String cmd = { + .size = STRLEN(eap->arg), + .data = (char *)eap->arg, + }; + const linenr_T range[] = { eap->line1, eap->line2 }; + NLUA_CALL_C_FUNCTION_2(global_lstate, nlua_exec_luado_string, 0, + (void *)&cmd, (void *)range); +} diff --git a/test/functional/lua/luaeval_spec.lua b/test/functional/lua/luaeval_spec.lua index 6132d44bee..5d0180b212 100644 --- a/test/functional/lua/luaeval_spec.lua +++ b/test/functional/lua/luaeval_spec.lua @@ -36,7 +36,7 @@ describe('luaeval()', function() it('is successfully received', function() local t = {t=true, f=false, --[[n=NIL,]] d={l={'string', 42, 0.42}}} eq(t, funcs.luaeval("_A", t)) - -- Not tested: nil, funcrefs, returned object identity: behaviour will + -- Not tested: nil, funcrefs, returned object identity: behaviour will -- most likely change. end) end) From e1bbaca7acf7fc498da49081d069b00aa05506df Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Jan 2017 19:26:22 +0300 Subject: [PATCH 0147/1671] executor,functests: Add tests for :luado, also some fixes Fixes: 1. Allocate space for the NUL byte. 2. Do not exclude last line from range. 3. Remove code for sandbox: it is handled earlier. 4. Fix index in new_line_transformed when converting NULs to NLs. 5. Always allocate new_line_transformed, but save allocated value. --- src/nvim/viml/executor/executor.c | 29 +++++++---------- test/functional/lua/commands_spec.lua | 45 +++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 18 deletions(-) diff --git a/src/nvim/viml/executor/executor.c b/src/nvim/viml/executor/executor.c index 80f2651afc..c426806b6f 100644 --- a/src/nvim/viml/executor/executor.c +++ b/src/nvim/viml/executor/executor.c @@ -160,17 +160,19 @@ static int nlua_exec_lua_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL static int nlua_exec_luado_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL { const String *const str = (const String *)lua_touserdata(lstate, 1); - const linenr_T *const range = (const linenr_T *)lua_touserdata(lstate, 1); + const linenr_T *const range = (const linenr_T *)lua_touserdata(lstate, 2); lua_pop(lstate, 1); #define DOSTART "return function(line, linenr) " #define DOEND " end" - const size_t lcmd_len = str->size + (sizeof(DOSTART) - 1) + (sizeof(DOEND) - 1); + const size_t lcmd_len = (str->size + + (sizeof(DOSTART) - 1) + + (sizeof(DOEND) - 1)); char *lcmd; if (lcmd_len < IOSIZE) { lcmd = (char *)IObuff; } else { - lcmd = xmalloc(lcmd_len); + lcmd = xmalloc(lcmd_len + 1); } memcpy(lcmd, S_LEN(DOSTART)); memcpy(lcmd + sizeof(DOSTART) - 1, str->data, str->size); @@ -186,7 +188,7 @@ static int nlua_exec_luado_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL nlua_error(lstate, _("E5110: Error while creating lua function: %.*s")); return 0; } - for (linenr_T l = range[0]; l < range[1]; l++) { + for (linenr_T l = range[0]; l <= range[1]; l++) { if (l > curbuf->b_ml.ml_line_count) { break; } @@ -198,24 +200,15 @@ static int nlua_exec_luado_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL break; } if (lua_isstring(lstate, -1)) { - if (sandbox) { - EMSG(_("E5112: Not allowed in sandbox")); - lua_pop(lstate, 1); - break; - } size_t new_line_len; - const char *new_line = lua_tolstring(lstate, -1, &new_line_len); - char *const new_line_transformed = ( - new_line_len < IOSIZE - ? memcpy(IObuff, new_line, new_line_len) - : xmemdupz(new_line, new_line_len)); - new_line_transformed[new_line_len] = NUL; + const char *const new_line = lua_tolstring(lstate, -1, &new_line_len); + char *const new_line_transformed = xmemdupz(new_line, new_line_len); for (size_t i = 0; i < new_line_len; i++) { - if (new_line_transformed[new_line_len] == NUL) { - new_line_transformed[new_line_len] = '\n'; + if (new_line_transformed[i] == NUL) { + new_line_transformed[i] = '\n'; } } - ml_replace(l, (char_u *)new_line_transformed, true); + ml_replace(l, (char_u *)new_line_transformed, false); changed_bytes(l, 0); } lua_pop(lstate, 1); diff --git a/test/functional/lua/commands_spec.lua b/test/functional/lua/commands_spec.lua index c607889edb..f1d430b34c 100644 --- a/test/functional/lua/commands_spec.lua +++ b/test/functional/lua/commands_spec.lua @@ -63,3 +63,48 @@ describe(':lua command', function() eq(42, funcs.luaeval('gvar')) end) end) + +describe(':luado command', function() + it('works', function() + curbufmeths.set_lines(0, 1, false, {"ABC", "def", "gHi"}) + eq('', redir_exec('luado lines = (lines or {}) lines[#lines + 1] = {linenr, line}')) + eq({'ABC', 'def', 'gHi'}, curbufmeths.get_lines(0, -1, false)) + eq({{1, 'ABC'}, {2, 'def'}, {3, 'gHi'}}, funcs.luaeval('lines')) + + -- Automatic transformation of numbers + eq('', redir_exec('luado return linenr')) + eq({'1', '2', '3'}, curbufmeths.get_lines(0, -1, false)) + + eq('', redir_exec('luado return ("<%02x>"):format(line:byte())')) + eq({'<31>', '<32>', '<33>'}, curbufmeths.get_lines(0, -1, false)) + end) + it('stops processing lines when suddenly out of lines', function() + curbufmeths.set_lines(0, 1, false, {"ABC", "def", "gHi"}) + eq('', redir_exec('2,$luado runs = ((runs or 0) + 1) vim.api.nvim_command("%d")')) + eq({''}, curbufmeths.get_lines(0, -1, false)) + eq(1, funcs.luaeval('runs')) + end) + it('works correctly when changing lines out of range', function() + curbufmeths.set_lines(0, 1, false, {"ABC", "def", "gHi"}) + eq('\nE322: line number out of range: 1 past the end\nE320: Cannot find line 2', + redir_exec('2,$luado vim.api.nvim_command("%d") return linenr')) + eq({''}, curbufmeths.get_lines(0, -1, false)) + end) + it('fails on errors', function() + eq([[Vim(luado):E5109: Error while creating lua chunk: [string ""]:1: unexpected symbol near ')']], + exc_exec('luado ()')) + eq([[Vim(luado):E5111: Error while calling lua function: [string ""]:1: attempt to perform arithmetic on global 'liness' (a nil value)]], + exc_exec('luado return liness + 1')) + end) + it('fails in sandbox when needed', function() + curbufmeths.set_lines(0, 1, false, {"ABC", "def", "gHi"}) + eq('\nE48: Not allowed in sandbox: sandbox luado runs = (runs or 0) + 1', + redir_exec('sandbox luado runs = (runs or 0) + 1')) + eq(NIL, funcs.luaeval('runs')) + end) + it('works with long strings', function() + local s = ('x'):rep(100500) + eq('', redir_exec(('luado return "%s"'):format(s))) + eq({s}, curbufmeths.get_lines(0, -1, false)) + end) +end) From 295e7607c472b49e8c0762977e38c4527b746b6f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Jan 2017 19:32:01 +0300 Subject: [PATCH 0148/1671] executor: Fix some memory leaks --- src/nvim/viml/executor/executor.c | 6 ++++++ test/functional/lua/commands_spec.lua | 13 +++++++++++++ test/functional/lua/luaeval_spec.lua | 3 ++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/nvim/viml/executor/executor.c b/src/nvim/viml/executor/executor.c index c426806b6f..df63667102 100644 --- a/src/nvim/viml/executor/executor.c +++ b/src/nvim/viml/executor/executor.c @@ -182,8 +182,14 @@ static int nlua_exec_luado_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL if (luaL_loadbuffer(lstate, lcmd, lcmd_len, NLUA_EVAL_NAME)) { nlua_error(lstate, _("E5109: Error while creating lua chunk: %.*s")); + if (lcmd_len >= IOSIZE) { + xfree(lcmd); + } return 0; } + if (lcmd_len >= IOSIZE) { + xfree(lcmd); + } if (lua_pcall(lstate, 0, 1, 0)) { nlua_error(lstate, _("E5110: Error while creating lua function: %.*s")); return 0; diff --git a/test/functional/lua/commands_spec.lua b/test/functional/lua/commands_spec.lua index f1d430b34c..41a5a8d9e0 100644 --- a/test/functional/lua/commands_spec.lua +++ b/test/functional/lua/commands_spec.lua @@ -62,6 +62,15 @@ describe(':lua command', function() eq(NIL, funcs.luaeval('lvar')) eq(42, funcs.luaeval('gvar')) end) + it('works with long strings', function() + local s = ('x'):rep(100500) + + eq('\nE5104: Error while creating lua chunk: [string ""]:1: unfinished string near \'\'', redir_exec(('lua vim.api.nvim_buf_set_lines(1, 1, 2, false, {"%s})'):format(s))) + eq({''}, curbufmeths.get_lines(0, -1, false)) + + eq('', redir_exec(('lua vim.api.nvim_buf_set_lines(1, 1, 2, false, {"%s"})'):format(s))) + eq({'', s}, curbufmeths.get_lines(0, -1, false)) + end) end) describe(':luado command', function() @@ -104,6 +113,10 @@ describe(':luado command', function() end) it('works with long strings', function() local s = ('x'):rep(100500) + + eq('\nE5109: Error while creating lua chunk: [string ""]:1: unfinished string near \'\'', redir_exec(('luado return "%s'):format(s))) + eq({''}, curbufmeths.get_lines(0, -1, false)) + eq('', redir_exec(('luado return "%s"'):format(s))) eq({s}, curbufmeths.get_lines(0, -1, false)) end) diff --git a/test/functional/lua/luaeval_spec.lua b/test/functional/lua/luaeval_spec.lua index 5d0180b212..7973cabae4 100644 --- a/test/functional/lua/luaeval_spec.lua +++ b/test/functional/lua/luaeval_spec.lua @@ -243,10 +243,11 @@ describe('luaeval()', function() exc_exec([[call luaeval("error('ERROR')")]])) end) - it('does not leak memory when called with too long line with syntax error', + it('does not leak memory when called with too long line', function() local s = ('x'):rep(65536) eq('Vim(call):E5107: Error while creating lua chunk for luaeval(): [string ""]:1: unexpected symbol near \')\'', exc_exec([[call luaeval("(']] .. s ..[[' + )")]])) + eq(s, funcs.luaeval('"' .. s .. '"')) end) end) From dcb992ab3759b604c1824913002714a018be0532 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Jan 2017 19:51:55 +0300 Subject: [PATCH 0149/1671] executor: Add :luafile command --- src/nvim/ex_cmds.lua | 2 +- src/nvim/viml/executor/executor.c | 37 ++++++++++++++++++++++++++- test/functional/lua/commands_spec.lua | 28 ++++++++++++++++++++ 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index 25e74ebf9b..ad2fb642d4 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -1554,7 +1554,7 @@ return { command='luafile', flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN), addr_type=ADDR_LINES, - func='ex_ni', + func='ex_luafile', }, { command='lvimgrep', diff --git a/src/nvim/viml/executor/executor.c b/src/nvim/viml/executor/executor.c index df63667102..a01bf9966d 100644 --- a/src/nvim/viml/executor/executor.c +++ b/src/nvim/viml/executor/executor.c @@ -161,7 +161,7 @@ static int nlua_exec_luado_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL { const String *const str = (const String *)lua_touserdata(lstate, 1); const linenr_T *const range = (const linenr_T *)lua_touserdata(lstate, 2); - lua_pop(lstate, 1); + lua_pop(lstate, 2); #define DOSTART "return function(line, linenr) " #define DOEND " end" @@ -225,6 +225,26 @@ static int nlua_exec_luado_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL return 0; } +/// Evaluate lua file +/// +/// Expects one value on the stack: file to evaluate. Always returns nothing +/// (from the lua point of view). +static int nlua_exec_lua_file(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL +{ + const char *const filename = (const char *)lua_touserdata(lstate, 1); + lua_pop(lstate, 1); + + if (luaL_loadfile(lstate, filename)) { + nlua_error(lstate, _("E5112: Error while creating lua chunk: %.*s")); + return 0; + } + if (lua_pcall(lstate, 0, 0, 0)) { + nlua_error(lstate, _("E5113: Error while calling lua chunk: %.*s")); + return 0; + } + return 0; +} + /// Initialize lua interpreter state /// /// Called by lua interpreter itself to initialize state. @@ -402,3 +422,18 @@ void ex_luado(exarg_T *const eap) NLUA_CALL_C_FUNCTION_2(global_lstate, nlua_exec_luado_string, 0, (void *)&cmd, (void *)range); } + +/// Run lua file +/// +/// Used for :luafile. +/// +/// @param eap VimL command being run. +void ex_luafile(exarg_T *const eap) + FUNC_ATTR_NONNULL_ALL +{ + if (global_lstate == NULL) { + global_lstate = init_lua(); + } + NLUA_CALL_C_FUNCTION_1(global_lstate, nlua_exec_lua_file, 0, + (void *)eap->arg); +} diff --git a/test/functional/lua/commands_spec.lua b/test/functional/lua/commands_spec.lua index 41a5a8d9e0..80fac07f8c 100644 --- a/test/functional/lua/commands_spec.lua +++ b/test/functional/lua/commands_spec.lua @@ -9,6 +9,7 @@ local funcs = helpers.funcs local source = helpers.source local dedent = helpers.dedent local exc_exec = helpers.exc_exec +local write_file = helpers.write_file local redir_exec = helpers.redir_exec local curbufmeths = helpers.curbufmeths @@ -121,3 +122,30 @@ describe(':luado command', function() eq({s}, curbufmeths.get_lines(0, -1, false)) end) end) + +describe(':luafile', function() + local fname = 'Xtest-functional-lua-commands-luafile' + + after_each(function() + os.remove(fname) + end) + + it('works', function() + write_file(fname, [[ + vim.api.nvim_buf_set_lines(1, 1, 2, false, {"ETTS"}) + vim.api.nvim_buf_set_lines(1, 2, 3, false, {"TTSE"}) + vim.api.nvim_buf_set_lines(1, 3, 4, false, {"STTE"}) + ]]) + eq('', redir_exec('luafile ' .. fname)) + eq({'', 'ETTS', 'TTSE', 'STTE'}, curbufmeths.get_lines(0, 100, false)) + end) + + it('correctly errors out', function() + write_file(fname, '()') + eq(("Vim(luafile):E5112: Error while creating lua chunk: %s:1: unexpected symbol near ')'"):format(fname), + exc_exec('luafile ' .. fname)) + write_file(fname, 'vimm.api.nvim_buf_set_lines(1, 1, 2, false, {"ETTS"})') + eq(("Vim(luafile):E5113: Error while calling lua chunk: %s:1: attempt to index global 'vimm' (a nil value)"):format(fname), + exc_exec('luafile ' .. fname)) + end) +end) From 09fe6185b7b2c1f1379ff4add9f3f4da79a7b043 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Jan 2017 20:02:46 +0300 Subject: [PATCH 0150/1671] doc: Enhance documentation --- runtime/doc/eval.txt | 4 ++++ runtime/doc/if_lua.txt | 15 ++++++++------- runtime/doc/index.txt | 3 +++ runtime/doc/usr_41.txt | 1 + 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 341e65d381..0b5a29080e 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2140,6 +2140,7 @@ lispindent({lnum}) Number Lisp indent for line {lnum} localtime() Number current time log({expr}) Float natural logarithm (base e) of {expr} log10({expr}) Float logarithm of Float {expr} to base 10 +luaeval({expr}[, {expr}]) any evaluate Lua expression map({expr}, {string}) List/Dict change each item in {expr} to {expr} maparg({name}[, {mode} [, {abbr} [, {dict}]]]) String or Dict @@ -5095,6 +5096,9 @@ log10({expr}) *log10()* :echo log10(0.01) < -2.0 +luaeval({expr}[, {expr}]) + Evaluate Lua expression {expr} and return its result converted + to Vim data structures. See |lua-luaeval| for more details. map({expr1}, {expr2}) *map()* {expr1} must be a |List| or a |Dictionary|. diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt index 0660ffc73b..932564170d 100644 --- a/runtime/doc/if_lua.txt +++ b/runtime/doc/if_lua.txt @@ -114,8 +114,9 @@ functions may be found in |api-funcs.txt|. *luaeval()* The (dual) equivalent of "vim.eval" for passing Lua values to Vim is -"luaeval". "luaeval" takes an expression string and an optional argument and -returns the result of the expression. It is semantically equivalent in Lua to: +"luaeval". "luaeval" takes an expression string and an optional argument used +for _A inside expression and returns the result of the expression. It is +semantically equivalent in Lua to: > local chunkheader = "local _A = select(1, ...) return " function luaeval (expstr, arg) @@ -129,8 +130,8 @@ thrown if conversion of any of the remaining Lua types is attempted. Note 2: lua tables are used as both dictionaries and lists, thus making it impossible to determine whether empty table is meant to be empty list or empty -dictionary. To distinguish between these cases there is the following -agreement: +dictionary. Additionally lua does not have integer numbers. To distinguish +between these cases there is the following agreement: 0. Empty table is empty list. 1. Table with N incrementally growing integral numbers, starting from 1 and @@ -165,9 +166,9 @@ Examples: > :echo Rand(1,10) Note that currently second argument to `luaeval` undergoes VimL to lua -conversion, so changing resulting containers in lua do not affect values in -VimL. Return value is also always converted. When converting, -|msgpack-special-dict|s are treated specially. +conversion, so changing containers in lua do not affect values in VimL. Return +value is also always converted. When converting, |msgpack-special-dict|s are +treated specially. ============================================================================== vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index bab15bcbb6..a1df0a265d 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1339,6 +1339,9 @@ tag command action ~ |:ltag| :lt[ag] jump to tag and add matching tags to the location list |:lunmap| :lu[nmap] like ":unmap!" but includes Lang-Arg mode +|:lua| :lua execute Lua command +|:luado| :luad[o] execute Lua command for each line +|:luafile| :luaf[ile] execute Lua script file |:lvimgrep| :lv[imgrep] search for pattern in files |:lvimgrepadd| :lvimgrepa[dd] like :vimgrep, but append to current list |:lwindow| :lw[indow] open or close location window diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index 77c238ae97..10d2a8cddd 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -950,6 +950,7 @@ Various: *various-functions* taglist() get list of matching tags tagfiles() get a list of tags files + luaeval() evaluate Lua expression py3eval() evaluate Python expression (|+python3|) pyeval() evaluate Python expression (|+python|) From 1801d44f53e4340f5483902e6f0a2aa0ab4f551a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Jan 2017 20:20:41 +0300 Subject: [PATCH 0151/1671] executor: Do not use S_LEN for memcpy Sometimes it is implemented as a macro and `S_LEN` is treated as a single argument in this case. --- src/nvim/viml/executor/executor.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nvim/viml/executor/executor.c b/src/nvim/viml/executor/executor.c index a01bf9966d..ad92b52f3e 100644 --- a/src/nvim/viml/executor/executor.c +++ b/src/nvim/viml/executor/executor.c @@ -174,9 +174,9 @@ static int nlua_exec_luado_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL } else { lcmd = xmalloc(lcmd_len + 1); } - memcpy(lcmd, S_LEN(DOSTART)); + memcpy(lcmd, DOSTART, sizeof(DOSTART) - 1); memcpy(lcmd + sizeof(DOSTART) - 1, str->data, str->size); - memcpy(lcmd + sizeof(DOSTART) - 1 + str->size, S_LEN(DOEND)); + memcpy(lcmd + sizeof(DOSTART) - 1 + str->size, DOEND, sizeof(DOEND) - 1); #undef DOSTART #undef DOEND @@ -325,7 +325,7 @@ static int nlua_eval_lua_string(lua_State *const lstate) } else { lcmd = xmalloc(lcmd_len); } - memcpy(lcmd, S_LEN(EVALHEADER)); + memcpy(lcmd, EVALHEADER, sizeof(EVALHEADER) - 1); memcpy(lcmd + sizeof(EVALHEADER) - 1, str->data, str->size); lcmd[lcmd_len - 1] = ')'; #undef EVALHEADER From f2ad6201d94fec1e0c98550e55f3b069fa24a68b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Jan 2017 21:03:36 +0300 Subject: [PATCH 0152/1671] api: Use a form of `1 << 63` for INTERNAL_CALL_MASK --- src/nvim/api/private/defs.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index 432ab347bc..8bd1dd5085 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -36,9 +36,7 @@ typedef enum { #define NO_RESPONSE UINT64_MAX /// Mask for all internal calls -#define INTERNAL_CALL_MASK (UINT64_MAX ^ (UINT64_MAX >> 1)) -// (1 << 63) in all forms produces “warning: shift count >= width of type -// [-Wshift-count-overflow]” +#define INTERNAL_CALL_MASK (((uint64_t)1) << (sizeof(uint64_t) * 8 - 1)) /// Internal call from VimL code #define VIML_INTERNAL_CALL INTERNAL_CALL_MASK From 90e2a043e3394f83eb5d5e32e13cda00d752f212 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Jan 2017 21:58:55 +0300 Subject: [PATCH 0153/1671] executor: Add print() function --- src/nvim/viml/executor/executor.c | 74 +++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/nvim/viml/executor/executor.c b/src/nvim/viml/executor/executor.c index ad92b52f3e..48149c4420 100644 --- a/src/nvim/viml/executor/executor.c +++ b/src/nvim/viml/executor/executor.c @@ -252,6 +252,8 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL { lua_pushcfunction(lstate, &nlua_stricmp); lua_setglobal(lstate, "stricmp"); + lua_pushcfunction(lstate, &nlua_print); + lua_setglobal(lstate, "print"); if (luaL_dostring(lstate, (char *)&vim_module[0])) { nlua_error(lstate, _("E5106: Error while creating vim module: %.*s")); return 1; @@ -358,6 +360,78 @@ static int nlua_eval_lua_string(lua_State *const lstate) return 0; } +/// Print as a Vim message +/// +/// @param lstate Lua interpreter state. +static int nlua_print(lua_State *const lstate) + FUNC_ATTR_NONNULL_ALL +{ +#define PRINT_ERROR(msg) \ + do { \ + errmsg = msg; \ + errmsg_len = sizeof(msg) - 1; \ + goto nlua_print_error; \ + } while (0) + const int nargs = lua_gettop(lstate); + lua_getglobal(lstate, "tostring"); + const char *errmsg = NULL; + size_t errmsg_len = 0; + garray_T msg_ga; + ga_init(&msg_ga, 1, 80); + int curargidx = 1; + for (; curargidx <= nargs; curargidx++) { + lua_pushvalue(lstate, -1); // tostring + lua_pushvalue(lstate, curargidx); // arg + if (lua_pcall(lstate, 1, 1, 0)) { + errmsg = lua_tolstring(lstate, -1, &errmsg_len); + if (!errmsg) { + PRINT_ERROR(""); + } + goto nlua_print_error; + } + size_t len; + const char *const s = lua_tolstring(lstate, -1, &len); + if (s == NULL) { + PRINT_ERROR( + ""); + } + ga_concat_len(&msg_ga, s, len); + if (curargidx < nargs) { + ga_append(&msg_ga, ' '); + } + lua_pop(lstate, 1); + } +#undef PRINT_ERROR + lua_pop(lstate, nargs + 1); + ga_append(&msg_ga, NUL); + { + const size_t len = (size_t)msg_ga.ga_len - 1; + char *const str = (char *)msg_ga.ga_data; + + for (size_t i = 0; i < len - 1;) { + const size_t start = i; + while (str[i] != NL && i < len - 1) { + if (str[i] == NUL) { + str[i] = NL; + } + i++; + } + if (str[i] == NL) { + str[i] = NUL; + } + msg((char_u *)str + start); + } + } + ga_clear(&msg_ga); + return 0; +nlua_print_error: + emsgf(_("E5114: Error while converting print argument #%i: %.*s"), + curargidx, errmsg_len, errmsg); + ga_clear(&msg_ga); + lua_pop(lstate, lua_gettop(lstate)); + return 0; +} + /// Evaluate lua string /// /// Used for luaeval(). From 279e3410cf503a89d58d63b12032205c877707bc Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Jan 2017 22:07:24 +0300 Subject: [PATCH 0154/1671] doc: Update vim_diff.txt --- runtime/doc/vim_diff.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 11dde27868..6d4bd984a9 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -245,6 +245,17 @@ coerced to strings. See |id()| for more details, currently it uses |c_CTRL-R| pasting a non-special register into |cmdline| omits the last . +Lua interface (|if_lua.txt|): + +- `:lua print("a\0b")` will print `a^@b`, like with `:echomsg "a\nb"` . In Vim + that prints `a` and `b` on separate lines, exactly like + `:lua print("a\nb")` . +- `:lua error('TEST')` will print “TEST” as the error in Vim and “E5105: Error + while calling lua chunk: [string ""]:1: TEST” in + Neovim. +- Lua has direct access to Neovim api via `vim.api`. +- Currently most of features are missing. + ============================================================================== 5. Missing legacy features *nvim-features-missing* *if_lua* *if_perl* *if_mzscheme* *if_tcl* From ebad04622056b0cb88e2b25211746b57fb4ef3c2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Jan 2017 22:11:31 +0300 Subject: [PATCH 0155/1671] doc: Update vim_diff data regarding ShaDa --- runtime/doc/vim_diff.txt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 6d4bd984a9..31bf098ba5 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -219,22 +219,15 @@ Additional differences: - |shada-c| has no effect. - |shada-s| now limits size of every item and not just registers. -- When reading ShaDa files items are merged according to the timestamp. - |shada-merging| - 'viminfo' option got renamed to 'shada'. Old option is kept as an alias for compatibility reasons. - |:wviminfo| was renamed to |:wshada|, |:rviminfo| to |:rshada|. Old commands are still kept. -- When writing (|:wshada| without bang or at exit) it merges much more data, - and does this according to the timestamp. Vim merges only marks. - |shada-merging| - ShaDa file format was designed with forward and backward compatibility in mind. |shada-compatibility| - Some errors make ShaDa code keep temporary file in-place for user to decide what to do with it. Vim deletes temporary file in these cases. |shada-error-handling| -- Vim keeps no timestamps at all, neither in viminfo file nor in the instance - itself. - ShaDa file keeps search direction (|v:searchforward|), viminfo does not. |printf()| returns something meaningful when used with `%p` argument: in Vim From 9fd2bf67aa1db66e3465753d5aaaec342f4ce193 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 29 Jan 2017 23:22:50 +0300 Subject: [PATCH 0156/1671] executor,functests: Add print() tests, some fixes --- src/nvim/viml/executor/executor.c | 32 ++++++++----- test/functional/lua/commands_spec.lua | 13 ++++++ test/functional/lua/luaeval_spec.lua | 2 + test/functional/lua/overrides_spec.lua | 65 ++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 11 deletions(-) create mode 100644 test/functional/lua/overrides_spec.lua diff --git a/src/nvim/viml/executor/executor.c b/src/nvim/viml/executor/executor.c index 48149c4420..4eb63f38ad 100644 --- a/src/nvim/viml/executor/executor.c +++ b/src/nvim/viml/executor/executor.c @@ -384,9 +384,6 @@ static int nlua_print(lua_State *const lstate) lua_pushvalue(lstate, curargidx); // arg if (lua_pcall(lstate, 1, 1, 0)) { errmsg = lua_tolstring(lstate, -1, &errmsg_len); - if (!errmsg) { - PRINT_ERROR(""); - } goto nlua_print_error; } size_t len; @@ -408,19 +405,32 @@ static int nlua_print(lua_State *const lstate) const size_t len = (size_t)msg_ga.ga_len - 1; char *const str = (char *)msg_ga.ga_data; - for (size_t i = 0; i < len - 1;) { + for (size_t i = 0; i < len;) { const size_t start = i; - while (str[i] != NL && i < len - 1) { - if (str[i] == NUL) { - str[i] = NL; + while (i < len) { + switch (str[i]) { + case NUL: { + str[i] = NL; + i++; + continue; + } + case NL: { + str[i] = NUL; + i++; + break; + } + default: { + i++; + continue; + } } - i++; - } - if (str[i] == NL) { - str[i] = NUL; + break; } msg((char_u *)str + start); } + if (str[len - 1] == NUL) { // Last was newline + msg((char_u *)""); + } } ga_clear(&msg_ga); return 0; diff --git a/test/functional/lua/commands_spec.lua b/test/functional/lua/commands_spec.lua index 80fac07f8c..017ee55729 100644 --- a/test/functional/lua/commands_spec.lua +++ b/test/functional/lua/commands_spec.lua @@ -46,6 +46,10 @@ describe(':lua command', function() exc_exec('lua vim.api.nvim_buf_set_lines(-10, 1, 1, false, {"TEST"})')) eq({''}, curbufmeths.get_lines(0, 100, false)) end) + it('works with NULL errors', function() + eq([=[Vim(lua):E5105: Error while calling lua chunk: [NULL]]=], + exc_exec('lua error(nil)')) + end) it('accepts embedded NLs without heredoc', function() -- Such code is usually used for `:execute 'lua' {generated_string}`: -- heredocs do not work in this case. @@ -106,6 +110,10 @@ describe(':luado command', function() eq([[Vim(luado):E5111: Error while calling lua function: [string ""]:1: attempt to perform arithmetic on global 'liness' (a nil value)]], exc_exec('luado return liness + 1')) end) + it('works with NULL errors', function() + eq([=[Vim(luado):E5111: Error while calling lua function: [NULL]]=], + exc_exec('luado error(nil)')) + end) it('fails in sandbox when needed', function() curbufmeths.set_lines(0, 1, false, {"ABC", "def", "gHi"}) eq('\nE48: Not allowed in sandbox: sandbox luado runs = (runs or 0) + 1', @@ -148,4 +156,9 @@ describe(':luafile', function() eq(("Vim(luafile):E5113: Error while calling lua chunk: %s:1: attempt to index global 'vimm' (a nil value)"):format(fname), exc_exec('luafile ' .. fname)) end) + it('works with NULL errors', function() + write_file(fname, 'error(nil)') + eq([=[Vim(luafile):E5113: Error while calling lua chunk: [NULL]]=], + exc_exec('luafile ' .. fname)) + end) end) diff --git a/test/functional/lua/luaeval_spec.lua b/test/functional/lua/luaeval_spec.lua index 7973cabae4..6da0001cea 100644 --- a/test/functional/lua/luaeval_spec.lua +++ b/test/functional/lua/luaeval_spec.lua @@ -241,6 +241,8 @@ describe('luaeval()', function() exc_exec([[call luaeval("vim.xxx_nonexistent_key_xxx()")]])) eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: ERROR', exc_exec([[call luaeval("error('ERROR')")]])) + eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [NULL]', + exc_exec([[call luaeval("error(nil)")]])) end) it('does not leak memory when called with too long line', diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua new file mode 100644 index 0000000000..92167d18dc --- /dev/null +++ b/test/functional/lua/overrides_spec.lua @@ -0,0 +1,65 @@ +-- Test for Vim overrides of lua built-ins +local helpers = require('test.functional.helpers')(after_each) + +local eq = helpers.eq +local NIL = helpers.NIL +local clear = helpers.clear +local funcs = helpers.funcs +local meths = helpers.meths +local command = helpers.command +local write_file = helpers.write_file +local redir_exec = helpers.redir_exec + +local fname = 'Xtest-functional-lua-overrides-luafile' + +before_each(clear) + +after_each(function() + os.remove(fname) +end) + +describe('print', function() + it('returns nothing', function() + eq(NIL, funcs.luaeval('print("abc")')) + eq(0, funcs.luaeval('select("#", print("abc"))')) + end) + it('allows catching printed text with :execute', function() + eq('\nabc', funcs.execute('lua print("abc")')) + eq('\nabc', funcs.execute('luado print("abc")')) + eq('\nabc', funcs.execute('call luaeval("print(\'abc\')")')) + write_file(fname, 'print("abc")') + eq('\nabc', funcs.execute('luafile ' .. fname)) + + eq('\nabc', redir_exec('lua print("abc")')) + eq('\nabc', redir_exec('luado print("abc")')) + eq('\nabc', redir_exec('call luaeval("print(\'abc\')")')) + write_file(fname, 'print("abc")') + eq('\nabc', redir_exec('luafile ' .. fname)) + end) + it('handles errors in __tostring', function() + write_file(fname, [[ + local meta_nilerr = { __tostring = function() error(nil) end } + local meta_abcerr = { __tostring = function() error("abc") end } + local meta_tblout = { __tostring = function() return {"TEST"} end } + v_nilerr = setmetatable({}, meta_nilerr) + v_abcerr = setmetatable({}, meta_abcerr) + v_tblout = setmetatable({}, meta_tblout) + ]]) + eq('', redir_exec('luafile ' .. fname)) + eq('\nE5114: Error while converting print argument #2: [NULL]', + redir_exec('lua print("foo", v_nilerr, "bar")')) + eq('\nE5114: Error while converting print argument #2: Xtest-functional-lua-overrides-luafile:2: abc', + redir_exec('lua print("foo", v_abcerr, "bar")')) + eq('\nE5114: Error while converting print argument #2: ', + redir_exec('lua print("foo", v_tblout, "bar")')) + end) + it('prints strings with NULs and NLs correctly', function() + meths.set_option('more', true) + eq('\nabc ^@ def\nghi^@^@^@jkl\nTEST\n\n\nT\n', + redir_exec([[lua print("abc \0 def\nghi\0\0\0jkl\nTEST\n\n\nT\n")]])) + eq('\nabc ^@ def\nghi^@^@^@jkl\nTEST\n\n\nT^@', + redir_exec([[lua print("abc \0 def\nghi\0\0\0jkl\nTEST\n\n\nT\0")]])) + eq('\nT^@', redir_exec([[lua print("T\0")]])) + eq('\nT\n', redir_exec([[lua print("T\n")]])) + end) +end) From edc80f6b46f51ea1137289301914c0f90db19295 Mon Sep 17 00:00:00 2001 From: raichoo Date: Sun, 26 Mar 2017 23:15:53 +0200 Subject: [PATCH 0157/1671] vim-patch:7.4.2357 (#6354) Problem: Attempt to read history entry while not initialized. Solution: Skip when the index is negative. https://github.com/vim/vim/commit/46643713dc6bb04b4e84986b1763ef309e960161 --- src/nvim/ex_getln.c | 4 ++-- src/nvim/version.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 58979c0e43..8758a63bce 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -4677,8 +4677,8 @@ add_to_history ( * down, only lines that were added. */ if (histype == HIST_SEARCH && in_map) { - if (maptick == last_maptick) { - /* Current line is from the same mapping, remove it */ + if (maptick == last_maptick && hisidx[HIST_SEARCH] >= 0) { + // Current line is from the same mapping, remove it hisptr = &history[HIST_SEARCH][hisidx[HIST_SEARCH]]; hist_free_entry(hisptr); --hisnum[histype]; diff --git a/src/nvim/version.c b/src/nvim/version.c index a354634218..a911e8ebc3 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -84,7 +84,7 @@ static int included_patches[] = { // 2360, // 2359 NA // 2358 NA - // 2357, + 2357, // 2356, 2355, // 2354, From 73d37f8b6e9eba0d2f2c59694ae5a13777a7f1cd Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 30 Jan 2017 00:34:33 +0300 Subject: [PATCH 0158/1671] executor: Add :lua debug.debug mock --- src/nvim/eval.c | 187 ++++++++++++++++-------------- src/nvim/ex_docmd.c | 4 +- src/nvim/message.c | 3 +- src/nvim/viml/executor/executor.c | 38 ++++++ 4 files changed, 141 insertions(+), 91 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8b6638f1d7..fa817bb705 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -12280,93 +12280,70 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) static int inputsecret_flag = 0; - -/* - * This function is used by f_input() and f_inputdialog() functions. The third - * argument to f_input() specifies the type of completion to use at the - * prompt. The third argument to f_inputdialog() specifies the value to return - * when the user cancels the prompt. - */ -static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) +/// Get user input +/// +/// Used for f_input and f_inputdialog functions. +/// +/// @param[in] prompt Input prompt. +/// @param[in] initval Initial value, may be NULL. +/// @param[in] xp_name Completion, for input(). +/// @param[in] cancelval Value returned when user cancelled dialog, for +/// inputdialog(). +/// +/// @return [allocated] User input or NULL. +char *get_user_input(const char *const prompt, + const char *const initval, + const char *const xp_name, + const char *const cancelval) + FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { - char_u *prompt = get_tv_string_chk(&argvars[0]); - char_u *p = NULL; - int c; - char_u buf[NUMBUFLEN]; - int cmd_silent_save = cmd_silent; - char_u *defstr = (char_u *)""; - int xp_type = EXPAND_NOTHING; - char_u *xp_arg = NULL; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - cmd_silent = FALSE; /* Want to see the prompt. */ - if (prompt != NULL) { - /* Only the part of the message after the last NL is considered as - * prompt for the command line */ - p = vim_strrchr(prompt, '\n'); - if (p == NULL) - p = prompt; - else { - ++p; - c = *p; - *p = NUL; - msg_start(); - msg_clr_eos(); - msg_puts_attr((const char *)prompt, echo_attr); - msg_didout = false; - msg_starthere(); - *p = c; - } - cmdline_row = msg_row; - - if (argvars[1].v_type != VAR_UNKNOWN) { - defstr = get_tv_string_buf_chk(&argvars[1], buf); - if (defstr != NULL) - stuffReadbuffSpec(defstr); - - if (!inputdialog && argvars[2].v_type != VAR_UNKNOWN) { - char_u *xp_name; - int xp_namelen; - uint32_t argt; - - /* input() with a third argument: completion */ - rettv->vval.v_string = NULL; - - xp_name = get_tv_string_buf_chk(&argvars[2], buf); - if (xp_name == NULL) - return; - - xp_namelen = (int)STRLEN(xp_name); - - if (parse_compl_arg(xp_name, xp_namelen, &xp_type, &argt, - &xp_arg) == FAIL) - return; - } - } - - if (defstr != NULL) { - int save_ex_normal_busy = ex_normal_busy; - ex_normal_busy = 0; - rettv->vval.v_string = - getcmdline_prompt(inputsecret_flag ? NUL : '@', p, echo_attr, - xp_type, xp_arg); - ex_normal_busy = save_ex_normal_busy; - } - if (inputdialog && rettv->vval.v_string == NULL - && argvars[1].v_type != VAR_UNKNOWN - && argvars[2].v_type != VAR_UNKNOWN) - rettv->vval.v_string = vim_strsave(get_tv_string_buf( - &argvars[2], buf)); - - xfree(xp_arg); - - /* since the user typed this, no need to wait for return */ - need_wait_return = FALSE; - msg_didout = FALSE; + char *ret = NULL; + const int saved_cmd_silent = cmd_silent; + cmd_silent = false; // Want to see the prompt. + // Only the part of the message after the last NL is considered as + // prompt for the command line. + const char *p = strrchr(prompt, NL); + if (p == NULL) { + p = prompt; + } else { + p++; + msg_start(); + msg_clr_eos(); + msg_puts_attr_len(prompt, (int)(p - prompt) + 1, echo_attr); + msg_didout = false; + msg_starthere(); } - cmd_silent = cmd_silent_save; + cmdline_row = msg_row; + + stuffReadbuffSpec((char_u *)initval); + + char *xp_arg = NULL; + int xp_type = EXPAND_NOTHING; + if (xp_name != NULL) { + uint32_t argt; + if (parse_compl_arg((const char_u *)xp_name, (int)strlen(xp_name), + &xp_type, &argt, (char_u **)&xp_arg) == FAIL) { + return NULL; + } + } + + const int saved_ex_normal_busy = ex_normal_busy; + ex_normal_busy = 0; + ret = (char *)getcmdline_prompt(inputsecret_flag ? NUL : '@', (char_u *)p, + echo_attr, xp_type, (char_u *)xp_arg); + ex_normal_busy = saved_ex_normal_busy; + + if (ret == NULL && cancelval != NULL) { + ret = xstrdup(cancelval); + } + + xfree(xp_arg); + + // Since the user typed this, no need to wait for return. + need_wait_return = false; + msg_didout = false; + cmd_silent = saved_cmd_silent; + return ret; } /* @@ -12375,7 +12352,25 @@ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) */ static void f_input(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - get_user_input(argvars, rettv, FALSE); + char initval_buf[NUMBUFLEN]; + char xp_name_buf[NUMBUFLEN]; + const char *const prompt = (const char *)get_tv_string_chk(&argvars[0]); + const char *const initval = ( + argvars[1].v_type != VAR_UNKNOWN + ? (const char *)get_tv_string_buf(&argvars[1], (char_u *)initval_buf) + : ""); + const char *const xp_name = ( + argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN + ? (const char *)get_tv_string_buf(&argvars[2], (char_u *)xp_name_buf) + : NULL); + if (prompt == NULL || initval == NULL || ( + argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN + && xp_name == NULL)) { + return; + } + rettv->v_type = VAR_STRING; + rettv->vval.v_string = (char_u *)get_user_input(prompt, initval, xp_name, + NULL); } /* @@ -12383,7 +12378,25 @@ static void f_input(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_inputdialog(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - get_user_input(argvars, rettv, TRUE); + char initval_buf[NUMBUFLEN]; + char cancelval_buf[NUMBUFLEN]; + const char *const prompt = (const char *)get_tv_string_chk(&argvars[0]); + const char *const initval = ( + argvars[1].v_type != VAR_UNKNOWN + ? (const char *)get_tv_string_buf(&argvars[1], (char_u *)initval_buf) + : ""); + const char *const cancelval = ( + argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN + ? (const char *)get_tv_string_buf(&argvars[2], (char_u *)cancelval_buf) + : NULL); + if (prompt == NULL || initval == NULL || ( + argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN + && cancelval == NULL)) { + return; + } + rettv->v_type = VAR_STRING; + rettv->vval.v_string = (char_u *)get_user_input(prompt, initval, NULL, + cancelval); } /* diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 74bb6177c6..69dab9c18f 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5757,10 +5757,10 @@ int parse_addr_type_arg(char_u *value, int vallen, uint32_t *argt, * copied to allocated memory and stored in "*compl_arg". * Returns FAIL if something is wrong. */ -int parse_compl_arg(char_u *value, int vallen, int *complp, +int parse_compl_arg(const char_u *value, int vallen, int *complp, uint32_t *argt, char_u **compl_arg) { - char_u *arg = NULL; + const char_u *arg = NULL; size_t arglen = 0; int i; int valend = vallen; diff --git a/src/nvim/message.c b/src/nvim/message.c index bf54284881..4b786c11dd 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1564,7 +1564,6 @@ void msg_puts_attr(const char *const s, const int attr) /// Like msg_puts_attr(), but with a maximum length "maxlen" (in bytes). /// When "maxlen" is -1 there is no maximum length. -/// When "maxlen" is >= 0 the message is not put in the history. void msg_puts_attr_len(const char *str, const ptrdiff_t maxlen, int attr) { // If redirection is on, also write to the redirection file. @@ -1576,7 +1575,7 @@ void msg_puts_attr_len(const char *str, const ptrdiff_t maxlen, int attr) } // if MSG_HIST flag set, add message to history - if ((attr & MSG_HIST) && maxlen < 0) { + if (attr & MSG_HIST) { add_msg_hist(str, -1, attr); attr &= ~MSG_HIST; } diff --git a/src/nvim/viml/executor/executor.c b/src/nvim/viml/executor/executor.c index 4eb63f38ad..d0e269bf6a 100644 --- a/src/nvim/viml/executor/executor.c +++ b/src/nvim/viml/executor/executor.c @@ -250,15 +250,28 @@ static int nlua_exec_lua_file(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL /// Called by lua interpreter itself to initialize state. static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL { + // stricmp lua_pushcfunction(lstate, &nlua_stricmp); lua_setglobal(lstate, "stricmp"); + + // print lua_pushcfunction(lstate, &nlua_print); lua_setglobal(lstate, "print"); + + // debug.debug + lua_getglobal(lstate, "debug"); + lua_pushcfunction(lstate, &nlua_debug); + lua_setfield(lstate, -2, "debug"); + lua_pop(lstate, 1); + + // vim if (luaL_dostring(lstate, (char *)&vim_module[0])) { nlua_error(lstate, _("E5106: Error while creating vim module: %.*s")); return 1; } + // vim.api nlua_add_api_functions(lstate); + // vim.types, vim.type_idx, vim.val_idx nlua_init_types(lstate); lua_setglobal(lstate, "vim"); return 0; @@ -442,6 +455,31 @@ nlua_print_error: return 0; } +/// debug.debug implementation: interaction with user while debugging +/// +/// @param lstate Lua interpreter state. +int nlua_debug(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL +{ + for (;;) { + lua_settop(lstate, 0); + char *const input = get_user_input("lua_debug> ", "", NULL, NULL); + msg_putchar('\n'); // Avoid outputting on input line. + if (input == NULL || *input == NUL || strcmp(input, "cont") == 0) { + xfree(input); + return 0; + } + if (luaL_loadbuffer(lstate, input, strlen(input), "=(debug command)")) { + nlua_error(lstate, _("E5115: Error while loading debug string: %.*s")); + } + xfree(input); + if (lua_pcall(lstate, 0, 0, 0)) { + nlua_error(lstate, _("E5116: Error while calling debug string: %.*s")); + } + } + return 0; +} + /// Evaluate lua string /// /// Used for luaeval(). From d13fdfd4467db8245159d10d96c1f30b24b51565 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 30 Jan 2017 03:30:45 +0300 Subject: [PATCH 0159/1671] functests: Add test for debug.debug --- test/functional/lua/overrides_spec.lua | 111 +++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua index 92167d18dc..60a0589b48 100644 --- a/test/functional/lua/overrides_spec.lua +++ b/test/functional/lua/overrides_spec.lua @@ -1,8 +1,10 @@ -- Test for Vim overrides of lua built-ins local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') local eq = helpers.eq local NIL = helpers.NIL +local feed = helpers.feed local clear = helpers.clear local funcs = helpers.funcs local meths = helpers.meths @@ -10,6 +12,8 @@ local command = helpers.command local write_file = helpers.write_file local redir_exec = helpers.redir_exec +local screen + local fname = 'Xtest-functional-lua-overrides-luafile' before_each(clear) @@ -63,3 +67,110 @@ describe('print', function() eq('\nT\n', redir_exec([[lua print("T\n")]])) end) end) + +describe('debug.debug', function() + before_each(function() + screen = Screen.new() + screen:attach() + screen:set_default_attr_ids({ + [0] = {bold=true, foreground=255}, + E = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, + cr = {bold = true, foreground = Screen.colors.SeaGreen4}, + }) + end) + it('works', function() + write_file(fname, [[ + function Test(a) + print(a) + debug.debug() + print(a * 100) + end + ]]) + eq('', redir_exec('luafile ' .. fname)) + feed(':lua Test()\n') + screen:expect([[ + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + nil | + lua_debug> ^ | + ]]) + feed('print("TEST")\n') + screen:expect([[ + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + nil | + lua_debug> print("TEST") | + TEST | + lua_debug> ^ | + ]]) + feed('') + screen:expect([[ + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + nil | + lua_debug> print("TEST") | + TEST | + | + {E:E5105: Error while calling lua chunk: Xtest-functiona}| + {E:l-lua-overrides-luafile:4: attempt to perform arithme}| + {E:tic on local 'a' (a nil value)} | + Interrupt: {cr:Press ENTER or type command to continue}^ | + ]]) + feed(':lua Test()\n') + screen:expect([[ + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + nil | + lua_debug> ^ | + ]]) + feed('\n') + screen:expect([[ + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + nil | + lua_debug> | + {E:E5105: Error while calling lua chunk: Xtest-functiona}| + {E:l-lua-overrides-luafile:4: attempt to perform arithme}| + {E:tic on local 'a' (a nil value)} | + {cr:Press ENTER or type command to continue}^ | + ]]) + end) +end) From a24e94215ed0a4bfe55b6741ab2e9477b278dbb7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 30 Jan 2017 03:35:45 +0300 Subject: [PATCH 0160/1671] eval,functests: Fix linter errors --- src/nvim/eval.c | 8 ++++---- test/functional/lua/overrides_spec.lua | 15 +++++++-------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index fa817bb705..67b755f053 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -12363,8 +12363,8 @@ static void f_input(typval_T *argvars, typval_T *rettv, FunPtr fptr) argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN ? (const char *)get_tv_string_buf(&argvars[2], (char_u *)xp_name_buf) : NULL); - if (prompt == NULL || initval == NULL || ( - argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN + if (prompt == NULL || initval == NULL + || (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN && xp_name == NULL)) { return; } @@ -12389,8 +12389,8 @@ static void f_inputdialog(typval_T *argvars, typval_T *rettv, FunPtr fptr) argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN ? (const char *)get_tv_string_buf(&argvars[2], (char_u *)cancelval_buf) : NULL); - if (prompt == NULL || initval == NULL || ( - argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN + if (prompt == NULL || initval == NULL + || (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN && cancelval == NULL)) { return; } diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua index 60a0589b48..c8aee130a7 100644 --- a/test/functional/lua/overrides_spec.lua +++ b/test/functional/lua/overrides_spec.lua @@ -79,14 +79,13 @@ describe('debug.debug', function() }) end) it('works', function() - write_file(fname, [[ + command([[lua function Test(a) print(a) debug.debug() print(a * 100) end ]]) - eq('', redir_exec('luafile ' .. fname)) feed(':lua Test()\n') screen:expect([[ {0:~ }| @@ -133,9 +132,9 @@ describe('debug.debug', function() lua_debug> print("TEST") | TEST | | - {E:E5105: Error while calling lua chunk: Xtest-functiona}| - {E:l-lua-overrides-luafile:4: attempt to perform arithme}| - {E:tic on local 'a' (a nil value)} | + {E:E5105: Error while calling lua chunk: [string ""]:5: attempt to perform arithmetic o}| + {E:n local 'a' (a nil value)} | Interrupt: {cr:Press ENTER or type command to continue}^ | ]]) feed(':lua Test()\n') @@ -167,9 +166,9 @@ describe('debug.debug', function() {0:~ }| nil | lua_debug> | - {E:E5105: Error while calling lua chunk: Xtest-functiona}| - {E:l-lua-overrides-luafile:4: attempt to perform arithme}| - {E:tic on local 'a' (a nil value)} | + {E:E5105: Error while calling lua chunk: [string ""]:5: attempt to perform arithmetic o}| + {E:n local 'a' (a nil value)} | {cr:Press ENTER or type command to continue}^ | ]]) end) From 5992cdf3c27ee9c73cea22e288c6ea6d54867394 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 30 Jan 2017 23:13:06 +0300 Subject: [PATCH 0161/1671] cmake: Use set_property in place of target_include_dirs Should work with cmake-2.8.7. --- src/nvim/CMakeLists.txt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 0cf331206e..a6ae758a8e 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -321,9 +321,11 @@ target_link_libraries(nvim ${NVIM_EXEC_LINK_LIBRARIES}) install_helper(TARGETS nvim) if(PREFER_LUAJIT) - target_include_directories(nvim SYSTEM PRIVATE ${LUAJIT_INCLUDE_DIRS}) + set_property(TARGET nvim APPEND PROPERTY + INCLUDE_DIRECTORIES ${LUAJIT_INCLUDE_DIRS}) else() - target_include_directories(nvim SYSTEM PRIVATE ${LUA_INCLUDE_DIRS}) + set_property(TARGET nvim APPEND PROPERTY + INCLUDE_DIRECTORIES ${LUA_INCLUDE_DIRS}) endif() if(WIN32) @@ -374,7 +376,8 @@ endif() add_library(libnvim STATIC EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_SOURCES} ${NEOVIM_SOURCES} ${NEOVIM_HEADERS}) -target_include_directories(libnvim SYSTEM PRIVATE ${LUAJIT_INCLUDE_DIRS}) +set_property(TARGET libnvim APPEND PROPERTY + INCLUDE_DIRECTORIES ${LUAJIT_INCLUDE_DIRS}) target_link_libraries(libnvim ${NVIM_TEST_LINK_LIBRARIES}) set_target_properties(libnvim PROPERTIES POSITION_INDEPENDENT_CODE ON @@ -385,7 +388,8 @@ set_property(TARGET libnvim add_library(nvim-test MODULE EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_SOURCES} ${NEOVIM_SOURCES} ${UNIT_TEST_FIXTURES} ${NEOVIM_HEADERS}) target_link_libraries(nvim-test ${NVIM_TEST_LINK_LIBRARIES}) -target_include_directories(nvim-test SYSTEM PRIVATE ${LUAJIT_INCLUDE_DIRS}) +set_property(TARGET nvim-test APPEND PROPERTY + INCLUDE_DIRECTORIES ${LUAJIT_INCLUDE_DIRS}) set_target_properties(nvim-test PROPERTIES POSITION_INDEPENDENT_CODE ON) set_property(TARGET nvim-test From 20e7652b6980e0efed5db65e0f80c0d72ef8c68d Mon Sep 17 00:00:00 2001 From: lonerover Date: Thu, 23 Mar 2017 22:42:26 +0800 Subject: [PATCH 0162/1671] vim-patch:7.4.2307 Problem: Several tests are old style. Solution: Turn them into new style tests. (Yegappan Lakshmanan) https://github.com/vim/vim/commit/cd055da370114f66c960be9c8b1eb0f33a9e0a85 --- src/nvim/testdir/Makefile | 3 ++ src/nvim/testdir/test_charsearch.vim | 62 +++++++++++++++++++++++++++ src/nvim/testdir/test_fnameescape.vim | 21 +++++++++ src/nvim/testdir/test_substitute.vim | 41 ++++++++++++++++++ src/nvim/version.c | 2 +- 5 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 src/nvim/testdir/test_charsearch.vim create mode 100644 src/nvim/testdir/test_fnameescape.vim create mode 100644 src/nvim/testdir/test_substitute.vim diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 531a07912f..fd557cca51 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -30,6 +30,7 @@ SCRIPTS ?= \ NEW_TESTS ?= \ test_autocmd.res \ test_bufwintabinfo.res \ + test_charsearch.res \ test_cmdline.res \ test_command_count.res \ test_cscope.res \ @@ -37,6 +38,7 @@ NEW_TESTS ?= \ test_diffmode.res \ test_farsi.res \ test_filter_map.res \ + test_fnameescape.res \ test_fold.res \ test_glob2regpat.res \ test_gn.res \ @@ -55,6 +57,7 @@ NEW_TESTS ?= \ test_normal.res \ test_quickfix.res \ test_signs.res \ + test_substitute.res \ test_syntax.res \ test_tabpage.res \ test_textobjects.res \ diff --git a/src/nvim/testdir/test_charsearch.vim b/src/nvim/testdir/test_charsearch.vim new file mode 100644 index 0000000000..115659a013 --- /dev/null +++ b/src/nvim/testdir/test_charsearch.vim @@ -0,0 +1,62 @@ + +function! Test_charsearch() + enew! + call append(0, ['Xabcdefghijkemnopqretuvwxyz', + \ 'Yabcdefghijkemnopqretuvwxyz', + \ 'Zabcdefghijkemnokqretkvwxyz']) + " check that "fe" and ";" work + 1 + normal! ylfep;;p,,p + call assert_equal('XabcdeXfghijkeXmnopqreXtuvwxyz', getline(1)) + " check that save/restore works + 2 + normal! ylfep + let csave = getcharsearch() + normal! fip + call setcharsearch(csave) + normal! ;p;p + call assert_equal('YabcdeYfghiYjkeYmnopqreYtuvwxyz', getline(2)) + + " check that setcharsearch() changes the settings. + 3 + normal! ylfep + call setcharsearch({'char': 'k'}) + normal! ;p + call setcharsearch({'forward': 0}) + normal! $;p + call setcharsearch({'until': 1}) + set cpo-=; + normal! ;;p + call assert_equal('ZabcdeZfghijkZZemnokqretkZvwxyz', getline(3)) + enew! +endfunction + +" Test for t,f,F,T movement commands and 'cpo-;' setting +function! Test_search_cmds() + enew! + call append(0, ["aaa two three four", " zzz", "yyy ", + \ "bbb yee yoo four", "ccc two three four", + \ "ddd yee yoo four"]) + set cpo-=; + 1 + normal! 0tt;D + 2 + normal! 0fz;D + 3 + normal! $Fy;D + 4 + normal! $Ty;D + set cpo+=; + 5 + normal! 0tt;;D + 6 + normal! $Ty;;D + + call assert_equal('aaa two', getline(1)) + call assert_equal(' z', getline(2)) + call assert_equal('y', getline(3)) + call assert_equal('bbb y', getline(4)) + call assert_equal('ccc', getline(5)) + call assert_equal('ddd yee y', getline(6)) + enew! +endfunction diff --git a/src/nvim/testdir/test_fnameescape.vim b/src/nvim/testdir/test_fnameescape.vim new file mode 100644 index 0000000000..ce2cd3f7a6 --- /dev/null +++ b/src/nvim/testdir/test_fnameescape.vim @@ -0,0 +1,21 @@ + +" Test if fnameescape is correct for special chars like ! +function! Test_fnameescape() + let fname = 'Xspa ce' + let status = v:false + try + exe "w! " . fnameescape(fname) + let status = v:true + endtry + call assert_true(status, "Space") + call delete(fname) + + let fname = 'Xemark!' + let status = v:false + try + exe "w! " . fnameescape(fname) + let status = v:true + endtry + call assert_true(status, "ExclamationMark") + call delete(fname) +endfunction diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim new file mode 100644 index 0000000000..e2b6de03c3 --- /dev/null +++ b/src/nvim/testdir/test_substitute.vim @@ -0,0 +1,41 @@ +" Tests for multi-line regexps with ":s". + +function! Test_multiline_subst() + enew! + call append(0, ["1 aa", + \ "bb", + \ "cc", + \ "2 dd", + \ "ee", + \ "3 ef", + \ "gh", + \ "4 ij", + \ "5 a8", + \ "8b c9", + \ "9d", + \ "6 e7", + \ "77f", + \ "xxxxx"]) + + 1 + " test if replacing a line break works with a back reference + /^1/,/^2/s/\n\(.\)/ \1/ + " test if inserting a line break works with a back reference + /^3/,/^4/s/\(.\)$/\r\1/ + " test if replacing a line break with another line break works + /^5/,/^6/s/\(\_d\{3}\)/x\1x/ + call assert_equal('1 aa bb cc 2 dd ee', getline(1)) + call assert_equal('3 e', getline(2)) + call assert_equal('f', getline(3)) + call assert_equal('g', getline(4)) + call assert_equal('h', getline(5)) + call assert_equal('4 i', getline(6)) + call assert_equal('j', getline(7)) + call assert_equal('5 ax8', getline(8)) + call assert_equal('8xb cx9', getline(9)) + call assert_equal('9xd', getline(10)) + call assert_equal('6 ex7', getline(11)) + call assert_equal('7x7f', getline(12)) + call assert_equal('xxxxx', getline(13)) + enew! +endfunction diff --git a/src/nvim/version.c b/src/nvim/version.c index a911e8ebc3..ee66b00b4e 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -134,7 +134,7 @@ static int included_patches[] = { // 2310 NA 2309, // 2308 NA - // 2307, + 2307, // 2306, 2305, // 2304 NA From 85ba14af6ad19fa3c4ba1ade3819014254635b2f Mon Sep 17 00:00:00 2001 From: lonerover Date: Thu, 23 Mar 2017 22:54:30 +0800 Subject: [PATCH 0163/1671] vim-patch:7.4.2330 Problem: Coverity complains about not checking curwin to be NULL. Solution: Use firstwin to avoid the warning. https://github.com/vim/vim/commit/030cddc7ec0c3d2fe3969140cd1b92b2f18633c0 --- src/nvim/buffer.c | 7 ++++--- src/nvim/version.c | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 3c416c157f..4a07884f98 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -627,10 +627,11 @@ void buf_freeall(buf_T *buf, int flags) */ if (buf == curbuf && !is_curbuf) return; - diff_buf_delete(buf); /* Can't use 'diff' for unloaded buffer. */ - /* Remove any ownsyntax, unless exiting. */ - if (firstwin != NULL && curwin->w_buffer == buf) + diff_buf_delete(buf); // Can't use 'diff' for unloaded buffer. + // Remove any ownsyntax, unless exiting. + if (curwin != NULL && curwin->w_buffer == buf) { reset_synblock(curwin); + } /* No folds in an empty buffer. */ FOR_ALL_TAB_WINDOWS(tp, win) { diff --git a/src/nvim/version.c b/src/nvim/version.c index ee66b00b4e..c18439c279 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -111,7 +111,7 @@ static int included_patches[] = { 2333, // 2332 NA 2331, - // 2330, + 2330, 2329, 2328, // 2327 NA From 9cd7e199048161c5224aca885af8b7945236392d Mon Sep 17 00:00:00 2001 From: lonerover Date: Fri, 24 Mar 2017 12:24:25 +0800 Subject: [PATCH 0164/1671] vim-patch:7.4.2334 Problem: On MS-Windows test_getcwd leaves Xtopdir behind. Solution: Set 'noswapfile'. (Michael Soyka) https://github.com/vim/vim/commit/1b0c2fcf6e85c9b85c24757ba970061e1f3e4e80 --- src/nvim/version.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/version.c b/src/nvim/version.c index c18439c279..500e8984c0 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -107,7 +107,7 @@ static int included_patches[] = { 2337, 2336, 2335, - // 2334, + 2334, 2333, // 2332 NA 2331, From 7bc37ffb22a84668bba5b2e3589c4c05ad43f7d0 Mon Sep 17 00:00:00 2001 From: Jakob Schnitzer Date: Fri, 24 Mar 2017 20:21:05 +0100 Subject: [PATCH 0165/1671] terminal: global 'scrollback' #6352 Make the 'scrollback' option work like most other buffer-local options: - `:set scrollback=x` sets the global and local value - `:setglobal scrollback=x` sets only the global default - new terminal buffers inherit the global Normal buffers are still always -1, and :setlocal there is an error. Closes #6337 --- runtime/doc/options.txt | 14 -------- src/nvim/globals.h | 2 +- src/nvim/option.c | 21 +++++------ src/nvim/option_defs.h | 3 ++ src/nvim/options.lua | 2 +- src/nvim/terminal.c | 13 ++++--- test/functional/terminal/scrollback_spec.lua | 37 ++++++++++++++++---- 7 files changed, 53 insertions(+), 39 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 1cb14f7771..c31ebf4ebc 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -398,20 +398,6 @@ command, not when assigning a value to an option with ":let". Note the maximum length of an expanded option is limited. How much depends on the system, mostly it is something like 256 or 1024 characters. - *Linux-backspace* - Note about Linux: By default the backspace key - produces CTRL-?, which is wrong. You can fix it by - putting this line in your rc.local: > - echo "keycode 14 = BackSpace" | loadkeys -< - *NetBSD-backspace* - Note about NetBSD: If your backspace doesn't produce - the right code, try this: > - xmodmap -e "keycode 22 = BackSpace" -< If this works, add this in your .Xmodmap file: > - keysym 22 = BackSpace -< You need to restart for this to take effect. - ============================================================================== 2. Automatically setting options *auto-setting* diff --git a/src/nvim/globals.h b/src/nvim/globals.h index ad14ec4f8c..de79ee2469 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -1255,7 +1255,7 @@ typedef enum { kCdScopeInvalid = -1, kCdScopeWindow, ///< Affects one window. kCdScopeTab, ///< Affects one tab page. - kCdScopeGlobal, ///< Affects the entire instance of Neovim. + kCdScopeGlobal, ///< Affects the entire Nvim instance. } CdScope; #define MIN_CD_SCOPE kCdScopeWindow diff --git a/src/nvim/option.c b/src/nvim/option.c index 2a17ca69d1..b037b0ae35 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -156,7 +156,6 @@ static long p_ts; static long p_tw; static int p_udf; static long p_wm; -static long p_scbk; static char_u *p_keymap; /* Saved values for when 'bin' is set. */ @@ -4199,16 +4198,13 @@ set_num_option ( FOR_ALL_TAB_WINDOWS(tp, wp) { check_colorcolumn(wp); } - } else if (pp == &curbuf->b_p_scbk) { + } else if (pp == &curbuf->b_p_scbk || pp == &p_scbk) { // 'scrollback' - if (!curbuf->terminal) { + if (*pp < -1 || *pp > SB_MAX + || (opt_flags == OPT_LOCAL && !curbuf->terminal)) { errmsg = e_invarg; - curbuf->b_p_scbk = -1; - } else { - if (curbuf->b_p_scbk < -1 || curbuf->b_p_scbk > 100000) { - errmsg = e_invarg; - curbuf->b_p_scbk = 1000; - } + *pp = old_value; + } else if (curbuf->terminal) { // Force the scrollback to take effect. terminal_resize(curbuf->terminal, UINT16_MAX, UINT16_MAX); } @@ -4331,6 +4327,11 @@ set_num_option ( if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) = *pp; + if (pp == &curbuf->b_p_scbk && !curbuf->terminal) { + // Normal buffer: reset local 'scrollback' after updating the global value. + curbuf->b_p_scbk = -1; + } + options[opt_idx].flags |= P_WAS_SET; if (!starting && errmsg == NULL) { @@ -4586,7 +4587,7 @@ get_option_value ( // // Pretends that option is absent if it is not present in the requested scope // (i.e. has no global, window-local or buffer-local value depending on -// opt_type). Uses +// opt_type). // // Returned flags: // 0 hidden or unknown option, also option that does not have requested diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index b171b23edb..94c6361236 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -524,6 +524,7 @@ EXTERN int p_ru; // 'ruler' EXTERN char_u *p_ruf; // 'rulerformat' EXTERN char_u *p_pp; // 'packpath' EXTERN char_u *p_rtp; // 'runtimepath' +EXTERN long p_scbk; // 'scrollback' EXTERN long p_sj; // 'scrolljump' EXTERN long p_so; // 'scrolloff' EXTERN char_u *p_sbo; // 'scrollopt' @@ -811,4 +812,6 @@ enum { /* Value for b_p_ul indicating the global value must be used. */ #define NO_LOCAL_UNDOLEVEL -123456 +#define SB_MAX 100000 // Maximum 'scrollback' value. + #endif // NVIM_OPTION_DEFS_H diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 853c2b52d7..ee2b8a563d 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -1918,7 +1918,7 @@ return { vi_def=true, varname='p_scbk', redraw={'current_buffer'}, - defaults={if_true={vi=-1}} + defaults={if_true={vi=1000}} }, { full_name='scrollbind', abbreviation='scb', diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 87ee8f410f..dc418e25fa 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -85,8 +85,6 @@ typedef struct terminal_state { # include "terminal.c.generated.h" #endif -#define SB_MAX 100000 // Maximum 'scrollback' value. - // Delay for refreshing the terminal buffer after receiving updates from // libvterm. Improves performance when receiving large bursts of data. #define REFRESH_DELAY 10 @@ -231,10 +229,10 @@ Terminal *terminal_open(TerminalOptions opts) set_option_value((uint8_t *)"buftype", 0, (uint8_t *)"terminal", OPT_LOCAL); // Default settings for terminal buffers - curbuf->b_p_ma = false; // 'nomodifiable' - curbuf->b_p_ul = -1; // 'undolevels' - curbuf->b_p_scbk = 1000; // 'scrollback' - curbuf->b_p_tw = 0; // 'textwidth' + curbuf->b_p_ma = false; // 'nomodifiable' + curbuf->b_p_ul = -1; // 'undolevels' + curbuf->b_p_scbk = p_scbk; // 'scrollback' + curbuf->b_p_tw = 0; // 'textwidth' set_option_value((uint8_t *)"wrap", false, NULL, OPT_LOCAL); set_option_value((uint8_t *)"number", false, NULL, OPT_LOCAL); set_option_value((uint8_t *)"relativenumber", false, NULL, OPT_LOCAL); @@ -248,7 +246,8 @@ Terminal *terminal_open(TerminalOptions opts) apply_autocmds(EVENT_TERMOPEN, NULL, NULL, false, curbuf); // Configure the scrollback buffer. - rv->sb_size = curbuf->b_p_scbk < 0 ? SB_MAX : (size_t)curbuf->b_p_scbk;; + rv->sb_size = curbuf->b_p_scbk < 0 + ? SB_MAX : (size_t)MAX(1, curbuf->b_p_scbk); rv->sb_buffer = xmalloc(sizeof(ScrollbackLine *) * rv->sb_size); if (!true_color) { diff --git a/test/functional/terminal/scrollback_spec.lua b/test/functional/terminal/scrollback_spec.lua index 81649f2bde..4ead288a19 100644 --- a/test/functional/terminal/scrollback_spec.lua +++ b/test/functional/terminal/scrollback_spec.lua @@ -8,6 +8,7 @@ local command = helpers.command local wait = helpers.wait local retry = helpers.retry local curbufmeths = helpers.curbufmeths +local nvim = helpers.nvim local feed_data = thelpers.feed_data if helpers.pending_win32(pending) then return end @@ -368,6 +369,12 @@ describe("'scrollback' option", function() clear() end) + local function set_fake_shell() + -- shell-test.c is a fake shell that prints its arguments and exits. + nvim('set_option', 'shell', nvim_dir..'/shell-test') + nvim('set_option', 'shellcmdflag', 'EXE') + end + local function expect_lines(expected, epsilon) local ep = epsilon and epsilon or 0 local actual = eval("line('$')") @@ -421,12 +428,13 @@ describe("'scrollback' option", function() screen:detach() end) - it('defaults to 1000', function() - execute('terminal') + it('defaults to 1000 in terminal buffers', function() + set_fake_shell() + command('terminal') eq(1000, curbufmeths.get_option('scrollback')) end) - it('error if set to invalid values', function() + it('error if set to invalid value', function() local status, rv = pcall(command, 'set scrollback=-2') eq(false, status) -- assert failure eq('E474:', string.match(rv, "E%d*:")) @@ -437,15 +445,32 @@ describe("'scrollback' option", function() end) it('defaults to -1 on normal buffers', function() - execute('new') + command('new') eq(-1, curbufmeths.get_option('scrollback')) end) - it('error if set on a normal buffer', function() + it(':setlocal in a normal buffer is an error', function() command('new') - execute('set scrollback=42') + execute('setlocal scrollback=42') feed('') eq('E474:', string.match(eval("v:errmsg"), "E%d*:")) + eq(-1, curbufmeths.get_option('scrollback')) + end) + + it(':set updates local value and global default', function() + set_fake_shell() + command('set scrollback=42') -- set global and (attempt) local + eq(-1, curbufmeths.get_option('scrollback')) -- normal buffer: -1 + command('terminal') + eq(42, curbufmeths.get_option('scrollback')) -- inherits global default + command('setlocal scrollback=99') + eq(99, curbufmeths.get_option('scrollback')) + command('set scrollback<') -- reset to global default + eq(42, curbufmeths.get_option('scrollback')) + command('setglobal scrollback=734') -- new global default + eq(42, curbufmeths.get_option('scrollback')) -- local value did not change + command('terminal') + eq(734, curbufmeths.get_option('scrollback')) end) end) From 62774e43564b166d6907c7abc2e3431a65bd5596 Mon Sep 17 00:00:00 2001 From: Eiichi NISHINA Date: Sun, 12 Feb 2017 02:10:53 +0900 Subject: [PATCH 0166/1671] ci: Check that `#include "*.h"` works as a single include Lesser form of include-what-you-use: at least guarantees that header file did not forget to include something through some other included file. Activate run_single_includes_tests on CI. Fix some IWYU violations. References #5321 --- .ci/common/test.sh | 4 ++ .ci/run_tests.sh | 1 + Makefile | 3 + src/nvim/CMakeLists.txt | 156 +++++++++++++++++++++++++++++++++------- src/nvim/eval_defs.h | 1 + src/nvim/ex_docmd.c | 1 + src/nvim/ex_docmd.h | 1 + src/nvim/mark.h | 2 +- src/nvim/os/fs_defs.h | 2 +- src/nvim/os/os_defs.h | 4 +- src/nvim/os/unix_defs.h | 2 +- src/nvim/os/win_defs.h | 6 +- src/nvim/strings.h | 3 +- 13 files changed, 153 insertions(+), 33 deletions(-) diff --git a/.ci/common/test.sh b/.ci/common/test.sh index b28e46a4df..4137472385 100644 --- a/.ci/common/test.sh +++ b/.ci/common/test.sh @@ -109,6 +109,10 @@ run_oldtests() { check_core_dumps } +run_single_includes_tests() { + ${MAKE_CMD} -C "${BUILD_DIR}" check-single-includes +} + install_nvim() { ${MAKE_CMD} -C "${BUILD_DIR}" install diff --git a/.ci/run_tests.sh b/.ci/run_tests.sh index 6347ac15d4..d994db471f 100755 --- a/.ci/run_tests.sh +++ b/.ci/run_tests.sh @@ -10,6 +10,7 @@ source "${CI_DIR}/common/test.sh" check_core_dumps --delete quiet prepare_build +run_single_includes_tests build_nvim if [ "$CLANG_SANITIZER" != "TSAN" ]; then diff --git a/Makefile b/Makefile index fed84582a9..47fb1e5edd 100644 --- a/Makefile +++ b/Makefile @@ -134,4 +134,7 @@ clint: lint: clint testlint +check-single-includes: build/.ran-cmake + +$(BUILD_CMD) -C build check-single-includes + .PHONY: test testlint functionaltest unittest lint clint clean distclean nvim libnvim cmake deps install diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 22cf1f3a3d..8af2dcf5f6 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -48,6 +48,7 @@ file(MAKE_DIRECTORY ${GENERATED_DIR}) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}) file(GLOB NEOVIM_SOURCES *.c) +file(GLOB NEOVIM_HEADERS *.h) foreach(subdir os @@ -65,10 +66,11 @@ foreach(subdir file(MAKE_DIRECTORY ${GENERATED_DIR}/${subdir}) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/${subdir}) file(GLOB sources ${subdir}/*.c) + file(GLOB headers ${subdir}/*.h) list(APPEND NEOVIM_SOURCES ${sources}) + list(APPEND NEOVIM_HEADERS ${headers}) endforeach() -file(GLOB_RECURSE NEOVIM_HEADERS *.h) file(GLOB UNIT_TEST_FIXTURES ${PROJECT_SOURCE_DIR}/test/unit/fixtures/*.c) # Sort file lists to ensure generated files are created in the same order from @@ -152,6 +154,19 @@ separate_arguments(C_FLAGS_ARRAY UNIX_COMMAND ${CMAKE_C_FLAGS}) separate_arguments(C_FLAGS_${build_type}_ARRAY UNIX_COMMAND ${CMAKE_C_FLAGS_${build_type}}) set(gen_cflags ${gen_cflags} ${C_FLAGS_${build_type}_ARRAY} ${C_FLAGS_ARRAY}) +function(get_preproc_output varname iname) + if(MSVC) + set(${varname} /P /Fi${iname} PARENT_SCOPE) + else() + set(${varname} -E -o ${iname} PARENT_SCOPE) + endif() +endfunction() + +# NEOVIM_GENERATED_FOR_HEADERS: header files generated to be included in headers +# NEOVIM_GENERATED_FOR_SOURCES: header files generated to be included in sources +# NEOVIM_GENERATED_SOURCES: generated source files +# These lists should be mutually exclusive + foreach(sfile ${NEOVIM_SOURCES} "${PROJECT_SOURCE_DIR}/src/nvim/regexp_nfa.c" ${GENERATED_API_DISPATCH}) @@ -166,26 +181,22 @@ foreach(sfile ${NEOVIM_SOURCES} set(f "${d}/${f}") set(r "${d}/${r}") endif() - set(gf1 "${GENERATED_DIR}/${r}.c.generated.h") - set(gf2 "${GENERATED_INCLUDES_DIR}/${r}.h.generated.h") - set(gf3 "${GENERATED_DIR}/${r}.i") + set(gf_c_h "${GENERATED_DIR}/${r}.c.generated.h") + set(gf_h_h "${GENERATED_INCLUDES_DIR}/${r}.h.generated.h") + set(gf_i "${GENERATED_DIR}/${r}.i") - if(MSVC) - set(PREPROC_OUTPUT /P /Fi${gf3}) - else() - set(PREPROC_OUTPUT -E -o ${gf3}) - endif() + get_preproc_output(PREPROC_OUTPUT ${gf_i}) add_custom_command( - OUTPUT "${gf1}" "${gf2}" + OUTPUT "${gf_c_h}" "${gf_h_h}" COMMAND ${CMAKE_C_COMPILER} ${sfile} ${PREPROC_OUTPUT} ${gen_cflags} ${C_FLAGS_ARRAY} - COMMAND "${LUA_PRG}" "${HEADER_GENERATOR}" "${sfile}" "${gf1}" "${gf2}" "${gf3}" + COMMAND "${LUA_PRG}" "${HEADER_GENERATOR}" "${sfile}" "${gf_c_h}" "${gf_h_h}" "${gf_i}" DEPENDS "${HEADER_GENERATOR}" "${sfile}" ) - list(APPEND NEOVIM_GENERATED_SOURCES "${gf1}") - list(APPEND NEOVIM_GENERATED_SOURCES "${gf2}") + list(APPEND NEOVIM_GENERATED_FOR_SOURCES "${gf_c_h}") + list(APPEND NEOVIM_GENERATED_FOR_HEADERS "${gf_h_h}") if(${d} MATCHES "^api$" AND NOT ${f} MATCHES "^api/helpers.c$") - list(APPEND API_HEADERS ${gf2}) + list(APPEND API_HEADERS ${gf_h_h}) endif() endforeach() @@ -210,17 +221,23 @@ add_custom_command(OUTPUT ${GENERATED_API_DISPATCH} ${GENERATED_FUNCS_METADATA} ${CMAKE_CURRENT_LIST_DIR}/api/dispatch_deprecated.lua ) -list(APPEND NEOVIM_GENERATED_SOURCES - "${PROJECT_BINARY_DIR}/config/auto/pathdef.c" - "${GENERATED_API_DISPATCH}" +list(APPEND NEOVIM_GENERATED_FOR_HEADERS "${GENERATED_EX_CMDS_ENUM}" - "${GENERATED_EX_CMDS_DEFS}" "${GENERATED_EVENTS_ENUM}" +) + +list(APPEND NEOVIM_GENERATED_FOR_SOURCES + "${GENERATED_API_DISPATCH}" + "${GENERATED_EX_CMDS_DEFS}" "${GENERATED_EVENTS_NAMES_MAP}" "${GENERATED_OPTIONS}" "${GENERATED_UNICODE_TABLES}" ) +list(APPEND NEOVIM_GENERATED_SOURCES + "${PROJECT_BINARY_DIR}/config/auto/pathdef.c" +) + add_custom_command(OUTPUT ${GENERATED_EX_CMDS_ENUM} ${GENERATED_EX_CMDS_DEFS} COMMAND ${LUA_PRG} ${EX_CMDS_GENERATOR} ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_INCLUDES_DIR} ${GENERATED_DIR} @@ -237,7 +254,7 @@ add_custom_command(OUTPUT ${GENERATED_FUNCS} ${FUNCS_DATA} ${GENERATED_FUNCS_HASH_INPUT} --output-file=${GENERATED_FUNCS} DEPENDS ${FUNCS_GENERATOR} ${EVAL_DEFS_FILE} ${API_METADATA} ) -list(APPEND NEOVIM_GENERATED_SOURCES +list(APPEND NEOVIM_GENERATED_FOR_SOURCES "${GENERATED_FUNCS}") add_custom_command(OUTPUT ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP} @@ -252,6 +269,15 @@ add_custom_command(OUTPUT ${GENERATED_OPTIONS} DEPENDS ${OPTIONS_GENERATOR} ${OPTIONS_LIST_FILE} ) +# Check that NEOVIM_GENERATED_FOR_SOURCES and NEOVIM_GENERATED_FOR_HEADERS are mutually exclusive + +foreach(hfile ${NEOVIM_GENERATED_FOR_HEADERS}) + list(FIND NEOVIM_GENERATED_FOR_SOURCES ${hfile} hfile_idx) + if(NOT ${hfile_idx} EQUAL -1) + message(FATAL_ERROR "File included in both NEOVIM_GENERATED_FOR_HEADERS and NEOVIM_GENERATED_FOR_SOURCES") + endif() +endforeach() + # Our dependencies come first. if (LibIntl_FOUND) @@ -286,8 +312,8 @@ if(JEMALLOC_FOUND) list(APPEND NVIM_EXEC_LINK_LIBRARIES ${JEMALLOC_LIBRARIES}) endif() -add_executable(nvim ${NEOVIM_GENERATED_SOURCES} ${NEOVIM_SOURCES} - ${NEOVIM_HEADERS}) +add_executable(nvim ${NEOVIM_GENERATED_FOR_SOURCES} ${NEOVIM_GENERATED_FOR_HEADERS} + ${NEOVIM_GENERATED_SOURCES} ${NEOVIM_SOURCES} ${NEOVIM_HEADERS}) target_link_libraries(nvim ${NVIM_EXEC_LINK_LIBRARIES}) install_helper(TARGETS nvim) @@ -361,17 +387,97 @@ elseif(CLANG_TSAN) set_property(TARGET nvim APPEND_STRING PROPERTY LINK_FLAGS "-fsanitize=thread ") endif() -add_library(libnvim STATIC EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_SOURCES} - ${NEOVIM_SOURCES} ${NEOVIM_HEADERS}) +add_library(libnvim STATIC EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_FOR_SOURCES} ${NEOVIM_GENERATED_FOR_HEADERS} + ${NEOVIM_GENERATED_SOURCES} ${NEOVIM_SOURCES} ${NEOVIM_HEADERS}) target_link_libraries(libnvim ${NVIM_LINK_LIBRARIES}) set_target_properties(libnvim PROPERTIES POSITION_INDEPENDENT_CODE ON OUTPUT_NAME nvim) set_property(TARGET libnvim APPEND_STRING PROPERTY COMPILE_FLAGS " -DMAKE_LIB ") -add_library(nvim-test MODULE EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_SOURCES} - ${NEOVIM_SOURCES} ${UNIT_TEST_FIXTURES} ${NEOVIM_HEADERS}) +add_library(nvim-test MODULE EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_FOR_SOURCES} ${NEOVIM_GENERATED_FOR_HEADERS} + ${NEOVIM_GENERATED_SOURCES} ${NEOVIM_SOURCES} ${UNIT_TEST_FIXTURES} ${NEOVIM_HEADERS}) target_link_libraries(nvim-test ${NVIM_LINK_LIBRARIES}) set_property(TARGET nvim-test APPEND_STRING PROPERTY COMPILE_FLAGS -DUNIT_TESTING) +set(NO_SINGLE_CHECK_HEADERS + buffer + charset + cursor_shape + diff + digraph + ex_cmds + ex_getln + file_search + fold + getchar + hardcopy + if_cscope + if_cscope_defs + mark + mbyte + memfile_defs + memline + memline_defs + menu + misc2 + move + msgpack_rpc/server + ops + option + os/shell + os_unix + os/win_defs + popupmnu + quickfix + regexp + regexp_defs + screen + search + sha256 + sign_defs + spell + syntax + syntax_defs + tag + terminal + tui/tui + ugrid + ui + ui_bridge + undo + undo_defs + version + window +) +foreach(hfile ${NEOVIM_HEADERS}) + get_filename_component(full_d ${hfile} PATH) + file(RELATIVE_PATH d "${PROJECT_SOURCE_DIR}/src/nvim" "${full_d}") + if(${d} MATCHES "^[.][.]") + file(RELATIVE_PATH d "${GENERATED_DIR}" "${full_d}") + endif() + get_filename_component(r ${hfile} NAME_WE) + if(NOT ${d} EQUAL ".") + set(r "${d}/${r}") + endif() + + if(NOT ${hfile} MATCHES "[.]c[.]h$") + set(tsource "${GENERATED_DIR}/${r}.test-include.c") + set(tresult "${GENERATED_DIR}/${r}.test-include.i") + string(REPLACE "/" "-" texe "${r}-test") + write_file("${tsource}" "#include \"${hfile}\"\nint main(int argc, char **argv) { return 0; }") + get_preproc_output(PREPROC_OUTPUT ${tresult}) + add_executable( + ${texe} + EXCLUDE_FROM_ALL + ${tsource} ${NEOVIM_HEADERS} ${NEOVIM_GENERATED_FOR_HEADERS}) + + list(FIND NO_SINGLE_CHECK_HEADERS "${r}" hfile_exclude_idx) + if(${hfile_exclude_idx} EQUAL -1) + list(APPEND HEADER_CHECK_TARGETS ${texe}) + endif() + endif() +endforeach() +add_custom_target(check-single-includes DEPENDS ${HEADER_CHECK_TARGETS}) + add_subdirectory(po) diff --git a/src/nvim/eval_defs.h b/src/nvim/eval_defs.h index 39028fdb11..8f5e1a897d 100644 --- a/src/nvim/eval_defs.h +++ b/src/nvim/eval_defs.h @@ -3,6 +3,7 @@ #include #include +#include #include "nvim/hashtab.h" #include "nvim/lib/queue.h" diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 486baaad47..d1557f9c82 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -67,6 +67,7 @@ #include "nvim/event/rstream.h" #include "nvim/event/wstream.h" #include "nvim/shada.h" +#include "nvim/globals.h" static int quitmore = 0; static int ex_pressedreturn = FALSE; diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h index 4def4cbbae..cff350de08 100644 --- a/src/nvim/ex_docmd.h +++ b/src/nvim/ex_docmd.h @@ -2,6 +2,7 @@ #define NVIM_EX_DOCMD_H #include "nvim/ex_cmds_defs.h" +#include "nvim/globals.h" // flags for do_cmdline() #define DOCMD_VERBOSE 0x01 // included command in error message diff --git a/src/nvim/mark.h b/src/nvim/mark.h index aff6e7273a..efba9708db 100644 --- a/src/nvim/mark.h +++ b/src/nvim/mark.h @@ -29,7 +29,7 @@ /// Clear given fmark #define CLEAR_FMARK(fmarkp_) \ - RESET_FMARK(fmarkp_, ((pos_T) {0, 0, 0}), 0) + RESET_FMARK(fmarkp_, ((pos_T) { 0, 0, 0 }), 0) /// Set given extended mark (regular mark + file name) #define SET_XFMARK(xfmarkp_, mark_, fnum_, fname_) \ diff --git a/src/nvim/os/fs_defs.h b/src/nvim/os/fs_defs.h index 0bd9c37750..2277d926b3 100644 --- a/src/nvim/os/fs_defs.h +++ b/src/nvim/os/fs_defs.h @@ -14,7 +14,7 @@ typedef struct { uint64_t device_id; ///< @private The id of the device containing the file } FileID; -#define FILE_ID_EMPTY (FileID) {.inode = 0, .device_id = 0} +#define FILE_ID_EMPTY (FileID) { .inode = 0, .device_id = 0 } typedef struct { uv_fs_t request; ///< @private The request to uv for the directory. diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h index 14c210c69c..f81785675e 100644 --- a/src/nvim/os/os_defs.h +++ b/src/nvim/os/os_defs.h @@ -27,11 +27,11 @@ // Use up to 5 Mbyte for a buffer. #ifndef DFLT_MAXMEM -# define DFLT_MAXMEM (5*1024) +# define DFLT_MAXMEM (5 * 1024) #endif // use up to 10 Mbyte for Vim. #ifndef DFLT_MAXMEMTOT -# define DFLT_MAXMEMTOT (10*1024) +# define DFLT_MAXMEMTOT (10 * 1024) #endif // Note: Some systems need both string.h and strings.h (Savage). However, diff --git a/src/nvim/os/unix_defs.h b/src/nvim/os/unix_defs.h index c98aa88bfa..5c9daca476 100644 --- a/src/nvim/os/unix_defs.h +++ b/src/nvim/os/unix_defs.h @@ -8,7 +8,7 @@ // POSIX.1-2008 says that NAME_MAX should be in here #include -#define TEMP_DIR_NAMES {"$TMPDIR", "/tmp", ".", "~"} +#define TEMP_DIR_NAMES { "$TMPDIR", "/tmp", ".", "~" } #define TEMP_FILE_PATH_MAXLEN 256 #define HAVE_ACL (HAVE_POSIX_ACL || HAVE_SOLARIS_ACL) diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h index 8de896c490..827fb2f247 100644 --- a/src/nvim/os/win_defs.h +++ b/src/nvim/os/win_defs.h @@ -1,6 +1,10 @@ #ifndef NVIM_OS_WIN_DEFS_H #define NVIM_OS_WIN_DEFS_H +#ifndef WIN32 +# error Header must be included only when compiling for Windows. +#endif + // winsock2.h must be first to avoid incompatibilities // with winsock.h (included by windows.h) #include @@ -15,7 +19,7 @@ #define NAME_MAX _MAX_PATH -#define TEMP_DIR_NAMES {"$TMP", "$TEMP", "$USERPROFILE", ""} +#define TEMP_DIR_NAMES { "$TMP", "$TEMP", "$USERPROFILE", "" } #define TEMP_FILE_PATH_MAXLEN _MAX_PATH #define FNAME_ILLEGAL "\"*?><|" diff --git a/src/nvim/strings.h b/src/nvim/strings.h index eb8b83c7d0..8aea374b96 100644 --- a/src/nvim/strings.h +++ b/src/nvim/strings.h @@ -1,9 +1,8 @@ #ifndef NVIM_STRINGS_H #define NVIM_STRINGS_H -#include #include -#include +#include #include "nvim/types.h" #include "nvim/eval_defs.h" From e20e9645b2c74b8cf6e1ba90961c08b079bbee3c Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 27 Mar 2017 14:12:23 +0200 Subject: [PATCH 0167/1671] build: Rename NEOVIM_* to NVIM_* --- src/nvim/CMakeLists.txt | 64 ++++++++++++++++++-------------------- src/nvim/po/CMakeLists.txt | 10 +++--- test/unit/os/env_spec.lua | 30 +++++++++--------- third-party/CMakeLists.txt | 2 +- 4 files changed, 52 insertions(+), 54 deletions(-) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 8af2dcf5f6..877b403463 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -47,8 +47,8 @@ include_directories(${GENERATED_INCLUDES_DIR}) file(MAKE_DIRECTORY ${GENERATED_DIR}) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}) -file(GLOB NEOVIM_SOURCES *.c) -file(GLOB NEOVIM_HEADERS *.h) +file(GLOB NVIM_SOURCES *.c) +file(GLOB NVIM_HEADERS *.h) foreach(subdir os @@ -67,18 +67,18 @@ foreach(subdir file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/${subdir}) file(GLOB sources ${subdir}/*.c) file(GLOB headers ${subdir}/*.h) - list(APPEND NEOVIM_SOURCES ${sources}) - list(APPEND NEOVIM_HEADERS ${headers}) + list(APPEND NVIM_SOURCES ${sources}) + list(APPEND NVIM_HEADERS ${headers}) endforeach() file(GLOB UNIT_TEST_FIXTURES ${PROJECT_SOURCE_DIR}/test/unit/fixtures/*.c) # Sort file lists to ensure generated files are created in the same order from # build to build. -list(SORT NEOVIM_SOURCES) -list(SORT NEOVIM_HEADERS) +list(SORT NVIM_SOURCES) +list(SORT NVIM_HEADERS) -foreach(sfile ${NEOVIM_SOURCES}) +foreach(sfile ${NVIM_SOURCES}) get_filename_component(f ${sfile} NAME) if(${f} MATCHES "^(regexp_nfa.c)$") list(APPEND to_remove ${sfile}) @@ -88,7 +88,7 @@ foreach(sfile ${NEOVIM_SOURCES}) endif() endforeach() -list(REMOVE_ITEM NEOVIM_SOURCES ${to_remove}) +list(REMOVE_ITEM NVIM_SOURCES ${to_remove}) # Legacy files that do not yet pass -Wconversion. set(CONV_SOURCES @@ -162,12 +162,11 @@ function(get_preproc_output varname iname) endif() endfunction() -# NEOVIM_GENERATED_FOR_HEADERS: header files generated to be included in headers -# NEOVIM_GENERATED_FOR_SOURCES: header files generated to be included in sources -# NEOVIM_GENERATED_SOURCES: generated source files -# These lists should be mutually exclusive - -foreach(sfile ${NEOVIM_SOURCES} +# NVIM_GENERATED_FOR_HEADERS: generated headers to be included in headers +# NVIM_GENERATED_FOR_SOURCES: generated headers to be included in sources +# NVIM_GENERATED_SOURCES: generated source files +# These lists must be mutually exclusive. +foreach(sfile ${NVIM_SOURCES} "${PROJECT_SOURCE_DIR}/src/nvim/regexp_nfa.c" ${GENERATED_API_DISPATCH}) get_filename_component(full_d ${sfile} PATH) @@ -193,8 +192,8 @@ foreach(sfile ${NEOVIM_SOURCES} COMMAND "${LUA_PRG}" "${HEADER_GENERATOR}" "${sfile}" "${gf_c_h}" "${gf_h_h}" "${gf_i}" DEPENDS "${HEADER_GENERATOR}" "${sfile}" ) - list(APPEND NEOVIM_GENERATED_FOR_SOURCES "${gf_c_h}") - list(APPEND NEOVIM_GENERATED_FOR_HEADERS "${gf_h_h}") + list(APPEND NVIM_GENERATED_FOR_SOURCES "${gf_c_h}") + list(APPEND NVIM_GENERATED_FOR_HEADERS "${gf_h_h}") if(${d} MATCHES "^api$" AND NOT ${f} MATCHES "^api/helpers.c$") list(APPEND API_HEADERS ${gf_h_h}) endif() @@ -221,12 +220,12 @@ add_custom_command(OUTPUT ${GENERATED_API_DISPATCH} ${GENERATED_FUNCS_METADATA} ${CMAKE_CURRENT_LIST_DIR}/api/dispatch_deprecated.lua ) -list(APPEND NEOVIM_GENERATED_FOR_HEADERS +list(APPEND NVIM_GENERATED_FOR_HEADERS "${GENERATED_EX_CMDS_ENUM}" "${GENERATED_EVENTS_ENUM}" ) -list(APPEND NEOVIM_GENERATED_FOR_SOURCES +list(APPEND NVIM_GENERATED_FOR_SOURCES "${GENERATED_API_DISPATCH}" "${GENERATED_EX_CMDS_DEFS}" "${GENERATED_EVENTS_NAMES_MAP}" @@ -234,7 +233,7 @@ list(APPEND NEOVIM_GENERATED_FOR_SOURCES "${GENERATED_UNICODE_TABLES}" ) -list(APPEND NEOVIM_GENERATED_SOURCES +list(APPEND NVIM_GENERATED_SOURCES "${PROJECT_BINARY_DIR}/config/auto/pathdef.c" ) @@ -254,7 +253,7 @@ add_custom_command(OUTPUT ${GENERATED_FUNCS} ${FUNCS_DATA} ${GENERATED_FUNCS_HASH_INPUT} --output-file=${GENERATED_FUNCS} DEPENDS ${FUNCS_GENERATOR} ${EVAL_DEFS_FILE} ${API_METADATA} ) -list(APPEND NEOVIM_GENERATED_FOR_SOURCES +list(APPEND NVIM_GENERATED_FOR_SOURCES "${GENERATED_FUNCS}") add_custom_command(OUTPUT ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP} @@ -269,12 +268,11 @@ add_custom_command(OUTPUT ${GENERATED_OPTIONS} DEPENDS ${OPTIONS_GENERATOR} ${OPTIONS_LIST_FILE} ) -# Check that NEOVIM_GENERATED_FOR_SOURCES and NEOVIM_GENERATED_FOR_HEADERS are mutually exclusive - -foreach(hfile ${NEOVIM_GENERATED_FOR_HEADERS}) - list(FIND NEOVIM_GENERATED_FOR_SOURCES ${hfile} hfile_idx) +# NVIM_GENERATED_FOR_SOURCES and NVIM_GENERATED_FOR_HEADERS must be mutually exclusive. +foreach(hfile ${NVIM_GENERATED_FOR_HEADERS}) + list(FIND NVIM_GENERATED_FOR_SOURCES ${hfile} hfile_idx) if(NOT ${hfile_idx} EQUAL -1) - message(FATAL_ERROR "File included in both NEOVIM_GENERATED_FOR_HEADERS and NEOVIM_GENERATED_FOR_SOURCES") + message(FATAL_ERROR "File included in both NVIM_GENERATED_FOR_HEADERS and NVIM_GENERATED_FOR_SOURCES") endif() endforeach() @@ -312,8 +310,8 @@ if(JEMALLOC_FOUND) list(APPEND NVIM_EXEC_LINK_LIBRARIES ${JEMALLOC_LIBRARIES}) endif() -add_executable(nvim ${NEOVIM_GENERATED_FOR_SOURCES} ${NEOVIM_GENERATED_FOR_HEADERS} - ${NEOVIM_GENERATED_SOURCES} ${NEOVIM_SOURCES} ${NEOVIM_HEADERS}) +add_executable(nvim ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} + ${NVIM_GENERATED_SOURCES} ${NVIM_SOURCES} ${NVIM_HEADERS}) target_link_libraries(nvim ${NVIM_EXEC_LINK_LIBRARIES}) install_helper(TARGETS nvim) @@ -387,16 +385,16 @@ elseif(CLANG_TSAN) set_property(TARGET nvim APPEND_STRING PROPERTY LINK_FLAGS "-fsanitize=thread ") endif() -add_library(libnvim STATIC EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_FOR_SOURCES} ${NEOVIM_GENERATED_FOR_HEADERS} - ${NEOVIM_GENERATED_SOURCES} ${NEOVIM_SOURCES} ${NEOVIM_HEADERS}) +add_library(libnvim STATIC EXCLUDE_FROM_ALL ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} + ${NVIM_GENERATED_SOURCES} ${NVIM_SOURCES} ${NVIM_HEADERS}) target_link_libraries(libnvim ${NVIM_LINK_LIBRARIES}) set_target_properties(libnvim PROPERTIES POSITION_INDEPENDENT_CODE ON OUTPUT_NAME nvim) set_property(TARGET libnvim APPEND_STRING PROPERTY COMPILE_FLAGS " -DMAKE_LIB ") -add_library(nvim-test MODULE EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_FOR_SOURCES} ${NEOVIM_GENERATED_FOR_HEADERS} - ${NEOVIM_GENERATED_SOURCES} ${NEOVIM_SOURCES} ${UNIT_TEST_FIXTURES} ${NEOVIM_HEADERS}) +add_library(nvim-test MODULE EXCLUDE_FROM_ALL ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} + ${NVIM_GENERATED_SOURCES} ${NVIM_SOURCES} ${UNIT_TEST_FIXTURES} ${NVIM_HEADERS}) target_link_libraries(nvim-test ${NVIM_LINK_LIBRARIES}) set_property(TARGET nvim-test APPEND_STRING PROPERTY COMPILE_FLAGS -DUNIT_TESTING) @@ -450,7 +448,7 @@ set(NO_SINGLE_CHECK_HEADERS version window ) -foreach(hfile ${NEOVIM_HEADERS}) +foreach(hfile ${NVIM_HEADERS}) get_filename_component(full_d ${hfile} PATH) file(RELATIVE_PATH d "${PROJECT_SOURCE_DIR}/src/nvim" "${full_d}") if(${d} MATCHES "^[.][.]") @@ -470,7 +468,7 @@ foreach(hfile ${NEOVIM_HEADERS}) add_executable( ${texe} EXCLUDE_FROM_ALL - ${tsource} ${NEOVIM_HEADERS} ${NEOVIM_GENERATED_FOR_HEADERS}) + ${tsource} ${NVIM_HEADERS} ${NVIM_GENERATED_FOR_HEADERS}) list(FIND NO_SINGLE_CHECK_HEADERS "${r}" hfile_exclude_idx) if(${hfile_exclude_idx} EQUAL -1) diff --git a/src/nvim/po/CMakeLists.txt b/src/nvim/po/CMakeLists.txt index d2b62f89a0..121f22129a 100644 --- a/src/nvim/po/CMakeLists.txt +++ b/src/nvim/po/CMakeLists.txt @@ -32,10 +32,10 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG) zh_CN.UTF-8 zh_TW.UTF-8) - set(NEOVIM_RELATIVE_SOURCES) - foreach(SRC ${NEOVIM_SOURCES} ${NEOVIM_HEADERS}) + set(NVIM_RELATIVE_SOURCES) + foreach(SRC ${NVIM_SOURCES} ${NVIM_HEADERS}) file(RELATIVE_PATH RELATIVE_SRC ${CMAKE_CURRENT_SOURCE_DIR} ${SRC}) - list(APPEND NEOVIM_RELATIVE_SOURCES ${RELATIVE_SRC}) + list(APPEND NVIM_RELATIVE_SOURCES ${RELATIVE_SRC}) endforeach() set(NVIM_POT ${CMAKE_CURRENT_BINARY_DIR}/nvim.pot) @@ -46,9 +46,9 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG) -DXGETTEXT_PRG=${XGETTEXT_PRG} -DPOT_FILE=${NVIM_POT} -DSEARCH_DIR=${CMAKE_CURRENT_SOURCE_DIR} - "\"-DSOURCES=${NEOVIM_RELATIVE_SOURCES}\"" + "\"-DSOURCES=${NVIM_RELATIVE_SOURCES}\"" -P ${PROJECT_SOURCE_DIR}/cmake/RunXgettext.cmake - DEPENDS ${NEOVIM_SOURCES}) + DEPENDS ${NVIM_SOURCES}) add_custom_target(potfile DEPENDS ${NVIM_POT}) diff --git a/test/unit/os/env_spec.lua b/test/unit/os/env_spec.lua index 1ffed784ff..823b6d6a85 100644 --- a/test/unit/os/env_spec.lua +++ b/test/unit/os/env_spec.lua @@ -35,17 +35,17 @@ describe('env function', function() local OK = 0 itp('sets an env variable and returns OK', function() - local name = 'NEOVIM_UNIT_TEST_SETENV_1N' - local value = 'NEOVIM_UNIT_TEST_SETENV_1V' + local name = 'NVIM_UNIT_TEST_SETENV_1N' + local value = 'NVIM_UNIT_TEST_SETENV_1V' eq(nil, os.getenv(name)) eq(OK, (os_setenv(name, value, 1))) eq(value, os.getenv(name)) end) itp("dosn't overwrite an env variable if overwrite is 0", function() - local name = 'NEOVIM_UNIT_TEST_SETENV_2N' - local value = 'NEOVIM_UNIT_TEST_SETENV_2V' - local value_updated = 'NEOVIM_UNIT_TEST_SETENV_2V_UPDATED' + local name = 'NVIM_UNIT_TEST_SETENV_2N' + local value = 'NVIM_UNIT_TEST_SETENV_2V' + local value_updated = 'NVIM_UNIT_TEST_SETENV_2V_UPDATED' eq(OK, (os_setenv(name, value, 0))) eq(value, os.getenv(name)) eq(OK, (os_setenv(name, value_updated, 0))) @@ -69,8 +69,8 @@ describe('env function', function() describe('os_getenv', function() itp('reads an env variable', function() - local name = 'NEOVIM_UNIT_TEST_GETENV_1N' - local value = 'NEOVIM_UNIT_TEST_GETENV_1V' + local name = 'NVIM_UNIT_TEST_GETENV_1N' + local value = 'NVIM_UNIT_TEST_GETENV_1V' eq(NULL, os_getenv(name)) -- need to use os_setenv, because lua dosn't have a setenv function os_setenv(name, value, 1) @@ -78,7 +78,7 @@ describe('env function', function() end) itp('returns NULL if the env variable is not found', function() - local name = 'NEOVIM_UNIT_TEST_GETENV_NOTFOUND' + local name = 'NVIM_UNIT_TEST_GETENV_NOTFOUND' return eq(NULL, os_getenv(name)) end) end) @@ -97,8 +97,8 @@ describe('env function', function() describe('os_getenvname_at_index', function() itp('returns names of environment variables', function() - local test_name = 'NEOVIM_UNIT_TEST_GETENVNAME_AT_INDEX_1N' - local test_value = 'NEOVIM_UNIT_TEST_GETENVNAME_AT_INDEX_1V' + local test_name = 'NVIM_UNIT_TEST_GETENVNAME_AT_INDEX_1N' + local test_value = 'NVIM_UNIT_TEST_GETENVNAME_AT_INDEX_1V' os_setenv(test_name, test_value, 1) local i = 0 local names = { } @@ -160,16 +160,16 @@ describe('env function', function() describe('expand_env_esc', function() itp('expands environment variables', function() - local name = 'NEOVIM_UNIT_TEST_EXPAND_ENV_ESCN' - local value = 'NEOVIM_UNIT_TEST_EXPAND_ENV_ESCV' + local name = 'NVIM_UNIT_TEST_EXPAND_ENV_ESCN' + local value = 'NVIM_UNIT_TEST_EXPAND_ENV_ESCV' os_setenv(name, value, 1) -- TODO(bobtwinkles) This only tests Unix expansions. There should be a -- test for Windows as well - local input1 = to_cstr('$NEOVIM_UNIT_TEST_EXPAND_ENV_ESCN/test') - local input2 = to_cstr('${NEOVIM_UNIT_TEST_EXPAND_ENV_ESCN}/test') + local input1 = to_cstr('$NVIM_UNIT_TEST_EXPAND_ENV_ESCN/test') + local input2 = to_cstr('${NVIM_UNIT_TEST_EXPAND_ENV_ESCN}/test') local output_buff1 = cstr(255, '') local output_buff2 = cstr(255, '') - local output_expected = 'NEOVIM_UNIT_TEST_EXPAND_ENV_ESCV/test' + local output_expected = 'NVIM_UNIT_TEST_EXPAND_ENV_ESCV/test' cimp.expand_env_esc(input1, output_buff1, 255, false, true, NULL) cimp.expand_env_esc(input2, output_buff2, 255, false, true, NULL) eq(output_expected, ffi.string(output_buff1)) diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index 809a3623e4..a5fd766aa8 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -1,6 +1,6 @@ # This is not meant to be included by the top-level. cmake_minimum_required (VERSION 2.8.7) -project(NEOVIM_DEPS) +project(NVIM_DEPS) # Point CMake at any custom modules we may ship list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") From 88124dfebc1a9c7eeb149beedd52a818058f9c16 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 27 Mar 2017 14:44:14 +0200 Subject: [PATCH 0168/1671] build: Prefix check-single-includes artifacts. The previous naming scheme could conflict with the test fixture artifacts from `test/functional/fixtures/`, which also produce `foo-test` artifacts. --- src/nvim/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 877b403463..3ee4380538 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -435,6 +435,7 @@ set(NO_SINGLE_CHECK_HEADERS sha256 sign_defs spell + spellfile syntax syntax_defs tag @@ -462,7 +463,7 @@ foreach(hfile ${NVIM_HEADERS}) if(NOT ${hfile} MATCHES "[.]c[.]h$") set(tsource "${GENERATED_DIR}/${r}.test-include.c") set(tresult "${GENERATED_DIR}/${r}.test-include.i") - string(REPLACE "/" "-" texe "${r}-test") + string(REPLACE "/" "-" texe "test-incl-${r}") write_file("${tsource}" "#include \"${hfile}\"\nint main(int argc, char **argv) { return 0; }") get_preproc_output(PREPROC_OUTPUT ${tresult}) add_executable( From 595acbbc42e2856d958f518421a8cb56b0501127 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 27 Mar 2017 16:28:05 +0200 Subject: [PATCH 0169/1671] rplugin.vim: GetManifestPath(): be more explicit (#6361) --- runtime/plugin/rplugin.vim | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/runtime/plugin/rplugin.vim b/runtime/plugin/rplugin.vim index 7d83668a30..ca15ec82d1 100644 --- a/runtime/plugin/rplugin.vim +++ b/runtime/plugin/rplugin.vim @@ -17,9 +17,11 @@ function! s:GetManifestPath() abort endif let dest = fnamemodify(expand(dest), ':p') - if !empty(dest) && !filereadable(dest) + if !empty(dest) let dest .= ('/' ==# dest[-1:] ? '' : '/') . 'nvim' - call mkdir(dest, 'p', 0700) + if !isdirectory(dest) + call mkdir(dest, 'p', 0700) + endif let manifest_base = dest endif From 9d200cd0a3ef749509cee3331ba1d0b5fb62e7f2 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 27 Mar 2017 21:04:52 +0200 Subject: [PATCH 0170/1671] getcompletion("cmdline") (#6376) Closes #5823 --- runtime/doc/eval.txt | 3 ++- src/nvim/eval.c | 18 +++++++++++++----- src/nvim/testdir/test_cmdline.vim | 14 ++++++++++++++ 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 341e65d381..9a86e13d95 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -3998,6 +3998,7 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()* augroup autocmd groups buffer buffer names behave :behave suboptions + cmdline |cmdline-completion| color color schemes command Ex command (and arguments) compiler compilers @@ -4026,7 +4027,7 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()* user user names var user variables - If {pat} is an empty string, then all the matches are returned. + If {pat} is an empty string then all matches are returned. Otherwise only items matching {pat} are returned. See |wildcards| for the use of special characters in {pat}. diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d49fcbf17e..927a1dcb0e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -10765,16 +10765,23 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) options |= WILD_KEEP_ALL; } + if (argvars[0].v_type != VAR_STRING || argvars[1].v_type != VAR_STRING) { + EMSG(_(e_invarg)); + return; + } + + if (STRCMP(get_tv_string(&argvars[1]), "cmdline") == 0) { + set_one_cmd_context(&xpc, get_tv_string(&argvars[0])); + xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); + goto theend; + } + ExpandInit(&xpc); xpc.xp_pattern = get_tv_string(&argvars[0]); xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); xpc.xp_context = cmdcomplete_str_to_type(get_tv_string(&argvars[1])); if (xpc.xp_context == EXPAND_NOTHING) { - if (argvars[1].v_type == VAR_STRING) { - EMSG2(_(e_invarg2), argvars[1].vval.v_string); - } else { - EMSG(_(e_invarg)); - } + EMSG2(_(e_invarg2), argvars[1].vval.v_string); return; } @@ -10793,6 +10800,7 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); } +theend: pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context); rettv_list_alloc(rettv); if (pat != NULL) { diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 40db227d97..f56227250c 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -156,6 +156,20 @@ func Test_getcompletion() call assert_equal(['Testing'], l) endif + " Command line completion tests + let l = getcompletion('cd ', 'cmdline') + call assert_true(index(l, 'sautest/') >= 0) + let l = getcompletion('cd NoMatch', 'cmdline') + call assert_equal([], l) + let l = getcompletion('let v:n', 'cmdline') + call assert_true(index(l, 'v:null') >= 0) + let l = getcompletion('let v:notexists', 'cmdline') + call assert_equal([], l) + let l = getcompletion('call tag', 'cmdline') + call assert_true(index(l, 'taglist(') >= 0) + let l = getcompletion('call paint', 'cmdline') + call assert_equal([], l) + " For others test if the name is recognized. let names = ['buffer', 'environment', 'file_in_path', \ 'mapping', 'shellcmd', 'tag', 'tag_listfiles', 'user'] From 6fbcbebae04af8e2f1b64f51049efbe49f771711 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 28 Mar 2017 01:02:42 +0200 Subject: [PATCH 0171/1671] win: health/provider.vim: check with `.exe` extension Also fix `python_multiple` comparison. --- runtime/autoload/health/provider.vim | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index 57dd508f96..9f3f492ef6 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -8,6 +8,11 @@ function! s:trim(s) abort return substitute(a:s, '^\_s*\|\_s*$', '', 'g') endfunction +" Convert '\' to '/'. Collapse '//' and '/./'. +function! s:normalize_path(s) abort + return substitute(substitute(a:s, '\', '/', 'g'), '/\./\|/\+', '/', 'g') +endfunction + " Simple version comparison. function! s:version_cmp(a, b) abort let a = split(a:a, '\.', 0) @@ -208,7 +213,7 @@ endfunction " Check the Python interpreter's usability. function! s:check_bin(bin) abort - if !filereadable(a:bin) + if !filereadable(a:bin) && (!has('win32') || !filereadable(a:bin.'.exe')) call health#report_error(printf('"%s" was not found.', a:bin)) return 0 elseif executable(a:bin) != 1 @@ -287,8 +292,9 @@ function! s:check_python(version) abort if exists('$PATH') for path in split($PATH, has('win32') ? ';' : ':') - let path_bin = path.'/'.pyname - if path_bin != python_bin && index(python_multiple, path_bin) == -1 + let path_bin = s:normalize_path(path.'/'.pyname) + if path_bin != s:normalize_path(python_bin) + \ && index(python_multiple, path_bin) == -1 \ && executable(path_bin) call add(python_multiple, path_bin) endif From b9e7ab1484784903b3233309a402138dc42e7e59 Mon Sep 17 00:00:00 2001 From: Samuel Catherasoo Date: Tue, 28 Mar 2017 19:21:30 -0400 Subject: [PATCH 0172/1671] refactor/single-include: charset.h (#6385) --- src/nvim/CMakeLists.txt | 1 - src/nvim/charset.h | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 3ee4380538..e752f5d4df 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -400,7 +400,6 @@ set_property(TARGET nvim-test APPEND_STRING PROPERTY COMPILE_FLAGS -DUNIT_TESTIN set(NO_SINGLE_CHECK_HEADERS buffer - charset cursor_shape diff digraph diff --git a/src/nvim/charset.h b/src/nvim/charset.h index 78d6f2a76c..8d25b828e5 100644 --- a/src/nvim/charset.h +++ b/src/nvim/charset.h @@ -1,6 +1,10 @@ #ifndef NVIM_CHARSET_H #define NVIM_CHARSET_H +#include "nvim/types.h" +#include "nvim/pos.h" +#include "nvim/buffer_defs.h" + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "charset.h.generated.h" #endif From e86042ae178736d657b5da12adce4479633aa8d5 Mon Sep 17 00:00:00 2001 From: lonerover Date: Wed, 29 Mar 2017 07:30:54 +0800 Subject: [PATCH 0173/1671] vim-patch:7.4.2343 and mark NA patches (#6384) vim-patch:7.4.2343 Problem: Too many old file tests. Solution: Turn several into new style tests. (Yegappan Lakshmanan) https://github.com/vim/vim/commit/53f1673cd909eb1c809c6a9086e3d104a0df9bed --- src/nvim/testdir/Makefile | 3 +++ src/nvim/testdir/test_charsearch.vim | 4 +-- src/nvim/testdir/test_fnameescape.vim | 2 +- src/nvim/testdir/test_gf.vim | 33 ++++++++++++++++++++++++ src/nvim/testdir/test_hlsearch.vim | 34 +++++++++++++++++++++++++ src/nvim/testdir/test_smartindent.vim | 14 ++++++++++ src/nvim/testdir/test_tagjump.vim | 30 ++++++++++++++++++++++ src/nvim/version.c | 6 ++--- test/functional/legacy/arglist_spec.lua | 29 +++++++++++++++++++++ 9 files changed, 149 insertions(+), 6 deletions(-) create mode 100644 src/nvim/testdir/test_gf.vim create mode 100644 src/nvim/testdir/test_hlsearch.vim create mode 100644 src/nvim/testdir/test_smartindent.vim diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index fd557cca51..70a9f2b8c4 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -41,10 +41,12 @@ NEW_TESTS ?= \ test_fnameescape.res \ test_fold.res \ test_glob2regpat.res \ + test_gf.res \ test_gn.res \ test_hardcopy.res \ test_help_tagjump.res \ test_history.res \ + test_hlsearch.res \ test_increment.res \ test_increment_dbcs.res \ test_lambda.res \ @@ -57,6 +59,7 @@ NEW_TESTS ?= \ test_normal.res \ test_quickfix.res \ test_signs.res \ + test_smartindent.res \ test_substitute.res \ test_syntax.res \ test_tabpage.res \ diff --git a/src/nvim/testdir/test_charsearch.vim b/src/nvim/testdir/test_charsearch.vim index 115659a013..8b313b5a35 100644 --- a/src/nvim/testdir/test_charsearch.vim +++ b/src/nvim/testdir/test_charsearch.vim @@ -2,8 +2,8 @@ function! Test_charsearch() enew! call append(0, ['Xabcdefghijkemnopqretuvwxyz', - \ 'Yabcdefghijkemnopqretuvwxyz', - \ 'Zabcdefghijkemnokqretkvwxyz']) + \ 'Yabcdefghijkemnopqretuvwxyz', + \ 'Zabcdefghijkemnokqretkvwxyz']) " check that "fe" and ";" work 1 normal! ylfep;;p,,p diff --git a/src/nvim/testdir/test_fnameescape.vim b/src/nvim/testdir/test_fnameescape.vim index ce2cd3f7a6..cdff0dfbd9 100644 --- a/src/nvim/testdir/test_fnameescape.vim +++ b/src/nvim/testdir/test_fnameescape.vim @@ -6,7 +6,7 @@ function! Test_fnameescape() try exe "w! " . fnameescape(fname) let status = v:true - endtry + endtry call assert_true(status, "Space") call delete(fname) diff --git a/src/nvim/testdir/test_gf.vim b/src/nvim/testdir/test_gf.vim new file mode 100644 index 0000000000..c4aa6f9218 --- /dev/null +++ b/src/nvim/testdir/test_gf.vim @@ -0,0 +1,33 @@ + +" This is a test if a URL is recognized by "gf", with the cursor before and +" after the "://". Also test ":\\". +function! Test_gf_url() + enew! + call append(0, [ + \ "first test for URL://machine.name/tmp/vimtest2a and other text", + \ "second test for URL://machine.name/tmp/vimtest2b. And other text", + \ "third test for URL:\\\\machine.name\\vimtest2c and other text", + \ "fourth test for URL:\\\\machine.name\\tmp\\vimtest2d, and other text" + \ ]) + call cursor(1,1) + call search("^first") + call search("tmp") + call assert_equal("URL://machine.name/tmp/vimtest2a", expand("")) + call search("^second") + call search("URL") + call assert_equal("URL://machine.name/tmp/vimtest2b", expand("")) + if has("ebcdic") + set isf=@,240-249,/,.,-,_,+,,,$,:,~,\ + else + set isf=@,48-57,/,.,-,_,+,,,$,:,~,\ + endif + call search("^third") + call search("name") + call assert_equal("URL:\\\\machine.name\\vimtest2c", expand("")) + call search("^fourth") + call search("URL") + call assert_equal("URL:\\\\machine.name\\tmp\\vimtest2d", expand("")) + + set isf&vim + enew! +endfunction diff --git a/src/nvim/testdir/test_hlsearch.vim b/src/nvim/testdir/test_hlsearch.vim new file mode 100644 index 0000000000..066fdd0250 --- /dev/null +++ b/src/nvim/testdir/test_hlsearch.vim @@ -0,0 +1,34 @@ +" Test for v:hlsearch + +function! Test_hlsearch() + new + call setline(1, repeat(['aaa'], 10)) + set hlsearch nolazyredraw + let r=[] + " redraw is needed to make hlsearch highlight the matches + exe "normal! /aaa\" | redraw + let r1 = screenattr(1, 1) + nohlsearch | redraw + call assert_notequal(r1, screenattr(1,1)) + let v:hlsearch=1 | redraw + call assert_equal(r1, screenattr(1,1)) + let v:hlsearch=0 | redraw + call assert_notequal(r1, screenattr(1,1)) + set hlsearch | redraw + call assert_equal(r1, screenattr(1,1)) + let v:hlsearch=0 | redraw + call assert_notequal(r1, screenattr(1,1)) + exe "normal! n" | redraw + call assert_equal(r1, screenattr(1,1)) + let v:hlsearch=0 | redraw + call assert_notequal(r1, screenattr(1,1)) + exe "normal! /\" | redraw + call assert_equal(r1, screenattr(1,1)) + set nohls + exe "normal! /\" | redraw + call assert_notequal(r1, screenattr(1,1)) + call assert_fails('let v:hlsearch=[]', 'E745') + call garbagecollect(1) + call getchar(1) + enew! +endfunction diff --git a/src/nvim/testdir/test_smartindent.vim b/src/nvim/testdir/test_smartindent.vim new file mode 100644 index 0000000000..d00eac9798 --- /dev/null +++ b/src/nvim/testdir/test_smartindent.vim @@ -0,0 +1,14 @@ + +" Tests for not doing smart indenting when it isn't set. +function! Test_nosmartindent() + new + call append(0, [" some test text", + \ " test text", + \ "test text", + \ " test text"]) + set nocindent nosmartindent autoindent + exe "normal! gg/some\" + exe "normal! 2cc#test\" + call assert_equal(" #test", getline(1)) + enew! | close +endfunction diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim index 2044c23f79..0d697b3f3e 100644 --- a/src/nvim/testdir/test_tagjump.vim +++ b/src/nvim/testdir/test_tagjump.vim @@ -65,4 +65,34 @@ func Test_duplicate_tagjump() call delete('Xfile1') endfunc +" Tests for [ CTRL-I and CTRL-W CTRL-I commands +function Test_keyword_jump() + call writefile(["#include Xinclude", "", + \ "", + \ "/* test text test tex start here", + \ " some text", + \ " test text", + \ " start OK if found this line", + \ " start found wrong line", + \ "test text"], 'Xtestfile') + call writefile(["/* test text test tex start here", + \ " some text", + \ " test text", + \ " start OK if found this line", + \ " start found wrong line", + \ "test text"], 'Xinclude') + new Xtestfile + call cursor(1,1) + call search("start") + exe "normal! 5[\" + call assert_equal(" start OK if found this line", getline('.')) + call cursor(1,1) + call search("start") + exe "normal! 5\\" + call assert_equal(" start OK if found this line", getline('.')) + enew! | only + call delete('Xtestfile') + call delete('Xinclude') +endfunction + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/version.c b/src/nvim/version.c index 500e8984c0..dd583f6ffd 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -74,10 +74,10 @@ static char *features[] = { // clang-format off static int included_patches[] = { - // 2367, + // 2367,NA // 2366 NA // 2365 NA - // 2364, + // 2364,NA // 2363 NA 2362, // 2361 NA @@ -98,7 +98,7 @@ static int included_patches[] = { 2346, // 2345 NA // 2344 NA - // 2343, + 2343, // 2342 NA 2341, // 2340 NA diff --git a/test/functional/legacy/arglist_spec.lua b/test/functional/legacy/arglist_spec.lua index f5e3522972..b9075620dc 100644 --- a/test/functional/legacy/arglist_spec.lua +++ b/test/functional/legacy/arglist_spec.lua @@ -269,4 +269,33 @@ describe('argument list commands', function() eq(0, eval('argidx()')) execute('%argd') end) + + + it('test for autocommand that redefines the argument list, when doing ":all"', function() + execute('autocmd BufReadPost Xxx2 next Xxx2 Xxx1') + execute("call writefile(['test file Xxx1'], 'Xxx1')") + execute("call writefile(['test file Xxx2'], 'Xxx2')") + execute("call writefile(['test file Xxx3'], 'Xxx3')") + + execute('new') + -- redefine arglist; go to Xxx1 + execute('next! Xxx1 Xxx2 Xxx3') + -- open window for all args + execute('all') + eq('test file Xxx1', eval('getline(1)')) + execute('wincmd w') + execute('wincmd w') + eq('test file Xxx1', eval('getline(1)')) + -- should now be in Xxx2 + execute('rewind') + eq('test file Xxx2', eval('getline(1)')) + + execute('autocmd! BufReadPost Xxx2') + execute('enew! | only') + execute("call delete('Xxx1')") + execute("call delete('Xxx2')") + execute("call delete('Xxx3')") + execute('argdelete Xxx*') + execute('bwipe! Xxx1 Xxx2 Xxx3') + end) end) From 18e7d552008b92dd3ecd42bf6855530838fd22ab Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 29 Mar 2017 02:13:50 +0200 Subject: [PATCH 0174/1671] terminal.c:redraw(): Avoid invalid cursor col (#6379) Removed the call to validate_cursor() because mb_check_adjust_col() is already called in adjust_topline(). Closes #6378 References #6203 https://s3.amazonaws.com/archive.travis-ci.org/jobs/215498258/log.txt [ ERROR ] ...ovim/neovim/test/functional/terminal/scrollback_spec.lua @ 386: 'scrollback' option set to 0 behaves as 1 (10621.17 ms) ==================== File /home/travis/build/neovim/neovim/build/log/ubsan.12836 ==================== = ================================================================= = ==12836==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62100002cd00 at pc 0x000000eafe90 bp 0x7ffc8661fe50 sp 0x7ffc8661fe48 = READ of size 1 at 0x62100002cd00 thread T0 = #0 0xeafe8f in utf_head_off /home/travis/build/neovim/neovim/src/nvim/mbyte.c:1457:7 = #1 0x6b890e in getvcol /home/travis/build/neovim/neovim/src/nvim/charset.c:1169:15 = #2 0x6bc777 in getvvcol /home/travis/build/neovim/neovim/src/nvim/charset.c:1336:5 = #3 0xfc067b in curs_columns /home/travis/build/neovim/neovim/src/nvim/move.c:730:5 = #4 0xfbc8db in validate_cursor /home/travis/build/neovim/neovim/src/nvim/move.c:510:5 = #5 0x14479ed in setcursor /home/travis/build/neovim/neovim/src/nvim/screen.c:6363:5 = #6 0x17fe054 in redraw /home/travis/build/neovim/neovim/src/nvim/terminal.c:1175:5 = #7 0x17f95e4 in terminal_enter /home/travis/build/neovim/neovim/src/nvim/terminal.c:392:3 = #8 0x70eb2b in edit /home/travis/build/neovim/neovim/src/nvim/edit.c:1300:7 = #9 0x11097d1 in normal_finish_command /home/travis/build/neovim/neovim/src/nvim/normal.c:947:13 = #10 0x1081191 in normal_execute /home/travis/build/neovim/neovim/src/nvim/normal.c:1138:3 = #11 0x170b813 in state_enter /home/travis/build/neovim/neovim/src/nvim/state.c:58:26 = #12 0x103631b in normal_enter /home/travis/build/neovim/neovim/src/nvim/normal.c:464:3 = #13 0xdfb7a8 in main /home/travis/build/neovim/neovim/src/nvim/main.c:552:3 = #14 0x2b8a3c85bf44 in __libc_start_main /build/eglibc-MjiXCM/eglibc-2.19/csu/libc-start.c:287 = #15 0x447b25 in _start (/home/travis/build/neovim/neovim/build/bin/nvim+0x447b25) = = 0x62100002cd00 is located 0 bytes to the right of 4096-byte region [0x62100002bd00,0x62100002cd00) = allocated by thread T0 here: = #0 0x4f1e98 in malloc (/home/travis/build/neovim/neovim/build/bin/nvim+0x4f1e98) = #1 0xf28774 in try_malloc /home/travis/build/neovim/neovim/src/nvim/memory.c:84:15 = #2 0xf28934 in xmalloc /home/travis/build/neovim/neovim/src/nvim/memory.c:118:15 = #3 0xec7be8 in mf_alloc_bhdr /home/travis/build/neovim/neovim/src/nvim/memfile.c:646:17 = #4 0xec58d4 in mf_new /home/travis/build/neovim/neovim/src/nvim/memfile.c:297:12 = #5 0xeda8a8 in ml_new_data /home/travis/build/neovim/neovim/src/nvim/memline.c:2697:16 = #6 0xed7beb in ml_open /home/travis/build/neovim/neovim/src/nvim/memline.c:349:8 = #7 0x643fcd in open_buffer /home/travis/build/neovim/neovim/src/nvim/buffer.c:109:7 = #8 0xa7038c in do_ecmd /home/travis/build/neovim/neovim/src/nvim/ex_cmds.c:2483:24 = #9 0xb5bb49 in do_exedit /home/travis/build/neovim/neovim/src/nvim/ex_docmd.c:6839:9 = #10 0xb7b6d8 in ex_edit /home/travis/build/neovim/neovim/src/nvim/ex_docmd.c:6767:3 = #11 0xb2a598 in do_one_cmd /home/travis/build/neovim/neovim/src/nvim/ex_docmd.c:2208:5 = #12 0xb08f47 in do_cmdline /home/travis/build/neovim/neovim/src/nvim/ex_docmd.c:602:20 = #13 0x109997b in nv_colon /home/travis/build/neovim/neovim/src/nvim/normal.c:4492:18 = #14 0x1081188 in normal_execute /home/travis/build/neovim/neovim/src/nvim/normal.c:1135:3 = #15 0x170b813 in state_enter /home/travis/build/neovim/neovim/src/nvim/state.c:58:26 = #16 0x103631b in normal_enter /home/travis/build/neovim/neovim/src/nvim/normal.c:464:3 = #17 0xdfb7a8 in main /home/travis/build/neovim/neovim/src/nvim/main.c:552:3 = #18 0x2b8a3c85bf44 in __libc_start_main /build/eglibc-MjiXCM/eglibc-2.19/csu/libc-start.c:287 = = SUMMARY: AddressSanitizer: heap-buffer-overflow /home/travis/build/neovim/neovim/src/nvim/mbyte.c:1457:7 in utf_head_off = Shadow bytes around the buggy address: = 0x0c427fffd950: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 = 0x0c427fffd960: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 = 0x0c427fffd970: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 = 0x0c427fffd980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 = 0x0c427fffd990: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 = =>0x0c427fffd9a0:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa = 0x0c427fffd9b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa = 0x0c427fffd9c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa = 0x0c427fffd9d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa = 0x0c427fffd9e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa = 0x0c427fffd9f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa = Shadow byte legend (one shadow byte represents 8 application bytes): = Addressable: 00 = Partially addressable: 01 02 03 04 05 06 07 = Heap left redzone: fa = Heap right redzone: fb = Freed heap region: fd = Stack left redzone: f1 = Stack mid redzone: f2 = Stack right redzone: f3 = Stack partial redzone: f4 = Stack after return: f5 = Stack use after scope: f8 = Global redzone: f9 = Global init order: f6 = Poisoned by user: f7 = Container overflow: fc = Array cookie: ac = Intra object redzone: bb = ASan internal: fe = Left alloca redzone: ca = Right alloca redzone: cb = ==12836==ABORTING ===================================================================================================== ./test/helpers.lua:82: assertion failed! stack traceback: ./test/helpers.lua:82: in function 'check_logs' ./test/functional/helpers.lua:643: in function <./test/functional/helpers.lua:642> --- src/nvim/terminal.c | 3 +-- test/functional/terminal/cursor_spec.lua | 17 +++++++++-------- test/functional/terminal/tui_spec.lua | 1 + 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index dc418e25fa..c81e883fb9 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -1155,7 +1155,6 @@ static void redraw(bool restore_cursor) save_col = ui_current_col(); } block_autocmds(); - validate_cursor(); if (must_redraw) { update_screen(0); @@ -1172,7 +1171,7 @@ static void redraw(bool restore_cursor) int off = is_focused(term) ? 0 : (curwin->w_p_rl ? 1 : -1); curwin->w_cursor.col = MAX(0, term->cursor.col + win_col_off(curwin) + off); curwin->w_cursor.coladd = 0; - setcursor(); + mb_check_adjust_col(curwin); } unblock_autocmds(); diff --git a/test/functional/terminal/cursor_spec.lua b/test/functional/terminal/cursor_spec.lua index d990f92c3a..84f14585fa 100644 --- a/test/functional/terminal/cursor_spec.lua +++ b/test/functional/terminal/cursor_spec.lua @@ -60,16 +60,17 @@ describe('terminal cursor', function() ]]) end) - pending('is positioned correctly when focused', function() + it('is positioned correctly when focused', function() feed('i') + helpers.wait() screen:expect([[ - 1 tty ready | - 2 {1: } | - 3 | - 4 | - 5 | - 6 | - -- TERMINAL -- | + {7: 1 }tty ready | + {7: 2 }{1: } | + {7: 3 } | + {7: 4 } | + {7: 5 } | + {7: 6 } | + {3:-- TERMINAL --} | ]]) end) end) diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 0e5c437c28..90051b8cd5 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -322,6 +322,7 @@ describe("tui 't_Co' (terminal colors)", function() helpers.nvim_prog)) thelpers.feed_data(":echo &t_Co\n") + helpers.wait() local tline if maxcolors == 8 then tline = "~ " From fb146e80aa1ead96518f38b9684e39249bc83485 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 26 Jul 2016 23:16:23 +0300 Subject: [PATCH 0175/1671] eval: Split eval.c into smaller files --- src/clint.py | 7 +- src/nvim/api/private/helpers.c | 17 +- src/nvim/api/vim.c | 5 +- src/nvim/buffer.c | 2 +- src/nvim/buffer_defs.h | 45 +- src/nvim/charset.c | 26 +- src/nvim/cursor.c | 15 +- src/nvim/edit.c | 13 +- src/nvim/eval.c | 2919 ++++++++--------------- src/nvim/eval/decode.c | 98 +- src/nvim/eval/decode.h | 2 +- src/nvim/eval/encode.c | 9 +- src/nvim/eval/executor.c | 114 + src/nvim/eval/executor.h | 9 + src/nvim/eval/gc.c | 11 + src/nvim/eval/gc.h | 12 + src/nvim/eval/typval.c | 1171 +++++++++ src/nvim/{eval_defs.h => eval/typval.h} | 189 +- src/nvim/eval/typval_encode.c.h | 2 +- src/nvim/eval/typval_encode.h | 2 +- src/nvim/ex_cmds.c | 2 +- src/nvim/ex_cmds.h | 2 +- src/nvim/ex_cmds2.c | 24 +- src/nvim/ex_docmd.c | 4 +- src/nvim/ex_eval.c | 14 +- src/nvim/ex_getln.c | 6 +- src/nvim/ex_getln.h | 2 +- src/nvim/globals.h | 16 +- src/nvim/main.c | 7 +- src/nvim/mark.c | 23 + src/nvim/mark_defs.h | 2 +- src/nvim/mbyte.c | 41 +- src/nvim/mbyte.h | 23 + src/nvim/memory.c | 13 + src/nvim/normal.c | 14 +- src/nvim/ops.c | 69 +- src/nvim/ops.h | 2 +- src/nvim/quickfix.c | 2 +- src/nvim/regexp.c | 51 +- src/nvim/regexp_nfa.c | 4 +- src/nvim/shada.c | 36 +- src/nvim/spell.c | 2 +- src/nvim/strings.c | 2 +- src/nvim/strings.h | 2 +- src/nvim/tag.c | 8 +- src/nvim/undo.c | 4 +- src/nvim/window.c | 15 +- test/unit/eval/decode_spec.lua | 2 +- test/unit/eval/helpers.lua | 16 +- test/unit/eval/tricks_spec.lua | 2 +- test/unit/eval/tv_clear_spec.lua | 12 +- 51 files changed, 2763 insertions(+), 2327 deletions(-) create mode 100644 src/nvim/eval/executor.c create mode 100644 src/nvim/eval/executor.h create mode 100644 src/nvim/eval/gc.c create mode 100644 src/nvim/eval/gc.h create mode 100644 src/nvim/eval/typval.c rename src/nvim/{eval_defs.h => eval/typval.h} (61%) diff --git a/src/clint.py b/src/clint.py index ce31822ada..61c53d128e 100755 --- a/src/clint.py +++ b/src/clint.py @@ -2268,11 +2268,14 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): # //!< Header comment # or they begin with multiple slashes followed by a space: # //////// Header comment + # or they are Vim {{{ fold markers match = (Search(r'[=/-]{4,}\s*$', line[commentend:]) or Search(r'^/$', line[commentend:]) or Search(r'^!< ', line[commentend:]) or Search(r'^/< ', line[commentend:]) or - Search(r'^/+ ', line[commentend:])) + Search(r'^/+ ', line[commentend:]) or + Search(r'^(?:\{{3}|\}{3})\d*(?: |$)', + line[commentend:])) if not match: error(filename, linenum, 'whitespace/comments', 4, 'Should have a space between // and comment') @@ -3575,7 +3578,7 @@ def main(): if __name__ == '__main__': main() -# vim: ts=4 sts=4 sw=4 +# vim: ts=4 sts=4 sw=4 foldmarker=▶,▲ # Ignore "too complex" warnings when using pymode. # pylama:ignore=C901 diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 7efa086af2..ff45cad8f5 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -14,6 +14,7 @@ #include "nvim/window.h" #include "nvim/memory.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/map_defs.h" #include "nvim/map.h" #include "nvim/option.h" @@ -87,7 +88,7 @@ bool try_end(Error *err) /// @param[out] err Details of an error that may have occurred Object dict_get_value(dict_T *dict, String key, Error *err) { - hashitem_T *hi = hash_find(&dict->dv_hashtab, (uint8_t *) key.data); + hashitem_T *hi = hash_find(&dict->dv_hashtab, (char_u *)key.data); if (HASHITEM_EMPTY(hi)) { api_set_error(err, Validation, _("Key not found")); @@ -177,13 +178,13 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, if (retval) { rv = vim_to_object(&di->di_tv); } - clear_tv(&di->di_tv); + tv_clear(&di->di_tv); } // Update the value copy_tv(&tv, &di->di_tv); // Clear the temporary variable - clear_tv(&tv); + tv_clear(&tv); } return rv; @@ -682,20 +683,20 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) break; case kObjectTypeArray: { - list_T *list = list_alloc(); + list_T *const list = tv_list_alloc(); for (uint32_t i = 0; i < obj.data.array.size; i++) { Object item = obj.data.array.items[i]; - listitem_T *li = listitem_alloc(); + listitem_T *li = tv_list_item_alloc(); if (!object_to_vim(item, &li->li_tv, err)) { // cleanup - listitem_free(li); - list_free(list); + tv_list_item_free(li); + tv_list_free(list); return false; } - list_append(list, li); + tv_list_append(list, li); } list->lv_refcount++; diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 413456c615..59c0200395 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -22,6 +22,7 @@ #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/option.h" #include "nvim/syntax.h" #include "nvim/getchar.h" @@ -237,11 +238,11 @@ Object nvim_call_function(String fname, Array args, Error *err) if (!try_end(err)) { rv = vim_to_object(&rettv); } - clear_tv(&rettv); + tv_clear(&rettv); free_vim_args: while (i > 0) { - clear_tv(&vim_args[--i]); + tv_clear(&vim_args[--i]); } return rv; diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 4a07884f98..f7333fead4 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1472,7 +1472,7 @@ static inline void buf_init_changedtick(buf_T *const buf) { STATIC_ASSERT(sizeof("changedtick") <= sizeof(buf->changedtick_di.di_key), "buf->changedtick_di cannot hold large enough keys"); - buf->changedtick_di = (dictitem16_T) { + buf->changedtick_di = (ChangedtickDictItem) { .di_flags = DI_FLAGS_RO|DI_FLAGS_FIX, // Must not include DI_FLAGS_ALLOC. .di_tv = (typval_T) { .v_type = VAR_NUMBER, diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 9d350c763e..20a2b931bd 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -21,8 +21,6 @@ typedef struct { #include "nvim/pos.h" // for the number window-local and buffer-local options #include "nvim/option_defs.h" -// for optional iconv support -#include "nvim/iconv.h" // for jump list and tag stack sizes in a buffer and mark types #include "nvim/mark_defs.h" // for u_header_T; needs buf_T. @@ -30,7 +28,9 @@ typedef struct { // for hashtab_T #include "nvim/hashtab.h" // for dict_T -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" +// for proftime_T +#include "nvim/profile.h" // for String #include "nvim/api/private/defs.h" // for Map(K, V) @@ -318,25 +318,6 @@ typedef struct { String save_inputbuf; } tasave_T; -/* - * Used for conversion of terminal I/O and script files. - */ -typedef struct { - int vc_type; /* zero or one of the CONV_ values */ - int vc_factor; /* max. expansion factor */ -# ifdef USE_ICONV - iconv_t vc_fd; /* for CONV_ICONV */ -# endif - bool vc_fail; /* fail for invalid char, don't use '?' */ -} vimconv_T; - -#define CONV_NONE 0 -#define CONV_TO_UTF8 1 -#define CONV_9_TO_UTF8 2 -#define CONV_TO_LATIN1 3 -#define CONV_TO_LATIN9 4 -#define CONV_ICONV 5 - /* * Structure used for mappings and abbreviations. */ @@ -447,6 +428,10 @@ typedef struct { char_u *b_syn_isk; // iskeyword option } synblock_T; +/// Type used for changedtick_di member in buf_T +/// +/// Primary exists so that literals of relevant type can be made. +typedef TV_DICTITEM_STRUCT(sizeof("changedtick")) ChangedtickDictItem; #define BUF_HAS_QF_ENTRY 1 #define BUF_HAS_LL_ENTRY 2 @@ -491,7 +476,7 @@ struct file_buffer { // file has been changed and not written out. /// Change identifier incremented for each change, including undo #define b_changedtick changedtick_di.di_tv.vval.v_number - dictitem16_T changedtick_di; // b:changedtick dictionary item. + ChangedtickDictItem changedtick_di; // b:changedtick dictionary item. bool b_saving; /* Set to true if we are in the middle of saving the buffer. */ @@ -735,8 +720,8 @@ struct file_buffer { int b_bad_char; /* "++bad=" argument when edit started or 0 */ int b_start_bomb; /* 'bomb' when it was read */ - dictitem_T b_bufvar; /* variable for "b:" Dictionary */ - dict_T *b_vars; /* internal variables, local to buffer */ + ScopeDictDictItem b_bufvar; ///< Variable for "b:" Dictionary. + dict_T *b_vars; ///< b: scope dictionary. /* When a buffer is created, it starts without a swap file. b_may_swap is * then set to indicate that a swap file may be opened later. It is reset @@ -824,9 +809,9 @@ struct tabpage_S { buf_T *(tp_diffbuf[DB_COUNT]); int tp_diff_invalid; ///< list of diffs is outdated frame_T *(tp_snapshot[SNAP_COUNT]); ///< window layout snapshots - dictitem_T tp_winvar; ///< variable for "t:" Dictionary - dict_T *tp_vars; ///< internal variables, local to tab page - char_u *tp_localdir; ///< Absolute path of local CWD or NULL + ScopeDictDictItem tp_winvar; ///< Variable for "t:" Dictionary. + dict_T *tp_vars; ///< Internal variables, local to tab page. + char_u *tp_localdir; ///< Absolute path of local cwd or NULL. }; /* @@ -1118,8 +1103,8 @@ struct window_S { long w_scbind_pos; - dictitem_T w_winvar; /* variable for "w:" Dictionary */ - dict_T *w_vars; /* internal variables, local to window */ + ScopeDictDictItem w_winvar; ///< Variable for "w:" dictionary. + dict_T *w_vars; ///< Dictionary with w: variables. int w_farsi; /* for the window dependent Farsi functions */ diff --git a/src/nvim/charset.c b/src/nvim/charset.c index efe32b915f..cb6a9fa43c 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -41,8 +41,10 @@ static bool chartab_initialized = false; (buf)->b_chartab[(unsigned)(c) >> 6] |= (1ull << ((c) & 0x3f)) #define RESET_CHARTAB(buf, c) \ (buf)->b_chartab[(unsigned)(c) >> 6] &= ~(1ull << ((c) & 0x3f)) +#define GET_CHARTAB_TAB(chartab, c) \ + ((chartab)[(unsigned)(c) >> 6] & (1ull << ((c) & 0x3f))) #define GET_CHARTAB(buf, c) \ - ((buf)->b_chartab[(unsigned)(c) >> 6] & (1ull << ((c) & 0x3f))) + GET_CHARTAB_TAB((buf)->b_chartab, c) // Table used below, see init_chartab() for an explanation static char_u g_chartab[256]; @@ -634,7 +636,7 @@ int char2cells(int c) /// @param p /// /// @return number of display cells. -int ptr2cells(char_u *p) +int ptr2cells(const char_u *p) { // For UTF-8 we need to look at more bytes if the first byte is >= 0x80. if (*p >= 0x80) { @@ -776,6 +778,21 @@ bool vim_iswordc(int c) return vim_iswordc_buf(c, curbuf); } +/// Check that "c" is a keyword character +/// Letters and characters from 'iskeyword' option for given buffer. +/// For multi-byte characters mb_get_class() is used (builtin rules). +/// +/// @param[in] c Character to check. +/// @param[in] chartab Buffer chartab. +bool vim_iswordc_tab(const int c, const uint64_t *const chartab) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + if (c >= 0x100) { + return utf_class(c) >= 2; + } + return c > 0 && c < 0x100 && GET_CHARTAB_TAB(chartab, c) != 0; +} + /// Check that "c" is a keyword character: /// Letters and characters from 'iskeyword' option for given buffer. /// For multi-byte characters mb_get_class() is used (builtin rules). @@ -785,10 +802,7 @@ bool vim_iswordc(int c) bool vim_iswordc_buf(int c, buf_T *buf) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(2) { - if (c >= 0x100) { - return utf_class(c) >= 2; - } - return c > 0 && c < 0x100 && GET_CHARTAB(buf, c) != 0; + return vim_iswordc_tab(c, buf->b_chartab); } /// Just like vim_iswordc() but uses a pointer to the (multi-byte) character. diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 82f1bf0a16..45abd314fc 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -12,6 +12,7 @@ #include "nvim/state.h" #include "nvim/vim.h" #include "nvim/ascii.h" +#include "nvim/mark.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "cursor.c.generated.h" @@ -227,9 +228,10 @@ static int coladvance2( } } - /* prevent from moving onto a trail byte */ - if (has_mbyte) - mb_adjustpos(curbuf, pos); + // Prevent from moving onto a trail byte. + if (has_mbyte) { + mark_mb_adjustpos(curbuf, pos); + } if (col < wcol) return FAIL; @@ -361,9 +363,10 @@ void check_cursor_col_win(win_T *win) win->w_cursor.col = len; } else { win->w_cursor.col = len - 1; - /* Move the cursor to the head byte. */ - if (has_mbyte) - mb_adjustpos(win->w_buffer, &win->w_cursor); + // Move the cursor to the head byte. + if (has_mbyte) { + mark_mb_adjustpos(win->w_buffer, &win->w_cursor); + } } } else if (win->w_cursor.col < 0) { win->w_cursor.col = 0; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 5544f0b163..77d5b2c816 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -15,6 +15,7 @@ #include "nvim/cursor.h" #include "nvim/digraph.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/farsi.h" @@ -3461,8 +3462,8 @@ expand_by_function ( matchdict = rettv.vval.v_dict; break; default: - /* TODO: Give error message? */ - clear_tv(&rettv); + // TODO(brammool): Give error message? + tv_clear(&rettv); break; } } @@ -3484,10 +3485,12 @@ expand_by_function ( ins_compl_add_dict(matchdict); theend: - if (matchdict != NULL) + if (matchdict != NULL) { dict_unref(matchdict); - if (matchlist != NULL) - list_unref(matchlist); + } + if (matchlist != NULL) { + tv_list_unref(matchlist); + } } /* diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 927a1dcb0e..03d6eef9e3 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -94,7 +94,11 @@ #include "nvim/lib/kvec.h" #include "nvim/lib/khash.h" #include "nvim/lib/queue.h" -#include "nvim/eval/typval_encode.h" +#include "nvim/eval/typval.h" +#include "nvim/eval/executor.h" +#include "nvim/eval/gc.h" + +// TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead #define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */ @@ -150,7 +154,6 @@ typedef struct lval_S { static char *e_letunexp = N_("E18: Unexpected characters in :let"); -static char *e_listidx = N_("E684: list index out of range: %" PRId64); static char *e_undefvar = N_("E121: Undefined variable: %s"); static char *e_missbrac = N_("E111: Missing ']'"); static char *e_listarg = N_("E686: Argument of %s must be a List"); @@ -167,17 +170,21 @@ static char *e_funcexts = N_( static char *e_funcdict = N_("E717: Dictionary entry already exists"); static char *e_funcref = N_("E718: Funcref required"); static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary"); -static char *e_letwrong = N_("E734: Wrong variable type for %s="); static char *e_nofunc = N_("E130: Unknown function: %s"); static char *e_illvar = N_("E461: Illegal variable name: %s"); static char *e_float_as_string = N_("E806: using Float as a String"); static const char *e_readonlyvar = N_( "E46: Cannot change read-only variable \"%.*s\""); -static char_u * const empty_string = (char_u *)""; +// TODO(ZyX-I): move to eval/executor +static char *e_letwrong = N_("E734: Wrong variable type for %s="); + static char_u * const namespace_char = (char_u *)"abglstvw"; -static dictitem_T globvars_var; /* variable used for g: */ +/// Variable used for g: +static ScopeDictDictItem globvars_var; + +/// g: value #define globvarht globvardict.dv_hashtab /* @@ -188,12 +195,15 @@ static hashtab_T compat_hashtab; hashtab_T func_hashtab; +// Used for checking if local variables or arguments used in a lambda. +static int *eval_lavars_used = NULL; + /* * Array to hold the hashtab with variables local to each sourced script. * Each item holds a variable (nameless) that points to the dict_T. */ typedef struct { - dictitem_T sv_var; + ScopeDictDictItem sv_var; dict_T sv_dict; } scriptvar_T; @@ -231,17 +241,44 @@ typedef enum { // The names of packages that once were loaded are remembered. static garray_T ga_loaded = { 0, 0, sizeof(char_u *), 4, NULL }; -// List heads for garbage collection. Although there can be a reference loop -// from partial to dict to partial, we don't need to keep track of the partial, -// since it will get freed when the dict is unused and gets freed. -static dict_T *first_dict = NULL; // list of all dicts -static list_T *first_list = NULL; // list of all lists - -#define FLEN_FIXED 40 - #define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j] #define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j] +/// Short variable name length +#define VAR_SHORT_LEN 20 +/// Number of fixed variables used for arguments +#define FIXVAR_CNT 12 + +struct funccall_S { + ufunc_T *func; ///< Function being called. + int linenr; ///< Next line to be executed. + int returned; ///< ":return" used. + /// Fixed variables for arguments. + TV_DICTITEM_STRUCT(VAR_SHORT_LEN + 1) fixvar[FIXVAR_CNT]; + dict_T l_vars; ///< l: local function variables. + ScopeDictDictItem l_vars_var; ///< Variable for l: scope. + dict_T l_avars; ///< a: argument variables. + ScopeDictDictItem l_avars_var; ///< Variable for a: scope. + list_T l_varlist; ///< List for a:000. + listitem_T l_listitems[MAX_FUNC_ARGS]; ///< List items for a:000. + typval_T *rettv; ///< Return value. + linenr_T breakpoint; ///< Next line with breakpoint or zero. + int dbg_tick; ///< Debug_tick when breakpoint was set. + int level; ///< Top nesting level of executed function. + proftime_T prof_child; ///< Time spent in a child. + funccall_T *caller; ///< Calling function or NULL. + int fc_refcount; ///< Number of user functions that reference this funccall. + int fc_copyID; ///< CopyID used for garbage collection. + garray_T fc_funcs; ///< List of ufunc_T* which keep a reference to "func". +}; + +///< Structure used by trans_function_name() +typedef struct { + dict_T *fd_dict; ///< Dictionary used. + char_u *fd_newkey; ///< New key in "dict" in allocated memory. + dictitem_T *fd_di; ///< Dictionary item used. +} funcdict_T; + /* * Info used by a ":for" loop. */ @@ -283,8 +320,8 @@ typedef enum { // variables with the VV_ defines. static struct vimvar { char *vv_name; ///< Name of the variable, without v:. - dictitem16_T vv_di; ///< Value and name for key (max 16 chars) - char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX. + TV_DICTITEM_STRUCT(17) vv_di; ///< Value and name for key (max 16 chars). + char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX. } vimvars[] = { // VV_ tails differing from upcased string literals: @@ -389,7 +426,10 @@ static struct vimvar { #define vv_dict vv_di.di_tv.vval.v_dict #define vv_tv vv_di.di_tv -static dictitem_T vimvars_var; // variable used for v: +/// Variable used for v: +static ScopeDictDictItem vimvars_var; + +/// v: hashtab #define vimvarht vimvardict.dv_hashtab typedef enum { @@ -465,6 +505,17 @@ typedef struct fst { KHASH_MAP_INIT_STR(functions, VimLFuncDef) +/// Type of assert_* check being performed +typedef enum +{ + ASSERT_EQUAL, + ASSERT_NOTEQUAL, + ASSERT_MATCH, + ASSERT_NOTMATCH, + ASSERT_INRANGE, + ASSERT_OTHER, +} assert_type_T; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval.c.generated.h" #endif @@ -547,7 +598,7 @@ void eval_init(void) dict_T *const msgpack_types_dict = dict_alloc(); for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) { - list_T *const type_list = list_alloc(); + list_T *const type_list = tv_list_alloc(); type_list->lv_lock = VAR_FIXED; type_list->lv_refcount = 1; dictitem_T *const di = dictitem_alloc((char_u *)msgpack_type_names[i]); @@ -570,7 +621,7 @@ void eval_init(void) dict_T *v_event = dict_alloc(); v_event->dv_lock = VAR_FIXED; set_vim_var_dict(VV_EVENT, v_event); - set_vim_var_list(VV_ERRORS, list_alloc()); + set_vim_var_list(VV_ERRORS, tv_list_alloc()); set_vim_var_nr(VV_SEARCHFORWARD, 1L); set_vim_var_nr(VV_HLSEARCH, 1L); set_vim_var_nr(VV_COUNT1, 1); @@ -601,7 +652,7 @@ void eval_clear(void) xfree(p->vv_str); p->vv_str = NULL; } else if (p->vv_di.di_tv.v_type == VAR_LIST) { - list_unref(p->vv_list); + tv_list_unref(p->vv_list); p->vv_list = NULL; } } @@ -832,7 +883,7 @@ void var_redir_stop(void) int eval_charconvert(const char *const enc_from, const char *const enc_to, const char *const fname_from, const char *const fname_to) { - int err = false; + bool err = false; set_vim_var_string(VV_CC_FROM, enc_from, -1); set_vim_var_string(VV_CC_TO, enc_to, -1); @@ -854,7 +905,7 @@ int eval_charconvert(const char *const enc_from, const char *const enc_to, int eval_printexpr(const char *const fname, const char *const args) { - int err = false; + bool err = false; set_vim_var_string(VV_FNAME_IN, fname, -1); set_vim_var_string(VV_CMDARG, args, -1); @@ -874,7 +925,7 @@ int eval_printexpr(const char *const fname, const char *const args) void eval_diff(const char *const origfile, const char *const newfile, const char *const outfile) { - int err = FALSE; + bool err = false; set_vim_var_string(VV_FNAME_IN, origfile, -1); set_vim_var_string(VV_FNAME_NEW, newfile, -1); @@ -888,7 +939,7 @@ void eval_diff(const char *const origfile, const char *const newfile, void eval_patch(const char *const origfile, const char *const difffile, const char *const outfile) { - int err; + bool err = false; set_vim_var_string(VV_FNAME_IN, origfile, -1); set_vim_var_string(VV_FNAME_DIFF, difffile, -1); @@ -907,27 +958,29 @@ void eval_patch(const char *const origfile, const char *const difffile, int eval_to_bool ( char_u *arg, - int *error, + bool *error, char_u **nextcmd, int skip /* only parse, don't execute */ ) { typval_T tv; - int retval = FALSE; + bool retval = false; - if (skip) - ++emsg_skip; - if (eval0(arg, &tv, nextcmd, !skip) == FAIL) - *error = TRUE; - else { - *error = FALSE; + if (skip) { + emsg_skip++; + } + if (eval0(arg, &tv, nextcmd, !skip) == FAIL) { + *error = true; + } else { + *error = false; if (!skip) { retval = (get_tv_number_chk(&tv, error) != 0); - clear_tv(&tv); + tv_clear(&tv); } } - if (skip) - --emsg_skip; + if (skip) { + emsg_skip--; + } return retval; } @@ -953,7 +1006,7 @@ eval_to_string_skip ( retval = NULL; else { retval = vim_strsave(get_tv_string(&tv)); - clear_tv(&tv); + tv_clear(&tv); } if (skip) --emsg_skip; @@ -992,9 +1045,10 @@ char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert) if (convert && tv.v_type == VAR_LIST) { ga_init(&ga, (int)sizeof(char), 80); if (tv.vval.v_list != NULL) { - list_join(&ga, tv.vval.v_list, "\n"); - if (tv.vval.v_list->lv_len > 0) + tv_list_join(&ga, tv.vval.v_list, "\n"); + if (tv.vval.v_list->lv_len > 0) { ga_append(&ga, NL); + } } ga_append(&ga, NUL); retval = (char_u *)ga.ga_data; @@ -1003,7 +1057,7 @@ char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert) retval = vim_strsave(numbuf); } else retval = vim_strsave(get_tv_string(&tv)); - clear_tv(&tv); + tv_clear(&tv); } return retval; @@ -1047,7 +1101,7 @@ int eval_to_number(char_u *expr) retval = -1; else { retval = get_tv_number_chk(&rettv, NULL); - clear_tv(&rettv); + tv_clear(&rettv); } --emsg_off; @@ -1104,11 +1158,12 @@ list_T *eval_spell_expr(char_u *badword, char_u *expr) if (p_verbose == 0) ++emsg_off; - if (eval1(&p, &rettv, TRUE) == OK) { - if (rettv.v_type != VAR_LIST) - clear_tv(&rettv); - else + if (eval1(&p, &rettv, true) == OK) { + if (rettv.v_type != VAR_LIST) { + tv_clear(&rettv); + } else { list = rettv.vval.v_list; + } } if (p_verbose == 0) @@ -1207,7 +1262,7 @@ int call_vim_function( ++sandbox; } - rettv->v_type = VAR_UNKNOWN; // clear_tv() uses this + rettv->v_type = VAR_UNKNOWN; // tv_clear() uses this. ret = call_func(func, (int)STRLEN(func), rettv, argc, argvars, NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &doesrange, true, NULL, NULL); @@ -1218,7 +1273,7 @@ int call_vim_function( xfree(argvars); if (ret == FAIL) { - clear_tv(rettv); + tv_clear(rettv); } return ret; @@ -1245,7 +1300,7 @@ call_func_retnr ( return -1; retval = get_tv_number_chk(&rettv, NULL); - clear_tv(&rettv); + tv_clear(&rettv); return retval; } @@ -1270,7 +1325,7 @@ call_func_retstr ( return NULL; retval = vim_strsave(get_tv_string(&rettv)); - clear_tv(&rettv); + tv_clear(&rettv); return retval; } @@ -1294,7 +1349,7 @@ call_func_retlist ( return NULL; if (rettv.v_type != VAR_LIST) { - clear_tv(&rettv); + tv_clear(&rettv); return NULL; } @@ -1392,7 +1447,7 @@ int eval_foldexpr(char_u *arg, int *cp) *cp = *s++; retval = atol((char *)s); } - clear_tv(&tv); + tv_clear(&tv); } --emsg_off; if (use_sandbox) @@ -1464,13 +1519,13 @@ void ex_let(exarg_T *eap) ++emsg_skip; i = eval0(expr, &rettv, &eap->nextcmd, !eap->skip); if (eap->skip) { - if (i != FAIL) - clear_tv(&rettv); - --emsg_skip; + if (i != FAIL) { + tv_clear(&rettv); + } + emsg_skip--; } else if (i != FAIL) { - (void)ex_let_vars(eap->arg, &rettv, FALSE, semicolon, var_count, - op); - clear_tv(&rettv); + (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, op); + tv_clear(&rettv); } } } @@ -1516,7 +1571,7 @@ ex_let_vars ( return FAIL; } - i = list_len(l); + i = tv_list_len(l); if (semicolon == 0 && var_count < i) { EMSG(_("E687: Less targets than List items")); return FAIL; @@ -1538,9 +1593,9 @@ ex_let_vars ( if (*arg == ';') { /* Put the rest of the list (may be empty) in the var after ';'. * Create a new list for this. */ - l = list_alloc(); + l = tv_list_alloc(); while (item != NULL) { - list_append_tv(l, &item->li_tv); + tv_list_append_tv(l, &item->li_tv); item = item->li_next; } @@ -1549,11 +1604,12 @@ ex_let_vars ( ltv.vval.v_list = l; l->lv_refcount = 1; - arg = ex_let_one(skipwhite(arg + 1), <v, FALSE, - (char_u *)"]", nextchars); - clear_tv(<v); - if (arg == NULL) + arg = ex_let_one(skipwhite(arg + 1), <v, false, + (char_u *)"]", nextchars); + tv_clear(<v); + if (arg == NULL) { return FAIL; + } break; } else if (*arg != ',' && *arg != ']') { EMSG2(_(e_intern2), "ex_let_vars()"); @@ -1774,7 +1830,7 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) tv.v_type, s == NULL ? "" : s, first); xfree(s); } - clear_tv(&tv); + tv_clear(&tv); } } } @@ -1788,6 +1844,8 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) return arg; } +// TODO(ZyX-I): move to eval/ex_cmds + /* * Set one item of ":let var = expr" or ":let [v1, v2] = list" to its value. * Returns a pointer to the char just after the var name. @@ -1952,6 +2010,8 @@ ex_let_one ( return arg_end; } +// TODO(ZyX-I): move to eval/executor + /// Get an lvalue /// /// Lvalue may be @@ -2081,8 +2141,8 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (eval1(&p, &var1, TRUE) == FAIL) /* recursive! */ return NULL; if (get_tv_string_chk(&var1) == NULL) { - /* not a number or string */ - clear_tv(&var1); + // Not a number or string. + tv_clear(&var1); return NULL; } } @@ -2090,35 +2150,41 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, /* Optionally get the second index [ :expr]. */ if (*p == ':') { if (lp->ll_tv->v_type == VAR_DICT) { - if (!quiet) + if (!quiet) { EMSG(_(e_dictrange)); - if (!empty1) - clear_tv(&var1); + } + if (!empty1) { + tv_clear(&var1); + } return NULL; } if (rettv != NULL && (rettv->v_type != VAR_LIST || rettv->vval.v_list == NULL)) { - if (!quiet) - EMSG(_("E709: [:] requires a List value")); - if (!empty1) - clear_tv(&var1); + if (!quiet) { + emsgf(_("E709: [:] requires a List value")); + } + if (!empty1) { + tv_clear(&var1); + } return NULL; } p = skipwhite(p + 1); - if (*p == ']') - lp->ll_empty2 = TRUE; - else { - lp->ll_empty2 = FALSE; - if (eval1(&p, &var2, TRUE) == FAIL) { /* recursive! */ - if (!empty1) - clear_tv(&var1); + if (*p == ']') { + lp->ll_empty2 = true; + } else { + lp->ll_empty2 = false; + if (eval1(&p, &var2, true) == FAIL) { // Recursive! + if (!empty1) { + tv_clear(&var1); + } return NULL; } if (get_tv_string_chk(&var2) == NULL) { - /* not a number or string */ - if (!empty1) - clear_tv(&var1); - clear_tv(&var2); + // Not a number or string. + if (!empty1) { + tv_clear(&var1); + } + tv_clear(&var2); return NULL; } } @@ -2127,12 +2193,15 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, lp->ll_range = FALSE; if (*p != ']') { - if (!quiet) - EMSG(_(e_missbrac)); - if (!empty1) - clear_tv(&var1); - if (lp->ll_range && !lp->ll_empty2) - clear_tv(&var2); + if (!quiet) { + emsgf(_(e_missbrac)); + } + if (!empty1) { + tv_clear(&var1); + } + if (lp->ll_range && !lp->ll_empty2) { + tv_clear(&var2); + } return NULL; } @@ -2145,7 +2214,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, // "[key]": get key from "var1" key = get_tv_string_chk(&var1); // is number or string if (key == NULL) { - clear_tv(&var1); + tv_clear(&var1); return NULL; } } @@ -2184,56 +2253,63 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, /* Key does not exist in dict: may need to add it. */ if (*p == '[' || *p == '.' || unlet) { - if (!quiet) - EMSG2(_(e_dictkey), key); - if (len == -1) - clear_tv(&var1); + if (!quiet) { + emsgf(_(e_dictkey), key); + } + if (len == -1) { + tv_clear(&var1); + } return NULL; } - if (len == -1) + if (len == -1) { lp->ll_newkey = vim_strsave(key); - else + } else { lp->ll_newkey = vim_strnsave(key, len); - if (len == -1) - clear_tv(&var1); + } + if (len == -1) { + tv_clear(&var1); + } break; // existing variable, need to check if it can be changed } else if (!(flags & GLV_READ_ONLY) && var_check_ro(lp->ll_di->di_flags, (const char *)name, (size_t)(p - name))) { if (len == -1) { - clear_tv(&var1); + tv_clear(&var1); } return NULL; } - if (len == -1) - clear_tv(&var1); + if (len == -1) { + tv_clear(&var1); + } lp->ll_tv = &lp->ll_di->di_tv; } else { /* * Get the number and item for the only or first index of the List. */ - if (empty1) + if (empty1) { lp->ll_n1 = 0; - else { - lp->ll_n1 = get_tv_number(&var1); /* is number or string */ - clear_tv(&var1); + } else { + lp->ll_n1 = get_tv_number(&var1); // Is number or string. + tv_clear(&var1); } lp->ll_dict = NULL; lp->ll_list = lp->ll_tv->vval.v_list; - lp->ll_li = list_find(lp->ll_list, lp->ll_n1); + lp->ll_li = tv_list_find(lp->ll_list, lp->ll_n1); if (lp->ll_li == NULL) { if (lp->ll_n1 < 0) { lp->ll_n1 = 0; - lp->ll_li = list_find(lp->ll_list, lp->ll_n1); + lp->ll_li = tv_list_find(lp->ll_list, lp->ll_n1); } } if (lp->ll_li == NULL) { - if (lp->ll_range && !lp->ll_empty2) - clear_tv(&var2); - if (!quiet) + if (lp->ll_range && !lp->ll_empty2) { + tv_clear(&var2); + } + if (!quiet) { EMSGN(_(e_listidx), lp->ll_n1); + } return NULL; } @@ -2244,24 +2320,26 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, * Otherwise "lp->ll_n2" is set to the second index. */ if (lp->ll_range && !lp->ll_empty2) { - lp->ll_n2 = get_tv_number(&var2); /* is number or string */ - clear_tv(&var2); + lp->ll_n2 = get_tv_number(&var2); // Is number or string. + tv_clear(&var2); if (lp->ll_n2 < 0) { - ni = list_find(lp->ll_list, lp->ll_n2); + ni = tv_list_find(lp->ll_list, lp->ll_n2); if (ni == NULL) { if (!quiet) EMSGN(_(e_listidx), lp->ll_n2); return NULL; } - lp->ll_n2 = list_idx_of_item(lp->ll_list, ni); + lp->ll_n2 = tv_list_idx_of_item(lp->ll_list, ni); } - /* Check that lp->ll_n2 isn't before lp->ll_n1. */ - if (lp->ll_n1 < 0) - lp->ll_n1 = list_idx_of_item(lp->ll_list, lp->ll_li); + // Check that lp->ll_n2 isn't before lp->ll_n1. + if (lp->ll_n1 < 0) { + lp->ll_n1 = tv_list_idx_of_item(lp->ll_list, lp->ll_li); + } if (lp->ll_n2 < lp->ll_n1) { - if (!quiet) + if (!quiet) { EMSGN(_(e_listidx), lp->ll_n2); + } return NULL; } } @@ -2273,6 +2351,8 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, return p; } +// TODO(ZyX-I): move to eval/executor + /* * Clear lval "lp" that was filled by get_lval(). */ @@ -2282,6 +2362,8 @@ static void clear_lval(lval_T *lp) xfree(lp->ll_newkey); } +// TODO(ZyX-I): move to eval/executor + /* * Set a variable that was parsed by get_lval() to "rettv". * "endp" points to just after the parsed name. @@ -2308,10 +2390,10 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch STRLEN(lp->ll_name)) && !tv_check_lock(di->di_tv.v_lock, (const char *)lp->ll_name, STRLEN(lp->ll_name)))) - && tv_op(&tv, rettv, op) == OK) { + && eexe_mod_op(&tv, rettv, (const char *)op) == OK) { set_var(lp->ll_name, &tv, false); } - clear_tv(&tv); + tv_clear(&tv); } } else { set_var(lp->ll_name, rettv, copy); @@ -2344,18 +2426,18 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch * Assign the List values to the list items. */ for (ri = rettv->vval.v_list->lv_first; ri != NULL; ) { - if (op != NULL && *op != '=') - tv_op(&lp->ll_li->li_tv, &ri->li_tv, op); - else { - clear_tv(&lp->ll_li->li_tv); + if (op != NULL && *op != '=') { + eexe_mod_op(&lp->ll_li->li_tv, &ri->li_tv, (const char *)op); + } else { + tv_clear(&lp->ll_li->li_tv); copy_tv(&ri->li_tv, &lp->ll_li->li_tv); } ri = ri->li_next; if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1)) break; if (lp->ll_li->li_next == NULL) { - /* Need to add an empty item. */ - list_append_number(lp->ll_list, 0); + // Need to add an empty item. + tv_list_append_number(lp->ll_list, 0); assert(lp->ll_li->li_next); } lp->ll_li = lp->ll_li->li_next; @@ -2398,10 +2480,10 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch } if (op != NULL && *op != '=') { - tv_op(lp->ll_tv, rettv, op); + eexe_mod_op(lp->ll_tv, rettv, (const char *)op); goto notify; } else { - clear_tv(lp->ll_tv); + tv_clear(lp->ll_tv); } } @@ -2421,145 +2503,13 @@ notify: } else { dictitem_T *di = lp->ll_di; dictwatcher_notify(dict, (char *)di->di_key, lp->ll_tv, &oldtv); - clear_tv(&oldtv); + tv_clear(&oldtv); } } } } -/* - * Handle "tv1 += tv2", "tv1 -= tv2" and "tv1 .= tv2" - * Returns OK or FAIL. - */ -static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op) -{ - long n; - char_u numbuf[NUMBUFLEN]; - char_u *s; - - // Can't do anything with a Funcref, a Dict or special value on the right. - if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT) { - switch (tv1->v_type) { - case VAR_DICT: - case VAR_FUNC: - case VAR_PARTIAL: - case VAR_SPECIAL: - break; - - case VAR_LIST: - if (*op != '+' || tv2->v_type != VAR_LIST) - break; - /* List += List */ - if (tv1->vval.v_list != NULL && tv2->vval.v_list != NULL) - list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL); - return OK; - - case VAR_NUMBER: - case VAR_STRING: - if (tv2->v_type == VAR_LIST) - break; - if (*op == '+' || *op == '-') { - /* nr += nr or nr -= nr*/ - n = get_tv_number(tv1); - if (tv2->v_type == VAR_FLOAT) { - float_T f = n; - - if (*op == '+') - f += tv2->vval.v_float; - else - f -= tv2->vval.v_float; - clear_tv(tv1); - tv1->v_type = VAR_FLOAT; - tv1->vval.v_float = f; - } else { - if (*op == '+') - n += get_tv_number(tv2); - else - n -= get_tv_number(tv2); - clear_tv(tv1); - tv1->v_type = VAR_NUMBER; - tv1->vval.v_number = n; - } - } else { - if (tv2->v_type == VAR_FLOAT) - break; - - /* str .= str */ - s = get_tv_string(tv1); - s = concat_str(s, get_tv_string_buf(tv2, numbuf)); - clear_tv(tv1); - tv1->v_type = VAR_STRING; - tv1->vval.v_string = s; - } - return OK; - - case VAR_FLOAT: - { - float_T f; - - if (*op == '.' || (tv2->v_type != VAR_FLOAT - && tv2->v_type != VAR_NUMBER - && tv2->v_type != VAR_STRING)) - break; - if (tv2->v_type == VAR_FLOAT) - f = tv2->vval.v_float; - else - f = get_tv_number(tv2); - if (*op == '+') - tv1->vval.v_float += f; - else - tv1->vval.v_float -= f; - } - return OK; - - case VAR_UNKNOWN: - assert(false); - } - } - - EMSG2(_(e_letwrong), op); - return FAIL; -} - -/* - * Add a watcher to a list. - */ -void list_add_watch(list_T *l, listwatch_T *lw) -{ - lw->lw_next = l->lv_watch; - l->lv_watch = lw; -} - -/* - * Remove a watcher from a list. - * No warning when it isn't found... - */ -void list_rem_watch(list_T *l, listwatch_T *lwrem) -{ - listwatch_T *lw, **lwp; - - lwp = &l->lv_watch; - for (lw = l->lv_watch; lw != NULL; lw = lw->lw_next) { - if (lw == lwrem) { - *lwp = lw->lw_next; - break; - } - lwp = &lw->lw_next; - } -} - -/* - * Just before removing an item from a list: advance watchers to the next - * item. - */ -static void list_fix_watch(list_T *l, listitem_T *item) -{ - listwatch_T *lw; - - for (lw = l->lv_watch; lw != NULL; lw = lw->lw_next) - if (lw->lw_item == item) - lw->lw_item = item->li_next; -} +// TODO(ZyX-I): move to eval/ex_cmds /* * Evaluate the expression used in a ":for var in expr" command. @@ -2567,14 +2517,14 @@ static void list_fix_watch(list_T *l, listitem_T *item) * Set "*errp" to TRUE for an error, FALSE otherwise; * Return a pointer that holds the info. Null when there is an error. */ -void *eval_for_line(char_u *arg, int *errp, char_u **nextcmdp, int skip) +void *eval_for_line(char_u *arg, bool *errp, char_u **nextcmdp, int skip) { forinfo_T *fi = xcalloc(1, sizeof(forinfo_T)); char_u *expr; typval_T tv; list_T *l; - *errp = TRUE; /* default: there is an error */ + *errp = true; // Default: there is an error. expr = skip_var_list(arg, &fi->fi_varcount, &fi->fi_semicolon); if (expr == NULL) @@ -2589,20 +2539,20 @@ void *eval_for_line(char_u *arg, int *errp, char_u **nextcmdp, int skip) if (skip) ++emsg_skip; if (eval0(skipwhite(expr + 2), &tv, nextcmdp, !skip) == OK) { - *errp = FALSE; + *errp = false; if (!skip) { l = tv.vval.v_list; if (tv.v_type != VAR_LIST) { EMSG(_(e_listreq)); - clear_tv(&tv); + tv_clear(&tv); } else if (l == NULL) { // a null list is like an empty list: do nothing - clear_tv(&tv); + tv_clear(&tv); } else { /* No need to increment the refcount, it's already set for the * list being used in "tv". */ fi->fi_list = l; - list_add_watch(l, &fi->fi_lw); + tv_list_watch_add(l, &fi->fi_lw); fi->fi_lw.lw_item = l->lv_first; } } @@ -2613,6 +2563,8 @@ void *eval_for_line(char_u *arg, int *errp, char_u **nextcmdp, int skip) return fi; } +// TODO(ZyX-I): move to eval/ex_cmds + /* * 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. @@ -2636,6 +2588,8 @@ int next_for_item(void *fi_void, char_u *arg) return result; } +// TODO(ZyX-I): move to eval/ex_cmds + /* * Free the structure used to store info used by ":for". */ @@ -2644,8 +2598,8 @@ void free_for_info(void *fi_void) forinfo_T *fi = (forinfo_T *)fi_void; if (fi != NULL && fi->fi_list != NULL) { - list_rem_watch(fi->fi_list, &fi->fi_lw); - list_unref(fi->fi_list); + tv_list_watch_remove(fi->fi_list, &fi->fi_lw); + tv_list_unref(fi->fi_list); } xfree(fi); } @@ -2733,6 +2687,7 @@ void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx) xp->xp_pattern = arg; } +// TODO(ZyX-I): move to eval/ex_cmds /* * ":1,25call func(arg1, arg2)" function call. @@ -2752,13 +2707,14 @@ void ex_call(exarg_T *eap) partial_T *partial = NULL; 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->nextcmd, FALSE) != FAIL) - clear_tv(&rettv); - --emsg_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->nextcmd, false) != FAIL) { + tv_clear(&rettv); + } + emsg_skip--; return; } @@ -2786,7 +2742,7 @@ void ex_call(exarg_T *eap) /* Skip white space to allow ":call func ()". Not good, but required for * backward compatibility. */ startarg = skipwhite(arg); - rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */ + rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this. if (*startarg != '(') { EMSG2(_("E107: Missing parentheses: %s"), eap->arg); @@ -2825,9 +2781,10 @@ void ex_call(exarg_T *eap) break; } - clear_tv(&rettv); - if (doesrange || eap->skip) + tv_clear(&rettv); + if (doesrange || eap->skip) { break; + } /* Stop when immediately aborting on error, or when an interrupt * occurred or an exception was thrown but not caught. @@ -2853,6 +2810,8 @@ end: xfree(tofree); } +// TODO(ZyX-I): move to eval/ex_cmds + /* * ":unlet[!] var1 ... " command. */ @@ -2861,6 +2820,8 @@ void ex_unlet(exarg_T *eap) ex_unletlock(eap, eap->arg, 0); } +// TODO(ZyX-I): move to eval/ex_cmds + /* * ":lockvar" and ":unlockvar" commands */ @@ -2879,6 +2840,8 @@ void ex_lockvar(exarg_T *eap) ex_unletlock(eap, arg, deep); } +// TODO(ZyX-I): move to eval/ex_cmds + /* * ":unlet", ":lockvar" and ":unlockvar" are quite similar. */ @@ -2912,8 +2875,9 @@ static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep) error = TRUE; } else { if (do_lock_var(&lv, name_end, deep, - eap->cmdidx == CMD_lockvar) == FAIL) - error = TRUE; + eap->cmdidx == CMD_lockvar) == FAIL) { + error = true; + } } } @@ -2926,6 +2890,8 @@ static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep) eap->nextcmd = check_nextcmd(arg); } +// TODO(ZyX-I): move to eval/ex_cmds + static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) { int ret = OK; @@ -2966,14 +2932,14 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) /* Delete a range of List items. */ while (lp->ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { li = lp->ll_li->li_next; - listitem_remove(lp->ll_list, lp->ll_li); + tv_list_item_remove(lp->ll_list, lp->ll_li); lp->ll_li = li; ++lp->ll_n1; } } else { if (lp->ll_list != NULL) { // unlet a List item. - listitem_remove(lp->ll_list, lp->ll_li); + tv_list_item_remove(lp->ll_list, lp->ll_li); } else { // unlet a Dictionary item. dict_T *d = lp->ll_dict; @@ -2992,7 +2958,7 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) if (watched) { dictwatcher_notify(d, key, NULL, &oldtv); - clear_tv(&oldtv); + tv_clear(&oldtv); xfree(key); } } @@ -3001,6 +2967,8 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) return ret; } +// TODO(ZyX-I): move to eval/ex_cmds + /* * "unlet" a variable. Return OK if it existed, FAIL if not. * When "forceit" is TRUE don't complain if the variable doesn't exist. @@ -3060,7 +3028,7 @@ int do_unlet(char_u *name, int forceit) if (watched) { dictwatcher_notify(dict, (char *)varname, NULL, &oldtv); - clear_tv(&oldtv); + tv_clear(&oldtv); } return OK; } @@ -3071,12 +3039,15 @@ int do_unlet(char_u *name, int forceit) return FAIL; } +// TODO(ZyX-I): move to eval/ex_cmds + /* * Lock or unlock variable indicated by "lp". * "deep" is the levels to go (-1 for unlimited); * "lock" is TRUE for ":lockvar", FALSE for ":unlockvar". */ -static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock) +static int do_lock_var(lval_T *lp, char_u *name_end, const int deep, + const bool lock) { int ret = OK; @@ -3104,117 +3075,28 @@ static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock) } else { di->di_flags &= ~DI_FLAGS_LOCK; } - item_lock(&di->di_tv, deep, lock); + tv_item_lock(&di->di_tv, deep, lock); } } else if (lp->ll_range) { listitem_T *li = lp->ll_li; /* (un)lock a range of List items. */ while (li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { - item_lock(&li->li_tv, deep, lock); + tv_item_lock(&li->li_tv, deep, lock); li = li->li_next; ++lp->ll_n1; } } else if (lp->ll_list != NULL) { // (un)lock a List item. - item_lock(&lp->ll_li->li_tv, deep, lock); + tv_item_lock(&lp->ll_li->li_tv, deep, lock); } else { // (un)lock a Dictionary item. - item_lock(&lp->ll_di->di_tv, deep, lock); + tv_item_lock(&lp->ll_di->di_tv, deep, lock); } return ret; } -/* - * Lock or unlock an item. "deep" is nr of levels to go. - */ -static void item_lock(typval_T *tv, int deep, int lock) -{ - static int recurse = 0; - - if (recurse >= DICT_MAXNEST) { - EMSG(_("E743: variable nested too deep for (un)lock")); - return; - } - if (deep == 0) { - return; - } - recurse++; - - // lock/unlock the item itself -#define CHANGE_LOCK(var, lock) \ - do { \ - var = ((VarLockStatus[]) { \ - [VAR_UNLOCKED] = (lock ? VAR_LOCKED : VAR_UNLOCKED), \ - [VAR_LOCKED] = (lock ? VAR_LOCKED : VAR_UNLOCKED), \ - [VAR_FIXED] = VAR_FIXED, \ - })[var]; \ - } while (0) - CHANGE_LOCK(tv->v_lock, lock); - - switch (tv->v_type) { - case VAR_LIST: { - list_T *const l = tv->vval.v_list; - if (l != NULL) { - CHANGE_LOCK(l->lv_lock, lock); - if (deep < 0 || deep > 1) { - // Recursive: lock/unlock the items the List contains. - for (listitem_T *li = l->lv_first; li != NULL; li = li->li_next) { - item_lock(&li->li_tv, deep - 1, lock); - } - } - } - break; - } - case VAR_DICT: { - dict_T *const d = tv->vval.v_dict; - if (d != NULL) { - CHANGE_LOCK(d->dv_lock, lock); - if (deep < 0 || deep > 1) { - // Recursive: lock/unlock the items the List contains. - int todo = (int)d->dv_hashtab.ht_used; - for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) { - if (!HASHITEM_EMPTY(hi)) { - todo--; - item_lock(&HI2DI(hi)->di_tv, deep - 1, lock); - } - } - } - } - break; - } - case VAR_NUMBER: - case VAR_FLOAT: - case VAR_STRING: - case VAR_FUNC: - case VAR_PARTIAL: - case VAR_SPECIAL: { - break; - } - case VAR_UNKNOWN: { - assert(false); - } - } -#undef CHANGE_LOCK - recurse--; -} - -/* - * Return TRUE if typeval "tv" is locked: Either that value is locked itself - * or it refers to a List or Dictionary that is locked. - */ -static int tv_islocked(typval_T *tv) -{ - return (tv->v_lock & VAR_LOCKED) - || (tv->v_type == VAR_LIST - && tv->vval.v_list != NULL - && (tv->vval.v_list->lv_lock & VAR_LOCKED)) - || (tv->v_type == VAR_DICT - && tv->vval.v_dict != NULL - && (tv->vval.v_dict->dv_lock & VAR_LOCKED)); -} - /* * Delete all "menutrans_" variables. */ @@ -3343,6 +3225,7 @@ char_u *get_user_var_name(expand_T *xp, int idx) return NULL; } +// TODO(ZyX-I): move to eval/expressions /// Return TRUE if "pat" matches "text". /// Does not use 'cpo' and always uses 'magic'. @@ -3379,6 +3262,8 @@ typedef enum { , TYPE_NOMATCH /* !~ */ } exptype_T; +// TODO(ZyX-I): move to eval/expressions + /* * 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 @@ -3400,15 +3285,15 @@ static int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) p = skipwhite(arg); ret = eval1(&p, rettv, evaluate); if (ret == FAIL || !ends_excmd(*p)) { - if (ret != FAIL) - clear_tv(rettv); - /* - * Report the invalid expression unless the expression evaluation has - * been cancelled due to an aborting error, an interrupt, or an - * exception. - */ - if (!aborting()) - EMSG2(_(e_invexpr2), arg); + if (ret != FAIL) { + tv_clear(rettv); + } + // Report the invalid expression unless the expression evaluation has + // been cancelled due to an aborting error, an interrupt, or an + // exception. + if (!aborting()) { + emsgf(_(e_invexpr2), arg); + } ret = FAIL; } if (nextcmd != NULL) @@ -3417,6 +3302,8 @@ static int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) return ret; } +// TODO(ZyX-I): move to eval/expressions + /* * Handle top level expression: * expr2 ? expr1 : expr1 @@ -3442,13 +3329,15 @@ static int eval1(char_u **arg, typval_T *rettv, int evaluate) if ((*arg)[0] == '?') { result = FALSE; if (evaluate) { - int error = FALSE; + bool error = false; - if (get_tv_number_chk(rettv, &error) != 0) - result = TRUE; - clear_tv(rettv); - if (error) + if (get_tv_number_chk(rettv, &error) != 0) { + result = true; + } + tv_clear(rettv); + if (error) { return FAIL; + } } /* @@ -3462,9 +3351,10 @@ static int eval1(char_u **arg, typval_T *rettv, int evaluate) * Check for the ":". */ if ((*arg)[0] != ':') { - EMSG(_("E109: Missing ':' after '?'")); - if (evaluate && result) - clear_tv(rettv); + emsgf(_("E109: Missing ':' after '?'")); + if (evaluate && result) { + tv_clear(rettv); + } return FAIL; } @@ -3472,9 +3362,10 @@ static int eval1(char_u **arg, typval_T *rettv, int evaluate) * Get the third variable. */ *arg = skipwhite(*arg + 1); - if (eval1(arg, &var2, evaluate && !result) == FAIL) { /* recursive! */ - if (evaluate && result) - clear_tv(rettv); + if (eval1(arg, &var2, evaluate && !result) == FAIL) { // Recursive! + if (evaluate && result) { + tv_clear(rettv); + } return FAIL; } if (evaluate && !result) @@ -3484,6 +3375,8 @@ static int eval1(char_u **arg, typval_T *rettv, int evaluate) return OK; } +// TODO(ZyX-I): move to eval/expressions + /* * Handle first level expression: * expr2 || expr2 || expr2 logical OR @@ -3498,7 +3391,7 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate) typval_T var2; long result; int first; - int error = FALSE; + bool error = false; /* * Get the first variable. @@ -3513,12 +3406,14 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate) result = FALSE; while ((*arg)[0] == '|' && (*arg)[1] == '|') { if (evaluate && first) { - if (get_tv_number_chk(rettv, &error) != 0) - result = TRUE; - clear_tv(rettv); - if (error) + if (get_tv_number_chk(rettv, &error) != 0) { + result = true; + } + tv_clear(rettv); + if (error) { return FAIL; - first = FALSE; + } + first = false; } /* @@ -3532,11 +3427,13 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate) * Compute the result. */ if (evaluate && !result) { - if (get_tv_number_chk(&var2, &error) != 0) - result = TRUE; - clear_tv(&var2); - if (error) + if (get_tv_number_chk(&var2, &error) != 0) { + result = true; + } + tv_clear(&var2); + if (error) { return FAIL; + } } if (evaluate) { rettv->v_type = VAR_NUMBER; @@ -3547,6 +3444,8 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate) return OK; } +// TODO(ZyX-I): move to eval/expressions + /* * Handle second level expression: * expr3 && expr3 && expr3 logical AND @@ -3561,7 +3460,7 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate) typval_T var2; long result; int first; - int error = FALSE; + bool error = false; /* * Get the first variable. @@ -3576,12 +3475,14 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate) result = TRUE; while ((*arg)[0] == '&' && (*arg)[1] == '&') { if (evaluate && first) { - if (get_tv_number_chk(rettv, &error) == 0) - result = FALSE; - clear_tv(rettv); - if (error) + if (get_tv_number_chk(rettv, &error) == 0) { + result = false; + } + tv_clear(rettv); + if (error) { return FAIL; - first = FALSE; + } + first = false; } /* @@ -3595,11 +3496,13 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate) * Compute the result. */ if (evaluate && result) { - if (get_tv_number_chk(&var2, &error) == 0) - result = FALSE; - clear_tv(&var2); - if (error) + if (get_tv_number_chk(&var2, &error) == 0) { + result = false; + } + tv_clear(&var2); + if (error) { return FAIL; + } } if (evaluate) { rettv->v_type = VAR_NUMBER; @@ -3610,6 +3513,8 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate) return OK; } +// TODO(ZyX-I): move to eval/expressions + /* * Handle third level expression: * var1 == var2 @@ -3706,7 +3611,7 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) */ *arg = skipwhite(p + len); if (eval5(arg, &var2, evaluate) == FAIL) { - clear_tv(rettv); + tv_clear(rettv); return FAIL; } @@ -3728,15 +3633,15 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) } else { EMSG(_("E692: Invalid operation for List")); } - clear_tv(rettv); - clear_tv(&var2); + tv_clear(rettv); + tv_clear(&var2); return FAIL; } else { - /* Compare two Lists for being equal or unequal. */ - n1 = list_equal(rettv->vval.v_list, var2.vval.v_list, - ic, FALSE); - if (type == TYPE_NEQUAL) + // Compare two Lists for being equal or unequal. + n1 = tv_list_equal(rettv->vval.v_list, var2.vval.v_list, ic, false); + if (type == TYPE_NEQUAL) { n1 = !n1; + } } } else if (rettv->v_type == VAR_DICT || var2.v_type == VAR_DICT) { if (type_is) { @@ -3750,8 +3655,8 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) EMSG(_("E735: Can only compare Dictionary with Dictionary")); else EMSG(_("E736: Invalid operation for Dictionary")); - clear_tv(rettv); - clear_tv(&var2); + tv_clear(rettv); + tv_clear(&var2); return FAIL; } else { /* Compare two Dictionaries for being equal or unequal. */ @@ -3765,8 +3670,8 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) || var2.v_type == VAR_PARTIAL) { if (type != TYPE_EQUAL && type != TYPE_NEQUAL) { EMSG(_("E694: Invalid operation for Funcrefs")); - clear_tv(rettv); - clear_tv(&var2); + tv_clear(rettv); + tv_clear(&var2); return FAIL; } if ((rettv->v_type == VAR_PARTIAL @@ -3868,8 +3773,8 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) case TYPE_UNKNOWN: break; /* avoid gcc warning */ } } - clear_tv(rettv); - clear_tv(&var2); + tv_clear(rettv); + tv_clear(&var2); rettv->v_type = VAR_NUMBER; rettv->vval.v_number = n1; } @@ -3878,6 +3783,8 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) return OK; } +// TODO(ZyX-I): move to eval/expressions + /* * Handle fourth level expression: * + number addition @@ -3925,7 +3832,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) * without evaluating the 2nd operand. So check before to avoid * side effects after an error. */ if (evaluate && get_tv_string_chk(rettv) == NULL) { - clear_tv(rettv); + tv_clear(rettv); return FAIL; } } @@ -3935,7 +3842,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) */ *arg = skipwhite(*arg + 1); if (eval6(arg, &var2, evaluate, op == '.') == FAIL) { - clear_tv(rettv); + tv_clear(rettv); return FAIL; } @@ -3946,28 +3853,28 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) if (op == '.') { s1 = get_tv_string_buf(rettv, buf1); /* already checked */ s2 = get_tv_string_buf_chk(&var2, buf2); - if (s2 == NULL) { /* type error ? */ - clear_tv(rettv); - clear_tv(&var2); + if (s2 == NULL) { // Type error ? + tv_clear(rettv); + tv_clear(&var2); return FAIL; } p = concat_str(s1, s2); - clear_tv(rettv); + tv_clear(rettv); rettv->v_type = VAR_STRING; rettv->vval.v_string = p; } else if (op == '+' && rettv->v_type == VAR_LIST && var2.v_type == VAR_LIST) { - /* concatenate Lists */ - if (list_concat(rettv->vval.v_list, var2.vval.v_list, - &var3) == FAIL) { - clear_tv(rettv); - clear_tv(&var2); + // Concatenate Lists. + if (tv_list_concat(rettv->vval.v_list, var2.vval.v_list, &var3) + == FAIL) { + tv_clear(rettv); + tv_clear(&var2); return FAIL; } - clear_tv(rettv); + tv_clear(rettv); *rettv = var3; } else { - int error = FALSE; + bool error = false; if (rettv->v_type == VAR_FLOAT) { f1 = rettv->vval.v_float; @@ -3978,7 +3885,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) /* This can only happen for "list + non-list". For * "non-list + ..." or "something - ...", we returned * before evaluating the 2nd operand. */ - clear_tv(rettv); + tv_clear(rettv); return FAIL; } if (var2.v_type == VAR_FLOAT) @@ -3990,14 +3897,14 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) } else { n2 = get_tv_number_chk(&var2, &error); if (error) { - clear_tv(rettv); - clear_tv(&var2); + tv_clear(rettv); + tv_clear(&var2); return FAIL; } if (rettv->v_type == VAR_FLOAT) f2 = n2; } - clear_tv(rettv); + tv_clear(rettv); /* If there is a float on either side the result is a float. */ if (rettv->v_type == VAR_FLOAT || var2.v_type == VAR_FLOAT) { @@ -4016,12 +3923,14 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) rettv->vval.v_number = n1; } } - clear_tv(&var2); + tv_clear(&var2); } } return OK; } +// TODO(ZyX-I): move to eval/expressions + /* * Handle fifth level expression: * * number multiplication @@ -4046,7 +3955,7 @@ eval6 ( long n1, n2; int use_float = FALSE; float_T f1 = 0, f2; - int error = FALSE; + bool error = false; /* * Get the first variable. @@ -4069,11 +3978,13 @@ eval6 ( n1 = 0; } else n1 = get_tv_number_chk(rettv, &error); - clear_tv(rettv); - if (error) + tv_clear(rettv); + if (error) { return FAIL; - } else + } + } else { n1 = 0; + } /* * Get the second variable. @@ -4092,11 +4003,13 @@ eval6 ( n2 = 0; } else { n2 = get_tv_number_chk(&var2, &error); - clear_tv(&var2); - if (error) + tv_clear(&var2); + if (error) { return FAIL; - if (use_float) + } + if (use_float) { f2 = n2; + } } /* @@ -4154,6 +4067,8 @@ eval6 ( return OK; } +// TODO(ZyX-I): move to eval/expressions + // Handle sixth level expression: // number number constant // "string" string constant @@ -4192,7 +4107,7 @@ static int eval7( int ret = OK; char_u *alias; - // Initialise variable so that clear_tv() can't mistake this for a + // Initialise variable so that tv_clear() can't mistake this for a // string and free a string that isn't there. rettv->v_type = VAR_UNKNOWN; @@ -4308,7 +4223,7 @@ static int eval7( ++*arg; } else if (ret == OK) { EMSG(_("E110: Missing ')'")); - clear_tv(rettv); + tv_clear(rettv); ret = FAIL; } break; @@ -4349,7 +4264,7 @@ static int eval7( // get_func_tv, but it's needed in handle_subscript() to parse // what follows. So set it here. if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') { - rettv->vval.v_string = empty_string; + rettv->vval.v_string = (char_u *)tv_empty_string; rettv->v_type = VAR_FUNC; } @@ -4358,7 +4273,7 @@ static int eval7( // an exception was thrown but not caught. if (aborting()) { if (ret == OK) { - clear_tv(rettv); + tv_clear(rettv); } ret = FAIL; } @@ -4382,7 +4297,7 @@ static int eval7( // Apply logical NOT and unary '-', from right to left, ignore '+'. if (ret == OK && evaluate && end_leader > start_leader) { - int error = false; + bool error = false; int val = 0; float_T f = 0.0; @@ -4392,7 +4307,7 @@ static int eval7( val = get_tv_number_chk(rettv, &error); } if (error) { - clear_tv(rettv); + tv_clear(rettv); ret = FAIL; } else { while (end_leader > start_leader) { @@ -4412,10 +4327,10 @@ static int eval7( } } if (rettv->v_type == VAR_FLOAT) { - clear_tv(rettv); + tv_clear(rettv); rettv->vval.v_float = f; } else { - clear_tv(rettv); + tv_clear(rettv); rettv->v_type = VAR_NUMBER; rettv->vval.v_number = val; } @@ -4425,6 +4340,8 @@ static int eval7( return ret; } +// TODO(ZyX-I): move to eval/expressions + /* * Evaluate an "[expr]" or "[expr:expr]" index. Also "dict.key". * "*arg" points to the '[' or '.'. @@ -4499,13 +4416,13 @@ eval_index ( * Get the (first) variable from inside the []. */ *arg = skipwhite(*arg + 1); - if (**arg == ':') - empty1 = TRUE; - else if (eval1(arg, &var1, evaluate) == FAIL) /* recursive! */ + if (**arg == ':') { + empty1 = true; + } else if (eval1(arg, &var1, evaluate) == FAIL) { // Recursive! return FAIL; - else if (evaluate && get_tv_string_chk(&var1) == NULL) { - /* not a number or string */ - clear_tv(&var1); + } else if (evaluate && get_tv_string_chk(&var1) == NULL) { + // Not a number or string. + tv_clear(&var1); return FAIL; } @@ -4515,28 +4432,32 @@ eval_index ( if (**arg == ':') { range = TRUE; *arg = skipwhite(*arg + 1); - if (**arg == ']') - empty2 = TRUE; - else if (eval1(arg, &var2, evaluate) == FAIL) { /* recursive! */ - if (!empty1) - clear_tv(&var1); + if (**arg == ']') { + empty2 = true; + } else if (eval1(arg, &var2, evaluate) == FAIL) { // Recursive! + if (!empty1) { + tv_clear(&var1); + } return FAIL; } else if (evaluate && get_tv_string_chk(&var2) == NULL) { - /* not a number or string */ - if (!empty1) - clear_tv(&var1); - clear_tv(&var2); + // Not a number or string. + if (!empty1) { + tv_clear(&var1); + } + tv_clear(&var2); return FAIL; } } /* Check for the ']'. */ if (**arg != ']') { - if (verbose) - EMSG(_(e_missbrac)); - clear_tv(&var1); - if (range) - clear_tv(&var2); + if (verbose) { + emsgf(_(e_missbrac)); + } + tv_clear(&var1); + if (range) { + tv_clear(&var2); + } return FAIL; } *arg = skipwhite(*arg + 1); /* skip the ']' */ @@ -4546,14 +4467,14 @@ eval_index ( n1 = 0; if (!empty1 && rettv->v_type != VAR_DICT) { n1 = get_tv_number(&var1); - clear_tv(&var1); + tv_clear(&var1); } if (range) { if (empty2) n2 = -1; else { n2 = get_tv_number(&var2); - clear_tv(&var2); + tv_clear(&var2); } } @@ -4587,15 +4508,16 @@ eval_index ( else s = vim_strnsave(s + n1, 1); } - clear_tv(rettv); + tv_clear(rettv); rettv->v_type = VAR_STRING; rettv->vval.v_string = s; break; case VAR_LIST: - len = list_len(rettv->vval.v_list); - if (n1 < 0) + len = tv_list_len(rettv->vval.v_list); + if (n1 < 0) { n1 = len + n1; + } if (!empty1 && (n1 < 0 || n1 >= len)) { /* For a range we allow invalid values and return an empty * list. A list index out of range is an error. */ @@ -4616,29 +4538,31 @@ eval_index ( n2 = len - 1; if (!empty2 && (n2 < 0 || n2 + 1 < n1)) n2 = -1; - l = list_alloc(); - item = list_find(rettv->vval.v_list, n1); + l = tv_list_alloc(); + item = tv_list_find(rettv->vval.v_list, n1); while (n1++ <= n2) { - list_append_tv(l, &item->li_tv); + tv_list_append_tv(l, &item->li_tv); item = item->li_next; } - clear_tv(rettv); + tv_clear(rettv); rettv->v_type = VAR_LIST; rettv->vval.v_list = l; ++l->lv_refcount; } else { - copy_tv(&list_find(rettv->vval.v_list, n1)->li_tv, &var1); - clear_tv(rettv); + copy_tv(&tv_list_find(rettv->vval.v_list, n1)->li_tv, &var1); + tv_clear(rettv); *rettv = var1; } break; case VAR_DICT: if (range) { - if (verbose) - EMSG(_(e_dictrange)); - if (len == -1) - clear_tv(&var1); + if (verbose) { + emsgf(_(e_dictrange)); + } + if (len == -1) { + tv_clear(&var1); + } return FAIL; } { @@ -4647,22 +4571,25 @@ eval_index ( if (len == -1) { key = get_tv_string_chk(&var1); if (key == NULL) { - clear_tv(&var1); + tv_clear(&var1); return FAIL; } } item = dict_find(rettv->vval.v_dict, key, (int)len); - if (item == NULL && verbose) - EMSG2(_(e_dictkey), key); - if (len == -1) - clear_tv(&var1); - if (item == NULL) + if (item == NULL && verbose) { + emsgf(_(e_dictkey), key); + } + if (len == -1) { + tv_clear(&var1); + } + if (item == NULL) { return FAIL; + } copy_tv(&item->di_tv, &var1); - clear_tv(rettv); + tv_clear(rettv); *rettv = var1; } break; @@ -4678,6 +4605,8 @@ eval_index ( return OK; } +// TODO(ZyX-I): move to eval/executor + /// Get an option value /// /// @param[in,out] arg Points to the '&' or '+' before the option name. Is @@ -4931,10 +4860,12 @@ char_u *partial_name(partial_T *pt) return pt->pt_func->uf_name; } +// TODO(ZyX-I): Move to eval/typval.h + static void partial_free(partial_T *pt) { for (int i = 0; i < pt->pt_argc; i++) { - clear_tv(&pt->pt_argv[i]); + tv_clear(&pt->pt_argv[i]); } xfree(pt->pt_argv); dict_unref(pt->pt_dict); @@ -4947,6 +4878,8 @@ static void partial_free(partial_T *pt) xfree(pt); } +// TODO(ZyX-I): Move to eval/typval.h + /// Unreference a closure: decrement the reference count and free it when it /// becomes zero. void partial_unref(partial_T *pt) @@ -4965,7 +4898,7 @@ static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) listitem_T *item; if (evaluate) { - l = list_alloc(); + l = tv_list_alloc(); } *arg = skipwhite(*arg + 1); @@ -4973,10 +4906,10 @@ static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) if (eval1(arg, &tv, evaluate) == FAIL) /* recursive! */ goto failret; if (evaluate) { - item = listitem_alloc(); + item = tv_list_item_alloc(); item->li_tv = tv; item->li_tv.v_lock = 0; - list_append(l, item); + tv_list_append(l, item); } if (**arg == ']') @@ -4992,7 +4925,7 @@ static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) EMSG2(_("E697: Missing end of List ']': %s"), *arg); failret: if (evaluate) { - list_free(l); + tv_list_free(l); } return FAIL; } @@ -5007,142 +4940,8 @@ failret: return OK; } -/* - * Allocate an empty header for a list. - * Caller should take care of the reference count. - */ -list_T *list_alloc(void) FUNC_ATTR_NONNULL_RET -{ - list_T *list = xcalloc(1, sizeof(list_T)); - /* Prepend the list to the list of lists for garbage collection. */ - if (first_list != NULL) - first_list->lv_used_prev = list; - list->lv_used_prev = NULL; - list->lv_used_next = first_list; - first_list = list; - return list; -} - -/* - * Allocate an empty list for a return value. - */ -static list_T *rettv_list_alloc(typval_T *rettv) -{ - list_T *l = list_alloc(); - rettv->vval.v_list = l; - rettv->v_type = VAR_LIST; - rettv->v_lock = VAR_UNLOCKED; - l->lv_refcount++; - return l; -} - -/// Unreference a list: decrement the reference count and free it when it -/// becomes zero. -void list_unref(list_T *l) { - if (l != NULL && --l->lv_refcount <= 0) { - list_free(l); - } -} - -/// Free a list, including all items it points to. -/// Ignores the reference count. -static void list_free_contents(list_T *l) { - listitem_T *item; - - for (item = l->lv_first; item != NULL; item = l->lv_first) { - // Remove the item before deleting it. - l->lv_first = item->li_next; - clear_tv(&item->li_tv); - xfree(item); - } -} - -static void list_free_list(list_T *l) { - // Remove the list from the list of lists for garbage collection. - if (l->lv_used_prev == NULL) { - first_list = l->lv_used_next; - } else { - l->lv_used_prev->lv_used_next = l->lv_used_next; - } - if (l->lv_used_next != NULL) { - l->lv_used_next->lv_used_prev = l->lv_used_prev; - } - - xfree(l); -} - -void list_free(list_T *l) { - if (!in_free_unref_items) { - list_free_contents(l); - list_free_list(l); - } -} - -/* - * Allocate a list item. - * It is not initialized, don't forget to set v_lock. - */ -listitem_T *listitem_alloc(void) FUNC_ATTR_NONNULL_RET -{ - return xmalloc(sizeof(listitem_T)); -} - -/* - * Free a list item. Also clears the value. Does not notify watchers. - */ -void listitem_free(listitem_T *item) -{ - clear_tv(&item->li_tv); - xfree(item); -} - -/* - * Remove a list item from a List and free it. Also clears the value. - */ -void listitem_remove(list_T *l, listitem_T *item) -{ - vim_list_remove(l, item, item); - listitem_free(item); -} - -/* - * Get the number of items in a list. - */ -static long list_len(list_T *l) -{ - if (l == NULL) - return 0L; - return l->lv_len; -} - -/* - * Return TRUE when two lists have exactly the same values. - */ -static int -list_equal ( - list_T *l1, - list_T *l2, - int ic, /* ignore case for strings */ - int recursive /* TRUE when used recursively */ -) -{ - listitem_T *item1, *item2; - - if (l1 == NULL || l2 == NULL) - return FALSE; - if (l1 == l2) - return TRUE; - if (list_len(l1) != list_len(l2)) - return FALSE; - - for (item1 = l1->lv_first, item2 = l2->lv_first; - item1 != NULL && item2 != NULL; - item1 = item1->li_next, item2 = item2->li_next) - if (!tv_equal(&item1->li_tv, &item2->li_tv, ic, recursive)) - return FALSE; - return item1 == NULL && item2 == NULL; -} +// TODO(ZyX-I): move to eval/typval /* * Return the dictitem that an entry in a hashtable points to. @@ -5152,6 +4951,8 @@ dictitem_T *dict_lookup(hashitem_T *hi) return HI2DI(hi); } +// TODO(ZyX-I): move to eval/typval + /* * Return TRUE when two dictionaries have exactly the same key/values. */ @@ -5255,8 +5056,7 @@ static bool func_equal( * Compares the items just like "==" would compare them, but strings and * numbers are different. Floats and numbers are also different. */ -static int -tv_equal ( +int tv_equal( typval_T *tv1, typval_T *tv2, int ic, /* ignore case */ @@ -5298,9 +5098,9 @@ tv_equal ( switch (tv1->v_type) { case VAR_LIST: - ++recursive_cnt; - r = list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, TRUE); - --recursive_cnt; + recursive_cnt++; + r = tv_list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, true); + recursive_cnt--; return r; case VAR_DICT: @@ -5335,466 +5135,6 @@ tv_equal ( return false; } -/* - * Locate item with index "n" in list "l" and return it. - * A negative index is counted from the end; -1 is the last item. - * Returns NULL when "n" is out of range. - */ -listitem_T *list_find(list_T *l, long n) -{ - listitem_T *item; - long idx; - - if (l == NULL) - return NULL; - - /* Negative index is relative to the end. */ - if (n < 0) - n = l->lv_len + n; - - /* Check for index out of range. */ - if (n < 0 || n >= l->lv_len) - return NULL; - - /* When there is a cached index may start search from there. */ - if (l->lv_idx_item != NULL) { - if (n < l->lv_idx / 2) { - /* closest to the start of the list */ - item = l->lv_first; - idx = 0; - } else if (n > (l->lv_idx + l->lv_len) / 2) { - /* closest to the end of the list */ - item = l->lv_last; - idx = l->lv_len - 1; - } else { - /* closest to the cached index */ - item = l->lv_idx_item; - idx = l->lv_idx; - } - } else { - if (n < l->lv_len / 2) { - /* closest to the start of the list */ - item = l->lv_first; - idx = 0; - } else { - /* closest to the end of the list */ - item = l->lv_last; - idx = l->lv_len - 1; - } - } - - while (n > idx) { - /* search forward */ - item = item->li_next; - ++idx; - } - while (n < idx) { - /* search backward */ - item = item->li_prev; - --idx; - } - - /* cache the used index */ - l->lv_idx = idx; - l->lv_idx_item = item; - - return item; -} - -/* - * Get list item "l[idx]" as a number. - */ -static long -list_find_nr ( - list_T *l, - long idx, - int *errorp /* set to TRUE when something wrong */ -) -{ - listitem_T *li; - - li = list_find(l, idx); - if (li == NULL) { - if (errorp != NULL) - *errorp = TRUE; - return -1L; - } - return get_tv_number_chk(&li->li_tv, errorp); -} - -/* - * Get list item "l[idx - 1]" as a string. Returns NULL for failure. - */ -char_u *list_find_str(list_T *l, long idx) -{ - listitem_T *li; - - li = list_find(l, idx - 1); - if (li == NULL) { - EMSGN(_(e_listidx), idx); - return NULL; - } - return get_tv_string(&li->li_tv); -} - -/* - * Locate "item" list "l" and return its index. - * Returns -1 when "item" is not in the list. - */ -static long list_idx_of_item(list_T *l, listitem_T *item) -{ - long idx = 0; - listitem_T *li; - - if (l == NULL) - return -1; - idx = 0; - for (li = l->lv_first; li != NULL && li != item; li = li->li_next) - ++idx; - if (li == NULL) - return -1; - return idx; -} - -/* - * Append item "item" to the end of list "l". - */ -void list_append(list_T *l, listitem_T *item) -{ - if (l->lv_last == NULL) { - /* empty list */ - l->lv_first = item; - l->lv_last = item; - item->li_prev = NULL; - } else { - l->lv_last->li_next = item; - item->li_prev = l->lv_last; - l->lv_last = item; - } - ++l->lv_len; - item->li_next = NULL; -} - -/* - * Append typval_T "tv" to the end of list "l". - */ -void list_append_tv(list_T *l, typval_T *tv) -{ - listitem_T *li = listitem_alloc(); - copy_tv(tv, &li->li_tv); - list_append(l, li); -} - -/* - * Add a list to a list. - */ -void list_append_list(list_T *list, list_T *itemlist) -{ - listitem_T *li = listitem_alloc(); - - li->li_tv.v_type = VAR_LIST; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_list = itemlist; - list_append(list, li); - ++itemlist->lv_refcount; -} - -/* - * Add a dictionary to a list. Used by getqflist(). - */ -void list_append_dict(list_T *list, dict_T *dict) -{ - listitem_T *li = listitem_alloc(); - - li->li_tv.v_type = VAR_DICT; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_dict = dict; - list_append(list, li); - ++dict->dv_refcount; -} - -/// Make a copy of "str" and append it as an item to list "l" -/// -/// @param[out] l List to append to. -/// @param[in] str String to append. -/// @param[in] len Length of the appended string. May be negative, in this -/// case string is considered to be usual zero-terminated -/// string. -void list_append_string(list_T *l, const char_u *str, int len) - FUNC_ATTR_NONNULL_ARG(1) -{ - if (str == NULL) { - list_append_allocated_string(l, NULL); - } else { - list_append_allocated_string(l, (len >= 0 - ? xmemdupz((char *) str, len) - : xstrdup((char *) str))); - } -} - -/// Append given string to the list -/// -/// Unlike list_append_string this function does not copy the string. -/// -/// @param[out] l List to append to. -/// @param[in] str String to append. -void list_append_allocated_string(list_T *l, char *const str) - FUNC_ATTR_NONNULL_ARG(1) -{ - listitem_T *li = listitem_alloc(); - - list_append(l, li); - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = (char_u *) str; -} - -/* - * Append "n" to list "l". - */ -void list_append_number(list_T *l, varnumber_T n) -{ - listitem_T *li = listitem_alloc(); - li->li_tv.v_type = VAR_NUMBER; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_number = n; - list_append(l, li); -} - -/* - * Insert typval_T "tv" in list "l" before "item". - * If "item" is NULL append at the end. - */ -void list_insert_tv(list_T *l, typval_T *tv, listitem_T *item) -{ - listitem_T *ni = listitem_alloc(); - - copy_tv(tv, &ni->li_tv); - list_insert(l, ni, item); -} - -void list_insert(list_T *l, listitem_T *ni, listitem_T *item) -{ - if (item == NULL) - /* Append new item at end of list. */ - list_append(l, ni); - else { - /* Insert new item before existing item. */ - ni->li_prev = item->li_prev; - ni->li_next = item; - if (item->li_prev == NULL) { - l->lv_first = ni; - ++l->lv_idx; - } else { - item->li_prev->li_next = ni; - l->lv_idx_item = NULL; - } - item->li_prev = ni; - ++l->lv_len; - } -} - -/* - * Extend "l1" with "l2". - * If "bef" is NULL append at the end, otherwise insert before this item. - */ -static void list_extend(list_T *l1, list_T *l2, listitem_T *bef) -{ - listitem_T *item; - int todo = l2->lv_len; - - /* We also quit the loop when we have inserted the original item count of - * the list, avoid a hang when we extend a list with itself. */ - for (item = l2->lv_first; item != NULL && --todo >= 0; item = item->li_next) { - list_insert_tv(l1, &item->li_tv, bef); - } -} - -/* - * Concatenate lists "l1" and "l2" into a new list, stored in "tv". - * Return FAIL on failure to copy. - */ -static int list_concat(list_T *l1, list_T *l2, typval_T *tv) -{ - list_T *l; - - if (l1 == NULL || l2 == NULL) - return FAIL; - - /* make a copy of the first list. */ - l = list_copy(NULL, l1, false, 0); - if (l == NULL) - return FAIL; - tv->v_type = VAR_LIST; - tv->vval.v_list = l; - - /* append all items from the second list */ - list_extend(l, l2, NULL); - return OK; -} - -/// Make a copy of list -/// -/// @param[in] conv If non-NULL, then all internal strings will be converted. -/// @param[in] orig Original list to copy. -/// @param[in] deep If false, then shallow copy will be done. -/// @param[in] copyID See var_item_copy(). -/// -/// @return Copied list. May be NULL in case original list is NULL or some -/// failure happens. The refcount of the new list is set to 1. -static list_T *list_copy(const vimconv_T *const conv, - list_T *const orig, - const bool deep, - const int copyID) - FUNC_ATTR_WARN_UNUSED_RESULT -{ - listitem_T *item; - listitem_T *ni; - - if (orig == NULL) - return NULL; - - list_T *copy = list_alloc(); - if (copyID != 0) { - /* Do this before adding the items, because one of the items may - * refer back to this list. */ - orig->lv_copyID = copyID; - orig->lv_copylist = copy; - } - for (item = orig->lv_first; item != NULL && !got_int; - item = item->li_next) { - ni = listitem_alloc(); - if (deep) { - if (var_item_copy(conv, &item->li_tv, &ni->li_tv, deep, copyID) == FAIL) { - xfree(ni); - break; - } - } else - copy_tv(&item->li_tv, &ni->li_tv); - list_append(copy, ni); - } - ++copy->lv_refcount; - if (item != NULL) { - list_unref(copy); - copy = NULL; - } - - return copy; -} - -/// Remove items "item" to "item2" from list "l". -/// @warning Does not free the listitem or the value! -void vim_list_remove(list_T *l, listitem_T *item, listitem_T *item2) -{ - // notify watchers - for (listitem_T *ip = item; ip != NULL; ip = ip->li_next) { - --l->lv_len; - list_fix_watch(l, ip); - if (ip == item2) { - break; - } - } - - if (item2->li_next == NULL) { - l->lv_last = item->li_prev; - } else { - item2->li_next->li_prev = item->li_prev; - } - if (item->li_prev == NULL) { - l->lv_first = item2->li_next; - } else { - item->li_prev->li_next = item2->li_next; - } - l->lv_idx_item = NULL; -} - -typedef struct join_S { - char_u *s; - char_u *tofree; -} join_T; - -/// Join list into a string, helper function -/// -/// @param[out] gap Garray where result will be saved. -/// @param[in] l List to join. -/// @param[in] sep Used separator. -/// @param[in] join_gap Garray to keep each list item string. -/// -/// @return OK in case of success, FAIL otherwise. -static int list_join_inner(garray_T *const gap, list_T *const l, - const char *const sep, garray_T *const join_gap) - FUNC_ATTR_NONNULL_ALL -{ - int sumlen = 0; - bool first = true; - listitem_T *item; - - /* Stringify each item in the list. */ - for (item = l->lv_first; item != NULL && !got_int; item = item->li_next) { - char *s; - size_t len; - s = encode_tv2echo(&item->li_tv, &len); - if (s == NULL) { - return FAIL; - } - - sumlen += (int) len; - - join_T *const p = GA_APPEND_VIA_PTR(join_T, join_gap); - p->tofree = p->s = (char_u *) s; - - line_breakcheck(); - } - - /* Allocate result buffer with its total size, avoid re-allocation and - * multiple copy operations. Add 2 for a tailing ']' and NUL. */ - if (join_gap->ga_len >= 2) - sumlen += (int)STRLEN(sep) * (join_gap->ga_len - 1); - ga_grow(gap, sumlen + 2); - - for (int i = 0; i < join_gap->ga_len && !got_int; ++i) { - if (first) { - first = false; - } else { - ga_concat(gap, (const char_u *) sep); - } - const join_T *const p = ((const join_T *)join_gap->ga_data) + i; - - if (p->s != NULL) - ga_concat(gap, p->s); - line_breakcheck(); - } - - return OK; -} - -/// Join list into a string using given separator -/// -/// @param[out] gap Garray where result will be saved. -/// @param[in] l Joined list. -/// @param[in] sep Separator. -/// -/// @return OK in case of success, FAIL otherwise. -static int list_join(garray_T *const gap, list_T *const l, - const char *const sep) - FUNC_ATTR_NONNULL_ALL -{ - if (l->lv_len < 1) { - return OK; - } - - garray_T join_ga; - int retval; - - ga_init(&join_ga, (int)sizeof(join_T), l->lv_len); - retval = list_join_inner(gap, l, sep, &join_ga); - -# define FREE_JOIN_TOFREE(join) xfree((join)->tofree) - GA_DEEP_CLEAR(&join_ga, join_T, FREE_JOIN_TOFREE); - - return retval; -} - /// Get next (unique) copy ID /// /// Used for traversing nested structures e.g. when serializing them or garbage @@ -6035,7 +5375,7 @@ bool garbage_collect(bool testing) /// Free lists and dictionaries that are no longer referenced. /// -/// Note: This function may only be called from garbage_collect(). +/// @note This function may only be called from garbage_collect(). /// /// @param copyID Free lists/dictionaries that don't have this ID. /// @return true, if something was freed. @@ -6048,14 +5388,14 @@ static int free_unref_items(int copyID) // Let all "free" functions know that we are here. This means no // dictionaries, lists, or jobs are to be freed, because we will // do that here. - in_free_unref_items = true; + tv_in_free_unref_items = true; // PASS 1: free the contents of the items. We don't free the items // themselves yet, so that it is possible to decrement refcount counters. // Go through the list of dicts and free items without the copyID. // Don't free dicts that are referenced internally. - for (dict_T *dd = first_dict; dd != NULL; dd = dd->dv_used_next) { + for (dict_T *dd = gc_first_dict; dd != NULL; dd = dd->dv_used_next) { if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) { // Free the Dictionary and ordinary items it contains, but don't // recurse into Lists and Dictionaries, they will be in the list @@ -6068,36 +5408,36 @@ static int free_unref_items(int copyID) // Go through the list of lists and free items without the copyID. // But don't free a list that has a watcher (used in a for loop), these // are not referenced anywhere. - for (list_T *ll = first_list; ll != NULL; ll = ll->lv_used_next) { + for (list_T *ll = gc_first_list; ll != NULL; ll = ll->lv_used_next) { if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK) && ll->lv_watch == NULL) { // Free the List and ordinary items it contains, but don't recurse // into Lists and Dictionaries, they will be in the list of dicts // or list of lists. - list_free_contents(ll); + tv_list_free_contents(ll); did_free = true; } } // PASS 2: free the items themselves. - for (dd = first_dict; dd != NULL; dd = dd_next) { + for (dd = gc_first_dict; dd != NULL; dd = dd_next) { dd_next = dd->dv_used_next; if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) { dict_free_dict(dd); } } - for (ll = first_list; ll != NULL; ll = ll_next) { + for (ll = gc_first_list; ll != NULL; ll = ll_next) { ll_next = ll->lv_used_next; if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK) && ll->lv_watch == NULL) { // Free the List and ordinary items it contains, but don't recurse // into Lists and Dictionaries, they will be in the list of dicts // or list of lists. - list_free_list(ll); + tv_list_free_list(ll); } } - in_free_unref_items = false; + tv_in_free_unref_items = false; return did_free; } @@ -6369,12 +5709,13 @@ dict_T *dict_alloc(void) FUNC_ATTR_NONNULL_RET { dict_T *d = xmalloc(sizeof(dict_T)); - /* Add the dict to the list of dicts for garbage collection. */ - if (first_dict != NULL) - first_dict->dv_used_prev = d; - d->dv_used_next = first_dict; + // Add the dict to the list of dicts for garbage collection. + if (gc_first_dict != NULL) { + gc_first_dict->dv_used_prev = d; + } + d->dv_used_next = gc_first_dict; d->dv_used_prev = NULL; - first_dict = d; + gc_first_dict = d; hash_init(&d->dv_hashtab); d->dv_lock = VAR_UNLOCKED; @@ -6434,21 +5775,18 @@ void dict_unref(dict_T *d) /// Free a Dictionary, including all items it contains. /// Ignores the reference count. -static void dict_free_contents(dict_T *d) { - int todo; - hashitem_T *hi; - dictitem_T *di; - - - /* Lock the hashtab, we don't want it to resize while freeing items. */ +static void dict_free_contents(dict_T *const d) + FUNC_ATTR_NONNULL_ALL +{ + // Lock the hashtab, we don't want it to resize while freeing items. hash_lock(&d->dv_hashtab); assert(d->dv_hashtab.ht_locked > 0); - todo = (int)d->dv_hashtab.ht_used; - for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { + size_t todo = (int)d->dv_hashtab.ht_used; + for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) { if (!HASHITEM_EMPTY(hi)) { - /* Remove the item before deleting it, just in case there is - * something recursive causing trouble. */ - di = HI2DI(hi); + // Remove the item before deleting it, just in case there is + // something recursive causing trouble. + dictitem_T *const di = HI2DI(hi); hash_remove(&d->dv_hashtab, hi); dictitem_free(di); todo--; @@ -6465,10 +5803,12 @@ static void dict_free_contents(dict_T *d) { hash_clear(&d->dv_hashtab); } -static void dict_free_dict(dict_T *d) { +static void dict_free_dict(dict_T *d) + FUNC_ATTR_NONNULL_ALL +{ // Remove the dict from the list of dicts for garbage collection. if (d->dv_used_prev == NULL) { - first_dict = d->dv_used_next; + gc_first_dict = d->dv_used_next; } else { d->dv_used_prev->dv_used_next = d->dv_used_next; } @@ -6479,8 +5819,10 @@ static void dict_free_dict(dict_T *d) { xfree(d); } -void dict_free(dict_T *d) { - if (!in_free_unref_items) { +void dict_free(dict_T *d) + FUNC_ATTR_NONNULL_ALL +{ + if (!tv_in_free_unref_items) { dict_free_contents(d); dict_free_dict(d); } @@ -6536,7 +5878,7 @@ static void dictitem_remove(dict_T *dict, dictitem_T *item) */ void dictitem_free(dictitem_T *item) { - clear_tv(&item->di_tv); + tv_clear(&item->di_tv); if (item->di_flags & DI_FLAGS_ALLOC) { xfree(item); } @@ -6767,7 +6109,7 @@ static bool get_dict_callback(dict_T *d, char *key, Callback *result) copy_tv(&di->di_tv, &tv); set_selfdict(&tv, d); bool res = callback_from_typval(result, &tv); - clear_tv(&tv); + tv_clear(&tv); return res; } @@ -6845,34 +6187,35 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) goto failret; if (**arg != ':') { EMSG2(_("E720: Missing colon in Dictionary: %s"), *arg); - clear_tv(&tvkey); + tv_clear(&tvkey); goto failret; } if (evaluate) { key = get_tv_string_buf_chk(&tvkey, buf); if (key == NULL) { // "key" is NULL when get_tv_string_buf_chk() gave an errmsg - clear_tv(&tvkey); + tv_clear(&tvkey); goto failret; } } *arg = skipwhite(*arg + 1); - if (eval1(arg, &tv, evaluate) == FAIL) { /* recursive! */ - if (evaluate) - clear_tv(&tvkey); + if (eval1(arg, &tv, evaluate) == FAIL) { // Recursive! + if (evaluate) { + tv_clear(&tvkey); + } goto failret; } if (evaluate) { item = dict_find(d, key, -1); if (item != NULL) { EMSG2(_("E721: Duplicate key in Dictionary: \"%s\""), key); - clear_tv(&tvkey); - clear_tv(&tv); + tv_clear(&tvkey); + tv_clear(&tv); goto failret; } item = dictitem_alloc(key); - clear_tv(&tvkey); + tv_clear(&tvkey); item->di_tv = tv; item->di_tv.v_lock = 0; if (dict_add(d, item) == FAIL) { @@ -7391,8 +6734,9 @@ get_func_tv ( } } - while (--argcount >= 0) - clear_tv(&argvars[argcount]); + while (--argcount >= 0) { + tv_clear(&argvars[argcount]); + } *arg = skipwhite(argp); return ret; @@ -7688,7 +7032,7 @@ call_func( } while (argv_clear > 0) { - clear_tv(&argv[--argv_clear]); + tv_clear(&argv[--argv_clear]); } xfree(tofree); xfree(name); @@ -7804,7 +7148,7 @@ static void f_abs(typval_T *argvars, typval_T *rettv, FunPtr fptr) float_op_wrapper(argvars, rettv, (FunPtr)&fabs); } else { varnumber_T n; - int error = FALSE; + bool error = false; n = get_tv_number_chk(&argvars[0], &error); if (error) @@ -7829,7 +7173,7 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) const size_t arg_errmsg_len = strlen(arg_errmsg); if ((l = argvars[0].vval.v_list) != NULL && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { - list_append_tv(l, &argvars[1]); + tv_list_append_tv(l, &argvars[1]); copy_tv(&argvars[0], rettv); } } else @@ -7950,9 +7294,10 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = NULL; rettv->v_type = VAR_STRING; } else { - rettv_list_alloc(rettv); - for (idx = 0; idx < ARGCOUNT; ++idx) { - list_append_string(rettv->vval.v_list, alist_name(&ARGLIST[idx]), -1); + tv_list_alloc_ret(rettv); + for (idx = 0; idx < ARGCOUNT; idx++) { + tv_list_append_string(rettv->vval.v_list, + (const char *)alist_name(&ARGLIST[idx]), -1); } } } @@ -8024,10 +7369,10 @@ static void assert_error(garray_T *gap) if (vp->vv_type != VAR_LIST || vimvars[VV_ERRORS].vv_list == NULL) { // Make sure v:errors is a list. - set_vim_var_list(VV_ERRORS, list_alloc()); + set_vim_var_list(VV_ERRORS, tv_list_alloc()); } - list_append_string(vimvars[VV_ERRORS].vv_list, - gap->ga_data, gap->ga_len); + tv_list_append_string(vimvars[VV_ERRORS].vv_list, + (const char *)gap->ga_data, (ptrdiff_t)gap->ga_len); } static void assert_equal_common(typval_T *argvars, assert_type_T atype) @@ -8116,10 +7461,10 @@ static void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr) void assert_inrange(typval_T *argvars) { - int error = (int)false; - varnumber_T lower = get_tv_number_chk(&argvars[0], &error); - varnumber_T upper = get_tv_number_chk(&argvars[1], &error); - varnumber_T actual = get_tv_number_chk(&argvars[2], &error); + bool error = false; + const varnumber_T lower = get_tv_number_chk(&argvars[0], &error); + const varnumber_T upper = get_tv_number_chk(&argvars[1], &error); + const varnumber_T actual = get_tv_number_chk(&argvars[2], &error); if (error) { return; @@ -8129,8 +7474,9 @@ void assert_inrange(typval_T *argvars) prepare_assert_error(&ga); char msg[55]; - vim_snprintf(msg, sizeof(msg), "range %" PRId64 " - %" PRId64 ",", - (int64_t)lower, (int64_t)upper); + vim_snprintf(msg, sizeof(msg), + "range %" PRIdVARNUMBER " - %" PRIdVARNUMBER ",", + lower, upper); fill_assert_error(&ga, &argvars[3], (char_u *)msg, NULL, &argvars[2], ASSERT_INRANGE); assert_error(&ga); @@ -8141,7 +7487,7 @@ void assert_inrange(typval_T *argvars) // Common for assert_true() and assert_false(). static void assert_bool(typval_T *argvars, bool is_true) { - int error = (int)false; + bool error = false; garray_T ga; if ((argvars[0].v_type != VAR_NUMBER @@ -8363,7 +7709,7 @@ static void f_bufname(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int error = false; + bool error = false; char_u *name; rettv->vval.v_number = -1; @@ -8512,9 +7858,10 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, &dummy, true, partial, selfdict); } - /* Free the arguments. */ - while (argc > 0) - clear_tv(&argv[--argc]); + // Free the arguments. + while (argc > 0) { + tv_clear(&argv[--argc]); + } return r; } @@ -8707,8 +8054,8 @@ static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr) char_u buf2[NUMBUFLEN]; int def = 1; int type = VIM_GENERIC; - char_u *typestr; - int error = FALSE; + char_u *typestr; + bool error = false; message = get_tv_string_chk(&argvars[0]); if (message == NULL) @@ -8768,15 +8115,16 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) if ((l = argvars[0].vval.v_list) != NULL) { li = l->lv_first; if (argvars[2].v_type != VAR_UNKNOWN) { - int error = FALSE; + bool error = false; ic = get_tv_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { idx = get_tv_number_chk(&argvars[3], &error); if (!error) { - li = list_find(l, idx); - if (li == NULL) + li = tv_list_find(l, idx); + if (li == NULL) { EMSGN(_(e_listidx), idx); + } } } if (error) @@ -8793,7 +8141,7 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) hashitem_T *hi; if ((d = argvars[0].vval.v_dict) != NULL) { - int error = FALSE; + bool error = false; if (argvars[2].v_type != VAR_UNKNOWN) { ic = get_tv_number_chk(&argvars[2], &error); @@ -9349,7 +8697,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Handle d.key, l[idx], f(expr). n = (handle_subscript((const char **)&p, &tv, true, false) == OK); if (n) { - clear_tv(&tv); + tv_clear(&tv); } } } @@ -9372,8 +8720,8 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) char_u *errormsg; int options = WILD_SILENT|WILD_USE_NL|WILD_LIST_NOTFOUND; expand_T xpc; - int error = FALSE; - char_u *result; + bool error = false; + char_u *result; rettv->v_type = VAR_STRING; if (argvars[1].v_type != VAR_UNKNOWN @@ -9390,9 +8738,9 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) result = eval_vars(s, s, &len, NULL, &errormsg, NULL); --emsg_off; if (rettv->v_type == VAR_LIST) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (result != NULL) { - list_append_string(rettv->vval.v_list, result, -1); + tv_list_append_string(rettv->vval.v_list, (const char *)result, -1); } } else rettv->vval.v_string = result; @@ -9407,14 +8755,14 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) xpc.xp_context = EXPAND_FILES; if (p_wic) options += WILD_ICASE; - if (rettv->v_type == VAR_STRING) - rettv->vval.v_string = ExpandOne(&xpc, s, NULL, - options, WILD_ALL); - else { - rettv_list_alloc(rettv); + if (rettv->v_type == VAR_STRING) { + rettv->vval.v_string = ExpandOne(&xpc, s, NULL, options, WILD_ALL); + } else { + tv_list_alloc_ret(rettv); ExpandOne(&xpc, s, NULL, options, WILD_ALL_KEEP); for (int i = 0; i < xpc.xp_numfiles; i++) { - list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1); + tv_list_append_string(rettv->vval.v_list, + (const char *)xpc.xp_files[i], -1); } ExpandCleanup(&xpc); } @@ -9479,12 +8827,12 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action) copy_tv(&di1->di_tv, &oldtv); } - clear_tv(&di1->di_tv); + tv_clear(&di1->di_tv); copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv); if (watched) { dictwatcher_notify(d1, (char *)di1->di_key, &di1->di_tv, &oldtv); - clear_tv(&oldtv); + tv_clear(&oldtv); } } } @@ -9504,7 +8852,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *l1, *l2; listitem_T *item; long before; - int error = FALSE; + bool error = false; l1 = argvars[0].vval.v_list; l2 = argvars[1].vval.v_list; @@ -9515,10 +8863,10 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (error) return; /* type error; errmsg already given */ - if (before == l1->lv_len) + if (before == l1->lv_len) { item = NULL; - else { - item = list_find(l1, before); + } else { + item = tv_list_find(l1, before); if (item == NULL) { EMSGN(_(e_listidx), before); return; @@ -9526,7 +8874,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else item = NULL; - list_extend(l1, l2, item); + tv_list_extend(l1, l2, item); copy_tv(&argvars[0], rettv); } @@ -9616,8 +8964,8 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) char_u *p; char_u pathbuf[NUMBUFLEN]; int count = 1; - int first = TRUE; - int error = FALSE; + bool first = true; + bool error = false; rettv->vval.v_string = NULL; rettv->v_type = VAR_STRING; @@ -9632,13 +8980,14 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) if (*p != NUL) path = p; - if (argvars[2].v_type != VAR_UNKNOWN) + if (argvars[2].v_type != VAR_UNKNOWN) { count = get_tv_number_chk(&argvars[2], &error); + } } } if (count < 0) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); } if (*fname != NUL && !error) { @@ -9652,11 +9001,11 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) (find_what == FINDFILE_DIR ? (char_u *)"" : curbuf->b_p_sua)); - first = FALSE; - - if (fresult != NULL && rettv->v_type == VAR_LIST) - list_append_string(rettv->vval.v_list, fresult, -1); + first = false; + if (fresult != NULL && rettv->v_type == VAR_LIST) { + tv_list_append_string(rettv->vval.v_list, (const char *)fresult, -1); + } } while ((rettv->v_type == VAR_LIST || --count > 0) && fresult != NULL); } @@ -9736,9 +9085,10 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) vimvars[VV_KEY].vv_str = vim_strsave(di->di_key); int r = filter_map_one(&di->di_tv, expr, map, &rem); - clear_tv(&vimvars[VV_KEY].vv_tv); - if (r == FAIL || did_emsg) + tv_clear(&vimvars[VV_KEY].vv_tv); + if (r == FAIL || did_emsg) { break; + } if (!map && rem) { if (var_check_fixed(di->di_flags, arg_errmsg, arg_errmsg_len) || var_check_ro(di->di_flags, arg_errmsg, arg_errmsg_len)) { @@ -9762,9 +9112,10 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL || did_emsg) break; - if (!map && rem) - listitem_remove(l, li); - ++idx; + if (!map && rem) { + tv_list_item_remove(l, li); + } + idx++; } } @@ -9819,24 +9170,25 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) } } if (map) { - /* map(): replace the list item value */ - clear_tv(tv); + // map(): replace the list item value. + tv_clear(tv); rettv.v_lock = 0; *tv = rettv; } else { - int error = FALSE; + bool error = false; - /* filter(): when expr is zero remove the item */ + // filter(): when expr is zero remove the item *remp = (get_tv_number_chk(&rettv, &error) == 0); - clear_tv(&rettv); - /* On type error, nothing has been removed; return FAIL to stop the - * loop. The error message was given by get_tv_number_chk(). */ - if (error) + tv_clear(&rettv); + // On type error, nothing has been removed; return FAIL to stop the + // loop. The error message was given by get_tv_number_chk(). + if (error) { goto theend; + } } retval = OK; theend: - clear_tv(&vimvars[VV_VAL].vv_tv); + tv_clear(&vimvars[VV_VAL].vv_tv); return retval; } @@ -10278,11 +9630,12 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) != NULL) { - int error = FALSE; + bool error = false; - li = list_find(l, get_tv_number_chk(&argvars[1], &error)); - if (!error && li != NULL) + li = tv_list_find(l, get_tv_number_chk(&argvars[1], &error)); + if (!error && li != NULL) { tv = &li->li_tv; + } } } else if (argvars[0].v_type == VAR_DICT) { if ((d = argvars[0].vval.v_dict) != NULL) { @@ -10326,11 +9679,9 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (STRCMP(what, "args") == 0) { rettv->v_type = VAR_LIST; - if (rettv_list_alloc(rettv) != NULL) { - int i; - - for (i = 0; i < pt->pt_argc; i++) { - list_append_tv(rettv->vval.v_list, &pt->pt_argv[i]); + if (tv_list_alloc_ret(rettv) != NULL) { + for (int i = 0; i < pt->pt_argc; i++) { + tv_list_append_tv(rettv->vval.v_list, &pt->pt_argv[i]); } } } else { @@ -10353,13 +9704,13 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void get_buffer_signs(buf_T *buf, list_T *l) { for (signlist_T *sign = buf->b_signlist; sign; sign = sign->next) { - dict_T *d = dict_alloc(); + dict_T *const d = dict_alloc(); dict_add_nr_str(d, "id", sign->id, NULL); dict_add_nr_str(d, "lnum", sign->lnum, NULL); dict_add_nr_str(d, "name", 0L, sign_typenr2name(sign->typenr)); - list_append_dict(l, d); + tv_list_append_dict(l, d); } } @@ -10384,17 +9735,17 @@ static dict_T *get_buffer_info(buf_T *buf) dict_add_dict(dict, "variables", buf->b_vars); // List of windows displaying this buffer - list_T *windows = list_alloc(); + list_T *const windows = tv_list_alloc(); FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer == buf) { - list_append_number(windows, (varnumber_T)wp->handle); + tv_list_append_number(windows, (varnumber_T)wp->handle); } } dict_add_list(dict, "windows", windows); if (buf->b_signlist != NULL) { // List of signs placed in this buffer - list_T *signs = list_alloc(); + list_T *const signs = tv_list_alloc(); get_buffer_signs(buf, signs); dict_add_list(dict, "signs", signs); } @@ -10410,7 +9761,7 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool sel_buflisted = false; bool sel_bufloaded = false; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); // List of all the buffers or selected buffers if (argvars[0].v_type == VAR_DICT) { @@ -10452,9 +9803,9 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) continue; } - dict_T *d = get_buffer_info(buf); + dict_T *const d = get_buffer_info(buf); if (d != NULL) { - list_append_dict(rettv->vval.v_list, d); + tv_list_append_dict(rettv->vval.v_list, d); } if (argbuf != NULL) { return; @@ -10475,7 +9826,7 @@ static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retli rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; if (retlist) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); } if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0) @@ -10496,8 +9847,8 @@ static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retli if (end > buf->b_ml.ml_line_count) end = buf->b_ml.ml_line_count; while (start <= end) { - list_append_string( - rettv->vval.v_list, ml_get_buf(buf, start++, FALSE), -1); + tv_list_append_string(rettv->vval.v_list, + (const char *)ml_get_buf(buf, start++, false), -1); } } } @@ -10594,7 +9945,7 @@ f_getbufvar_end: static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { varnumber_T n; - int error = FALSE; + bool error = false; no_mapping++; for (;; ) { @@ -10802,12 +10153,13 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) theend: pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context); - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (pat != NULL) { ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP); for (int i = 0; i < xpc.xp_numfiles; i++) { - list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1); + tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], + -1); } } xfree(pat); @@ -11110,7 +10462,7 @@ static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv) { if (what_arg->v_type == VAR_UNKNOWN) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (is_qf || wp != NULL) { (void)get_errorlist(wp, -1, rettv->vval.v_list); } @@ -11145,7 +10497,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) matchitem_T *cur = curwin->w_match_head; int i; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); while (cur != NULL) { dict_T *dict = dict_alloc(); if (cur->match.regprog == NULL) { @@ -11158,11 +10510,11 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (llpos->lnum == 0) { break; } - list_T *l = list_alloc(); - list_append_number(l, (varnumber_T)llpos->lnum); + list_T *l = tv_list_alloc(); + tv_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); + tv_list_append_number(l, (varnumber_T)llpos->col); + tv_list_append_number(l, (varnumber_T)llpos->len); } sprintf(buf, "pos%d", i + 1); dict_add_list(dict, buf, l); @@ -11181,7 +10533,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_add_nr_str(dict, "conceal", 0L, (char_u *)&buf); } - list_append_dict(rettv->vval.v_list, dict); + tv_list_append_dict(rettv->vval.v_list, dict); cur = cur->next; } } @@ -11205,20 +10557,22 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos) fp = var2fpos(&argvars[0], true, &fnum); } - list_T *l = rettv_list_alloc(rettv); - list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0); - list_append_number(l, (fp != NULL) ? (varnumber_T)fp->lnum : (varnumber_T)0); - list_append_number(l, - (fp != NULL) - ? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1) - : (varnumber_T)0); - list_append_number(l, - (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0); + list_T *l = tv_list_alloc_ret(rettv); + tv_list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0); + tv_list_append_number(l, ((fp != NULL) + ? (varnumber_T)fp->lnum + : (varnumber_T)0)); + tv_list_append_number( + l, ((fp != NULL) + ? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1) + : (varnumber_T)0)); + tv_list_append_number( + l, (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0); if (getcurpos) { update_curswant(); - list_append_number(l, curwin->w_curswant == MAXCOL + tv_list_append_number(l, (curwin->w_curswant == MAXCOL ? (varnumber_T)MAXCOL - : (varnumber_T)curwin->w_curswant + 1); + : (varnumber_T)curwin->w_curswant + 1)); } } @@ -11251,7 +10605,7 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) int regname; int arg2 = false; bool return_list = false; - int error = false; + bool error = false; if (argvars[0].v_type != VAR_UNKNOWN) { strregname = get_tv_string_chk(&argvars[0]); @@ -11279,7 +10633,7 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_list = get_reg_contents(regname, (arg2 ? kGRegExprSrc : 0) | kGRegList); if (rettv->vval.v_list == NULL) { - rettv->vval.v_list = list_alloc(); + rettv->vval.v_list = tv_list_alloc(); } rettv->vval.v_list->lv_refcount++; } else { @@ -11328,9 +10682,9 @@ static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx) dict_add_nr_str(dict, "tabnr", tp_idx, NULL); - list_T *l = list_alloc(); + list_T *const l = tv_list_alloc(); FOR_ALL_WINDOWS_IN_TAB(wp, tp) { - list_append_number(l, (varnumber_T)wp->handle); + tv_list_append_number(l, (varnumber_T)wp->handle); } dict_add_list(dict, "windows", l); @@ -11345,7 +10699,7 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tabpage_T *tparg = NULL; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (argvars[0].v_type != VAR_UNKNOWN) { // Information about one tab page @@ -11362,9 +10716,9 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (tparg != NULL && tp != tparg) { continue; } - dict_T *d = get_tabpage_info(tp, tpnr); + dict_T *const d = get_tabpage_info(tp, tpnr); if (d != NULL) { - list_append_dict(rettv->vval.v_list, d); + tv_list_append_dict(rettv->vval.v_list, d); } if (tparg != NULL) { return; @@ -11447,7 +10801,7 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) { win_T *wparg = NULL; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (argvars[0].v_type != VAR_UNKNOWN) { wparg = win_id2wp(argvars); @@ -11467,9 +10821,9 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) continue; } winnr++; - dict_T *d = get_win_info(wp, tabnr, winnr); + dict_T *const d = get_win_info(wp, tabnr, winnr); if (d != NULL) { - list_append_dict(rettv->vval.v_list, d); + tv_list_append_dict(rettv->vval.v_list, d); } if (wparg != NULL) { // found information about a specific window @@ -11504,7 +10858,7 @@ find_win_by_nr ( tabpage_T *tp /* NULL for current tab page */ ) { - int nr = get_tv_number_chk(vp, NULL); + int nr = (int)get_tv_number_chk(vp, NULL); if (nr < 0) { return NULL; @@ -11645,7 +10999,7 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int options = WILD_SILENT|WILD_USE_NL; expand_T xpc; - int error = FALSE; + bool error = false; /* When the optional second argument is non-zero, don't remove matches * for 'wildignore' and don't put matches for 'suffixes' at the end. */ @@ -11669,14 +11023,15 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) xpc.xp_context = EXPAND_FILES; if (p_wic) options += WILD_ICASE; - if (rettv->v_type == VAR_STRING) - rettv->vval.v_string = ExpandOne(&xpc, get_tv_string(&argvars[0]), - NULL, options, WILD_ALL); - else { - rettv_list_alloc(rettv); + if (rettv->v_type == VAR_STRING) { + rettv->vval.v_string = ExpandOne(&xpc, get_tv_string(&argvars[0]), NULL, + options, WILD_ALL); + } else { + tv_list_alloc_ret(rettv); ExpandOne(&xpc, get_tv_string(&argvars[0]), NULL, options, WILD_ALL_KEEP); for (int i = 0; i < xpc.xp_numfiles; i++) { - list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1); + tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], + -1); } ExpandCleanup(&xpc); } @@ -11688,7 +11043,7 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int flags = 0; // Flags for globpath. - int error = false; + bool error = false; // Return a string, or a list if the optional third argument is non-zero. rettv->v_type = VAR_STRING; @@ -11722,10 +11077,10 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (rettv->v_type == VAR_STRING) { rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n"); } else { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); for (int i = 0; i < ga.ga_len; i++) { - list_append_string(rettv->vval.v_list, - ((char_u **)(ga.ga_data))[i], -1); + tv_list_append_string(rettv->vval.v_list, + ((const char **)(ga.ga_data))[i], -1); } } @@ -12249,11 +11604,11 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (l != NULL) { item = l->lv_first; if (argvars[2].v_type != VAR_UNKNOWN) { - int error = FALSE; + bool error = false; - /* Start at specified item. Use the cached index that list_find() - * sets, so that a negative number also works. */ - item = list_find(l, get_tv_number_chk(&argvars[2], &error)); + // Start at specified item. Use the cached index that tv_list_find() + // sets, so that a negative number also works. + item = tv_list_find(l, get_tv_number_chk(&argvars[2], &error)); idx = l->lv_idx; if (argvars[3].v_type != VAR_UNKNOWN) ic = get_tv_number_chk(&argvars[3], &error); @@ -12456,10 +11811,8 @@ static void f_inputsecret(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - long before = 0; - listitem_T *item; - list_T *l; - int error = false; + list_T *l; + bool error = false; const char *const arg_errmsg = _("insert() argument"); const size_t arg_errmsg_len = strlen(arg_errmsg); @@ -12467,6 +11820,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "insert()"); } else if ((l = argvars[0].vval.v_list) != NULL && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { + long before = 0; if (argvars[2].v_type != VAR_UNKNOWN) { before = get_tv_number_chk(&argvars[2], &error); } @@ -12475,17 +11829,16 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - if (before == l->lv_len) - item = NULL; - else { - item = list_find(l, before); + listitem_T *item = NULL; + if (before != l->lv_len) { + item = tv_list_find(l, before); if (item == NULL) { EMSGN(_(e_listidx), before); l = NULL; } } if (l != NULL) { - list_insert_tv(l, &argvars[1], item); + tv_list_insert_tv(l, &argvars[1], item); copy_tv(&argvars[0], rettv); } } @@ -12574,7 +11927,7 @@ static void dict_list(typval_T *argvars, typval_T *rettv, int what) if ((d = argvars[0].vval.v_dict) == NULL) return; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); todo = (int)d->dv_hashtab.ht_used; for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { @@ -12582,8 +11935,8 @@ static void dict_list(typval_T *argvars, typval_T *rettv, int what) --todo; di = HI2DI(hi); - li = listitem_alloc(); - list_append(rettv->vval.v_list, li); + li = tv_list_item_alloc(); + tv_list_append(rettv->vval.v_list, li); if (what == 0) { /* keys() */ @@ -12594,21 +11947,21 @@ static void dict_list(typval_T *argvars, typval_T *rettv, int what) /* values() */ copy_tv(&di->di_tv, &li->li_tv); } else { - /* items() */ - l2 = list_alloc(); + // items() + l2 = tv_list_alloc(); li->li_tv.v_type = VAR_LIST; li->li_tv.v_lock = 0; li->li_tv.vval.v_list = l2; ++l2->lv_refcount; - li2 = listitem_alloc(); - list_append(l2, li2); + li2 = tv_list_item_alloc(); + tv_list_append(l2, li2); li2->li_tv.v_type = VAR_STRING; li2->li_tv.v_lock = 0; li2->li_tv.vval.v_string = vim_strsave(di->di_key); - li2 = listitem_alloc(); - list_append(l2, li2); + li2 = tv_list_item_alloc(); + tv_list_append(l2, li2); copy_tv(&di->di_tv, &li2->li_tv); } } @@ -12983,7 +12336,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) } list_T *args = argvars[0].vval.v_list; - list_T *rv = list_alloc(); + list_T *rv = tv_list_alloc(); ui_busy_start(); MultiQueue *waiting_jobs = multiqueue_new_parent(loop_on_put, &main_loop); @@ -12994,11 +12347,11 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) TerminalJobData *data = NULL; if (arg->li_tv.v_type != VAR_NUMBER || !(data = find_job(arg->li_tv.vval.v_number))) { - list_append_number(rv, -3); + tv_list_append_number(rv, -3); } else { // append the list item and set the status pointer so we'll collect the // status code when the job exits - list_append_number(rv, -1); + tv_list_append_number(rv, -1); data->status_ptr = &rv->lv_last->li_tv.vval.v_number; // Process any pending events for the job because we'll temporarily // replace the parent queue @@ -13097,7 +12450,7 @@ static void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (sep != NULL) { ga_init(&ga, (int)sizeof(char), 80); - list_join(&ga, argvars[0].vval.v_list, (char *) sep); + tv_list_join(&ga, argvars[0].vval.v_list, (const char *)sep); ga_append(&ga, NUL); rettv->vval.v_string = (char_u *)ga.ga_data; } else @@ -13181,7 +12534,7 @@ static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr) get_tv_string(&argvars[0])); break; case VAR_LIST: - rettv->vval.v_number = list_len(argvars[0].vval.v_list); + rettv->vval.v_number = tv_list_len(argvars[0].vval.v_list); break; case VAR_DICT: rettv->vval.v_number = dict_len(argvars[0].vval.v_dict); @@ -13435,12 +12788,12 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) if (type == 3 || type == 4) { // type 3: return empty list when there are no matches. // type 4: return ["", -1, -1, -1] - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (type == 4) { - list_append_string(rettv->vval.v_list, (char_u *)"", 0); - list_append_number(rettv->vval.v_list, (varnumber_T)-1); - list_append_number(rettv->vval.v_list, (varnumber_T)-1); - list_append_number(rettv->vval.v_list, (varnumber_T)-1); + tv_list_append_string(rettv->vval.v_list, "", 0); + tv_list_append_number(rettv->vval.v_list, -1); + tv_list_append_number(rettv->vval.v_list, -1); + tv_list_append_number(rettv->vval.v_list, -1); } } else if (type == 2) { rettv->v_type = VAR_STRING; @@ -13461,16 +12814,17 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) goto theend; if (argvars[2].v_type != VAR_UNKNOWN) { - int error = FALSE; + bool error = false; start = get_tv_number_chk(&argvars[2], &error); if (error) goto theend; if (l != NULL) { - li = list_find(l, start); - if (li == NULL) + li = tv_list_find(l, start); + if (li == NULL) { goto theend; - idx = l->lv_idx; /* use the cached index */ + } + idx = l->lv_idx; // Use the cached index. } else { if (start < 0) start = 0; @@ -13552,11 +12906,11 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) /* return list with matched string and submatches */ for (i = 0; i < NSUBEXP; ++i) { if (regmatch.endp[i] == NULL) { - list_append_string(rettv->vval.v_list, (char_u *)"", 0); + tv_list_append_string(rettv->vval.v_list, NULL, 0); } else { - list_append_string(rettv->vval.v_list, - regmatch.startp[i], - (int)(regmatch.endp[i] - regmatch.startp[i])); + tv_list_append_string(rettv->vval.v_list, + (const char *)regmatch.startp[i], + (regmatch.endp[i] - regmatch.startp[i])); } } } else if (type == 2) { @@ -13583,7 +12937,8 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) if (type == 4 && l == NULL) { // matchstrpos() without a list: drop the second item - listitem_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first->li_next); + tv_list_item_remove(rettv->vval.v_list, + rettv->vval.v_list->lv_first->li_next); } theend: @@ -13609,7 +12964,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) char_u *pat = get_tv_string_buf_chk(&argvars[1], buf); /* pattern */ int prio = 10; /* default priority */ int id = -1; - int error = false; + bool error = false; char_u *conceal_char = NULL; rettv->vval.v_number = -1; @@ -13667,7 +13022,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - int error = false; + bool error = false; int prio = 10; int id = -1; char_u *conceal_char = NULL; @@ -13708,7 +13063,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); int id = get_tv_number(&argvars[0]); @@ -13716,11 +13071,12 @@ static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) matchitem_T *m; 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); + tv_list_append_string(rettv->vval.v_list, + (const char *)syn_id2name(m->hlg_id), -1); + tv_list_append_string(rettv->vval.v_list, (const char *)m->pattern, -1); } else { - list_append_string(rettv->vval.v_list, NULL, -1); - list_append_string(rettv->vval.v_list, NULL, -1); + tv_list_append_string(rettv->vval.v_list, NULL, 0); + tv_list_append_string(rettv->vval.v_list, NULL, 0); } } } @@ -13768,7 +13124,7 @@ static void max_min(typval_T *argvars, typval_T *rettv, int domax) { long n = 0; long i; - int error = FALSE; + bool error = false; if (argvars[0].v_type == VAR_LIST) { list_T *l; @@ -13938,7 +13294,7 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "msgpackdump()"); return; } - list_T *ret_list = rettv_list_alloc(rettv); + list_T *ret_list = tv_list_alloc_ret(rettv); const list_T *list = argvars[0].vval.v_list; if (list == NULL) { return; @@ -13966,7 +13322,7 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "msgpackparse()"); return; } - list_T *ret_list = rettv_list_alloc(rettv); + list_T *ret_list = tv_list_alloc_ret(rettv); const list_T *list = argvars[0].vval.v_list; if (list == NULL || list->lv_first == NULL) { return; @@ -14011,9 +13367,9 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) goto f_msgpackparse_exit; } if (result == MSGPACK_UNPACK_SUCCESS) { - listitem_T *li = listitem_alloc(); + listitem_T *li = tv_list_item_alloc(); li->li_tv.v_type = VAR_UNKNOWN; - list_append(ret_list, li); + tv_list_append(ret_list, li); if (msgpack_to_vim(unpacked.data, &li->li_tv) == FAIL) { EMSG2(_(e_invarg2), "Failed to convert msgpack string"); goto f_msgpackparse_exit; @@ -14188,11 +13544,11 @@ static void f_py3eval(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - long start; - long end; - long stride = 1; + varnumber_T start; + varnumber_T end; + varnumber_T stride = 1; long i; - int error = FALSE; + bool error = false; start = get_tv_number_chk(&argvars[0], &error); if (argvars[1].v_type == VAR_UNKNOWN) { @@ -14204,16 +13560,17 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr) stride = get_tv_number_chk(&argvars[2], &error); } - if (error) - return; /* type error; errmsg already given */ - if (stride == 0) - EMSG(_("E726: Stride is zero")); - else if (stride > 0 ? end + 1 < start : end - 1 > start) - EMSG(_("E727: Start past end")); - else { - rettv_list_alloc(rettv); + if (error) { + return; // Type error; errmsg already given. + } + if (stride == 0) { + emsgf(_("E726: Stride is zero")); + } else if (stride > 0 ? end + 1 < start : end - 1 > start) { + emsgf(_("E727: Start past end")); + } else { + tv_list_alloc_ret(rettv); for (i = start; stride > 0 ? i <= end : i >= end; i += stride) { - list_append_number(rettv->vval.v_list, (varnumber_T)i); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)i); } } } @@ -14244,7 +13601,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) maxline = get_tv_number(&argvars[2]); } - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); /* Always open the file in binary mode, library functions have a mind of * their own about CR-LF conversion. */ @@ -14293,11 +13650,11 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) prevlen = prevsize = 0; } - li = listitem_alloc(); + li = tv_list_item_alloc(); li->li_tv.v_type = VAR_STRING; li->li_tv.v_lock = 0; li->li_tv.vval.v_string = s; - list_append(rettv->vval.v_list, li); + tv_list_append(rettv->vval.v_list, li); start = p + 1; /* step over newline */ if ((++cnt >= maxline && maxline >= 0) || readlen <= 0) @@ -14372,8 +13729,8 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ if (maxline < 0) while (cnt > -maxline) { - listitem_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first); - --cnt; + tv_list_item_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first); + cnt--; } xfree(prev); @@ -14395,9 +13752,9 @@ static int list2proftime(typval_T *arg, proftime_T *tm) FUNC_ATTR_NONNULL_ALL return FAIL; } - int error = false; - varnumber_T n1 = list_find_nr(arg->vval.v_list, 0L, &error); - varnumber_T n2 = list_find_nr(arg->vval.v_list, 1L, &error); + bool error = false; + varnumber_T n1 = tv_list_find_nr(arg->vval.v_list, 0L, &error); + varnumber_T n2 = tv_list_find_nr(arg->vval.v_list, 1L, &error); if (error) { return FAIL; } @@ -14456,9 +13813,9 @@ static void f_reltime(typval_T *argvars, typval_T *rettv, FunPtr fptr) STATIC_ASSERT(sizeof(u.prof) == sizeof(u) && sizeof(u.split) == sizeof(u), "type punning will produce incorrect results on this platform"); - rettv_list_alloc(rettv); - list_append_number(rettv->vval.v_list, u.split.high); - list_append_number(rettv->vval.v_list, u.split.low); + tv_list_alloc_ret(rettv); + tv_list_append_number(rettv->vval.v_list, u.split.high); + tv_list_append_number(rettv->vval.v_list, u.split.low); } /// f_reltimestr - return a string that represents the value of {time} @@ -14519,27 +13876,27 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listdictarg), "remove()"); } else if ((l = argvars[0].vval.v_list) != NULL && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { - int error = (int)false; + bool error = false; idx = get_tv_number_chk(&argvars[1], &error); - if (error) - ; /* type error: do nothing, errmsg already given */ - else if ((item = list_find(l, idx)) == NULL) + if (error) { + // Type error: do nothing, errmsg already given. + } else if ((item = tv_list_find(l, idx)) == NULL) { EMSGN(_(e_listidx), idx); - else { + } else { if (argvars[2].v_type == VAR_UNKNOWN) { // Remove one item, return its value. - vim_list_remove(l, item, item); + tv_list_remove_items(l, item, item); *rettv = item->li_tv; xfree(item); } else { /* Remove range of items, return list with values. */ end = get_tv_number_chk(&argvars[2], &error); - if (error) - ; /* type error: do nothing */ - else if ((item2 = list_find(l, end)) == NULL) + if (error) { + // Type error: do nothing. + } else if ((item2 = tv_list_find(l, end)) == NULL) { EMSGN(_(e_listidx), end); - else { + } else { int cnt = 0; for (li = item; li != NULL; li = li->li_next) { @@ -14547,11 +13904,11 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (li == item2) break; } - if (li == NULL) /* didn't find "item2" after "item" */ - EMSG(_(e_invrange)); - else { - vim_list_remove(l, item, item2); - l = rettv_list_alloc(rettv); + if (li == NULL) { // Didn't find "item2" after "item". + emsgf(_(e_invrange)); + } else { + tv_list_remove_items(l, item, item2); + l = tv_list_alloc_ret(rettv); l->lv_first = item; l->lv_last = item2; item->li_prev = NULL; @@ -14588,10 +13945,10 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr) n = get_tv_number(&argvars[1]); if (argvars[0].v_type == VAR_LIST) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (argvars[0].vval.v_list != NULL) { while (n-- > 0) { - list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL); + tv_list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL); } } } else { @@ -14802,12 +14159,12 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) l->lv_len = 0; while (li != NULL) { listitem_T *const ni = li->li_prev; - list_append(l, li); + tv_list_append(l, li); li = ni; } rettv->vval.v_list = l; rettv->v_type = VAR_LIST; - ++l->lv_refcount; + l->lv_refcount++; l->lv_idx = l->lv_len - l->lv_idx - 1; } } @@ -15243,7 +14600,7 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int locally = 1; int thisblock = 0; - int error = FALSE; + bool error = false; rettv->vval.v_number = 1; /* default: FAIL */ @@ -15346,15 +14703,15 @@ static void f_searchpairpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) int lnum = 0; int col = 0; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (searchpair_cmn(argvars, &match_pos) > 0) { lnum = match_pos.lnum; col = match_pos.col; } - list_append_number(rettv->vval.v_list, (varnumber_T)lnum); - list_append_number(rettv->vval.v_list, (varnumber_T)col); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)lnum); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)col); } /* @@ -15386,7 +14743,6 @@ do_searchpair ( int n; int r; int nest = 1; - int err; int options = SEARCH_KEEP; proftime_T tm; @@ -15442,7 +14798,8 @@ do_searchpair ( if (*skip != NUL) { save_pos = curwin->w_cursor; curwin->w_cursor = pos; - r = eval_to_bool(skip, &err, NULL, FALSE); + bool err; + r = eval_to_bool(skip, &err, NULL, false); curwin->w_cursor = save_pos; if (err) { /* Evaluating {skip} caused an error, break here. */ @@ -15513,7 +14870,7 @@ static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) int n; int flags = 0; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); n = search_cmn(argvars, &match_pos, &flags); if (n > 0) { @@ -15521,10 +14878,11 @@ static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) col = match_pos.col; } - list_append_number(rettv->vval.v_list, (varnumber_T)lnum); - list_append_number(rettv->vval.v_list, (varnumber_T)col); - if (flags & SP_SUBPAT) - list_append_number(rettv->vval.v_list, (varnumber_T)n); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)lnum); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)col); + if (flags & SP_SUBPAT) { + tv_list_append_number(rettv->vval.v_list, (varnumber_T)n); + } } /// "serverlist()" function @@ -15534,13 +14892,13 @@ static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) char **addrs = server_address_list(&n); // Copy addrs into a linked list. - list_T *l = rettv_list_alloc(rettv); + list_T *l = tv_list_alloc_ret(rettv); for (size_t i = 0; i < n; i++) { - listitem_T *li = listitem_alloc(); + listitem_T *li = tv_list_item_alloc(); li->li_tv.v_type = VAR_STRING; li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = (char_u *) addrs[i]; - list_append(l, li); + li->li_tv.vval.v_string = (char_u *)addrs[i]; + tv_list_append(l, li); } xfree(addrs); } @@ -15610,7 +14968,7 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (*varname == '&') { long numval; char_u *strval; - int error = false; + bool error = false; aco_save_T aco; // set curbuf to be our buf, temporarily @@ -15915,7 +15273,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) d = li->li_tv.vval.v_dict; if (dict_find(d, (char_u *)"pattern", -1) == NULL) { if (s == NULL) { - s = list_alloc(); + s = tv_list_alloc(); if (s == NULL) { return; } @@ -15929,7 +15287,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - list_append_tv(s, &di->di_tv); + tv_list_append_tv(s, &di->di_tv); s->lv_refcount++; } else { break; @@ -15949,7 +15307,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) priority, id, NULL, conceal); } else { match_add(curwin, group, NULL, priority, id, s, conceal); - list_unref(s); + tv_list_unref(s); s = NULL; } xfree(group); @@ -16193,7 +15551,7 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) if (*varname == '&') { long numval; char_u *strval; - int error = false; + bool error = false; ++varname; numval = get_tv_number_chk(varp, &error); @@ -16272,7 +15630,7 @@ typedef struct { char_u *item_compare_func; partial_T *item_compare_partial; dict_T *item_compare_selfdict; - int item_compare_func_err; + bool item_compare_func_err; } sortinfo_T; static sortinfo_T *sortinfo = NULL; @@ -16399,13 +15757,13 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) copy_tv(&si1->item->li_tv, &argv[0]); copy_tv(&si2->item->li_tv, &argv[1]); - rettv.v_type = VAR_UNKNOWN; // clear_tv() uses this + rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this res = call_func(func_name, (int)STRLEN(func_name), &rettv, 2, argv, NULL, 0L, 0L, &dummy, true, partial, sortinfo->item_compare_selfdict); - clear_tv(&argv[0]); - clear_tv(&argv[1]); + tv_clear(&argv[0]); + tv_clear(&argv[1]); if (res == FAIL) { res = ITEM_COMPARE_FAIL; @@ -16415,7 +15773,7 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) if (sortinfo->item_compare_func_err) { res = ITEM_COMPARE_FAIL; // return value has wrong type } - clear_tv(&rettv); + tv_clear(&rettv); // When the result would be zero, compare the pointers themselves. Makes // the sort stable. @@ -16470,7 +15828,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) rettv->v_type = VAR_LIST; ++l->lv_refcount; - len = list_len(l); + len = tv_list_len(l); if (len <= 1) { goto theend; // short list sorts pretty quickly } @@ -16490,7 +15848,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) } else if (argvars[1].v_type == VAR_PARTIAL) { info.item_compare_partial = argvars[1].vval.v_partial; } else { - int error = FALSE; + bool error = false; i = get_tv_number_chk(&argvars[1], &error); if (error) { @@ -16569,7 +15927,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) l->lv_len = 0; for (i = 0; i < len; i++) { - list_append(l, ptrs[i].item); + tv_list_append(l, ptrs[i].item); } } } @@ -16605,8 +15963,8 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) } else { l->lv_last = ptrs[i].item; } - list_fix_watch(l, li); - listitem_free(li); + tv_list_watch_fix(l, li); + tv_list_item_free(li); l->lv_len--; } } @@ -16667,7 +16025,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) hlf_T attr = HLF_COUNT; size_t len = 0; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (argvars[0].v_type == VAR_UNKNOWN) { /* Find the start and length of the badly spelled word. */ @@ -16692,14 +16050,13 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) } assert(len <= INT_MAX); - list_append_string(rettv->vval.v_list, word, (int)len); - list_append_string(rettv->vval.v_list, - (char_u *)(attr == HLF_SPB ? "bad" : - attr == HLF_SPR ? "rare" : - attr == HLF_SPL ? "local" : - attr == HLF_SPC ? "caps" : - ""), - -1); + tv_list_append_string(rettv->vval.v_list, (const char *)word, len); + tv_list_append_string(rettv->vval.v_list, + (attr == HLF_SPB ? "bad" + : attr == HLF_SPR ? "rare" + : attr == HLF_SPL ? "local" + : attr == HLF_SPC ? "caps" + : NULL), -1); } /* @@ -16708,13 +16065,13 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char_u *str; - int typeerr = FALSE; + bool typeerr = false; int maxcount; garray_T ga; listitem_T *li; bool need_capital = false; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) { str = get_tv_string(&argvars[0]); @@ -16735,11 +16092,11 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) for (int i = 0; i < ga.ga_len; ++i) { str = ((char_u **)ga.ga_data)[i]; - li = listitem_alloc(); + li = tv_list_item_alloc(); li->li_tv.v_type = VAR_STRING; li->li_tv.v_lock = 0; li->li_tv.vval.v_string = str; - list_append(rettv->vval.v_list, li); + tv_list_append(rettv->vval.v_list, li); } ga_clear(&ga); } @@ -16755,8 +16112,8 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) char_u *save_cpo; int match; colnr_T col = 0; - int keepempty = FALSE; - int typeerr = FALSE; + bool keepempty = false; + bool typeerr = false; /* Make 'cpoptions' empty, the 'l' flag should not be used here. */ save_cpo = p_cpo; @@ -16765,15 +16122,17 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) str = get_tv_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { pat = get_tv_string_buf_chk(&argvars[1], patbuf); - if (pat == NULL) - typeerr = TRUE; - if (argvars[2].v_type != VAR_UNKNOWN) - keepempty = get_tv_number_chk(&argvars[2], &typeerr); + if (pat == NULL) { + typeerr = true; + } + if (argvars[2].v_type != VAR_UNKNOWN) { + keepempty = (bool)get_tv_number_chk(&argvars[2], &typeerr); + } } if (pat == NULL || *pat == NUL) pat = (char_u *)"[\\x01- ]\\+"; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (typeerr) return; @@ -16793,7 +16152,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (keepempty || end > str || (rettv->vval.v_list->lv_len > 0 && *str != NUL && match && end < regmatch.endp[0])) { - list_append_string(rettv->vval.v_list, str, (int)(end - str)); + tv_list_append_string(rettv->vval.v_list, (const char *)str, end - str); } if (!match) break; @@ -16917,33 +16276,29 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) // "strgetchar()" function static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *str; - int len; - int error = false; - int charidx; - rettv->vval.v_number = -1; - str = get_tv_string_chk(&argvars[0]); + + char_u *str = get_tv_string_chk(&argvars[0]); if (str == NULL) { return; } - len = (int)STRLEN(str); - charidx = get_tv_number_chk(&argvars[1], &error); + + bool error = false; + varnumber_T charidx = get_tv_number_chk(&argvars[1], &error); if (error) { return; } - { - int byteidx = 0; + const size_t len = STRLEN(str); + size_t byteidx = 0; - while (charidx >= 0 && byteidx < len) { - if (charidx == 0) { - rettv->vval.v_number = mb_ptr2char(str + byteidx); - break; - } - charidx--; - byteidx += MB_CPTR2LEN(str + byteidx); + while (charidx >= 0 && byteidx < len) { + if (charidx == 0) { + rettv->vval.v_number = mb_ptr2char(str + byteidx); + break; } + charidx--; + byteidx += MB_CPTR2LEN(str + byteidx); } } @@ -16966,7 +16321,7 @@ static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; /* type error; errmsg already given */ if (argvars[2].v_type != VAR_UNKNOWN) { - int error = FALSE; + bool error = false; start_idx = get_tv_number_chk(&argvars[2], &error); if (error || start_idx >= (int)STRLEN(haystack)) @@ -17048,22 +16403,17 @@ static void f_strwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) } // "strcharpart()" function -static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p; - int nchar; +static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + char_u *const p = get_tv_string(&argvars[0]); + const size_t slen = STRLEN(p); + int nbyte = 0; - int charlen; - int len = 0; - int slen; - int error = false; - - p = get_tv_string(&argvars[0]); - slen = (int)STRLEN(p); - - nchar = get_tv_number_chk(&argvars[1], &error); + bool error = false; + varnumber_T nchar = get_tv_number_chk(&argvars[1], &error); if (!error) { if (nchar > 0) { - while (nchar > 0 && nbyte < slen) { + while (nchar > 0 && (size_t)nbyte < slen) { nbyte += MB_CPTR2LEN(p + nbyte); nchar--; } @@ -17071,9 +16421,10 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { nbyte = nchar; } } + int len = 0; if (argvars[2].v_type != VAR_UNKNOWN) { - charlen = get_tv_number(&argvars[2]); - while (charlen > 0 && nbyte + len < slen) { + int charlen = get_tv_number(&argvars[2]); + while (charlen > 0 && nbyte + len < (int)slen) { int off = nbyte + len; if (off < 0) { @@ -17092,12 +16443,12 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if (nbyte < 0) { len += nbyte; nbyte = 0; - } else if (nbyte > slen) { + } else if ((size_t)nbyte > slen) { nbyte = slen; } if (len < 0) { len = 0; - } else if (nbyte + len > slen) { + } else if (nbyte + len > (int)slen) { len = slen - nbyte; } @@ -17114,7 +16465,7 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) int n; int len; int slen; - int error = FALSE; + bool error = false; p = get_tv_string(&argvars[0]); slen = (int)STRLEN(p); @@ -17205,7 +16556,7 @@ static void f_strtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int error = FALSE; + bool error = false; int no = (int)get_tv_number_chk(&argvars[0], &error); if (error) { return; @@ -17270,7 +16621,7 @@ static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) long lnum; long col; int trans; - int transerr = FALSE; + bool transerr = false; lnum = get_tv_lnum(argvars); /* -1 on type error */ col = get_tv_number(&argvars[1]) - 1; /* -1 on type error */ @@ -17396,7 +16747,7 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) memset(str, NUL, sizeof(str)); - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 && col <= (long)STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) { (void)syn_get_id(curwin, lnum, col, FALSE, NULL, FALSE); @@ -17417,10 +16768,10 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - list_append_number(rettv->vval.v_list, (syntax_flags & HL_CONCEAL) != 0); + tv_list_append_number(rettv->vval.v_list, (syntax_flags & HL_CONCEAL) != 0); // -1 to auto-determine strlen - list_append_string(rettv->vval.v_list, str, -1); - list_append_number(rettv->vval.v_list, matchid); + tv_list_append_string(rettv->vval.v_list, (const char *)str, -1); + tv_list_append_number(rettv->vval.v_list, matchid); } /* @@ -17441,43 +16792,24 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) && lnum <= curbuf->b_ml.ml_line_count && col >= 0 && col <= (long)STRLEN(ml_get(lnum))) { - rettv_list_alloc(rettv); - (void)syn_get_id(curwin, lnum, (colnr_T)col, FALSE, NULL, TRUE); + tv_list_alloc_ret(rettv); + (void)syn_get_id(curwin, lnum, (colnr_T)col, false, NULL, true); int id; int i = 0; while ((id = syn_get_stack_item(i++)) >= 0) { - list_append_number(rettv->vval.v_list, id); + tv_list_append_number(rettv->vval.v_list, id); } } } -static list_T* string_to_list(char_u *str, size_t len, bool keepempty) +static list_T *string_to_list(const char *str, size_t len, const bool keepempty) { - list_T *list = list_alloc(); - - // Copy each line to a list element using NL as the delimiter. - for (size_t i = 0; i < len; i++) { - char_u *start = str + i; - size_t line_len = (char_u *) xmemscan(start, NL, len - i) - start; - i += line_len; - - // Don't use a str function to copy res as it may contains NULs. - char_u *s = xmemdupz(start, line_len); - memchrsub(s, NUL, NL, line_len); // Replace NUL with NL to avoid truncation - - listitem_T *li = listitem_alloc(); - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = s; - list_append(list, li); + if (!keepempty && str[len - 1] == NL) { + len--; } - - // Optionally retain final newline, if present - if (keepempty && str[len-1] == NL) { - list_append_string(list, (char_u*)"", 0); - } - + list_T *const list = tv_list_alloc(); + encode_list_write(list, str, len); return list; } @@ -17522,7 +16854,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, if (res == NULL) { if (retlist) { // return an empty list when there's no output - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); } else { rettv->vval.v_string = (char_u *) xstrdup(""); } @@ -17534,7 +16866,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) { keepempty = get_tv_number(&argvars[2]); } - rettv->vval.v_list = string_to_list((char_u *) res, nread, keepempty != 0); + rettv->vval.v_list = string_to_list(res, nread, (bool)keepempty); rettv->vval.v_list->lv_refcount++; rettv->v_type = VAR_LIST; @@ -17588,9 +16920,9 @@ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) wp = (tp == curtab) ? firstwin : tp->tp_firstwin; } if (wp != NULL) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); while (wp != NULL) { - list_append_number(rettv->vval.v_list, wp->w_buffer->b_fnum); + tv_list_append_number(rettv->vval.v_list, wp->w_buffer->b_fnum); wp = wp->w_next; } } @@ -17683,16 +17015,16 @@ static void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *fname; + char *fname; tagname_T tn; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); fname = xmalloc(MAXPATHL); - int first = TRUE; - while (get_tagfname(&tn, first, fname) == OK) { - list_append_string(rettv->vval.v_list, fname, -1); - first = FALSE; + bool first = true; + while (get_tagfname(&tn, first, (char_u *)fname) == OK) { + tv_list_append_string(rettv->vval.v_list, fname, -1); + first = false; } tagname_free(&tn); @@ -17712,7 +17044,7 @@ static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (*tag_pattern == NUL) return; - (void)get_tags(rettv_list_alloc(rettv), tag_pattern); + (void)get_tags(tv_list_alloc_ret(rettv), tag_pattern); } /* @@ -18029,7 +17361,7 @@ static void timer_due_cb(TimeWatcher *tw, void *data) init_tv(&rettv); callback_call(&timer->callback, 1, argv, &rettv); - clear_tv(&rettv); + tv_clear(&rettv); if (!timer->stopped && timer->timeout == 0) { // special case: timeout=0 means the callback will be @@ -18295,7 +17627,7 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_add_nr_str(dict, "time_cur", (long)curbuf->b_u_time_cur, NULL); dict_add_nr_str(dict, "save_cur", (long)curbuf->b_u_save_nr_cur, NULL); - list = list_alloc(); + list = tv_list_alloc(); u_eval_tree(curbuf->b_u_oldhead, list); dict_add_list(dict, "entries", list); } @@ -18356,7 +17688,7 @@ static void f_wildmenumode(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "win_findbuf()" function static void f_win_findbuf(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); win_findbuf(argvars, rettv->vval.v_list); } @@ -18375,7 +17707,7 @@ static void f_win_gotoid(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "win_id2tabwin()" function static void f_win_id2tabwin(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); win_id2tabwin(argvars, rettv->vval.v_list); } @@ -18786,40 +18118,47 @@ var2fpos ( if (varp->v_type == VAR_LIST) { list_T *l; int len; - int error = FALSE; - listitem_T *li; + bool error = false; + listitem_T *li; l = varp->vval.v_list; if (l == NULL) return NULL; - /* Get the line number */ - pos.lnum = list_find_nr(l, 0L, &error); - if (error || pos.lnum <= 0 || pos.lnum > curbuf->b_ml.ml_line_count) - return NULL; /* invalid line number */ - - /* Get the column number */ - pos.col = list_find_nr(l, 1L, &error); - if (error) + // Get the line number. + pos.lnum = tv_list_find_nr(l, 0L, &error); + if (error || pos.lnum <= 0 || pos.lnum > curbuf->b_ml.ml_line_count) { + // Invalid line number. return NULL; + } + + // Get the column number. + pos.col = tv_list_find_nr(l, 1L, &error); + if (error) { + return NULL; + } len = (long)STRLEN(ml_get(pos.lnum)); - /* We accept "$" for the column number: last column. */ - li = list_find(l, 1L); + // We accept "$" for the column number: last column. + li = tv_list_find(l, 1L); if (li != NULL && li->li_tv.v_type == VAR_STRING && li->li_tv.vval.v_string != NULL - && STRCMP(li->li_tv.vval.v_string, "$") == 0) + && STRCMP(li->li_tv.vval.v_string, "$") == 0) { pos.col = len + 1; + } - /* Accept a position up to the NUL after the line. */ - if (pos.col == 0 || (int)pos.col > len + 1) - return NULL; /* invalid column number */ - --pos.col; + // Accept a position up to the NUL after the line. + if (pos.col == 0 || (int)pos.col > len + 1) { + // Invalid column number. + return NULL; + } + pos.col--; - /* Get the virtual offset. Defaults to zero. */ - pos.coladd = list_find_nr(l, 2L, &error); - if (error) + // Get the virtual offset. Defaults to zero. + pos.coladd = tv_list_find_nr(l, 2L, &error); + if (error) { pos.coladd = 0; + } return &pos; } @@ -18890,32 +18229,37 @@ static int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp) return FAIL; if (fnump != NULL) { - n = list_find_nr(l, i++, NULL); /* fnum */ - if (n < 0) + n = tv_list_find_nr(l, i++, NULL); // fnum + if (n < 0) { return FAIL; - if (n == 0) - n = curbuf->b_fnum; /* current buffer */ + } + if (n == 0) { + n = curbuf->b_fnum; // Current buffer. + } *fnump = n; } - n = list_find_nr(l, i++, NULL); /* lnum */ - if (n < 0) + n = tv_list_find_nr(l, i++, NULL); // lnum + if (n < 0) { return FAIL; + } posp->lnum = n; - n = list_find_nr(l, i++, NULL); /* col */ - if (n < 0) + n = tv_list_find_nr(l, i++, NULL); // col + if (n < 0) { return FAIL; + } posp->col = n; - n = list_find_nr(l, i, NULL); // off - if (n < 0) + n = tv_list_find_nr(l, i, NULL); // off + if (n < 0) { posp->coladd = 0; - else + } else { posp->coladd = n; + } if (curswantp != NULL) { - *curswantp = list_find_nr(l, i + 1, NULL); // curswant + *curswantp = tv_list_find_nr(l, i + 1, NULL); // curswant } return OK; @@ -19259,7 +18603,7 @@ void set_vcount(long count, long count1, int set_prevcount) /// @param[in] val Value to set to. void set_vim_var_nr(const VimVarIndex idx, const varnumber_T val) { - clear_tv(&vimvars[idx].vv_tv); + tv_clear(&vimvars[idx].vv_tv); vimvars[idx].vv_type = VAR_NUMBER; vimvars[idx].vv_nr = val; } @@ -19270,7 +18614,7 @@ void set_vim_var_nr(const VimVarIndex idx, const varnumber_T val) /// @param[in] val Value to set to. void set_vim_var_special(const VimVarIndex idx, const SpecialVarValue val) { - clear_tv(&vimvars[idx].vv_tv); + tv_clear(&vimvars[idx].vv_tv); vimvars[idx].vv_type = VAR_SPECIAL; vimvars[idx].vv_special = val; } @@ -19284,7 +18628,7 @@ void set_vim_var_special(const VimVarIndex idx, const SpecialVarValue val) void set_vim_var_string(const VimVarIndex idx, const char *const val, const ptrdiff_t len) { - clear_tv(&vimvars[idx].vv_di.di_tv); + tv_clear(&vimvars[idx].vv_di.di_tv); vimvars[idx].vv_type = VAR_STRING; if (val == NULL) { vimvars[idx].vv_str = NULL; @@ -19301,7 +18645,7 @@ void set_vim_var_string(const VimVarIndex idx, const char *const val, /// @param[in,out] val Value to set to. Reference count will be incremented. void set_vim_var_list(const VimVarIndex idx, list_T *const val) { - clear_tv(&vimvars[idx].vv_di.di_tv); + tv_clear(&vimvars[idx].vv_di.di_tv); vimvars[idx].vv_type = VAR_LIST; vimvars[idx].vv_list = val; if (val != NULL) { @@ -19316,7 +18660,7 @@ void set_vim_var_list(const VimVarIndex idx, list_T *const val) /// Also keys of the dictionary will be made read-only. void set_vim_var_dict(const VimVarIndex idx, dict_T *const val) { - clear_tv(&vimvars[idx].vv_di.di_tv); + tv_clear(&vimvars[idx].vv_di.di_tv); vimvars[idx].vv_type = VAR_DICT; vimvars[idx].vv_dict = val; @@ -19536,17 +18880,19 @@ handle_subscript( curwin->w_cursor.lnum, curwin->w_cursor.lnum, &len, evaluate, pt, selfdict); - /* Clear the funcref afterwards, so that deleting it while - * evaluating the arguments is possible (see test55). */ - if (evaluate) - clear_tv(&functv); + // Clear the funcref afterwards, so that deleting it while + // evaluating the arguments is possible (see test55). + if (evaluate) { + tv_clear(&functv); + } /* Stop the expression evaluation when immediately aborting on * error, or when an interrupt occurred or an exception was thrown * but not caught. */ if (aborting()) { - if (ret == OK) - clear_tv(rettv); + if (ret == OK) { + tv_clear(rettv); + } ret = FAIL; } dict_unref(selfdict); @@ -19560,7 +18906,7 @@ handle_subscript( } else selfdict = NULL; if (eval_index((char_u **)arg, rettv, evaluate, verbose) == FAIL) { - clear_tv(rettv); + tv_clear(rettv); ret = FAIL; } } @@ -19667,7 +19013,7 @@ void free_tv(typval_T *varp) partial_unref(varp->vval.v_partial); break; case VAR_LIST: - list_unref(varp->vval.v_list); + tv_list_unref(varp->vval.v_list); break; case VAR_DICT: dict_unref(varp->vval.v_dict); @@ -19682,247 +19028,6 @@ void free_tv(typval_T *varp) } } -#define TYPVAL_ENCODE_ALLOW_SPECIALS false - -#define TYPVAL_ENCODE_CONV_NIL(tv) \ - do { \ - tv->vval.v_special = kSpecialVarFalse; \ - tv->v_lock = VAR_UNLOCKED; \ - } while (0) - -#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \ - TYPVAL_ENCODE_CONV_NIL(tv) - -#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \ - do { \ - (void)num; \ - tv->vval.v_number = 0; \ - tv->v_lock = VAR_UNLOCKED; \ - } while (0) - -#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) - -#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \ - do { \ - tv->vval.v_float = 0; \ - tv->v_lock = VAR_UNLOCKED; \ - } while (0) - -#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \ - do { \ - xfree(buf); \ - tv->vval.v_string = NULL; \ - tv->v_lock = VAR_UNLOCKED; \ - } while (0) - -#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len) - -#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type) - -static inline int _nothing_conv_func_start(typval_T *const tv, - char_u *const fun) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(1) -{ - tv->v_lock = VAR_UNLOCKED; - if (tv->v_type == VAR_PARTIAL) { - partial_T *const pt_ = tv->vval.v_partial; - if (pt_ != NULL && pt_->pt_refcount > 1) { - pt_->pt_refcount--; - tv->vval.v_partial = NULL; - return OK; - } - } else { - func_unref(fun); - if (fun != empty_string) { - xfree(fun); - } - tv->vval.v_string = NULL; - } - return NOTDONE; -} -#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ - do { \ - if (_nothing_conv_func_start(tv, fun) != NOTDONE) { \ - return OK; \ - } \ - } while (0) - -#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len) -#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len) - -static inline void _nothing_conv_func_end(typval_T *const tv, const int copyID) - FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL -{ - if (tv->v_type == VAR_PARTIAL) { - partial_T *const pt = tv->vval.v_partial; - if (pt == NULL) { - return; - } - // Dictionary should already be freed by the time. - // If it was not freed then it is a part of the reference cycle. - assert(pt->pt_dict == NULL || pt->pt_dict->dv_copyID == copyID); - pt->pt_dict = NULL; - // As well as all arguments. - pt->pt_argc = 0; - assert(pt->pt_refcount <= 1); - partial_unref(pt); - tv->vval.v_partial = NULL; - assert(tv->v_lock == VAR_UNLOCKED); - } -} -#define TYPVAL_ENCODE_CONV_FUNC_END(tv) _nothing_conv_func_end(tv, copyID) - -#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \ - do { \ - list_unref(tv->vval.v_list); \ - tv->vval.v_list = NULL; \ - tv->v_lock = VAR_UNLOCKED; \ - } while (0) - -#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ - do { \ - assert((void *)&dict != (void *)&TYPVAL_ENCODE_NODICT_VAR); \ - dict_unref((dict_T *)dict); \ - *((dict_T **)&dict) = NULL; \ - if (tv != NULL) { \ - ((typval_T *)tv)->v_lock = VAR_UNLOCKED; \ - } \ - } while (0) - -static inline int _nothing_conv_real_list_after_start( - typval_T *const tv, MPConvStackVal *const mpsv) - FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT -{ - assert(tv != NULL); - tv->v_lock = VAR_UNLOCKED; - if (tv->vval.v_list->lv_refcount > 1) { - tv->vval.v_list->lv_refcount--; - tv->vval.v_list = NULL; - mpsv->data.l.li = NULL; - return OK; - } - return NOTDONE; -} -#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) - -#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) \ - do { \ - if (_nothing_conv_real_list_after_start(tv, &mpsv) != NOTDONE) { \ - goto typval_encode_stop_converting_one_item; \ - } \ - } while (0) - -#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) - -static inline void _nothing_conv_list_end(typval_T *const tv) - FUNC_ATTR_ALWAYS_INLINE -{ - if (tv == NULL) { - return; - } - assert(tv->v_type == VAR_LIST); - list_T *const list = tv->vval.v_list; - list_unref(list); - tv->vval.v_list = NULL; -} -#define TYPVAL_ENCODE_CONV_LIST_END(tv) _nothing_conv_list_end(tv) - -static inline int _nothing_conv_real_dict_after_start( - typval_T *const tv, dict_T **const dictp, const void *const nodictvar, - MPConvStackVal *const mpsv) - FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT -{ - if (tv != NULL) { - tv->v_lock = VAR_UNLOCKED; - } - if ((const void *)dictp != nodictvar && (*dictp)->dv_refcount > 1) { - (*dictp)->dv_refcount--; - *dictp = NULL; - mpsv->data.d.todo = 0; - return OK; - } - return NOTDONE; -} -#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) - -#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv) \ - do { \ - if (_nothing_conv_real_dict_after_start( \ - tv, (dict_T **)&dict, (void *)&TYPVAL_ENCODE_NODICT_VAR, \ - &mpsv) != NOTDONE) { \ - goto typval_encode_stop_converting_one_item; \ - } \ - } while (0) - -#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(tv, dict) -#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) -#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) - -static inline void _nothing_conv_dict_end(typval_T *const tv, - dict_T **const dictp, - const void *const nodictvar) - FUNC_ATTR_ALWAYS_INLINE -{ - if ((const void *)dictp != nodictvar) { - dict_unref(*dictp); - *dictp = NULL; - } -} -#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \ - _nothing_conv_dict_end(tv, (dict_T **)&dict, \ - (void *)&TYPVAL_ENCODE_NODICT_VAR) - -#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) - -#define TYPVAL_ENCODE_SCOPE static -#define TYPVAL_ENCODE_NAME nothing -#define TYPVAL_ENCODE_FIRST_ARG_TYPE const void *const -#define TYPVAL_ENCODE_FIRST_ARG_NAME ignored -#include "nvim/eval/typval_encode.c.h" -#undef TYPVAL_ENCODE_SCOPE -#undef TYPVAL_ENCODE_NAME -#undef TYPVAL_ENCODE_FIRST_ARG_TYPE -#undef TYPVAL_ENCODE_FIRST_ARG_NAME - -#undef TYPVAL_ENCODE_ALLOW_SPECIALS -#undef TYPVAL_ENCODE_CONV_NIL -#undef TYPVAL_ENCODE_CONV_BOOL -#undef TYPVAL_ENCODE_CONV_NUMBER -#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER -#undef TYPVAL_ENCODE_CONV_FLOAT -#undef TYPVAL_ENCODE_CONV_STRING -#undef TYPVAL_ENCODE_CONV_STR_STRING -#undef TYPVAL_ENCODE_CONV_EXT_STRING -#undef TYPVAL_ENCODE_CONV_FUNC_START -#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS -#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF -#undef TYPVAL_ENCODE_CONV_FUNC_END -#undef TYPVAL_ENCODE_CONV_EMPTY_LIST -#undef TYPVAL_ENCODE_CONV_EMPTY_DICT -#undef TYPVAL_ENCODE_CONV_LIST_START -#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START -#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS -#undef TYPVAL_ENCODE_CONV_LIST_END -#undef TYPVAL_ENCODE_CONV_DICT_START -#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START -#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK -#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY -#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS -#undef TYPVAL_ENCODE_CONV_DICT_END -#undef TYPVAL_ENCODE_CONV_RECURSE - -/// Free memory for a variable value and set the value to NULL or 0 -/// -/// @param[in,out] varp Value to free. -void clear_tv(typval_T *varp) -{ - if (varp != NULL && varp->v_type != VAR_UNKNOWN) { - const int evn_ret = encode_vim_to_nothing(varp, varp, "clear_tv argument"); - (void)evn_ret; - assert(evn_ret == OK); - } -} - /* * Set the value of a variable to NULL without freeing items. */ @@ -19932,69 +19037,25 @@ static void init_tv(typval_T *varp) memset(varp, 0, sizeof(typval_T)); } -/// Check that given value is a number or string +// TODO(ZyX-I): move to eval/typval + +/// Get the number value of a variable /// -/// Error messages are compatible with get_tv_number() previously used for the -/// same purpose in buf*() functions. Special values are not accepted (previous -/// behaviour: silently fail to find buffer). +/// @note Use get_tv_number_chk() if you need to determine whether there was an +/// error. /// -/// @param[in] tv Value to check. +/// @param[in] varp Variable to get value from. /// -/// @return true if everything is OK, false otherwise. -bool tv_check_str_or_nr(const typval_T *const tv) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE +/// @return Number value: vim_str2nr() output for VAR_STRING variables, value +/// for VAR_NUMBER variables, -1 for other types. +varnumber_T get_tv_number(const typval_T *const varp) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - switch (tv->v_type) { - case VAR_NUMBER: - case VAR_STRING: { - return true; - } - case VAR_FLOAT: { - EMSG(_("E805: Expected a Number or a String, Float found")); - return false; - } - case VAR_PARTIAL: - case VAR_FUNC: { - EMSG(_("E703: Expected a Number or a String, Funcref found")); - return false; - } - case VAR_LIST: { - EMSG(_("E745: Expected a Number or a String, List found")); - return false; - } - case VAR_DICT: { - EMSG(_("E728: Expected a Number or a String, Dictionary found")); - return false; - } - case VAR_SPECIAL: { - EMSG(_("E5300: Expected a Number or a String")); - return false; - } - case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "tv_check_str_or_nr(UNKNOWN)"); - return false; - } - } - assert(false); - return false; + bool error = false; + return get_tv_number_chk(varp, &error); } -/* - * Get the number value of a variable. - * If it is a String variable, uses vim_str2nr(). - * For incompatible types, return 0. - * get_tv_number_chk() is similar to get_tv_number(), but informs the - * caller of incompatible types: it sets *denote to TRUE if "denote" - * is not NULL or returns -1 otherwise. - */ -long get_tv_number(typval_T *varp) -{ - int error = FALSE; - - return get_tv_number_chk(varp, &error); /* return 0L on error */ -} - -long get_tv_number_chk(typval_T *varp, int *denote) +varnumber_T get_tv_number_chk(const typval_T *const varp, bool *const denote) { long n = 0L; @@ -20087,7 +19148,7 @@ static linenr_T get_tv_lnum(typval_T *argvars) rettv.v_type = VAR_NUMBER; f_line(argvars, &rettv, NULL); lnum = rettv.vval.v_number; - clear_tv(&rettv); + tv_clear(&rettv); } return lnum; } @@ -20107,35 +19168,41 @@ static linenr_T get_tv_lnum_buf(typval_T *argvars, buf_T *buf) return get_tv_number_chk(&argvars[0], NULL); } -/* - * Get the string value of a variable. - * If it is a Number variable, the number is converted into a string. - * get_tv_string() uses a single, static buffer. YOU CAN ONLY USE IT ONCE! - * get_tv_string_buf() uses a given buffer. - * If the String variable has never been set, return an empty string. - * Never returns NULL; - * get_tv_string_chk() and get_tv_string_buf_chk() are similar, but return - * NULL on error. - */ -static char_u *get_tv_string(const typval_T *varp) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET +// TODO(ZyX-I): move to eval/typval + +/// Get the string value of a variable +/// +/// @warning For number and special values it uses a single, static buffer. It +/// may be used only once, next call to get_tv_string may reuse it. Use +/// get_tv_string_buf() if you need to use get_tv_string() output after +/// calling it again. +/// +/// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but +/// return NULL on error. +/// +/// @param[in] varp Varible to get value of. +/// +/// @return Variable value if it is VAR_STRING variable, number converted to +/// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or empty +/// string. +char_u *get_tv_string(const typval_T *const varp) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { static char_u mybuf[NUMBUFLEN]; - return get_tv_string_buf(varp, mybuf); } -static char_u *get_tv_string_buf(const typval_T *varp, char_u *buf) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET +char_u *get_tv_string_buf(const typval_T *varp, char_u *buf) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { - char_u *res = get_tv_string_buf_chk(varp, buf); + char_u *const res = get_tv_string_buf_chk(varp, buf); return res != NULL ? res : (char_u *)""; } /// Careful: This uses a single, static buffer. YOU CAN ONLY USE IT ONCE! char_u *get_tv_string_chk(const typval_T *varp) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { static char_u mybuf[NUMBUFLEN]; @@ -20143,7 +19210,7 @@ char_u *get_tv_string_chk(const typval_T *varp) } char_u *get_tv_string_buf_chk(const typval_T *varp, char_u *buf) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { switch (varp->v_type) { case VAR_NUMBER: @@ -20438,7 +19505,7 @@ void new_script_vars(scid_T id) * Initialize dictionary "dict" as a scope and set variable "dict_var" to * point to it. */ -void init_var_dict(dict_T *dict, dictitem_T *dict_var, int scope) +void init_var_dict(dict_T *dict, ScopeDictDictItem *dict_var, int scope) { hash_init(&dict->dv_hashtab); dict->dv_lock = VAR_UNLOCKED; @@ -20494,7 +19561,7 @@ static void vars_clear_ext(hashtab_T *ht, int free_val) // later. v = HI2DI(hi); if (free_val) { - clear_tv(&v->di_tv); + tv_clear(&v->di_tv); } if (v->di_flags & DI_FLAGS_ALLOC) { xfree(v); @@ -20514,7 +19581,7 @@ static void delete_var(hashtab_T *ht, hashitem_T *hi) dictitem_T *di = HI2DI(hi); hash_remove(ht, hi); - clear_tv(&di->di_tv); + tv_clear(&di->di_tv); xfree(di); } @@ -20652,11 +19719,11 @@ set_var ( if (watched) { copy_tv(&v->di_tv, &oldtv); } - clear_tv(&v->di_tv); - } else { /* add a new variable */ - /* Can't add "v:" variable. */ + tv_clear(&v->di_tv); + } else { // Add a new variable. + // Can't add "v:" variable. if (ht == &vimvarht) { - EMSG2(_(e_illvar), name); + emsgf(_(e_illvar), name); return; } @@ -20686,7 +19753,7 @@ set_var ( dictwatcher_notify(dict, (char *)v->di_key, &v->di_tv, NULL); } else { dictwatcher_notify(dict, (char *)v->di_key, &v->di_tv, &oldtv); - clear_tv(&oldtv); + tv_clear(&oldtv); } } } @@ -20934,7 +20001,7 @@ int var_item_copy(const vimconv_T *const conv, to->vval.v_list = from->vval.v_list->lv_copylist; ++to->vval.v_list->lv_refcount; } else { - to->vval.v_list = list_copy(conv, from->vval.v_list, deep, copyID); + to->vval.v_list = tv_list_copy(conv, from->vval.v_list, deep, copyID); } if (to->vval.v_list == NULL) ret = FAIL; @@ -21036,7 +20103,7 @@ void ex_echo(exarg_T *eap) } xfree(tofree); } - clear_tv(&rettv); + tv_clear(&rettv); arg = skipwhite(arg); } eap->nextcmd = check_nextcmd(arg); @@ -21111,7 +20178,7 @@ void ex_execute(exarg_T *eap) ga.ga_len += len; } - clear_tv(&rettv); + tv_clear(&rettv); arg = skipwhite(arg); } @@ -21670,9 +20737,10 @@ void ex_function(exarg_T *eap) xfree(fp); goto erret; } - } else - /* overwrite existing dict entry */ - clear_tv(&fudi.fd_di->di_tv); + } else { + // Overwrite existing dict entry. + tv_clear(&fudi.fd_di->di_tv); + } fudi.fd_di->di_tv.v_type = VAR_FUNC; fudi.fd_di->di_tv.v_lock = 0; fudi.fd_di->di_tv.vval.v_string = vim_strsave(name); @@ -21733,7 +20801,7 @@ ret_free: /// Advances "pp" to just after the function name (if no error). /// /// @return the function name in allocated memory, or NULL for failure. -char_u * +static char_u * trans_function_name( char_u **pp, int skip, /* only find the end, don't evaluate */ @@ -22720,9 +21788,9 @@ call_user_func( // Init l: variables. init_var_dict(&fc->l_vars, &fc->l_vars_var, VAR_DEF_SCOPE); if (selfdict != NULL) { - /* Set l:self to "selfdict". Use "name" to avoid a warning from - * some compiler that checks the destination size. */ - v = &fc->fixvar[fixvar_idx++].var; + // Set l:self to "selfdict". Use "name" to avoid a warning from + // some compiler that checks the destination size. + v = (dictitem_T *)&fc->fixvar[fixvar_idx++]; #ifndef __clang_analyzer__ name = v->di_key; STRCPY(name, "self"); @@ -22741,11 +21809,11 @@ call_user_func( * Set a:000 to a list with room for the "..." arguments. */ init_var_dict(&fc->l_avars, &fc->l_avars_var, VAR_SCOPE); - add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "0", - (varnumber_T)(argcount - fp->uf_args.ga_len)); - /* Use "name" to avoid a warning from some compiler that checks the - * destination size. */ - v = &fc->fixvar[fixvar_idx++].var; + add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], "0", + (varnumber_T)(argcount - fp->uf_args.ga_len)); + // Use "name" to avoid a warning from some compiler that checks the + // destination size. + v = (dictitem_T *)&fc->fixvar[fixvar_idx++]; #ifndef __clang_analyzer__ name = v->di_key; STRCPY(name, "000"); @@ -22762,10 +21830,10 @@ call_user_func( // Set a:firstline to "firstline" and a:lastline to "lastline". // Set a:name to named arguments. // Set a:N to the "..." arguments. - add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "firstline", - (varnumber_T)firstline); - add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "lastline", - (varnumber_T)lastline); + add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], + "firstline", (varnumber_T)firstline); + add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], + "lastline", (varnumber_T)lastline); for (int i = 0; i < argcount; i++) { bool addlocal = false; @@ -22782,7 +21850,7 @@ call_user_func( name = numbuf; } if (fixvar_idx < FIXVAR_CNT && STRLEN(name) <= VAR_SHORT_LEN) { - v = &fc->fixvar[fixvar_idx++].var; + v = (dictitem_T *)&fc->fixvar[fixvar_idx++]; v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; } else { v = xmalloc(sizeof(dictitem_T) + STRLEN(name)); @@ -22805,7 +21873,7 @@ call_user_func( } if (ai >= 0 && ai < MAX_FUNC_ARGS) { - list_append(&fc->l_varlist, &fc->l_listitems[ai]); + tv_list_append(&fc->l_varlist, &fc->l_listitems[ai]); fc->l_listitems[ai].li_tv = argvars[i]; fc->l_listitems[ai].li_tv.v_lock = VAR_FIXED; } @@ -22913,7 +21981,7 @@ call_user_func( // when the function was aborted because of an error, return -1 if ((did_emsg && (fp->uf_flags & FC_ABORT)) || rettv->v_type == VAR_UNKNOWN) { - clear_tv(rettv); + tv_clear(rettv); rettv->v_type = VAR_NUMBER; rettv->vval.v_number = -1; } @@ -23109,7 +22177,7 @@ free_funccal ( // Free the a:000 variables if they were allocated. if (free_val) { for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) { - clear_tv(&li->li_tv); + tv_clear(&li->li_tv); } } @@ -23152,10 +22220,11 @@ void ex_return(exarg_T *eap) eap->nextcmd = NULL; if ((*arg != NUL && *arg != '|' && *arg != '\n') && eval0(arg, &rettv, &eap->nextcmd, !eap->skip) != FAIL) { - if (!eap->skip) - returning = do_return(eap, FALSE, TRUE, &rettv); - else - clear_tv(&rettv); + if (!eap->skip) { + returning = do_return(eap, false, true, &rettv); + } else { + tv_clear(&rettv); + } } /* It's safer to return also on error. */ else if (!eap->skip) { @@ -23242,7 +22311,7 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv) * a return immediately after reanimation, the value is already * there. */ if (!reanimate && rettv != NULL) { - clear_tv(current_funccal->rettv); + tv_clear(current_funccal->rettv); *current_funccal->rettv = *(typval_T *)rettv; if (!is_cmd) xfree(rettv); @@ -23652,12 +22721,12 @@ void ex_oldfiles(exarg_T *eap) nr = prompt_for_number(false); msg_starthere(); if (nr > 0 && nr <= l->lv_len) { - char_u *p = list_find_str(l, nr); + char *p = tv_list_find_str(l, nr); if (p == NULL) { return; } - p = expand_env_save(p); - eap->arg = p; + p = (char *)expand_env_save((char_u *)p); + eap->arg = (char_u *)p; eap->cmdidx = CMD_edit; do_exedit(eap, NULL); xfree(p); @@ -24148,7 +23217,7 @@ static inline void process_job_event(TerminalJobData *data, Callback *callback, JobEvent event_data; event_data.received = NULL; if (buf) { - event_data.received = list_alloc(); + event_data.received = tv_list_alloc(); char *ptr = buf; size_t remaining = count; size_t off = 0; @@ -24156,7 +23225,7 @@ static inline void process_job_event(TerminalJobData *data, Callback *callback, while (off < remaining) { // append the line if (ptr[off] == NL) { - list_append_string(event_data.received, (uint8_t *)ptr, off); + tv_list_append_string(event_data.received, ptr, off); size_t skip = off + 1; ptr += skip; remaining -= skip; @@ -24169,7 +23238,7 @@ static inline void process_job_event(TerminalJobData *data, Callback *callback, } off++; } - list_append_string(event_data.received, (uint8_t *)ptr, off); + tv_list_append_string(event_data.received, ptr, off); } else { event_data.status = status; } @@ -24318,7 +23387,7 @@ static void on_job_event(JobEvent *ev) typval_T rettv; init_tv(&rettv); callback_call(ev->callback, 3, argv, &rettv); - clear_tv(&rettv); + tv_clear(&rettv); } static TerminalJobData *find_job(uint64_t id) @@ -24341,8 +23410,8 @@ static void script_host_eval(char *name, typval_T *argvars, typval_T *rettv) return; } - list_T *args = list_alloc(); - list_append_string(args, argvars[0].vval.v_string, -1); + list_T *args = tv_list_alloc(); + tv_list_append_string(args, (const char *)argvars[0].vval.v_string, -1); *rettv = eval_call_provider(name, "eval", args); } @@ -24387,7 +23456,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) NULL, NULL); - list_unref(arguments); + tv_list_unref(arguments); // Restore caller scope information restore_funccal(provider_caller_scope.funccalp); provider_caller_scope = saved_provider_caller_scope; @@ -24479,12 +23548,12 @@ static void dictwatcher_notify(dict_T *dict, const char *key, typval_T *newtv, watcher->busy = true; callback_call(&watcher->callback, 3, argv, &rettv); watcher->busy = false; - clear_tv(&rettv); + tv_clear(&rettv); } } for (size_t i = 1; i < ARRAY_SIZE(argv); i++) { - clear_tv(argv + i); + tv_clear(argv + i); } } diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 43e9f76c0f..1d30f51f55 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -2,7 +2,7 @@ #include -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/ascii.h" @@ -118,18 +118,18 @@ static inline int json_decoder_pop(ValuesStackItem obj, if (last_container.container.vval.v_list->lv_len != 0 && !obj.didcomma) { EMSG2(_("E474: Expected comma before list item: %s"), val_location); - clear_tv(&obj.val); + tv_clear(&obj.val); return FAIL; } assert(last_container.special_val == NULL); - listitem_T *obj_li = listitem_alloc(); + listitem_T *obj_li = tv_list_item_alloc(); obj_li->li_tv = obj.val; - list_append(last_container.container.vval.v_list, obj_li); + tv_list_append(last_container.container.vval.v_list, obj_li); } else if (last_container.stack_index == kv_size(*stack) - 2) { if (!obj.didcolon) { EMSG2(_("E474: Expected colon before dictionary value: %s"), val_location); - clear_tv(&obj.val); + tv_clear(&obj.val); return FAIL; } ValuesStackItem key = kv_pop(*stack); @@ -139,33 +139,33 @@ static inline int json_decoder_pop(ValuesStackItem obj, || key.val.vval.v_string == NULL || *key.val.vval.v_string == NUL)); dictitem_T *obj_di = dictitem_alloc(key.val.vval.v_string); - clear_tv(&key.val); + tv_clear(&key.val); if (dict_add(last_container.container.vval.v_dict, obj_di) == FAIL) { assert(false); } obj_di->di_tv = obj.val; } else { - list_T *const kv_pair = list_alloc(); - list_append_list(last_container.special_val, kv_pair); - listitem_T *const key_li = listitem_alloc(); + list_T *const kv_pair = tv_list_alloc(); + tv_list_append_list(last_container.special_val, kv_pair); + listitem_T *const key_li = tv_list_item_alloc(); key_li->li_tv = key.val; - list_append(kv_pair, key_li); - listitem_T *const val_li = listitem_alloc(); + tv_list_append(kv_pair, key_li); + listitem_T *const val_li = tv_list_item_alloc(); val_li->li_tv = obj.val; - list_append(kv_pair, val_li); + tv_list_append(kv_pair, val_li); } } else { // Object with key only if (!obj.is_special_string && obj.val.v_type != VAR_STRING) { EMSG2(_("E474: Expected string key: %s"), *pp); - clear_tv(&obj.val); + tv_clear(&obj.val); return FAIL; } else if (!obj.didcomma && (last_container.special_val == NULL && (DICT_LEN(last_container.container.vval.v_dict) != 0))) { EMSG2(_("E474: Expected comma before dictionary key: %s"), val_location); - clear_tv(&obj.val); + tv_clear(&obj.val); return FAIL; } // Handle empty key and key represented as special dictionary @@ -175,14 +175,14 @@ static inline int json_decoder_pop(ValuesStackItem obj, || *obj.val.vval.v_string == NUL || dict_find(last_container.container.vval.v_dict, obj.val.vval.v_string, -1))) { - clear_tv(&obj.val); + tv_clear(&obj.val); // Restart (void) kv_pop(*container_stack); ValuesStackItem last_container_val = kv_A(*stack, last_container.stack_index); while (kv_size(*stack) > last_container.stack_index) { - clear_tv(&(kv_pop(*stack).val)); + tv_clear(&(kv_pop(*stack).val)); } *pp = last_container.s; *didcomma = last_container_val.didcomma; @@ -430,7 +430,7 @@ static inline int parse_json_string(vimconv_T *const conv, } if (hasnul) { typval_T obj; - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(&obj, kMPString, ((typval_T) { .v_type = VAR_LIST, @@ -439,7 +439,7 @@ static inline int parse_json_string(vimconv_T *const conv, })); if (encode_list_write((void *) list, str, (size_t) (str_end - str)) == -1) { - clear_tv(&obj); + tv_clear(&obj); goto parse_json_string_fail; } xfree(str); @@ -806,7 +806,7 @@ json_decode_string_cycle_start: break; } case '[': { - list_T *list = list_alloc(); + list_T *list = tv_list_alloc(); list->lv_refcount++; typval_T tv = { .v_type = VAR_LIST, @@ -827,7 +827,7 @@ json_decode_string_cycle_start: list_T *val_list = NULL; if (next_map_special) { next_map_special = false; - val_list = list_alloc(); + val_list = tv_list_alloc(); val_list->lv_refcount++; create_special_dict(&tv, kMPMap, ((typval_T) { .v_type = VAR_LIST, @@ -887,7 +887,7 @@ json_decode_string_after_cycle: json_decode_string_fail: ret = FAIL; while (kv_size(stack)) { - clear_tv(&(kv_pop(stack).val)); + tv_clear(&(kv_pop(stack).val)); } json_decode_string_ret: kv_destroy(stack); @@ -933,7 +933,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_number = (varnumber_T) mobj.via.u64 }, }; } else { - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(rettv, kMPInteger, ((typval_T) { .v_type = VAR_LIST, @@ -941,10 +941,10 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_list = list }, })); uint64_t n = mobj.via.u64; - list_append_number(list, 1); - list_append_number(list, (varnumber_T) ((n >> 62) & 0x3)); - list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF)); - list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF)); + tv_list_append_number(list, 1); + tv_list_append_number(list, (varnumber_T)((n >> 62) & 0x3)); + tv_list_append_number(list, (varnumber_T)((n >> 31) & 0x7FFFFFFF)); + tv_list_append_number(list, (varnumber_T)(n & 0x7FFFFFFF)); } break; } @@ -956,18 +956,18 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_number = (varnumber_T) mobj.via.i64 }, }; } else { - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(rettv, kMPInteger, ((typval_T) { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, .vval = { .v_list = list }, })); - uint64_t n = -((uint64_t) mobj.via.i64); - list_append_number(list, -1); - list_append_number(list, (varnumber_T) ((n >> 62) & 0x3)); - list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF)); - list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF)); + uint64_t n = -((uint64_t)mobj.via.i64); + tv_list_append_number(list, -1); + tv_list_append_number(list, (varnumber_T)((n >> 62) & 0x3)); + tv_list_append_number(list, (varnumber_T)((n >> 31) & 0x7FFFFFFF)); + tv_list_append_number(list, (varnumber_T)(n & 0x7FFFFFFF)); } break; } @@ -980,7 +980,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) break; } case MSGPACK_OBJECT_STR: { - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(rettv, kMPString, ((typval_T) { .v_type = VAR_LIST, @@ -1002,7 +1002,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) }; break; } - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(rettv, kMPBinary, ((typval_T) { .v_type = VAR_LIST, @@ -1016,7 +1016,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) break; } case MSGPACK_OBJECT_ARRAY: { - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; *rettv = (typval_T) { .v_type = VAR_LIST, @@ -1024,9 +1024,9 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_list = list }, }; for (size_t i = 0; i < mobj.via.array.size; i++) { - listitem_T *const li = listitem_alloc(); + listitem_T *const li = tv_list_item_alloc(); li->li_tv.v_type = VAR_UNKNOWN; - list_append(list, li); + tv_list_append(list, li); if (msgpack_to_vim(mobj.via.array.ptr[i], &li->li_tv) == FAIL) { return FAIL; } @@ -1057,7 +1057,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) di->di_tv.v_type = VAR_UNKNOWN; if (dict_add(dict, di) == FAIL) { // Duplicate key: fallback to generic map - clear_tv(rettv); + tv_clear(rettv); xfree(di); goto msgpack_to_vim_generic_map; } @@ -1067,7 +1067,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) } break; msgpack_to_vim_generic_map: {} - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(rettv, kMPMap, ((typval_T) { .v_type = VAR_LIST, @@ -1075,14 +1075,14 @@ msgpack_to_vim_generic_map: {} .vval = { .v_list = list }, })); for (size_t i = 0; i < mobj.via.map.size; i++) { - list_T *const kv_pair = list_alloc(); - list_append_list(list, kv_pair); - listitem_T *const key_li = listitem_alloc(); + list_T *const kv_pair = tv_list_alloc(); + tv_list_append_list(list, kv_pair); + listitem_T *const key_li = tv_list_item_alloc(); key_li->li_tv.v_type = VAR_UNKNOWN; - list_append(kv_pair, key_li); - listitem_T *const val_li = listitem_alloc(); + tv_list_append(kv_pair, key_li); + listitem_T *const val_li = tv_list_item_alloc(); val_li->li_tv.v_type = VAR_UNKNOWN; - list_append(kv_pair, val_li); + tv_list_append(kv_pair, val_li); if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_li->li_tv) == FAIL) { return FAIL; } @@ -1093,11 +1093,11 @@ msgpack_to_vim_generic_map: {} break; } case MSGPACK_OBJECT_EXT: { - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; - list_append_number(list, mobj.via.ext.type); - list_T *const ext_val_list = list_alloc(); - list_append_list(list, ext_val_list); + tv_list_append_number(list, mobj.via.ext.type); + list_T *const ext_val_list = tv_list_alloc(); + tv_list_append_list(list, ext_val_list); create_special_dict(rettv, kMPExt, ((typval_T) { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, diff --git a/src/nvim/eval/decode.h b/src/nvim/eval/decode.h index 5c25a64f7a..c8e7a189e3 100644 --- a/src/nvim/eval/decode.h +++ b/src/nvim/eval/decode.h @@ -5,7 +5,7 @@ #include -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/decode.h.generated.h" diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index ee66b7cf09..1416806ca6 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -13,7 +13,7 @@ #include "nvim/eval/encode.h" #include "nvim/buffer_defs.h" // vimconv_T #include "nvim/eval.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/garray.h" #include "nvim/mbyte.h" #include "nvim/message.h" @@ -45,7 +45,8 @@ const char *const encode_special_var_names[] = { #endif /// Msgpack callback for writing to readfile()-style list -int encode_list_write(void *data, const char *buf, size_t len) +int encode_list_write(void *const data, const char *const buf, const size_t len) + FUNC_ATTR_NONNULL_ARG(1) { if (len == 0) { return 0; @@ -80,11 +81,11 @@ int encode_list_write(void *data, const char *buf, size_t len) str = xmemdupz(line_start, line_length); memchrsub(str, NUL, NL, line_length); } - list_append_allocated_string(list, str); + tv_list_append_allocated_string(list, str); line_end++; } if (line_end == end) { - list_append_allocated_string(list, NULL); + tv_list_append_allocated_string(list, NULL); } return 0; } diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c new file mode 100644 index 0000000000..ab48ace400 --- /dev/null +++ b/src/nvim/eval/executor.c @@ -0,0 +1,114 @@ +#include "nvim/eval/typval.h" +#include "nvim/eval/executor.h" +#include "nvim/eval.h" +#include "nvim/message.h" +#include "nvim/vim.h" +#include "nvim/globals.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/executor.c.generated.h" +#endif + +static char *e_letwrong = N_("E734: Wrong variable type for %s="); + +char *e_listidx = N_("E684: list index out of range: %" PRId64); + +/// Hanle tv1 += tv2, -=, .= +/// +/// @param[in,out] tv1 First operand, modified typval. +/// @param[in] tv2 Second operand. +/// @param[in] op Used operator. +/// +/// @return OK or FAIL. +int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, + const char *const op) + FUNC_ATTR_NONNULL_ALL +{ + // Can't do anything with a Funcref, a Dict or special value on the right. + if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT) { + switch (tv1->v_type) { + case VAR_DICT: + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_SPECIAL: { + break; + } + case VAR_LIST: { + if (*op != '+' || tv2->v_type != VAR_LIST) { + break; + } + // List += List + if (tv1->vval.v_list != NULL && tv2->vval.v_list != NULL) { + tv_list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL); + } + return OK; + } + case VAR_NUMBER: + case VAR_STRING: { + if (tv2->v_type == VAR_LIST) { + break; + } + if (*op == '+' || *op == '-') { + // nr += nr or nr -= nr + varnumber_T n = get_tv_number(tv1); + if (tv2->v_type == VAR_FLOAT) { + float_T f = n; + + if (*op == '+') { + f += tv2->vval.v_float; + } else { + f -= tv2->vval.v_float; + } + tv_clear(tv1); + tv1->v_type = VAR_FLOAT; + tv1->vval.v_float = f; + } else { + if (*op == '+') { + n += get_tv_number(tv2); + } else { + n -= get_tv_number(tv2); + } + tv_clear(tv1); + tv1->v_type = VAR_NUMBER; + tv1->vval.v_number = n; + } + } else { + // str .= str + if (tv2->v_type == VAR_FLOAT) { + break; + } + char *s = (char *)get_tv_string(tv1); + char numbuf[NUMBUFLEN]; + s = (char *)concat_str((char_u *)s, + get_tv_string_buf(tv2, (char_u *)numbuf)); + tv_clear(tv1); + tv1->v_type = VAR_STRING; + tv1->vval.v_string = (char_u *)s; + } + return OK; + } + case VAR_FLOAT: { + if (*op == '.' || (tv2->v_type != VAR_FLOAT + && tv2->v_type != VAR_NUMBER + && tv2->v_type != VAR_STRING)) { + break; + } + const float_T f = (tv2->v_type == VAR_FLOAT + ? tv2->vval.v_float + : get_tv_number(tv2)); + if (*op == '+') { + tv1->vval.v_float += f; + } else { + tv1->vval.v_float -= f; + } + return OK; + } + case VAR_UNKNOWN: { + assert(false); + } + } + } + + EMSG2(_(e_letwrong), op); + return FAIL; +} diff --git a/src/nvim/eval/executor.h b/src/nvim/eval/executor.h new file mode 100644 index 0000000000..19e2a75914 --- /dev/null +++ b/src/nvim/eval/executor.h @@ -0,0 +1,9 @@ +#ifndef NVIM_EVAL_EXECUTOR_H +#define NVIM_EVAL_EXECUTOR_H + +extern char *e_listidx; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/executor.h.generated.h" +#endif +#endif // NVIM_EVAL_EXECUTOR_H diff --git a/src/nvim/eval/gc.c b/src/nvim/eval/gc.c new file mode 100644 index 0000000000..5ce52ddd70 --- /dev/null +++ b/src/nvim/eval/gc.c @@ -0,0 +1,11 @@ +#include "nvim/eval/typval.h" +#include "nvim/eval/gc.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/gc.c.generated.h" +#endif + +/// Head of list of all dictionaries +dict_T *gc_first_dict = NULL; +/// Head of list of all lists +list_T *gc_first_list = NULL; diff --git a/src/nvim/eval/gc.h b/src/nvim/eval/gc.h new file mode 100644 index 0000000000..c2e862e469 --- /dev/null +++ b/src/nvim/eval/gc.h @@ -0,0 +1,12 @@ +#ifndef NVIM_EVAL_GC_H +#define NVIM_EVAL_GC_H + +#include "nvim/eval/typval.h" + +extern dict_T *gc_first_dict; +extern list_T *gc_first_list; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/gc.h.generated.h" +#endif +#endif // NVIM_EVAL_GC_H diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c new file mode 100644 index 0000000000..7726e106a1 --- /dev/null +++ b/src/nvim/eval/typval.c @@ -0,0 +1,1171 @@ +#include +#include +#include + +#include "nvim/eval/typval.h" +#include "nvim/eval/gc.h" +#include "nvim/eval/executor.h" +#include "nvim/eval/encode.h" +#include "nvim/eval/typval_encode.h" +#include "nvim/eval.h" +#include "nvim/types.h" +#include "nvim/assert.h" +#include "nvim/memory.h" +#include "nvim/globals.h" +// TODO(ZyX-I): Move line_breakcheck out of misc1 +#include "nvim/misc1.h" // For line_breakcheck + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/typval.c.generated.h" +#endif + +bool tv_in_free_unref_items = false; + +// TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead + +#define DICT_MAXNEST 100 + +const char *const tv_empty_string = ""; + +//{{{1 Lists +//{{{2 List item + +/// Allocate a list item +/// +/// @warning Allocated item is not initialized, do not forget to initialize it +/// and specifically set lv_lock. +/// +/// @return [allocated] new list item. +listitem_T *tv_list_item_alloc(void) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC +{ + return xmalloc(sizeof(listitem_T)); +} + +/// Free a list item +/// +/// Also clears the value. Does not touch watchers. +/// +/// @param[out] item Item to free. +void tv_list_item_free(listitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + tv_clear(&item->li_tv); + xfree(item); +} + +/// Remove a list item from a List and free it +/// +/// Also clears the value. +/// +/// @param[out] l List to remove item from. +/// @param[in,out] item Item to remove. +void tv_list_item_remove(list_T *const l, listitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + tv_list_remove_items(l, item, item); + tv_list_item_free(item); +} + +//{{{2 List watchers + +/// Add a watcher to a list +/// +/// @param[out] l List to add watcher to. +/// @param[in] lw Watcher to add. +void tv_list_watch_add(list_T *const l, listwatch_T *const lw) + FUNC_ATTR_NONNULL_ALL +{ + lw->lw_next = l->lv_watch; + l->lv_watch = lw; +} + +/// Remove a watcher from a list +/// +/// Does not give a warning if watcher was not found. +/// +/// @param[out] l List to remove watcher from. +/// @param[in] lwrem Watcher to remove. +void tv_list_watch_remove(list_T *const l, listwatch_T *const lwrem) + FUNC_ATTR_NONNULL_ALL +{ + listwatch_T **lwp = &l->lv_watch; + for (listwatch_T *lw = l->lv_watch; lw != NULL; lw = lw->lw_next) { + if (lw == lwrem) { + *lwp = lw->lw_next; + break; + } + lwp = &lw->lw_next; + } +} + +/// Advance watchers to the next item +/// +/// Used just before removing an item from a list. +/// +/// @param[out] l List from which item is removed. +/// @param[in] item List item being removed. +void tv_list_watch_fix(list_T *const l, const listitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + for (listwatch_T *lw = l->lv_watch; lw != NULL; lw = lw->lw_next) { + if (lw->lw_item == item) { + lw->lw_item = item->li_next; + } + } +} + +//{{{2 Lists +//{{{3 Alloc/free + +/// Allocate an empty list +/// +/// Caller should take care of the reference count. +/// +/// @return [allocated] new list. +list_T *tv_list_alloc(void) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC +{ + list_T *const list = xcalloc(1, sizeof(list_T)); + + // Prepend the list to the list of lists for garbage collection. + if (gc_first_list != NULL) { + gc_first_list->lv_used_prev = list; + } + list->lv_used_prev = NULL; + list->lv_used_next = gc_first_list; + gc_first_list = list; + return list; +} + +/// Free items contained in a list +/// +/// @param[in,out] l List to clear. +void tv_list_free_contents(list_T *const l) + FUNC_ATTR_NONNULL_ALL +{ + for (listitem_T *item = l->lv_first; item != NULL; item = l->lv_first) { + // Remove the item before deleting it. + l->lv_first = item->li_next; + tv_clear(&item->li_tv); + xfree(item); + } + l->lv_len = 0; + l->lv_idx_item = NULL; + for (listwatch_T *lw = l->lv_watch; lw != NULL; lw = lw->lw_next) { + lw->lw_item = NULL; + } +} + +/// Free a list itself, ignoring items it contains +/// +/// Ignores the reference count. +/// +/// @param[in,out] l List to free. +void tv_list_free_list(list_T *const l) + FUNC_ATTR_NONNULL_ALL +{ + // Remove the list from the list of lists for garbage collection. + if (l->lv_used_prev == NULL) { + gc_first_list = l->lv_used_next; + } else { + l->lv_used_prev->lv_used_next = l->lv_used_next; + } + if (l->lv_used_next != NULL) { + l->lv_used_next->lv_used_prev = l->lv_used_prev; + } + + xfree(l); +} + +/// Free a list, including all items it points to +/// +/// Ignores the reference count. Does not do anything if +/// tv_in_free_unref_items is true. +/// +/// @param[in,out] l List to free. +void tv_list_free(list_T *const l) + FUNC_ATTR_NONNULL_ALL +{ + if (!tv_in_free_unref_items) { + tv_list_free_contents(l); + tv_list_free_list(l); + } +} + +/// Unreference a list +/// +/// Decrements the reference count and frees when it becomes zero or less. +/// +/// @param[in,out] l List to unreference. +void tv_list_unref(list_T *const l) +{ + if (l != NULL && --l->lv_refcount <= 0) { + tv_list_free(l); + } +} + +//{{{3 Add/remove + +/// Remove items "item" to "item2" from list "l". +/// +/// @warning Does not free the listitem or the value! +/// +/// @param[out] l List to remove from. +/// @param[in] item First item to remove. +/// @param[in] item2 Last item to remove. +void tv_list_remove_items(list_T *const l, listitem_T *const item, + listitem_T *const item2) +{ + // notify watchers + for (listitem_T *ip = item; ip != NULL; ip = ip->li_next) { + l->lv_len--; + tv_list_watch_fix(l, ip); + if (ip == item2) { + break; + } + } + + if (item2->li_next == NULL) { + l->lv_last = item->li_prev; + } else { + item2->li_next->li_prev = item->li_prev; + } + if (item->li_prev == NULL) { + l->lv_first = item2->li_next; + } else { + item->li_prev->li_next = item2->li_next; + } + l->lv_idx_item = NULL; +} + +/// Insert list item +/// +/// @param[out] l List to insert to. +/// @param[in,out] ni Item to insert. +/// @param[in] item Item to insert before. If NULL, inserts at the end of the +/// list. +void tv_list_insert(list_T *const l, listitem_T *const ni, + listitem_T *const item) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + if (item == NULL) { + // Append new item at end of list. + tv_list_append(l, ni); + } else { + // Insert new item before existing item. + ni->li_prev = item->li_prev; + ni->li_next = item; + if (item->li_prev == NULL) { + l->lv_first = ni; + l->lv_idx++; + } else { + item->li_prev->li_next = ni; + l->lv_idx_item = NULL; + } + item->li_prev = ni; + l->lv_len++; + } +} + +/// Insert VimL value into a list +/// +/// @param[out] l List to insert to. +/// @param[in,out] tv Value to insert. Is copied (@see copy_tv()) to an +/// allocated listitem_T and inserted. +/// @param[in] item Item to insert before. If NULL, inserts at the end of the +/// list. +void tv_list_insert_tv(list_T *const l, typval_T *const tv, + listitem_T *const item) +{ + listitem_T *const ni = tv_list_item_alloc(); + + copy_tv(tv, &ni->li_tv); + tv_list_insert(l, ni, item); +} + +/// Append item to the end of list +/// +/// @param[out] l List to append to. +/// @param[in,out] item Item to append. +void tv_list_append(list_T *const l, listitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + if (l->lv_last == NULL) { + // empty list + l->lv_first = item; + l->lv_last = item; + item->li_prev = NULL; + } else { + l->lv_last->li_next = item; + item->li_prev = l->lv_last; + l->lv_last = item; + } + l->lv_len++; + item->li_next = NULL; +} + +/// Append VimL value to the end of list +/// +/// @param[out] l List to append to. +/// @param[in,out] tv Value to append. Is copied (@see copy_tv()) to an +/// allocated listitem_T. +void tv_list_append_tv(list_T *const l, typval_T *const tv) + FUNC_ATTR_NONNULL_ALL +{ + listitem_T *const li = tv_list_item_alloc(); + copy_tv(tv, &li->li_tv); + tv_list_append(l, li); +} + +/// Append a list to a list as one item +/// +/// @param[out] l List to append to. +/// @param[in,out] itemlist List to append. Reference count is increased. +void tv_list_append_list(list_T *const list, list_T *const itemlist) + FUNC_ATTR_NONNULL_ARG(1) +{ + listitem_T *const li = tv_list_item_alloc(); + + li->li_tv.v_type = VAR_LIST; + li->li_tv.v_lock = VAR_UNLOCKED; + li->li_tv.vval.v_list = itemlist; + tv_list_append(list, li); + if (itemlist != NULL) { + itemlist->lv_refcount++; + } +} + +/// Append a dictionary to a list +/// +/// @param[out] l List to append to. +/// @param[in,out] dict Dictionary to append. Reference count is increased. +void tv_list_append_dict(list_T *const list, dict_T *const dict) + FUNC_ATTR_NONNULL_ARG(1) +{ + listitem_T *const li = tv_list_item_alloc(); + + li->li_tv.v_type = VAR_DICT; + li->li_tv.v_lock = VAR_UNLOCKED; + li->li_tv.vval.v_dict = dict; + tv_list_append(list, li); + if (dict != NULL) { + dict->dv_refcount++; + } +} + +/// Make a copy of "str" and append it as an item to list "l" +/// +/// @param[out] l List to append to. +/// @param[in] str String to append. +/// @param[in] len Length of the appended string. May be -1, in this +/// case string is considered to be usual zero-terminated +/// string or NULL “empty” string. +void tv_list_append_string(list_T *const l, const char *const str, + const ptrdiff_t len) + FUNC_ATTR_NONNULL_ARG(1) +{ + if (str == NULL) { + assert(len == 0 || len == -1); + tv_list_append_allocated_string(l, NULL); + } else { + tv_list_append_allocated_string(l, (len >= 0 + ? xmemdupz(str, (size_t)len) + : xstrdup(str))); + } +} + +/// Append given string to the list +/// +/// Unlike list_append_string this function does not copy the string. +/// +/// @param[out] l List to append to. +/// @param[in] str String to append. +void tv_list_append_allocated_string(list_T *const l, char *const str) + FUNC_ATTR_NONNULL_ARG(1) +{ + listitem_T *const li = tv_list_item_alloc(); + + tv_list_append(l, li); + li->li_tv.v_type = VAR_STRING; + li->li_tv.v_lock = VAR_UNLOCKED; + li->li_tv.vval.v_string = (char_u *)str; +} + +/// Append number to the list +/// +/// @param[out] l List to append to. +/// @param[in] n Number to append. Will be recorded in the allocated +/// listitem_T. +void tv_list_append_number(list_T *const l, const varnumber_T n) +{ + listitem_T *const li = tv_list_item_alloc(); + li->li_tv.v_type = VAR_NUMBER; + li->li_tv.v_lock = VAR_UNLOCKED; + li->li_tv.vval.v_number = n; + tv_list_append(l, li); +} + +//{{{3 Operations on the whole list + +/// Make a copy of list +/// +/// @param[in] conv If non-NULL, then all internal strings will be converted. +/// @param[in] orig Original list to copy. +/// @param[in] deep If false, then shallow copy will be done. +/// @param[in] copyID See var_item_copy(). +/// +/// @return Copied list. May be NULL in case original list is NULL or some +/// failure happens. The refcount of the new list is set to 1. +list_T *tv_list_copy(const vimconv_T *const conv, list_T *const orig, + const bool deep, const int copyID) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (orig == NULL) { + return NULL; + } + + list_T *copy = tv_list_alloc(); + if (copyID != 0) { + // Do this before adding the items, because one of the items may + // refer back to this list. + orig->lv_copyID = copyID; + orig->lv_copylist = copy; + } + listitem_T *item; + for (item = orig->lv_first; item != NULL && !got_int; + item = item->li_next) { + listitem_T *const ni = tv_list_item_alloc(); + if (deep) { + if (var_item_copy(conv, &item->li_tv, &ni->li_tv, deep, copyID) == FAIL) { + xfree(ni); + break; + } + } else { + copy_tv(&item->li_tv, &ni->li_tv); + } + tv_list_append(copy, ni); + } + copy->lv_refcount++; + if (item != NULL) { + tv_list_unref(copy); + copy = NULL; + } + + return copy; +} + +/// Extend first list with the second +/// +/// @param[out] l1 List to extend. +/// @param[in] l2 List to extend with. +/// @param[in] bef If not NULL, extends before this item. +void tv_list_extend(list_T *const l1, list_T *const l2, + listitem_T *const bef) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + int todo = l2->lv_len; + // We also quit the loop when we have inserted the original item count of + // the list, avoid a hang when we extend a list with itself. + for (listitem_T *item = l2->lv_first + ; item != NULL && --todo >= 0 + ; item = item->li_next) { + tv_list_insert_tv(l1, &item->li_tv, bef); + } +} + +/// Concatenate lists into a new list +/// +/// @param[in] l1 First list. +/// @param[in] l2 Second list. +/// @param[out] ret_tv Location where new list is saved. +/// +/// @return OK or FAIL. +int tv_list_concat(list_T *const l1, list_T *const l2, typval_T *const tv) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (l1 == NULL || l2 == NULL) { + return FAIL; + } + + // make a copy of the first list. + list_T *const l = tv_list_copy(NULL, l1, false, 0); + if (l == NULL) { + return FAIL; + } + tv->v_type = VAR_LIST; + tv->vval.v_list = l; + + // append all items from the second list + tv_list_extend(l, l2, NULL); + return OK; +} + +typedef struct { + char_u *s; + char_u *tofree; +} Join; + +/// Join list into a string, helper function +/// +/// @param[out] gap Garray where result will be saved. +/// @param[in] l List to join. +/// @param[in] sep Used separator. +/// @param[in] join_gap Garray to keep each list item string. +/// +/// @return OK in case of success, FAIL otherwise. +static int list_join_inner(garray_T *const gap, list_T *const l, + const char *const sep, garray_T *const join_gap) + FUNC_ATTR_NONNULL_ALL +{ + size_t sumlen = 0; + bool first = true; + listitem_T *item; + + // Stringify each item in the list. + for (item = l->lv_first; item != NULL && !got_int; item = item->li_next) { + char *s; + size_t len; + s = encode_tv2echo(&item->li_tv, &len); + if (s == NULL) { + return FAIL; + } + + sumlen += len; + + Join *const p = GA_APPEND_VIA_PTR(Join, join_gap); + p->tofree = p->s = (char_u *)s; + + line_breakcheck(); + } + + // Allocate result buffer with its total size, avoid re-allocation and + // multiple copy operations. Add 2 for a tailing ']' and NUL. + if (join_gap->ga_len >= 2) { + sumlen += strlen(sep) * (size_t)(join_gap->ga_len - 1); + } + ga_grow(gap, (int)sumlen + 2); + + for (int i = 0; i < join_gap->ga_len && !got_int; i++) { + if (first) { + first = false; + } else { + ga_concat(gap, (const char_u *)sep); + } + const Join *const p = ((const Join *)join_gap->ga_data) + i; + + if (p->s != NULL) { + ga_concat(gap, p->s); + } + line_breakcheck(); + } + + return OK; +} + +/// Join list into a string using given separator +/// +/// @param[out] gap Garray where result will be saved. +/// @param[in] l Joined list. +/// @param[in] sep Separator. +/// +/// @return OK in case of success, FAIL otherwise. +int tv_list_join(garray_T *const gap, list_T *const l, const char *const sep) + FUNC_ATTR_NONNULL_ALL +{ + if (l->lv_len < 1) { + return OK; + } + + garray_T join_ga; + int retval; + + ga_init(&join_ga, (int)sizeof(Join), l->lv_len); + retval = list_join_inner(gap, l, sep, &join_ga); + +#define FREE_JOIN_TOFREE(join) xfree((join)->tofree) + GA_DEEP_CLEAR(&join_ga, Join, FREE_JOIN_TOFREE); +#undef FREE_JOIN_TOFREE + + return retval; +} + +/// Chech whether two lists are equal +/// +/// @param[in] l1 First list to compare. +/// @param[in] l2 Second list to compare. +/// @param[in] ic True if case is to be ignored. +/// @param[in] recursive True when used recursively. +bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic, + const bool recursive) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (l1 == NULL || l2 == NULL) { + // FIXME? compare empty list with NULL list equal + return false; + } + if (l1 == l2) { + return true; + } + if (tv_list_len(l1) != tv_list_len(l2)) { + return false; + } + + listitem_T *item1 = l1->lv_first; + listitem_T *item2 = l2->lv_first; + for (; item1 != NULL && item2 != NULL + ; item1 = item1->li_next, item2 = item2->li_next) { + if (!tv_equal(&item1->li_tv, &item2->li_tv, ic, recursive)) { + return false; + } + } + assert(item1 == NULL && item2 == NULL); + return true; +} + +//{{{3 Indexing/searching + +/// Locate item with a given index in a list and return it +/// +/// @param[in] l List to index. +/// @param[in] n Index. Negative index is counted from the end, -1 is the last +/// item. +/// +/// @return Item at the given index or NULL if `n` is out of range. +listitem_T *tv_list_find(list_T *const l, int n) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + STATIC_ASSERT(sizeof(n) == sizeof(l->lv_idx), + "n and lv_idx sizes do not match"); + if (l == NULL) { + return NULL; + } + + // Negative index is relative to the end. + if (n < 0) { + n = l->lv_len + n; + } + + // Check for index out of range. + if (n < 0 || n >= l->lv_len) { + return NULL; + } + + int idx; + listitem_T *item; + + // When there is a cached index may start search from there. + if (l->lv_idx_item != NULL) { + if (n < l->lv_idx / 2) { + // Closest to the start of the list. + item = l->lv_first; + idx = 0; + } else if (n > (l->lv_idx + l->lv_len) / 2) { + // Closest to the end of the list. + item = l->lv_last; + idx = l->lv_len - 1; + } else { + // Closest to the cached index. + item = l->lv_idx_item; + idx = l->lv_idx; + } + } else { + if (n < l->lv_len / 2) { + // Closest to the start of the list. + item = l->lv_first; + idx = 0; + } else { + // Closest to the end of the list. + item = l->lv_last; + idx = l->lv_len - 1; + } + } + + while (n > idx) { + // Search forward. + item = item->li_next; + idx++; + } + while (n < idx) { + // Search backward. + item = item->li_prev; + idx--; + } + + assert(idx == n); + // Cache the used index. + l->lv_idx = idx; + l->lv_idx_item = item; + + return item; +} + +/// Get list item l[n] as a number +/// +/// @param[in] l List to index. +/// @param[in] n Index in a list. +/// @param[out] ret_error Location where 1 will be saved if index was not +/// found. May be NULL. If everything is OK, +/// `*ret_error` is not touched. +/// +/// @return Integer value at the given index or -1. +varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *ret_error) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + const listitem_T *const li = tv_list_find(l, n); + if (li == NULL) { + if (ret_error != NULL) { + *ret_error = true; + } + return -1; + } + return get_tv_number_chk(&li->li_tv, ret_error); +} + +/// Get list item l[n - 1] as a string +/// +/// @param[in] l List to index. +/// @param[in] n Index in a list. +/// +/// @return [allocated] Copy of the list item string value. +char *tv_list_find_str(list_T *l, int n) + FUNC_ATTR_MALLOC +{ + const listitem_T *const li = tv_list_find(l, n - 1); + if (li == NULL) { + EMSGN(_(e_listidx), n); + return NULL; + } + return (char *)get_tv_string(&li->li_tv); +} + +/// Locate item in a list and return its index +/// +/// @param[in] l List to search. +/// @param[in] item Item to search for. +/// +/// @return Index of an item or -1 if item is not in the list. +long tv_list_idx_of_item(const list_T *const l, const listitem_T *const item) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + if (l == NULL) { + return -1; + } + long idx = 0; + listitem_T *li; + for (li = l->lv_first; li != NULL && li != item; li = li->li_next) { + idx++; + } + if (li == NULL) { + return -1; + } + return idx; +} +//{{{1 Generic typval operations +//{{{2 Init/alloc/clear +//{{{3 Alloc + +/// Allocate an empty list for a return value +/// +/// Also sets reference count. +/// +/// @param[out] ret_tv Structure where list is saved. +/// +/// @return [allocated] pointer to the created list. +list_T *tv_list_alloc_ret(typval_T *const ret_tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_MALLOC +{ + list_T *const l = tv_list_alloc(); + ret_tv->vval.v_list = l; + ret_tv->v_type = VAR_LIST; + ret_tv->v_lock = VAR_UNLOCKED; + l->lv_refcount++; + return l; +} + +//{{{3 Clear +#define TYPVAL_ENCODE_ALLOW_SPECIALS false + +#define TYPVAL_ENCODE_CONV_NIL(tv) \ + do { \ + tv->vval.v_special = kSpecialVarFalse; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \ + TYPVAL_ENCODE_CONV_NIL(tv) + +#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \ + do { \ + (void)num; \ + tv->vval.v_number = 0; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) + +#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \ + do { \ + tv->vval.v_float = 0; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \ + do { \ + xfree(buf); \ + tv->vval.v_string = NULL; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len) + +#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type) + +static inline int _nothing_conv_func_start(typval_T *const tv, + char_u *const fun) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(1) +{ + tv->v_lock = VAR_UNLOCKED; + if (tv->v_type == VAR_PARTIAL) { + partial_T *const pt_ = tv->vval.v_partial; + if (pt_ != NULL && pt_->pt_refcount > 1) { + pt_->pt_refcount--; + tv->vval.v_partial = NULL; + return OK; + } + } else { + func_unref(fun); + if ((const char *)fun != tv_empty_string) { + xfree(fun); + } + tv->vval.v_string = NULL; + } + return NOTDONE; +} +#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ + do { \ + if (_nothing_conv_func_start(tv, fun) != NOTDONE) { \ + return OK; \ + } \ + } while (0) + +#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len) +#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len) + +static inline void _nothing_conv_func_end(typval_T *const tv, const int copyID) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL +{ + if (tv->v_type == VAR_PARTIAL) { + partial_T *const pt = tv->vval.v_partial; + if (pt == NULL) { + return; + } + // Dictionary should already be freed by the time. + // If it was not freed then it is a part of the reference cycle. + assert(pt->pt_dict == NULL || pt->pt_dict->dv_copyID == copyID); + pt->pt_dict = NULL; + // As well as all arguments. + pt->pt_argc = 0; + assert(pt->pt_refcount <= 1); + partial_unref(pt); + tv->vval.v_partial = NULL; + assert(tv->v_lock == VAR_UNLOCKED); + } +} +#define TYPVAL_ENCODE_CONV_FUNC_END(tv) _nothing_conv_func_end(tv, copyID) + +#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \ + do { \ + tv_list_unref(tv->vval.v_list); \ + tv->vval.v_list = NULL; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ + do { \ + assert((void *)&dict != (void *)&TYPVAL_ENCODE_NODICT_VAR); \ + dict_unref((dict_T *)dict); \ + *((dict_T **)&dict) = NULL; \ + if (tv != NULL) { \ + ((typval_T *)tv)->v_lock = VAR_UNLOCKED; \ + } \ + } while (0) + +static inline int _nothing_conv_real_list_after_start( + typval_T *const tv, MPConvStackVal *const mpsv) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT +{ + assert(tv != NULL); + tv->v_lock = VAR_UNLOCKED; + if (tv->vval.v_list->lv_refcount > 1) { + tv->vval.v_list->lv_refcount--; + tv->vval.v_list = NULL; + mpsv->data.l.li = NULL; + return OK; + } + return NOTDONE; +} +#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) + +#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) \ + do { \ + if (_nothing_conv_real_list_after_start(tv, &mpsv) != NOTDONE) { \ + goto typval_encode_stop_converting_one_item; \ + } \ + } while (0) + +#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) + +static inline void _nothing_conv_list_end(typval_T *const tv) + FUNC_ATTR_ALWAYS_INLINE +{ + if (tv == NULL) { + return; + } + assert(tv->v_type == VAR_LIST); + list_T *const list = tv->vval.v_list; + tv_list_unref(list); + tv->vval.v_list = NULL; +} +#define TYPVAL_ENCODE_CONV_LIST_END(tv) _nothing_conv_list_end(tv) + +static inline int _nothing_conv_real_dict_after_start( + typval_T *const tv, dict_T **const dictp, const void *const nodictvar, + MPConvStackVal *const mpsv) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (tv != NULL) { + tv->v_lock = VAR_UNLOCKED; + } + if ((const void *)dictp != nodictvar && (*dictp)->dv_refcount > 1) { + (*dictp)->dv_refcount--; + *dictp = NULL; + mpsv->data.d.todo = 0; + return OK; + } + return NOTDONE; +} +#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) + +#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv) \ + do { \ + if (_nothing_conv_real_dict_after_start( \ + tv, (dict_T **)&dict, (void *)&TYPVAL_ENCODE_NODICT_VAR, \ + &mpsv) != NOTDONE) { \ + goto typval_encode_stop_converting_one_item; \ + } \ + } while (0) + +#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(tv, dict) +#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) +#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) + +static inline void _nothing_conv_dict_end(typval_T *const tv, + dict_T **const dictp, + const void *const nodictvar) + FUNC_ATTR_ALWAYS_INLINE +{ + if ((const void *)dictp != nodictvar) { + dict_unref(*dictp); + *dictp = NULL; + } +} +#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \ + _nothing_conv_dict_end(tv, (dict_T **)&dict, \ + (void *)&TYPVAL_ENCODE_NODICT_VAR) + +#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) + +#define TYPVAL_ENCODE_SCOPE static +#define TYPVAL_ENCODE_NAME nothing +#define TYPVAL_ENCODE_FIRST_ARG_TYPE const void *const +#define TYPVAL_ENCODE_FIRST_ARG_NAME ignored +#include "nvim/eval/typval_encode.c.h" +#undef TYPVAL_ENCODE_SCOPE +#undef TYPVAL_ENCODE_NAME +#undef TYPVAL_ENCODE_FIRST_ARG_TYPE +#undef TYPVAL_ENCODE_FIRST_ARG_NAME + +#undef TYPVAL_ENCODE_ALLOW_SPECIALS +#undef TYPVAL_ENCODE_CONV_NIL +#undef TYPVAL_ENCODE_CONV_BOOL +#undef TYPVAL_ENCODE_CONV_NUMBER +#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER +#undef TYPVAL_ENCODE_CONV_FLOAT +#undef TYPVAL_ENCODE_CONV_STRING +#undef TYPVAL_ENCODE_CONV_STR_STRING +#undef TYPVAL_ENCODE_CONV_EXT_STRING +#undef TYPVAL_ENCODE_CONV_FUNC_START +#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS +#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF +#undef TYPVAL_ENCODE_CONV_FUNC_END +#undef TYPVAL_ENCODE_CONV_EMPTY_LIST +#undef TYPVAL_ENCODE_CONV_EMPTY_DICT +#undef TYPVAL_ENCODE_CONV_LIST_START +#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START +#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_CONV_LIST_END +#undef TYPVAL_ENCODE_CONV_DICT_START +#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START +#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK +#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY +#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_CONV_DICT_END +#undef TYPVAL_ENCODE_CONV_RECURSE + +/// Free memory for a variable value and set the value to NULL or 0 +/// +/// @param[in,out] varp Value to free. +void tv_clear(typval_T *varp) +{ + if (varp != NULL && varp->v_type != VAR_UNKNOWN) { + const int evn_ret = encode_vim_to_nothing(varp, varp, "tv_clear argument"); + (void)evn_ret; + assert(evn_ret == OK); + } +} + +//{{{2 Locks + +/// Lock or unlock an item +/// +/// @param[out] tv Item to (un)lock. +/// @param[in] deep Levels to (un)lock, -1 to (un)lock everything. +/// @param[in] lock True if it is needed to lock an item, false to unlock. +void tv_item_lock(typval_T *const tv, const int deep, const bool lock) +{ + // TODO(ZyX-I): Make this not recursive + static int recurse = 0; + + if (recurse >= DICT_MAXNEST) { + emsgf(_("E743: variable nested too deep for (un)lock")); + return; + } + if (deep == 0) { + return; + } + recurse++; + + // lock/unlock the item itself +#define CHANGE_LOCK(lock, var) \ + do { \ + var = ((VarLockStatus[]) { \ + [VAR_UNLOCKED] = (lock ? VAR_LOCKED : VAR_UNLOCKED), \ + [VAR_LOCKED] = (lock ? VAR_LOCKED : VAR_UNLOCKED), \ + [VAR_FIXED] = VAR_FIXED, \ + })[var]; \ + } while (0) + CHANGE_LOCK(lock, tv->v_lock); + + switch (tv->v_type) { + case VAR_LIST: { + list_T *const l = tv->vval.v_list; + if (l != NULL) { + CHANGE_LOCK(lock, l->lv_lock); + if (deep < 0 || deep > 1) { + // Recursive: lock/unlock the items the List contains. + for (listitem_T *li = l->lv_first; li != NULL; li = li->li_next) { + tv_item_lock(&li->li_tv, deep - 1, lock); + } + } + } + break; + } + case VAR_DICT: { + dict_T *const d = tv->vval.v_dict; + if (d != NULL) { + CHANGE_LOCK(lock, d->dv_lock); + if (deep < 0 || deep > 1) { + // recursive: lock/unlock the items the List contains + int todo = (int)d->dv_hashtab.ht_used; + for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) { + if (!HASHITEM_EMPTY(hi)) { + todo--; + tv_item_lock(&HI2DI(hi)->di_tv, deep - 1, lock); + } + } + } + } + break; + } + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_STRING: + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_SPECIAL: { + break; + } + case VAR_UNKNOWN: { + assert(false); + } + } +#undef CHANGE_LOCK + recurse--; +} + +/// Check whether VimL value is locked itself or refers to a locked container +/// +/// @param[in] tv Value to check. +/// +/// @return True if value is locked, false otherwise. +bool tv_islocked(const typval_T *const tv) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + return ((tv->v_lock & VAR_LOCKED) + || (tv->v_type == VAR_LIST + && tv->vval.v_list != NULL + && (tv->vval.v_list->lv_lock & VAR_LOCKED)) + || (tv->v_type == VAR_DICT + && tv->vval.v_dict != NULL + && (tv->vval.v_dict->dv_lock & VAR_LOCKED))); +} + +//{{{2 Type checks + +/// Check that given value is a number or string +/// +/// Error messages are compatible with get_tv_number() previously used for the +/// same purpose in buf*() functions. Special values are not accepted (previous +/// behaviour: silently fail to find buffer). +/// +/// @param[in] tv Value to check. +/// +/// @return true if everything is OK, false otherwise. +bool tv_check_str_or_nr(const typval_T *const tv) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE +{ + switch (tv->v_type) { + case VAR_NUMBER: + case VAR_STRING: { + return true; + } + case VAR_FLOAT: { + EMSG(_("E805: Expected a Number or a String, Float found")); + return false; + } + case VAR_PARTIAL: + case VAR_FUNC: { + EMSG(_("E703: Expected a Number or a String, Funcref found")); + return false; + } + case VAR_LIST: { + EMSG(_("E745: Expected a Number or a String, List found")); + return false; + } + case VAR_DICT: { + EMSG(_("E728: Expected a Number or a String, Dictionary found")); + return false; + } + case VAR_SPECIAL: { + EMSG(_("E5300: Expected a Number or a String")); + return false; + } + case VAR_UNKNOWN: { + EMSG2(_(e_intern2), "tv_check_str_or_nr(UNKNOWN)"); + return false; + } + } + assert(false); + return false; +} diff --git a/src/nvim/eval_defs.h b/src/nvim/eval/typval.h similarity index 61% rename from src/nvim/eval_defs.h rename to src/nvim/eval/typval.h index 8f5e1a897d..cf83904ffc 100644 --- a/src/nvim/eval_defs.h +++ b/src/nvim/eval/typval.h @@ -1,22 +1,32 @@ -#ifndef NVIM_EVAL_DEFS_H -#define NVIM_EVAL_DEFS_H +#ifndef NVIM_EVAL_TYPVAL_H +#define NVIM_EVAL_TYPVAL_H #include #include #include #include "nvim/hashtab.h" +#include "nvim/garray.h" +#include "nvim/mbyte.h" #include "nvim/lib/queue.h" -#include "nvim/garray.h" // for garray_T #include "nvim/profile.h" // for proftime_T #include "nvim/pos.h" // for linenr_T +/// Type used for VimL VAR_NUMBER values typedef int varnumber_T; + +/// Type used for VimL VAR_FLOAT values typedef double float_T; +/// Maximal possible value of varnumber_T variable #define VARNUMBER_MAX INT_MAX + +/// Mimimal possible value of varnumber_T variable #define VARNUMBER_MIN INT_MIN +/// %d printf format specifier for varnumber_T +#define PRIdVARNUMBER "d" + typedef struct listvar_S list_T; typedef struct dictvar_S dict_T; typedef struct partial_S partial_T; @@ -64,35 +74,32 @@ typedef struct { } vval; ///< Actual value. } typval_T; -/* Values for "dv_scope". */ -#define VAR_SCOPE 1 /* a:, v:, s:, etc. scope dictionaries */ -#define VAR_DEF_SCOPE 2 /* l:, g: scope dictionaries: here funcrefs are not - allowed to mask existing functions */ +/// Values for (struct dictvar_S).dv_scope +typedef enum { + VAR_NO_SCOPE = 0, ///< Not a scope dictionary. + VAR_SCOPE = 1, ///< Scope dictionary which requires prefix (a:, v:, …). + VAR_DEF_SCOPE = 2, ///< Scope dictionary which may be accessed without prefix + ///< (l:, g:). +} ScopeType; -/* - * Structure to hold an item of a list: an internal variable without a name. - */ +/// Structure to hold an item of a list typedef struct listitem_S listitem_T; struct listitem_S { - listitem_T *li_next; /* next item in list */ - listitem_T *li_prev; /* previous item in list */ - typval_T li_tv; /* type and value of the variable */ + listitem_T *li_next; ///< Next item in list. + listitem_T *li_prev; ///< Previous item in list. + typval_T li_tv; ///< Item value. }; -/* - * Struct used by those that are using an item in a list. - */ +/// Structure used by those that are using an item in a list typedef struct listwatch_S listwatch_T; struct listwatch_S { - listitem_T *lw_item; /* item being watched */ - listwatch_T *lw_next; /* next watcher */ + listitem_T *lw_item; ///< Item being watched. + listwatch_T *lw_next; ///< Next watcher. }; -/* - * Structure to hold info about a list. - */ +/// Structure to hold info about a list struct listvar_S { listitem_T *lv_first; ///< First item, NULL if none. listitem_T *lv_last; ///< Last item, NULL if none. @@ -123,28 +130,41 @@ struct dictitem_S { char_u di_key[1]; ///< key (actually longer!) }; -typedef struct dictitem_S dictitem_T; +#define TV_DICTITEM_STRUCT(KEY_LEN) \ + struct { \ + typval_T di_tv; /* Structure that holds scope dictionary itself. */ \ + uint8_t di_flags; /* Flags. */ \ + char_u di_key[KEY_LEN]; /* NUL. */ \ + } -/// A dictitem with a 16 character key (plus NUL) -struct dictitem16_S { - typval_T di_tv; ///< type and value of the variable - char_u di_flags; ///< flags (only used for variable) - char_u di_key[17]; ///< key -}; +/// Structure to hold a scope dictionary +/// +/// @warning Must be compatible with dictitem_T. +/// +/// For use in find_var_in_ht to pretend that it found dictionary item when it +/// finds scope dictionary. +typedef TV_DICTITEM_STRUCT(1) ScopeDictDictItem; -typedef struct dictitem16_S dictitem16_T; +/// Structure to hold an item of a Dictionary +/// +/// @warning Must be compatible with ScopeDictDictItem. +/// +/// Also used for a variable. +typedef TV_DICTITEM_STRUCT() dictitem_T; - -#define DI_FLAGS_RO 1 // "di_flags" value: read-only variable -#define DI_FLAGS_RO_SBX 2 // "di_flags" value: read-only in the sandbox -#define DI_FLAGS_FIX 4 // "di_flags" value: fixed: no :unlet or remove() -#define DI_FLAGS_LOCK 8 // "di_flags" value: locked variable -#define DI_FLAGS_ALLOC 16 // "di_flags" value: separately allocated +/// Flags for dictitem_T.di_flags +typedef enum { + DI_FLAGS_RO = 1, ///< Read-only value + DI_FLAGS_RO_SBX = 2, ///< Value, read-only in the sandbox + DI_FLAGS_FIX = 4, ///< Fixed value: cannot be :unlet or remove()d. + DI_FLAGS_LOCK = 8, ///< Locked value. + DI_FLAGS_ALLOC = 16, ///< Separately allocated. +} DictItemFlags; /// Structure representing a Dictionary struct dictvar_S { VarLockStatus dv_lock; ///< Whole dictionary lock status. - char dv_scope; ///< Non-zero (#VAR_SCOPE, #VAR_DEF_SCOPE) if + ScopeType dv_scope; ///< Non-zero (#VAR_SCOPE, #VAR_DEF_SCOPE) if ///< dictionary represents a scope (i.e. g:, l: …). int dv_refcount; ///< Reference count. int dv_copyID; ///< ID used when recursivery traversing a value. @@ -155,7 +175,10 @@ struct dictvar_S { QUEUE watchers; ///< Dictionary key watchers set by user code. }; -typedef int scid_T; // script ID +/// Type used for script ID +typedef int scid_T; + +// Structure to hold info for a function that is currently being executed. typedef struct funccall_S funccall_T; // Structure to hold info for a user function. @@ -194,63 +217,26 @@ struct ufunc { /// Maximum number of function arguments #define MAX_FUNC_ARGS 20 -#define VAR_SHORT_LEN 20 // short variable name length -#define FIXVAR_CNT 12 // number of fixed variables - -// structure to hold info for a function that is currently being executed. -struct funccall_S { - ufunc_T *func; ///< function being called - int linenr; ///< next line to be executed - int returned; ///< ":return" used - struct { ///< fixed variables for arguments - dictitem_T var; ///< variable (without room for name) - char_u room[VAR_SHORT_LEN]; ///< room for the name - } fixvar[FIXVAR_CNT]; - dict_T l_vars; ///< l: local function variables - dictitem_T l_vars_var; ///< variable for l: scope - dict_T l_avars; ///< a: argument variables - dictitem_T l_avars_var; ///< variable for a: scope - list_T l_varlist; ///< list for a:000 - listitem_T l_listitems[MAX_FUNC_ARGS]; ///< listitems for a:000 - typval_T *rettv; ///< return value - linenr_T breakpoint; ///< next line with breakpoint or zero - int dbg_tick; ///< debug_tick when breakpoint was set - int level; ///< top nesting level of executed function - proftime_T prof_child; ///< time spent in a child - funccall_T *caller; ///< calling function or NULL - int fc_refcount; ///< number of user functions that reference - // this funccal - int fc_copyID; ///< for garbage collection - garray_T fc_funcs; ///< list of ufunc_T* which keep a reference - // to "func" -}; - -// structure used by trans_function_name() -typedef struct { - dict_T *fd_dict; ///< Dictionary used. - char_u *fd_newkey; ///< New key in "dict" in allocated memory. - dictitem_T *fd_di; ///< Dictionary item used. -} funcdict_T; struct partial_S { - int pt_refcount; ///< Reference count. - char_u *pt_name; ///< Function name; when NULL use pt_func->name. - ufunc_T *pt_func; ///< Function pointer; when NULL lookup function - ///< with pt_name. - bool pt_auto; ///< when true the partial was created for using - ///< dict.member in handle_subscript(). - int pt_argc; ///< Number of arguments. - typval_T *pt_argv; ///< Arguments in allocated array. - dict_T *pt_dict; ///< Dict for "self". + int pt_refcount; ///< Reference count. + char_u *pt_name; ///< Function name; when NULL use pt_func->name. + ufunc_T *pt_func; ///< Function pointer; when NULL lookup function with + ///< pt_name. + bool pt_auto; ///< When true the partial was created by using dict.member + ///< in handle_subscript(). + int pt_argc; ///< Number of arguments. + typval_T *pt_argv; ///< Arguments in allocated array. + dict_T *pt_dict; ///< Dict for "self". }; -// structure used for explicit stack while garbage collecting hash tables +/// Structure used for explicit stack while garbage collecting hash tables typedef struct ht_stack_S { hashtab_T *ht; struct ht_stack_S *prev; } ht_stack_T; -// structure used for explicit stack while garbage collecting lists +/// Structure used for explicit stack while garbage collecting lists typedef struct list_stack_S { list_T *list; struct list_stack_S *prev; @@ -272,15 +258,28 @@ typedef struct list_stack_S { /// Convert a hashitem pointer to a dictitem pointer #define HI2DI(hi) HIKEY2DI((hi)->hi_key) -/// Type of assert_* check being performed -typedef enum +/// Get the number of items in a list +/// +/// @param[in] l List to check. +static inline long tv_list_len(list_T *const l) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - ASSERT_EQUAL, - ASSERT_NOTEQUAL, - ASSERT_MATCH, - ASSERT_NOTMATCH, - ASSERT_INRANGE, - ASSERT_OTHER, -} assert_type_T; + if (l == NULL) { + return 0; + } + return l->lv_len; +} -#endif // NVIM_EVAL_DEFS_H +/// Empty string +/// +/// Needed for hack which allows not allocating empty string and still not +/// crashing when freeing it. +extern const char *const tv_empty_string; + +/// Specifies that free_unref_items() function has (not) been entered +extern bool tv_in_free_unref_items; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/typval.h.generated.h" +#endif +#endif // NVIM_EVAL_TYPVAL_H diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index 4ff5589887..eb89a601ff 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -242,7 +242,7 @@ #include #include "nvim/lib/kvec.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/eval/encode.h" #include "nvim/func_attr.h" #include "nvim/eval/typval_encode.h" diff --git a/src/nvim/eval/typval_encode.h b/src/nvim/eval/typval_encode.h index 46145c5d03..3475f6d8b3 100644 --- a/src/nvim/eval/typval_encode.h +++ b/src/nvim/eval/typval_encode.h @@ -11,7 +11,7 @@ #include #include "nvim/lib/kvec.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/func_attr.h" /// Type of the stack entry diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 678102daf6..151a4d375f 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -2958,7 +2958,7 @@ void sub_set_replacement(SubReplacementString sub) { xfree(old_sub.sub); if (sub.additional_elements != old_sub.additional_elements) { - list_unref(old_sub.additional_elements); + tv_list_unref(old_sub.additional_elements); } old_sub = sub; } diff --git a/src/nvim/ex_cmds.h b/src/nvim/ex_cmds.h index 243b11255e..65bbd8a99e 100644 --- a/src/nvim/ex_cmds.h +++ b/src/nvim/ex_cmds.h @@ -4,8 +4,8 @@ #include #include "nvim/os/time.h" -#include "nvim/eval_defs.h" #include "nvim/pos.h" +#include "nvim/eval/typval.h" // flags for do_ecmd() #define ECMD_HIDE 0x01 // don't free the current buffer diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 9fc4ef2a02..e59a87b335 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -3698,12 +3698,12 @@ static void script_host_execute(char *name, exarg_T *eap) uint8_t *script = script_get(eap, eap->arg); if (!eap->skip) { - list_T *args = list_alloc(); + list_T *args = tv_list_alloc(); // script - list_append_string(args, script ? script : eap->arg, -1); + tv_list_append_string(args, (const char *)(script ? script : eap->arg), -1); // current range - list_append_number(args, (int)eap->line1); - list_append_number(args, (int)eap->line2); + tv_list_append_number(args, (int)eap->line1); + tv_list_append_number(args, (int)eap->line2); (void)eval_call_provider(name, "execute", args); } @@ -3715,21 +3715,21 @@ static void script_host_execute_file(char *name, exarg_T *eap) uint8_t buffer[MAXPATHL]; vim_FullName((char *)eap->arg, (char *)buffer, sizeof(buffer), false); - list_T *args = list_alloc(); + list_T *args = tv_list_alloc(); // filename - list_append_string(args, buffer, -1); + tv_list_append_string(args, (const char *)buffer, -1); // current range - list_append_number(args, (int)eap->line1); - list_append_number(args, (int)eap->line2); + tv_list_append_number(args, (int)eap->line1); + tv_list_append_number(args, (int)eap->line2); (void)eval_call_provider(name, "execute_file", args); } static void script_host_do_range(char *name, exarg_T *eap) { - list_T *args = list_alloc(); - list_append_number(args, (int)eap->line1); - list_append_number(args, (int)eap->line2); - list_append_string(args, eap->arg, -1); + list_T *args = tv_list_alloc(); + tv_list_append_number(args, (int)eap->line1); + tv_list_append_number(args, (int)eap->line2); + tv_list_append_string(args, (const char *)eap->arg, -1); (void)eval_call_provider(name, "do_range", args); } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index d1557f9c82..dc99c0771d 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -8416,8 +8416,8 @@ eval_vars ( *usedlen = 1; return NULL; } - result = list_find_str(get_vim_var_list(VV_OLDFILES), - (long)i); + result = (char_u *)tv_list_find_str(get_vim_var_list(VV_OLDFILES), + (long)i); if (result == NULL) { *errormsg = (char_u *)""; return NULL; diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 4bb6f97035..7a34a181e2 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -1,6 +1,8 @@ -/* - * ex_eval.c: functions for Ex command line for the +eval feature. - */ +// TODO(ZyX-I): move to eval/executor + +/// @file ex_eval.c +/// +/// Functions for Ex command line for the +eval feature. #include #include #include @@ -779,7 +781,6 @@ void report_discard_pending(int pending, void *value) */ void ex_if(exarg_T *eap) { - int error; int skip; int result; struct condstack *cstack = eap->cstack; @@ -800,6 +801,7 @@ void ex_if(exarg_T *eap) 1] & CSF_ACTIVE)); + bool error; result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip); if (!skip && !error) { @@ -844,7 +846,6 @@ void ex_endif(exarg_T *eap) */ void ex_else(exarg_T *eap) { - int error; int skip; int result; struct condstack *cstack = eap->cstack; @@ -901,6 +902,7 @@ void ex_else(exarg_T *eap) } if (eap->cmdidx == CMD_elseif) { + bool error; result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip); /* When throwing error exceptions, we want to throw always the first * of several errors in a row. This is what actually happens when @@ -925,7 +927,7 @@ void ex_else(exarg_T *eap) */ void ex_while(exarg_T *eap) { - int error; + bool error; int skip; int result; struct condstack *cstack = eap->cstack; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 8758a63bce..9851ed5396 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -4249,7 +4249,7 @@ static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file) GA_APPEND(char_u *, &ga, vim_strsave(li->li_tv.vval.v_string)); } - list_unref(retlist); + tv_list_unref(retlist); *file = ga.ga_data; *num_file = ga.ga_len; @@ -4545,7 +4545,7 @@ static inline void hist_free_entry(histentry_T *hisptr) FUNC_ATTR_NONNULL_ALL { xfree(hisptr->hisstr); - list_unref(hisptr->additional_elements); + tv_list_unref(hisptr->additional_elements); clear_hist_entry(hisptr); } @@ -4601,7 +4601,7 @@ in_history ( history[type][last_i] = history[type][i]; last_i = i; } - list_unref(list); + tv_list_unref(list); history[type][i].hisnum = ++hisnum[type]; history[type][i].hisstr = str; history[type][i].timestamp = os_time(); diff --git a/src/nvim/ex_getln.h b/src/nvim/ex_getln.h index 24eebdc303..5a1ca5213a 100644 --- a/src/nvim/ex_getln.h +++ b/src/nvim/ex_getln.h @@ -1,7 +1,7 @@ #ifndef NVIM_EX_GETLN_H #define NVIM_EX_GETLN_H -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" /* Values for nextwild() and ExpandOne(). See ExpandOne() for meaning. */ diff --git a/src/nvim/globals.h b/src/nvim/globals.h index de79ee2469..d87407f099 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -12,6 +12,7 @@ #include "nvim/syntax_defs.h" #include "nvim/types.h" #include "nvim/event/loop.h" +#include "nvim/os/os_defs.h" #define IOSIZE (1024+1) // file I/O and sprintf buffer size @@ -21,16 +22,6 @@ # define MSG_BUF_CLEN (MSG_BUF_LEN / 6) // cell length (worst case: utf-8 // takes 6 bytes for one cell) -// Maximum length of a file path. Make it a bit long, to stay -// on the safe side. But not too long to put on the stack. -#ifndef MAXPATHL -# ifdef MAXPATHLEN -# define MAXPATHL MAXPATHLEN -# else -# define MAXPATHL 256 -# endif -#endif - #ifdef WIN32 # define _PATHSEPSTR "\\" #else @@ -1226,11 +1217,6 @@ EXTERN FILE *time_fd INIT(= NULL); /* where to write startup timing */ EXTERN int ignored; EXTERN char *ignoredp; -EXTERN bool in_free_unref_items INIT(= false); - -// Used for checking if local variables or arguments used in a lambda. -EXTERN int *eval_lavars_used INIT(= NULL); - // If a msgpack-rpc channel should be started over stdin/stdout EXTERN bool embedded_mode INIT(= false); diff --git a/src/nvim/main.c b/src/nvim/main.c index 7b1c912f4b..0c978dc47d 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -391,9 +391,10 @@ int main(int argc, char **argv) shada_read_everything(NULL, false, true); TIME_MSG("reading ShaDa"); } - /* It's better to make v:oldfiles an empty list than NULL. */ - if (get_vim_var_list(VV_OLDFILES) == NULL) - set_vim_var_list(VV_OLDFILES, list_alloc()); + // It's better to make v:oldfiles an empty list than NULL. + if (get_vim_var_list(VV_OLDFILES) == NULL) { + set_vim_var_list(VV_OLDFILES, tv_list_alloc()); + } /* * "-q errorfile": Load the error file now. diff --git a/src/nvim/mark.c b/src/nvim/mark.c index de2fdd7f13..1d1e13e7e0 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -1431,3 +1431,26 @@ void free_all_marks(void) memset(&namedfm[0], 0, sizeof(namedfm)); } #endif + +/// Adjust position to point to the first byte of a multi-byte character +/// +/// If it points to a tail byte it is move backwards to the head byte. +/// +/// @param[in] buf Buffer to adjust position in. +/// @param[out] lp Position to adjust. +void mark_mb_adjustpos(buf_T *buf, pos_T *lp) + FUNC_ATTR_NONNULL_ALL +{ + if (lp->col > 0 || lp->coladd > 1) { + const char_u *const p = ml_get_buf(buf, lp->lnum, false); + lp->col -= (*mb_head_off)(p, p + lp->col); + // Reset "coladd" when the cursor would be on the right half of a + // double-wide character. + if (lp->coladd == 1 + && p[lp->col] != TAB + && vim_isprintc((*mb_ptr2char)(p + lp->col)) + && ptr2cells(p + lp->col) > 1) { + lp->coladd = 0; + } + } +} diff --git a/src/nvim/mark_defs.h b/src/nvim/mark_defs.h index 720b2475ed..2cb489501e 100644 --- a/src/nvim/mark_defs.h +++ b/src/nvim/mark_defs.h @@ -3,7 +3,7 @@ #include "nvim/pos.h" #include "nvim/os/time.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" /* * marks: positions in a file diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 0ab133a545..57518a9535 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -50,6 +50,7 @@ #include "nvim/strings.h" #include "nvim/os/os.h" #include "nvim/arabic.h" +#include "nvim/mark.h" typedef struct { int rangeStart; @@ -375,16 +376,18 @@ void remove_bom(char_u *s) */ int mb_get_class(const char_u *p) { - return mb_get_class_buf(p, curbuf); + return mb_get_class_tab(p, curbuf->b_chartab); } -int mb_get_class_buf(const char_u *p, buf_T *buf) +int mb_get_class_tab(const char_u *p, const uint64_t *const chartab) { if (MB_BYTE2LEN(p[0]) == 1) { - if (p[0] == NUL || ascii_iswhite(p[0])) + if (p[0] == NUL || ascii_iswhite(p[0])) { return 0; - if (vim_iswordc_buf(p[0], buf)) + } + if (vim_iswordc_tab(p[0], chartab)) { return 2; + } return 1; } return utf_class(utf_ptr2char(p)); @@ -1639,38 +1642,16 @@ theend: */ void mb_adjust_cursor(void) { - mb_adjustpos(curbuf, &curwin->w_cursor); -} - -/* - * Adjust position "*lp" to point to the first byte of a multi-byte character. - * If it points to a tail byte it's moved backwards to the head byte. - */ -void mb_adjustpos(buf_T *buf, pos_T *lp) -{ - char_u *p; - - if (lp->col > 0 - || lp->coladd > 1 - ) { - p = ml_get_buf(buf, lp->lnum, FALSE); - lp->col -= (*mb_head_off)(p, p + lp->col); - /* Reset "coladd" when the cursor would be on the right half of a - * double-wide character. */ - if (lp->coladd == 1 - && p[lp->col] != TAB - && vim_isprintc((*mb_ptr2char)(p + lp->col)) - && ptr2cells(p + lp->col) > 1) - lp->coladd = 0; - } + mark_mb_adjustpos(curbuf, &curwin->w_cursor); } /// Checks and adjusts cursor column. Not mode-dependent. /// @see check_cursor_col_win /// -/// @param win Places cursor on a valid column for this window. -void mb_check_adjust_col(win_T *win) +/// @param win_ Places cursor on a valid column for this window. +void mb_check_adjust_col(void *win_) { + win_T *win = (win_T *)win_; colnr_T oldcol = win->w_cursor.col; // Column 0 is always valid. diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h index 2c92a0fbb2..5f5bab9fcd 100644 --- a/src/nvim/mbyte.h +++ b/src/nvim/mbyte.h @@ -3,6 +3,8 @@ #include +#include "nvim/iconv.h" + /* * Return byte length of character that starts with byte "b". * Returns 1 for a single-byte character. @@ -40,6 +42,27 @@ #define mb_ptr2char utf_ptr2char #define mb_head_off utf_head_off +/// Flags for vimconv_T +typedef enum { + CONV_NONE = 0, + CONV_TO_UTF8 = 1, + CONV_9_TO_UTF8 = 2, + CONV_TO_LATIN1 = 3, + CONV_TO_LATIN9 = 4, + CONV_ICONV = 5, +} ConvFlags; + +/// Structure used for string conversions +typedef struct { + int vc_type; ///< Zero or more ConvFlags. + int vc_factor; ///< Maximal expansion factor. +# ifdef USE_ICONV + iconv_t vc_fd; ///< Value for CONV_ICONV. +# endif + bool vc_fail; ///< What to do with invalid characters: if true, fail, + ///< otherwise use '?'. +} vimconv_T; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "mbyte.h.generated.h" #endif diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 58c01fbe7a..b4fdd86a6d 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -430,6 +430,19 @@ char *xstrdup(const char *str) return xmemdupz(str, strlen(str)); } +/// strdup() wrapper +/// +/// Unlike xstrdup() allocates a new empty string if it receives NULL. +char *xstrdupnul(const char *const str) + FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET +{ + if (str == NULL) { + return xmallocz(0); + } else { + return xstrdup(str); + } +} + /// A version of memchr that starts the search at `src + len`. /// /// Based on glibc's memrchr. diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 2ade9cb87d..4cca5ec948 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2465,7 +2465,7 @@ do_mouse ( &rettv, ARRAY_SIZE(argv), argv, NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &doesrange, true, NULL, NULL); - clear_tv(&rettv); + tv_clear(&rettv); break; } } @@ -7290,11 +7290,11 @@ static bool unadjust_for_sel(void) pp = &curwin->w_cursor; else pp = &VIsual; - if (pp->coladd > 0) - --pp->coladd; - else if (pp->col > 0) { - --pp->col; - mb_adjustpos(curbuf, pp); + if (pp->coladd > 0) { + pp->coladd--; + } else if (pp->col > 0) { + pp->col--; + mark_mb_adjustpos(curbuf, pp); } else if (pp->lnum > 1) { --pp->lnum; pp->col = (colnr_T)STRLEN(ml_get(pp->lnum)); @@ -7829,7 +7829,7 @@ static void get_op_vcol( // prevent from moving onto a trail byte if (has_mbyte) { - mb_adjustpos(curwin->w_buffer, &oap->end); + mark_mb_adjustpos(curwin->w_buffer, &oap->end); } getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index c13b6f736a..85cef59aec 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2555,9 +2555,9 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) dict_T *dict = get_vim_var_dict(VV_EVENT); // the yanked text - list_T *list = list_alloc(); + list_T *list = tv_list_alloc(); for (size_t i = 0; i < reg->y_size; i++) { - list_append_string(list, reg->y_array[i], -1); + tv_list_append_string(list, (const char *)reg->y_array[i], -1); } list->lv_lock = VAR_FIXED; dict_add_list(dict, "regcontents", list); @@ -4844,8 +4844,8 @@ static void *get_reg_wrap_one_line(char_u *s, int flags) if (!(flags & kGRegList)) { return s; } - list_T *list = list_alloc(); - list_append_string(list, NULL, -1); + list_T *list = tv_list_alloc(); + tv_list_append_string(list, NULL, 0); list->lv_first->li_tv.vval.v_string = s; return list; } @@ -4895,9 +4895,9 @@ void *get_reg_contents(int regname, int flags) return NULL; if (flags & kGRegList) { - list_T *list = list_alloc(); + list_T *list = tv_list_alloc(); for (size_t i = 0; i < reg->y_size; i++) { - list_append_string(list, reg->y_array[i], -1); + tv_list_append_string(list, (const char *)reg->y_array[i], -1); } return list; @@ -5570,9 +5570,9 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) } free_register(reg); - list_T *args = list_alloc(); - char_u regname = (char_u)name; - list_append_string(args, ®name, 1); + list_T *const args = tv_list_alloc(); + const char regname = (char)name; + tv_list_append_string(args, ®name, 1); typval_T result = eval_call_provider("clipboard", "get", args); @@ -5584,7 +5584,8 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) goto err; } - list_T *res = result.vval.v_list, *lines = NULL; + list_T *res = result.vval.v_list; + list_T *lines = NULL; if (res->lv_len == 2 && res->lv_first->li_tv.v_type == VAR_LIST) { lines = res->lv_first->li_tv.vval.v_list; if (res->lv_last->li_tv.v_type != VAR_STRING) { @@ -5628,7 +5629,7 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) if (li->li_tv.v_type != VAR_STRING) { goto err; } - reg->y_array[i++] = (uint8_t *)xstrdup((char *)li->li_tv.vval.v_string); + reg->y_array[i++] = (char_u *)xstrdupnul((char *)li->li_tv.vval.v_string); } if (reg->y_size > 0 && strlen((char*)reg->y_array[reg->y_size-1]) == 0) { @@ -5686,35 +5687,39 @@ static void set_clipboard(int name, yankreg_T *reg) return; } - list_T *lines = list_alloc(); + list_T *lines = tv_list_alloc(); for (size_t i = 0; i < reg->y_size; i++) { - list_append_string(lines, reg->y_array[i], -1); + tv_list_append_string(lines, (const char *)reg->y_array[i], -1); } - list_T *args = list_alloc(); - list_append_list(args, lines); + list_T *args = tv_list_alloc(); + tv_list_append_list(args, lines); - char_u regtype; + char regtype; switch (reg->y_type) { - case kMTLineWise: - regtype = 'V'; - list_append_string(lines, (char_u*)"", 0); - break; - case kMTCharWise: - regtype = 'v'; - break; - case kMTBlockWise: - regtype = 'b'; - list_append_string(lines, (char_u*)"", 0); - break; - case kMTUnknown: - assert(false); + case kMTLineWise: { + regtype = 'V'; + tv_list_append_string(lines, NULL, 0); + break; + } + case kMTCharWise: { + regtype = 'v'; + break; + } + case kMTBlockWise: { + regtype = 'b'; + tv_list_append_string(lines, NULL, 0); + break; + } + case kMTUnknown: { + assert(false); + } } - list_append_string(args, ®type, 1); + tv_list_append_string(args, ®type, 1); - char_u regname = (char_u)name; - list_append_string(args, ®name, 1); + const char regname = (char)name; + tv_list_append_string(args, ®name, 1); (void)eval_call_provider("clipboard", "set", args); } diff --git a/src/nvim/ops.h b/src/nvim/ops.h index 44df2e9e0c..13d0142343 100644 --- a/src/nvim/ops.h +++ b/src/nvim/ops.h @@ -6,7 +6,7 @@ #include "nvim/macros.h" #include "nvim/ascii.h" #include "nvim/types.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/os/time.h" typedef int (*Indenter)(void); diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 8d8c20c1d0..323503c4f5 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -4003,7 +4003,7 @@ int get_errorlist(win_T *wp, int qf_idx, list_T *list) bufnum = 0; dict = dict_alloc(); - list_append_dict(list, dict); + tv_list_append_dict(list, dict); buf[0] = qfp->qf_type; buf[1] = NUL; diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 1cd334abcd..9c6f02f778 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -3650,9 +3650,11 @@ static long regtry(bt_regprog_T *prog, colnr_T col) */ static int reg_prev_class(void) { - if (reginput > regline) - return mb_get_class_buf(reginput - 1 - - (*mb_head_off)(regline, reginput - 1), reg_buf); + if (reginput > regline) { + return mb_get_class_tab(reginput - 1 - (*mb_head_off)(regline, + reginput - 1), + reg_buf->b_chartab); + } return -1; } @@ -3918,12 +3920,13 @@ regmatch ( else if (has_mbyte) { int this_class; - /* Get class of current and previous char (if it exists). */ - this_class = mb_get_class_buf(reginput, reg_buf); - if (this_class <= 1) - status = RA_NOMATCH; /* not on a word at all */ - else if (reg_prev_class() == this_class) - status = RA_NOMATCH; /* previous char is in same word */ + // Get class of current and previous char (if it exists). + this_class = mb_get_class_tab(reginput, reg_buf->b_chartab); + if (this_class <= 1) { + status = RA_NOMATCH; // Not on a word at all. + } else if (reg_prev_class() == this_class) { + status = RA_NOMATCH; // Previous char is in same word. + } } else { if (!vim_iswordc_buf(c, reg_buf) || (reginput > regline && vim_iswordc_buf(reginput[-1 @@ -3938,8 +3941,8 @@ regmatch ( else if (has_mbyte) { int this_class, prev_class; - /* Get class of current and previous char (if it exists). */ - this_class = mb_get_class_buf(reginput, reg_buf); + // Get class of current and previous char (if it exists). + this_class = mb_get_class_tab(reginput, reg_buf->b_chartab); prev_class = reg_prev_class(); if (this_class == prev_class || prev_class == 0 || prev_class == 1) @@ -6617,7 +6620,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, if (eval_result != NULL) { eval_result = vim_strsave(eval_result); } - clear_tv(&rettv); + tv_clear(&rettv); } else { eval_result = eval_to_string(source + 2, NULL, true); } @@ -6976,7 +6979,7 @@ list_T *reg_submatch_list(int no) linenr_T slnum; linenr_T elnum; list_T *list; - char_u *s; + const char *s; if (submatch_match == NULL) { slnum = submatch_mmatch->startpos[no].lnum; @@ -6988,27 +6991,27 @@ list_T *reg_submatch_list(int no) colnr_T scol = submatch_mmatch->startpos[no].col; colnr_T ecol = submatch_mmatch->endpos[no].col; - list = list_alloc(); + list = tv_list_alloc(); - s = reg_getline_submatch(slnum) + scol; + s = (const char *)reg_getline_submatch(slnum) + scol; if (slnum == elnum) { - list_append_string(list, s, ecol - scol); + tv_list_append_string(list, s, ecol - scol); } else { - list_append_string(list, s, -1); + tv_list_append_string(list, s, -1); for (int i = 1; i < elnum - slnum; i++) { - s = reg_getline_submatch(slnum + i); - list_append_string(list, s, -1); + s = (const char *)reg_getline_submatch(slnum + i); + tv_list_append_string(list, s, -1); } - s = reg_getline_submatch(elnum); - list_append_string(list, s, ecol); + s = (const char *)reg_getline_submatch(elnum); + tv_list_append_string(list, s, ecol); } } else { - s = submatch_match->startp[no]; + s = (const char *)submatch_match->startp[no]; if (s == NULL || submatch_match->endp[no] == NULL) { return NULL; } - list = list_alloc(); - list_append_string(list, s, (int)(submatch_match->endp[no] - s)); + list = tv_list_alloc(); + tv_list_append_string(list, s, (const char *)submatch_match->endp[no] - s); } return list; diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 3f4e12af4a..5b49ab38f0 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -5410,7 +5410,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, int this_class; // Get class of current and previous char (if it exists). - this_class = mb_get_class_buf(reginput, reg_buf); + this_class = mb_get_class_tab(reginput, reg_buf->b_chartab); if (this_class <= 1) { result = false; } else if (reg_prev_class() == this_class) { @@ -5435,7 +5435,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, int this_class, prev_class; // Get class of current and previous char (if it exists). - this_class = mb_get_class_buf(reginput, reg_buf); + this_class = mb_get_class_tab(reginput, reg_buf->b_chartab); prev_class = reg_prev_class(); if (this_class == prev_class || prev_class == 0 || prev_class == 1) { diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 197b029591..c550cb0888 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -30,7 +30,7 @@ #include "nvim/ex_getln.h" #include "nvim/search.h" #include "nvim/regexp.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/version.h" #include "nvim/path.h" #include "nvim/fileio.h" @@ -1223,7 +1223,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) khash_t(fnamebufs) fname_bufs = KHASH_EMPTY_TABLE(fnamebufs); khash_t(strset) oldfiles_set = KHASH_EMPTY_TABLE(strset); if (get_old_files && (oldfiles_list == NULL || force)) { - oldfiles_list = list_alloc(); + oldfiles_list = tv_list_alloc(); set_vim_var_list(VV_OLDFILES, oldfiles_list); } ShaDaReadResult srni_ret; @@ -1435,8 +1435,8 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) fname = xstrdup(fname); } int kh_ret; - (void) kh_put(strset, &oldfiles_set, fname, &kh_ret); - list_append_allocated_string(oldfiles_list, fname); + (void)kh_put(strset, &oldfiles_set, fname, &kh_ret); + tv_list_append_allocated_string(oldfiles_list, fname); if (!want_marks) { // Avoid free because this string was already used. cur_entry.data.filemark.fname = NULL; @@ -1573,7 +1573,9 @@ static char *shada_filename(const char *file) do { \ const String s_ = (s); \ msgpack_pack_str(spacker, s_.size); \ - msgpack_pack_str_body(spacker, s_.data, s_.size); \ + if (s_.size) { \ + msgpack_pack_str_body(spacker, s_.data, s_.size); \ + } \ } while (0) #define PACK_BIN(s) \ do { \ @@ -1965,7 +1967,7 @@ static ShaDaWriteResult shada_pack_encoded_entry(msgpack_packer *const packer, typval_T tgttv; var_item_copy(sd_conv, &entry.data.data.global_var.value, &tgttv, true, 0); - clear_tv(&entry.data.data.global_var.value); + tv_clear(&entry.data.data.global_var.value); entry.data.data.global_var.value = tgttv; } ret = shada_pack_entry(packer, entry.data, max_kbyte); @@ -2573,13 +2575,13 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, } } }, max_kbyte)) == kSDWriteFailed) { - clear_tv(&vartv); - clear_tv(&tgttv); + tv_clear(&vartv); + tv_clear(&tgttv); ret = kSDWriteFailed; goto shada_write_exit; } - clear_tv(&vartv); - clear_tv(&tgttv); + tv_clear(&vartv); + tv_clear(&tgttv); if (spe_ret == kSDWriteSuccessfull) { int kh_ret; (void) kh_put(strset, &wms->dumped_variables, name, &kh_ret); @@ -3172,18 +3174,18 @@ static void shada_free_shada_entry(ShadaEntry *const entry) break; } case kSDItemHistoryEntry: { - list_unref(entry->data.history_item.additional_elements); + tv_list_unref(entry->data.history_item.additional_elements); xfree(entry->data.history_item.string); break; } case kSDItemVariable: { - list_unref(entry->data.global_var.additional_elements); + tv_list_unref(entry->data.global_var.additional_elements); xfree(entry->data.global_var.name); - clear_tv(&entry->data.global_var.value); + tv_clear(&entry->data.global_var.value); break; } case kSDItemSubString: { - list_unref(entry->data.sub_string.additional_elements); + tv_list_unref(entry->data.sub_string.additional_elements); xfree(entry->data.sub_string.sub); break; } @@ -3451,7 +3453,7 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv, "cannot be converted to a VimL dictionary")), \ initial_fpos); \ ga_clear(&ad_ga); \ - clear_tv(&adtv); \ + tv_clear(&adtv); \ goto shada_read_next_item_error; \ } \ tgt = adtv.vval.v_dict; \ @@ -3474,7 +3476,7 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv, if (msgpack_to_vim(obj, &aetv) == FAIL) { \ emsgf(_(READERR(name, "cannot be converted to a VimL list")), \ initial_fpos); \ - clear_tv(&aetv); \ + tv_clear(&aetv); \ goto shada_read_next_item_error; \ } \ assert(aetv.v_type == VAR_LIST); \ @@ -3866,7 +3868,7 @@ shada_read_next_item_hist_no_conv: &tgttv, true, 0); - clear_tv(&entry->data.global_var.value); + tv_clear(&entry->data.global_var.value); entry->data.global_var.value = tgttv; } SET_ADDITIONAL_ELEMENTS(unpacked.data.via.array, 2, diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 3b891d998f..5ca5ab3339 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -3248,7 +3248,7 @@ static void spell_suggest_expr(suginfo_T *su, char_u *expr) add_suggestion(su, &su->su_ga, p, su->su_badlen, score, 0, true, su->su_sallang, false); } - list_unref(list); + tv_list_unref(list); } // Remove bogus suggestions, sort and truncate at "maxcount". diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 267832ed2d..b964fed35a 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -571,7 +571,7 @@ static varnumber_T tv_nr(typval_T *tvs, int *idxp) EMSG(_(e_printf)); } else { (*idxp)++; - int err = false; + bool err = false; n = (varnumber_T)get_tv_number_chk(&tvs[idx], &err); if (err) { n = 0; diff --git a/src/nvim/strings.h b/src/nvim/strings.h index 8aea374b96..59b8701a3f 100644 --- a/src/nvim/strings.h +++ b/src/nvim/strings.h @@ -5,7 +5,7 @@ #include #include "nvim/types.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "strings.h.generated.h" diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 7bcaff662c..b0ffc12b5f 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -674,7 +674,7 @@ do_tag ( fname = xmalloc(MAXPATHL + 1); cmd = xmalloc(CMDBUFFSIZE + 1); - list = list_alloc(); + list = tv_list_alloc(); for (i = 0; i < num_matches; ++i) { int len, cmd_len; @@ -774,7 +774,7 @@ do_tag ( } dict = dict_alloc(); - list_append_dict(list, dict); + tv_list_append_dict(list, dict); dict_add_nr_str(dict, "text", 0L, tag_name); dict_add_nr_str(dict, "filename", 0L, fname); @@ -786,7 +786,7 @@ do_tag ( vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag); set_errorlist(curwin, list, ' ', IObuff, NULL); - list_free(list); + tv_list_free(list); xfree(fname); xfree(cmd); @@ -2825,7 +2825,7 @@ int get_tags(list_T *list, char_u *pat) continue; dict = dict_alloc(); - list_append_dict(list, dict); + tv_list_append_dict(list, dict); full_fname = tag_full_fname(&tp); if (add_tag_field(dict, "name", tp.tagname, tp.tagname_end) == FAIL diff --git a/src/nvim/undo.c b/src/nvim/undo.c index c95a795587..729cf03e15 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -2952,14 +2952,14 @@ void u_eval_tree(u_header_T *first_uhp, list_T *list) dict_add_nr_str(dict, "save", uhp->uh_save_nr, NULL); if (uhp->uh_alt_next.ptr != NULL) { - list_T *alt_list = list_alloc(); + list_T *alt_list = tv_list_alloc(); /* Recursive call to add alternate undo tree. */ u_eval_tree(uhp->uh_alt_next.ptr, alt_list); dict_add_list(dict, "alt", alt_list); } - list_append_dict(list, dict); + tv_list_append_dict(list, dict); uhp = uhp->uh_prev.ptr; } } diff --git a/src/nvim/window.c b/src/nvim/window.c index 4fac730f02..47a97da0cf 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5582,7 +5582,7 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, int len = 1; list_T *subl; listitem_T *subli; - int error = false; + bool error = false; if (li->li_tv.v_type == VAR_LIST) { subl = li->li_tv.vval.v_list; @@ -5594,7 +5594,7 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, goto fail; } lnum = get_tv_number_chk(&subli->li_tv, &error); - if (error == true) { + if (error) { goto fail; } if (lnum == 0) { @@ -5605,12 +5605,13 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, subli = subli->li_next; if (subli != NULL) { col = get_tv_number_chk(&subli->li_tv, &error); - if (error == true) + if (error) { goto fail; + } subli = subli->li_next; if (subli != NULL) { len = get_tv_number_chk(&subli->li_tv, &error); - if (error == true) { + if (error) { goto fail; } } @@ -5881,8 +5882,8 @@ void win_id2tabwin(typval_T *argvars, list_T *list) int id = get_tv_number(&argvars[0]); win_get_tabwin(id, &tabnr, &winnr); - list_append_number(list, tabnr); - list_append_number(list, winnr); + tv_list_append_number(list, tabnr); + tv_list_append_number(list, winnr); } win_T * win_id2wp(typval_T *argvars) @@ -5918,7 +5919,7 @@ void win_findbuf(typval_T *argvars, list_T *list) FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer->b_fnum == bufnr) { - list_append_number(list, wp->handle); + tv_list_append_number(list, wp->handle); } } } diff --git a/test/unit/eval/decode_spec.lua b/test/unit/eval/decode_spec.lua index 2d7597c0f4..0b2a423cd6 100644 --- a/test/unit/eval/decode_spec.lua +++ b/test/unit/eval/decode_spec.lua @@ -7,7 +7,7 @@ local eq = helpers.eq local neq = helpers.neq local ffi = helpers.ffi -local decode = cimport('./src/nvim/eval/decode.h', './src/nvim/eval_defs.h', +local decode = cimport('./src/nvim/eval/decode.h', './src/nvim/eval/typval.h', './src/nvim/globals.h', './src/nvim/memory.h', './src/nvim/message.h') diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index 1377d5b501..49f929937f 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -5,7 +5,7 @@ local to_cstr = helpers.to_cstr local ffi = helpers.ffi local eq = helpers.eq -local eval = cimport('./src/nvim/eval.h', './src/nvim/eval_defs.h', +local eval = cimport('./src/nvim/eval.h', './src/nvim/eval/typval.h', './src/nvim/hashtab.h') local null_string = {[true]='NULL string'} @@ -33,7 +33,7 @@ local function li_alloc(nogc) end local function list(...) - local ret = ffi.gc(eval.list_alloc(), eval.list_unref) + local ret = ffi.gc(eval.tv_list_alloc(), eval.tv_list_unref) eq(0, ret.lv_refcount) ret.lv_refcount = 1 for i = 1, select('#', ...) do @@ -241,7 +241,7 @@ local typvalt = function(typ, vval) elseif type(typ) == 'string' then typ = eval[typ] end - return ffi.gc(ffi.new('typval_T', {v_type=typ, vval=vval}), eval.clear_tv) + return ffi.gc(ffi.new('typval_T', {v_type=typ, vval=vval}), eval.tv_clear) end local lua2typvalt_type_tab = { @@ -256,14 +256,14 @@ local lua2typvalt_type_tab = { processed[l].lv_refcount = processed[l].lv_refcount + 1 return typvalt(eval.VAR_LIST, {v_list=processed[l]}) end - local lst = eval.list_alloc() + local lst = eval.tv_list_alloc() lst.lv_refcount = 1 processed[l] = lst local ret = typvalt(eval.VAR_LIST, {v_list=lst}) for i = 1, #l do local item_tv = ffi.gc(lua2typvalt(l[i], processed), nil) - eval.list_append_tv(lst, item_tv) - eval.clear_tv(item_tv) + eval.tv_list_append_tv(lst, item_tv) + eval.tv_clear(item_tv) end return ret end, @@ -281,7 +281,7 @@ local lua2typvalt_type_tab = { local di = eval.dictitem_alloc(to_cstr(k)) local val_tv = ffi.gc(lua2typvalt(v, processed), nil) eval.copy_tv(val_tv, di.di_tv) - eval.clear_tv(val_tv) + eval.tv_clear(val_tv) eval.dict_add(dct, di) end end @@ -301,7 +301,7 @@ local lua2typvalt_type_tab = { for i, arg in ipairs(l.args) do local arg_tv = ffi.gc(lua2typvalt(arg, processed), nil) eval.copy_tv(arg_tv, argv[i - 1]) - eval.clear_tv(arg_tv) + eval.tv_clear(arg_tv) end end local dict = nil diff --git a/test/unit/eval/tricks_spec.lua b/test/unit/eval/tricks_spec.lua index ec79a9cad5..7f0a445f2c 100644 --- a/test/unit/eval/tricks_spec.lua +++ b/test/unit/eval/tricks_spec.lua @@ -10,7 +10,7 @@ local eval = cimport('./src/nvim/eval.h', './src/nvim/memory.h') local eval_expr = function(expr) return ffi.gc(eval.eval_expr(to_cstr(expr), nil), function(tv) - eval.clear_tv(tv) + eval.tv_clear(tv) eval.xfree(tv) end) end diff --git a/test/unit/eval/tv_clear_spec.lua b/test/unit/eval/tv_clear_spec.lua index 47d4661ad8..ca37301b32 100644 --- a/test/unit/eval/tv_clear_spec.lua +++ b/test/unit/eval/tv_clear_spec.lua @@ -14,7 +14,7 @@ local list_items = eval_helpers.list_items local dict_items = eval_helpers.dict_items local lua2typvalt = eval_helpers.lua2typvalt -local lib = cimport('./src/nvim/eval_defs.h', './src/nvim/eval.h') +local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/eval.h') local alloc_log = alloc_log_new() @@ -26,7 +26,7 @@ after_each(function() alloc_log:after_each() end) -describe('clear_tv()', function() +describe('tv_clear()', function() itp('successfully frees all lists in [&l [1], *l, *l]', function() local l_inner = {1} local list = {l_inner, l_inner, l_inner} @@ -44,7 +44,7 @@ describe('clear_tv()', function() a.li(lis[3]), }) eq(3, list_inner_p.lv_refcount) - lib.clear_tv(list_tv) + lib.tv_clear(list_tv) alloc_log:check({ a.freed(lis_inner[1]), a.freed(list_inner_p), @@ -69,7 +69,7 @@ describe('clear_tv()', function() a.li(lis[3]), }) eq(3, list_inner_p.lv_refcount) - lib.clear_tv(list_tv) + lib.tv_clear(list_tv) alloc_log:check({ a.freed(list_inner_p), a.freed(lis[1]), @@ -92,7 +92,7 @@ describe('clear_tv()', function() a.li(lis[2]), }) eq(2, dict_inner_p.dv_refcount) - lib.clear_tv(list_tv) + lib.tv_clear(list_tv) alloc_log:check({ a.freed(dict_inner_p), a.freed(lis[1]), @@ -116,7 +116,7 @@ describe('clear_tv()', function() a.li(lis[2]), }) eq(2, dict_inner_p.dv_refcount) - lib.clear_tv(list_tv) + lib.tv_clear(list_tv) alloc_log:check({ a.freed(dis.a), a.freed(dict_inner_p), From 50a48f2a0ecf7f767df961f7f5060505cf28e331 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Jul 2016 01:39:32 +0300 Subject: [PATCH 0176/1671] functests: Add tests for some *buf* functions --- test/functional/eval/buf_functions_spec.lua | 300 ++++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 test/functional/eval/buf_functions_spec.lua diff --git a/test/functional/eval/buf_functions_spec.lua b/test/functional/eval/buf_functions_spec.lua new file mode 100644 index 0000000000..a130da4452 --- /dev/null +++ b/test/functional/eval/buf_functions_spec.lua @@ -0,0 +1,300 @@ +local helpers = require('test.functional.helpers')(after_each) + +local lfs = require('lfs') + +local eq = helpers.eq +local clear = helpers.clear +local funcs = helpers.funcs +local meths = helpers.meths +local command = helpers.command +local exc_exec = helpers.exc_exec +local bufmeths = helpers.bufmeths +local winmeths = helpers.winmeths +local curbufmeths = helpers.curbufmeths +local curwinmeths = helpers.curwinmeths +local curtabmeths = helpers.curtabmeths + +local fname = 'Xtest-functional-eval-buf_functions' +local fname2 = fname .. '.2' +local dirname = fname .. '.d' + +before_each(clear) + +for _, func in ipairs({'bufname(%s)', 'bufnr(%s)', 'bufwinnr(%s)', + 'getbufline(%s, 1)', 'getbufvar(%s, "changedtick")', + 'setbufvar(%s, "f", 0)'}) do + local funcname = func:match('%w+') + describe(funcname .. '() function', function() + it('errors out when receives v:true/v:false/v:null', function() + -- Not compatible with Vim: in Vim it always results in buffer not found + -- without any error messages. + for _, var in ipairs({'v:true', 'v:false', 'v:null'}) do + eq('Vim(call):E5300: Expected a Number or a String', + exc_exec('call ' .. func:format(var))) + end + end) + it('errors out when receives invalid argument', function() + eq('Vim(call):E745: Expected a Number or a String, List found', + exc_exec('call ' .. func:format('[]'))) + eq('Vim(call):E728: Expected a Number or a String, Dictionary found', + exc_exec('call ' .. func:format('{}'))) + eq('Vim(call):E805: Expected a Number or a String, Float found', + exc_exec('call ' .. func:format('0.0'))) + eq('Vim(call):E703: Expected a Number or a String, Funcref found', + exc_exec('call ' .. func:format('function("tr")'))) + end) + end) +end + +describe('bufname() function', function() + it('returns empty string when buffer was not found', function() + command('file ' .. fname) + eq('', funcs.bufname(2)) + eq('', funcs.bufname('non-existent-buffer')) + eq('', funcs.bufname('#')) + command('edit ' .. fname2) + eq(2, funcs.bufnr('%')) + eq('', funcs.bufname('X')) + end) + before_each(function() + lfs.mkdir(dirname) + end) + after_each(function() + lfs.rmdir(dirname) + end) + it('returns expected buffer name', function() + eq('', funcs.bufname('%')) -- Buffer has no name yet + command('file ' .. fname) + local wd = lfs.currentdir() + local curdirname = funcs.fnamemodify(wd, ':t') + for _, arg in ipairs({'%', 1, 'X', wd}) do + eq(fname, funcs.bufname(arg)) + meths.set_current_dir('..') + eq(curdirname .. '/' .. fname, funcs.bufname(arg)) + meths.set_current_dir(curdirname) + meths.set_current_dir(dirname) + eq(wd .. '/' .. fname, funcs.bufname(arg)) + meths.set_current_dir('..') + eq(fname, funcs.bufname(arg)) + command('enew') + end + eq('', funcs.bufname('%')) + eq('', funcs.bufname('$')) + eq(2, funcs.bufnr('%')) + end) +end) + +describe('bufnr() function', function() + it('returns -1 when buffer was not found', function() + command('file ' .. fname) + eq(-1, funcs.bufnr(2)) + eq(-1, funcs.bufnr('non-existent-buffer')) + eq(-1, funcs.bufnr('#')) + command('edit ' .. fname2) + eq(2, funcs.bufnr('%')) + eq(-1, funcs.bufnr('X')) + end) + it('returns expected buffer number', function() + eq(1, funcs.bufnr('%')) + command('file ' .. fname) + local wd = lfs.currentdir() + local curdirname = funcs.fnamemodify(wd, ':t') + eq(1, funcs.bufnr(fname)) + eq(1, funcs.bufnr(wd)) + eq(1, funcs.bufnr(curdirname)) + eq(1, funcs.bufnr('X')) + end) + it('returns number of last buffer with "$"', function() + eq(1, funcs.bufnr('$')) + command('new') + eq(2, funcs.bufnr('$')) + command('new') + eq(3, funcs.bufnr('$')) + command('only') + eq(3, funcs.bufnr('$')) + eq(3, funcs.bufnr('%')) + command('buffer 1') + eq(3, funcs.bufnr('$')) + eq(1, funcs.bufnr('%')) + command('bwipeout 2') + eq(3, funcs.bufnr('$')) + eq(1, funcs.bufnr('%')) + command('bwipeout 3') + eq(1, funcs.bufnr('$')) + eq(1, funcs.bufnr('%')) + command('new') + eq(4, funcs.bufnr('$')) + end) +end) + +describe('bufwinnr() function', function() + it('returns -1 when buffer was not found', function() + command('file ' .. fname) + eq(-1, funcs.bufwinnr(2)) + eq(-1, funcs.bufwinnr('non-existent-buffer')) + eq(-1, funcs.bufwinnr('#')) + command('split ' .. fname2) -- It would be OK if there was one window + eq(2, funcs.bufnr('%')) + eq(-1, funcs.bufwinnr('X')) + end) + before_each(function() + lfs.mkdir(dirname) + end) + after_each(function() + lfs.rmdir(dirname) + end) + it('returns expected window number', function() + eq(1, funcs.bufwinnr('%')) + command('file ' .. fname) + command('vsplit') + command('split ' .. fname2) + eq(2, funcs.bufwinnr(fname)) + eq(1, funcs.bufwinnr(fname2)) + eq(-1, funcs.bufwinnr(fname:sub(1, #fname - 1))) + meths.set_current_dir(dirname) + eq(2, funcs.bufwinnr(fname)) + eq(1, funcs.bufwinnr(fname2)) + eq(-1, funcs.bufwinnr(fname:sub(1, #fname - 1))) + eq(1, funcs.bufwinnr('%')) + eq(2, funcs.bufwinnr(1)) + eq(1, funcs.bufwinnr(2)) + eq(-1, funcs.bufwinnr(3)) + eq(1, funcs.bufwinnr('$')) + end) +end) + +describe('getbufline() function', function() + it('returns empty list when buffer was not found', function() + command('file ' .. fname) + eq({}, funcs.getbufline(2, 1)) + eq({}, funcs.getbufline('non-existent-buffer', 1)) + eq({}, funcs.getbufline('#', 1)) + command('edit ' .. fname2) + eq(2, funcs.bufnr('%')) + eq({}, funcs.getbufline('X', 1)) + end) + it('returns empty list when range is invalid', function() + eq({}, funcs.getbufline(1, 0)) + curbufmeths.set_lines(0, 1, false, {'foo', 'bar', 'baz'}) + eq({}, funcs.getbufline(1, 2, 1)) + eq({}, funcs.getbufline(1, -10, -20)) + eq({}, funcs.getbufline(1, -2, -1)) + eq({}, funcs.getbufline(1, -1, 9999)) + end) + it('returns expected lines', function() + meths.set_option('hidden', true) + command('file ' .. fname) + curbufmeths.set_lines(0, 1, false, {'foo\0', '\0bar', 'baz'}) + command('edit ' .. fname2) + curbufmeths.set_lines(0, 1, false, {'abc\0', '\0def', 'ghi'}) + eq({'foo\n', '\nbar', 'baz'}, funcs.getbufline(1, 1, 9999)) + eq({'abc\n', '\ndef', 'ghi'}, funcs.getbufline(2, 1, 9999)) + eq({'foo\n', '\nbar', 'baz'}, funcs.getbufline(1, 1, '$')) + eq({'baz'}, funcs.getbufline(1, '$', '$')) + eq({'baz'}, funcs.getbufline(1, '$', 9999)) + end) +end) + +describe('getbufvar() function', function() + it('returns empty list when buffer was not found', function() + command('file ' .. fname) + eq('', funcs.getbufvar(2, '&autoindent')) + eq('', funcs.getbufvar('non-existent-buffer', '&autoindent')) + eq('', funcs.getbufvar('#', '&autoindent')) + command('edit ' .. fname2) + eq(2, funcs.bufnr('%')) + eq('', funcs.getbufvar('X', '&autoindent')) + end) + it('returns empty list when variable/option/etc was not found', function() + command('file ' .. fname) + eq('', funcs.getbufvar(1, '&autondent')) + eq('', funcs.getbufvar(1, 'changedtic')) + end) + it('returns expected option value', function() + eq(0, funcs.getbufvar(1, '&autoindent')) + eq(0, funcs.getbufvar(1, '&l:autoindent')) + eq(0, funcs.getbufvar(1, '&g:autoindent')) + -- Also works with global-only options + eq(0, funcs.getbufvar(1, '&hidden')) + eq(0, funcs.getbufvar(1, '&l:hidden')) + eq(0, funcs.getbufvar(1, '&g:hidden')) + -- Also works with window-local options + eq(0, funcs.getbufvar(1, '&number')) + eq(0, funcs.getbufvar(1, '&l:number')) + eq(0, funcs.getbufvar(1, '&g:number')) + command('new') + -- But with window-local options it probably does not what you expect + curwinmeths.set_option('number', true) + -- (note that current window’s buffer is 2, but getbufvar() receives 1) + eq(2, bufmeths.get_number(curwinmeths.get_buf())) + eq(1, funcs.getbufvar(1, '&number')) + eq(1, funcs.getbufvar(1, '&l:number')) + -- You can get global value though, if you find this useful. + eq(0, funcs.getbufvar(1, '&g:number')) + end) + it('returns expected variable value', function() + eq(2, funcs.getbufvar(1, 'changedtick')) + curbufmeths.set_lines(0, 1, false, {'abc\0', '\0def', 'ghi'}) + eq(3, funcs.getbufvar(1, 'changedtick')) + curbufmeths.set_var('test', true) + eq(true, funcs.getbufvar(1, 'test')) + eq({test=true, changedtick=3}, funcs.getbufvar(1, '')) + command('new') + eq(3, funcs.getbufvar(1, 'changedtick')) + eq(true, funcs.getbufvar(1, 'test')) + eq({test=true, changedtick=3}, funcs.getbufvar(1, '')) + end) +end) + +describe('setbufvar() function', function() + it('throws the error or ignores the input when buffer was not found', function() + command('file ' .. fname) + eq(0, + exc_exec('call setbufvar(2, "&autoindent", 0)')) + eq('Vim(call):E94: No matching buffer for non-existent-buffer', + exc_exec('call setbufvar("non-existent-buffer", "&autoindent", 0)')) + eq(0, + exc_exec('call setbufvar("#", "&autoindent", 0)')) + command('edit ' .. fname2) + eq(2, funcs.bufnr('%')) + eq('Vim(call):E93: More than one match for X', + exc_exec('call setbufvar("X", "&autoindent", 0)')) + end) + it('may set options, including window-local and global values', function() + local buf1 = meths.get_current_buf() + eq(false, curwinmeths.get_option('number')) + command('split') + command('new') + eq(2, bufmeths.get_number(curwinmeths.get_buf())) + funcs.setbufvar(1, '&number', true) + local windows = curtabmeths.list_wins() + eq(false, winmeths.get_option(windows[1], 'number')) + eq(true, winmeths.get_option(windows[2], 'number')) + eq(false, winmeths.get_option(windows[3], 'number')) + eq(false, winmeths.get_option(meths.get_current_win(), 'number')) + + eq(false, meths.get_option('hidden')) + funcs.setbufvar(1, '&hidden', true) + eq(true, meths.get_option('hidden')) + + eq(false, bufmeths.get_option(buf1, 'autoindent')) + funcs.setbufvar(1, '&autoindent', true) + eq(true, bufmeths.get_option(buf1, 'autoindent')) + eq('Vim(call):E355: Unknown option: xxx', + exc_exec('call setbufvar(1, "&xxx", 0)')) + end) + it('may set variables', function() + local buf1 = meths.get_current_buf() + command('split') + command('new') + eq(2, curbufmeths.get_number()) + funcs.setbufvar(1, 'number', true) + eq(true, bufmeths.get_var(buf1, 'number')) + eq('Vim(call):E461: Illegal variable name: b:', + exc_exec('call setbufvar(1, "", 0)')) + eq(true, bufmeths.get_var(buf1, 'number')) + funcs.setbufvar(1, 'changedtick', true) + -- eq(true, bufmeths.get_var(buf1, 'changedtick')) + eq(2, funcs.getbufvar(1, 'changedtick')) + end) +end) From e18a5783080f7c94f408ec5f53dedffdb69789e1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 20 Aug 2016 22:24:34 +0300 Subject: [PATCH 0177/1671] *: Move some dictionary functions to typval.h and use char* Also fixes buffer reusage in setmatches() and complete(). --- src/.asan-blacklist | 2 +- src/nvim/api/private/helpers.c | 33 +- src/nvim/api/vim.c | 2 +- src/nvim/ascii.h | 8 +- src/nvim/buffer.c | 12 +- src/nvim/buffer.h | 5 +- src/nvim/charset.h | 10 + src/nvim/diff.c | 2 +- src/nvim/edit.c | 182 +- src/nvim/eval.c | 1750 +++++------------ src/nvim/eval.h | 8 +- src/nvim/eval/decode.c | 26 +- src/nvim/eval/encode.c | 4 +- src/nvim/eval/typval.c | 788 +++++++- src/nvim/eval/typval.h | 128 +- src/nvim/eval/typval_encode.c.h | 10 +- src/nvim/event/process.h | 2 +- src/nvim/ex_cmds.c | 4 +- src/nvim/ex_cmds2.c | 65 +- src/nvim/ex_docmd.c | 28 +- src/nvim/ex_getln.c | 4 +- src/nvim/file_search.c | 2 +- src/nvim/getchar.c | 5 +- src/nvim/hashtab.c | 7 +- src/nvim/hashtab.h | 19 + src/nvim/macros.h | 9 + src/nvim/main.c | 45 +- src/nvim/mark.c | 2 +- src/nvim/mbyte.c | 36 +- src/nvim/mbyte.h | 15 + src/nvim/memline.c | 2 +- src/nvim/ops.c | 5 +- src/nvim/option.c | 364 ++-- src/nvim/path.c | 73 +- src/nvim/popupmnu.c | 11 +- src/nvim/quickfix.c | 69 +- src/nvim/search.c | 9 +- src/nvim/shada.c | 14 +- src/nvim/spell.c | 9 +- src/nvim/spellfile.c | 2 +- src/nvim/syntax.c | 30 +- src/nvim/tag.c | 8 +- src/nvim/terminal.c | 10 +- src/nvim/undo.c | 4 +- src/nvim/vim.h | 38 +- src/nvim/window.c | 20 +- test/functional/eval/match_functions_spec.lua | 39 + test/functional/eval/string_spec.lua | 10 +- .../ex_cmds/dict_notifications_spec.lua | 4 +- .../ex_cmds/quickfix_commands_spec.lua | 83 + test/functional/viml/completion_spec.lua | 36 + test/unit/eval/helpers.lua | 6 +- 52 files changed, 2215 insertions(+), 1844 deletions(-) create mode 100644 test/functional/eval/match_functions_spec.lua create mode 100644 test/functional/ex_cmds/quickfix_commands_spec.lua diff --git a/src/.asan-blacklist b/src/.asan-blacklist index 7636f8fa82..928d81bd5a 100644 --- a/src/.asan-blacklist +++ b/src/.asan-blacklist @@ -1,3 +1,3 @@ # multiqueue.h pointer arithmetic is not accepted by asan fun:multiqueue_node_data -fun:dictwatcher_node_data +fun:tv_dict_watcher_node_data diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index ff45cad8f5..6b17ba1a11 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -88,14 +88,13 @@ bool try_end(Error *err) /// @param[out] err Details of an error that may have occurred Object dict_get_value(dict_T *dict, String key, Error *err) { - hashitem_T *hi = hash_find(&dict->dv_hashtab, (char_u *)key.data); + dictitem_T *const di = tv_dict_find(dict, key.data, (ptrdiff_t)key.size); - if (HASHITEM_EMPTY(hi)) { + if (di == NULL) { api_set_error(err, Validation, _("Key not found")); return (Object) OBJECT_INIT; } - dictitem_T *di = dict_lookup(hi); return vim_to_object(&di->di_tv); } @@ -130,7 +129,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, return rv; } - dictitem_T *di = dict_find(dict, (char_u *)key.data, (int)key.size); + dictitem_T *di = tv_dict_find(dict, key.data, (ptrdiff_t)key.size); if (di != NULL) { if (di->di_flags & DI_FLAGS_RO) { @@ -156,9 +155,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, rv = vim_to_object(&di->di_tv); } // Delete the entry - hashitem_T *hi = hash_find(&dict->dv_hashtab, di->di_key); - hash_remove(&dict->dv_hashtab, hi); - dictitem_free(di); + tv_dict_item_remove(dict, di); } } else { // Update the key @@ -171,8 +168,8 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, if (di == NULL) { // Need to create an entry - di = dictitem_alloc((uint8_t *) key.data); - dict_add(dict, di); + di = tv_dict_item_alloc_len(key.data, key.size); + tv_dict_add(dict, di); } else { // Return the old value if (retval) { @@ -706,7 +703,7 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) } case kObjectTypeDictionary: { - dict_T *dict = dict_alloc(); + dict_T *const dict = tv_dict_alloc(); for (uint32_t i = 0; i < obj.data.dictionary.size; i++) { KeyValuePair item = obj.data.dictionary.items[i]; @@ -716,20 +713,20 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) api_set_error(err, Validation, _("Empty dictionary keys aren't allowed")); // cleanup - dict_free(dict); + tv_dict_free(dict); return false; } - dictitem_T *di = dictitem_alloc((uint8_t *)key.data); + dictitem_T *const di = tv_dict_item_alloc(key.data); if (!object_to_vim(item.value, &di->di_tv, err)) { // cleanup - dictitem_free(di); - dict_free(dict); + tv_dict_item_free(di); + tv_dict_free(dict); return false; } - dict_add(dict, di); + tv_dict_add(dict, di); } dict->dv_refcount++; @@ -960,11 +957,7 @@ static void set_option_value_err(char *key, { char *errmsg; - if ((errmsg = (char *)set_option_value((uint8_t *)key, - numval, - (uint8_t *)stringval, - opt_flags))) - { + if ((errmsg = set_option_value(key, numval, stringval, opt_flags))) { if (try_end(err)) { return; } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 59c0200395..db2f25a2a6 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -185,7 +185,7 @@ Object nvim_eval(String expr, Error *err) typval_T *expr_result = eval_expr((char_u *)expr.data, NULL); if (!expr_result) { - api_set_error(err, Exception, _("Failed to evaluate expression")); + api_set_error(err, Exception, "Failed to evaluate expression"); } if (!try_end(err)) { diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h index 44ff540b40..31851a84e6 100644 --- a/src/nvim/ascii.h +++ b/src/nvim/ascii.h @@ -8,9 +8,11 @@ // Definitions of various common control characters. -#define CharOrd(x) ((x) < 'a' ? (x) - 'A' : (x) - 'a') -#define CharOrdLow(x) ((x) - 'a') -#define CharOrdUp(x) ((x) - 'A') +#define CharOrd(x) ((uint8_t)(x) < 'a' \ + ? (uint8_t)(x) - 'A'\ + : (uint8_t)(x) - 'a') +#define CharOrdLow(x) ((uint8_t)(x) - 'a') +#define CharOrdUp(x) ((uint8_t)(x) - 'A') #define ROT13(c, a) (((((c) - (a)) + 13) % 26) + (a)) #define NUL '\000' diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index f7333fead4..0ce9ce073e 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -661,7 +661,7 @@ static void free_buffer(buf_T *buf) free_buffer_stuff(buf, true); unref_var_dict(buf->b_vars); aubuflocal_remove(buf); - dict_unref(buf->additional_data); + tv_dict_unref(buf->additional_data); clear_fmark(&buf->b_last_cursor); clear_fmark(&buf->b_last_insert); clear_fmark(&buf->b_last_change); @@ -1481,7 +1481,7 @@ static inline void buf_init_changedtick(buf_T *const buf) }, .di_key = "changedtick", }; - dict_add(buf->b_vars, (dictitem_T *)&buf->changedtick_di); + tv_dict_add(buf->b_vars, (dictitem_T *)&buf->changedtick_di); } /// Add a file name to the buffer list. @@ -1573,7 +1573,7 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags) if (buf != curbuf || curbuf == NULL) { buf = xcalloc(1, sizeof(buf_T)); // init b: variables - buf->b_vars = dict_alloc(); + buf->b_vars = tv_dict_alloc(); init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE); buf_init_changedtick(buf); } @@ -5443,8 +5443,8 @@ void buf_open_scratch(handle_T bufnr, char *bufname) { (void)do_ecmd((int)bufnr, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, NULL); (void)setfname(curbuf, (char_u *)bufname, NULL, true); - set_option_value((char_u *)"bh", 0L, (char_u *)"hide", OPT_LOCAL); - set_option_value((char_u *)"bt", 0L, (char_u *)"nofile", OPT_LOCAL); - set_option_value((char_u *)"swf", 0L, NULL, OPT_LOCAL); + set_option_value("bh", 0L, "hide", OPT_LOCAL); + set_option_value("bt", 0L, "nofile", OPT_LOCAL); + set_option_value("swf", 0L, NULL, OPT_LOCAL); RESET_BINDING(curwin); } diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h index ed3e6ab6cc..c915d373aa 100644 --- a/src/nvim/buffer.h +++ b/src/nvim/buffer.h @@ -7,6 +7,7 @@ #include "nvim/screen.h" // for StlClickRecord #include "nvim/func_attr.h" #include "nvim/eval.h" +#include "nvim/macros.h" // Values for buflist_getfile() enum getf_values { @@ -91,8 +92,8 @@ static inline void buf_set_changedtick(buf_T *const buf, const int changedtick) static inline void buf_set_changedtick(buf_T *const buf, const int changedtick) { #ifndef NDEBUG - dictitem_T *const changedtick_di = dict_find( - buf->b_vars, (char_u *)"changedtick", sizeof("changedtick") - 1); + dictitem_T *const changedtick_di = tv_dict_find( + buf->b_vars, S_LEN("changedtick")); assert(changedtick_di != NULL); assert(changedtick_di->di_tv.v_type == VAR_NUMBER); assert(changedtick_di->di_tv.v_lock == VAR_FIXED); diff --git a/src/nvim/charset.h b/src/nvim/charset.h index 8d25b828e5..c69582c4c6 100644 --- a/src/nvim/charset.h +++ b/src/nvim/charset.h @@ -5,6 +5,16 @@ #include "nvim/pos.h" #include "nvim/buffer_defs.h" +/// Return the folded-case equivalent of the given character +/// +/// @param[in] c Character to transform. +/// +/// @return Folded variant. +#define CH_FOLD(c) \ + utf_fold((sizeof(c) == sizeof(char)) \ + ?((int)(uint8_t)(c)) \ + :((int)(c))) + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "charset.h.generated.h" #endif diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 11ade20c1c..ff76abc01f 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -1586,7 +1586,7 @@ static int diff_cmp(char_u *s1, char_u *s2) } if ((diff_flags & DIFF_ICASE) && !(diff_flags & DIFF_IWHITE)) { - return mb_stricmp(s1, s2); + return mb_stricmp((const char *)s1, (const char *)s2); } // Ignore white space changes and possibly ignore case. diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 77d5b2c816..53b196c61f 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -2094,38 +2094,56 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int xfree(wca); - return ins_compl_add(IObuff, len, icase, fname, NULL, dir, - flags, FALSE); + return ins_compl_add(IObuff, len, icase, fname, NULL, true, dir, flags, + false); } - return ins_compl_add(str, len, icase, fname, NULL, dir, flags, FALSE); + return ins_compl_add(str, len, icase, fname, NULL, false, dir, flags, false); } -/* - * Add a match to the list of matches. - * If the given string is already in the list of completions, then return - * NOTDONE, otherwise add it to the list and return OK. If there is an error - * then FAIL is returned. - */ -static int -ins_compl_add ( - char_u *str, - int len, - int icase, - char_u *fname, - char_u **cptext, /* extra text for popup menu or NULL */ - int cdir, - int flags, - int adup /* accept duplicate match */ -) +/// Add a match to the list of matches +/// +/// @param[in] str Match to add. +/// @param[in] len Match length, -1 to use #STRLEN. +/// @param[in] icase Whether case is to be ignored. +/// @param[in] fname File name match comes from. May be NULL. +/// @param[in] cptext Extra text for popup menu. May be NULL. If not NULL, +/// must have exactly #CPT_COUNT items. +/// @param[in] cptext_allocated If true, will not copy cptext strings. +/// +/// @note Will free strings in case of error. +/// cptext itself will not be freed. +/// @param[in] cdir Completion direction. +/// @param[in] adup True if duplicate matches are to be accepted. +/// +/// @return NOTDONE if the given string is already in the list of completions, +/// otherwise it is added to the list and OK is returned. FAIL will be +/// returned in case of error. +static int ins_compl_add(char_u *const str, int len, + const bool icase, char_u *const fname, + char_u *const *const cptext, + const bool cptext_allocated, + const Direction cdir, int flags, const bool adup) + FUNC_ATTR_NONNULL_ARG(1) { compl_T *match; - int dir = (cdir == 0 ? compl_direction : cdir); + int dir = (cdir == kDirectionNotSet ? compl_direction : cdir); os_breakcheck(); - if (got_int) +#define FREE_CPTEXT(cptext, cptext_allocated) \ + do { \ + if (cptext_allocated) { \ + for (size_t i = 0; i < CPT_COUNT; i++) { \ + xfree(cptext[i]); \ + } \ + } \ + } while (0) + if (got_int) { + FREE_CPTEXT(cptext, cptext_allocated); return FAIL; - if (len < 0) + } + if (len < 0) { len = (int)STRLEN(str); + } /* * If the same match is already present, don't add it. @@ -2133,10 +2151,12 @@ ins_compl_add ( if (compl_first_match != NULL && !adup) { match = compl_first_match; do { - if ( !(match->cp_flags & ORIGINAL_TEXT) - && STRNCMP(match->cp_str, str, len) == 0 - && match->cp_str[len] == NUL) + if (!(match->cp_flags & ORIGINAL_TEXT) + && STRNCMP(match->cp_str, str, len) == 0 + && match->cp_str[len] == NUL) { + FREE_CPTEXT(cptext, cptext_allocated); return NOTDONE; + } match = match->cp_next; } while (match != NULL && match != compl_first_match); } @@ -2167,16 +2187,26 @@ ins_compl_add ( else if (fname != NULL) { match->cp_fname = vim_strsave(fname); flags |= FREE_FNAME; - } else + } else { match->cp_fname = NULL; + } match->cp_flags = flags; if (cptext != NULL) { int i; - for (i = 0; i < CPT_COUNT; ++i) - if (cptext[i] != NULL && *cptext[i] != NUL) - match->cp_text[i] = vim_strsave(cptext[i]); + for (i = 0; i < CPT_COUNT; i++) { + if (cptext[i] == NULL) { + continue; + } + if (*cptext[i] != NUL) { + match->cp_text[i] = (cptext_allocated + ? cptext[i] + : (char_u *)xstrdup((char *)cptext[i])); + } else if (cptext_allocated) { + xfree(cptext[i]); + } + } } /* @@ -2299,9 +2329,10 @@ static void ins_compl_add_matches(int num_matches, char_u **matches, int icase) for (i = 0; i < num_matches && add_r != FAIL; i++) if ((add_r = ins_compl_add(matches[i], -1, icase, - NULL, NULL, dir, 0, FALSE)) == OK) - /* if dir was BACKWARD then honor it just once */ + NULL, NULL, false, dir, 0, false)) == OK) { + // If dir was BACKWARD then honor it just once. dir = FORWARD; + } FreeWild(num_matches, matches); } @@ -2365,8 +2396,8 @@ void set_completion(colnr_T startcol, list_T *list) /* compl_pattern doesn't need to be set */ compl_orig_text = vim_strnsave(get_cursor_line_ptr() + compl_col, compl_length); - if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, 0, - ORIGINAL_TEXT, FALSE) != OK) { + if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, false, 0, + ORIGINAL_TEXT, false) != OK) { return; } @@ -2888,7 +2919,7 @@ static void ins_compl_clear(void) compl_orig_text = NULL; compl_enter_selects = FALSE; // clear v:completed_item - set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc()); + set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc()); } /// Check that Insert completion is active. @@ -3486,7 +3517,7 @@ expand_by_function ( theend: if (matchdict != NULL) { - dict_unref(matchdict); + tv_dict_unref(matchdict); } if (matchlist != NULL) { tv_list_unref(matchlist); @@ -3519,53 +3550,60 @@ static void ins_compl_add_dict(dict_T *dict) dictitem_T *di_refresh; dictitem_T *di_words; - /* Check for optional "refresh" item. */ - compl_opt_refresh_always = FALSE; - di_refresh = dict_find(dict, (char_u *)"refresh", 7); + // Check for optional "refresh" item. + compl_opt_refresh_always = false; + di_refresh = tv_dict_find(dict, S_LEN("refresh")); if (di_refresh != NULL && di_refresh->di_tv.v_type == VAR_STRING) { - char_u *v = di_refresh->di_tv.vval.v_string; + const char *v = (const char *)di_refresh->di_tv.vval.v_string; - if (v != NULL && STRCMP(v, (char_u *)"always") == 0) - compl_opt_refresh_always = TRUE; + if (v != NULL && strcmp(v, "always") == 0) { + compl_opt_refresh_always = true; + } } - /* Add completions from a "words" list. */ - di_words = dict_find(dict, (char_u *)"words", 5); - if (di_words != NULL && di_words->di_tv.v_type == VAR_LIST) + // Add completions from a "words" list. + di_words = tv_dict_find(dict, S_LEN("words")); + if (di_words != NULL && di_words->di_tv.v_type == VAR_LIST) { ins_compl_add_list(di_words->di_tv.vval.v_list); + } } -/* - * Add a match to the list of matches from a typeval_T. - * If the given string is already in the list of completions, then return - * NOTDONE, otherwise add it to the list and return OK. If there is an error - * then FAIL is returned. - */ -int ins_compl_add_tv(typval_T *tv, int dir) +/// Add a match to the list of matches from VimL object +/// +/// @param[in] tv Object to get matches from. +/// @param[in] dir Completion direction. +/// +/// @return NOTDONE if the given string is already in the list of completions, +/// otherwise it is added to the list and OK is returned. FAIL will be +/// returned in case of error. +int ins_compl_add_tv(typval_T *const tv, const Direction dir) + FUNC_ATTR_NONNULL_ALL { - char_u *word; - int icase = FALSE; - int adup = FALSE; - int aempty = FALSE; - char_u *(cptext[CPT_COUNT]); + const char *word; + bool icase = false; + bool adup = false; + bool aempty = false; + char *(cptext[CPT_COUNT]); if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL) { - word = get_dict_string(tv->vval.v_dict, "word", false); - cptext[CPT_ABBR] = get_dict_string(tv->vval.v_dict, "abbr", false); - cptext[CPT_MENU] = get_dict_string(tv->vval.v_dict, "menu", false); - cptext[CPT_KIND] = get_dict_string(tv->vval.v_dict, "kind", false); - cptext[CPT_INFO] = get_dict_string(tv->vval.v_dict, "info", false); + word = tv_dict_get_string(tv->vval.v_dict, "word", false); + cptext[CPT_ABBR] = tv_dict_get_string(tv->vval.v_dict, "abbr", true); + cptext[CPT_MENU] = tv_dict_get_string(tv->vval.v_dict, "menu", true); + cptext[CPT_KIND] = tv_dict_get_string(tv->vval.v_dict, "kind", true); + cptext[CPT_INFO] = tv_dict_get_string(tv->vval.v_dict, "info", true); - icase = get_dict_number(tv->vval.v_dict, "icase"); - adup = get_dict_number(tv->vval.v_dict, "dup"); - aempty = get_dict_number(tv->vval.v_dict, "empty"); + icase = (bool)tv_dict_get_number(tv->vval.v_dict, "icase"); + adup = (bool)tv_dict_get_number(tv->vval.v_dict, "dup"); + aempty = (bool)tv_dict_get_number(tv->vval.v_dict, "empty"); } else { - word = get_tv_string_chk(tv); + word = (const char *)get_tv_string_chk(tv); memset(cptext, 0, sizeof(cptext)); } - if (word == NULL || (!aempty && *word == NUL)) + if (word == NULL || (!aempty && *word == NUL)) { return FAIL; - return ins_compl_add(word, -1, icase, NULL, cptext, dir, 0, adup); + } + return ins_compl_add((char_u *)word, -1, icase, NULL, + (char_u **)cptext, true, dir, 0, adup); } /* @@ -3980,7 +4018,7 @@ static void ins_compl_delete(void) // causes flicker, thus we can't do that. changed_cline_bef_curs(); // clear v:completed_item - set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc()); + set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc()); } // Insert the new text being completed. @@ -3995,7 +4033,7 @@ static void ins_compl_insert(int in_compl_func) // Set completed item. // { word, abbr, menu, kind, info } - dict_T *dict = dict_alloc(); + dict_T *dict = tv_dict_alloc(); dict_add_nr_str(dict, "word", 0L, EMPTY_IF_NULL(compl_shown_match->cp_str)); dict_add_nr_str(dict, "abbr", 0L, @@ -4667,8 +4705,8 @@ static int ins_complete(int c, bool enable_pum) /* Always add completion for the original text. */ xfree(compl_orig_text); compl_orig_text = vim_strnsave(line + compl_col, compl_length); - if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, 0, - ORIGINAL_TEXT, FALSE) != OK) { + if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, false, 0, + ORIGINAL_TEXT, false) != OK) { xfree(compl_pattern); compl_pattern = NULL; xfree(compl_orig_text); diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 03d6eef9e3..5015deead7 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -97,6 +97,7 @@ #include "nvim/eval/typval.h" #include "nvim/eval/executor.h" #include "nvim/eval/gc.h" +#include "nvim/macros.h" // TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead @@ -432,21 +433,6 @@ static ScopeDictDictItem vimvars_var; /// v: hashtab #define vimvarht vimvardict.dv_hashtab -typedef enum { - kCallbackNone, - kCallbackFuncref, - kCallbackPartial, -} CallbackType; - -typedef struct { - union { - char_u *funcref; - partial_T *partial; - } data; - CallbackType type; -} Callback; -#define CALLBACK_NONE ((Callback){ .type = kCallbackNone }) - typedef struct { union { LibuvProcess uv; @@ -464,13 +450,6 @@ typedef struct { MultiQueue *events; } TerminalJobData; -typedef struct dict_watcher { - Callback callback; - char *key_pattern; - QUEUE node; - bool busy; // prevent recursion if the dict is changed in the callback -} DictWatcher; - typedef struct { TerminalJobData *data; Callback *callback; @@ -596,19 +575,19 @@ void eval_init(void) } vimvars[VV_VERSION].vv_nr = VIM_VERSION_100; - dict_T *const msgpack_types_dict = dict_alloc(); + dict_T *const msgpack_types_dict = tv_dict_alloc(); for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) { list_T *const type_list = tv_list_alloc(); type_list->lv_lock = VAR_FIXED; type_list->lv_refcount = 1; - dictitem_T *const di = dictitem_alloc((char_u *)msgpack_type_names[i]); + dictitem_T *const di = tv_dict_item_alloc(msgpack_type_names[i]); di->di_flags |= DI_FLAGS_RO|DI_FLAGS_FIX; di->di_tv = (typval_T) { .v_type = VAR_LIST, .vval = { .v_list = type_list, }, }; eval_msgpack_type_lists[i] = type_list; - if (dict_add(msgpack_types_dict, di) == FAIL) { + if (tv_dict_add(msgpack_types_dict, di) == FAIL) { // There must not be duplicate items in this dictionary by definition. assert(false); } @@ -616,9 +595,9 @@ void eval_init(void) msgpack_types_dict->dv_lock = VAR_FIXED; set_vim_var_dict(VV_MSGPACK_TYPES, msgpack_types_dict); - set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc()); + set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc()); - dict_T *v_event = dict_alloc(); + dict_T *v_event = tv_dict_alloc(); v_event->dv_lock = VAR_FIXED; set_vim_var_dict(VV_EVENT, v_event); set_vim_var_list(VV_ERRORS, tv_list_alloc()); @@ -745,7 +724,7 @@ void set_internal_string_var(char_u *name, char_u *value) tvp->v_type = VAR_STRING; tvp->vval.v_string = val; - set_var(name, tvp, FALSE); + set_var((const char *)name, tvp, false); free_tv(tvp); } @@ -1689,7 +1668,7 @@ static void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty, for (hi = ht->ht_array; todo > 0 && !got_int; ++hi) { if (!HASHITEM_EMPTY(hi)) { todo--; - di = HI2DI(hi); + di = TV_DICT_HI2DI(hi); if (empty || di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string != NULL) { list_one_var(di, prefix, first); @@ -1951,7 +1930,7 @@ ex_let_one ( } } if (s != NULL) { - set_option_value(arg, n, s, opt_flags); + set_option_value((const char *)arg, n, (char *)s, opt_flags); arg_end = (char_u *)p; } *p = c1; @@ -2220,7 +2199,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } lp->ll_list = NULL; lp->ll_dict = lp->ll_tv->vval.v_dict; - lp->ll_di = dict_find(lp->ll_dict, key, len); + lp->ll_di = tv_dict_find(lp->ll_dict, (const char *)key, len); /* When assigning to a scope dictionary check that a function and * variable name is valid (only variable name unless it is l: or @@ -2232,16 +2211,19 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (len != -1) { prevval = key[len]; key[len] = NUL; - } else - prevval = 0; /* avoid compiler warning */ - wrong = (lp->ll_dict->dv_scope == VAR_DEF_SCOPE - && rettv->v_type == VAR_FUNC - && var_check_func_name(key, lp->ll_di == NULL)) - || !valid_varname(key); - if (len != -1) + } else { + prevval = 0; // Avoid compiler warning. + } + wrong = ((lp->ll_dict->dv_scope == VAR_DEF_SCOPE + && rettv->v_type == VAR_FUNC + && !var_check_func_name((const char *)key, lp->ll_di == NULL)) + || !valid_varname((const char *)key)); + if (len != -1) { key[len] = prevval; - if (wrong) + } + if (wrong) { return NULL; + } } if (lp->ll_di == NULL) { @@ -2391,12 +2373,12 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch && !tv_check_lock(di->di_tv.v_lock, (const char *)lp->ll_name, STRLEN(lp->ll_name)))) && eexe_mod_op(&tv, rettv, (const char *)op) == OK) { - set_var(lp->ll_name, &tv, false); + set_var((const char *)lp->ll_name, &tv, false); } tv_clear(&tv); } } else { - set_var(lp->ll_name, rettv, copy); + set_var((const char *)lp->ll_name, rettv, copy); } *endp = cc; } else if (tv_check_lock(lp->ll_newkey == NULL @@ -2450,26 +2432,20 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch : lp->ll_n1 != lp->ll_n2) EMSG(_("E711: List value has not enough items")); } else { - typval_T oldtv; + typval_T oldtv = TV_INITIAL_VALUE; dict_T *dict = lp->ll_dict; - bool watched = is_watched(dict); + bool watched = tv_dict_is_watched(dict); - if (watched) { - init_tv(&oldtv); - } - - /* - * Assign to a List or Dictionary item. - */ + // Assign to a List or Dictionary item. if (lp->ll_newkey != NULL) { if (op != NULL && *op != '=') { EMSG2(_(e_letwrong), op); return; } - /* Need to add an item to the Dictionary. */ - di = dictitem_alloc(lp->ll_newkey); - if (dict_add(lp->ll_tv->vval.v_dict, di) == FAIL) { + // Need to add an item to the Dictionary. + di = tv_dict_item_alloc((const char *)lp->ll_newkey); + if (tv_dict_add(lp->ll_tv->vval.v_dict, di) == FAIL) { xfree(di); return; } @@ -2493,16 +2469,16 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch } else { *lp->ll_tv = *rettv; lp->ll_tv->v_lock = 0; - init_tv(rettv); + tv_init(rettv); } notify: if (watched) { if (oldtv.v_type == VAR_UNKNOWN) { - dictwatcher_notify(dict, (char *)lp->ll_newkey, lp->ll_tv, NULL); + tv_dict_watcher_notify(dict, (char *)lp->ll_newkey, lp->ll_tv, NULL); } else { dictitem_T *di = lp->ll_di; - dictwatcher_notify(dict, (char *)di->di_key, lp->ll_tv, &oldtv); + tv_dict_watcher_notify(dict, (char *)di->di_key, lp->ll_tv, &oldtv); tv_clear(&oldtv); } } @@ -2806,7 +2782,7 @@ void ex_call(exarg_T *eap) } end: - dict_unref(fudi.fd_dict); + tv_dict_unref(fudi.fd_dict); xfree(tofree); } @@ -2944,7 +2920,7 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) // unlet a Dictionary item. dict_T *d = lp->ll_dict; dictitem_T *di = lp->ll_di; - bool watched = is_watched(d); + bool watched = tv_dict_is_watched(d); char *key = NULL; typval_T oldtv; @@ -2954,10 +2930,10 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) key = xstrdup((char *)di->di_key); } - dictitem_remove(d, di); + tv_dict_item_remove(d, di); if (watched) { - dictwatcher_notify(d, key, NULL, &oldtv); + tv_dict_watcher_notify(d, key, NULL, &oldtv); tv_clear(&oldtv); xfree(key); } @@ -3005,7 +2981,7 @@ int do_unlet(char_u *name, int forceit) hi = find_hi_in_scoped_ht((const char *)name, &ht); } if (hi != NULL && !HASHITEM_EMPTY(hi)) { - di = HI2DI(hi); + di = TV_DICT_HI2DI(hi); if (var_check_fixed(di->di_flags, (const char *)name, STRLEN(name)) || var_check_ro(di->di_flags, (const char *)name, STRLEN(name)) || tv_check_lock(d->dv_lock, (const char *)name, STRLEN(name))) { @@ -3018,7 +2994,7 @@ int do_unlet(char_u *name, int forceit) } typval_T oldtv; - bool watched = is_watched(dict); + bool watched = tv_dict_is_watched(dict); if (watched) { copy_tv(&di->di_tv, &oldtv); @@ -3027,7 +3003,7 @@ int do_unlet(char_u *name, int forceit) delete_var(ht, hi); if (watched) { - dictwatcher_notify(dict, (char *)varname, NULL, &oldtv); + tv_dict_watcher_notify(dict, (char *)varname, NULL, &oldtv); tv_clear(&oldtv); } return OK; @@ -3102,18 +3078,12 @@ static int do_lock_var(lval_T *lp, char_u *name_end, const int deep, */ void del_menutrans_vars(void) { - hashitem_T *hi; - int todo; - hash_lock(&globvarht); - todo = (int)globvarht.ht_used; - for (hi = globvarht.ht_array; todo > 0 && !got_int; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - if (STRNCMP(HI2DI(hi)->di_key, "menutrans_", 10) == 0) - delete_var(&globvarht, hi); + HASHTAB_ITER(&globvarht, hi, { + if (STRNCMP(hi->hi_key, "menutrans_", 10) == 0) { + delete_var(&globvarht, hi); } - } + }); hash_unlock(&globvarht); } @@ -3659,11 +3629,12 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) tv_clear(&var2); return FAIL; } else { - /* Compare two Dictionaries for being equal or unequal. */ - n1 = dict_equal(rettv->vval.v_dict, var2.vval.v_dict, - ic, FALSE); - if (type == TYPE_NEQUAL) + // Compare two Dictionaries for being equal or unequal. + n1 = tv_dict_equal(rettv->vval.v_dict, var2.vval.v_dict, + ic, false); + if (type == TYPE_NEQUAL) { n1 = !n1; + } } } else if (rettv->v_type == VAR_FUNC || var2.v_type == VAR_FUNC || rettv->v_type == VAR_PARTIAL @@ -3749,11 +3720,12 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) } else { s1 = get_tv_string_buf(rettv, buf1); s2 = get_tv_string_buf(&var2, buf2); - if (type != TYPE_MATCH && type != TYPE_NOMATCH) - i = ic ? mb_stricmp(s1, s2) : STRCMP(s1, s2); - else + if (type != TYPE_MATCH && type != TYPE_NOMATCH) { + i = mb_strcmp_ic((bool)ic, (const char *)s1, (const char *)s2); + } else { i = 0; - n1 = FALSE; + } + n1 = false; switch (type) { case TYPE_EQUAL: n1 = (i == 0); break; case TYPE_NEQUAL: n1 = (i != 0); break; @@ -4355,11 +4327,11 @@ eval_index ( int verbose /* give error messages */ ) { - int empty1 = FALSE, empty2 = FALSE; - typval_T var1, var2; + bool empty1 = false; + bool empty2 = false; long n1, n2 = 0; long len = -1; - int range = FALSE; + int range = false; char_u *s; char_u *key = NULL; @@ -4397,8 +4369,8 @@ eval_index ( } } - init_tv(&var1); - init_tv(&var2); + typval_T var1 = TV_INITIAL_VALUE; + typval_T var2 = TV_INITIAL_VALUE; if (**arg == '.') { /* * dict.name @@ -4576,7 +4548,7 @@ eval_index ( } } - item = dict_find(rettv->vval.v_dict, key, (int)len); + item = tv_dict_find(rettv->vval.v_dict, (const char *)key, len); if (item == NULL && verbose) { emsgf(_(e_dictkey), key); @@ -4868,7 +4840,7 @@ static void partial_free(partial_T *pt) tv_clear(&pt->pt_argv[i]); } xfree(pt->pt_argv); - dict_unref(pt->pt_dict); + tv_dict_unref(pt->pt_dict); if (pt->pt_name != NULL) { func_unref(pt->pt_name); xfree(pt->pt_name); @@ -4940,64 +4912,7 @@ failret: return OK; } - -// TODO(ZyX-I): move to eval/typval - -/* - * Return the dictitem that an entry in a hashtable points to. - */ -dictitem_T *dict_lookup(hashitem_T *hi) -{ - return HI2DI(hi); -} - -// TODO(ZyX-I): move to eval/typval - -/* - * Return TRUE when two dictionaries have exactly the same key/values. - */ -static int -dict_equal ( - dict_T *d1, - dict_T *d2, - int ic, /* ignore case for strings */ - int recursive /* TRUE when used recursively */ -) -{ - hashitem_T *hi; - dictitem_T *item2; - int todo; - - if (d1 == NULL && d2 == NULL) { - return true; - } - if (d1 == NULL || d2 == NULL) { - return false; - } - if (d1 == d2) { - return true; - } - if (dict_len(d1) != dict_len(d2)) { - return false; - } - - todo = (int)d1->dv_hashtab.ht_used; - for (hi = d1->dv_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - item2 = dict_find(d2, hi->hi_key, -1); - if (item2 == NULL) - return FALSE; - if (!tv_equal(&HI2DI(hi)->di_tv, &item2->di_tv, ic, recursive)) - return FALSE; - --todo; - } - } - return TRUE; -} - -static int tv_equal_recurse_limit; - -static bool func_equal( +bool func_equal( typval_T *tv1, typval_T *tv2, bool ic // ignore case @@ -5032,7 +4947,7 @@ static bool func_equal( if (d1 != d2) { return false; } - } else if (!dict_equal(d1, d2, ic, true)) { + } else if (!tv_dict_equal(d1, d2, ic, true)) { return false; } @@ -5051,90 +4966,6 @@ static bool func_equal( return true; } -/* - * Return TRUE if "tv1" and "tv2" have the same value. - * Compares the items just like "==" would compare them, but strings and - * numbers are different. Floats and numbers are also different. - */ -int tv_equal( - typval_T *tv1, - typval_T *tv2, - int ic, /* ignore case */ - int recursive /* TRUE when used recursively */ -) -{ - char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; - char_u *s1, *s2; - static int recursive_cnt = 0; /* catch recursive loops */ - int r; - - /* Catch lists and dicts that have an endless loop by limiting - * recursiveness to a limit. We guess they are equal then. - * A fixed limit has the problem of still taking an awful long time. - * Reduce the limit every time running into it. That should work fine for - * deeply linked structures that are not recursively linked and catch - * recursiveness quickly. */ - if (!recursive) - tv_equal_recurse_limit = 1000; - if (recursive_cnt >= tv_equal_recurse_limit) { - --tv_equal_recurse_limit; - return TRUE; - } - - // For VAR_FUNC and VAR_PARTIAL compare the function name, bound dict and - // arguments. - if ((tv1->v_type == VAR_FUNC - || (tv1->v_type == VAR_PARTIAL && tv1->vval.v_partial != NULL)) - && (tv2->v_type == VAR_FUNC - || (tv2->v_type == VAR_PARTIAL && tv2->vval.v_partial != NULL))) { - recursive_cnt++; - r = func_equal(tv1, tv2, ic); - recursive_cnt--; - return r; - } - if (tv1->v_type != tv2->v_type) { - return false; - } - - switch (tv1->v_type) { - case VAR_LIST: - recursive_cnt++; - r = tv_list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, true); - recursive_cnt--; - return r; - - case VAR_DICT: - ++recursive_cnt; - r = dict_equal(tv1->vval.v_dict, tv2->vval.v_dict, ic, TRUE); - --recursive_cnt; - return r; - - case VAR_NUMBER: - return tv1->vval.v_number == tv2->vval.v_number; - - case VAR_FLOAT: - return tv1->vval.v_float == tv2->vval.v_float; - - case VAR_STRING: - s1 = get_tv_string_buf(tv1, buf1); - s2 = get_tv_string_buf(tv2, buf2); - return (ic ? mb_stricmp(s1, s2) : STRCMP(s1, s2)) == 0; - - case VAR_SPECIAL: - return tv1->vval.v_special == tv2->vval.v_special; - - case VAR_FUNC: - case VAR_PARTIAL: - case VAR_UNKNOWN: - // VAR_UNKNOWN can be the result of an invalid expression, let’s say it does - // not equal anything, not even self. - return false; - } - - assert(false); - return false; -} - /// Get next (unique) copy ID /// /// Used for traversing nested structures e.g. when serializing them or garbage @@ -5400,7 +5231,7 @@ static int free_unref_items(int copyID) // Free the Dictionary and ordinary items it contains, but don't // recurse into Lists and Dictionaries, they will be in the list // of dicts or list of lists. - dict_free_contents(dd); + tv_dict_free_contents(dd); did_free = true; } } @@ -5423,7 +5254,7 @@ static int free_unref_items(int copyID) for (dd = gc_first_dict; dd != NULL; dd = dd_next) { dd_next = dd->dv_used_next; if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) { - dict_free_dict(dd); + tv_dict_free_dict(dd); } } @@ -5460,14 +5291,10 @@ bool set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack) // Mark each item in the hashtab. If the item contains a hashtab // it is added to ht_stack, if it contains a list it is added to // list_stack. - int todo = (int)cur_ht->ht_used; - for (hashitem_T *hi = cur_ht->ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - abort = abort || set_ref_in_item(&HI2DI(hi)->di_tv, copyID, &ht_stack, - list_stack); - } - } + HASHTAB_ITER(cur_ht, hi, { + abort = abort || set_ref_in_item( + &TV_DICT_HI2DI(hi)->di_tv, copyID, &ht_stack, list_stack); + }); } if (ht_stack == NULL) { @@ -5559,7 +5386,7 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, QUEUE *w = NULL; DictWatcher *watcher = NULL; QUEUE_FOREACH(w, &dd->watchers) { - watcher = dictwatcher_node_data(w); + watcher = tv_dict_watcher_node_data(w); set_ref_in_callback(&watcher->callback, copyID, ht_stack, list_stack); } } @@ -5704,265 +5531,6 @@ static bool set_ref_in_funccal(funccall_T *fc, int copyID) return abort; } -/// Allocate an empty header for a dictionary. -dict_T *dict_alloc(void) FUNC_ATTR_NONNULL_RET -{ - dict_T *d = xmalloc(sizeof(dict_T)); - - // Add the dict to the list of dicts for garbage collection. - if (gc_first_dict != NULL) { - gc_first_dict->dv_used_prev = d; - } - d->dv_used_next = gc_first_dict; - d->dv_used_prev = NULL; - gc_first_dict = d; - - hash_init(&d->dv_hashtab); - d->dv_lock = VAR_UNLOCKED; - d->dv_scope = 0; - d->dv_refcount = 0; - d->dv_copyID = 0; - QUEUE_INIT(&d->watchers); - - return d; -} - -/* - * Allocate an empty dict for a return value. - */ -static void rettv_dict_alloc(typval_T *rettv) -{ - dict_T *d = dict_alloc(); - - rettv->vval.v_dict = d; - rettv->v_type = VAR_DICT; - rettv->v_lock = VAR_UNLOCKED; - d->dv_refcount++; -} - -/// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary. -/// -/// @param d The Dictionary to clear -void dict_clear(dict_T *d) - FUNC_ATTR_NONNULL_ALL -{ - hash_lock(&d->dv_hashtab); - assert(d->dv_hashtab.ht_locked > 0); - - size_t todo = d->dv_hashtab.ht_used; - for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) { - if (!HASHITEM_EMPTY(hi)) { - dictitem_free(HI2DI(hi)); - hash_remove(&d->dv_hashtab, hi); - todo--; - } - } - - hash_unlock(&d->dv_hashtab); -} - - -/* - * Unreference a Dictionary: decrement the reference count and free it when it - * becomes zero. - */ -void dict_unref(dict_T *d) -{ - if (d != NULL && --d->dv_refcount <= 0) { - dict_free(d); - } -} - -/// Free a Dictionary, including all items it contains. -/// Ignores the reference count. -static void dict_free_contents(dict_T *const d) - FUNC_ATTR_NONNULL_ALL -{ - // Lock the hashtab, we don't want it to resize while freeing items. - hash_lock(&d->dv_hashtab); - assert(d->dv_hashtab.ht_locked > 0); - size_t todo = (int)d->dv_hashtab.ht_used; - for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) { - if (!HASHITEM_EMPTY(hi)) { - // Remove the item before deleting it, just in case there is - // something recursive causing trouble. - dictitem_T *const di = HI2DI(hi); - hash_remove(&d->dv_hashtab, hi); - dictitem_free(di); - todo--; - } - } - - while (!QUEUE_EMPTY(&d->watchers)) { - QUEUE *w = QUEUE_HEAD(&d->watchers); - DictWatcher *watcher = dictwatcher_node_data(w); - QUEUE_REMOVE(w); - dictwatcher_free(watcher); - } - - hash_clear(&d->dv_hashtab); -} - -static void dict_free_dict(dict_T *d) - FUNC_ATTR_NONNULL_ALL -{ - // Remove the dict from the list of dicts for garbage collection. - if (d->dv_used_prev == NULL) { - gc_first_dict = d->dv_used_next; - } else { - d->dv_used_prev->dv_used_next = d->dv_used_next; - } - if (d->dv_used_next != NULL) { - d->dv_used_next->dv_used_prev = d->dv_used_prev; - } - - xfree(d); -} - -void dict_free(dict_T *d) - FUNC_ATTR_NONNULL_ALL -{ - if (!tv_in_free_unref_items) { - dict_free_contents(d); - dict_free_dict(d); - } -} - -/* - * Allocate a Dictionary item. - * The "key" is copied to the new item. - * Note that the value of the item "di_tv" still needs to be initialized! - */ -dictitem_T *dictitem_alloc(char_u *key) FUNC_ATTR_NONNULL_RET -{ - dictitem_T *di = xmalloc(offsetof(dictitem_T, di_key) + STRLEN(key) + 1); -#ifndef __clang_analyzer__ - STRCPY(di->di_key, key); -#endif - di->di_flags = DI_FLAGS_ALLOC; - return di; -} - -/* - * Make a copy of a Dictionary item. - */ -static dictitem_T *dictitem_copy(dictitem_T *org) FUNC_ATTR_NONNULL_RET -{ - dictitem_T *di = xmalloc(sizeof(dictitem_T) + STRLEN(org->di_key)); - - STRCPY(di->di_key, org->di_key); - di->di_flags = DI_FLAGS_ALLOC; - copy_tv(&org->di_tv, &di->di_tv); - - return di; -} - -/* - * Remove item "item" from Dictionary "dict" and free it. - */ -static void dictitem_remove(dict_T *dict, dictitem_T *item) -{ - hashitem_T *hi; - - hi = hash_find(&dict->dv_hashtab, item->di_key); - if (HASHITEM_EMPTY(hi)) { - EMSG2(_(e_intern2), "dictitem_remove()"); - } else { - hash_remove(&dict->dv_hashtab, hi); - } - dictitem_free(item); -} - -/* - * Free a dict item. Also clears the value. - */ -void dictitem_free(dictitem_T *item) -{ - tv_clear(&item->di_tv); - if (item->di_flags & DI_FLAGS_ALLOC) { - xfree(item); - } -} - -/// Make a copy of dictionary -/// -/// @param[in] conv If non-NULL, then all internal strings will be converted. -/// @param[in] orig Original dictionary to copy. -/// @param[in] deep If false, then shallow copy will be done. -/// @param[in] copyID See var_item_copy(). -/// -/// @return Copied dictionary. May be NULL in case original dictionary is NULL -/// or some failure happens. The refcount of the new dictionary is set -/// to 1. -static dict_T *dict_copy(const vimconv_T *const conv, - dict_T *const orig, - const bool deep, - const int copyID) -{ - dictitem_T *di; - int todo; - hashitem_T *hi; - - if (orig == NULL) - return NULL; - - dict_T *copy = dict_alloc(); - { - if (copyID != 0) { - orig->dv_copyID = copyID; - orig->dv_copydict = copy; - } - todo = (int)orig->dv_hashtab.ht_used; - for (hi = orig->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - - if (conv == NULL || conv->vc_type == CONV_NONE) { - di = dictitem_alloc(hi->hi_key); - } else { - char *const key = (char *) string_convert((vimconv_T *) conv, - hi->hi_key, NULL); - if (key == NULL) { - di = dictitem_alloc(hi->hi_key); - } else { - di = dictitem_alloc((char_u *) key); - xfree(key); - } - } - if (deep) { - if (var_item_copy(conv, &HI2DI(hi)->di_tv, &di->di_tv, deep, - copyID) == FAIL) { - xfree(di); - break; - } - } else - copy_tv(&HI2DI(hi)->di_tv, &di->di_tv); - if (dict_add(copy, di) == FAIL) { - dictitem_free(di); - break; - } - } - } - - ++copy->dv_refcount; - if (todo > 0) { - dict_unref(copy); - copy = NULL; - } - } - - return copy; -} - -/* - * Add item "item" to Dictionary "d". - * Returns FAIL when key already exists. - */ -int dict_add(dict_T *d, dictitem_T *item) -{ - return hash_add(&d->dv_hashtab, item->di_key); -} - /* * Add a number or string entry to dictionary "d". * When "str" is NULL use number "nr", otherwise use "str". @@ -5972,7 +5540,7 @@ int dict_add_nr_str(dict_T *d, char *key, long nr, char_u *str) { dictitem_T *item; - item = dictitem_alloc((char_u *)key); + item = tv_dict_item_alloc(key); item->di_tv.v_lock = 0; if (str == NULL) { item->di_tv.v_type = VAR_NUMBER; @@ -5981,8 +5549,8 @@ int dict_add_nr_str(dict_T *d, char *key, long nr, char_u *str) item->di_tv.v_type = VAR_STRING; item->di_tv.vval.v_string = vim_strsave(str); } - if (dict_add(d, item) == FAIL) { - dictitem_free(item); + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); return FAIL; } return OK; @@ -5994,13 +5562,13 @@ int dict_add_nr_str(dict_T *d, char *key, long nr, char_u *str) */ int dict_add_list(dict_T *d, char *key, list_T *list) { - dictitem_T *item = dictitem_alloc((char_u *)key); + dictitem_T *item = tv_dict_item_alloc(key); item->di_tv.v_lock = 0; item->di_tv.v_type = VAR_LIST; item->di_tv.vval.v_list = list; - if (dict_add(d, item) == FAIL) { - dictitem_free(item); + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); return FAIL; } ++list->lv_refcount; @@ -6011,13 +5579,13 @@ int dict_add_list(dict_T *d, char *key, list_T *list) /// Returns FAIL when out of memory and when key already exists. int dict_add_dict(dict_T *d, char *key, dict_T *dict) { - dictitem_T *item = dictitem_alloc((char_u *)key); + dictitem_T *const item = tv_dict_item_alloc(key); item->di_tv.v_lock = 0; item->di_tv.v_type = VAR_DICT; item->di_tv.vval.v_dict = dict; - if (dict_add(d, item) == FAIL) { - dictitem_free(item); + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); return FAIL; } dict->dv_refcount++; @@ -6029,60 +5597,12 @@ int dict_add_dict(dict_T *d, char *key, dict_T *dict) /// This does not protect against adding new keys to the Dictionary. /// /// @param dict The dict whose keys should be frozen -void dict_set_keys_readonly(dict_T *dict) +void dict_set_keys_readonly(dict_T *const dict) FUNC_ATTR_NONNULL_ALL { - size_t todo = dict->dv_hashtab.ht_used; - for (hashitem_T *hi = dict->dv_hashtab.ht_array; todo > 0 ; hi++) { - if (HASHITEM_EMPTY(hi)) { - continue; - } - todo--; - HI2DI(hi)->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX; - } -} - -/* - * Get the number of items in a Dictionary. - */ -static long dict_len(dict_T *d) -{ - if (d == NULL) - return 0L; - return (long)d->dv_hashtab.ht_used; -} - -/* - * Find item "key[len]" in Dictionary "d". - * If "len" is negative use strlen(key). - * Returns NULL when not found. - */ -dictitem_T *dict_find(dict_T *d, char_u *key, int len) -{ -#define AKEYLEN 200 - char_u buf[AKEYLEN]; - char_u *akey; - char_u *tofree = NULL; - hashitem_T *hi; - - if (d == NULL) { - return NULL; - } - if (len < 0) { - akey = key; - } else if (len >= AKEYLEN) { - tofree = akey = vim_strnsave(key, len); - } else { - /* Avoid a malloc/free by using buf[]. */ - STRLCPY(buf, key, len + 1); - akey = buf; - } - - hi = hash_find(&d->dv_hashtab, akey); - xfree(tofree); - if (HASHITEM_EMPTY(hi)) - return NULL; - return HI2DI(hi); + TV_DICT_ITER(dict, di, { + di->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX; + }); } /// Get a function from a dictionary @@ -6091,7 +5611,7 @@ dictitem_T *dict_find(dict_T *d, char_u *key, int len) /// @return true/false on success/failure. static bool get_dict_callback(dict_T *d, char *key, Callback *result) { - dictitem_T *di = dict_find(d, (uint8_t *)key, -1); + dictitem_T *const di = tv_dict_find(d, key, -1); if (di == NULL) { result->type = kCallbackNone; @@ -6113,40 +5633,6 @@ static bool get_dict_callback(dict_T *d, char *key, Callback *result) return res; } -/// Get a string item from a dictionary. -/// -/// @param save whether memory should be allocated for the return value -/// when false a shared buffer is used, can only be used once! -/// -/// @return the entry or NULL if the entry doesn't exist. -char_u *get_dict_string(dict_T *d, char *key, bool save) -{ - dictitem_T *di; - char_u *s; - - di = dict_find(d, (char_u *)key, -1); - if (di == NULL) { - return NULL; - } - s = get_tv_string(&di->di_tv); - if (save) { - s = vim_strsave(s); - } - return s; -} - -/// Get a number item from a dictionary. -/// -/// @return the entry or 0 if the entry doesn't exist. -long get_dict_number(dict_T *d, char *key) -{ - dictitem_T *di = dict_find(d, (char_u *)key, -1); - if (di == NULL) { - return 0; - } - return get_tv_number(&di->di_tv); -} - /* * Allocate a variable for a Dictionary and fill it from "*arg". * Return OK or FAIL. Returns NOTDONE for {expr}. @@ -6176,7 +5662,7 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) } if (evaluate) { - d = dict_alloc(); + d = tv_dict_alloc(); } tvkey.v_type = VAR_UNKNOWN; tv.v_type = VAR_UNKNOWN; @@ -6207,19 +5693,19 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) goto failret; } if (evaluate) { - item = dict_find(d, key, -1); + item = tv_dict_find(d, (const char *)key, -1); if (item != NULL) { EMSG2(_("E721: Duplicate key in Dictionary: \"%s\""), key); tv_clear(&tvkey); tv_clear(&tv); goto failret; } - item = dictitem_alloc(key); + item = tv_dict_item_alloc((const char *)key); tv_clear(&tvkey); item->di_tv = tv; item->di_tv.v_lock = 0; - if (dict_add(d, item) == FAIL) { - dictitem_free(item); + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); } } @@ -6236,7 +5722,7 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) EMSG2(_("E723: Missing end of Dictionary '}': %s"), *arg); failret: if (evaluate) { - dict_free(d); + tv_dict_free(d); } return FAIL; } @@ -8152,9 +7638,10 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) todo = error ? 0 : (int)d->dv_hashtab.ht_used; for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { if (!HASHITEM_EMPTY(hi)) { - --todo; - if (tv_equal(&HI2DI(hi)->di_tv, &argvars[1], ic, FALSE)) - ++n; + todo--; + if (tv_equal(&TV_DICT_HI2DI(hi)->di_tv, &argvars[1], ic, false)) { + n++; + } } } } @@ -8381,7 +7868,7 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) DictWatcher *watcher = NULL; bool matched = false; QUEUE_FOREACH(w, &dict->watchers) { - watcher = dictwatcher_node_data(w); + watcher = tv_dict_watcher_node_data(w); if (callback_equal(&watcher->callback, &callback) && !strcmp(watcher->key_pattern, key_pattern)) { matched = true; @@ -8397,7 +7884,7 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) } QUEUE_REMOVE(w); - dictwatcher_free(watcher); + tv_dict_watcher_free(watcher); } /* @@ -8771,74 +8258,6 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * Go over all entries in "d2" and add them to "d1". - * When "action" is "error" then a duplicate key is an error. - * When "action" is "force" then a duplicate key is overwritten. - * Otherwise duplicate keys are ignored ("action" is "keep"). - */ -void dict_extend(dict_T *d1, dict_T *d2, char_u *action) -{ - dictitem_T *di1; - hashitem_T *hi2; - int todo; - bool watched = is_watched(d1); - const char *const arg_errmsg = _("extend() argument"); - const size_t arg_errmsg_len = strlen(arg_errmsg); - - todo = (int)d2->dv_hashtab.ht_used; - for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2) { - if (!HASHITEM_EMPTY(hi2)) { - --todo; - di1 = dict_find(d1, hi2->hi_key, -1); - if (d1->dv_scope != 0) { - /* Disallow replacing a builtin function in l: and g:. - * Check the key to be valid when adding to any - * scope. */ - if (d1->dv_scope == VAR_DEF_SCOPE - && HI2DI(hi2)->di_tv.v_type == VAR_FUNC - && var_check_func_name(hi2->hi_key, - di1 == NULL)) - break; - if (!valid_varname(hi2->hi_key)) - break; - } - if (di1 == NULL) { - di1 = dictitem_copy(HI2DI(hi2)); - if (dict_add(d1, di1) == FAIL) { - dictitem_free(di1); - } - - if (watched) { - dictwatcher_notify(d1, (char *)di1->di_key, &di1->di_tv, NULL); - } - } else if (*action == 'e') { - EMSG2(_("E737: Key already exists: %s"), hi2->hi_key); - break; - } else if (*action == 'f' && HI2DI(hi2) != di1) { - typval_T oldtv; - - if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, arg_errmsg_len) - || var_check_ro(di1->di_flags, arg_errmsg, arg_errmsg_len)) { - break; - } - - if (watched) { - copy_tv(&di1->di_tv, &oldtv); - } - - tv_clear(&di1->di_tv); - copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv); - - if (watched) { - dictwatcher_notify(d1, (char *)di1->di_key, &di1->di_tv, &oldtv); - tv_clear(&oldtv); - } - } - } - } -} - /* * "extend(list, list [, idx])" function * "extend(dict, dict [, action])" function @@ -8880,37 +8299,41 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT) { - dict_T *d1, *d2; - char_u *action; - int i; + dict_T *d1; + dict_T *d2; d1 = argvars[0].vval.v_dict; d2 = argvars[1].vval.v_dict; if (d1 != NULL && !tv_check_lock(d1->dv_lock, arg_errmsg, arg_errmsg_len) && d2 != NULL) { - /* Check the third argument. */ + const char *action = "force"; + // Check the third argument. if (argvars[2].v_type != VAR_UNKNOWN) { - static char *(av[]) = {"keep", "force", "error"}; + const char *const av[] = { "keep", "force", "error" }; - action = get_tv_string_chk(&argvars[2]); - if (action == NULL) - return; /* type error; errmsg already given */ - for (i = 0; i < 3; ++i) - if (STRCMP(action, av[i]) == 0) + action = (const char *)get_tv_string_chk(&argvars[2]); + if (action == NULL) { + return; // Type error; error message already given. + } + size_t i; + for (i = 0; i < ARRAY_SIZE(av); i++) { + if (strcmp(action, av[i]) == 0) { break; + } + } if (i == 3) { EMSG2(_(e_invarg2), action); return; } - } else - action = (char_u *)"force"; + } - dict_extend(d1, d2, action); + tv_dict_extend(d1, d2, action); copy_tv(&argvars[0], rettv); } - } else + } else { EMSG2(_(e_listdictarg), "extend()"); + } } /* @@ -9076,7 +8499,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) if (!HASHITEM_EMPTY(hi)) { --todo; - di = HI2DI(hi); + di = TV_DICT_HI2DI(hi); if (map && (tv_check_lock(di->di_tv.v_lock, arg_errmsg, arg_errmsg_len) || var_check_ro(di->di_flags, arg_errmsg, arg_errmsg_len))) { @@ -9094,7 +8517,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) || var_check_ro(di->di_flags, arg_errmsg, arg_errmsg_len)) { break; } - dictitem_remove(d, di); + tv_dict_item_remove(d, di); } } } @@ -9639,9 +9062,10 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (argvars[0].v_type == VAR_DICT) { if ((d = argvars[0].vval.v_dict) != NULL) { - di = dict_find(d, get_tv_string(&argvars[1]), -1); - if (di != NULL) + di = tv_dict_find(d, (const char *)get_tv_string(&argvars[1]), -1); + if (di != NULL) { tv = &di->di_tv; + } } } else if (argvars[0].v_type == VAR_PARTIAL || argvars[0].v_type == VAR_FUNC) { @@ -9704,7 +9128,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void get_buffer_signs(buf_T *buf, list_T *l) { for (signlist_T *sign = buf->b_signlist; sign; sign = sign->next) { - dict_T *const d = dict_alloc(); + dict_T *const d = tv_dict_alloc(); dict_add_nr_str(d, "id", sign->id, NULL); dict_add_nr_str(d, "lnum", sign->lnum, NULL); @@ -9717,7 +9141,7 @@ static void get_buffer_signs(buf_T *buf, list_T *l) /// Returns buffer options, variables and other attributes in a dictionary. static dict_T *get_buffer_info(buf_T *buf) { - dict_T *dict = dict_alloc(); + dict_T *const dict = tv_dict_alloc(); dict_add_nr_str(dict, "bufnr", buf->b_fnum, NULL); dict_add_nr_str(dict, "name", 0L, @@ -9772,12 +9196,12 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) filtered = true; - di = dict_find(sel_d, (char_u *)"buflisted", -1); + di = tv_dict_find(sel_d, S_LEN("buflisted")); if (di != NULL && get_tv_number(&di->di_tv)) { sel_buflisted = true; } - di = dict_find(sel_d, (char_u *)"bufloaded", -1); + di = tv_dict_find(sel_d, S_LEN("bufloaded")); if (di != NULL && get_tv_number(&di->di_tv)) { sel_bufloaded = true; } @@ -10047,7 +9471,7 @@ static void f_getcharmod(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_dict_alloc(rettv); + tv_dict_alloc_ret(rettv); dict_T *dict = rettv->vval.v_dict; @@ -10467,7 +9891,7 @@ static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, (void)get_errorlist(wp, -1, rettv->vval.v_list); } } else { - rettv_dict_alloc(rettv); + tv_dict_alloc_ret(rettv); if (is_qf || wp != NULL) { if (what_arg->v_type == VAR_DICT) { dict_T *d = what_arg->vval.v_dict; @@ -10499,7 +9923,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_alloc_ret(rettv); while (cur != NULL) { - dict_T *dict = dict_alloc(); + dict_T *dict = tv_dict_alloc(); if (cur->match.regprog == NULL) { // match added with matchaddpos() for (i = 0; i < MAXPOSMATCH; ++i) { @@ -10678,7 +10102,7 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// as a dictionary. static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx) { - dict_T *dict = dict_alloc(); + dict_T *const dict = tv_dict_alloc(); dict_add_nr_str(dict, "tabnr", tp_idx, NULL); @@ -10776,7 +10200,7 @@ static void f_gettabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// Returns information about a window as a dictionary. static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr) { - dict_T *dict = dict_alloc(); + dict_T *const dict = tv_dict_alloc(); dict_add_nr_str(dict, "tabnr", tpnr, NULL); dict_add_nr_str(dict, "winnr", winnr, NULL); @@ -11286,8 +10710,9 @@ static void f_has_key(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].vval.v_dict == NULL) return; - rettv->vval.v_number = dict_find(argvars[0].vval.v_dict, - get_tv_string(&argvars[1]), -1) != NULL; + rettv->vval.v_number = tv_dict_find(argvars[0].vval.v_dict, + (const char *)get_tv_string(&argvars[1]), + -1) != NULL; } /// `haslocaldir([{win}[, {tab}]])` function @@ -11912,60 +11337,48 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void dict_list(typval_T *argvars, typval_T *rettv, int what) { - list_T *l2; - dictitem_T *di; - hashitem_T *hi; - listitem_T *li; - listitem_T *li2; - dict_T *d; - int todo; - if (argvars[0].v_type != VAR_DICT) { EMSG(_(e_dictreq)); return; } - if ((d = argvars[0].vval.v_dict) == NULL) + dict_T *const d = argvars[0].vval.v_dict; + if (d == NULL) { return; + } tv_list_alloc_ret(rettv); - todo = (int)d->dv_hashtab.ht_used; - for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - di = HI2DI(hi); + TV_DICT_ITER(d, di, { + listitem_T *const li = tv_list_item_alloc(); + tv_list_append(rettv->vval.v_list, li); - li = tv_list_item_alloc(); - tv_list_append(rettv->vval.v_list, li); + if (what == 0) { + // keys() + li->li_tv.v_type = VAR_STRING; + li->li_tv.v_lock = 0; + li->li_tv.vval.v_string = vim_strsave(di->di_key); + } else if (what == 1) { + // values() + copy_tv(&di->di_tv, &li->li_tv); + } else { + // items() + list_T *const l2 = tv_list_alloc(); + li->li_tv.v_type = VAR_LIST; + li->li_tv.v_lock = 0; + li->li_tv.vval.v_list = l2; + l2->lv_refcount++; - if (what == 0) { - /* keys() */ - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = vim_strsave(di->di_key); - } else if (what == 1) { - /* values() */ - copy_tv(&di->di_tv, &li->li_tv); - } else { - // items() - l2 = tv_list_alloc(); - li->li_tv.v_type = VAR_LIST; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_list = l2; - ++l2->lv_refcount; + listitem_T *sub_li = tv_list_item_alloc(); + tv_list_append(l2, sub_li); + sub_li->li_tv.v_type = VAR_STRING; + sub_li->li_tv.v_lock = 0; + sub_li->li_tv.vval.v_string = vim_strsave(di->di_key); - li2 = tv_list_item_alloc(); - tv_list_append(l2, li2); - li2->li_tv.v_type = VAR_STRING; - li2->li_tv.v_lock = 0; - li2->li_tv.vval.v_string = vim_strsave(di->di_key); - - li2 = tv_list_item_alloc(); - tv_list_append(l2, li2); - copy_tv(&di->di_tv, &li2->li_tv); - } + sub_li = tv_list_item_alloc(); + tv_list_append(l2, sub_li); + copy_tv(&di->di_tv, &sub_li->li_tv); } - } + }); } /// "id()" function @@ -12230,23 +11643,26 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_T *job_opts = NULL; - bool detach = false, rpc = false, pty = false; - Callback on_stdout = CALLBACK_NONE, on_stderr = CALLBACK_NONE, - on_exit = CALLBACK_NONE; + bool detach = false; + bool rpc = false; + bool pty = false; + Callback on_stdout = CALLBACK_NONE; + Callback on_stderr = CALLBACK_NONE; + Callback on_exit = CALLBACK_NONE; char *cwd = NULL; if (argvars[1].v_type == VAR_DICT) { job_opts = argvars[1].vval.v_dict; - detach = get_dict_number(job_opts, "detach") != 0; - rpc = get_dict_number(job_opts, "rpc") != 0; - pty = get_dict_number(job_opts, "pty") != 0; + detach = tv_dict_get_number(job_opts, "detach") != 0; + rpc = tv_dict_get_number(job_opts, "rpc") != 0; + pty = tv_dict_get_number(job_opts, "pty") != 0; if (pty && rpc) { EMSG2(_(e_invarg2), "job cannot have both 'pty' and 'rpc' options set"); shell_free_argv(argv); return; } - char *new_cwd = (char *)get_dict_string(job_opts, "cwd", false); + char *new_cwd = tv_dict_get_string(job_opts, "cwd", false); if (new_cwd && strlen(new_cwd) > 0) { cwd = new_cwd; // The new cwd must be a directory. @@ -12268,15 +11684,15 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) Process *proc = (Process *)&data->proc; if (pty) { - uint16_t width = get_dict_number(job_opts, "width"); + uint16_t width = (uint16_t)tv_dict_get_number(job_opts, "width"); if (width > 0) { data->proc.pty.width = width; } - uint16_t height = get_dict_number(job_opts, "height"); + uint16_t height = (uint16_t)tv_dict_get_number(job_opts, "height"); if (height > 0) { data->proc.pty.height = height; } - char *term = (char *)get_dict_string(job_opts, "TERM", true); + char *term = tv_dict_get_string(job_opts, "TERM", true); if (term) { data->proc.pty.term_name = term; } @@ -12537,7 +11953,7 @@ static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = tv_list_len(argvars[0].vval.v_list); break; case VAR_DICT: - rettv->vval.v_number = dict_len(argvars[0].vval.v_dict); + rettv->vval.v_number = tv_dict_len(argvars[0].vval.v_dict); break; case VAR_UNKNOWN: case VAR_SPECIAL: @@ -12714,7 +12130,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) rettv->vval.v_string = str2special_save(rhs, FALSE); } else { - rettv_dict_alloc(rettv); + tv_dict_alloc_ret(rettv); if (rhs != NULL) { // Return a dictionary. char_u *lhs = str2special_save(mp->m_keys, true); @@ -12980,10 +12396,10 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_dictreq)); return; } - if (dict_find(argvars[4].vval.v_dict, - (char_u *)"conceal", -1) != NULL) { - conceal_char = get_dict_string(argvars[4].vval.v_dict, - "conceal", false); + dictitem_T *di; + if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) + != NULL) { + conceal_char = get_tv_string(&di->di_tv); } } } @@ -12996,8 +12412,9 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - rettv->vval.v_number = match_add(curwin, grp, pat, prio, id, NULL, - conceal_char); + rettv->vval.v_number = match_add(curwin, (const char *)grp, + (const char *)pat, prio, id, + NULL, (const char *)conceal_char); } static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) @@ -13005,8 +12422,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = -1; char_u buf[NUMBUFLEN]; - char_u *group; - group = get_tv_string_buf_chk(&argvars[0], buf); + const char_u *const group = get_tv_string_buf_chk(&argvars[0], buf); if (group == NULL) { return; } @@ -13036,10 +12452,10 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_dictreq)); return; } - if (dict_find(argvars[4].vval.v_dict, - (char_u *)"conceal", -1) != NULL) { - conceal_char = get_dict_string(argvars[4].vval.v_dict, - "conceal", false); + dictitem_T *di; + if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) + != NULL) { + conceal_char = get_tv_string(&di->di_tv); } } } @@ -13054,8 +12470,8 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l, - conceal_char); + rettv->vval.v_number = match_add(curwin, (const char *)group, NULL, prio, id, + l, (const char *)conceal_char); } /* @@ -13156,8 +12572,8 @@ static void max_min(typval_T *argvars, typval_T *rettv, int domax) todo = (int)d->dv_hashtab.ht_used; for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { if (!HASHITEM_EMPTY(hi)) { - --todo; - i = get_tv_number_chk(&HI2DI(hi)->di_tv, &error); + todo--; + i = get_tv_number_chk(&TV_DICT_HI2DI(hi)->di_tv, &error); if (first) { n = i; first = FALSE; @@ -13845,7 +13261,6 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) listitem_T *li; long idx; long end; - char_u *key; dict_T *d; dictitem_T *di; const char *const arg_errmsg = _("remove() argument"); @@ -13856,18 +13271,18 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_toomanyarg), "remove()"); } else if ((d = argvars[0].vval.v_dict) != NULL && !tv_check_lock(d->dv_lock, arg_errmsg, arg_errmsg_len)) { - key = get_tv_string_chk(&argvars[1]); + const char *key = (const char *)get_tv_string_chk(&argvars[1]); if (key != NULL) { - di = dict_find(d, key, -1); + di = tv_dict_find(d, key, -1); if (di == NULL) { EMSG2(_(e_dictkey), key); } else if (!var_check_fixed(di->di_flags, arg_errmsg, arg_errmsg_len) && !var_check_ro(di->di_flags, arg_errmsg, arg_errmsg_len)) { *rettv = di->di_tv; - init_tv(&di->di_tv); - dictitem_remove(d, di); - if (is_watched(d)) { - dictwatcher_notify(d, (char *)key, NULL, rettv); + di->di_tv = TV_INITIAL_VALUE; + tv_dict_item_remove(d, di); + if (tv_dict_is_watched(d)) { + tv_dict_watcher_notify(d, key, NULL, rettv); } } } @@ -14967,7 +14382,6 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (buf != NULL && varname != NULL && varp != NULL) { if (*varname == '&') { long numval; - char_u *strval; bool error = false; aco_save_T aco; @@ -14976,9 +14390,9 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) varname++; numval = get_tv_number_chk(varp, &error); - strval = get_tv_string_buf_chk(varp, nbuf); + char *const strval = (char *)get_tv_string_buf_chk(varp, nbuf); if (!error && strval != NULL) { - set_option_value((char_u *)varname, numval, strval, OPT_LOCAL); + set_option_value(varname, numval, strval, OPT_LOCAL); } // reset notion of buffer @@ -14987,7 +14401,7 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) buf_T *save_curbuf = curbuf; const size_t varname_len = STRLEN(varname); - char_u *const bufvarname = xmalloc(STRLEN(varname) + 3); + char *const bufvarname = xmalloc(varname_len + 3); curbuf = buf; memcpy(bufvarname, "b:", 2); memcpy(bufvarname + 2, varname, varname_len + 1); @@ -15002,7 +14416,6 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) { dict_T *d; dictitem_T *di; - char_u *csearch; if (argvars[0].v_type != VAR_DICT) { EMSG(_(e_dictreq)); @@ -15010,7 +14423,7 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if ((d = argvars[0].vval.v_dict) != NULL) { - csearch = get_dict_string(d, "char", false); + char_u *const csearch = (char_u *)tv_dict_get_string(d, "char", false); if (csearch != NULL) { if (enc_utf8) { int pcc[MAX_MCO]; @@ -15022,13 +14435,15 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) csearch, MB_PTR2LEN(csearch)); } - di = dict_find(d, (char_u *)"forward", -1); - if (di != NULL) + di = tv_dict_find(d, S_LEN("forward")); + if (di != NULL) { set_csearch_direction(get_tv_number(&di->di_tv) ? FORWARD : BACKWARD); + } - di = dict_find(d, (char_u *)"until", -1); - if (di != NULL) + di = tv_dict_find(d, S_LEN("until")); + if (di != NULL) { set_csearch_until(!!get_tv_number(&di->di_tv)); + } } } @@ -15252,11 +14667,11 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) 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)) { + if (!(tv_dict_find(d, S_LEN("group")) != NULL + && (tv_dict_find(d, S_LEN("pattern")) != NULL + || tv_dict_find(d, S_LEN("pos1")) != NULL) + && tv_dict_find(d, S_LEN("priority")) != NULL + && tv_dict_find(d, S_LEN("id")) != NULL)) { EMSG(_(e_invarg)); return; } @@ -15267,11 +14682,10 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) li = l->lv_first; while (li != NULL) { int i = 0; - char_u buf[5]; - dictitem_T *di; d = li->li_tv.vval.v_dict; - if (dict_find(d, (char_u *)"pattern", -1) == NULL) { + dictitem_T *const di = tv_dict_find(d, S_LEN("pattern")); + if (di == NULL) { if (s == NULL) { s = tv_list_alloc(); if (s == NULL) { @@ -15280,14 +14694,16 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) } // match from matchaddpos() - for (i = 1; i < 9; ++i) { - snprintf((char *)buf, sizeof(buf), (char *)"pos%d", i); - if ((di = dict_find(d, (char_u *)buf, -1)) != NULL) { - if (di->di_tv.v_type != VAR_LIST) { + for (i = 1; i < 9; i++) { + char buf[5]; + snprintf(buf, sizeof(buf), "pos%d", i); + dictitem_T *const pos_di = tv_dict_find(d, buf, -1); + if (pos_di != NULL) { + if (pos_di->di_tv.v_type != VAR_LIST) { return; } - tv_list_append_tv(s, &di->di_tv); + tv_list_append_tv(s, &pos_di->di_tv); s->lv_refcount++; } else { break; @@ -15295,23 +14711,31 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - char_u *group = get_dict_string(d, "group", true); - int priority = get_dict_number(d, "priority"); - int id = get_dict_number(d, "id"); - char_u *conceal = dict_find(d, (char_u *)"conceal", -1) != NULL - ? get_dict_string(d, "conceal", true) - : NULL; + // Note: there are three number buffers involved: + // - group_buf below. + // - numbuf in tv_dict_get_string(). + // - mybuf in get_tv_string(). + // + // If you change this code make sure that buffers will not get + // accidentally reused. + char group_buf[NUMBUFLEN]; + const char *const group = tv_dict_get_string_buf(d, "group", group_buf); + const int priority = (int)tv_dict_get_number(d, "priority"); + const int id = (int)tv_dict_get_number(d, "id"); + dictitem_T *const conceal_di = tv_dict_find(d, S_LEN("conceal")); + const char *const conceal = (conceal_di != NULL + ? (const char *)get_tv_string( + &conceal_di->di_tv) + : NULL); if (i == 0) { match_add(curwin, group, - get_dict_string(d, "pattern", false), + tv_dict_get_string(d, "pattern", false), priority, id, NULL, conceal); } else { match_add(curwin, group, NULL, priority, id, s, conceal); tv_list_unref(s); s = NULL; } - xfree(group); - xfree(conceal); li = li->li_next; } rettv->vval.v_number = 0; @@ -15471,35 +14895,31 @@ free_lstval: */ static void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - tabpage_T *save_curtab; - tabpage_T *tp; - char_u *varname, *tabvarname; - typval_T *varp; - rettv->vval.v_number = 0; - if (check_restricted() || check_secure()) + if (check_restricted() || check_secure()) { return; + } - tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); - varname = get_tv_string_chk(&argvars[1]); - varp = &argvars[2]; + tabpage_T *const tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); + const char *const varname = (const char *)get_tv_string_chk(&argvars[1]); + typval_T *const varp = &argvars[2]; - if (varname != NULL && varp != NULL - && tp != NULL - ) { - save_curtab = curtab; - goto_tabpage_tp(tp, FALSE, FALSE); + if (varname != NULL && varp != NULL && tp != NULL) { + tabpage_T *const save_curtab = curtab; + goto_tabpage_tp(tp, false, false); - tabvarname = xmalloc(STRLEN(varname) + 3); - STRCPY(tabvarname, "t:"); - STRCPY(tabvarname + 2, varname); - set_var(tabvarname, varp, TRUE); + const size_t varname_len = strlen(varname); + char *const tabvarname = xmalloc(varname_len + 3); + memcpy(tabvarname, "t:", 2); + memcpy(tabvarname + 2, varname, varname_len + 1); + set_var(tabvarname, varp, true); xfree(tabvarname); - /* Restore current tabpage */ - if (valid_tabpage(save_curtab)) - goto_tabpage_tp(save_curtab, FALSE, FALSE); + // Restore current tabpage. + if (valid_tabpage(save_curtab)) { + goto_tabpage_tp(save_curtab, false, false); + } } } @@ -15525,44 +14945,42 @@ static void f_setwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void setwinvar(typval_T *argvars, typval_T *rettv, int off) { - win_T *win; - win_T *save_curwin; - tabpage_T *save_curtab; - char_u *varname, *winvarname; - typval_T *varp; - char_u nbuf[NUMBUFLEN]; - tabpage_T *tp = NULL; - - if (check_restricted() || check_secure()) + if (check_restricted() || check_secure()) { return; + } - if (off == 1) + tabpage_T *tp = NULL; + if (off == 1) { tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); - else + } else { tp = curtab; - win = find_win_by_nr(&argvars[off], tp); - varname = get_tv_string_chk(&argvars[off + 1]); - varp = &argvars[off + 2]; + } + win_T *const win = find_win_by_nr(&argvars[off], tp); + const char *varname = (const char *)get_tv_string_chk(&argvars[off + 1]); + typval_T *varp = &argvars[off + 2]; if (win != NULL && varname != NULL && varp != NULL) { + win_T *save_curwin; + tabpage_T *save_curtab; bool need_switch_win = tp != curtab || win != curwin; if (!need_switch_win || switch_win(&save_curwin, &save_curtab, win, tp, true) == OK) { if (*varname == '&') { long numval; - char_u *strval; bool error = false; - ++varname; + varname++; numval = get_tv_number_chk(varp, &error); - strval = get_tv_string_buf_chk(varp, nbuf); + char_u nbuf[NUMBUFLEN]; + char *const strval = (char *)get_tv_string_buf_chk(varp, nbuf); if (!error && strval != NULL) { set_option_value(varname, numval, strval, OPT_LOCAL); } } else { - winvarname = xmalloc(STRLEN(varname) + 3); - STRCPY(winvarname, "w:"); - STRCPY(winvarname + 2, varname); + const size_t varname_len = strlen(varname); + char *const winvarname = xmalloc(varname_len + 3); + memcpy(winvarname, "w:", 2); + memcpy(winvarname + 2, varname, varname_len + 1); set_var(winvarname, varp, true); xfree(winvarname); } @@ -17086,15 +16504,15 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) Callback on_stdout = CALLBACK_NONE, on_stderr = CALLBACK_NONE, on_exit = CALLBACK_NONE; dict_T *job_opts = NULL; - char *cwd = "."; + const char *cwd = "."; if (argvars[1].v_type == VAR_DICT) { job_opts = argvars[1].vval.v_dict; - char *new_cwd = (char *)get_dict_string(job_opts, "cwd", false); - if (new_cwd && strlen(new_cwd) > 0) { + const char *const new_cwd = tv_dict_get_string(job_opts, "cwd", false); + if (new_cwd && *new_cwd != NUL) { cwd = new_cwd; // The new cwd must be a directory. - if (!os_isdir((char_u *)cwd)) { + if (!os_isdir((const char_u *)cwd)) { EMSG2(_(e_invarg2), "expected valid directory"); shell_free_argv(argv); return; @@ -17131,7 +16549,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) // at this point the buffer has no terminal instance associated yet, so unset // the 'swapfile' option to ensure no swap file will be created curbuf->b_p_swf = false; - (void)setfname(curbuf, (uint8_t *)buf, NULL, true); + (void)setfname(curbuf, (char_u *)buf, NULL, true); // Save the job id and pid in b:terminal_job_{id,pid} Error err; dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"), @@ -17177,23 +16595,25 @@ static bool callback_from_typval(Callback *callback, typval_T *arg) /// Unref/free callback -static void callback_free(Callback *callback) +void callback_free(Callback *const callback) + FUNC_ATTR_NONNULL_ALL { switch (callback->type) { - case kCallbackFuncref: + case kCallbackFuncref: { func_unref(callback->data.funcref); xfree(callback->data.funcref); break; - - case kCallbackPartial: + } + case kCallbackPartial: { partial_unref(callback->data.partial); break; - - case kCallbackNone: + } + case kCallbackNone: { break; - - default: + } + default: { abort(); + } } callback->type = kCallbackNone; } @@ -17220,8 +16640,9 @@ static bool callback_equal(Callback *cb1, Callback *cb2) } } -static bool callback_call(Callback *callback, int argcount_in, - typval_T *argvars_in, typval_T *rettv) +bool callback_call(Callback *const callback, const int argcount_in, + typval_T *const argvars_in, typval_T *const rettv) + FUNC_ATTR_NONNULL_ALL { partial_T *partial; char_u *name; @@ -17290,8 +16711,9 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_invarg2), get_tv_string(&argvars[2])); return; } - if (dict_find(dict, (char_u *)"repeat", -1) != NULL) { - repeat = get_dict_number(dict, "repeat"); + dictitem_T *const di = tv_dict_find(dict, S_LEN("repeat")); + if (di != NULL) { + repeat = get_tv_number(&di->di_tv); if (repeat == 0) { repeat = 1; } @@ -17353,13 +16775,11 @@ static void timer_due_cb(TimeWatcher *tw, void *data) timer_stop(timer); } - typval_T argv[2]; - init_tv(argv); + typval_T argv[2] = { TV_INITIAL_VALUE, TV_INITIAL_VALUE }; argv[0].v_type = VAR_NUMBER; argv[0].vval.v_number = timer->timer_id; - typval_T rettv; + typval_T rettv = TV_INITIAL_VALUE; - init_tv(&rettv); callback_call(&timer->callback, 1, argv, &rettv); tv_clear(&rettv); @@ -17614,7 +17034,7 @@ static void f_undofile(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_dict_alloc(rettv); + tv_dict_alloc_ret(rettv); dict_T *dict = rettv->vval.v_dict; list_T *list; @@ -17802,36 +17222,37 @@ static void f_winrestcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_winrestview(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - dict_T *dict; + dict_T *dict; if (argvars[0].v_type != VAR_DICT - || (dict = argvars[0].vval.v_dict) == NULL) - EMSG(_(e_invarg)); - else { - if (dict_find(dict, (char_u *)"lnum", -1) != NULL) { - curwin->w_cursor.lnum = get_dict_number(dict, "lnum"); + || (dict = argvars[0].vval.v_dict) == NULL) { + emsgf(_(e_invarg)); + } else { + dictitem_T *di; + if ((di = tv_dict_find(dict, S_LEN("lnum"))) != NULL) { + curwin->w_cursor.lnum = get_tv_number(&di->di_tv); } - if (dict_find(dict, (char_u *)"col", -1) != NULL) { - curwin->w_cursor.col = get_dict_number(dict, "col"); + if ((di = tv_dict_find(dict, S_LEN("col"))) != NULL) { + curwin->w_cursor.col = get_tv_number(&di->di_tv); } - if (dict_find(dict, (char_u *)"coladd", -1) != NULL) { - curwin->w_cursor.coladd = get_dict_number(dict, "coladd"); + if ((di = tv_dict_find(dict, S_LEN("coladd"))) != NULL) { + curwin->w_cursor.coladd = get_tv_number(&di->di_tv); } - if (dict_find(dict, (char_u *)"curswant", -1) != NULL) { - curwin->w_curswant = get_dict_number(dict, "curswant"); + if ((di = tv_dict_find(dict, S_LEN("curswant"))) != NULL) { + curwin->w_curswant = get_tv_number(&di->di_tv); curwin->w_set_curswant = false; } - if (dict_find(dict, (char_u *)"topline", -1) != NULL) { - set_topline(curwin, get_dict_number(dict, "topline")); + if ((di = tv_dict_find(dict, S_LEN("topline"))) != NULL) { + set_topline(curwin, get_tv_number(&di->di_tv)); } - if (dict_find(dict, (char_u *)"topfill", -1) != NULL) { - curwin->w_topfill = get_dict_number(dict, "topfill"); + if ((di = tv_dict_find(dict, S_LEN("topfill"))) != NULL) { + curwin->w_topfill = get_tv_number(&di->di_tv); } - if (dict_find(dict, (char_u *)"leftcol", -1) != NULL) { - curwin->w_leftcol = get_dict_number(dict, "leftcol"); + if ((di = tv_dict_find(dict, S_LEN("leftcol"))) != NULL) { + curwin->w_leftcol = get_tv_number(&di->di_tv); } - if (dict_find(dict, (char_u *)"skipcol", -1) != NULL) { - curwin->w_skipcol = get_dict_number(dict, "skipcol"); + if ((di = tv_dict_find(dict, S_LEN("skipcol"))) != NULL) { + curwin->w_skipcol = get_tv_number(&di->di_tv); } check_cursor(); @@ -17854,7 +17275,7 @@ static void f_winsaveview(typval_T *argvars, typval_T *rettv, FunPtr fptr) { dict_T *dict; - rettv_dict_alloc(rettv); + tv_dict_alloc_ret(rettv); dict = rettv->vval.v_dict; dict_add_nr_str(dict, "lnum", (long)curwin->w_cursor.lnum, NULL); @@ -18027,7 +17448,7 @@ static void f_winwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "wordcount()" function static void f_wordcount(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_dict_alloc(rettv); + tv_dict_alloc_ret(rettv); cursor_pos_info(rettv->vval.v_dict); } @@ -18895,10 +18316,10 @@ handle_subscript( } ret = FAIL; } - dict_unref(selfdict); + tv_dict_unref(selfdict); selfdict = NULL; - } else { /* **arg == '[' || **arg == '.' */ - dict_unref(selfdict); + } else { // **arg == '[' || **arg == '.' + tv_dict_unref(selfdict); if (rettv->v_type == VAR_DICT) { selfdict = rettv->vval.v_dict; if (selfdict != NULL) @@ -18919,7 +18340,7 @@ handle_subscript( set_selfdict(rettv, selfdict); } - dict_unref(selfdict); + tv_dict_unref(selfdict); return ret; } @@ -19016,7 +18437,7 @@ void free_tv(typval_T *varp) tv_list_unref(varp->vval.v_list); break; case VAR_DICT: - dict_unref(varp->vval.v_dict); + tv_dict_unref(varp->vval.v_dict); break; case VAR_SPECIAL: case VAR_NUMBER: @@ -19028,15 +18449,6 @@ void free_tv(typval_T *varp) } } -/* - * Set the value of a variable to NULL without freeing items. - */ -static void init_tv(typval_T *varp) -{ - if (varp != NULL) - memset(varp, 0, sizeof(typval_T)); -} - // TODO(ZyX-I): move to eval/typval /// Get the number value of a variable @@ -19057,44 +18469,53 @@ varnumber_T get_tv_number(const typval_T *const varp) varnumber_T get_tv_number_chk(const typval_T *const varp, bool *const denote) { - long n = 0L; + varnumber_T n = 0; switch (varp->v_type) { - case VAR_NUMBER: - return (long)(varp->vval.v_number); - case VAR_FLOAT: - EMSG(_("E805: Using a Float as a Number")); - break; - case VAR_FUNC: - case VAR_PARTIAL: - EMSG(_("E703: Using a Funcref as a Number")); - break; - case VAR_STRING: - if (varp->vval.v_string != NULL) { - vim_str2nr(varp->vval.v_string, NULL, NULL, - STR2NR_ALL, &n, NULL, 0); + case VAR_NUMBER: { + return varp->vval.v_number; } - return n; - case VAR_LIST: - EMSG(_("E745: Using a List as a Number")); - break; - case VAR_DICT: - EMSG(_("E728: Using a Dictionary as a Number")); - break; - case VAR_SPECIAL: - switch (varp->vval.v_special) { - case kSpecialVarTrue: { - return 1; - } - case kSpecialVarFalse: - case kSpecialVarNull: { - return 0; - } + case VAR_FLOAT: { + EMSG(_("E805: Using a Float as a Number")); + break; + } + case VAR_PARTIAL: + case VAR_FUNC: { + EMSG(_("E703: Using a Funcref as a Number")); + break; + } + case VAR_STRING: { + if (varp->vval.v_string != NULL) { + long nr; + vim_str2nr(varp->vval.v_string, NULL, NULL, STR2NR_ALL, &nr, NULL, 0); + n = (varnumber_T)nr; + } + return n; + } + case VAR_LIST: { + EMSG(_("E745: Using a List as a Number")); + break; + } + case VAR_DICT: { + EMSG(_("E728: Using a Dictionary as a Number")); + break; + } + case VAR_SPECIAL: { + switch (varp->vval.v_special) { + case kSpecialVarTrue: { + return 1; + } + case kSpecialVarFalse: + case kSpecialVarNull: { + return 0; + } + } + break; + } + case VAR_UNKNOWN: { + EMSG2(_(e_intern2), "get_tv_number(UNKNOWN)"); + break; } - break; - case VAR_UNKNOWN: - EMSG2(_(e_intern2), "get_tv_number(UNKNOWN)"); - break; } if (denote == NULL) { // useful for values that must be unsigned @@ -19254,16 +18675,17 @@ static dictitem_T *find_var(const char *const name, const size_t name_len, hashtab_T **htp, int no_autoload) { const char *varname; - hashtab_T *ht = find_var_ht(name, name_len, &varname); + hashtab_T *const ht = find_var_ht(name, name_len, &varname); if (htp != NULL) { *htp = ht; } if (ht == NULL) { return NULL; } - dictitem_T *ret = find_var_in_ht(ht, *name, - varname, name_len - (size_t)(varname - name), - no_autoload || htp != NULL); + dictitem_T *const ret = find_var_in_ht(ht, *name, + varname, + name_len - (size_t)(varname - name), + no_autoload || htp != NULL); if (ret != NULL) { return ret; } @@ -19327,7 +18749,7 @@ static dictitem_T *find_var_in_ht(hashtab_T *const ht, return NULL; } } - return HI2DI(hi); + return TV_DICT_HI2DI(hi); } // Get function call environment based on backtrace debug level @@ -19528,7 +18950,7 @@ void unref_var_dict(dict_T *dict) /* Now the dict needs to be freed if no one else is using it, go back to * normal reference counting. */ dict->dv_refcount -= DO_NOT_FREE_CNT - 1; - dict_unref(dict); + tv_dict_unref(dict); } /* @@ -19559,7 +18981,7 @@ static void vars_clear_ext(hashtab_T *ht, int free_val) // Free the variable. Don't remove it from the hashtab, // ht_array might change then. hash_clear() takes care of it // later. - v = HI2DI(hi); + v = TV_DICT_HI2DI(hi); if (free_val) { tv_clear(&v->di_tv); } @@ -19578,7 +19000,7 @@ static void vars_clear_ext(hashtab_T *ht, int free_val) */ static void delete_var(hashtab_T *ht, hashitem_T *hi) { - dictitem_T *di = HI2DI(hi); + dictitem_T *di = TV_DICT_HI2DI(hi); hash_remove(ht, hi); tv_clear(&di->di_tv); @@ -19637,40 +19059,31 @@ static void list_one_var_a(const char *prefix, const char *name, } } -/* - * Set variable "name" to value in "tv". - * If the variable already exists, the value is updated. - * Otherwise the variable is created. - */ -static void -set_var ( - char_u *name, - typval_T *tv, - int copy /* make copy of value in "tv" */ -) +/// Set variable to the given value +/// +/// If the variable already exists, the value is updated. Otherwise the variable +/// is created. +/// +/// @param[in] name Variable name to set. +/// @param tv Variable value. +/// @param[in] copy True if value in tv is to be copied. +static void set_var(const char *name, typval_T *const tv, const bool copy) + FUNC_ATTR_NONNULL_ALL { dictitem_T *v; hashtab_T *ht; - typval_T oldtv; dict_T *dict; - const size_t name_len = STRLEN(name); - char_u *varname; - ht = find_var_ht_dict((const char *)name, name_len, (const char **)&varname, - &dict); - bool watched = is_watched(dict); - - if (watched) { - init_tv(&oldtv); - } + const size_t name_len = strlen(name); + const char *varname; + ht = find_var_ht_dict(name, name_len, &varname, &dict); + const bool watched = tv_dict_is_watched(dict); if (ht == NULL || *varname == NUL) { EMSG2(_(e_illvar), name); return; } - v = find_var_in_ht(ht, 0, - (const char *)varname, name_len - (size_t)(varname - name), - true); + v = find_var_in_ht(ht, 0, varname, name_len - (size_t)(varname - name), true); // Search in parent scope which is possible to reference from lambda if (v == NULL) { @@ -19678,10 +19091,11 @@ set_var ( } if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL) - && var_check_func_name(name, v == NULL)) { + && !var_check_func_name(name, v == NULL)) { return; } + typval_T oldtv = TV_INITIAL_VALUE; if (v != NULL) { // existing variable, need to clear the value if (var_check_ro(v->di_flags, (const char *)name, name_len) @@ -19704,9 +19118,9 @@ set_var ( return; } else if (v->di_tv.v_type == VAR_NUMBER) { v->di_tv.vval.v_number = get_tv_number(tv); - if (STRCMP(varname, "searchforward") == 0) + if (strcmp(varname, "searchforward") == 0) { set_search_direction(v->di_tv.vval.v_number ? '/' : '?'); - else if (STRCMP(varname, "hlsearch") == 0) { + } else if (strcmp(varname, "hlsearch") == 0) { no_hlsearch = !v->di_tv.vval.v_number; redraw_all_later(SOME_VALID); } @@ -19727,13 +19141,14 @@ set_var ( return; } - /* Make sure the variable name is valid. */ - if (!valid_varname(varname)) + // Make sure the variable name is valid. + if (!valid_varname(varname)) { return; + } - v = xmalloc(sizeof(dictitem_T) + STRLEN(varname)); + v = xmalloc(sizeof(dictitem_T) + strlen(varname)); STRCPY(v->di_key, varname); - if (hash_add(ht, DI2HIKEY(v)) == FAIL) { + if (tv_dict_add(dict, v) == FAIL) { xfree(v); return; } @@ -19745,14 +19160,14 @@ set_var ( } else { v->di_tv = *tv; v->di_tv.v_lock = 0; - init_tv(tv); + tv_init(tv); } if (watched) { if (oldtv.v_type == VAR_UNKNOWN) { - dictwatcher_notify(dict, (char *)v->di_key, &v->di_tv, NULL); + tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, NULL); } else { - dictwatcher_notify(dict, (char *)v->di_key, &v->di_tv, &oldtv); + tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, &oldtv); tv_clear(&oldtv); } } @@ -19768,8 +19183,8 @@ set_var ( /// /// @return True if variable is read-only: either always or in sandbox when /// sandbox is enabled, false otherwise. -static bool var_check_ro(const int flags, const char *const name, - const size_t name_len) +bool var_check_ro(const int flags, const char *const name, + const size_t name_len) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { if (flags & DI_FLAGS_RO) { @@ -19804,22 +19219,24 @@ static bool var_check_fixed(const int flags, const char *const name, return false; } -/* - * Check if a funcref is assigned to a valid variable name. - * Return TRUE and give an error if not. - */ -static int -var_check_func_name ( - char_u *name, /* points to start of variable name */ - int new_var /* TRUE when creating the variable */ -) +// TODO(ZyX-I): move to eval/expressions + +/// Check if name is a valid name to assign funcref to +/// +/// @param[in] name Possible function/funcref name. +/// @param[in] new_var True if it is a name for a variable. +/// +/// @return false in case of error, true in case of success. Also gives an +/// error message if appropriate. +bool var_check_func_name(const char *const name, const bool new_var) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { // Allow for w: b: s: and t:. if (!(vim_strchr((char_u *)"wbst", name[0]) != NULL && name[1] == ':') && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') ? name[2] : name[0])) { EMSG2(_("E704: Funcref variable name must start with a capital: %s"), name); - return TRUE; + return false; } // Don't allow hiding a function. When "v" is not NULL we might be // assigning another function to the same var, the type is checked @@ -19827,63 +19244,30 @@ var_check_func_name ( if (new_var && function_exists((const char *)name, false)) { EMSG2(_("E705: Variable name conflicts with existing function: %s"), name); - return true; + return false; } - return false; + return true; } -/* - * Check if a variable name is valid. - * Return FALSE and give an error if not. - */ -static int valid_varname(char_u *varname) -{ - char_u *p; +// TODO(ZyX-I): move to eval/expressions - for (p = varname; *p != NUL; ++p) - if (!eval_isnamec1(*p) && (p == varname || !ascii_isdigit(*p)) +/// Check if a variable name is valid +/// +/// @param[in] varname Variable name to check. +/// +/// @return false when variable name is not valid, true when it is. Also gives +/// an error message if appropriate. +bool valid_varname(const char *varname) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + for (const char *p = varname; *p != NUL; p++) { + if (!eval_isnamec1((int)(uint8_t)(*p)) + && (p == varname || !ascii_isdigit(*p)) && *p != AUTOLOAD_CHAR) { - EMSG2(_(e_illvar), varname); - return FALSE; - } - return TRUE; -} - -/// Check whether typval is locked -/// -/// Also gives an error message. -/// -/// @param[in] lock Lock status. -/// @param[in] name Value name, for use in error message. -/// @param[in] name_len Value name length. -/// -/// @return True if value is locked. -static bool tv_check_lock(const VarLockStatus lock, - const char *const name, - const size_t name_len) - FUNC_ATTR_WARN_UNUSED_RESULT -{ - const char *error_message = NULL; - switch (lock) { - case VAR_UNLOCKED: { + emsgf(_(e_illvar), varname); return false; } - case VAR_LOCKED: { - error_message = N_("E741: Value is locked: %.*s"); - break; - } - case VAR_FIXED: { - error_message = N_("E742: Cannot change value of %.*s"); - break; - } } - assert(error_message != NULL); - - const char *const unknown_name = _("Unknown"); - - emsgf(_(error_message), (name != NULL ? name_len : strlen(unknown_name)), - (name != NULL ? name : unknown_name)); - return true; } @@ -20016,7 +19400,7 @@ int var_item_copy(const vimconv_T *const conv, to->vval.v_dict = from->vval.v_dict->dv_copydict; ++to->vval.v_dict->dv_refcount; } else { - to->vval.v_dict = dict_copy(conv, from->vval.v_dict, deep, copyID); + to->vval.v_dict = tv_dict_copy(conv, from->vval.v_dict, deep, copyID); } if (to->vval.v_dict == NULL) ret = FAIL; @@ -20730,9 +20114,9 @@ void ex_function(exarg_T *eap) if (fudi.fd_dict != NULL) { if (fudi.fd_di == NULL) { - /* add new dict entry */ - fudi.fd_di = dictitem_alloc(fudi.fd_newkey); - if (dict_add(fudi.fd_dict, fudi.fd_di) == FAIL) { + // Add new dict entry + fudi.fd_di = tv_dict_item_alloc((const char *)fudi.fd_newkey); + if (tv_dict_add(fudi.fd_dict, fudi.fd_di) == FAIL) { xfree(fudi.fd_di); xfree(fp); goto erret; @@ -21002,7 +20386,7 @@ theend: */ static int eval_fname_script(const char *const p) { - // Use mb_stricmp() because in Turkish comparing the "I" may not work with + // Use mb_strnicmp() because in Turkish comparing the "I" may not work with // the standard library function. if (p[0] == '<' && (mb_strnicmp((char_u *)p + 1, (char_u *)"SID>", 4) == 0 @@ -21558,9 +20942,9 @@ void ex_delfunction(exarg_T *eap) } if (fudi.fd_dict != NULL) { - /* Delete the dict item that refers to the function, it will - * invoke func_unref() and possibly delete the function. */ - dictitem_remove(fudi.fd_dict, fudi.fd_di); + // Delete the dict item that refers to the function, it will + // invoke func_unref() and possibly delete the function. + tv_dict_item_remove(fudi.fd_dict, fudi.fd_di); } else { // A normal function (not a numbered function or lambda) has a // refcount of 1 for the entry in the hashtable. When deleting @@ -21675,6 +21059,11 @@ void func_unref(char_u *name) /// Unreference a Function: decrement the reference count and free it when it /// becomes zero. +/// Unreference user function, freeing it if needed +/// +/// Decrements the reference count and frees when it becomes zero. +/// +/// @param fp Function to unreference. void func_ptr_unref(ufunc_T *fp) { if (fp != NULL && --fp->uf_refcount <= 0) { @@ -21712,17 +21101,19 @@ void func_ptr_ref(ufunc_T *fp) } } -/// Call a user function. -static void -call_user_func( - ufunc_T *fp, // pointer to function - int argcount, // nr of args - typval_T *argvars, // arguments - typval_T *rettv, // return value - linenr_T firstline, // first line of range - linenr_T lastline, // last line of range - dict_T *selfdict // Dictionary for "self" -) +/// Call a user function +/// +/// @param fp Function to call. +/// @param[in] argcount Number of arguments. +/// @param argvars Arguments. +/// @param[out] rettv Return value. +/// @param[in] firstline First line of range. +/// @param[in] lastline Last line of range. +/// @param selfdict Dictionary for "self" for dictionary functions. +void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, + typval_T *rettv, linenr_T firstline, linenr_T lastline, + dict_T *selfdict) + FUNC_ATTR_NONNULL_ARG(1, 3, 4) { char_u *save_sourcing_name; linenr_T save_sourcing_lnum; @@ -21796,7 +21187,7 @@ call_user_func( STRCPY(name, "self"); #endif v->di_flags = DI_FLAGS_RO + DI_FLAGS_FIX; - hash_add(&fc->l_vars.dv_hashtab, DI2HIKEY(v)); + tv_dict_add(&fc->l_vars, v); v->di_tv.v_type = VAR_DICT; v->di_tv.v_lock = 0; v->di_tv.vval.v_dict = selfdict; @@ -21819,7 +21210,7 @@ call_user_func( STRCPY(name, "000"); #endif v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v)); + tv_dict_add(&fc->l_avars, v); v->di_tv.v_type = VAR_LIST; v->di_tv.v_lock = VAR_FIXED; v->di_tv.vval.v_list = &fc->l_varlist; @@ -21867,9 +21258,9 @@ call_user_func( // Named arguments can be accessed without the "a:" prefix in lambda // expressions. Add to the l: dict. copy_tv(&v->di_tv, &v->di_tv); - hash_add(&fc->l_vars.dv_hashtab, DI2HIKEY(v)); + tv_dict_add(&fc->l_vars, v); } else { - hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v)); + tv_dict_add(&fc->l_avars, v); } if (ai >= 0 && ai < MAX_FUNC_ARGS) { @@ -22065,29 +21456,22 @@ call_user_func( && fc->fc_refcount <= 0) { free_funccal(fc, false); } else { - hashitem_T *hi; - listitem_T *li; - int todo; - // "fc" is still in use. This can happen when returning "a:000", // assigning "l:" to a global variable or defining a closure. // Link "fc" in the list for garbage collection later. fc->caller = previous_funccal; previous_funccal = fc; - /* Make a copy of the a: variables, since we didn't do that above. */ - todo = (int)fc->l_avars.dv_hashtab.ht_used; - for (hi = fc->l_avars.dv_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - v = HI2DI(hi); - copy_tv(&v->di_tv, &v->di_tv); - } - } + // Make a copy of the a: variables, since we didn't do that above. + TV_DICT_ITER(&fc->l_avars, di, { + copy_tv(&di->di_tv, &di->di_tv); + }); - /* Make a copy of the a:000 items, since we didn't do that above. */ - for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) + // Make a copy of the a:000 items, since we didn't do that above. + for (listitem_T *li = fc->l_varlist.lv_first; li != NULL; + li = li->li_next) { copy_tv(&li->li_tv, &li->li_tv); + } } if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) { @@ -22194,7 +21578,7 @@ static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, varnumber_T nr) STRCPY(v->di_key, name); #endif v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - hash_add(&dp->dv_hashtab, DI2HIKEY(v)); + tv_dict_add(dp, v); v->di_tv.v_type = VAR_NUMBER; v->di_tv.v_lock = VAR_FIXED; v->di_tv.vval.v_number = nr; @@ -22590,7 +21974,7 @@ const void *var_shada_iter(const void *const iter, const char **const name, hi = globvarht.ht_array; while ((size_t) (hi - hifirst) < hinum && (HASHITEM_EMPTY(hi) - || var_flavour(HI2DI(hi)->di_key) != VAR_FLAVOUR_SHADA)) { + || var_flavour(hi->hi_key) != VAR_FLAVOUR_SHADA)) { hi++; } if ((size_t) (hi - hifirst) == hinum) { @@ -22599,11 +21983,10 @@ const void *var_shada_iter(const void *const iter, const char **const name, } else { hi = (const hashitem_T *) iter; } - *name = (char *) HI2DI(hi)->di_key; - copy_tv(&(HI2DI(hi)->di_tv), rettv); - while ((size_t) (++hi - hifirst) < hinum) { - if (!HASHITEM_EMPTY(hi) - && var_flavour(HI2DI(hi)->di_key) == VAR_FLAVOUR_SHADA) { + *name = (char *)TV_DICT_HI2DI(hi)->di_key; + copy_tv(&TV_DICT_HI2DI(hi)->di_tv, rettv); + while ((size_t)(++hi - hifirst) < hinum) { + if (!HASHITEM_EMPTY(hi) && var_flavour(hi->hi_key) == VAR_FLAVOUR_SHADA) { return hi; } } @@ -22614,62 +21997,54 @@ void var_set_global(const char *const name, typval_T vartv) { funccall_T *const saved_current_funccal = current_funccal; current_funccal = NULL; - set_var((char_u *) name, &vartv, false); + set_var(name, &vartv, false); current_funccal = saved_current_funccal; } int store_session_globals(FILE *fd) { - hashitem_T *hi; - dictitem_T *this_var; - int todo; - char_u *p, *t; - - todo = (int)globvarht.ht_used; - for (hi = globvarht.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - this_var = HI2DI(hi); - if ((this_var->di_tv.v_type == VAR_NUMBER - || this_var->di_tv.v_type == VAR_STRING) - && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { - /* Escape special characters with a backslash. Turn a LF and - * CR into \n and \r. */ - p = vim_strsave_escaped(get_tv_string(&this_var->di_tv), - (char_u *)"\\\"\n\r"); - for (t = p; *t != NUL; ++t) - if (*t == '\n') - *t = 'n'; - else if (*t == '\r') - *t = 'r'; - if ((fprintf(fd, "let %s = %c%s%c", - this_var->di_key, - (this_var->di_tv.v_type == VAR_STRING) ? '"' - : ' ', - p, - (this_var->di_tv.v_type == VAR_STRING) ? '"' - : ' ') < 0) - || put_eol(fd) == FAIL) { - xfree(p); - return FAIL; + TV_DICT_ITER(&globvardict, this_var, { + if ((this_var->di_tv.v_type == VAR_NUMBER + || this_var->di_tv.v_type == VAR_STRING) + && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { + // Escape special characters with a backslash. Turn a LF and + // CR into \n and \r. + char_u *const p = vim_strsave_escaped(get_tv_string(&this_var->di_tv), + (char_u *)"\\\"\n\r"); + for (char_u *t = p; *t != NUL; t++) { + if (*t == '\n') { + *t = 'n'; + } else if (*t == '\r') { + *t = 'r'; } + } + if ((fprintf(fd, "let %s = %c%s%c", + this_var->di_key, + ((this_var->di_tv.v_type == VAR_STRING) ? '"' + : ' '), + p, + ((this_var->di_tv.v_type == VAR_STRING) ? '"' + : ' ')) < 0) + || put_eol(fd) == FAIL) { xfree(p); - } else if (this_var->di_tv.v_type == VAR_FLOAT - && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { - float_T f = this_var->di_tv.vval.v_float; - int sign = ' '; + return FAIL; + } + xfree(p); + } else if (this_var->di_tv.v_type == VAR_FLOAT + && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { + float_T f = this_var->di_tv.vval.v_float; + int sign = ' '; - if (f < 0) { - f = -f; - sign = '-'; - } - if ((fprintf(fd, "let %s = %c%f", - this_var->di_key, sign, f) < 0) - || put_eol(fd) == FAIL) - return FAIL; + if (f < 0) { + f = -f; + sign = '-'; + } + if ((fprintf(fd, "let %s = %c%f", this_var->di_key, sign, f) < 0) + || put_eol(fd) == FAIL) { + return FAIL; } } - } + }); return OK; } @@ -23094,7 +22469,7 @@ static inline TerminalJobData *common_job_init(char **argv, bool pty, bool rpc, bool detach, - char *cwd) + const char *cwd) { TerminalJobData *data = xcalloc(1, sizeof(TerminalJobData)); data->stopped = false; @@ -23384,8 +22759,7 @@ static void on_job_event(JobEvent *ev) argv[2].v_lock = 0; argv[2].vval.v_string = (uint8_t *)ev->type; - typval_T rettv; - init_tv(&rettv); + typval_T rettv = TV_INITIAL_VALUE; callback_call(ev->callback, 3, argv, &rettv); tv_clear(&rettv); } @@ -23499,91 +22873,3 @@ bool eval_has_provider(char *name) return false; } - -// Compute the `DictWatcher` address from a QUEUE node. This only exists for -// .asan-blacklist (ASAN doesn't handle QUEUE_DATA pointer arithmetic). -static DictWatcher *dictwatcher_node_data(QUEUE *q) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET -{ - return QUEUE_DATA(q, DictWatcher, node); -} - -// Send a change notification to all `dict` watchers that match `key`. -static void dictwatcher_notify(dict_T *dict, const char *key, typval_T *newtv, - typval_T *oldtv) - FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(2) -{ - typval_T argv[4]; - for (size_t i = 0; i < ARRAY_SIZE(argv); i++) { - init_tv(argv + i); - } - - argv[0].v_type = VAR_DICT; - argv[0].vval.v_dict = dict; - argv[1].v_type = VAR_STRING; - argv[1].vval.v_string = (char_u *)xstrdup(key); - argv[2].v_type = VAR_DICT; - argv[2].vval.v_dict = dict_alloc(); - argv[2].vval.v_dict->dv_refcount++; - - if (newtv) { - dictitem_T *v = dictitem_alloc((char_u *)"new"); - copy_tv(newtv, &v->di_tv); - dict_add(argv[2].vval.v_dict, v); - } - - if (oldtv) { - dictitem_T *v = dictitem_alloc((char_u *)"old"); - copy_tv(oldtv, &v->di_tv); - dict_add(argv[2].vval.v_dict, v); - } - - typval_T rettv; - - QUEUE *w; - QUEUE_FOREACH(w, &dict->watchers) { - DictWatcher *watcher = dictwatcher_node_data(w); - if (!watcher->busy && dictwatcher_matches(watcher, key)) { - init_tv(&rettv); - watcher->busy = true; - callback_call(&watcher->callback, 3, argv, &rettv); - watcher->busy = false; - tv_clear(&rettv); - } - } - - for (size_t i = 1; i < ARRAY_SIZE(argv); i++) { - tv_clear(argv + i); - } -} - -// Test if `key` matches with with `watcher->key_pattern` -static bool dictwatcher_matches(DictWatcher *watcher, const char *key) - FUNC_ATTR_NONNULL_ALL -{ - // For now only allow very simple globbing in key patterns: a '*' at the end - // of the string means it should match everything up to the '*' instead of the - // whole string. - char *nul = strchr(watcher->key_pattern, NUL); - size_t len = nul - watcher->key_pattern; - if (*(nul - 1) == '*') { - return !strncmp(key, watcher->key_pattern, len - 1); - } else { - return !strcmp(key, watcher->key_pattern); - } -} - -// Perform all necessary cleanup for a `DictWatcher` instance. -static void dictwatcher_free(DictWatcher *watcher) - FUNC_ATTR_NONNULL_ALL -{ - callback_free(&watcher->callback); - xfree(watcher->key_pattern); - xfree(watcher); -} - -// Check if `d` has at least one watcher. -static bool is_watched(dict_T *d) -{ - return d && !QUEUE_EMPTY(&d->watchers); -} diff --git a/src/nvim/eval.h b/src/nvim/eval.h index 57fee5c5a2..7f6fd76c46 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -1,21 +1,23 @@ #ifndef NVIM_EVAL_H #define NVIM_EVAL_H -#include "nvim/profile.h" #include "nvim/hashtab.h" // For hashtab_T -#include "nvim/garray.h" // For garray_T #include "nvim/buffer_defs.h" // For scid_T #include "nvim/ex_cmds_defs.h" // For exarg_T +#include "nvim/eval/typval.h" +#include "nvim/profile.h" +#include "nvim/garray.h" #define COPYID_INC 2 #define COPYID_MASK (~0x1) // All user-defined functions are found in this hashtable. extern hashtab_T func_hashtab; + // From user function to hashitem and back. EXTERN ufunc_T dumuf; #define UF2HIKEY(fp) ((fp)->uf_name) -#define HIKEY2UF(p) ((ufunc_T *)(p - (dumuf.uf_name - (char_u *)&dumuf))) +#define HIKEY2UF(p) ((ufunc_T *)(p - offsetof(ufunc_T, uf_name))) #define HI2UF(hi) HIKEY2UF((hi)->hi_key) /// Defines for Vim variables diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 1d30f51f55..3cb68e093b 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -6,6 +6,7 @@ #include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/ascii.h" +#include "nvim/macros.h" #include "nvim/message.h" #include "nvim/charset.h" // vim_str2nr #include "nvim/lib/kvec.h" @@ -51,16 +52,16 @@ static inline void create_special_dict(typval_T *const rettv, typval_T val) FUNC_ATTR_NONNULL_ALL { - dict_T *const dict = dict_alloc(); - dictitem_T *const type_di = dictitem_alloc((char_u *) "_TYPE"); + dict_T *const dict = tv_dict_alloc(); + dictitem_T *const type_di = tv_dict_item_alloc_len(S_LEN("_TYPE")); type_di->di_tv.v_type = VAR_LIST; type_di->di_tv.v_lock = VAR_UNLOCKED; type_di->di_tv.vval.v_list = (list_T *) eval_msgpack_type_lists[type]; type_di->di_tv.vval.v_list->lv_refcount++; - dict_add(dict, type_di); - dictitem_T *const val_di = dictitem_alloc((char_u *) "_VAL"); + tv_dict_add(dict, type_di); + dictitem_T *const val_di = tv_dict_item_alloc_len(S_LEN("_VAL")); val_di->di_tv = val; - dict_add(dict, val_di); + tv_dict_add(dict, val_di); dict->dv_refcount++; *rettv = (typval_T) { .v_type = VAR_DICT, @@ -138,9 +139,10 @@ static inline int json_decoder_pop(ValuesStackItem obj, assert(!(key.is_special_string || key.val.vval.v_string == NULL || *key.val.vval.v_string == NUL)); - dictitem_T *obj_di = dictitem_alloc(key.val.vval.v_string); + dictitem_T *const obj_di = tv_dict_item_alloc( + (const char *)key.val.vval.v_string); tv_clear(&key.val); - if (dict_add(last_container.container.vval.v_dict, obj_di) + if (tv_dict_add(last_container.container.vval.v_dict, obj_di) == FAIL) { assert(false); } @@ -173,8 +175,8 @@ static inline int json_decoder_pop(ValuesStackItem obj, && (obj.is_special_string || obj.val.vval.v_string == NULL || *obj.val.vval.v_string == NUL - || dict_find(last_container.container.vval.v_dict, - obj.val.vval.v_string, -1))) { + || tv_dict_find(last_container.container.vval.v_dict, + (const char *)obj.val.vval.v_string, -1))) { tv_clear(&obj.val); // Restart @@ -835,7 +837,7 @@ json_decode_string_cycle_start: .vval = { .v_list = val_list }, })); } else { - dict_T *dict = dict_alloc(); + dict_T *dict = tv_dict_alloc(); dict->dv_refcount++; tv = (typval_T) { .v_type = VAR_DICT, @@ -1042,7 +1044,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) goto msgpack_to_vim_generic_map; } } - dict_T *const dict = dict_alloc(); + dict_T *const dict = tv_dict_alloc(); dict->dv_refcount++; *rettv = (typval_T) { .v_type = VAR_DICT, @@ -1055,7 +1057,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) memcpy(&di->di_key[0], mobj.via.map.ptr[i].key.via.str.ptr, mobj.via.map.ptr[i].key.via.str.size); di->di_tv.v_type = VAR_UNKNOWN; - if (dict_add(dict, di) == FAIL) { + if (tv_dict_add(dict, di) == FAIL) { // Duplicate key: fallback to generic map tv_clear(rettv); xfree(di); diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index 1416806ca6..26f9aaa27d 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -744,11 +744,11 @@ bool encode_check_json_key(const typval_T *const tv) } const dictitem_T *type_di; const dictitem_T *val_di; - if ((type_di = dict_find((dict_T *) spdict, (char_u *) "_TYPE", -1)) == NULL + if ((type_di = tv_dict_find(spdict, S_LEN("_TYPE"))) == NULL || type_di->di_tv.v_type != VAR_LIST || (type_di->di_tv.vval.v_list != eval_msgpack_type_lists[kMPString] && type_di->di_tv.vval.v_list != eval_msgpack_type_lists[kMPBinary]) - || (val_di = dict_find((dict_T *) spdict, (char_u *) "_VAL", -1)) == NULL + || (val_di = tv_dict_find(spdict, S_LEN("_VAL"))) == NULL || val_di->di_tv.v_type != VAR_LIST) { return false; } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 7726e106a1..bb7baed7d2 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -2,6 +2,7 @@ #include #include +#include "nvim/lib/queue.h" #include "nvim/eval/typval.h" #include "nvim/eval/gc.h" #include "nvim/eval/executor.h" @@ -12,6 +13,9 @@ #include "nvim/assert.h" #include "nvim/memory.h" #include "nvim/globals.h" +#include "nvim/hashtab.h" +#include "nvim/vim.h" +#include "nvim/ascii.h" // TODO(ZyX-I): Move line_breakcheck out of misc1 #include "nvim/misc1.h" // For line_breakcheck @@ -115,8 +119,7 @@ void tv_list_watch_fix(list_T *const l, const listitem_T *const item) } } -//{{{2 Lists -//{{{3 Alloc/free +//{{{2 Alloc/free /// Allocate an empty list /// @@ -205,7 +208,7 @@ void tv_list_unref(list_T *const l) } } -//{{{3 Add/remove +//{{{2 Add/remove /// Remove items "item" to "item2" from list "l". /// @@ -406,7 +409,7 @@ void tv_list_append_number(list_T *const l, const varnumber_T n) tv_list_append(l, li); } -//{{{3 Operations on the whole list +//{{{2 Operations on the whole list /// Make a copy of list /// @@ -596,6 +599,8 @@ int tv_list_join(garray_T *const gap, list_T *const l, const char *const sep) /// @param[in] l2 Second list to compare. /// @param[in] ic True if case is to be ignored. /// @param[in] recursive True when used recursively. +/// +/// @return True if lists are equal, false otherwise. bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic, const bool recursive) FUNC_ATTR_WARN_UNUSED_RESULT @@ -623,7 +628,7 @@ bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic, return true; } -//{{{3 Indexing/searching +//{{{2 Indexing/searching /// Locate item with a given index in a list and return it /// @@ -761,6 +766,552 @@ long tv_list_idx_of_item(const list_T *const l, const listitem_T *const item) } return idx; } + +//{{{1 Dictionaries +//{{{2 Dictionary watchers + +/// Perform all necessary cleanup for a `DictWatcher` instance +/// +/// @param watcher Watcher to free. +void tv_dict_watcher_free(DictWatcher *watcher) + FUNC_ATTR_NONNULL_ALL +{ + callback_free(&watcher->callback); + xfree(watcher->key_pattern); + xfree(watcher); +} + +/// Test if `key` matches with with `watcher->key_pattern` +/// +/// @param[in] watcher Watcher to check key pattern from. +/// @param[in] key Key to check. +/// +/// @return true if key matches, false otherwise. +static bool tv_dict_watcher_matches(DictWatcher *watcher, const char *const key) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + // For now only allow very simple globbing in key patterns: a '*' at the end + // of the string means it should match everything up to the '*' instead of the + // whole string. + const size_t len = strlen(watcher->key_pattern); + if (watcher->key_pattern[len - 1] == '*') { + return strncmp(key, watcher->key_pattern, len - 1) == 0; + } else { + return strcmp(key, watcher->key_pattern) == 0; + } +} + +/// Send a change notification to all dictionary watchers that match given key +/// +/// @param[in] dict Dictionary which was modified. +/// @param[in] key Key which was modified. +/// @param[in] newtv New key value. +/// @param[in] oldtv Old key value. +void tv_dict_watcher_notify(dict_T *const dict, const char *const key, + typval_T *const newtv, typval_T *const oldtv) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + typval_T argv[3]; + + argv[0].v_type = VAR_DICT; + argv[0].v_lock = VAR_UNLOCKED; + argv[0].vval.v_dict = dict; + argv[1].v_type = VAR_STRING; + argv[1].v_lock = VAR_UNLOCKED; + argv[1].vval.v_string = (char_u *)xstrdup(key); + argv[2].v_type = VAR_DICT; + argv[2].v_lock = VAR_UNLOCKED; + argv[2].vval.v_dict = tv_dict_alloc(); + argv[2].vval.v_dict->dv_refcount++; + + if (newtv) { + dictitem_T *const v = tv_dict_item_alloc_len(S_LEN("new")); + copy_tv(newtv, &v->di_tv); + tv_dict_add(argv[2].vval.v_dict, v); + } + + if (oldtv) { + dictitem_T *const v = tv_dict_item_alloc_len(S_LEN("old")); + copy_tv(oldtv, &v->di_tv); + tv_dict_add(argv[2].vval.v_dict, v); + } + + typval_T rettv; + + QUEUE *w; + QUEUE_FOREACH(w, &dict->watchers) { + DictWatcher *watcher = tv_dict_watcher_node_data(w); + if (!watcher->busy && tv_dict_watcher_matches(watcher, key)) { + rettv = TV_INITIAL_VALUE; + watcher->busy = true; + callback_call(&watcher->callback, 3, argv, &rettv); + watcher->busy = false; + tv_clear(&rettv); + } + } + + for (size_t i = 1; i < ARRAY_SIZE(argv); i++) { + tv_clear(argv + i); + } +} + +//{{{2 Dictionary item + +/// Allocate a dictionary item +/// +/// @note that the value of the item (->di_tv) still needs to be initialized. +/// +/// @param[in] key Key, is copied to the new item. +/// @param[in] key_len Key length. +/// +/// @return [allocated] new dictionary item. +dictitem_T *tv_dict_item_alloc_len(const char *const key, const size_t key_len) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_MALLOC +{ + dictitem_T *const di = xmalloc(offsetof(dictitem_T, di_key) + key_len + 1); + memcpy(di->di_key, key, key_len); + di->di_key[key_len] = NUL; + di->di_flags = DI_FLAGS_ALLOC; + return di; +} + +/// Allocate a dictionary item +/// +/// @note that the value of the item (->di_tv) still needs to be initialized. +/// +/// @param[in] key Key, is copied to the new item. +/// +/// @return [allocated] new dictionary item. +dictitem_T *tv_dict_item_alloc(const char *const key) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_MALLOC +{ + return tv_dict_item_alloc_len(key, strlen(key)); +} + +/// Free a dictionary item, also clearing the value +/// +/// @param item Item to free. +void tv_dict_item_free(dictitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + tv_clear(&item->di_tv); + if (item->di_flags & DI_FLAGS_ALLOC) { + xfree(item); + } +} + +/// Add item to dictionary +/// +/// @param[out] d Dictionary to add to. +/// @param[in] item Item to add. +/// +/// @return FAIL if key already exists. +int tv_dict_add(dict_T *const d, dictitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + return hash_add(&d->dv_hashtab, item->di_key); +} + +/// Make a copy of a dictionary item +/// +/// @param[in] di Item to copy. +/// +/// @return [allocated] new dictionary item. +static dictitem_T *tv_dict_item_copy(dictitem_T *const di) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_MALLOC +{ + dictitem_T *const new_di = tv_dict_item_alloc((const char *)di->di_key); + copy_tv(&di->di_tv, &new_di->di_tv); + return new_di; +} + +/// Remove item from dictionary and free it +/// +/// @param dict Dictionary to remove item from. +/// @param item Item to remove. +void tv_dict_item_remove(dict_T *const dict, dictitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + hashitem_T *const hi = hash_find(&dict->dv_hashtab, item->di_key); + if (HASHITEM_EMPTY(hi)) { + emsgf(_(e_intern2), "tv_dict_item_remove()"); + } else { + hash_remove(&dict->dv_hashtab, hi); + } + tv_dict_item_free(item); +} + +//{{{2 Alloc/free + +/// Allocate an empty dictionary +/// +/// @return [allocated] new dictionary. +dict_T *tv_dict_alloc(void) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT +{ + dict_T *const d = xmalloc(sizeof(dict_T)); + + // Add the dict to the list of dicts for garbage collection. + if (gc_first_dict != NULL) { + gc_first_dict->dv_used_prev = d; + } + d->dv_used_next = gc_first_dict; + d->dv_used_prev = NULL; + gc_first_dict = d; + + hash_init(&d->dv_hashtab); + d->dv_lock = VAR_UNLOCKED; + d->dv_scope = VAR_NO_SCOPE; + d->dv_refcount = 0; + d->dv_copyID = 0; + QUEUE_INIT(&d->watchers); + + return d; +} + +/// Free items contained in a dictionary +/// +/// @param[in,out] d Dictionary to clear. +void tv_dict_free_contents(dict_T *const d) + FUNC_ATTR_NONNULL_ALL +{ + // Lock the hashtab, we don't want it to resize while freeing items. + hash_lock(&d->dv_hashtab); + assert(d->dv_hashtab.ht_locked > 0); + HASHTAB_ITER(&d->dv_hashtab, hi, { + // Remove the item before deleting it, just in case there is + // something recursive causing trouble. + dictitem_T *const di = TV_DICT_HI2DI(hi); + hash_remove(&d->dv_hashtab, hi); + tv_dict_item_free(di); + }); + + while (!QUEUE_EMPTY(&d->watchers)) { + QUEUE *w = QUEUE_HEAD(&d->watchers); + QUEUE_REMOVE(w); + DictWatcher *watcher = tv_dict_watcher_node_data(w); + tv_dict_watcher_free(watcher); + } + + hash_clear(&d->dv_hashtab); + d->dv_hashtab.ht_locked--; + hash_init(&d->dv_hashtab); +} + +/// Free a dictionary itself, ignoring items it contains +/// +/// Ignores the reference count. +/// +/// @param[in,out] d Dictionary to free. +void tv_dict_free_dict(dict_T *const d) + FUNC_ATTR_NONNULL_ALL +{ + // Remove the dict from the list of dicts for garbage collection. + if (d->dv_used_prev == NULL) { + gc_first_dict = d->dv_used_next; + } else { + d->dv_used_prev->dv_used_next = d->dv_used_next; + } + if (d->dv_used_next != NULL) { + d->dv_used_next->dv_used_prev = d->dv_used_prev; + } + + xfree(d); +} + +/// Free a dictionary, including all items it contains +/// +/// Ignores the reference count. +/// +/// @param d Dictionary to free. +void tv_dict_free(dict_T *const d) + FUNC_ATTR_NONNULL_ALL +{ + if (!tv_in_free_unref_items) { + tv_dict_free_contents(d); + tv_dict_free_dict(d); + } +} + + +/// Unreference a dictionary +/// +/// Decrements the reference count and frees dictionary when it becomes zero. +/// +/// @param[in] d Dictionary to operate on. +void tv_dict_unref(dict_T *const d) +{ + if (d != NULL && --d->dv_refcount <= 0) { + tv_dict_free(d); + } +} + +//{{{2 Indexing/searching + +/// Find item in dictionary +/// +/// @param[in] d Dictionary to check. +/// @param[in] key Dictionary key. +/// @param[in] len Key length. If negative, then strlen(key) is used. +/// +/// @return found item or NULL if nothing was found. +dictitem_T *tv_dict_find(const dict_T *const d, const char *const key, + const ptrdiff_t len) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + hashitem_T *const hi = (len < 0 + ? hash_find(&d->dv_hashtab, (const char_u *)key) + : hash_find_len(&d->dv_hashtab, key, (size_t)len)); + if (HASHITEM_EMPTY(hi)) { + return NULL; + } + return TV_DICT_HI2DI(hi); +} + +/// Get a number item from a dictionary +/// +/// Returns 0 if the entry does not exist. +/// +/// @param[in] d Dictionary to get item from. +/// @param[in] key Key to find in dictionary. +/// +/// @return Dictionary item. +varnumber_T tv_dict_get_number(dict_T *const d, const char *const key) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + dictitem_T *const di = tv_dict_find(d, key, -1); + if (di == NULL) { + return 0; + } + return get_tv_number(&di->di_tv); +} + +/// Get a string item from a dictionary +/// +/// @param[in] d Dictionary to get item from. +/// @param[in] key Dictionary key. +/// @param[in] save If true, returned string will be placed in the allocated +/// memory. +/// +/// @return NULL if key does not exist, empty string in case of type error, +/// string item value otherwise. If returned value is not NULL, it may +/// be allocated depending on `save` argument. +char *tv_dict_get_string(dict_T *const d, const char *const key, + const bool save) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + static char numbuf[NUMBUFLEN]; + const char *const s = tv_dict_get_string_buf(d, key, numbuf); + if (save && s != NULL) { + return xstrdup(s); + } + return (char *)s; +} + +/// Get a string item from a dictionary +/// +/// @param[in] d Dictionary to get item from. +/// @param[in] key Dictionary key. +/// @param[in] numbuf Numbuf for. +/// +/// @return NULL if key does not exist, empty string in case of type error, +/// string item value otherwise. +const char *tv_dict_get_string_buf(dict_T *const d, const char *const key, + char *const numbuf) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + dictitem_T *const di = tv_dict_find(d, key, -1); + if (di == NULL) { + return NULL; + } + return (const char *)get_tv_string_buf(&di->di_tv, (char_u *)numbuf); +} + +//{{{2 Operations on the whole dict + +/// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary. +/// +/// @param d The Dictionary to clear +void tv_dict_clear(dict_T *const d) + FUNC_ATTR_NONNULL_ALL +{ + hash_lock(&d->dv_hashtab); + assert(d->dv_hashtab.ht_locked > 0); + + HASHTAB_ITER(&d->dv_hashtab, hi, { + tv_dict_item_free(TV_DICT_HI2DI(hi)); + hash_remove(&d->dv_hashtab, hi); + }); + + hash_unlock(&d->dv_hashtab); +} + +/// Extend dictionary with items from another dictionary +/// +/// @param d1 Dictionary to extend. +/// @param[in] d2 Dictionary to extend with. +/// @param[in] action "error", "force", "keep": +/// +/// e*, including "error": duplicate key gives an error. +/// f*, including "force": duplicate d2 keys override d1. +/// other, including "keep": duplicate d2 keys ignored. +void tv_dict_extend(dict_T *const d1, dict_T *const d2, + const char *const action) + FUNC_ATTR_NONNULL_ALL +{ + const bool watched = tv_dict_is_watched(d1); + const char *const arg_errmsg = _("extend() argument"); + const size_t arg_errmsg_len = strlen(arg_errmsg); + + TV_DICT_ITER(d2, di2, { + dictitem_T *const di1 = tv_dict_find(d1, (const char *)di2->di_key, -1); + if (d1->dv_scope != VAR_NO_SCOPE) { + // Disallow replacing a builtin function in l: and g:. + // Check the key to be valid when adding to any scope. + if (d1->dv_scope == VAR_DEF_SCOPE + && di2->di_tv.v_type == VAR_FUNC + && !var_check_func_name((const char *)di2->di_key, di1 == NULL)) { + break; + } + if (!valid_varname((const char *)di2->di_key)) { + break; + } + } + if (di1 == NULL) { + dictitem_T *const new_di = tv_dict_item_copy(di2); + if (tv_dict_add(d1, new_di) == FAIL) { + tv_dict_item_free(new_di); + } else if (watched) { + tv_dict_watcher_notify(d1, (const char *)new_di->di_key, &new_di->di_tv, + NULL); + } + } else if (*action == 'e') { + emsgf(_("E737: Key already exists: %s"), di2->di_key); + break; + } else if (*action == 'f' && di2 != di1) { + typval_T oldtv; + + if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, arg_errmsg_len) + || var_check_ro(di1->di_flags, arg_errmsg, arg_errmsg_len)) { + break; + } + + if (watched) { + copy_tv(&di1->di_tv, &oldtv); + } + + tv_clear(&di1->di_tv); + copy_tv(&di2->di_tv, &di1->di_tv); + + if (watched) { + tv_dict_watcher_notify(d1, (const char *)di1->di_key, &di1->di_tv, + &oldtv); + tv_clear(&oldtv); + } + } + }); +} + +/// Compare two dictionaries +/// +/// @param[in] d1 First dictionary. +/// @param[in] d2 Second dictionary. +/// @param[in] ic True if case is to be ignored. +/// @param[in] recursive True when used recursively. +bool tv_dict_equal(dict_T *const d1, dict_T *const d2, + const bool ic, const bool recursive) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (d1 == d2) { + return true; + } + if (d1 == NULL || d2 == NULL) { + return false; + } + if (tv_dict_len(d1) != tv_dict_len(d2)) { + return false; + } + + TV_DICT_ITER(d1, di1, { + dictitem_T *const di2 = tv_dict_find(d2, (const char *)di1->di_key, -1); + if (di2 == NULL) { + return false; + } + if (!tv_equal(&di1->di_tv, &di2->di_tv, ic, recursive)) { + return false; + } + }); + return true; +} + +/// Make a copy of dictionary +/// +/// @param[in] conv If non-NULL, then all internal strings will be converted. +/// @param[in] orig Original dictionary to copy. +/// @param[in] deep If false, then shallow copy will be done. +/// @param[in] copyID See var_item_copy(). +/// +/// @return Copied dictionary. May be NULL in case original dictionary is NULL +/// or some failure happens. The refcount of the new dictionary is set +/// to 1. +dict_T *tv_dict_copy(const vimconv_T *const conv, + dict_T *const orig, + const bool deep, + const int copyID) +{ + if (orig == NULL) { + return NULL; + } + + dict_T *copy = tv_dict_alloc(); + if (copyID != 0) { + orig->dv_copyID = copyID; + orig->dv_copydict = copy; + } + TV_DICT_ITER(orig, di, { + if (got_int) { + break; + } + dictitem_T *new_di; + if (conv == NULL || conv->vc_type == CONV_NONE) { + new_di = tv_dict_item_alloc((const char *)di->di_key); + } else { + size_t len = STRLEN(di->di_key); + char *const key = (char *)string_convert(conv, di->di_key, &len); + if (key == NULL) { + new_di = tv_dict_item_alloc_len((const char *)di->di_key, len); + } else { + new_di = tv_dict_item_alloc_len(key, len); + xfree(key); + } + } + if (deep) { + if (var_item_copy(conv, &di->di_tv, &new_di->di_tv, deep, + copyID) == FAIL) { + xfree(new_di); + break; + } + } else { + copy_tv(&di->di_tv, &new_di->di_tv); + } + if (tv_dict_add(copy, new_di) == FAIL) { + tv_dict_item_free(new_di); + break; + } + }); + + copy->dv_refcount++; + if (got_int) { + tv_dict_unref(copy); + copy = NULL; + } + + return copy; +} + //{{{1 Generic typval operations //{{{2 Init/alloc/clear //{{{3 Alloc @@ -783,6 +1334,21 @@ list_T *tv_list_alloc_ret(typval_T *const ret_tv) return l; } +/// Allocate an empty dictionary for a return value +/// +/// Also sets reference count. +/// +/// @param[out] ret_tv Structure where dictionary is saved. +void tv_dict_alloc_ret(typval_T *const ret_tv) + FUNC_ATTR_NONNULL_ALL +{ + dict_T *const d = tv_dict_alloc(); + ret_tv->vval.v_dict = d; + ret_tv->v_type = VAR_DICT; + ret_tv->v_lock = VAR_UNLOCKED; + d->dv_refcount++; +} + //{{{3 Clear #define TYPVAL_ENCODE_ALLOW_SPECIALS false @@ -884,7 +1450,7 @@ static inline void _nothing_conv_func_end(typval_T *const tv, const int copyID) #define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ do { \ assert((void *)&dict != (void *)&TYPVAL_ENCODE_NODICT_VAR); \ - dict_unref((dict_T *)dict); \ + tv_dict_unref((dict_T *)dict); \ *((dict_T **)&dict) = NULL; \ if (tv != NULL) { \ ((typval_T *)tv)->v_lock = VAR_UNLOCKED; \ @@ -966,7 +1532,7 @@ static inline void _nothing_conv_dict_end(typval_T *const tv, FUNC_ATTR_ALWAYS_INLINE { if ((const void *)dictp != nodictvar) { - dict_unref(*dictp); + tv_dict_unref(*dictp); *dictp = NULL; } } @@ -1077,13 +1643,9 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock) CHANGE_LOCK(lock, d->dv_lock); if (deep < 0 || deep > 1) { // recursive: lock/unlock the items the List contains - int todo = (int)d->dv_hashtab.ht_used; - for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) { - if (!HASHITEM_EMPTY(hi)) { - todo--; - tv_item_lock(&HI2DI(hi)->di_tv, deep - 1, lock); - } - } + TV_DICT_ITER(d, di, { + tv_item_lock(&di->di_tv, deep - 1, lock); + }); } } break; @@ -1121,6 +1683,140 @@ bool tv_islocked(const typval_T *const tv) && (tv->vval.v_dict->dv_lock & VAR_LOCKED))); } +/// Return true if typval is locked +/// +/// Also gives an error message when typval is locked. +/// +/// @param[in] lock Lock status. +/// @param[in] name Variable name, used in the error message. +/// @param[in] use_gettext True if variable name also is to be translated. +/// +/// @return true if variable is locked, false otherwise. +bool tv_check_lock(const VarLockStatus lock, const char *const name, + const size_t name_len) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + const char *error_message = NULL; + switch (lock) { + case VAR_UNLOCKED: { + return false; + } + case VAR_LOCKED: { + error_message = N_("E741: Value is locked: %.*s"); + break; + } + case VAR_FIXED: { + error_message = N_("E742: Cannot change value of %.*s"); + break; + } + } + assert(error_message != NULL); + + const char *const unknown_name = _("Unknown"); + + emsgf(_(error_message), (name != NULL ? name_len : strlen(unknown_name)), + (name != NULL ? name : unknown_name)); + + return true; +} + +//{{{2 Comparison + +static int tv_equal_recurse_limit; + +/// Compare two VimL values +/// +/// Like "==", but strings and numbers are different, as well as floats and +/// numbers. +/// +/// @warning Too nested structures may be considered equal even if they are not. +/// +/// @param[in] tv1 First value to compare. +/// @param[in] tv2 Second value to compare. +/// @param[in] ic True if case is to be ignored. +/// @param[in] recursive True when used recursively. +/// +/// @return true if values are equal. +bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic, + const bool recursive) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + // TODO(ZyX-I): Make this not recursive + static int recursive_cnt = 0; // Catch recursive loops. + + if (!((tv1->v_type == VAR_FUNC || tv1->v_type == VAR_PARTIAL) + && (tv2->v_type == VAR_FUNC || tv2->v_type == VAR_PARTIAL)) + && tv1->v_type != tv2->v_type) { + return false; + } + + // Catch lists and dicts that have an endless loop by limiting + // recursiveness to a limit. We guess they are equal then. + // A fixed limit has the problem of still taking an awful long time. + // Reduce the limit every time running into it. That should work fine for + // deeply linked structures that are not recursively linked and catch + // recursiveness quickly. + if (!recursive) { + tv_equal_recurse_limit = 1000; + } + if (recursive_cnt >= tv_equal_recurse_limit) { + tv_equal_recurse_limit--; + return true; + } + + switch (tv1->v_type) { + case VAR_LIST: { + recursive_cnt++; + const bool r = tv_list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, + true); + recursive_cnt--; + return r; + } + case VAR_DICT: { + recursive_cnt++; + const bool r = tv_dict_equal(tv1->vval.v_dict, tv2->vval.v_dict, ic, + true); + recursive_cnt--; + return r; + } + case VAR_PARTIAL: + case VAR_FUNC: { + if ((tv1->v_type == VAR_PARTIAL && tv1->vval.v_partial == NULL) + || (tv2->v_type == VAR_PARTIAL && tv2->vval.v_partial == NULL)) { + return false; + } + recursive_cnt++; + const bool r = func_equal(tv1, tv2, ic); + recursive_cnt--; + return r; + } + case VAR_NUMBER: { + return tv1->vval.v_number == tv2->vval.v_number; + } + case VAR_FLOAT: { + return tv1->vval.v_float == tv2->vval.v_float; + } + case VAR_STRING: { + char buf1[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + const char *s1 = (const char *)get_tv_string_buf(tv1, (char_u *)buf1); + const char *s2 = (const char *)get_tv_string_buf(tv2, (char_u *)buf2); + return mb_strcmp_ic((bool)ic, s1, s2) == 0; + } + case VAR_SPECIAL: { + return tv1->vval.v_special == tv2->vval.v_special; + } + case VAR_UNKNOWN: { + // VAR_UNKNOWN can be the result of an invalid expression, let’s say it + // does not equal anything, not even self. + return false; + } + } + + assert(false); + return false; +} + //{{{2 Type checks /// Check that given value is a number or string @@ -1135,37 +1831,37 @@ bool tv_islocked(const typval_T *const tv) bool tv_check_str_or_nr(const typval_T *const tv) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE { - switch (tv->v_type) { - case VAR_NUMBER: - case VAR_STRING: { - return true; - } - case VAR_FLOAT: { - EMSG(_("E805: Expected a Number or a String, Float found")); - return false; - } - case VAR_PARTIAL: - case VAR_FUNC: { - EMSG(_("E703: Expected a Number or a String, Funcref found")); - return false; - } - case VAR_LIST: { - EMSG(_("E745: Expected a Number or a String, List found")); - return false; - } - case VAR_DICT: { - EMSG(_("E728: Expected a Number or a String, Dictionary found")); - return false; - } - case VAR_SPECIAL: { - EMSG(_("E5300: Expected a Number or a String")); - return false; - } - case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "tv_check_str_or_nr(UNKNOWN)"); - return false; - } + switch (tv->v_type) { + case VAR_NUMBER: + case VAR_STRING: { + return true; } - assert(false); - return false; + case VAR_FLOAT: { + emsgf(_("E805: Expected a Number or a String, Float found")); + return false; + } + case VAR_PARTIAL: + case VAR_FUNC: { + emsgf(_("E703: Expected a Number or a String, Funcref found")); + return false; + } + case VAR_LIST: { + emsgf(_("E745: Expected a Number or a String, List found")); + return false; + } + case VAR_DICT: { + emsgf(_("E728: Expected a Number or a String, Dictionary found")); + return false; + } + case VAR_SPECIAL: { + emsgf(_("E5300: Expected a Number or a String")); + return false; + } + case VAR_UNKNOWN: { + emsgf(_(e_intern2), "tv_check_str_or_nr(UNKNOWN)"); + return false; + } + } + assert(false); + return false; } diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index cf83904ffc..6183397d12 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -8,6 +8,7 @@ #include "nvim/hashtab.h" #include "nvim/garray.h" #include "nvim/mbyte.h" +#include "nvim/func_attr.h" #include "nvim/lib/queue.h" #include "nvim/profile.h" // for proftime_T #include "nvim/pos.h" // for linenr_T @@ -31,6 +32,31 @@ typedef struct listvar_S list_T; typedef struct dictvar_S dict_T; typedef struct partial_S partial_T; +typedef struct ufunc ufunc_T; + +typedef enum { + kCallbackNone, + kCallbackFuncref, + kCallbackPartial, +} CallbackType; + +typedef struct { + union { + char_u *funcref; + partial_T *partial; + } data; + CallbackType type; +} Callback; +#define CALLBACK_NONE ((Callback){ .type = kCallbackNone }) + +/// Structure holding dictionary watcher +typedef struct dict_watcher { + Callback callback; + char *key_pattern; + QUEUE node; + bool busy; // prevent recursion if the dict is changed in the callback +} DictWatcher; + /// Special variable values typedef enum { kSpecialVarFalse, ///< v:false @@ -134,7 +160,7 @@ struct dictitem_S { struct { \ typval_T di_tv; /* Structure that holds scope dictionary itself. */ \ uint8_t di_flags; /* Flags. */ \ - char_u di_key[KEY_LEN]; /* NUL. */ \ + char_u di_key[KEY_LEN]; /* Key value. */ \ } /// Structure to hold a scope dictionary @@ -181,9 +207,7 @@ typedef int scid_T; // Structure to hold info for a function that is currently being executed. typedef struct funccall_S funccall_T; -// Structure to hold info for a user function. -typedef struct ufunc ufunc_T; - +/// Structure to hold info for a user function. struct ufunc { int uf_varargs; ///< variable nr of arguments int uf_flags; @@ -207,12 +231,12 @@ struct ufunc { int uf_tml_idx; ///< index of line being timed; -1 if none int uf_tml_execed; ///< line being timed was executed scid_T uf_script_ID; ///< ID of script where function was defined, - // used for s: variables + ///< used for s: variables int uf_refcount; ///< reference count, see func_name_refcount() funccall_T *uf_scoped; ///< l: local variables for closure char_u uf_name[1]; ///< name of function (actually longer); can - // start with 123_ ( is K_SPECIAL - // KS_EXTRA KE_SNR) + ///< start with 123_ ( is K_SPECIAL + ///< KS_EXTRA KE_SNR) }; /// Maximum number of function arguments @@ -245,24 +269,17 @@ typedef struct list_stack_S { // In a hashtab item "hi_key" points to "di_key" in a dictitem. // This avoids adding a pointer to the hashtab item. -/// Convert a dictitem pointer to a hashitem key pointer -#define DI2HIKEY(di) ((di)->di_key) - -/// Convert a hashitem key pointer to a dictitem pointer -#define HIKEY2DI(p) ((dictitem_T *)(p - offsetof(dictitem_T, di_key))) - -/// Convert a hashitem value pointer to a dictitem pointer -#define HIVAL2DI(p) \ - ((dictitem_T *)(((char *)p) - offsetof(dictitem_T, di_tv))) - /// Convert a hashitem pointer to a dictitem pointer -#define HI2DI(hi) HIKEY2DI((hi)->hi_key) +#define TV_DICT_HI2DI(hi) \ + ((dictitem_T *)((hi)->hi_key - offsetof(dictitem_T, di_key))) + +static inline long tv_list_len(list_T *const l) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get the number of items in a list /// /// @param[in] l List to check. static inline long tv_list_len(list_T *const l) - FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { if (l == NULL) { return 0; @@ -270,6 +287,64 @@ static inline long tv_list_len(list_T *const l) return l->lv_len; } +static inline long tv_dict_len(const dict_T *const d) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Get the number of items in a Dictionary +/// +/// @param[in] d Dictionary to check. +static inline long tv_dict_len(const dict_T *const d) +{ + if (d == NULL) { + return 0L; + } + return (long)d->dv_hashtab.ht_used; +} + +static inline bool tv_dict_is_watched(const dict_T *const d) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Check if dictionary is watched +/// +/// @param[in] d Dictionary to check. +/// +/// @return true if there is at least one watcher. +static inline bool tv_dict_is_watched(const dict_T *const d) +{ + return d && !QUEUE_EMPTY(&d->watchers); +} + +static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) + REAL_FATTR_NONNULL_ALL REAL_FATTR_NONNULL_RET REAL_FATTR_PURE + REAL_FATTR_WARN_UNUSED_RESULT; + +/// Compute the `DictWatcher` address from a QUEUE node. +/// +/// This only exists for .asan-blacklist (ASAN doesn't handle QUEUE_DATA pointer +/// arithmetic). +static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) +{ + return QUEUE_DATA(q, DictWatcher, node); +} + +/// Initialize VimL object +/// +/// Initializes to unlocked VAR_UNKNOWN object. +/// +/// @param[out] tv Object to initialize. +static inline void tv_init(typval_T *const tv) +{ + if (tv != NULL) { + memset(tv, 0, sizeof(*tv)); + } +} + +#define TV_INITIAL_VALUE \ + ((typval_T) { \ + .v_type = VAR_UNKNOWN, \ + .v_lock = VAR_UNLOCKED, \ + }) + /// Empty string /// /// Needed for hack which allows not allocating empty string and still not @@ -279,6 +354,21 @@ extern const char *const tv_empty_string; /// Specifies that free_unref_items() function has (not) been entered extern bool tv_in_free_unref_items; +/// Iterate over a dictionary +/// +/// @param[in] d Dictionary to iterate over. +/// @param di Name of the variable with current dictitem_T entry. +/// @param code Cycle body. +#define TV_DICT_ITER(d, di, code) \ + HASHTAB_ITER(&(d)->dv_hashtab, di##hi_, { \ + { \ + dictitem_T *const di = TV_DICT_HI2DI(di##hi_); \ + { \ + code \ + } \ + } \ + }) + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/typval.h.generated.h" #endif diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index eb89a601ff..ad54eef4a0 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -406,11 +406,11 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( const dictitem_T *val_di; if (TYPVAL_ENCODE_ALLOW_SPECIALS && tv->vval.v_dict->dv_hashtab.ht_used == 2 - && (type_di = dict_find((dict_T *)tv->vval.v_dict, - (char_u *)"_TYPE", -1)) != NULL + && (type_di = tv_dict_find((dict_T *)tv->vval.v_dict, + S_LEN("_TYPE"))) != NULL && type_di->di_tv.v_type == VAR_LIST - && (val_di = dict_find((dict_T *)tv->vval.v_dict, - (char_u *)"_VAL", -1)) != NULL) { + && (val_di = tv_dict_find((dict_T *)tv->vval.v_dict, + S_LEN("_VAL"))) != NULL) { size_t i; for (i = 0; i < ARRAY_SIZE(eval_msgpack_type_lists); i++) { if (type_di->di_tv.vval.v_list == eval_msgpack_type_lists[i]) { @@ -662,7 +662,7 @@ typval_encode_stop_converting_one_item: while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) { cur_mpsv->data.d.hi++; } - dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi); + dictitem_T *const di = TV_DICT_HI2DI(cur_mpsv->data.d.hi); cur_mpsv->data.d.todo--; cur_mpsv->data.d.hi++; TYPVAL_ENCODE_CONV_STR_STRING(NULL, &di->di_key[0], diff --git a/src/nvim/event/process.h b/src/nvim/event/process.h index 5cbf7f9ce7..26d70a5e6d 100644 --- a/src/nvim/event/process.h +++ b/src/nvim/event/process.h @@ -21,7 +21,7 @@ struct process { int pid, status, refcount; // set to the hrtime of when process_stop was called for the process. uint64_t stopped_time; - char *cwd; + const char *cwd; char **argv; Stream *in, *out, *err; process_exit_cb cb; diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 151a4d375f..9681527fee 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -4764,8 +4764,8 @@ void fix_help_buffer(void) char_u *p; char_u *rt; - /* set filetype to "help". */ - set_option_value((char_u *)"ft", 0L, (char_u *)"help", OPT_LOCAL); + // Set filetype to "help". + set_option_value("ft", 0L, "help", OPT_LOCAL); if (!syntax_present(curwin)) { for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) { diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index e59a87b335..b84834d351 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -2073,9 +2073,9 @@ void ex_listdo(exarg_T *eap) // Clear 'shm' to avoid that the file message overwrites // any output from the command. p_shm_save = vim_strsave(p_shm); - set_option_value((char_u *)"shm", 0L, (char_u *)"", 0); + set_option_value("shm", 0L, "", 0); do_argfile(eap, i); - set_option_value((char_u *)"shm", 0L, p_shm_save, 0); + set_option_value("shm", 0L, (char *)p_shm_save, 0); xfree(p_shm_save); } if (curwin->w_arg_idx != i) { @@ -2138,9 +2138,9 @@ void ex_listdo(exarg_T *eap) // Go to the next buffer. Clear 'shm' to avoid that the file // message overwrites any output from the command. p_shm_save = vim_strsave(p_shm); - set_option_value((char_u *)"shm", 0L, (char_u *)"", 0); + set_option_value("shm", 0L, "", 0); goto_buffer(eap, DOBUF_FIRST, FORWARD, next_fnum); - set_option_value((char_u *)"shm", 0L, p_shm_save, 0); + set_option_value("shm", 0L, (char *)p_shm_save, 0); xfree(p_shm_save); // If autocommands took us elsewhere, quit here. @@ -2496,16 +2496,16 @@ static int APP_BOTH; static void add_pack_plugin(char_u *fname, void *cookie) { char_u *p4, *p3, *p2, *p1, *p; - char_u *new_rtp; - char_u *ffname = (char_u *)fix_fname((char *)fname); + + char *const ffname = fix_fname((char *)fname); if (ffname == NULL) { return; } - if (cookie != &APP_LOAD && strstr((char *)p_rtp, (char *)ffname) == NULL) { + if (cookie != &APP_LOAD && strstr((char *)p_rtp, ffname) == NULL) { // directory is not yet in 'runtimepath', add it - p4 = p3 = p2 = p1 = get_past_head(ffname); + p4 = p3 = p2 = p1 = get_past_head((char_u *)ffname); for (p = p1; *p; mb_ptr_adv(p)) { if (vim_ispathsep_nocolon(*p)) { p4 = p3; p3 = p2; p2 = p1; p1 = p; @@ -2521,13 +2521,13 @@ static void add_pack_plugin(char_u *fname, void *cookie) *p4 = NUL; // Find "ffname" in "p_rtp", ignoring '/' vs '\' differences - size_t fname_len = STRLEN(ffname); - char_u *insp = p_rtp; + size_t fname_len = strlen(ffname); + const char *insp = (const char *)p_rtp; for (;;) { - if (vim_fnamencmp(insp, ffname, fname_len) == 0) { + if (path_fnamencmp(insp, ffname, fname_len) == 0) { break; } - insp = vim_strchr(insp, ','); + insp = strchr(insp, ','); if (insp == NULL) { break; } @@ -2536,10 +2536,10 @@ static void add_pack_plugin(char_u *fname, void *cookie) if (insp == NULL) { // not found, append at the end - insp = p_rtp + STRLEN(p_rtp); + insp = (const char *)p_rtp + STRLEN(p_rtp); } else { // append after the matching directory. - insp += STRLEN(ffname); + insp += strlen(ffname); while (*insp != NUL && *insp != ',') { insp++; } @@ -2547,31 +2547,40 @@ static void add_pack_plugin(char_u *fname, void *cookie) *p4 = c; // check if rtp/pack/name/start/name/after exists - char *afterdir = concat_fnames((char *)ffname, "after", true); + char *afterdir = concat_fnames(ffname, "after", true); size_t afterlen = 0; if (os_isdir((char_u *)afterdir)) { - afterlen = STRLEN(afterdir) + 1; // add one for comma + afterlen = strlen(afterdir) + 1; // add one for comma } - size_t oldlen = STRLEN(p_rtp); - size_t addlen = STRLEN(ffname) + 1; // add one for comma - new_rtp = try_malloc(oldlen + addlen + afterlen + 1); // add one for NUL + const size_t oldlen = STRLEN(p_rtp); + const size_t addlen = strlen(ffname) + 1; // add one for comma + const size_t new_rtp_len = oldlen + addlen + afterlen + 1; + // add one for NUL -------------------------------------^ + char *const new_rtp = try_malloc(new_rtp_len); if (new_rtp == NULL) { goto theend; } - uintptr_t keep = (uintptr_t)(insp - p_rtp); + const size_t keep = (size_t)(insp - (const char *)p_rtp); + size_t new_rtp_fill = 0; memmove(new_rtp, p_rtp, keep); - new_rtp[keep] = ','; - memmove(new_rtp + keep + 1, ffname, addlen); + new_rtp_fill += keep; + new_rtp[new_rtp_fill++] = ','; + memmove(new_rtp + new_rtp_fill, ffname, addlen); + new_rtp_fill += addlen - 1; + assert(new_rtp[new_rtp_fill] == NUL || new_rtp[new_rtp_fill] == ','); if (p_rtp[keep] != NUL) { - memmove(new_rtp + keep + addlen, p_rtp + keep, - oldlen - keep + 1); + memmove(new_rtp + new_rtp_fill, p_rtp + keep, oldlen - keep + 1); + new_rtp_fill += oldlen - keep; } if (afterlen > 0) { - STRCAT(new_rtp, ","); - STRCAT(new_rtp, afterdir); + assert(new_rtp[new_rtp_fill] == NUL); + new_rtp[new_rtp_fill++] = ','; + memmove(new_rtp + new_rtp_fill, afterdir, afterlen - 1); + new_rtp_fill += afterlen - 1; } - set_option_value((char_u *)"rtp", 0L, new_rtp, 0); + new_rtp[new_rtp_fill] = NUL; + set_option_value("rtp", 0L, new_rtp, 0); xfree(new_rtp); xfree(afterdir); } @@ -2580,7 +2589,7 @@ static void add_pack_plugin(char_u *fname, void *cookie) static const char *plugpat = "%s/plugin/**/*.vim"; // NOLINT static const char *ftpat = "%s/ftdetect/*.vim"; // NOLINT - size_t len = STRLEN(ffname) + STRLEN(ftpat); + size_t len = strlen(ffname) + STRLEN(ftpat); char_u *pat = try_malloc(len + 1); if (pat == NULL) { goto theend; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index dc99c0771d..87fe52c119 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -9483,18 +9483,18 @@ void dialog_msg(char_u *buff, char *format, char_u *fname) static void ex_behave(exarg_T *eap) { if (STRCMP(eap->arg, "mswin") == 0) { - set_option_value((char_u *)"selection", 0L, (char_u *)"exclusive", 0); - set_option_value((char_u *)"selectmode", 0L, (char_u *)"mouse,key", 0); - set_option_value((char_u *)"mousemodel", 0L, (char_u *)"popup", 0); - set_option_value((char_u *)"keymodel", 0L, - (char_u *)"startsel,stopsel", 0); + set_option_value("selection", 0L, "exclusive", 0); + set_option_value("selectmode", 0L, "mouse,key", 0); + set_option_value("mousemodel", 0L, "popup", 0); + set_option_value("keymodel", 0L, "startsel,stopsel", 0); } else if (STRCMP(eap->arg, "xterm") == 0) { - set_option_value((char_u *)"selection", 0L, (char_u *)"inclusive", 0); - set_option_value((char_u *)"selectmode", 0L, (char_u *)"", 0); - set_option_value((char_u *)"mousemodel", 0L, (char_u *)"extend", 0); - set_option_value((char_u *)"keymodel", 0L, (char_u *)"", 0); - } else + set_option_value("selection", 0L, "inclusive", 0); + set_option_value("selectmode", 0L, "", 0); + set_option_value("mousemodel", 0L, "extend", 0); + set_option_value("keymodel", 0L, "", 0); + } else { EMSG2(_(e_invarg2), eap->arg); + } } /* @@ -9608,8 +9608,9 @@ void filetype_maybe_enable(void) */ static void ex_setfiletype(exarg_T *eap) { - if (!did_filetype) - set_option_value((char_u *)"filetype", 0L, eap->arg, OPT_LOCAL); + if (!did_filetype) { + set_option_value("filetype", 0L, (char *)eap->arg, OPT_LOCAL); + } } static void ex_digraphs(exarg_T *eap) @@ -9695,7 +9696,8 @@ static void ex_match(exarg_T *eap) c = *end; *end = NUL; - match_add(curwin, g, p + 1, 10, id, NULL, NULL); + match_add(curwin, (const char *)g, (const char *)p + 1, 10, id, + NULL, NULL); xfree(g); *end = c; } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 9851ed5396..872b7fe365 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -5173,7 +5173,7 @@ static int ex_window(void) // Create empty command-line buffer. buf_open_scratch(0, "[Command Line]"); // Command-line buffer has bufhidden=wipe, unlike a true "scratch" buffer. - set_option_value((char_u *)"bh", 0L, (char_u *)"wipe", OPT_LOCAL); + set_option_value("bh", 0L, "wipe", OPT_LOCAL); curwin->w_p_rl = cmdmsg_rl; cmdmsg_rl = false; curbuf->b_p_ma = true; @@ -5191,7 +5191,7 @@ static int ex_window(void) add_map((char_u *)" ", INSERT); add_map((char_u *)" a", NORMAL); } - set_option_value((char_u *)"ft", 0L, (char_u *)"vim", OPT_LOCAL); + set_option_value("ft", 0L, "vim", OPT_LOCAL); } /* Reset 'textwidth' after setting 'filetype' (the Vim filetype plugin diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index b73d9944ce..8ab6955042 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1556,7 +1556,7 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope) apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, (char_u *)new_dir, false, NULL); - dict_clear(dict); + tv_dict_clear(dict); recursive = false; } diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 0c131d7b33..076ee13a80 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1881,9 +1881,8 @@ static int vgetorpeek(int advance) (size_t)(mlen - typebuf.tb_maplen)); } - del_typebuf(mlen, 0); /* remove the chars */ - set_option_value((char_u *)"paste", - (long)!p_paste, NULL, 0); + del_typebuf(mlen, 0); // Remove the chars. + set_option_value("paste", !p_paste, NULL, 0); if (!(State & INSERT)) { msg_col = 0; msg_row = (int)Rows - 1; diff --git a/src/nvim/hashtab.c b/src/nvim/hashtab.c index b14bb01c0e..354c9718ba 100644 --- a/src/nvim/hashtab.c +++ b/src/nvim/hashtab.c @@ -82,7 +82,7 @@ void hash_clear_all(hashtab_T *ht, unsigned int off) /// used for that key. /// WARNING: Returned pointer becomes invalid as soon as the hash table /// is changed in any way. -hashitem_T *hash_find(hashtab_T *ht, const char_u *key) +hashitem_T *hash_find(const hashtab_T *const ht, const char_u *const key) { return hash_lookup(ht, (const char *)key, STRLEN(key), hash_hash(key)); } @@ -99,7 +99,8 @@ hashitem_T *hash_find(hashtab_T *ht, const char_u *key) /// /// @warning Returned pointer becomes invalid as soon as the hash table /// is changed in any way. -hashitem_T *hash_find_len(hashtab_T *ht, const char *key, const size_t len) +hashitem_T *hash_find_len(const hashtab_T *const ht, const char *const key, + const size_t len) { return hash_lookup(ht, key, len, hash_hash_len(key, len)); } @@ -115,7 +116,7 @@ hashitem_T *hash_find_len(hashtab_T *ht, const char *key, const size_t len) /// used for that key. /// WARNING: Returned pointer becomes invalid as soon as the hash table /// is changed in any way. -hashitem_T *hash_lookup(hashtab_T *const ht, +hashitem_T *hash_lookup(const hashtab_T *const ht, const char *const key, const size_t key_len, const hash_T hash) { diff --git a/src/nvim/hashtab.h b/src/nvim/hashtab.h index 0da2b13f2e..973b97d476 100644 --- a/src/nvim/hashtab.h +++ b/src/nvim/hashtab.h @@ -70,6 +70,25 @@ typedef struct hashtable_S { hashitem_T ht_smallarray[HT_INIT_SIZE]; /// initial array } hashtab_T; +/// Iterate over a hashtab +/// +/// @param[in] ht Hashtab to iterate over. +/// @param hi Name of the variable with current hashtab entry. +/// @param code Cycle body. +#define HASHTAB_ITER(ht, hi, code) \ + do { \ + hashtab_T *const hi##ht_ = (ht); \ + size_t hi##todo_ = hi##ht_->ht_used; \ + for (hashitem_T *hi = hi##ht_->ht_array; hi##todo_; hi++) { \ + if (!HASHITEM_EMPTY(hi)) { \ + { \ + code \ + } \ + hi##todo_--; \ + } \ + } \ + } while (0) + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "hashtab.h.generated.h" #endif diff --git a/src/nvim/macros.h b/src/nvim/macros.h index 650bf76156..5042663041 100644 --- a/src/nvim/macros.h +++ b/src/nvim/macros.h @@ -19,6 +19,15 @@ # define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) #endif +/// String with length +/// +/// For use in functions which accept (char *s, size_t len) pair in arguments. +/// +/// @param[in] s Static string. +/// +/// @return `s, sizeof(s) - 1` +#define S_LEN(s) (s), (sizeof(s) - 1) + /* * Position comparisons */ diff --git a/src/nvim/main.c b/src/nvim/main.c index 0c978dc47d..33e1551351 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -803,17 +803,18 @@ static void command_line_scan(mparm_T *parmp) argv_idx = -1; /* skip to next argument */ break; - case 'A': /* "-A" start in Arabic mode */ - set_option_value((char_u *)"arabic", 1L, NULL, 0); + case 'A': { // "-A" start in Arabic mode. + set_option_value("arabic", 1L, NULL, 0); break; - - case 'b': /* "-b" binary mode */ - /* Needs to be effective before expanding file names, because - * for Win32 this makes us edit a shortcut file itself, - * instead of the file it links to. */ + } + case 'b': { // "-b" binary mode. + // Needs to be effective before expanding file names, because + // for Win32 this makes us edit a shortcut file itself, + // instead of the file it links to. set_options_bin(curbuf->b_p_bin, 1, 0); - curbuf->b_p_bin = 1; /* binary file I/O */ + curbuf->b_p_bin = 1; // Binary file I/O. break; + } case 'e': /* "-e" Ex mode */ exmode_active = EXMODE_NORMAL; @@ -830,24 +831,27 @@ static void command_line_scan(mparm_T *parmp) main_start_gui(); break; - case 'F': /* "-F" start in Farsi mode: rl + fkmap set */ - p_fkmap = TRUE; - set_option_value((char_u *)"rl", 1L, NULL, 0); + case 'F': { // "-F" start in Farsi mode: rl + fkmap set. + p_fkmap = true; + set_option_value("rl", 1L, NULL, 0); break; + } case 'h': /* "-h" give help message */ usage(); mch_exit(0); - case 'H': /* "-H" start in Hebrew mode: rl + hkmap set */ - p_hkmap = TRUE; - set_option_value((char_u *)"rl", 1L, NULL, 0); + case 'H': { // "-H" start in Hebrew mode: rl + hkmap set. + p_hkmap = true; + set_option_value("rl", 1L, NULL, 0); break; + } - case 'l': /* "-l" lisp mode, 'lisp' and 'showmatch' on */ - set_option_value((char_u *)"lisp", 1L, NULL, 0); - p_sm = TRUE; + case 'l': { // "-l" lisp mode, 'lisp' and 'showmatch' on. + set_option_value("lisp", 1L, NULL, 0); + p_sm = true; break; + } case 'M': /* "-M" no changes or writing of files */ reset_modifiable(); @@ -946,8 +950,7 @@ static void command_line_scan(mparm_T *parmp) /* default is 10: a little bit verbose */ p_verbose = get_number_arg(argv[0], &argv_idx, 10); if (argv[0][argv_idx] != NUL) { - set_option_value((char_u *)"verbosefile", 0L, - (char_u *)argv[0] + argv_idx, 0); + set_option_value("verbosefile", 0L, argv[0] + argv_idx, 0); argv_idx = (int)STRLEN(argv[0]); } break; @@ -956,7 +959,7 @@ static void command_line_scan(mparm_T *parmp) /* "-w {scriptout}" write to script */ if (ascii_isdigit(((char_u *)argv[0])[argv_idx])) { n = get_number_arg(argv[0], &argv_idx, 10); - set_option_value((char_u *)"window", n, NULL, 0); + set_option_value("window", n, NULL, 0); break; } want_argument = TRUE; @@ -1088,7 +1091,7 @@ scripterror: if (ascii_isdigit(*((char_u *)argv[0]))) { argv_idx = 0; n = get_number_arg(argv[0], &argv_idx, 10); - set_option_value((char_u *)"window", n, NULL, 0); + set_option_value("window", n, NULL, 0); argv_idx = -1; break; } diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 1d1e13e7e0..ae94ec7ecd 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -62,7 +62,7 @@ int setmark(int c) /// Free fmark_T item void free_fmark(fmark_T fm) { - dict_unref(fm.additional_data); + tv_dict_unref(fm.additional_data); } /// Free xfmark_T item diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 57518a9535..621fa6fefc 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -583,7 +583,7 @@ int utf_ptr2char(const char_u *p) * If byte sequence is illegal or incomplete, returns -1 and does not advance * "s". */ -static int utf_safe_read_char_adv(char_u **s, size_t *n) +static int utf_safe_read_char_adv(const char_u **s, size_t *n) { int c; @@ -1233,7 +1233,8 @@ bool utf_isupper(int a) return utf_tolower(a) != a; } -static int utf_strnicmp(char_u *s1, char_u *s2, size_t n1, size_t n2) +static int utf_strnicmp(const char_u *s1, const char_u *s2, size_t n1, + size_t n2) { int c1, c2, cdiff; char_u buffer[6]; @@ -1392,19 +1393,26 @@ int utf16_to_utf8(const WCHAR *strw, char **str) * Returns zero if s1 and s2 are equal (ignoring case), the difference between * two characters otherwise. */ -int mb_strnicmp(char_u *s1, char_u *s2, size_t nn) +int mb_strnicmp(const char_u *s1, const char_u *s2, const size_t nn) { return utf_strnicmp(s1, s2, nn, nn); } -/* We need to call mb_stricmp() even when we aren't dealing with a multi-byte - * encoding because mb_stricmp() takes care of all ascii and non-ascii - * encodings, including characters with umlauts in latin1, etc., while - * STRICMP() only handles the system locale version, which often does not - * handle non-ascii properly. */ -int mb_stricmp(char_u *s1, char_u *s2) +/// Compare strings case-insensitively +/// +/// @note We need to call mb_stricmp() even when we aren't dealing with +/// a multi-byte encoding because mb_stricmp() takes care of all ASCII and +/// non-ascii encodings, including characters with umlauts in latin1, +/// etc., while STRICMP() only handles the system locale version, which +/// often does not handle non-ascii properly. +/// +/// @param[in] s1 First string to compare, not more then #MAXCOL characters. +/// @param[in] s2 Second string to compare, not more then #MAXCOL characters. +/// +/// @return 0 if strings are equal, <0 if s1 < s2, >0 if s1 > s2. +int mb_stricmp(const char *s1, const char *s2) { - return mb_strnicmp(s1, s2, MAXCOL); + return mb_strnicmp((const char_u *)s1, (const char_u *)s2, MAXCOL); } /* @@ -2020,8 +2028,8 @@ void * my_iconv_open(char_u *to, char_u *from) * Returns the converted string in allocated memory. NULL for an error. * If resultlenp is not NULL, sets it to the result length in bytes. */ -static char_u * iconv_string(vimconv_T *vcp, char_u *str, size_t slen, - size_t *unconvlenp, size_t *resultlenp) +static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, + size_t slen, size_t *unconvlenp, size_t *resultlenp) { const char *from; size_t fromlen; @@ -2306,7 +2314,7 @@ int convert_setup_ext(vimconv_T *vcp, char_u *from, bool from_unicode_is_utf8, * Illegal chars are often changed to "?", unless vcp->vc_fail is set. * When something goes wrong, NULL is returned and "*lenp" is unchanged. */ -char_u * string_convert(vimconv_T *vcp, char_u *ptr, size_t *lenp) +char_u *string_convert(const vimconv_T *const vcp, char_u *ptr, size_t *lenp) { return string_convert_ext(vcp, ptr, lenp, NULL); } @@ -2316,7 +2324,7 @@ char_u * string_convert(vimconv_T *vcp, char_u *ptr, size_t *lenp) * an incomplete sequence at the end it is not converted and "*unconvlenp" is * set to the number of remaining bytes. */ -char_u * string_convert_ext(vimconv_T *vcp, char_u *ptr, +char_u * string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp, size_t *unconvlenp) { char_u *retval = NULL; diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h index 5f5bab9fcd..c20e6d47ff 100644 --- a/src/nvim/mbyte.h +++ b/src/nvim/mbyte.h @@ -2,8 +2,10 @@ #define NVIM_MBYTE_H #include +#include #include "nvim/iconv.h" +#include "nvim/func_attr.h" /* * Return byte length of character that starts with byte "b". @@ -66,4 +68,17 @@ typedef struct { #ifdef INCLUDE_GENERATED_DECLARATIONS # include "mbyte.h.generated.h" #endif + +static inline int mb_strcmp_ic(bool ic, const char *s1, const char *s2) + REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Compare strings +/// +/// @param[in] ic True if case is to be ignored. +/// +/// @return 0 if s1 == s2, <0 if s1 < s2, >0 if s1 > s2. +static inline int mb_strcmp_ic(bool ic, const char *s1, const char *s2) +{ + return (ic ? mb_stricmp(s1, s2) : strcmp(s1, s2)); +} #endif // NVIM_MBYTE_H diff --git a/src/nvim/memline.c b/src/nvim/memline.c index f9d3751390..5ea2397db3 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -992,7 +992,7 @@ void ml_recover(void) if (b0_ff != 0) set_fileformat(b0_ff - 1, OPT_LOCAL); if (b0_fenc != NULL) { - set_option_value((char_u *)"fenc", 0L, b0_fenc, OPT_LOCAL); + set_option_value("fenc", 0L, (char *)b0_fenc, OPT_LOCAL); xfree(b0_fenc); } unchanged(curbuf, TRUE); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 85cef59aec..d634a8c393 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -16,6 +16,7 @@ #include "nvim/cursor.h" #include "nvim/edit.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_getln.h" @@ -888,7 +889,7 @@ static void set_yreg_additional_data(yankreg_T *reg, dict_T *additional_data) if (reg->additional_data == additional_data) { return; } - dict_unref(reg->additional_data); + tv_dict_unref(reg->additional_data); reg->additional_data = additional_data; } @@ -2581,7 +2582,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) textlock++; apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf); textlock--; - dict_clear(dict); + tv_dict_clear(dict); recursive = false; } diff --git a/src/nvim/option.c b/src/nvim/option.c index b037b0ae35..4c8606a999 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -25,6 +25,7 @@ #include #include "nvim/vim.h" +#include "nvim/macros.h" #include "nvim/ascii.h" #include "nvim/edit.h" #include "nvim/option.h" @@ -34,6 +35,7 @@ #include "nvim/diff.h" #include "nvim/digraph.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" @@ -604,7 +606,7 @@ void set_init_1(void) /* * 'maxmemtot' and 'maxmem' may have to be adjusted for available memory */ - opt_idx = findoption((char_u *)"maxmemtot"); + opt_idx = findoption("maxmemtot"); if (opt_idx >= 0) { { /* Use half of amount of memory available to Vim. */ @@ -614,7 +616,7 @@ void set_init_1(void) ? UINTPTR_MAX : (uintptr_t)(available_kib /2); options[opt_idx].def_val[VI_DEFAULT] = (char_u *)n; - opt_idx = findoption((char_u *)"maxmem"); + opt_idx = findoption("maxmem"); if (opt_idx >= 0) { options[opt_idx].def_val[VI_DEFAULT] = (char_u *)n; } @@ -645,7 +647,7 @@ void set_init_1(void) } } buf[j] = NUL; - opt_idx = findoption((char_u *)"cdpath"); + opt_idx = findoption("cdpath"); if (opt_idx >= 0) { options[opt_idx].def_val[VI_DEFAULT] = buf; options[opt_idx].flags |= P_DEF_ALLOCED; @@ -764,8 +766,9 @@ void set_init_1(void) * NOTE: mlterm's author is being asked to 'set' a variable * instead of an environment variable due to inheritance. */ - if (os_env_exists("MLTERM")) - set_option_value((char_u *)"tbidi", 1L, NULL, 0); + if (os_env_exists("MLTERM")) { + set_option_value("tbidi", 1L, NULL, 0); + } didset_options2(); @@ -775,7 +778,7 @@ void set_init_1(void) char_u *p = enc_locale(); if (p == NULL) { // use utf-8 as 'default' if locale encoding can't be detected. - p = vim_strsave((char_u *)"utf-8"); + p = (char_u *)xmemdupz(S_LEN("utf-8")); } fenc_default = p; @@ -882,7 +885,7 @@ set_options_default ( static void set_string_default(const char *name, char *val, bool allocated) FUNC_ATTR_NONNULL_ALL { - int opt_idx = findoption((char_u *)name); + int opt_idx = findoption(name); if (opt_idx >= 0) { if (options[opt_idx].flags & P_DEF_ALLOCED) { xfree(options[opt_idx].def_val[VI_DEFAULT]); @@ -904,9 +907,10 @@ void set_number_default(char *name, long val) { int opt_idx; - opt_idx = findoption((char_u *)name); - if (opt_idx >= 0) + opt_idx = findoption(name); + if (opt_idx >= 0) { options[opt_idx].def_val[VI_DEFAULT] = (char_u *)val; + } } #if defined(EXITFREE) @@ -947,17 +951,19 @@ void set_init_2(void) * wrong when the window height changes. */ set_number_default("scroll", Rows / 2); - idx = findoption((char_u *)"scroll"); - if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) + idx = findoption("scroll"); + if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) { set_option_default(idx, OPT_LOCAL, p_cp); + } comp_col(); /* * 'window' is only for backwards compatibility with Vi. * Default is Rows - 1. */ - if (!option_was_set((char_u *)"window")) + if (!option_was_set("window")) { p_window = Rows - 1; + } set_number_default("window", Rows - 1); parse_shape_opt(SHAPE_CURSOR); /* set cursor shapes from 'guicursor' */ (void)parse_printoptions(); /* parse 'printoptions' default value */ @@ -976,16 +982,18 @@ void set_init_3(void) int idx_sp; int do_sp; - idx_srr = findoption((char_u *)"srr"); - if (idx_srr < 0) - do_srr = FALSE; - else + idx_srr = findoption("srr"); + if (idx_srr < 0) { + do_srr = false; + } else { do_srr = !(options[idx_srr].flags & P_WAS_SET); - idx_sp = findoption((char_u *)"sp"); - if (idx_sp < 0) - do_sp = FALSE; - else + } + idx_sp = findoption("sp"); + if (idx_sp < 0) { + do_sp = false; + } else { do_sp = !(options[idx_sp].flags & P_WAS_SET); + } size_t len = 0; char_u *p = (char_u *)invocation_path_tail(p_sh, &len); @@ -1029,7 +1037,7 @@ void set_init_3(void) } if (bufempty()) { - int idx_ffs = findoption((char_u *)"ffs"); + int idx_ffs = findoption_len(S_LEN("ffs")); // Apply the first entry of 'fileformats' to the initial buffer. if (idx_ffs >= 0 && (options[idx_ffs].flags & P_WAS_SET)) { @@ -1048,14 +1056,16 @@ void set_helplang_default(const char *lang) { int idx; - if (lang == NULL || STRLEN(lang) < 2) /* safety check */ + const size_t lang_len = strlen(lang); + if (lang == NULL || lang_len < 2) { // safety check return; - idx = findoption((char_u *)"hlg"); + } + idx = findoption("hlg"); if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) { if (options[idx].flags & P_ALLOCED) free_string_option(p_hlg); - p_hlg = (char_u *)xstrdup(lang); - /* zh_CN becomes "cn", zh_TW becomes "tw". */ + p_hlg = (char_u *)xmemdupz(lang, lang_len); + // zh_CN becomes "cn", zh_TW becomes "tw". if (STRNICMP(p_hlg, "zh_", 3) == 0 && STRLEN(p_hlg) >= 5) { p_hlg[0] = (char_u)TOLOWER_ASC(p_hlg[3]); p_hlg[1] = (char_u)TOLOWER_ASC(p_hlg[4]); @@ -1082,12 +1092,12 @@ void set_title_defaults(void) * icon name. Saves a bit of time, because the X11 display server does * not need to be contacted. */ - idx1 = findoption((char_u *)"title"); + idx1 = findoption("title"); if (idx1 >= 0 && !(options[idx1].flags & P_WAS_SET)) { options[idx1].def_val[VI_DEFAULT] = (char_u *)(intptr_t)0; p_title = 0; } - idx1 = findoption((char_u *)"icon"); + idx1 = findoption("icon"); if (idx1 >= 0 && !(options[idx1].flags & P_WAS_SET)) { options[idx1].def_val[VI_DEFAULT] = (char_u *)(intptr_t)0; p_icon = 0; @@ -1193,7 +1203,7 @@ do_set ( goto skip; } if (arg[1] == 't' && arg[2] == '_') { // could be term code - opt_idx = findoption_len(arg + 1, (size_t) (len - 1)); + opt_idx = findoption_len((const char *)arg + 1, (size_t)(len - 1)); } len++; if (opt_idx == -1) { @@ -1209,7 +1219,7 @@ do_set ( len++; } } - opt_idx = findoption_len(arg, (size_t) len); + opt_idx = findoption_len((const char *)arg, (size_t)len); if (opt_idx == -1) { key = find_key_option(arg); } @@ -1391,11 +1401,10 @@ do_set ( value = prefix; } - errmsg = set_bool_option(opt_idx, varp, (int)value, - opt_flags); - } else { /* numeric or string */ - if (vim_strchr((char_u *)"=:&<", nextchar) == NULL - || prefix != 1) { + errmsg = (char_u *)set_bool_option(opt_idx, varp, (int)value, + opt_flags); + } else { // Numeric or string. + if (strchr("=:&<", nextchar) == NULL || prefix != 1) { errmsg = e_invarg; goto skip; } @@ -1448,15 +1457,19 @@ do_set ( goto skip; } - if (adding) + if (adding) { value = *(long *)varp + value; - if (prepending) + } + if (prepending) { value = *(long *)varp * value; - if (removing) + } + if (removing) { value = *(long *)varp - value; - errmsg = set_num_option(opt_idx, varp, value, - errbuf, sizeof(errbuf), opt_flags); - } else if (opt_idx >= 0) { /* string */ + } + errmsg = (char_u *)set_num_option(opt_idx, varp, value, + errbuf, sizeof(errbuf), + opt_flags); + } else if (opt_idx >= 0) { // String. char_u *save_arg = NULL; char_u *s = NULL; char_u *oldval = NULL; // previous value if *varp @@ -2221,7 +2234,7 @@ static void check_string_option(char_u **pp) */ int was_set_insecurely(char_u *opt, int opt_flags) { - int idx = findoption(opt); + int idx = findoption((const char *)opt); if (idx >= 0) { uint32_t *flagp = insecure_flag(idx, opt_flags); @@ -2283,9 +2296,9 @@ set_string_option_direct ( int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; int idx = opt_idx; - if (idx == -1) { /* use name */ - idx = findoption(name); - if (idx < 0) { /* not found (should not happen) */ + if (idx == -1) { // Use name. + idx = findoption((const char *)name); + if (idx < 0) { // Not found (should not happen). EMSG2(_(e_intern2), "set_string_option_direct()"); EMSG2(_("For option %s"), name); return; @@ -2765,7 +2778,7 @@ did_set_string_option ( // option. opt_idx = ((options[opt_idx].fullname[0] == 'v') ? (shada_idx == -1 - ? ((shada_idx = findoption((char_u *) "shada"))) + ? ((shada_idx = findoption("shada"))) : shada_idx) : opt_idx); // Update free_oldval now that we have the opt_idx for 'shada', otherwise @@ -3575,24 +3588,24 @@ static void set_option_scriptID_idx(int opt_idx, int opt_flags, int id) } } -/* - * Set the value of a boolean option, and take care of side effects. - * Returns NULL for success, or an error message for an error. - */ -static char_u * -set_bool_option ( - int opt_idx, /* index in options[] table */ - char_u *varp, /* pointer to the option variable */ - int value, /* new value */ - int opt_flags /* OPT_LOCAL and/or OPT_GLOBAL */ -) +/// Set the value of a boolean option, taking care of side effects +/// +/// @param[in] opt_idx Option index in options[] table. +/// @param[out] varp Pointer to the option variable. +/// @param[in] value New value. +/// @param[in] opt_flags OPT_LOCAL and/or OPT_GLOBAL. +/// +/// @return NULL on success, error message on error. +static char *set_bool_option(const int opt_idx, char_u *const varp, + const int value, + const int opt_flags) { int old_value = *(int *)varp; /* Disallow changing some options from secure mode */ if ((secure || sandbox != 0) && (options[opt_idx].flags & P_SECURE)) { - return e_secure; + return (char *)e_secure; } *(int *)varp = value; /* set the new value */ @@ -3605,20 +3618,18 @@ set_bool_option ( *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) = value; // Ensure that options set to p_force_on cannot be disabled. - if ((int *)varp == &p_force_on && p_force_on == FALSE) { - p_force_on = TRUE; - return e_unsupportedoption; - } + if ((int *)varp == &p_force_on && p_force_on == false) { + p_force_on = true; + return (char *)e_unsupportedoption; // Ensure that options set to p_force_off cannot be enabled. - else if ((int *)varp == &p_force_off && p_force_off == TRUE) { - p_force_off = FALSE; - return e_unsupportedoption; - } - /* 'undofile' */ - else if ((int *)varp == &curbuf->b_p_udf || (int *)varp == &p_udf) { - /* Only take action when the option was set. When reset we do not - * delete the undo file, the option may be set again without making - * any changes in between. */ + } else if ((int *)varp == &p_force_off && p_force_off == true) { + p_force_off = false; + return (char *)e_unsupportedoption; + // 'undofile' + } else if ((int *)varp == &curbuf->b_p_udf || (int *)varp == &p_udf) { + // Only take action when the option was set. When reset we do not + // delete the undo file, the option may be set again without making + // any changes in between. if (curbuf->b_p_udf || p_udf) { char_u hash[UNDO_HASH_SIZE]; buf_T *save_curbuf = curbuf; @@ -3740,8 +3751,8 @@ set_bool_option ( if (curwin->w_p_pvw) { FOR_ALL_WINDOWS_IN_TAB(win, curtab) { if (win->w_p_pvw && win != curwin) { - curwin->w_p_pvw = FALSE; - return (char_u *)N_("E590: A preview window already exists"); + curwin->w_p_pvw = false; + return N_("E590: A preview window already exists"); } } } @@ -3889,9 +3900,8 @@ set_bool_option ( /* set 'delcombine' */ p_deco = TRUE; - /* Force-set the necessary keymap for arabic */ - set_option_value((char_u *)"keymap", 0L, (char_u *)"arabic", - OPT_LOCAL); + // Force-set the necessary keymap for arabic. + set_option_value("keymap", 0L, "arabic", OPT_LOCAL); p_altkeymap = 0; p_hkmap = 0; p_fkmap = 0; @@ -3957,20 +3967,18 @@ set_bool_option ( return NULL; } -/* - * Set the value of a number option, and take care of side effects. - * Returns NULL for success, or an error message for an error. - */ -static char_u * -set_num_option ( - int opt_idx, /* index in options[] table */ - char_u *varp, /* pointer to the option variable */ - long value, /* new value */ - char_u *errbuf, /* buffer for error messages */ - size_t errbuflen, /* length of "errbuf" */ - int opt_flags /* OPT_LOCAL, OPT_GLOBAL and - OPT_MODELINE */ -) +/// Set the value of a number option, taking care of side effects +/// +/// @param[in] opt_idx Option index in options[] table. +/// @param[out] varp Pointer to the option variable. +/// @param[in] value New value. +/// @param errbuf Buffer for error messages. +/// @param[in] errbuflen Length of `errbuf`. +/// @param[in] opt_flags OPT_LOCAL, OPT_GLOBAL or OPT_MODELINE. +/// +/// @return NULL on success, error message on error. +static char *set_num_option(int opt_idx, char_u *varp, long value, + char_u *errbuf, size_t errbuflen, int opt_flags) { char_u *errmsg = NULL; long old_value = *(long *)varp; @@ -3981,7 +3989,7 @@ set_num_option ( /* Disallow changing some options from secure mode. */ if ((secure || sandbox != 0) && (options[opt_idx].flags & P_SECURE)) { - return e_secure; + return (char *)e_secure; } *pp = value; @@ -4251,8 +4259,9 @@ set_num_option ( cmdline_row = (int)(Rows - p_ch); } } - if (p_window >= Rows || !option_was_set((char_u *)"window")) + if (p_window >= Rows || !option_was_set("window")) { p_window = Rows - 1; + } } if (curbuf->b_p_ts <= 0) { @@ -4357,7 +4366,7 @@ set_num_option ( curwin->w_set_curswant = TRUE; check_redraw(options[opt_idx].flags); - return errmsg; + return (char *)errmsg; } /* @@ -4395,39 +4404,36 @@ static void check_redraw(uint32_t flags) /// @param[in] len Length of the option. /// /// @return Index of the option or -1 if option was not found. -int findoption_len(const char_u *const arg, const size_t len) +int findoption_len(const char *const arg, const size_t len) { - char *s, *p; + const char *s; + const char *p; static int quick_tab[27] = { 0, 0 }; // quick access table - int is_term_opt; - /* - * For first call: Initialize the quick-access table. - * It contains the index for the first option that starts with a certain - * letter. There are 26 letters, plus the first "t_" option. - */ + // For first call: Initialize the quick-access table. + // It contains the index for the first option that starts with a certain + // letter. There are 26 letters, plus the first "t_" option. if (quick_tab[1] == 0) { p = options[0].fullname; for (short int i = 1; (s = options[i].fullname) != NULL; i++) { if (s[0] != p[0]) { - if (s[0] == 't' && s[1] == '_') + if (s[0] == 't' && s[1] == '_') { quick_tab[26] = i; - else + } else { quick_tab[CharOrdLow(s[0])] = i; + } } p = s; } } - /* - * Check for name starting with an illegal character. - */ + // Check for name starting with an illegal character. if (len == 0 || arg[0] < 'a' || arg[0] > 'z') { return -1; } int opt_idx; - is_term_opt = (len > 2 && arg[0] == 't' && arg[1] == '_'); + const bool is_term_opt = (len > 2 && arg[0] == 't' && arg[1] == '_'); if (is_term_opt) { opt_idx = quick_tab[26]; } else { @@ -4435,7 +4441,7 @@ int findoption_len(const char_u *const arg, const size_t len) } // Match full name for (; (s = options[opt_idx].fullname) != NULL; opt_idx++) { - if (STRNCMP(arg, s, len) == 0 && s[len] == NUL) { + if (strncmp(arg, s, len) == 0 && s[len] == NUL) { break; } } @@ -4444,20 +4450,22 @@ int findoption_len(const char_u *const arg, const size_t len) // Match short name for (; options[opt_idx].fullname != NULL; opt_idx++) { s = options[opt_idx].shortname; - if (s != NULL && STRNCMP(arg, s, len) == 0 && s[len] == NUL) { + if (s != NULL && strncmp(arg, s, len) == 0 && s[len] == NUL) { break; } s = NULL; } } - if (s == NULL) + if (s == NULL) { opt_idx = -1; + } return opt_idx; } -bool is_tty_option(char *name) +bool is_tty_option(const char *name) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - return (name[0] == 't' && name[1] == '_') || !strcmp((char *)name, "term"); + return (name[0] == 't' && name[1] == '_') || strcmp(name, "term") == 0; } #define TCO_BUFFER_SIZE 8 @@ -4493,7 +4501,7 @@ bool get_tty_option(char *name, char **value) return false; } -bool set_tty_option(char *name, char *value) +bool set_tty_option(const char *name, const char *value) { if (!strcmp(name, "t_Co")) { int colors = atoi(value); @@ -4504,23 +4512,24 @@ bool set_tty_option(char *name, char *value) if (colors != t_colors) { t_colors = colors; // We now have a different color setup, initialize it again. - init_highlight(TRUE, FALSE); + init_highlight(true, false); } return true; } - return is_tty_option(name) || !strcmp(name, "term") - || !strcmp(name, "ttytype"); + return (is_tty_option(name) || !strcmp(name, "term") + || !strcmp(name, "ttytype")); } -/* - * Find index for option 'arg'. - * Return -1 if not found. - */ -static int findoption(char_u *arg) +/// Find index for an option +/// +/// @param[in] arg Option name. +/// +/// @return Option index or -1 if option was not found. +static int findoption(const char *const arg) { - return findoption_len(arg, STRLEN(arg)); + return findoption_len(arg, strlen(arg)); } /* @@ -4548,9 +4557,10 @@ get_option_value ( int opt_idx; char_u *varp; - opt_idx = findoption(name); - if (opt_idx < 0) /* unknown option */ + opt_idx = findoption((const char *)name); + if (opt_idx < 0) { // Unknown option. return -3; + } varp = get_varp_scope(&(options[opt_idx]), opt_flags); @@ -4608,7 +4618,7 @@ int get_option_value_strict(char *name, char_u *varp = NULL; vimoption_T *p; int rv = 0; - int opt_idx = findoption((uint8_t *)name); + int opt_idx = findoption(name); if (opt_idx < 0) { return 0; } @@ -4702,21 +4712,19 @@ int get_option_value_strict(char *name, return rv; } -/* - * Set the value of option "name". - * Use "string" for string options, use "number" for other options. - * - * Returns NULL on success or error message on error. - */ -char_u * -set_option_value ( - char_u *name, - long number, - char_u *string, - int opt_flags /* OPT_LOCAL or 0 (both) */ -) +/// Set the value of an option +/// +/// @param[in] name Option name. +/// @param[in] number New value for the number or boolean option. +/// @param[in] string New value for string option. +/// @param[in] opt_flags Flags: OPT_LOCAL or 0 (both). +/// +/// @return NULL on success, error message on error. +char *set_option_value(const char *const name, const long number, + const char *const string, const int opt_flags) + FUNC_ATTR_NONNULL_ARG(1) { - if (set_tty_option((char *)name, (char *)string)) { + if (set_tty_option(name, string)) { return NULL; } @@ -4724,9 +4732,9 @@ set_option_value ( char_u *varp; opt_idx = findoption(name); - if (opt_idx < 0) + if (opt_idx < 0) { EMSG2(_("E355: Unknown option: %s"), name); - else { + } else { uint32_t flags = options[opt_idx].flags; // Disallow changing some options in the sandbox if (sandbox > 0 && (flags & P_SECURE)) { @@ -4734,11 +4742,11 @@ set_option_value ( return NULL; } if (flags & P_STRING) { - const char *s = (const char *)string; + const char *s = string; if (s == NULL) { s = ""; } - return (char_u *)set_string_option(opt_idx, s, opt_flags); + return set_string_option(opt_idx, s, opt_flags); } else { varp = get_varp_scope(&(options[opt_idx]), opt_flags); if (varp != NULL) { /* hidden option is not changed */ @@ -4757,12 +4765,11 @@ set_option_value ( return NULL; // do nothing as we hit an error } } - if (flags & P_NUM) - return set_num_option(opt_idx, varp, number, - NULL, 0, opt_flags); - else - return set_bool_option(opt_idx, varp, (int)number, - opt_flags); + if (flags & P_NUM) { + return set_num_option(opt_idx, varp, number, NULL, 0, opt_flags); + } else { + return set_bool_option(opt_idx, varp, (int)number, opt_flags); + } } } } @@ -4773,9 +4780,10 @@ char_u *get_highlight_default(void) { int i; - i = findoption((char_u *)"hl"); - if (i >= 0) + i = findoption("hl"); + if (i >= 0) { return options[i].def_val[VI_DEFAULT]; + } return (char_u *)NULL; } @@ -5212,7 +5220,7 @@ void unset_global_local_option(char *name, void *from) vimoption_T *p; buf_T *buf = (buf_T *)from; - int opt_idx = findoption((uint8_t *)name); + int opt_idx = findoption(name); if (opt_idx < 0) { EMSG2(_("E355: Unknown option: %s"), name); return; @@ -5775,11 +5783,12 @@ void reset_modifiable(void) { int opt_idx; - curbuf->b_p_ma = FALSE; - p_ma = FALSE; - opt_idx = findoption((char_u *)"ma"); - if (opt_idx >= 0) - options[opt_idx].def_val[VI_DEFAULT] = FALSE; + curbuf->b_p_ma = false; + p_ma = false; + opt_idx = findoption("ma"); + if (opt_idx >= 0) { + options[opt_idx].def_val[VI_DEFAULT] = false; + } } /* @@ -5877,15 +5886,15 @@ set_context_in_set_cmd ( expand_option_name[2] = p[-2]; expand_option_name[3] = p[-1]; } else { - /* Allow * wildcard */ - while (ASCII_ISALNUM(*p) || *p == '_' || *p == '*') + // Allow * wildcard. + while (ASCII_ISALNUM(*p) || *p == '_' || *p == '*') { p++; - if (*p == NUL) + } + if (*p == NUL) { return; + } nextchar = *p; - *p = NUL; - opt_idx = findoption(arg); - *p = nextchar; + opt_idx = findoption_len((const char *)arg, (size_t)(p - arg)); if (opt_idx == -1 || options[opt_idx].var == NULL) { xp->xp_context = EXPAND_NOTHING; return; @@ -6047,7 +6056,7 @@ void ExpandOldSetting(int *num_file, char_u ***file) * For a terminal key code expand_option_idx is < 0. */ if (expand_option_idx < 0) { - expand_option_idx = findoption(expand_option_name); + expand_option_idx = findoption((const char *)expand_option_name); } if (expand_option_idx >= 0) { @@ -6447,20 +6456,22 @@ void vimrc_found(char_u *fname, char_u *envname) } } -/* - * Return TRUE when option "name" has been set. - * Only works correctly for global options. - */ -int option_was_set(char_u *name) +/// Check whether global option has been set +/// +/// @param[in] name Option name. +/// +/// @return True if it was set. +static bool option_was_set(const char *name) { int idx; idx = findoption(name); - if (idx < 0) /* unknown option */ - return FALSE; - if (options[idx].flags & P_WAS_SET) - return TRUE; - return FALSE; + if (idx < 0) { // Unknown option. + return false; + } else if (options[idx].flags & P_WAS_SET) { + return true; + } + return false; } /* @@ -6945,10 +6956,11 @@ bool signcolumn_on(win_T *wp) return wp->w_buffer->b_signlist != NULL; } -/// Get window or buffer local options. -dict_T * get_winbuf_options(int bufopt) +/// Get window or buffer local options +dict_T *get_winbuf_options(const int bufopt) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { - dict_T *d = dict_alloc(); + dict_T *const d = tv_dict_alloc(); for (int opt_idx = 0; options[opt_idx].fullname; opt_idx++) { struct vimoption *opt = &options[opt_idx]; diff --git a/src/nvim/path.c b/src/nvim/path.c index dfcafc85de..2bd87b608e 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -282,48 +282,63 @@ bool dir_of_file_exists(char_u *fname) return retval; } -/* - * Versions of fnamecmp() and fnamencmp() that handle '/' and '\' equally - * and deal with 'fileignorecase'. - */ -int vim_fnamecmp(char_u *x, char_u *y) +/// Compare two file names +/// +/// Handles '/' and '\\' correctly and deals with &fileignorecase option. +/// +/// @param[in] fname1 First file name. +/// @param[in] fname2 Second file name. +/// +/// @return 0 if they are equal, non-zero otherwise. +int path_fnamecmp(const char *fname1, const char *fname2) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { #ifdef BACKSLASH_IN_FILENAME - return vim_fnamencmp(x, y, MAXPATHL); + const size_t len1 = strlen(fname1); + const size_t len2 = strlen(fname2); + return path_fnamencmp(fname1, fname2, MAX(len1, len2)); #else - if (p_fic) - return mb_stricmp(x, y); - return STRCMP(x, y); + return mb_strcmp_ic((bool)p_fic, fname1, fname2); #endif } -int vim_fnamencmp(char_u *x, char_u *y, size_t len) +/// Compare two file names +/// +/// Handles '/' and '\\' correctly and deals with &fileignorecase option. +/// +/// @param[in] fname1 First file name. +/// @param[in] fname2 Second file name. +/// @param[in] len Compare at most len bytes. +/// +/// @return 0 if they are equal, non-zero otherwise. +int path_fnamencmp(const char *const fname1, const char *const fname2, + const size_t len) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { #ifdef BACKSLASH_IN_FILENAME - char_u *px = x; - char_u *py = y; - int cx = NUL; - int cy = NUL; + int c1 = NUL; + int c2 = NUL; + const char *p1 = fname1; + const char *p2 = fname2; while (len > 0) { - cx = PTR2CHAR(px); - cy = PTR2CHAR(py); - if (cx == NUL || cy == NUL - || ((p_fic ? vim_tolower(cx) != vim_tolower(cy) : cx != cy) - && !(cx == '/' && cy == '\\') - && !(cx == '\\' && cy == '/'))) + c1 = PTR2CHAR(p1); + c2 = PTR2CHAR(p2); + if (c1 == NUL || c2 == NUL + || (!((c1 == '/' || c1 == '\\') && (c2 == '\\' || c2 == '/'))) + || (p_fic ? (c1 != c2 && CH_FOLD(c1) != CH_FOLD(c2)) : c1 != c2)) { break; - len -= MB_PTR2LEN(px); - px += MB_PTR2LEN(px); - py += MB_PTR2LEN(py); + } + len -= MB_PTR2LEN(p1); + p1 += MB_PTR2LEN(p1); + p2 += MB_PTR2LEN(p2); } - if (len == 0) - return 0; - return cx - cy; + return c1 - c2; #else - if (p_fic) - return mb_strnicmp(x, y, len); - return STRNCMP(x, y, len); + if (p_fic) { + return mb_strnicmp((const char_u *)fname1, (const char_u *)fname2, len); + } + return strncmp(fname1, fname2, len); #endif } diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index ea00afbd86..6346951c05 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -610,13 +610,10 @@ static int pum_set_selected(int n, int repeat) if (res == OK) { // Edit a new, empty buffer. Set options for a "wipeout" // buffer. - set_option_value((char_u *)"swf", 0L, NULL, OPT_LOCAL); - set_option_value((char_u *)"bt", 0L, - (char_u *)"nofile", OPT_LOCAL); - set_option_value((char_u *)"bh", 0L, - (char_u *)"wipe", OPT_LOCAL); - set_option_value((char_u *)"diff", 0L, - NULL, OPT_LOCAL); + set_option_value("swf", 0L, NULL, OPT_LOCAL); + set_option_value("bt", 0L, "nofile", OPT_LOCAL); + set_option_value("bh", 0L, "wipe", OPT_LOCAL); + set_option_value("diff", 0L, NULL, OPT_LOCAL); } } diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 323503c4f5..d23059ff22 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -1236,7 +1236,7 @@ static int qf_add_entry(qf_info_T *qi, char_u *dir, char_u *fname, int bufnum, qfp->qf_nr = nr; if (type != 1 && !vim_isprintc(type)) /* only printable chars allowed */ type = 0; - qfp->qf_type = type; + qfp->qf_type = (char_u)type; qfp->qf_valid = valid; lastp = &qi->qf_lists[qi->qf_curlist].qf_last; @@ -2581,15 +2581,13 @@ void ex_copen(exarg_T *eap) else { /* Create a new quickfix buffer */ (void)do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, oldwin); - /* switch off 'swapfile' */ - set_option_value((char_u *)"swf", 0L, NULL, OPT_LOCAL); - set_option_value((char_u *)"bt", 0L, (char_u *)"quickfix", - OPT_LOCAL); - set_option_value((char_u *)"bh", 0L, (char_u *)"wipe", OPT_LOCAL); + // Switch off 'swapfile'. + set_option_value("swf", 0L, NULL, OPT_LOCAL); + set_option_value("bt", 0L, "quickfix", OPT_LOCAL); + set_option_value("bh", 0L, "wipe", OPT_LOCAL); RESET_BINDING(curwin); - curwin->w_p_diff = FALSE; - set_option_value((char_u *)"fdm", 0L, (char_u *)"manual", - OPT_LOCAL); + curwin->w_p_diff = false; + set_option_value("fdm", 0L, "manual", OPT_LOCAL); } /* Only set the height when still in the same tab page and there is no @@ -2901,14 +2899,14 @@ static void qf_fill_buffer(qf_info_T *qi, buf_T *buf, qfline_T *old_last) } } - /* correct cursor position */ - check_lnums(TRUE); + // Correct cursor position. + check_lnums(true); if (old_last == NULL) { // Set the 'filetype' to "qf" each time after filling the buffer. This // resembles reading a file into a buffer, it's more logical when using // autocommands. - set_option_value((char_u *)"ft", 0L, (char_u *)"qf", OPT_LOCAL); + set_option_value("ft", 0L, "qf", OPT_LOCAL); curbuf->b_p_ma = false; keep_filetype = true; // don't detect 'filetype' @@ -4002,7 +4000,7 @@ int get_errorlist(win_T *wp, int qf_idx, list_T *list) if (bufnum != 0 && (buflist_findnr(bufnum) == NULL)) bufnum = 0; - dict = dict_alloc(); + dict = tv_dict_alloc(); tv_list_append_dict(list, dict); buf[0] = qfp->qf_type; @@ -4057,7 +4055,7 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) int flags = QF_GETLIST_NONE; int qf_idx = qi->qf_curlist; // default is the current list - if ((di = dict_find(what, (char_u *)"nr", -1)) != NULL) { + if ((di = tv_dict_find(what, S_LEN("nr"))) != NULL) { // Use the specified quickfix/location list if (di->di_tv.v_type == VAR_NUMBER) { qf_idx = di->di_tv.vval.v_number - 1; @@ -4070,15 +4068,15 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) } } - if (dict_find(what, (char_u *)"all", -1) != NULL) { + if (tv_dict_find(what, S_LEN("all")) != NULL) { flags |= QF_GETLIST_ALL; } - if (dict_find(what, (char_u *)"title", -1) != NULL) { + if (tv_dict_find(what, S_LEN("title")) != NULL) { flags |= QF_GETLIST_TITLE; } - if (dict_find(what, (char_u *)"winid", -1) != NULL) { + if (tv_dict_find(what, S_LEN("winid")) != NULL) { flags |= QF_GETLIST_WINID; } @@ -4132,17 +4130,18 @@ static int qf_add_entries(qf_info_T *qi, list_T *list, char_u *title, if (d == NULL) continue; - char_u *filename = get_dict_string(d, "filename", true); - int bufnum = (int)get_dict_number(d, "bufnr"); - long lnum = get_dict_number(d, "lnum"); - int col = (int)get_dict_number(d, "col"); - char_u vcol = (char_u)get_dict_number(d, "vcol"); - int nr = (int)get_dict_number(d, "nr"); - char_u *type = get_dict_string(d, "type", true); - char_u *pattern = get_dict_string(d, "pattern", true); - char_u *text = get_dict_string(d, "text", true); + char *const filename = tv_dict_get_string(d, "filename", true); + int bufnum = (int)tv_dict_get_number(d, "bufnr"); + long lnum = tv_dict_get_number(d, "lnum"); + int col = (int)tv_dict_get_number(d, "col"); + char_u vcol = (char_u)tv_dict_get_number(d, "vcol"); + int nr = (int)tv_dict_get_number(d, "nr"); + const char *type_str = tv_dict_get_string(d, "type", false); + const char_u type = (char_u)(uint8_t)(type_str == NULL ? NUL : *type_str); + char *const pattern = tv_dict_get_string(d, "pattern", true); + char *text = tv_dict_get_string(d, "text", true); if (text == NULL) { - text = vim_strsave((char_u *)""); + text = xcalloc(1, 1); } bool valid = true; if ((filename == NULL && bufnum == 0) || (lnum == 0 && pattern == NULL)) { @@ -4162,21 +4161,20 @@ static int qf_add_entries(qf_info_T *qi, list_T *list, char_u *title, int status = qf_add_entry(qi, NULL, // dir - filename, + (char_u *)filename, bufnum, - text, + (char_u *)text, lnum, col, vcol, // vis_col - pattern, // search pattern + (char_u *)pattern, // search pattern nr, - (char_u)(type == NULL ? NUL : *type), + type, valid); xfree(filename); xfree(pattern); xfree(text); - xfree(type); if (status == FAIL) { retval = FAIL; @@ -4213,7 +4211,7 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action) newlist = true; } int qf_idx = qi->qf_curlist; // default is the current list - if ((di = dict_find(what, (char_u *)"nr", -1)) != NULL) { + if ((di = tv_dict_find(what, S_LEN("nr"))) != NULL) { // Use the specified quickfix/location list if (di->di_tv.v_type == VAR_NUMBER) { qf_idx = di->di_tv.vval.v_number - 1; @@ -4231,10 +4229,11 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action) qf_idx = qi->qf_curlist; } - if ((di = dict_find(what, (char_u *)"title", -1)) != NULL) { + if ((di = tv_dict_find(what, S_LEN("title"))) != NULL) { if (di->di_tv.v_type == VAR_STRING) { xfree(qi->qf_lists[qf_idx].qf_title); - qi->qf_lists[qf_idx].qf_title = get_dict_string(what, "title", true); + qi->qf_lists[qf_idx].qf_title = (char_u *)tv_dict_get_string( + what, "title", true); if (qf_idx == qi->qf_curlist) { qf_update_win_titlevar(qi); } diff --git a/src/nvim/search.c b/src/nvim/search.c index 8c56eda7cf..fb9e820928 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -282,7 +282,7 @@ void restore_search_patterns(void) static inline void free_spat(struct spat *const spat) { xfree(spat->pat); - dict_unref(spat->additional_data); + tv_dict_unref(spat->additional_data); } #if defined(EXITFREE) @@ -1290,10 +1290,11 @@ int search_for_exact_line(buf_T *buf, pos_T *pos, int dir, char_u *pat) * ignored because we are interested in the next line -- Acevedo */ if ((compl_cont_status & CONT_ADDING) && !(compl_cont_status & CONT_SOL)) { - if ((p_ic ? mb_stricmp(p, pat) : STRCMP(p, pat)) == 0) + if (mb_strcmp_ic((bool)p_ic, (const char *)p, (const char *)pat) == 0) { return OK; - } else if (*p != NUL) { /* ignore empty lines */ - /* expanding lines or words */ + } + } else if (*p != NUL) { // Ignore empty lines. + // Expanding lines or words. assert(compl_length >= 0); if ((p_ic ? mb_strnicmp(p, pat, (size_t)compl_length) : STRNCMP(p, pat, compl_length)) == 0) diff --git a/src/nvim/shada.c b/src/nvim/shada.c index c550cb0888..2fe042cda8 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -1623,10 +1623,10 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, for (const hashitem_T *hi= d->dv_hashtab.ht_array; todo; hi++) { \ if (!HASHITEM_EMPTY(hi)) { \ todo--; \ - dictitem_T *const di = HI2DI(hi); \ - const size_t key_len = strlen((const char *) hi->hi_key); \ + dictitem_T *const di = TV_DICT_HI2DI(hi); \ + const size_t key_len = strlen((const char *)hi->hi_key); \ msgpack_pack_str(spacker, key_len); \ - msgpack_pack_str_body(spacker, (const char *) hi->hi_key, key_len); \ + msgpack_pack_str_body(spacker, (const char *)hi->hi_key, key_len); \ if (encode_vim_to_msgpack(spacker, &di->di_tv, \ _("additional data of ShaDa " what)) \ == FAIL) { \ @@ -3156,17 +3156,17 @@ static void shada_free_shada_entry(ShadaEntry *const entry) case kSDItemJump: case kSDItemGlobalMark: case kSDItemLocalMark: { - dict_unref(entry->data.filemark.additional_data); + tv_dict_unref(entry->data.filemark.additional_data); xfree(entry->data.filemark.fname); break; } case kSDItemSearchPattern: { - dict_unref(entry->data.search_pattern.additional_data); + tv_dict_unref(entry->data.search_pattern.additional_data); xfree(entry->data.search_pattern.pat); break; } case kSDItemRegister: { - dict_unref(entry->data.reg.additional_data); + tv_dict_unref(entry->data.reg.additional_data); for (size_t i = 0; i < entry->data.reg.contents_size; i++) { xfree(entry->data.reg.contents[i]); } @@ -3192,7 +3192,7 @@ static void shada_free_shada_entry(ShadaEntry *const entry) case kSDItemBufferList: { for (size_t i = 0; i < entry->data.buffer_list.size; i++) { xfree(entry->data.buffer_list.buffers[i].fname); - dict_unref(entry->data.buffer_list.buffers[i].additional_data); + tv_dict_unref(entry->data.buffer_list.buffers[i].additional_data); } xfree(entry->data.buffer_list.buffers); break; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 5ca5ab3339..56f0350aef 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -7134,16 +7134,17 @@ void ex_spelldump(exarg_T *eap) char_u *spl; long dummy; - if (no_spell_checking(curwin)) + if (no_spell_checking(curwin)) { return; - get_option_value((char_u*)"spl", &dummy, &spl, OPT_LOCAL); + } + get_option_value((char_u *)"spl", &dummy, &spl, OPT_LOCAL); // Create a new empty buffer in a new window. do_cmdline_cmd("new"); // enable spelling locally in the new window - set_option_value((char_u*)"spell", true, (char_u*)"", OPT_LOCAL); - set_option_value((char_u*)"spl", dummy, spl, OPT_LOCAL); + set_option_value("spell", true, "", OPT_LOCAL); + set_option_value("spl", dummy, (char *)spl, OPT_LOCAL); xfree(spl); if (!bufempty()) { diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index c108ae4a2c..6ba2801203 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -5439,7 +5439,7 @@ static void init_spellfile(void) fname != NULL && strstr((char *)path_tail(fname), ".ascii.") != NULL ? (char_u *)"ascii" : spell_enc()); - set_option_value((char_u *)"spellfile", 0L, buf, OPT_LOCAL); + set_option_value("spellfile", 0L, (const char *)buf, OPT_LOCAL); break; } aspath = false; diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 0a27d9dd92..632a5bf2a5 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -1406,14 +1406,14 @@ static int syn_stack_equal(synstate_T *sp) /* If the pointer is different it can still be the * same text. Compare the strings, ignore case when * the start item has the sp_ic flag set. */ - if (bsx->matches[j] == NULL - || six->matches[j] == NULL) + if (bsx->matches[j] == NULL || six->matches[j] == NULL) { break; - if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic - ? mb_stricmp(bsx->matches[j], - six->matches[j]) != 0 - : STRCMP(bsx->matches[j], six->matches[j]) != 0) + } + if (mb_strcmp_ic((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic, + (const char *)bsx->matches[j], + (const char *)six->matches[j]) != 0) { break; + } } } if (j != NSUBEXP) @@ -6510,16 +6510,16 @@ do_highlight ( if (!ui_rgb_attached()) { must_redraw = CLEAR; if (color >= 0) { - if (t_colors < 16) + if (t_colors < 16) { i = (color == 0 || color == 4); - else + } else { i = (color < 7 || color == 8); - /* Set the 'background' option if the value is - * wrong. */ - if (i != (*p_bg == 'd')) - set_option_value((char_u *)"bg", 0L, - i ? (char_u *)"dark" - : (char_u *)"light", 0); + } + // Set the 'background' option if the value is + // wrong. + if (i != (*p_bg == 'd')) { + set_option_value("bg", 0L, (i ? "dark" : "light"), 0); + } } } } @@ -7113,7 +7113,7 @@ set_hl_attr ( * Lookup a highlight group name and return it's ID. * If it is not found, 0 is returned. */ -int syn_name2id(char_u *name) +int syn_name2id(const char_u *name) { int i; char_u name_u[200]; diff --git a/src/nvim/tag.c b/src/nvim/tag.c index b0ffc12b5f..af19a1bfc4 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -773,7 +773,7 @@ do_tag ( cmd[len] = NUL; } - dict = dict_alloc(); + dict = tv_dict_alloc(); tv_list_append_dict(list, dict); dict_add_nr_str(dict, "text", 0L, tag_name); @@ -2768,8 +2768,8 @@ add_tag_field ( int len = 0; int retval; - /* check that the field name doesn't exist yet */ - if (dict_find(dict, (char_u *)field_name, -1) != NULL) { + // Check that the field name doesn't exist yet. + if (tv_dict_find(dict, field_name, -1) != NULL) { if (p_verbose > 0) { verbose_enter(); smsg(_("Duplicate field name: %s"), field_name); @@ -2824,7 +2824,7 @@ int get_tags(list_T *list, char_u *pat) if (STRNCMP(tp.tagname, "!_TAG_", 6) == 0) continue; - dict = dict_alloc(); + dict = tv_dict_alloc(); tv_list_append_dict(list, dict); full_fname = tag_full_fname(&tp); diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index c81e883fb9..85c4950b42 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -226,17 +226,17 @@ Terminal *terminal_open(TerminalOptions opts) rv->invalid_start = 0; rv->invalid_end = opts.height; refresh_screen(rv, curbuf); - set_option_value((uint8_t *)"buftype", 0, (uint8_t *)"terminal", OPT_LOCAL); + set_option_value("buftype", 0, "terminal", OPT_LOCAL); // Default settings for terminal buffers curbuf->b_p_ma = false; // 'nomodifiable' curbuf->b_p_ul = -1; // 'undolevels' curbuf->b_p_scbk = p_scbk; // 'scrollback' curbuf->b_p_tw = 0; // 'textwidth' - set_option_value((uint8_t *)"wrap", false, NULL, OPT_LOCAL); - set_option_value((uint8_t *)"number", false, NULL, OPT_LOCAL); - set_option_value((uint8_t *)"relativenumber", false, NULL, OPT_LOCAL); - set_option_value((uint8_t *)"list", false, NULL, OPT_LOCAL); + set_option_value("wrap", false, NULL, OPT_LOCAL); + set_option_value("number", false, NULL, OPT_LOCAL); + set_option_value("relativenumber", false, NULL, OPT_LOCAL); + set_option_value("list", false, NULL, OPT_LOCAL); buf_set_term_title(curbuf, (char *)curbuf->b_ffname); RESET_BINDING(curwin); // Reset cursor in current window. diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 729cf03e15..6e21dccebb 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -317,7 +317,7 @@ static long get_undolevel(void) static inline void zero_fmark_additional_data(fmark_T *fmarks) { for (size_t i = 0; i < NMARKS; i++) { - dict_unref(fmarks[i].additional_data); + tv_dict_unref(fmarks[i].additional_data); fmarks[i].additional_data = NULL; } } @@ -2941,7 +2941,7 @@ void u_eval_tree(u_header_T *first_uhp, list_T *list) dict_T *dict; while (uhp != NULL) { - dict = dict_alloc(); + dict = tv_dict_alloc(); dict_add_nr_str(dict, "seq", uhp->uh_seq, NULL); dict_add_nr_str(dict, "time", (long)uhp->uh_time, NULL); if (uhp == curbuf->b_u_newhead) diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 87e9889465..ae654251f9 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -109,11 +109,14 @@ Error: configure did not run properly.Check auto/config.log. // all mode bits used for mapping #define MAP_ALL_MODES (0x3f | SELECTMODE | TERM_FOCUS) -/* directions */ -#define FORWARD 1 -#define BACKWARD (-1) -#define FORWARD_FILE 3 -#define BACKWARD_FILE (-3) +/// Directions. +typedef enum { + kDirectionNotSet = 0, + FORWARD = 1, + BACKWARD = (-1), + FORWARD_FILE = 3, + BACKWARD_FILE = (-3), +} Direction; /* return values for functions */ #if !(defined(OK) && (OK == 1)) @@ -282,15 +285,22 @@ enum { #define SHOWCMD_COLS 10 /* columns needed by shown command */ #define STL_MAX_ITEM 80 /* max nr of % in statusline */ -/* - * fnamecmp() is used to compare file names. - * On some systems case in a file name does not matter, on others it does. - * (this does not account for maximum name lengths and things like "../dir", - * thus it is not 100% accurate!) - */ -#define fnamecmp(x, y) vim_fnamecmp((char_u *)(x), (char_u *)(y)) -#define fnamencmp(x, y, n) vim_fnamencmp((char_u *)(x), (char_u *)(y), \ - (size_t)(n)) +/// Compare file names +/// +/// On some systems case in a file name does not matter, on others it does. +/// +/// @note Does not account for maximum name lengths and things like "../dir", +/// thus it is not 100% accurate. OS may also use different algorythm for +/// case-insensitive comparison. +/// +/// @param[in] x First file name to compare. +/// @param[in] y Second file name to compare. +/// +/// @return 0 for equal file names, non-zero otherwise. +#define fnamecmp(x, y) path_fnamecmp((const char *)(x), (const char *)(y)) +#define fnamencmp(x, y, n) path_fnamencmp((const char *)(x), \ + (const char *)(y), \ + (size_t)(n)) /* * Enums need a typecast to be used as array index (for Ultrix). diff --git a/src/nvim/window.c b/src/nvim/window.c index 47a97da0cf..a3b0e6fc2d 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2983,8 +2983,8 @@ static tabpage_T *alloc_tabpage(void) tp->handle = ++last_tp_handle; handle_register_tabpage(tp); - /* init t: variables */ - tp->tp_vars = dict_alloc(); + // Init t: variables. + tp->tp_vars = tv_dict_alloc(); init_var_dict(tp->tp_vars, &tp->tp_winvar, VAR_SCOPE); tp->tp_diff_invalid = TRUE; tp->tp_ch_used = p_ch; @@ -3811,8 +3811,8 @@ static win_T *win_alloc(win_T *after, int hidden) new_wp->handle = ++last_win_id; handle_register_window(new_wp); - /* init w: variables */ - new_wp->w_vars = dict_alloc(); + // Init w: variables. + new_wp->w_vars = tv_dict_alloc(); init_var_dict(new_wp->w_vars, &new_wp->w_winvar, VAR_SCOPE); /* Don't execute autocommands while the window is not properly @@ -5504,9 +5504,9 @@ void restore_buffer(bufref_T *save_curbuf) // 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. -int match_add(win_T *wp, char_u *grp, char_u *pat, +int match_add(win_T *wp, const char *const grp, const char *const pat, int prio, int id, list_T *pos_list, - char_u *conceal_char) + const char *const conceal_char) { matchitem_T *cur; matchitem_T *prev; @@ -5534,11 +5534,11 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, cur = cur->next; } } - if ((hlg_id = syn_namen2id(grp, (int)STRLEN(grp))) == 0) { + if ((hlg_id = syn_name2id((const char_u *)grp)) == 0) { EMSG2(_(e_nogroup), grp); return -1; } - if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL) { + if (pat != NULL && (regprog = vim_regcomp((char_u *)pat, RE_MAGIC)) == NULL) { EMSG2(_(e_invarg2), pat); return -1; } @@ -5557,14 +5557,14 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, m = xcalloc(1, sizeof(matchitem_T)); m->id = id; m->priority = prio; - m->pattern = pat == NULL ? NULL: vim_strsave(pat); + m->pattern = pat == NULL ? NULL: (char_u *)xstrdup(pat); m->hlg_id = hlg_id; m->match.regprog = regprog; m->match.rmm_ic = FALSE; m->match.rmm_maxcol = 0; m->conceal_char = 0; if (conceal_char != NULL) { - m->conceal_char = (*mb_ptr2char)(conceal_char); + m->conceal_char = (*mb_ptr2char)((const char_u *)conceal_char); } // Set up position matches diff --git a/test/functional/eval/match_functions_spec.lua b/test/functional/eval/match_functions_spec.lua new file mode 100644 index 0000000000..df91e41475 --- /dev/null +++ b/test/functional/eval/match_functions_spec.lua @@ -0,0 +1,39 @@ +local helpers = require('test.functional.helpers')(after_each) + +local eq = helpers.eq +local clear = helpers.clear +local funcs = helpers.funcs +local command = helpers.command + +before_each(clear) + +describe('setmatches()', function() + it('correctly handles case when both group and pattern entries are numbers', + function() + command('hi def link 1 PreProc') + eq(0, funcs.setmatches({{group=1, pattern=2, id=3, priority=4}})) + eq({{ + group='1', + pattern='2', + id=3, + priority=4, + }}, funcs.getmatches()) + eq(0, funcs.setmatches({{group=1, pattern=2, id=3, priority=4, conceal=5}})) + eq({{ + group='1', + pattern='2', + id=3, + priority=4, + conceal='5', + }}, funcs.getmatches()) + eq(0, funcs.setmatches({{group=1, pos1={2}, pos2={6}, id=3, priority=4, conceal=5}})) + eq({{ + group='1', + pos1={2}, + pos2={6}, + id=3, + priority=4, + conceal='5', + }}, funcs.getmatches()) + end) +end) diff --git a/test/functional/eval/string_spec.lua b/test/functional/eval/string_spec.lua index f6279e85e8..adc1af9b8e 100644 --- a/test/functional/eval/string_spec.lua +++ b/test/functional/eval/string_spec.lua @@ -7,7 +7,6 @@ local eval = helpers.eval local exc_exec = helpers.exc_exec local redir_exec = helpers.redir_exec local funcs = helpers.funcs -local write_file = helpers.write_file local NIL = helpers.NIL local source = helpers.source local dedent = helpers.dedent @@ -105,10 +104,8 @@ describe('string() function', function() end) describe('used to represent funcrefs', function() - local fname = 'Xtest-functional-eval-string_spec-fref-script.vim' - before_each(function() - write_file(fname, [[ + source([[ function Test1() endfunction @@ -120,11 +117,6 @@ describe('string() function', function() let g:Test2_f = function('s:Test2') ]]) - command('source ' .. fname) - end) - - after_each(function() - os.remove(fname) end) it('dumps references to built-in functions', function() diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua index 30753c34ac..057245db1c 100644 --- a/test/functional/ex_cmds/dict_notifications_spec.lua +++ b/test/functional/ex_cmds/dict_notifications_spec.lua @@ -48,7 +48,7 @@ describe('dictionary change notifications', function() eq({'notification', 'values', {key, vals}}, next_msg()) end - describe('watcher', function() + describe(dict_expr .. ' watcher', function() if dict_init then setup(function() source(dict_init) @@ -58,7 +58,7 @@ describe('dictionary change notifications', function() before_each(function() source([[ function! g:Changed(dict, key, value) - if a:dict != ]]..dict_expr..[[ | + if a:dict isnot ]]..dict_expr..[[ | throw 'invalid dict' endif call rpcnotify(g:channel, 'values', a:key, a:value) diff --git a/test/functional/ex_cmds/quickfix_commands_spec.lua b/test/functional/ex_cmds/quickfix_commands_spec.lua new file mode 100644 index 0000000000..5ab34db3fb --- /dev/null +++ b/test/functional/ex_cmds/quickfix_commands_spec.lua @@ -0,0 +1,83 @@ +local helpers = require('test.functional.helpers')(after_each) + +local eq = helpers.eq +local clear = helpers.clear +local funcs = helpers.funcs +local command = helpers.command +local exc_exec = helpers.exc_exec +local write_file = helpers.write_file +local curbufmeths = helpers.curbufmeths + +local file_base = 'Xtest-functional-ex_cmds-quickfix_commands' + +before_each(clear) + +for _, c in ipairs({'l', 'c'}) do + local file = ('%s.%s'):format(file_base, c) + local filecmd = c .. 'file' + local getfcmd = c .. 'getfile' + local addfcmd = c .. 'addfile' + local getlist = (c == 'c') and funcs.getqflist or ( + function() return funcs.getloclist(0) end) + + describe((':%s*file commands'):format(c), function() + before_each(function() + write_file(file, ([[ + %s-1.res:700:10:Line 700 + %s-2.res:800:15:Line 800 + ]]):format(file, file)) + end) + after_each(function() + os.remove(file) + end) + + it('work', function() + command(('%s %s'):format(filecmd, file)) + -- Second line of each entry (i.e. `nr=-1, …`) was obtained from actual + -- results. First line (i.e. `{lnum=…`) was obtained from legacy test. + local list = { + {lnum=700, col=10, text='Line 700', + nr=-1, bufnr=2, valid=1, pattern='', vcol=0, ['type']=''}, + {lnum=800, col=15, text='Line 800', + nr=-1, bufnr=3, valid=1, pattern='', vcol=0, ['type']=''}, + } + eq(list, getlist()) + eq(('%s-1.res'):format(file), funcs.bufname(list[1].bufnr)) + eq(('%s-2.res'):format(file), funcs.bufname(list[2].bufnr)) + + -- Run cfile/lfile from a modified buffer + command('enew!') + curbufmeths.set_lines(1, 1, true, {'Quickfix'}) + eq(('Vim(%s):E37: No write since last change (add ! to override)'):format( + filecmd), + exc_exec(('%s %s'):format(filecmd, file))) + + write_file(file, ([[ + %s-3.res:900:30:Line 900 + ]]):format(file)) + command(('%s %s'):format(addfcmd, file)) + list[#list + 1] = { + lnum=900, col=30, text='Line 900', + nr=-1, bufnr=5, valid=1, pattern='', vcol=0, ['type']='', + } + eq(list, getlist()) + eq(('%s-3.res'):format(file), funcs.bufname(list[3].bufnr)) + + write_file(file, ([[ + %s-1.res:222:77:Line 222 + %s-2.res:333:88:Line 333 + ]]):format(file, file)) + command('enew!') + command(('%s %s'):format(getfcmd, file)) + list = { + {lnum=222, col=77, text='Line 222', + nr=-1, bufnr=2, valid=1, pattern='', vcol=0, ['type']=''}, + {lnum=333, col=88, text='Line 333', + nr=-1, bufnr=3, valid=1, pattern='', vcol=0, ['type']=''}, + } + eq(list, getlist()) + eq(('%s-1.res'):format(file), funcs.bufname(list[1].bufnr)) + eq(('%s-2.res'):format(file), funcs.bufname(list[2].bufnr)) + end) + end) +end diff --git a/test/functional/viml/completion_spec.lua b/test/functional/viml/completion_spec.lua index cd5f4260e0..3c09d71eb7 100644 --- a/test/functional/viml/completion_spec.lua +++ b/test/functional/viml/completion_spec.lua @@ -3,6 +3,7 @@ local Screen = require('test.functional.ui.screen') local clear, feed = helpers.clear, helpers.feed local eval, eq, neq = helpers.eval, helpers.eq, helpers.neq local execute, source, expect = helpers.execute, helpers.source, helpers.expect +local meths = helpers.meths if helpers.pending_win32(pending) then return end @@ -814,6 +815,41 @@ describe('completion', function() end) end) + describe('with numeric items', function() + before_each(function() + source([[ + function! TestComplete() abort + call complete(1, g:_complist) + return '' + endfunction + ]]) + meths.set_option('completeopt', 'menuone,noselect') + meths.set_var('_complist', {{ + word=0, + abbr=1, + menu=2, + kind=3, + info=4, + icase=5, + dup=6, + empty=7, + }}) + end) + + it('shows correct variant as word', function() + feed('i=TestComplete()') + screen:expect([[ + ^ | + {1:1 3 2 }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + end) + end) end) describe('External completion popupmenu', function() diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index 49f929937f..be77ef4c83 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -272,17 +272,17 @@ local lua2typvalt_type_tab = { processed[l].dv_refcount = processed[l].dv_refcount + 1 return typvalt(eval.VAR_DICT, {v_dict=processed[l]}) end - local dct = eval.dict_alloc() + local dct = eval.tv_dict_alloc() dct.dv_refcount = 1 processed[l] = dct local ret = typvalt(eval.VAR_DICT, {v_dict=dct}) for k, v in pairs(l) do if type(k) == 'string' then - local di = eval.dictitem_alloc(to_cstr(k)) + local di = eval.tv_dict_item_alloc(to_cstr(k)) local val_tv = ffi.gc(lua2typvalt(v, processed), nil) eval.copy_tv(val_tv, di.di_tv) eval.tv_clear(val_tv) - eval.dict_add(dct, di) + eval.tv_dict_add(dct, di) end end return ret From 54bd2e8b731376b29c150f909c6b50103bfbb91a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 20 Aug 2016 22:25:57 +0300 Subject: [PATCH 0178/1671] eval: Make setmatches() return -1 in case of some failures --- src/nvim/eval.c | 17 ++++++++++++----- test/functional/eval/match_functions_spec.lua | 7 +++++++ .../legacy/063_match_and_matchadd_spec.lua | 5 ++--- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5015deead7..7ec1fda0a8 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -14680,6 +14680,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) clear_matches(curwin); li = l->lv_first; + bool match_add_failed = false; while (li != NULL) { int i = 0; @@ -14728,17 +14729,23 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) &conceal_di->di_tv) : NULL); if (i == 0) { - match_add(curwin, group, - tv_dict_get_string(d, "pattern", false), - priority, id, NULL, conceal); + if (match_add(curwin, group, + tv_dict_get_string(d, "pattern", false), + priority, id, NULL, conceal) != id) { + match_add_failed = true; + } } else { - match_add(curwin, group, NULL, priority, id, s, conceal); + if (match_add(curwin, group, NULL, priority, id, s, conceal) != id) { + match_add_failed = true; + } tv_list_unref(s); s = NULL; } li = li->li_next; } - rettv->vval.v_number = 0; + if (!match_add_failed) { + rettv->vval.v_number = 0; + } } } diff --git a/test/functional/eval/match_functions_spec.lua b/test/functional/eval/match_functions_spec.lua index df91e41475..f6bad59c66 100644 --- a/test/functional/eval/match_functions_spec.lua +++ b/test/functional/eval/match_functions_spec.lua @@ -36,4 +36,11 @@ describe('setmatches()', function() conceal='5', }}, funcs.getmatches()) end) + + it('fails with -1 if highlight group is not defined', function() + eq(-1, funcs.setmatches({{group=1, pattern=2, id=3, priority=4}})) + eq({}, funcs.getmatches()) + eq(-1, funcs.setmatches({{group=1, pos1={2}, pos2={6}, id=3, priority=4, conceal=5}})) + eq({}, funcs.getmatches()) + end) end) diff --git a/test/functional/legacy/063_match_and_matchadd_spec.lua b/test/functional/legacy/063_match_and_matchadd_spec.lua index 298e0a31ea..5818bb6b3a 100644 --- a/test/functional/legacy/063_match_and_matchadd_spec.lua +++ b/test/functional/legacy/063_match_and_matchadd_spec.lua @@ -97,11 +97,10 @@ describe('063: Test for ":match", "matchadd()" and related functions', function( -- Check that "setmatches()" will not add two matches with the same ID. The -- expected behaviour (for now) is to add the first match but not the - -- second and to return 0 (even though it is a matter of debate whether - -- this can be considered successful behaviour). + -- second and to return -1. execute("let r1 = setmatches([{'group': 'MyGroup1', 'pattern': 'TODO', 'priority': 10, 'id': 1}, {'group': 'MyGroup2', 'pattern': 'FIXME', 'priority': 10, 'id': 1}])") feed("") - eq(0, eval("r1")) + eq(-1, eval("r1")) eq({{group = 'MyGroup1', pattern = 'TODO', priority = 10, id = 1}}, eval('getmatches()')) -- Check that "setmatches()" returns 0 if successful and otherwise -1. From ecff8387f465bbf5d57de09498059ff1de671131 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 20 Aug 2016 23:12:15 +0300 Subject: [PATCH 0179/1671] eval: Move get_dict_callback to typval.c --- src/nvim/eval.c | 265 ++++++++++++++++++++--------------------- src/nvim/eval/typval.c | 36 ++++++ 2 files changed, 164 insertions(+), 137 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7ec1fda0a8..8d6b6d2dc1 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -729,8 +729,8 @@ void set_internal_string_var(char_u *name, char_u *value) } static lval_T *redir_lval = NULL; -static garray_T redir_ga; /* only valid when redir_lval is not NULL */ -static char_u *redir_endp = NULL; +static garray_T redir_ga; // Only valid when redir_lval is not NULL. +static char_u *redir_endp = NULL; static char_u *redir_varname = NULL; /* @@ -761,9 +761,9 @@ var_redir_start ( /* The output is stored in growarray "redir_ga" until redirection ends. */ ga_init(&redir_ga, (int)sizeof(char), 500); - /* Parse the variable name (can be a dict or list entry). */ - redir_endp = get_lval(redir_varname, NULL, redir_lval, FALSE, FALSE, 0, - FNE_CHECK_START); + // Parse the variable name (can be a dict or list entry). + redir_endp = (char_u *)get_lval(redir_varname, NULL, redir_lval, false, false, + 0, FNE_CHECK_START); if (redir_endp == NULL || redir_lval->ll_name == NULL || *redir_endp != NUL) { clear_lval(redir_lval); @@ -839,12 +839,13 @@ void var_redir_stop(void) ga_append(&redir_ga, NUL); /* Append the trailing NUL. */ tv.v_type = VAR_STRING; tv.vval.v_string = redir_ga.ga_data; - /* Call get_lval() again, if it's inside a Dict or List it may - * have changed. */ - redir_endp = get_lval(redir_varname, NULL, redir_lval, - FALSE, FALSE, 0, FNE_CHECK_START); - if (redir_endp != NULL && redir_lval->ll_name != NULL) - set_var_lval(redir_lval, redir_endp, &tv, FALSE, (char_u *)"."); + // Call get_lval() again, if it's inside a Dict or List it may + // have changed. + redir_endp = (char_u *)get_lval(redir_varname, NULL, redir_lval, + false, false, 0, FNE_CHECK_START); + if (redir_endp != NULL && redir_lval->ll_name != NULL) { + set_var_lval(redir_lval, redir_endp, &tv, false, (char_u *)"."); + } clear_lval(redir_lval); } @@ -1457,11 +1458,13 @@ void ex_let(exarg_T *eap) char_u *argend; int first = TRUE; - argend = skip_var_list(arg, &var_count, &semicolon); - if (argend == NULL) + argend = (char_u *)skip_var_list(arg, &var_count, &semicolon); + if (argend == NULL) { return; - if (argend > arg && argend[-1] == '.') /* for var.='str' */ - --argend; + } + if (argend > arg && argend[-1] == '.') { // For var.='str'. + argend--; + } expr = skipwhite(argend); if (*expr != '=' && !(vim_strchr((char_u *)"+-.", *expr) != NULL && expr[1] == '=')) { @@ -1527,7 +1530,7 @@ ex_let_vars ( char_u *nextchars ) { - char_u *arg = arg_start; + char_u *arg = arg_start; list_T *l; int i; listitem_T *item; @@ -1606,9 +1609,11 @@ ex_let_vars ( * for "[var, var; var]" set "semicolon". * Return NULL for an error. */ -static char_u *skip_var_list(char_u *arg, int *var_count, int *semicolon) +static const char_u *skip_var_list(const char_u *arg, int *var_count, + int *semicolon) { - char_u *p, *s; + const char_u *p; + const char_u *s; if (*arg == '[') { /* "[var, var]": find the matching ']'. */ @@ -1645,7 +1650,7 @@ static char_u *skip_var_list(char_u *arg, int *var_count, int *semicolon) * Skip one (assignable) variable name, including @r, $VAR, &option, d.key, * l[idx]. */ -static char_u *skip_var_one(char_u *arg) +static const char_u *skip_var_one(const char_u *arg) { if (*arg == '@' && arg[1] != NUL) return arg + 2; @@ -1825,34 +1830,36 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) // TODO(ZyX-I): move to eval/ex_cmds -/* - * Set one item of ":let var = expr" or ":let [v1, v2] = list" to its value. - * Returns a pointer to the char just after the var name. - * Returns NULL if there is an error. - */ -static char_u * -ex_let_one ( - char_u *arg, /* points to variable name */ - typval_T *tv, /* value to assign to variable */ - int copy, /* copy value from "tv" */ - char_u *endchars, /* valid chars after variable name or NULL */ - char_u *op /* "+", "-", "." or NULL*/ -) +/// Set one item of `:let var = expr` or `:let [v1, v2] = list` to its value +/// +/// @param[in] arg Start of the variable name. +/// @param[in] tv Value to assign to the variable. +/// @param[in] copy If true, copy value from `tv`. +/// @param[in] endchars Valid characters after variable name or NULL. +/// @param[in] op Operation performed: *op is `+`, `-`, `.` for `+=`, etc. +/// NULL for `=`. +/// +/// @return a pointer to the char just after the var name or NULL in case of +/// error. +static char_u *ex_let_one(char_u *arg, typval_T *const tv, + const bool copy, const char_u *const endchars, + const char_u *const op) + FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT { - char_u *name; - char_u *arg_end = NULL; + char_u *name; + char_u *arg_end = NULL; int len; int opt_flags; - char_u *tofree = NULL; + char_u *tofree = NULL; /* * ":let $VAR = expr": Set environment variable. */ if (*arg == '$') { - /* Find the end of the name. */ - ++arg; + // Find the end of the name. + arg++; name = arg; - len = get_env_len(&arg); + len = get_env_len((const char_u **)&arg); if (len == 0) { EMSG2(_(e_invarg2), name - 1); } else { @@ -1975,9 +1982,9 @@ ex_let_one ( char_u *const p = get_lval(arg, tv, &lv, false, false, 0, FNE_CHECK_START); if (p != NULL && lv.ll_name != NULL) { - if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL) + if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL) { EMSG(_(e_letunexp)); - else { + } else { set_var_lval(&lv, p, tv, copy, op); arg_end = p; } @@ -2020,7 +2027,6 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, FUNC_ATTR_NONNULL_ARG(1, 3) { char_u *p; - char_u *expr_start, *expr_end; int cc; dictitem_T *v; typval_T var1; @@ -2036,13 +2042,17 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, memset(lp, 0, sizeof(lval_T)); if (skip) { - /* 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); + // When skipping just find the end of the name. + lp->ll_name = (char_u *)name; + return (char_u *)find_name_end(name, NULL, NULL, FNE_INCL_BR | fne_flags); } - /* Find the end of the name. */ - p = find_name_end(name, &expr_start, &expr_end, fne_flags); + // Find the end of the name. + char_u *expr_start; + char_u *expr_end; + p = (char_u *)find_name_end(name, + (const char_u **)&expr_start, + (const char_u **)&expr_end, fne_flags); if (expr_start != NULL) { /* Don't expand the name when we already know there is an error. */ if (unlet && !ascii_iswhite(*p) && !ends_excmd(*p) @@ -2063,8 +2073,9 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } } lp->ll_name = lp->ll_exp_name; - } else - lp->ll_name = name; + } else { + lp->ll_name = (char_u *)name; + } /* Without [idx] or .key we are done. */ if ((*p != '[' && *p != '.') || lp->ll_name == NULL) @@ -2351,7 +2362,8 @@ static void clear_lval(lval_T *lp) * "endp" points to just after the parsed name. * "op" is NULL, "+" for "+=", "-" for "-=", "." for ".=" or "=" for "=". */ -static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, char_u *op) +static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, + int copy, const char_u *op) { int cc; listitem_T *ri; @@ -2493,10 +2505,10 @@ notify: * Set "*errp" to TRUE for an error, FALSE otherwise; * Return a pointer that holds the info. Null when there is an error. */ -void *eval_for_line(char_u *arg, bool *errp, char_u **nextcmdp, int skip) +void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip) { forinfo_T *fi = xcalloc(1, sizeof(forinfo_T)); - char_u *expr; + const char_u *expr; typval_T tv; list_T *l; @@ -2824,16 +2836,17 @@ void ex_lockvar(exarg_T *eap) static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep) { char_u *arg = argstart; - char_u *name_end; - int error = FALSE; + bool error = false; lval_T lv; do { - /* Parse the name and find the end. */ - name_end = get_lval(arg, NULL, &lv, TRUE, eap->skip || error, 0, - FNE_CHECK_START); - if (lv.ll_name == NULL) - error = TRUE; /* error but continue parsing */ + // Parse the name and find the end. + char_u *const name_end = (char_u *)get_lval(arg, NULL, &lv, true, + eap->skip || error, + 0, FNE_CHECK_START); + if (lv.ll_name == NULL) { + error = true; // error, but continue parsing. + } if (name_end == NULL || (!ascii_iswhite(*name_end) && !ends_excmd(*name_end))) { if (name_end != NULL) { @@ -2868,7 +2881,7 @@ static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep) // TODO(ZyX-I): move to eval/ex_cmds -static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) +static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) { int ret = OK; int cc; @@ -3022,7 +3035,7 @@ int do_unlet(char_u *name, int forceit) * "deep" is the levels to go (-1 for unlimited); * "lock" is TRUE for ":lockvar", FALSE for ":unlockvar". */ -static int do_lock_var(lval_T *lp, char_u *name_end, const int deep, +static int do_lock_var(lval_T *lp, char_u *const name_end, const int deep, const bool lock) { int ret = OK; @@ -5605,34 +5618,6 @@ void dict_set_keys_readonly(dict_T *const dict) }); } -/// Get a function from a dictionary -/// @param[out] result The address where a pointer to the wanted callback -/// will be left. -/// @return true/false on success/failure. -static bool get_dict_callback(dict_T *d, char *key, Callback *result) -{ - dictitem_T *const di = tv_dict_find(d, key, -1); - - if (di == NULL) { - result->type = kCallbackNone; - return true; - } - - if (di->di_tv.v_type != VAR_FUNC && di->di_tv.v_type != VAR_STRING - && di->di_tv.v_type != VAR_PARTIAL) { - EMSG(_("Argument is not a function or function name")); - result->type = kCallbackNone; - return false; - } - - typval_T tv; - copy_tv(&di->di_tv, &tv); - set_selfdict(&tv, d); - bool res = callback_from_typval(result, &tv); - tv_clear(&tv); - return res; -} - /* * Allocate a variable for a Dictionary and fill it from "*arg". * Return OK or FAIL. Returns NOTDONE for {expr}. @@ -5997,7 +5982,7 @@ static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate) ++*arg; name = *arg; - len = get_env_len(arg); + len = get_env_len((const char_u **)arg); if (evaluate) { if (len == 0) { @@ -11291,16 +11276,16 @@ static void f_isdirectory(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) { lval_T lv; - char_u *end; dictitem_T *di; rettv->vval.v_number = -1; - end = get_lval(get_tv_string(&argvars[0]), NULL, &lv, false, false, - GLV_NO_AUTOLOAD|GLV_READ_ONLY, FNE_CHECK_START); + const char_u *const end = get_lval(get_tv_string(&argvars[0]), NULL, + &lv, false, false, + GLV_NO_AUTOLOAD, FNE_CHECK_START); if (end != NULL && lv.ll_name != NULL) { - if (*end != NUL) + if (*end != NUL) { EMSG(_(e_trailing)); - else { + } else { if (lv.ll_tv == NULL) { di = find_var((const char *)lv.ll_name, STRLEN(lv.ll_name), NULL, true); if (di != NULL) { @@ -16580,7 +16565,8 @@ static void f_test_garbagecollect_now(typval_T *argvars, garbage_collect(true); } -static bool callback_from_typval(Callback *callback, typval_T *arg) +bool callback_from_typval(Callback *const callback, typval_T *const arg) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { if (arg->v_type == VAR_PARTIAL && arg->vval.v_partial != NULL) { callback->data.partial = arg->vval.v_partial; @@ -17698,15 +17684,16 @@ static int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp) * Advance "arg" to the first character after the name. * Return 0 for error. */ -static int get_env_len(char_u **arg) +static int get_env_len(const char_u **arg) { - char_u *p; int len; - for (p = *arg; vim_isIDc(*p); ++p) - ; - if (p == *arg) /* no name found */ + const char_u *p; + for (p = *arg; vim_isIDc(*p); p++) { + } + if (p == *arg) { // No name found. return 0; + } len = (int)(p - *arg); *arg = p; @@ -17758,8 +17745,6 @@ static int get_name_len(const char **const arg, int verbose) { int len; - char_u *expr_start; - char_u *expr_end; *alias = NULL; /* default to no alias */ @@ -17775,12 +17760,12 @@ static int get_name_len(const char **const arg, *arg += len; } - /* - * Find the end of the name; check for {} construction. - */ + // Find the end of the name; check for {} construction. + char_u *expr_start; + char_u *expr_end; const char *p = (const char *)find_name_end((char_u *)(*arg), - &expr_start, - &expr_end, + (const char_u **)&expr_start, + (const char_u **)&expr_end, len > 0 ? 0 : FNE_CHECK_START); if (expr_start != NULL) { if (!evaluate) { @@ -17816,12 +17801,11 @@ static int get_name_len(const char **const arg, // "flags" can have FNE_INCL_BR and FNE_CHECK_START. // Return a pointer to just after the name. Equal to "arg" if there is no // valid name. -static char_u *find_name_end(char_u *arg, char_u **expr_start, - char_u **expr_end, int flags) +static const char_u *find_name_end(const char_u *arg, const char_u **expr_start, + const char_u **expr_end, int flags) { int mb_nest = 0; int br_nest = 0; - char_u *p; int len; if (expr_start != NULL) { @@ -17834,6 +17818,7 @@ static char_u *find_name_end(char_u *arg, char_u **expr_start, return arg; } + const char_u *p; for (p = arg; *p != NUL && (eval_isnamec(*p) || *p == '{' @@ -17905,7 +17890,8 @@ static char_u *find_name_end(char_u *arg, char_u **expr_start, * Returns a new allocated string, which the caller must free. * Returns NULL for failure. */ -static char_u *make_expanded_name(char_u *in_start, char_u *expr_start, char_u *expr_end, char_u *in_end) +static char_u *make_expanded_name(const char_u *in_start, char_u *expr_start, + char_u *expr_end, char_u *in_end) { char_u c1; char_u *retval = NULL; @@ -17934,7 +17920,9 @@ static char_u *make_expanded_name(char_u *in_start, char_u *expr_start, char_u * *expr_end = '}'; if (retval != NULL) { - temp_result = find_name_end(retval, &expr_start, &expr_end, 0); + temp_result = (char_u *)find_name_end(retval, + (const char_u **)&expr_start, + (const char_u **)&expr_end, 0); if (expr_start != NULL) { /* Further expansion! */ temp_result = make_expanded_name(retval, expr_start, @@ -18351,7 +18339,7 @@ handle_subscript( return ret; } -static void set_selfdict(typval_T *rettv, dict_T *selfdict) +void set_selfdict(typval_T *rettv, dict_T *selfdict) { // Don't do this when "dict.Func" is already a partial that was bound // explicitly (pt_auto is false). @@ -20195,15 +20183,15 @@ ret_free: static char_u * trans_function_name( char_u **pp, - int skip, /* only find the end, don't evaluate */ + int skip, // only find the end, don't evaluate int flags, funcdict_T *fdp, // return: info about dictionary used partial_T **partial // return: partial of a FuncRef ) { char_u *name = NULL; - char_u *start; - char_u *end; + const char_u *start; + const char_u *end; int lead; char_u sid_buf[20]; int len; @@ -20229,9 +20217,9 @@ trans_function_name( start += lead; } - /* Note that TFN_ flags use the same values as GLV_ flags. */ - end = get_lval(start, NULL, &lv, FALSE, skip, flags, - lead > 2 ? 0 : FNE_CHECK_START); + // Note that TFN_ flags use the same values as GLV_ flags. + end = get_lval((char_u *)start, NULL, &lv, false, skip, flags, + lead > 2 ? 0 : FNE_CHECK_START); if (end == start) { if (!skip) EMSG(_("E129: Function name required")); @@ -20244,10 +20232,12 @@ trans_function_name( * interrupt, or an exception. */ if (!aborting()) { - if (end != NULL) - EMSG2(_(e_invarg2), start); - } else - *pp = find_name_end(start, NULL, NULL, FNE_INCL_BR); + if (end != NULL) { + emsgf(_(e_invarg2), start); + } + } else { + *pp = (char_u *)find_name_end(start, NULL, NULL, FNE_INCL_BR); + } goto theend; } @@ -20260,11 +20250,11 @@ trans_function_name( } if (lv.ll_tv->v_type == VAR_FUNC && lv.ll_tv->vval.v_string != NULL) { name = vim_strsave(lv.ll_tv->vval.v_string); - *pp = end; + *pp = (char_u *)end; } else if (lv.ll_tv->v_type == VAR_PARTIAL && lv.ll_tv->vval.v_partial != NULL) { name = vim_strsave(partial_name(lv.ll_tv->vval.v_partial)); - *pp = end; + *pp = (char_u *)end; if (partial != NULL) { *partial = lv.ll_tv->vval.v_partial; } @@ -20274,7 +20264,7 @@ trans_function_name( || fdp->fd_newkey == NULL)) { EMSG(_(e_funcref)); } else { - *pp = end; + *pp = (char_u *)end; } name = NULL; } @@ -20282,8 +20272,8 @@ trans_function_name( } if (lv.ll_name == NULL) { - /* Error found, but continue after the function name. */ - *pp = end; + // Error found, but continue after the function name. + *pp = (char_u *)end; goto theend; } @@ -20305,7 +20295,7 @@ trans_function_name( } if (name != NULL) { name = vim_strsave(name); - *pp = end; + *pp = (char_u *)end; if (strncmp((char *)name, "", 5) == 0) { // Change "" to the byte sequence. name[0] = K_SPECIAL; @@ -20379,7 +20369,7 @@ trans_function_name( } memmove(name + lead, lv.ll_name, (size_t)len); name[lead + len] = NUL; - *pp = end; + *pp = (char_u *)end; theend: clear_lval(&lv); @@ -20408,8 +20398,8 @@ static int eval_fname_script(const char *const p) /// Check whether function name starts with or s: /// -/// Only works for names previously checked by eval_fname_script(), if it -/// returned non-zero. +/// @warning Only works for names previously checked by eval_fname_script(), if +/// it returned non-zero. /// /// @param[in] name Name to check. /// @@ -20558,14 +20548,15 @@ bool translated_function_exists(const char *name) /// @return True if it exists, false otherwise. static bool function_exists(const char *const name, bool no_deref) { - char_u *nm = (char_u *)name; + const char_u *nm = (const char_u *)name; bool n = false; int flag = TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD; if (no_deref) { flag |= TFN_NO_DEREF; } - char *const p = (char *)trans_function_name(&nm, false, flag, NULL, NULL); + char *const p = (char *)trans_function_name((char_u **)&nm, false, flag, NULL, + NULL); nm = skipwhite(nm); /* Only accept "funcname", "funcname ", "funcname (..." and @@ -22510,9 +22501,9 @@ static inline TerminalJobData *common_job_init(char **argv, static inline bool common_job_callbacks(dict_T *vopts, Callback *on_stdout, Callback *on_stderr, Callback *on_exit) { - if (get_dict_callback(vopts, "on_stdout", on_stdout) - && get_dict_callback(vopts, "on_stderr", on_stderr) - && get_dict_callback(vopts, "on_exit", on_exit)) { + if (tv_dict_get_callback(vopts, S_LEN("on_stdout"), on_stdout) + &&tv_dict_get_callback(vopts, S_LEN("on_stderr"), on_stderr) + && tv_dict_get_callback(vopts, S_LEN("on_exit"), on_exit)) { vopts->dv_refcount++; return true; } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index bb7baed7d2..f4f34e0d92 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1130,6 +1130,42 @@ const char *tv_dict_get_string_buf(dict_T *const d, const char *const key, return (const char *)get_tv_string_buf(&di->di_tv, (char_u *)numbuf); } +/// Get a function from a dictionary +/// +/// @param[in] d Dictionary to get callback from. +/// @param[in] key Dictionary key. +/// @param[in] key_len Key length, may be -1 to use strlen(). +/// @param[out] result The address where a pointer to the wanted callback +/// will be left. +/// +/// @return true/false on success/failure. +bool tv_dict_get_callback(dict_T *const d, + const char *const key, const ptrdiff_t key_len, + Callback *const result) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + result->type = kCallbackNone; + + dictitem_T *const di = tv_dict_find(d, key, key_len); + + if (di == NULL) { + return true; + } + + if (di->di_tv.v_type != VAR_FUNC && di->di_tv.v_type != VAR_STRING + && di->di_tv.v_type != VAR_PARTIAL) { + EMSG(_("Argument is not a function or function name")); + return false; + } + + typval_T tv; + copy_tv(&di->di_tv, &tv); + set_selfdict(&tv, d); + bool res = callback_from_typval(result, &tv); + tv_clear(&tv); + return res; +} + //{{{2 Operations on the whole dict /// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary. From 983a5532ca17060508ea308a9b7069dae4facb59 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 20 Aug 2016 23:14:50 +0300 Subject: [PATCH 0180/1671] eval: Move dict_set_keys_readonly to typval.c --- src/nvim/eval.c | 15 +-------------- src/nvim/eval/typval.c | 17 +++++++++++++++-- src/nvim/file_search.c | 2 +- src/nvim/ops.c | 2 +- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8d6b6d2dc1..afbb93ef92 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5605,19 +5605,6 @@ int dict_add_dict(dict_T *d, char *key, dict_T *dict) return OK; } -/// Set all existing keys in "dict" as read-only. -/// -/// This does not protect against adding new keys to the Dictionary. -/// -/// @param dict The dict whose keys should be frozen -void dict_set_keys_readonly(dict_T *const dict) - FUNC_ATTR_NONNULL_ALL -{ - TV_DICT_ITER(dict, di, { - di->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX; - }); -} - /* * Allocate a variable for a Dictionary and fill it from "*arg". * Return OK or FAIL. Returns NOTDONE for {expr}. @@ -18083,7 +18070,7 @@ void set_vim_var_dict(const VimVarIndex idx, dict_T *const val) if (val != NULL) { val->dv_refcount++; // Set readonly - dict_set_keys_readonly(val); + tv_dict_set_keys_readonly(val); } } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index f4f34e0d92..0df818040b 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1135,8 +1135,8 @@ const char *tv_dict_get_string_buf(dict_T *const d, const char *const key, /// @param[in] d Dictionary to get callback from. /// @param[in] key Dictionary key. /// @param[in] key_len Key length, may be -1 to use strlen(). -/// @param[out] result The address where a pointer to the wanted callback -/// will be left. +/// @param[out] result The address where a pointer to the wanted callback +/// will be left. /// /// @return true/false on success/failure. bool tv_dict_get_callback(dict_T *const d, @@ -1348,6 +1348,19 @@ dict_T *tv_dict_copy(const vimconv_T *const conv, return copy; } +/// Set all existing keys in "dict" as read-only. +/// +/// This does not protect against adding new keys to the Dictionary. +/// +/// @param dict The dict whose keys should be frozen. +void tv_dict_set_keys_readonly(dict_T *const dict) + FUNC_ATTR_NONNULL_ALL +{ + TV_DICT_ITER(dict, di, { + di->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX; + }); +} + //{{{1 Generic typval operations //{{{2 Init/alloc/clear //{{{3 Alloc diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index 8ab6955042..d7120a7309 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1551,7 +1551,7 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope) dict_add_nr_str(dict, "scope", 0L, (char_u *)buf); dict_add_nr_str(dict, "cwd", 0L, (char_u *)new_dir); - dict_set_keys_readonly(dict); + tv_dict_set_keys_readonly(dict); apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, (char_u *)new_dir, false, NULL); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index d634a8c393..4bca2f0889 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2578,7 +2578,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) buf[1] = NUL; dict_add_nr_str(dict, "operator", 0, (char_u *)buf); - dict_set_keys_readonly(dict); + tv_dict_set_keys_readonly(dict); textlock++; apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf); textlock--; From 210342d795a13e88579d3e55ae931463d16f241d Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 11 Jan 2017 23:20:03 +0300 Subject: [PATCH 0181/1671] eval: Move dict_add_list and dict_add_dict to typval.c --- src/nvim/eval.c | 57 ++++++++---------------------------------- src/nvim/eval/typval.c | 52 ++++++++++++++++++++++++++++++++++++++ src/nvim/ops.c | 3 ++- src/nvim/undo.c | 3 ++- 4 files changed, 67 insertions(+), 48 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index afbb93ef92..e7c2d95cdd 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5569,42 +5569,6 @@ int dict_add_nr_str(dict_T *d, char *key, long nr, char_u *str) return OK; } -/* - * Add a list entry to dictionary "d". - * Returns FAIL when key already exists. - */ -int dict_add_list(dict_T *d, char *key, list_T *list) -{ - dictitem_T *item = tv_dict_item_alloc(key); - - item->di_tv.v_lock = 0; - item->di_tv.v_type = VAR_LIST; - item->di_tv.vval.v_list = list; - if (tv_dict_add(d, item) == FAIL) { - tv_dict_item_free(item); - return FAIL; - } - ++list->lv_refcount; - return OK; -} - -/// Add a dict entry to dictionary "d". -/// Returns FAIL when out of memory and when key already exists. -int dict_add_dict(dict_T *d, char *key, dict_T *dict) -{ - dictitem_T *const item = tv_dict_item_alloc(key); - - item->di_tv.v_lock = 0; - item->di_tv.v_type = VAR_DICT; - item->di_tv.vval.v_dict = dict; - if (tv_dict_add(d, item) == FAIL) { - tv_dict_item_free(item); - return FAIL; - } - dict->dv_refcount++; - return OK; -} - /* * Allocate a variable for a Dictionary and fill it from "*arg". * Return OK or FAIL. Returns NOTDONE for {expr}. @@ -9128,7 +9092,7 @@ static dict_T *get_buffer_info(buf_T *buf) NULL); // Get a reference to buffer variables - dict_add_dict(dict, "variables", buf->b_vars); + tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars); // List of windows displaying this buffer list_T *const windows = tv_list_alloc(); @@ -9137,13 +9101,13 @@ static dict_T *get_buffer_info(buf_T *buf) tv_list_append_number(windows, (varnumber_T)wp->handle); } } - dict_add_list(dict, "windows", windows); + tv_dict_add_list(dict, S_LEN("windows"), windows); if (buf->b_signlist != NULL) { // List of signs placed in this buffer list_T *const signs = tv_list_alloc(); get_buffer_signs(buf, signs); - dict_add_list(dict, "signs", signs); + tv_dict_add_list(dict, S_LEN("signs"), signs); } return dict; @@ -9900,7 +9864,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) // match added with matchaddpos() for (i = 0; i < MAXPOSMATCH; ++i) { llpos_T *llpos; - char buf[6]; + char buf[6]; llpos = &cur->pos.pos[i]; if (llpos->lnum == 0) { @@ -9912,8 +9876,9 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_append_number(l, (varnumber_T)llpos->col); tv_list_append_number(l, (varnumber_T)llpos->len); } - sprintf(buf, "pos%d", i + 1); - dict_add_list(dict, buf, l); + int len = snprintf(buf, sizeof(buf), "pos%d", i + 1); + assert((size_t)len < sizeof(buf)); + tv_dict_add_list(dict, buf, (size_t)len, l); } } else { dict_add_nr_str(dict, "pattern", 0L, cur->pattern); @@ -10082,10 +10047,10 @@ static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx) FOR_ALL_WINDOWS_IN_TAB(wp, tp) { tv_list_append_number(l, (varnumber_T)wp->handle); } - dict_add_list(dict, "windows", l); + tv_dict_add_list(dict, S_LEN("windows"), l); // Make a reference to tabpage variables - dict_add_dict(dict, "variables", tp->tp_vars); + tv_dict_add_dict(dict, S_LEN("variables"), tp->tp_vars); return dict; } @@ -10187,7 +10152,7 @@ static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr) NULL); // Add a reference to window variables - dict_add_dict(dict, "variables", wp->w_vars); + tv_dict_add_dict(dict, S_LEN("variables"), wp->w_vars); return dict; } @@ -17029,7 +16994,7 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) list = tv_list_alloc(); u_eval_tree(curbuf->b_u_oldhead, list); - dict_add_list(dict, "entries", list); + tv_dict_add_list(dict, S_LEN("entries"), list); } /* diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 0df818040b..f9885926c7 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1166,6 +1166,58 @@ bool tv_dict_get_callback(dict_T *const d, return res; } +//{{{2 dict_add* + +/// Add a list entry to dictionary +/// +/// @param[out] d Dictionary to add entry to. +/// @param[in] key Key to add. +/// @param[in] key_len Key length. +/// @param list List to add. Will have reference count incremented. +/// +/// @return OK in case of success, FAIL when key already exists. +int tv_dict_add_list(dict_T *const d, const char *const key, + const size_t key_len, list_T *const list) + FUNC_ATTR_NONNULL_ALL +{ + dictitem_T *item = tv_dict_item_alloc_len(key, key_len); + + item->di_tv.v_lock = VAR_UNLOCKED; + item->di_tv.v_type = VAR_LIST; + item->di_tv.vval.v_list = list; + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); + return FAIL; + } + list->lv_refcount++; + return OK; +} + +/// Add a dictionary entry to dictionary +/// +/// @param[out] d Dictionary to add entry to. +/// @param[in] key Key to add. +/// @param[in] key_len Key length. +/// @param dict Dictionary to add. Will have reference count incremented. +/// +/// @return OK in case of success, FAIL when key already exists. +int tv_dict_add_dict(dict_T *const d, const char *const key, + const size_t key_len, dict_T *const dict) + FUNC_ATTR_NONNULL_ALL +{ + dictitem_T *const item = tv_dict_item_alloc(key); + + item->di_tv.v_lock = VAR_UNLOCKED; + item->di_tv.v_type = VAR_DICT; + item->di_tv.vval.v_dict = dict; + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); + return FAIL; + } + dict->dv_refcount++; + return OK; +} + //{{{2 Operations on the whole dict /// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary. diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 4bca2f0889..28ac5aad4e 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -42,6 +42,7 @@ #include "nvim/terminal.h" #include "nvim/ui.h" #include "nvim/undo.h" +#include "nvim/macros.h" #include "nvim/window.h" #include "nvim/os/input.h" #include "nvim/os/time.h" @@ -2561,7 +2562,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) tv_list_append_string(list, (const char *)reg->y_array[i], -1); } list->lv_lock = VAR_FIXED; - dict_add_list(dict, "regcontents", list); + tv_dict_add_list(dict, S_LEN("regcontents"), list); // the register type char buf[NUMBUFLEN+2]; diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 6e21dccebb..8c75d28f2a 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -82,6 +82,7 @@ #include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/undo.h" +#include "nvim/macros.h" #include "nvim/cursor.h" #include "nvim/edit.h" #include "nvim/eval.h" @@ -2956,7 +2957,7 @@ void u_eval_tree(u_header_T *first_uhp, list_T *list) /* Recursive call to add alternate undo tree. */ u_eval_tree(uhp->uh_alt_next.ptr, alt_list); - dict_add_list(dict, "alt", alt_list); + tv_dict_add_list(dict, S_LEN("alt"), alt_list); } tv_list_append_dict(list, dict); From 2dcfc439b2ec2be4d830826061e89860977516be Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 20 Aug 2016 23:56:49 +0300 Subject: [PATCH 0182/1671] eval: Split and move dict_add_nr_str to typval.c Function was split into tv_dict_add_nr() and tv_dict_add_str(). --- src/nvim/edit.c | 24 ++++--- src/nvim/eval.c | 143 +++++++++++++++++------------------------ src/nvim/eval/typval.c | 50 +++++++++++++- src/nvim/eval/typval.h | 1 + src/nvim/file_search.c | 4 +- src/nvim/ops.c | 29 +++++---- src/nvim/option.c | 5 +- src/nvim/quickfix.c | 44 ++++++++----- src/nvim/search.c | 5 +- src/nvim/tag.c | 29 +++++---- src/nvim/undo.c | 21 +++--- 11 files changed, 201 insertions(+), 154 deletions(-) diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 53b196c61f..d1cceb435a 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -4034,16 +4034,20 @@ static void ins_compl_insert(int in_compl_func) // Set completed item. // { word, abbr, menu, kind, info } dict_T *dict = tv_dict_alloc(); - dict_add_nr_str(dict, "word", 0L, - EMPTY_IF_NULL(compl_shown_match->cp_str)); - dict_add_nr_str(dict, "abbr", 0L, - EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_ABBR])); - dict_add_nr_str(dict, "menu", 0L, - EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_MENU])); - dict_add_nr_str(dict, "kind", 0L, - EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_KIND])); - dict_add_nr_str(dict, "info", 0L, - EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_INFO])); + tv_dict_add_str(dict, S_LEN("word"), + (const char *)EMPTY_IF_NULL(compl_shown_match->cp_str)); + tv_dict_add_str( + dict, S_LEN("abbr"), + (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_ABBR])); + tv_dict_add_str( + dict, S_LEN("menu"), + (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_MENU])); + tv_dict_add_str( + dict, S_LEN("kind"), + (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_KIND])); + tv_dict_add_str( + dict, S_LEN("info"), + (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_INFO])); set_vim_var_dict(VV_COMPLETED_ITEM, dict); if (!in_compl_func) { compl_curr_match = compl_shown_match; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index e7c2d95cdd..a64eb3c959 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5544,31 +5544,6 @@ static bool set_ref_in_funccal(funccall_T *fc, int copyID) return abort; } -/* - * Add a number or string entry to dictionary "d". - * When "str" is NULL use number "nr", otherwise use "str". - * Returns FAIL when key already exists. - */ -int dict_add_nr_str(dict_T *d, char *key, long nr, char_u *str) -{ - dictitem_T *item; - - item = tv_dict_item_alloc(key); - item->di_tv.v_lock = 0; - if (str == NULL) { - item->di_tv.v_type = VAR_NUMBER; - item->di_tv.vval.v_number = nr; - } else { - item->di_tv.v_type = VAR_STRING; - item->di_tv.vval.v_string = vim_strsave(str); - } - if (tv_dict_add(d, item) == FAIL) { - tv_dict_item_free(item); - return FAIL; - } - return OK; -} - /* * Allocate a variable for a Dictionary and fill it from "*arg". * Return OK or FAIL. Returns NOTDONE for {expr}. @@ -9066,9 +9041,10 @@ static void get_buffer_signs(buf_T *buf, list_T *l) for (signlist_T *sign = buf->b_signlist; sign; sign = sign->next) { dict_T *const d = tv_dict_alloc(); - dict_add_nr_str(d, "id", sign->id, NULL); - dict_add_nr_str(d, "lnum", sign->lnum, NULL); - dict_add_nr_str(d, "name", 0L, sign_typenr2name(sign->typenr)); + tv_dict_add_nr(d, S_LEN("id"), sign->id); + tv_dict_add_nr(d, S_LEN("lnum"), sign->lnum); + tv_dict_add_str(d, S_LEN("name"), + (const char *)sign_typenr2name(sign->typenr)); tv_list_append_dict(l, d); } @@ -9079,17 +9055,16 @@ static dict_T *get_buffer_info(buf_T *buf) { dict_T *const dict = tv_dict_alloc(); - dict_add_nr_str(dict, "bufnr", buf->b_fnum, NULL); - dict_add_nr_str(dict, "name", 0L, - buf->b_ffname != NULL ? buf->b_ffname : (char_u *)""); - dict_add_nr_str(dict, "lnum", buflist_findlnum(buf), NULL); - dict_add_nr_str(dict, "loaded", buf->b_ml.ml_mfp != NULL, NULL); - dict_add_nr_str(dict, "listed", buf->b_p_bl, NULL); - dict_add_nr_str(dict, "changed", bufIsChanged(buf), NULL); - dict_add_nr_str(dict, "changedtick", buf->b_changedtick, NULL); - dict_add_nr_str(dict, "hidden", - buf->b_ml.ml_mfp != NULL && buf->b_nwindows == 0, - NULL); + tv_dict_add_nr(dict, S_LEN("bufnr"), buf->b_fnum); + tv_dict_add_str(dict, S_LEN("name"), + buf->b_ffname != NULL ? (const char *)buf->b_ffname : ""); + tv_dict_add_nr(dict, S_LEN("lnum"), buflist_findlnum(buf)); + tv_dict_add_nr(dict, S_LEN("loaded"), buf->b_ml.ml_mfp != NULL); + tv_dict_add_nr(dict, S_LEN("listed"), buf->b_p_bl); + tv_dict_add_nr(dict, S_LEN("changed"), bufIsChanged(buf)); + tv_dict_add_nr(dict, S_LEN("changedtick"), buf->b_changedtick); + tv_dict_add_nr(dict, S_LEN("hidden"), + buf->b_ml.ml_mfp != NULL && buf->b_nwindows == 0); // Get a reference to buffer variables tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars); @@ -9411,9 +9386,9 @@ static void f_getcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_T *dict = rettv->vval.v_dict; - dict_add_nr_str(dict, "char", 0L, last_csearch()); - dict_add_nr_str(dict, "forward", last_csearch_forward(), NULL); - dict_add_nr_str(dict, "until", last_csearch_until(), NULL); + tv_dict_add_str(dict, S_LEN("char"), last_csearch()); + tv_dict_add_nr(dict, S_LEN("forward"), last_csearch_forward()); + tv_dict_add_nr(dict, S_LEN("until"), last_csearch_until()); } /* @@ -9881,17 +9856,18 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_add_list(dict, buf, (size_t)len, l); } } else { - dict_add_nr_str(dict, "pattern", 0L, cur->pattern); + tv_dict_add_str(dict, S_LEN("pattern"), (const char *)cur->pattern); } - dict_add_nr_str(dict, "group", 0L, syn_id2name(cur->hlg_id)); - dict_add_nr_str(dict, "priority", (long)cur->priority, NULL); - dict_add_nr_str(dict, "id", (long)cur->id, NULL); + tv_dict_add_str(dict, S_LEN("group"), + (const char *)syn_id2name(cur->hlg_id)); + tv_dict_add_nr(dict, S_LEN("priority"), (varnumber_T)cur->priority); + tv_dict_add_nr(dict, S_LEN("id"), (varnumber_T)cur->id); if (cur->conceal_char) { - char_u buf[MB_MAXBYTES + 1]; + char buf[MB_MAXBYTES + 1]; - buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL; - dict_add_nr_str(dict, "conceal", 0L, (char_u *)&buf); + buf[(*mb_char2bytes)((int)cur->conceal_char, (char_u *)buf)] = NUL; + tv_dict_add_str(dict, S_LEN("conceal"), buf); } tv_list_append_dict(rettv->vval.v_list, dict); @@ -10041,7 +10017,7 @@ static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx) { dict_T *const dict = tv_dict_alloc(); - dict_add_nr_str(dict, "tabnr", tp_idx, NULL); + tv_dict_add_nr(dict, S_LEN("tabnr"), tp_idx); list_T *const l = tv_list_alloc(); FOR_ALL_WINDOWS_IN_TAB(wp, tp) { @@ -10139,17 +10115,16 @@ static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr) { dict_T *const dict = tv_dict_alloc(); - dict_add_nr_str(dict, "tabnr", tpnr, NULL); - dict_add_nr_str(dict, "winnr", winnr, NULL); - dict_add_nr_str(dict, "winid", wp->handle, NULL); - dict_add_nr_str(dict, "height", wp->w_height, NULL); - dict_add_nr_str(dict, "width", wp->w_width, NULL); - dict_add_nr_str(dict, "bufnr", wp->w_buffer->b_fnum, NULL); + tv_dict_add_nr(dict, S_LEN("tabnr"), tpnr); + tv_dict_add_nr(dict, S_LEN("winnr"), winnr); + tv_dict_add_nr(dict, S_LEN("winid"), wp->handle); + tv_dict_add_nr(dict, S_LEN("height"), wp->w_height); + tv_dict_add_nr(dict, S_LEN("width"), wp->w_width); + tv_dict_add_nr(dict, S_LEN("bufnr"), wp->w_buffer->b_fnum); - dict_add_nr_str(dict, "quickfix", bt_quickfix(wp->w_buffer), NULL); - dict_add_nr_str(dict, "loclist", - (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL), - NULL); + tv_dict_add_nr(dict, S_LEN("quickfix"), bt_quickfix(wp->w_buffer)); + tv_dict_add_nr(dict, S_LEN("loclist"), + (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL)); // Add a reference to window variables tv_dict_add_dict(dict, S_LEN("variables"), wp->w_vars); @@ -12074,15 +12049,15 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) char *const mapmode = map_mode_to_chars(mp->m_mode); dict_T *dict = rettv->vval.v_dict; - dict_add_nr_str(dict, "lhs", 0L, lhs); - dict_add_nr_str(dict, "rhs", 0L, mp->m_orig_str); - dict_add_nr_str(dict, "noremap", mp->m_noremap ? 1L : 0L, NULL); - dict_add_nr_str(dict, "expr", mp->m_expr ? 1L : 0L, NULL); - dict_add_nr_str(dict, "silent", mp->m_silent ? 1L : 0L, NULL); - dict_add_nr_str(dict, "sid", (long)mp->m_script_ID, NULL); - dict_add_nr_str(dict, "buffer", (long)buffer_local, NULL); - dict_add_nr_str(dict, "nowait", mp->m_nowait ? 1L : 0L, NULL); - dict_add_nr_str(dict, "mode", 0L, (char_u *)mapmode); + tv_dict_add_str(dict, S_LEN("lhs"), (const char *)lhs); + tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str); + tv_dict_add_nr(dict, S_LEN("noremap"), mp->m_noremap ? 1 : 0); + tv_dict_add_nr(dict, S_LEN("expr"), mp->m_expr ? 1 : 0); + tv_dict_add_nr(dict, S_LEN("silent"), mp->m_silent ? 1 : 0); + tv_dict_add_nr(dict, S_LEN("sid"), (varnumber_T)mp->m_script_ID); + tv_dict_add_nr(dict, S_LEN("buffer"), (varnumber_T)buffer_local); + tv_dict_add_nr(dict, S_LEN("nowait"), mp->m_nowait ? 1 : 0); + tv_dict_add_str(dict, S_LEN("mode"), mapmode); xfree(lhs); xfree(mapmode); @@ -16984,13 +16959,13 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_T *dict = rettv->vval.v_dict; list_T *list; - dict_add_nr_str(dict, "synced", (long)curbuf->b_u_synced, NULL); - dict_add_nr_str(dict, "seq_last", curbuf->b_u_seq_last, NULL); - dict_add_nr_str(dict, "save_last", - (long)curbuf->b_u_save_nr_last, NULL); - dict_add_nr_str(dict, "seq_cur", curbuf->b_u_seq_cur, NULL); - dict_add_nr_str(dict, "time_cur", (long)curbuf->b_u_time_cur, NULL); - dict_add_nr_str(dict, "save_cur", (long)curbuf->b_u_save_nr_cur, NULL); + tv_dict_add_nr(dict, S_LEN("synced"), (varnumber_T)curbuf->b_u_synced); + tv_dict_add_nr(dict, S_LEN("seq_last"), (varnumber_T)curbuf->b_u_seq_last); + tv_dict_add_nr(dict, S_LEN("save_last"), + (varnumber_T)curbuf->b_u_save_nr_last); + tv_dict_add_nr(dict, S_LEN("seq_cur"), (varnumber_T)curbuf->b_u_seq_cur); + tv_dict_add_nr(dict, S_LEN("time_cur"), (varnumber_T)curbuf->b_u_time_cur); + tv_dict_add_nr(dict, S_LEN("save_cur"), (varnumber_T)curbuf->b_u_save_nr_cur); list = tv_list_alloc(); u_eval_tree(curbuf->b_u_oldhead, list); @@ -17223,16 +17198,16 @@ static void f_winsaveview(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_alloc_ret(rettv); dict = rettv->vval.v_dict; - dict_add_nr_str(dict, "lnum", (long)curwin->w_cursor.lnum, NULL); - dict_add_nr_str(dict, "col", (long)curwin->w_cursor.col, NULL); - dict_add_nr_str(dict, "coladd", (long)curwin->w_cursor.coladd, NULL); + tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)curwin->w_cursor.lnum); + tv_dict_add_nr(dict, S_LEN("col"), (varnumber_T)curwin->w_cursor.col); + tv_dict_add_nr(dict, S_LEN("coladd"), (varnumber_T)curwin->w_cursor.coladd); update_curswant(); - dict_add_nr_str(dict, "curswant", (long)curwin->w_curswant, NULL); + tv_dict_add_nr(dict, S_LEN("curswant"), (varnumber_T)curwin->w_curswant); - dict_add_nr_str(dict, "topline", (long)curwin->w_topline, NULL); - dict_add_nr_str(dict, "topfill", (long)curwin->w_topfill, NULL); - dict_add_nr_str(dict, "leftcol", (long)curwin->w_leftcol, NULL); - dict_add_nr_str(dict, "skipcol", (long)curwin->w_skipcol, NULL); + tv_dict_add_nr(dict, S_LEN("topline"), (varnumber_T)curwin->w_topline); + tv_dict_add_nr(dict, S_LEN("topfill"), (varnumber_T)curwin->w_topfill); + tv_dict_add_nr(dict, S_LEN("leftcol"), (varnumber_T)curwin->w_leftcol); + tv_dict_add_nr(dict, S_LEN("skipcol"), (varnumber_T)curwin->w_skipcol); } /// Writes list of strings to file diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index f9885926c7..62460bcc3a 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1180,7 +1180,7 @@ int tv_dict_add_list(dict_T *const d, const char *const key, const size_t key_len, list_T *const list) FUNC_ATTR_NONNULL_ALL { - dictitem_T *item = tv_dict_item_alloc_len(key, key_len); + dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); item->di_tv.v_lock = VAR_UNLOCKED; item->di_tv.v_type = VAR_LIST; @@ -1218,6 +1218,54 @@ int tv_dict_add_dict(dict_T *const d, const char *const key, return OK; } +/// Add a number entry to dictionary +/// +/// @param[out] d Dictionary to add entry to. +/// @param[in] key Key to add. +/// @param[in] key_len Key length. +/// @param[in] nr Number to add. +/// +/// @return OK in case of success, FAIL when key already exists. +int tv_dict_add_nr(dict_T *const d, const char *const key, + const size_t key_len, const varnumber_T nr) +{ + dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); + + item->di_tv.v_lock = VAR_UNLOCKED; + item->di_tv.v_type = VAR_NUMBER; + item->di_tv.vval.v_number = nr; + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); + return FAIL; + } + return OK; +} + +/// Add a string entry to dictionary +/// +/// @param[out] d Dictionary to add entry to. +/// @param[in] key Key to add. +/// @param[in] key_len Key length. +/// @param[in] val String to add. +/// +/// @return OK in case of success, FAIL when key already exists. +int tv_dict_add_str(dict_T *const d, + const char *const key, const size_t key_len, + const char *const val) + FUNC_ATTR_NONNULL_ALL +{ + dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); + + item->di_tv.v_lock = VAR_UNLOCKED; + item->di_tv.v_type = VAR_STRING; + item->di_tv.vval.v_string = (char_u *)xstrdup(val); + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); + return FAIL; + } + return OK; +} + //{{{2 Operations on the whole dict /// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary. diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 6183397d12..6929e37e43 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "nvim/hashtab.h" #include "nvim/garray.h" diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index d7120a7309..f7932bc296 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1549,8 +1549,8 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope) assert(false); } - dict_add_nr_str(dict, "scope", 0L, (char_u *)buf); - dict_add_nr_str(dict, "cwd", 0L, (char_u *)new_dir); + tv_dict_add_str(dict, S_LEN("scope"), buf); + tv_dict_add_str(dict, S_LEN("cwd"), new_dir); tv_dict_set_keys_readonly(dict); apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, (char_u *)new_dir, false, diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 28ac5aad4e..fd1e2d20f2 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -14,6 +14,7 @@ #include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/cursor.h" +#include "nvim/assert.h" #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" @@ -2567,17 +2568,17 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) // the register type char buf[NUMBUFLEN+2]; format_reg_type(reg->y_type, reg->y_width, buf, ARRAY_SIZE(buf)); - dict_add_nr_str(dict, "regtype", 0, (char_u *)buf); + tv_dict_add_str(dict, S_LEN("regtype"), buf); // name of requested register or the empty string for an unnamed operation. buf[0] = (char)oap->regname; buf[1] = NUL; - dict_add_nr_str(dict, "regname", 0, (char_u *)buf); + tv_dict_add_str(dict, S_LEN("regname"), buf); // kind of operation (yank/delete/change) buf[0] = (char)get_op_char(oap->op_type); buf[1] = NUL; - dict_add_nr_str(dict, "operator", 0, (char_u *)buf); + tv_dict_add_str(dict, S_LEN("operator"), buf); tv_dict_set_keys_readonly(dict); textlock++; @@ -5484,17 +5485,19 @@ void cursor_pos_info(dict_T *dict) if (dict != NULL) { // Don't shorten this message, the user asked for it. - dict_add_nr_str(dict, "words", word_count, NULL); - dict_add_nr_str(dict, "chars", char_count, NULL); - dict_add_nr_str(dict, "bytes", byte_count + bom_count, NULL); + tv_dict_add_nr(dict, S_LEN("words"), (varnumber_T)word_count); + tv_dict_add_nr(dict, S_LEN("chars"), (varnumber_T)char_count); + tv_dict_add_nr(dict, S_LEN("bytes"), (varnumber_T)(byte_count + bom_count)); - dict_add_nr_str(dict, l_VIsual_active ? "visual_bytes" : "cursor_bytes", - byte_count_cursor, NULL); - dict_add_nr_str(dict, l_VIsual_active ? "visual_chars" : "cursor_chars", - char_count_cursor, NULL); - dict_add_nr_str(dict, l_VIsual_active ? "visual_words" : "cursor_words", - word_count_cursor, NULL); - } + STATIC_ASSERT(sizeof("visual") == sizeof("cursor"), + "key_len argument in tv_dict_add_nr is wrong"); + tv_dict_add_nr(dict, l_VIsual_active ? "visual_bytes" : "cursor_bytes", + sizeof("visual_bytes") - 1, (varnumber_T)byte_count_cursor); + tv_dict_add_nr(dict, l_VIsual_active ? "visual_chars" : "cursor_chars", + sizeof("visual_chars") - 1, (varnumber_T)char_count_cursor); + tv_dict_add_nr(dict, l_VIsual_active ? "visual_words" : "cursor_words", + sizeof("visual_words") - 1, (varnumber_T)word_count_cursor); + } } /// Check if the default register (used in an unnamed paste) should be a diff --git a/src/nvim/option.c b/src/nvim/option.c index 4c8606a999..1881b329ec 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -6971,9 +6971,10 @@ dict_T *get_winbuf_options(const int bufopt) if (varp != NULL) { if (opt->flags & P_STRING) { - dict_add_nr_str(d, opt->fullname, 0L, *(char_u **)varp); + tv_dict_add_str(d, opt->fullname, strlen(opt->fullname), + *(const char **)varp); } else { - dict_add_nr_str(d, opt->fullname, *varp, NULL); + tv_dict_add_nr(d, opt->fullname, strlen(opt->fullname), *varp); } } } diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index d23059ff22..06ac2821b0 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -3972,7 +3972,6 @@ static void unload_dummy_buffer(buf_T *buf, char_u *dirname_start) int get_errorlist(win_T *wp, int qf_idx, list_T *list) { qf_info_T *qi = &ql_info; - dict_T *dict; char_u buf[2]; qfline_T *qfp; int i; @@ -4000,23 +3999,34 @@ int get_errorlist(win_T *wp, int qf_idx, list_T *list) if (bufnum != 0 && (buflist_findnr(bufnum) == NULL)) bufnum = 0; - dict = tv_dict_alloc(); + dict_T *const dict = tv_dict_alloc(); tv_list_append_dict(list, dict); buf[0] = qfp->qf_type; buf[1] = NUL; - if ( dict_add_nr_str(dict, "bufnr", (long)bufnum, NULL) == FAIL - || dict_add_nr_str(dict, "lnum", (long)qfp->qf_lnum, NULL) == FAIL - || dict_add_nr_str(dict, "col", (long)qfp->qf_col, NULL) == FAIL - || dict_add_nr_str(dict, "vcol", (long)qfp->qf_viscol, NULL) == FAIL - || dict_add_nr_str(dict, "nr", (long)qfp->qf_nr, NULL) == FAIL - || dict_add_nr_str(dict, "pattern", 0L, - qfp->qf_pattern == NULL ? (char_u *)"" : qfp->qf_pattern) == FAIL - || dict_add_nr_str(dict, "text", 0L, - qfp->qf_text == NULL ? (char_u *)"" : qfp->qf_text) == FAIL - || dict_add_nr_str(dict, "type", 0L, buf) == FAIL - || dict_add_nr_str(dict, "valid", (long)qfp->qf_valid, NULL) == FAIL) - return FAIL; + if (tv_dict_add_nr(dict, S_LEN("bufnr"), (varnumber_T)bufnum) == FAIL + || (tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)qfp->qf_lnum) + == FAIL) + || (tv_dict_add_nr(dict, S_LEN("col"), (varnumber_T)qfp->qf_col) + == FAIL) + || (tv_dict_add_nr(dict, S_LEN("vcol"), (varnumber_T)qfp->qf_viscol) + == FAIL) + || (tv_dict_add_nr(dict, S_LEN("nr"), (varnumber_T)qfp->qf_nr) == FAIL) + || tv_dict_add_str(dict, S_LEN("pattern"), + (qfp->qf_pattern == NULL + ? "" + : (const char *)qfp->qf_pattern)) == FAIL + || tv_dict_add_str(dict, S_LEN("text"), + (qfp->qf_text == NULL + ? "" + : (const char *)qfp->qf_text)) == FAIL + || tv_dict_add_str(dict, S_LEN("type"), (const char *)buf) == FAIL + || (tv_dict_add_nr(dict, S_LEN("valid"), (varnumber_T)qfp->qf_valid) + == FAIL)) { + // tv_dict_add* fail only if key already exist, but this is a newly + // allocated dictionary which is thus guaranteed to have no existing keys. + assert(false); + } qfp = qfp->qf_next; if (qfp == NULL) { @@ -4085,15 +4095,15 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) if (t == NULL) { t = (char_u *)""; } - status = dict_add_nr_str(retdict, "title", 0L, t); + status = tv_dict_add_str(retdict, S_LEN("title"), (const char *)t); } if ((status == OK) && (flags & QF_GETLIST_NR)) { - status = dict_add_nr_str(retdict, "nr", qf_idx + 1, NULL); + status = tv_dict_add_nr(retdict, S_LEN("nr"), qf_idx + 1); } if ((status == OK) && (flags & QF_GETLIST_WINID)) { win_T *win = qf_find_win(qi); if (win != NULL) { - status = dict_add_nr_str(retdict, "winid", win->handle, NULL); + status = tv_dict_add_nr(retdict, S_LEN("winid"), win->handle); } } diff --git a/src/nvim/search.c b/src/nvim/search.c index fb9e820928..c5c92b41c5 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -356,9 +356,10 @@ int pat_has_uppercase(char_u *pat) return FALSE; } -char_u *last_csearch(void) +const char *last_csearch(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - return lastc_bytes; + return (const char *)lastc_bytes; } int last_csearch_forward(void) diff --git a/src/nvim/tag.c b/src/nvim/tag.c index af19a1bfc4..b812dd2ffd 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -776,11 +776,12 @@ do_tag ( dict = tv_dict_alloc(); tv_list_append_dict(list, dict); - dict_add_nr_str(dict, "text", 0L, tag_name); - dict_add_nr_str(dict, "filename", 0L, fname); - dict_add_nr_str(dict, "lnum", lnum, NULL); - if (lnum == 0) - dict_add_nr_str(dict, "pattern", 0L, cmd); + tv_dict_add_str(dict, S_LEN("text"), (const char *)tag_name); + tv_dict_add_str(dict, S_LEN("filename"), (const char *)fname); + tv_dict_add_nr(dict, S_LEN("lnum"), lnum); + if (lnum == 0) { + tv_dict_add_str(dict, S_LEN("pattern"), (const char *)cmd); + } } vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag); @@ -2203,7 +2204,7 @@ parse_tag_line ( * Return TRUE if it is a static tag and adjust *tagname to the real tag. * Return FALSE if it is not a static tag. */ -static int test_for_static(tagptrs_T *tagp) +static bool test_for_static(tagptrs_T *tagp) { char_u *p; @@ -2790,7 +2791,8 @@ add_tag_field ( STRLCPY(buf, start, len + 1); } buf[len] = NUL; - retval = dict_add_nr_str(dict, field_name, 0L, buf); + retval = tv_dict_add_str(dict, field_name, STRLEN(field_name), + (const char *)buf); xfree(buf); return retval; } @@ -2806,7 +2808,7 @@ int get_tags(list_T *list, char_u *pat) char_u *full_fname; dict_T *dict; tagptrs_T tp; - long is_static; + bool is_static; ret = find_tags(pat, &num_matches, &matches, TAG_REGEXP | TAG_NOIC, (int)MAXCOL, NULL); @@ -2829,14 +2831,13 @@ int get_tags(list_T *list, char_u *pat) full_fname = tag_full_fname(&tp); if (add_tag_field(dict, "name", tp.tagname, tp.tagname_end) == FAIL - || add_tag_field(dict, "filename", full_fname, - NULL) == FAIL - || add_tag_field(dict, "cmd", tp.command, - tp.command_end) == FAIL + || add_tag_field(dict, "filename", full_fname, NULL) == FAIL + || add_tag_field(dict, "cmd", tp.command, tp.command_end) == FAIL || add_tag_field(dict, "kind", tp.tagkind, - tp.tagkind ? tp.tagkind_end : NULL) == FAIL - || dict_add_nr_str(dict, "static", is_static, NULL) == FAIL) + tp.tagkind ? tp.tagkind_end : NULL) == FAIL + || tv_dict_add_nr(dict, S_LEN("static"), is_static) == FAIL) { ret = FAIL; + } xfree(full_fname); diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 8c75d28f2a..ba687bf6da 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -2943,19 +2943,22 @@ void u_eval_tree(u_header_T *first_uhp, list_T *list) while (uhp != NULL) { dict = tv_dict_alloc(); - dict_add_nr_str(dict, "seq", uhp->uh_seq, NULL); - dict_add_nr_str(dict, "time", (long)uhp->uh_time, NULL); - if (uhp == curbuf->b_u_newhead) - dict_add_nr_str(dict, "newhead", 1, NULL); - if (uhp == curbuf->b_u_curhead) - dict_add_nr_str(dict, "curhead", 1, NULL); - if (uhp->uh_save_nr > 0) - dict_add_nr_str(dict, "save", uhp->uh_save_nr, NULL); + tv_dict_add_nr(dict, S_LEN("seq"), (varnumber_T)uhp->uh_seq); + tv_dict_add_nr(dict, S_LEN("time"), (varnumber_T)uhp->uh_time); + if (uhp == curbuf->b_u_newhead) { + tv_dict_add_nr(dict, S_LEN("newhead"), 1); + } + if (uhp == curbuf->b_u_curhead) { + tv_dict_add_nr(dict, S_LEN("curhead"), 1); + } + if (uhp->uh_save_nr > 0) { + tv_dict_add_nr(dict, S_LEN("save"), (varnumber_T)uhp->uh_save_nr); + } if (uhp->uh_alt_next.ptr != NULL) { list_T *alt_list = tv_list_alloc(); - /* Recursive call to add alternate undo tree. */ + // Recursive call to add alternate undo tree. u_eval_tree(uhp->uh_alt_next.ptr, alt_list); tv_dict_add_list(dict, S_LEN("alt"), alt_list); } From 5cdf7177ec71e4b9b3295ead93bedf7ff226a2b2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 21 Aug 2016 00:20:01 +0300 Subject: [PATCH 0183/1671] eval: Move get_float_arg to typval.h Assuming `inline` is there for a reason, so it is kept and function was moved to typval.h and not to typval.c which does not have problems with #including message.h. --- src/nvim/eval.c | 60 ++++++++++++++++-------------------------- src/nvim/eval/typval.h | 27 +++++++++++++++++++ src/nvim/gettext.h | 21 +++++++++++++++ src/nvim/vim.h | 17 +----------- 4 files changed, 72 insertions(+), 53 deletions(-) create mode 100644 src/nvim/gettext.h diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a64eb3c959..1d060eff33 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6473,24 +6473,6 @@ static int non_zero_arg(typval_T *argvars) */ -/* - * Get the float value of "argvars[0]" into "f". - * Returns FAIL when the argument is not a Number or Float. - */ -static inline int get_float_arg(typval_T *argvars, float_T *f) -{ - if (argvars[0].v_type == VAR_FLOAT) { - *f = argvars[0].vval.v_float; - return OK; - } - if (argvars[0].v_type == VAR_NUMBER) { - *f = (float_T)argvars[0].vval.v_number; - return OK; - } - EMSG(_("E808: Number or Float required")); - return FAIL; -} - // Apply a floating point C function on a typval with one float_T. // // Some versions of glibc on i386 have an optimization that makes it harder to @@ -6502,7 +6484,7 @@ static void float_op_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr) float_T (*function)(float_T) = (float_T (*)(float_T))fptr; rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) { + if (tv_get_float(argvars, &f)) { rettv->vval.v_float = function(f); } else { rettv->vval.v_float = 0.0; @@ -6957,14 +6939,15 @@ static void f_assert_true(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_atan2(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - float_T fx, fy; + float_T fx; + float_T fy; rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &fx) == OK - && get_float_arg(&argvars[1], &fy) == OK) + if (tv_get_float(argvars, &fx) && tv_get_float(&argvars[1], &fy)) { rettv->vval.v_float = atan2(fx, fy); - else + } else { rettv->vval.v_float = 0.0; + } } /* @@ -8557,13 +8540,14 @@ static void f_float2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { float_T f; - if (get_float_arg(argvars, &f) == OK) { - if (f < -0x7fffffff) - rettv->vval.v_number = -0x7fffffff; - else if (f > 0x7fffffff) - rettv->vval.v_number = 0x7fffffff; - else + if (tv_get_float(argvars, &f)) { + if (f < VARNUMBER_MIN) { + rettv->vval.v_number = VARNUMBER_MIN; + } else if (f > VARNUMBER_MAX) { + rettv->vval.v_number = VARNUMBER_MAX; + } else { rettv->vval.v_number = (varnumber_T)f; + } } } @@ -8572,14 +8556,15 @@ static void f_float2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - float_T fx, fy; + float_T fx; + float_T fy; rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &fx) == OK - && get_float_arg(&argvars[1], &fy) == OK) + if (tv_get_float(argvars, &fx) && tv_get_float(&argvars[1], &fy)) { rettv->vval.v_float = fmod(fx, fy); - else + } else { rettv->vval.v_float = 0.0; + } } /* @@ -12790,14 +12775,15 @@ static void f_pathshorten(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_pow(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - float_T fx, fy; + float_T fx; + float_T fy; rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &fx) == OK - && get_float_arg(&argvars[1], &fy) == OK) + if (tv_get_float(argvars, &fx) && tv_get_float(&argvars[1], &fy)) { rettv->vval.v_float = pow(fx, fy); - else + } else { rettv->vval.v_float = 0.0; + } } /* diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 6929e37e43..0eef2ddaac 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -13,6 +13,7 @@ #include "nvim/lib/queue.h" #include "nvim/profile.h" // for proftime_T #include "nvim/pos.h" // for linenr_T +#include "nvim/gettext.h" /// Type used for VimL VAR_NUMBER values typedef int varnumber_T; @@ -370,6 +371,32 @@ extern bool tv_in_free_unref_items; } \ }) +static inline bool tv_get_float(const typval_T *const tv, float_T *const ret_f) + REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT; + +// FIXME circular dependency, cannot import message.h. +bool emsgf(const char *const fmt, ...); + +/// Get the float value +/// +/// @param[in] tv VimL object to get value from. +/// @param[out] ret_f Location where resulting float is stored. +/// +/// @return true in case of success, false if tv is not a number or float. +static inline bool tv_get_float(const typval_T *const tv, float_T *const ret_f) +{ + if (tv->v_type == VAR_FLOAT) { + *ret_f = tv->vval.v_float; + return true; + } + if (tv->v_type == VAR_NUMBER) { + *ret_f = (float_T)tv->vval.v_number; + return true; + } + emsgf(_("E808: Number or Float required")); + return false; +} + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/typval.h.generated.h" #endif diff --git a/src/nvim/gettext.h b/src/nvim/gettext.h new file mode 100644 index 0000000000..aa0e97233e --- /dev/null +++ b/src/nvim/gettext.h @@ -0,0 +1,21 @@ +#ifndef NVIM_GETTEXT_H +#define NVIM_GETTEXT_H + +#ifdef HAVE_WORKING_LIBINTL +# include +# define _(x) gettext((char *)(x)) +// XXX do we actually need this? +# ifdef gettext_noop +# define N_(x) gettext_noop(x) +# else +# define N_(x) x +# endif +#else +# define _(x) ((char *)(x)) +# define N_(x) x +# define bindtextdomain(x, y) // empty +# define bind_textdomain_codeset(x, y) // empty +# define textdomain(x) // empty +#endif + +#endif // NVIM_GETTEXT_H diff --git a/src/nvim/vim.h b/src/nvim/vim.h index ae654251f9..e16ee00309 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -51,22 +51,7 @@ Error: configure did not run properly.Check auto/config.log. /* ================ end of the header file puzzle =============== */ -#ifdef HAVE_WORKING_LIBINTL -# include -# define _(x) gettext((char *)(x)) -// XXX do we actually need this? -# ifdef gettext_noop -# define N_(x) gettext_noop(x) -# else -# define N_(x) x -# endif -#else -# define _(x) ((char *)(x)) -# define N_(x) x -# define bindtextdomain(x, y) /* empty */ -# define bind_textdomain_codeset(x, y) /* empty */ -# define textdomain(x) /* empty */ -#endif +#include "nvim/gettext.h" /* special attribute addition: Put message in history */ #define MSG_HIST 0x1000 From 28dafe3ff0b0dc082fb62b2251fd64a167ce7188 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 21 Aug 2016 08:16:47 +0300 Subject: [PATCH 0184/1671] eval,*: Move get_tv_string to typval.c Function was renamed and changed to return `const char *`. --- src/nvim/api/private/helpers.c | 2 +- src/nvim/buffer.c | 6 +- src/nvim/edit.c | 14 +- src/nvim/eval.c | 1436 ++++++++++++++++---------------- src/nvim/eval.h | 2 +- src/nvim/eval/executor.c | 6 +- src/nvim/eval/typval.c | 28 +- src/nvim/eval/typval.h | 2 + src/nvim/ex_docmd.c | 6 +- src/nvim/ex_eval.c | 20 +- src/nvim/ex_getln.c | 69 +- src/nvim/fileio.c | 36 +- src/nvim/getchar.c | 109 ++- src/nvim/hardcopy.c | 8 +- src/nvim/message.c | 2 +- src/nvim/msgpack_rpc/channel.c | 20 +- src/nvim/msgpack_rpc/helpers.c | 4 +- src/nvim/normal.c | 21 +- src/nvim/os/fs.c | 25 +- src/nvim/os_unix.c | 4 +- src/nvim/path.c | 36 +- src/nvim/sha256.c | 14 +- src/nvim/shada.c | 2 - src/nvim/spell.c | 48 +- src/nvim/strings.c | 38 +- src/nvim/syntax.c | 92 +- src/nvim/undo.c | 2 +- src/nvim/version.c | 12 +- 28 files changed, 1072 insertions(+), 992 deletions(-) diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 6b17ba1a11..2c8a56cc1a 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -625,7 +625,7 @@ String cstr_as_string(char *str) FUNC_ATTR_PURE if (str == NULL) { return (String) STRING_INIT; } - return (String) {.data = str, .size = strlen(str)}; + return (String) { .data = str, .size = strlen(str) }; } /// Converts from type Object to a VimL value. diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 0ce9ce073e..0fb9a21354 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -4207,11 +4207,11 @@ void fname_expand(buf_T *buf, char_u **ffname, char_u **sfname) #ifdef WIN32 if (!buf->b_p_bin) { // If the file name is a shortcut file, use the file it links to. - char_u *rfname = (char_u *)os_resolve_shortcut(*ffname); + char *rfname = os_resolve_shortcut((const char *)(*ffname)); if (rfname != NULL) { xfree(*ffname); - *ffname = rfname; - *sfname = rfname; + *ffname = (char_u *)rfname; + *sfname = (char_u *)rfname; } } #endif diff --git a/src/nvim/edit.c b/src/nvim/edit.c index d1cceb435a..b396251051 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -3464,7 +3464,6 @@ expand_by_function ( { list_T *matchlist = NULL; dict_T *matchdict = NULL; - char_u *args[2]; char_u *funcname; pos_T pos; win_T *curwin_save; @@ -3475,9 +3474,8 @@ expand_by_function ( if (*funcname == NUL) return; - /* Call 'completefunc' to obtain the list of matches. */ - args[0] = (char_u *)"0"; - args[1] = base; + // Call 'completefunc' to obtain the list of matches. + const char_u *const args[2] = { (char_u *)"0", base }; pos = curwin->w_cursor; curwin_save = curwin; @@ -4589,7 +4587,6 @@ static int ins_complete(int c, bool enable_pum) * Call user defined function 'completefunc' with "a:findstart" * set to 1 to obtain the length of text to use for completion. */ - char_u *args[2]; int col; char_u *funcname; pos_T pos; @@ -4608,8 +4605,7 @@ static int ins_complete(int c, bool enable_pum) return FAIL; } - args[0] = (char_u *)"1"; - args[1] = NULL; + const char_u *const args[2] = { (char_u *)"1", NULL }; pos = curwin->w_cursor; curwin_save = curwin; curbuf_save = curbuf; @@ -7111,8 +7107,8 @@ static void ins_ctrl_g(void) */ static void ins_ctrl_hat(void) { - if (map_to_exists_mode((char_u *)"", LANGMAP, FALSE)) { - /* ":lmap" mappings exists, Toggle use of ":lmap" mappings. */ + if (map_to_exists_mode("", LANGMAP, false)) { + // ":lmap" mappings exists, Toggle use of ":lmap" mappings. if (State & LANGMAP) { curbuf->b_p_iminsert = B_IMODE_NONE; State &= ~LANGMAP; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 1d060eff33..7e40f5e828 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -965,31 +965,34 @@ eval_to_bool ( return retval; } -/* - * Top level evaluation function, returning a string. If "skip" is TRUE, - * only parsing to "nextcmd" is done, without reporting errors. Return - * pointer to allocated memory, or NULL for failure or when "skip" is TRUE. - */ -char_u * -eval_to_string_skip ( - char_u *arg, - char_u **nextcmd, - int skip /* only parse, don't execute */ -) +/// Top level evaluation function, returning a string +/// +/// @param[in] arg String to evaluate. +/// @param nextcmd Pointer to the start of the next Ex command. +/// @param[in] skip If true, only do parsing to nextcmd without reporting +/// errors or actually evaluating anything. +/// +/// @return [allocated] string result of evaluation or NULL in case of error or +/// when skipping. +char *eval_to_string_skip(const char *arg, const char **nextcmd, + const bool skip) + FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT { typval_T tv; - char_u *retval; + char *retval; - if (skip) - ++emsg_skip; - if (eval0(arg, &tv, nextcmd, !skip) == FAIL || skip) + if (skip) { + emsg_skip++; + } + if (eval0((char_u *)arg, &tv, (char_u **)nextcmd, !skip) == FAIL || skip) { retval = NULL; - else { - retval = vim_strsave(get_tv_string(&tv)); + } else { + retval = xstrdup(tv_get_string(&tv)); tv_clear(&tv); } - if (skip) - --emsg_skip; + if (skip) { + emsg_skip--; + } return retval; } @@ -1015,13 +1018,12 @@ int skip_expr(char_u **pp) char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert) { typval_T tv; - char_u *retval; + char *retval; garray_T ga; - char_u numbuf[NUMBUFLEN]; - if (eval0(arg, &tv, nextcmd, TRUE) == FAIL) + if (eval0(arg, &tv, nextcmd, true) == FAIL) { retval = NULL; - else { + } else { if (convert && tv.v_type == VAR_LIST) { ga_init(&ga, (int)sizeof(char), 80); if (tv.vval.v_list != NULL) { @@ -1031,16 +1033,18 @@ char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert) } } ga_append(&ga, NUL); - retval = (char_u *)ga.ga_data; + retval = (char *)ga.ga_data; } else if (convert && tv.v_type == VAR_FLOAT) { - vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv.vval.v_float); - retval = vim_strsave(numbuf); - } else - retval = vim_strsave(get_tv_string(&tv)); + char numbuf[NUMBUFLEN]; + vim_snprintf(numbuf, NUMBUFLEN, "%g", tv.vval.v_float); + retval = xstrdup(numbuf); + } else { + retval = xstrdup(tv_get_string(&tv)); + } tv_clear(&tv); } - return retval; + return (char_u *)retval; } /* @@ -1159,14 +1163,15 @@ list_T *eval_spell_expr(char_u *badword, char_u *expr) * Return -1 if anything isn't right. * Used to get the good word and score from the eval_spell_expr() result. */ -int get_spellword(list_T *list, char_u **pp) +int get_spellword(list_T *list, const char **pp) { listitem_T *li; li = list->lv_first; - if (li == NULL) + if (li == NULL) { return -1; - *pp = get_tv_string(&li->li_tv); + } + *pp = tv_get_string(&li->li_tv); li = li->li_next; if (li == NULL) @@ -1198,9 +1203,9 @@ typval_T *eval_expr(char_u *arg, char_u **nextcmd) // // Return OK or FAIL. int call_vim_function( - char_u *func, + const char_u *func, int argc, - char_u **argv, + const char_u *const *const argv, int safe, // use the sandbox int str_arg_only, // all arguments are strings typval_T *rettv @@ -1233,7 +1238,7 @@ int call_vim_function( argvars[i].vval.v_number = n; } else { argvars[i].v_type = VAR_STRING; - argvars[i].vval.v_string = argv[i]; + argvars[i].vval.v_string = (char_u *)argv[i]; } } @@ -1268,8 +1273,8 @@ long call_func_retnr ( char_u *func, int argc, - char_u **argv, - int safe /* use the sandbox */ + const char_u *const *const argv, + int safe // use the sandbox ) { typval_T rettv; @@ -1284,27 +1289,28 @@ call_func_retnr ( return retval; } -/* - * Call vimL function "func" and return the result as a string. - * Returns NULL when calling the function fails. - * Uses argv[argc] for the function arguments. - */ -void * -call_func_retstr ( - char_u *func, - int argc, - char_u **argv, - int safe /* use the sandbox */ -) +/// Call VimL function and return the result as a string +/// +/// @param[in] func Function name. +/// @param[in] argc Number of arguments. +/// @param[in] argv Array with string arguments. +/// @param[in] safe Use the sandbox. +/// +/// @return [allocated] NULL when calling function failes, allocated string +/// otherwise. +char *call_func_retstr(const char *const func, const int argc, + const char_u *const *const argv, + const bool safe) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { typval_T rettv; - char_u *retval; - - /* All arguments are passed as strings, no conversion to number. */ - if (call_vim_function(func, argc, argv, safe, TRUE, &rettv) == FAIL) + // All arguments are passed as strings, no conversion to number. + if (call_vim_function((const char_u *)func, argc, argv, safe, true, &rettv) + == FAIL) { return NULL; + } - retval = vim_strsave(get_tv_string(&rettv)); + char *const retval = xstrdup(tv_get_string(&rettv)); tv_clear(&rettv); return retval; } @@ -1318,8 +1324,8 @@ void * call_func_retlist ( char_u *func, int argc, - char_u **argv, - int safe /* use the sandbox */ + const char_u *const *const argv, + int safe // use the sandbox ) { typval_T rettv; @@ -2202,7 +2208,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (lp->ll_tv->v_type == VAR_DICT) { if (len == -1) { // "[key]": get key from "var1" - key = get_tv_string_chk(&var1); // is number or string + key = (char_u *)tv_get_string(&var1); // is number or string if (key == NULL) { tv_clear(&var1); return NULL; @@ -4343,9 +4349,8 @@ eval_index ( bool empty1 = false; bool empty2 = false; long n1, n2 = 0; - long len = -1; + ptrdiff_t len = -1; int range = false; - char_u *s; char_u *key = NULL; switch (rettv->v_type) { @@ -4455,103 +4460,109 @@ eval_index ( tv_clear(&var1); } if (range) { - if (empty2) + if (empty2) { n2 = -1; - else { + } else { n2 = get_tv_number(&var2); tv_clear(&var2); } } switch (rettv->v_type) { - case VAR_NUMBER: - case VAR_STRING: - s = get_tv_string(rettv); - len = (long)STRLEN(s); - if (range) { - /* The resulting variable is a substring. If the indexes - * are out of range the result is empty. */ + case VAR_NUMBER: + case VAR_STRING: { + const char *const s = tv_get_string(rettv); + char *v; + len = (ptrdiff_t)strlen(s); + if (range) { + // The resulting variable is a substring. If the indexes + // are out of range the result is empty. + if (n1 < 0) { + n1 = len + n1; + if (n1 < 0) { + n1 = 0; + } + } + if (n2 < 0) { + n2 = len + n2; + } else if (n2 >= len) { + n2 = len; + } + if (n1 >= len || n2 < 0 || n1 > n2) { + v = NULL; + } else { + v = xmemdupz(s + n1, (size_t)(n2 - n1 + 1)); + } + } else { + // The resulting variable is a string of a single + // character. If the index is too big or negative the + // result is empty. + if (n1 >= len || n1 < 0) { + v = NULL; + } else { + v = xmemdupz(s + n1, 1); + } + } + tv_clear(rettv); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = (char_u *)v; + break; + } + case VAR_LIST: { + len = tv_list_len(rettv->vval.v_list); if (n1 < 0) { n1 = len + n1; - if (n1 < 0) - n1 = 0; } - if (n2 < 0) - n2 = len + n2; - else if (n2 >= len) - n2 = len; - if (n1 >= len || n2 < 0 || n1 > n2) - s = NULL; - else - s = vim_strnsave(s + n1, (int)(n2 - n1 + 1)); - } else { - /* The resulting variable is a string of a single - * character. If the index is too big or negative the - * result is empty. */ - if (n1 >= len || n1 < 0) - s = NULL; - else - s = vim_strnsave(s + n1, 1); - } - tv_clear(rettv); - rettv->v_type = VAR_STRING; - rettv->vval.v_string = s; - break; + if (!empty1 && (n1 < 0 || n1 >= len)) { + // For a range we allow invalid values and return an empty + // list. A list index out of range is an error. + if (!range) { + if (verbose) { + EMSGN(_(e_listidx), n1); + } + return FAIL; + } + n1 = len; + } + if (range) { + list_T *l; + listitem_T *item; - case VAR_LIST: - len = tv_list_len(rettv->vval.v_list); - if (n1 < 0) { - n1 = len + n1; + if (n2 < 0) { + n2 = len + n2; + } else if (n2 >= len) { + n2 = len - 1; + } + if (!empty2 && (n2 < 0 || n2 + 1 < n1)) { + n2 = -1; + } + l = tv_list_alloc(); + item = tv_list_find(rettv->vval.v_list, n1); + while (n1++ <= n2) { + tv_list_append_tv(l, &item->li_tv); + item = item->li_next; + } + tv_clear(rettv); + rettv->v_type = VAR_LIST; + rettv->vval.v_list = l; + l->lv_refcount++; + } else { + copy_tv(&tv_list_find(rettv->vval.v_list, n1)->li_tv, &var1); + tv_clear(rettv); + *rettv = var1; + } + break; } - if (!empty1 && (n1 < 0 || n1 >= len)) { - /* For a range we allow invalid values and return an empty - * list. A list index out of range is an error. */ - if (!range) { - if (verbose) - EMSGN(_(e_listidx), n1); + case VAR_DICT: { + if (range) { + if (verbose) { + emsgf(_(e_dictrange)); + } + if (len == -1) { + tv_clear(&var1); + } return FAIL; } - n1 = len; - } - if (range) { - list_T *l; - listitem_T *item; - - if (n2 < 0) - n2 = len + n2; - else if (n2 >= len) - n2 = len - 1; - if (!empty2 && (n2 < 0 || n2 + 1 < n1)) - n2 = -1; - l = tv_list_alloc(); - item = tv_list_find(rettv->vval.v_list, n1); - while (n1++ <= n2) { - tv_list_append_tv(l, &item->li_tv); - item = item->li_next; - } - tv_clear(rettv); - rettv->v_type = VAR_LIST; - rettv->vval.v_list = l; - ++l->lv_refcount; - } else { - copy_tv(&tv_list_find(rettv->vval.v_list, n1)->li_tv, &var1); - tv_clear(rettv); - *rettv = var1; - } - break; - - case VAR_DICT: - if (range) { - if (verbose) { - emsgf(_(e_dictrange)); - } - if (len == -1) { - tv_clear(&var1); - } - return FAIL; - } - { - dictitem_T *item; if (len == -1) { key = get_tv_string_chk(&var1); @@ -4561,7 +4572,8 @@ eval_index ( } } - item = tv_dict_find(rettv->vval.v_dict, (const char *)key, len); + dictitem_T *const item = tv_dict_find(rettv->vval.v_dict, + (const char *)key, len); if (item == NULL && verbose) { emsgf(_(e_dictkey), key); @@ -4576,14 +4588,15 @@ eval_index ( copy_tv(&item->di_tv, &var1); tv_clear(rettv); *rettv = var1; + break; + } + case VAR_SPECIAL: + case VAR_FUNC: + case VAR_FLOAT: + case VAR_PARTIAL: + case VAR_UNKNOWN: { + break; // Not evaluating, skipping over subscript } - break; - case VAR_SPECIAL: - case VAR_FUNC: - case VAR_PARTIAL: - case VAR_FLOAT: - case VAR_UNKNOWN: - break; // Not evaluating, skipping over subscript } } @@ -6251,7 +6264,7 @@ bool set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID) /// Also returns OK when an error was encountered while executing the function. int call_func( - char_u *funcname, // name of the function + const char_u *funcname, // name of the function int len, // length of "name" typval_T *rettv, // return value goes here int argcount_in, // number of "argvars" @@ -7249,24 +7262,24 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, /// "call(func, arglist [, dict])" function static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *func; - partial_T *partial = NULL; - dict_T *selfdict = NULL; - if (argvars[1].v_type != VAR_LIST) { EMSG(_(e_listreq)); return; } - if (argvars[1].vval.v_list == NULL) + if (argvars[1].vval.v_list == NULL) { return; + } + char_u *func; + partial_T *partial = NULL; + dict_T *selfdict = NULL; if (argvars[0].v_type == VAR_FUNC) { func = argvars[0].vval.v_string; } else if (argvars[0].v_type == VAR_PARTIAL) { partial = argvars[0].vval.v_partial; func = partial_name(partial); } else { - func = get_tv_string(&argvars[0]); + func = (char_u *)tv_get_string(&argvars[0]); } if (*func == NUL) { return; // type error or empty name @@ -7299,15 +7312,15 @@ static void f_char2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (has_mbyte) { int utf8 = 0; - if (argvars[1].v_type != VAR_UNKNOWN) + if (argvars[1].v_type != VAR_UNKNOWN) { utf8 = get_tv_number_chk(&argvars[1], NULL); + } - if (utf8) - rettv->vval.v_number = (*utf_ptr2char)(get_tv_string(&argvars[0])); - else - rettv->vval.v_number = (*mb_ptr2char)(get_tv_string(&argvars[0])); - } else - rettv->vval.v_number = get_tv_string(&argvars[0])[0]; + rettv->vval.v_number = (utf8 ? *utf_ptr2char : *mb_ptr2char)( + (const char_u *)tv_get_string(&argvars[0])); + } else { + rettv->vval.v_number = (uint8_t)(tv_get_string(&argvars[0])[0]); + } } /* @@ -7552,16 +7565,17 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_cscope_connection(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int num = 0; - char_u *dbpath = NULL; - char_u *prepend = NULL; + char_u *dbpath = NULL; + char_u *prepend = NULL; char_u buf[NUMBUFLEN]; if (argvars[0].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_UNKNOWN) { num = (int)get_tv_number(&argvars[0]); - dbpath = get_tv_string(&argvars[1]); - if (argvars[2].v_type != VAR_UNKNOWN) + dbpath = (char_u *)tv_get_string(&argvars[1]); + if (argvars[2].v_type != VAR_UNKNOWN) { prepend = get_tv_string_buf(&argvars[2], buf); + } } rettv->vval.v_number = cs_connection(num, dbpath, prepend); @@ -7648,7 +7662,6 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char_u nbuf[NUMBUFLEN]; - char_u *name; char_u *flags; rettv->vval.v_number = -1; @@ -7656,7 +7669,7 @@ static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - name = get_tv_string(&argvars[0]); + const char *const name = tv_get_string(&argvars[0]); if (name == NULL || *name == NUL) { EMSG(_(e_invarg)); return; @@ -7670,10 +7683,10 @@ static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (*flags == NUL) { // delete a file - rettv->vval.v_number = os_remove((char *)name) == 0 ? 0 : -1; + rettv->vval.v_number = os_remove(name) == 0 ? 0 : -1; } else if (STRCMP(flags, "d") == 0) { // delete an empty directory - rettv->vval.v_number = os_rmdir((char *)name) == 0 ? 0 : -1; + rettv->vval.v_number = os_rmdir(name) == 0 ? 0 : -1; } else if (STRCMP(flags, "rf") == 0) { // delete a directory recursively rettv->vval.v_number = delete_recursive(name); @@ -7894,7 +7907,8 @@ static void f_escape(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char_u buf[NUMBUFLEN]; - rettv->vval.v_string = vim_strsave_escaped(get_tv_string(&argvars[0]), + rettv->vval.v_string = vim_strsave_escaped( + (const char_u *)tv_get_string(&argvars[0]), get_tv_string_buf(&argvars[1], buf)); rettv->v_type = VAR_STRING; } @@ -7936,11 +7950,13 @@ static void f_eventhandler(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_executable(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *name = get_tv_string(&argvars[0]); + const char *name = tv_get_string(&argvars[0]); // Check in $PATH and also check directly if there is a directory name - rettv->vval.v_number = os_can_exe(name, NULL, true) - || (gettail_dir(name) != name && os_can_exe(name, NULL, false)); + rettv->vval.v_number = ( + os_can_exe((const char_u *)name, NULL, true) + || (gettail_dir(name) != name + && os_can_exe((const char_u *)name, NULL, false))); } static char_u * get_list_line(int c, void *cookie, int indent) @@ -7993,11 +8009,11 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) capture_ga = &capture_local; if (argvars[0].v_type != VAR_LIST) { - do_cmdline_cmd((char *)get_tv_string(&argvars[0])); + do_cmdline_cmd(tv_get_string(&argvars[0])); } else if (argvars[0].vval.v_list != NULL) { - list_T *list = argvars[0].vval.v_list; + list_T *const list = argvars[0].vval.v_list; list->lv_refcount++; - listitem_T *item = list->lv_first; + listitem_T *const item = list->lv_first; do_cmdline(NULL, get_list_line, (void *)&item, DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); list->lv_refcount--; @@ -8017,10 +8033,10 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "exepath()" function static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *arg = get_tv_string(&argvars[0]); + const char *arg = tv_get_string(&argvars[0]); char_u *path = NULL; - (void)os_can_exe(arg, &path, true); + (void)os_can_exe((const char_u *)arg, &path, true); rettv->v_type = VAR_STRING; rettv->vval.v_string = path; @@ -8034,21 +8050,21 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr) int n = false; int len = 0; - char *p = (char *)get_tv_string(&argvars[0]); + const char *p = tv_get_string(&argvars[0]); if (*p == '$') { // Environment variable. // First try "normal" environment variables (fast). if (os_getenv(p + 1) != NULL) { n = true; } else { // Try expanding things like $VIM and ${HOME}. - p = (char *)expand_env_save((char_u *)p); - if (p != NULL && *p != '$') { + char_u *const exp = expand_env_save((char_u *)p); + if (exp != NULL && *exp != '$') { n = true; } - xfree(p); + xfree(exp); } } else if (*p == '&' || *p == '+') { // Option. - n = (get_option_tv((const char **)&p, NULL, true) == OK); + n = (get_option_tv(&p, NULL, true) == OK); if (*skipwhite((const char_u *)p) != NUL) { n = false; // Trailing garbage. } @@ -8076,7 +8092,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr) n = (get_var_tv(name, len, &tv, NULL, false, true) == OK); if (n) { // Handle d.key, l[idx], f(expr). - n = (handle_subscript((const char **)&p, &tv, true, false) == OK); + n = (handle_subscript(&p, &tv, true, false) == OK); if (n) { tv_clear(&tv); } @@ -8096,7 +8112,6 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *s; size_t len; char_u *errormsg; int options = WILD_SILENT|WILD_USE_NL|WILD_LIST_NOTFOUND; @@ -8113,11 +8128,11 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_list = NULL; } - s = get_tv_string(&argvars[0]); + const char *s = tv_get_string(&argvars[0]); if (*s == '%' || *s == '#' || *s == '<') { - ++emsg_off; - result = eval_vars(s, s, &len, NULL, &errormsg, NULL); - --emsg_off; + emsg_off++; + result = eval_vars((char_u *)s, (char_u *)s, &len, NULL, &errormsg, NULL); + emsg_off--; if (rettv->v_type == VAR_LIST) { tv_list_alloc_ret(rettv); if (result != NULL) { @@ -8134,21 +8149,24 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (!error) { ExpandInit(&xpc); xpc.xp_context = EXPAND_FILES; - if (p_wic) + if (p_wic) { options += WILD_ICASE; + } if (rettv->v_type == VAR_STRING) { - rettv->vval.v_string = ExpandOne(&xpc, s, NULL, options, WILD_ALL); + rettv->vval.v_string = ExpandOne(&xpc, (char_u *)s, NULL, options, + WILD_ALL); } else { tv_list_alloc_ret(rettv); - ExpandOne(&xpc, s, NULL, options, WILD_ALL_KEEP); + ExpandOne(&xpc, (char_u *)s, NULL, options, WILD_ALL_KEEP); for (int i = 0; i < xpc.xp_numfiles; i++) { tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], -1); } ExpandCleanup(&xpc); } - } else + } else { rettv->vval.v_string = NULL; + } } } @@ -8235,7 +8253,6 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_feedkeys(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *keys, *flags = NULL; char_u nbuf[NUMBUFLEN]; /* This is not allowed in the sandbox. If the commands would still be @@ -8244,10 +8261,11 @@ static void f_feedkeys(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (check_secure()) return; - keys = get_tv_string(&argvars[0]); + const char *const keys = tv_get_string(&argvars[0]); + const char *flags = NULL; if (argvars[1].v_type != VAR_UNKNOWN) { - flags = get_tv_string_buf(&argvars[1], nbuf); + flags = (const char *)get_tv_string_buf(&argvars[1], nbuf); } nvim_feedkeys(cstr_as_string((char *)keys), @@ -8257,9 +8275,9 @@ static void f_feedkeys(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "filereadable()" function static void f_filereadable(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p = get_tv_string(&argvars[0]); + const char *const p = tv_get_string(&argvars[0]); rettv->vval.v_number = - (*p && !os_isdir(p) && os_file_is_readable((char*)p)); + (*p && !os_isdir((const char_u *)p) && os_file_is_readable(p)); } /* @@ -8268,14 +8286,13 @@ static void f_filereadable(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_filewritable(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char *filename = (char *)get_tv_string(&argvars[0]); + const char *filename = tv_get_string(&argvars[0]); rettv->vval.v_number = os_file_is_writable(filename); } static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) { - char_u *fname; char_u *fresult = NULL; char_u *path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; char_u *p; @@ -8287,7 +8304,7 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) rettv->vval.v_string = NULL; rettv->v_type = VAR_STRING; - fname = get_tv_string(&argvars[0]); + const char *fname = tv_get_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { p = get_tv_string_buf_chk(&argvars[1], pathbuf); @@ -8311,8 +8328,8 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) do { if (rettv->v_type == VAR_STRING || rettv->v_type == VAR_LIST) xfree(fresult); - fresult = find_file_in_path_option(first ? fname : NULL, - first ? STRLEN(fname) : 0, + fresult = find_file_in_path_option(first ? (char_u *)fname : NULL, + first ? strlen(fname) : 0, 0, first, path, find_what, curbuf->b_ffname, (find_what == FINDFILE_DIR @@ -8572,8 +8589,8 @@ static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_fnameescape(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_string = vim_strsave_fnameescape( - get_tv_string(&argvars[0]), FALSE); + rettv->vval.v_string = (char_u *)vim_strsave_fnameescape( + tv_get_string(&argvars[0]), false); rettv->v_type = VAR_STRING; } @@ -8765,7 +8782,7 @@ static void common_function(typval_T *argvars, typval_T *rettv, s = partial_name(arg_pt); } else { // function('MyFunc', [arg], dict) - s = get_tv_string(&argvars[0]); + s = (char_u *)tv_get_string(&argvars[0]); use_string = true; } @@ -8780,7 +8797,9 @@ static void common_function(typval_T *argvars, typval_T *rettv, } if (s == NULL || *s == NUL || (use_string && ascii_isdigit(*s)) || (is_funcref && trans_name == NULL)) { - EMSG2(_(e_invarg2), use_string ? get_tv_string(&argvars[0]) : s); + emsgf(_(e_invarg2), (use_string + ? tv_get_string(&argvars[0]) + : (const char *)s)); // Don't check an autoload name for existence here. } else if (trans_name != NULL && (is_funcref ? find_func(trans_name) == NULL @@ -8958,7 +8977,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (argvars[0].v_type == VAR_DICT) { if ((d = argvars[0].vval.v_dict) != NULL) { - di = tv_dict_find(d, (const char *)get_tv_string(&argvars[1]), -1); + di = tv_dict_find(d, tv_get_string(&argvars[1]), -1); if (di != NULL) { tv = &di->di_tv; } @@ -8977,27 +8996,26 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (pt != NULL) { - char_u *what = get_tv_string(&argvars[1]); - char_u *n; + const char *const what = tv_get_string(&argvars[1]); - if (STRCMP(what, "func") == 0 || STRCMP(what, "name") == 0) { + if (strcmp(what, "func") == 0 || strcmp(what, "name") == 0) { rettv->v_type = (*what == 'f' ? VAR_FUNC : VAR_STRING); - n = partial_name(pt); + const char *const n = (const char *)partial_name(pt); if (n == NULL) { rettv->vval.v_string = NULL; } else { - rettv->vval.v_string = vim_strsave(n); + rettv->vval.v_string = (char_u *)xstrdup(n); if (rettv->v_type == VAR_FUNC) { func_ref(rettv->vval.v_string); } } - } else if (STRCMP(what, "dict") == 0) { + } else if (strcmp(what, "dict") == 0) { rettv->v_type = VAR_DICT; rettv->vval.v_dict = pt->pt_dict; if (pt->pt_dict != NULL) { (pt->pt_dict->dv_refcount)++; } - } else if (STRCMP(what, "args") == 0) { + } else if (strcmp(what, "args") == 0) { rettv->v_type = VAR_LIST; if (tv_list_alloc_ret(rettv) != NULL) { for (int i = 0; i < pt->pt_argc; i++) { @@ -9448,9 +9466,10 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) } ExpandInit(&xpc); - xpc.xp_pattern = get_tv_string(&argvars[0]); + xpc.xp_pattern = (char_u *)tv_get_string(&argvars[0]); xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); - xpc.xp_context = cmdcomplete_str_to_type(get_tv_string(&argvars[1])); + xpc.xp_context = cmdcomplete_str_to_type( + (char_u *)tv_get_string(&argvars[1])); if (xpc.xp_context == EXPAND_NOTHING) { EMSG2(_(e_invarg2), argvars[1].vval.v_string); return; @@ -9623,20 +9642,21 @@ static void f_getfontname(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *perm = NULL; + char *perm = NULL; char_u flags[] = "rwx"; - char_u *filename = get_tv_string(&argvars[0]); + const char *filename = tv_get_string(&argvars[0]); int32_t file_perm = os_getperm(filename); if (file_perm >= 0) { - perm = vim_strsave((char_u *)"---------"); + perm = xstrdup("---------"); for (int i = 0; i < 9; i++) { - if (file_perm & (1 << (8 - i))) + if (file_perm & (1 << (8 - i))) { perm[i] = flags[i % 3]; + } } } rettv->v_type = VAR_STRING; - rettv->vval.v_string = perm; + rettv->vval.v_string = (char_u *)perm; } /* @@ -9644,16 +9664,16 @@ static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getfsize(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char *fname = (char *)get_tv_string(&argvars[0]); + const char *fname = tv_get_string(&argvars[0]); rettv->v_type = VAR_NUMBER; FileInfo file_info; if (os_fileinfo(fname, &file_info)) { uint64_t filesize = os_fileinfo_size(&file_info); - if (os_isdir((char_u *)fname)) + if (os_isdir((const char_u *)fname)) { rettv->vval.v_number = 0; - else { + } else { rettv->vval.v_number = (varnumber_T)filesize; /* non-perfect check for overflow */ @@ -9671,7 +9691,7 @@ static void f_getfsize(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char *fname = (char *)get_tv_string(&argvars[0]); + const char *fname = tv_get_string(&argvars[0]); FileInfo file_info; if (os_fileinfo(fname, &file_info)) { @@ -9686,15 +9706,14 @@ static void f_getftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *fname; char_u *type = NULL; char *t; - fname = get_tv_string(&argvars[0]); + const char *fname = tv_get_string(&argvars[0]); rettv->v_type = VAR_STRING; FileInfo file_info; - if (os_fileinfo_link((char *)fname, &file_info)) { + if (os_fileinfo_link(fname, &file_info)) { uint64_t mode = file_info.stat.st_mode; #ifdef S_ISREG if (S_ISREG(mode)) @@ -9746,10 +9765,11 @@ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr) default: t = "other"; } # else - if (os_isdir(fname)) + if (os_isdir((const char_u *)fname)) { t = "dir"; - else + } else { t = "file"; + } # endif #endif type = vim_strsave((char_u *)t); @@ -10345,11 +10365,12 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (p_wic) options += WILD_ICASE; if (rettv->v_type == VAR_STRING) { - rettv->vval.v_string = ExpandOne(&xpc, get_tv_string(&argvars[0]), NULL, - options, WILD_ALL); + rettv->vval.v_string = ExpandOne( + &xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options, WILD_ALL); } else { tv_list_alloc_ret(rettv); - ExpandOne(&xpc, get_tv_string(&argvars[0]), NULL, options, WILD_ALL_KEEP); + ExpandOne(&xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options, + WILD_ALL_KEEP); for (int i = 0; i < xpc.xp_numfiles; i++) { tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], -1); @@ -10393,7 +10414,7 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (file != NULL && !error) { garray_T ga; ga_init(&ga, (int)sizeof(char_u *), 10); - globpath(get_tv_string(&argvars[0]), file, &ga, flags); + globpath((char_u *)tv_get_string(&argvars[0]), file, &ga, flags); if (rettv->v_type == VAR_STRING) { rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n"); @@ -10425,7 +10446,7 @@ static void f_glob2regpat(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "has()" function static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - static char *(has_list[]) = { + static const char *const has_list[] = { #ifdef UNIX "unix", #endif @@ -10536,13 +10557,11 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) "winaltkeys", "writebackup", "nvim", - NULL }; bool n = false; - char *name = (char *)get_tv_string(&argvars[0]); - - for (int i = 0; has_list[i] != NULL; i++) { + const char *const name = tv_get_string(&argvars[0]); + for (size_t i = 0; i < ARRAY_SIZE(has_list); i++) { if (STRICMP(name, has_list[i]) == 0) { n = true; break; @@ -10608,7 +10627,7 @@ static void f_has_key(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; rettv->vval.v_number = tv_dict_find(argvars[0].vval.v_dict, - (const char *)get_tv_string(&argvars[1]), + tv_get_string(&argvars[1]), -1) != NULL; } @@ -10718,24 +10737,24 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *name; - char_u *mode; + const char *mode; + const char *const name = tv_get_string(&argvars[0]); + bool abbr = false; char_u buf[NUMBUFLEN]; - int abbr = FALSE; - - name = get_tv_string(&argvars[0]); - if (argvars[1].v_type == VAR_UNKNOWN) - mode = (char_u *)"nvo"; - else { - mode = get_tv_string_buf(&argvars[1], buf); - if (argvars[2].v_type != VAR_UNKNOWN) + if (argvars[1].v_type == VAR_UNKNOWN) { + mode = "nvo"; + } else { + mode = (const char *)get_tv_string_buf(&argvars[1], buf); + if (argvars[2].v_type != VAR_UNKNOWN) { abbr = get_tv_number(&argvars[2]); + } } - if (map_to_exists(name, mode, abbr)) - rettv->vval.v_number = TRUE; - else - rettv->vval.v_number = FALSE; + if (map_to_exists(name, mode, abbr)) { + rettv->vval.v_number = true; + } else { + rettv->vval.v_number = false; + } } /* @@ -10840,7 +10859,8 @@ static void f_histnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = syn_name2id(get_tv_string(&argvars[0])); + rettv->vval.v_number = syn_name2id( + (const char_u *)tv_get_string(&argvars[0])); } /* @@ -10848,7 +10868,8 @@ static void f_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_hlexists(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = highlight_exists(get_tv_string(&argvars[0])); + rettv->vval.v_number = highlight_exists( + (const char_u *)tv_get_string(&argvars[0])); } /* @@ -10868,25 +10889,27 @@ static void f_hostname(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf1[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; - char_u *from, *to, *str; vimconv_T vimconv; rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - str = get_tv_string(&argvars[0]); - from = enc_canonize(enc_skip(get_tv_string_buf(&argvars[1], buf1))); - to = enc_canonize(enc_skip(get_tv_string_buf(&argvars[2], buf2))); + const char *const str = tv_get_string(&argvars[0]); + char_u buf1[NUMBUFLEN]; + char_u *const from = enc_canonize(enc_skip( + get_tv_string_buf(&argvars[1], buf1))); + char_u buf2[NUMBUFLEN]; + char_u *const to = enc_canonize(enc_skip( + get_tv_string_buf(&argvars[2], buf2))); vimconv.vc_type = CONV_NONE; convert_setup(&vimconv, from, to); - /* If the encodings are equal, no conversion needed. */ - if (vimconv.vc_type == CONV_NONE) - rettv->vval.v_string = vim_strsave(str); - else - rettv->vval.v_string = string_convert(&vimconv, str, NULL); + // If the encodings are equal, no conversion needed. + if (vimconv.vc_type == CONV_NONE) { + rettv->vval.v_string = (char_u *)xstrdup(str); + } else { + rettv->vval.v_string = string_convert(&vimconv, (char_u *)str, NULL); + } convert_setup(&vimconv, NULL, NULL); xfree(from); @@ -11075,7 +11098,7 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) msg_clr_eos(); for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next) { - msg_puts((const char *)get_tv_string(&li->li_tv)); + msg_puts(tv_get_string(&li->li_tv)); msg_putchar('\n'); } @@ -11179,7 +11202,7 @@ static void f_invert(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_isdirectory(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = os_isdir(get_tv_string(&argvars[0])); + rettv->vval.v_number = os_isdir((const char_u *)tv_get_string(&argvars[0])); } /* @@ -11191,9 +11214,11 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) dictitem_T *di; rettv->vval.v_number = -1; - const char_u *const end = get_lval(get_tv_string(&argvars[0]), NULL, + const char_u *const end = get_lval((char_u *)tv_get_string(&argvars[0]), + NULL, &lv, false, false, - GLV_NO_AUTOLOAD, FNE_CHECK_START); + GLV_NO_AUTOLOAD|GLV_READ_ONLY, + FNE_CHECK_START); if (end != NULL && lv.ll_name != NULL) { if (*end != NUL) { EMSG(_(e_trailing)); @@ -11416,8 +11441,8 @@ static void f_jobsend(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - ssize_t input_len; - char *input = (char *) save_tv_as_string(&argvars[1], &input_len, false); + ptrdiff_t input_len = 0; + char *input = save_tv_as_string(&argvars[1], &input_len, false); if (!input) { // Either the error has been handled by save_tv_as_string(), or there is no // input to send. @@ -11462,10 +11487,10 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = 1; } -static char **tv_to_argv(typval_T *cmd_tv, char **cmd, bool *executable) +static char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable) { if (cmd_tv->v_type == VAR_STRING) { - char *cmd_str = (char *)get_tv_string(cmd_tv); + const char *cmd_str = tv_get_string(cmd_tv); if (cmd) { *cmd = cmd_str; } @@ -11504,7 +11529,7 @@ static char **tv_to_argv(typval_T *cmd_tv, char **cmd, bool *executable) for (listitem_T *arg = argl->lv_first; arg != NULL; arg = arg->li_next) { char *a = (char *)get_tv_string_chk(&arg->li_tv); if (!a) { - // Did emsg in get_tv_string; just deallocate argv. + // Did emsg in tv_get_string_chk; just deallocate argv. shell_free_argv(argv); return NULL; } @@ -11841,24 +11866,28 @@ static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr) { switch (argvars[0].v_type) { - case VAR_STRING: - case VAR_NUMBER: - rettv->vval.v_number = (varnumber_T)STRLEN( - get_tv_string(&argvars[0])); - break; - case VAR_LIST: - rettv->vval.v_number = tv_list_len(argvars[0].vval.v_list); - break; - case VAR_DICT: - rettv->vval.v_number = tv_dict_len(argvars[0].vval.v_dict); - break; - case VAR_UNKNOWN: - case VAR_SPECIAL: - case VAR_FLOAT: - case VAR_FUNC: - case VAR_PARTIAL: - EMSG(_("E701: Invalid type for len()")); - break; + case VAR_STRING: + case VAR_NUMBER: { + rettv->vval.v_number = (varnumber_T)strlen( + tv_get_string(&argvars[0])); + break; + } + case VAR_LIST: { + rettv->vval.v_number = tv_list_len(argvars[0].vval.v_list); + break; + } + case VAR_DICT: { + rettv->vval.v_number = tv_dict_len(argvars[0].vval.v_dict); + break; + } + case VAR_UNKNOWN: + case VAR_SPECIAL: + case VAR_FLOAT: + case VAR_PARTIAL: + case VAR_FUNC: { + EMSG(_("E701: Invalid type for len()")); + break; + } } } @@ -11983,7 +12012,6 @@ static void f_localtime(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) { - char_u *keys; char_u *which; char_u buf[NUMBUFLEN]; char_u *keys_buf = NULL; @@ -11994,13 +12022,14 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) mapblock_T *mp; int buffer_local; - /* return empty string for failure */ + // Return empty string for failure. rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - keys = get_tv_string(&argvars[0]); - if (*keys == NUL) + char_u *keys = (char_u *)tv_get_string(&argvars[0]); + if (*keys == NUL) { return; + } if (argvars[1].v_type != VAR_UNKNOWN) { which = get_tv_string_buf_chk(&argvars[1], buf); @@ -12118,7 +12147,7 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) goto theend; li = l->lv_first; } else { - expr = str = get_tv_string(&argvars[0]); + expr = str = (char_u *)tv_get_string(&argvars[0]); len = (long)STRLEN(str); } @@ -12278,7 +12307,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) int prio = 10; /* default priority */ int id = -1; bool error = false; - char_u *conceal_char = NULL; + const char *conceal_char = NULL; rettv->vval.v_number = -1; @@ -12296,7 +12325,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) dictitem_T *di; if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) != NULL) { - conceal_char = get_tv_string(&di->di_tv); + conceal_char = tv_get_string(&di->di_tv); } } } @@ -12311,7 +12340,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = match_add(curwin, (const char *)grp, (const char *)pat, prio, id, - NULL, (const char *)conceal_char); + NULL, conceal_char); } static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) @@ -12338,7 +12367,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool error = false; int prio = 10; int id = -1; - char_u *conceal_char = NULL; + const char *conceal_char = NULL; if (argvars[2].v_type != VAR_UNKNOWN) { prio = get_tv_number_chk(&argvars[2], &error); @@ -12352,7 +12381,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) dictitem_T *di; if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) != NULL) { - conceal_char = get_tv_string(&di->di_tv); + conceal_char = tv_get_string(&di->di_tv); } } } @@ -12368,7 +12397,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) } rettv->vval.v_number = match_add(curwin, (const char *)group, NULL, prio, id, - l, (const char *)conceal_char); + l, conceal_char); } /* @@ -12523,9 +12552,10 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) *path_tail_with_sep(dir) = NUL; if (argvars[1].v_type != VAR_UNKNOWN) { - if (argvars[2].v_type != VAR_UNKNOWN) + if (argvars[2].v_type != VAR_UNKNOWN) { prot = get_tv_number_chk(&argvars[2], NULL); - if (prot != -1 && STRCMP(get_tv_string(&argvars[1]), "p") == 0) { + } + if (prot != -1 && strcmp(tv_get_string(&argvars[1]), "p") == 0) { char *failed_dir; int ret = os_mkdir_recurse((char *) dir, prot, &failed_dir); if (ret != 0) { @@ -12894,8 +12924,7 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int binary = FALSE; - char_u *fname; + bool binary = false; FILE *fd; char_u buf[(IOSIZE/256)*256]; /* rounded to avoid odd + 1 */ int io_size = sizeof(buf); @@ -12909,19 +12938,21 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) char_u *start; /* start of current line */ if (argvars[1].v_type != VAR_UNKNOWN) { - if (STRCMP(get_tv_string(&argvars[1]), "b") == 0) - binary = TRUE; - if (argvars[2].v_type != VAR_UNKNOWN) + if (strcmp(tv_get_string(&argvars[1]), "b") == 0) { + binary = true; + } + if (argvars[2].v_type != VAR_UNKNOWN) { maxline = get_tv_number(&argvars[2]); + } } tv_list_alloc_ret(rettv); - /* Always open the file in binary mode, library functions have a mind of - * their own about CR-LF conversion. */ - fname = get_tv_string(&argvars[0]); - if (*fname == NUL || (fd = mch_fopen((char *)fname, READBIN)) == NULL) { - EMSG2(_(e_notopen), *fname == NUL ? (char_u *)_("") : fname); + // Always open the file in binary mode, library functions have a mind of + // their own about CR-LF conversion. + const char *const fname = tv_get_string(&argvars[0]); + if (*fname == NUL || (fd = mch_fopen(fname, READBIN)) == NULL) { + EMSG2(_(e_notopen), *fname == NUL ? _("") : fname); return; } @@ -13241,11 +13272,13 @@ static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char_u buf[NUMBUFLEN]; - if (check_restricted() || check_secure()) + if (check_restricted() || check_secure()) { rettv->vval.v_number = -1; - else - rettv->vval.v_number = vim_rename(get_tv_string(&argvars[0]), + } else { + rettv->vval.v_number = vim_rename( + (char_u *)tv_get_string(&argvars[0]), get_tv_string_buf(&argvars[1], buf)); + } } /* @@ -13253,32 +13286,37 @@ static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p; - int n; - - n = get_tv_number(&argvars[1]); + varnumber_T n = get_tv_number(&argvars[1]); if (argvars[0].v_type == VAR_LIST) { tv_list_alloc_ret(rettv); - if (argvars[0].vval.v_list != NULL) { - while (n-- > 0) { - tv_list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL); - } + while (n-- > 0) { + tv_list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL); } } else { - p = get_tv_string(&argvars[0]); rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - - int slen = (int)STRLEN(p); - int len = slen * n; - if (len <= 0) + if (n <= 0) { return; + } - char_u *r = xmallocz(len); - for (int i = 0; i < n; i++) - memmove(r + i * slen, p, (size_t)slen); + const char *const p = tv_get_string(&argvars[0]); - rettv->vval.v_string = r; + const size_t slen = strlen(p); + if (slen == 0) { + return; + } + const size_t len = slen * n; + // Detect overflow. + if (len / n != slen) { + return; + } + + char *const r = xmallocz(len); + for (varnumber_T i = 0; i < n; i++) { + memmove(r + i * slen, p, slen); + } + + rettv->vval.v_string = (char_u *)r; } } @@ -13287,59 +13325,49 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p; -#ifdef HAVE_READLINK - char_u *buf = NULL; -#endif - - p = get_tv_string(&argvars[0]); + rettv->v_type = VAR_STRING; + const char *fname = tv_get_string(&argvars[0]); #ifdef WIN32 - { - char *v = os_resolve_shortcut(p); - if (v != NULL) { - rettv->vval.v_string = (char_u *)v; - } else { - rettv->vval.v_string = vim_strsave(p); - } - } + char *const v = os_resolve_shortcut(fname); + rettv->vval.v_string = (char_u *)(v == NULL ? xstrdup(fname) : v); #else # ifdef HAVE_READLINK { - char_u *cpy; - int len; - char_u *remain = NULL; - char_u *q; - int is_relative_to_current = FALSE; - int has_trailing_pathsep = FALSE; + bool is_relative_to_current = false; + bool has_trailing_pathsep = false; int limit = 100; - p = vim_strsave(p); + char *p = xstrdup(fname); if (p[0] == '.' && (vim_ispathsep(p[1]) - || (p[1] == '.' && (vim_ispathsep(p[2]))))) - is_relative_to_current = TRUE; - - len = STRLEN(p); - if (len > 0 && after_pathsep((char *)p, (char *)p + len)) { - has_trailing_pathsep = TRUE; - p[len - 1] = NUL; /* the trailing slash breaks readlink() */ + || (p[1] == '.' && (vim_ispathsep(p[2]))))) { + is_relative_to_current = true; } - q = path_next_component(p); + ptrdiff_t len = (ptrdiff_t)strlen(p); + if (len > 0 && after_pathsep(p, p + len)) { + has_trailing_pathsep = true; + p[len - 1] = NUL; // The trailing slash breaks readlink(). + } + + char *q = (char *)path_next_component(p); + char *remain = NULL; if (*q != NUL) { - /* Separate the first path component in "p", and keep the - * remainder (beginning with the path separator). */ - remain = vim_strsave(q - 1); + // Separate the first path component in "p", and keep the + // remainder (beginning with the path separator). + remain = xstrdup(q - 1); q[-1] = NUL; } - buf = xmallocz(MAXPATHL); + char *const buf = xmallocz(MAXPATHL); + char *cpy; for (;; ) { for (;; ) { - len = readlink((char *)p, (char *)buf, MAXPATHL); - if (len <= 0) + len = readlink(p, buf, MAXPATHL); + if (len <= 0) { break; + } buf[len] = NUL; if (limit-- == 0) { @@ -13347,66 +13375,74 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) xfree(remain); EMSG(_("E655: Too many symbolic links (cycle?)")); rettv->vval.v_string = NULL; - goto fail; + xfree(buf); + return; } - /* Ensure that the result will have a trailing path separator - * if the argument has one. */ - if (remain == NULL && has_trailing_pathsep) - add_pathsep((char *)buf); + // Ensure that the result will have a trailing path separator + // if the argument has one. */ + if (remain == NULL && has_trailing_pathsep) { + add_pathsep(buf); + } - /* Separate the first path component in the link value and - * concatenate the remainders. */ - q = path_next_component(vim_ispathsep(*buf) ? buf + 1 : buf); + // Separate the first path component in the link value and + // concatenate the remainders. */ + q = (char *)path_next_component(vim_ispathsep(*buf) ? buf + 1 : buf); if (*q != NUL) { cpy = remain; - remain = remain ? - concat_str(q - 1, remain) : (char_u *) xstrdup((char *)q - 1); + remain = (remain + ? (char *)concat_str((char_u *)q - 1, (char_u *)remain) + : xstrdup(q - 1)); xfree(cpy); q[-1] = NUL; } - q = path_tail(p); + q = (char *)path_tail((char_u *)p); if (q > p && *q == NUL) { - /* Ignore trailing path separator. */ + // Ignore trailing path separator. q[-1] = NUL; - q = path_tail(p); + q = (char *)path_tail((char_u *)p); } - if (q > p && !path_is_absolute_path(buf)) { - /* symlink is relative to directory of argument */ - cpy = xmalloc(STRLEN(p) + STRLEN(buf) + 1); - STRCPY(cpy, p); - STRCPY(path_tail(cpy), buf); + if (q > p && !path_is_absolute_path((const char_u *)buf)) { + // Symlink is relative to directory of argument. + const size_t p_len = strlen(p); + const size_t buf_len = strlen(buf); + cpy = xmalloc(p_len + buf_len + 1); + memcpy(cpy, p, p_len); + memcpy(path_tail((char_u *)cpy), buf, buf_len + 1); xfree(p); p = cpy; } else { xfree(p); - p = vim_strsave(buf); + p = xstrdup(buf); } } - if (remain == NULL) + if (remain == NULL) { break; + } - /* Append the first path component of "remain" to "p". */ - q = path_next_component(remain + 1); + // Append the first path component of "remain" to "p". + q = (char *)path_next_component(remain + 1); len = q - remain - (*q != NUL); - cpy = vim_strnsave(p, STRLEN(p) + len); - STRNCAT(cpy, remain, len); + const size_t p_len = strlen(p); + cpy = xmallocz(p_len + len); + memcpy(cpy, p, p_len + 1); + strncat(cpy + p_len, remain, len); xfree(p); p = cpy; - /* Shorten "remain". */ - if (*q != NUL) + // Shorten "remain". + if (*q != NUL) { STRMOVE(remain, q - 1); - else { + } else { xfree(remain); remain = NULL; } } - /* If the result is a relative path name, make it explicitly relative to - * the current directory if and only if the argument had this form. */ + // If the result is a relative path name, make it explicitly relative to + // the current directory if and only if the argument had this form. if (!vim_ispathsep(*p)) { if (is_relative_to_current && *p != NUL @@ -13416,42 +13452,40 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) || (p[1] == '.' && (p[2] == NUL || vim_ispathsep(p[2])))))) { - /* Prepend "./". */ - cpy = concat_str((char_u *)"./", p); + // Prepend "./". + cpy = (char *)concat_str((const char_u *)"./", (const char_u *)p); xfree(p); p = cpy; } else if (!is_relative_to_current) { - /* Strip leading "./". */ + // Strip leading "./". q = p; - while (q[0] == '.' && vim_ispathsep(q[1])) + while (q[0] == '.' && vim_ispathsep(q[1])) { q += 2; - if (q > p) + } + if (q > p) { STRMOVE(p, p + 2); + } } } - /* Ensure that the result will have no trailing path separator - * if the argument had none. But keep "/" or "//". */ + // Ensure that the result will have no trailing path separator + // if the argument had none. But keep "/" or "//". if (!has_trailing_pathsep) { - q = p + STRLEN(p); - if (after_pathsep((char *)p, (char *)q)) - *path_tail_with_sep(p) = NUL; + q = p + strlen(p); + if (after_pathsep(p, q)) { + *path_tail_with_sep((char_u *)p) = NUL; + } } - rettv->vval.v_string = p; + rettv->vval.v_string = (char_u *)p; + xfree(buf); } # else - rettv->vval.v_string = vim_strsave(p); + rettv->vval.v_string = (char_u *)xstrdup(p); # endif #endif simplify_filename(rettv->vval.v_string); - -#ifdef HAVE_READLINK -fail: - xfree(buf); -#endif - rettv->v_type = VAR_STRING; } /* @@ -13542,7 +13576,6 @@ static int get_search_arg(typval_T *varp, int *flagsp) static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) { int flags; - char_u *pat; pos_T pos; pos_T save_cursor; bool save_p_ws = p_ws; @@ -13554,10 +13587,11 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) int options = SEARCH_KEEP; int subpatnum; - pat = get_tv_string(&argvars[0]); - dir = get_search_arg(&argvars[1], flagsp); /* may set p_ws */ - if (dir == 0) + const char *const pat = tv_get_string(&argvars[0]); + dir = get_search_arg(&argvars[1], flagsp); // May set p_ws. + if (dir == 0) { goto theend; + } flags = *flagsp; if (flags & SP_START) { options |= SEARCH_START; @@ -13592,13 +13626,13 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) */ if (((flags & (SP_REPEAT | SP_RETCOUNT)) != 0) || ((flags & SP_NOMOVE) && (flags & SP_SETPCMARK))) { - EMSG2(_(e_invarg2), get_tv_string(&argvars[1])); + EMSG2(_(e_invarg2), tv_get_string(&argvars[1])); goto theend; } pos = save_cursor = curwin->w_cursor; - subpatnum = searchit(curwin, curbuf, &pos, dir, pat, 1L, - options, RE_SEARCH, (linenr_T)lnum_stop, &tm); + subpatnum = searchit(curwin, curbuf, &pos, dir, (char_u *)pat, 1, + options, RE_SEARCH, (linenr_T)lnum_stop, &tm); if (subpatnum != FAIL) { if (flags & SP_SUBPAT) retval = subpatnum; @@ -13655,7 +13689,7 @@ static void f_rpcnotify(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (!channel_send_event((uint64_t)argvars[0].vval.v_number, - (char *)get_tv_string(&argvars[1]), + tv_get_string(&argvars[1]), args)) { EMSG2(_(e_invarg2), "Channel doesn't exist"); return; @@ -13722,7 +13756,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) Error err = ERROR_INIT; Object result = channel_send_call((uint64_t)argvars[0].vval.v_number, - (char *)get_tv_string(&argvars[1]), + tv_get_string(&argvars[1]), args, &err); @@ -13797,7 +13831,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Copy arguments to the vector if (argsl > 0) { for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - argv[i++] = xstrdup((char *) get_tv_string(&arg->li_tv)); + argv[i++] = xstrdup(tv_get_string(&arg->li_tv)); } } @@ -13962,7 +13996,7 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) */ if ((flags & (SP_END | SP_SUBPAT)) != 0 || ((flags & SP_NOMOVE) && (flags & SP_SETPCMARK))) { - EMSG2(_(e_invarg2), get_tv_string(&argvars[3])); + EMSG2(_(e_invarg2), tv_get_string(&argvars[3])); goto theend; } @@ -14232,7 +14266,7 @@ static void f_serverstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_invarg)); return; } else { - rettv->vval.v_string = vim_strsave(get_tv_string(argvars)); + rettv->vval.v_string = (char_u *)xstrdup(tv_get_string(argvars)); } } else { rettv->vval.v_string = (char_u *)server_address_new(); @@ -14613,7 +14647,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Note: there are three number buffers involved: // - group_buf below. // - numbuf in tv_dict_get_string(). - // - mybuf in get_tv_string(). + // - mybuf in tv_get_string(). // // If you change this code make sure that buffers will not get // accidentally reused. @@ -14623,8 +14657,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) const int id = (int)tv_dict_get_number(d, "id"); dictitem_T *const conceal_di = tv_dict_find(d, S_LEN("conceal")); const char *const conceal = (conceal_di != NULL - ? (const char *)get_tv_string( - &conceal_di->di_tv) + ? tv_get_string(&conceal_di->di_tv) : NULL); if (i == 0) { if (match_add(curwin, group, @@ -14899,11 +14932,11 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) /// f_sha256 - sha256({string}) function static void f_sha256(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p = get_tv_string(&argvars[0]); - const char_u *hash = sha256_bytes(p, (int) STRLEN(p) , NULL, 0); + const char *p = tv_get_string(&argvars[0]); + const char *hash = sha256_bytes((const uint8_t *)p, strlen(p) , NULL, 0); // make a copy of the hash (sha256_bytes returns a static buffer) - rettv->vval.v_string = (char_u *) xstrdup((char *) hash); + rettv->vval.v_string = (char_u *)xstrdup(hash); rettv->v_type = VAR_STRING; } @@ -14913,7 +14946,8 @@ static void f_sha256(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_shellescape(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_string = vim_strsave_shellescape( - get_tv_string(&argvars[0]), non_zero_arg(&argvars[1]), true); + (const char_u *)tv_get_string(&argvars[0]), non_zero_arg(&argvars[1]), + true); rettv->v_type = VAR_STRING; } @@ -14930,11 +14964,9 @@ static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_simplify(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p; - - p = get_tv_string(&argvars[0]); - rettv->vval.v_string = vim_strsave(p); - simplify_filename(rettv->vval.v_string); /* simplify in place */ + const char *const p = tv_get_string(&argvars[0]); + rettv->vval.v_string = (char_u *)xstrdup(p); + simplify_filename(rettv->vval.v_string); // Simplify in place. rettv->v_type = VAR_STRING; } @@ -14950,7 +14982,7 @@ typedef struct { bool item_compare_numeric; bool item_compare_numbers; bool item_compare_float; - char_u *item_compare_func; + const char *item_compare_func; partial_T *item_compare_partial; dict_T *item_compare_selfdict; bool item_compare_func_err; @@ -15058,7 +15090,7 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) typval_T rettv; typval_T argv[3]; int dummy; - char_u *func_name; + const char *func_name; partial_T *partial = sortinfo->item_compare_partial; // shortcut after failure in previous call; compare all items equal @@ -15072,7 +15104,7 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) if (partial == NULL) { func_name = sortinfo->item_compare_func; } else { - func_name = partial_name(partial); + func_name = (const char *)partial_name(partial); } // Copy the values. This is needed to be able to set v_lock to VAR_FIXED @@ -15081,7 +15113,7 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) copy_tv(&si2->item->li_tv, &argv[1]); rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this - res = call_func(func_name, + res = call_func((const char_u *)func_name, (int)STRLEN(func_name), &rettv, 2, argv, NULL, 0L, 0L, &dummy, true, partial, sortinfo->item_compare_selfdict); @@ -15167,7 +15199,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) if (argvars[1].v_type != VAR_UNKNOWN) { /* optional second argument: {func} */ if (argvars[1].v_type == VAR_FUNC) { - info.item_compare_func = argvars[1].vval.v_string; + info.item_compare_func = (const char *)argvars[1].vval.v_string; } else if (argvars[1].v_type == VAR_PARTIAL) { info.item_compare_partial = argvars[1].vval.v_partial; } else { @@ -15180,7 +15212,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) if (i == 1) { info.item_compare_ic = true; } else if (argvars[1].v_type != VAR_NUMBER) { - info.item_compare_func = get_tv_string(&argvars[1]); + info.item_compare_func = tv_get_string(&argvars[1]); } else if (i != 0) { EMSG(_(e_invarg)); goto theend; @@ -15189,16 +15221,16 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) if (*info.item_compare_func == NUL) { // empty string means default sort info.item_compare_func = NULL; - } else if (STRCMP(info.item_compare_func, "n") == 0) { + } else if (strcmp(info.item_compare_func, "n") == 0) { info.item_compare_func = NULL; info.item_compare_numeric = true; - } else if (STRCMP(info.item_compare_func, "N") == 0) { + } else if (strcmp(info.item_compare_func, "N") == 0) { info.item_compare_func = NULL; info.item_compare_numbers = true; - } else if (STRCMP(info.item_compare_func, "f") == 0) { + } else if (strcmp(info.item_compare_func, "f") == 0) { info.item_compare_func = NULL; info.item_compare_float = true; - } else if (STRCMP(info.item_compare_func, "i") == 0) { + } else if (strcmp(info.item_compare_func, "i") == 0) { info.item_compare_func = NULL; info.item_compare_ic = true; } @@ -15332,11 +15364,9 @@ static void f_reltimefloat(typval_T *argvars , typval_T *rettv, FunPtr fptr) */ static void f_soundfold(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *s; - rettv->v_type = VAR_STRING; - s = get_tv_string(&argvars[0]); - rettv->vval.v_string = eval_soundfold(s); + const char *const s = tv_get_string(&argvars[0]); + rettv->vval.v_string = (char_u *)eval_soundfold(s); } /* @@ -15387,7 +15417,6 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *str; bool typeerr = false; int maxcount; garray_T ga; @@ -15397,7 +15426,7 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_alloc_ret(rettv); if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) { - str = get_tv_string(&argvars[0]); + const char *const str = tv_get_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { maxcount = get_tv_number_chk(&argvars[1], &typeerr); if (maxcount <= 0) @@ -15410,15 +15439,15 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else maxcount = 25; - spell_suggest_list(&ga, str, maxcount, need_capital, false); + spell_suggest_list(&ga, (char_u *)str, maxcount, need_capital, false); - for (int i = 0; i < ga.ga_len; ++i) { - str = ((char_u **)ga.ga_data)[i]; + for (int i = 0; i < ga.ga_len; i++) { + char *p = ((char **)ga.ga_data)[i]; li = tv_list_item_alloc(); li->li_tv.v_type = VAR_STRING; li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = str; + li->li_tv.vval.v_string = (char_u *)p; tv_list_append(rettv->vval.v_list, li); } ga_clear(&ga); @@ -15427,8 +15456,6 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *str; - char_u *end; char_u *pat = NULL; regmatch_T regmatch; char_u patbuf[NUMBUFLEN]; @@ -15442,7 +15469,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) save_cpo = p_cpo; p_cpo = (char_u *)""; - str = get_tv_string(&argvars[0]); + const char *str = tv_get_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { pat = get_tv_string_buf_chk(&argvars[1], patbuf); if (pat == NULL) { @@ -15464,29 +15491,33 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (regmatch.regprog != NULL) { regmatch.rm_ic = FALSE; while (*str != NUL || keepempty) { - if (*str == NUL) - match = FALSE; /* empty item at the end */ - else - match = vim_regexec_nl(®match, str, col); - if (match) - end = regmatch.startp[0]; - else - end = str + STRLEN(str); + if (*str == NUL) { + match = false; // Empty item at the end. + } else { + match = vim_regexec_nl(®match, (char_u *)str, col); + } + const char *end; + if (match) { + end = (const char *)regmatch.startp[0]; + } else { + end = str + strlen(str); + } if (keepempty || end > str || (rettv->vval.v_list->lv_len > 0 - && *str != NUL && match && end < - regmatch.endp[0])) { - tv_list_append_string(rettv->vval.v_list, (const char *)str, end - str); + && *str != NUL + && match + && end < (const char *)regmatch.endp[0])) { + tv_list_append_string(rettv->vval.v_list, str, end - str); } if (!match) break; - /* Advance to just after the match. */ - if (regmatch.endp[0] > str) + // Advance to just after the match. + if (regmatch.endp[0] > (char_u *)str) { col = 0; - else { - /* Don't get stuck at the same match. */ + } else { + // Don't get stuck at the same match. col = (*mb_ptr2len)(regmatch.endp[0]); } - str = regmatch.endp[0]; + str = (const char *)regmatch.endp[0]; } vim_regfree(regmatch.regprog); @@ -15500,11 +15531,12 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_str2float(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p = skipwhite(get_tv_string(&argvars[0])); + char_u *p = skipwhite((const char_u *)tv_get_string(&argvars[0])); - if (*p == '+') + if (*p == '+') { p = skipwhite(p + 1); - (void) string2float((char *) p, &rettv->vval.v_float); + } + (void)string2float((char *)p, &rettv->vval.v_float); rettv->v_type = VAR_FLOAT; } @@ -15512,7 +15544,6 @@ static void f_str2float(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int base = 10; - char_u *p; long n; int what; @@ -15524,22 +15555,26 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - p = skipwhite(get_tv_string(&argvars[0])); + char_u *p = skipwhite((const char_u *)tv_get_string(&argvars[0])); if (*p == '+') { p = skipwhite(p + 1); } switch (base) { - case 2: - what = STR2NR_BIN + STR2NR_FORCE; + case 2: { + what = STR2NR_BIN | STR2NR_FORCE; break; - case 8: - what = STR2NR_OCT + STR2NR_FORCE; + } + case 8: { + what = STR2NR_OCT | STR2NR_FORCE; break; - case 16: - what = STR2NR_HEX + STR2NR_FORCE; + } + case 16: { + what = STR2NR_HEX | STR2NR_FORCE; break; - default: + } + default: { what = 0; + } } vim_str2nr(p, NULL, NULL, what, &n, NULL, 0); rettv->vval.v_number = n; @@ -15550,17 +15585,16 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u result_buf[256]; time_t seconds; - char_u *p; rettv->v_type = VAR_STRING; - p = get_tv_string(&argvars[0]); - if (argvars[1].v_type == VAR_UNKNOWN) + char *p = (char *)tv_get_string(&argvars[0]); + if (argvars[1].v_type == VAR_UNKNOWN) { seconds = time(NULL); - else + } else { seconds = (time_t)get_tv_number(&argvars[1]); + } struct tm curtime; struct tm *curtime_ptr = os_localtime_r(&seconds, &curtime); @@ -15574,23 +15608,27 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) conv.vc_type = CONV_NONE; enc = enc_locale(); convert_setup(&conv, p_enc, enc); - if (conv.vc_type != CONV_NONE) - p = string_convert(&conv, p, NULL); - if (p != NULL) - (void)strftime((char *)result_buf, sizeof(result_buf), - (char *)p, curtime_ptr); - else + if (conv.vc_type != CONV_NONE) { + p = (char *)string_convert(&conv, (char_u *)p, NULL); + } + char result_buf[256]; + if (p != NULL) { + (void)strftime(result_buf, sizeof(result_buf), p, curtime_ptr); + } else { result_buf[0] = NUL; + } - if (conv.vc_type != CONV_NONE) + if (conv.vc_type != CONV_NONE) { xfree(p); + } convert_setup(&conv, enc, p_enc); - if (conv.vc_type != CONV_NONE) - rettv->vval.v_string = string_convert(&conv, result_buf, NULL); - else - rettv->vval.v_string = vim_strsave(result_buf); + if (conv.vc_type != CONV_NONE) { + rettv->vval.v_string = string_convert(&conv, (char_u *)result_buf, NULL); + } else { + rettv->vval.v_string = (char_u *)xstrdup(result_buf); + } - /* Release conversion descriptors */ + // Release conversion descriptors. convert_setup(&conv, NULL, NULL); xfree(enc); } @@ -15672,8 +15710,7 @@ static void f_string(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strlen(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = (varnumber_T)(STRLEN( - get_tv_string(&argvars[0]))); + rettv->vval.v_number = (varnumber_T)strlen(tv_get_string(&argvars[0])); } /* @@ -15681,7 +15718,7 @@ static void f_strlen(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *s = get_tv_string(&argvars[0]); + const char *s = tv_get_string(&argvars[0]); int skipcc = 0; varnumber_T len = 0; int (*func_mb_ptr2char_adv)(char_u **pp); @@ -15694,8 +15731,8 @@ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv; while (*s != NUL) { - func_mb_ptr2char_adv(&s); - ++len; + func_mb_ptr2char_adv((char_u **)&s); + len++; } rettv->vval.v_number = len; } @@ -15706,13 +15743,14 @@ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *s = get_tv_string(&argvars[0]); + const char *const s = tv_get_string(&argvars[0]); int col = 0; - if (argvars[1].v_type != VAR_UNKNOWN) + if (argvars[1].v_type != VAR_UNKNOWN) { col = get_tv_number(&argvars[1]); + } - rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, s) - col); + rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char_u *)s) - col); } /* @@ -15720,15 +15758,15 @@ static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *s = get_tv_string(&argvars[0]); + const char *const s = tv_get_string(&argvars[0]); - rettv->vval.v_number = (varnumber_T) mb_string2cells(s); + rettv->vval.v_number = (varnumber_T)mb_string2cells((const char_u *)s); } // "strcharpart()" function static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *const p = get_tv_string(&argvars[0]); + const char *const p = tv_get_string(&argvars[0]); const size_t slen = STRLEN(p); int nbyte = 0; @@ -15737,7 +15775,7 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (!error) { if (nchar > 0) { while (nchar > 0 && (size_t)nbyte < slen) { - nbyte += MB_CPTR2LEN(p + nbyte); + nbyte += MB_CPTR2LEN((const char_u *)p + nbyte); nchar--; } } else { @@ -15753,7 +15791,7 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (off < 0) { len += 1; } else { - len += MB_CPTR2LEN(p + off); + len += (size_t)MB_CPTR2LEN((const char_u *)p + off); } charlen--; } @@ -15776,7 +15814,7 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) } rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strnsave(p + nbyte, len); + rettv->vval.v_string = (char_u *)xstrndup(p + nbyte, (size_t)len); } /* @@ -15784,39 +15822,37 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p; - int n; - int len; - int slen; bool error = false; - p = get_tv_string(&argvars[0]); - slen = (int)STRLEN(p); + const char *const p = tv_get_string(&argvars[0]); + const size_t slen = strlen(p); - n = get_tv_number_chk(&argvars[1], &error); - if (error) + varnumber_T n = get_tv_number_chk(&argvars[1], &error); + varnumber_T len; + if (error) { len = 0; - else if (argvars[2].v_type != VAR_UNKNOWN) + } else if (argvars[2].v_type != VAR_UNKNOWN) { len = get_tv_number(&argvars[2]); - else - len = slen - n; /* default len: all bytes that are available. */ + } else { + len = slen - n; // Default len: all bytes that are available. + } - /* - * Only return the overlap between the specified part and the actual - * string. - */ + // Only return the overlap between the specified part and the actual + // string. if (n < 0) { len += n; n = 0; - } else if (n > slen) + } else if (n > (varnumber_T)slen) { n = slen; - if (len < 0) + } + if (len < 0) { len = 0; - else if (n + len > slen) + } else if (n + len > (varnumber_T)slen) { len = slen - n; + } rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strnsave(p + n, len); + rettv->vval.v_string = (char_u *)xmemdupz(p + n, (size_t)len); } /* @@ -15871,7 +15907,7 @@ static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_strtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; - rettv->vval.v_string = transstr(get_tv_string(&argvars[0])); + rettv->vval.v_string = transstr((char_u *)tv_get_string(&argvars[0])); } /* @@ -15962,20 +15998,17 @@ static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p = NULL; - int id; - char_u *what; - char_u *mode; - char_u modebuf[NUMBUFLEN]; + const int id = get_tv_number(&argvars[0]); + const char *const what = tv_get_string(&argvars[1]); int modec; - - id = get_tv_number(&argvars[0]); - what = get_tv_string(&argvars[1]); if (argvars[2].v_type != VAR_UNKNOWN) { - mode = get_tv_string_buf(&argvars[2], modebuf); + char modebuf[NUMBUFLEN]; + const char *const mode = (const char *)get_tv_string_buf(&argvars[2], + (char_u *)modebuf); modec = TOLOWER_ASC(mode[0]); - if (modec != 'c' && modec != 'g') - modec = 0; /* replace invalid with current */ + if (modec != 'c' && modec != 'g') { + modec = 0; // Replace invalid with current. + } } else if (ui_rgb_attached()) { modec = 'g'; } else { @@ -15983,54 +16016,56 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) } + const char *p = NULL; switch (TOLOWER_ASC(what[0])) { - case 'b': - if (TOLOWER_ASC(what[1]) == 'g') /* bg[#] */ + case 'b': { + if (TOLOWER_ASC(what[1]) == 'g') { // bg[#] + p = highlight_color(id, what, modec); + } else { // bold + p = highlight_has_attr(id, HL_BOLD, modec); + } + break; + } + case 'f': { // fg[#] or font p = highlight_color(id, what, modec); - else /* bold */ - p = highlight_has_attr(id, HL_BOLD, modec); - break; - - case 'f': /* fg[#] or font */ - p = highlight_color(id, what, modec); - break; - - case 'i': - if (TOLOWER_ASC(what[1]) == 'n') /* inverse */ + break; + } + case 'i': { + if (TOLOWER_ASC(what[1]) == 'n') { // inverse + p = highlight_has_attr(id, HL_INVERSE, modec); + } else { // italic + p = highlight_has_attr(id, HL_ITALIC, modec); + } + break; + } + case 'n': { // name + p = get_highlight_name(NULL, id - 1); + break; + } + case 'r': { // reverse p = highlight_has_attr(id, HL_INVERSE, modec); - else /* italic */ - p = highlight_has_attr(id, HL_ITALIC, modec); - break; - - case 'n': // name - p = (char_u *)get_highlight_name(NULL, id - 1); - break; - - case 'r': /* reverse */ - p = highlight_has_attr(id, HL_INVERSE, modec); - break; - - case 's': - if (TOLOWER_ASC(what[1]) == 'p') /* sp[#] */ - p = highlight_color(id, what, modec); - else /* standout */ - p = highlight_has_attr(id, HL_STANDOUT, modec); - break; - - case 'u': - if (STRLEN(what) <= 5 || TOLOWER_ASC(what[5]) != 'c') - /* underline */ - p = highlight_has_attr(id, HL_UNDERLINE, modec); - else - /* undercurl */ - p = highlight_has_attr(id, HL_UNDERCURL, modec); - break; + break; + } + case 's': { + if (TOLOWER_ASC(what[1]) == 'p') { // sp[#] + p = highlight_color(id, what, modec); + } else { // standout + p = highlight_has_attr(id, HL_STANDOUT, modec); + } + break; + } + case 'u': { + if (STRLEN(what) <= 5 || TOLOWER_ASC(what[5]) != 'c') { // underline + p = highlight_has_attr(id, HL_UNDERLINE, modec); + } else { // undercurl + p = highlight_has_attr(id, HL_UNDERCURL, modec); + } + break; + } } - if (p != NULL) - p = vim_strsave(p); rettv->v_type = VAR_STRING; - rettv->vval.v_string = p; + rettv->vval.v_string = (char_u *)(p == NULL ? p : xstrdup(p)); } /* @@ -16147,8 +16182,8 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, } // get input to the shell command (if any), and its length - ssize_t input_len; - char *input = (char *) save_tv_as_string(&argvars[1], &input_len, false); + ptrdiff_t input_len; + char *input = save_tv_as_string(&argvars[1], &input_len, false); if (input_len < 0) { assert(input == NULL); return; @@ -16359,15 +16394,14 @@ static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *tag_pattern; + const char *const tag_pattern = tv_get_string(&argvars[0]); - tag_pattern = get_tv_string(&argvars[0]); - - rettv->vval.v_number = FALSE; - if (*tag_pattern == NUL) + rettv->vval.v_number = false; + if (*tag_pattern == NUL) { return; + } - (void)get_tags(tv_list_alloc_ret(rettv), tag_pattern); + (void)get_tags(tv_list_alloc_ret(rettv), (char_u *)tag_pattern); } /* @@ -16391,7 +16425,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - char *cmd; + const char *cmd; bool executable = true; char **argv = tv_to_argv(&argvars[0], &cmd, &executable); if (!argv) { @@ -16614,7 +16648,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[2].v_type != VAR_UNKNOWN) { if (argvars[2].v_type != VAR_DICT || (dict = argvars[2].vval.v_dict) == NULL) { - EMSG2(_(e_invarg2), get_tv_string(&argvars[2])); + EMSG2(_(e_invarg2), tv_get_string(&argvars[2])); return; } dictitem_T *const di = tv_dict_find(dict, S_LEN("repeat")); @@ -16740,7 +16774,7 @@ void timer_teardown(void) */ static void f_tolower(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p = vim_strsave(get_tv_string(&argvars[0])); + char_u *p = (char_u *)xstrdup(tv_get_string(&argvars[0])); rettv->v_type = VAR_STRING; rettv->vval.v_string = p; @@ -16772,7 +16806,7 @@ static void f_tolower(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_toupper(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; - rettv->vval.v_string = strup_save(get_tv_string(&argvars[0])); + rettv->vval.v_string = (char_u *)strup_save(tv_get_string(&argvars[0])); } /* @@ -16780,56 +16814,48 @@ static void f_toupper(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *in_str; - char_u *fromstr; - char_u *tostr; - char_u *p; - int inlen; int fromlen; int tolen; int idx; - char_u *cpstr; - int cplen; - int first = TRUE; char_u buf[NUMBUFLEN]; char_u buf2[NUMBUFLEN]; garray_T ga; - in_str = get_tv_string(&argvars[0]); - fromstr = get_tv_string_buf_chk(&argvars[1], buf); - tostr = get_tv_string_buf_chk(&argvars[2], buf2); + const char *in_str = tv_get_string(&argvars[0]); + const char_u *fromstr = get_tv_string_buf_chk(&argvars[1], buf); + const char_u *tostr = get_tv_string_buf_chk(&argvars[2], buf2); - /* Default return value: empty string. */ + // Default return value: empty string. rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - if (fromstr == NULL || tostr == NULL) - return; /* type error; errmsg already given */ + if (fromstr == NULL || tostr == NULL) { + return; // type error; errmsg already given + } ga_init(&ga, (int)sizeof(char), 80); - if (!has_mbyte) - /* not multi-byte: fromstr and tostr must be the same length */ + if (!has_mbyte) { + // Not multi-byte: fromstr and tostr must be the same length. if (STRLEN(fromstr) != STRLEN(tostr)) { -error: - EMSG2(_(e_invarg2), fromstr); - ga_clear(&ga); - return; + goto error; } + } - /* fromstr and tostr have to contain the same number of chars */ + // fromstr and tostr have to contain the same number of chars. + bool first = true; while (*in_str != NUL) { if (has_mbyte) { - inlen = (*mb_ptr2len)(in_str); - cpstr = in_str; - cplen = inlen; + const char *cpstr = in_str; + const int inlen = (*mb_ptr2len)((const char_u *)in_str); + int cplen = inlen; idx = 0; - for (p = fromstr; *p != NUL; p += fromlen) { + for (const char_u *p = fromstr; *p != NUL; p += fromlen) { fromlen = (*mb_ptr2len)(p); if (fromlen == inlen && STRNCMP(in_str, p, inlen) == 0) { for (p = tostr; *p != NUL; p += tolen) { tolen = (*mb_ptr2len)(p); if (idx-- == 0) { cplen = tolen; - cpstr = p; + cpstr = (char *)p; break; } } @@ -16841,16 +16867,17 @@ error: } if (first && cpstr == in_str) { - /* Check that fromstr and tostr have the same number of - * (multi-byte) characters. Done only once when a character - * of in_str doesn't appear in fromstr. */ - first = FALSE; - for (p = tostr; *p != NUL; p += tolen) { + // Check that fromstr and tostr have the same number of + // (multi-byte) characters. Done only once when a character + // of in_str doesn't appear in fromstr. + first = false; + for (const char_u *p = tostr; *p != NUL; p += tolen) { tolen = (*mb_ptr2len)(p); - --idx; + idx--; } - if (idx != 0) + if (idx != 0) { goto error; + } } ga_grow(&ga, cplen); @@ -16859,13 +16886,14 @@ error: in_str += inlen; } else { - /* When not using multi-byte chars we can do it faster. */ - p = vim_strchr(fromstr, *in_str); - if (p != NULL) + // When not using multi-byte chars we can do it faster. + char_u *p = vim_strchr(fromstr, *in_str); + if (p != NULL) { ga_append(&ga, tostr[p - fromstr]); - else + } else { ga_append(&ga, *in_str); - ++in_str; + } + in_str++; } } @@ -16873,6 +16901,11 @@ error: ga_append(&ga, NUL); rettv->vval.v_string = ga.ga_data; + return; +error: + EMSG2(_(e_invarg2), fromstr); + ga_clear(&ga); + return; } /* @@ -16918,20 +16951,18 @@ static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_undofile(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; - { - char_u *fname = get_tv_string(&argvars[0]); + const char *const fname = tv_get_string(&argvars[0]); - if (*fname == NUL) { - /* If there is no file name there will be no undo file. */ - rettv->vval.v_string = NULL; - } else { - char *ffname = FullName_save((char *)fname, false); + if (*fname == NUL) { + // If there is no file name there will be no undo file. + rettv->vval.v_string = NULL; + } else { + char *ffname = FullName_save(fname, false); - if (ffname != NULL) { - rettv->vval.v_string = (char_u *)u_get_undo_file_name(ffname, false); - } - xfree(ffname); + if (ffname != NULL) { + rettv->vval.v_string = (char_u *)u_get_undo_file_name(ffname, false); } + xfree(ffname); } } @@ -17290,7 +17321,7 @@ void init_static_list(staticList10_T *sl) /// @param[in] endnl If true, the output will end in a newline (if a list). /// @returns an allocated string if `tv` represents a VimL string, list, or /// number; NULL otherwise. -static char_u *save_tv_as_string(typval_T *tv, ssize_t *len, bool endnl) +static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { if (tv->v_type == VAR_UNKNOWN) { @@ -17301,9 +17332,9 @@ static char_u *save_tv_as_string(typval_T *tv, ssize_t *len, bool endnl) // For types other than list, let get_tv_string_buf_chk() get the value or // print an error. if (tv->v_type != VAR_LIST) { - char_u *ret = get_tv_string_chk(tv); - if (ret && (*len = STRLEN(ret))) { - ret = vim_strsave(ret); + char *ret = (char *)get_tv_string_chk(tv); + if (ret && (*len = strlen(ret))) { + ret = xstrdup(ret); } else { ret = NULL; *len = -1; @@ -17315,17 +17346,17 @@ static char_u *save_tv_as_string(typval_T *tv, ssize_t *len, bool endnl) *len = 0; list_T *list = tv->vval.v_list; for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - *len += STRLEN(get_tv_string(&li->li_tv)) + 1; + *len += strlen(tv_get_string(&li->li_tv)) + 1; } if (*len == 0) { return NULL; } - char_u *ret = xmalloc(*len + endnl); - char_u *end = ret; + char *ret = xmalloc(*len + endnl); + char *end = ret; for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - for (char_u *s = get_tv_string(&li->li_tv); *s != NUL; s++) { + for (const char *s = tv_get_string(&li->li_tv); *s != NUL; s++) { *end++ = (*s == '\n') ? NUL : *s; } if (endnl || li->li_next != NULL) { @@ -17879,7 +17910,7 @@ long get_vim_var_nr(int idx) FUNC_ATTR_PURE */ char_u *get_vim_var_str(int idx) FUNC_ATTR_PURE FUNC_ATTR_NONNULL_RET { - return get_tv_string(&vimvars[idx].vv_tv); + return (char_u *)tv_get_string(&vimvars[idx].vv_tv); } /* @@ -18499,28 +18530,6 @@ static linenr_T get_tv_lnum_buf(typval_T *argvars, buf_T *buf) // TODO(ZyX-I): move to eval/typval -/// Get the string value of a variable -/// -/// @warning For number and special values it uses a single, static buffer. It -/// may be used only once, next call to get_tv_string may reuse it. Use -/// get_tv_string_buf() if you need to use get_tv_string() output after -/// calling it again. -/// -/// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but -/// return NULL on error. -/// -/// @param[in] varp Varible to get value of. -/// -/// @return Variable value if it is VAR_STRING variable, number converted to -/// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or empty -/// string. -char_u *get_tv_string(const typval_T *const varp) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT -{ - static char_u mybuf[NUMBUFLEN]; - return get_tv_string_buf(varp, mybuf); -} - char_u *get_tv_string_buf(const typval_T *varp, char_u *buf) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { @@ -18787,7 +18796,7 @@ static hashtab_T *find_var_ht(const char *name, const size_t name_len, /* * Get the string value of a (global/local) variable. - * Note: see get_tv_string() for how long the pointer remains valid. + * Note: see tv_get_string() for how long the pointer remains valid. * Returns NULL when it doesn't exist. */ char_u *get_var_value(const char *const name) @@ -18798,7 +18807,7 @@ char_u *get_var_value(const char *const name) if (v == NULL) { return NULL; } - return get_tv_string(&v->di_tv); + return (char_u *)tv_get_string(&v->di_tv); } /* @@ -19016,9 +19025,9 @@ static void set_var(const char *name, typval_T *const tv, const bool copy) if (ht == &vimvarht) { if (v->di_tv.v_type == VAR_STRING) { xfree(v->di_tv.vval.v_string); - if (copy || tv->v_type != VAR_STRING) - v->di_tv.vval.v_string = vim_strsave(get_tv_string(tv)); - else { + if (copy || tv->v_type != VAR_STRING) { + v->di_tv.vval.v_string = (char_u *)xstrdup(tv_get_string(tv)); + } else { // Take over the string to avoid an extra alloc/free. v->di_tv.vval.v_string = tv->vval.v_string; tv->vval.v_string = NULL; @@ -19439,7 +19448,6 @@ void ex_execute(exarg_T *eap) int ret = OK; char_u *p; garray_T ga; - int len; int save_did_emsg; ga_init(&ga, 1, 80); @@ -19461,12 +19469,13 @@ void ex_execute(exarg_T *eap) } if (!eap->skip) { - p = get_tv_string(&rettv); - len = (int)STRLEN(p); + const char *const argstr = tv_get_string(&rettv); + const size_t len = strlen(argstr); ga_grow(&ga, len + 2); - if (!GA_EMPTY(&ga)) + if (!GA_EMPTY(&ga)) { ((char_u *)(ga.ga_data))[ga.ga_len++] = ' '; - STRCPY((char_u *)(ga.ga_data) + ga.ga_len, p); + } + memcpy((char_u *)(ga.ga_data) + ga.ga_len, argstr, len + 1); ga.ga_len += len; } @@ -21920,8 +21929,9 @@ int store_session_globals(FILE *fd) && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { // Escape special characters with a backslash. Turn a LF and // CR into \n and \r. - char_u *const p = vim_strsave_escaped(get_tv_string(&this_var->di_tv), - (char_u *)"\\\"\n\r"); + char_u *const p = vim_strsave_escaped( + (const char_u *)tv_get_string(&this_var->di_tv), + (const char_u *)"\\\"\n\r"); for (char_u *t = p; *t != NUL; t++) { if (*t == '\n') { *t = 'n'; @@ -21992,7 +22002,7 @@ void ex_oldfiles(exarg_T *eap) for (li = l->lv_first; li != NULL && !got_int; li = li->li_next) { msg_outnum(++nr); MSG_PUTS(": "); - msg_outtrans(get_tv_string(&li->li_tv)); + msg_outtrans((char_u *)tv_get_string(&li->li_tv)); msg_clr_eos(); msg_putchar('\n'); ui_flush(); /* output one line at a time */ @@ -22007,15 +22017,15 @@ void ex_oldfiles(exarg_T *eap) nr = prompt_for_number(false); msg_starthere(); if (nr > 0 && nr <= l->lv_len) { - char *p = tv_list_find_str(l, nr); + const char *const p = tv_list_find_str(l, nr); if (p == NULL) { return; } - p = (char *)expand_env_save((char_u *)p); - eap->arg = (char_u *)p; + char *const s = (char *)expand_env_save((char_u *)p); + eap->arg = (char_u *)s; eap->cmdidx = CMD_edit; do_exedit(eap, NULL); - xfree(p); + xfree(s); } } } @@ -22728,7 +22738,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) arguments->lv_refcount++; int dummy; - (void)call_func((uint8_t *)func, + (void)call_func((const char_u *)func, name_len, &rettv, 2, @@ -22750,7 +22760,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) return rettv; } -bool eval_has_provider(char *name) +bool eval_has_provider(const char *name) { #define check_provider(name) \ if (has_##name == -1) { \ diff --git a/src/nvim/eval.h b/src/nvim/eval.h index 7f6fd76c46..070bc35bd5 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -2,7 +2,7 @@ #define NVIM_EVAL_H #include "nvim/hashtab.h" // For hashtab_T -#include "nvim/buffer_defs.h" // For scid_T +#include "nvim/buffer_defs.h" #include "nvim/ex_cmds_defs.h" // For exarg_T #include "nvim/eval/typval.h" #include "nvim/profile.h" diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c index ab48ace400..41b55e4a57 100644 --- a/src/nvim/eval/executor.c +++ b/src/nvim/eval/executor.c @@ -77,10 +77,10 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, if (tv2->v_type == VAR_FLOAT) { break; } - char *s = (char *)get_tv_string(tv1); + const char *tvs = tv_get_string(tv1); char numbuf[NUMBUFLEN]; - s = (char *)concat_str((char_u *)s, - get_tv_string_buf(tv2, (char_u *)numbuf)); + char *const s = (char *)concat_str( + (const char_u *)tvs, get_tv_string_buf(tv2, (char_u *)numbuf)); tv_clear(tv1); tv1->v_type = VAR_STRING; tv1->vval.v_string = (char_u *)s; diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 62460bcc3a..cbeb2e059c 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -733,7 +733,7 @@ varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *ret_error) /// @param[in] n Index in a list. /// /// @return [allocated] Copy of the list item string value. -char *tv_list_find_str(list_T *l, int n) +const char *tv_list_find_str(list_T *l, int n) FUNC_ATTR_MALLOC { const listitem_T *const li = tv_list_find(l, n - 1); @@ -741,7 +741,7 @@ char *tv_list_find_str(list_T *l, int n) EMSGN(_(e_listidx), n); return NULL; } - return (char *)get_tv_string(&li->li_tv); + return tv_get_string(&li->li_tv); } /// Locate item in a list and return its index @@ -2014,3 +2014,27 @@ bool tv_check_str_or_nr(const typval_T *const tv) assert(false); return false; } + +//{{{2 Get + +/// Get the string value of a variable +/// +/// @warning For number and special values it uses a single, static buffer. It +/// may be used only once, next call to get_tv_string may reuse it. Use +/// get_tv_string_buf() if you need to use tv_get_string() output after +/// calling it again. +/// +/// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but +/// return NULL on error. +/// +/// @param[in] varp Varible to get value of. +/// +/// @return Variable value if it is VAR_STRING variable, number converted to +/// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or empty +/// string. +const char *tv_get_string(const typval_T *const varp) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT +{ + static char_u mybuf[NUMBUFLEN]; + return (const char *)get_tv_string_buf((typval_T *)varp, mybuf); +} diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 0eef2ddaac..5645772124 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -205,6 +205,8 @@ struct dictvar_S { /// Type used for script ID typedef int scid_T; +/// Format argument for scid_T +#define PRIdSCID "d" // Structure to hold info for a function that is currently being executed. typedef struct funccall_S funccall_T; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 87fe52c119..5209dfc451 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -270,7 +270,7 @@ do_exmode ( /* * Execute a simple command line. Used for translated commands like "*". */ -int do_cmdline_cmd(char *cmd) +int do_cmdline_cmd(const char *cmd) { return do_cmdline((char_u *)cmd, NULL, NULL, DOCMD_NOWAIT|DOCMD_KEYTYPED); @@ -9347,8 +9347,8 @@ static int ses_put_fname(FILE *fd, char_u *name, unsigned *flagp) *p = '/'; } - /* escape special characters */ - p = vim_strsave_fnameescape(sname, FALSE); + // Escape special characters. + p = (char_u *)vim_strsave_fnameescape((const char *)sname, false); xfree(sname); /* write the result */ diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 7a34a181e2..3f71ae1795 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -1149,23 +1149,25 @@ void ex_endwhile(exarg_T *eap) */ void ex_throw(exarg_T *eap) { - char_u *arg = eap->arg; - char_u *value; + const char *arg = (const char *)eap->arg; + char *value; - if (*arg != NUL && *arg != '|' && *arg != '\n') - value = eval_to_string_skip(arg, &eap->nextcmd, eap->skip); - else { + if (*arg != NUL && *arg != '|' && *arg != '\n') { + value = eval_to_string_skip(arg, (const char **)&eap->nextcmd, + (bool)eap->skip); + } else { EMSG(_(e_argreq)); value = NULL; } - /* On error or when an exception is thrown during argument evaluation, do - * not throw. */ + // On error or when an exception is thrown during argument evaluation, do + // not throw. if (!eap->skip && value != NULL) { - if (throw_exception(value, ET_USER, NULL) == FAIL) + if (throw_exception((char_u *)value, ET_USER, NULL) == FAIL) { xfree(value); - else + } else { do_throw(eap->cstack); + } } } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 872b7fe365..3bd8d580ab 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -8,6 +8,7 @@ #include #include +#include "nvim/assert.h" #include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/arabic.h" @@ -960,7 +961,7 @@ static int command_line_handle_key(CommandLineState *s) return command_line_not_changed(s); case Ctrl_HAT: - if (map_to_exists_mode((char_u *)"", LANGMAP, false)) { + if (map_to_exists_mode("", LANGMAP, false)) { // ":lmap" mappings exists, toggle use of mappings. State ^= LANGMAP; if (s->b_im_ptr != NULL) { @@ -3120,9 +3121,10 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o #endif } #ifdef BACKSLASH_IN_FILENAME - p = vim_strsave_fnameescape(files[i], FALSE); + p = (char_u *)vim_strsave_fnameescape((const char *)files[i], false); #else - p = vim_strsave_fnameescape(files[i], xp->xp_shell); + p = (char_u *)vim_strsave_fnameescape((const char *)files[i], + xp->xp_shell); #endif xfree(files[i]); files[i] = p; @@ -3152,42 +3154,49 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o } } -/* - * Escape special characters in "fname" for when used as a file name argument - * after a Vim command, or, when "shell" is non-zero, a shell command. - * Returns the result in allocated memory. - */ -char_u *vim_strsave_fnameescape(char_u *fname, int shell) FUNC_ATTR_NONNULL_RET +/// Escape special characters in a file name for use as a command argument +/// +/// @param[in] fname File name to escape. +/// @param[in] shell What to escape for: if false, escapes for VimL command, +/// if true then it escapes for a shell command. +/// +/// @return [allocated] escaped file name. +char *vim_strsave_fnameescape(const char *const fname, const bool shell) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { - char_u *p; #ifdef BACKSLASH_IN_FILENAME -#define PATH_ESC_CHARS ((char_u *)" \t\n*?[{`%#'\"|!<") - char_u buf[20]; +#define PATH_ESC_CHARS " \t\n*?[{`%#'\"|!<" + char_u buf[sizeof(PATH_ESC_CHARS)]; int j = 0; - /* Don't escape '[', '{' and '!' if they are in 'isfname'. */ - for (p = PATH_ESC_CHARS; *p != NUL; ++p) - if ((*p != '[' && *p != '{' && *p != '!') || !vim_isfilec(*p)) - buf[j++] = *p; + // Don't escape '[', '{' and '!' if they are in 'isfname'. + for (const char *s = PATH_ESC_CHARS; *s != NUL; s++) { + if ((*s != '[' && *s != '{' && *s != '!') || !vim_isfilec(*s)) { + buf[j++] = *s; + } + } buf[j] = NUL; - p = vim_strsave_escaped(fname, buf); + char *p = (char *)vim_strsave_escaped((const char_u *)fname, + (const char_u *)buf); #else #define PATH_ESC_CHARS ((char_u *)" \t\n*?[{`$\\%#'\"|!<") #define SHELL_ESC_CHARS ((char_u *)" \t\n*?[{`$\\%#'\"|!<>();&") - p = vim_strsave_escaped(fname, shell ? SHELL_ESC_CHARS : PATH_ESC_CHARS); + char *p = (char *)vim_strsave_escaped( + (const char_u *)fname, (shell ? SHELL_ESC_CHARS : PATH_ESC_CHARS)); if (shell && csh_like_shell()) { - /* For csh and similar shells need to put two backslashes before '!'. - * One is taken by Vim, one by the shell. */ - char_u *s = vim_strsave_escaped(p, (char_u *)"!"); + // For csh and similar shells need to put two backslashes before '!'. + // One is taken by Vim, one by the shell. + char *s = (char *)vim_strsave_escaped((const char_u *)p, + (const char_u *)"!"); xfree(p); p = s; } #endif - /* '>' and '+' are special at the start of some commands, e.g. ":edit" and - * ":write". "cd -" has a special meaning. */ + // '>' and '+' are special at the start of some commands, e.g. ":edit" and + // ":write". "cd -" has a special meaning. if (*p == '>' || *p == '+' || (*p == '-' && p[1] == NUL)) { - escape_fname(&p); + escape_fname((char_u **)&p); } return p; @@ -4197,9 +4206,11 @@ static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u keep; garray_T ga; - retstr = call_user_expand_func(call_func_retstr, xp, num_file, file); - if (retstr == NULL) + retstr = call_user_expand_func((user_expand_func_T)call_func_retstr, xp, + num_file, file); + if (retstr == NULL) { return FAIL; + } ga_init(&ga, (int)sizeof(char *), 3); for (s = retstr; *s != NUL; s = e) { @@ -4237,9 +4248,11 @@ static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file) listitem_T *li; garray_T ga; - retlist = call_user_expand_func(call_func_retlist, xp, num_file, file); - if (retlist == NULL) + retlist = call_user_expand_func((user_expand_func_T)call_func_retlist, xp, + num_file, file); + if (retlist == NULL) { return FAIL; + } ga_init(&ga, (int)sizeof(char *), 3); /* Loop over the items in the list. */ diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index d948e20b32..67ac9f9957 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -428,7 +428,7 @@ readfile ( } if (!read_buffer && !read_stdin) { - perm = os_getperm(fname); + perm = os_getperm((const char *)fname); #ifdef UNIX // On Unix it is possible to read a directory, so we have to // check for it before os_open(). @@ -2606,10 +2606,10 @@ buf_write ( newfile = TRUE; perm = -1; } else { - perm = os_getperm(fname); - if (perm < 0) - newfile = TRUE; - else if (os_isdir(fname)) { + perm = os_getperm((const char *)fname); + if (perm < 0) { + newfile = true; + } else if (os_isdir(fname)) { errnum = (char_u *)"E502: "; errmsg = (char_u *)_("is a directory"); goto fail; @@ -3628,7 +3628,7 @@ restore_backup: close(empty_fd); } if (org != NULL) { - os_setperm((char_u *)org, os_getperm(fname) & 0777); + os_setperm((char_u *)org, os_getperm((const char *)fname) & 0777); xfree(org); } } @@ -4548,9 +4548,9 @@ int put_time(FILE *fd, time_t time_) /// os_rename() only works if both files are on the same file system, this /// function will (attempts to?) copy the file across if rename fails -- webb -// +/// /// @return -1 for failure, 0 for success -int vim_rename(char_u *from, char_u *to) +int vim_rename(const char_u *from, const char_u *to) { int fd_in; int fd_out; @@ -4569,10 +4569,12 @@ int vim_rename(char_u *from, char_u *to) * the file name differs we need to go through a temp file. */ if (fnamecmp(from, to) == 0) { - if (p_fic && STRCMP(path_tail(from), path_tail(to)) != 0) + if (p_fic && (STRCMP(path_tail((char_u *)from), path_tail((char_u *)to)) + != 0)) { use_tmp_file = true; - else + } else { return 0; + } } // Fail if the "from" file doesn't exist. Avoids that "to" is deleted. @@ -4638,9 +4640,9 @@ int vim_rename(char_u *from, char_u *to) /* * Rename() failed, try copying the file. */ - perm = os_getperm(from); + perm = os_getperm((const char *)from); #ifdef HAVE_ACL - /* For systems that support ACL: get the ACL from the original file. */ + // For systems that support ACL: get the ACL from the original file. acl = mch_get_acl(from); #endif fd_in = os_open((char *)from, O_RDONLY, 0); @@ -5261,7 +5263,7 @@ static void vim_maketempdir(void) /// Delete "name" and everything in it, recursively. /// @param name The path which should be deleted. /// @return 0 for success, -1 if some file was not deleted. -int delete_recursive(char_u *name) +int delete_recursive(const char *name) { int result = 0; @@ -5275,7 +5277,7 @@ int delete_recursive(char_u *name) EW_DIR | EW_FILE | EW_SILENT | EW_ALLLINKS | EW_DODOT | EW_EMPTYOK) == OK) { for (int i = 0; i < file_count; i++) { - if (delete_recursive(files[i]) != 0) { + if (delete_recursive((const char *)files[i]) != 0) { result = -1; } } @@ -5285,9 +5287,9 @@ int delete_recursive(char_u *name) } xfree(exp); - os_rmdir((char *)name); + os_rmdir(name); } else { - result = os_remove((char *)name) == 0 ? 0 : -1; + result = os_remove(name) == 0 ? 0 : -1; } return result; @@ -5299,7 +5301,7 @@ void vim_deltempdir(void) if (vim_tempdir != NULL) { // remove the trailing path separator path_tail(vim_tempdir)[-1] = NUL; - delete_recursive(vim_tempdir); + delete_recursive((const char *)vim_tempdir); xfree(vim_tempdir); vim_tempdir = NULL; } diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 076ee13a80..b64f089b96 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -3218,82 +3218,99 @@ showmap ( ui_flush(); /* show one line at a time */ } -/* - * Return TRUE if a map exists that has "str" in the rhs for mode "modechars". - * Recognize termcap codes in "str". - * Also checks mappings local to the current buffer. - */ -int map_to_exists(char_u *str, char_u *modechars, int abbr) +/// Check if a map exists that has given string in the rhs +/// +/// Also checks mappings local to the current buffer. +/// +/// @param[in] str String which mapping must have in the rhs. Termcap codes +/// are recognized in this argument. +/// @param[in] modechars Mode(s) in which mappings are checked. +/// @param[in] abbr true if checking abbreviations in place of mappings. +/// +/// @return true if there is at least one mapping with given parameters. +bool map_to_exists(const char *const str, const char *const modechars, + const bool abbr) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE { int mode = 0; - char_u *rhs; - char_u *buf; int retval; - rhs = replace_termcodes(str, STRLEN(str), &buf, false, true, false, - CPO_TO_CPO_FLAGS); + char_u *buf; + char_u *const rhs = replace_termcodes((const char_u *)str, strlen(str), &buf, + false, true, false, + CPO_TO_CPO_FLAGS); - if (vim_strchr(modechars, 'n') != NULL) - mode |= NORMAL; - if (vim_strchr(modechars, 'v') != NULL) - mode |= VISUAL + SELECTMODE; - if (vim_strchr(modechars, 'x') != NULL) - mode |= VISUAL; - if (vim_strchr(modechars, 's') != NULL) - mode |= SELECTMODE; - if (vim_strchr(modechars, 'o') != NULL) - mode |= OP_PENDING; - if (vim_strchr(modechars, 'i') != NULL) - mode |= INSERT; - if (vim_strchr(modechars, 'l') != NULL) - mode |= LANGMAP; - if (vim_strchr(modechars, 'c') != NULL) - mode |= CMDLINE; +#define MAPMODE(mode, modechars, chr, modeflags) \ + do { \ + if (strchr(modechars, chr) != NULL) { \ + mode |= modeflags; \ + } \ + } while (0) + MAPMODE(mode, modechars, 'n', NORMAL); + MAPMODE(mode, modechars, 'v', VISUAL|SELECTMODE); + MAPMODE(mode, modechars, 'x', VISUAL); + MAPMODE(mode, modechars, 's', SELECTMODE); + MAPMODE(mode, modechars, 'o', OP_PENDING); + MAPMODE(mode, modechars, 'i', INSERT); + MAPMODE(mode, modechars, 'l', LANGMAP); + MAPMODE(mode, modechars, 'c', CMDLINE); +#undef MAPMODE - retval = map_to_exists_mode(rhs, mode, abbr); + retval = map_to_exists_mode((const char *)rhs, mode, abbr); xfree(buf); return retval; } -/* - * Return TRUE if a map exists that has "str" in the rhs for mode "mode". - * Also checks mappings local to the current buffer. - */ -int map_to_exists_mode(char_u *rhs, int mode, int abbr) +/// Check if a map exists that has given string in the rhs +/// +/// Also checks mappings local to the current buffer. +/// +/// @param[in] rhs String which mapping must have in the rhs. Termcap codes +/// are recognized in this argument. +/// @param[in] mode Mode(s) in which mappings are checked. +/// @param[in] abbr true if checking abbreviations in place of mappings. +/// +/// @return true if there is at least one mapping with given parameters. +int map_to_exists_mode(const char *const rhs, const int mode, const bool abbr) { mapblock_T *mp; int hash; - int expand_buffer = FALSE; + bool expand_buffer = false; validate_maphash(); - /* Do it twice: once for global maps and once for local maps. */ - for (;; ) { - for (hash = 0; hash < 256; ++hash) { + // Do it twice: once for global maps and once for local maps. + for (;;) { + for (hash = 0; hash < 256; hash++) { if (abbr) { - if (hash > 0) /* there is only one abbr list */ + if (hash > 0) { // There is only one abbr list. break; - if (expand_buffer) + } + if (expand_buffer) { mp = curbuf->b_first_abbr; - else + } else { mp = first_abbr; - } else if (expand_buffer) + } + } else if (expand_buffer) { mp = curbuf->b_maphash[hash]; - else + } else { mp = maphash[hash]; + } for (; mp; mp = mp->m_next) { if ((mp->m_mode & mode) - && strstr((char *)mp->m_str, (char *)rhs) != NULL) - return TRUE; + && strstr((char *)mp->m_str, rhs) != NULL) { + return true; + } } } - if (expand_buffer) + if (expand_buffer) { break; - expand_buffer = TRUE; + } + expand_buffer = true; } - return FALSE; + return false; } /* diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index 19d97ecfef..4cb05ffc12 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -369,7 +369,6 @@ static void prt_get_attr(int hl_id, prt_text_attr_T *pattr, int modec) { int colorindex; uint32_t fg_color; - char *color; pattr->bold = (highlight_has_attr(hl_id, HL_BOLD, modec) != NULL); pattr->italic = (highlight_has_attr(hl_id, HL_ITALIC, modec) != NULL); @@ -377,11 +376,12 @@ static void prt_get_attr(int hl_id, prt_text_attr_T *pattr, int modec) pattr->undercurl = (highlight_has_attr(hl_id, HL_UNDERCURL, modec) != NULL); { - color = (char *)highlight_color(hl_id, (char_u *)"fg", modec); - if (color == NULL) + const char *color = highlight_color(hl_id, "fg", modec); + if (color == NULL) { colorindex = 0; - else + } else { colorindex = atoi(color); + } if (colorindex >= 0 && colorindex < t_colors) fg_color = prt_get_term_color(colorindex); diff --git a/src/nvim/message.c b/src/nvim/message.c index bf54284881..423f5a27e7 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -718,7 +718,7 @@ int delete_first_msg(void) void ex_messages(void *const eap_p) FUNC_ATTR_NONNULL_ALL { - exarg_T *eap = (exarg_T *)eap_p; + const exarg_T *const eap = (const exarg_T *)eap_p; struct msg_hist *p; int c = 0; diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index de6167c7fc..259dcc523c 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -147,7 +147,7 @@ void channel_from_connection(SocketWatcher *watcher) /// @param name The event name, an arbitrary string /// @param args Array with event arguments /// @return True if the event was sent successfully, false otherwise. -bool channel_send_event(uint64_t id, char *name, Array args) +bool channel_send_event(uint64_t id, const char *name, Array args) { Channel *channel = NULL; @@ -160,7 +160,7 @@ bool channel_send_event(uint64_t id, char *name, Array args) if (channel) { if (channel->pending_requests) { // Pending request, queue the notification for later sending. - String method = cstr_as_string(name); + const String method = cstr_as_string((char *)name); WBuffer *buffer = serialize_request(id, 0, method, args, &out_buffer, 1); kv_push(channel->delayed_notifications, buffer); } else { @@ -182,7 +182,7 @@ bool channel_send_event(uint64_t id, char *name, Array args) /// @param[out] error True if the return value is an error /// @return Whatever the remote method returned Object channel_send_call(uint64_t id, - char *method_name, + const char *method_name, Array args, Error *err) { @@ -519,10 +519,10 @@ static void send_error(Channel *channel, uint64_t id, char *err) static void send_request(Channel *channel, uint64_t id, - char *name, + const char *name, Array args) { - String method = {.size = strlen(name), .data = name}; + const String method = cstr_as_string((char *)name); channel_write(channel, serialize_request(channel->id, id, method, @@ -532,10 +532,10 @@ static void send_request(Channel *channel, } static void send_event(Channel *channel, - char *name, + const char *name, Array args) { - String method = {.size = strlen(name), .data = name}; + const String method = cstr_as_string((char *)name); channel_write(channel, serialize_request(channel->id, 0, method, @@ -544,7 +544,7 @@ static void send_event(Channel *channel, 1)); } -static void broadcast_event(char *name, Array args) +static void broadcast_event(const char *name, Array args) { kvec_t(Channel *) subscribed = KV_INITIAL_VALUE; Channel *channel; @@ -560,7 +560,7 @@ static void broadcast_event(char *name, Array args) goto end; } - String method = {.size = strlen(name), .data = name}; + const String method = cstr_as_string((char *)name); WBuffer *buffer = serialize_request(0, 0, method, @@ -728,7 +728,7 @@ static void call_set_error(Channel *channel, char *msg) static WBuffer *serialize_request(uint64_t channel_id, uint64_t request_id, - String method, + const String method, Array args, msgpack_sbuffer *sbuffer, size_t refcount) diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index 5137b375f0..808bb863fd 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -322,7 +322,7 @@ void msgpack_rpc_from_float(Float result, msgpack_packer *res) msgpack_pack_double(res, result); } -void msgpack_rpc_from_string(String result, msgpack_packer *res) +void msgpack_rpc_from_string(const String result, msgpack_packer *res) FUNC_ATTR_NONNULL_ARG(2) { msgpack_pack_str(res, result.size); @@ -478,7 +478,7 @@ Object msgpack_rpc_handle_invalid_arguments(uint64_t channel_id, /// Serializes a msgpack-rpc request or notification(id == 0) void msgpack_rpc_serialize_request(uint64_t request_id, - String method, + const String method, Array args, msgpack_packer *pac) FUNC_ATTR_NONNULL_ARG(4) diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 4cca5ec948..6ef929120e 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2075,7 +2075,6 @@ static void op_colon(oparg_T *oap) */ static void op_function(oparg_T *oap) { - char_u *(argv[1]); int save_virtual_op = virtual_op; if (*p_opfunc == NUL) @@ -2089,16 +2088,16 @@ static void op_function(oparg_T *oap) decl(&curbuf->b_op_end); } - if (oap->motion_type == kMTBlockWise) { - argv[0] = (char_u *)"block"; - } else if (oap->motion_type == kMTLineWise) { - argv[0] = (char_u *)"line"; - } else { - argv[0] = (char_u *)"char"; - } + const char_u *const argv[1] = { + (const char_u *)(((const char *const[]) { + [kMTBlockWise] = "block", + [kMTLineWise] = "line", + [kMTCharWise] = "char", + })[oap->motion_type]), + }; - /* Reset virtual_op so that 'virtualedit' can be changed in the - * function. */ + // Reset virtual_op so that 'virtualedit' can be changed in the + // function. virtual_op = MAYBE; (void)call_func_retnr(p_opfunc, 1, argv, false); @@ -4757,7 +4756,7 @@ static void nv_ident(cmdarg_T *cap) ptr = vim_strnsave(ptr, n); if (kp_ex) { // Escape the argument properly for an Ex command - p = vim_strsave_fnameescape(ptr, false); + p = (char_u *)vim_strsave_fnameescape((const char *)ptr, false); } else { // Escape the argument properly for a shell command p = vim_strsave_shellescape(ptr, true, true); diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 2beeae7ec6..0372bc8a8c 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -91,11 +91,11 @@ int os_dirname(char_u *buf, size_t len) /// Check if the given path is a directory and not a symlink to a directory. /// @return `true` if `name` is a directory and NOT a symlink to a directory. /// `false` if `name` is not a directory or if an error occurred. -bool os_isrealdir(const char_u *name) +bool os_isrealdir(const char *name) FUNC_ATTR_NONNULL_ALL { uv_fs_t request; - if (uv_fs_lstat(&fs_loop, &request, (char *)name, NULL) != kLibuvSuccess) { + if (uv_fs_lstat(&fs_loop, &request, name, NULL) != kLibuvSuccess) { return false; } if (S_ISLNK(request.statbuf.st_mode)) { @@ -111,7 +111,7 @@ bool os_isrealdir(const char_u *name) bool os_isdir(const char_u *name) FUNC_ATTR_NONNULL_ALL { - int32_t mode = os_getperm(name); + int32_t mode = os_getperm((const char *)name); if (mode < 0) { return false; } @@ -236,7 +236,8 @@ bool os_can_exe(const char_u *name, char_u **abspath, bool use_path) pathext); #else // Must have path separator, cannot execute files in the current directory. - bool ok = gettail_dir(name) != name && is_executable((char *)name); + const bool ok = ((const char_u *)gettail_dir((const char *)name) != name + && is_executable((char *)name)); #endif if (ok) { if (abspath != NULL) { @@ -254,7 +255,7 @@ bool os_can_exe(const char_u *name, char_u **abspath, bool use_path) static bool is_executable(const char *name) FUNC_ATTR_NONNULL_ALL { - int32_t mode = os_getperm((char_u *)name); + int32_t mode = os_getperm((const char *)name); if (mode < 0) { return false; @@ -606,11 +607,11 @@ static int os_stat(const char *name, uv_stat_t *statbuf) /// Get the file permissions for a given file. /// /// @return libuv error code on error. -int32_t os_getperm(const char_u *name) +int32_t os_getperm(const char *name) FUNC_ATTR_NONNULL_ALL { uv_stat_t statbuf; - int stat_result = os_stat((char *)name, &statbuf); + int stat_result = os_stat(name, &statbuf); if (stat_result == kLibuvSuccess) { return (int32_t)statbuf.st_mode; } else { @@ -979,13 +980,13 @@ bool os_fileid_equal_fileinfo(const FileID *file_id, /// When "fname" is the name of a shortcut (*.lnk) resolve the file it points /// to and return that name in allocated memory. /// Otherwise NULL is returned. -char *os_resolve_shortcut(char_u *fname) +char *os_resolve_shortcut(const char *fname) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { HRESULT hr; IPersistFile *ppf = NULL; OLECHAR wsz[MAX_PATH]; char *rfname = NULL; - int len; IShellLinkW *pslw = NULL; WIN32_FIND_DATAW ffdw; @@ -994,7 +995,7 @@ char *os_resolve_shortcut(char_u *fname) if (fname == NULL) { return rfname; } - len = (int)STRLEN(fname); + const size_t len = strlen(fname); if (len <= 4 || STRNICMP(fname + len - 4, ".lnk", 4) != 0) { return rfname; } @@ -1006,7 +1007,7 @@ char *os_resolve_shortcut(char_u *fname) &IID_IShellLinkW, (void **)&pslw); if (hr == S_OK) { WCHAR *p; - int conversion_result = utf8_to_utf16((char *)fname, &p); + const int conversion_result = utf8_to_utf16(fname, &p); if (conversion_result != 0) { EMSG2("utf8_to_utf16 failed: %s", uv_strerror(conversion_result)); } @@ -1036,7 +1037,7 @@ char *os_resolve_shortcut(char_u *fname) ZeroMemory(wsz, MAX_PATH * sizeof(WCHAR)); hr = pslw->lpVtbl->GetPath(pslw, wsz, MAX_PATH, &ffdw, 0); if (hr == S_OK && wsz[0] != NUL) { - int conversion_result = utf16_to_utf8(wsz, &rfname); + const int conversion_result = utf16_to_utf8(wsz, &rfname); if (conversion_result != 0) { EMSG2("utf16_to_utf8 failed: %s", uv_strerror(conversion_result)); } diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c index ed3410ffe5..acd86f06dc 100644 --- a/src/nvim/os_unix.c +++ b/src/nvim/os_unix.c @@ -110,14 +110,14 @@ void mch_copy_sec(char_u *from_file, char_u *to_file) // Return a pointer to the ACL of file "fname" in allocated memory. // Return NULL if the ACL is not available for whatever reason. -vim_acl_T mch_get_acl(char_u *fname) +vim_acl_T mch_get_acl(const char_u *fname) { vim_acl_T ret = NULL; return ret; } // Set the ACL of file "fname" to "acl" (unless it's NULL). -void mch_set_acl(char_u *fname, vim_acl_T aclent) +void mch_set_acl(const char_u *fname, vim_acl_T aclent) { if (aclent == NULL) return; diff --git a/src/nvim/path.c b/src/nvim/path.c index 2bd87b608e..e92261f4fd 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -159,7 +159,7 @@ const char_u *invocation_path_tail(const char_u *invocation, size_t *len) /// @param fname A file path. (Must be != NULL.) /// @return Pointer to first found path separator + 1. /// An empty string, if `fname` doesn't contain a path separator, -char_u *path_next_component(char_u *fname) +const char *path_next_component(const char *fname) { assert(fname != NULL); while (*fname != NUL && !vim_ispathsep(*fname)) { @@ -431,7 +431,7 @@ bool add_pathsep(char *p) /// /// @return [allocated] Copy of absolute path to `fname` or NULL when /// `fname` is NULL. -char *FullName_save(char *fname, bool force) +char *FullName_save(const char *fname, bool force) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC { if (fname == NULL) { @@ -906,9 +906,9 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern) in_curdir = xcalloc((size_t)gap->ga_len, sizeof(char_u *)); for (int i = 0; i < gap->ga_len && !got_int; i++) { - char_u *path = fnames[i]; + char_u *path = fnames[i]; int is_in_curdir; - char_u *dir_end = gettail_dir(path); + char_u *dir_end = (char_u *)gettail_dir((const char *)path); char_u *pathsep_p; char_u *path_cutoff; @@ -1010,18 +1010,22 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern) ga_remove_duplicate_strings(gap); } -/// Return the end of the directory name, on the first path -/// separator: -/// "/path/file", "/path/dir/", "/path//dir", "/file" -/// ^ ^ ^ ^ -char_u *gettail_dir(const char_u *fname) +/// Find end of the directory name +/// +/// @param[in] fname File name to process. +/// +/// @return end of the directory name, on the first path separator: +/// +/// "/path/file", "/path/dir/", "/path//dir", "/file" +/// ^ ^ ^ ^ +const char *gettail_dir(const char *const fname) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - const char_u *dir_end = fname; - const char_u *next_dir_end = fname; + const char *dir_end = fname; + const char *next_dir_end = fname; bool look_for_sep = true; - const char_u *p; - for (p = fname; *p != NUL; ) { + for (const char *p = fname; *p != NUL; ) { if (vim_ispathsep(*p)) { if (look_for_sep) { next_dir_end = p; @@ -1034,7 +1038,7 @@ char_u *gettail_dir(const char_u *fname) } mb_ptr_adv(p); } - return (char_u *)dir_end; + return dir_end; } @@ -1553,8 +1557,8 @@ void simplify_filename(char_u *filename) p = tail; /* skip to char after ".." or "../" */ } } else { - ++components; /* simple path component */ - p = path_next_component(p); + components++; // Simple path component. + p = (char_u *)path_next_component((const char *)p); } } while (*p != NUL); } diff --git a/src/nvim/sha256.c b/src/nvim/sha256.c index 7670b64468..c72dafd08e 100644 --- a/src/nvim/sha256.c +++ b/src/nvim/sha256.c @@ -259,11 +259,11 @@ void sha256_finish(context_sha256_T *ctx, char_u digest[SHA256_SUM_SIZE]) /// /// @returns hex digest of "buf[buf_len]" in a static array. /// if "salt" is not NULL also do "salt[salt_len]". -char_u *sha256_bytes(const char_u *restrict buf, size_t buf_len, - const char_u *restrict salt, size_t salt_len) +const char *sha256_bytes(const uint8_t *restrict buf, size_t buf_len, + const uint8_t *restrict salt, size_t salt_len) { char_u sha256sum[SHA256_SUM_SIZE]; - static char_u hexit[SHA256_BUFFER_SIZE + 1]; // buf size + NULL + static char hexit[SHA256_BUFFER_SIZE + 1]; // buf size + NULL context_sha256_T ctx; sha256_self_test(); @@ -277,7 +277,7 @@ char_u *sha256_bytes(const char_u *restrict buf, size_t buf_len, sha256_finish(&ctx, sha256sum); for (size_t j = 0; j < SHA256_SUM_SIZE; j++) { - snprintf((char *) hexit + j * SHA_STEP, SHA_STEP+1, "%02x", sha256sum[j]); + snprintf(hexit + j * SHA_STEP, SHA_STEP + 1, "%02x", sha256sum[j]); } hexit[sizeof(hexit) - 1] = '\0'; return hexit; @@ -308,7 +308,7 @@ bool sha256_self_test(void) context_sha256_T ctx; char_u buf[1000]; char_u sha256sum[SHA256_SUM_SIZE]; - char_u *hexit; + const char *hexit; static bool sha256_self_tested = false; static bool failures = false; @@ -320,8 +320,8 @@ bool sha256_self_test(void) for (size_t i = 0; i < 3; i++) { if (i < 2) { - hexit = sha256_bytes((char_u *) sha_self_test_msg[i], - STRLEN(sha_self_test_msg[i]), + hexit = sha256_bytes((uint8_t *)sha_self_test_msg[i], + strlen(sha_self_test_msg[i]), NULL, 0); STRCPY(output, hexit); } else { diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 2fe042cda8..0f04a5e9cf 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -82,8 +82,6 @@ KHASH_SET_INIT_STR(strset) (buflist_new((char_u *)ffname, (char_u *)sfname, __VA_ARGS__)) #define convert_setup(vcp, from, to) \ (convert_setup(vcp, (char_u *)from, (char_u *)to)) -#define os_getperm(f) \ - (os_getperm((char_u *) f)) #define os_isdir(f) (os_isdir((char_u *) f)) #define regtilde(s, m) ((char *) regtilde((char_u *) s, m)) #define path_tail_with_sep(f) ((char *) path_tail_with_sep((char_u *)f)) diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 56f0350aef..84bee9b97f 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -3232,7 +3232,7 @@ static void spell_suggest_expr(suginfo_T *su, char_u *expr) list_T *list; listitem_T *li; int score; - char_u *p; + const char *p; // The work is split up in a few parts to avoid having to export // suginfo_T. @@ -3244,9 +3244,10 @@ static void spell_suggest_expr(suginfo_T *su, char_u *expr) if (li->li_tv.v_type == VAR_LIST) { // Get the word and the score from the items. score = get_spellword(li->li_tv.vval.v_list, &p); - if (score >= 0 && score <= su->su_maxscore) - add_suggestion(su, &su->su_ga, p, su->su_badlen, - score, 0, true, su->su_sallang, false); + if (score >= 0 && score <= su->su_maxscore) { + add_suggestion(su, &su->su_ga, (const char_u *)p, su->su_badlen, + score, 0, true, su->su_sallang, false); + } } tv_list_unref(list); } @@ -5616,7 +5617,7 @@ static void add_suggestion ( suginfo_T *su, garray_T *gap, // either su_ga or su_sga - char_u *goodword, + const char_u *goodword, int badlenarg, // len of bad word replaced with "goodword" int score, int altscore, @@ -5630,13 +5631,11 @@ add_suggestion ( int badlen; // len of bad word changed suggest_T *stp; suggest_T new_sug; - int i; - char_u *pgood, *pbad; // Minimize "badlen" for consistency. Avoids that changing "the the" to // "thee the" is added next to changing the first "the" the "thee". - pgood = goodword + STRLEN(goodword); - pbad = su->su_badptr + badlenarg; + const char_u *pgood = goodword + STRLEN(goodword); + char_u *pbad = su->su_badptr + badlenarg; for (;; ) { goodlen = (int)(pgood - goodword); badlen = (int)(pbad - su->su_badptr); @@ -5656,9 +5655,10 @@ add_suggestion ( // the first "the" to itself. return; - if (GA_EMPTY(gap)) + int i; + if (GA_EMPTY(gap)) { i = -1; - else { + } else { // Check if the word is already there. Also check the length that is // being replaced "thes," -> "these" is a different suggestion from // "thes" -> "these". @@ -5857,27 +5857,31 @@ cleanup_suggestions ( return maxscore; } -// Soundfold a string, for soundfold(). -// Result is in allocated memory, NULL for an error. -char_u *eval_soundfold(char_u *word) +/// Soundfold a string, for soundfold() +/// +/// @param[in] word Word to soundfold. +/// +/// @return [allocated] soundfolded string or NULL in case of error. May return +/// copy of the input string if soundfolding is not +/// supported by any of the languages in &spellang. +char *eval_soundfold(const char *const word) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { - langp_T *lp; - char_u sound[MAXWLEN]; - if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) { // Use the sound-folding of the first language that supports it. - for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi) { - lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi); + for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; lpi++) { + langp_T *const lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi); if (!GA_EMPTY(&lp->lp_slang->sl_sal)) { // soundfold the word - spell_soundfold(lp->lp_slang, word, false, sound); - return vim_strsave(sound); + char_u sound[MAXWLEN]; + spell_soundfold(lp->lp_slang, (char_u *)word, false, sound); + return xstrdup((const char *)sound); } } } // No language with sound folding, return word as-is. - return vim_strsave(word); + return xstrdup(word); } /// Turn "inword" into its sound-a-like equivalent in "res[MAXWLEN]". diff --git a/src/nvim/strings.c b/src/nvim/strings.c index b964fed35a..9cfa126a56 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -291,30 +291,33 @@ void vim_strup(char_u *p) } } -/* - * Make string "s" all upper-case and return it in allocated memory. - * Handles multi-byte characters as well as possible. - */ -char_u *strup_save(const char_u *orig) +/// Make given string all upper-case +/// +/// Handels multi-byte characters as good as possible. +/// +/// @param[in] orig Input string. +/// +/// @return [allocated] upper-cased string. +char *strup_save(const char *const orig) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { - char_u *res = vim_strsave(orig); + char *res = xstrdup(orig); - char_u *p = res; + char *p = res; while (*p != NUL) { int l; if (enc_utf8) { - int c = utf_ptr2char(p); + int c = utf_ptr2char((const char_u *)p); int uc = utf_toupper(c); - /* Reallocate string when byte count changes. This is rare, - * thus it's OK to do another malloc()/free(). */ - l = utf_ptr2len(p); + // Reallocate string when byte count changes. This is rare, + // thus it's OK to do another malloc()/free(). + l = utf_ptr2len((const char_u *)p); int newl = utf_char2len(uc); if (newl != l) { // TODO(philix): use xrealloc() in strup_save() - char_u *s = xmalloc(STRLEN(res) + (size_t)(1 + newl - l)); + char *s = xmalloc(STRLEN(res) + (size_t)(1 + newl - l)); memcpy(s, res, (size_t)(p - res)); STRCPY(s + (p - res) + newl, p + l); p = s + (p - res); @@ -322,12 +325,13 @@ char_u *strup_save(const char_u *orig) res = s; } - utf_char2bytes(uc, p); + utf_char2bytes(uc, (char_u *)p); p += newl; - } else if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1) - p += l; /* skip multi-byte character */ - else { - *p = (char_u) TOUPPER_LOC(*p); // note that toupper() can be a macro + } else if (has_mbyte && (l = (*mb_ptr2len)((const char_u *)p)) > 1) { + p += l; // Skip multi-byte character. + } else { + // note that toupper() can be a macro + *p = (char)(uint8_t)TOUPPER_LOC(*p); p++; } } diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 632a5bf2a5..3f84b8080f 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -6930,21 +6930,21 @@ static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg return didh; } -/* - * Return "1" if highlight group "id" has attribute "flag". - * Return NULL otherwise. - */ -char_u * -highlight_has_attr ( - int id, - int flag, - int modec // 'g' for GUI, 'c' for cterm -) +/// Check whether highlight group has attribute +/// +/// @param[in] id Highilght group to check. +/// @param[in] flag Attribute to check. +/// @param[in] modec 'g' for GUI, 'c' for term. +/// +/// @return "1" if highlight group has attribute, NULL otherwise. +const char *highlight_has_attr(const int id, const int flag, const int modec) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE { int attr; - if (id <= 0 || id > highlight_ga.ga_len) + if (id <= 0 || id > highlight_ga.ga_len) { return NULL; + } if (modec == 'g') { attr = HL_TABLE()[id - 1].sg_gui; @@ -6952,39 +6952,42 @@ highlight_has_attr ( attr = HL_TABLE()[id - 1].sg_cterm; } - if (attr & flag) - return (char_u *)"1"; - return NULL; + return (attr & flag) ? "1" : NULL; } -/* - * Return color name of highlight group "id". - */ -char_u * -highlight_color ( - int id, - char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */ - int modec /* 'g' for GUI, 'c' for cterm, 't' for term */ -) +/// Return color name of the given highlight group +/// +/// @param[in] id Highlight group to work with. +/// @param[in] what What to return: one of "font", "fg", "bg", "sp", "fg#", +/// "bg#" or "sp#". +/// @param[in] modec 'g' for GUI, 'c' for cterm and 't' for term. +/// +/// @return color name, possibly in a static buffer. Buffer will be overwritten +/// on next highlight_color() call. May return NULL. +const char *highlight_color(const int id, const char *const what, + const int modec) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - static char_u name[20]; + static char name[20]; int n; - int fg = FALSE; - int sp = FALSE; - int font = FALSE; + bool fg = false; + bool sp = false; + bool font = false; - if (id <= 0 || id > highlight_ga.ga_len) + if (id <= 0 || id > highlight_ga.ga_len) { return NULL; + } - if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g') - fg = TRUE; - else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o' - && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't') - font = TRUE; - else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p') - sp = TRUE; - else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g')) + if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g') { + fg = true; + } else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o' + && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't') { + font = true; + } else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p') { + sp = true; + } else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g')) { return NULL; + } if (modec == 'g') { if (what[2] == '#' && ui_rgb_attached()) { if (fg) { @@ -6997,19 +7000,20 @@ highlight_color ( if (n < 0 || n > 0xffffff) { return NULL; } - snprintf((char *)name, sizeof(name), "#%06x", n); + snprintf(name, sizeof(name), "#%06x", n); return name; } if (fg) { - return HL_TABLE()[id - 1].sg_rgb_fg_name; + return (const char *)HL_TABLE()[id - 1].sg_rgb_fg_name; } if (sp) { - return HL_TABLE()[id - 1].sg_rgb_sp_name; + return (const char *)HL_TABLE()[id - 1].sg_rgb_sp_name; } - return HL_TABLE()[id - 1].sg_rgb_bg_name; + return (const char *)HL_TABLE()[id - 1].sg_rgb_bg_name; } - if (font || sp) + if (font || sp) { return NULL; + } if (modec == 'c') { if (fg) { n = HL_TABLE()[id - 1].sg_cterm_fg - 1; @@ -7019,10 +7023,10 @@ highlight_color ( if (n < 0) { return NULL; } - snprintf((char *)name, sizeof(name), "%d", n); + snprintf(name, sizeof(name), "%d", n); return name; } - /* term doesn't have color */ + // term doesn't have color. return NULL; } @@ -7133,7 +7137,7 @@ int syn_name2id(const char_u *name) /* * Return TRUE if highlight group "name" exists. */ -int highlight_exists(char_u *name) +int highlight_exists(const char_u *name) { return syn_name2id(name) > 0; } diff --git a/src/nvim/undo.c b/src/nvim/undo.c index ba687bf6da..010ef2c8fa 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -1081,7 +1081,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, */ perm = 0600; if (buf->b_ffname != NULL) { - perm = os_getperm(buf->b_ffname); + perm = os_getperm((const char *)buf->b_ffname); if (perm < 0) { perm = 0600; } diff --git a/src/nvim/version.c b/src/nvim/version.c index dd583f6ffd..f1d39b8492 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -73,7 +73,7 @@ static char *features[] = { }; // clang-format off -static int included_patches[] = { +static const int included_patches[] = { // 2367,NA // 2366 NA // 2365 NA @@ -2461,10 +2461,10 @@ static char *(extra_patches[]) = { /// @param version Version string like "1.3.42" /// /// @return true if Nvim is at or above the version. -bool has_nvim_version(char *version_str) - FUNC_ATTR_NONNULL_ALL +bool has_nvim_version(const char *const version_str) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - char *p = version_str; + const char *p = version_str; int major = 0; int minor = 0; int patch = 0; @@ -2473,7 +2473,7 @@ bool has_nvim_version(char *version_str) return false; } major = atoi(p); - p = strchr(p, '.'); // Find the next dot. + p = strchr(p, '.'); // Find the next dot. if (p) { p++; // Advance past the dot. @@ -2481,7 +2481,7 @@ bool has_nvim_version(char *version_str) return false; } minor = atoi(p); - p = strchr(p, '.'); + p = strchr(p, '.'); if (p) { p++; if (!ascii_isdigit(*p)) { From 949f09bdbba592a12629c71e20ff7bb49a21db6c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 21 Aug 2016 08:47:56 +0300 Subject: [PATCH 0185/1671] eval: Move get_tv_string_buf() to eval/typval.c --- src/nvim/eval.c | 182 +++++++++++++++++++-------------------- src/nvim/eval/executor.c | 3 +- src/nvim/eval/typval.c | 36 ++++++-- src/nvim/ex_docmd.c | 13 ++- 4 files changed, 129 insertions(+), 105 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7e40f5e828..d26ff5e41d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3531,8 +3531,6 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) int type_is = FALSE; /* TRUE for "is" and "isnot" */ int len = 2; long n1, n2; - char_u *s1, *s2; - char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; int ic; /* @@ -3737,31 +3735,33 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) case TYPE_NOMATCH: break; /* avoid gcc warning */ } } else { - s1 = get_tv_string_buf(rettv, buf1); - s2 = get_tv_string_buf(&var2, buf2); + char buf1[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + const char *const s1 = tv_get_string_buf(rettv, buf1); + const char *const s2 = tv_get_string_buf(&var2, buf2); if (type != TYPE_MATCH && type != TYPE_NOMATCH) { - i = mb_strcmp_ic((bool)ic, (const char *)s1, (const char *)s2); + i = mb_strcmp_ic((bool)ic, s1, s2); } else { i = 0; } n1 = false; switch (type) { - case TYPE_EQUAL: n1 = (i == 0); break; - case TYPE_NEQUAL: n1 = (i != 0); break; - case TYPE_GREATER: n1 = (i > 0); break; - case TYPE_GEQUAL: n1 = (i >= 0); break; - case TYPE_SMALLER: n1 = (i < 0); break; - case TYPE_SEQUAL: n1 = (i <= 0); break; + case TYPE_EQUAL: n1 = (i == 0); break; + case TYPE_NEQUAL: n1 = (i != 0); break; + case TYPE_GREATER: n1 = (i > 0); break; + case TYPE_GEQUAL: n1 = (i >= 0); break; + case TYPE_SMALLER: n1 = (i < 0); break; + case TYPE_SEQUAL: n1 = (i <= 0); break; - case TYPE_MATCH: - case TYPE_NOMATCH: - n1 = pattern_match(s2, s1, ic); - if (type == TYPE_NOMATCH) { - n1 = !n1; + case TYPE_MATCH: + case TYPE_NOMATCH: { + n1 = pattern_match((char_u *)s2, (char_u *)s1, ic); + if (type == TYPE_NOMATCH) { + n1 = !n1; + } + break; } - break; - - case TYPE_UNKNOWN: break; /* avoid gcc warning */ + case TYPE_UNKNOWN: break; // Avoid gcc warning. } } tv_clear(rettv); @@ -3794,8 +3794,6 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) int op; long n1, n2; float_T f1 = 0, f2 = 0; - char_u *s1, *s2; - char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; char_u *p; /* @@ -3842,14 +3840,17 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) * Compute the result. */ if (op == '.') { - s1 = get_tv_string_buf(rettv, buf1); /* already checked */ - s2 = get_tv_string_buf_chk(&var2, buf2); - if (s2 == NULL) { // Type error ? + char buf1[NUMBUFLEN]; + char_u buf2[NUMBUFLEN]; + // s1 already checked + const char *const s1 = tv_get_string_buf(rettv, buf1); + const char *const s2 = (const char *)get_tv_string_buf_chk(&var2, buf2); + if (s2 == NULL) { // Type error? tv_clear(rettv); tv_clear(&var2); return FAIL; } - p = concat_str(s1, s2); + p = concat_str((const char_u *)s1, (const char_u *)s2); tv_clear(rettv); rettv->v_type = VAR_STRING; rettv->vval.v_string = p; @@ -7565,20 +7566,21 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_cscope_connection(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int num = 0; - char_u *dbpath = NULL; - char_u *prepend = NULL; - char_u buf[NUMBUFLEN]; + const char *dbpath = NULL; + const char *prepend = NULL; + char buf[NUMBUFLEN]; if (argvars[0].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_UNKNOWN) { num = (int)get_tv_number(&argvars[0]); - dbpath = (char_u *)tv_get_string(&argvars[1]); + dbpath = tv_get_string(&argvars[1]); if (argvars[2].v_type != VAR_UNKNOWN) { - prepend = get_tv_string_buf(&argvars[2], buf); + prepend = tv_get_string_buf(&argvars[2], buf); } } - rettv->vval.v_number = cs_connection(num, dbpath, prepend); + rettv->vval.v_number = cs_connection(num, (char_u *)dbpath, + (char_u *)prepend); } /// "cursor(lnum, col)" function, or @@ -7661,9 +7663,6 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr) // "delete()" function static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u nbuf[NUMBUFLEN]; - char_u *flags; - rettv->vval.v_number = -1; if (check_restricted() || check_secure()) { return; @@ -7675,19 +7674,21 @@ static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } + char nbuf[NUMBUFLEN]; + const char *flags; if (argvars[1].v_type != VAR_UNKNOWN) { - flags = get_tv_string_buf(&argvars[1], nbuf); + flags = tv_get_string_buf(&argvars[1], nbuf); } else { - flags = (char_u *)""; + flags = ""; } if (*flags == NUL) { // delete a file rettv->vval.v_number = os_remove(name) == 0 ? 0 : -1; - } else if (STRCMP(flags, "d") == 0) { + } else if (strcmp(flags, "d") == 0) { // delete an empty directory rettv->vval.v_number = os_rmdir(name) == 0 ? 0 : -1; - } else if (STRCMP(flags, "rf") == 0) { + } else if (strcmp(flags, "rf") == 0) { // delete a directory recursively rettv->vval.v_number = delete_recursive(name); } else { @@ -7905,11 +7906,11 @@ static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_escape(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf[NUMBUFLEN]; + char buf[NUMBUFLEN]; rettv->vval.v_string = vim_strsave_escaped( (const char_u *)tv_get_string(&argvars[0]), - get_tv_string_buf(&argvars[1], buf)); + (const char_u *)tv_get_string_buf(&argvars[1], buf)); rettv->v_type = VAR_STRING; } @@ -8253,19 +8254,18 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_feedkeys(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u nbuf[NUMBUFLEN]; - - /* This is not allowed in the sandbox. If the commands would still be - * executed in the sandbox it would be OK, but it probably happens later, - * when "sandbox" is no longer set. */ - if (check_secure()) + // This is not allowed in the sandbox. If the commands would still be + // executed in the sandbox it would be OK, but it probably happens later, + // when "sandbox" is no longer set. + if (check_secure()) { return; + } const char *const keys = tv_get_string(&argvars[0]); - + char nbuf[NUMBUFLEN]; const char *flags = NULL; if (argvars[1].v_type != VAR_UNKNOWN) { - flags = (const char *)get_tv_string_buf(&argvars[1], nbuf); + flags = tv_get_string_buf(&argvars[1], nbuf); } nvim_feedkeys(cstr_as_string((char *)keys), @@ -10740,11 +10740,11 @@ static void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *mode; const char *const name = tv_get_string(&argvars[0]); bool abbr = false; - char_u buf[NUMBUFLEN]; + char buf[NUMBUFLEN]; if (argvars[1].v_type == VAR_UNKNOWN) { mode = "nvo"; } else { - mode = (const char *)get_tv_string_buf(&argvars[1], buf); + mode = tv_get_string_buf(&argvars[1], buf); if (argvars[2].v_type != VAR_UNKNOWN) { abbr = get_tv_number(&argvars[2]); } @@ -10763,17 +10763,16 @@ static void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) { HistoryType histype; - char_u *str; - char_u buf[NUMBUFLEN]; rettv->vval.v_number = false; if (check_restricted() || check_secure()) { return; } - str = get_tv_string_chk(&argvars[0]); // NULL on type error + char_u *str = get_tv_string_chk(&argvars[0]); // NULL on type error histype = str != NULL ? get_histtype(str, STRLEN(str), false) : HIST_INVALID; if (histype != HIST_INVALID) { - str = get_tv_string_buf(&argvars[1], buf); + char buf[NUMBUFLEN]; + str = (char_u *)tv_get_string_buf(&argvars[1], buf); if (*str != NUL) { init_history(); add_to_history(histype, str, false, NUL); @@ -10789,7 +10788,6 @@ static void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_histdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int n; - char_u buf[NUMBUFLEN]; char_u *str; str = get_tv_string_chk(&argvars[0]); // NULL on type error @@ -10801,11 +10799,12 @@ static void f_histdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else if (argvars[1].v_type == VAR_NUMBER) { // index given: remove that entry n = del_history_idx(get_histtype(str, STRLEN(str), false), - (int) get_tv_number(&argvars[1])); + (int)get_tv_number(&argvars[1])); } else { // string given: remove all matching entries + char buf[NUMBUFLEN]; n = del_history_entry(get_histtype(str, STRLEN(str), false), - get_tv_string_buf(&argvars[1], buf)); + (char_u *)tv_get_string_buf(&argvars[1], buf)); } rettv->vval.v_number = n; } @@ -10895,12 +10894,12 @@ static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = NULL; const char *const str = tv_get_string(&argvars[0]); - char_u buf1[NUMBUFLEN]; + char buf1[NUMBUFLEN]; char_u *const from = enc_canonize(enc_skip( - get_tv_string_buf(&argvars[1], buf1))); - char_u buf2[NUMBUFLEN]; + (char_u *)tv_get_string_buf(&argvars[1], buf1))); + char buf2[NUMBUFLEN]; char_u *const to = enc_canonize(enc_skip( - get_tv_string_buf(&argvars[2], buf2))); + (char_u *)tv_get_string_buf(&argvars[2], buf2))); vimconv.vc_type = CONV_NONE; convert_setup(&vimconv, from, to); @@ -10983,7 +10982,6 @@ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) char_u *prompt = get_tv_string_chk(&argvars[0]); char_u *p = NULL; int c; - char_u buf[NUMBUFLEN]; int cmd_silent_save = cmd_silent; char_u *defstr = (char_u *)""; int xp_type = EXPAND_NOTHING; @@ -11013,9 +11011,11 @@ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) cmdline_row = msg_row; if (argvars[1].v_type != VAR_UNKNOWN) { + char_u buf[NUMBUFLEN]; defstr = get_tv_string_buf_chk(&argvars[1], buf); - if (defstr != NULL) + if (defstr != NULL) { stuffReadbuffSpec(defstr); + } if (!inputdialog && argvars[2].v_type != VAR_UNKNOWN) { char_u *xp_name; @@ -11026,8 +11026,9 @@ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) rettv->vval.v_string = NULL; xp_name = get_tv_string_buf_chk(&argvars[2], buf); - if (xp_name == NULL) + if (xp_name == NULL) { return; + } xp_namelen = (int)STRLEN(xp_name); @@ -11047,9 +11048,11 @@ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) } if (inputdialog && rettv->vval.v_string == NULL && argvars[1].v_type != VAR_UNKNOWN - && argvars[2].v_type != VAR_UNKNOWN) - rettv->vval.v_string = vim_strsave(get_tv_string_buf( - &argvars[2], buf)); + && argvars[2].v_type != VAR_UNKNOWN) { + char buf[NUMBUFLEN]; + rettv->vval.v_string = (char_u *)xstrdup(tv_get_string_buf( + &argvars[2], buf)); + } xfree(xp_arg); @@ -12535,21 +12538,21 @@ static void f_min(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *dir; - char_u buf[NUMBUFLEN]; int prot = 0755; rettv->vval.v_number = FAIL; if (check_restricted() || check_secure()) return; - dir = get_tv_string_buf(&argvars[0], buf); - if (*dir == NUL) + char buf[NUMBUFLEN]; + const char *const dir = tv_get_string_buf(&argvars[0], buf); + if (*dir == NUL) { rettv->vval.v_number = FAIL; - else { - if (*path_tail(dir) == NUL) - /* remove trailing slashes */ - *path_tail_with_sep(dir) = NUL; + } else { + if (*path_tail((char_u *)dir) == NUL) { + // Remove trailing slashes. + *path_tail_with_sep((char_u *)dir) = NUL; + } if (argvars[1].v_type != VAR_UNKNOWN) { if (argvars[2].v_type != VAR_UNKNOWN) { @@ -12557,7 +12560,7 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (prot != -1 && strcmp(tv_get_string(&argvars[1]), "p") == 0) { char *failed_dir; - int ret = os_mkdir_recurse((char *) dir, prot, &failed_dir); + int ret = os_mkdir_recurse(dir, prot, &failed_dir); if (ret != 0) { EMSG3(_(e_mkdir), failed_dir, os_strerror(ret)); xfree(failed_dir); @@ -12840,14 +12843,13 @@ static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; { - char_u buf[NUMBUFLEN]; int len; int saved_did_emsg = did_emsg; - char *fmt; - /* Get the required length, allocate the buffer and do it for real. */ - did_emsg = FALSE; - fmt = (char *)get_tv_string_buf(&argvars[0], buf); + // Get the required length, allocate the buffer and do it for real. + did_emsg = false; + char buf[NUMBUFLEN]; + const char *fmt = tv_get_string_buf(&argvars[0], buf); len = vim_vsnprintf(NULL, 0, fmt, dummy_ap, argvars + 1); if (!did_emsg) { char *s = xmalloc(len + 1); @@ -13270,14 +13272,13 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf[NUMBUFLEN]; - if (check_restricted() || check_secure()) { rettv->vval.v_number = -1; } else { + char buf[NUMBUFLEN]; rettv->vval.v_number = vim_rename( - (char_u *)tv_get_string(&argvars[0]), - get_tv_string_buf(&argvars[1], buf)); + (const char_u *)tv_get_string(&argvars[0]), + (const char_u *)tv_get_string_buf(&argvars[1], buf)); } } @@ -16003,8 +16004,7 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) int modec; if (argvars[2].v_type != VAR_UNKNOWN) { char modebuf[NUMBUFLEN]; - const char *const mode = (const char *)get_tv_string_buf(&argvars[2], - (char_u *)modebuf); + const char *const mode = tv_get_string_buf(&argvars[2], modebuf); modec = TOLOWER_ASC(mode[0]); if (modec != 'c' && modec != 'g') { modec = 0; // Replace invalid with current. @@ -18530,14 +18530,6 @@ static linenr_T get_tv_lnum_buf(typval_T *argvars, buf_T *buf) // TODO(ZyX-I): move to eval/typval -char_u *get_tv_string_buf(const typval_T *varp, char_u *buf) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT -{ - char_u *const res = get_tv_string_buf_chk(varp, buf); - - return res != NULL ? res : (char_u *)""; -} - /// Careful: This uses a single, static buffer. YOU CAN ONLY USE IT ONCE! char_u *get_tv_string_chk(const typval_T *varp) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c index 41b55e4a57..d2d0873792 100644 --- a/src/nvim/eval/executor.c +++ b/src/nvim/eval/executor.c @@ -80,7 +80,8 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, const char *tvs = tv_get_string(tv1); char numbuf[NUMBUFLEN]; char *const s = (char *)concat_str( - (const char_u *)tvs, get_tv_string_buf(tv2, (char_u *)numbuf)); + (const char_u *)tvs, (const char_u *)tv_get_string_buf(tv2, + numbuf)); tv_clear(tv1); tv1->v_type = VAR_STRING; tv1->vval.v_string = (char_u *)s; diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index cbeb2e059c..2c2d0ecaab 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1127,7 +1127,7 @@ const char *tv_dict_get_string_buf(dict_T *const d, const char *const key, if (di == NULL) { return NULL; } - return (const char *)get_tv_string_buf(&di->di_tv, (char_u *)numbuf); + return tv_get_string_buf(&di->di_tv, numbuf); } /// Get a function from a dictionary @@ -1948,8 +1948,8 @@ bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic, case VAR_STRING: { char buf1[NUMBUFLEN]; char buf2[NUMBUFLEN]; - const char *s1 = (const char *)get_tv_string_buf(tv1, (char_u *)buf1); - const char *s2 = (const char *)get_tv_string_buf(tv2, (char_u *)buf2); + const char *s1 = tv_get_string_buf(tv1, buf1); + const char *s2 = tv_get_string_buf(tv2, buf2); return mb_strcmp_ic((bool)ic, s1, s2) == 0; } case VAR_SPECIAL: { @@ -2021,7 +2021,7 @@ bool tv_check_str_or_nr(const typval_T *const tv) /// /// @warning For number and special values it uses a single, static buffer. It /// may be used only once, next call to get_tv_string may reuse it. Use -/// get_tv_string_buf() if you need to use tv_get_string() output after +/// tv_get_string_buf() if you need to use tv_get_string() output after /// calling it again. /// /// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but @@ -2035,6 +2035,30 @@ bool tv_check_str_or_nr(const typval_T *const tv) const char *tv_get_string(const typval_T *const varp) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { - static char_u mybuf[NUMBUFLEN]; - return (const char *)get_tv_string_buf((typval_T *)varp, mybuf); + static char mybuf[NUMBUFLEN]; + return tv_get_string_buf((typval_T *)varp, mybuf); +} + +/// Get the string value of a variable +/// +/// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but +/// return NULL on error. +/// +/// @param[in] varp Varible to get value of. +/// @param buf Buffer used to hold numbers and special variables converted to +/// string. When function encounters one of these stringified value +/// will be written to buf and buf will be returned. +/// +/// Buffer must have NUMBUFLEN size. +/// +/// @return Variable value if it is VAR_STRING variable, number converted to +/// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or empty +/// string. +const char *tv_get_string_buf(const typval_T *const varp, char *const buf) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT +{ + const char *const res = (const char *)get_tv_string_buf_chk( + (typval_T *)varp, (char_u *)buf); + + return res != NULL ? res : ""; } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 5209dfc451..6b661cff11 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -7724,7 +7724,7 @@ static void ex_mkrc(exarg_T *eap) /* When using 'viewdir' may have to create the directory. */ if (using_vdir && !os_isdir(p_vdir)) { - vim_mkdir_emsg(p_vdir, 0755); + vim_mkdir_emsg((const char *)p_vdir, 0755); } fd = open_exfile((char_u *) fname, eap->forceit, WRITEBIN); @@ -7836,10 +7836,17 @@ static void ex_mkrc(exarg_T *eap) xfree(viewFile); } -int vim_mkdir_emsg(char_u *name, int prot) +/// Try creating a directory, give error message on failure +/// +/// @param[in] name Directory to create. +/// @param[in] prot Directory permissions. +/// +/// @return OK in case of success, FAIL otherwise. +int vim_mkdir_emsg(const char *const name, const int prot) + FUNC_ATTR_NONNULL_ALL { int ret; - if ((ret = os_mkdir((char *)name, prot)) != 0) { + if ((ret = os_mkdir(name, prot)) != 0) { EMSG3(_(e_mkdir), name, os_strerror(ret)); return FAIL; } From 7ee5cc7429017a4a08a8b62b628bea156fea0008 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 28 Aug 2016 08:09:29 +0300 Subject: [PATCH 0186/1671] eval: Move get_tv_lnum and get_tv_float to eval/typval.h Additionally - Rename former tv_get_float to tv_get_float_chk due to name conflict (former get_tv_float is better suited for being tv_get_float). - Add E907 error to get_tv_float() and test that it is being raised when appropriate. --- src/nvim/eval.c | 265 ++++++++++++----------------- src/nvim/eval/typval.c | 94 ++++++++-- src/nvim/eval/typval.h | 9 +- test/functional/eval/sort_spec.lua | 41 +++++ 4 files changed, 236 insertions(+), 173 deletions(-) create mode 100644 test/functional/eval/sort_spec.lua diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d26ff5e41d..6307dd8abc 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6498,7 +6498,7 @@ static void float_op_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr) float_T (*function)(float_T) = (float_T (*)(float_T))fptr; rettv->v_type = VAR_FLOAT; - if (tv_get_float(argvars, &f)) { + if (tv_get_float_chk(argvars, &f)) { rettv->vval.v_float = function(f); } else { rettv->vval.v_float = 0.0; @@ -6610,7 +6610,7 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) u_sync(TRUE); } - lnum = get_tv_lnum(argvars); + lnum = tv_get_lnum(argvars); if (lnum >= 0 && lnum <= curbuf->b_ml.ml_line_count && u_save(lnum, lnum + 1) == OK) { @@ -6957,7 +6957,7 @@ static void f_atan2(typval_T *argvars, typval_T *rettv, FunPtr fptr) float_T fy; rettv->v_type = VAR_FLOAT; - if (tv_get_float(argvars, &fx) && tv_get_float(&argvars[1], &fy)) { + if (tv_get_float_chk(argvars, &fx) && tv_get_float_chk(&argvars[1], &fy)) { rettv->vval.v_float = atan2(fx, fy); } else { rettv->vval.v_float = 0.0; @@ -7333,7 +7333,7 @@ static void f_cindent(typval_T *argvars, typval_T *rettv, FunPtr fptr) linenr_T lnum; pos = curwin->w_cursor; - lnum = get_tv_lnum(argvars); + lnum = tv_get_lnum(argvars); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { curwin->w_cursor.lnum = lnum; rettv->vval.v_number = get_c_indent(); @@ -7613,7 +7613,7 @@ static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr) set_curswant = false; } } else { - line = get_tv_lnum(argvars); + line = tv_get_lnum(argvars); col = get_tv_number_chk(&argvars[1], NULL); if (argvars[2].v_type != VAR_UNKNOWN) { coladd = get_tv_number_chk(&argvars[2], NULL); @@ -7808,7 +7808,7 @@ static void f_did_filetype(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_diff_filler(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = diff_check_fill(curwin, get_tv_lnum(argvars)); + rettv->vval.v_number = diff_check_fill(curwin, tv_get_lnum(argvars)); } /* @@ -7816,7 +7816,7 @@ static void f_diff_filler(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum = get_tv_lnum(argvars); + linenr_T lnum = tv_get_lnum(argvars); static linenr_T prev_lnum = 0; static int changedtick = 0; static int fnum = 0; @@ -8557,7 +8557,7 @@ static void f_float2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { float_T f; - if (tv_get_float(argvars, &f)) { + if (tv_get_float_chk(argvars, &f)) { if (f < VARNUMBER_MIN) { rettv->vval.v_number = VARNUMBER_MIN; } else if (f > VARNUMBER_MAX) { @@ -8577,7 +8577,7 @@ static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr) float_T fy; rettv->v_type = VAR_FLOAT; - if (tv_get_float(argvars, &fx) && tv_get_float(&argvars[1], &fy)) { + if (tv_get_float_chk(argvars, &fx) && tv_get_float_chk(&argvars[1], &fy)) { rettv->vval.v_float = fmod(fx, fy); } else { rettv->vval.v_float = 0.0; @@ -8629,16 +8629,16 @@ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void foldclosed_both(typval_T *argvars, typval_T *rettv, int end) { - linenr_T lnum; - linenr_T first, last; - - lnum = get_tv_lnum(argvars); + const linenr_T lnum = tv_get_lnum(argvars); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { - if (hasFoldingWin(curwin, lnum, &first, &last, FALSE, NULL)) { - if (end) + linenr_T first; + linenr_T last; + if (hasFoldingWin(curwin, lnum, &first, &last, false, NULL)) { + if (end) { rettv->vval.v_number = (varnumber_T)last; - else + } else { rettv->vval.v_number = (varnumber_T)first; + } return; } } @@ -8666,11 +8666,10 @@ static void f_foldclosedend(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_foldlevel(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; - - lnum = get_tv_lnum(argvars); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) + const linenr_T lnum = tv_get_lnum(argvars); + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { rettv->vval.v_number = foldLevel(lnum); + } } /* @@ -8734,7 +8733,6 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; char_u *text; char_u buf[51]; foldinfo_T foldinfo; @@ -8742,10 +8740,11 @@ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - lnum = get_tv_lnum(argvars); - /* treat illegal types and illegal string values for {lnum} the same */ - if (lnum < 0) + linenr_T lnum = tv_get_lnum(argvars); + // Treat illegal types and illegal string values for {lnum} the same. + if (lnum < 0) { lnum = 0; + } fold_count = foldedCount(curwin, lnum, &foldinfo); if (fold_count > 0) { text = get_foldtext(curwin, lnum, lnum + fold_count - 1, @@ -9782,17 +9781,16 @@ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getline(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; linenr_T end; - int retlist; + bool retlist; - lnum = get_tv_lnum(argvars); + const linenr_T lnum = tv_get_lnum(argvars); if (argvars[1].v_type == VAR_UNKNOWN) { end = 0; - retlist = FALSE; + retlist = false; } else { - end = get_tv_lnum(&argvars[1]); - retlist = TRUE; + end = tv_get_lnum(&argvars[1]); + retlist = true; } get_buffer_lines(curbuf, lnum, end, retlist, rettv); @@ -10920,13 +10918,12 @@ static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_indent(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; - - lnum = get_tv_lnum(argvars); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) + const linenr_T lnum = tv_get_lnum(argvars); + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { rettv->vval.v_number = get_indent_lnum(lnum); - else + } else { rettv->vval.v_number = -1; + } } /* @@ -11975,15 +11972,15 @@ static void f_line(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_line2byte(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; - - lnum = get_tv_lnum(argvars); - if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) + const linenr_T lnum = tv_get_lnum(argvars); + if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) { rettv->vval.v_number = -1; - else + } else { rettv->vval.v_number = ml_find_line_or_offset(curbuf, lnum, NULL); - if (rettv->vval.v_number >= 0) - ++rettv->vval.v_number; + } + if (rettv->vval.v_number >= 0) { + rettv->vval.v_number++; + } } /* @@ -11991,17 +11988,15 @@ static void f_line2byte(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_lispindent(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - pos_T pos; - linenr_T lnum; - - pos = curwin->w_cursor; - lnum = get_tv_lnum(argvars); + const pos_T pos = curwin->w_cursor; + const linenr_T lnum = tv_get_lnum(argvars); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { curwin->w_cursor.lnum = lnum; rettv->vval.v_number = get_lisp_indent(); curwin->w_cursor = pos; - } else + } else { rettv->vval.v_number = -1; + } } /* @@ -12746,13 +12741,14 @@ static void f_nextnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr) { linenr_T lnum; - for (lnum = get_tv_lnum(argvars);; ++lnum) { + for (lnum = tv_get_lnum(argvars);; lnum++) { if (lnum < 0 || lnum > curbuf->b_ml.ml_line_count) { lnum = 0; break; } - if (*skipwhite(ml_get(lnum)) != NUL) + if (*skipwhite(ml_get(lnum)) != NUL) { break; + } } rettv->vval.v_number = lnum; } @@ -12812,7 +12808,7 @@ static void f_pow(typval_T *argvars, typval_T *rettv, FunPtr fptr) float_T fy; rettv->v_type = VAR_FLOAT; - if (tv_get_float(argvars, &fx) && tv_get_float(&argvars[1], &fy)) { + if (tv_get_float_chk(argvars, &fx) && tv_get_float_chk(&argvars[1], &fy)) { rettv->vval.v_float = pow(fx, fy); } else { rettv->vval.v_float = 0.0; @@ -12824,14 +12820,14 @@ static void f_pow(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_prevnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; - - lnum = get_tv_lnum(argvars); - if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) + linenr_T lnum = tv_get_lnum(argvars); + if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) { lnum = 0; - else - while (lnum >= 1 && *skipwhite(ml_get(lnum)) == NUL) - --lnum; + } else { + while (lnum >= 1 && *skipwhite(ml_get(lnum)) == NUL) { + lnum--; + } + } rettv->vval.v_number = lnum; } @@ -14428,14 +14424,13 @@ static void f_setfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; char_u *line = NULL; list_T *l = NULL; listitem_T *li = NULL; long added = 0; linenr_T lcount = curbuf->b_ml.ml_line_count; - lnum = get_tv_lnum(&argvars[0]); + linenr_T lnum = tv_get_lnum(&argvars[0]); if (argvars[1].v_type == VAR_LIST) { l = argvars[1].vval.v_list; li = l->lv_first; @@ -15017,8 +15012,8 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) } if (sortinfo->item_compare_float) { - float_T v1 = get_tv_float(tv1); - float_T v2 = get_tv_float(tv2); + const float_T v1 = tv_get_float(tv1); + const float_T v2 = tv_get_float(tv2); return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; } @@ -15977,19 +15972,18 @@ static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "synID(lnum, col, trans)" function static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int id = 0; - long lnum; - long col; - int trans; + // -1 on type error (both) + const linenr_T lnum = tv_get_lnum(argvars); + const colnr_T col = (colnr_T)get_tv_number(&argvars[1]) - 1; + bool transerr = false; + const int trans = get_tv_number_chk(&argvars[2], &transerr); - lnum = get_tv_lnum(argvars); /* -1 on type error */ - col = get_tv_number(&argvars[1]) - 1; /* -1 on type error */ - trans = get_tv_number_chk(&argvars[2], &transerr); - + int id = 0; if (!transerr && lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count - && col >= 0 && col < (long)STRLEN(ml_get(lnum))) - id = syn_get_id(curwin, lnum, (colnr_T)col, trans, NULL, FALSE); + && col >= 0 && (size_t)col < STRLEN(ml_get(lnum))) { + id = syn_get_id(curwin, lnum, col, trans, NULL, false); + } rettv->vval.v_number = id; } @@ -16090,8 +16084,6 @@ static void f_synIDtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - long lnum; - long col; int syntax_flags = 0; int cchar; int matchid = 0; @@ -16100,15 +16092,16 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_LIST; rettv->vval.v_list = NULL; - lnum = get_tv_lnum(argvars); /* -1 on type error */ - col = get_tv_number(&argvars[1]) - 1; /* -1 on type error */ + // -1 on type error (both) + const linenr_T lnum = tv_get_lnum(argvars); + const colnr_T col = (colnr_T)get_tv_number(&argvars[1]) - 1; memset(str, NUL, sizeof(str)); tv_list_alloc_ret(rettv); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 - && col <= (long)STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) { - (void)syn_get_id(curwin, lnum, col, FALSE, NULL, FALSE); + && (size_t)col <= STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) { + (void)syn_get_id(curwin, lnum, col, false, NULL, false); syntax_flags = get_syntax_info(&matchid); // get the conceal character @@ -16137,21 +16130,19 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - long lnum; - long col; - rettv->v_type = VAR_LIST; rettv->vval.v_list = NULL; - lnum = get_tv_lnum(argvars); /* -1 on type error */ - col = get_tv_number(&argvars[1]) - 1; /* -1 on type error */ + // -1 on type error (both) + const linenr_T lnum = tv_get_lnum(argvars); + const colnr_T col = (colnr_T)get_tv_number(&argvars[1]) - 1; if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 - && col <= (long)STRLEN(ml_get(lnum))) { + && (size_t)col <= STRLEN(ml_get(lnum))) { tv_list_alloc_ret(rettv); - (void)syn_get_id(curwin, lnum, (colnr_T)col, false, NULL, true); + (void)syn_get_id(curwin, lnum, col, false, NULL, true); int id; int i = 0; @@ -17457,31 +17448,35 @@ static void f_xor(typval_T *argvars, typval_T *rettv, FunPtr fptr) } -/* - * Translate a String variable into a position. - * Returns NULL when there is an error. - */ -static pos_T * -var2fpos ( - typval_T *varp, - int dollar_lnum, /* TRUE when $ is last line */ - int *fnum /* set to fnum for '0, 'A, etc. */ -) +/// Translate a VimL object into a position +/// +/// Accepts VAR_LIST and VAR_STRING objects. Does not give an error for invalid +/// type. +/// +/// @param[in] tv Object to translate. +/// @param[in] dollar_lnum True when "$" is last line. +/// @param[out] ret_fnum Set to fnum for marks. +/// +/// @return Pointer to position or NULL in case of error (e.g. invalid type). +pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum, + int *const ret_fnum) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { char_u *name; static pos_T pos; pos_T *pp; - /* Argument can be [lnum, col, coladd]. */ - if (varp->v_type == VAR_LIST) { + // Argument can be [lnum, col, coladd]. + if (tv->v_type == VAR_LIST) { list_T *l; int len; bool error = false; listitem_T *li; - l = varp->vval.v_list; - if (l == NULL) + l = tv->vval.v_list; + if (l == NULL) { return NULL; + } // Get the line number. pos.lnum = tv_list_find_nr(l, 0L, &error); @@ -17521,20 +17516,24 @@ var2fpos ( return &pos; } - name = get_tv_string_chk(varp); - if (name == NULL) + name = get_tv_string_chk(tv); + if (name == NULL) { return NULL; - if (name[0] == '.') /* cursor */ - return &curwin->w_cursor; - if (name[0] == 'v' && name[1] == NUL) { /* Visual start */ - if (VIsual_active) - return &VIsual; + } + if (name[0] == '.') { // Cursor. return &curwin->w_cursor; } - if (name[0] == '\'') { /* mark */ - pp = getmark_buf_fnum(curbuf, name[1], FALSE, fnum); - if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) + if (name[0] == 'v' && name[1] == NUL) { // Visual start. + if (VIsual_active) { + return &VIsual; + } + return &curwin->w_cursor; + } + if (name[0] == '\'') { // Mark. + pp = getmark_buf_fnum(curbuf, name[1], false, ret_fnum); + if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) { return NULL; + } return pp; } @@ -18465,54 +18464,6 @@ varnumber_T get_tv_number_chk(const typval_T *const varp, bool *const denote) return n; } -static float_T get_tv_float(typval_T *varp) -{ - switch (varp->v_type) { - case VAR_NUMBER: - return (float_T)(varp->vval.v_number); - case VAR_FLOAT: - return varp->vval.v_float; - break; - case VAR_FUNC: - case VAR_PARTIAL: - EMSG(_("E891: Using a Funcref as a Float")); - break; - case VAR_STRING: - EMSG(_("E892: Using a String as a Float")); - break; - case VAR_LIST: - EMSG(_("E893: Using a List as a Float")); - break; - case VAR_DICT: - EMSG(_("E894: Using a Dictionary as a Float")); - break; - default: - EMSG2(_(e_intern2), "get_tv_float()"); - break; - } - return 0; -} - -/* - * Get the lnum from the first argument. - * Also accepts ".", "$", etc., but that only works for the current buffer. - * Returns -1 on error. - */ -static linenr_T get_tv_lnum(typval_T *argvars) -{ - typval_T rettv; - linenr_T lnum; - - lnum = get_tv_number_chk(&argvars[0], NULL); - if (lnum == 0) { /* no valid number, try using line() */ - rettv.v_type = VAR_NUMBER; - f_line(argvars, &rettv, NULL); - lnum = rettv.vval.v_number; - tv_clear(&rettv); - } - return lnum; -} - /* * Get the lnum from the first argument. * Also accepts "$", then "buf" is used. diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 2c2d0ecaab..6a16b3869a 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -16,6 +16,7 @@ #include "nvim/hashtab.h" #include "nvim/vim.h" #include "nvim/ascii.h" +#include "nvim/pos.h" // TODO(ZyX-I): Move line_breakcheck out of misc1 #include "nvim/misc1.h" // For line_breakcheck @@ -1730,11 +1731,11 @@ static inline void _nothing_conv_dict_end(typval_T *const tv, /// Free memory for a variable value and set the value to NULL or 0 /// -/// @param[in,out] varp Value to free. -void tv_clear(typval_T *varp) +/// @param[in,out] tv Value to free. +void tv_clear(typval_T *tv) { - if (varp != NULL && varp->v_type != VAR_UNKNOWN) { - const int evn_ret = encode_vim_to_nothing(varp, varp, "tv_clear argument"); + if (tv != NULL && tv->v_type != VAR_UNKNOWN) { + const int evn_ret = encode_vim_to_nothing(NULL, tv, "tv_clear argument"); (void)evn_ret; assert(evn_ret == OK); } @@ -2017,7 +2018,72 @@ bool tv_check_str_or_nr(const typval_T *const tv) //{{{2 Get -/// Get the string value of a variable +/// Get the line number from VimL object +/// +/// @param[in] tv Object to get value from. Is expected to be a number or +/// a special string like ".", "$", … (works with current buffer +/// only). +/// +/// @return Line number or -1 or 0. +linenr_T tv_get_lnum(const typval_T *const tv) +{ + linenr_T lnum = get_tv_number_chk(tv, NULL); + if (lnum == 0) { // No valid number, try using same function as line() does. + int fnum; + pos_T *const fp = var2fpos(tv, true, &fnum); + if (fp != NULL) { + lnum = fp->lnum; + } + } + return lnum; +} + +/// Get the floating-point value of a VimL object +/// +/// Raises an error if object is not number or floating-point. +/// +/// @param[in] tv Object to get value of. +/// +/// @return Floating-point value of the variable or zero. +float_T tv_get_float(const typval_T *const tv) +{ + switch (tv->v_type) { + case VAR_NUMBER: { + return (float_T)(tv->vval.v_number); + } + case VAR_FLOAT: { + return tv->vval.v_float; + } + case VAR_PARTIAL: + case VAR_FUNC: { + EMSG(_("E891: Using a Funcref as a Float")); + break; + } + case VAR_STRING: { + EMSG(_("E892: Using a String as a Float")); + break; + } + case VAR_LIST: { + EMSG(_("E893: Using a List as a Float")); + break; + } + case VAR_DICT: { + EMSG(_("E894: Using a Dictionary as a Float")); + break; + } + case VAR_SPECIAL: { + EMSG(_("E907: Using a special value as a Float")); + break; + } + case VAR_UNKNOWN: { + EMSG2(_(e_intern2), "get_tv_float()"); + break; + } + } + return 0; +} + +/// Get the string value of a VimL object /// /// @warning For number and special values it uses a single, static buffer. It /// may be used only once, next call to get_tv_string may reuse it. Use @@ -2027,38 +2093,38 @@ bool tv_check_str_or_nr(const typval_T *const tv) /// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but /// return NULL on error. /// -/// @param[in] varp Varible to get value of. +/// @param[in] tv Object to get value of. /// -/// @return Variable value if it is VAR_STRING variable, number converted to +/// @return Object value if it is VAR_STRING object, number converted to /// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or empty /// string. -const char *tv_get_string(const typval_T *const varp) +const char *tv_get_string(const typval_T *const tv) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { static char mybuf[NUMBUFLEN]; - return tv_get_string_buf((typval_T *)varp, mybuf); + return tv_get_string_buf((typval_T *)tv, mybuf); } -/// Get the string value of a variable +/// Get the string value of a VimL object /// /// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but /// return NULL on error. /// -/// @param[in] varp Varible to get value of. +/// @param[in] tv Object to get value of. /// @param buf Buffer used to hold numbers and special variables converted to /// string. When function encounters one of these stringified value /// will be written to buf and buf will be returned. /// /// Buffer must have NUMBUFLEN size. /// -/// @return Variable value if it is VAR_STRING variable, number converted to +/// @return Object value if it is VAR_STRING object, number converted to /// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or empty /// string. -const char *tv_get_string_buf(const typval_T *const varp, char *const buf) +const char *tv_get_string_buf(const typval_T *const tv, char *const buf) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { const char *const res = (const char *)get_tv_string_buf_chk( - (typval_T *)varp, (char_u *)buf); + (typval_T *)tv, (char_u *)buf); return res != NULL ? res : ""; } diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 5645772124..3c294b45b8 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -14,6 +14,7 @@ #include "nvim/profile.h" // for proftime_T #include "nvim/pos.h" // for linenr_T #include "nvim/gettext.h" +#include "nvim/message.h" /// Type used for VimL VAR_NUMBER values typedef int varnumber_T; @@ -373,7 +374,8 @@ extern bool tv_in_free_unref_items; } \ }) -static inline bool tv_get_float(const typval_T *const tv, float_T *const ret_f) +static inline bool tv_get_float_chk(const typval_T *const tv, + float_T *const ret_f) REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT; // FIXME circular dependency, cannot import message.h. @@ -381,11 +383,14 @@ bool emsgf(const char *const fmt, ...); /// Get the float value /// +/// Raises an error if object is not number or floating-point. +/// /// @param[in] tv VimL object to get value from. /// @param[out] ret_f Location where resulting float is stored. /// /// @return true in case of success, false if tv is not a number or float. -static inline bool tv_get_float(const typval_T *const tv, float_T *const ret_f) +static inline bool tv_get_float_chk(const typval_T *const tv, + float_T *const ret_f) { if (tv->v_type == VAR_FLOAT) { *ret_f = tv->vval.v_float; diff --git a/test/functional/eval/sort_spec.lua b/test/functional/eval/sort_spec.lua new file mode 100644 index 0000000000..4e5a0afba4 --- /dev/null +++ b/test/functional/eval/sort_spec.lua @@ -0,0 +1,41 @@ +local helpers = require('test.functional.helpers')(after_each) + +local eq = helpers.eq +local NIL = helpers.NIL +local eval = helpers.eval +local clear = helpers.clear +local meths = helpers.meths +local funcs = helpers.funcs +local command = helpers.command +local exc_exec = helpers.exc_exec + +before_each(clear) + +describe('sort()', function() + it('errors out when sorting special values', function() + eq('Vim(call):E907: Using a special value as a Float', + exc_exec('call sort([v:true, v:false], "f")')) + end) + + it('sorts “wrong” values between -0.0001 and 0.0001, preserving order', + function() + meths.set_var('list', {true, false, NIL, {}, {a=42}, 'check', + 0.0001, -0.0001}) + command('call insert(g:list, function("tr"))') + local error_lines = funcs.split( + funcs.execute('silent! call sort(g:list, "f")'), '\n') + local errors = {} + for _, err in ipairs(error_lines) do + errors[err] = true + end + eq({ + ['E891: Using a Funcref as a Float']=true, + ['E892: Using a String as a Float']=true, + ['E893: Using a List as a Float']=true, + ['E894: Using a Dictionary as a Float']=true, + ['E907: Using a special value as a Float']=true, + }, errors) + eq('[-1.0e-4, function(\'tr\'), v:true, v:false, v:null, [], {\'a\': 42}, \'check\', 1.0e-4]', + eval('string(g:list)')) + end) +end) From 1b3e13da5be48739ac4291208e421b68e607cc0f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 28 Aug 2016 08:18:29 +0300 Subject: [PATCH 0187/1671] eval: Refactor get_tv_lnum_buf --- src/nvim/eval.c | 50 +++++++++++++++++++++++------------------- src/nvim/eval/typval.c | 2 ++ 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 6307dd8abc..2a7314d969 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -9190,13 +9190,34 @@ static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retli } } +/// Get the line number from VimL object +/// +/// @note Unlike tv_get_lnum(), this one supports only "$" special string. +/// +/// @param[in] tv Object to get value from. Is expected to be a number or +/// a special string "$". +/// @param[in] buf Buffer to take last line number from in case tv is "$". May +/// be NULL, in this case "$" results in zero return. +/// +/// @return Line number or 0 in case of error. +static linenr_T tv_get_lnum_buf(const typval_T *const tv, + const buf_T *const buf) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (tv->v_type == VAR_STRING + && tv->vval.v_string != NULL + && tv->vval.v_string[0] == '$' + && buf != NULL) { + return buf->b_ml.ml_line_count; + } + return get_tv_number_chk(tv, NULL); +} + /* * "getbufline()" function */ static void f_getbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; - linenr_T end; buf_T *buf = NULL; if (tv_check_str_or_nr(&argvars[0])) { @@ -9205,12 +9226,10 @@ static void f_getbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) emsg_off--; } - lnum = get_tv_lnum_buf(&argvars[1], buf); - if (argvars[2].v_type == VAR_UNKNOWN) { - end = lnum; - } else { - end = get_tv_lnum_buf(&argvars[2], buf); - } + const linenr_T lnum = tv_get_lnum_buf(&argvars[1], buf); + const linenr_T end = (argvars[2].v_type == VAR_UNKNOWN + ? lnum + : tv_get_lnum_buf(&argvars[2], buf)); get_buffer_lines(buf, lnum, end, true, rettv); } @@ -18464,21 +18483,6 @@ varnumber_T get_tv_number_chk(const typval_T *const varp, bool *const denote) return n; } -/* - * Get the lnum from the first argument. - * Also accepts "$", then "buf" is used. - * Returns 0 on error. - */ -static linenr_T get_tv_lnum_buf(typval_T *argvars, buf_T *buf) -{ - if (argvars[0].v_type == VAR_STRING - && argvars[0].vval.v_string != NULL - && argvars[0].vval.v_string[0] == '$' - && buf != NULL) - return buf->b_ml.ml_line_count; - return get_tv_number_chk(&argvars[0], NULL); -} - // TODO(ZyX-I): move to eval/typval /// Careful: This uses a single, static buffer. YOU CAN ONLY USE IT ONCE! diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 6a16b3869a..38032762dc 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -2026,6 +2026,7 @@ bool tv_check_str_or_nr(const typval_T *const tv) /// /// @return Line number or -1 or 0. linenr_T tv_get_lnum(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { linenr_T lnum = get_tv_number_chk(tv, NULL); if (lnum == 0) { // No valid number, try using same function as line() does. @@ -2046,6 +2047,7 @@ linenr_T tv_get_lnum(const typval_T *const tv) /// /// @return Floating-point value of the variable or zero. float_T tv_get_float(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { switch (tv->v_type) { case VAR_NUMBER: { From 233b0c93bba66492d7b8b61f8ac61082f03668a1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 28 Aug 2016 08:55:58 +0300 Subject: [PATCH 0188/1671] eval: Move get_tv_number[_chk] to eval/typval.c --- src/nvim/eval.c | 659 ++++++++++++++++++--------------------- src/nvim/eval/executor.c | 8 +- src/nvim/eval/typval.c | 130 +++++++- src/nvim/strings.c | 2 +- src/nvim/window.c | 20 +- 5 files changed, 446 insertions(+), 373 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 2a7314d969..676df1a301 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -954,7 +954,7 @@ eval_to_bool ( } else { *error = false; if (!skip) { - retval = (get_tv_number_chk(&tv, error) != 0); + retval = (tv_get_number_chk(&tv, error) != 0); tv_clear(&tv); } } @@ -1081,10 +1081,10 @@ int eval_to_number(char_u *expr) ++emsg_off; - if (eval1(&p, &rettv, TRUE) == FAIL) + if (eval1(&p, &rettv, true) == FAIL) { retval = -1; - else { - retval = get_tv_number_chk(&rettv, NULL); + } else { + retval = tv_get_number_chk(&rettv, NULL); tv_clear(&rettv); } --emsg_off; @@ -1174,9 +1174,10 @@ int get_spellword(list_T *list, const char **pp) *pp = tv_get_string(&li->li_tv); li = li->li_next; - if (li == NULL) + if (li == NULL) { return -1; - return get_tv_number(&li->li_tv); + } + return tv_get_number(&li->li_tv); } /* @@ -1284,7 +1285,7 @@ call_func_retnr ( if (call_vim_function(func, argc, argv, safe, TRUE, &rettv) == FAIL) return -1; - retval = get_tv_number_chk(&rettv, NULL); + retval = tv_get_number_chk(&rettv, NULL); tv_clear(&rettv); return retval; } @@ -1912,7 +1913,6 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, && vim_strchr(endchars, *skipwhite((const char_u *)p)) == NULL)) { EMSG(_(e_letunexp)); } else { - long n; int opt_type; long numval; char_u *stringval = NULL; @@ -1921,8 +1921,8 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, const char c1 = *p; *p = NUL; - n = get_tv_number(tv); - s = get_tv_string_chk(tv); /* != NULL if number or string */ + varnumber_T n = tv_get_number(tv); + s = get_tv_string_chk(tv); // != NULL if number or string. if (s != NULL && op != NULL && *op != '=') { opt_type = get_option_value(arg, &numval, &stringval, opt_flags); if ((opt_type == 1 && *op == '.') @@ -2284,13 +2284,11 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } lp->ll_tv = &lp->ll_di->di_tv; } else { - /* - * Get the number and item for the only or first index of the List. - */ + // Get the number and item for the only or first index of the List. if (empty1) { lp->ll_n1 = 0; } else { - lp->ll_n1 = get_tv_number(&var1); // Is number or string. + lp->ll_n1 = tv_get_number(&var1); // Is number or string. tv_clear(&var1); } lp->ll_dict = NULL; @@ -2319,7 +2317,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, * Otherwise "lp->ll_n2" is set to the second index. */ if (lp->ll_range && !lp->ll_empty2) { - lp->ll_n2 = get_tv_number(&var2); // Is number or string. + lp->ll_n2 = tv_get_number(&var2); // Is number or string. tv_clear(&var2); if (lp->ll_n2 < 0) { ni = tv_list_find(lp->ll_list, lp->ll_n2); @@ -3320,7 +3318,7 @@ static int eval1(char_u **arg, typval_T *rettv, int evaluate) if (evaluate) { bool error = false; - if (get_tv_number_chk(rettv, &error) != 0) { + if (tv_get_number_chk(rettv, &error) != 0) { result = true; } tv_clear(rettv); @@ -3395,7 +3393,7 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate) result = FALSE; while ((*arg)[0] == '|' && (*arg)[1] == '|') { if (evaluate && first) { - if (get_tv_number_chk(rettv, &error) != 0) { + if (tv_get_number_chk(rettv, &error) != 0) { result = true; } tv_clear(rettv); @@ -3416,7 +3414,7 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate) * Compute the result. */ if (evaluate && !result) { - if (get_tv_number_chk(&var2, &error) != 0) { + if (tv_get_number_chk(&var2, &error) != 0) { result = true; } tv_clear(&var2); @@ -3464,7 +3462,7 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate) result = TRUE; while ((*arg)[0] == '&' && (*arg)[1] == '&') { if (evaluate && first) { - if (get_tv_number_chk(rettv, &error) == 0) { + if (tv_get_number_chk(rettv, &error) == 0) { result = false; } tv_clear(rettv); @@ -3485,7 +3483,7 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate) * Compute the result. */ if (evaluate && result) { - if (get_tv_number_chk(&var2, &error) == 0) { + if (tv_get_number_chk(&var2, &error) == 0) { result = false; } tv_clear(&var2); @@ -3694,25 +3692,27 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) && type != TYPE_MATCH && type != TYPE_NOMATCH) { float_T f1, f2; - if (rettv->v_type == VAR_FLOAT) + if (rettv->v_type == VAR_FLOAT) { f1 = rettv->vval.v_float; - else - f1 = get_tv_number(rettv); - if (var2.v_type == VAR_FLOAT) + } else { + f1 = tv_get_number(rettv); + } + if (var2.v_type == VAR_FLOAT) { f2 = var2.vval.v_float; - else - f2 = get_tv_number(&var2); - n1 = FALSE; + } else { + f2 = tv_get_number(&var2); + } + n1 = false; switch (type) { - case TYPE_EQUAL: n1 = (f1 == f2); break; - case TYPE_NEQUAL: n1 = (f1 != f2); break; - case TYPE_GREATER: n1 = (f1 > f2); break; - case TYPE_GEQUAL: n1 = (f1 >= f2); break; - case TYPE_SMALLER: n1 = (f1 < f2); break; - case TYPE_SEQUAL: n1 = (f1 <= f2); break; - case TYPE_UNKNOWN: - case TYPE_MATCH: - case TYPE_NOMATCH: break; /* avoid gcc warning */ + case TYPE_EQUAL: n1 = (f1 == f2); break; + case TYPE_NEQUAL: n1 = (f1 != f2); break; + case TYPE_GREATER: n1 = (f1 > f2); break; + case TYPE_GEQUAL: n1 = (f1 >= f2); break; + case TYPE_SMALLER: n1 = (f1 < f2); break; + case TYPE_SEQUAL: n1 = (f1 <= f2); break; + case TYPE_UNKNOWN: + case TYPE_MATCH: + case TYPE_NOMATCH: break; } } /* @@ -3721,18 +3721,18 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) */ else if ((rettv->v_type == VAR_NUMBER || var2.v_type == VAR_NUMBER) && type != TYPE_MATCH && type != TYPE_NOMATCH) { - n1 = get_tv_number(rettv); - n2 = get_tv_number(&var2); + n1 = tv_get_number(rettv); + n2 = tv_get_number(&var2); switch (type) { - case TYPE_EQUAL: n1 = (n1 == n2); break; - case TYPE_NEQUAL: n1 = (n1 != n2); break; - case TYPE_GREATER: n1 = (n1 > n2); break; - case TYPE_GEQUAL: n1 = (n1 >= n2); break; - case TYPE_SMALLER: n1 = (n1 < n2); break; - case TYPE_SEQUAL: n1 = (n1 <= n2); break; - case TYPE_UNKNOWN: - case TYPE_MATCH: - case TYPE_NOMATCH: break; /* avoid gcc warning */ + case TYPE_EQUAL: n1 = (n1 == n2); break; + case TYPE_NEQUAL: n1 = (n1 != n2); break; + case TYPE_GREATER: n1 = (n1 > n2); break; + case TYPE_GEQUAL: n1 = (n1 >= n2); break; + case TYPE_SMALLER: n1 = (n1 < n2); break; + case TYPE_SEQUAL: n1 = (n1 <= n2); break; + case TYPE_UNKNOWN: + case TYPE_MATCH: + case TYPE_NOMATCH: break; } } else { char buf1[NUMBUFLEN]; @@ -3872,7 +3872,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) f1 = rettv->vval.v_float; n1 = 0; } else { - n1 = get_tv_number_chk(rettv, &error); + n1 = tv_get_number_chk(rettv, &error); if (error) { /* This can only happen for "list + non-list". For * "non-list + ..." or "something - ...", we returned @@ -3887,7 +3887,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) f2 = var2.vval.v_float; n2 = 0; } else { - n2 = get_tv_number_chk(&var2, &error); + n2 = tv_get_number_chk(&var2, &error); if (error) { tv_clear(rettv); tv_clear(&var2); @@ -3968,8 +3968,9 @@ eval6 ( f1 = rettv->vval.v_float; use_float = TRUE; n1 = 0; - } else - n1 = get_tv_number_chk(rettv, &error); + } else { + n1 = tv_get_number_chk(rettv, &error); + } tv_clear(rettv); if (error) { return FAIL; @@ -3994,7 +3995,7 @@ eval6 ( f2 = var2.vval.v_float; n2 = 0; } else { - n2 = get_tv_number_chk(&var2, &error); + n2 = tv_get_number_chk(&var2, &error); tv_clear(&var2); if (error) { return FAIL; @@ -4296,7 +4297,7 @@ static int eval7( if (rettv->v_type == VAR_FLOAT) { f = rettv->vval.v_float; } else { - val = get_tv_number_chk(rettv, &error); + val = tv_get_number_chk(rettv, &error); } if (error) { tv_clear(rettv); @@ -4457,14 +4458,14 @@ eval_index ( if (evaluate) { n1 = 0; if (!empty1 && rettv->v_type != VAR_DICT) { - n1 = get_tv_number(&var1); + n1 = tv_get_number(&var1); tv_clear(&var1); } if (range) { if (empty2) { n2 = -1; } else { - n2 = get_tv_number(&var2); + n2 = tv_get_number(&var2); tv_clear(&var2); } } @@ -6543,13 +6544,14 @@ static void f_abs(typval_T *argvars, typval_T *rettv, FunPtr fptr) varnumber_T n; bool error = false; - n = get_tv_number_chk(&argvars[0], &error); - if (error) + n = tv_get_number_chk(&argvars[0], &error); + if (error) { rettv->vval.v_number = -1; - else if (n > 0) + } else if (n > 0) { rettv->vval.v_number = n; - else + } else { rettv->vval.v_number = -n; + } } } @@ -6578,8 +6580,8 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_and(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = get_tv_number_chk(&argvars[0], NULL) - & get_tv_number_chk(&argvars[1], NULL); + rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) + & tv_get_number_chk(&argvars[1], NULL); } @@ -6680,11 +6682,13 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr) int idx; if (argvars[0].v_type != VAR_UNKNOWN) { - idx = get_tv_number_chk(&argvars[0], NULL); - if (idx >= 0 && idx < ARGCOUNT) - rettv->vval.v_string = vim_strsave(alist_name(&ARGLIST[idx])); - else + idx = tv_get_number_chk(&argvars[0], NULL); + if (idx >= 0 && idx < ARGCOUNT) { + rettv->vval.v_string = (char_u *)xstrdup( + (const char *)alist_name(&ARGLIST[idx])); + } else { rettv->vval.v_string = NULL; + } rettv->v_type = VAR_STRING; } else { tv_list_alloc_ret(rettv); @@ -6855,9 +6859,9 @@ static void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr) void assert_inrange(typval_T *argvars) { bool error = false; - const varnumber_T lower = get_tv_number_chk(&argvars[0], &error); - const varnumber_T upper = get_tv_number_chk(&argvars[1], &error); - const varnumber_T actual = get_tv_number_chk(&argvars[2], &error); + const varnumber_T lower = tv_get_number_chk(&argvars[0], &error); + const varnumber_T upper = tv_get_number_chk(&argvars[1], &error); + const varnumber_T actual = tv_get_number_chk(&argvars[2], &error); if (error) { return; @@ -6884,7 +6888,7 @@ static void assert_bool(typval_T *argvars, bool is_true) garray_T ga; if ((argvars[0].v_type != VAR_NUMBER - || (get_tv_number_chk(&argvars[0], &error) == 0) == is_true + || (tv_get_number_chk(&argvars[0], &error) == 0) == is_true || error) && (argvars[0].v_type != VAR_SPECIAL || (argvars[0].vval.v_special @@ -7118,7 +7122,7 @@ static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) // new buffer. if (buf == NULL && argvars[1].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[1], &error) != 0 + && tv_get_number_chk(&argvars[1], &error) != 0 && !error && (name = get_tv_string_chk(&argvars[0])) != NULL && !error) { @@ -7176,7 +7180,7 @@ static void f_bufwinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_byte2line(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - long boff = get_tv_number(&argvars[0]) - 1; + long boff = tv_get_number(&argvars[0]) - 1; if (boff < 0) { rettv->vval.v_number = -1; } else { @@ -7192,7 +7196,7 @@ static void byteidx(typval_T *argvars, typval_T *rettv, int comp) long idx; str = get_tv_string_chk(&argvars[0]); - idx = get_tv_number_chk(&argvars[1], NULL); + idx = tv_get_number_chk(&argvars[1], NULL); rettv->vval.v_number = -1; if (str == NULL || idx < 0) return; @@ -7314,7 +7318,7 @@ static void f_char2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) int utf8 = 0; if (argvars[1].v_type != VAR_UNKNOWN) { - utf8 = get_tv_number_chk(&argvars[1], NULL); + utf8 = tv_get_number_chk(&argvars[1], NULL); } rettv->vval.v_number = (utf8 ? *utf_ptr2char : *mb_ptr2char)( @@ -7392,8 +7396,6 @@ static void f_col(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int startcol; - if ((State & INSERT) == 0) { EMSG(_("E785: complete() can only be used in Insert mode")); return; @@ -7409,9 +7411,10 @@ static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - startcol = get_tv_number_chk(&argvars[0], NULL); - if (startcol <= 0) + const int startcol = tv_get_number_chk(&argvars[0], NULL); + if (startcol <= 0) { return; + } set_completion(startcol - 1, argvars[1].vval.v_list); } @@ -7459,7 +7462,7 @@ static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (buttons == NULL) error = TRUE; if (argvars[2].v_type != VAR_UNKNOWN) { - def = get_tv_number_chk(&argvars[2], &error); + def = tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { typestr = get_tv_string_buf_chk(&argvars[3], buf2); if (typestr == NULL) @@ -7511,9 +7514,9 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[2].v_type != VAR_UNKNOWN) { bool error = false; - ic = get_tv_number_chk(&argvars[2], &error); + ic = tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { - idx = get_tv_number_chk(&argvars[3], &error); + idx = tv_get_number_chk(&argvars[3], &error); if (!error) { li = tv_list_find(l, idx); if (li == NULL) { @@ -7538,9 +7541,10 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool error = false; if (argvars[2].v_type != VAR_UNKNOWN) { - ic = get_tv_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) + ic = tv_get_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) { EMSG(_(e_invarg)); + } } todo = error ? 0 : (int)d->dv_hashtab.ht_used; @@ -7572,7 +7576,7 @@ static void f_cscope_connection(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_UNKNOWN) { - num = (int)get_tv_number(&argvars[0]); + num = (int)tv_get_number(&argvars[0]); dbpath = tv_get_string(&argvars[1]); if (argvars[2].v_type != VAR_UNKNOWN) { prepend = tv_get_string_buf(&argvars[2], buf); @@ -7614,9 +7618,9 @@ static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else { line = tv_get_lnum(argvars); - col = get_tv_number_chk(&argvars[1], NULL); + col = tv_get_number_chk(&argvars[1], NULL); if (argvars[2].v_type != VAR_UNKNOWN) { - coladd = get_tv_number_chk(&argvars[2], NULL); + coladd = tv_get_number_chk(&argvars[2], NULL); } } if (line < 0 || col < 0 @@ -7649,10 +7653,11 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int noref = 0; - if (argvars[1].v_type != VAR_UNKNOWN) - noref = get_tv_number_chk(&argvars[1], NULL); + if (argvars[1].v_type != VAR_UNKNOWN) { + noref = tv_get_number_chk(&argvars[1], NULL); + } if (noref < 0 || noref > 1) { - EMSG(_(e_invarg)); + emsgf(_(e_invarg)); } else { var_item_copy(NULL, &argvars[0], rettv, true, (noref == 0 ? get_copyID() @@ -7851,11 +7856,12 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (hlID == HLF_CHD || hlID == HLF_TXD) { - col = get_tv_number(&argvars[1]) - 1; /* ignore type error in {col} */ - if (col >= change_start && col <= change_end) - hlID = HLF_TXD; /* changed text */ - else - hlID = HLF_CHD; /* changed line */ + col = tv_get_number(&argvars[1]) - 1; // Ignore type error in {col}. + if (col >= change_start && col <= change_end) { + hlID = HLF_TXD; // Changed text. + } else { + hlID = HLF_CHD; // Changed line. + } } rettv->vval.v_number = hlID == (hlf_T)0 ? 0 : (int)hlID; } @@ -8123,7 +8129,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[2], &error) + && tv_get_number_chk(&argvars[2], &error) && !error) { rettv->v_type = VAR_LIST; rettv->vval.v_list = NULL; @@ -8145,8 +8151,9 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) /* When the optional second argument is non-zero, don't remove matches * for 'wildignore' and don't put matches for 'suffixes' at the end. */ if (argvars[1].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[1], &error)) + && tv_get_number_chk(&argvars[1], &error)) { options |= WILD_KEEP_ALL; + } if (!error) { ExpandInit(&xpc); xpc.xp_context = EXPAND_FILES; @@ -8191,9 +8198,10 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (l1 != NULL && !tv_check_lock(l1->lv_lock, arg_errmsg, arg_errmsg_len) && l2 != NULL) { if (argvars[2].v_type != VAR_UNKNOWN) { - before = get_tv_number_chk(&argvars[2], &error); - if (error) - return; /* type error; errmsg already given */ + before = tv_get_number_chk(&argvars[2], &error); + if (error) { + return; // Type error; errmsg already given. + } if (before == l1->lv_len) { item = NULL; @@ -8315,7 +8323,7 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) path = p; if (argvars[2].v_type != VAR_UNKNOWN) { - count = get_tv_number_chk(&argvars[2], &error); + count = tv_get_number_chk(&argvars[2], &error); } } } @@ -8512,10 +8520,10 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) bool error = false; // filter(): when expr is zero remove the item - *remp = (get_tv_number_chk(&rettv, &error) == 0); + *remp = (tv_get_number_chk(&rettv, &error) == 0); tv_clear(&rettv); // On type error, nothing has been removed; return FAIL to stop the - // loop. The error message was given by get_tv_number_chk(). + // loop. The error message was given by tv_get_number_chk(). if (error) { goto theend; } @@ -8946,12 +8954,13 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "garbagecollect()" function static void f_garbagecollect(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - /* This is postponed until we are back at the toplevel, because we may be - * using Lists and Dicts internally. E.g.: ":echo [garbagecollect()]". */ - want_garbage_collect = TRUE; + // This is postponed until we are back at the toplevel, because we may be + // using Lists and Dicts internally. E.g.: ":echo [garbagecollect()]". + want_garbage_collect = true; - if (argvars[0].v_type != VAR_UNKNOWN && get_tv_number(&argvars[0]) == 1) - garbage_collect_at_exit = TRUE; + if (argvars[0].v_type != VAR_UNKNOWN && tv_get_number(&argvars[0]) == 1) { + garbage_collect_at_exit = true; + } } /* @@ -8969,7 +8978,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) if ((l = argvars[0].vval.v_list) != NULL) { bool error = false; - li = tv_list_find(l, get_tv_number_chk(&argvars[1], &error)); + li = tv_list_find(l, tv_get_number_chk(&argvars[1], &error)); if (!error && li != NULL) { tv = &li->li_tv; } @@ -9110,23 +9119,24 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) filtered = true; di = tv_dict_find(sel_d, S_LEN("buflisted")); - if (di != NULL && get_tv_number(&di->di_tv)) { + if (di != NULL && tv_get_number(&di->di_tv)) { sel_buflisted = true; } di = tv_dict_find(sel_d, S_LEN("bufloaded")); - if (di != NULL && get_tv_number(&di->di_tv)) { + if (di != NULL && tv_get_number(&di->di_tv)) { sel_bufloaded = true; } } } else if (argvars[0].v_type != VAR_UNKNOWN) { // Information about one buffer. Argument specifies the buffer - (void)get_tv_number(&argvars[0]); // issue errmsg if type error - emsg_off++; - argbuf = get_buf_tv(&argvars[0], false); - emsg_off--; - if (argbuf == NULL) { - return; + if (tv_check_num(&argvars[0])) { // issue errmsg if type error + emsg_off++; + argbuf = get_buf_tv(&argvars[0], false); + emsg_off--; + if (argbuf == NULL) { + return; + } } } @@ -9210,7 +9220,7 @@ static linenr_T tv_get_lnum_buf(const typval_T *const tv, && buf != NULL) { return buf->b_ml.ml_line_count; } - return get_tv_number_chk(tv, NULL); + return tv_get_number_chk(tv, NULL); } /* @@ -9321,7 +9331,7 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } n = safe_vgetc(); - } else if (get_tv_number_chk(&argvars[0], &error) == 1) { + } else if (tv_get_number_chk(&argvars[0], &error) == 1) { // getchar(1): only check if char avail n = vpeekc_any(); } else if (error || vpeekc_any() == NUL) { @@ -9332,8 +9342,9 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) n = safe_vgetc(); } - if (n == K_IGNORE) + if (n == K_IGNORE) { continue; + } break; } no_mapping--; @@ -9460,7 +9471,7 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) | WILD_NO_BEEP; if (argvars[2].v_type != VAR_UNKNOWN) { - filtered = get_tv_number_chk(&argvars[2], NULL); + filtered = (bool)tv_get_number_chk(&argvars[2], NULL); } if (p_wic) { @@ -9970,9 +9981,9 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) strregname = get_tv_string_chk(&argvars[0]); error = strregname == NULL; if (argvars[1].v_type != VAR_UNKNOWN) { - arg2 = get_tv_number_chk(&argvars[1], &error); + arg2 = tv_get_number_chk(&argvars[1], &error); if (!error && argvars[2].v_type != VAR_UNKNOWN) { - return_list = get_tv_number_chk(&argvars[2], &error); + return_list = tv_get_number_chk(&argvars[2], &error); } } } else { @@ -10062,7 +10073,7 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type != VAR_UNKNOWN) { // Information about one tab page - tparg = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); + tparg = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); if (tparg == NULL) { return; } @@ -10099,7 +10110,7 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = NULL; const char *const varname = (const char *)get_tv_string_chk(&argvars[1]); - tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); + tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); if (tp != NULL && varname != NULL) { // Set tp to be our tabpage, temporarily. Also set the window to the // first window in the tabpage, otherwise the window is not valid. @@ -10216,7 +10227,7 @@ find_win_by_nr ( tabpage_T *tp /* NULL for current tab page */ ) { - int nr = (int)get_tv_number_chk(vp, NULL); + int nr = (int)tv_get_number_chk(vp, NULL); if (nr < 0) { return NULL; @@ -10251,7 +10262,7 @@ static win_T *find_tabwin(typval_T *wvp, typval_T *tvp) if (wvp->v_type != VAR_UNKNOWN) { if (tvp->v_type != VAR_UNKNOWN) { - long n = get_tv_number(tvp); + long n = tv_get_number(tvp); if (n >= 0) { tp = find_tabpage(n); } @@ -10291,10 +10302,11 @@ getwinvar ( tabpage_T *oldtabpage = NULL; bool done = false; - if (off == 1) - tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); - else + if (off == 1) { + tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); + } else { tp = curtab; + } win = find_win_by_nr(&argvars[off], tp); const char *varname = (const char *)get_tv_string_chk( &argvars[off + 1]); @@ -10363,15 +10375,16 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) * for 'wildignore' and don't put matches for 'suffixes' at the end. */ rettv->v_type = VAR_STRING; if (argvars[1].v_type != VAR_UNKNOWN) { - if (get_tv_number_chk(&argvars[1], &error)) + if (tv_get_number_chk(&argvars[1], &error)) { options |= WILD_KEEP_ALL; + } if (argvars[2].v_type != VAR_UNKNOWN) { - if (get_tv_number_chk(&argvars[2], &error)) { + if (tv_get_number_chk(&argvars[2], &error)) { rettv->v_type = VAR_LIST; rettv->vval.v_list = NULL; } if (argvars[3].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[3], &error)) { + && tv_get_number_chk(&argvars[3], &error)) { options |= WILD_ALLLINKS; } } @@ -10410,17 +10423,17 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[2].v_type != VAR_UNKNOWN) { // When the optional second argument is non-zero, don't remove matches // for 'wildignore' and don't put matches for 'suffixes' at the end. - if (get_tv_number_chk(&argvars[2], &error)) { + if (tv_get_number_chk(&argvars[2], &error)) { flags |= WILD_KEEP_ALL; } if (argvars[3].v_type != VAR_UNKNOWN) { - if (get_tv_number_chk(&argvars[3], &error)) { + if (tv_get_number_chk(&argvars[3], &error)) { rettv->v_type = VAR_LIST; rettv->vval.v_list = NULL; } if (argvars[4].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[4], &error)) { + && tv_get_number_chk(&argvars[4], &error)) { flags |= WILD_ALLLINKS; } } @@ -10763,7 +10776,7 @@ static void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { mode = tv_get_string_buf(&argvars[1], buf); if (argvars[2].v_type != VAR_UNKNOWN) { - abbr = get_tv_number(&argvars[2]); + abbr = tv_get_number(&argvars[2]); } } @@ -10816,7 +10829,7 @@ static void f_histdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else if (argvars[1].v_type == VAR_NUMBER) { // index given: remove that entry n = del_history_idx(get_histtype(str, STRLEN(str), false), - (int)get_tv_number(&argvars[1])); + (int)tv_get_number(&argvars[1])); } else { // string given: remove all matching entries char buf[NUMBUFLEN]; @@ -10843,7 +10856,7 @@ static void f_histget(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[1].v_type == VAR_UNKNOWN) { idx = get_history_idx(type); } else { - idx = (int)get_tv_number_chk(&argvars[1], NULL); + idx = (int)tv_get_number_chk(&argvars[1], NULL); } // -1 on type error rettv->vval.v_string = vim_strsave(get_history_entry(type, idx)); @@ -10968,12 +10981,14 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Start at specified item. Use the cached index that tv_list_find() // sets, so that a negative number also works. - item = tv_list_find(l, get_tv_number_chk(&argvars[2], &error)); + item = tv_list_find(l, tv_get_number_chk(&argvars[2], &error)); idx = l->lv_idx; - if (argvars[3].v_type != VAR_UNKNOWN) - ic = get_tv_number_chk(&argvars[3], &error); - if (error) + if (argvars[3].v_type != VAR_UNKNOWN) { + ic = tv_get_number_chk(&argvars[3], &error); + } + if (error) { item = NULL; + } } for (; item != NULL; item = item->li_next, ++idx) @@ -11186,7 +11201,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { long before = 0; if (argvars[2].v_type != VAR_UNKNOWN) { - before = get_tv_number_chk(&argvars[2], &error); + before = tv_get_number_chk(&argvars[2], &error); } if (error) { // type error; errmsg already given @@ -11213,7 +11228,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_invert(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = ~get_tv_number_chk(&argvars[0], NULL); + rettv->vval.v_number = ~tv_get_number_chk(&argvars[0], NULL); } /* @@ -12051,9 +12066,10 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) if (argvars[1].v_type != VAR_UNKNOWN) { which = get_tv_string_buf_chk(&argvars[1], buf); if (argvars[2].v_type != VAR_UNKNOWN) { - abbr = get_tv_number(&argvars[2]); - if (argvars[3].v_type != VAR_UNKNOWN) - get_dict = get_tv_number(&argvars[3]); + abbr = tv_get_number(&argvars[2]); + if (argvars[3].v_type != VAR_UNKNOWN) { + get_dict = tv_get_number(&argvars[3]); + } } } else which = (char_u *)""; @@ -12175,9 +12191,10 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) if (argvars[2].v_type != VAR_UNKNOWN) { bool error = false; - start = get_tv_number_chk(&argvars[2], &error); - if (error) + start = tv_get_number_chk(&argvars[2], &error); + if (error) { goto theend; + } if (l != NULL) { li = tv_list_find(l, start); if (li == NULL) { @@ -12200,10 +12217,12 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) } } - if (argvars[3].v_type != VAR_UNKNOWN) - nth = get_tv_number_chk(&argvars[3], &error); - if (error) + if (argvars[3].v_type != VAR_UNKNOWN) { + nth = tv_get_number_chk(&argvars[3], &error); + } + if (error) { goto theend; + } } regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); @@ -12331,9 +12350,9 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (grp == NULL || pat == NULL) return; if (argvars[2].v_type != VAR_UNKNOWN) { - prio = get_tv_number_chk(&argvars[2], &error); + prio = tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { - id = get_tv_number_chk(&argvars[3], &error); + id = tv_get_number_chk(&argvars[3], &error); if (argvars[4].v_type != VAR_UNKNOWN) { if (argvars[4].v_type != VAR_DICT) { EMSG(_(e_dictreq)); @@ -12387,9 +12406,9 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *conceal_char = NULL; if (argvars[2].v_type != VAR_UNKNOWN) { - prio = get_tv_number_chk(&argvars[2], &error); + prio = tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { - id = get_tv_number_chk(&argvars[3], &error); + id = tv_get_number_chk(&argvars[3], &error); if (argvars[4].v_type != VAR_UNKNOWN) { if (argvars[4].v_type != VAR_DICT) { EMSG(_(e_dictreq)); @@ -12424,7 +12443,7 @@ static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tv_list_alloc_ret(rettv); - int id = get_tv_number(&argvars[0]); + int id = tv_get_number(&argvars[0]); if (id >= 1 && id <= 3) { matchitem_T *m; @@ -12446,7 +12465,7 @@ static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = match_delete(curwin, - (int)get_tv_number(&argvars[0]), TRUE); + (int)tv_get_number(&argvars[0]), true); } /* @@ -12493,14 +12512,16 @@ static void max_min(typval_T *argvars, typval_T *rettv, int domax) if (l != NULL) { li = l->lv_first; if (li != NULL) { - n = get_tv_number_chk(&li->li_tv, &error); + n = tv_get_number_chk(&li->li_tv, &error); for (;; ) { li = li->li_next; - if (li == NULL) + if (li == NULL) { break; - i = get_tv_number_chk(&li->li_tv, &error); - if (domax ? i > n : i < n) + } + i = tv_get_number_chk(&li->li_tv, &error); + if (domax ? i > n : i < n) { n = i; + } } } } @@ -12516,7 +12537,7 @@ static void max_min(typval_T *argvars, typval_T *rettv, int domax) for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { if (!HASHITEM_EMPTY(hi)) { todo--; - i = get_tv_number_chk(&TV_DICT_HI2DI(hi)->di_tv, &error); + i = tv_get_number_chk(&TV_DICT_HI2DI(hi)->di_tv, &error); if (first) { n = i; first = FALSE; @@ -12570,7 +12591,7 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[1].v_type != VAR_UNKNOWN) { if (argvars[2].v_type != VAR_UNKNOWN) { - prot = get_tv_number_chk(&argvars[2], NULL); + prot = tv_get_number_chk(&argvars[2], NULL); } if (prot != -1 && strcmp(tv_get_string(&argvars[1]), "p") == 0) { char *failed_dir; @@ -12782,14 +12803,16 @@ static void f_nr2char(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (has_mbyte) { int utf8 = 0; - if (argvars[1].v_type != VAR_UNKNOWN) - utf8 = get_tv_number_chk(&argvars[1], NULL); - if (utf8) - buf[(*utf_char2bytes)((int)get_tv_number(&argvars[0]), buf)] = NUL; - else - buf[(*mb_char2bytes)((int)get_tv_number(&argvars[0]), buf)] = NUL; + if (argvars[1].v_type != VAR_UNKNOWN) { + utf8 = tv_get_number_chk(&argvars[1], NULL); + } + if (utf8) { + buf[(*utf_char2bytes)((int)tv_get_number(&argvars[0]), buf)] = NUL; + } else { + buf[(*mb_char2bytes)((int)tv_get_number(&argvars[0]), buf)] = NUL; + } } else { - buf[0] = (char_u)get_tv_number(&argvars[0]); + buf[0] = (char_u)tv_get_number(&argvars[0]); buf[1] = NUL; } rettv->v_type = VAR_STRING; @@ -12801,8 +12824,8 @@ static void f_nr2char(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_or(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = get_tv_number_chk(&argvars[0], NULL) - | get_tv_number_chk(&argvars[1], NULL); + rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) + | tv_get_number_chk(&argvars[1], NULL); } /* @@ -12911,14 +12934,15 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr) long i; bool error = false; - start = get_tv_number_chk(&argvars[0], &error); + start = tv_get_number_chk(&argvars[0], &error); if (argvars[1].v_type == VAR_UNKNOWN) { end = start - 1; start = 0; } else { - end = get_tv_number_chk(&argvars[1], &error); - if (argvars[2].v_type != VAR_UNKNOWN) - stride = get_tv_number_chk(&argvars[2], &error); + end = tv_get_number_chk(&argvars[1], &error); + if (argvars[2].v_type != VAR_UNKNOWN) { + stride = tv_get_number_chk(&argvars[2], &error); + } } if (error) { @@ -12959,7 +12983,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) binary = true; } if (argvars[2].v_type != VAR_UNKNOWN) { - maxline = get_tv_number(&argvars[2]); + maxline = tv_get_number(&argvars[2]); } } @@ -13239,7 +13263,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { bool error = false; - idx = get_tv_number_chk(&argvars[1], &error); + idx = tv_get_number_chk(&argvars[1], &error); if (error) { // Type error: do nothing, errmsg already given. } else if ((item = tv_list_find(l, idx)) == NULL) { @@ -13251,8 +13275,8 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) *rettv = item->li_tv; xfree(item); } else { - /* Remove range of items, return list with values. */ - end = get_tv_number_chk(&argvars[2], &error); + // Remove range of items, return list with values. + end = tv_get_number_chk(&argvars[2], &error); if (error) { // Type error: do nothing. } else if ((item2 = tv_list_find(l, end)) == NULL) { @@ -13302,7 +13326,7 @@ static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - varnumber_T n = get_tv_number(&argvars[1]); + varnumber_T n = tv_get_number(&argvars[1]); if (argvars[0].v_type == VAR_LIST) { tv_list_alloc_ret(rettv); while (n-- > 0) { @@ -13621,13 +13645,15 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) /* Optional arguments: line number to stop searching and timeout. */ if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) { - lnum_stop = get_tv_number_chk(&argvars[2], NULL); - if (lnum_stop < 0) + lnum_stop = tv_get_number_chk(&argvars[2], NULL); + if (lnum_stop < 0) { goto theend; + } if (argvars[3].v_type != VAR_UNKNOWN) { - time_limit = get_tv_number_chk(&argvars[3], NULL); - if (time_limit < 0) + time_limit = tv_get_number_chk(&argvars[3], NULL); + if (time_limit < 0) { goto theend; + } } } @@ -13889,17 +13915,16 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int row; - int col; int c; - row = get_tv_number_chk(&argvars[0], NULL) - 1; - col = get_tv_number_chk(&argvars[1], NULL) - 1; + const int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; + const int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; if (row < 0 || row >= screen_Rows - || col < 0 || col >= screen_Columns) + || col < 0 || col >= screen_Columns) { c = -1; - else + } else { c = ScreenAttrs[LineOffset[row] + col]; + } rettv->vval.v_number = c; } @@ -13908,17 +13933,15 @@ static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int row; - int col; int off; int c; - row = get_tv_number_chk(&argvars[0], NULL) - 1; - col = get_tv_number_chk(&argvars[1], NULL) - 1; + const int row = tv_get_number_chk(&argvars[0], NULL) - 1; + const int col = tv_get_number_chk(&argvars[1], NULL) - 1; if (row < 0 || row >= screen_Rows - || col < 0 || col >= screen_Columns) + || col < 0 || col >= screen_Columns) { c = -1; - else { + } else { off = LineOffset[row] + col; if (enc_utf8 && ScreenLinesUC[off] != 0) c = ScreenLinesUC[off]; @@ -13969,9 +13992,10 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr) char_u *name = get_tv_string_chk(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { - locally = get_tv_number_chk(&argvars[1], &error) == 0; - if (!error && argvars[2].v_type != VAR_UNKNOWN) - thisblock = get_tv_number_chk(&argvars[2], &error) != 0; + locally = tv_get_number_chk(&argvars[1], &error) == 0; + if (!error && argvars[2].v_type != VAR_UNKNOWN) { + thisblock = tv_get_number_chk(&argvars[2], &error) != 0; + } } if (!error && name != NULL) rettv->vval.v_number = find_decl(name, STRLEN(name), locally, @@ -14027,13 +14051,15 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) else { skip = get_tv_string_buf_chk(&argvars[4], nbuf3); if (argvars[5].v_type != VAR_UNKNOWN) { - lnum_stop = get_tv_number_chk(&argvars[5], NULL); - if (lnum_stop < 0) + lnum_stop = tv_get_number_chk(&argvars[5], NULL); + if (lnum_stop < 0) { goto theend; + } if (argvars[6].v_type != VAR_UNKNOWN) { - time_limit = get_tv_number_chk(&argvars[6], NULL); - if (time_limit < 0) + time_limit = tv_get_number_chk(&argvars[6], NULL); + if (time_limit < 0) { goto theend; + } } } } @@ -14337,7 +14363,7 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) aucmd_prepbuf(&aco, buf); varname++; - numval = get_tv_number_chk(varp, &error); + numval = tv_get_number_chk(varp, &error); char *const strval = (char *)get_tv_string_buf_chk(varp, nbuf); if (!error && strval != NULL) { set_option_value(varname, numval, strval, OPT_LOCAL); @@ -14385,12 +14411,12 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) di = tv_dict_find(d, S_LEN("forward")); if (di != NULL) { - set_csearch_direction(get_tv_number(&di->di_tv) ? FORWARD : BACKWARD); + set_csearch_direction(tv_get_number(&di->di_tv) ? FORWARD : BACKWARD); } di = tv_dict_find(d, S_LEN("until")); if (di != NULL) { - set_csearch_until(!!get_tv_number(&di->di_tv)); + set_csearch_until(!!tv_get_number(&di->di_tv)); } } } @@ -14400,10 +14426,11 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_setcmdpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int pos = (int)get_tv_number(&argvars[0]) - 1; + const int pos = (int)tv_get_number(&argvars[0]) - 1; - if (pos >= 0) + if (pos >= 0) { rettv->vval.v_number = set_cmdline_pos(pos); + } } @@ -14854,7 +14881,7 @@ static void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - tabpage_T *const tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); + tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); const char *const varname = (const char *)get_tv_string_chk(&argvars[1]); typval_T *const varp = &argvars[2]; @@ -14904,7 +14931,7 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) tabpage_T *tp = NULL; if (off == 1) { - tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); + tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); } else { tp = curtab; } @@ -14923,7 +14950,7 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) bool error = false; varname++; - numval = get_tv_number_chk(varp, &error); + numval = tv_get_number_chk(varp, &error); char_u nbuf[NUMBUFLEN]; char *const strval = (char *)get_tv_string_buf_chk(varp, nbuf); if (!error && strval != NULL) { @@ -15024,8 +15051,8 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) typval_T *tv2 = &si2->item->li_tv; if (sortinfo->item_compare_numbers) { - long v1 = get_tv_number(tv1); - long v2 = get_tv_number(tv2); + const varnumber_T v1 = tv_get_number(tv1); + const varnumber_T v2 = tv_get_number(tv2); return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; } @@ -15138,7 +15165,7 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) if (res == FAIL) { res = ITEM_COMPARE_FAIL; } else { - res = get_tv_number_chk(&rettv, &sortinfo->item_compare_func_err); + res = tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err); } if (sortinfo->item_compare_func_err) { res = ITEM_COMPARE_FAIL; // return value has wrong type @@ -15220,7 +15247,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) } else { bool error = false; - i = get_tv_number_chk(&argvars[1], &error); + i = tv_get_number_chk(&argvars[1], &error); if (error) { goto theend; // type error; errmsg already given } @@ -15443,13 +15470,15 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) { const char *const str = tv_get_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { - maxcount = get_tv_number_chk(&argvars[1], &typeerr); - if (maxcount <= 0) + maxcount = tv_get_number_chk(&argvars[1], &typeerr); + if (maxcount <= 0) { return; + } if (argvars[2].v_type != VAR_UNKNOWN) { - need_capital = get_tv_number_chk(&argvars[2], &typeerr); - if (typeerr) + need_capital = tv_get_number_chk(&argvars[2], &typeerr); + if (typeerr) { return; + } } } else maxcount = 25; @@ -15491,7 +15520,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) typeerr = true; } if (argvars[2].v_type != VAR_UNKNOWN) { - keepempty = (bool)get_tv_number_chk(&argvars[2], &typeerr); + keepempty = (bool)tv_get_number_chk(&argvars[2], &typeerr); } } if (pat == NULL || *pat == NUL) @@ -15563,7 +15592,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) int what; if (argvars[1].v_type != VAR_UNKNOWN) { - base = get_tv_number(&argvars[1]); + base = tv_get_number(&argvars[1]); if (base != 2 && base != 8 && base != 10 && base != 16) { EMSG(_(e_invarg)); return; @@ -15608,7 +15637,7 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[1].v_type == VAR_UNKNOWN) { seconds = time(NULL); } else { - seconds = (time_t)get_tv_number(&argvars[1]); + seconds = (time_t)tv_get_number(&argvars[1]); } struct tm curtime; @@ -15658,9 +15687,8 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (str == NULL) { return; } - bool error = false; - varnumber_T charidx = get_tv_number_chk(&argvars[1], &error); + varnumber_T charidx = tv_get_number_chk(&argvars[1], &error); if (error) { return; } @@ -15699,11 +15727,13 @@ static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[2].v_type != VAR_UNKNOWN) { bool error = false; - start_idx = get_tv_number_chk(&argvars[2], &error); - if (error || start_idx >= (int)STRLEN(haystack)) + start_idx = tv_get_number_chk(&argvars[2], &error); + if (error || start_idx >= (int)STRLEN(haystack)) { return; - if (start_idx >= 0) + } + if (start_idx >= 0) { haystack += start_idx; + } } pos = (char_u *)strstr((char *)haystack, (char *)needle); @@ -15739,7 +15769,7 @@ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) int (*func_mb_ptr2char_adv)(char_u **pp); if (argvars[1].v_type != VAR_UNKNOWN) { - skipcc = get_tv_number_chk(&argvars[1], NULL); + skipcc = tv_get_number_chk(&argvars[1], NULL); } if (skipcc < 0 || skipcc > 1) { EMSG(_(e_invarg)); @@ -15762,7 +15792,7 @@ static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) int col = 0; if (argvars[1].v_type != VAR_UNKNOWN) { - col = get_tv_number(&argvars[1]); + col = tv_get_number(&argvars[1]); } rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char_u *)s) - col); @@ -15786,7 +15816,7 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) int nbyte = 0; bool error = false; - varnumber_T nchar = get_tv_number_chk(&argvars[1], &error); + varnumber_T nchar = tv_get_number_chk(&argvars[1], &error); if (!error) { if (nchar > 0) { while (nchar > 0 && (size_t)nbyte < slen) { @@ -15799,7 +15829,7 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) } int len = 0; if (argvars[2].v_type != VAR_UNKNOWN) { - int charlen = get_tv_number(&argvars[2]); + int charlen = tv_get_number(&argvars[2]); while (charlen > 0 && nbyte + len < (int)slen) { int off = nbyte + len; @@ -15842,12 +15872,12 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *const p = tv_get_string(&argvars[0]); const size_t slen = strlen(p); - varnumber_T n = get_tv_number_chk(&argvars[1], &error); + varnumber_T n = tv_get_number_chk(&argvars[1], &error); varnumber_T len; if (error) { len = 0; } else if (argvars[2].v_type != VAR_UNKNOWN) { - len = get_tv_number(&argvars[2]); + len = tv_get_number(&argvars[2]); } else { len = slen - n; // Default len: all bytes that are available. } @@ -15891,12 +15921,14 @@ static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) haystack_len = (int)STRLEN(haystack); if (argvars[2].v_type != VAR_UNKNOWN) { - /* Third argument: upper limit for index */ - end_idx = get_tv_number_chk(&argvars[2], NULL); - if (end_idx < 0) - return; /* can never find a match */ - } else + // Third argument: upper limit for index. + end_idx = tv_get_number_chk(&argvars[2], NULL); + if (end_idx < 0) { + return; // Can never find a match. + } + } else { end_idx = haystack_len; + } if (*needle == NUL) { /* Empty string matches past the end. */ @@ -15931,7 +15963,7 @@ static void f_strtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) { bool error = false; - int no = (int)get_tv_number_chk(&argvars[0], &error); + int no = (int)tv_get_number_chk(&argvars[0], &error); if (error) { return; } @@ -15943,7 +15975,7 @@ static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) int retList = 0; if (argvars[1].v_type != VAR_UNKNOWN) { - retList = get_tv_number_chk(&argvars[1], &error); + retList = tv_get_number_chk(&argvars[1], &error); if (error) { return; } @@ -15993,10 +16025,10 @@ static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { // -1 on type error (both) const linenr_T lnum = tv_get_lnum(argvars); - const colnr_T col = (colnr_T)get_tv_number(&argvars[1]) - 1; + const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1; bool transerr = false; - const int trans = get_tv_number_chk(&argvars[2], &transerr); + const int trans = tv_get_number_chk(&argvars[2], &transerr); int id = 0; if (!transerr && lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count @@ -16012,7 +16044,7 @@ static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - const int id = get_tv_number(&argvars[0]); + const int id = (int)tv_get_number(&argvars[0]); const char *const what = tv_get_string(&argvars[1]); int modec; if (argvars[2].v_type != VAR_UNKNOWN) { @@ -16086,14 +16118,13 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_synIDtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int id; + int id = tv_get_number(&argvars[0]); - id = get_tv_number(&argvars[0]); - - if (id > 0) + if (id > 0) { id = syn_get_final_id(id); - else + } else { id = 0; + } rettv->vval.v_number = id; } @@ -16113,7 +16144,7 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) // -1 on type error (both) const linenr_T lnum = tv_get_lnum(argvars); - const colnr_T col = (colnr_T)get_tv_number(&argvars[1]) - 1; + const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1; memset(str, NUL, sizeof(str)); @@ -16154,7 +16185,7 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) // -1 on type error (both) const linenr_T lnum = tv_get_lnum(argvars); - const colnr_T col = (colnr_T)get_tv_number(&argvars[1]) - 1; + const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1; if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count @@ -16232,7 +16263,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, if (retlist) { int keepempty = 0; if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) { - keepempty = get_tv_number(&argvars[2]); + keepempty = tv_get_number(&argvars[2]); } rettv->vval.v_list = string_to_list(res, nread, (bool)keepempty); rettv->vval.v_list->lv_refcount++; @@ -16277,15 +16308,15 @@ static void f_systemlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - tabpage_T *tp; win_T *wp = NULL; - if (argvars[0].v_type == VAR_UNKNOWN) + if (argvars[0].v_type == VAR_UNKNOWN) { wp = firstwin; - else { - tp = find_tabpage((int)get_tv_number(&argvars[0])); - if (tp != NULL) + } else { + tabpage_T *const tp = find_tabpage((int)tv_get_number(&argvars[0])); + if (tp != NULL) { wp = (tp == curtab) ? firstwin : tp->tp_firstwin; + } } if (wp != NULL) { tv_list_alloc_ret(rettv); @@ -16367,13 +16398,12 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar) static void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int nr = 1; - tabpage_T *tp; - - tp = find_tabpage((int)get_tv_number(&argvars[0])); - if (tp == NULL) + tabpage_T *const tp = find_tabpage((int)tv_get_number(&argvars[0])); + if (tp == NULL) { nr = 0; - else + } else { nr = get_winnr(tp, &argvars[1]); + } rettv->vval.v_number = nr; } @@ -16648,7 +16678,7 @@ static bool set_ref_in_callback(Callback *callback, int copyID, /// "timer_start(timeout, callback, opts)" function static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - long timeout = get_tv_number(&argvars[0]); + const long timeout = tv_get_number(&argvars[0]); timer_T *timer; int repeat = 1; dict_T *dict; @@ -16663,7 +16693,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) } dictitem_T *const di = tv_dict_find(dict, S_LEN("repeat")); if (di != NULL) { - repeat = get_tv_number(&di->di_tv); + repeat = tv_get_number(&di->di_tv); if (repeat == 0) { repeat = 1; } @@ -16703,7 +16733,7 @@ static void f_timer_stop(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - timer_T *timer = pmap_get(uint64_t)(timers, get_tv_number(&argvars[0])); + timer_T *timer = pmap_get(uint64_t)(timers, tv_get_number(&argvars[0])); if (timer == NULL) { return; @@ -17177,29 +17207,29 @@ static void f_winrestview(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { dictitem_T *di; if ((di = tv_dict_find(dict, S_LEN("lnum"))) != NULL) { - curwin->w_cursor.lnum = get_tv_number(&di->di_tv); + curwin->w_cursor.lnum = tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("col"))) != NULL) { - curwin->w_cursor.col = get_tv_number(&di->di_tv); + curwin->w_cursor.col = tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("coladd"))) != NULL) { - curwin->w_cursor.coladd = get_tv_number(&di->di_tv); + curwin->w_cursor.coladd = tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("curswant"))) != NULL) { - curwin->w_curswant = get_tv_number(&di->di_tv); + curwin->w_curswant = tv_get_number(&di->di_tv); curwin->w_set_curswant = false; } if ((di = tv_dict_find(dict, S_LEN("topline"))) != NULL) { - set_topline(curwin, get_tv_number(&di->di_tv)); + set_topline(curwin, tv_get_number(&di->di_tv)); } if ((di = tv_dict_find(dict, S_LEN("topfill"))) != NULL) { - curwin->w_topfill = get_tv_number(&di->di_tv); + curwin->w_topfill = tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("leftcol"))) != NULL) { - curwin->w_leftcol = get_tv_number(&di->di_tv); + curwin->w_leftcol = tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("skipcol"))) != NULL) { - curwin->w_skipcol = get_tv_number(&di->di_tv); + curwin->w_skipcol = tv_get_number(&di->di_tv); } check_cursor(); @@ -17462,8 +17492,8 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_xor(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = get_tv_number_chk(&argvars[0], NULL) - ^ get_tv_number_chk(&argvars[1], NULL); + rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) + ^ tv_get_number_chk(&argvars[1], NULL); } @@ -18408,83 +18438,6 @@ void free_tv(typval_T *varp) // TODO(ZyX-I): move to eval/typval -/// Get the number value of a variable -/// -/// @note Use get_tv_number_chk() if you need to determine whether there was an -/// error. -/// -/// @param[in] varp Variable to get value from. -/// -/// @return Number value: vim_str2nr() output for VAR_STRING variables, value -/// for VAR_NUMBER variables, -1 for other types. -varnumber_T get_tv_number(const typval_T *const varp) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - bool error = false; - return get_tv_number_chk(varp, &error); -} - -varnumber_T get_tv_number_chk(const typval_T *const varp, bool *const denote) -{ - varnumber_T n = 0; - - switch (varp->v_type) { - case VAR_NUMBER: { - return varp->vval.v_number; - } - case VAR_FLOAT: { - EMSG(_("E805: Using a Float as a Number")); - break; - } - case VAR_PARTIAL: - case VAR_FUNC: { - EMSG(_("E703: Using a Funcref as a Number")); - break; - } - case VAR_STRING: { - if (varp->vval.v_string != NULL) { - long nr; - vim_str2nr(varp->vval.v_string, NULL, NULL, STR2NR_ALL, &nr, NULL, 0); - n = (varnumber_T)nr; - } - return n; - } - case VAR_LIST: { - EMSG(_("E745: Using a List as a Number")); - break; - } - case VAR_DICT: { - EMSG(_("E728: Using a Dictionary as a Number")); - break; - } - case VAR_SPECIAL: { - switch (varp->vval.v_special) { - case kSpecialVarTrue: { - return 1; - } - case kSpecialVarFalse: - case kSpecialVarNull: { - return 0; - } - } - break; - } - case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "get_tv_number(UNKNOWN)"); - break; - } - } - if (denote == NULL) { - // useful for values that must be unsigned - n = -1; - } else { - *denote = true; - } - return n; -} - -// TODO(ZyX-I): move to eval/typval - /// Careful: This uses a single, static buffer. YOU CAN ONLY USE IT ONCE! char_u *get_tv_string_chk(const typval_T *varp) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT @@ -18981,7 +18934,7 @@ static void set_var(const char *name, typval_T *const tv, const bool copy) } return; } else if (v->di_tv.v_type == VAR_NUMBER) { - v->di_tv.vval.v_number = get_tv_number(tv); + v->di_tv.vval.v_number = tv_get_number(tv); if (strcmp(varname, "searchforward") == 0) { set_search_direction(v->di_tv.vval.v_number ? '/' : '?'); } else if (strcmp(varname, "hlsearch") == 0) { diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c index d2d0873792..ec6c86ac64 100644 --- a/src/nvim/eval/executor.c +++ b/src/nvim/eval/executor.c @@ -50,7 +50,7 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, } if (*op == '+' || *op == '-') { // nr += nr or nr -= nr - varnumber_T n = get_tv_number(tv1); + varnumber_T n = tv_get_number(tv1); if (tv2->v_type == VAR_FLOAT) { float_T f = n; @@ -64,9 +64,9 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, tv1->vval.v_float = f; } else { if (*op == '+') { - n += get_tv_number(tv2); + n += tv_get_number(tv2); } else { - n -= get_tv_number(tv2); + n -= tv_get_number(tv2); } tv_clear(tv1); tv1->v_type = VAR_NUMBER; @@ -96,7 +96,7 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, } const float_T f = (tv2->v_type == VAR_FLOAT ? tv2->vval.v_float - : get_tv_number(tv2)); + : tv_get_number(tv2)); if (*op == '+') { tv1->vval.v_float += f; } else { diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 38032762dc..087e76de10 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -17,6 +17,7 @@ #include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/pos.h" +#include "nvim/charset.h" // TODO(ZyX-I): Move line_breakcheck out of misc1 #include "nvim/misc1.h" // For line_breakcheck @@ -725,7 +726,7 @@ varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *ret_error) } return -1; } - return get_tv_number_chk(&li->li_tv, ret_error); + return tv_get_number_chk(&li->li_tv, ret_error); } /// Get list item l[n - 1] as a string @@ -1087,7 +1088,7 @@ varnumber_T tv_dict_get_number(dict_T *const d, const char *const key) if (di == NULL) { return 0; } - return get_tv_number(&di->di_tv); + return tv_get_number(&di->di_tv); } /// Get a string item from a dictionary @@ -1971,7 +1972,7 @@ bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic, /// Check that given value is a number or string /// -/// Error messages are compatible with get_tv_number() previously used for the +/// Error messages are compatible with tv_get_number() previously used for the /// same purpose in buf*() functions. Special values are not accepted (previous /// behaviour: silently fail to find buffer). /// @@ -2016,8 +2017,127 @@ bool tv_check_str_or_nr(const typval_T *const tv) return false; } +#define FUNC_ERROR "E703: Using a Funcref as a Number" + +static const char *const num_errors[] = { + [VAR_PARTIAL]=N_(FUNC_ERROR), + [VAR_FUNC]=N_(FUNC_ERROR), + [VAR_LIST]=N_("E745: Using a List as a Number"), + [VAR_DICT]=N_("E728: Using a Dictionary as a Number"), + [VAR_FLOAT]=N_("E805: Using a Float as a Number"), + [VAR_UNKNOWN]=N_("E685: using an invalid value as a Number"), +}; + +#undef FUNC_ERROR + +/// Check that given value is a number or can be converted to it +/// +/// Error messages are compatible with tv_get_number() previously used for +/// the same purpose. +/// +/// @param[in] tv Value to check. +/// +/// @return true if everything is OK, false otherwise. +bool tv_check_num(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + switch (tv->v_type) { + case VAR_NUMBER: + case VAR_SPECIAL: + case VAR_STRING: { + return true; + } + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_LIST: + case VAR_DICT: + case VAR_FLOAT: + case VAR_UNKNOWN: { + EMSG(_(num_errors[tv->v_type])); + return false; + } + } + assert(false); + return false; +} + //{{{2 Get +/// Get the number value of a VimL object +/// +/// @note Use tv_get_number_chk() if you need to determine whether there was an +/// error. +/// +/// @param[in] tv Object to get value from. +/// +/// @return Number value: vim_str2nr() output for VAR_STRING objects, value +/// for VAR_NUMBER objects, -1 for other types. +varnumber_T tv_get_number(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + bool error = false; + return tv_get_number_chk(tv, &error); +} + +/// Get the number value of a VimL object +/// +/// @param[in] tv Object to get value from. +/// @param[out] ret_error If type error occurred then `true` will be written +/// to this location. Otherwise it is not touched. +/// +/// @note Needs to be initialized to `false` to be +/// useful. +/// +/// @return Number value: vim_str2nr() output for VAR_STRING objects, value +/// for VAR_NUMBER objects, -1 (ret_error == NULL) or 0 (otherwise) for +/// other types. +varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) +{ + switch (tv->v_type) { + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_LIST: + case VAR_DICT: + case VAR_FLOAT: { + EMSG(_(num_errors[tv->v_type])); + break; + } + case VAR_NUMBER: { + return tv->vval.v_number; + } + case VAR_STRING: { + varnumber_T n = 0; + if (tv->vval.v_string != NULL) { + long nr; + vim_str2nr(tv->vval.v_string, NULL, NULL, STR2NR_ALL, &nr, NULL, 0); + n = (varnumber_T)nr; + } + return n; + } + case VAR_SPECIAL: { + switch (tv->vval.v_special) { + case kSpecialVarTrue: { + return 1; + } + case kSpecialVarFalse: + case kSpecialVarNull: { + return 0; + } + } + break; + } + case VAR_UNKNOWN: { + EMSG2(_(e_intern2), "tv_get_number(UNKNOWN)"); + break; + } + } + if (ret_error != NULL) { + *ret_error = true; + } + return (ret_error == NULL ? -1 : 0); +} + /// Get the line number from VimL object /// /// @param[in] tv Object to get value from. Is expected to be a number or @@ -2028,7 +2148,7 @@ bool tv_check_str_or_nr(const typval_T *const tv) linenr_T tv_get_lnum(const typval_T *const tv) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - linenr_T lnum = get_tv_number_chk(tv, NULL); + linenr_T lnum = tv_get_number_chk(tv, NULL); if (lnum == 0) { // No valid number, try using same function as line() does. int fnum; pos_T *const fp = var2fpos(tv, true, &fnum); @@ -2078,7 +2198,7 @@ float_T tv_get_float(const typval_T *const tv) break; } case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "get_tv_float()"); + EMSG2(_(e_intern2), "get_tv_float(UNKNOWN)"); break; } } diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 9cfa126a56..e20979c307 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -576,7 +576,7 @@ static varnumber_T tv_nr(typval_T *tvs, int *idxp) } else { (*idxp)++; bool err = false; - n = (varnumber_T)get_tv_number_chk(&tvs[idx], &err); + n = tv_get_number_chk(&tvs[idx], &err); if (err) { n = 0; } diff --git a/src/nvim/window.c b/src/nvim/window.c index a3b0e6fc2d..6020159af9 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5593,7 +5593,7 @@ int match_add(win_T *wp, const char *const grp, const char *const pat, if (subli == NULL) { goto fail; } - lnum = get_tv_number_chk(&subli->li_tv, &error); + lnum = tv_get_number_chk(&subli->li_tv, &error); if (error) { goto fail; } @@ -5604,13 +5604,13 @@ int match_add(win_T *wp, const char *const grp, const char *const pat, m->pos.pos[i].lnum = lnum; subli = subli->li_next; if (subli != NULL) { - col = get_tv_number_chk(&subli->li_tv, &error); + col = tv_get_number_chk(&subli->li_tv, &error); if (error) { goto fail; } subli = subli->li_next; if (subli != NULL) { - len = get_tv_number_chk(&subli->li_tv, &error); + len = tv_get_number_chk(&subli->li_tv, &error); if (error) { goto fail; } @@ -5810,14 +5810,14 @@ int win_getid(typval_T *argvars) if (argvars[0].v_type == VAR_UNKNOWN) { return curwin->handle; } - int winnr = get_tv_number(&argvars[0]); + int winnr = tv_get_number(&argvars[0]); win_T *wp; if (winnr > 0) { if (argvars[1].v_type == VAR_UNKNOWN) { wp = firstwin; } else { tabpage_T *tp = NULL; - int tabnr = get_tv_number(&argvars[1]); + int tabnr = tv_get_number(&argvars[1]); FOR_ALL_TABS(tp2) { if (--tabnr == 0) { tp = tp2; @@ -5844,7 +5844,7 @@ int win_getid(typval_T *argvars) int win_gotoid(typval_T *argvars) { - int id = get_tv_number(&argvars[0]); + int id = tv_get_number(&argvars[0]); FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->handle == id) { @@ -5879,7 +5879,7 @@ void win_id2tabwin(typval_T *argvars, list_T *list) { int winnr = 1; int tabnr = 1; - int id = get_tv_number(&argvars[0]); + handle_T id = (handle_T)tv_get_number(&argvars[0]); win_get_tabwin(id, &tabnr, &winnr); tv_list_append_number(list, tabnr); @@ -5888,7 +5888,7 @@ void win_id2tabwin(typval_T *argvars, list_T *list) win_T * win_id2wp(typval_T *argvars) { - int id = get_tv_number(&argvars[0]); + int id = tv_get_number(&argvars[0]); FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->handle == id) { @@ -5902,7 +5902,7 @@ win_T * win_id2wp(typval_T *argvars) int win_id2win(typval_T *argvars) { int nr = 1; - int id = get_tv_number(&argvars[0]); + int id = tv_get_number(&argvars[0]); FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->handle == id) { @@ -5915,7 +5915,7 @@ int win_id2win(typval_T *argvars) void win_findbuf(typval_T *argvars, list_T *list) { - int bufnr = get_tv_number(&argvars[0]); + int bufnr = tv_get_number(&argvars[0]); FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer->b_fnum == bufnr) { From 50ebd1dff5c4e995c4f7e7980870e43d9defabc6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 28 Aug 2016 09:15:28 +0300 Subject: [PATCH 0189/1671] eval: Move free_tv to eval/typval.h, remove most of its usages --- src/nvim/api/vim.c | 11 +++++---- src/nvim/eval.c | 53 +++++------------------------------------- src/nvim/eval/typval.c | 40 +++++++++++++++++++++++++++++++ src/nvim/ex_eval.c | 15 ++++++------ src/nvim/quickfix.c | 13 +++++------ 5 files changed, 66 insertions(+), 66 deletions(-) diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index db2f25a2a6..975446057c 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -182,19 +182,20 @@ Object nvim_eval(String expr, Error *err) Object rv = OBJECT_INIT; // Evaluate the expression try_start(); - typval_T *expr_result = eval_expr((char_u *)expr.data, NULL); - if (!expr_result) { + typval_T rettv; + if (eval0((char_u *)expr.data, &rettv, NULL, true) == FAIL) { api_set_error(err, Exception, "Failed to evaluate expression"); } if (!try_end(err)) { // No errors, convert the result - rv = vim_to_object(expr_result); + rv = vim_to_object(&rettv); } - // Free the vim object - free_tv(expr_result); + // Free the Vim object + tv_clear(&rettv); + return rv; } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 676df1a301..65bb90fc15 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -719,13 +719,12 @@ int current_func_returned(void) */ void set_internal_string_var(char_u *name, char_u *value) { - char_u *val = vim_strsave(value); - typval_T *tvp = xcalloc(1, sizeof(typval_T)); + const typval_T tv = { + .v_type = VAR_STRING, + .vval.v_string = value, + }; - tvp->v_type = VAR_STRING; - tvp->vval.v_string = val; - set_var((const char *)name, tvp, false); - free_tv(tvp); + set_var((const char *)name, (typval_T *)&tv, true); } static lval_T *redir_lval = NULL; @@ -3264,7 +3263,7 @@ typedef enum { * Note: "rettv.v_lock" is not set. * Return OK or FAIL. */ -static int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) +int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) { int ret; char_u *p; @@ -18404,38 +18403,6 @@ void set_selfdict(typval_T *rettv, dict_T *selfdict) } } -/* - * Free the memory for a variable type-value. - */ -void free_tv(typval_T *varp) -{ - if (varp != NULL) { - switch (varp->v_type) { - case VAR_FUNC: - func_unref(varp->vval.v_string); - // FALLTHROUGH - case VAR_STRING: - xfree(varp->vval.v_string); - break; - case VAR_PARTIAL: - partial_unref(varp->vval.v_partial); - break; - case VAR_LIST: - tv_list_unref(varp->vval.v_list); - break; - case VAR_DICT: - tv_dict_unref(varp->vval.v_dict); - break; - case VAR_SPECIAL: - case VAR_NUMBER: - case VAR_FLOAT: - case VAR_UNKNOWN: - break; - } - xfree(varp); - } -} - // TODO(ZyX-I): move to eval/typval /// Careful: This uses a single, static buffer. YOU CAN ONLY USE IT ONCE! @@ -21525,14 +21492,6 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv) return idx < 0; } -/* - * Free the variable with a pending return value. - */ -void discard_pending_return(void *rettv) -{ - free_tv((typval_T *)rettv); -} - /* * Generate a return command for producing the value of "rettv". The result * is an allocated string. Used by report_pending() for verbose messages. diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 087e76de10..ca635dcae9 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1742,6 +1742,46 @@ void tv_clear(typval_T *tv) } } +//{{{3 Free + +/// Free allocated VimL object and value stored inside +/// +/// @param tv Object to free. +void tv_free(typval_T *tv) +{ + if (tv != NULL) { + switch (tv->v_type) { + case VAR_PARTIAL: { + partial_unref(tv->vval.v_partial); + break; + } + case VAR_FUNC: { + func_unref(tv->vval.v_string); + // FALLTHROUGH + } + case VAR_STRING: { + xfree(tv->vval.v_string); + break; + } + case VAR_LIST: { + tv_list_unref(tv->vval.v_list); + break; + } + case VAR_DICT: { + tv_dict_unref(tv->vval.v_dict); + break; + } + case VAR_SPECIAL: + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_UNKNOWN: { + break; + } + } + xfree(tv); + } +} + //{{{2 Locks /// Lock or unlock an item diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 3f71ae1795..65112c4dd8 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -14,6 +14,7 @@ #include "nvim/ex_eval.h" #include "nvim/charset.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" #include "nvim/message.h" @@ -21,8 +22,6 @@ #include "nvim/regexp.h" #include "nvim/strings.h" - - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_eval.c.generated.h" #endif @@ -59,12 +58,14 @@ * is an error exception.) - The macros can be defined as expressions checking * for a variable that is allowed to be changed during execution of a script. */ -/* Values used for the Vim release. */ -# define THROW_ON_ERROR TRUE -# define THROW_ON_ERROR_TRUE -# define THROW_ON_INTERRUPT TRUE -# define THROW_ON_INTERRUPT_TRUE +// Values used for the Vim release. +#define THROW_ON_ERROR true +#define THROW_ON_ERROR_TRUE +#define THROW_ON_INTERRUPT true +#define THROW_ON_INTERRUPT_TRUE + +#define discard_pending_return(p) tv_free((typval_T *)(p)) /* * When several errors appear in a row, setting "force_abort" is delayed until diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 06ac2821b0..4fa5c85abd 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -4372,7 +4372,6 @@ void ex_cbuffer(exarg_T *eap) */ void ex_cexpr(exarg_T *eap) { - typval_T *tv; qf_info_T *qi = &ql_info; const char *au_name = NULL; @@ -4412,11 +4411,11 @@ void 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, NULL); - if (tv != NULL) { - if ((tv->v_type == VAR_STRING && tv->vval.v_string != NULL) - || (tv->v_type == VAR_LIST && tv->vval.v_list != NULL)) { - if (qf_init_ext(qi, NULL, NULL, tv, p_efm, + typval_T tv; + if (eval0(eap->arg, &tv, NULL, true) != FAIL) { + if ((tv.v_type == VAR_STRING && tv.vval.v_string != NULL) + || (tv.v_type == VAR_LIST && tv.vval.v_list != NULL)) { + if (qf_init_ext(qi, NULL, NULL, &tv, p_efm, (eap->cmdidx != CMD_caddexpr && eap->cmdidx != CMD_laddexpr), (linenr_T)0, (linenr_T)0, *eap->cmdlinep) > 0) { @@ -4431,7 +4430,7 @@ void ex_cexpr(exarg_T *eap) } else { EMSG(_("E777: String or List expected")); } - free_tv(tv); + tv_clear(&tv); } } From c8e63a8db84e9d9f7bd855085a87d93631504fc7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Sep 2016 02:25:24 +0300 Subject: [PATCH 0190/1671] eval: Move remaining get_tv_string* functions to eval/typval.c --- src/nvim/ascii.h | 8 +- src/nvim/charset.c | 8 +- src/nvim/edit.c | 204 +-- src/nvim/eval.c | 1280 ++++++++--------- src/nvim/eval/typval.c | 118 +- src/nvim/eval/typval.h | 1 + src/nvim/ex_cmds.c | 4 +- src/nvim/ex_docmd.c | 2 +- src/nvim/ex_getln.c | 31 +- src/nvim/fileio.c | 37 +- src/nvim/getchar.c | 159 +- src/nvim/globals.h | 1 + src/nvim/mbyte.c | 4 +- src/nvim/normal.c | 71 +- src/nvim/ops.c | 60 +- src/nvim/option.c | 19 +- src/nvim/os/fs.c | 4 +- src/nvim/regexp.c | 15 +- src/nvim/screen.c | 2 +- src/nvim/spell.c | 82 +- src/nvim/spellfile.c | 54 +- src/nvim/strings.c | 11 +- src/nvim/undo.c | 4 +- test/functional/eval/input_spec.lua | 38 + test/functional/eval/match_functions_spec.lua | 15 + .../ex_cmds/dict_notifications_spec.lua | 67 +- 26 files changed, 1244 insertions(+), 1055 deletions(-) create mode 100644 test/functional/eval/input_spec.lua diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h index 31851a84e6..37b83efb61 100644 --- a/src/nvim/ascii.h +++ b/src/nvim/ascii.h @@ -20,15 +20,13 @@ #define BS '\010' #define TAB '\011' #define NL '\012' -#define NL_STR (char_u *)"\012" +#define NL_STR "\012" #define FF '\014' #define CAR '\015' /* CR is used by Mac OS X */ #define ESC '\033' -#define ESC_STR (char_u *)"\033" -#define ESC_STR_nc "\033" +#define ESC_STR "\033" #define DEL 0x7f -#define DEL_STR (char_u *)"\177" -#define CSI 0x9b /* Control Sequence Introducer */ +#define CSI 0x9b // Control Sequence Introducer #define CSI_STR "\233" #define DCS 0x90 /* Device Control String */ #define STERM 0x9c /* String Terminator */ diff --git a/src/nvim/charset.c b/src/nvim/charset.c index cb6a9fa43c..6c22108853 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -90,7 +90,6 @@ int buf_init_chartab(buf_T *buf, int global) { int c; int c2; - char_u *p; int i; bool tilde; bool do_isalpha; @@ -144,7 +143,8 @@ int buf_init_chartab(buf_T *buf, int global) // Walk through the 'isident', 'iskeyword', 'isfname' and 'isprint' // options Each option is a list of characters, character numbers or // ranges, separated by commas, e.g.: "200-210,x,#-178,-" - for (i = global ? 0 : 3; i <= 3; ++i) { + for (i = global ? 0 : 3; i <= 3; i++) { + const char_u *p; if (i == 0) { // first round: 'isident' p = p_isi; @@ -169,7 +169,7 @@ int buf_init_chartab(buf_T *buf, int global) } if (ascii_isdigit(*p)) { - c = getdigits_int(&p); + c = getdigits_int((char_u **)&p); } else { c = mb_ptr2char_adv(&p); } @@ -179,7 +179,7 @@ int buf_init_chartab(buf_T *buf, int global) ++p; if (ascii_isdigit(*p)) { - c2 = getdigits_int(&p); + c2 = getdigits_int((char_u **)&p); } else { c2 = mb_ptr2char_adv(&p); } diff --git a/src/nvim/edit.c b/src/nvim/edit.c index b396251051..da09aed3dc 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -1423,7 +1423,7 @@ static void ins_ctrl_v(void) edit_putchar('^', TRUE); did_putchar = TRUE; } - AppendToRedobuff((char_u *)CTRL_V_STR); /* CTRL-V */ + AppendToRedobuff(CTRL_V_STR); add_to_showcmd_c(Ctrl_V); @@ -1977,7 +1977,6 @@ static bool ins_compl_accept_char(int c) */ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int dir, int flags) { - char_u *p; int i, c; int actual_len; /* Take multi-byte characters */ int actual_compl_length; /* into account. */ @@ -1987,11 +1986,11 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int int was_letter = FALSE; if (p_ic && curbuf->b_p_inf && len > 0) { - /* Infer case of completed part. */ + // Infer case of completed part. - /* Find actual length of completion. */ + // Find actual length of completion. if (has_mbyte) { - p = str; + const char_u *p = str; actual_len = 0; while (*p != NUL) { mb_ptr_adv(p); @@ -2002,7 +2001,7 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int /* Find actual length of original text. */ if (has_mbyte) { - p = compl_orig_text; + const char_u *p = compl_orig_text; actual_compl_length = 0; while (*p != NUL) { mb_ptr_adv(p); @@ -2018,27 +2017,35 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int /* Allocate wide character array for the completion and fill it. */ wca = xmalloc(actual_len * sizeof(*wca)); - p = str; - for (i = 0; i < actual_len; ++i) - if (has_mbyte) - wca[i] = mb_ptr2char_adv(&p); - else - wca[i] = *(p++); + { + const char_u *p = str; + for (i = 0; i < actual_len; i++) { + if (has_mbyte) { + wca[i] = mb_ptr2char_adv(&p); + } else { + wca[i] = *(p++); + } + } + } - /* Rule 1: Were any chars converted to lower? */ - p = compl_orig_text; - for (i = 0; i < min_len; ++i) { - if (has_mbyte) - c = mb_ptr2char_adv(&p); - else - c = *(p++); - if (vim_islower(c)) { - has_lower = TRUE; - if (vim_isupper(wca[i])) { - /* Rule 1 is satisfied. */ - for (i = actual_compl_length; i < actual_len; ++i) - wca[i] = vim_tolower(wca[i]); - break; + // Rule 1: Were any chars converted to lower? + { + const char_u *p = compl_orig_text; + for (i = 0; i < min_len; i++) { + if (has_mbyte) { + c = mb_ptr2char_adv(&p); + } else { + c = *(p++); + } + if (vim_islower(c)) { + has_lower = true; + if (vim_isupper(wca[i])) { + // Rule 1 is satisfied. + for (i = actual_compl_length; i < actual_len; i++) { + wca[i] = vim_tolower(wca[i]); + } + break; + } } } } @@ -2048,49 +2055,57 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int * upper case. */ if (!has_lower) { - p = compl_orig_text; - for (i = 0; i < min_len; ++i) { - if (has_mbyte) + const char_u *p = compl_orig_text; + for (i = 0; i < min_len; i++) { + if (has_mbyte) { c = mb_ptr2char_adv(&p); - else + } else { c = *(p++); + } if (was_letter && vim_isupper(c) && vim_islower(wca[i])) { - /* Rule 2 is satisfied. */ - for (i = actual_compl_length; i < actual_len; ++i) + // Rule 2 is satisfied. + for (i = actual_compl_length; i < actual_len; i++) { wca[i] = vim_toupper(wca[i]); + } break; } was_letter = vim_islower(c) || vim_isupper(c); } } - /* Copy the original case of the part we typed. */ - p = compl_orig_text; - for (i = 0; i < min_len; ++i) { - if (has_mbyte) - c = mb_ptr2char_adv(&p); - else - c = *(p++); - if (vim_islower(c)) - wca[i] = vim_tolower(wca[i]); - else if (vim_isupper(c)) - wca[i] = vim_toupper(wca[i]); + // Copy the original case of the part we typed. + { + const char_u *p = compl_orig_text; + for (i = 0; i < min_len; i++) { + if (has_mbyte) { + c = mb_ptr2char_adv(&p); + } else { + c = *(p++); + } + if (vim_islower(c)) { + wca[i] = vim_tolower(wca[i]); + } else if (vim_isupper(c)) { + wca[i] = vim_toupper(wca[i]); + } + } } - /* - * Generate encoding specific output from wide character array. - * Multi-byte characters can occupy up to five bytes more than - * ASCII characters, and we also need one byte for NUL, so stay - * six bytes away from the edge of IObuff. - */ - p = IObuff; - i = 0; - while (i < actual_len && (p - IObuff + 6) < IOSIZE) - if (has_mbyte) - p += (*mb_char2bytes)(wca[i++], p); - else - *(p++) = wca[i++]; - *p = NUL; + // Generate encoding specific output from wide character array. + // Multi-byte characters can occupy up to five bytes more than + // ASCII characters, and we also need one byte for NUL, so stay + // six bytes away from the edge of IObuff. + { + char_u *p = IObuff; + i = 0; + while (i < actual_len && (p - IObuff + 6) < IOSIZE) { + if (has_mbyte) { + p += (*mb_char2bytes)(wca[i++], p); + } else { + *(p++) = wca[i++]; + } + } + *p = NUL; + } xfree(wca); @@ -3594,7 +3609,7 @@ int ins_compl_add_tv(typval_T *const tv, const Direction dir) adup = (bool)tv_dict_get_number(tv->vval.v_dict, "dup"); aempty = (bool)tv_dict_get_number(tv->vval.v_dict, "empty"); } else { - word = (const char *)get_tv_string_chk(tv); + word = (const char *)tv_get_string_chk(tv); memset(cptext, 0, sizeof(cptext)); } if (word == NULL || (!aempty && *word == NUL)) { @@ -5785,15 +5800,16 @@ comp_textwidth ( */ static void redo_literal(int c) { - char_u buf[10]; + char buf[10]; - /* Only digits need special treatment. Translate them into a string of - * three digits. */ + // Only digits need special treatment. Translate them into a string of + // three digits. if (ascii_isdigit(c)) { - vim_snprintf((char *)buf, sizeof(buf), "%03d", c); + vim_snprintf(buf, sizeof(buf), "%03d", c); AppendToRedobuff(buf); - } else + } else { AppendCharToRedobuff(c); + } } // start_arrow() is called when an arrow key is used in insert mode. @@ -5822,8 +5838,8 @@ static void start_arrow_common(pos_T *end_insert_pos, bool end_change) { if (!arrow_used && end_change) { // something has been inserted AppendToRedobuff(ESC_STR); - stop_insert(end_insert_pos, FALSE, FALSE); - arrow_used = TRUE; /* this means we stopped the current insert */ + stop_insert(end_insert_pos, false, false); + arrow_used = true; // This means we stopped the current insert. } check_spell_redraw(); } @@ -5880,7 +5896,7 @@ int stop_arrow(void) vr_lines_changed = 1; } ResetRedobuff(); - AppendToRedobuff((char_u *)"1i"); /* pretend we start an insertion */ + AppendToRedobuff("1i"); // Pretend we start an insertion. new_insert_skip = 2; } else if (ins_need_undo) { if (u_save_cursor() == OK) @@ -6345,12 +6361,13 @@ stuff_inserted ( } do { - stuffReadbuff(ptr); - /* a trailing "0" is inserted as "048", "^" as "^" */ - if (last) - stuffReadbuff((char_u *)(last == '0' - ? "\026\060\064\070" - : "\026^")); + stuffReadbuff((const char *)ptr); + // A trailing "0" is inserted as "048", "^" as "^". + if (last) { + stuffReadbuff((last == '0' + ? "\026\060\064\070" + : "\026^")); + } } while (--count > 0); if (last) @@ -7143,13 +7160,12 @@ static bool ins_esc(long *count, int cmdchar, bool nomove) disabled_redraw = false; } if (!arrow_used) { - /* - * Don't append the ESC for "r" and "grx". - * When 'insertmode' is set only CTRL-L stops Insert mode. Needed for - * when "count" is non-zero. - */ - if (cmdchar != 'r' && cmdchar != 'v') - AppendToRedobuff(p_im ? (char_u *)"\014" : ESC_STR); + // Don't append the ESC for "r" and "grx". + // When 'insertmode' is set only CTRL-L stops Insert mode. Needed for + // when "count" is non-zero. + if (cmdchar != 'r' && cmdchar != 'v') { + AppendToRedobuff(p_im ? "\014" : ESC_STR); + } /* * Repeating insert may take a long time. Check for @@ -7303,7 +7319,8 @@ static bool ins_start_select(int c) // Execute the key in (insert) Select mode. stuffcharReadbuff(Ctrl_O); if (mod_mask) { - char_u buf[4] = { K_SPECIAL, KS_MODIFIER, mod_mask, NUL }; + const char buf[] = { (char)K_SPECIAL, (char)KS_MODIFIER, + (char)(uint8_t)mod_mask, NUL }; stuffReadbuff(buf); } stuffcharReadbuff(c); @@ -8111,11 +8128,11 @@ static bool ins_tab(void) return true; } - did_ai = FALSE; - did_si = FALSE; - can_si = FALSE; - can_si_back = FALSE; - AppendToRedobuff((char_u *)"\t"); + did_ai = false; + did_si = false; + can_si = false; + can_si_back = false; + AppendToRedobuff("\t"); if (p_sta && ind) { // insert tab in indent, use "shiftwidth" temp = get_sw_value(curbuf); @@ -8380,8 +8397,8 @@ static int ins_digraph(void) edit_unputchar(); } if (cc != ESC) { - AppendToRedobuff((char_u *)CTRL_V_STR); - c = getdigraph(c, cc, TRUE); + AppendToRedobuff(CTRL_V_STR); + c = getdigraph(c, cc, true); clear_showcmd(); return c; } @@ -8443,12 +8460,13 @@ static int ins_ctrl_ey(int tc) if (c != NUL) { long tw_save; - /* The character must be taken literally, insert like it - * was typed after a CTRL-V, and pretend 'textwidth' - * wasn't set. Digits, 'o' and 'x' are special after a - * CTRL-V, don't use it for these. */ - if (c < 256 && !isalnum(c)) - AppendToRedobuff((char_u *)CTRL_V_STR); /* CTRL-V */ + // The character must be taken literally, insert like it + // was typed after a CTRL-V, and pretend 'textwidth' + // wasn't set. Digits, 'o' and 'x' are special after a + // CTRL-V, don't use it for these. + if (c < 256 && !isalnum(c)) { + AppendToRedobuff(CTRL_V_STR); + } tw_save = curbuf->b_p_tw; curbuf->b_p_tw = -1; insert_special(c, TRUE, FALSE); diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 65bb90fc15..3a0075e48a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -173,7 +173,6 @@ static char *e_funcref = N_("E718: Funcref required"); static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary"); static char *e_nofunc = N_("E130: Unknown function: %s"); static char *e_illvar = N_("E461: Illegal variable name: %s"); -static char *e_float_as_string = N_("E806: using Float as a String"); static const char *e_readonlyvar = N_( "E46: Cannot change read-only variable \"%.*s\""); @@ -1852,7 +1851,6 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, const char_u *const op) FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT { - char_u *name; char_u *arg_end = NULL; int len; int opt_flags; @@ -1864,7 +1862,7 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, if (*arg == '$') { // Find the end of the name. arg++; - name = arg; + char *name = (char *)arg; len = get_env_len((const char_u **)&arg); if (len == 0) { EMSG2(_(e_invarg2), name - 1); @@ -1875,26 +1873,28 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, && vim_strchr(endchars, *skipwhite(arg)) == NULL) { EMSG(_(e_letunexp)); } else if (!check_secure()) { - const char_u c1 = name[len]; + const char c1 = name[len]; name[len] = NUL; - char_u *p = get_tv_string_chk(tv); + const char *p = tv_get_string_chk(tv); if (p != NULL && op != NULL && *op == '.') { - char *s = vim_getenv((char *)name); + char *s = vim_getenv(name); if (s != NULL) { - p = tofree = concat_str((char_u *)s, p); + tofree = concat_str((const char_u *)s, (const char_u *)p); + p = (const char *)tofree; xfree(s); } } if (p != NULL) { - vim_setenv((char *)name, (char *)p); - if (STRICMP(name, "HOME") == 0) + vim_setenv(name, p); + if (STRICMP(name, "HOME") == 0) { init_homedir(); - else if (didset_vim && STRICMP(name, "VIM") == 0) - didset_vim = FALSE; - else if (didset_vimruntime - && STRICMP(name, "VIMRUNTIME") == 0) - didset_vimruntime = FALSE; + } else if (didset_vim && STRICMP(name, "VIM") == 0) { + didset_vim = false; + } else if (didset_vimruntime + && STRICMP(name, "VIMRUNTIME") == 0) { + didset_vimruntime = false; + } arg_end = arg; } name[len] = c1; @@ -1914,16 +1914,16 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, } else { int opt_type; long numval; - char_u *stringval = NULL; - char_u *s; + char *stringval = NULL; const char c1 = *p; *p = NUL; varnumber_T n = tv_get_number(tv); - s = get_tv_string_chk(tv); // != NULL if number or string. + const char *s = tv_get_string_chk(tv); // != NULL if number or string. if (s != NULL && op != NULL && *op != '=') { - opt_type = get_option_value(arg, &numval, &stringval, opt_flags); + opt_type = get_option_value(arg, &numval, (char_u **)&stringval, + opt_flags); if ((opt_type == 1 && *op == '.') || (opt_type == 0 && *op != '.')) { EMSG2(_(e_letwrong), op); @@ -1935,44 +1935,45 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, n = numval - n; } } else if (opt_type == 0 && stringval != NULL) { // string - s = concat_str(stringval, s); - xfree(stringval); - stringval = s; + char *const oldstringval = stringval; + stringval = (char *)concat_str((const char_u *)stringval, + (const char_u *)s); + xfree(oldstringval); + s = stringval; } } } if (s != NULL) { - set_option_value((const char *)arg, n, (char *)s, opt_flags); + set_option_value((const char *)arg, n, s, opt_flags); arg_end = (char_u *)p; } *p = c1; xfree(stringval); } - } - /* - * ":let @r = expr": Set register contents. - */ - else if (*arg == '@') { - ++arg; - if (op != NULL && (*op == '+' || *op == '-')) - EMSG2(_(e_letwrong), op); - else if (endchars != NULL - && vim_strchr(endchars, *skipwhite(arg + 1)) == NULL) - EMSG(_(e_letunexp)); - else { - char_u *ptofree = NULL; + // ":let @r = expr": Set register contents. + } else if (*arg == '@') { + arg++; + if (op != NULL && (*op == '+' || *op == '-')) { + emsgf(_(e_letwrong), op); + } else if (endchars != NULL + && vim_strchr(endchars, *skipwhite(arg + 1)) == NULL) { + emsgf(_(e_letunexp)); + } else { char_u *s; - char_u *p = get_tv_string_chk(tv); + char_u *ptofree = NULL; + const char *p = tv_get_string_chk(tv); if (p != NULL && op != NULL && *op == '.') { s = get_reg_contents(*arg == '@' ? '"' : *arg, kGRegExprSrc); if (s != NULL) { - p = ptofree = concat_str(s, p); + ptofree = concat_str(s, (const char_u *)p); + p = (const char *)ptofree; xfree(s); } } if (p != NULL) { - write_reg_contents(*arg == '@' ? '"' : *arg, p, STRLEN(p), false); + write_reg_contents(*arg == '@' ? '"' : *arg, + (const char_u *)p, STRLEN(p), false); arg_end = arg + 1; } xfree(ptofree); @@ -2129,13 +2130,14 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } else { /* Get the index [expr] or the first index [expr: ]. */ p = skipwhite(p + 1); - if (*p == ':') - empty1 = TRUE; - else { - empty1 = FALSE; - if (eval1(&p, &var1, TRUE) == FAIL) /* recursive! */ + if (*p == ':') { + empty1 = true; + } else { + empty1 = false; + if (eval1(&p, &var1, true) == FAIL) { // Recursive! return NULL; - if (get_tv_string_chk(&var1) == NULL) { + } + if (!tv_check_str(&var1)) { // Not a number or string. tv_clear(&var1); return NULL; @@ -2174,7 +2176,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } return NULL; } - if (get_tv_string_chk(&var2) == NULL) { + if (!tv_check_str(&var2)) { // Not a number or string. if (!empty1) { tv_clear(&var1); @@ -3810,16 +3812,15 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) break; if ((op != '+' || rettv->v_type != VAR_LIST) - && (op == '.' || rettv->v_type != VAR_FLOAT) - ) { - /* For "list + ...", an illegal use of the first operand as - * a number cannot be determined before evaluating the 2nd - * operand: if this is also a list, all is ok. - * For "something . ...", "something - ..." or "non-list + ...", - * 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 (evaluate && get_tv_string_chk(rettv) == NULL) { + && (op == '.' || rettv->v_type != VAR_FLOAT)) { + // For "list + ...", an illegal use of the first operand as + // a number cannot be determined before evaluating the 2nd + // operand: if this is also a list, all is ok. + // For "something . ...", "something - ..." or "non-list + ...", + // 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 (evaluate && !tv_check_str(rettv)) { tv_clear(rettv); return FAIL; } @@ -3840,10 +3841,10 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) */ if (op == '.') { char buf1[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; + char buf2[NUMBUFLEN]; // s1 already checked const char *const s1 = tv_get_string_buf(rettv, buf1); - const char *const s2 = (const char *)get_tv_string_buf_chk(&var2, buf2); + const char *const s2 = tv_get_string_buf_chk(&var2, buf2); if (s2 == NULL) { // Type error? tv_clear(rettv); tv_clear(&var2); @@ -4411,7 +4412,7 @@ eval_index ( empty1 = true; } else if (eval1(arg, &var1, evaluate) == FAIL) { // Recursive! return FAIL; - } else if (evaluate && get_tv_string_chk(&var1) == NULL) { + } else if (evaluate && !tv_check_str(&var1)) { // Not a number or string. tv_clear(&var1); return FAIL; @@ -4430,7 +4431,7 @@ eval_index ( tv_clear(&var1); } return FAIL; - } else if (evaluate && get_tv_string_chk(&var2) == NULL) { + } else if (evaluate && !tv_check_str(&var2)) { // Not a number or string. if (!empty1) { tv_clear(&var1); @@ -4566,7 +4567,7 @@ eval_index ( } if (len == -1) { - key = get_tv_string_chk(&var1); + key = (char_u *)tv_get_string_chk(&var1); if (key == NULL) { tv_clear(&var1); return FAIL; @@ -5570,7 +5571,7 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) char_u *key = NULL; dictitem_T *item; char_u *start = skipwhite(*arg + 1); - char_u buf[NUMBUFLEN]; + char buf[NUMBUFLEN]; /* * First check if it's not a curly-braces thing: {expr}. @@ -5602,9 +5603,9 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) goto failret; } if (evaluate) { - key = get_tv_string_buf_chk(&tvkey, buf); + key = (char_u *)tv_get_string_buf_chk(&tvkey, buf); if (key == NULL) { - // "key" is NULL when get_tv_string_buf_chk() gave an errmsg + // "key" is NULL when tv_get_string_buf_chk() gave an errmsg tv_clear(&tvkey); goto failret; } @@ -6598,7 +6599,6 @@ static void f_api_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) { long lnum; - char_u *line; list_T *l = NULL; listitem_T *li = NULL; typval_T *tv; @@ -6622,21 +6622,23 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) li = l->lv_first; } for (;; ) { - if (l == NULL) - tv = &argvars[1]; /* append a string */ - else if (li == NULL) - break; /* end of list */ - else - tv = &li->li_tv; /* append item from list */ - line = get_tv_string_chk(tv); - if (line == NULL) { /* type error */ - rettv->vval.v_number = 1; /* Failed */ + if (l == NULL) { + tv = &argvars[1]; // Append a string. + } else if (li == NULL) { + break; // End of list. + } else { + tv = &li->li_tv; // Append item from list. + } + const char *const line = tv_get_string_chk(tv); + if (line == NULL) { // Type error. + rettv->vval.v_number = 1; // Failed. break; } - ml_append(lnum + added, line, (colnr_T)0, FALSE); - ++added; - if (l == NULL) + ml_append(lnum + added, (char_u *)line, (colnr_T)0, false); + added++; + if (l == NULL) { break; + } li = li->li_next; } @@ -6802,7 +6804,7 @@ static void f_assert_exception(typval_T *argvars, typval_T *rettv, FunPtr fptr) { garray_T ga; - char *error = (char *)get_tv_string_chk(&argvars[0]); + const char *const error = tv_get_string_chk(&argvars[0]); if (vimvars[VV_EXCEPTION].vv_str == NULL) { prepare_assert_error(&ga); ga_concat(&ga, (char_u *)"v:exception is not set"); @@ -6821,22 +6823,22 @@ static void f_assert_exception(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "assert_fails(cmd [, error])" function static void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *cmd = get_tv_string_chk(&argvars[0]); + const char *const cmd = tv_get_string_chk(&argvars[0]); garray_T ga; called_emsg = false; suppress_errthrow = true; emsg_silent = true; - do_cmdline_cmd((char *)cmd); + do_cmdline_cmd(cmd); if (!called_emsg) { prepare_assert_error(&ga); - ga_concat(&ga, (char_u *)"command did not fail: "); - ga_concat(&ga, cmd); + ga_concat(&ga, (const char_u *)"command did not fail: "); + ga_concat(&ga, (const char_u *)cmd); assert_error(&ga); ga_clear(&ga); } else if (argvars[1].v_type != VAR_UNKNOWN) { - char_u buf[NUMBUFLEN]; - char *error = (char *)get_tv_string_buf_chk(&argvars[1], buf); + char buf[NUMBUFLEN]; + const char *const error = tv_get_string_buf_chk(&argvars[1], buf); if (error == NULL || strstr((char *)vimvars[VV_ERRMSG].vv_str, error) == NULL) { @@ -6911,14 +6913,15 @@ static void f_assert_false(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void assert_match_common(typval_T *argvars, assert_type_T atype) { - char_u buf1[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; - char_u *pat = get_tv_string_buf_chk(&argvars[0], buf1); - char_u *text = get_tv_string_buf_chk(&argvars[1], buf2); + char buf1[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + const char *const pat = tv_get_string_buf_chk(&argvars[0], buf1); + const char *const text = tv_get_string_buf_chk(&argvars[1], buf2); if (pat == NULL || text == NULL) { EMSG(_(e_invarg)); - } else if (pattern_match(pat, text, false) != (atype == ASSERT_MATCH)) { + } else if (pattern_match((char_u *)pat, (char_u *)text, false) + != (atype == ASSERT_MATCH)) { garray_T ga; prepare_assert_error(&ga); fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1], atype); @@ -7107,7 +7110,6 @@ static void f_bufname(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { bool error = false; - char_u *name; rettv->vval.v_number = -1; if (!tv_check_str_or_nr(&argvars[0])) { @@ -7119,13 +7121,14 @@ static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) // If the buffer isn't found and the second argument is not zero create a // new buffer. + const char *name; if (buf == NULL && argvars[1].v_type != VAR_UNKNOWN && tv_get_number_chk(&argvars[1], &error) != 0 && !error - && (name = get_tv_string_chk(&argvars[0])) != NULL + && (name = tv_get_string_chk(&argvars[0])) != NULL && !error) { - buf = buflist_new(name, NULL, (linenr_T)1, 0); + buf = buflist_new((char_u *)name, NULL, 1, 0); } if (buf != NULL) { @@ -7190,24 +7193,23 @@ static void f_byte2line(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void byteidx(typval_T *argvars, typval_T *rettv, int comp) { - char_u *t; - char_u *str; - long idx; - - str = get_tv_string_chk(&argvars[0]); - idx = tv_get_number_chk(&argvars[1], NULL); + const char *const str = tv_get_string_chk(&argvars[0]); + varnumber_T idx = tv_get_number_chk(&argvars[1], NULL); rettv->vval.v_number = -1; - if (str == NULL || idx < 0) + if (str == NULL || idx < 0) { return; + } - t = str; + const char *t = str; for (; idx > 0; idx--) { - if (*t == NUL) /* EOL reached */ + if (*t == NUL) { // EOL reached. return; - if (enc_utf8 && comp) - t += utf_ptr2len(t); - else - t += (*mb_ptr2len)(t); + } + if (enc_utf8 && comp) { + t += utf_ptr2len((const char_u *)t); + } else { + t += (*mb_ptr2len)((const char_u *)t); + } } rettv->vval.v_number = (varnumber_T)(t - str); } @@ -7444,47 +7446,51 @@ static void f_complete_check(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *message; - char_u *buttons = NULL; - char_u buf[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; + char buf[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + const char *message; + const char *buttons = NULL; int def = 1; int type = VIM_GENERIC; - char_u *typestr; + const char *typestr; bool error = false; - message = get_tv_string_chk(&argvars[0]); - if (message == NULL) - error = TRUE; + message = tv_get_string_chk(&argvars[0]); + if (message == NULL) { + error = true; + } if (argvars[1].v_type != VAR_UNKNOWN) { - buttons = get_tv_string_buf_chk(&argvars[1], buf); - if (buttons == NULL) - error = TRUE; + buttons = tv_get_string_buf_chk(&argvars[1], buf); + if (buttons == NULL) { + error = true; + } if (argvars[2].v_type != VAR_UNKNOWN) { def = tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { - typestr = get_tv_string_buf_chk(&argvars[3], buf2); - if (typestr == NULL) - error = TRUE; - else { + typestr = tv_get_string_buf_chk(&argvars[3], buf2); + if (typestr == NULL) { + error = true; + } else { switch (TOUPPER_ASC(*typestr)) { - case 'E': type = VIM_ERROR; break; - case 'Q': type = VIM_QUESTION; break; - case 'I': type = VIM_INFO; break; - case 'W': type = VIM_WARNING; break; - case 'G': type = VIM_GENERIC; break; + case 'E': type = VIM_ERROR; break; + case 'Q': type = VIM_QUESTION; break; + case 'I': type = VIM_INFO; break; + case 'W': type = VIM_WARNING; break; + case 'G': type = VIM_GENERIC; break; } } } } } - if (buttons == NULL || *buttons == NUL) - buttons = (char_u *)_("&Ok"); + if (buttons == NULL || *buttons == NUL) { + buttons = _("&Ok"); + } - if (!error) - rettv->vval.v_number = do_dialog(type, NULL, message, buttons, - def, NULL, FALSE); + if (!error) { + rettv->vval.v_number = do_dialog( + type, NULL, (char_u *)message, (char_u *)buttons, def, NULL, false); + } } /* @@ -7708,32 +7714,29 @@ static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (argvars[0].v_type != VAR_DICT) { - EMSG2(e_invarg2, "dict"); + emsgf(_(e_invarg2), "dict"); return; } if (argvars[1].v_type != VAR_STRING && argvars[1].v_type != VAR_NUMBER) { - EMSG2(e_invarg2, "key"); + emsgf(_(e_invarg2), "key"); return; } - char *key_pattern = (char *)get_tv_string_chk(argvars + 1); - assert(key_pattern); - const size_t key_len = STRLEN(argvars[1].vval.v_string); - - if (key_len == 0) { - EMSG(_(e_emptykey)); + const char *const key_pattern = tv_get_string_chk(argvars + 1); + if (key_pattern == NULL) { return; } + const size_t key_pattern_len = strlen(key_pattern); Callback callback; if (!callback_from_typval(&callback, &argvars[2])) { - EMSG2(e_invarg2, "funcref"); + emsgf(_(e_invarg2), "funcref"); return; } DictWatcher *watcher = xmalloc(sizeof(DictWatcher)); - watcher->key_pattern = xmemdupz(key_pattern, key_len); + watcher->key_pattern = xmemdupz(key_pattern, key_pattern_len); watcher->callback = callback; watcher->busy = false; QUEUE_INSERT_TAIL(&argvars[0].vval.v_dict->watchers, &watcher->node); @@ -7747,26 +7750,17 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (argvars[0].v_type != VAR_DICT) { - EMSG2(e_invarg2, "dict"); - return; - } - - if (argvars[1].v_type != VAR_STRING && argvars[1].v_type != VAR_NUMBER) { - EMSG2(e_invarg2, "key"); + emsgf(_(e_invarg2), "dict"); return; } if (argvars[2].v_type != VAR_FUNC && argvars[2].v_type != VAR_STRING) { - EMSG2(e_invarg2, "funcref"); + emsgf(_(e_invarg2), "funcref"); return; } - char *key_pattern = (char *)get_tv_string_chk(argvars + 1); - assert(key_pattern); - const size_t key_len = STRLEN(argvars[1].vval.v_string); - - if (key_len == 0) { - EMSG(_(e_emptykey)); + const char *const key_pattern = tv_get_string_chk(argvars + 1); + if (key_pattern == NULL) { return; } @@ -7924,16 +7918,15 @@ static void f_escape(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_eval(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *s; + const char *s = tv_get_string_chk(&argvars[0]); + if (s != NULL) { + s = (const char *)skipwhite((const char_u *)s); + } - s = get_tv_string_chk(&argvars[0]); - if (s != NULL) - s = skipwhite(s); - - char_u *p = s; - if (s == NULL || eval1(&s, rettv, TRUE) == FAIL) { - if (p != NULL && !aborting()) { - EMSG2(_(e_invexpr2), p); + const char *const expr_start = s; + if (s == NULL || eval1((char_u **)&s, rettv, true) == FAIL) { + if (expr_start != NULL && !aborting()) { + EMSG2(_(e_invexpr2), expr_start); } need_clr_eos = FALSE; rettv->v_type = VAR_NUMBER; @@ -7965,75 +7958,74 @@ static void f_executable(typval_T *argvars, typval_T *rettv, FunPtr fptr) && os_can_exe((const char_u *)name, NULL, false))); } -static char_u * get_list_line(int c, void *cookie, int indent) +static char_u *get_list_line(int c, void *cookie, int indent) { - listitem_T **p = (listitem_T **)cookie; - listitem_T *item = *p; - char_u buf[NUMBUFLEN]; - char_u *s; + const listitem_T **const p = (const listitem_T **)cookie; + const listitem_T *item = *p; if (item == NULL) { return NULL; } - s = get_tv_string_buf_chk(&item->li_tv, buf); + char buf[NUMBUFLEN]; + const char *const s = tv_get_string_buf_chk(&item->li_tv, buf); *p = item->li_next; - return s == NULL ? NULL : vim_strsave(s); + return (char_u *)(s == NULL ? NULL : xstrdup(s)); } // "execute(command)" function static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int save_msg_silent = msg_silent; - int save_emsg_silent = emsg_silent; - bool save_emsg_noredir = emsg_noredir; - garray_T *save_capture_ga = capture_ga; + const int save_msg_silent = msg_silent; + const int save_emsg_silent = emsg_silent; + const bool save_emsg_noredir = emsg_noredir; + garray_T *const save_capture_ga = capture_ga; - if (check_secure()) { + if (check_secure()) { + return; + } + + if (argvars[1].v_type != VAR_UNKNOWN) { + char buf[NUMBUFLEN]; + const char *const s = tv_get_string_buf_chk(&argvars[1], buf); + + if (s == NULL) { return; } - - if (argvars[1].v_type != VAR_UNKNOWN) { - char_u buf[NUMBUFLEN]; - char_u *s = get_tv_string_buf_chk(&argvars[1], buf); - - if (s == NULL) { - return; - } - if (STRNCMP(s, "silent", 6) == 0) { - msg_silent++; - } - if (STRCMP(s, "silent!") == 0) { - emsg_silent = true; - emsg_noredir = true; - } - } else { + if (strncmp(s, S_LEN("silent")) == 0) { msg_silent++; } - - garray_T capture_local; - ga_init(&capture_local, (int)sizeof(char), 80); - capture_ga = &capture_local; - - if (argvars[0].v_type != VAR_LIST) { - do_cmdline_cmd(tv_get_string(&argvars[0])); - } else if (argvars[0].vval.v_list != NULL) { - list_T *const list = argvars[0].vval.v_list; - list->lv_refcount++; - listitem_T *const item = list->lv_first; - do_cmdline(NULL, get_list_line, (void *)&item, - DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); - list->lv_refcount--; + if (strcmp(s, "silent!") == 0) { + emsg_silent = true; + emsg_noredir = true; } - msg_silent = save_msg_silent; - emsg_silent = save_emsg_silent; - emsg_noredir = save_emsg_noredir; + } else { + msg_silent++; + } - ga_append(capture_ga, NUL); - rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strsave(capture_ga->ga_data); - ga_clear(capture_ga); + garray_T capture_local; + ga_init(&capture_local, (int)sizeof(char), 80); + capture_ga = &capture_local; - capture_ga = save_capture_ga; + if (argvars[0].v_type != VAR_LIST) { + do_cmdline_cmd(tv_get_string(&argvars[0])); + } else if (argvars[0].vval.v_list != NULL) { + list_T *const list = argvars[0].vval.v_list; + list->lv_refcount++; + listitem_T *const item = list->lv_first; + do_cmdline(NULL, get_list_line, (void *)&item, + DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); + list->lv_refcount--; + } + msg_silent = save_msg_silent; + emsg_silent = save_emsg_silent; + emsg_noredir = save_emsg_noredir; + + ga_append(capture_ga, NUL); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strsave(capture_ga->ga_data); + ga_clear(capture_ga); + + capture_ga = save_capture_ga; } /// "exepath()" function @@ -8231,7 +8223,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[2].v_type != VAR_UNKNOWN) { const char *const av[] = { "keep", "force", "error" }; - action = (const char *)get_tv_string_chk(&argvars[2]); + action = tv_get_string_chk(&argvars[2]); if (action == NULL) { return; // Type error; error message already given. } @@ -8300,10 +8292,8 @@ static void f_filewritable(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) { - char_u *fresult = NULL; - char_u *path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; - char_u *p; - char_u pathbuf[NUMBUFLEN]; + char_u *fresult = NULL; + char_u *path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; int count = 1; bool first = true; bool error = false; @@ -8313,13 +8303,15 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) const char *fname = tv_get_string(&argvars[0]); + char pathbuf[NUMBUFLEN]; if (argvars[1].v_type != VAR_UNKNOWN) { - p = get_tv_string_buf_chk(&argvars[1], pathbuf); - if (p == NULL) - error = TRUE; - else { - if (*p != NUL) - path = p; + const char *p = tv_get_string_buf_chk(&argvars[1], pathbuf); + if (p == NULL) { + error = true; + } else { + if (*p != NUL) { + path = (char_u *)p; + } if (argvars[2].v_type != VAR_UNKNOWN) { count = tv_get_number_chk(&argvars[2], &error); @@ -8473,8 +8465,6 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) { typval_T rettv; typval_T argv[3]; - char_u buf[NUMBUFLEN]; - char_u *s; int retval = FAIL; int dummy; @@ -8482,7 +8472,7 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) argv[0] = vimvars[VV_KEY].vv_tv; argv[1] = vimvars[VV_VAL].vv_tv; if (expr->v_type == VAR_FUNC) { - s = expr->vval.v_string; + const char_u *const s = expr->vval.v_string; if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL, 0L, 0L, &dummy, true, NULL, NULL) == FAIL) { goto theend; @@ -8490,23 +8480,24 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) } else if (expr->v_type == VAR_PARTIAL) { partial_T *partial = expr->vval.v_partial; - s = partial_name(partial); + const char_u *const s = partial_name(partial); if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL, 0L, 0L, &dummy, true, partial, NULL) == FAIL) { goto theend; } } else { - s = get_tv_string_buf_chk(expr, buf); + char buf[NUMBUFLEN]; + const char *s = tv_get_string_buf_chk(expr, buf); if (s == NULL) { goto theend; } - s = skipwhite(s); - if (eval1(&s, &rettv, true) == FAIL) { + s = (const char *)skipwhite((const char_u *)s); + if (eval1((char_u **)&s, &rettv, true) == FAIL) { goto theend; } if (*s != NUL) { // check for trailing chars after expr - EMSG2(_(e_invexpr2), s); + emsgf(_(e_invexpr2), s); goto theend; } } @@ -8606,27 +8597,26 @@ static void f_fnameescape(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *fname; - char_u *mods; - size_t usedlen = 0; + char_u *fbuf = NULL; size_t len; - char_u *fbuf = NULL; - char_u buf[NUMBUFLEN]; - - fname = get_tv_string_chk(&argvars[0]); - mods = get_tv_string_buf_chk(&argvars[1], buf); - if (fname == NULL || mods == NULL) + char buf[NUMBUFLEN]; + const char *fname = tv_get_string_chk(&argvars[0]); + const char *const mods = tv_get_string_buf_chk(&argvars[1], buf); + if (fname == NULL || mods == NULL) { fname = NULL; - else { - len = STRLEN(fname); - (void)modify_fname(mods, &usedlen, &fname, &fbuf, &len); + } else { + len = strlen(fname); + size_t usedlen = 0; + (void)modify_fname((char_u *)mods, &usedlen, (char_u **)&fname, &fbuf, + &len); } rettv->v_type = VAR_STRING; - if (fname == NULL) + if (fname == NULL) { rettv->vval.v_string = NULL; - else - rettv->vval.v_string = vim_strnsave(fname, len); + } else { + rettv->vval.v_string = (char_u *)xmemdupz(fname, len); + } xfree(fbuf); } @@ -9257,7 +9247,7 @@ static void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) goto f_getbufvar_end; } - const char *varname = (const char *)get_tv_string_chk(&argvars[1]); + const char *varname = tv_get_string_chk(&argvars[1]); emsg_off++; buf_T *const buf = get_buf_tv(&argvars[0], false); @@ -9970,14 +9960,13 @@ static void f_getqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "getreg()" function static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *strregname; - int regname; + const char *strregname; int arg2 = false; bool return_list = false; bool error = false; if (argvars[0].v_type != VAR_UNKNOWN) { - strregname = get_tv_string_chk(&argvars[0]); + strregname = tv_get_string_chk(&argvars[0]); error = strregname == NULL; if (argvars[1].v_type != VAR_UNKNOWN) { arg2 = tv_get_number_chk(&argvars[1], &error); @@ -9986,16 +9975,17 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } else { - strregname = vimvars[VV_REG].vv_str; + strregname = (const char *)vimvars[VV_REG].vv_str; } if (error) { return; } - regname = (strregname == NULL ? '"' : *strregname); - if (regname == 0) + int regname = (uint8_t)(strregname == NULL ? '"' : *strregname); + if (regname == 0) { regname = '"'; + } if (return_list) { rettv->v_type = VAR_LIST; @@ -10016,23 +10006,24 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getregtype(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *strregname; - int regname; + const char *strregname; if (argvars[0].v_type != VAR_UNKNOWN) { - strregname = get_tv_string_chk(&argvars[0]); - if (strregname == NULL) { /* type error; errmsg already given */ + strregname = tv_get_string_chk(&argvars[0]); + if (strregname == NULL) { // Type error; errmsg already given. rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; return; } - } else - /* Default to v:register */ - strregname = vimvars[VV_REG].vv_str; + } else { + // Default to v:register. + strregname = (const char *)vimvars[VV_REG].vv_str; + } - regname = (strregname == NULL ? '"' : *strregname); - if (regname == 0) + int regname = (uint8_t)(strregname == NULL ? '"' : *strregname); + if (regname == 0) { regname = '"'; + } colnr_T reglen = 0; char buf[NUMBUFLEN + 2]; @@ -10108,7 +10099,7 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - const char *const varname = (const char *)get_tv_string_chk(&argvars[1]); + const char *const varname = tv_get_string_chk(&argvars[1]); tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); if (tp != NULL && varname != NULL) { // Set tp to be our tabpage, temporarily. Also set the window to the @@ -10307,8 +10298,7 @@ getwinvar ( tp = curtab; } win = find_win_by_nr(&argvars[off], tp); - const char *varname = (const char *)get_tv_string_chk( - &argvars[off + 1]); + const char *varname = tv_get_string_chk(&argvars[off + 1]); rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; @@ -10438,12 +10428,12 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - char_u buf1[NUMBUFLEN]; - char_u *file = get_tv_string_buf_chk(&argvars[1], buf1); + char buf1[NUMBUFLEN]; + const char *const file = tv_get_string_buf_chk(&argvars[1], buf1); if (file != NULL && !error) { garray_T ga; ga_init(&ga, (int)sizeof(char_u *), 10); - globpath((char_u *)tv_get_string(&argvars[0]), file, &ga, flags); + globpath((char_u *)tv_get_string(&argvars[0]), (char_u *)file, &ga, flags); if (rettv->v_type == VAR_STRING) { rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n"); @@ -10464,12 +10454,13 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) // "glob2regpat()" function static void f_glob2regpat(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *pat = get_tv_string_chk(&argvars[0]); // NULL on type error + const char *const pat = tv_get_string_chk(&argvars[0]); // NULL on type error rettv->v_type = VAR_STRING; - rettv->vval.v_string = (pat == NULL) - ? NULL - : file_pat_to_reg_pat(pat, NULL, NULL, false); + rettv->vval.v_string = ((pat == NULL) + ? NULL + : file_pat_to_reg_pat((char_u *)pat, NULL, NULL, + false)); } /// "has()" function @@ -10797,14 +10788,14 @@ static void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (check_restricted() || check_secure()) { return; } - char_u *str = get_tv_string_chk(&argvars[0]); // NULL on type error - histype = str != NULL ? get_histtype(str, STRLEN(str), false) : HIST_INVALID; + const char *str = tv_get_string_chk(&argvars[0]); // NULL on type error + histype = str != NULL ? get_histtype(str, strlen(str), false) : HIST_INVALID; if (histype != HIST_INVALID) { char buf[NUMBUFLEN]; - str = (char_u *)tv_get_string_buf(&argvars[1], buf); + str = tv_get_string_buf(&argvars[1], buf); if (*str != NUL) { init_history(); - add_to_history(histype, str, false, NUL); + add_to_history(histype, (char_u *)str, false, NUL); rettv->vval.v_number = true; return; } @@ -10817,22 +10808,20 @@ static void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_histdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int n; - char_u *str; - - str = get_tv_string_chk(&argvars[0]); // NULL on type error + const char *const str = tv_get_string_chk(&argvars[0]); // NULL on type error if (str == NULL) { n = 0; } else if (argvars[1].v_type == VAR_UNKNOWN) { // only one argument: clear entire history - n = clr_history(get_histtype(str, STRLEN(str), false)); + n = clr_history(get_histtype(str, strlen(str), false)); } else if (argvars[1].v_type == VAR_NUMBER) { // index given: remove that entry - n = del_history_idx(get_histtype(str, STRLEN(str), false), + n = del_history_idx(get_histtype(str, strlen(str), false), (int)tv_get_number(&argvars[1])); } else { // string given: remove all matching entries char buf[NUMBUFLEN]; - n = del_history_entry(get_histtype(str, STRLEN(str), false), + n = del_history_entry(get_histtype(str, strlen(str), false), (char_u *)tv_get_string_buf(&argvars[1], buf)); } rettv->vval.v_number = n; @@ -10845,13 +10834,12 @@ static void f_histget(typval_T *argvars, typval_T *rettv, FunPtr fptr) { HistoryType type; int idx; - char_u *str; - str = get_tv_string_chk(&argvars[0]); // NULL on type error + const char *const str = tv_get_string_chk(&argvars[0]); // NULL on type error if (str == NULL) { rettv->vval.v_string = NULL; } else { - type = get_histtype(str, STRLEN(str), false); + type = get_histtype(str, strlen(str), false); if (argvars[1].v_type == VAR_UNKNOWN) { idx = get_history_idx(type); } else { @@ -10870,9 +10858,9 @@ static void f_histnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int i; - char_u *history = get_tv_string_chk(&argvars[0]); + const char *const history = tv_get_string_chk(&argvars[0]); - i = history == NULL ? HIST_CMD - 1 : get_histtype(history, STRLEN(history), + i = history == NULL ? HIST_CMD - 1 : get_histtype(history, strlen(history), false); if (i != HIST_INVALID) { i = get_history_idx(i); @@ -11009,11 +10997,8 @@ static int inputsecret_flag = 0; */ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) { - char_u *prompt = get_tv_string_chk(&argvars[0]); - char_u *p = NULL; - int c; + const char *prompt = tv_get_string_chk(&argvars[0]); int cmd_silent_save = cmd_silent; - char_u *defstr = (char_u *)""; int xp_type = EXPAND_NOTHING; char_u *xp_arg = NULL; @@ -11022,49 +11007,45 @@ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) cmd_silent = FALSE; /* Want to see the prompt. */ if (prompt != NULL) { - /* Only the part of the message after the last NL is considered as - * prompt for the command line */ - p = vim_strrchr(prompt, '\n'); - if (p == NULL) + // Only the part of the message after the last NL is considered as + // prompt for the command line. + const char *p = strrchr(prompt, '\n'); + if (p == NULL) { p = prompt; - else { - ++p; - c = *p; - *p = NUL; + } else { + p++; msg_start(); msg_clr_eos(); - msg_puts_attr((const char *)prompt, echo_attr); + msg_puts_attr_len(prompt, p - prompt, echo_attr); msg_didout = false; msg_starthere(); - *p = c; } cmdline_row = msg_row; + const char *defstr = ""; if (argvars[1].v_type != VAR_UNKNOWN) { - char_u buf[NUMBUFLEN]; - defstr = get_tv_string_buf_chk(&argvars[1], buf); + char buf[NUMBUFLEN]; + defstr = tv_get_string_buf_chk(&argvars[1], buf); if (defstr != NULL) { stuffReadbuffSpec(defstr); } if (!inputdialog && argvars[2].v_type != VAR_UNKNOWN) { - char_u *xp_name; - int xp_namelen; - uint32_t argt; - - /* input() with a third argument: completion */ + // input() with a third argument: completion rettv->vval.v_string = NULL; - xp_name = get_tv_string_buf_chk(&argvars[2], buf); + const char *const xp_name = tv_get_string_buf_chk(&argvars[2], buf); if (xp_name == NULL) { return; } - xp_namelen = (int)STRLEN(xp_name); + const int xp_namelen = (int)strlen(xp_name); - if (parse_compl_arg(xp_name, xp_namelen, &xp_type, &argt, - &xp_arg) == FAIL) + uint32_t argt; + if (parse_compl_arg((char_u *)xp_name, xp_namelen, &xp_type, &argt, + &xp_arg) == FAIL) { return; + } } } @@ -11072,8 +11053,8 @@ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) int save_ex_normal_busy = ex_normal_busy; ex_normal_busy = 0; rettv->vval.v_string = - getcmdline_prompt(inputsecret_flag ? NUL : '@', p, echo_attr, - xp_type, xp_arg); + getcmdline_prompt(inputsecret_flag ? NUL : '@', (char_u *)p, echo_attr, + xp_type, xp_arg); ex_normal_busy = save_ex_normal_busy; } if (inputdialog && rettv->vval.v_string == NULL @@ -11544,8 +11525,8 @@ static char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable) assert(argl->lv_first); - const char_u *exe = get_tv_string_chk(&argl->lv_first->li_tv); - if (!exe || !os_can_exe(exe, NULL, true)) { + const char *exe = tv_get_string_chk(&argl->lv_first->li_tv); + if (!exe || !os_can_exe((const char_u *)exe, NULL, true)) { if (exe && executable) { *executable = false; } @@ -11553,14 +11534,14 @@ static char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable) } if (cmd) { - *cmd = (char *)exe; + *cmd = exe; } // Build the argument vector int i = 0; char **argv = xcalloc(argc + 1, sizeof(char *)); for (listitem_T *arg = argl->lv_first; arg != NULL; arg = arg->li_next) { - char *a = (char *)get_tv_string_chk(&arg->li_tv); + const char *a = tv_get_string_chk(&arg->li_tv); if (!a) { // Did emsg in tv_get_string_chk; just deallocate argv. shell_free_argv(argv); @@ -11803,50 +11784,49 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - garray_T ga; - char_u *sep; - if (argvars[0].v_type != VAR_LIST) { EMSG(_(e_listreq)); return; } - if (argvars[0].vval.v_list == NULL) + if (argvars[0].vval.v_list == NULL) { return; - if (argvars[1].v_type == VAR_UNKNOWN) - sep = (char_u *)" "; - else - sep = get_tv_string_chk(&argvars[1]); + } + const char *const sep = (argvars[1].v_type == VAR_UNKNOWN + ? " " + : tv_get_string_chk(&argvars[1])); rettv->v_type = VAR_STRING; if (sep != NULL) { + garray_T ga; ga_init(&ga, (int)sizeof(char), 80); - tv_list_join(&ga, argvars[0].vval.v_list, (const char *)sep); + tv_list_join(&ga, argvars[0].vval.v_list, sep); ga_append(&ga, NUL); rettv->vval.v_string = (char_u *)ga.ga_data; - } else + } else { rettv->vval.v_string = NULL; + } } /// json_decode() function static void f_json_decode(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char numbuf[NUMBUFLEN]; - char *s = NULL; + const char *s = NULL; char *tofree = NULL; size_t len; if (argvars[0].v_type == VAR_LIST) { - if (!encode_vim_list_to_buf(argvars[0].vval.v_list, &len, &s)) { + if (!encode_vim_list_to_buf(argvars[0].vval.v_list, &len, &tofree)) { EMSG(_("E474: Failed to convert list to string")); return; } - tofree = s; + s = tofree; if (s == NULL) { assert(len == 0); s = ""; } } else { - s = (char *) get_tv_string_buf_chk(&argvars[0], (char_u *) numbuf); + s = tv_get_string_buf_chk(&argvars[0], numbuf); if (s) { len = strlen(s); } else { @@ -12043,10 +12023,8 @@ static void f_localtime(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) { - char_u *which; - char_u buf[NUMBUFLEN]; - char_u *keys_buf = NULL; - char_u *rhs; + char_u *keys_buf = NULL; + char_u *rhs; int mode; int abbr = FALSE; int get_dict = FALSE; @@ -12062,20 +12040,24 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) return; } + char buf[NUMBUFLEN]; + const char *which; if (argvars[1].v_type != VAR_UNKNOWN) { - which = get_tv_string_buf_chk(&argvars[1], buf); + which = tv_get_string_buf_chk(&argvars[1], buf); if (argvars[2].v_type != VAR_UNKNOWN) { abbr = tv_get_number(&argvars[2]); if (argvars[3].v_type != VAR_UNKNOWN) { get_dict = tv_get_number(&argvars[3]); } } - } else - which = (char_u *)""; - if (which == NULL) + } else { + which = ""; + } + if (which == NULL) { return; + } - mode = get_map_mode(&which, 0); + mode = get_map_mode((char_u **)&which, 0); keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, false, CPO_TO_CPO_FLAGS); @@ -12141,9 +12123,7 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) char_u *str = NULL; long len = 0; char_u *expr = NULL; - char_u *pat; regmatch_T regmatch; - char_u patbuf[NUMBUFLEN]; char_u *save_cpo; long start = 0; long nth = 1; @@ -12183,9 +12163,11 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) len = (long)STRLEN(str); } - pat = get_tv_string_buf_chk(&argvars[1], patbuf); - if (pat == NULL) + char patbuf[NUMBUFLEN]; + const char *const pat = tv_get_string_buf_chk(&argvars[1], patbuf); + if (pat == NULL) { goto theend; + } if (argvars[2].v_type != VAR_UNKNOWN) { bool error = false; @@ -12224,7 +12206,7 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) } } - regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + regmatch.regprog = vim_regcomp((char_u *)pat, RE_MAGIC + RE_STRING); if (regmatch.regprog != NULL) { regmatch.rm_ic = p_ic; @@ -12336,18 +12318,20 @@ static void f_match(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf[NUMBUFLEN]; - char_u *grp = get_tv_string_buf_chk(&argvars[0], buf); /* group */ - char_u *pat = get_tv_string_buf_chk(&argvars[1], buf); /* pattern */ - int prio = 10; /* default priority */ + char grpbuf[NUMBUFLEN]; + char patbuf[NUMBUFLEN]; + const char *const grp = tv_get_string_buf_chk(&argvars[0], grpbuf); + const char *const pat = tv_get_string_buf_chk(&argvars[1], patbuf); + int prio = 10; int id = -1; bool error = false; const char *conceal_char = NULL; rettv->vval.v_number = -1; - if (grp == NULL || pat == NULL) + if (grp == NULL || pat == NULL) { return; + } if (argvars[2].v_type != VAR_UNKNOWN) { prio = tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { @@ -12365,7 +12349,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } - if (error == true) { + if (error) { return; } if (id >= 1 && id <= 3) { @@ -12373,17 +12357,16 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - rettv->vval.v_number = match_add(curwin, (const char *)grp, - (const char *)pat, prio, id, - NULL, conceal_char); + rettv->vval.v_number = match_add(curwin, grp, pat, prio, id, NULL, + conceal_char); } static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = -1; - - char_u buf[NUMBUFLEN]; - const char_u *const group = get_tv_string_buf_chk(&argvars[0], buf); + + char buf[NUMBUFLEN]; + const char *const group = tv_get_string_buf_chk(&argvars[0], buf); if (group == NULL) { return; } @@ -12431,8 +12414,8 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - rettv->vval.v_number = match_add(curwin, (const char *)group, NULL, prio, id, - l, conceal_char); + rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l, + conceal_char); } /* @@ -12833,11 +12816,11 @@ static void f_or(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_pathshorten(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; - rettv->vval.v_string = get_tv_string_chk(&argvars[0]); - if (!rettv->vval.v_string) { + const char *const s = tv_get_string_chk(&argvars[0]); + if (!s) { return; } - rettv->vval.v_string = shorten_dir(vim_strsave(rettv->vval.v_string)); + rettv->vval.v_string = shorten_dir((char_u *)xstrdup(s)); } /* @@ -13240,7 +13223,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_toomanyarg), "remove()"); } else if ((d = argvars[0].vval.v_dict) != NULL && !tv_check_lock(d->dv_lock, arg_errmsg, arg_errmsg_len)) { - const char *key = (const char *)get_tv_string_chk(&argvars[1]); + const char *key = tv_get_string_chk(&argvars[1]); if (key != NULL) { di = tv_dict_find(d, key, -1); if (di == NULL) { @@ -13572,40 +13555,45 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) static int get_search_arg(typval_T *varp, int *flagsp) { int dir = FORWARD; - char_u *flags; - char_u nbuf[NUMBUFLEN]; int mask; if (varp->v_type != VAR_UNKNOWN) { - flags = get_tv_string_buf_chk(varp, nbuf); - if (flags == NULL) - return 0; /* type error; errmsg already given */ + char nbuf[NUMBUFLEN]; + const char *flags = tv_get_string_buf_chk(varp, nbuf); + if (flags == NULL) { + return 0; // Type error; errmsg already given. + } while (*flags != NUL) { switch (*flags) { - case 'b': dir = BACKWARD; break; - case 'w': p_ws = true; break; - case 'W': p_ws = false; break; - default: mask = 0; - if (flagsp != NULL) - switch (*flags) { - case 'c': mask = SP_START; break; - case 'e': mask = SP_END; break; - case 'm': mask = SP_RETCOUNT; break; - case 'n': mask = SP_NOMOVE; break; - case 'p': mask = SP_SUBPAT; break; - case 'r': mask = SP_REPEAT; break; - case 's': mask = SP_SETPCMARK; break; - case 'z': mask = SP_COLUMN; break; + case 'b': dir = BACKWARD; break; + case 'w': p_ws = true; break; + case 'W': p_ws = false; break; + default: { + mask = 0; + if (flagsp != NULL) { + switch (*flags) { + case 'c': mask = SP_START; break; + case 'e': mask = SP_END; break; + case 'm': mask = SP_RETCOUNT; break; + case 'n': mask = SP_NOMOVE; break; + case 'p': mask = SP_SUBPAT; break; + case 'r': mask = SP_REPEAT; break; + case 's': mask = SP_SETPCMARK; break; + case 'z': mask = SP_COLUMN; break; + } } - if (mask == 0) { - EMSG2(_(e_invarg2), flags); - dir = 0; - } else - *flagsp |= mask; + if (mask == 0) { + emsgf(_(e_invarg2), flags); + dir = 0; + } else { + *flagsp |= mask; + } + } } - if (dir == 0) + if (dir == 0) { break; - ++flags; + } + flags++; } } return dir; @@ -13989,16 +13977,17 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = 1; /* default: FAIL */ - char_u *name = get_tv_string_chk(&argvars[0]); + const char *const name = tv_get_string_chk(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { locally = tv_get_number_chk(&argvars[1], &error) == 0; if (!error && argvars[2].v_type != VAR_UNKNOWN) { thisblock = tv_get_number_chk(&argvars[2], &error) != 0; } } - if (!error && name != NULL) - rettv->vval.v_number = find_decl(name, STRLEN(name), locally, + if (!error && name != NULL) { + rettv->vval.v_number = find_decl((char_u *)name, strlen(name), locally, thisblock, SEARCH_KEEP) == FAIL; + } } /* @@ -14006,49 +13995,53 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) { - char_u *spat, *mpat, *epat; - char_u *skip; bool save_p_ws = p_ws; int dir; int flags = 0; - char_u nbuf1[NUMBUFLEN]; - char_u nbuf2[NUMBUFLEN]; - char_u nbuf3[NUMBUFLEN]; - int retval = 0; /* default: FAIL */ + int retval = 0; // default: FAIL long lnum_stop = 0; long time_limit = 0; - /* Get the three pattern arguments: start, middle, end. */ - spat = get_tv_string_chk(&argvars[0]); - mpat = get_tv_string_buf_chk(&argvars[1], nbuf1); - epat = get_tv_string_buf_chk(&argvars[2], nbuf2); - if (spat == NULL || mpat == NULL || epat == NULL) - goto theend; /* type error */ + // Get the three pattern arguments: start, middle, end. + char nbuf1[NUMBUFLEN]; + char nbuf2[NUMBUFLEN]; + char nbuf3[NUMBUFLEN]; + const char *spat = tv_get_string_chk(&argvars[0]); + const char *mpat = tv_get_string_buf_chk(&argvars[1], nbuf1); + const char *epat = tv_get_string_buf_chk(&argvars[2], nbuf2); + if (spat == NULL || mpat == NULL || epat == NULL) { + goto theend; // Type error. + } - /* Handle the optional fourth argument: flags */ - dir = get_search_arg(&argvars[3], &flags); /* may set p_ws */ - if (dir == 0) + // Handle the optional fourth argument: flags. + dir = get_search_arg(&argvars[3], &flags); // may set p_ws. + if (dir == 0) { goto theend; + } - /* Don't accept SP_END or SP_SUBPAT. - * Only one of the SP_NOMOVE or SP_SETPCMARK flags can be set. - */ + // Don't accept SP_END or SP_SUBPAT. + // Only one of the SP_NOMOVE or SP_SETPCMARK flags can be set. if ((flags & (SP_END | SP_SUBPAT)) != 0 || ((flags & SP_NOMOVE) && (flags & SP_SETPCMARK))) { EMSG2(_(e_invarg2), tv_get_string(&argvars[3])); goto theend; } - /* Using 'r' implies 'W', otherwise it doesn't work. */ - if (flags & SP_REPEAT) + // Using 'r' implies 'W', otherwise it doesn't work. + if (flags & SP_REPEAT) { p_ws = false; + } - /* Optional fifth argument: skip expression */ + // Optional fifth argument: skip expression. + const char *skip; if (argvars[3].v_type == VAR_UNKNOWN - || argvars[4].v_type == VAR_UNKNOWN) - skip = (char_u *)""; - else { - skip = get_tv_string_buf_chk(&argvars[4], nbuf3); + || argvars[4].v_type == VAR_UNKNOWN) { + skip = ""; + } else { + skip = tv_get_string_buf_chk(&argvars[4], nbuf3); + if (skip == NULL) { + goto theend; // Type error. + } if (argvars[5].v_type != VAR_UNKNOWN) { lnum_stop = tv_get_number_chk(&argvars[5], NULL); if (lnum_stop < 0) { @@ -14062,11 +14055,10 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) } } } - if (skip == NULL) - goto theend; /* type error */ - retval = do_searchpair(spat, mpat, epat, dir, skip, flags, - match_pos, lnum_stop, time_limit); + retval = do_searchpair( + (char_u *)spat, (char_u *)mpat, (char_u *)epat, dir, (char_u *)skip, + flags, match_pos, lnum_stop, time_limit); theend: p_ws = save_p_ws; @@ -14341,14 +14333,12 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u nbuf[NUMBUFLEN]; - if (check_restricted() || check_secure() || !tv_check_str_or_nr(&argvars[0])) { return; } - const char *varname = (const char *)get_tv_string_chk(&argvars[1]); + const char *varname = tv_get_string_chk(&argvars[1]); buf_T *const buf = get_buf_tv(&argvars[0], false); typval_T *varp = &argvars[2]; @@ -14363,7 +14353,8 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) varname++; numval = tv_get_number_chk(varp, &error); - char *const strval = (char *)get_tv_string_buf_chk(varp, nbuf); + char nbuf[NUMBUFLEN]; + const char *const strval = tv_get_string_buf_chk(varp, nbuf); if (!error && strval != NULL) { set_option_value(varname, numval, strval, OPT_LOCAL); } @@ -14438,17 +14429,17 @@ static void f_setfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = 0; - char_u *fname = get_tv_string_chk(&argvars[0]); + const char *const fname = tv_get_string_chk(&argvars[0]); if (fname == NULL) { return; } - char_u modebuf[NUMBUFLEN]; - char_u *mode_str = get_tv_string_buf_chk(&argvars[1], modebuf); + char modebuf[NUMBUFLEN]; + const char *const mode_str = tv_get_string_buf_chk(&argvars[1], modebuf); if (mode_str == NULL) { return; } - if (STRLEN(mode_str) != 9) { + if (strlen(mode_str) != 9) { EMSG2(_(e_invarg2), mode_str); return; } @@ -14469,26 +14460,28 @@ static void f_setfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *line = NULL; list_T *l = NULL; listitem_T *li = NULL; long added = 0; linenr_T lcount = curbuf->b_ml.ml_line_count; linenr_T lnum = tv_get_lnum(&argvars[0]); + const char *line = NULL; if (argvars[1].v_type == VAR_LIST) { l = argvars[1].vval.v_list; li = l->lv_first; - } else - line = get_tv_string_chk(&argvars[1]); + } else { + line = tv_get_string_chk(&argvars[1]); + } /* default result is zero == OK */ for (;; ) { if (l != NULL) { - /* list argument, get next string */ - if (li == NULL) + // List argument, get next string. + if (li == NULL) { break; - line = get_tv_string_chk(&li->li_tv); + } + line = tv_get_string_chk(&li->li_tv); li = li->li_next; } @@ -14504,18 +14497,20 @@ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (lnum <= curbuf->b_ml.ml_line_count) { - /* existing line, replace it */ - if (u_savesub(lnum) == OK && ml_replace(lnum, line, TRUE) == OK) { + // Existing line, replace it. + if (u_savesub(lnum) == OK + && ml_replace(lnum, (char_u *)line, true) == OK) { changed_bytes(lnum, 0); if (lnum == curwin->w_cursor.lnum) check_cursor_col(); rettv->vval.v_number = 0; /* OK */ } } else if (added > 0 || u_save(lnum - 1, lnum) == OK) { - /* lnum is one past the last line, append the line */ - ++added; - if (ml_append(lnum - 1, line, (colnr_T)0, FALSE) == OK) - rettv->vval.v_number = 0; /* OK */ + // lnum is one past the last line, append the line. + added++; + if (ml_append(lnum - 1, (char_u *)line, 0, false) == OK) { + rettv->vval.v_number = 0; // OK + } } if (l == NULL) /* only one string argument */ @@ -14544,7 +14539,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) FUNC_ATTR_NONNULL_ARG(2, 3) { static char *e_invact = N_("E927: Invalid action: '%s'"); - char_u *title = NULL; + const char *title = NULL; int action = ' '; rettv->vval.v_number = -1; dict_T *d = NULL; @@ -14563,7 +14558,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) EMSG(_(e_stringreq)); return; } - char_u *act = get_tv_string_chk(action_arg); + const char *const act = tv_get_string_chk(action_arg); if ((*act == 'a' || *act == 'r' || *act == ' ') && act[1] == NUL) { action = *act; } else { @@ -14576,7 +14571,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) // Option argument was not given. goto skip_args; } else if (title_arg->v_type == VAR_STRING) { - title = get_tv_string_chk(title_arg); + title = tv_get_string_chk(title_arg); if (!title) { // Type error. Error already printed by get_tv_string_chk(). return; @@ -14584,17 +14579,17 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) } else if (title_arg->v_type == VAR_DICT) { d = title_arg->vval.v_dict; } else { - EMSG(_(e_dictreq)); + emsgf(_(e_dictreq)); return; } skip_args: if (!title) { - title = (char_u*)(wp ? "setloclist()" : "setqflist()"); + title = (wp ? "setloclist()" : "setqflist()"); } list_T *l = list_arg->vval.v_list; - if (l && set_errorlist(wp, l, action, title, d) == OK) { + if (l && set_errorlist(wp, l, action, (char_u *)title, d) == OK) { rettv->vval.v_number = 0; } } @@ -14728,11 +14723,10 @@ static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { pos_T pos; int fnum; - char_u *name; colnr_T curswant = -1; rettv->vval.v_number = -1; - name = get_tv_string_chk(argvars); + const char *const name = tv_get_string_chk(argvars); if (name != NULL) { if (list2fpos(&argvars[1], &pos, &fnum, &curswant) == OK) { if (--pos.col < 0) { @@ -14753,7 +14747,7 @@ static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) { // set mark - if (setmark_pos(name[1], &pos, fnum) == OK) { + if (setmark_pos((uint8_t)name[1], &pos, fnum) == OK) { rettv->vval.v_number = 0; } } else { @@ -14777,8 +14771,6 @@ static void f_setqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int regname; - char_u *strregname; - char_u *stropt; bool append = false; MotionType yank_type; long block_len; @@ -14786,39 +14778,47 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) block_len = -1; yank_type = kMTUnknown; - strregname = get_tv_string_chk(argvars); - rettv->vval.v_number = 1; /* FAIL is default */ + rettv->vval.v_number = 1; // FAIL is default. - if (strregname == NULL) - return; /* type error; errmsg already given */ - regname = *strregname; - if (regname == 0 || regname == '@') + const char *const strregname = tv_get_string_chk(argvars); + if (strregname == NULL) { + return; // Type error; errmsg already given. + } + regname = (uint8_t)(*strregname); + if (regname == 0 || regname == '@') { regname = '"'; + } if (argvars[2].v_type != VAR_UNKNOWN) { - stropt = get_tv_string_chk(&argvars[2]); - if (stropt == NULL) - return; /* type error */ - for (; *stropt != NUL; ++stropt) + const char *stropt = tv_get_string_chk(&argvars[2]); + if (stropt == NULL) { + return; // Type error. + } + for (; *stropt != NUL; stropt++) { switch (*stropt) { - case 'a': case 'A': // append - append = true; - break; - case 'v': case 'c': // character-wise selection - yank_type = kMTCharWise; - break; - case 'V': case 'l': // line-wise selection - yank_type = kMTLineWise; - break; - case 'b': case Ctrl_V: // block-wise selection - yank_type = kMTBlockWise; - if (ascii_isdigit(stropt[1])) { - ++stropt; - block_len = getdigits_long(&stropt) - 1; - --stropt; + case 'a': case 'A': { // append + append = true; + break; + } + case 'v': case 'c': { // character-wise selection + yank_type = kMTCharWise; + break; + } + case 'V': case 'l': { // line-wise selection + yank_type = kMTLineWise; + break; + } + case 'b': case Ctrl_V: { // block-wise selection + yank_type = kMTBlockWise; + if (ascii_isdigit(stropt[1])) { + stropt++; + block_len = getdigits_long((char_u **)&stropt) - 1; + stropt--; + } + break; } - break; } + } } if (argvars[1].v_type == VAR_LIST) { @@ -14828,42 +14828,44 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) // First half: use for pointers to result lines; second half: use for // pointers to allocated copies. - char_u **lstval = xmalloc(sizeof(char_u *) * ((len + 1) * 2)); - char_u **curval = lstval; - char_u **allocval = lstval + len + 2; - char_u **curallocval = allocval; + char **lstval = xmalloc(sizeof(char *) * ((len + 1) * 2)); + const char **curval = (const char **)lstval; + char **allocval = lstval + len + 2; + char **curallocval = allocval; - char_u buf[NUMBUFLEN]; for (listitem_T *li = ll == NULL ? NULL : ll->lv_first; li != NULL; li = li->li_next) { - char_u *strval = get_tv_string_buf_chk(&li->li_tv, buf); - if (strval == NULL) { + char buf[NUMBUFLEN]; + *curval = tv_get_string_buf_chk(&li->li_tv, buf); + if (*curval == NULL) { goto free_lstval; } - if (strval == buf) { + if (*curval == buf) { // Need to make a copy, - // next get_tv_string_buf_chk() will overwrite the string. - strval = vim_strsave(buf); - *curallocval++ = strval; + // next tv_get_string_buf_chk() will overwrite the string. + *curallocval = xstrdup(*curval); + *curval = *curallocval; + curallocval++; } - *curval++ = strval; + curval++; } *curval++ = NULL; - write_reg_contents_lst(regname, lstval, STRLEN(lstval), - append, yank_type, block_len); + write_reg_contents_lst(regname, (char_u **)lstval, append, yank_type, + block_len); free_lstval: - while (curallocval > allocval) - xfree(*--curallocval); + while (curallocval > allocval) { + xfree(*--curallocval); + } xfree(lstval); } else { - char_u *strval = get_tv_string_chk(&argvars[1]); + const char *strval = tv_get_string_chk(&argvars[1]); if (strval == NULL) { return; } - write_reg_contents_ex(regname, strval, STRLEN(strval), + write_reg_contents_ex(regname, (const char_u *)strval, STRLEN(strval), append, yank_type, block_len); } rettv->vval.v_number = 0; @@ -14881,7 +14883,7 @@ static void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); - const char *const varname = (const char *)get_tv_string_chk(&argvars[1]); + const char *const varname = tv_get_string_chk(&argvars[1]); typval_T *const varp = &argvars[2]; if (varname != NULL && varp != NULL && tp != NULL) { @@ -14935,7 +14937,7 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) tp = curtab; } win_T *const win = find_win_by_nr(&argvars[off], tp); - const char *varname = (const char *)get_tv_string_chk(&argvars[off + 1]); + const char *varname = tv_get_string_chk(&argvars[off + 1]); typval_T *varp = &argvars[off + 2]; if (win != NULL && varname != NULL && varp != NULL) { @@ -14950,8 +14952,8 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) varname++; numval = tv_get_number_chk(varp, &error); - char_u nbuf[NUMBUFLEN]; - char *const strval = (char *)get_tv_string_buf_chk(varp, nbuf); + char nbuf[NUMBUFLEN]; + const char *const strval = tv_get_string_buf_chk(varp, nbuf); if (!error && strval != NULL) { set_option_value(varname, numval, strval, OPT_LOCAL); } @@ -15415,25 +15417,26 @@ static void f_soundfold(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *word = (char_u *)""; + const char *word = ""; hlf_T attr = HLF_COUNT; size_t len = 0; tv_list_alloc_ret(rettv); if (argvars[0].v_type == VAR_UNKNOWN) { - /* Find the start and length of the badly spelled word. */ - len = spell_move_to(curwin, FORWARD, TRUE, TRUE, &attr); - if (len != 0) - word = get_cursor_pos_ptr(); + // Find the start and length of the badly spelled word. + len = spell_move_to(curwin, FORWARD, true, true, &attr); + if (len != 0) { + word = (char *)get_cursor_pos_ptr(); + } } else if (curwin->w_p_spell && *curbuf->b_s.b_p_spl != NUL) { - char_u *str = get_tv_string_chk(&argvars[0]); + const char *str = tv_get_string_chk(&argvars[0]); int capcol = -1; if (str != NULL) { - /* Check the argument for spelling. */ + // Check the argument for spelling. while (*str != NUL) { - len = spell_check(curwin, str, &attr, &capcol, false); + len = spell_check(curwin, (char_u *)str, &attr, &capcol, false); if (attr != HLF_COUNT) { word = str; break; @@ -15444,7 +15447,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) } assert(len <= INT_MAX); - tv_list_append_string(rettv->vval.v_list, (const char *)word, len); + tv_list_append_string(rettv->vval.v_list, word, len); tv_list_append_string(rettv->vval.v_list, (attr == HLF_SPB ? "bad" : attr == HLF_SPR ? "rare" @@ -15499,9 +15502,7 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *pat = NULL; regmatch_T regmatch; - char_u patbuf[NUMBUFLEN]; char_u *save_cpo; int match; colnr_T col = 0; @@ -15513,8 +15514,10 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) p_cpo = (char_u *)""; const char *str = tv_get_string(&argvars[0]); + const char *pat = NULL; + char patbuf[NUMBUFLEN]; if (argvars[1].v_type != VAR_UNKNOWN) { - pat = get_tv_string_buf_chk(&argvars[1], patbuf); + pat = tv_get_string_buf_chk(&argvars[1], patbuf); if (pat == NULL) { typeerr = true; } @@ -15522,15 +15525,16 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) keepempty = (bool)tv_get_number_chk(&argvars[2], &typeerr); } } - if (pat == NULL || *pat == NUL) - pat = (char_u *)"[\\x01- ]\\+"; + if (pat == NULL || *pat == NUL) { + pat = "[\\x01- ]\\+"; + } tv_list_alloc_ret(rettv); if (typeerr) return; - regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + regmatch.regprog = vim_regcomp((char_u *)pat, RE_MAGIC + RE_STRING); if (regmatch.regprog != NULL) { regmatch.rm_ic = FALSE; while (*str != NUL || keepempty) { @@ -15682,7 +15686,7 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = -1; - char_u *str = get_tv_string_chk(&argvars[0]); + const char *const str = tv_get_string_chk(&argvars[0]); if (str == NULL) { return; } @@ -15697,11 +15701,11 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) while (charidx >= 0 && byteidx < len) { if (charidx == 0) { - rettv->vval.v_number = mb_ptr2char(str + byteidx); + rettv->vval.v_number = mb_ptr2char((const char_u *)str + byteidx); break; } charidx--; - byteidx += MB_CPTR2LEN(str + byteidx); + byteidx += MB_CPTR2LEN((const char_u *)str + byteidx); } } @@ -15710,24 +15714,22 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf[NUMBUFLEN]; - char_u *needle; - char_u *haystack; - char_u *save_haystack; - char_u *pos; - int start_idx; - - needle = get_tv_string_chk(&argvars[1]); - save_haystack = haystack = get_tv_string_buf_chk(&argvars[0], buf); rettv->vval.v_number = -1; - if (needle == NULL || haystack == NULL) - return; /* type error; errmsg already given */ + + char buf[NUMBUFLEN]; + const char *const needle = tv_get_string_chk(&argvars[1]); + const char *haystack = tv_get_string_buf_chk(&argvars[0], buf); + const char *const haystack_start = haystack; + if (needle == NULL || haystack == NULL) { + return; // Type error; errmsg already given. + } if (argvars[2].v_type != VAR_UNKNOWN) { bool error = false; - start_idx = tv_get_number_chk(&argvars[2], &error); - if (error || start_idx >= (int)STRLEN(haystack)) { + const ptrdiff_t start_idx = (ptrdiff_t)tv_get_number_chk(&argvars[2], + &error); + if (error || start_idx >= (ptrdiff_t)strlen(haystack)) { return; } if (start_idx >= 0) { @@ -15735,9 +15737,10 @@ static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - pos = (char_u *)strstr((char *)haystack, (char *)needle); - if (pos != NULL) - rettv->vval.v_number = (varnumber_T)(pos - save_haystack); + const char *pos = strstr(haystack, needle); + if (pos != NULL) { + rettv->vval.v_number = (varnumber_T)(pos - haystack_start); + } } /* @@ -15765,7 +15768,7 @@ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *s = tv_get_string(&argvars[0]); int skipcc = 0; varnumber_T len = 0; - int (*func_mb_ptr2char_adv)(char_u **pp); + int (*func_mb_ptr2char_adv)(const char_u **pp); if (argvars[1].v_type != VAR_UNKNOWN) { skipcc = tv_get_number_chk(&argvars[1], NULL); @@ -15775,7 +15778,7 @@ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv; while (*s != NUL) { - func_mb_ptr2char_adv((char_u **)&s); + func_mb_ptr2char_adv((const char_u **)&s); len++; } rettv->vval.v_number = len; @@ -15904,47 +15907,46 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf[NUMBUFLEN]; - char_u *needle; - char_u *haystack; - char_u *rest; - char_u *lastmatch = NULL; - int haystack_len, end_idx; - - needle = get_tv_string_chk(&argvars[1]); - haystack = get_tv_string_buf_chk(&argvars[0], buf); + char buf[NUMBUFLEN]; + const char *const needle = tv_get_string_chk(&argvars[1]); + const char *const haystack = tv_get_string_buf_chk(&argvars[0], buf); rettv->vval.v_number = -1; - if (needle == NULL || haystack == NULL) - return; /* type error; errmsg already given */ + if (needle == NULL || haystack == NULL) { + return; // Type error; errmsg already given. + } - haystack_len = (int)STRLEN(haystack); + const size_t haystack_len = STRLEN(haystack); + ptrdiff_t end_idx; if (argvars[2].v_type != VAR_UNKNOWN) { // Third argument: upper limit for index. - end_idx = tv_get_number_chk(&argvars[2], NULL); + end_idx = (ptrdiff_t)tv_get_number_chk(&argvars[2], NULL); if (end_idx < 0) { return; // Can never find a match. } } else { - end_idx = haystack_len; + end_idx = (ptrdiff_t)haystack_len; } + const char *lastmatch = NULL; if (*needle == NUL) { - /* Empty string matches past the end. */ + // Empty string matches past the end. lastmatch = haystack + end_idx; } else { - for (rest = haystack; *rest != NUL; ++rest) { - rest = (char_u *)strstr((char *)rest, (char *)needle); - if (rest == NULL || rest > haystack + end_idx) + for (const char *rest = haystack; *rest != NUL; rest++) { + rest = strstr(rest, needle); + if (rest == NULL || rest > haystack + end_idx) { break; + } lastmatch = rest; } } - if (lastmatch == NULL) + if (lastmatch == NULL) { rettv->vval.v_number = -1; - else + } else { rettv->vval.v_number = (varnumber_T)(lastmatch - haystack); + } } /* @@ -15994,20 +15996,20 @@ static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u patbuf[NUMBUFLEN]; - char_u subbuf[NUMBUFLEN]; - char_u flagsbuf[NUMBUFLEN]; + char patbuf[NUMBUFLEN]; + char subbuf[NUMBUFLEN]; + char flagsbuf[NUMBUFLEN]; - char_u *str = get_tv_string_chk(&argvars[0]); - char_u *pat = get_tv_string_buf_chk(&argvars[1], patbuf); - char_u *sub = NULL; - typval_T *expr = NULL; - char_u *flg = get_tv_string_buf_chk(&argvars[3], flagsbuf); + const char *const str = tv_get_string_chk(&argvars[0]); + const char *const pat = tv_get_string_buf_chk(&argvars[1], patbuf); + const char *sub = NULL; + const char *const flg = tv_get_string_buf_chk(&argvars[3], flagsbuf); + typval_T *expr = NULL; if (argvars[2].v_type == VAR_FUNC || argvars[2].v_type == VAR_PARTIAL) { expr = &argvars[2]; } else { - sub = get_tv_string_buf_chk(&argvars[2], subbuf); + sub = tv_get_string_buf_chk(&argvars[2], subbuf); } rettv->v_type = VAR_STRING; @@ -16015,7 +16017,8 @@ static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr) || flg == NULL) { rettv->vval.v_string = NULL; } else { - rettv->vval.v_string = do_string_sub(str, pat, sub, expr, flg); + rettv->vval.v_string = do_string_sub((char_u *)str, (char_u *)pat, + (char_u *)sub, expr, (char_u *)flg); } } @@ -16333,19 +16336,20 @@ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_tabpagenr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int nr = 1; - char_u *arg; if (argvars[0].v_type != VAR_UNKNOWN) { - arg = get_tv_string_chk(&argvars[0]); + const char *const arg = tv_get_string_chk(&argvars[0]); nr = 0; if (arg != NULL) { - if (STRCMP(arg, "$") == 0) + if (strcmp(arg, "$") == 0) { nr = tabpage_index(NULL) - 1; - else + } else { EMSG2(_(e_invexpr2), arg); + } } - } else + } else { nr = tabpage_index(curtab); + } rettv->vval.v_number = nr; } @@ -16359,19 +16363,19 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar) win_T *twin; int nr = 1; win_T *wp; - char_u *arg; twin = (tp == curtab) ? curwin : tp->tp_curwin; if (argvar->v_type != VAR_UNKNOWN) { - arg = get_tv_string_chk(argvar); - if (arg == NULL) - nr = 0; /* type error; errmsg already given */ - else if (STRCMP(arg, "$") == 0) + const char *const arg = tv_get_string_chk(argvar); + if (arg == NULL) { + nr = 0; // Type error; errmsg already given. + } else if (strcmp(arg, "$") == 0) { twin = (tp == curtab) ? lastwin : tp->tp_lastwin; - else if (STRCMP(arg, "#") == 0) { + } else if (strcmp(arg, "#") == 0) { twin = (tp == curtab) ? prevwin : tp->tp_prevwin; - if (twin == NULL) + if (twin == NULL) { nr = 0; + } } else { EMSG2(_(e_invexpr2), arg); nr = 0; @@ -16853,28 +16857,25 @@ static void f_toupper(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int fromlen; - int tolen; - int idx; - char_u buf[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; - garray_T ga; + char buf[NUMBUFLEN]; + char buf2[NUMBUFLEN]; const char *in_str = tv_get_string(&argvars[0]); - const char_u *fromstr = get_tv_string_buf_chk(&argvars[1], buf); - const char_u *tostr = get_tv_string_buf_chk(&argvars[2], buf2); + const char *fromstr = tv_get_string_buf_chk(&argvars[1], buf); + const char *tostr = tv_get_string_buf_chk(&argvars[2], buf2); // Default return value: empty string. rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; if (fromstr == NULL || tostr == NULL) { - return; // type error; errmsg already given + return; // Type error; errmsg already given. } + garray_T ga; ga_init(&ga, (int)sizeof(char), 80); if (!has_mbyte) { // Not multi-byte: fromstr and tostr must be the same length. - if (STRLEN(fromstr) != STRLEN(tostr)) { + if (strlen(fromstr) != strlen(tostr)) { goto error; } } @@ -16886,23 +16887,26 @@ static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *cpstr = in_str; const int inlen = (*mb_ptr2len)((const char_u *)in_str); int cplen = inlen; - idx = 0; - for (const char_u *p = fromstr; *p != NUL; p += fromlen) { - fromlen = (*mb_ptr2len)(p); + int idx = 0; + int fromlen; + for (const char *p = fromstr; *p != NUL; p += fromlen) { + fromlen = (*mb_ptr2len)((const char_u *)p); if (fromlen == inlen && STRNCMP(in_str, p, inlen) == 0) { + int tolen; for (p = tostr; *p != NUL; p += tolen) { - tolen = (*mb_ptr2len)(p); + tolen = (*mb_ptr2len)((const char_u *)p); if (idx-- == 0) { cplen = tolen; cpstr = (char *)p; break; } } - if (*p == NUL) /* tostr is shorter than fromstr */ + if (*p == NUL) { // tostr is shorter than fromstr. goto error; + } break; } - ++idx; + idx++; } if (first && cpstr == in_str) { @@ -16910,8 +16914,9 @@ static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr) // (multi-byte) characters. Done only once when a character // of in_str doesn't appear in fromstr. first = false; - for (const char_u *p = tostr; *p != NUL; p += tolen) { - tolen = (*mb_ptr2len)(p); + int tolen; + for (const char *p = tostr; *p != NUL; p += tolen) { + tolen = (*mb_ptr2len)((const char_u *)p); idx--; } if (idx != 0) { @@ -16926,7 +16931,7 @@ static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr) in_str += inlen; } else { // When not using multi-byte chars we can do it faster. - char_u *p = vim_strchr(fromstr, *in_str); + const char *const p = strchr(fromstr, *in_str); if (p != NULL) { ga_append(&ga, tostr[p - fromstr]); } else { @@ -17278,8 +17283,7 @@ static bool write_list(FileDescriptor *const fp, const list_T *const list, { int error = 0; for (const listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - const char *const s = (const char *)get_tv_string_chk( - (typval_T *)&li->li_tv); + const char *const s = tv_get_string_chk(&li->li_tv); if (s == NULL) { return false; } @@ -17368,17 +17372,16 @@ static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) return NULL; } - // For types other than list, let get_tv_string_buf_chk() get the value or + // For types other than list, let tv_get_string_buf_chk() get the value or // print an error. if (tv->v_type != VAR_LIST) { - char *ret = (char *)get_tv_string_chk(tv); + const char *ret = tv_get_string_chk(tv); if (ret && (*len = strlen(ret))) { - ret = xstrdup(ret); + return xmemdupz(ret, (size_t)(*len)); } else { - ret = NULL; *len = -1; + return NULL; } - return ret; } // Pre-calculate the resulting length. @@ -17448,7 +17451,7 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool binary = false; bool append = false; if (argvars[2].v_type != VAR_UNKNOWN) { - const char *const flags = (const char *)get_tv_string_chk(&argvars[2]); + const char *const flags = tv_get_string_chk(&argvars[2]); if (flags == NULL) { return; } @@ -17460,9 +17463,8 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - const char buf[NUMBUFLEN]; - const char *const fname = (const char *)get_tv_string_buf_chk(&argvars[1], - (char_u *)buf); + char buf[NUMBUFLEN]; + const char *const fname = tv_get_string_buf_chk(&argvars[1], buf); if (fname == NULL) { return; } @@ -17510,7 +17512,6 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum, int *const ret_fnum) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - char_u *name; static pos_T pos; pos_T *pp; @@ -17564,7 +17565,7 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum, return &pos; } - name = get_tv_string_chk(tv); + const char *const name = tv_get_string_chk(tv); if (name == NULL) { return NULL; } @@ -17578,7 +17579,7 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum, return &curwin->w_cursor; } if (name[0] == '\'') { // Mark. - pp = getmark_buf_fnum(curbuf, name[1], false, ret_fnum); + pp = getmark_buf_fnum(curbuf, (uint8_t)name[1], false, ret_fnum); if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) { return NULL; } @@ -18403,58 +18404,11 @@ void set_selfdict(typval_T *rettv, dict_T *selfdict) } } -// TODO(ZyX-I): move to eval/typval - -/// Careful: This uses a single, static buffer. YOU CAN ONLY USE IT ONCE! -char_u *get_tv_string_chk(const typval_T *varp) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - static char_u mybuf[NUMBUFLEN]; - - return get_tv_string_buf_chk(varp, mybuf); -} - -char_u *get_tv_string_buf_chk(const typval_T *varp, char_u *buf) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - switch (varp->v_type) { - case VAR_NUMBER: - sprintf((char *)buf, "%" PRId64, (int64_t)varp->vval.v_number); - return buf; - case VAR_FUNC: - case VAR_PARTIAL: - EMSG(_("E729: using Funcref as a String")); - break; - case VAR_LIST: - EMSG(_("E730: using List as a String")); - break; - case VAR_DICT: - EMSG(_("E731: using Dictionary as a String")); - break; - case VAR_FLOAT: - EMSG(_(e_float_as_string)); - break; - case VAR_STRING: - if (varp->vval.v_string != NULL) - return varp->vval.v_string; - return (char_u *)""; - case VAR_SPECIAL: - STRCPY(buf, encode_special_var_names[varp->vval.v_special]); - return buf; - case VAR_UNKNOWN: - EMSG(_("E908: using an invalid value as a String")); - break; - } - return NULL; -} - -/* - * Find variable "name" in the list of variables. - * Return a pointer to it if found, NULL if not found. - * Careful: "a:0" variables don't have a name. - * When "htp" is not NULL we are writing to the variable, set "htp" to the - * hashtab_T used. - */ +// Find variable "name" in the list of variables. +// Return a pointer to it if found, NULL if not found. +// Careful: "a:0" variables don't have a name. +// When "htp" is not NULL we are writing to the variable, set "htp" to the +// hashtab_T used. static dictitem_T *find_var(const char *const name, const size_t name_len, hashtab_T **htp, int no_autoload) { diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index ca635dcae9..78eca15fec 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -796,7 +796,7 @@ static bool tv_dict_watcher_matches(DictWatcher *watcher, const char *const key) // of the string means it should match everything up to the '*' instead of the // whole string. const size_t len = strlen(watcher->key_pattern); - if (watcher->key_pattern[len - 1] == '*') { + if (len && watcher->key_pattern[len - 1] == '*') { return strncmp(key, watcher->key_pattern, len - 1) == 0; } else { return strcmp(key, watcher->key_pattern) == 0; @@ -2020,7 +2020,7 @@ bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic, /// /// @return true if everything is OK, false otherwise. bool tv_check_str_or_nr(const typval_T *const tv) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { switch (tv->v_type) { case VAR_NUMBER: @@ -2072,7 +2072,7 @@ static const char *const num_errors[] = { /// Check that given value is a number or can be converted to it /// -/// Error messages are compatible with tv_get_number() previously used for +/// Error messages are compatible with tv_get_number_chk() previously used for /// the same purpose. /// /// @param[in] tv Value to check. @@ -2101,6 +2101,50 @@ bool tv_check_num(const typval_T *const tv) return false; } +#define FUNC_ERROR "E729: using Funcref as a String" + +static const char *const str_errors[] = { + [VAR_PARTIAL]=N_(FUNC_ERROR), + [VAR_FUNC]=N_(FUNC_ERROR), + [VAR_LIST]=N_("E730: using List as a String"), + [VAR_DICT]=N_("E731: using Dictionary as a String"), + [VAR_FLOAT]=((const char *)e_float_as_string), + [VAR_UNKNOWN]=N_("E908: using an invalid value as a String"), +}; + +#undef FUNC_ERROR + +/// Check that given value is a string or can be converted to it +/// +/// Error messages are compatible with tv_get_string_chk() previously used for +/// the same purpose. +/// +/// @param[in] tv Value to check. +/// +/// @return true if everything is OK, false otherwise. +bool tv_check_str(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + switch (tv->v_type) { + case VAR_NUMBER: + case VAR_SPECIAL: + case VAR_STRING: { + return true; + } + case VAR_PARTIAL: + case VAR_FUNC: + case VAR_LIST: + case VAR_DICT: + case VAR_FLOAT: + case VAR_UNKNOWN: { + EMSG(_(str_errors[tv->v_type])); + return false; + } + } + assert(false); + return false; +} + //{{{2 Get /// Get the number value of a VimL object @@ -2245,6 +2289,48 @@ float_T tv_get_float(const typval_T *const tv) return 0; } +/// Get the string value of a VimL object +/// +/// @param[in] tv Object to get value of. +/// @param buf Buffer used to hold numbers and special variables converted to +/// string. When function encounters one of these stringified value +/// will be written to buf and buf will be returned. +/// +/// Buffer must have NUMBUFLEN size. +/// +/// @return Object value if it is VAR_STRING object, number converted to +/// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or NULL. +const char *tv_get_string_buf_chk(const typval_T *const tv, char *const buf) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + switch (tv->v_type) { + case VAR_NUMBER: { + snprintf(buf, NUMBUFLEN, "%" PRIdVARNUMBER, tv->vval.v_number); + return buf; + } + case VAR_STRING: { + if (tv->vval.v_string != NULL) { + return (const char *)tv->vval.v_string; + } + return ""; + } + case VAR_SPECIAL: { + STRCPY(buf, encode_special_var_names[tv->vval.v_special]); + return buf; + } + case VAR_PARTIAL: + case VAR_FUNC: + case VAR_LIST: + case VAR_DICT: + case VAR_FLOAT: + case VAR_UNKNOWN: { + EMSG(_(str_errors[tv->v_type])); + return false; + } + } + return NULL; +} + /// Get the string value of a VimL object /// /// @warning For number and special values it uses a single, static buffer. It @@ -2252,7 +2338,26 @@ float_T tv_get_float(const typval_T *const tv) /// tv_get_string_buf() if you need to use tv_get_string() output after /// calling it again. /// -/// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but +/// @param[in] tv Object to get value of. +/// +/// @return Object value if it is VAR_STRING object, number converted to +/// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or NULL. +const char *tv_get_string_chk(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + static char mybuf[NUMBUFLEN]; + + return tv_get_string_buf_chk(tv, mybuf); +} + +/// Get the string value of a VimL object +/// +/// @warning For number and special values it uses a single, static buffer. It +/// may be used only once, next call to get_tv_string may reuse it. Use +/// tv_get_string_buf() if you need to use tv_get_string() output after +/// calling it again. +/// +/// @note tv_get_string_chk() and tv_get_string_buf_chk() are similar, but /// return NULL on error. /// /// @param[in] tv Object to get value of. @@ -2269,7 +2374,7 @@ const char *tv_get_string(const typval_T *const tv) /// Get the string value of a VimL object /// -/// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but +/// @note tv_get_string_chk() and tv_get_string_buf_chk() are similar, but /// return NULL on error. /// /// @param[in] tv Object to get value of. @@ -2285,8 +2390,7 @@ const char *tv_get_string(const typval_T *const tv) const char *tv_get_string_buf(const typval_T *const tv, char *const buf) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { - const char *const res = (const char *)get_tv_string_buf_chk( - (typval_T *)tv, (char_u *)buf); + const char *const res = (const char *)tv_get_string_buf_chk(tv, buf); return res != NULL ? res : ""; } diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 3c294b45b8..42581939e3 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -27,6 +27,7 @@ typedef double float_T; /// Mimimal possible value of varnumber_T variable #define VARNUMBER_MIN INT_MIN +#define PRIdVARNUMBER "d" /// %d printf format specifier for varnumber_T #define PRIdVARNUMBER "d" diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 9681527fee..9a847a4c0a 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1008,8 +1008,8 @@ void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, int do_out) AppendToRedobuffLit(cmd, -1); xfree(cmd); - AppendToRedobuff((char_u *)"\n"); - bangredo = FALSE; + AppendToRedobuff("\n"); + bangredo = false; } /* * Add quotes around the command, for shells that need them. diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 6b661cff11..73b81ac2d9 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -6794,7 +6794,7 @@ do_exedit ( int ms = msg_scroll; if (eap->nextcmd != NULL) { - stuffReadbuff(eap->nextcmd); + stuffReadbuff((const char *)eap->nextcmd); eap->nextcmd = NULL; } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 3bd8d580ab..a0981a42ce 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2554,19 +2554,22 @@ void cmdline_paste_str(char_u *s, int literally) else while (*s != NUL) { cv = *s; - if (cv == Ctrl_V && s[1]) - ++s; - if (has_mbyte) - c = mb_cptr2char_adv(&s); - else + if (cv == Ctrl_V && s[1]) { + s++; + } + if (has_mbyte) { + c = mb_cptr2char_adv((const char_u **)&s); + } else { c = *s++; + } if (cv == Ctrl_V || c == ESC || c == Ctrl_C || c == CAR || c == NL || c == Ctrl_L #ifdef UNIX || c == intr_char #endif - || (c == Ctrl_BSL && *s == Ctrl_N)) + || (c == Ctrl_BSL && *s == Ctrl_N)) { stuffcharReadbuff(Ctrl_V); + } stuffcharReadbuff(c); } } @@ -4636,7 +4639,7 @@ in_history ( /// /// @return Any value from HistoryType enum, including HIST_INVALID. May not /// return HIST_DEFAULT unless return_default is true. -HistoryType get_histtype(const char_u *const name, const size_t len, +HistoryType get_histtype(const char *const name, const size_t len, const bool return_default) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { @@ -5029,7 +5032,7 @@ void ex_history(exarg_T *eap) while (ASCII_ISALPHA(*end) || vim_strchr((char_u *)":=@>/?", *end) != NULL) end++; - histype1 = get_histtype(arg, end - arg, false); + histype1 = get_histtype((const char *)arg, end - arg, false); if (histype1 == HIST_INVALID) { if (STRNICMP(arg, "all", end - arg) == 0) { histype1 = 0; @@ -5288,18 +5291,18 @@ static int ex_window(void) cmdwin_result = Ctrl_C; /* Set the new command line from the cmdline buffer. */ xfree(ccline.cmdbuff); - if (cmdwin_result == K_XF1 || cmdwin_result == K_XF2) { /* :qa[!] typed */ - char *p = (cmdwin_result == K_XF2) ? "qa" : "qa!"; + if (cmdwin_result == K_XF1 || cmdwin_result == K_XF2) { // :qa[!] typed + const char *p = (cmdwin_result == K_XF2) ? "qa" : "qa!"; if (histtype == HIST_CMD) { - /* Execute the command directly. */ - ccline.cmdbuff = vim_strsave((char_u *)p); + // Execute the command directly. + ccline.cmdbuff = (char_u *)xstrdup(p); cmdwin_result = CAR; } else { - /* First need to cancel what we were doing. */ + // First need to cancel what we were doing. ccline.cmdbuff = NULL; stuffcharReadbuff(':'); - stuffReadbuff((char_u *)p); + stuffReadbuff(p); stuffcharReadbuff(CAR); } } else if (cmdwin_result == K_XF2) { /* :qa typed */ diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 67ac9f9957..127efda65c 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -614,10 +614,12 @@ readfile ( return FAIL; } #ifdef UNIX - /* Set swap file protection bits after creating it. */ + // Set swap file protection bits after creating it. if (swap_mode > 0 && curbuf->b_ml.ml_mfp != NULL - && curbuf->b_ml.ml_mfp->mf_fname != NULL) - (void)os_setperm(curbuf->b_ml.ml_mfp->mf_fname, (long)swap_mode); + && curbuf->b_ml.ml_mfp->mf_fname != NULL) { + (void)os_setperm((const char *)curbuf->b_ml.ml_mfp->mf_fname, + (long)swap_mode); + } #endif } @@ -2870,9 +2872,9 @@ buf_write ( xfree(backup); backup = NULL; } else { - /* set file protection same as original file, but - * strip s-bit */ - (void)os_setperm(backup, perm & 0777); + // set file protection same as original file, but + // strip s-bit. + (void)os_setperm((const char *)backup, perm & 0777); #ifdef UNIX /* @@ -2883,7 +2885,8 @@ buf_write ( */ if (file_info_new.stat.st_gid != file_info_old.stat.st_gid && os_fchown(bfd, -1, file_info_old.stat.st_gid) != 0) { - os_setperm(backup, (perm & 0707) | ((perm & 07) << 3)); + os_setperm((const char *)backup, + (perm & 0707) | ((perm & 07) << 3)); } # ifdef HAVE_SELINUX mch_copy_sec(fname, backup); @@ -3037,8 +3040,8 @@ nobackup: && file_info_old.stat.st_uid == getuid() && vim_strchr(p_cpo, CPO_FWRITE) == NULL) { perm |= 0200; - (void)os_setperm(fname, perm); - made_writable = TRUE; + (void)os_setperm((const char *)fname, perm); + made_writable = true; } #endif @@ -3402,8 +3405,9 @@ restore_backup: || file_info.stat.st_uid != file_info_old.stat.st_uid || file_info.stat.st_gid != file_info_old.stat.st_gid) { os_fchown(fd, file_info_old.stat.st_uid, file_info_old.stat.st_gid); - if (perm >= 0) /* set permission again, may have changed */ - (void)os_setperm(wfname, perm); + if (perm >= 0) { // Set permission again, may have changed. + (void)os_setperm((const char *)wfname, perm); + } } buf_set_file_id(buf); } else if (!buf->file_id_valid) { @@ -3421,8 +3425,9 @@ restore_backup: if (made_writable) perm &= ~0200; /* reset 'w' bit for security reasons */ #endif - if (perm >= 0) /* set perm. of new file same as old file */ - (void)os_setperm(wfname, perm); + if (perm >= 0) { // Set perm. of new file same as old file. + (void)os_setperm((const char *)wfname, perm); + } #ifdef HAVE_ACL /* Probably need to set the ACL before changing the user (can't set the * ACL on a file the user doesn't own). */ @@ -3628,7 +3633,7 @@ restore_backup: close(empty_fd); } if (org != NULL) { - os_setperm((char_u *)org, os_getperm((const char *)fname) & 0777); + os_setperm(org, os_getperm((const char *)fname) & 0777); xfree(org); } } @@ -4690,8 +4695,8 @@ int vim_rename(const char_u *from, const char_u *to) errmsg = _("E210: Error reading \"%s\""); to = from; } -#ifndef UNIX /* for Unix os_open() already set the permission */ - os_setperm(to, perm); +#ifndef UNIX // For Unix os_open() already set the permission. + os_setperm((const char *)to, perm); #endif #ifdef HAVE_ACL mch_set_acl(to, acl); diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index b64f089b96..7143819e21 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -235,19 +235,18 @@ char_u *get_inserted(void) return get_buffcont(&redobuff, FALSE); } -/* - * Add string "s" after the current block of buffer "buf". - * K_SPECIAL and CSI should have been escaped already. - */ -static void -add_buff ( - buffheader_T *buf, - char_u *s, - ssize_t slen // length of "s" or -1 -) +/// Add string after the current block of the given buffer +/// +/// K_SPECIAL and CSI should have been escaped already. +/// +/// @param[out] buf Buffer to add to. +/// @param[in] s String to add. +/// @param[in] slen String length or -1 for NUL-terminated string. +static void add_buff(buffheader_T *const buf, const char *const s, + ptrdiff_t slen) { if (slen < 0) { - slen = (ssize_t)STRLEN(s); + slen = (ptrdiff_t)strlen(s); } if (slen == 0) { // don't add empty strings return; @@ -292,9 +291,8 @@ add_buff ( */ static void add_num_buff(buffheader_T *buf, long n) { - char_u number[32]; - - sprintf((char *)number, "%" PRId64, (int64_t)n); + char number[32]; + snprintf(number, sizeof(number), "%ld", n); add_buff(buf, number, -1L); } @@ -304,27 +302,29 @@ static void add_num_buff(buffheader_T *buf, long n) */ static void add_char_buff(buffheader_T *buf, int c) { - char_u bytes[MB_MAXBYTES + 1]; + char bytes[MB_MAXBYTES + 1]; + int len; - int i; - char_u temp[4]; - - if (IS_SPECIAL(c)) + if (IS_SPECIAL(c)) { len = 1; - else - len = (*mb_char2bytes)(c, bytes); - for (i = 0; i < len; ++i) { - if (!IS_SPECIAL(c)) - c = bytes[i]; + } else { + len = (*mb_char2bytes)(c, (char_u *)bytes); + } + for (int i = 0; i < len; i++) { + if (!IS_SPECIAL(c)) { + c = bytes[i]; + } + + char temp[4]; if (IS_SPECIAL(c) || c == K_SPECIAL || c == NUL) { - /* translate special key code into three byte sequence */ - temp[0] = K_SPECIAL; - temp[1] = (char_u)K_SECOND(c); - temp[2] = (char_u)K_THIRD(c); + // Translate special key code into three byte sequence. + temp[0] = (char)K_SPECIAL; + temp[1] = (char)K_SECOND(c); + temp[2] = (char)K_THIRD(c); temp[3] = NUL; } else { - temp[0] = (char_u)c; + temp[0] = (char)c; temp[1] = NUL; } add_buff(buf, temp, -1L); @@ -479,16 +479,14 @@ static int save_level = 0; void saveRedobuff(void) { - char_u *s; - if (save_level++ == 0) { save_redobuff = redobuff; redobuff.bh_first.b_next = NULL; save_old_redobuff = old_redobuff; old_redobuff.bh_first.b_next = NULL; - /* Make a copy, so that ":normal ." in a function works. */ - s = get_buffcont(&save_redobuff, FALSE); + // Make a copy, so that ":normal ." in a function works. + char *const s = (char *)get_buffcont(&save_redobuff, false); if (s != NULL) { add_buff(&redobuff, s, -1L); xfree(s); @@ -514,10 +512,11 @@ void restoreRedobuff(void) * Append "s" to the redo buffer. * K_SPECIAL and CSI should already have been escaped. */ -void AppendToRedobuff(char_u *s) +void AppendToRedobuff(const char *s) { - if (!block_redo) - add_buff(&redobuff, s, -1L); + if (!block_redo) { + add_buff(&redobuff, (const char *)s, -1L); + } } /* @@ -530,44 +529,47 @@ AppendToRedobuffLit ( int len /* length of "str" or -1 for up to the NUL */ ) { - char_u *s = str; - int c; - char_u *start; - - if (block_redo) + if (block_redo) { return; + } - while (len < 0 ? *s != NUL : s - str < len) { - /* Put a string of normal characters in the redo buffer (that's - * faster). */ - start = s; - while (*s >= ' ' && *s < DEL && (len < 0 || s - str < len)) - ++s; + const char *s = (const char *)str; + while (len < 0 ? *s != NUL : s - (const char *)str < len) { + // Put a string of normal characters in the redo buffer (that's + // faster). + const char *start = s; + while (*s >= ' ' && *s < DEL && (len < 0 || s - (const char *)str < len)) { + s++; + } - /* Don't put '0' or '^' as last character, just in case a CTRL-D is - * typed next. */ - if (*s == NUL && (s[-1] == '0' || s[-1] == '^')) - --s; - if (s > start) + // Don't put '0' or '^' as last character, just in case a CTRL-D is + // typed next. + if (*s == NUL && (s[-1] == '0' || s[-1] == '^')) { + s--; + } + if (s > start) { add_buff(&redobuff, start, (long)(s - start)); + } - if (*s == NUL || (len >= 0 && s - str >= len)) + if (*s == NUL || (len >= 0 && s - (const char *)str >= len)) { break; + } - /* Handle a special or multibyte character. */ - if (has_mbyte) - /* Handle composing chars separately. */ - c = mb_cptr2char_adv(&s); - else - c = *s++; - if (c < ' ' || c == DEL || (*s == NUL && (c == '0' || c == '^'))) + // Handle a special or multibyte character. + // Composing chars separately are handled separately. + const int c = (has_mbyte + ? mb_cptr2char_adv((const char_u **)&s) + : (uint8_t)(*s++)); + if (c < ' ' || c == DEL || (*s == NUL && (c == '0' || c == '^'))) { add_char_buff(&redobuff, Ctrl_V); + } - /* CTRL-V '0' must be inserted as CTRL-V 048 */ - if (*s == NUL && c == '0') - add_buff(&redobuff, (char_u *)"048", 3L); - else + // CTRL-V '0' must be inserted as CTRL-V 048. + if (*s == NUL && c == '0') { + add_buff(&redobuff, "048", 3L); + } else { add_char_buff(&redobuff, c); + } } } @@ -594,19 +596,19 @@ void AppendNumberToRedobuff(long n) * Append string "s" to the stuff buffer. * CSI and K_SPECIAL must already have been escaped. */ -void stuffReadbuff(char_u *s) +void stuffReadbuff(const char *s) { add_buff(&readbuf1, s, -1L); } /// Append string "s" to the redo stuff buffer. /// @remark CSI and K_SPECIAL must already have been escaped. -void stuffRedoReadbuff(char_u *s) +void stuffRedoReadbuff(const char *s) { add_buff(&readbuf2, s, -1L); } -void stuffReadbuffLen(char_u *s, long len) +void stuffReadbuffLen(const char *s, long len) { add_buff(&readbuf1, s, len); } @@ -616,19 +618,18 @@ void stuffReadbuffLen(char_u *s, long len) * escaping other K_SPECIAL and CSI bytes. * Change CR, LF and ESC into a space. */ -void stuffReadbuffSpec(char_u *s) +void stuffReadbuffSpec(const char *s) { - int c; - while (*s != NUL) { - if (*s == K_SPECIAL && s[1] != NUL && s[2] != NUL) { - /* Insert special key literally. */ - stuffReadbuffLen(s, 3L); + if ((uint8_t)(*s) == K_SPECIAL && s[1] != NUL && s[2] != NUL) { + // Insert special key literally. + stuffReadbuffLen(s, 3); s += 3; } else { - c = mb_ptr2char_adv(&s); - if (c == CAR || c == NL || c == ESC) + int c = mb_ptr2char_adv((const char_u **)&s); + if (c == CAR || c == NL || c == ESC) { c = ' '; + } stuffcharReadbuff(c); } } @@ -747,8 +748,8 @@ int start_redo(long count, int old_redo) /* copy the buffer name, if present */ if (c == '"') { - add_buff(&readbuf2, (char_u *)"\"", 1L); - c = read_redo(FALSE, old_redo); + add_buff(&readbuf2, "\"", 1L); + c = read_redo(false, old_redo); /* if a numbered buffer is used, increment the number */ if (c >= '1' && c < '9') @@ -1091,21 +1092,19 @@ static void gotchars(char_u *chars, size_t len) { char_u *s = chars; int c; - char_u buf[2]; // remember how many chars were last recorded if (Recording) { last_recorded_len += len; } - buf[1] = NUL; while (len--) { // Handle one byte at a time; no translation to be done. c = *s++; updatescript(c); if (Recording) { - buf[0] = (char_u)c; + char buf[2] = { (char)c, NUL }; add_buff(&recordbuff, buf, 1L); } } diff --git a/src/nvim/globals.h b/src/nvim/globals.h index d87407f099..c15287aa38 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -1199,6 +1199,7 @@ EXTERN char_u e_dirnotf[] INIT(= N_( "E919: Directory not found in '%s': \"%s\"")); EXTERN char_u e_unsupportedoption[] INIT(= N_("E519: Option not supported")); EXTERN char_u e_fnametoolong[] INIT(= N_("E856: Filename too long")); +EXTERN char_u e_float_as_string[] INIT(= N_("E806: using Float as a String")); EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM")); diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 621fa6fefc..d96848754c 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -625,7 +625,7 @@ static int utf_safe_read_char_adv(const char_u **s, size_t *n) * Get character at **pp and advance *pp to the next character. * Note: composing characters are skipped! */ -int mb_ptr2char_adv(char_u **pp) +int mb_ptr2char_adv(const char_u **const pp) { int c; @@ -638,7 +638,7 @@ int mb_ptr2char_adv(char_u **pp) * Get character at **pp and advance *pp to the next character. * Note: composing characters are returned as separate characters. */ -int mb_cptr2char_adv(char_u **pp) +int mb_cptr2char_adv(const char_u **pp) { int c; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 6ef929120e..d4919dc3b6 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1155,7 +1155,7 @@ static void normal_check_stuff_buffer(NormalState *s) if (need_start_insertmode && goto_im() && !VIsual_active) { need_start_insertmode = false; - stuffReadbuff((uint8_t *)"i"); // start insert mode next + stuffReadbuff("i"); // start insert mode next // skip the fileinfo message now, because it would be shown // after insert mode finishes! need_fileinfo = false; @@ -1469,8 +1469,9 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) * If 'cpoptions' does not contain 'r', insert the search * pattern to really repeat the same command. */ - if (vim_strchr(p_cpo, CPO_REDO) == NULL) + if (vim_strchr(p_cpo, CPO_REDO) == NULL) { AppendToRedobuffLit(cap->searchbuf, -1); + } AppendToRedobuff(NL_STR); } else if (cap->cmdchar == ':') { /* do_cmdline() has stored the first typed line in @@ -1853,10 +1854,11 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) break; case OP_FILTER: - if (vim_strchr(p_cpo, CPO_FILTER) != NULL) - AppendToRedobuff((char_u *)"!\r"); /* use any last used !cmd */ - else - bangredo = true; /* do_bang() will put cmd in redo buffer */ + if (vim_strchr(p_cpo, CPO_FILTER) != NULL) { + AppendToRedobuff("!\r"); // Use any last used !cmd. + } else { + bangredo = true; // do_bang() will put cmd in redo buffer. + } case OP_INDENT: case OP_COLON: @@ -2026,43 +2028,44 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) static void op_colon(oparg_T *oap) { stuffcharReadbuff(':'); - if (oap->is_VIsual) - stuffReadbuff((char_u *)"'<,'>"); - else { - /* - * Make the range look nice, so it can be repeated. - */ - if (oap->start.lnum == curwin->w_cursor.lnum) + if (oap->is_VIsual) { + stuffReadbuff("'<,'>"); + } else { + // Make the range look nice, so it can be repeated. + if (oap->start.lnum == curwin->w_cursor.lnum) { stuffcharReadbuff('.'); - else + } else { stuffnumReadbuff((long)oap->start.lnum); + } if (oap->end.lnum != oap->start.lnum) { stuffcharReadbuff(','); - if (oap->end.lnum == curwin->w_cursor.lnum) + if (oap->end.lnum == curwin->w_cursor.lnum) { stuffcharReadbuff('.'); - else if (oap->end.lnum == curbuf->b_ml.ml_line_count) + } else if (oap->end.lnum == curbuf->b_ml.ml_line_count) { stuffcharReadbuff('$'); - else if (oap->start.lnum == curwin->w_cursor.lnum) { - stuffReadbuff((char_u *)".+"); + } else if (oap->start.lnum == curwin->w_cursor.lnum) { + stuffReadbuff(".+"); stuffnumReadbuff(oap->line_count - 1); - } else + } else { stuffnumReadbuff((long)oap->end.lnum); + } } } - if (oap->op_type != OP_COLON) - stuffReadbuff((char_u *)"!"); + if (oap->op_type != OP_COLON) { + stuffReadbuff("!"); + } if (oap->op_type == OP_INDENT) { - stuffReadbuff(get_equalprg()); - stuffReadbuff((char_u *)"\n"); + stuffReadbuff((const char *)get_equalprg()); + stuffReadbuff("\n"); } else if (oap->op_type == OP_FORMAT) { if (*curbuf->b_p_fp != NUL) { - stuffReadbuff(curbuf->b_p_fp); + stuffReadbuff((const char *)curbuf->b_p_fp); } else if (*p_fp != NUL) { - stuffReadbuff(p_fp); + stuffReadbuff((const char *)p_fp); } else { - stuffReadbuff((char_u *)"fmt"); + stuffReadbuff("fmt"); } - stuffReadbuff((char_u *)"\n']"); + stuffReadbuff("\n']"); } /* @@ -2304,7 +2307,7 @@ do_mouse ( if (VIsual_active) { if (VIsual_select) { stuffcharReadbuff(Ctrl_G); - stuffReadbuff((char_u *)"\"+p"); + stuffReadbuff("\"+p"); } else { stuffcharReadbuff('y'); stuffcharReadbuff(K_MIDDLEMOUSE); @@ -4476,7 +4479,7 @@ static void nv_colon(cmdarg_T *cap) /* translate "count:" into ":.,.+(count - 1)" */ stuffcharReadbuff('.'); if (cap->count0 > 1) { - stuffReadbuff((char_u *)",.+"); + stuffReadbuff(",.+"); stuffnumReadbuff(cap->count0 - 1L); } } @@ -6156,17 +6159,15 @@ static void nv_abbrev(cmdarg_T *cap) */ static void nv_optrans(cmdarg_T *cap) { - static char_u *(ar[8]) = {(char_u *)"dl", (char_u *)"dh", - (char_u *)"d$", (char_u *)"c$", - (char_u *)"cl", (char_u *)"cc", - (char_u *)"yy", (char_u *)":s\r"}; - static char_u *str = (char_u *)"xXDCsSY&"; + static const char *(ar[]) = { "dl", "dh", "d$", "c$", "cl", "cc", "yy", + ":s\r" }; + static const char *str = "xXDCsSY&"; if (!checkclearopq(cap->oap)) { if (cap->count0) { stuffnumReadbuff(cap->count0); } - stuffReadbuff(ar[(int)(vim_strchr(str, cap->cmdchar) - str)]); + stuffReadbuff(ar[strchr(str, (char)cap->cmdchar) - str]); } cap->opcount = 0; } diff --git a/src/nvim/ops.c b/src/nvim/ops.c index fd1e2d20f2..68ef27222c 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -1109,7 +1109,6 @@ int insert_reg( ) { int retval = OK; - char_u *arg; int allocated; /* @@ -1125,21 +1124,24 @@ int insert_reg( if (regname != NUL && !valid_yank_reg(regname, false)) return FAIL; - if (regname == '.') /* insert last inserted text */ - retval = stuff_inserted(NUL, 1L, TRUE); - else if (get_spec_reg(regname, &arg, &allocated, TRUE)) { - if (arg == NULL) + char_u *arg; + if (regname == '.') { // Insert last inserted text. + retval = stuff_inserted(NUL, 1L, true); + } else if (get_spec_reg(regname, &arg, &allocated, true)) { + if (arg == NULL) { return FAIL; - stuffescaped(arg, literally); - if (allocated) + } + stuffescaped((const char *)arg, literally); + if (allocated) { xfree(arg); - } else { /* name or number register */ + } + } else { // Name or number register. yankreg_T *reg = get_yank_register(regname, YREG_PASTE); if (reg->y_array == NULL) { retval = FAIL; } else { for (size_t i = 0; i < reg->y_size; i++) { - stuffescaped(reg->y_array[i], literally); + stuffescaped((const char *)reg->y_array[i], literally); // Insert a newline between lines and after last line if // y_type is kMTLineWise. if (reg->y_type == kMTLineWise || i < reg->y_size - 1) { @@ -1156,29 +1158,29 @@ int insert_reg( * Stuff a string into the typeahead buffer, such that edit() will insert it * literally ("literally" TRUE) or interpret is as typed characters. */ -static void stuffescaped(char_u *arg, int literally) +static void stuffescaped(const char *arg, int literally) { - int c; - char_u *start; - while (*arg != NUL) { - /* Stuff a sequence of normal ASCII characters, that's fast. Also - * stuff K_SPECIAL to get the effect of a special key when "literally" - * is TRUE. */ - start = arg; - while ((*arg >= ' ' && *arg < DEL) || (*arg == K_SPECIAL && !literally)) - ++arg; - if (arg > start) + // Stuff a sequence of normal ASCII characters, that's fast. Also + // stuff K_SPECIAL to get the effect of a special key when "literally" + // is TRUE. + const char *const start = arg; + while ((*arg >= ' ' && *arg < DEL) || ((uint8_t)(*arg) == K_SPECIAL + && !literally)) { + arg++; + } + if (arg > start) { stuffReadbuffLen(start, (long)(arg - start)); + } /* stuff a single special character */ if (*arg != NUL) { - if (has_mbyte) - c = mb_cptr2char_adv(&arg); - else - c = *arg++; - if (literally && ((c < ' ' && c != TAB) || c == DEL)) + const int c = (has_mbyte + ? mb_cptr2char_adv((const char_u **)&arg) + : (uint8_t)(*arg++)); + if (literally && ((c < ' ' && c != TAB) || c == DEL)) { stuffcharReadbuff(Ctrl_V); + } stuffcharReadbuff(c); } } @@ -2663,7 +2665,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) // back to the previous line in the case of 'noautoindent' and // 'backspace' includes "eol". So we insert a dummy space for Ctrl_U // to consume. - stuffReadbuff((char_u *)"\n "); + stuffReadbuff("\n "); stuffcharReadbuff(Ctrl_U); } } @@ -2675,7 +2677,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) // character. Simulate it with motion commands after the insert. if (flags & PUT_CURSEND) { if (flags & PUT_LINE) { - stuffReadbuff((char_u *)"j0"); + stuffReadbuff("j0"); } else { // Avoid ringing the bell from attempting to move into the space after // the current line. We can stuff the readbuffer with "l" if: @@ -2705,7 +2707,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } } } else if (flags & PUT_LINE) { - stuffReadbuff((char_u *)"g'["); + stuffReadbuff("g'["); } // So the 'u' command restores cursor position after ".p, save the cursor @@ -4981,7 +4983,7 @@ void write_reg_contents(int name, const char_u *str, ssize_t len, write_reg_contents_ex(name, str, len, must_append, kMTUnknown, 0L); } -void write_reg_contents_lst(int name, char_u **strings, int maxlen, +void write_reg_contents_lst(int name, char_u **strings, bool must_append, MotionType yank_type, colnr_T block_len) { diff --git a/src/nvim/option.c b/src/nvim/option.c index 1881b329ec..8a80c46cf2 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -3419,15 +3419,18 @@ static char_u *set_chars_option(char_u **varp) && p[len] == ':' && p[len + 1] != NUL) { s = p + len + 1; - c1 = mb_ptr2char_adv(&s); - if (mb_char2cells(c1) > 1) + c1 = mb_ptr2char_adv((const char_u **)&s); + if (mb_char2cells(c1) > 1) { continue; + } if (tab[i].cp == &lcs_tab2) { - if (*s == NUL) + if (*s == NUL) { continue; - c2 = mb_ptr2char_adv(&s); - if (mb_char2cells(c2) > 1) + } + c2 = mb_ptr2char_adv((const char_u **)&s); + if (mb_char2cells(c2) > 1) { continue; + } } if (*s == ',' || *s == NUL) { if (round) { @@ -6887,8 +6890,8 @@ void set_fileformat(int eol_style, int opt_flags) need_maketitle = true; // Set window title later. } -/// Skip to next part of an option argument: Skip space and comma. -char_u *skip_to_option_part(char_u *p) +/// Skip to next part of an option argument: skip space and comma +char_u *skip_to_option_part(const char_u *p) { if (*p == ',') { p++; @@ -6896,7 +6899,7 @@ char_u *skip_to_option_part(char_u *p) while (*p == ' ') { p++; } - return p; + return (char_u *)p; } /// Isolate one part of a string option separated by `sep_chars`. diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 0372bc8a8c..3833a43f5f 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -622,11 +622,11 @@ int32_t os_getperm(const char *name) /// Set the permission of a file. /// /// @return `OK` for success, `FAIL` for failure. -int os_setperm(const char_u *name, int perm) +int os_setperm(const char *const name, int perm) FUNC_ATTR_NONNULL_ALL { int r; - RUN_UV_FS_FUNC(r, uv_fs_chmod, (const char *)name, perm, NULL); + RUN_UV_FS_FUNC(r, uv_fs_chmod, name, perm, NULL); return (r == kLibuvSuccess ? OK : FAIL); } diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 9c6f02f778..9baa53d2a2 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -2221,10 +2221,11 @@ collection: if (*regparse == '[') endc = get_coll_element(®parse); if (endc == 0) { - if (has_mbyte) - endc = mb_ptr2char_adv(®parse); - else + if (has_mbyte) { + endc = mb_ptr2char_adv((const char_u **)®parse); + } else { endc = *regparse++; + } } /* Handle \o40, \x20 and \u20AC style sequences */ @@ -6271,8 +6272,8 @@ static int cstrncmp(char_u *s1, char_u *s2, int *n) str2 = s2; c1 = c2 = 0; while ((int)(str1 - s1) < *n) { - c1 = mb_ptr2char_adv(&str1); - c2 = mb_ptr2char_adv(&str2); + c1 = mb_ptr2char_adv((const char_u **)&str1); + c2 = mb_ptr2char_adv((const char_u **)&str2); /* decompose the character if necessary, into 'base' characters * because I don't care about Arabic, I will hard-code the Hebrew @@ -6586,7 +6587,6 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, if (expr != NULL) { typval_T argv[2]; int dummy; - char_u buf[NUMBUFLEN]; typval_T rettv; staticList10_T matchList; @@ -6616,7 +6616,8 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, clear_submatch_list(&matchList); } } - eval_result = get_tv_string_buf_chk(&rettv, buf); + char buf[NUMBUFLEN]; + eval_result = (char_u *)tv_get_string_buf_chk(&rettv, buf); if (eval_result != NULL) { eval_result = vim_strsave(eval_result); } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 505d04ed56..cf460adb82 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -3141,7 +3141,7 @@ win_line ( p_extra = extra; c = *p_extra; - mb_c = mb_ptr2char_adv(&p_extra); + mb_c = mb_ptr2char_adv((const char_u **)&p_extra); mb_utf8 = (c >= 0x80); n_extra = (int)STRLEN(p_extra); c_extra = NUL; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 84bee9b97f..d4f49bffb2 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -2309,10 +2309,11 @@ int captype(char_u *word, char_u *end) for (p = word; !spell_iswordp_nmw(p, curwin); mb_ptr_adv(p)) if (end == NULL ? *p == NUL : p >= end) return 0; // only non-word characters, illegal word - if (has_mbyte) - c = mb_ptr2char_adv(&p); - else + if (has_mbyte) { + c = mb_ptr2char_adv((const char_u **)&p); + } else { c = *p++; + } firstcap = allcap = SPELL_ISUPPER(c); // Need to check all letters to find a word with mixed upper/lower. @@ -2607,14 +2608,15 @@ static bool spell_iswordp(char_u *p, win_T *wp) // Returns true if "p" points to a word character. // Unlike spell_iswordp() this doesn't check for "midword" characters. -bool spell_iswordp_nmw(char_u *p, win_T *wp) +bool spell_iswordp_nmw(const char_u *p, win_T *wp) { int c; if (has_mbyte) { c = mb_ptr2char(p); - if (c > 255) + if (c > 255) { return spell_mb_isword_class(mb_get_class(p), wp); + } return spelltab.st_isw[c]; } return spelltab.st_isw[*p]; @@ -2675,7 +2677,7 @@ int spell_casefold(char_u *str, int len, char_u *buf, int buflen) buf[outi] = NUL; return FAIL; } - c = mb_cptr2char_adv(&p); + c = mb_cptr2char_adv((const char_u **)&p); outi += mb_char2bytes(SPELL_TOFOLD(c), buf + outi); } buf[outi] = NUL; @@ -2937,7 +2939,7 @@ void spell_suggest(int count) // For redo we use a change-word command. ResetRedobuff(); - AppendToRedobuff((char_u *)"ciw"); + AppendToRedobuff("ciw"); AppendToRedobuffLit(p + c, stp->st_wordlen + sug.su_badlen - stp->st_orglen); AppendCharToRedobuff(ESC); @@ -3406,17 +3408,19 @@ void onecap_copy(char_u *word, char_u *wcopy, bool upper) int l; p = word; - if (has_mbyte) - c = mb_cptr2char_adv(&p); - else + if (has_mbyte) { + c = mb_cptr2char_adv((const char_u **)&p); + } else { c = *p++; - if (upper) + } + if (upper) { c = SPELL_TOUPPER(c); - else + } else { c = SPELL_TOFOLD(c); - if (has_mbyte) + } + if (has_mbyte) { l = mb_char2bytes(c, wcopy); - else { + } else { l = 1; wcopy[0] = c; } @@ -3433,10 +3437,11 @@ static void allcap_copy(char_u *word, char_u *wcopy) d = wcopy; for (s = word; *s != NUL; ) { - if (has_mbyte) - c = mb_cptr2char_adv(&s); - else + if (has_mbyte) { + c = mb_cptr2char_adv((const char_u **)&s); + } else { c = *s++; + } if (c == 0xdf) { c = 'S'; @@ -5938,12 +5943,12 @@ static void spell_soundfold_sofo(slang_T *slang, char_u *inword, char_u *res) // The sl_sal_first[] table contains the translation for chars up to // 255, sl_sal the rest. for (s = inword; *s != NUL; ) { - c = mb_cptr2char_adv(&s); - if (enc_utf8 ? utf_class(c) == 0 : ascii_iswhite(c)) + c = mb_cptr2char_adv((const char_u **)&s); + if (enc_utf8 ? utf_class(c) == 0 : ascii_iswhite(c)) { c = ' '; - else if (c < 256) + } else if (c < 256) { c = slang->sl_sal_first[c]; - else { + } else { ip = ((int **)slang->sl_sal.ga_data)[c & 0xff]; if (ip == NULL) // empty list, can't match c = NUL; @@ -6228,9 +6233,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) int word[MAXWLEN]; int wres[MAXWLEN]; int l; - char_u *s; int *ws; - char_u *t; int *pf; int i, j, z; int reslen; @@ -6250,9 +6253,9 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) // Remove accents, if wanted. We actually remove all non-word characters. // But keep white space. wordlen = 0; - for (s = inword; *s != NUL; ) { - t = s; - c = mb_cptr2char_adv(&s); + for (const char_u *s = inword; *s != NUL; ) { + const char_u *t = s; + c = mb_cptr2char_adv((const char_u **)&s); if (slang->sl_rem_accents) { if (enc_utf8 ? utf_class(c) == 0 : ascii_iswhite(c)) { if (did_white) @@ -6261,8 +6264,9 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) did_white = true; } else { did_white = false; - if (!spell_iswordp_nmw(t, curwin)) + if (!spell_iswordp_nmw(t, curwin)) { continue; + } } } word[wordlen++] = c; @@ -6309,7 +6313,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) continue; ++k; } - s = smp[n].sm_rules; + char_u *s = smp[n].sm_rules; pri = 5; // default priority p0 = *s; @@ -6708,25 +6712,30 @@ soundalike_score ( // support multi-byte characters. static int spell_edit_score(slang_T *slang, char_u *badword, char_u *goodword) { - int *cnt; - int badlen, goodlen; // lengths including NUL + int *cnt; int j, i; int t; int bc, gc; int pbc, pgc; - char_u *p; int wbadword[MAXWLEN]; int wgoodword[MAXWLEN]; const bool l_has_mbyte = has_mbyte; + // Lengths with NUL. + int badlen; + int goodlen; if (l_has_mbyte) { // Get the characters from the multi-byte strings and put them in an // int array for easy access. - for (p = badword, badlen = 0; *p != NUL; ) + badlen = 0; + for (const char_u *p = badword; *p != NUL; ) { wbadword[badlen++] = mb_cptr2char_adv(&p); + } wbadword[badlen++] = 0; - for (p = goodword, goodlen = 0; *p != NUL; ) + goodlen = 0; + for (const char_u *p = goodword; *p != NUL; ) { wgoodword[goodlen++] = mb_cptr2char_adv(&p); + } wgoodword[goodlen++] = 0; } else { badlen = (int)STRLEN(badword) + 1; @@ -6960,19 +6969,20 @@ static int spell_edit_score_limit_w(slang_T *slang, char_u *badword, char_u *goo int score_off; int minscore; int round; - char_u *p; int wbadword[MAXWLEN]; int wgoodword[MAXWLEN]; // Get the characters from the multi-byte strings and put them in an // int array for easy access. bi = 0; - for (p = badword; *p != NUL; ) + for (const char_u *p = badword; *p != NUL; ) { wbadword[bi++] = mb_cptr2char_adv(&p); + } wbadword[bi++] = 0; gi = 0; - for (p = goodword; *p != NUL; ) + for (const char_u *p = goodword; *p != NUL; ) { wgoodword[gi++] = mb_cptr2char_adv(&p); + } wgoodword[gi++] = 0; // The idea is to go from start to end over the words. So long as diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 6ba2801203..4d7ff558ad 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -1435,7 +1435,7 @@ static int set_sofo(slang_T *lp, char_u *from, char_u *to) // First count the number of items for each list. Temporarily use // sl_sal_first[] for this. for (p = from, s = to; *p != NUL && *s != NUL; ) { - c = mb_cptr2char_adv(&p); + c = mb_cptr2char_adv((const char_u **)&p); mb_cptr_adv(s); if (c >= 256) ++lp->sl_sal_first[c & 0xff]; @@ -1455,8 +1455,8 @@ static int set_sofo(slang_T *lp, char_u *from, char_u *to) // list. memset(lp->sl_sal_first, 0, sizeof(salfirst_T) * 256); for (p = from, s = to; *p != NUL && *s != NUL; ) { - c = mb_cptr2char_adv(&p); - i = mb_cptr2char_adv(&s); + c = mb_cptr2char_adv((const char_u **)&p); + i = mb_cptr2char_adv((const char_u **)&s); if (c >= 256) { // Append the from-to chars at the end of the list with // the low byte. @@ -1542,8 +1542,9 @@ static int *mb_str2wide(char_u *s) int i = 0; int *res = xmalloc((mb_charlen(s) + 1) * sizeof(int)); - for (char_u *p = s; *p != NUL; ) - res[i++] = mb_ptr2char_adv(&p); + for (char_u *p = s; *p != NUL; ) { + res[i++] = mb_ptr2char_adv((const char_u **)&p); + } res[i] = NUL; return res; @@ -2486,13 +2487,14 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) // Check that every character appears only once. for (p = items[1]; *p != NUL; ) { - c = mb_ptr2char_adv(&p); + c = mb_ptr2char_adv((const char_u **)&p); if ((!GA_EMPTY(&spin->si_map) && vim_strchr(spin->si_map.ga_data, c) != NULL) - || vim_strchr(p, c) != NULL) + || vim_strchr(p, c) != NULL) { smsg(_("Duplicate character in MAP in %s line %d"), fname, lnum); + } } // We simply concatenate all the MAP strings, separated by @@ -2717,12 +2719,12 @@ static unsigned get_affitem(int flagtype, char_u **pp) } res = getdigits_int(pp); } else { - res = mb_ptr2char_adv(pp); + res = mb_ptr2char_adv((const char_u **)pp); if (flagtype == AFT_LONG || (flagtype == AFT_CAPLONG && res >= 'A' && res <= 'Z')) { if (**pp == NUL) return 0; - res = mb_ptr2char_adv(pp) + (res << 16); + res = mb_ptr2char_adv((const char_u **)pp) + (res << 16); } } return res; @@ -2823,12 +2825,14 @@ static bool flag_in_afflist(int flagtype, char_u *afflist, unsigned flag) case AFT_CAPLONG: case AFT_LONG: for (p = afflist; *p != NUL; ) { - n = mb_ptr2char_adv(&p); + n = mb_ptr2char_adv((const char_u **)&p); if ((flagtype == AFT_LONG || (n >= 'A' && n <= 'Z')) - && *p != NUL) - n = mb_ptr2char_adv(&p) + (n << 16); - if (n == flag) + && *p != NUL) { + n = mb_ptr2char_adv((const char_u **)&p) + (n << 16); + } + if (n == flag) { return true; + } } break; @@ -5436,9 +5440,10 @@ static void init_spellfile(void) fname = LANGP_ENTRY(curwin->w_s->b_langp, 0) ->lp_slang->sl_fname; vim_snprintf((char *)buf + l, MAXPATHL - l, ".%s.add", - fname != NULL - && strstr((char *)path_tail(fname), ".ascii.") != NULL - ? (char_u *)"ascii" : spell_enc()); + ((fname != NULL + && strstr((char *)path_tail(fname), ".ascii.") != NULL) + ? "ascii" + : (const char *)spell_enc())); set_option_value("spellfile", 0L, (const char *)buf, OPT_LOCAL); break; } @@ -5465,9 +5470,9 @@ static int set_spell_chartab(char_u *fol, char_u *low, char_u *upp) EMSG(_(e_affform)); return FAIL; } - f = mb_ptr2char_adv(&pf); - l = mb_ptr2char_adv(&pl); - u = mb_ptr2char_adv(&pu); + f = mb_ptr2char_adv((const char_u **)&pf); + l = mb_ptr2char_adv((const char_u **)&pl); + u = mb_ptr2char_adv((const char_u **)&pu); // Every character that appears is a word character. if (f < 256) new_st.st_isw[f] = true; @@ -5532,7 +5537,7 @@ set_spell_charflags ( } if (*p != NUL) { - c = mb_ptr2char_adv(&p); + c = mb_ptr2char_adv((const char_u **)&p); new_st.st_fold[i + 128] = c; if (i + 128 != c && new_st.st_isu[i + 128] && c < 256) new_st.st_upper[c] = i + 128; @@ -5619,12 +5624,13 @@ static void set_map_str(slang_T *lp, char_u *map) // "aaa/bbb/ccc/". Fill sl_map_array[c] with the character before c and // before the same slash. For characters above 255 sl_map_hash is used. for (p = map; *p != NUL; ) { - c = mb_cptr2char_adv(&p); - if (c == '/') + c = mb_cptr2char_adv((const char_u **)&p); + if (c == '/') { headc = 0; - else { - if (headc == 0) + } else { + if (headc == 0) { headc = c; + } // Characters above 255 don't fit in sl_map_array[], put them in // the hash table. Each entry is the char, a NUL the headchar and diff --git a/src/nvim/strings.c b/src/nvim/strings.c index e20979c307..5dcffe00e0 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -598,22 +598,21 @@ static varnumber_T tv_nr(typval_T *tvs, int *idxp) /// free "*tofree". /// /// @return String value or NULL in case of error. -static char *tv_str(typval_T *tvs, int *idxp, char ** const tofree) +static const char *tv_str(typval_T *tvs, int *idxp, char **const tofree) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { int idx = *idxp - 1; - char *s = NULL; + const char *s = NULL; if (tvs[idx].v_type == VAR_UNKNOWN) { EMSG(_(e_printf)); } else { (*idxp)++; if (tvs[idx].v_type == VAR_STRING || tvs[idx].v_type == VAR_NUMBER) { - s = (char *)get_tv_string_chk(&tvs[idx]); + s = tv_get_string_chk(&tvs[idx]); *tofree = NULL; } else { - s = encode_tv2echo(&tvs[idx], NULL); - *tofree = s; + s = *tofree = encode_tv2echo(&tvs[idx], NULL); } } return s; @@ -953,7 +952,7 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, case 's': case 'S': str_arg = tvs ? tv_str(tvs, &arg_idx, &tofree) - : va_arg(ap, char *); + : va_arg(ap, const char *); if (!str_arg) { str_arg = "[NULL]"; str_arg_l = 6; diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 010ef2c8fa..4d4e8d9bb9 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -1140,7 +1140,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, EMSG2(_(e_not_open), file_name); goto theend; } - (void)os_setperm((char_u *)file_name, perm); + (void)os_setperm(file_name, perm); if (p_verbose > 0) { verbose_enter(); smsg(_("Writing undo file: %s"), file_name); @@ -1165,7 +1165,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, && os_fileinfo(file_name, &file_info_new) && file_info_old.stat.st_gid != file_info_new.stat.st_gid && os_fchown(fd, (uv_uid_t)-1, (uv_gid_t)file_info_old.stat.st_gid)) { - os_setperm((char_u *)file_name, (perm & 0707) | ((perm & 07) << 3)); + os_setperm(file_name, (perm & 0707) | ((perm & 07) << 3)); } # ifdef HAVE_SELINUX if (buf->b_ffname != NULL) diff --git a/test/functional/eval/input_spec.lua b/test/functional/eval/input_spec.lua new file mode 100644 index 0000000000..393fc10175 --- /dev/null +++ b/test/functional/eval/input_spec.lua @@ -0,0 +1,38 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') + +local feed = helpers.feed +local clear = helpers.clear +local command = helpers.command + +local screen + +before_each(function() + clear() + screen = Screen.new(25, 5) + screen:attach() +end) + +describe('input()', function() + it('works correctly with multiline prompts', function() + feed([[:call input("Test\nFoo")]]) + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + Test | + Foo^ | + ]], {{bold=true, foreground=Screen.colors.Blue}}) + end) + it('works correctly with multiline prompts and :echohl', function() + command('hi Test ctermfg=Red guifg=Red term=bold') + feed([[:echohl Test | call input("Test\nFoo")]]) + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + {2:Test} | + {2:Foo}^ | + ]], {{bold=true, foreground=Screen.colors.Blue}, {foreground=Screen.colors.Red}}) + end) +end) diff --git a/test/functional/eval/match_functions_spec.lua b/test/functional/eval/match_functions_spec.lua index f6bad59c66..3150d89f62 100644 --- a/test/functional/eval/match_functions_spec.lua +++ b/test/functional/eval/match_functions_spec.lua @@ -44,3 +44,18 @@ describe('setmatches()', function() eq({}, funcs.getmatches()) end) end) + +describe('matchadd()', function() + it('correctly works when first two arguments and conceal are numbers at once', + function() + command('hi def link 1 PreProc') + eq(4, funcs.matchadd(1, 2, 3, 4, {conceal=5})) + eq({{ + group='1', + pattern='2', + priority=3, + id=4, + conceal='5', + }}, funcs.getmatches()) + end) +end) diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua index 057245db1c..8cc717483e 100644 --- a/test/functional/ex_cmds/dict_notifications_spec.lua +++ b/test/functional/ex_cmds/dict_notifications_spec.lua @@ -9,7 +9,7 @@ local eval = helpers.eval describe('dictionary change notifications', function() local channel - setup(function() + before_each(function() clear() channel = nvim('get_api_info')[1] nvim('set_var', 'channel', channel) @@ -18,19 +18,15 @@ describe('dictionary change notifications', function() -- the same set of tests are applied to top-level dictionaries(g:, b:, w: and -- t:) and a dictionary variable, so we generate them in the following -- function. - local function gentests(dict_expr, dict_expr_suffix, dict_init) - if not dict_expr_suffix then - dict_expr_suffix = '' - end - + local function gentests(dict_expr, dict_init) local function update(opval, key) if not key then key = 'watched' end if opval == '' then - nvim('command', "unlet "..dict_expr..dict_expr_suffix..key) + command(('unlet %s[\'%s\']'):format(dict_expr, key)) else - nvim('command', "let "..dict_expr..dict_expr_suffix..key.." "..opval) + command(('let %s[\'%s\'] %s'):format(dict_expr, key, opval)) end end @@ -50,7 +46,7 @@ describe('dictionary change notifications', function() describe(dict_expr .. ' watcher', function() if dict_init then - setup(function() + before_each(function() source(dict_init) end) end @@ -143,6 +139,32 @@ describe('dictionary change notifications', function() ]]) end) + it('is triggered for empty keys', function() + command([[ + call dictwatcheradd(]]..dict_expr..[[, "", "g:Changed") + ]]) + update('= 1', '') + verify_value({new = 1}, '') + update('= 2', '') + verify_value({old = 1, new = 2}, '') + command([[ + call dictwatcherdel(]]..dict_expr..[[, "", "g:Changed") + ]]) + end) + + it('is triggered for empty keys when using catch-all *', function() + command([[ + call dictwatcheradd(]]..dict_expr..[[, "*", "g:Changed") + ]]) + update('= 1', '') + verify_value({new = 1}, '') + update('= 2', '') + verify_value({old = 1, new = 2}, '') + command([[ + call dictwatcherdel(]]..dict_expr..[[, "*", "g:Changed") + ]]) + end) + -- test a sequence of updates of different types to ensure proper memory -- management(with ASAN) local function test_updates(tests) @@ -190,10 +212,10 @@ describe('dictionary change notifications', function() gentests('b:') gentests('w:') gentests('t:') - gentests('g:dict_var', '.', 'let g:dict_var = {}') + gentests('g:dict_var', 'let g:dict_var = {}') describe('multiple watchers on the same dict/key', function() - setup(function() + before_each(function() source([[ function! g:Watcher1(dict, key, value) call rpcnotify(g:channel, '1', a:key, a:value) @@ -213,6 +235,9 @@ describe('dictionary change notifications', function() end) it('only removes watchers that fully match dict, key and callback', function() + nvim('command', 'let g:key = "value"') + eq({'notification', '1', {'key', {new = 'value'}}}, next_msg()) + eq({'notification', '2', {'key', {new = 'value'}}}, next_msg()) nvim('command', 'call dictwatcherdel(g:, "key", "g:Watcher1")') nvim('command', 'let g:key = "v2"') eq({'notification', '2', {'key', {old = 'value', new = 'v2'}}}, next_msg()) @@ -220,6 +245,17 @@ describe('dictionary change notifications', function() end) describe('errors', function() + before_each(function() + source([[ + function! g:Watcher1(dict, key, value) + call rpcnotify(g:channel, '1', a:key, a:value) + endfunction + function! g:Watcher2(dict, key, value) + call rpcnotify(g:channel, '2', a:key, a:value) + endfunction + ]]) + end) + -- WARNING: This suite depends on the above tests it('fails to remove if no watcher with matching callback is found', function() eq("Vim(call):Couldn't find a watcher matching key and callback", @@ -236,15 +272,10 @@ describe('dictionary change notifications', function() command('call dictwatcherdel(g:, "key", "g:InvalidCb")') end) - it('fails with empty keys', function() - eq("Vim(call):E713: Cannot use empty key for Dictionary", - exc_exec('call dictwatcheradd(g:, "", "g:Watcher1")')) - eq("Vim(call):E713: Cannot use empty key for Dictionary", - exc_exec('call dictwatcherdel(g:, "", "g:Watcher1")')) - end) - it('does not fail to replace a watcher function', function() source([[ + let g:key = 'v2' + call dictwatcheradd(g:, "key", "g:Watcher2") function! g:ReplaceWatcher2() function! g:Watcher2(dict, key, value) call rpcnotify(g:channel, '2b', a:key, a:value) From 5df35297f832b3247c18253c916be6066c603739 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Sep 2016 02:50:50 +0300 Subject: [PATCH 0191/1671] eval: Remove eval_expr() completely --- runtime/doc/if_pyth.txt | 5 ----- src/nvim/eval.c | 17 ----------------- test/unit/eval/tricks_spec.lua | 22 +++++++++++++--------- 3 files changed, 13 insertions(+), 31 deletions(-) diff --git a/runtime/doc/if_pyth.txt b/runtime/doc/if_pyth.txt index f2a7d91bb7..6321175420 100644 --- a/runtime/doc/if_pyth.txt +++ b/runtime/doc/if_pyth.txt @@ -181,11 +181,6 @@ vim.eval(str) *python-eval* # string.atoi() to convert to # a number. - :py tagList = vim.eval('taglist("eval_expr")') -< The latter will return a python list of python dicts, for instance: - [{'cmd': '/^eval_expr(arg, nextcmd)$/', 'static': 0, 'name': - 'eval_expr', 'kind': 'f', 'filename': './src/eval.c'}] - vim.bindeval(str) *python-bindeval* Like |python-eval|, but returns special objects described in |python-bindeval-objects|. These python objects let you modify (|List| diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 3a0075e48a..51ffa8887d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1178,23 +1178,6 @@ int get_spellword(list_T *list, const char **pp) return tv_get_number(&li->li_tv); } -/* - * Top level evaluation function. - * Returns an allocated typval_T with the result. - * Returns NULL when there is an error. - */ -typval_T *eval_expr(char_u *arg, char_u **nextcmd) -{ - typval_T *tv = xmalloc(sizeof(typval_T)); - - if (eval0(arg, tv, nextcmd, TRUE) == FAIL) { - xfree(tv); - return NULL; - } - - return tv; -} - // Call some vimL function and return the result in "*rettv". // Uses argv[argc] for the function arguments. Only Number and String diff --git a/test/unit/eval/tricks_spec.lua b/test/unit/eval/tricks_spec.lua index 7f0a445f2c..54029734fb 100644 --- a/test/unit/eval/tricks_spec.lua +++ b/test/unit/eval/tricks_spec.lua @@ -6,13 +6,17 @@ local to_cstr = helpers.to_cstr local ffi = helpers.ffi local eq = helpers.eq -local eval = cimport('./src/nvim/eval.h', './src/nvim/memory.h') +local eval = cimport('./src/nvim/eval.h', './src/nvim/eval/typval.h', + './src/nvim/memory.h') -local eval_expr = function(expr) - return ffi.gc(eval.eval_expr(to_cstr(expr), nil), function(tv) - eval.tv_clear(tv) - eval.xfree(tv) - end) +local eval0 = function(expr) + local tv = ffi.gc(ffi.new('typval_T', {v_type=eval.VAR_UNKNOWN}), + eval.tv_clear) + if eval.eval0(to_cstr(expr), tv, nil, true) == 0 then + return nil + else + return tv + end end describe('NULL typval_T', function() @@ -25,19 +29,19 @@ describe('NULL typval_T', function() while os.getenv(unexistent_env) ~= nil do unexistent_env = unexistent_env .. '_XXX' end - local rettv = eval_expr('$' .. unexistent_env) + local rettv = eval0('$' .. unexistent_env) eq(eval.VAR_STRING, rettv.v_type) eq(nil, rettv.vval.v_string) end) itp('is produced by v:_null_list', function() - local rettv = eval_expr('v:_null_list') + local rettv = eval0('v:_null_list') eq(eval.VAR_LIST, rettv.v_type) eq(nil, rettv.vval.v_list) end) itp('is produced by v:_null_dict', function() - local rettv = eval_expr('v:_null_dict') + local rettv = eval0('v:_null_dict') eq(eval.VAR_DICT, rettv.v_type) eq(nil, rettv.vval.v_dict) end) From 31a3158d0b574385186ab55c074edf85356d1d6c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Sep 2016 03:46:49 +0300 Subject: [PATCH 0192/1671] eval: Make sort always stable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Should fix test failures on QB: 20:00:51,837 INFO - not ok 420 - sort() sorts “wrong” values between -0.0001 and 0.0001, preserving order 20:00:51,837 INFO - # test/functional/eval/sort_spec.lua @ 21 20:00:51,837 INFO - # Failure message: test/functional/eval/sort_spec.lua:39: Expected objects to be the same. 20:00:51,837 INFO - # Passed in: 20:00:51,837 INFO - # (string) '[-1.0e-4, v:true, v:false, v:null, function('tr'), {'a': 42}, 'check', [], 1.0e-4]' 20:00:51,837 INFO - # Expected: 20:00:51,837 INFO - # (string) '[-1.0e-4, function('tr'), v:true, v:false, v:null, [], {'a': 42}, 'check', 1.0e-4]' 20:00:51,837 INFO - # stack traceback: 20:00:51,837 INFO - # test/functional/eval/sort_spec.lua:39: in function 20:00:51,837 INFO - # --- src/nvim/eval.c | 58 ++++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 51ffa8887d..2dc1cb50af 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -15022,58 +15022,61 @@ static sortinfo_T *sortinfo = NULL; */ static int item_compare(const void *s1, const void *s2, bool keep_zero) { - sortItem_T *si1, *si2; - char_u *p1; - char_u *p2; - char_u *tofree1 = NULL; - char_u *tofree2 = NULL; + sortItem_T *const si1 = (sortItem_T *)s1; + sortItem_T *const si2 = (sortItem_T *)s2; + + typval_T *const tv1 = &si1->item->li_tv; + typval_T *const tv2 = &si2->item->li_tv; + int res; - si1 = (sortItem_T *)s1; - si2 = (sortItem_T *)s2; - typval_T *tv1 = &si1->item->li_tv; - typval_T *tv2 = &si2->item->li_tv; - if (sortinfo->item_compare_numbers) { - const varnumber_T v1 = tv_get_number(tv1); - const varnumber_T v2 = tv_get_number(tv2); + const long v1 = tv_get_number(tv1); + const long v2 = tv_get_number(tv2); - return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + goto item_compare_end; } if (sortinfo->item_compare_float) { const float_T v1 = tv_get_float(tv1); const float_T v2 = tv_get_float(tv2); - return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + goto item_compare_end; } + char *tofree1 = NULL; + char *tofree2 = NULL; + char *p1; + char *p2; + // encode_tv2string() puts quotes around a string and allocates memory. Don't // do that for string variables. Use a single quote when comparing with // a non-string to do what the docs promise. if (tv1->v_type == VAR_STRING) { if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric) { - p1 = (char_u *)"'"; + p1 = "'"; } else { - p1 = tv1->vval.v_string; + p1 = (char *)tv1->vval.v_string; } } else { - tofree1 = p1 = (char_u *) encode_tv2string(tv1, NULL); + tofree1 = p1 = encode_tv2string(tv1, NULL); } if (tv2->v_type == VAR_STRING) { if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric) { - p2 = (char_u *)"'"; + p2 = "'"; } else { - p2 = tv2->vval.v_string; + p2 = (char *)tv2->vval.v_string; } } else { - tofree2 = p2 = (char_u *) encode_tv2string(tv2, NULL); + tofree2 = p2 = encode_tv2string(tv2, NULL); } if (p1 == NULL) { - p1 = (char_u *)""; + p1 = ""; } if (p2 == NULL) { - p2 = (char_u *)""; + p2 = ""; } if (!sortinfo->item_compare_numeric) { if (sortinfo->item_compare_ic) { @@ -15083,19 +15086,20 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) } } else { double n1, n2; - n1 = strtod((char *)p1, (char **)&p1); - n2 = strtod((char *)p2, (char **)&p2); + n1 = strtod(p1, &p1); + n2 = strtod(p2, &p2); res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1; } + xfree(tofree1); + xfree(tofree2); + +item_compare_end: // When the result would be zero, compare the item indexes. Makes the // sort stable. if (res == 0 && !keep_zero) { res = si1->idx > si2->idx ? 1 : -1; } - - xfree(tofree1); - xfree(tofree2); return res; } From 6cc3d59ec8e4d6f32c8c3d9755c625e32512b8e2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Sep 2016 04:29:56 +0300 Subject: [PATCH 0193/1671] misc1: Refactor ask_yesno() --- src/nvim/misc1.c | 49 ++++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index d751f13644..0b74b4437e 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -2211,39 +2211,44 @@ change_warning ( } } -/* - * Ask for a reply from the user, a 'y' or a 'n'. - * No other characters are accepted, the message is repeated until a valid - * reply is entered or CTRL-C is hit. - * If direct is TRUE, don't use vgetc() but ui_inchar(), don't get characters - * from any buffers but directly from the user. - * - * return the 'y' or 'n' - */ -int ask_yesno(const char *str, bool direct) +/// Ask for a reply from the user, 'y' or 'n' +/// +/// No other characters are accepted, the message is repeated until a valid +/// reply is entered or is hit. +/// +/// @param[in] str Prompt: question to ask user. Is always followed by +/// " (y/n)?". +/// @param[in] direct Determines what function to use to get user input. If +/// true then ui_inchar() will be used, otherwise vgetc(). +/// I.e. when direct is true then characters are obtained +/// directly from the user without buffers involved. +/// +/// @return 'y' or 'n'. Last is also what will be returned in case of interrupt. +int ask_yesno(const char *const str, const bool direct) { - int r = ' '; - int save_State = State; + const int save_State = State; no_wait_return++; - State = CONFIRM; // mouse behaves like with :confirm - setmouse(); // disables mouse for xterm + State = CONFIRM; // Mouse behaves like with :confirm. + setmouse(); // Disable mouse in xterm. no_mapping++; + int r = ' '; while (r != 'y' && r != 'n') { - /* same highlighting as for wait_return */ - smsg_attr(hl_attr(HLF_R), - "%s (y/n)?", str); - if (direct) + // Same highlighting as for wait_return. + smsg_attr(hl_attr(HLF_R), "%s (y/n)?", str); + if (direct) { r = get_keystroke(); - else + } else { r = plain_vgetc(); - if (r == Ctrl_C || r == ESC) + } + if (r == Ctrl_C || r == ESC) { r = 'n'; - msg_putchar(r); /* show what you typed */ + } + msg_putchar(r); // Show what you typed. ui_flush(); } - --no_wait_return; + no_wait_return--; State = save_State; setmouse(); no_mapping--; From c4fe656fef5ade20da4861a2f1e5d0f776b0df8f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Sep 2016 17:56:24 +0300 Subject: [PATCH 0194/1671] typval.h: Allow non-var expressions in TV_DICT_ITER first argument --- src/nvim/eval.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 2dc1cb50af..a5b0a65497 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -11248,26 +11248,26 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) } -/* - * Turn a dict into a list: - * "what" == 0: list of keys - * "what" == 1: list of values - * "what" == 2: list of items - */ -static void dict_list(typval_T *argvars, typval_T *rettv, int what) +/// Turn a dictionary into a list +/// +/// @param[in] tv Dictionary to convert. Is checked for actually being +/// a dictionary, will give an error if not. +/// @param[out] rettv Location where result will be saved. +/// @param[in] what What to save in rettv. +static void dict_list(typval_T *const tv, typval_T *const rettv, + const DictListType what) { - if (argvars[0].v_type != VAR_DICT) { - EMSG(_(e_dictreq)); + if (tv->v_type != VAR_DICT) { + emsgf(_(e_dictreq)); return; } - dict_T *const d = argvars[0].vval.v_dict; - if (d == NULL) { + if (tv->vval.v_dict == NULL) { return; } tv_list_alloc_ret(rettv); - TV_DICT_ITER(d, di, { + TV_DICT_ITER(tv->vval.v_dict, di, { listitem_T *const li = tv_list_item_alloc(); tv_list_append(rettv->vval.v_list, li); From 86fc4580b83f20211d7f85566e84a0e1dad0a136 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Sep 2016 18:40:19 +0300 Subject: [PATCH 0195/1671] eval: Fix max_min functions Found two bugs: 1. Multiple unneeded error messages, vim/vim#1039. 2. Unformatted error string, vim/vim#1040. --- src/nvim/eval.c | 76 +++++++++---------- src/nvim/eval/typval.h | 4 +- .../functional/eval/minmax_functions_spec.lua | 51 +++++++++++++ 3 files changed, 89 insertions(+), 42 deletions(-) create mode 100644 test/functional/eval/minmax_functions_spec.lua diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a5b0a65497..c96abfa149 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -12463,53 +12463,49 @@ static void f_matchstrpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) find_some_match(argvars, rettv, 4); } -static void max_min(typval_T *argvars, typval_T *rettv, int domax) +/// Get maximal/minimal number value in a list or dictionary +/// +/// @param[in] tv List or dictionary to work with. If it contains something +/// that is not an integer number (or cannot be coerced to +/// it) error is given. +/// @param[out] rettv Location where result will be saved. Only assigns +/// vval.v_number, type is not touched. Returns zero for +/// empty lists/dictionaries. +/// @param[in] domax Determines whether maximal or minimal value is desired. +static void max_min(const typval_T *const tv, typval_T *const rettv, + const bool domax) + FUNC_ATTR_NONNULL_ALL { - long n = 0; - long i; + varnumber_T n = 0; bool error = false; - if (argvars[0].v_type == VAR_LIST) { - list_T *l; - listitem_T *li; - - l = argvars[0].vval.v_list; - if (l != NULL) { - li = l->lv_first; - if (li != NULL) { - n = tv_get_number_chk(&li->li_tv, &error); - for (;; ) { - li = li->li_next; - if (li == NULL) { - break; - } - i = tv_get_number_chk(&li->li_tv, &error); - if (domax ? i > n : i < n) { - n = i; - } + if (tv->v_type == VAR_LIST) { + const list_T *const l = tv->vval.v_list; + if (tv_list_len(l) != 0) { + n = tv_get_number_chk(&l->lv_first->li_tv, &error); + for (const listitem_T *li = l->lv_first->li_next; li != NULL && !error; + li = li->li_next) { + const varnumber_T i = tv_get_number_chk(&li->li_tv, &error); + if (domax ? i > n : i < n) { + n = i; } } } - } else if (argvars[0].v_type == VAR_DICT) { - dict_T *d; - int first = TRUE; - hashitem_T *hi; - int todo; - - d = argvars[0].vval.v_dict; - if (d != NULL) { - todo = (int)d->dv_hashtab.ht_used; - for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - todo--; - i = tv_get_number_chk(&TV_DICT_HI2DI(hi)->di_tv, &error); - if (first) { - n = i; - first = FALSE; - } else if (domax ? i > n : i < n) - n = i; + } else if (tv->v_type == VAR_DICT) { + if (tv->vval.v_dict != NULL) { + bool first = true; + TV_DICT_ITER(tv->vval.v_dict, di, { + const varnumber_T i = tv_get_number_chk(&di->di_tv, &error); + if (error) { + break; } - } + if (first) { + n = i; + first = true; + } else if (domax ? i > n : i < n) { + n = i; + } + }); } } else { EMSG2(_(e_listdictarg), domax ? "max()" : "min()"); diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 42581939e3..3b41314b46 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -279,13 +279,13 @@ typedef struct list_stack_S { #define TV_DICT_HI2DI(hi) \ ((dictitem_T *)((hi)->hi_key - offsetof(dictitem_T, di_key))) -static inline long tv_list_len(list_T *const l) +static inline long tv_list_len(const list_T *const l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get the number of items in a list /// /// @param[in] l List to check. -static inline long tv_list_len(list_T *const l) +static inline long tv_list_len(const list_T *const l) { if (l == NULL) { return 0; diff --git a/test/functional/eval/minmax_functions_spec.lua b/test/functional/eval/minmax_functions_spec.lua new file mode 100644 index 0000000000..c6eb754f91 --- /dev/null +++ b/test/functional/eval/minmax_functions_spec.lua @@ -0,0 +1,51 @@ +local helpers = require('test.functional.helpers')(after_each) + +local eq = helpers.eq +local eval = helpers.eval +local clear = helpers.clear +local funcs = helpers.funcs +local redir_exec = helpers.redir_exec + +before_each(clear) +for _, func in ipairs({'min', 'max'}) do + describe(func .. '()', function() + it('gives a single error message when multiple values failed conversions', + function() + eq('\nE745: Using a List as a Number\n0', + redir_exec('echo ' .. func .. '([-5, [], [], [], 5])')) + eq('\nE745: Using a List as a Number\n0', + redir_exec('echo ' .. func .. '({1:-5, 2:[], 3:[], 4:[], 5:5})')) + for errmsg, errinput in pairs({ + ['E745: Using a List as a Number'] = '[]', + ['E805: Using a Float as a Number'] = '0.0', + ['E703: Using a Funcref as a Number'] = 'function("tr")', + ['E728: Using a Dictionary as a Number'] = '{}', + }) do + eq('\n' .. errmsg .. '\n0', + redir_exec('echo ' .. func .. '([' .. errinput .. '])')) + eq('\n' .. errmsg .. '\n0', + redir_exec('echo ' .. func .. '({1:' .. errinput .. '})')) + end + end) + it('works with arrays/dictionaries with zero items', function() + eq(0, funcs[func]({})) + eq(0, eval(func .. '({})')) + end) + it('works with arrays/dictionaries with one item', function() + eq(5, funcs[func]({5})) + eq(5, funcs[func]({test=5})) + end) + it('works with NULL arrays/dictionaries', function() + eq(0, eval(func .. '(v:_null_list)')) + eq(0, eval(func .. '(v:_null_dict)')) + end) + it('errors out for invalid types', function() + for _, errinput in ipairs({'1', 'v:true', 'v:false', 'v:null', + 'function("tr")', '""'}) do + eq(('\nE712: Argument of %s() must be a List or Dictionary\n0'):format( + func), + redir_exec('echo ' .. func .. '(' .. errinput .. ')')) + end + end) + end) +end From 3a3816c990362e1ad8321e6b4748a33c94d1d797 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Sep 2016 21:42:24 +0300 Subject: [PATCH 0196/1671] cmake: Use CMAKE_CURRENT_LIST_DIR and remove vars used only once --- src/nvim/CMakeLists.txt | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index e752f5d4df..1c2932ed50 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -22,7 +22,6 @@ set(GENERATED_API_DISPATCH ${GENERATED_DIR}/api/private/dispatch_wrappers.genera set(GENERATED_FUNCS_METADATA ${GENERATED_DIR}/api/private/funcs_metadata.generated.h) set(GENERATED_EX_CMDS_ENUM ${GENERATED_INCLUDES_DIR}/ex_cmds_enum.generated.h) set(GENERATED_EX_CMDS_DEFS ${GENERATED_DIR}/ex_cmds_defs.generated.h) -set(GENERATED_FUNCS_HASH_INPUT ${GENERATED_DIR}/funcs.generated.h.gperf) set(GENERATED_FUNCS ${GENERATED_DIR}/funcs.generated.h) set(GENERATED_EVENTS_ENUM ${GENERATED_INCLUDES_DIR}/auevents_enum.generated.h) set(GENERATED_EVENTS_NAMES_MAP ${GENERATED_DIR}/auevents_name_map.generated.h) @@ -31,10 +30,6 @@ set(EX_CMDS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genex_cmds.lua) set(FUNCS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/geneval.lua) set(EVENTS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gen_events.lua) set(OPTIONS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genoptions.lua) -set(EVENTS_LIST_FILE ${PROJECT_SOURCE_DIR}/src/nvim/auevents.lua) -set(EX_CMDS_DEFS_FILE ${PROJECT_SOURCE_DIR}/src/nvim/ex_cmds.lua) -set(EVAL_DEFS_FILE ${PROJECT_SOURCE_DIR}/src/nvim/eval.lua) -set(OPTIONS_LIST_FILE ${PROJECT_SOURCE_DIR}/src/nvim/options.lua) set(UNICODE_TABLES_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genunicodetables.lua) set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/unicode) file(GLOB UNICODE_FILES ${UNICODE_DIR}/*.txt) @@ -112,7 +107,7 @@ set(CONV_SOURCES window.c) foreach(sfile ${CONV_SOURCES}) - if(NOT EXISTS "${PROJECT_SOURCE_DIR}/src/nvim/${sfile}") + if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/${sfile}") message(FATAL_ERROR "${sfile} doesn't exist (it was added to CONV_SOURCES)") endif() endforeach() @@ -167,11 +162,11 @@ endfunction() # NVIM_GENERATED_SOURCES: generated source files # These lists must be mutually exclusive. foreach(sfile ${NVIM_SOURCES} - "${PROJECT_SOURCE_DIR}/src/nvim/regexp_nfa.c" + "${CMAKE_CURRENT_LIST_DIR}/regexp_nfa.c" ${GENERATED_API_DISPATCH}) get_filename_component(full_d ${sfile} PATH) - file(RELATIVE_PATH d "${PROJECT_SOURCE_DIR}/src/nvim" "${full_d}") - if(${d} MATCHES "^([.][.]|auto/)") + file(RELATIVE_PATH d "${CMAKE_CURRENT_LIST_DIR}" "${full_d}") + if(${d} MATCHES "^[.][.]|auto/") file(RELATIVE_PATH d "${GENERATED_DIR}" "${full_d}") endif() get_filename_component(f ${sfile} NAME) @@ -239,8 +234,8 @@ list(APPEND NVIM_GENERATED_SOURCES add_custom_command(OUTPUT ${GENERATED_EX_CMDS_ENUM} ${GENERATED_EX_CMDS_DEFS} COMMAND ${LUA_PRG} ${EX_CMDS_GENERATOR} - ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_INCLUDES_DIR} ${GENERATED_DIR} - DEPENDS ${EX_CMDS_GENERATOR} ${EX_CMDS_DEFS_FILE} + ${CMAKE_CURRENT_LIST_DIR} ${GENERATED_INCLUDES_DIR} ${GENERATED_DIR} + DEPENDS ${EX_CMDS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/ex_cmds.lua ) if(NOT GPERF_PRG) @@ -248,24 +243,24 @@ if(NOT GPERF_PRG) endif() add_custom_command(OUTPUT ${GENERATED_FUNCS} ${FUNCS_DATA} COMMAND ${LUA_PRG} ${FUNCS_GENERATOR} - ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_DIR} ${API_METADATA} ${FUNCS_DATA} + ${CMAKE_CURRENT_LIST_DIR} ${GENERATED_DIR} ${API_METADATA} ${FUNCS_DATA} COMMAND ${GPERF_PRG} - ${GENERATED_FUNCS_HASH_INPUT} --output-file=${GENERATED_FUNCS} - DEPENDS ${FUNCS_GENERATOR} ${EVAL_DEFS_FILE} ${API_METADATA} + ${GENERATED_DIR}/funcs.generated.h.gperf --output-file=${GENERATED_FUNCS} + DEPENDS ${FUNCS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/eval.lua ${API_METADATA} ) list(APPEND NVIM_GENERATED_FOR_SOURCES "${GENERATED_FUNCS}") add_custom_command(OUTPUT ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP} COMMAND ${LUA_PRG} ${EVENTS_GENERATOR} - ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP} - DEPENDS ${EVENTS_GENERATOR} ${EVENTS_LIST_FILE} + ${CMAKE_CURRENT_LIST_DIR} ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP} + DEPENDS ${EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/auevents.lua ) add_custom_command(OUTPUT ${GENERATED_OPTIONS} COMMAND ${LUA_PRG} ${OPTIONS_GENERATOR} - ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_OPTIONS} - DEPENDS ${OPTIONS_GENERATOR} ${OPTIONS_LIST_FILE} + ${CMAKE_CURRENT_LIST_DIR} ${GENERATED_OPTIONS} + DEPENDS ${OPTIONS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/options.lua ) # NVIM_GENERATED_FOR_SOURCES and NVIM_GENERATED_FOR_HEADERS must be mutually exclusive. From 40feac6efcc75395bb20da2b028b67a2b6e95bd9 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 10 Sep 2016 22:32:14 +0300 Subject: [PATCH 0197/1671] message: Revise maxlen argument in msg_puts_attr_len `attr` argument is enough to forbid putting message in history. Also forbid strings with NUL before `len` just in case (it appears that this does not ever happen). --- src/nvim/message.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/nvim/message.c b/src/nvim/message.c index 423f5a27e7..1eab9a403b 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1562,13 +1562,17 @@ void msg_puts_attr(const char *const s, const int attr) msg_puts_attr_len(s, -1, attr); } -/// Like msg_puts_attr(), but with a maximum length "maxlen" (in bytes). -/// When "maxlen" is -1 there is no maximum length. -/// When "maxlen" is >= 0 the message is not put in the history. -void msg_puts_attr_len(const char *str, const ptrdiff_t maxlen, int attr) +/// Write a message with highlight attributes +/// +/// @param[in] str NUL-terminated message string. +/// @param[in] len Length of the string or -1. +/// @param[in] attr Highlight attribute. +void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr) + FUNC_ATTR_NONNULL_ALL { + assert(len < 0 || memchr(str, 0, len) == NULL); // If redirection is on, also write to the redirection file. - redir_write(str, maxlen); + redir_write(str, len); // Don't print anything when using ":silent cmd". if (msg_silent != 0) { @@ -1576,8 +1580,9 @@ void msg_puts_attr_len(const char *str, const ptrdiff_t maxlen, int attr) } // if MSG_HIST flag set, add message to history - if ((attr & MSG_HIST) && maxlen < 0) { - add_msg_hist(str, -1, attr); + if (attr & MSG_HIST) { + assert(len < 0); + add_msg_hist(str, (int)len, attr); attr &= ~MSG_HIST; } @@ -1596,9 +1601,9 @@ void msg_puts_attr_len(const char *str, const ptrdiff_t maxlen, int attr) // different, e.g. for Win32 console) or we just don't know where the // cursor is. if (msg_use_printf()) { - msg_puts_printf(str, maxlen); + msg_puts_printf(str, len); } else { - msg_puts_display((const char_u *)str, maxlen, attr, false); + msg_puts_display((const char_u *)str, len, attr, false); } } From 8b0fa64ed3dbd4376cbfa195526ec97d8c944a97 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 10 Sep 2016 22:35:47 +0300 Subject: [PATCH 0198/1671] message: Remove incorrect assertion It was there only to prove that *now* `len` argument is not used to forbid putting message into history (i.e. that Neovim behaviour did not change). After this commit it should be possible to use msg_puts_attr_len with len>=0 to put message into history should new code need this. --- src/nvim/message.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/nvim/message.c b/src/nvim/message.c index 1eab9a403b..83f2735b50 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1581,7 +1581,6 @@ void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr) // if MSG_HIST flag set, add message to history if (attr & MSG_HIST) { - assert(len < 0); add_msg_hist(str, (int)len, attr); attr &= ~MSG_HIST; } From 2ad4fba46db26d0724106b194f7cb628fb9b02e8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 11 Sep 2016 03:37:03 +0300 Subject: [PATCH 0199/1671] eval: Move copy_tv to eval/typval --- src/nvim/api/private/helpers.c | 2 +- src/nvim/eval.c | 211 ++++++++++++++------------------- src/nvim/eval/typval.c | 86 ++++++++++++-- src/nvim/move.PVS-Studio.cfg | 10 ++ src/nvim/shada.c | 2 +- test/unit/eval/helpers.lua | 4 +- 6 files changed, 176 insertions(+), 139 deletions(-) create mode 100644 src/nvim/move.PVS-Studio.cfg diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 2c8a56cc1a..b245132ba7 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -179,7 +179,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, } // Update the value - copy_tv(&tv, &di->di_tv); + tv_copy(&tv, &di->di_tv); // Clear the temporary variable tv_clear(&tv); } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c96abfa149..a2b6d12288 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -494,6 +494,13 @@ typedef enum ASSERT_OTHER, } assert_type_T; +/// Type for dict_list function +typedef enum { + kDictListKeys, ///< List dictionary keys. + kDictListValues, ///< List dictionary values. + kDictListItems, ///< List dictionary contents: [keys, values]. +} DictListType; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval.c.generated.h" #endif @@ -2412,7 +2419,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, eexe_mod_op(&lp->ll_li->li_tv, &ri->li_tv, (const char *)op); } else { tv_clear(&lp->ll_li->li_tv); - copy_tv(&ri->li_tv, &lp->ll_li->li_tv); + tv_copy(&ri->li_tv, &lp->ll_li->li_tv); } ri = ri->li_next; if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1)) @@ -2452,7 +2459,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, lp->ll_tv = &di->di_tv; } else { if (watched) { - copy_tv(lp->ll_tv, &oldtv); + tv_copy(lp->ll_tv, &oldtv); } if (op != NULL && *op != '=') { @@ -2465,7 +2472,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, // Assign the value to the variable or list item. if (copy) { - copy_tv(rettv, lp->ll_tv); + tv_copy(rettv, lp->ll_tv); } else { *lp->ll_tv = *rettv; lp->ll_tv->v_lock = 0; @@ -2926,7 +2933,7 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) typval_T oldtv; if (watched) { - copy_tv(&di->di_tv, &oldtv); + tv_copy(&di->di_tv, &oldtv); // need to save key because dictitem_remove will free it key = xstrdup((char *)di->di_key); } @@ -2998,7 +3005,7 @@ int do_unlet(char_u *name, int forceit) bool watched = tv_dict_is_watched(dict); if (watched) { - copy_tv(&di->di_tv, &oldtv); + tv_copy(&di->di_tv, &oldtv); } delete_var(ht, hi); @@ -4532,7 +4539,7 @@ eval_index ( rettv->vval.v_list = l; l->lv_refcount++; } else { - copy_tv(&tv_list_find(rettv->vval.v_list, n1)->li_tv, &var1); + tv_copy(&tv_list_find(rettv->vval.v_list, n1)->li_tv, &var1); tv_clear(rettv); *rettv = var1; } @@ -4570,7 +4577,7 @@ eval_index ( return FAIL; } - copy_tv(&item->di_tv, &var1); + tv_copy(&item->di_tv, &var1); tv_clear(rettv); *rettv = var1; break; @@ -6298,7 +6305,7 @@ call_func( } if (error == ERROR_NONE && partial->pt_argc > 0) { for (argv_clear = 0; argv_clear < partial->pt_argc; argv_clear++) { - copy_tv(&partial->pt_argv[argv_clear], &argv[argv_clear]); + tv_copy(&partial->pt_argv[argv_clear], &argv[argv_clear]); } for (int i = 0; i < argcount_in; i++) { argv[i + argv_clear] = argvars_in[i]; @@ -6552,7 +6559,7 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) if ((l = argvars[0].vval.v_list) != NULL && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { tv_list_append_tv(l, &argvars[1]); - copy_tv(&argvars[0], rettv); + tv_copy(&argvars[0], rettv); } } else EMSG(_(e_listreq)); @@ -7231,7 +7238,7 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, /* Make a copy of each argument. This is needed to be able to set * v_lock to VAR_FIXED in the copy without changing the original list. */ - copy_tv(&item->li_tv, &argv[argc++]); + tv_copy(&item->li_tv, &argv[argc++]); } if (item == NULL) { @@ -8190,7 +8197,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) item = NULL; tv_list_extend(l1, l2, item); - copy_tv(&argvars[0], rettv); + tv_copy(&argvars[0], rettv); } } else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT) { @@ -8224,7 +8231,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_extend(d1, d2, action); - copy_tv(&argvars[0], rettv); + tv_copy(&argvars[0], rettv); } } else { EMSG2(_(e_listdictarg), "extend()"); @@ -8441,7 +8448,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) did_emsg |= save_did_emsg; } - copy_tv(&argvars[0], rettv); + tv_copy(&argvars[0], rettv); } static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) @@ -8451,7 +8458,7 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) int retval = FAIL; int dummy; - copy_tv(tv, &vimvars[VV_VAL].vv_tv); + tv_copy(tv, &vimvars[VV_VAL].vv_tv); argv[0] = vimvars[VV_KEY].vv_tv; argv[1] = vimvars[VV_VAL].vv_tv; if (expr->v_type == VAR_FUNC) { @@ -8859,13 +8866,13 @@ static void common_function(typval_T *argvars, typval_T *rettv, } int i = 0; for (; i < arg_len; i++) { - copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]); + tv_copy(&arg_pt->pt_argv[i], &pt->pt_argv[i]); } if (lv_len > 0) { for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - copy_tv(&li->li_tv, &pt->pt_argv[i++]); + tv_copy(&li->li_tv, &pt->pt_argv[i++]); } } } @@ -9012,10 +9019,12 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (tv == NULL) { - if (argvars[2].v_type != VAR_UNKNOWN) - copy_tv(&argvars[2], rettv); - } else - copy_tv(tv, rettv); + if (argvars[2].v_type != VAR_UNKNOWN) { + tv_copy(&argvars[2], rettv); + } + } else { + tv_copy(tv, rettv); + } } /// Returns information about signs placed in a buffer as list of dicts. @@ -9260,7 +9269,7 @@ static void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) dictitem_T *const v = find_var_in_ht(&curbuf->b_vars->dv_hashtab, 'b', varname, strlen(varname), false); if (v != NULL) { - copy_tv(&v->di_tv, rettv); + tv_copy(&v->di_tv, rettv); done = true; } } @@ -9273,7 +9282,7 @@ static void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) f_getbufvar_end: if (!done && argvars[2].v_type != VAR_UNKNOWN) { // use the default value - copy_tv(&argvars[2], rettv); + tv_copy(&argvars[2], rettv); } } @@ -10094,7 +10103,7 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't', varname, strlen(varname), false); if (v != NULL) { - copy_tv(&v->di_tv, rettv); + tv_copy(&v->di_tv, rettv); done = true; } } @@ -10104,7 +10113,7 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (!done && argvars[2].v_type != VAR_UNKNOWN) { - copy_tv(&argvars[2], rettv); + tv_copy(&argvars[2], rettv); } } @@ -10315,7 +10324,7 @@ getwinvar ( v = find_var_in_ht(&win->w_vars->dv_hashtab, 'w', varname, strlen(varname), false); if (v != NULL) { - copy_tv(&v->di_tv, rettv); + tv_copy(&v->di_tv, rettv); done = true; } } @@ -10330,7 +10339,7 @@ getwinvar ( if (!done && argvars[off + 2].v_type != VAR_UNKNOWN) { // use the default return value - copy_tv(&argvars[off + 2], rettv); + tv_copy(&argvars[off + 2], rettv); } } @@ -11181,7 +11190,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (l != NULL) { tv_list_insert_tv(l, &argvars[1], item); - copy_tv(&argvars[0], rettv); + tv_copy(&argvars[0], rettv); } } } @@ -11271,31 +11280,36 @@ static void dict_list(typval_T *const tv, typval_T *const rettv, listitem_T *const li = tv_list_item_alloc(); tv_list_append(rettv->vval.v_list, li); - if (what == 0) { - // keys() - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = vim_strsave(di->di_key); - } else if (what == 1) { - // values() - copy_tv(&di->di_tv, &li->li_tv); - } else { - // items() - list_T *const l2 = tv_list_alloc(); - li->li_tv.v_type = VAR_LIST; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_list = l2; - l2->lv_refcount++; + switch (what) { + case kDictListKeys: { + li->li_tv.v_type = VAR_STRING; + li->li_tv.v_lock = VAR_UNLOCKED; + li->li_tv.vval.v_string = vim_strsave(di->di_key); + break; + } + case kDictListValues: { + tv_copy(&di->di_tv, &li->li_tv); + break; + } + case kDictListItems: { + // items() + list_T *const sub_l = tv_list_alloc(); + li->li_tv.v_type = VAR_LIST; + li->li_tv.v_lock = VAR_UNLOCKED; + li->li_tv.vval.v_list = sub_l; + sub_l->lv_refcount++; - listitem_T *sub_li = tv_list_item_alloc(); - tv_list_append(l2, sub_li); - sub_li->li_tv.v_type = VAR_STRING; - sub_li->li_tv.v_lock = 0; - sub_li->li_tv.vval.v_string = vim_strsave(di->di_key); + listitem_T *sub_li = tv_list_item_alloc(); + tv_list_append(sub_l, sub_li); + sub_li->li_tv.v_type = VAR_STRING; + sub_li->li_tv.v_lock = VAR_UNLOCKED; + sub_li->li_tv.vval.v_string = vim_strsave(di->di_key); - sub_li = tv_list_item_alloc(); - tv_list_append(l2, sub_li); - copy_tv(&di->di_tv, &sub_li->li_tv); + sub_li = tv_list_item_alloc(); + tv_list_append(sub_l, sub_li); + tv_copy(&di->di_tv, &sub_li->li_tv); + break; + } } }); } @@ -12256,21 +12270,24 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) } } } else if (type == 2) { - /* return matched string */ - if (l != NULL) - copy_tv(&li->li_tv, rettv); - else - rettv->vval.v_string = vim_strnsave(regmatch.startp[0], - (int)(regmatch.endp[0] - regmatch.startp[0])); - } else if (l != NULL) + // Return matched string. + if (l != NULL) { + tv_copy(&li->li_tv, rettv); + } else { + rettv->vval.v_string = (char_u *)xmemdupz( + (const char *)regmatch.startp[0], + (size_t)(regmatch.endp[0] - regmatch.startp[0])); + } + } else if (l != NULL) { rettv->vval.v_number = idx; - else { - if (type != 0) + } else { + if (type != 0) { rettv->vval.v_number = (varnumber_T)(regmatch.startp[0] - str); - else + } else { rettv->vval.v_number = (varnumber_T)(regmatch.endp[0] - str); + } rettv->vval.v_number += (varnumber_T)(str - expr); } } @@ -15135,8 +15152,8 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) // Copy the values. This is needed to be able to set v_lock to VAR_FIXED // in the copy without changing the original list items. - copy_tv(&si1->item->li_tv, &argv[0]); - copy_tv(&si2->item->li_tv, &argv[1]); + tv_copy(&si1->item->li_tv, &argv[0]); + tv_copy(&si2->item->li_tv, &argv[1]); rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this res = call_func((const char_u *)func_name, @@ -18203,7 +18220,7 @@ static int get_var_tv( } ret = FAIL; } else if (rettv != NULL) { - copy_tv(tv, rettv); + tv_copy(tv, rettv); } return ret; @@ -18375,7 +18392,7 @@ void set_selfdict(typval_T *rettv, dict_T *selfdict) } else { pt->pt_argc = ret_pt->pt_argc; for (i = 0; i < pt->pt_argc; i++) { - copy_tv(&ret_pt->pt_argv[i], &pt->pt_argv[i]); + tv_copy(&ret_pt->pt_argv[i], &pt->pt_argv[i]); } } } @@ -18852,7 +18869,7 @@ static void set_var(const char *name, typval_T *const tv, const bool copy) } if (watched) { - copy_tv(&v->di_tv, &oldtv); + tv_copy(&v->di_tv, &oldtv); } tv_clear(&v->di_tv); } else { // Add a new variable. @@ -18877,7 +18894,7 @@ static void set_var(const char *name, typval_T *const tv, const bool copy) } if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT) { - copy_tv(tv, &v->di_tv); + tv_copy(tv, &v->di_tv); } else { v->di_tv = *tv; v->di_tv.v_lock = 0; @@ -18992,56 +19009,6 @@ bool valid_varname(const char *varname) return true; } -/* - * Copy the values from typval_T "from" to typval_T "to". - * When needed allocates string or increases reference count. - * Does not make a copy of a list or dict but copies the reference! - * It is OK for "from" and "to" to point to the same item. This is used to - * make a copy later. - */ -void copy_tv(typval_T *from, typval_T *to) -{ - to->v_type = from->v_type; - to->v_lock = 0; - memmove(&to->vval, &from->vval, sizeof(to->vval)); - switch (from->v_type) { - case VAR_NUMBER: - case VAR_FLOAT: - case VAR_SPECIAL: - break; - case VAR_STRING: - case VAR_FUNC: - if (from->vval.v_string != NULL) { - to->vval.v_string = vim_strsave(from->vval.v_string); - if (from->v_type == VAR_FUNC) { - func_ref(to->vval.v_string); - } - } - break; - case VAR_PARTIAL: - if (from->vval.v_partial == NULL) { - to->vval.v_partial = NULL; - } else { - to->vval.v_partial = from->vval.v_partial; - (to->vval.v_partial->pt_refcount)++; - } - break; - case VAR_LIST: - if (from->vval.v_list != NULL) { - to->vval.v_list->lv_refcount++; - } - break; - case VAR_DICT: - if (from->vval.v_dict != NULL) { - to->vval.v_dict->dv_refcount++; - } - break; - case VAR_UNKNOWN: - EMSG2(_(e_intern2), "copy_tv(UNKNOWN)"); - break; - } -} - /// Make a copy of an item /// /// Lists and Dictionaries are also copied. @@ -19080,11 +19047,11 @@ int var_item_copy(const vimconv_T *const conv, case VAR_FUNC: case VAR_PARTIAL: case VAR_SPECIAL: - copy_tv(from, to); + tv_copy(from, to); break; case VAR_STRING: if (conv == NULL || conv->vc_type == CONV_NONE) { - copy_tv(from, to); + tv_copy(from, to); } else { to->v_type = VAR_STRING; to->v_lock = 0; @@ -20981,7 +20948,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, if (addlocal) { // Named arguments can be accessed without the "a:" prefix in lambda // expressions. Add to the l: dict. - copy_tv(&v->di_tv, &v->di_tv); + tv_copy(&v->di_tv, &v->di_tv); tv_dict_add(&fc->l_vars, v); } else { tv_dict_add(&fc->l_avars, v); @@ -21188,13 +21155,13 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, // Make a copy of the a: variables, since we didn't do that above. TV_DICT_ITER(&fc->l_avars, di, { - copy_tv(&di->di_tv, &di->di_tv); + tv_copy(&di->di_tv, &di->di_tv); }); // Make a copy of the a:000 items, since we didn't do that above. for (listitem_T *li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) { - copy_tv(&li->li_tv, &li->li_tv); + tv_copy(&li->li_tv, &li->li_tv); } } @@ -21700,7 +21667,7 @@ const void *var_shada_iter(const void *const iter, const char **const name, hi = (const hashitem_T *) iter; } *name = (char *)TV_DICT_HI2DI(hi)->di_key; - copy_tv(&TV_DICT_HI2DI(hi)->di_tv, rettv); + tv_copy(&TV_DICT_HI2DI(hi)->di_tv, rettv); while ((size_t)(++hi - hifirst) < hinum) { if (!HASHITEM_EMPTY(hi) && var_flavour(hi->hi_key) == VAR_FLAVOUR_SHADA) { return hi; diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 78eca15fec..8a407a4513 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1,6 +1,8 @@ +#include #include -#include +#include #include +#include #include "nvim/lib/queue.h" #include "nvim/eval/typval.h" @@ -276,7 +278,7 @@ void tv_list_insert(list_T *const l, listitem_T *const ni, /// Insert VimL value into a list /// /// @param[out] l List to insert to. -/// @param[in,out] tv Value to insert. Is copied (@see copy_tv()) to an +/// @param[in,out] tv Value to insert. Is copied (@see tv_copy()) to an /// allocated listitem_T and inserted. /// @param[in] item Item to insert before. If NULL, inserts at the end of the /// list. @@ -285,7 +287,7 @@ void tv_list_insert_tv(list_T *const l, typval_T *const tv, { listitem_T *const ni = tv_list_item_alloc(); - copy_tv(tv, &ni->li_tv); + tv_copy(tv, &ni->li_tv); tv_list_insert(l, ni, item); } @@ -313,13 +315,13 @@ void tv_list_append(list_T *const l, listitem_T *const item) /// Append VimL value to the end of list /// /// @param[out] l List to append to. -/// @param[in,out] tv Value to append. Is copied (@see copy_tv()) to an +/// @param[in,out] tv Value to append. Is copied (@see tv_copy()) to an /// allocated listitem_T. void tv_list_append_tv(list_T *const l, typval_T *const tv) FUNC_ATTR_NONNULL_ALL { listitem_T *const li = tv_list_item_alloc(); - copy_tv(tv, &li->li_tv); + tv_copy(tv, &li->li_tv); tv_list_append(l, li); } @@ -447,7 +449,7 @@ list_T *tv_list_copy(const vimconv_T *const conv, list_T *const orig, break; } } else { - copy_tv(&item->li_tv, &ni->li_tv); + tv_copy(&item->li_tv, &ni->li_tv); } tv_list_append(copy, ni); } @@ -828,13 +830,13 @@ void tv_dict_watcher_notify(dict_T *const dict, const char *const key, if (newtv) { dictitem_T *const v = tv_dict_item_alloc_len(S_LEN("new")); - copy_tv(newtv, &v->di_tv); + tv_copy(newtv, &v->di_tv); tv_dict_add(argv[2].vval.v_dict, v); } if (oldtv) { dictitem_T *const v = tv_dict_item_alloc_len(S_LEN("old")); - copy_tv(oldtv, &v->di_tv); + tv_copy(oldtv, &v->di_tv); tv_dict_add(argv[2].vval.v_dict, v); } @@ -926,7 +928,7 @@ static dictitem_T *tv_dict_item_copy(dictitem_T *const di) FUNC_ATTR_MALLOC { dictitem_T *const new_di = tv_dict_item_alloc((const char *)di->di_key); - copy_tv(&di->di_tv, &new_di->di_tv); + tv_copy(&di->di_tv, &new_di->di_tv); return new_di; } @@ -1161,7 +1163,7 @@ bool tv_dict_get_callback(dict_T *const d, } typval_T tv; - copy_tv(&di->di_tv, &tv); + tv_copy(&di->di_tv, &tv); set_selfdict(&tv, d); bool res = callback_from_typval(result, &tv); tv_clear(&tv); @@ -1338,11 +1340,11 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, } if (watched) { - copy_tv(&di1->di_tv, &oldtv); + tv_copy(&di1->di_tv, &oldtv); } tv_clear(&di1->di_tv); - copy_tv(&di2->di_tv, &di1->di_tv); + tv_copy(&di2->di_tv, &di1->di_tv); if (watched) { tv_dict_watcher_notify(d1, (const char *)di1->di_key, &di1->di_tv, @@ -1433,7 +1435,7 @@ dict_T *tv_dict_copy(const vimconv_T *const conv, break; } } else { - copy_tv(&di->di_tv, &new_di->di_tv); + tv_copy(&di->di_tv, &new_di->di_tv); } if (tv_dict_add(copy, new_di) == FAIL) { tv_dict_item_free(new_di); @@ -1782,6 +1784,64 @@ void tv_free(typval_T *tv) } } +//{{{3 Copy + +/// Copy typval from one location to another +/// +/// When needed allocates string or increases reference count. Does not make +/// a copy of a container, but copies its reference! +/// +/// It is OK for `from` and `to` to point to the same location; this is used to +/// make a copy later. +/// +/// @param[in] from Location to copy from. +/// @param[out] to Location to copy to. +void tv_copy(typval_T *const from, typval_T *const to) +{ + to->v_type = from->v_type; + to->v_lock = VAR_UNLOCKED; + memmove(&to->vval, &from->vval, sizeof(to->vval)); + switch (from->v_type) { + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_SPECIAL: { + break; + } + case VAR_STRING: + case VAR_FUNC: { + if (from->vval.v_string != NULL) { + to->vval.v_string = vim_strsave(from->vval.v_string); + if (from->v_type == VAR_FUNC) { + func_ref(to->vval.v_string); + } + } + break; + } + case VAR_PARTIAL: { + if (to->vval.v_partial != NULL) { + to->vval.v_partial->pt_refcount++; + } + break; + } + case VAR_LIST: { + if (from->vval.v_list != NULL) { + to->vval.v_list->lv_refcount++; + } + break; + } + case VAR_DICT: { + if (from->vval.v_dict != NULL) { + to->vval.v_dict->dv_refcount++; + } + break; + } + case VAR_UNKNOWN: { + EMSG2(_(e_intern2), "tv_copy(UNKNOWN)"); + break; + } + } +} + //{{{2 Locks /// Lock or unlock an item diff --git a/src/nvim/move.PVS-Studio.cfg b/src/nvim/move.PVS-Studio.cfg new file mode 100644 index 0000000000..cb6da32f12 --- /dev/null +++ b/src/nvim/move.PVS-Studio.cfg @@ -0,0 +1,10 @@ + +source-file=/home/zyx/a.a/Proj/c/neovim/src/nvim/move.c +i-file=/home/zyx/a.a/Proj/c/neovim/src/nvim/move.i +language=C +skip-cl-exe=yes +preprocessor=gcc +platform=linux64 +lic-file=/home/zyx/a.a/Proj/c/neovim/build/../PVS-Studio.lic +output-file=/home/zyx/a.a/Proj/c/neovim/build/../PVS-Studio.log.x +analysis-mode=4 diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 0f04a5e9cf..f65fdaf1c0 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -2559,7 +2559,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, if (sd_writer->sd_conv.vc_type != CONV_NONE) { var_item_copy(&sd_writer->sd_conv, &vartv, &tgttv, true, 0); } else { - copy_tv(&vartv, &tgttv); + tv_copy(&vartv, &tgttv); } ShaDaWriteResult spe_ret; if ((spe_ret = shada_pack_entry(packer, (ShadaEntry) { diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index be77ef4c83..c603953a05 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -280,7 +280,7 @@ local lua2typvalt_type_tab = { if type(k) == 'string' then local di = eval.tv_dict_item_alloc(to_cstr(k)) local val_tv = ffi.gc(lua2typvalt(v, processed), nil) - eval.copy_tv(val_tv, di.di_tv) + eval.tv_copy(val_tv, di.di_tv) eval.tv_clear(val_tv) eval.tv_dict_add(dct, di) end @@ -300,7 +300,7 @@ local lua2typvalt_type_tab = { argv = ffi.gc(ffi.cast('typval_T*', eval.xmalloc(ffi.sizeof('typval_T') * #l.args)), nil) for i, arg in ipairs(l.args) do local arg_tv = ffi.gc(lua2typvalt(arg, processed), nil) - eval.copy_tv(arg_tv, argv[i - 1]) + eval.tv_copy(arg_tv, argv[i - 1]) eval.tv_clear(arg_tv) end end From a32db8ed19c35847373d4a7fd56d7797a5a26897 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 11 Sep 2016 03:54:13 +0300 Subject: [PATCH 0200/1671] eval/typval: Add missing includes, also add a script to find them Contains unfinished attempt to integrate IWYU (ref #549). To finish it different job should be done, specifically: - Instead of feeding IWYU with modified file a mirror source tree should be created with the help of CMake which will contain modified sources. This solves the problem with IWYU thinking that `*.generated.h` headers should be included in place of `*` headers. - Build IWYU as all other third-party utilities. - Make modified sources avoid problems with `nvim/func_attr.h` includes and various related tricks. Current script may only be used for manual checks like this: ./scripts/check-includes.py \ --generated-includes-dir build/include \ --generated-includes-dir build/src/nvim/auto \ --file src/nvim/eval/typval.c \ -- -Isrc -Ibuild/include -Ibuild/src/nvim/auto \ -DINCLUDE_GENERATED_DECLARATIONS (it is also somewhat fine with `--file src/nvim/eval/typval.h`). I have no idea why (I mean, why developer think that these lines are needed, why they are suggested is pretty obvious: because there is typedef which mentions them before structs are defined), but for typval.h it reports, among other things, that it should add lines struct dictvar_S; struct listitem_S; struct listvar_S; struct listwatch_S; --- scripts/check-includes.py | 66 +++++++++++++++++++++++++++++++++++++++ src/nvim/eval/typval.c | 5 +++ src/nvim/eval/typval.h | 4 ++- 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100755 scripts/check-includes.py diff --git a/scripts/check-includes.py b/scripts/check-includes.py new file mode 100755 index 0000000000..21308a21aa --- /dev/null +++ b/scripts/check-includes.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +import sys +import re +import os + +from subprocess import Popen, PIPE +from argparse import ArgumentParser + + +GENERATED_INCLUDE_RE = re.compile( + r'^\s*#\s*include\s*"([/a-z_0-9.]+\.generated\.h)"(\s+//.*)?$') + + +def main(argv): + argparser = ArgumentParser() + argparser.add_argument('--generated-includes-dir', action='append', + help='Directory where generated includes are located.') + argparser.add_argument('--file', type=open, help='File to check.') + argparser.add_argument('iwyu_args', nargs='*', + help='IWYU arguments, must go after --.') + args = argparser.parse_args(argv) + + with args.file: + include_dirs = [] + + iwyu = Popen(['include-what-you-use', '-xc'] + args.iwyu_args + ['/dev/stdin'], + stdin=PIPE, stdout=PIPE, stderr=PIPE) + + for line in args.file: + match = GENERATED_INCLUDE_RE.match(line) + if match: + for d in args.generated_includes_dir: + try: + f = open(os.path.join(d, match.group(1))) + except IOError: + continue + else: + with f: + for generated_line in f: + iwyu.stdin.write(generated_line) + break + else: + raise IOError('Failed to find {0}'.format(match.group(1))) + else: + iwyu.stdin.write(line) + + iwyu.stdin.close() + + out = iwyu.stdout.read() + err = iwyu.stderr.read() + + ret = iwyu.wait() + + if ret != 2: + print('IWYU failed with exit code {0}:'.format(ret)) + print('{0} stdout {0}'.format('=' * ((80 - len(' stdout ')) // 2))) + print(out) + print('{0} stderr {0}'.format('=' * ((80 - len(' stderr ')) // 2))) + print(err) + return 1 + return 0 + + +if __name__ == '__main__': + raise SystemExit(main(sys.argv[1:])) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 8a407a4513..4777e00a76 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -20,6 +20,11 @@ #include "nvim/ascii.h" #include "nvim/pos.h" #include "nvim/charset.h" +#include "nvim/garray.h" +#include "nvim/gettext.h" +#include "nvim/macros.h" +#include "nvim/mbyte.h" +#include "nvim/message.h" // TODO(ZyX-I): Move line_breakcheck out of misc1 #include "nvim/misc1.h" // For line_breakcheck diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 3b41314b46..791b191009 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -3,9 +3,11 @@ #include #include -#include +#include #include +#include +#include "nvim/types.h" #include "nvim/hashtab.h" #include "nvim/garray.h" #include "nvim/mbyte.h" From a394167177390a66c275d32e3862c82c546970ff Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 12 Mar 2017 13:41:11 +0300 Subject: [PATCH 0201/1671] unittests: Test tv_list_item_\* functions To check that memory is free()d correctly. --- src/nvim/eval/typval.c | 1 + test/unit/eval/helpers.lua | 11 +- test/unit/eval/typval_spec.lua | 210 +++++++++++++++++++++++++++++++++ 3 files changed, 218 insertions(+), 4 deletions(-) create mode 100644 test/unit/eval/typval_spec.lua diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 4777e00a76..a26afb20c6 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -228,6 +228,7 @@ void tv_list_unref(list_T *const l) /// @param[in] item2 Last item to remove. void tv_list_remove_items(list_T *const l, listitem_T *const item, listitem_T *const item2) + FUNC_ATTR_NONNULL_ALL { // notify watchers for (listitem_T *ip = item; ip != NULL; ip = ip->li_next) { diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index c603953a05..8bf06e61f1 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -12,6 +12,7 @@ local null_string = {[true]='NULL string'} local null_list = {[true]='NULL list'} local null_dict = {[true]='NULL dict'} local type_key = {[true]='type key'} +local locks_key = {[true]='locks key'} local list_type = {[true]='list type'} local dict_type = {[true]='dict type'} local func_type = {[true]='func type'} @@ -23,9 +24,9 @@ local nil_value = {[true]='nil'} local lua2typvalt local function li_alloc(nogc) - local gcfunc = eval.listitem_free + local gcfunc = eval.tv_list_item_free if nogc then gcfunc = nil end - local li = ffi.gc(eval.listitem_alloc(), gcfunc) + local li = ffi.gc(eval.tv_list_item_alloc(), gcfunc) li.li_next = nil li.li_prev = nil li.li_tv = {v_type=eval.VAR_UNKNOWN, v_lock=eval.VAR_UNLOCKED} @@ -373,8 +374,9 @@ lua2typvalt = function(l, processed) end end +local void_ptr = ffi.typeof('void *') local function void(ptr) - return ffi.cast('void*', ptr) + return ffi.cast(void_ptr, ptr) end local alloc_logging_helpers = { @@ -386,7 +388,7 @@ local alloc_logging_helpers = { end, str = function(s, size) return {func='malloc', args={size + 1}, ret=void(s)} end, - freed = function(p) return {func='free', args={p and void(p)}} end, + freed = function(p) return {func='free', args={type(p) == 'table' and p or void(p)}} end, } return { @@ -402,6 +404,7 @@ return { nil_value=nil_value, type_key=type_key, + locks_key=locks_key, list=list, lst2tbl=lst2tbl, diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua new file mode 100644 index 0000000000..f2cb593cf5 --- /dev/null +++ b/test/unit/eval/typval_spec.lua @@ -0,0 +1,210 @@ +local helpers = require('test.unit.helpers')(after_each) +local eval_helpers = require('test.unit.eval.helpers') + +local itp = helpers.gen_itp(it) + +local eq = helpers.eq +local neq = helpers.neq +local ffi = helpers.ffi +local NULL = helpers.NULL +local cimport = helpers.cimport +local alloc_log_new = helpers.alloc_log_new + +local a = eval_helpers.alloc_logging_helpers +local list = eval_helpers.list +local lst2tbl = eval_helpers.lst2tbl +local type_key = eval_helpers.type_key +local dict_type = eval_helpers.dict_type +local lua2typvalt = eval_helpers.lua2typvalt + +local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h') + +local function li_alloc(nogc) + local gcfunc = lib.tv_list_item_free + if nogc then gcfunc = nil end + local li = ffi.gc(lib.tv_list_item_alloc(), gcfunc) + li.li_next = nil + li.li_prev = nil + li.li_tv = {v_type=lib.VAR_UNKNOWN, v_lock=lib.VAR_UNLOCKED} + return li +end + +local function list_index(l, idx) + return tv_list_find(l, idx) +end + +local function list_items(l) + local lis = {} + local li = l.lv_first + for i = 1, l.lv_len do + lis[i] = li + li = li.li_next + end + return lis +end + +local function list_watch(li) + return ffi.new('listwatch_T', {lw_item=li}) +end + +local alloc_log +local restore_allocators + +local to_cstr_nofree = function(v) return lib.xstrdup(v) end + +local alloc_log = alloc_log_new() + +before_each(function() + alloc_log:before_each() +end) + +after_each(function() + alloc_log:after_each() +end) + +describe('typval.c', function() + describe('list', function() + describe('item', function() + describe('alloc()/free()', function() + itp('works', function() + local li = li_alloc(true) + neq(nil, li) + lib.tv_list_item_free(li) + alloc_log:check({ + a.li(li), + a.freed(li), + }) + end) + itp('also frees the value', function() + local li + local s + local l + local tv + li = li_alloc(true) + li.li_tv.v_type = lib.VAR_NUMBER + li.li_tv.vval.v_number = 10 + lib.tv_list_item_free(li) + alloc_log:check({ + a.li(li), + a.freed(li), + }) + + li = li_alloc(true) + li.li_tv.v_type = lib.VAR_FLOAT + li.li_tv.vval.v_float = 10.5 + lib.tv_list_item_free(li) + alloc_log:check({ + a.li(li), + a.freed(li), + }) + + li = li_alloc(true) + li.li_tv.v_type = lib.VAR_STRING + li.li_tv.vval.v_string = nil + lib.tv_list_item_free(li) + alloc_log:check({ + a.li(li), + a.freed(alloc_log.null), + a.freed(li), + }) + + li = li_alloc(true) + li.li_tv.v_type = lib.VAR_STRING + s = to_cstr_nofree('test') + li.li_tv.vval.v_string = s + lib.tv_list_item_free(li) + alloc_log:check({ + a.li(li), + a.str(s, #('test')), + a.freed(s), + a.freed(li), + }) + + li = li_alloc(true) + li.li_tv.v_type = lib.VAR_LIST + l = list() + l.lv_refcount = 2 + li.li_tv.vval.v_list = l + lib.tv_list_item_free(li) + alloc_log:check({ + a.li(li), + a.list(l), + a.freed(li), + }) + eq(1, l.lv_refcount) + + li = li_alloc(true) + tv = lua2typvalt({[type_key]=dict_type}) + tv.vval.v_dict.dv_refcount = 2 + li.li_tv = tv + lib.tv_list_item_free(li) + alloc_log:check({ + a.li(li), + a.dict(tv.vval.v_dict), + a.freed(li), + }) + eq(1, tv.vval.v_dict.dv_refcount) + end) + end) + describe('remove()', function() + itp('works', function() + local l = list(1, 2, 3, 4, 5, 6, 7) + neq(nil, l) + local lis = list_items(l) + alloc_log:check({ + a.list(l), + a.li(lis[1]), + a.li(lis[2]), + a.li(lis[3]), + a.li(lis[4]), + a.li(lis[5]), + a.li(lis[6]), + a.li(lis[7]), + }) + + lib.tv_list_item_remove(l, lis[1]) + alloc_log:check({ + a.freed(table.remove(lis, 1)), + }) + eq(lis, list_items(l)) + + lib.tv_list_item_remove(l, lis[6]) + alloc_log:check({ + a.freed(table.remove(lis)), + }) + eq(lis, list_items(l)) + + lib.tv_list_item_remove(l, lis[3]) + alloc_log:check({ + a.freed(table.remove(lis, 3)), + }) + eq(lis, list_items(l)) + end) + itp('works and adjusts watchers correctly', function() + local l = ffi.gc(list(1, 2, 3, 4, 5, 6, 7), nil) + neq(nil, l) + local lis = list_items(l) + -- Three watchers: pointing to first, middle and last elements. + local lws = {list_watch(lis[1]), list_watch(lis[4]), list_watch(lis[7])} + lib.tv_list_watch_add(l, lws[1]) + lib.tv_list_watch_add(l, lws[2]) + lib.tv_list_watch_add(l, lws[3]) + + lib.tv_list_item_remove(l, lis[4]) + eq({lis[1], lis[5], lis[7]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item}) + + lib.tv_list_item_remove(l, lis[2]) + eq({lis[1], lis[5], lis[7]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item}) + + lib.tv_list_item_remove(l, lis[7]) + eq({lis[1], lis[5], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) + + lib.tv_list_item_remove(l, lis[1]) + eq({lis[3], lis[5], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) + + lib.tv_list_free(l) + end) + end) + end) + end) +end) From d2639e1e1d041af583dadd08e34405321fc24eea Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 14 Sep 2016 19:39:19 +0300 Subject: [PATCH 0202/1671] unittests: Add tests for list watchers and list alloc/free/unref --- test/unit/eval/typval_spec.lua | 180 ++++++++++++++++++++++++++++----- 1 file changed, 154 insertions(+), 26 deletions(-) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index f2cb593cf5..0967e38eef 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -6,49 +6,48 @@ local itp = helpers.gen_itp(it) local eq = helpers.eq local neq = helpers.neq local ffi = helpers.ffi -local NULL = helpers.NULL local cimport = helpers.cimport local alloc_log_new = helpers.alloc_log_new local a = eval_helpers.alloc_logging_helpers local list = eval_helpers.list -local lst2tbl = eval_helpers.lst2tbl local type_key = eval_helpers.type_key +local li_alloc = eval_helpers.li_alloc local dict_type = eval_helpers.dict_type +local list_type = eval_helpers.list_type local lua2typvalt = eval_helpers.lua2typvalt local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h') -local function li_alloc(nogc) - local gcfunc = lib.tv_list_item_free - if nogc then gcfunc = nil end - local li = ffi.gc(lib.tv_list_item_alloc(), gcfunc) - li.li_next = nil - li.li_prev = nil - li.li_tv = {v_type=lib.VAR_UNKNOWN, v_lock=lib.VAR_UNLOCKED} - return li -end - -local function list_index(l, idx) - return tv_list_find(l, idx) -end - local function list_items(l) local lis = {} local li = l.lv_first for i = 1, l.lv_len do - lis[i] = li + lis[i] = ffi.gc(li, nil) li = li.li_next end return lis end -local function list_watch(li) - return ffi.new('listwatch_T', {lw_item=li}) +local function list_watch_alloc(li) + return ffi.cast('listwatch_T*', ffi.new('listwatch_T[1]', {{lw_item=li}})) end -local alloc_log -local restore_allocators +local function list_watch(l, li) + local lw = list_watch_alloc(li or l.lv_first) + lib.tv_list_watch_add(l, lw) + return lw +end + +local function get_alloc_rets(exp_log, res) + for i = 1,#exp_log do + if ({malloc=true, calloc=true})[exp_log[i].func] then + res[#res + 1] = exp_log[i].ret + end + end + res.freed = function(r, n) return {func='free', args={r[n]}} end + return exp_log +end local to_cstr_nofree = function(v) return lib.xstrdup(v) end @@ -122,7 +121,7 @@ describe('typval.c', function() li = li_alloc(true) li.li_tv.v_type = lib.VAR_LIST - l = list() + l = ffi.gc(list(), nil) l.lv_refcount = 2 li.li_tv.vval.v_list = l lib.tv_list_item_free(li) @@ -185,26 +184,155 @@ describe('typval.c', function() neq(nil, l) local lis = list_items(l) -- Three watchers: pointing to first, middle and last elements. - local lws = {list_watch(lis[1]), list_watch(lis[4]), list_watch(lis[7])} - lib.tv_list_watch_add(l, lws[1]) - lib.tv_list_watch_add(l, lws[2]) - lib.tv_list_watch_add(l, lws[3]) + local lws = { + list_watch(l, lis[1]), + list_watch(l, lis[4]), + list_watch(l, lis[7]), + } lib.tv_list_item_remove(l, lis[4]) + ffi.gc(lis[4], lib.tv_list_item_free) eq({lis[1], lis[5], lis[7]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item}) lib.tv_list_item_remove(l, lis[2]) + ffi.gc(lis[2], lib.tv_list_item_free) eq({lis[1], lis[5], lis[7]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item}) lib.tv_list_item_remove(l, lis[7]) + ffi.gc(lis[7], lib.tv_list_item_free) eq({lis[1], lis[5], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) lib.tv_list_item_remove(l, lis[1]) + ffi.gc(lis[1], lib.tv_list_item_free) eq({lis[3], lis[5], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) + alloc_log:clear() lib.tv_list_free(l) + alloc_log:check({ + a.freed(lis[3]), + a.freed(lis[5]), + a.freed(lis[6]), + a.freed(l), + }) end) end) end) + describe('watch', function() + describe('remove()', function() + itp('works', function() + local l = ffi.gc(list(1, 2, 3, 4, 5, 6, 7), nil) + eq(nil, l.lv_watch) + local lw = list_watch(l) + neq(nil, l.lv_watch) + alloc_log:clear() + lib.tv_list_watch_remove(l, lw) + eq(nil, l.lv_watch) + alloc_log:check({ + -- Does not free anything. + }) + local lws = { list_watch(l), list_watch(l), list_watch(l) } + alloc_log:clear() + lib.tv_list_watch_remove(l, lws[2]) + eq(lws[3], l.lv_watch) + eq(lws[1], l.lv_watch.lw_next) + lib.tv_list_watch_remove(l, lws[1]) + eq(lws[3], l.lv_watch) + eq(nil, l.lv_watch.lw_next) + lib.tv_list_watch_remove(l, lws[3]) + eq(nil, l.lv_watch) + alloc_log:check({ + -- Does not free anything. + }) + end) + itp('ignores not found watchers', function() + local l = list(1, 2, 3, 4, 5, 6, 7) + local lw = list_watch_alloc() + lib.tv_list_watch_remove(l, lw) + end) + end) + end) + -- add() and fix() were tested when testing tv_list_item_remove() + describe('alloc()/free()', function() + itp('recursively frees list with', function() + local l1 = ffi.gc(list(1, 'abc'), nil) + local l2 = ffi.gc(list({[type_key]=dict_type}), nil) + local l3 = ffi.gc(list({[type_key]=list_type}), nil) + local alloc_rets = {} + alloc_log:check(get_alloc_rets({ + a.list(l1), + a.li(l1.lv_first), + a.str(l1.lv_last.li_tv.vval.v_string, #('abc')), + a.li(l1.lv_last), + a.list(l2), + a.dict(l2.lv_first.li_tv.vval.v_dict), + a.li(l2.lv_first), + a.list(l3), + a.list(l3.lv_first.li_tv.vval.v_list), + a.li(l3.lv_first), + }, alloc_rets)) + lib.tv_list_free(l1) + alloc_log:check({ + alloc_rets:freed(2), + alloc_rets:freed(3), + alloc_rets:freed(4), + alloc_rets:freed(1), + }) + lib.tv_list_free(l2) + alloc_log:check({ + alloc_rets:freed(6), + alloc_rets:freed(7), + alloc_rets:freed(5), + }) + lib.tv_list_free(l3) + alloc_log:check({ + alloc_rets:freed(9), + alloc_rets:freed(10), + alloc_rets:freed(8), + }) + end) + itp('frees all containers inside a list', function() + local l1 = ffi.gc(list('abc', {[type_key]=dict_type}, {[type_key]=list_type}), nil) + local alloc_rets = {} + alloc_log:check(get_alloc_rets({ + a.list(l1), + a.str(l1.lv_first.li_tv.vval.v_string, #('abc')), + a.li(l1.lv_first), + a.dict(l1.lv_first.li_next.li_tv.vval.v_dict), + a.li(l1.lv_first.li_next), + a.list(l1.lv_last.li_tv.vval.v_list), + a.li(l1.lv_last), + }, alloc_rets)) + lib.tv_list_free(l1) + alloc_log:check({ + alloc_rets:freed(2), + alloc_rets:freed(3), + alloc_rets:freed(4), + alloc_rets:freed(5), + alloc_rets:freed(6), + alloc_rets:freed(7), + alloc_rets:freed(1), + }) + end) + end) + describe('unref()', function() + itp('recursively frees list when reference count goes to 0', function() + local l = ffi.gc(list({[type_key]=list_type}), nil) + local alloc_rets = {} + alloc_log:check(get_alloc_rets({ + a.list(l), + a.list(l.lv_first.li_tv.vval.v_list), + a.li(l.lv_first), + }, alloc_rets)) + l.lv_refcount = 2 + lib.tv_list_unref(l) + alloc_log:check({}) + lib.tv_list_unref(l) + alloc_log:check({ + alloc_rets:freed(2), + alloc_rets:freed(3), + alloc_rets:freed(1), + }) + end) + end) end) end) From be360d88414fa3b874262f3ed3a2bba5926a751e Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 14 Sep 2016 20:32:07 +0300 Subject: [PATCH 0203/1671] unittests: Add tests for tv_list_insert() --- test/unit/eval/typval_spec.lua | 72 ++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 0967e38eef..fa15686fb2 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -16,6 +16,7 @@ local li_alloc = eval_helpers.li_alloc local dict_type = eval_helpers.dict_type local list_type = eval_helpers.list_type local lua2typvalt = eval_helpers.lua2typvalt +local typvalt2lua = eval_helpers.typvalt2lua local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h') @@ -334,5 +335,76 @@ describe('typval.c', function() }) end) end) + describe('remove_items()', function() + itp('works', function() + local l_tv = lua2typvalt({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}) + local l = l_tv.vval.v_list + local lis = list_items(l) + -- Three watchers: pointing to first, middle and last elements. + local lws = { + list_watch(l, lis[1]), + list_watch(l, lis[7]), + list_watch(l, lis[13]), + } + alloc_log:clear() + + lib.tv_list_remove_items(l, lis[1], lis[3]) + eq({4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, typvalt2lua(l_tv)) + eq({lis[4], lis[7], lis[13]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item}) + + lib.tv_list_remove_items(l, lis[11], lis[13]) + eq({4, 5, 6, 7, 8, 9, 10}, typvalt2lua(l_tv)) + eq({lis[4], lis[7], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) + + lib.tv_list_remove_items(l, lis[6], lis[8]) + eq({4, 5, 9, 10}, typvalt2lua(l_tv)) + eq({lis[4], lis[9], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) + + lib.tv_list_remove_items(l, lis[4], lis[10]) + eq({[type_key]=list_type}, typvalt2lua(l_tv)) + eq({true, true, true}, {lws[1].lw_item == nil, lws[2].lw_item == nil, lws[3].lw_item == nil}) + + alloc_log:check({}) + end) + end) + describe('insert()', function() + itp('works', function() + local l_tv = lua2typvalt({1, 2, 3, 4, 5, 6, 7}) + local l = l_tv.vval.v_list + local lis = list_items(l) + local li + + li = li_alloc(true) + li.li_tv = {v_type=lib.VAR_FLOAT, vval={v_float=100500}} + lib.tv_list_insert(l, li, nil) + eq(l.lv_last, li) + eq({1, 2, 3, 4, 5, 6, 7, 100500}, typvalt2lua(l_tv)) + + li = li_alloc(true) + li.li_tv = {v_type=lib.VAR_FLOAT, vval={v_float=0}} + lib.tv_list_insert(l, li, lis[1]) + eq(l.lv_first, li) + eq({0, 1, 2, 3, 4, 5, 6, 7, 100500}, typvalt2lua(l_tv)) + + li = li_alloc(true) + li.li_tv = {v_type=lib.VAR_FLOAT, vval={v_float=4.5}} + lib.tv_list_insert(l, li, lis[5]) + eq(list_items(l)[6], li) + eq({0, 1, 2, 3, 4, 4.5, 5, 6, 7, 100500}, typvalt2lua(l_tv)) + end) + itp('works with an empty list', function() + local l_tv = lua2typvalt({[type_key]=list_type}) + local l = l_tv.vval.v_list + + eq(nil, l.lv_first) + eq(nil, l.lv_last) + + local li = li_alloc(true) + li.li_tv = {v_type=lib.VAR_FLOAT, vval={v_float=100500}} + lib.tv_list_insert(l, li, nil) + eq(l.lv_last, li) + eq({100500}, typvalt2lua(l_tv)) + end) + end) end) end) From 9b8beaff021fd83770fb5510904910de2e696263 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 17 Sep 2016 04:25:49 +0300 Subject: [PATCH 0204/1671] =?UTF-8?q?unittests:=20Add=20tests=20for=20tv?= =?UTF-8?q?=5Flist=5Finsert*()/=E2=80=A6append*()=20functions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/unit/eval/helpers.lua | 18 ++- test/unit/eval/typval_spec.lua | 222 ++++++++++++++++++++++++++++----- 2 files changed, 207 insertions(+), 33 deletions(-) diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index 8bf06e61f1..f4ea6799f5 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -369,7 +369,7 @@ lua2typvalt = function(l, processed) return typvalt(eval.VAR_STRING, {v_string=eval.xmemdupz(to_cstr(l), #l)}) elseif type(l) == 'cdata' then local tv = typvalt(eval.VAR_UNKNOWN) - eval.copy_tv(l, tv) + eval.tv_copy(l, tv) return tv end end @@ -379,14 +379,28 @@ local function void(ptr) return ffi.cast(void_ptr, ptr) end +local function alloc_len(len, get_ptr) + if type(len) == 'string' or type(len) == 'table' then + return #len + elseif len == nil then + return eval.strlen(get_ptr()) + else + return len + end +end + local alloc_logging_helpers = { list = function(l) return {func='calloc', args={1, ffi.sizeof('list_T')}, ret=void(l)} end, li = function(li) return {func='malloc', args={ffi.sizeof('listitem_T')}, ret=void(li)} end, dict = function(d) return {func='malloc', args={ffi.sizeof('dict_T')}, ret=void(d)} end, di = function(di, size) + size = alloc_len(size, function() return di.di_key end) return {func='malloc', args={ffi.offsetof('dictitem_T', 'di_key') + size + 1}, ret=void(di)} end, - str = function(s, size) return {func='malloc', args={size + 1}, ret=void(s)} end, + str = function(s, size) + size = alloc_len(size, function() return s end) + return {func='malloc', args={size + 1}, ret=void(s)} + end, freed = function(p) return {func='free', args={type(p) == 'table' and p or void(p)}} end, } diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index fa15686fb2..680d5fa3b4 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -13,10 +13,14 @@ local a = eval_helpers.alloc_logging_helpers local list = eval_helpers.list local type_key = eval_helpers.type_key local li_alloc = eval_helpers.li_alloc +local int_type = eval_helpers.int_type local dict_type = eval_helpers.dict_type local list_type = eval_helpers.list_type +local null_list = eval_helpers.null_list +local null_dict = eval_helpers.null_dict local lua2typvalt = eval_helpers.lua2typvalt local typvalt2lua = eval_helpers.typvalt2lua +local null_string = eval_helpers.null_string local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h') @@ -367,43 +371,199 @@ describe('typval.c', function() alloc_log:check({}) end) end) - describe('insert()', function() - itp('works', function() - local l_tv = lua2typvalt({1, 2, 3, 4, 5, 6, 7}) - local l = l_tv.vval.v_list - local lis = list_items(l) - local li + describe('insert', function() + describe('()', function() + itp('works', function() + local l_tv = lua2typvalt({1, 2, 3, 4, 5, 6, 7}) + local l = l_tv.vval.v_list + local lis = list_items(l) + local li - li = li_alloc(true) - li.li_tv = {v_type=lib.VAR_FLOAT, vval={v_float=100500}} - lib.tv_list_insert(l, li, nil) - eq(l.lv_last, li) - eq({1, 2, 3, 4, 5, 6, 7, 100500}, typvalt2lua(l_tv)) + li = li_alloc(true) + li.li_tv = {v_type=lib.VAR_FLOAT, vval={v_float=100500}} + lib.tv_list_insert(l, li, nil) + eq(l.lv_last, li) + eq({1, 2, 3, 4, 5, 6, 7, 100500}, typvalt2lua(l_tv)) - li = li_alloc(true) - li.li_tv = {v_type=lib.VAR_FLOAT, vval={v_float=0}} - lib.tv_list_insert(l, li, lis[1]) - eq(l.lv_first, li) - eq({0, 1, 2, 3, 4, 5, 6, 7, 100500}, typvalt2lua(l_tv)) + li = li_alloc(true) + li.li_tv = {v_type=lib.VAR_FLOAT, vval={v_float=0}} + lib.tv_list_insert(l, li, lis[1]) + eq(l.lv_first, li) + eq({0, 1, 2, 3, 4, 5, 6, 7, 100500}, typvalt2lua(l_tv)) - li = li_alloc(true) - li.li_tv = {v_type=lib.VAR_FLOAT, vval={v_float=4.5}} - lib.tv_list_insert(l, li, lis[5]) - eq(list_items(l)[6], li) - eq({0, 1, 2, 3, 4, 4.5, 5, 6, 7, 100500}, typvalt2lua(l_tv)) + li = li_alloc(true) + li.li_tv = {v_type=lib.VAR_FLOAT, vval={v_float=4.5}} + lib.tv_list_insert(l, li, lis[5]) + eq(list_items(l)[6], li) + eq({0, 1, 2, 3, 4, 4.5, 5, 6, 7, 100500}, typvalt2lua(l_tv)) + end) + itp('works with an empty list', function() + local l_tv = lua2typvalt({[type_key]=list_type}) + local l = l_tv.vval.v_list + + eq(nil, l.lv_first) + eq(nil, l.lv_last) + + local li = li_alloc(true) + li.li_tv = {v_type=lib.VAR_FLOAT, vval={v_float=100500}} + lib.tv_list_insert(l, li, nil) + eq(l.lv_last, li) + eq({100500}, typvalt2lua(l_tv)) + end) end) - itp('works with an empty list', function() - local l_tv = lua2typvalt({[type_key]=list_type}) - local l = l_tv.vval.v_list + describe('tv()', function() + itp('works', function() + local l_tv = lua2typvalt({[type_key]=list_type}) + local l = l_tv.vval.v_list - eq(nil, l.lv_first) - eq(nil, l.lv_last) + local l_l_tv = lua2typvalt({[type_key]=list_type}) + alloc_log:clear() + local l_l = l_l_tv.vval.v_list + eq(1, l_l.lv_refcount) + lib.tv_list_insert_tv(l, l_l_tv, nil) + eq(2, l_l.lv_refcount) + eq(l_l, l.lv_first.li_tv.vval.v_list) + alloc_log:check({ + a.li(l.lv_first), + }) - local li = li_alloc(true) - li.li_tv = {v_type=lib.VAR_FLOAT, vval={v_float=100500}} - lib.tv_list_insert(l, li, nil) - eq(l.lv_last, li) - eq({100500}, typvalt2lua(l_tv)) + local l_s_tv = lua2typvalt('test') + alloc_log:check({ + a.str(l_s_tv.vval.v_string, 'test'), + }) + lib.tv_list_insert_tv(l, l_s_tv, l.lv_first) + alloc_log:check({ + a.li(l.lv_first), + a.str(l.lv_first.li_tv.vval.v_string, 'test'), + }) + + eq({'test', {[type_key]=list_type}}, typvalt2lua(l_tv)) + end) + end) + end) + describe('append', function() + describe('list()', function() + itp('works', function() + local l_tv = lua2typvalt({[type_key]=list_type}) + local l = l_tv.vval.v_list + + local l_l = list(1) + alloc_log:clear() + eq(1, l_l.lv_refcount) + lib.tv_list_append_list(l, l_l) + eq(2, l_l.lv_refcount) + eq(l_l, l.lv_first.li_tv.vval.v_list) + alloc_log:check({ + a.li(l.lv_last), + }) + + lib.tv_list_append_list(l, nil) + alloc_log:check({ + a.li(l.lv_last), + }) + + eq({{1}, null_list}, typvalt2lua(l_tv)) + end) + end) + describe('dict()', function() + itp('works', function() + local l_tv = lua2typvalt({[type_key]=list_type}) + local l = l_tv.vval.v_list + + local l_d_tv = lua2typvalt({test=1}) + local l_d = l_d_tv.vval.v_dict + alloc_log:clear() + eq(1, l_d.dv_refcount) + lib.tv_list_append_dict(l, l_d) + eq(2, l_d.dv_refcount) + eq(l_d, l.lv_first.li_tv.vval.v_list) + alloc_log:check({ + a.li(l.lv_last), + }) + + lib.tv_list_append_dict(l, nil) + alloc_log:check({ + a.li(l.lv_last), + }) + + eq({{test=1}, null_dict}, typvalt2lua(l_tv)) + end) + end) + describe('string()', function() + itp('works', function() + local l_tv = lua2typvalt({[type_key]=list_type}) + local l = l_tv.vval.v_list + + alloc_log:clear() + lib.tv_list_append_string(l, 'test', 3) + alloc_log:check({ + a.str(l.lv_last.li_tv.vval.v_string, 'tes'), + a.li(l.lv_last), + }) + + lib.tv_list_append_string(l, nil, 0) + alloc_log:check({ + a.li(l.lv_last), + }) + + lib.tv_list_append_string(l, nil, -1) + alloc_log:check({ + a.li(l.lv_last), + }) + + lib.tv_list_append_string(l, 'test', -1) + alloc_log:check({ + a.str(l.lv_last.li_tv.vval.v_string, 'test'), + a.li(l.lv_last), + }) + + eq({'tes', null_string, null_string, 'test'}, typvalt2lua(l_tv)) + end) + end) + describe('allocated string()', function() + itp('works', function() + local l_tv = lua2typvalt({[type_key]=list_type}) + local l = l_tv.vval.v_list + + local s = lib.xstrdup('test') + alloc_log:clear() + lib.tv_list_append_allocated_string(l, s) + alloc_log:check({ + a.li(l.lv_last), + }) + + lib.tv_list_append_allocated_string(l, nil) + alloc_log:check({ + a.li(l.lv_last), + }) + + lib.tv_list_append_allocated_string(l, nil) + alloc_log:check({ + a.li(l.lv_last), + }) + + eq({'test', null_string, null_string}, typvalt2lua(l_tv)) + end) + end) + describe('number()', function() + itp('works', function() + local l_tv = lua2typvalt({[type_key]=list_type}) + local l = l_tv.vval.v_list + + alloc_log:clear() + lib.tv_list_append_number(l, -100500) + alloc_log:check({ + a.li(l.lv_last), + }) + + lib.tv_list_append_number(l, 100500) + alloc_log:check({ + a.li(l.lv_last), + }) + + eq({{[type_key]=int_type, value=-100500}, + {[type_key]=int_type, value=100500}}, typvalt2lua(l_tv)) + end) end) end) end) From 9898f36aa306d2a7f53fbe08c64048260992d3bb Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 17 Sep 2016 22:06:11 +0300 Subject: [PATCH 0205/1671] unittests: Test tv_list_copy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also found some bugs: 1. var_item_copy() always fails to copy v:_null_list and v:_null_dict. Fixing this should mean fixing `deepcopy(v:_null_list)` which should’ve been, but was not listed in #4615. This also fixes `deepcopy(v:_null_dict)`. 2. var_item_copy() crashes when trying to copy NULL string with `conv != NULL`. 3. `conv` argument is ignored when copying list unless `deep` is true, but it was not reflected in documentation. 4. `tv_dict_item_alloc_len()` allocated more memory then needed. 5. typvalt2lua was not able to handle self-referencing containers. --- src/nvim/eval.c | 11 +- src/nvim/eval/typval.c | 1 + test/unit/eval/typval_spec.lua | 183 ++++++++++++++++++++++++++++++++- 3 files changed, 190 insertions(+), 5 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a2b6d12288..7c3754607e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -19024,7 +19024,7 @@ bool valid_varname(const char *varname) /// list[1]`) var_item_copy with zero copyID will emit /// a copy with (`copy[0] isnot copy[1]`), with non-zero it /// will emit a copy with (`copy[0] is copy[1]`) like in the -/// original list. Not use when deep is false. +/// original list. Not used when deep is false. int var_item_copy(const vimconv_T *const conv, typval_T *const from, typval_T *const to, @@ -19050,7 +19050,8 @@ int var_item_copy(const vimconv_T *const conv, tv_copy(from, to); break; case VAR_STRING: - if (conv == NULL || conv->vc_type == CONV_NONE) { + if (conv == NULL || conv->vc_type == CONV_NONE + || from->vval.v_string == NULL) { tv_copy(from, to); } else { to->v_type = VAR_STRING; @@ -19075,8 +19076,9 @@ int var_item_copy(const vimconv_T *const conv, } else { to->vval.v_list = tv_list_copy(conv, from->vval.v_list, deep, copyID); } - if (to->vval.v_list == NULL) + if (to->vval.v_list == NULL && from->vval.v_list != NULL) { ret = FAIL; + } break; case VAR_DICT: to->v_type = VAR_DICT; @@ -19090,8 +19092,9 @@ int var_item_copy(const vimconv_T *const conv, } else { to->vval.v_dict = tv_dict_copy(conv, from->vval.v_dict, deep, copyID); } - if (to->vval.v_dict == NULL) + if (to->vval.v_dict == NULL && from->vval.v_dict != NULL) { ret = FAIL; + } break; case VAR_UNKNOWN: EMSG2(_(e_intern2), "var_item_copy(UNKNOWN)"); diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index a26afb20c6..ae4199697f 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -424,6 +424,7 @@ void tv_list_append_number(list_T *const l, const varnumber_T n) /// Make a copy of list /// /// @param[in] conv If non-NULL, then all internal strings will be converted. +/// Only used when `deep` is true. /// @param[in] orig Original list to copy. /// @param[in] deep If false, then shallow copy will be done. /// @param[in] copyID See var_item_copy(). diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 680d5fa3b4..7a6b4579f9 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -3,17 +3,21 @@ local eval_helpers = require('test.unit.eval.helpers') local itp = helpers.gen_itp(it) +local OK = helpers.OK local eq = helpers.eq local neq = helpers.neq local ffi = helpers.ffi local cimport = helpers.cimport +local to_cstr = helpers.to_cstr local alloc_log_new = helpers.alloc_log_new local a = eval_helpers.alloc_logging_helpers local list = eval_helpers.list +local lst2tbl = eval_helpers.lst2tbl local type_key = eval_helpers.type_key local li_alloc = eval_helpers.li_alloc local int_type = eval_helpers.int_type +local first_di = eval_helpers.first_di local dict_type = eval_helpers.dict_type local list_type = eval_helpers.list_type local null_list = eval_helpers.null_list @@ -22,7 +26,8 @@ local lua2typvalt = eval_helpers.lua2typvalt local typvalt2lua = eval_helpers.typvalt2lua local null_string = eval_helpers.null_string -local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h') +local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h', + './src/nvim/mbyte.h') local function list_items(l) local lis = {} @@ -62,6 +67,30 @@ before_each(function() alloc_log:before_each() end) +local function clear_tmp_allocs() + local toremove = {} + local allocs = {} + for i, v in ipairs(alloc_log.log) do + if v.func == 'malloc' or v.func == 'calloc' then + allocs[tostring(v.ret)] = i + elseif v.func == 'realloc' or v.func == 'free' then + if allocs[tostring(v.args[1])] then + toremove[#toremove + 1] = allocs[tostring(v.args[1])] + if v.func == 'free' then + toremove[#toremove + 1] = i + end + end + if v.func == 'realloc' then + allocs[tostring(v.ret)] = i + end + end + end + table.sort(toremove) + for i = #toremove,1,-1 do + table.remove(alloc_log.log, toremove[i]) + end +end + after_each(function() alloc_log:after_each() end) @@ -566,5 +595,157 @@ describe('typval.c', function() end) end) end) + describe('copy()', function() + local function tv_list_copy(...) + return ffi.gc(lib.tv_list_copy(...), lib.tv_list_unref) + end + itp('copies NULL correctly', function() + eq(nil, lib.tv_list_copy(nil, nil, true, 0)) + eq(nil, lib.tv_list_copy(nil, nil, false, 0)) + eq(nil, lib.tv_list_copy(nil, nil, true, 1)) + eq(nil, lib.tv_list_copy(nil, nil, false, 1)) + end) + itp('copies list correctly without converting items', function() + local v = {{['«']='»'}, {'„'}, 1, '“', null_string, null_list, null_dict} + local l_tv = lua2typvalt(v) + local l = l_tv.vval.v_list + local lis = list_items(l) + alloc_log:clear() + + eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) + eq(1, lis[2].li_tv.vval.v_list.lv_refcount) + local l_copy1 = tv_list_copy(nil, l, false, 0) + eq(2, lis[1].li_tv.vval.v_dict.dv_refcount) + eq(2, lis[2].li_tv.vval.v_list.lv_refcount) + local lis_copy1 = list_items(l_copy1) + eq(lis[1].li_tv.vval.v_dict, lis_copy1[1].li_tv.vval.v_dict) + eq(lis[2].li_tv.vval.v_list, lis_copy1[2].li_tv.vval.v_list) + eq(v, lst2tbl(l_copy1)) + alloc_log:check({ + a.list(l_copy1), + a.li(lis_copy1[1]), + a.li(lis_copy1[2]), + a.li(lis_copy1[3]), + a.li(lis_copy1[4]), + a.str(lis_copy1[4].li_tv.vval.v_string, #v[4]), + a.li(lis_copy1[5]), + a.li(lis_copy1[6]), + a.li(lis_copy1[7]), + }) + lib.tv_list_free(ffi.gc(l_copy1, nil)) + alloc_log:clear() + + eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) + eq(1, lis[2].li_tv.vval.v_list.lv_refcount) + local l_deepcopy1 = tv_list_copy(nil, l, true, 0) + neq(nil, l_deepcopy1) + eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) + eq(1, lis[2].li_tv.vval.v_list.lv_refcount) + local lis_deepcopy1 = list_items(l_deepcopy1) + neq(lis[1].li_tv.vval.v_dict, lis_deepcopy1[1].li_tv.vval.v_dict) + neq(lis[2].li_tv.vval.v_list, lis_deepcopy1[2].li_tv.vval.v_list) + eq(v, lst2tbl(l_deepcopy1)) + local di_deepcopy1 = first_di(lis_deepcopy1[1].li_tv.vval.v_dict) + alloc_log:check({ + a.list(l_deepcopy1), + a.li(lis_deepcopy1[1]), + a.dict(lis_deepcopy1[1].li_tv.vval.v_dict), + a.di(di_deepcopy1, #('«')), + a.str(di_deepcopy1.di_tv.vval.v_string, #v[1]['«']), + a.li(lis_deepcopy1[2]), + a.list(lis_deepcopy1[2].li_tv.vval.v_list), + a.li(lis_deepcopy1[2].li_tv.vval.v_list.lv_first), + a.str(lis_deepcopy1[2].li_tv.vval.v_list.lv_first.li_tv.vval.v_string, #v[2][1]), + a.li(lis_deepcopy1[3]), + a.li(lis_deepcopy1[4]), + a.str(lis_deepcopy1[4].li_tv.vval.v_string, #v[4]), + a.li(lis_deepcopy1[5]), + a.li(lis_deepcopy1[6]), + a.li(lis_deepcopy1[7]), + }) + end) + itp('copies list correctly and converts items', function() + local vc = ffi.gc(ffi.new('vimconv_T[1]'), function(vc) + lib.convert_setup(vc, nil, nil) + end) + -- UTF-8 ↔ latin1 conversions need no iconv + eq(OK, lib.convert_setup(vc, to_cstr('utf-8'), to_cstr('latin1'))) + + local v = {{['«']='»'}, {'„'}, 1, '“', null_string, null_list, null_dict} + local l_tv = lua2typvalt(v) + local l = l_tv.vval.v_list + local lis = list_items(l) + alloc_log:clear() + + eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) + eq(1, lis[2].li_tv.vval.v_list.lv_refcount) + local l_deepcopy1 = tv_list_copy(vc, l, true, 0) + neq(nil, l_deepcopy1) + eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) + eq(1, lis[2].li_tv.vval.v_list.lv_refcount) + local lis_deepcopy1 = list_items(l_deepcopy1) + neq(lis[1].li_tv.vval.v_dict, lis_deepcopy1[1].li_tv.vval.v_dict) + neq(lis[2].li_tv.vval.v_list, lis_deepcopy1[2].li_tv.vval.v_list) + eq({{['\171']='\187'}, {'\191'}, 1, '\191', null_string, null_list, null_dict}, + lst2tbl(l_deepcopy1)) + local di_deepcopy1 = first_di(lis_deepcopy1[1].li_tv.vval.v_dict) + clear_tmp_allocs() + alloc_log:check({ + a.list(l_deepcopy1), + a.li(lis_deepcopy1[1]), + a.dict(lis_deepcopy1[1].li_tv.vval.v_dict), + a.di(di_deepcopy1, 1), + a.str(di_deepcopy1.di_tv.vval.v_string, 2), + a.li(lis_deepcopy1[2]), + a.list(lis_deepcopy1[2].li_tv.vval.v_list), + a.li(lis_deepcopy1[2].li_tv.vval.v_list.lv_first), + a.str(lis_deepcopy1[2].li_tv.vval.v_list.lv_first.li_tv.vval.v_string, #v[2][1]), + a.li(lis_deepcopy1[3]), + a.li(lis_deepcopy1[4]), + a.str(lis_deepcopy1[4].li_tv.vval.v_string, #v[4]), + a.li(lis_deepcopy1[5]), + a.li(lis_deepcopy1[6]), + a.li(lis_deepcopy1[7]), + }) + end) + itp('returns different/same containers with(out) copyID', function() + local l_inner_tv = lua2typvalt({[type_key]=list_type}) + local l_tv = lua2typvalt({l_inner_tv, l_inner_tv}) + eq(3, l_inner_tv.vval.v_list.lv_refcount) + local l = l_tv.vval.v_list + eq(l.lv_first.li_tv.vval.v_list, l.lv_last.li_tv.vval.v_list) + + local l_copy1 = tv_list_copy(nil, l, true, 0) + neq(l_copy1.lv_first.li_tv.vval.v_list, l_copy1.lv_last.li_tv.vval.v_list) + eq({{[type_key]=list_type}, {[type_key]=list_type}}, lst2tbl(l_copy1)) + + local l_copy2 = tv_list_copy(nil, l, true, 2) + eq(l_copy2.lv_first.li_tv.vval.v_list, l_copy2.lv_last.li_tv.vval.v_list) + eq({{[type_key]=list_type}, {[type_key]=list_type}}, lst2tbl(l_copy2)) + + eq(3, l_inner_tv.vval.v_list.lv_refcount) + end) + itp('works with self-referencing list with copyID', function() + local l_tv = lua2typvalt({[type_key]=list_type}) + local l = l_tv.vval.v_list + eq(1, l.lv_refcount) + lib.tv_list_append_list(l, l) + eq(2, l.lv_refcount) + + local l_copy1 = tv_list_copy(nil, l, true, 2) + eq(2, l_copy1.lv_refcount) + local v = {} + v[1] = v + eq(v, lst2tbl(l_copy1)) + + local lis = list_items(l) + lib.tv_list_item_remove(l, lis[1]) + eq(1, l.lv_refcount) + + local lis_copy1 = list_items(l_copy1) + lib.tv_list_item_remove(l_copy1, lis_copy1[1]) + eq(1, l_copy1.lv_refcount) + end) + end) end) end) From 82e6cac5f9d437da5aeb8c4cf51945cfeb5922ff Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 18 Sep 2016 01:54:23 +0300 Subject: [PATCH 0206/1671] functests: Add null_spec.lua from #4615 For now it is full of FIXMEs and tests for incorrect behaviour. Sorted out to have FIXMEs in one place, commented crashing tests in other and correctly working tests in the third one. --- test/functional/eval/null_spec.lua | 141 +++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 test/functional/eval/null_spec.lua diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua new file mode 100644 index 0000000000..9a35905be0 --- /dev/null +++ b/test/functional/eval/null_spec.lua @@ -0,0 +1,141 @@ +local helpers = require('test.functional.helpers')(after_each) + +local curbufmeths = helpers.curbufmeths +local redir_exec = helpers.redir_exec +local exc_exec = helpers.exc_exec +local command = helpers.command +local clear = helpers.clear +local meths = helpers.meths +local funcs = helpers.funcs +local eq = helpers.eq + +describe('NULL', function() + before_each(function() + clear() + command('let L = v:_null_list') + command('let D = v:_null_dict') + command('let S = $XXX_NONEXISTENT_VAR_XXX') + end) + local tmpfname = 'Xtest-functional-viml-null' + after_each(function() + os.remove(tmpfname) + end) + describe('list', function() + local null_list_test = function(name, cmd, err) + it(name, function() + eq(err, exc_exec(cmd)) + end) + end + local null_list_expr_test = function(name, expr, err, val, after) + it(name, function() + eq((err == 0) and ('') or ('\n' .. err), + redir_exec('let g:_var = ' .. expr)) + if val == nil then + eq(0, funcs.exists('g:_var')) + else + eq(val, meths.get_var('_var')) + end + if after ~= nil then + after() + end + end) + end + + -- Incorrect behaviour + + -- FIXME map() should not return 0 without error + null_list_expr_test('does not crash map()', 'map(L, "v:val")', 0, 0) + -- FIXME map() should not return 0 without error + null_list_expr_test('does not crash filter()', 'filter(L, "1")', 0, 0) + -- FIXME map() should at least return L + null_list_expr_test('makes map() return v:_null_list', 'map(L, "v:val") is# L', 0, 0) + -- FIXME filter() should at least return L + null_list_expr_test('makes filter() return v:_null_list', 'map(L, "1") is# L', 0, 0) + -- FIXME add() should not return 1 at all + null_list_expr_test('does not crash add()', 'add(L, 0)', 0, 1) + -- FIXME extend() should not return 0 without error + null_list_expr_test('does not crash extend()', 'extend(L, [1])', 0, 0) + -- FIXME extend() should not return 0 at all + null_list_expr_test('does not crash extend() (second position)', 'extend([1], L)', 0, 0) + -- FIXME Should return 1 + null_list_expr_test('is equal to itself', 'L == L', 0, 0) + -- FIXME Should return 0 + null_list_expr_test('is not not equal to itself', 'L != L', 0, 1) + -- FIXME Should return empty list + null_list_expr_test('can be added to itself', '(L + L)', 'E15: Invalid expression: (L + L)', nil) + -- FIXME Should return [1] + null_list_expr_test('can be added to non-empty list (reversed)', '(L + [1])', + 'E15: Invalid expression: (L + [1])', nil) + -- FIXME Should return [1] + null_list_expr_test('can be added to non-empty list', '([1] + L)', + 'E15: Invalid expression: ([1] + L)', nil) + -- FIXME Should return 1 + null_list_expr_test('counts correctly', 'count([L], L)', 0, 0) + -- FIXME should be accepted by inputlist() + null_list_expr_test('is accepted as an empty list by inputlist()', + '[feedkeys("\\n"), inputlist(L)]', 'E686: Argument of inputlist() must be a List', {0, 0}) + -- FIXME should be accepted by writefile(), return {0, {}} + null_list_expr_test('is accepted as an empty list by writefile()', + ('[writefile(L, "%s"), readfile("%s")]'):format(tmpfname, tmpfname), + 'E484: Can\'t open file ' .. tmpfname, {0, {}}) + -- FIXME should give error message + null_list_expr_test('does not crash remove()', 'remove(L, 0)', 0, 0) + -- FIXME should return 0 + null_list_expr_test('is accepted by setqflist()', 'setqflist(L)', 0, -1) + -- FIXME should return 0 + null_list_expr_test('is accepted by setloclist()', 'setloclist(1, L)', 0, -1) + -- FIXME should return 0 + null_list_expr_test('is accepted by setmatches()', 'setmatches(L)', 0, -1) + -- FIXME should return empty list or error out + null_list_expr_test('is accepted by sort()', 'sort(L)', 0, 0) + -- FIXME Should return 1 + null_list_expr_test('is accepted by sort()', 'sort(L) is L', 0, 0) + -- FIXME should not error out + null_list_test('is accepted by :cexpr', 'cexpr L', 'Vim(cexpr):E777: String or List expected') + -- FIXME should not error out + null_list_test('is accepted by :lexpr', 'lexpr L', 'Vim(lexpr):E777: String or List expected') + -- FIXME should not error out + null_list_test('is accepted by :for', 'for x in L|throw x|endfor', 'Vim(for):E714: List required') + + -- Subjectable behaviour + + -- FIXME Should return 1 + null_list_expr_test('is equal to empty list', 'L == []', 0, 0) + -- FIXME Should return 1 + null_list_expr_test('is equal to empty list (reverse order)', '[] == L', 0, 0) + -- FIXME Should return 1 + null_list_expr_test('is not locked', 'islocked("v:_null_list")', 0, 0) + + -- Crashes + + -- null_list_expr_test('does not crash setreg', 'setreg("x", L)', 0, 0) + -- null_list_expr_test('does not crash setline', 'setline(1, L)', 0, 0) + -- null_list_expr_test('does not crash system()', 'system("cat", L)', 0, '') + -- null_list_expr_test('does not crash systemlist()', 'systemlist("cat", L)', 0, {}) + + -- Correct behaviour + null_list_expr_test('does not crash append()', 'append(1, L)', 0, 0, function() + eq({''}, curbufmeths.get_lines(0, -1, false)) + end) + null_list_expr_test('is identical to itself', 'L is L', 0, 1) + null_list_expr_test('can be sliced', 'L[:]', 0, {}) + null_list_expr_test('can be copied', 'copy(L)', 0, {}) + null_list_expr_test('can be deepcopied', 'deepcopy(L)', 0, {}) + null_list_expr_test('does not crash when indexed', 'L[1]', + 'E684: list index out of range: 1\nE15: Invalid expression: L[1]', nil) + null_list_expr_test('does not crash call()', 'call("arglistid", L)', 0, 0) + null_list_expr_test('does not crash col()', 'col(L)', 0, 0) + null_list_expr_test('does not crash virtcol()', 'virtcol(L)', 0, 0) + null_list_expr_test('does not crash line()', 'line(L)', 0, 0) + null_list_expr_test('does not crash count()', 'count(L, 1)', 0, 0) + null_list_expr_test('does not crash cursor()', 'cursor(L)', 'E474: Invalid argument', -1) + null_list_expr_test('is empty', 'empty(L)', 0, 1) + null_list_expr_test('does not crash get()', 'get(L, 1, 10)', 0, 10) + null_list_expr_test('has zero length', 'len(L)', 0, 0) + null_list_expr_test('is accepted as an empty list by max()', 'max(L)', 0, 0) + null_list_expr_test('is accepted as an empty list by min()', 'min(L)', 0, 0) + null_list_expr_test('is stringified correctly', 'string(L)', 0, '[]') + null_list_expr_test('is JSON encoded correctly', 'json_encode(L)', 0, '[]') + null_list_test('does not crash lockvar', 'lockvar! L', 0) + end) +end) From f80a00469fdba8a3dec0edae30c911d050485055 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 18 Sep 2016 01:59:07 +0300 Subject: [PATCH 0207/1671] eval/typval: Make tv_list_concat handle NULL lists correctly Fixes some FIXMEs in eval/null_spec.lua. --- src/nvim/eval/typval.c | 27 ++++++++++++++++----------- test/functional/eval/null_spec.lua | 12 ++++-------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index ae4199697f..9dd1b62d7d 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -498,20 +498,25 @@ void tv_list_extend(list_T *const l1, list_T *const l2, int tv_list_concat(list_T *const l1, list_T *const l2, typval_T *const tv) FUNC_ATTR_WARN_UNUSED_RESULT { - if (l1 == NULL || l2 == NULL) { - return FAIL; - } + list_T *l; - // make a copy of the first list. - list_T *const l = tv_list_copy(NULL, l1, false, 0); - if (l == NULL) { - return FAIL; - } tv->v_type = VAR_LIST; - tv->vval.v_list = l; - // append all items from the second list - tv_list_extend(l, l2, NULL); + if (l1 == NULL && l2 == NULL) { + l = NULL; + } else if (l1 == NULL) { + l = tv_list_copy(NULL, l2, false, 0); + } else { + l = tv_list_copy(NULL, l1, false, 0); + if (l != NULL && l2 != NULL) { + tv_list_extend(l, l2, NULL); + } + } + if (l == NULL && !(l1 == NULL && l2 == NULL)) { + return FAIL; + } + + tv->vval.v_list = l; return OK; } diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index 9a35905be0..ccdd1ce34c 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -61,14 +61,6 @@ describe('NULL', function() null_list_expr_test('is equal to itself', 'L == L', 0, 0) -- FIXME Should return 0 null_list_expr_test('is not not equal to itself', 'L != L', 0, 1) - -- FIXME Should return empty list - null_list_expr_test('can be added to itself', '(L + L)', 'E15: Invalid expression: (L + L)', nil) - -- FIXME Should return [1] - null_list_expr_test('can be added to non-empty list (reversed)', '(L + [1])', - 'E15: Invalid expression: (L + [1])', nil) - -- FIXME Should return [1] - null_list_expr_test('can be added to non-empty list', '([1] + L)', - 'E15: Invalid expression: ([1] + L)', nil) -- FIXME Should return 1 null_list_expr_test('counts correctly', 'count([L], L)', 0, 0) -- FIXME should be accepted by inputlist() @@ -137,5 +129,9 @@ describe('NULL', function() null_list_expr_test('is stringified correctly', 'string(L)', 0, '[]') null_list_expr_test('is JSON encoded correctly', 'json_encode(L)', 0, '[]') null_list_test('does not crash lockvar', 'lockvar! L', 0) + null_list_expr_test('can be added to itself', '(L + L)', 0, {}) + null_list_expr_test('can be added to itself', '(L + L) is L', 0, 1) + null_list_expr_test('can be added to non-empty list', '([1] + L)', 0, {1}) + null_list_expr_test('can be added to non-empty list (reversed)', '(L + [1])', 0, {1}) end) end) From 56e4c2f67e54b88f637d30e565313bc6b2c22f29 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 19 Sep 2016 00:50:07 +0300 Subject: [PATCH 0208/1671] unittests: Test tv_list_concat() --- src/nvim/eval/typval.c | 2 +- test/unit/eval/typval_spec.lua | 142 +++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 1 deletion(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 9dd1b62d7d..1cc195ddc4 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -481,7 +481,7 @@ void tv_list_extend(list_T *const l1, list_T *const l2, int todo = l2->lv_len; // We also quit the loop when we have inserted the original item count of // the list, avoid a hang when we extend a list with itself. - for (listitem_T *item = l2->lv_first + for (listitem_T *item = l2->lv_first ; item != NULL && --todo >= 0 ; item = item->li_next) { tv_list_insert_tv(l1, &item->li_tv, bef); diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 7a6b4579f9..2cc22053f3 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -14,6 +14,7 @@ local alloc_log_new = helpers.alloc_log_new local a = eval_helpers.alloc_logging_helpers local list = eval_helpers.list local lst2tbl = eval_helpers.lst2tbl +local typvalt = eval_helpers.typvalt local type_key = eval_helpers.type_key local li_alloc = eval_helpers.li_alloc local int_type = eval_helpers.int_type @@ -747,5 +748,146 @@ describe('typval.c', function() eq(1, l_copy1.lv_refcount) end) end) + describe('concat()', function() + itp('works with NULL lists', function() + local l = list(1, {}) + alloc_log:clear() + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + + local rettv1 = typvalt() + eq(OK, lib.tv_list_concat(nil, l, rettv1)) + eq(1, l.lv_refcount) + eq(tonumber(lib.VAR_LIST), tonumber(rettv1.v_type)) + eq({1, {}}, typvalt2lua(rettv1)) + eq(1, rettv1.vval.v_list.lv_refcount) + alloc_log:check({ + a.list(rettv1.vval.v_list), + a.li(rettv1.vval.v_list.lv_first), + a.li(rettv1.vval.v_list.lv_last), + }) + eq(2, l.lv_last.li_tv.vval.v_dict.dv_refcount) + + local rettv2 = typvalt() + eq(OK, lib.tv_list_concat(l, nil, rettv2)) + eq(1, l.lv_refcount) + eq(tonumber(lib.VAR_LIST), tonumber(rettv2.v_type)) + eq({1, {}}, typvalt2lua(rettv2)) + eq(1, rettv2.vval.v_list.lv_refcount) + alloc_log:check({ + a.list(rettv2.vval.v_list), + a.li(rettv2.vval.v_list.lv_first), + a.li(rettv2.vval.v_list.lv_last), + }) + eq(3, l.lv_last.li_tv.vval.v_dict.dv_refcount) + + local rettv3 = typvalt() + eq(OK, lib.tv_list_concat(nil, nil, rettv3)) + eq(tonumber(lib.VAR_LIST), tonumber(rettv3.v_type)) + eq(null_list, typvalt2lua(rettv3)) + alloc_log:check({}) + end) + itp('works with two different lists', function() + local l1 = list(1, {}) + local l2 = list(3, {[type_key]=list_type}) + eq(1, l1.lv_refcount) + eq(1, l1.lv_last.li_tv.vval.v_dict.dv_refcount) + eq(1, l2.lv_refcount) + eq(1, l2.lv_last.li_tv.vval.v_list.lv_refcount) + alloc_log:clear() + + local rettv = typvalt() + eq(OK, lib.tv_list_concat(l1, l2, rettv)) + eq(1, l1.lv_refcount) + eq(2, l1.lv_last.li_tv.vval.v_dict.dv_refcount) + eq(1, l2.lv_refcount) + eq(2, l2.lv_last.li_tv.vval.v_list.lv_refcount) + alloc_log:check({ + a.list(rettv.vval.v_list), + a.li(rettv.vval.v_list.lv_first), + a.li(rettv.vval.v_list.lv_first.li_next), + a.li(rettv.vval.v_list.lv_last.li_prev), + a.li(rettv.vval.v_list.lv_last), + }) + eq({1, {}, 3, {[type_key]=list_type}}, typvalt2lua(rettv)) + end) + itp('can concatenate list with itself', function() + local l = list(1, {}) + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + alloc_log:clear() + + local rettv = typvalt() + eq(OK, lib.tv_list_concat(l, l, rettv)) + eq(1, l.lv_refcount) + eq(3, l.lv_last.li_tv.vval.v_dict.dv_refcount) + alloc_log:check({ + a.list(rettv.vval.v_list), + a.li(rettv.vval.v_list.lv_first), + a.li(rettv.vval.v_list.lv_first.li_next), + a.li(rettv.vval.v_list.lv_last.li_prev), + a.li(rettv.vval.v_list.lv_last), + }) + eq({1, {}, 1, {}}, typvalt2lua(rettv)) + end) + itp('can concatenate empty non-NULL lists', function() + local l = list(1, {}) + local le = list() + local le2 = list() + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + eq(1, le.lv_refcount) + eq(1, le2.lv_refcount) + alloc_log:clear() + + local rettv1 = typvalt() + eq(OK, lib.tv_list_concat(l, le, rettv1)) + eq(1, l.lv_refcount) + eq(2, l.lv_last.li_tv.vval.v_dict.dv_refcount) + eq(1, le.lv_refcount) + eq(1, le2.lv_refcount) + alloc_log:check({ + a.list(rettv1.vval.v_list), + a.li(rettv1.vval.v_list.lv_first), + a.li(rettv1.vval.v_list.lv_last), + }) + eq({1, {}}, typvalt2lua(rettv1)) + + local rettv2 = typvalt() + eq(OK, lib.tv_list_concat(le, l, rettv2)) + eq(1, l.lv_refcount) + eq(3, l.lv_last.li_tv.vval.v_dict.dv_refcount) + eq(1, le.lv_refcount) + eq(1, le2.lv_refcount) + alloc_log:check({ + a.list(rettv2.vval.v_list), + a.li(rettv2.vval.v_list.lv_first), + a.li(rettv2.vval.v_list.lv_last), + }) + eq({1, {}}, typvalt2lua(rettv2)) + + local rettv3 = typvalt() + eq(OK, lib.tv_list_concat(le, le, rettv3)) + eq(1, l.lv_refcount) + eq(3, l.lv_last.li_tv.vval.v_dict.dv_refcount) + eq(1, le.lv_refcount) + eq(1, le2.lv_refcount) + alloc_log:check({ + a.list(rettv3.vval.v_list), + }) + eq({[type_key]=list_type}, typvalt2lua(rettv3)) + + local rettv4 = typvalt() + eq(OK, lib.tv_list_concat(le, le2, rettv4)) + eq(1, l.lv_refcount) + eq(3, l.lv_last.li_tv.vval.v_dict.dv_refcount) + eq(1, le.lv_refcount) + eq(1, le2.lv_refcount) + alloc_log:check({ + a.list(rettv4.vval.v_list), + }) + eq({[type_key]=list_type}, typvalt2lua(rettv4)) + end) + end) end) end) From 7ceebacb3fad49ba8321397cf839948caa55b3f5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 24 Sep 2016 00:51:34 +0300 Subject: [PATCH 0209/1671] eval/typval,tests: Fix extending list with itself, add tests Adds unit test for tv_list_extend and regression test for extend() VimL function. --- src/nvim/eval/typval.c | 4 +- .../eval/container_functions_spec.lua | 24 +++ test/unit/eval/helpers.lua | 2 + test/unit/eval/typval_spec.lua | 184 +++++++++++++++--- 4 files changed, 187 insertions(+), 27 deletions(-) create mode 100644 test/functional/eval/container_functions_spec.lua diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 1cc195ddc4..746cbbfe1c 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -479,11 +479,13 @@ void tv_list_extend(list_T *const l1, list_T *const l2, FUNC_ATTR_NONNULL_ARG(1, 2) { int todo = l2->lv_len; + listitem_T *const befbef = (bef == NULL ? NULL : bef->li_prev); + listitem_T *const saved_next = (befbef == NULL ? NULL : befbef->li_next); // We also quit the loop when we have inserted the original item count of // the list, avoid a hang when we extend a list with itself. for (listitem_T *item = l2->lv_first ; item != NULL && --todo >= 0 - ; item = item->li_next) { + ; item = (item == befbef ? saved_next : item->li_next)) { tv_list_insert_tv(l1, &item->li_tv, bef); } } diff --git a/test/functional/eval/container_functions_spec.lua b/test/functional/eval/container_functions_spec.lua new file mode 100644 index 0000000000..04a3248c49 --- /dev/null +++ b/test/functional/eval/container_functions_spec.lua @@ -0,0 +1,24 @@ +local helpers = require('test.functional.helpers')(after_each) + +local eq = helpers.eq +local eval = helpers.eval +local meths = helpers.meths +local clear = helpers.clear + +before_each(clear) + +describe('extend()', function() + it('suceeds to extend list with itself', function() + meths.set_var('l', {1, {}}) + eq({1, {}, 1, {}}, eval('extend(l, l)')) + eq({1, {}, 1, {}}, meths.get_var('l')) + + meths.set_var('l', {1, {}}) + eq({1, {}, 1, {}}, eval('extend(l, l, 0)')) + eq({1, {}, 1, {}}, meths.get_var('l')) + + meths.set_var('l', {1, {}}) + eq({1, 1, {}, {}}, eval('extend(l, l, 1)')) + eq({1, 1, {}, {}}, meths.get_var('l')) + end) +end) diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index f4ea6799f5..eea5cc8880 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -439,4 +439,6 @@ return { list_items=list_items, dict_items=dict_items, + + empty_list = {[type_key]=list_type}, } diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 2cc22053f3..926bf1c478 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -19,10 +19,9 @@ local type_key = eval_helpers.type_key local li_alloc = eval_helpers.li_alloc local int_type = eval_helpers.int_type local first_di = eval_helpers.first_di -local dict_type = eval_helpers.dict_type -local list_type = eval_helpers.list_type local null_list = eval_helpers.null_list local null_dict = eval_helpers.null_dict +local empty_list = eval_helpers.empty_list local lua2typvalt = eval_helpers.lua2typvalt local typvalt2lua = eval_helpers.typvalt2lua local null_string = eval_helpers.null_string @@ -168,7 +167,7 @@ describe('typval.c', function() eq(1, l.lv_refcount) li = li_alloc(true) - tv = lua2typvalt({[type_key]=dict_type}) + tv = lua2typvalt({}) tv.vval.v_dict.dv_refcount = 2 li.li_tv = tv lib.tv_list_item_free(li) @@ -290,8 +289,8 @@ describe('typval.c', function() describe('alloc()/free()', function() itp('recursively frees list with', function() local l1 = ffi.gc(list(1, 'abc'), nil) - local l2 = ffi.gc(list({[type_key]=dict_type}), nil) - local l3 = ffi.gc(list({[type_key]=list_type}), nil) + local l2 = ffi.gc(list({}), nil) + local l3 = ffi.gc(list(empty_list), nil) local alloc_rets = {} alloc_log:check(get_alloc_rets({ a.list(l1), @@ -325,8 +324,8 @@ describe('typval.c', function() alloc_rets:freed(8), }) end) - itp('frees all containers inside a list', function() - local l1 = ffi.gc(list('abc', {[type_key]=dict_type}, {[type_key]=list_type}), nil) + itp('does not free container items with recurse=false', function() + local l1 = ffi.gc(list('abc', {}, empty_list), nil) local alloc_rets = {} alloc_log:check(get_alloc_rets({ a.list(l1), @@ -351,7 +350,7 @@ describe('typval.c', function() end) describe('unref()', function() itp('recursively frees list when reference count goes to 0', function() - local l = ffi.gc(list({[type_key]=list_type}), nil) + local l = ffi.gc(list(empty_list), nil) local alloc_rets = {} alloc_log:check(get_alloc_rets({ a.list(l), @@ -395,7 +394,7 @@ describe('typval.c', function() eq({lis[4], lis[9], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) lib.tv_list_remove_items(l, lis[4], lis[10]) - eq({[type_key]=list_type}, typvalt2lua(l_tv)) + eq(empty_list, typvalt2lua(l_tv)) eq({true, true, true}, {lws[1].lw_item == nil, lws[2].lw_item == nil, lws[3].lw_item == nil}) alloc_log:check({}) @@ -428,7 +427,7 @@ describe('typval.c', function() eq({0, 1, 2, 3, 4, 4.5, 5, 6, 7, 100500}, typvalt2lua(l_tv)) end) itp('works with an empty list', function() - local l_tv = lua2typvalt({[type_key]=list_type}) + local l_tv = lua2typvalt(empty_list) local l = l_tv.vval.v_list eq(nil, l.lv_first) @@ -443,10 +442,10 @@ describe('typval.c', function() end) describe('tv()', function() itp('works', function() - local l_tv = lua2typvalt({[type_key]=list_type}) + local l_tv = lua2typvalt(empty_list) local l = l_tv.vval.v_list - local l_l_tv = lua2typvalt({[type_key]=list_type}) + local l_l_tv = lua2typvalt(empty_list) alloc_log:clear() local l_l = l_l_tv.vval.v_list eq(1, l_l.lv_refcount) @@ -467,14 +466,14 @@ describe('typval.c', function() a.str(l.lv_first.li_tv.vval.v_string, 'test'), }) - eq({'test', {[type_key]=list_type}}, typvalt2lua(l_tv)) + eq({'test', empty_list}, typvalt2lua(l_tv)) end) end) end) describe('append', function() describe('list()', function() itp('works', function() - local l_tv = lua2typvalt({[type_key]=list_type}) + local l_tv = lua2typvalt(empty_list) local l = l_tv.vval.v_list local l_l = list(1) @@ -497,7 +496,7 @@ describe('typval.c', function() end) describe('dict()', function() itp('works', function() - local l_tv = lua2typvalt({[type_key]=list_type}) + local l_tv = lua2typvalt(empty_list) local l = l_tv.vval.v_list local l_d_tv = lua2typvalt({test=1}) @@ -521,7 +520,7 @@ describe('typval.c', function() end) describe('string()', function() itp('works', function() - local l_tv = lua2typvalt({[type_key]=list_type}) + local l_tv = lua2typvalt(empty_list) local l = l_tv.vval.v_list alloc_log:clear() @@ -552,7 +551,7 @@ describe('typval.c', function() end) describe('allocated string()', function() itp('works', function() - local l_tv = lua2typvalt({[type_key]=list_type}) + local l_tv = lua2typvalt(empty_list) local l = l_tv.vval.v_list local s = lib.xstrdup('test') @@ -577,7 +576,7 @@ describe('typval.c', function() end) describe('number()', function() itp('works', function() - local l_tv = lua2typvalt({[type_key]=list_type}) + local l_tv = lua2typvalt(empty_list) local l = l_tv.vval.v_list alloc_log:clear() @@ -710,7 +709,7 @@ describe('typval.c', function() }) end) itp('returns different/same containers with(out) copyID', function() - local l_inner_tv = lua2typvalt({[type_key]=list_type}) + local l_inner_tv = lua2typvalt(empty_list) local l_tv = lua2typvalt({l_inner_tv, l_inner_tv}) eq(3, l_inner_tv.vval.v_list.lv_refcount) local l = l_tv.vval.v_list @@ -718,16 +717,16 @@ describe('typval.c', function() local l_copy1 = tv_list_copy(nil, l, true, 0) neq(l_copy1.lv_first.li_tv.vval.v_list, l_copy1.lv_last.li_tv.vval.v_list) - eq({{[type_key]=list_type}, {[type_key]=list_type}}, lst2tbl(l_copy1)) + eq({empty_list, empty_list}, lst2tbl(l_copy1)) local l_copy2 = tv_list_copy(nil, l, true, 2) eq(l_copy2.lv_first.li_tv.vval.v_list, l_copy2.lv_last.li_tv.vval.v_list) - eq({{[type_key]=list_type}, {[type_key]=list_type}}, lst2tbl(l_copy2)) + eq({empty_list, empty_list}, lst2tbl(l_copy2)) eq(3, l_inner_tv.vval.v_list.lv_refcount) end) itp('works with self-referencing list with copyID', function() - local l_tv = lua2typvalt({[type_key]=list_type}) + local l_tv = lua2typvalt(empty_list) local l = l_tv.vval.v_list eq(1, l.lv_refcount) lib.tv_list_append_list(l, l) @@ -748,6 +747,139 @@ describe('typval.c', function() eq(1, l_copy1.lv_refcount) end) end) + describe('extend()', function() + itp('can extend list with itself', function() + local l + + l = list(1, {}) + alloc_log:clear() + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + + lib.tv_list_extend(l, l, nil) + alloc_log:check({ + a.li(l.lv_last.li_prev), + a.li(l.lv_last), + }) + eq(1, l.lv_refcount) + eq(2, l.lv_last.li_tv.vval.v_dict.dv_refcount) + eq({1, {}, 1, {}}, lst2tbl(l)) + + l = list(1, {}) + alloc_log:clear() + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + + lib.tv_list_extend(l, l, l.lv_last) + alloc_log:check({ + a.li(l.lv_last.li_prev.li_prev), + a.li(l.lv_last.li_prev), + }) + eq({1, 1, {}, {}}, lst2tbl(l)) + eq(1, l.lv_refcount) + eq(2, l.lv_last.li_tv.vval.v_dict.dv_refcount) + + l = list(1, {}) + alloc_log:clear() + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + + lib.tv_list_extend(l, l, l.lv_first) + alloc_log:check({ + a.li(l.lv_first), + a.li(l.lv_first.li_next), + }) + eq({1, {}, 1, {}}, lst2tbl(l)) + eq(1, l.lv_refcount) + eq(2, l.lv_last.li_tv.vval.v_dict.dv_refcount) + end) + itp('can extend list with an empty list', function() + local l = list(1, {}) + local el = list() + alloc_log:clear() + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + eq(1, el.lv_refcount) + + lib.tv_list_extend(l, el, nil) + alloc_log:check({ + }) + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + eq(1, el.lv_refcount) + eq({1, {}}, lst2tbl(l)) + + lib.tv_list_extend(l, el, l.lv_first) + alloc_log:check({ + }) + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + eq(1, el.lv_refcount) + eq({1, {}}, lst2tbl(l)) + + lib.tv_list_extend(l, el, l.lv_last) + alloc_log:check({ + }) + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + eq(1, el.lv_refcount) + eq({1, {}}, lst2tbl(l)) + end) + itp('can extend list with another non-empty list', function() + local l + local l2 = list(42, empty_list) + eq(1, l2.lv_refcount) + eq(1, l2.lv_last.li_tv.vval.v_list.lv_refcount) + + l = ffi.gc(list(1, {}), nil) + alloc_log:clear() + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + + lib.tv_list_extend(l, l2, nil) + alloc_log:check({ + a.li(l.lv_last.li_prev), + a.li(l.lv_last), + }) + eq(1, l2.lv_refcount) + eq(2, l2.lv_last.li_tv.vval.v_list.lv_refcount) + eq({1, {}, 42, empty_list}, lst2tbl(l)) + lib.tv_list_free(l) + eq(1, l2.lv_last.li_tv.vval.v_list.lv_refcount) + + l = ffi.gc(list(1, {}), nil) + alloc_log:clear() + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + + lib.tv_list_extend(l, l2, l.lv_first) + alloc_log:check({ + a.li(l.lv_first), + a.li(l.lv_first.li_next), + }) + eq(1, l2.lv_refcount) + eq(2, l2.lv_last.li_tv.vval.v_list.lv_refcount) + eq({42, empty_list, 1, {}}, lst2tbl(l)) + lib.tv_list_free(l) + eq(1, l2.lv_last.li_tv.vval.v_list.lv_refcount) + + l = ffi.gc(list(1, {}), nil) + alloc_log:clear() + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + + lib.tv_list_extend(l, l2, l.lv_last) + alloc_log:check({ + a.li(l.lv_first.li_next), + a.li(l.lv_first.li_next.li_next), + }) + eq(1, l2.lv_refcount) + eq(2, l2.lv_last.li_tv.vval.v_list.lv_refcount) + eq({1, 42, empty_list, {}}, lst2tbl(l)) + lib.tv_list_free(l) + eq(1, l2.lv_last.li_tv.vval.v_list.lv_refcount) + end) + end) describe('concat()', function() itp('works with NULL lists', function() local l = list(1, {}) @@ -789,7 +921,7 @@ describe('typval.c', function() end) itp('works with two different lists', function() local l1 = list(1, {}) - local l2 = list(3, {[type_key]=list_type}) + local l2 = list(3, empty_list) eq(1, l1.lv_refcount) eq(1, l1.lv_last.li_tv.vval.v_dict.dv_refcount) eq(1, l2.lv_refcount) @@ -809,7 +941,7 @@ describe('typval.c', function() a.li(rettv.vval.v_list.lv_last.li_prev), a.li(rettv.vval.v_list.lv_last), }) - eq({1, {}, 3, {[type_key]=list_type}}, typvalt2lua(rettv)) + eq({1, {}, 3, empty_list}, typvalt2lua(rettv)) end) itp('can concatenate list with itself', function() local l = list(1, {}) @@ -875,7 +1007,7 @@ describe('typval.c', function() alloc_log:check({ a.list(rettv3.vval.v_list), }) - eq({[type_key]=list_type}, typvalt2lua(rettv3)) + eq(empty_list, typvalt2lua(rettv3)) local rettv4 = typvalt() eq(OK, lib.tv_list_concat(le, le2, rettv4)) @@ -886,7 +1018,7 @@ describe('typval.c', function() alloc_log:check({ a.list(rettv4.vval.v_list), }) - eq({[type_key]=list_type}, typvalt2lua(rettv4)) + eq(empty_list, typvalt2lua(rettv4)) end) end) end) From cf45c7bb059dafeb882133273f77042cb06b37e4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 25 Sep 2016 18:16:17 +0300 Subject: [PATCH 0210/1671] unittests: Fix tests crash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tests crash at some point without - `after_each(collectgarbage)` right before “typval.c list copy() copies list correctly and converts items” test. - Commenting out that test. - Adding `collectgarbage()` after the test (what actually this commit does). Adding `collectgarbage()` to top-level `after_each` block right after `restore_allocators` makes running this file crash even if it is run alone. --- test/unit/eval/typval_spec.lua | 113 +++++++++++++++++---------------- 1 file changed, 58 insertions(+), 55 deletions(-) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 926bf1c478..2c9b7c66db 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -606,63 +606,66 @@ describe('typval.c', function() eq(nil, lib.tv_list_copy(nil, nil, false, 1)) end) itp('copies list correctly without converting items', function() - local v = {{['«']='»'}, {'„'}, 1, '“', null_string, null_list, null_dict} - local l_tv = lua2typvalt(v) - local l = l_tv.vval.v_list - local lis = list_items(l) - alloc_log:clear() + do + local v = {{['«']='»'}, {'„'}, 1, '“', null_string, null_list, null_dict} + local l_tv = lua2typvalt(v) + local l = l_tv.vval.v_list + local lis = list_items(l) + alloc_log:clear() - eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) - eq(1, lis[2].li_tv.vval.v_list.lv_refcount) - local l_copy1 = tv_list_copy(nil, l, false, 0) - eq(2, lis[1].li_tv.vval.v_dict.dv_refcount) - eq(2, lis[2].li_tv.vval.v_list.lv_refcount) - local lis_copy1 = list_items(l_copy1) - eq(lis[1].li_tv.vval.v_dict, lis_copy1[1].li_tv.vval.v_dict) - eq(lis[2].li_tv.vval.v_list, lis_copy1[2].li_tv.vval.v_list) - eq(v, lst2tbl(l_copy1)) - alloc_log:check({ - a.list(l_copy1), - a.li(lis_copy1[1]), - a.li(lis_copy1[2]), - a.li(lis_copy1[3]), - a.li(lis_copy1[4]), - a.str(lis_copy1[4].li_tv.vval.v_string, #v[4]), - a.li(lis_copy1[5]), - a.li(lis_copy1[6]), - a.li(lis_copy1[7]), - }) - lib.tv_list_free(ffi.gc(l_copy1, nil)) - alloc_log:clear() + eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) + eq(1, lis[2].li_tv.vval.v_list.lv_refcount) + local l_copy1 = tv_list_copy(nil, l, false, 0) + eq(2, lis[1].li_tv.vval.v_dict.dv_refcount) + eq(2, lis[2].li_tv.vval.v_list.lv_refcount) + local lis_copy1 = list_items(l_copy1) + eq(lis[1].li_tv.vval.v_dict, lis_copy1[1].li_tv.vval.v_dict) + eq(lis[2].li_tv.vval.v_list, lis_copy1[2].li_tv.vval.v_list) + eq(v, lst2tbl(l_copy1)) + alloc_log:check({ + a.list(l_copy1), + a.li(lis_copy1[1]), + a.li(lis_copy1[2]), + a.li(lis_copy1[3]), + a.li(lis_copy1[4]), + a.str(lis_copy1[4].li_tv.vval.v_string, #v[4]), + a.li(lis_copy1[5]), + a.li(lis_copy1[6]), + a.li(lis_copy1[7]), + }) + lib.tv_list_free(ffi.gc(l_copy1, nil)) + alloc_log:clear() - eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) - eq(1, lis[2].li_tv.vval.v_list.lv_refcount) - local l_deepcopy1 = tv_list_copy(nil, l, true, 0) - neq(nil, l_deepcopy1) - eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) - eq(1, lis[2].li_tv.vval.v_list.lv_refcount) - local lis_deepcopy1 = list_items(l_deepcopy1) - neq(lis[1].li_tv.vval.v_dict, lis_deepcopy1[1].li_tv.vval.v_dict) - neq(lis[2].li_tv.vval.v_list, lis_deepcopy1[2].li_tv.vval.v_list) - eq(v, lst2tbl(l_deepcopy1)) - local di_deepcopy1 = first_di(lis_deepcopy1[1].li_tv.vval.v_dict) - alloc_log:check({ - a.list(l_deepcopy1), - a.li(lis_deepcopy1[1]), - a.dict(lis_deepcopy1[1].li_tv.vval.v_dict), - a.di(di_deepcopy1, #('«')), - a.str(di_deepcopy1.di_tv.vval.v_string, #v[1]['«']), - a.li(lis_deepcopy1[2]), - a.list(lis_deepcopy1[2].li_tv.vval.v_list), - a.li(lis_deepcopy1[2].li_tv.vval.v_list.lv_first), - a.str(lis_deepcopy1[2].li_tv.vval.v_list.lv_first.li_tv.vval.v_string, #v[2][1]), - a.li(lis_deepcopy1[3]), - a.li(lis_deepcopy1[4]), - a.str(lis_deepcopy1[4].li_tv.vval.v_string, #v[4]), - a.li(lis_deepcopy1[5]), - a.li(lis_deepcopy1[6]), - a.li(lis_deepcopy1[7]), - }) + eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) + eq(1, lis[2].li_tv.vval.v_list.lv_refcount) + local l_deepcopy1 = tv_list_copy(nil, l, true, 0) + neq(nil, l_deepcopy1) + eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) + eq(1, lis[2].li_tv.vval.v_list.lv_refcount) + local lis_deepcopy1 = list_items(l_deepcopy1) + neq(lis[1].li_tv.vval.v_dict, lis_deepcopy1[1].li_tv.vval.v_dict) + neq(lis[2].li_tv.vval.v_list, lis_deepcopy1[2].li_tv.vval.v_list) + eq(v, lst2tbl(l_deepcopy1)) + local di_deepcopy1 = first_di(lis_deepcopy1[1].li_tv.vval.v_dict) + alloc_log:check({ + a.list(l_deepcopy1), + a.li(lis_deepcopy1[1]), + a.dict(lis_deepcopy1[1].li_tv.vval.v_dict), + a.di(di_deepcopy1, #('«')), + a.str(di_deepcopy1.di_tv.vval.v_string, #v[1]['«']), + a.li(lis_deepcopy1[2]), + a.list(lis_deepcopy1[2].li_tv.vval.v_list), + a.li(lis_deepcopy1[2].li_tv.vval.v_list.lv_first), + a.str(lis_deepcopy1[2].li_tv.vval.v_list.lv_first.li_tv.vval.v_string, #v[2][1]), + a.li(lis_deepcopy1[3]), + a.li(lis_deepcopy1[4]), + a.str(lis_deepcopy1[4].li_tv.vval.v_string, #v[4]), + a.li(lis_deepcopy1[5]), + a.li(lis_deepcopy1[6]), + a.li(lis_deepcopy1[7]), + }) + end + collectgarbage() end) itp('copies list correctly and converts items', function() local vc = ffi.gc(ffi.new('vimconv_T[1]'), function(vc) From 4f9e7844272335943c075b9444934f77d3cba234 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 25 Sep 2016 22:50:03 +0300 Subject: [PATCH 0211/1671] unittests: Test tv_list_join() --- test/unit/eval/typval_spec.lua | 43 +++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 2c9b7c66db..382ec79429 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -27,7 +27,7 @@ local typvalt2lua = eval_helpers.typvalt2lua local null_string = eval_helpers.null_string local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h', - './src/nvim/mbyte.h') + './src/nvim/mbyte.h', './src/nvim/garray.h') local function list_items(l) local lis = {} @@ -95,6 +95,13 @@ after_each(function() alloc_log:after_each() end) +local function ga_alloc(itemsize, growsize) + local ga = ffi.gc(ffi.cast('garray_T*', ffi.new('garray_T[1]', {})), + lib.ga_clear) + lib.ga_init(ga, itemsize or 1, growsize or 80) + return ga +end + describe('typval.c', function() describe('list', function() describe('item', function() @@ -1025,4 +1032,38 @@ describe('typval.c', function() end) end) end) + describe('join()', function() + local function list_join(l, sep, ret) + local ga = ga_alloc() + eq(ret or OK, lib.tv_list_join(ga, l, sep)) + if ga.ga_data == nil then return '' + else return ffi.string(ga.ga_data) + end + end + it('works', function() + local l + l = list('boo', 'far') + eq('boo far', list_join(l, ' ')) + eq('boofar', list_join(l, '')) + + l = list('boo') + eq('boo', list_join(l, ' ')) + + l = list() + eq('', list_join(l, ' ')) + + l = list({}, 'far') + eq('{} far', list_join(l, ' ')) + + local recursive_list = {} + recursive_list[1] = recursive_list + l = ffi.gc(list(recursive_list, 'far'), nil) + eq('[[...@0]] far', list_join(l, ' ')) + + local recursive_l = l.lv_first.li_tv.vval.v_list + local recursive_li = recursive_l.lv_first + lib.tv_list_item_remove(recursive_l, recursive_li) + lib.tv_list_free(l, true) + end) + end) end) From b3672ae2fced4715963442d2e19048f8fadbe0b8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Oct 2016 04:34:30 +0300 Subject: [PATCH 0212/1671] eval/typval: Add tv_list_equal() tests, compare NULL lists equal --- src/nvim/eval/typval.c | 7 +- test/functional/eval/null_spec.lua | 9 +-- test/unit/eval/typval_spec.lua | 123 ++++++++++++++++++++++------- 3 files changed, 102 insertions(+), 37 deletions(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 746cbbfe1c..b0b95e955f 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -623,13 +623,12 @@ bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic, const bool recursive) FUNC_ATTR_WARN_UNUSED_RESULT { - if (l1 == NULL || l2 == NULL) { - // FIXME? compare empty list with NULL list equal - return false; - } if (l1 == l2) { return true; } + if (l1 == NULL || l2 == NULL) { + return false; + } if (tv_list_len(l1) != tv_list_len(l2)) { return false; } diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index ccdd1ce34c..e587ede319 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -57,12 +57,6 @@ describe('NULL', function() null_list_expr_test('does not crash extend()', 'extend(L, [1])', 0, 0) -- FIXME extend() should not return 0 at all null_list_expr_test('does not crash extend() (second position)', 'extend([1], L)', 0, 0) - -- FIXME Should return 1 - null_list_expr_test('is equal to itself', 'L == L', 0, 0) - -- FIXME Should return 0 - null_list_expr_test('is not not equal to itself', 'L != L', 0, 1) - -- FIXME Should return 1 - null_list_expr_test('counts correctly', 'count([L], L)', 0, 0) -- FIXME should be accepted by inputlist() null_list_expr_test('is accepted as an empty list by inputlist()', '[feedkeys("\\n"), inputlist(L)]', 'E686: Argument of inputlist() must be a List', {0, 0}) @@ -133,5 +127,8 @@ describe('NULL', function() null_list_expr_test('can be added to itself', '(L + L) is L', 0, 1) null_list_expr_test('can be added to non-empty list', '([1] + L)', 0, {1}) null_list_expr_test('can be added to non-empty list (reversed)', '(L + [1])', 0, {1}) + null_list_expr_test('is equal to itself', 'L == L', 0, 1) + null_list_expr_test('is not not equal to itself', 'L != L', 0, 0) + null_list_expr_test('counts correctly', 'count([L], L)', 0, 1) end) end) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 382ec79429..8740e9eb1f 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1031,39 +1031,108 @@ describe('typval.c', function() eq(empty_list, typvalt2lua(rettv4)) end) end) - end) - describe('join()', function() - local function list_join(l, sep, ret) - local ga = ga_alloc() - eq(ret or OK, lib.tv_list_join(ga, l, sep)) - if ga.ga_data == nil then return '' - else return ffi.string(ga.ga_data) + describe('join()', function() + local function list_join(l, sep, ret) + local ga = ga_alloc() + eq(ret or OK, lib.tv_list_join(ga, l, sep)) + if ga.ga_data == nil then return '' + else return ffi.string(ga.ga_data) + end end - end - it('works', function() - local l - l = list('boo', 'far') - eq('boo far', list_join(l, ' ')) - eq('boofar', list_join(l, '')) + itp('works', function() + local l + l = list('boo', 'far') + eq('boo far', list_join(l, ' ')) + eq('boofar', list_join(l, '')) - l = list('boo') - eq('boo', list_join(l, ' ')) + l = list('boo') + eq('boo', list_join(l, ' ')) - l = list() - eq('', list_join(l, ' ')) + l = list() + eq('', list_join(l, ' ')) - l = list({}, 'far') - eq('{} far', list_join(l, ' ')) + l = list({}, 'far') + eq('{} far', list_join(l, ' ')) - local recursive_list = {} - recursive_list[1] = recursive_list - l = ffi.gc(list(recursive_list, 'far'), nil) - eq('[[...@0]] far', list_join(l, ' ')) + local recursive_list = {} + recursive_list[1] = recursive_list + l = ffi.gc(list(recursive_list, 'far'), nil) + eq('[[...@0]] far', list_join(l, ' ')) - local recursive_l = l.lv_first.li_tv.vval.v_list - local recursive_li = recursive_l.lv_first - lib.tv_list_item_remove(recursive_l, recursive_li) - lib.tv_list_free(l, true) + local recursive_l = l.lv_first.li_tv.vval.v_list + local recursive_li = recursive_l.lv_first + lib.tv_list_item_remove(recursive_l, recursive_li) + lib.tv_list_free(l, true) + end) + end) + describe('equal()', function() + itp('compares empty and NULL lists correctly', function() + local l = list() + local l2 = list() + + -- NULL lists are not equal to empty lists + eq(false, lib.tv_list_equal(l, nil, true, false)) + eq(false, lib.tv_list_equal(nil, l, false, false)) + eq(false, lib.tv_list_equal(nil, l, false, true)) + eq(false, lib.tv_list_equal(l, nil, true, true)) + + -- Yet NULL lists are equal themselves + eq(true, lib.tv_list_equal(nil, nil, true, false)) + eq(true, lib.tv_list_equal(nil, nil, false, false)) + eq(true, lib.tv_list_equal(nil, nil, false, true)) + eq(true, lib.tv_list_equal(nil, nil, true, true)) + + -- As well as empty lists + eq(true, lib.tv_list_equal(l, l, true, false)) + eq(true, lib.tv_list_equal(l, l2, false, false)) + eq(true, lib.tv_list_equal(l2, l, false, true)) + eq(true, lib.tv_list_equal(l2, l2, true, true)) + end) + -- Must not use recursive=true argument in the following tests because it + -- indicates that tv_equal_recurse_limit and recursive_cnt were set which + -- is essential. This argument will be set when comparing inner lists. + itp('compares lists correctly when case is not ignored', function() + local l1 = list('abc', {1, 2, 'Abc'}, 'def') + local l2 = list('abc', {1, 2, 'Abc'}) + local l3 = list('abc', {1, 2, 'Abc'}, 'Def') + local l4 = list('abc', {1, 2, 'Abc', 4}, 'def') + local l5 = list('Abc', {1, 2, 'Abc'}, 'def') + local l6 = list('abc', {1, 2, 'Abc'}, 'def') + local l7 = list('abc', {1, 2, 'abc'}, 'def') + local l8 = list('abc', nil, 'def') + local l9 = list('abc', {1, 2, nil}, 'def') + + eq(true, lib.tv_list_equal(l1, l1, false, false)) + eq(false, lib.tv_list_equal(l1, l2, false, false)) + eq(false, lib.tv_list_equal(l1, l3, false, false)) + eq(false, lib.tv_list_equal(l1, l4, false, false)) + eq(false, lib.tv_list_equal(l1, l5, false, false)) + eq(true, lib.tv_list_equal(l1, l6, false, false)) + eq(false, lib.tv_list_equal(l1, l7, false, false)) + eq(false, lib.tv_list_equal(l1, l8, false, false)) + eq(false, lib.tv_list_equal(l1, l9, false, false)) + end) + itp('compares lists correctly when case is ignored', function() + local l1 = list('abc', {1, 2, 'Abc'}, 'def') + local l2 = list('abc', {1, 2, 'Abc'}) + local l3 = list('abc', {1, 2, 'Abc'}, 'Def') + local l4 = list('abc', {1, 2, 'Abc', 4}, 'def') + local l5 = list('Abc', {1, 2, 'Abc'}, 'def') + local l6 = list('abc', {1, 2, 'Abc'}, 'def') + local l7 = list('abc', {1, 2, 'abc'}, 'def') + local l8 = list('abc', nil, 'def') + local l9 = list('abc', {1, 2, nil}, 'def') + + eq(true, lib.tv_list_equal(l1, l1, true, false)) + eq(false, lib.tv_list_equal(l1, l2, true, false)) + eq(true, lib.tv_list_equal(l1, l3, true, false)) + eq(false, lib.tv_list_equal(l1, l4, true, false)) + eq(true, lib.tv_list_equal(l1, l5, true, false)) + eq(true, lib.tv_list_equal(l1, l6, true, false)) + eq(true, lib.tv_list_equal(l1, l7, true, false)) + eq(false, lib.tv_list_equal(l1, l8, true, false)) + eq(false, lib.tv_list_equal(l1, l9, true, false)) + end) end) end) end) From e5edf07ec44f8d147d7482cae2997be62c30373f Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 4 Nov 2016 19:33:36 +0300 Subject: [PATCH 0213/1671] unittests: Add tests for tv_list_find*() functions Additional modifications: - More `const` qualifiers in tested functions. - `tv_list_find_str()` second argument is more in-line with other `tv_list_find*()` functions. --- src/nvim/eval.c | 2 +- src/nvim/eval/typval.c | 12 +-- src/nvim/ex_docmd.c | 2 +- test/unit/eval/helpers.lua | 6 ++ test/unit/eval/typval_spec.lua | 190 +++++++++++++++++++++++++++++++++ 5 files changed, 204 insertions(+), 8 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7c3754607e..d5624f354a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -21783,7 +21783,7 @@ void ex_oldfiles(exarg_T *eap) nr = prompt_for_number(false); msg_starthere(); if (nr > 0 && nr <= l->lv_len) { - const char *const p = tv_list_find_str(l, nr); + const char *const p = tv_list_find_str(l, nr - 1); if (p == NULL) { return; } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index b0b95e955f..ef64467ee5 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -731,7 +731,7 @@ listitem_T *tv_list_find(list_T *const l, int n) /// `*ret_error` is not touched. /// /// @return Integer value at the given index or -1. -varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *ret_error) +varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *const ret_error) FUNC_ATTR_WARN_UNUSED_RESULT { const listitem_T *const li = tv_list_find(l, n); @@ -744,16 +744,16 @@ varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *ret_error) return tv_get_number_chk(&li->li_tv, ret_error); } -/// Get list item l[n - 1] as a string +/// Get list item l[n] as a string /// /// @param[in] l List to index. /// @param[in] n Index in a list. /// -/// @return [allocated] Copy of the list item string value. -const char *tv_list_find_str(list_T *l, int n) - FUNC_ATTR_MALLOC +/// @return List item string value or NULL in case of error. +const char *tv_list_find_str(list_T *const l, const int n) + FUNC_ATTR_WARN_UNUSED_RESULT { - const listitem_T *const li = tv_list_find(l, n - 1); + const listitem_T *const li = tv_list_find(l, n); if (li == NULL) { EMSGN(_(e_listidx), n); return NULL; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 73b81ac2d9..26cfec991f 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -8424,7 +8424,7 @@ eval_vars ( return NULL; } result = (char_u *)tv_list_find_str(get_vim_var_list(VV_OLDFILES), - (long)i); + i - 1); if (result == NULL) { *errormsg = (char_u *)""; return NULL; diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index eea5cc8880..cd85b143d9 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -405,7 +405,13 @@ local alloc_logging_helpers = { freed = function(p) return {func='free', args={type(p) == 'table' and p or void(p)}} end, } +local function int(n) + return {[type_key]=int_type, value=n} +end + return { + int=int, + null_string=null_string, null_list=null_list, null_dict=null_dict, diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 8740e9eb1f..94ee394009 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -12,6 +12,7 @@ local to_cstr = helpers.to_cstr local alloc_log_new = helpers.alloc_log_new local a = eval_helpers.alloc_logging_helpers +local int = eval_helpers.int local list = eval_helpers.list local lst2tbl = eval_helpers.lst2tbl local typvalt = eval_helpers.typvalt @@ -1134,5 +1135,194 @@ describe('typval.c', function() eq(false, lib.tv_list_equal(l1, l9, true, false)) end) end) + describe('find', function() + describe('()', function() + itp('correctly indexes list', function() + local l = list(1, 2, 3, 4, 5) + local lis = list_items(l) + clear_alloc_log() + + eq(nil, lib.tv_list_find(nil, -1)) + eq(nil, lib.tv_list_find(nil, 0)) + eq(nil, lib.tv_list_find(nil, 1)) + + eq(nil, lib.tv_list_find(l, 5)) + eq(nil, lib.tv_list_find(l, -6)) + eq(lis[1], lib.tv_list_find(l, -5)) + eq(lis[5], lib.tv_list_find(l, 4)) + eq(lis[3], lib.tv_list_find(l, 2)) + eq(lis[3], lib.tv_list_find(l, -3)) + eq(lis[3], lib.tv_list_find(l, 2)) + eq(lis[3], lib.tv_list_find(l, 2)) + eq(lis[3], lib.tv_list_find(l, -3)) + + l.lv_idx_item = nil + eq(lis[1], lib.tv_list_find(l, -5)) + l.lv_idx_item = nil + eq(lis[5], lib.tv_list_find(l, 4)) + l.lv_idx_item = nil + eq(lis[3], lib.tv_list_find(l, 2)) + l.lv_idx_item = nil + eq(lis[3], lib.tv_list_find(l, -3)) + l.lv_idx_item = nil + eq(lis[3], lib.tv_list_find(l, 2)) + l.lv_idx_item = nil + eq(lis[3], lib.tv_list_find(l, 2)) + l.lv_idx_item = nil + eq(lis[3], lib.tv_list_find(l, -3)) + + l.lv_idx_item = nil + eq(lis[3], lib.tv_list_find(l, 2)) + eq(lis[1], lib.tv_list_find(l, -5)) + eq(lis[3], lib.tv_list_find(l, 2)) + eq(lis[5], lib.tv_list_find(l, 4)) + eq(lis[3], lib.tv_list_find(l, 2)) + eq(lis[3], lib.tv_list_find(l, 2)) + eq(lis[3], lib.tv_list_find(l, 2)) + eq(lis[3], lib.tv_list_find(l, -3)) + eq(lis[3], lib.tv_list_find(l, 2)) + eq(lis[3], lib.tv_list_find(l, 2)) + eq(lis[3], lib.tv_list_find(l, 2)) + eq(lis[3], lib.tv_list_find(l, -3)) + + check_alloc_log({}) + end) + end) + local function check_emsg(f, msg) + local saved_last_msg_hist = lib.last_msg_hist + local ret = {f()} + if msg ~= nil then + neq(saved_last_msg_hist, lib.last_msg_hist) + eq(msg, ffi.string(lib.last_msg_hist.msg)) + else + eq(saved_last_msg_hist, lib.last_msg_hist) + end + return unpack(ret) + end + describe('nr()', function() + local function tv_list_find_nr(l, n, msg) + return check_emsg(function() + local err = ffi.new('bool[1]', {false}) + local ret = lib.tv_list_find_nr(l, n, err) + return (err[0] == true), ret + end, msg) + end + it('returns correct number', function() + local l = list(int(1), int(2), int(3), int(4), int(5)) + clear_alloc_log() + + eq({false, 1}, {tv_list_find_nr(l, -5)}) + eq({false, 5}, {tv_list_find_nr(l, 4)}) + eq({false, 3}, {tv_list_find_nr(l, 2)}) + eq({false, 3}, {tv_list_find_nr(l, -3)}) + + check_alloc_log({}) + end) + it('returns correct number when given a string', function() + local l = list('1', '2', '3', '4', '5') + clear_alloc_log() + + eq({false, 1}, {tv_list_find_nr(l, -5)}) + eq({false, 5}, {tv_list_find_nr(l, 4)}) + eq({false, 3}, {tv_list_find_nr(l, 2)}) + eq({false, 3}, {tv_list_find_nr(l, -3)}) + + check_alloc_log({}) + end) + it('returns zero when given a NULL string', function() + local l = list(null_string) + clear_alloc_log() + + eq({false, 0}, {tv_list_find_nr(l, 0)}) + + check_alloc_log({}) + end) + it('errors out on NULL lists', function() + eq({true, -1}, {tv_list_find_nr(nil, -5)}) + eq({true, -1}, {tv_list_find_nr(nil, 4)}) + eq({true, -1}, {tv_list_find_nr(nil, 2)}) + eq({true, -1}, {tv_list_find_nr(nil, -3)}) + + check_alloc_log({}) + end) + it('errors out on out-of-range indexes', function() + local l = list(int(1), int(2), int(3), int(4), int(5)) + clear_alloc_log() + + eq({true, -1}, {tv_list_find_nr(l, -6)}) + eq({true, -1}, {tv_list_find_nr(l, 5)}) + + check_alloc_log({}) + end) + it('errors out on invalid types', function() + local l = list(1, empty_list, {}) + + eq({true, 0}, {tv_list_find_nr(l, 0, 'E805: Using a Float as a Number')}) + eq({true, 0}, {tv_list_find_nr(l, 1, 'E745: Using a List as a Number')}) + eq({true, 0}, {tv_list_find_nr(l, 2, 'E728: Using a Dictionary as a Number')}) + eq({true, 0}, {tv_list_find_nr(l, -1, 'E728: Using a Dictionary as a Number')}) + eq({true, 0}, {tv_list_find_nr(l, -2, 'E745: Using a List as a Number')}) + eq({true, 0}, {tv_list_find_nr(l, -3, 'E805: Using a Float as a Number')}) + end) + end) + local function tv_list_find_str(l, n, msg) + return check_emsg(function() + local ret = lib.tv_list_find_str(l, n) + local s = nil + if ret ~= nil then + s = ffi.string(ret) + end + return s + end, msg) + end + describe('str()', function() + it('returns correct string', function() + local l = list(int(1), int(2), int(3), int(4), int(5)) + clear_alloc_log() + + eq('1', tv_list_find_str(l, -5)) + eq('5', tv_list_find_str(l, 4)) + eq('3', tv_list_find_str(l, 2)) + eq('3', tv_list_find_str(l, -3)) + + check_alloc_log({}) + end) + it('returns string when used with VAR_STRING items', function() + local l = list('1', '2', '3', '4', '5') + clear_alloc_log() + + eq('1', tv_list_find_str(l, -5)) + eq('5', tv_list_find_str(l, 4)) + eq('3', tv_list_find_str(l, 2)) + eq('3', tv_list_find_str(l, -3)) + + check_alloc_log({}) + end) + it('returns empty when used with NULL string', function() + local l = list(null_string) + clear_alloc_log() + + eq('', tv_list_find_str(l, 0)) + + check_alloc_log({}) + end) + it('fails with error message when index is out of range', function() + local l = list(int(1), int(2), int(3), int(4), int(5)) + + eq(nil, tv_list_find_str(l, -6, 'E684: list index out of range: -6')) + eq(nil, tv_list_find_str(l, 5, 'E684: list index out of range: 5')) + end) + it('fails with error message on invalid types', function() + local l = list(1, empty_list, {}) + + eq('', tv_list_find_str(l, 0, 'E806: using Float as a String')) + eq('', tv_list_find_str(l, 1, 'E730: using List as a String')) + eq('', tv_list_find_str(l, 2, 'E731: using Dictionary as a String')) + eq('', tv_list_find_str(l, -1, 'E731: using Dictionary as a String')) + eq('', tv_list_find_str(l, -2, 'E730: using List as a String')) + eq('', tv_list_find_str(l, -3, 'E806: using Float as a String')) + end) + end) + end) end) end) From 56e51033abf00d66e9c6f9412e8f57c9a24b86ae Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 5 Nov 2016 00:07:34 +0300 Subject: [PATCH 0214/1671] unittests: Add tests for tv_list_idx_of_item --- src/nvim/eval/typval.c | 2 +- test/unit/eval/typval_spec.lua | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index ef64467ee5..4adc31d10a 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -774,7 +774,7 @@ long tv_list_idx_of_item(const list_T *const l, const listitem_T *const item) return -1; } long idx = 0; - listitem_T *li; + const listitem_T *li; for (li = l->lv_first; li != NULL && li != item; li = li->li_next) { idx++; } diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 94ee394009..d308ee5794 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1324,5 +1324,21 @@ describe('typval.c', function() end) end) end) + describe('idx_of_item()', function() + it('works', function() + local l = list(1, 2, 3, 4, 5) + local l2 = list(42, empty_list) + local lis = list_items(l) + local lis2 = list_items(l2) + + for i, li in ipairs(lis) do + eq(i - 1, lib.tv_list_idx_of_item(l, li)) + end + eq(-1, lib.tv_list_idx_of_item(l, lis2[1])) + eq(-1, lib.tv_list_idx_of_item(l, nil)) + eq(-1, lib.tv_list_idx_of_item(nil, nil)) + eq(-1, lib.tv_list_idx_of_item(nil, lis[1])) + end) + end) end) end) From 9ed9af7e11e3a707f65abfeb1d02b029e39241ea Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 5 Nov 2016 00:26:33 +0300 Subject: [PATCH 0215/1671] eval/typval: More `const` qualifiers in `tv_dict*` function signatures --- src/nvim/eval/typval.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 4adc31d10a..57025250d4 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1096,7 +1096,7 @@ dictitem_T *tv_dict_find(const dict_T *const d, const char *const key, /// @param[in] key Key to find in dictionary. /// /// @return Dictionary item. -varnumber_T tv_dict_get_number(dict_T *const d, const char *const key) +varnumber_T tv_dict_get_number(const dict_T *const d, const char *const key) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { dictitem_T *const di = tv_dict_find(d, key, -1); @@ -1116,7 +1116,7 @@ varnumber_T tv_dict_get_number(dict_T *const d, const char *const key) /// @return NULL if key does not exist, empty string in case of type error, /// string item value otherwise. If returned value is not NULL, it may /// be allocated depending on `save` argument. -char *tv_dict_get_string(dict_T *const d, const char *const key, +char *tv_dict_get_string(const dict_T *const d, const char *const key, const bool save) FUNC_ATTR_WARN_UNUSED_RESULT { @@ -1136,11 +1136,11 @@ char *tv_dict_get_string(dict_T *const d, const char *const key, /// /// @return NULL if key does not exist, empty string in case of type error, /// string item value otherwise. -const char *tv_dict_get_string_buf(dict_T *const d, const char *const key, +const char *tv_dict_get_string_buf(const dict_T *const d, const char *const key, char *const numbuf) FUNC_ATTR_WARN_UNUSED_RESULT { - dictitem_T *const di = tv_dict_find(d, key, -1); + const dictitem_T *const di = tv_dict_find(d, key, -1); if (di == NULL) { return NULL; } From 4bcee963471abd939bb9edd1709418e30be7290f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 5 Nov 2016 01:03:44 +0300 Subject: [PATCH 0216/1671] *: Fix some Windows-specific warnings Also fixed an error in path_fnamecmp(). --- src/nvim/api/buffer.c | 4 +- src/nvim/event/rstream.c | 10 ++- src/nvim/ex_getln.c | 2 +- src/nvim/file_search.c | 5 +- src/nvim/fileio.c | 2 +- src/nvim/indent_c.c | 2 +- src/nvim/os/env.c | 1 + src/nvim/os/pty_process_win.h | 5 +- src/nvim/path.c | 26 +++---- test/unit/eval/typval_spec.lua | 130 ++++++++++++++++++++++----------- 10 files changed, 122 insertions(+), 65 deletions(-) diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index b75a2c7211..037a6ee1da 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -395,10 +395,10 @@ void nvim_buf_set_lines(uint64_t channel_id, mark_adjust((linenr_T)start, (linenr_T)(end - 1), MAXLNUM, extra); } - changed_lines((linenr_T)start, 0, (linenr_T)end, extra); + changed_lines((linenr_T)start, 0, (linenr_T)end, (long)extra); if (save_curbuf.br_buf == NULL) { - fix_cursor((linenr_T)start, (linenr_T)end, extra); + fix_cursor((linenr_T)start, (linenr_T)end, (linenr_T)extra); } end: diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c index 92efc9fa2e..2737dad305 100644 --- a/src/nvim/event/rstream.c +++ b/src/nvim/event/rstream.c @@ -89,7 +89,10 @@ static void on_rbuffer_nonfull(RBuffer *buf, void *data) static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf) { Stream *stream = handle->data; - buf->base = rbuffer_write_ptr(stream->buffer, &buf->len); + // `uv_buf_t.len` happens to have different size on Windows. + size_t write_count; + buf->base = rbuffer_write_ptr(stream->buffer, &write_count); + buf->len = write_count; } // Callback invoked by libuv after it copies the data into the buffer provided @@ -136,7 +139,10 @@ static void fread_idle_cb(uv_idle_t *handle) uv_fs_t req; Stream *stream = handle->data; - stream->uvbuf.base = rbuffer_write_ptr(stream->buffer, &stream->uvbuf.len); + // `uv_buf_t.len` happens to have different size on Windows. + size_t write_count; + stream->uvbuf.base = rbuffer_write_ptr(stream->buffer, &write_count); + stream->uvbuf.len = write_count; // the offset argument to uv_fs_read is int64_t, could someone really try // to read more than 9 quintillion (9e18) bytes? diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index a0981a42ce..e140dfa886 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -579,7 +579,7 @@ static int command_line_execute(VimState *state, int key) } if (vim_ispathsep(ccline.cmdbuff[s->j]) #ifdef BACKSLASH_IN_FILENAME - && vim_strchr(" *?[{`$%#", ccline.cmdbuff[s->j + 1]) + && strchr(" *?[{`$%#", ccline.cmdbuff[s->j + 1]) == NULL #endif ) { diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index f7932bc296..9592235905 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -322,8 +322,11 @@ vim_findfile_init ( drive[0] = path[0]; drive[1] = ':'; drive[2] = NUL; - if (vim_FullName(drive, ff_expand_buffer, MAXPATHL, TRUE) == FAIL) + if (vim_FullName((const char *)drive, (char *)ff_expand_buffer, MAXPATHL, + true) + == FAIL) { goto error_return; + } path += 2; } else #endif diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 127efda65c..bd632b2755 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -5207,7 +5207,7 @@ void forward_slash(char_u *fname) { char_u *p; - if (path_with_url(fname)) { + if (path_with_url((const char *)fname)) { return; } for (p = fname; *p != NUL; p++) { diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index 7b758b4dac..4a73fbaf61 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -174,7 +174,7 @@ static char_u *skip_string(char_u *p) char_u *paren = vim_strchr(delim, '('); if (paren != NULL) { - ptrdiff_t delim_len = paren - delim; + const ptrdiff_t delim_len = paren - delim; for (p += 3; *p; ++p) if (p[0] == ')' && STRNCMP(p + 1, delim, delim_len) == 0 diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index a73d753e46..ae69462055 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -18,6 +18,7 @@ #include "nvim/eval.h" #include "nvim/ex_getln.h" #include "nvim/version.h" +#include "nvim/fileio.h" #ifdef WIN32 #include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8 diff --git a/src/nvim/os/pty_process_win.h b/src/nvim/os/pty_process_win.h index 20cc589925..8e2b37a1c1 100644 --- a/src/nvim/os/pty_process_win.h +++ b/src/nvim/os/pty_process_win.h @@ -12,8 +12,9 @@ typedef struct pty_process { #define pty_process_spawn(job) libuv_process_spawn((LibuvProcess *)job) #define pty_process_close(job) libuv_process_close((LibuvProcess *)job) #define pty_process_close_master(job) libuv_process_close((LibuvProcess *)job) -#define pty_process_resize(job, width, height) -#define pty_process_teardown(loop) +#define pty_process_resize(job, width, height) ( \ + (void)job, (void)width, (void)height, 0) +#define pty_process_teardown(loop) ((void)loop, 0) static inline PtyProcess pty_process_init(Loop *loop, void *data) { diff --git a/src/nvim/path.c b/src/nvim/path.c index e92261f4fd..d0248690d9 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -312,7 +312,7 @@ int path_fnamecmp(const char *fname1, const char *fname2) /// /// @return 0 if they are equal, non-zero otherwise. int path_fnamencmp(const char *const fname1, const char *const fname2, - const size_t len) + size_t len) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { #ifdef BACKSLASH_IN_FILENAME @@ -322,16 +322,16 @@ int path_fnamencmp(const char *const fname1, const char *const fname2, const char *p1 = fname1; const char *p2 = fname2; while (len > 0) { - c1 = PTR2CHAR(p1); - c2 = PTR2CHAR(p2); - if (c1 == NUL || c2 == NUL - || (!((c1 == '/' || c1 == '\\') && (c2 == '\\' || c2 == '/'))) - || (p_fic ? (c1 != c2 && CH_FOLD(c1) != CH_FOLD(c2)) : c1 != c2)) { + c1 = PTR2CHAR((const char_u *)p1); + c2 = PTR2CHAR((const char_u *)p2); + if ((c1 == NUL || c2 == NUL + || (!((c1 == '/' || c1 == '\\') && (c2 == '\\' || c2 == '/')))) + && (p_fic ? (c1 != c2 && CH_FOLD(c1) != CH_FOLD(c2)) : c1 != c2)) { break; } - len -= MB_PTR2LEN(p1); - p1 += MB_PTR2LEN(p1); - p2 += MB_PTR2LEN(p2); + len -= MB_PTR2LEN((const char_u *)p1); + p1 += MB_PTR2LEN((const char_u *)p1); + p2 += MB_PTR2LEN((const char_u *)p2); } return c1 - c2; #else @@ -819,7 +819,7 @@ static void expand_path_option(char_u *curdir, garray_T *gap) } STRMOVE(buf + len + 1, buf); STRCPY(buf, curdir); - buf[len] = PATHSEP; + buf[len] = (char_u)PATHSEP; simplify_filename(buf); } @@ -1333,12 +1333,12 @@ static int expand_backtick( /// When the path looks like a URL leave it unmodified. void slash_adjust(char_u *p) { - if (path_with_url(p)) { + if (path_with_url((const char *)p)) { return; } while (*p) { - if (*p == psepcN) { - *p = psepc; + if (*p == (char_u)psepcN) { + *p = (char_u)psepc; } mb_ptr_adv(p); } diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index d308ee5794..a785350c15 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -294,8 +294,8 @@ describe('typval.c', function() end) end) -- add() and fix() were tested when testing tv_list_item_remove() - describe('alloc()/free()', function() - itp('recursively frees list with', function() + describe('free()', function() + itp('recursively frees list', function() local l1 = ffi.gc(list(1, 'abc'), nil) local l2 = ffi.gc(list({}), nil) local l3 = ffi.gc(list(empty_list), nil) @@ -332,27 +332,73 @@ describe('typval.c', function() alloc_rets:freed(8), }) end) - itp('does not free container items with recurse=false', function() - local l1 = ffi.gc(list('abc', {}, empty_list), nil) + end) + describe('free_list()', function() + itp('does not free list contents', function() + local l1 = ffi.gc(list(1, 'abc'), nil) + local l2 = ffi.gc(list({}), nil) + local l3 = ffi.gc(list(empty_list), nil) local alloc_rets = {} alloc_log:check(get_alloc_rets({ a.list(l1), - a.str(l1.lv_first.li_tv.vval.v_string, #('abc')), a.li(l1.lv_first), - a.dict(l1.lv_first.li_next.li_tv.vval.v_dict), - a.li(l1.lv_first.li_next), - a.list(l1.lv_last.li_tv.vval.v_list), + a.str(l1.lv_last.li_tv.vval.v_string, #('abc')), a.li(l1.lv_last), + a.list(l2), + a.dict(l2.lv_first.li_tv.vval.v_dict), + a.li(l2.lv_first), + a.list(l3), + a.list(l3.lv_first.li_tv.vval.v_list), + a.li(l3.lv_first), }, alloc_rets)) - lib.tv_list_free(l1) + lib.tv_list_free_list(l1) + alloc_log:check({ + alloc_rets:freed(1), + }) + lib.tv_list_free_list(l2) + alloc_log:check({ + alloc_rets:freed(5), + }) + lib.tv_list_free_list(l3) + alloc_log:check({ + alloc_rets:freed(8), + }) + end) + end) + describe('free_contents()', function() + itp('recursively frees list, except for the list structure itself', + function() + local l1 = ffi.gc(list(1, 'abc'), nil) + local l2 = ffi.gc(list({}), nil) + local l3 = ffi.gc(list(empty_list), nil) + local alloc_rets = {} + alloc_log:check(get_alloc_rets({ + a.list(l1), + a.li(l1.lv_first), + a.str(l1.lv_last.li_tv.vval.v_string, #('abc')), + a.li(l1.lv_last), + a.list(l2), + a.dict(l2.lv_first.li_tv.vval.v_dict), + a.li(l2.lv_first), + a.list(l3), + a.list(l3.lv_first.li_tv.vval.v_list), + a.li(l3.lv_first), + }, alloc_rets)) + lib.tv_list_free_contents(l1) alloc_log:check({ alloc_rets:freed(2), alloc_rets:freed(3), alloc_rets:freed(4), - alloc_rets:freed(5), + }) + lib.tv_list_free_contents(l2) + alloc_log:check({ alloc_rets:freed(6), alloc_rets:freed(7), - alloc_rets:freed(1), + }) + lib.tv_list_free_contents(l3) + alloc_log:check({ + alloc_rets:freed(9), + alloc_rets:freed(10), }) end) end) @@ -1063,8 +1109,8 @@ describe('typval.c', function() local recursive_l = l.lv_first.li_tv.vval.v_list local recursive_li = recursive_l.lv_first lib.tv_list_item_remove(recursive_l, recursive_li) - lib.tv_list_free(l, true) - end) + lib.tv_list_free(l) + end, true) end) describe('equal()', function() itp('compares empty and NULL lists correctly', function() @@ -1140,7 +1186,7 @@ describe('typval.c', function() itp('correctly indexes list', function() local l = list(1, 2, 3, 4, 5) local lis = list_items(l) - clear_alloc_log() + alloc_log:clear() eq(nil, lib.tv_list_find(nil, -1)) eq(nil, lib.tv_list_find(nil, 0)) @@ -1185,7 +1231,7 @@ describe('typval.c', function() eq(lis[3], lib.tv_list_find(l, 2)) eq(lis[3], lib.tv_list_find(l, -3)) - check_alloc_log({}) + alloc_log:check({}) end) end) local function check_emsg(f, msg) @@ -1207,54 +1253,54 @@ describe('typval.c', function() return (err[0] == true), ret end, msg) end - it('returns correct number', function() + itp('returns correct number', function() local l = list(int(1), int(2), int(3), int(4), int(5)) - clear_alloc_log() + alloc_log:clear() eq({false, 1}, {tv_list_find_nr(l, -5)}) eq({false, 5}, {tv_list_find_nr(l, 4)}) eq({false, 3}, {tv_list_find_nr(l, 2)}) eq({false, 3}, {tv_list_find_nr(l, -3)}) - check_alloc_log({}) + alloc_log:check({}) end) - it('returns correct number when given a string', function() + itp('returns correct number when given a string', function() local l = list('1', '2', '3', '4', '5') - clear_alloc_log() + alloc_log:clear() eq({false, 1}, {tv_list_find_nr(l, -5)}) eq({false, 5}, {tv_list_find_nr(l, 4)}) eq({false, 3}, {tv_list_find_nr(l, 2)}) eq({false, 3}, {tv_list_find_nr(l, -3)}) - check_alloc_log({}) + alloc_log:check({}) end) - it('returns zero when given a NULL string', function() + itp('returns zero when given a NULL string', function() local l = list(null_string) - clear_alloc_log() + alloc_log:clear() eq({false, 0}, {tv_list_find_nr(l, 0)}) - check_alloc_log({}) + alloc_log:check({}) end) - it('errors out on NULL lists', function() + itp('errors out on NULL lists', function() eq({true, -1}, {tv_list_find_nr(nil, -5)}) eq({true, -1}, {tv_list_find_nr(nil, 4)}) eq({true, -1}, {tv_list_find_nr(nil, 2)}) eq({true, -1}, {tv_list_find_nr(nil, -3)}) - check_alloc_log({}) + alloc_log:check({}) end) - it('errors out on out-of-range indexes', function() + itp('errors out on out-of-range indexes', function() local l = list(int(1), int(2), int(3), int(4), int(5)) - clear_alloc_log() + alloc_log:clear() eq({true, -1}, {tv_list_find_nr(l, -6)}) eq({true, -1}, {tv_list_find_nr(l, 5)}) - check_alloc_log({}) + alloc_log:check({}) end) - it('errors out on invalid types', function() + itp('errors out on invalid types', function() local l = list(1, empty_list, {}) eq({true, 0}, {tv_list_find_nr(l, 0, 'E805: Using a Float as a Number')}) @@ -1276,43 +1322,43 @@ describe('typval.c', function() end, msg) end describe('str()', function() - it('returns correct string', function() + itp('returns correct string', function() local l = list(int(1), int(2), int(3), int(4), int(5)) - clear_alloc_log() + alloc_log:clear() eq('1', tv_list_find_str(l, -5)) eq('5', tv_list_find_str(l, 4)) eq('3', tv_list_find_str(l, 2)) eq('3', tv_list_find_str(l, -3)) - check_alloc_log({}) + alloc_log:check({}) end) - it('returns string when used with VAR_STRING items', function() + itp('returns string when used with VAR_STRING items', function() local l = list('1', '2', '3', '4', '5') - clear_alloc_log() + alloc_log:clear() eq('1', tv_list_find_str(l, -5)) eq('5', tv_list_find_str(l, 4)) eq('3', tv_list_find_str(l, 2)) eq('3', tv_list_find_str(l, -3)) - check_alloc_log({}) + alloc_log:check({}) end) - it('returns empty when used with NULL string', function() + itp('returns empty when used with NULL string', function() local l = list(null_string) - clear_alloc_log() + alloc_log:clear() eq('', tv_list_find_str(l, 0)) - check_alloc_log({}) + alloc_log:check({}) end) - it('fails with error message when index is out of range', function() + itp('fails with error message when index is out of range', function() local l = list(int(1), int(2), int(3), int(4), int(5)) eq(nil, tv_list_find_str(l, -6, 'E684: list index out of range: -6')) eq(nil, tv_list_find_str(l, 5, 'E684: list index out of range: 5')) end) - it('fails with error message on invalid types', function() + itp('fails with error message on invalid types', function() local l = list(1, empty_list, {}) eq('', tv_list_find_str(l, 0, 'E806: using Float as a String')) @@ -1325,7 +1371,7 @@ describe('typval.c', function() end) end) describe('idx_of_item()', function() - it('works', function() + itp('works', function() local l = list(1, 2, 3, 4, 5) local l2 = list(42, empty_list) local lis = list_items(l) From 5239616297182601a5d244a482e6cef307e901f1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 5 Nov 2016 21:34:22 +0300 Subject: [PATCH 0217/1671] functests: Fix buf_functions test on Windows --- test/functional/eval/buf_functions_spec.lua | 6 ++++-- test/functional/helpers.lua | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/test/functional/eval/buf_functions_spec.lua b/test/functional/eval/buf_functions_spec.lua index a130da4452..db50874c53 100644 --- a/test/functional/eval/buf_functions_spec.lua +++ b/test/functional/eval/buf_functions_spec.lua @@ -13,6 +13,7 @@ local winmeths = helpers.winmeths local curbufmeths = helpers.curbufmeths local curwinmeths = helpers.curwinmeths local curtabmeths = helpers.curtabmeths +local get_pathsep = helpers.get_pathsep local fname = 'Xtest-functional-eval-buf_functions' local fname2 = fname .. '.2' @@ -66,14 +67,15 @@ describe('bufname() function', function() eq('', funcs.bufname('%')) -- Buffer has no name yet command('file ' .. fname) local wd = lfs.currentdir() + local sep = get_pathsep() local curdirname = funcs.fnamemodify(wd, ':t') for _, arg in ipairs({'%', 1, 'X', wd}) do eq(fname, funcs.bufname(arg)) meths.set_current_dir('..') - eq(curdirname .. '/' .. fname, funcs.bufname(arg)) + eq(curdirname .. sep .. fname, funcs.bufname(arg)) meths.set_current_dir(curdirname) meths.set_current_dir(dirname) - eq(wd .. '/' .. fname, funcs.bufname(arg)) + eq(wd .. sep .. fname, funcs.bufname(arg)) meths.set_current_dir('..') eq(fname, funcs.bufname(arg)) command('enew') diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 13a0cff137..7ce95d0b7c 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -570,6 +570,10 @@ local curbufmeths = create_callindex(curbuf) local curwinmeths = create_callindex(curwin) local curtabmeths = create_callindex(curtab) +local function get_pathsep() + return funcs.fnamemodify('.', ':p'):sub(-1) +end + local M = { prepend_argv = prepend_argv, clear = clear, @@ -635,6 +639,7 @@ local M = { tmpname = tmpname, meth_pcall = meth_pcall, NIL = mpack.NIL, + get_pathsep = get_pathsep, } return function(after_each) From 1e3e302dc2bdced563ecd2c3fb101af31f72b3df Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Dec 2016 21:58:19 +0300 Subject: [PATCH 0218/1671] eval: Move part of dictwatcher* functions to eval/typval --- src/nvim/eval.c | 57 ++++--------------- src/nvim/eval/typval.c | 89 +++++++++++++++++++++++++++++- src/nvim/eval/typval.h | 27 ++++----- src/nvim/os/env.c | 1 - test/functional/eval/null_spec.lua | 3 +- 5 files changed, 112 insertions(+), 65 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d5624f354a..80278cf3bb 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7706,6 +7706,11 @@ static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type != VAR_DICT) { emsgf(_(e_invarg2), "dict"); return; + } else if (argvars[0].vval.v_dict == NULL) { + const char *const arg_errmsg = _("dictwatcheradd() argument"); + const size_t arg_errmsg_len = strlen(arg_errmsg); + emsgf(_(e_readonlyvar), (int)arg_errmsg_len, arg_errmsg); + return; } if (argvars[1].v_type != VAR_STRING && argvars[1].v_type != VAR_NUMBER) { @@ -7725,11 +7730,8 @@ static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - DictWatcher *watcher = xmalloc(sizeof(DictWatcher)); - watcher->key_pattern = xmemdupz(key_pattern, key_pattern_len); - watcher->callback = callback; - watcher->busy = false; - QUEUE_INSERT_TAIL(&argvars[0].vval.v_dict->watchers, &watcher->node); + tv_dict_watcher_add(argvars[0].vval.v_dict, key_pattern, key_pattern_len, + callback); } // dictwatcherdel(dict, key, funcref) function @@ -7759,28 +7761,12 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - dict_T *dict = argvars[0].vval.v_dict; - QUEUE *w = NULL; - DictWatcher *watcher = NULL; - bool matched = false; - QUEUE_FOREACH(w, &dict->watchers) { - watcher = tv_dict_watcher_node_data(w); - if (callback_equal(&watcher->callback, &callback) - && !strcmp(watcher->key_pattern, key_pattern)) { - matched = true; - break; - } + if (!tv_dict_watcher_remove(argvars[0].vval.v_dict, key_pattern, + strlen(key_pattern), callback)) { + EMSG("Couldn't find a watcher matching key and callback"); } callback_free(&callback); - - if (!matched) { - EMSG("Couldn't find a watcher matching key and callback"); - return; - } - - QUEUE_REMOVE(w); - tv_dict_watcher_free(watcher); } /* @@ -16576,7 +16562,6 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg) return true; } - /// Unref/free callback void callback_free(Callback *const callback) FUNC_ATTR_NONNULL_ALL @@ -16601,28 +16586,6 @@ void callback_free(Callback *const callback) callback->type = kCallbackNone; } -static bool callback_equal(Callback *cb1, Callback *cb2) -{ - if (cb1->type != cb2->type) { - return false; - } - switch (cb1->type) { - case kCallbackFuncref: - return STRCMP(cb1->data.funcref, cb2->data.funcref) == 0; - - case kCallbackPartial: - // FIXME: this is inconsistent with tv_equal but is needed for precision - // maybe change dictwatcheradd to return a watcher id instead? - return cb1->data.partial == cb2->data.partial; - - case kCallbackNone: - return true; - - default: - abort(); - } -} - bool callback_call(Callback *const callback, const int argcount_in, typval_T *const argvars_in, typval_T *const rettv) FUNC_ATTR_NONNULL_ALL diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 57025250d4..243e738c50 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -790,7 +790,7 @@ long tv_list_idx_of_item(const list_T *const l, const listitem_T *const item) /// Perform all necessary cleanup for a `DictWatcher` instance /// /// @param watcher Watcher to free. -void tv_dict_watcher_free(DictWatcher *watcher) +static void tv_dict_watcher_free(DictWatcher *watcher) FUNC_ATTR_NONNULL_ALL { callback_free(&watcher->callback); @@ -798,6 +798,91 @@ void tv_dict_watcher_free(DictWatcher *watcher) xfree(watcher); } +/// Add watcher to a dictionary +/// +/// @param[in] dict Dictionary to add watcher to. +/// @param[in] key_pattern Pattern to watch for. +/// @param[in] key_pattern_len Key pattern length. +/// @param callback Function to be called on events. +void tv_dict_watcher_add(dict_T *const dict, const char *const key_pattern, + const size_t key_pattern_len, Callback callback) + FUNC_ATTR_NONNULL_ARG(2) +{ + DictWatcher *const watcher = xmalloc(sizeof(DictWatcher)); + watcher->key_pattern = xmemdupz(key_pattern, key_pattern_len); + watcher->key_pattern_len = key_pattern_len; + watcher->callback = callback; + watcher->busy = false; + QUEUE_INSERT_TAIL(&dict->watchers, &watcher->node); +} + +/// Check whether two callbacks are equal +/// +/// @param[in] cb1 First callback to check. +/// @param[in] cb2 Second callback to check. +/// +/// @return True if they are equal, false otherwise. +static bool tv_callback_equal(const Callback *const cb1, + const Callback *const cb2) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (cb1->type != cb2->type) { + return false; + } + switch (cb1->type) { + case kCallbackFuncref: { + return STRCMP(cb1->data.funcref, cb2->data.funcref) == 0; + } + case kCallbackPartial: { + // FIXME: this is inconsistent with tv_equal but is needed for precision + // maybe change dictwatcheradd to return a watcher id instead? + return cb1->data.partial == cb2->data.partial; + } + case kCallbackNone: { + return true; + } + } +} + +/// Remove watcher from a dictionary +/// +/// @param dict Dictionary to remove watcher from. +/// @param[in] key_pattern Pattern to remove watcher for. +/// @param[in] key_pattern_len Pattern length. +/// @param callback Callback to remove watcher for. +/// +/// @return True on success, false if relevant watcher was not found. +bool tv_dict_watcher_remove(dict_T *const dict, const char *const key_pattern, + const size_t key_pattern_len, + Callback callback) + FUNC_ATTR_NONNULL_ARG(2) +{ + if (dict == NULL) { + return false; + } + + QUEUE *w = NULL; + DictWatcher *watcher = NULL; + bool matched = false; + QUEUE_FOREACH(w, &dict->watchers) { + watcher = tv_dict_watcher_node_data(w); + if (tv_callback_equal(&watcher->callback, &callback) + && watcher->key_pattern_len == key_pattern_len + && memcmp(watcher->key_pattern, key_pattern, key_pattern_len) == 0) { + matched = true; + break; + } + } + + if (!matched) { + return false; + } + + QUEUE_REMOVE(w); + tv_dict_watcher_free(watcher); + return true; +} + /// Test if `key` matches with with `watcher->key_pattern` /// /// @param[in] watcher Watcher to check key pattern from. @@ -810,7 +895,7 @@ static bool tv_dict_watcher_matches(DictWatcher *watcher, const char *const key) // For now only allow very simple globbing in key patterns: a '*' at the end // of the string means it should match everything up to the '*' instead of the // whole string. - const size_t len = strlen(watcher->key_pattern); + const size_t len = watcher->key_pattern_len; if (len && watcher->key_pattern[len - 1] == '*') { return strncmp(key, watcher->key_pattern, len - 1) == 0; } else { diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 791b191009..fa0105197f 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -59,6 +59,7 @@ typedef struct { typedef struct dict_watcher { Callback callback; char *key_pattern; + size_t key_pattern_len; QUEUE node; bool busy; // prevent recursion if the dict is changed in the callback } DictWatcher; @@ -322,19 +323,6 @@ static inline bool tv_dict_is_watched(const dict_T *const d) return d && !QUEUE_EMPTY(&d->watchers); } -static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) - REAL_FATTR_NONNULL_ALL REAL_FATTR_NONNULL_RET REAL_FATTR_PURE - REAL_FATTR_WARN_UNUSED_RESULT; - -/// Compute the `DictWatcher` address from a QUEUE node. -/// -/// This only exists for .asan-blacklist (ASAN doesn't handle QUEUE_DATA pointer -/// arithmetic). -static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) -{ - return QUEUE_DATA(q, DictWatcher, node); -} - /// Initialize VimL object /// /// Initializes to unlocked VAR_UNKNOWN object. @@ -407,6 +395,19 @@ static inline bool tv_get_float_chk(const typval_T *const tv, return false; } +static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) + REAL_FATTR_NONNULL_ALL REAL_FATTR_NONNULL_RET REAL_FATTR_PURE + REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_ALWAYS_INLINE; + +/// Compute the `DictWatcher` address from a QUEUE node. +/// +/// This only exists for .asan-blacklist (ASAN doesn't handle QUEUE_DATA pointer +/// arithmetic). +static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) +{ + return QUEUE_DATA(q, DictWatcher, node); +} + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/typval.h.generated.h" #endif diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index ae69462055..a73d753e46 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -18,7 +18,6 @@ #include "nvim/eval.h" #include "nvim/ex_getln.h" #include "nvim/version.h" -#include "nvim/fileio.h" #ifdef WIN32 #include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8 diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index e587ede319..f72d05f2a3 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -80,8 +80,7 @@ describe('NULL', function() null_list_test('is accepted by :cexpr', 'cexpr L', 'Vim(cexpr):E777: String or List expected') -- FIXME should not error out null_list_test('is accepted by :lexpr', 'lexpr L', 'Vim(lexpr):E777: String or List expected') - -- FIXME should not error out - null_list_test('is accepted by :for', 'for x in L|throw x|endfor', 'Vim(for):E714: List required') + null_list_test('is accepted by :for', 'for x in L|throw x|endfor', 0) -- Subjectable behaviour From a56f2d27e3c09aaae00a58a70652ac5db3287dee Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Dec 2016 22:35:10 +0300 Subject: [PATCH 0219/1671] eval: Make dictionary watchers work with empty keys Looks like dict_notifications_spec test used to depend on some state which should not be preserved. Changed all `setup()` calls to `before_each()` and added necessary state in addition to changes required to test empty keys. Note: unit tests for tv_dict_watcher* are still needed. --- src/nvim/eval.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 80278cf3bb..bd88678f16 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -160,7 +160,6 @@ static char *e_missbrac = N_("E111: Missing ']'"); static char *e_listarg = N_("E686: Argument of %s must be a List"); static char *e_listdictarg = N_( "E712: Argument of %s must be a List or Dictionary"); -static char *e_emptykey = N_("E713: Cannot use empty key for Dictionary"); static char *e_listreq = N_("E714: List required"); static char *e_dictreq = N_("E715: Dictionary required"); static char *e_stringreq = N_("E928: String required"); @@ -2112,8 +2111,9 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len) ; if (len == 0) { - if (!quiet) - EMSG(_(e_emptykey)); + if (!quiet) { + EMSG(_("E713: Cannot use empty key after .")); + } return NULL; } p = key + len; From 3025431c81daac873e69a71aee695ebfd00504f7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Dec 2016 22:59:25 +0300 Subject: [PATCH 0220/1671] eval: Make sure that v:_null_dict does not crash dictwatcher*() Ref #4615 --- src/nvim/eval/typval.c | 3 +++ .../ex_cmds/dict_notifications_spec.lua | 24 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 243e738c50..8c11f4bd8f 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -808,6 +808,9 @@ void tv_dict_watcher_add(dict_T *const dict, const char *const key_pattern, const size_t key_pattern_len, Callback callback) FUNC_ATTR_NONNULL_ARG(2) { + if (dict == NULL) { + return; + } DictWatcher *const watcher = xmalloc(sizeof(DictWatcher)); watcher->key_pattern = xmemdupz(key_pattern, key_pattern_len); watcher->key_pattern_len = key_pattern_len; diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua index 8cc717483e..e3b4a1c504 100644 --- a/test/functional/ex_cmds/dict_notifications_spec.lua +++ b/test/functional/ex_cmds/dict_notifications_spec.lua @@ -244,6 +244,16 @@ describe('dictionary change notifications', function() end) end) + it('errors out when adding to v:_null_dict', function() + command([[ + function! g:Watcher1(dict, key, value) + call rpcnotify(g:channel, '1', a:key, a:value) + endfunction + ]]) + eq('Vim(call):E46: Cannot change read-only variable "dictwatcheradd() argument"', + exc_exec('call dictwatcheradd(v:_null_dict, "x", "g:Watcher1")')) + end) + describe('errors', function() before_each(function() source([[ @@ -272,6 +282,20 @@ describe('dictionary change notifications', function() command('call dictwatcherdel(g:, "key", "g:InvalidCb")') end) + it('fails to remove watcher from v:_null_dict', function() + eq("Vim(call):Couldn't find a watcher matching key and callback", + exc_exec('call dictwatcherdel(v:_null_dict, "x", "g:Watcher2")')) + end) + + --[[ + [ it("fails to add/remove if the callback doesn't exist", function() + [ eq("Vim(call):Function g:InvalidCb doesn't exist", + [ exc_exec('call dictwatcheradd(g:, "key", "g:InvalidCb")')) + [ eq("Vim(call):Function g:InvalidCb doesn't exist", + [ exc_exec('call dictwatcherdel(g:, "key", "g:InvalidCb")')) + [ end) + ]] + it('does not fail to replace a watcher function', function() source([[ let g:key = 'v2' From 506b938947b7083fc8bef20eabc08ed033298add Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 14 Feb 2017 00:12:57 +0300 Subject: [PATCH 0221/1671] *: Make some more things const and with length --- src/nvim/buffer.c | 2 +- src/nvim/eval.c | 201 +++++++++++++++++++++-------------------- src/nvim/eval/typval.c | 2 +- src/nvim/ex_cmds2.c | 6 +- src/nvim/option.c | 2 +- src/nvim/syntax.c | 19 ++-- 6 files changed, 118 insertions(+), 114 deletions(-) diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 0fb9a21354..c9101c5b53 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -3504,7 +3504,7 @@ int build_stl_str_hl( curbuf = o_curbuf; // Remove the variable we just stored - do_unlet((char_u *)"g:actual_curbuf", true); + do_unlet(S_LEN("g:actual_curbuf"), true); // } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index bd88678f16..67f6c8ba3a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -137,25 +137,24 @@ * "newkey" is the key for the new item. */ typedef struct lval_S { - char_u *ll_name; /* start of variable name (can be NULL) */ - char_u *ll_exp_name; /* NULL or expanded name in allocated memory. */ - typval_T *ll_tv; /* Typeval of item being used. If "newkey" - isn't NULL it's the Dict to which to add - the item. */ - listitem_T *ll_li; /* The list item or NULL. */ - list_T *ll_list; /* The list or NULL. */ - int ll_range; /* TRUE when a [i:j] range was used */ - long ll_n1; /* First index for list */ - long ll_n2; /* Second index for list range */ - int ll_empty2; /* Second index is empty: [i:] */ - dict_T *ll_dict; /* The Dictionary or NULL */ - dictitem_T *ll_di; /* The dictitem or NULL */ - char_u *ll_newkey; /* New key for Dict in alloc. mem or NULL. */ + const char *ll_name; ///< Start of variable name (can be NULL). + size_t ll_name_len; ///< Length of the .ll_name. + char *ll_exp_name; ///< NULL or expanded name in allocated memory. + typval_T *ll_tv; ///< Typeval of item being used. If "newkey" + ///< isn't NULL it's the Dict to which to add the item. + listitem_T *ll_li; ///< The list item or NULL. + list_T *ll_list; ///< The list or NULL. + int ll_range; ///< TRUE when a [i:j] range was used. + long ll_n1; ///< First index for list. + long ll_n2; ///< Second index for list range. + int ll_empty2; ///< Second index is empty: [i:]. + dict_T *ll_dict; ///< The Dictionary or NULL. + dictitem_T *ll_di; ///< The dictitem or NULL. + char_u *ll_newkey; ///< New key for Dict in allocated memory or NULL. } lval_T; static char *e_letunexp = N_("E18: Unexpected characters in :let"); -static char *e_undefvar = N_("E121: Undefined variable: %s"); static char *e_missbrac = N_("E111: Missing ']'"); static char *e_listarg = N_("E686: Argument of %s must be a List"); static char *e_listdictarg = N_( @@ -729,7 +728,7 @@ void set_internal_string_var(char_u *name, char_u *value) .vval.v_string = value, }; - set_var((const char *)name, (typval_T *)&tv, true); + set_var((const char *)name, STRLEN(name), (typval_T *)&tv, true); } static lval_T *redir_lval = NULL; @@ -768,8 +767,8 @@ var_redir_start ( // Parse the variable name (can be a dict or list entry). redir_endp = (char_u *)get_lval(redir_varname, NULL, redir_lval, false, false, 0, FNE_CHECK_START); - if (redir_endp == NULL || redir_lval->ll_name == NULL || *redir_endp != - NUL) { + if (redir_endp == NULL || redir_lval->ll_name == NULL + || *redir_endp != NUL) { clear_lval(redir_lval); if (redir_endp != NULL && *redir_endp != NUL) /* Trailing characters are present after the variable name */ @@ -2021,15 +2020,11 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, const int flags, const int fne_flags) FUNC_ATTR_NONNULL_ARG(1, 3) { - char_u *p; - int cc; dictitem_T *v; typval_T var1; typval_T var2; int empty1 = FALSE; listitem_T *ni; - char_u *key = NULL; - int len; hashtab_T *ht; int quiet = flags & GLV_QUIET; @@ -2038,16 +2033,18 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (skip) { // When skipping just find the end of the name. - lp->ll_name = (char_u *)name; - return (char_u *)find_name_end(name, NULL, NULL, FNE_INCL_BR | fne_flags); + lp->ll_name = (const char *)name; + return (char_u *)find_name_end((const char_u *)name, NULL, NULL, + FNE_INCL_BR | fne_flags); } // Find the end of the name. char_u *expr_start; char_u *expr_end; - p = (char_u *)find_name_end(name, - (const char_u **)&expr_start, - (const char_u **)&expr_end, fne_flags); + char_u *p = (char_u *)find_name_end(name, + (const char_u **)&expr_start, + (const char_u **)&expr_end, + fne_flags); if (expr_start != NULL) { /* Don't expand the name when we already know there is an error. */ if (unlet && !ascii_iswhite(*p) && !ends_excmd(*p) @@ -2056,7 +2053,8 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, return NULL; } - lp->ll_exp_name = make_expanded_name(name, expr_start, expr_end, p); + lp->ll_exp_name = (char *)make_expanded_name(name, expr_start, expr_end, + (char_u *)p); if (lp->ll_exp_name == NULL) { /* Report an invalid expression in braces, unless the * expression evaluation has been cancelled due to an @@ -2068,22 +2066,22 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } } lp->ll_name = lp->ll_exp_name; + lp->ll_name_len = strlen(lp->ll_name); } else { - lp->ll_name = (char_u *)name; + lp->ll_name = (const char *)name; + lp->ll_name_len = (size_t)((const char *)p - lp->ll_name); } - /* Without [idx] or .key we are done. */ - if ((*p != '[' && *p != '.') || lp->ll_name == NULL) + // Without [idx] or .key we are done. + if ((*p != '[' && *p != '.') || lp->ll_name == NULL) { return p; - - cc = *p; - *p = NUL; - v = find_var((const char *)lp->ll_name, STRLEN(lp->ll_name), &ht, - flags & GLV_NO_AUTOLOAD); - if (v == NULL && !quiet) { - EMSG2(_(e_undefvar), lp->ll_name); } - *p = cc; + + v = find_var(lp->ll_name, lp->ll_name_len, &ht, flags & GLV_NO_AUTOLOAD); + if (v == NULL && !quiet) { + emsgf(_("E121: Undefined variable: %.*s"), + (int)lp->ll_name_len, lp->ll_name); + } if (v == NULL) return NULL; @@ -2105,11 +2103,12 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, return NULL; } - len = -1; + int len = -1; + char_u *key; if (*p == '.') { key = p + 1; - for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len) - ; + for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) { + } if (len == 0) { if (!quiet) { EMSG(_("E713: Cannot use empty key after .")); @@ -2380,12 +2379,12 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, && !tv_check_lock(di->di_tv.v_lock, (const char *)lp->ll_name, STRLEN(lp->ll_name)))) && eexe_mod_op(&tv, rettv, (const char *)op) == OK) { - set_var((const char *)lp->ll_name, &tv, false); + set_var(lp->ll_name, lp->ll_name_len, &tv, false); } tv_clear(&tv); } } else { - set_var((const char *)lp->ll_name, rettv, copy); + set_var(lp->ll_name, lp->ll_name_len, rettv, copy); } *endp = cc; } else if (tv_check_lock(lp->ll_newkey == NULL @@ -2886,17 +2885,17 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) *name_end = NUL; // Normal name or expanded name. - if (do_unlet(lp->ll_name, forceit) == FAIL) { + if (do_unlet(lp->ll_name, lp->ll_name_len, forceit) == FAIL) { ret = FAIL; } *name_end = cc; } else if ((lp->ll_list != NULL && tv_check_lock(lp->ll_list->lv_lock, (const char *)lp->ll_name, - STRLEN(lp->ll_name))) + lp->ll_name_len)) || (lp->ll_dict != NULL && tv_check_lock(lp->ll_dict->dv_lock, (const char *)lp->ll_name, - STRLEN(lp->ll_name)))) { + lp->ll_name_len))) { return FAIL; } else if (lp->ll_range) { listitem_T *li; @@ -2906,7 +2905,7 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) { li = ll_li->li_next; if (tv_check_lock(ll_li->li_tv.v_lock, (const char *)lp->ll_name, - STRLEN(lp->ll_name))) { + lp->ll_name_len)) { return false; } ll_li = li; @@ -2953,22 +2952,22 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) // TODO(ZyX-I): move to eval/ex_cmds -/* - * "unlet" a variable. Return OK if it existed, FAIL if not. - * When "forceit" is TRUE don't complain if the variable doesn't exist. - */ -int do_unlet(char_u *name, int forceit) +/// unlet a variable +/// +/// @param[in] name Variable name to unlet. +/// @param[in] name_len Variable name length. +/// @param[in] fonceit If true, do not complain if variable doesn’t exist. +/// +/// @return OK if it existed, FAIL otherwise. +int do_unlet(const char *const name, const size_t name_len, const int forceit) + FUNC_ATTR_NONNULL_ALL { - hashtab_T *ht; - hashitem_T *hi; - char_u *varname; - dict_T *d; - dictitem_T *di; + const char *varname; dict_T *dict; - ht = find_var_ht_dict((const char *)name, STRLEN(name), - (const char **)&varname, &dict); + hashtab_T *ht = find_var_ht_dict(name, name_len, &varname, &dict); if (ht != NULL && *varname != NUL) { + dict_T *d; if (ht == &globvarht) { d = &globvardict; } else if (current_funccal != NULL @@ -2977,19 +2976,19 @@ int do_unlet(char_u *name, int forceit) } else if (ht == &compat_hashtab) { d = &vimvardict; } else { - di = find_var_in_ht(ht, *name, "", 0, false); + dictitem_T *const di = find_var_in_ht(ht, *name, "", 0, false); d = di->di_tv.vval.v_dict; } if (d == NULL) { EMSG2(_(e_intern2), "do_unlet()"); return FAIL; } - hi = hash_find(ht, varname); + hashitem_T *hi = hash_find(ht, (const char_u *)varname); if (HASHITEM_EMPTY(hi)) { hi = find_hi_in_scoped_ht((const char *)name, &ht); } if (hi != NULL && !HASHITEM_EMPTY(hi)) { - di = TV_DICT_HI2DI(hi); + dictitem_T *const di = TV_DICT_HI2DI(hi); if (var_check_fixed(di->di_flags, (const char *)name, STRLEN(name)) || var_check_ro(di->di_flags, (const char *)name, STRLEN(name)) || tv_check_lock(d->dv_lock, (const char *)name, STRLEN(name))) { @@ -3011,7 +3010,7 @@ int do_unlet(char_u *name, int forceit) delete_var(ht, hi); if (watched) { - tv_dict_watcher_notify(dict, (char *)varname, NULL, &oldtv); + tv_dict_watcher_notify(dict, varname, NULL, &oldtv); tv_clear(&oldtv); } return OK; @@ -3041,9 +3040,8 @@ static int do_lock_var(lval_T *lp, char_u *const name_end, const int deep, if (lp->ll_tv == NULL) { // Normal name or expanded name. - const size_t name_len = (size_t)(name_end - lp->ll_name); dictitem_T *const di = find_var( - (const char *)lp->ll_name, name_len, NULL, + (const char *)lp->ll_name, lp->ll_name_len, NULL, true); if (di == NULL) { ret = FAIL; @@ -6561,8 +6559,9 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_append_tv(l, &argvars[1]); tv_copy(&argvars[0], rettv); } - } else + } else { EMSG(_(e_listreq)); + } } /* @@ -11216,7 +11215,7 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_trailing)); } else { if (lv.ll_tv == NULL) { - di = find_var((const char *)lv.ll_name, STRLEN(lv.ll_name), NULL, true); + di = find_var((const char *)lv.ll_name, lv.ll_name_len, NULL, true); if (di != NULL) { // Consider a variable locked when: // 1. the variable itself is locked @@ -14351,7 +14350,7 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) curbuf = buf; memcpy(bufvarname, "b:", 2); memcpy(bufvarname + 2, varname, varname_len + 1); - set_var(bufvarname, varp, true); + set_var(bufvarname, varname_len + 2, varp, true); xfree(bufvarname); curbuf = save_curbuf; } @@ -14876,7 +14875,7 @@ static void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) char *const tabvarname = xmalloc(varname_len + 3); memcpy(tabvarname, "t:", 2); memcpy(tabvarname + 2, varname, varname_len + 1); - set_var(tabvarname, varp, true); + set_var(tabvarname, varname_len + 2, varp, true); xfree(tabvarname); // Restore current tabpage. @@ -14944,7 +14943,7 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) char *const winvarname = xmalloc(varname_len + 3); memcpy(winvarname, "w:", 2); memcpy(winvarname + 2, varname, varname_len + 1); - set_var(winvarname, varp, true); + set_var(winvarname, varname_len + 2, varp, true); xfree(winvarname); } } @@ -18766,16 +18765,17 @@ static void list_one_var_a(const char *prefix, const char *name, /// is created. /// /// @param[in] name Variable name to set. +/// @param[in] name_len Length of the variable name. /// @param tv Variable value. /// @param[in] copy True if value in tv is to be copied. -static void set_var(const char *name, typval_T *const tv, const bool copy) +static void set_var(const char *name, const size_t name_len, typval_T *const tv, + const bool copy) FUNC_ATTR_NONNULL_ALL { dictitem_T *v; hashtab_T *ht; dict_T *dict; - const size_t name_len = strlen(name); const char *varname; ht = find_var_ht_dict(name, name_len, &varname, &dict); const bool watched = tv_dict_is_watched(dict); @@ -18799,8 +18799,8 @@ static void set_var(const char *name, typval_T *const tv, const bool copy) typval_T oldtv = TV_INITIAL_VALUE; if (v != NULL) { // existing variable, need to clear the value - if (var_check_ro(v->di_flags, (const char *)name, name_len) - || tv_check_lock(v->di_tv.v_lock, (const char *)name, name_len)) { + if (var_check_ro(v->di_flags, name, name_len) + || tv_check_lock(v->di_tv.v_lock, name, name_len)) { return; } @@ -19852,7 +19852,6 @@ trans_function_name( const char_u *start; const char_u *end; int lead; - char_u sid_buf[20]; int len; lval_T lv; @@ -19938,10 +19937,10 @@ trans_function_name( /* Check if the name is a Funcref. If so, use the value. */ if (lv.ll_exp_name != NULL) { - len = (int)STRLEN(lv.ll_exp_name); - name = deref_func_name((const char *)lv.ll_exp_name, &len, partial, + len = (int)strlen(lv.ll_exp_name); + name = deref_func_name(lv.ll_exp_name, &len, partial, flags & TFN_NO_AUTOLOAD); - if (name == lv.ll_exp_name) { + if ((const char *)name == lv.ll_exp_name) { name = NULL; } } else if (!(flags & TFN_NO_DEREF)) { @@ -19966,12 +19965,13 @@ trans_function_name( } if (lv.ll_exp_name != NULL) { - len = (int)STRLEN(lv.ll_exp_name); + len = (int)strlen(lv.ll_exp_name); if (lead <= 2 && lv.ll_name == lv.ll_exp_name - && STRNCMP(lv.ll_name, "s:", 2) == 0) { + && lv.ll_name_len >= 2 && memcmp(lv.ll_name, "s:", 2) == 0) { /* When there was "s:" already or the name expanded to get a * leading "s:" then remove it. */ lv.ll_name += 2; + lv.ll_name_len -= 2; len -= 2; lead = 2; } @@ -19979,38 +19979,40 @@ trans_function_name( // Skip over "s:" and "g:". if (lead == 2 || (lv.ll_name[0] == 'g' && lv.ll_name[1] == ':')) { lv.ll_name += 2; + lv.ll_name_len -= 2; } - len = (int)(end - lv.ll_name); + len = (int)((const char *)end - lv.ll_name); } - /* - * Copy the function name to allocated memory. - * Accept name() inside a script, translate into 123_name(). - * Accept 123_name() outside a script. - */ - if (skip) - lead = 0; /* do nothing */ - else if (lead > 0) { + size_t sid_buf_len = 0; + char sid_buf[20]; + + // Copy the function name to allocated memory. + // Accept name() inside a script, translate into 123_name(). + // Accept 123_name() outside a script. + if (skip) { + lead = 0; // do nothing + } else if (lead > 0) { lead = 3; - if ((lv.ll_exp_name != NULL && eval_fname_sid((const char *)lv.ll_exp_name)) + if ((lv.ll_exp_name != NULL && eval_fname_sid(lv.ll_exp_name)) || eval_fname_sid((const char *)(*pp))) { // It's "s:" or "". if (current_SID <= 0) { EMSG(_(e_usingsid)); goto theend; } - sprintf((char *)sid_buf, "%" PRId64 "_", (int64_t)current_SID); - lead += (int)STRLEN(sid_buf); + sid_buf_len = snprintf(S_LEN(sid_buf), "%" PRIdSCID "_", current_SID); + lead += sid_buf_len; } } else if (!(flags & TFN_INT) - && builtin_function((const char *)lv.ll_name, len)) { + && builtin_function(lv.ll_name, lv.ll_name_len)) { EMSG2(_("E128: Function name must start with a capital or \"s:\": %s"), start); goto theend; } if (!skip && !(flags & TFN_QUIET) && !(flags & TFN_NO_DEREF)) { - char_u *cp = vim_strchr(lv.ll_name, ':'); + char_u *cp = xmemrchr(lv.ll_name, ':', lv.ll_name_len); if (cp != NULL && cp < end) { EMSG2(_("E884: Function name cannot contain a colon: %s"), start); @@ -20023,10 +20025,11 @@ trans_function_name( name[0] = K_SPECIAL; name[1] = KS_EXTRA; name[2] = (int)KE_SNR; - if (lead > 3) /* If it's "" */ - STRCPY(name + 3, sid_buf); + if (sid_buf_len > 0) { // If it's "" + memcpy(name + 3, sid_buf, sid_buf_len); + } } - memmove(name + lead, lv.ll_name, (size_t)len); + memmove(name + lead, lv.ll_name, len); name[lead + len] = NUL; *pp = (char_u *)end; @@ -21646,7 +21649,7 @@ void var_set_global(const char *const name, typval_T vartv) { funccall_T *const saved_current_funccal = current_funccal; current_funccal = NULL; - set_var(name, &vartv, false); + set_var(name, strlen(name), &vartv, false); current_funccal = saved_current_funccal; } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 8c11f4bd8f..2f3415a59d 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -2041,7 +2041,7 @@ bool tv_islocked(const typval_T *const tv) /// /// @param[in] lock Lock status. /// @param[in] name Variable name, used in the error message. -/// @param[in] use_gettext True if variable name also is to be translated. +/// @param[in] name_len Variable name length. /// /// @return true if variable is locked, false otherwise. bool tv_check_lock(const VarLockStatus lock, const char *const name, diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index b84834d351..213641667d 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -2256,8 +2256,8 @@ void ex_compiler(exarg_T *eap) } do_cmdline_cmd("command -nargs=* CompilerSet setlocal "); } - do_unlet((char_u *)"g:current_compiler", true); - do_unlet((char_u *)"b:current_compiler", true); + do_unlet(S_LEN("g:current_compiler"), true); + do_unlet(S_LEN("b:current_compiler"), true); snprintf((char *)buf, bufsize, "compiler/%s.vim", eap->arg); if (source_runtime(buf, DIP_ALL) == FAIL) { @@ -2280,7 +2280,7 @@ void ex_compiler(exarg_T *eap) old_cur_comp); xfree(old_cur_comp); } else { - do_unlet((char_u *)"g:current_compiler", true); + do_unlet(S_LEN("g:current_compiler"), true); } } } diff --git a/src/nvim/option.c b/src/nvim/option.c index 8a80c46cf2..9b31e14ea7 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2582,7 +2582,7 @@ did_set_string_option ( // The color scheme must have set 'background' back to another // value, that's not what we want here. Disable the color // scheme and set the colors again. - do_unlet((char_u *)"g:colors_name", true); + do_unlet(S_LEN("g:colors_name"), true); free_string_option(p_bg); p_bg = vim_strsave((char_u *)(dark ? "dark" : "light")); check_string_option(&p_bg); diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 3f84b8080f..c0adfb10fc 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -3259,9 +3259,10 @@ static void syn_cmd_clear(exarg_T *eap, int syncing) syntax_sync_clear(); else { syntax_clear(curwin->w_s); - if (curwin->w_s == &curwin->w_buffer->b_s) - do_unlet((char_u *)"b:current_syntax", TRUE); - do_unlet((char_u *)"w:current_syntax", TRUE); + if (curwin->w_s == &curwin->w_buffer->b_s) { + do_unlet(S_LEN("b:current_syntax"), true); + } + do_unlet(S_LEN("w:current_syntax"), true); } } else { /* @@ -3337,7 +3338,7 @@ static void syn_cmd_enable(exarg_T *eap, int syncing) { set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable"); syn_cmd_onoff(eap, "syntax"); - do_unlet((char_u *)"g:syntax_cmd", TRUE); + do_unlet(S_LEN("g:syntax_cmd"), true); } /* @@ -3350,7 +3351,7 @@ static void syn_cmd_reset(exarg_T *eap, int syncing) if (!eap->skip) { set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset"); do_cmdline_cmd("runtime! syntax/syncolor.vim"); - do_unlet((char_u *)"g:syntax_cmd", TRUE); + do_unlet(S_LEN("g:syntax_cmd"), true); } } @@ -5538,9 +5539,9 @@ void ex_ownsyntax(exarg_T *eap) } /* restore value of b:current_syntax */ - if (old_value == NULL) - do_unlet((char_u *)"b:current_syntax", TRUE); - else { + if (old_value == NULL) { + do_unlet(S_LEN("b:current_syntax"), true); + } else { set_internal_string_var((char_u *)"b:current_syntax", old_value); xfree(old_value); } @@ -6231,7 +6232,7 @@ do_highlight ( */ line = linep; if (ends_excmd(*line)) { - do_unlet((char_u *)"colors_name", TRUE); + do_unlet(S_LEN("colors_name"), true); restore_cterm_colors(); /* From 6aa6e5007596f1eaa23c45376e3a72d908c6688e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 4 Mar 2017 21:35:23 +0300 Subject: [PATCH 0222/1671] eval: Fix linter errors --- src/nvim/eval.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 67f6c8ba3a..937b56179d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2082,8 +2082,9 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, emsgf(_("E121: Undefined variable: %.*s"), (int)lp->ll_name_len, lp->ll_name); } - if (v == NULL) + if (v == NULL) { return NULL; + } /* * Loop until no more [idx] or .key is following. @@ -13431,7 +13432,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) const size_t p_len = strlen(p); cpy = xmallocz(p_len + len); memcpy(cpy, p, p_len + 1); - strncat(cpy + p_len, remain, len); + xstrlcat(cpy + p_len, remain, len); xfree(p); p = cpy; @@ -19968,8 +19969,8 @@ trans_function_name( len = (int)strlen(lv.ll_exp_name); if (lead <= 2 && lv.ll_name == lv.ll_exp_name && lv.ll_name_len >= 2 && memcmp(lv.ll_name, "s:", 2) == 0) { - /* When there was "s:" already or the name expanded to get a - * leading "s:" then remove it. */ + // When there was "s:" already or the name expanded to get a + // leading "s:" then remove it. lv.ll_name += 2; lv.ll_name_len -= 2; len -= 2; From 140174669e359ae7147288366dd94952955980cc Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 4 Mar 2017 21:46:24 +0300 Subject: [PATCH 0223/1671] unittests: Run tv_list_join tests in case it stopped failing --- test/unit/eval/typval_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index a785350c15..b0a7e9109a 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1110,7 +1110,7 @@ describe('typval.c', function() local recursive_li = recursive_l.lv_first lib.tv_list_item_remove(recursive_l, recursive_li) lib.tv_list_free(l) - end, true) + end) end) describe('equal()', function() itp('compares empty and NULL lists correctly', function() From 4c3be98db9b24b7d5b8dc2cf55574e778b7ad137 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 4 Mar 2017 23:28:54 +0300 Subject: [PATCH 0224/1671] unittests: Add tv_dict_watcher_{add,remove} tests --- test/unit/eval/helpers.lua | 246 ++++++++++++++++++++++----------- test/unit/eval/typval_spec.lua | 115 ++++++++++++++- 2 files changed, 282 insertions(+), 79 deletions(-) diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index cd85b143d9..21ef51ff20 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -33,18 +33,62 @@ local function li_alloc(nogc) return li end -local function list(...) - local ret = ffi.gc(eval.tv_list_alloc(), eval.tv_list_unref) - eq(0, ret.lv_refcount) - ret.lv_refcount = 1 - for i = 1, select('#', ...) do - local val = select(i, ...) - local li_tv = ffi.gc(lua2typvalt(val), nil) - local li = li_alloc(true) - li.li_tv = li_tv - eval.tv_list_append(ret, li) +local function populate_list(l, lua_l, processed) + processed = processed or {} + eq(0, l.lv_refcount) + l.lv_refcount = 1 + processed[lua_l] = l + for i = 1, #lua_l do + local item_tv = ffi.gc(lua2typvalt(lua_l[i], processed), nil) + local item_li = eval.tv_list_item_alloc() + item_li.li_tv = item_tv + eval.tv_list_append(l, item_li) end - return ret + return l +end + +local function populate_dict(d, lua_d, processed) + processed = processed or {} + eq(0, d.dv_refcount) + d.dv_refcount = 1 + processed[lua_d] = d + for k, v in pairs(lua_d) do + if type(k) == 'string' then + local di = eval.tv_dict_item_alloc(to_cstr(k)) + local val_tv = ffi.gc(lua2typvalt(v, processed), nil) + eval.tv_copy(val_tv, di.di_tv) + eval.tv_clear(val_tv) + eval.tv_dict_add(d, di) + end + end + return d +end + +local function populate_partial(pt, lua_pt, processed) + processed = processed or {} + eq(0, pt.pt_refcount) + processed[lua_pt] = pt + local argv = nil + if lua_pt.args and #lua_pt.args > 0 then + argv = ffi.gc(ffi.cast('typval_T*', eval.xmalloc(ffi.sizeof('typval_T') * #lua_pt.args)), nil) + for i, arg in ipairs(lua_pt.args) do + local arg_tv = ffi.gc(lua2typvalt(arg, processed), nil) + argv[i - 1] = arg_tv + end + end + local dict = nil + if lua_pt.dict then + local dict_tv = ffi.gc(lua2typvalt(lua_pt.dict, processed), nil) + assert(dict_tv.v_type == eval.VAR_DICT) + dict = dict_tv.vval.v_dict + end + pt.pt_refcount = 1 + pt.pt_name = eval.xmemdupz(to_cstr(lua_pt.value), #lua_pt.value) + pt.pt_auto = not not lua_pt.auto + pt.pt_argc = lua_pt.args and #lua_pt.args or 0 + pt.pt_argv = argv + pt.pt_dict = dict + return pt end local ptr2key = function(ptr) @@ -55,6 +99,30 @@ local lst2tbl local dct2tbl local typvalt2lua + +local function partial2lua(pt, processed) + processed = processed or {} + local value, auto, dict, argv = nil, nil, nil, nil + if pt ~= nil then + value = ffi.string(pt.pt_name) + auto = pt.pt_auto and true or nil + argv = {} + for i = 1, pt.pt_argc do + argv[i] = typvalt2lua(pt.pt_argv[i - 1], processed) + end + if pt.pt_dict ~= nil then + dict = dct2tbl(pt.pt_dict) + end + end + return { + [type_key]=func_type, + value=value, + auto=auto, + args=argv, + dict=dict, + } +end + local typvalt2lua_tab = nil local function typvalt2lua_tab_init() @@ -97,26 +165,7 @@ local function typvalt2lua_tab_init() if processed[p_key] then return processed[p_key] end - local pt = t.vval.v_partial - local value, auto, dict, argv = nil, nil, nil, nil - if pt ~= nil then - value = ffi.string(pt.pt_name) - auto = pt.pt_auto and true or nil - argv = {} - for i = 1, pt.pt_argc do - argv[i] = typvalt2lua(pt.pt_argv[i - 1], processed) - end - if pt.pt_dict ~= nil then - dict = dct2tbl(pt.pt_dict) - end - end - return { - [type_key]=func_type, - value=value, - auto=auto, - args=argv, - dict=dict, - } + return partial2lua(t.vval.v_partial, processed) end, } end @@ -257,36 +306,16 @@ local lua2typvalt_type_tab = { processed[l].lv_refcount = processed[l].lv_refcount + 1 return typvalt(eval.VAR_LIST, {v_list=processed[l]}) end - local lst = eval.tv_list_alloc() - lst.lv_refcount = 1 - processed[l] = lst - local ret = typvalt(eval.VAR_LIST, {v_list=lst}) - for i = 1, #l do - local item_tv = ffi.gc(lua2typvalt(l[i], processed), nil) - eval.tv_list_append_tv(lst, item_tv) - eval.tv_clear(item_tv) - end - return ret + local lst = populate_list(eval.tv_list_alloc(), l, processed) + return typvalt(eval.VAR_LIST, {v_list=lst}) end, [dict_type] = function(l, processed) if processed[l] then processed[l].dv_refcount = processed[l].dv_refcount + 1 return typvalt(eval.VAR_DICT, {v_dict=processed[l]}) end - local dct = eval.tv_dict_alloc() - dct.dv_refcount = 1 - processed[l] = dct - local ret = typvalt(eval.VAR_DICT, {v_dict=dct}) - for k, v in pairs(l) do - if type(k) == 'string' then - local di = eval.tv_dict_item_alloc(to_cstr(k)) - local val_tv = ffi.gc(lua2typvalt(v, processed), nil) - eval.tv_copy(val_tv, di.di_tv) - eval.tv_clear(val_tv) - eval.tv_dict_add(dct, di) - end - end - return ret + local dct = populate_dict(eval.tv_dict_alloc(), l, processed) + return typvalt(eval.VAR_DICT, {v_dict=dct}) end, [func_type] = function(l, processed) if processed[l] then @@ -294,29 +323,8 @@ local lua2typvalt_type_tab = { return typvalt(eval.VAR_PARTIAL, {v_partial=processed[l]}) end if l.args or l.dict then - local pt = ffi.gc(ffi.cast('partial_T*', eval.xmalloc(ffi.sizeof('partial_T'))), nil) - processed[l] = pt - local argv = nil - if l.args and #l.args > 0 then - argv = ffi.gc(ffi.cast('typval_T*', eval.xmalloc(ffi.sizeof('typval_T') * #l.args)), nil) - for i, arg in ipairs(l.args) do - local arg_tv = ffi.gc(lua2typvalt(arg, processed), nil) - eval.tv_copy(arg_tv, argv[i - 1]) - eval.tv_clear(arg_tv) - end - end - local dict = nil - if l.dict then - local dict_tv = ffi.gc(lua2typvalt(l.dict, processed), nil) - assert(dict_tv.v_type == eval.VAR_DICT) - dict = dict_tv.vval.v_dict - end - pt.pt_refcount = 1 - pt.pt_name = eval.xmemdupz(to_cstr(l.value), #l.value) - pt.pt_auto = not not l.auto - pt.pt_argc = l.args and #l.args or 0 - pt.pt_argv = argv - pt.pt_dict = dict + local pt = populate_partial(ffi.gc(ffi.cast('partial_T*', + eval.xcalloc(1, ffi.sizeof('partial_T'))), nil), l, processed) return typvalt(eval.VAR_PARTIAL, {v_partial=pt}) else return typvalt(eval.VAR_FUNC, { @@ -402,13 +410,91 @@ local alloc_logging_helpers = { return {func='malloc', args={size + 1}, ret=void(s)} end, + dwatcher = function(w) return {func='malloc', args={ffi.sizeof('DictWatcher')}, ret=void(w)} end, + freed = function(p) return {func='free', args={type(p) == 'table' and p or void(p)}} end, + + -- lua_…: allocated by this file, not by some Neovim function + lua_pt = function(pt) return {func='calloc', args={1, ffi.sizeof('partial_T')}, ret=void(pt)} end, + lua_tvs = function(argv, argc) return {func='malloc', args={ffi.sizeof('typval_T')*argc}, ret=void(argv)} end, } local function int(n) return {[type_key]=int_type, value=n} end +local function list(...) + return populate_list(ffi.gc(eval.tv_list_alloc(), eval.tv_list_unref), + {...}, {}) +end + +local function dict(d) + return populate_dict(ffi.gc(eval.tv_dict_alloc(), eval.tv_dict_free), + d, {}) +end + +local callback2tbl_type_tab = nil + +local function init_callback2tbl_type_tab() + if callback2tbl_type_tab then + return + end + callback2tbl_type_tab = { + [tonumber(eval.kCallbackNone)] = function(_) return {type='none'} end, + [tonumber(eval.kCallbackFuncref)] = function(cb) + return {type='fref', fref=ffi.string(cb.data.funcref)} + end, + [tonumber(eval.kCallbackPartial)] = function(cb) + local lua_pt = partial2lua(cb.data.partial) + return {type='pt', fref=ffi.string(lua_pt.value), pt=lua_pt} + end + } +end + +local function callback2tbl(cb) + init_callback2tbl_type_tab() + return callback2tbl_type_tab[tonumber(cb.type)](cb) +end + +local function tbl2callback(tbl) + local ret = nil + if tbl.type == 'none' then + ret = ffi.new('Callback[1]', {{type=eval.kCallbackNone}}) + elseif tbl.type == 'fref' then + ret = ffi.new('Callback[1]', {{type=eval.kCallbackFuncref, + data={funcref=eval.xstrdup(tbl.fref)}}}) + elseif tbl.type == 'pt' then + local pt = ffi.gc(ffi.cast('partial_T*', + eval.xcalloc(1, ffi.sizeof('partial_T'))), eval.partial_unref) + ret = ffi.new('Callback[1]', {{type=eval.kCallbackPartial, + data={partial=populate_partial(pt, tbl.pt, {})}}}) + else + assert(false) + end + return ffi.gc(ffi.cast('Callback*', ret), helpers.callback_free) +end + +local function dict_watchers(d) + local ret = {} + local h = d.watchers + local q = h.next + local qs = {} + local key_patterns = {} + while q ~= h do + local qitem = ffi.cast('DictWatcher *', + ffi.cast('char *', q) - ffi.offsetof('DictWatcher', 'node')) + ret[#ret + 1] = { + cb=callback2tbl(qitem.callback), + pat=ffi.string(qitem.key_pattern, qitem.key_pattern_len), + busy=qitem.busy, + } + qs[#qs + 1] = qitem + key_patterns[#key_patterns + 1] = {qitem.key_pattern, qitem.key_pattern_len} + q = q.next + end + return ret, qs, key_patterns +end + return { int=int, @@ -427,6 +513,7 @@ return { locks_key=locks_key, list=list, + dict=dict, lst2tbl=lst2tbl, dct2tbl=dct2tbl, @@ -446,5 +533,8 @@ return { list_items=list_items, dict_items=dict_items, + dict_watchers=dict_watchers, + tbl2callback=tbl2callback, + empty_list = {[type_key]=list_type}, } diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index b0a7e9109a..4efde9c6c1 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -14,21 +14,26 @@ local alloc_log_new = helpers.alloc_log_new local a = eval_helpers.alloc_logging_helpers local int = eval_helpers.int local list = eval_helpers.list +local dict = eval_helpers.dict local lst2tbl = eval_helpers.lst2tbl local typvalt = eval_helpers.typvalt local type_key = eval_helpers.type_key local li_alloc = eval_helpers.li_alloc local int_type = eval_helpers.int_type local first_di = eval_helpers.first_di +local func_type = eval_helpers.func_type local null_list = eval_helpers.null_list local null_dict = eval_helpers.null_dict local empty_list = eval_helpers.empty_list local lua2typvalt = eval_helpers.lua2typvalt local typvalt2lua = eval_helpers.typvalt2lua local null_string = eval_helpers.null_string +local tbl2callback = eval_helpers.tbl2callback +local dict_watchers = eval_helpers.dict_watchers local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h', - './src/nvim/mbyte.h', './src/nvim/garray.h') + './src/nvim/mbyte.h', './src/nvim/garray.h', + './src/nvim/eval.h') local function list_items(l) local lis = {} @@ -1387,4 +1392,112 @@ describe('typval.c', function() end) end) end) + describe('dict', function() + describe('watcher', function() + describe('add/remove', function() + itp('works with an empty key', function() + local d = dict({}) + eq({}, dict_watchers(d)) + local cb = ffi.gc(tbl2callback({type='none'}), nil) + alloc_log:clear() + lib.tv_dict_watcher_add(d, '*', 0, cb[0]) + local ws, qs = dict_watchers(d) + local key_p = qs[1].key_pattern + alloc_log:check({ + a.dwatcher(qs[1]), + a.str(key_p, 0), + }) + eq({{busy=false, cb={type='none'}, pat=''}}, ws) + eq(true, lib.tv_dict_watcher_remove(d, 'x', 0, cb[0])) + alloc_log:check({ + a.freed(key_p), + a.freed(qs[1]), + }) + eq({}, dict_watchers(d)) + end) + itp('works with multiple callbacks', function() + local d = dict({}) + eq({}, dict_watchers(d)) + alloc_log:check({a.dict(d)}) + local cbs = {} + cbs[1] = {'te', ffi.gc(tbl2callback({type='none'}), nil)} + alloc_log:check({}) + cbs[2] = {'foo', ffi.gc(tbl2callback({type='fref', fref='tr'}), nil)} + alloc_log:check({ + a.str(cbs[2][2].data.funcref, #('tr')), + }) + cbs[3] = {'te', ffi.gc(tbl2callback({type='pt', fref='tr', pt={ + value='tr', + args={'test'}, + dict={}, + }}), nil)} + local pt3 = cbs[3][2].data.partial + local pt3_argv = pt3.pt_argv + local pt3_dict = pt3.pt_dict + local pt3_name = pt3.pt_name + local pt3_str_arg = pt3.pt_argv[0].vval.v_string + alloc_log:check({ + a.lua_pt(pt3), + a.lua_tvs(pt3_argv, pt3.pt_argc), + a.str(pt3_str_arg, #('test')), + a.dict(pt3_dict), + a.str(pt3_name, #('tr')), + }) + for _, v in ipairs(cbs) do + lib.tv_dict_watcher_add(d, v[1], #(v[1]), v[2][0]) + end + local ws, qs, kps = dict_watchers(d) + eq({{busy=false, pat=cbs[1][1], cb={type='none'}}, + {busy=false, pat=cbs[2][1], cb={type='fref', fref='tr'}}, + {busy=false, pat=cbs[3][1], cb={type='pt', fref='tr', pt={ + [type_key]=func_type, + value='tr', + args={'test'}, + dict={}, + }}}}, ws) + alloc_log:check({ + a.dwatcher(qs[1]), + a.str(kps[1][1], kps[1][2]), + a.dwatcher(qs[2]), + a.str(kps[2][1], kps[2][2]), + a.dwatcher(qs[3]), + a.str(kps[3][1], kps[3][2]), + }) + eq(true, lib.tv_dict_watcher_remove(d, cbs[2][1], #cbs[2][1], cbs[2][2][0])) + alloc_log:check({ + a.freed(cbs[2][2].data.funcref), + a.freed(kps[2][1]), + a.freed(qs[2]), + }) + eq(false, lib.tv_dict_watcher_remove(d, cbs[2][1], #cbs[2][1], cbs[2][2][0])) + eq({{busy=false, pat=cbs[1][1], cb={type='none'}}, + {busy=false, pat=cbs[3][1], cb={type='pt', fref='tr', pt={ + [type_key]=func_type, + value='tr', + args={'test'}, + dict={}, + }}}}, dict_watchers(d)) + eq(true, lib.tv_dict_watcher_remove(d, cbs[3][1], #cbs[3][1], cbs[3][2][0])) + alloc_log:check({ + a.freed(pt3_str_arg), + a.freed(pt3_argv), + a.freed(pt3_dict), + a.freed(pt3_name), + a.freed(pt3), + a.freed(kps[3][1]), + a.freed(qs[3]), + }) + eq(false, lib.tv_dict_watcher_remove(d, cbs[3][1], #cbs[3][1], cbs[3][2][0])) + eq({{busy=false, pat=cbs[1][1], cb={type='none'}}}, dict_watchers(d)) + eq(true, lib.tv_dict_watcher_remove(d, cbs[1][1], #cbs[1][1], cbs[1][2][0])) + alloc_log:check({ + a.freed(kps[1][1]), + a.freed(qs[1]), + }) + eq(false, lib.tv_dict_watcher_remove(d, cbs[1][1], #cbs[1][1], cbs[1][2][0])) + eq({}, dict_watchers(d)) + end) + end) + end) + end) end) From 78a0de2c1b722d5445dfc44cc0635e5ce4bcd23e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 4 Mar 2017 23:30:10 +0300 Subject: [PATCH 0225/1671] eval/typval: Fix -Werror=return-type --- src/nvim/eval/typval.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 2f3415a59d..326334f149 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -845,6 +845,8 @@ static bool tv_callback_equal(const Callback *const cb1, return true; } } + assert(false); + return false; } /// Remove watcher from a dictionary From c6c48e8672ab6adbf78453e2b133fe3bd2ee1eb5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 4 Mar 2017 23:30:42 +0300 Subject: [PATCH 0226/1671] syntax: Fix linter error --- src/nvim/syntax.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index c0adfb10fc..a54e36a609 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -5538,7 +5538,7 @@ void ex_ownsyntax(exarg_T *eap) set_internal_string_var((char_u *)"w:current_syntax", new_value); } - /* restore value of b:current_syntax */ + // Restore value of b:current_syntax. if (old_value == NULL) { do_unlet(S_LEN("b:current_syntax"), true); } else { From 2c8ad276523489223d2d804075a19367280b0f10 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Mar 2017 00:14:11 +0300 Subject: [PATCH 0227/1671] ascii: Readd DEL_STR define --- src/nvim/ascii.h | 1 + src/nvim/tui/tui.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h index 37b83efb61..adde91f9ec 100644 --- a/src/nvim/ascii.h +++ b/src/nvim/ascii.h @@ -26,6 +26,7 @@ #define ESC '\033' #define ESC_STR "\033" #define DEL 0x7f +#define DEL_STR "\177" #define CSI 0x9b // Control Sequence Introducer #define CSI_STR "\233" #define DCS 0x90 /* Device Control String */ diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 9fbbe8be92..55936ad58d 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1015,7 +1015,7 @@ static const char *tui_tk_ti_getstr(const char *name, const char *value, ILOG("libtermkey:kdch1=%s", value); // Vim: "If and are now the same, redefine ." if (stty_erase != NULL && value != NULL && strcmp(stty_erase, value) == 0) { - return stty_erase[0] == DEL ? (char *)CTRL_H_STR : (char *)DEL_STR; + return stty_erase[0] == DEL ? CTRL_H_STR : DEL_STR; } } From cdb1aa3e47cb0ec19d2ae597c1d21b7e892d0d7e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Mar 2017 00:47:37 +0300 Subject: [PATCH 0228/1671] eval: Fix len argument to xstrlcat --- src/nvim/eval.c | 2 +- test/functional/shada/shada_spec.lua | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 937b56179d..cab9d07e56 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -13432,7 +13432,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) const size_t p_len = strlen(p); cpy = xmallocz(p_len + len); memcpy(cpy, p, p_len + 1); - xstrlcat(cpy + p_len, remain, len); + xstrlcat(cpy + p_len, remain, len + 1); xfree(p); p = cpy; diff --git a/test/functional/shada/shada_spec.lua b/test/functional/shada/shada_spec.lua index 32598fc399..ca44026852 100644 --- a/test/functional/shada/shada_spec.lua +++ b/test/functional/shada/shada_spec.lua @@ -180,8 +180,7 @@ describe('ShaDa support code', function() nvim_command('undo') nvim_command('set shada+=%') nvim_command('wshada! ' .. shada_fname) - local readme_fname = paths.test_source_path .. '/README.md' - readme_fname = helpers.eval( 'resolve("' .. readme_fname .. '")' ) + local readme_fname = funcs.resolve(paths.test_source_path) .. '/README.md' eq({[7]=1, [8]=2, [9]=1, [10]=4, [11]=1}, find_file(readme_fname)) nvim_command('set shada+=r~') nvim_command('wshada! ' .. shada_fname) @@ -189,7 +188,8 @@ describe('ShaDa support code', function() nvim_command('set shada-=r~') nvim_command('wshada! ' .. shada_fname) eq({[7]=1, [8]=2, [9]=1, [10]=4, [11]=1}, find_file(readme_fname)) - nvim_command('set shada+=r' .. paths.test_source_path) + nvim_command('set shada+=r' .. funcs.escape( + funcs.escape(paths.test_source_path, '$~'), ' "\\,')) nvim_command('wshada! ' .. shada_fname) eq({}, find_file(readme_fname)) end) From ffaf7c7521399826d9a32b12a1180bcd162f88be Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Mar 2017 01:09:55 +0300 Subject: [PATCH 0229/1671] unittests: Add tv_dict_item_{alloc,free} tests --- test/helpers.lua | 14 +++++++++++ test/unit/eval/typval_spec.lua | 45 ++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/test/helpers.lua b/test/helpers.lua index e5224349c2..1a86effa1c 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -225,6 +225,19 @@ local function which(exe) end end +local function concat_tables(...) + local ret = {} + for i = 1, select('#', ...) do + local tbl = select(i, ...) + if tbl then + for _, v in ipairs(tbl) do + ret[#ret + 1] = v + end + end + end + return ret +end + return { eq = eq, neq = neq, @@ -238,4 +251,5 @@ return { check_cores = check_cores, hasenv = hasenv, which = which, + concat_tables = concat_tables, } diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 4efde9c6c1..3bd7d9dbaf 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1,4 +1,5 @@ local helpers = require('test.unit.helpers')(after_each) +local global_helpers = require('test.helpers') local eval_helpers = require('test.unit.eval.helpers') local itp = helpers.gen_itp(it) @@ -31,6 +32,8 @@ local null_string = eval_helpers.null_string local tbl2callback = eval_helpers.tbl2callback local dict_watchers = eval_helpers.dict_watchers +local concat_tables = global_helpers.concat_tables + local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h', './src/nvim/mbyte.h', './src/nvim/garray.h', './src/nvim/eval.h') @@ -1498,6 +1501,48 @@ describe('typval.c', function() eq({}, dict_watchers(d)) end) end) + describe('notify', function() + -- Way too hard to test it here, functional tests in + -- dict_notifications_spec.lua. + end) + end) + describe('item', function() + describe('alloc/free', function() + local function check_tv_dict_item_alloc_len(s, len, tv, more_frees) + local di + if len == nil then + di = ffi.gc(lib.tv_dict_item_alloc(s), nil) + len = #s + else + di = ffi.gc(lib.tv_dict_item_alloc_len(s, len or #s), nil) + end + eq(s:sub(1, len), ffi.string(di.di_key)) + alloc_log:check({a.di(di, len)}) + if tv then + di.di_tv = tv + else + di.di_tv.v_type = lib.VAR_UNKNOWN + end + lib.tv_dict_item_free(di) + alloc_log:check(concat_tables(more_frees, {a.freed(di)})) + end + local function check_tv_dict_item_alloc(s, tv, more_frees) + return check_tv_dict_item_alloc_len(s, nil, tv, more_frees) + end + itp('works', function() + check_tv_dict_item_alloc('') + check_tv_dict_item_alloc('t') + check_tv_dict_item_alloc('TEST') + check_tv_dict_item_alloc_len('', 0) + check_tv_dict_item_alloc_len('TEST', 2) + local tv = lua2typvalt('test') + alloc_log:check({a.str(tv.vval.v_string, #('test'))}) + check_tv_dict_item_alloc('', tv, {a.freed(tv.vval.v_string)}) + tv = lua2typvalt('test') + alloc_log:check({a.str(tv.vval.v_string, #('test'))}) + check_tv_dict_item_alloc_len('', 0, tv, {a.freed(tv.vval.v_string)}) + end) + end) end) end) end) From 6c622ed08bb56f08f4cc4dbf6251220ccf7ba2ba Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Mar 2017 01:26:26 +0300 Subject: [PATCH 0230/1671] unittests: Add tv_dict_item_{add,remove} tests --- test/unit/eval/helpers.lua | 7 ++++-- test/unit/eval/typval_spec.lua | 46 ++++++++++++++++++++++++++-------- 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index 21ef51ff20..8faa8dbf5c 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -416,7 +416,10 @@ local alloc_logging_helpers = { -- lua_…: allocated by this file, not by some Neovim function lua_pt = function(pt) return {func='calloc', args={1, ffi.sizeof('partial_T')}, ret=void(pt)} end, - lua_tvs = function(argv, argc) return {func='malloc', args={ffi.sizeof('typval_T')*argc}, ret=void(argv)} end, + lua_tvs = function(argv, argc) + argc = alloc_len(argc) + return {func='malloc', args={ffi.sizeof('typval_T')*argc}, ret=void(argv)} + end, } local function int(n) @@ -430,7 +433,7 @@ end local function dict(d) return populate_dict(ffi.gc(eval.tv_dict_alloc(), eval.tv_dict_free), - d, {}) + d or {}, {}) end local callback2tbl_type_tab = nil diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 3bd7d9dbaf..ebd53469e8 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -8,6 +8,7 @@ local OK = helpers.OK local eq = helpers.eq local neq = helpers.neq local ffi = helpers.ffi +local FAIL = helpers.FAIL local cimport = helpers.cimport local to_cstr = helpers.to_cstr local alloc_log_new = helpers.alloc_log_new @@ -17,6 +18,7 @@ local int = eval_helpers.int local list = eval_helpers.list local dict = eval_helpers.dict local lst2tbl = eval_helpers.lst2tbl +local dct2tbl = eval_helpers.dct2tbl local typvalt = eval_helpers.typvalt local type_key = eval_helpers.type_key local li_alloc = eval_helpers.li_alloc @@ -111,6 +113,18 @@ local function ga_alloc(itemsize, growsize) return ga end +local function check_emsg(f, msg) + local saved_last_msg_hist = lib.last_msg_hist + local ret = {f()} + if msg ~= nil then + neq(saved_last_msg_hist, lib.last_msg_hist) + eq(msg, ffi.string(lib.last_msg_hist.msg)) + else + eq(saved_last_msg_hist, lib.last_msg_hist) + end + return unpack(ret) +end + describe('typval.c', function() describe('list', function() describe('item', function() @@ -1242,17 +1256,6 @@ describe('typval.c', function() alloc_log:check({}) end) end) - local function check_emsg(f, msg) - local saved_last_msg_hist = lib.last_msg_hist - local ret = {f()} - if msg ~= nil then - neq(saved_last_msg_hist, lib.last_msg_hist) - eq(msg, ffi.string(lib.last_msg_hist.msg)) - else - eq(saved_last_msg_hist, lib.last_msg_hist) - end - return unpack(ret) - end describe('nr()', function() local function tv_list_find_nr(l, n, msg) return check_emsg(function() @@ -1543,6 +1546,27 @@ describe('typval.c', function() check_tv_dict_item_alloc_len('', 0, tv, {a.freed(tv.vval.v_string)}) end) end) + describe('add/remove', function() + itp('works', function() + local d = dict() + eq({}, dct2tbl(d)) + alloc_log:check({a.dict(d)}) + local di = ffi.gc(lib.tv_dict_item_alloc(''), nil) + local tv = lua2typvalt('test') + di.di_tv = tv + alloc_log:check({a.di(di, ''), a.str(tv.vval.v_string, 'test')}) + eq(OK, lib.tv_dict_add(d, di)) + alloc_log:check({}) + eq(FAIL, check_emsg(function() return lib.tv_dict_add(d, di) end, + 'E685: Internal error: hash_add()')) + alloc_log:clear() + lib.tv_dict_item_remove(d, di) + alloc_log:check({ + a.freed(tv.vval.v_string), + a.freed(di), + }) + end) + end) end) end) end) From faddd83db8a71623e78a4d919b2bb55e6a58439d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Mar 2017 01:37:16 +0300 Subject: [PATCH 0231/1671] eval: Fix SEGV in test49 --- src/nvim/eval.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index cab9d07e56..882b2dfa74 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2055,6 +2055,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, lp->ll_exp_name = (char *)make_expanded_name(name, expr_start, expr_end, (char_u *)p); + lp->ll_name = lp->ll_exp_name; if (lp->ll_exp_name == NULL) { /* Report an invalid expression in braces, unless the * expression evaluation has been cancelled due to an @@ -2064,9 +2065,10 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, EMSG2(_(e_invarg2), name); return NULL; } + lp->ll_name_len = 0; + } else { + lp->ll_name_len = strlen(lp->ll_name); } - lp->ll_name = lp->ll_exp_name; - lp->ll_name_len = strlen(lp->ll_name); } else { lp->ll_name = (const char *)name; lp->ll_name_len = (size_t)((const char *)p - lp->ll_name); From 38dd81c136c2edbda66614622a4747bf1785af94 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Mar 2017 02:15:17 +0300 Subject: [PATCH 0232/1671] eval/typval: Fix SEGV in test_alot.vim test --- src/nvim/eval/typval.c | 5 ++++- test/functional/eval/null_spec.lua | 6 ++++++ test/unit/eval/typval_spec.lua | 7 +++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 326334f149..8d1ecf8f14 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1167,8 +1167,11 @@ void tv_dict_unref(dict_T *const d) /// @return found item or NULL if nothing was found. dictitem_T *tv_dict_find(const dict_T *const d, const char *const key, const ptrdiff_t len) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { + if (d == NULL) { + return NULL; + } hashitem_T *const hi = (len < 0 ? hash_find(&d->dv_hashtab, (const char_u *)key) : hash_find_len(&d->dv_hashtab, key, (size_t)len)); diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index f72d05f2a3..3b361ceeda 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -130,4 +130,10 @@ describe('NULL', function() null_list_expr_test('is not not equal to itself', 'L != L', 0, 0) null_list_expr_test('counts correctly', 'count([L], L)', 0, 1) end) + describe('dict', function() + it('does not crash when indexing NULL dict', function() + eq('\nE716: Key not present in Dictionary: test\nE15: Invalid expression: v:_null_dict.test', + redir_exec('echo v:_null_dict.test')) + end) + end) end) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index ebd53469e8..937869a7bc 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1568,5 +1568,12 @@ describe('typval.c', function() end) end) end) + describe('indexing', function() + describe('find', function() + itp('works with NULL dict', function() + eq(nil, lib.tv_dict_find(nil, '', 0)) + end) + end) + end) end) end) From 3bf87a5a6b0e8b2e81534081196c20dddc45474b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Mar 2017 02:51:44 +0300 Subject: [PATCH 0233/1671] eval: Do not use S_LEN as snprintf argument --- src/nvim/eval.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 882b2dfa74..88ba0b95cc 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -20004,7 +20004,8 @@ trans_function_name( EMSG(_(e_usingsid)); goto theend; } - sid_buf_len = snprintf(S_LEN(sid_buf), "%" PRIdSCID "_", current_SID); + sid_buf_len = snprintf(sid_buf, sizeof(sid_buf), + "%" PRIdSCID "_", current_SID); lead += sid_buf_len; } } else if (!(flags & TFN_INT) From f830243ff7b78b9bb41e72fd602b99c2893b5437 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Mar 2017 03:10:34 +0300 Subject: [PATCH 0234/1671] mbyte: Include os_defs.h in mbyte.h --- src/nvim/mbyte.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h index c20e6d47ff..3565202466 100644 --- a/src/nvim/mbyte.h +++ b/src/nvim/mbyte.h @@ -6,6 +6,7 @@ #include "nvim/iconv.h" #include "nvim/func_attr.h" +#include "nvim/os/os_defs.h" // For WCHAR, indirect /* * Return byte length of character that starts with byte "b". From 52e226ff74f49a02f884247b61d1865fa14e810d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Mar 2017 03:53:42 +0300 Subject: [PATCH 0235/1671] unittests: Disable tv_list_join test on Mac OS only --- test/unit/eval/typval_spec.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 937869a7bc..2653706e5b 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -34,6 +34,7 @@ local null_string = eval_helpers.null_string local tbl2callback = eval_helpers.tbl2callback local dict_watchers = eval_helpers.dict_watchers +local uname = global_helpers.uname local concat_tables = global_helpers.concat_tables local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h', @@ -1132,7 +1133,7 @@ describe('typval.c', function() local recursive_li = recursive_l.lv_first lib.tv_list_item_remove(recursive_l, recursive_li) lib.tv_list_free(l) - end) + end, uname() == 'Darwin') end) describe('equal()', function() itp('compares empty and NULL lists correctly', function() From 5ce624324136408a3f412d6c2ff437455f1e3344 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 12 Mar 2017 14:55:14 +0300 Subject: [PATCH 0236/1671] unittests: Enable tv_list_join tests back Unable to reproduce the problem on Mac OS X Sierra VPS, need to check whether it is reproducible on travis. --- test/unit/eval/typval_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 2653706e5b..2f01e9634e 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1133,7 +1133,7 @@ describe('typval.c', function() local recursive_li = recursive_l.lv_first lib.tv_list_item_remove(recursive_l, recursive_li) lib.tv_list_free(l) - end, uname() == 'Darwin') + end) end) describe('equal()', function() itp('compares empty and NULL lists correctly', function() From b222453c95dbe466b79abd578642fa6038770a55 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 12 Mar 2017 21:58:38 +0300 Subject: [PATCH 0237/1671] eval/typval: Refactor errors a bit: use emsgf always --- src/nvim/eval/typval.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 8d1ecf8f14..867330c137 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -755,7 +755,7 @@ const char *tv_list_find_str(list_T *const l, const int n) { const listitem_T *const li = tv_list_find(l, n); if (li == NULL) { - EMSGN(_(e_listidx), n); + emsgf(_(e_listidx), (int64_t)n); return NULL; } return tv_get_string(&li->li_tv); @@ -1264,14 +1264,14 @@ bool tv_dict_get_callback(dict_T *const d, if (di->di_tv.v_type != VAR_FUNC && di->di_tv.v_type != VAR_STRING && di->di_tv.v_type != VAR_PARTIAL) { - EMSG(_("Argument is not a function or function name")); + emsgf(_("E6000: Argument is not a function or function name")); return false; } typval_T tv; tv_copy(&di->di_tv, &tv); set_selfdict(&tv, d); - bool res = callback_from_typval(result, &tv); + const bool res = callback_from_typval(result, &tv); tv_clear(&tv); return res; } @@ -1942,7 +1942,7 @@ void tv_copy(typval_T *const from, typval_T *const to) break; } case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "tv_copy(UNKNOWN)"); + emsgf(_(e_intern2), "tv_copy(UNKNOWN)"); break; } } @@ -2259,7 +2259,7 @@ bool tv_check_num(const typval_T *const tv) case VAR_DICT: case VAR_FLOAT: case VAR_UNKNOWN: { - EMSG(_(num_errors[tv->v_type])); + emsgf(_(num_errors[tv->v_type])); return false; } } @@ -2303,7 +2303,7 @@ bool tv_check_str(const typval_T *const tv) case VAR_DICT: case VAR_FLOAT: case VAR_UNKNOWN: { - EMSG(_(str_errors[tv->v_type])); + emsgf(_(str_errors[tv->v_type])); return false; } } @@ -2350,7 +2350,7 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error) case VAR_LIST: case VAR_DICT: case VAR_FLOAT: { - EMSG(_(num_errors[tv->v_type])); + emsgf(_(num_errors[tv->v_type])); break; } case VAR_NUMBER: { @@ -2378,7 +2378,7 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error) break; } case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "tv_get_number(UNKNOWN)"); + emsgf(_(e_intern2), "tv_get_number(UNKNOWN)"); break; } } @@ -2428,27 +2428,27 @@ float_T tv_get_float(const typval_T *const tv) } case VAR_PARTIAL: case VAR_FUNC: { - EMSG(_("E891: Using a Funcref as a Float")); + emsgf(_("E891: Using a Funcref as a Float")); break; } case VAR_STRING: { - EMSG(_("E892: Using a String as a Float")); + emsgf(_("E892: Using a String as a Float")); break; } case VAR_LIST: { - EMSG(_("E893: Using a List as a Float")); + emsgf(_("E893: Using a List as a Float")); break; } case VAR_DICT: { - EMSG(_("E894: Using a Dictionary as a Float")); + emsgf(_("E894: Using a Dictionary as a Float")); break; } case VAR_SPECIAL: { - EMSG(_("E907: Using a special value as a Float")); + emsgf(_("E907: Using a special value as a Float")); break; } case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "get_tv_float(UNKNOWN)"); + emsgf(_(e_intern2), "get_tv_float(UNKNOWN)"); break; } } @@ -2490,7 +2490,7 @@ const char *tv_get_string_buf_chk(const typval_T *const tv, char *const buf) case VAR_DICT: case VAR_FLOAT: case VAR_UNKNOWN: { - EMSG(_(str_errors[tv->v_type])); + emsgf(_(str_errors[tv->v_type])); return false; } } From bc87d23c28c10c70b4addaf18ae16bcbe0682c8a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 12 Mar 2017 21:58:57 +0300 Subject: [PATCH 0238/1671] unittests: Add tests for dictionary indexing --- src/nvim/vim.h | 2 +- test/unit/eval/helpers.lua | 1 + test/unit/eval/typval_spec.lua | 281 ++++++++++++++++++++++++++++----- test/unit/helpers.lua | 23 +++ 4 files changed, 263 insertions(+), 44 deletions(-) diff --git a/src/nvim/vim.h b/src/nvim/vim.h index e16ee00309..cc0587fb88 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -30,7 +30,7 @@ Error: configure did not run properly.Check auto/config.log. #include "nvim/os/os_defs.h" /* bring lots of system header files */ /// length of a buffer to store a number in ASCII (64 bits binary + NUL) -#define NUMBUFLEN 65 +enum { NUMBUFLEN = 65 }; // flags for vim_str2nr() #define STR2NR_BIN 1 diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index 8faa8dbf5c..6909953022 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -538,6 +538,7 @@ return { dict_watchers=dict_watchers, tbl2callback=tbl2callback, + callback2tbl=callback2tbl, empty_list = {[type_key]=list_type}, } diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 2f01e9634e..d91fd3e78d 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -20,26 +20,26 @@ local dict = eval_helpers.dict local lst2tbl = eval_helpers.lst2tbl local dct2tbl = eval_helpers.dct2tbl local typvalt = eval_helpers.typvalt -local type_key = eval_helpers.type_key -local li_alloc = eval_helpers.li_alloc -local int_type = eval_helpers.int_type -local first_di = eval_helpers.first_di -local func_type = eval_helpers.func_type -local null_list = eval_helpers.null_list -local null_dict = eval_helpers.null_dict -local empty_list = eval_helpers.empty_list -local lua2typvalt = eval_helpers.lua2typvalt -local typvalt2lua = eval_helpers.typvalt2lua -local null_string = eval_helpers.null_string +local type_key = eval_helpers.type_key +local li_alloc = eval_helpers.li_alloc +local first_di = eval_helpers.first_di +local func_type = eval_helpers.func_type +local null_list = eval_helpers.null_list +local null_dict = eval_helpers.null_dict +local dict_items = eval_helpers.dict_items +local empty_list = eval_helpers.empty_list +local lua2typvalt = eval_helpers.lua2typvalt +local typvalt2lua = eval_helpers.typvalt2lua +local null_string = eval_helpers.null_string +local callback2tbl = eval_helpers.callback2tbl local tbl2callback = eval_helpers.tbl2callback local dict_watchers = eval_helpers.dict_watchers -local uname = global_helpers.uname local concat_tables = global_helpers.concat_tables local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h', './src/nvim/mbyte.h', './src/nvim/garray.h', - './src/nvim/eval.h') + './src/nvim/eval.h', './src/nvim/vim.h') local function list_items(l) local lis = {} @@ -79,30 +79,6 @@ before_each(function() alloc_log:before_each() end) -local function clear_tmp_allocs() - local toremove = {} - local allocs = {} - for i, v in ipairs(alloc_log.log) do - if v.func == 'malloc' or v.func == 'calloc' then - allocs[tostring(v.ret)] = i - elseif v.func == 'realloc' or v.func == 'free' then - if allocs[tostring(v.args[1])] then - toremove[#toremove + 1] = allocs[tostring(v.args[1])] - if v.func == 'free' then - toremove[#toremove + 1] = i - end - end - if v.func == 'realloc' then - allocs[tostring(v.ret)] = i - end - end - end - table.sort(toremove) - for i = #toremove,1,-1 do - table.remove(alloc_log.log, toremove[i]) - end -end - after_each(function() alloc_log:after_each() end) @@ -116,12 +92,19 @@ end local function check_emsg(f, msg) local saved_last_msg_hist = lib.last_msg_hist + if saved_last_msg_hist == nil then + saved_last_msg_hist = nil + end local ret = {f()} if msg ~= nil then - neq(saved_last_msg_hist, lib.last_msg_hist) eq(msg, ffi.string(lib.last_msg_hist.msg)) + neq(saved_last_msg_hist, lib.last_msg_hist) else - eq(saved_last_msg_hist, lib.last_msg_hist) + if saved_last_msg_hist ~= lib.last_msg_hist then + eq(nil, ffi.string(lib.last_msg_hist.msg)) + else + eq(saved_last_msg_hist, lib.last_msg_hist) + end end return unpack(ret) end @@ -667,8 +650,7 @@ describe('typval.c', function() a.li(l.lv_last), }) - eq({{[type_key]=int_type, value=-100500}, - {[type_key]=int_type, value=100500}}, typvalt2lua(l_tv)) + eq({int(-100500), int(100500)}, typvalt2lua(l_tv)) end) end) end) @@ -769,7 +751,7 @@ describe('typval.c', function() eq({{['\171']='\187'}, {'\191'}, 1, '\191', null_string, null_list, null_dict}, lst2tbl(l_deepcopy1)) local di_deepcopy1 = first_di(lis_deepcopy1[1].li_tv.vval.v_dict) - clear_tmp_allocs() + alloc_log:clear_tmp_allocs() alloc_log:check({ a.list(l_deepcopy1), a.li(lis_deepcopy1[1]), @@ -1570,9 +1552,222 @@ describe('typval.c', function() end) end) describe('indexing', function() - describe('find', function() + describe('find()', function() + local function tv_dict_find(d, key, key_len) + local di = lib.tv_dict_find(d, key, key_len or #key) + if di == nil then + return nil, nil, nil + end + return typvalt2lua(di.di_tv), ffi.string(di.di_key), di + end itp('works with NULL dict', function() eq(nil, lib.tv_dict_find(nil, '', 0)) + eq(nil, lib.tv_dict_find(nil, 'test', -1)) + eq(nil, lib.tv_dict_find(nil, nil, 0)) + end) + itp('works with NULL key', function() + local lua_d = { + ['']=0, + t=1, + te=2, + tes=3, + test=4, + testt=5, + } + local d = dict(lua_d) + alloc_log:clear() + eq(lua_d, dct2tbl(d)) + alloc_log:check({}) + local dis = dict_items(d) + eq({0, '', dis['']}, {tv_dict_find(d, '', 0)}) + eq({0, '', dis['']}, {tv_dict_find(d, nil, 0)}) + end) + itp('works with len properly', function() + local lua_d = { + ['']=0, + t=1, + te=2, + tes=3, + test=4, + testt=5, + } + local d = dict(lua_d) + alloc_log:clear() + eq(lua_d, dct2tbl(d)) + alloc_log:check({}) + for i = 0, 5 do + local v, k = tv_dict_find(d, 'testt', i) + eq({i, ('testt'):sub(1, i)}, {v, k}) + end + eq(nil, tv_dict_find(d, 'testt', 6)) -- Should take NUL byte + eq(5, tv_dict_find(d, 'testt', -1)) + alloc_log:check({}) + end) + end) + describe('get_number()', function() + itp('works with NULL dict', function() + eq(0, check_emsg(function() return lib.tv_dict_get_number(nil, 'test') end, + nil)) + end) + itp('works', function() + local d = ffi.gc(dict({test={}}), nil) + eq(0, check_emsg(function() return lib.tv_dict_get_number(d, 'test') end, + 'E728: Using a Dictionary as a Number')) + d = ffi.gc(dict({tes=int(42), t=44, te='43'}), nil) + alloc_log:clear() + eq(0, check_emsg(function() return lib.tv_dict_get_number(d, 'test') end, + nil)) + eq(42, check_emsg(function() return lib.tv_dict_get_number(d, 'tes') end, + nil)) + eq(43, check_emsg(function() return lib.tv_dict_get_number(d, 'te') end, + nil)) + alloc_log:check({}) + eq(0, check_emsg(function() return lib.tv_dict_get_number(d, 't') end, + 'E805: Using a Float as a Number')) + end) + end) + describe('get_string()', function() + itp('works with NULL dict', function() + eq(nil, check_emsg(function() return lib.tv_dict_get_string(nil, 'test', false) end, + nil)) + end) + itp('works', function() + local d = ffi.gc(dict({test={}}), nil) + eq('', ffi.string(check_emsg(function() return lib.tv_dict_get_string(d, 'test', false) end, + 'E731: using Dictionary as a String'))) + d = ffi.gc(dict({tes=int(42), t=44, te='43', xx=int(45)}), nil) + alloc_log:clear() + local dis = dict_items(d) + eq(nil, check_emsg(function() return lib.tv_dict_get_string(d, 'test', false) end, + nil)) + local s42 = check_emsg(function() return lib.tv_dict_get_string(d, 'tes', false) end, + nil) + eq('42', ffi.string(s42)) + local s45 = check_emsg(function() return lib.tv_dict_get_string(d, 'xx', false) end, + nil) + eq(s42, s45) + eq('45', ffi.string(s45)) + eq('45', ffi.string(s42)) + local s43 = check_emsg(function() return lib.tv_dict_get_string(d, 'te', false) end, + nil) + eq('43', ffi.string(s43)) + neq(s42, s43) + eq(s43, dis.te.di_tv.vval.v_string) + alloc_log:check({}) + eq('', ffi.string(check_emsg(function() return lib.tv_dict_get_string(d, 't', false) end, + 'E806: using Float as a String'))) + end) + itp('allocates a string copy when requested', function() + local function tv_dict_get_string_alloc(d, key, emsg) + alloc_log:clear() + local ret = check_emsg(function() return lib.tv_dict_get_string(d, key, true) end, + emsg) + local s_ret = (ret ~= nil) and ffi.string(ret) or nil + if not emsg then + if s_ret then + alloc_log:check({a.str(ret, s_ret)}) + else + alloc_log:check({}) + end + end + lib.xfree(ret) + return s_ret + end + local d = ffi.gc(dict({test={}}), nil) + eq('', tv_dict_get_string_alloc(d, 'test', 'E731: using Dictionary as a String')) + d = ffi.gc(dict({tes=int(42), t=44, te='43', xx=int(45)}), nil) + alloc_log:clear() + eq(nil, tv_dict_get_string_alloc(d, 'test')) + eq('42', tv_dict_get_string_alloc(d, 'tes')) + eq('45', tv_dict_get_string_alloc(d, 'xx')) + eq('43', tv_dict_get_string_alloc(d, 'te')) + eq('', tv_dict_get_string_alloc(d, 't', 'E806: using Float as a String')) + end) + end) + describe('get_string_buf()', function() + local function tv_dict_get_string_buf(d, key, buf, emsg) + buf = buf or ffi.gc(lib.xmalloc(lib.NUMBUFLEN), lib.xfree) + alloc_log:clear() + local ret = check_emsg(function() return lib.tv_dict_get_string_buf(d, key, buf) end, + emsg) + local s_ret = (ret ~= nil) and ffi.string(ret) or nil + if not emsg then + alloc_log:check({}) + end + return s_ret, ret, buf + end + itp('works with NULL dict', function() + eq(nil, tv_dict_get_string_buf(nil, 'test')) + end) + itp('works', function() + local lua_d = { + ['']={}, + t=1, + te=int(2), + tes=empty_list, + test='tset', + testt=5, + } + local d = dict(lua_d) + alloc_log:clear() + eq(lua_d, dct2tbl(d)) + alloc_log:check({}) + local s, r, b + s, r, b = tv_dict_get_string_buf(d, 'test') + neq(r, b) + eq('tset', s) + s, r, b = tv_dict_get_string_buf(d, 't', nil, 'E806: using Float as a String') + neq(r, b) + eq('', s) + s, r, b = tv_dict_get_string_buf(d, 'te') + eq(r, b) + eq('2', s) + end) + end) + describe('get_callback()', function() + local function tv_dict_get_callback(d, key, key_len, emsg) + key_len = key_len or #key + local cb = ffi.gc(ffi.cast('Callback*', lib.xmalloc(ffi.sizeof('Callback'))), lib.callback_free) + alloc_log:clear() + local ret = check_emsg(function() + return lib.tv_dict_get_callback(d, key, key_len, cb) + end, emsg) + local cb_lua = callback2tbl(cb[0]) + return cb_lua, ret + end + itp('works with NULL dict', function() + eq({{type='none'}, true}, + {tv_dict_get_callback(nil, nil, 0)}) + end) + itp('works', function() + local lua_d = { + ['']='tr', + t=int(1), + te={[type_key]=func_type, value='tr'}, + tes={[type_key]=func_type, value='tr', args={'a', 'b'}}, + test={[type_key]=func_type, value='Test', dict={test=1}, args={}}, + testt={[type_key]=func_type, value='Test', dict={test=1}, args={1}}, + } + local d = dict(lua_d) + eq(lua_d, dct2tbl(d)) + eq({{type='fref', fref='tr'}, true}, + {tv_dict_get_callback(d, nil, 0)}) + eq({{type='fref', fref='tr'}, true}, + {tv_dict_get_callback(d, '', -1)}) + eq({{type='none'}, true}, + {tv_dict_get_callback(d, 'x', -1)}) + eq({{type='fref', fref='tr'}, true}, + {tv_dict_get_callback(d, 'testt', 0)}) + eq({{type='none'}, false}, + {tv_dict_get_callback(d, 'test', 1, 'E6000: Argument is not a function or function name')}) + eq({{type='fref', fref='tr'}, true}, + {tv_dict_get_callback(d, 'testt', 2)}) + eq({{ type='pt', fref='tr', pt={ [type_key]=func_type, value='tr', args={ 'a', 'b' } } }, true}, + {tv_dict_get_callback(d, 'testt', 3)}) + eq({{ type='pt', fref='Test', pt={ [type_key]=func_type, value='Test', dict={ test=1 }, args={} } }, true}, + {tv_dict_get_callback(d, 'testt', 4)}) + eq({{ type='pt', fref='Test', pt={ [type_key]=func_type, value='Test', dict={ test=1 }, args={1} } }, true}, + {tv_dict_get_callback(d, 'testt', 5)}) end) end) end) diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 612b337ee7..8aad3acd98 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -314,6 +314,29 @@ local function alloc_log_new() eq(exp, self.log) self:clear() end + function log:clear_tmp_allocs() + local toremove = {} + local allocs = {} + for i, v in ipairs(self.log) do + if v.func == 'malloc' or v.func == 'calloc' then + allocs[tostring(v.ret)] = i + elseif v.func == 'realloc' or v.func == 'free' then + if allocs[tostring(v.args[1])] then + toremove[#toremove + 1] = allocs[tostring(v.args[1])] + if v.func == 'free' then + toremove[#toremove + 1] = i + end + end + if v.func == 'realloc' then + allocs[tostring(v.ret)] = i + end + end + end + table.sort(toremove) + for i = #toremove,1,-1 do + table.remove(self.log, toremove[i]) + end + end function log:restore_original_functions() -- Do nothing: set mocks live in a separate process return From 270a3889af024485fa7b63f34c4dd3f92f6e0f98 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 01:45:44 +0300 Subject: [PATCH 0239/1671] unittests: Add tv_dict_add* unit tests Also fixes incorrect location of `tv_dict_add` function and three bugs in other functions: 1. `tv_dict_add_list` may free list it does not own (vim/vim#1555). 2. `tv_dict_add_dict` may free dictionary it does not own (vim/vim#1555). 3. `tv_dict_add_dict` ignores `key_len` argument. --- src/nvim/eval/typval.c | 32 ++++----- test/unit/eval/typval_spec.lua | 116 +++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 16 deletions(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 867330c137..d9feb2d88e 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1009,18 +1009,6 @@ void tv_dict_item_free(dictitem_T *const item) } } -/// Add item to dictionary -/// -/// @param[out] d Dictionary to add to. -/// @param[in] item Item to add. -/// -/// @return FAIL if key already exists. -int tv_dict_add(dict_T *const d, dictitem_T *const item) - FUNC_ATTR_NONNULL_ALL -{ - return hash_add(&d->dv_hashtab, item->di_key); -} - /// Make a copy of a dictionary item /// /// @param[in] di Item to copy. @@ -1278,6 +1266,18 @@ bool tv_dict_get_callback(dict_T *const d, //{{{2 dict_add* +/// Add item to dictionary +/// +/// @param[out] d Dictionary to add to. +/// @param[in] item Item to add. +/// +/// @return FAIL if key already exists. +int tv_dict_add(dict_T *const d, dictitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + return hash_add(&d->dv_hashtab, item->di_key); +} + /// Add a list entry to dictionary /// /// @param[out] d Dictionary to add entry to. @@ -1295,11 +1295,11 @@ int tv_dict_add_list(dict_T *const d, const char *const key, item->di_tv.v_lock = VAR_UNLOCKED; item->di_tv.v_type = VAR_LIST; item->di_tv.vval.v_list = list; + list->lv_refcount++; if (tv_dict_add(d, item) == FAIL) { tv_dict_item_free(item); return FAIL; } - list->lv_refcount++; return OK; } @@ -1313,18 +1313,18 @@ int tv_dict_add_list(dict_T *const d, const char *const key, /// @return OK in case of success, FAIL when key already exists. int tv_dict_add_dict(dict_T *const d, const char *const key, const size_t key_len, dict_T *const dict) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ALL { - dictitem_T *const item = tv_dict_item_alloc(key); + dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); item->di_tv.v_lock = VAR_UNLOCKED; item->di_tv.v_type = VAR_DICT; item->di_tv.vval.v_dict = dict; + dict->dv_refcount++; if (tv_dict_add(d, item) == FAIL) { tv_dict_item_free(item); return FAIL; } - dict->dv_refcount++; return OK; } diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index d91fd3e78d..c710171779 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1770,6 +1770,122 @@ describe('typval.c', function() {tv_dict_get_callback(d, 'testt', 5)}) end) end) + describe('dict_add()', function() + itp('works', function() + local di = lib.tv_dict_item_alloc_len('t-est', 5) + alloc_log:check({a.di(di, 't-est')}) + di.di_tv.v_type = lib.VAR_NUMBER + di.di_tv.vval.v_number = 42 + local d = dict({test=10}) + local dis = dict_items(d) + alloc_log:check({ + a.dict(d), + a.di(dis.test, 'test') + }) + eq({test=10}, dct2tbl(d)) + alloc_log:clear() + eq(OK, lib.tv_dict_add(d, di)) + alloc_log:check({}) + eq({test=10, ['t-est']=int(42)}, dct2tbl(d)) + eq(FAIL, check_emsg(function() return lib.tv_dict_add(d, di) end, + 'E685: Internal error: hash_add()')) + end) + end) + describe('dict_add_list()', function() + itp('works', function() + local l = list(1, 2, 3) + alloc_log:clear() + eq(1, l.lv_refcount) + local d = dict({test=10}) + alloc_log:clear() + eq({test=10}, dct2tbl(d)) + eq(OK, lib.tv_dict_add_list(d, 'testt', 3, l)) + local dis = dict_items(d) + alloc_log:check({a.di(dis.tes, 'tes')}) + eq({test=10, tes={1, 2, 3}}, dct2tbl(d)) + eq(2, l.lv_refcount) + eq(FAIL, check_emsg(function() return lib.tv_dict_add_list(d, 'testt', 3, l) end, + 'E685: Internal error: hash_add()')) + eq(2, l.lv_refcount) + alloc_log:clear() + lib.emsg_skip = lib.emsg_skip + 1 + eq(FAIL, check_emsg(function() return lib.tv_dict_add_list(d, 'testt', 3, l) end, + nil)) + eq(2, l.lv_refcount) + lib.emsg_skip = lib.emsg_skip - 1 + alloc_log:clear_tmp_allocs() + alloc_log:check({}) + end) + end) + describe('dict_add_dict()', function() + itp('works', function() + local d2 = dict({foo=42}) + alloc_log:clear() + eq(1, d2.dv_refcount) + local d = dict({test=10}) + alloc_log:clear() + eq({test=10}, dct2tbl(d)) + eq(OK, lib.tv_dict_add_dict(d, 'testt', 3, d2)) + local dis = dict_items(d) + alloc_log:check({a.di(dis.tes, 'tes')}) + eq({test=10, tes={foo=42}}, dct2tbl(d)) + eq(2, d2.dv_refcount) + eq(FAIL, check_emsg(function() return lib.tv_dict_add_dict(d, 'testt', 3, d2) end, + 'E685: Internal error: hash_add()')) + eq(2, d2.dv_refcount) + alloc_log:clear() + lib.emsg_skip = lib.emsg_skip + 1 + eq(FAIL, check_emsg(function() return lib.tv_dict_add_dict(d, 'testt', 3, d2) end, + nil)) + eq(2, d2.dv_refcount) + lib.emsg_skip = lib.emsg_skip - 1 + alloc_log:clear_tmp_allocs() + alloc_log:check({}) + end) + end) + describe('dict_add_nr()', function() + itp('works', function() + local d = dict({test=10}) + alloc_log:clear() + eq({test=10}, dct2tbl(d)) + eq(OK, lib.tv_dict_add_nr(d, 'testt', 3, 2)) + local dis = dict_items(d) + alloc_log:check({a.di(dis.tes, 'tes')}) + eq({test=10, tes=int(2)}, dct2tbl(d)) + eq(FAIL, check_emsg(function() return lib.tv_dict_add_nr(d, 'testt', 3, 2) end, + 'E685: Internal error: hash_add()')) + alloc_log:clear() + lib.emsg_skip = lib.emsg_skip + 1 + eq(FAIL, check_emsg(function() return lib.tv_dict_add_nr(d, 'testt', 3, 2) end, + nil)) + lib.emsg_skip = lib.emsg_skip - 1 + alloc_log:clear_tmp_allocs() + alloc_log:check({}) + end) + end) + describe('dict_add_str()', function() + itp('works', function() + local d = dict({test=10}) + alloc_log:clear() + eq({test=10}, dct2tbl(d)) + eq(OK, lib.tv_dict_add_str(d, 'testt', 3, 'TEST')) + local dis = dict_items(d) + alloc_log:check({ + a.di(dis.tes, 'tes'), + a.str(dis.tes.di_tv.vval.v_string, 'TEST') + }) + eq({test=10, tes='TEST'}, dct2tbl(d)) + eq(FAIL, check_emsg(function() return lib.tv_dict_add_str(d, 'testt', 3, 'TEST') end, + 'E685: Internal error: hash_add()')) + alloc_log:clear() + lib.emsg_skip = lib.emsg_skip + 1 + eq(FAIL, check_emsg(function() return lib.tv_dict_add_str(d, 'testt', 3, 'TEST') end, + nil)) + lib.emsg_skip = lib.emsg_skip - 1 + alloc_log:clear_tmp_allocs() + alloc_log:check({}) + end) + end) end) end) end) From 43e9fad1c8835a3136f4db53c82608e034df2a5e Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 13:43:01 +0300 Subject: [PATCH 0240/1671] eval: Use tv_is_func in place of ==VAR_FUNC||==VAR_PARTIAL Also fixes same error as in vim/vim#1557 --- src/nvim/eval.c | 25 ++++++++----------------- src/nvim/eval/typval.c | 9 +++------ src/nvim/eval/typval.h | 15 +++++++++++++++ 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 88ba0b95cc..cab22c599a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2225,7 +2225,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, prevval = 0; // Avoid compiler warning. } wrong = ((lp->ll_dict->dv_scope == VAR_DEF_SCOPE - && rettv->v_type == VAR_FUNC + && tv_is_func(*rettv) && !var_check_func_name((const char *)key, lp->ll_di == NULL)) || !valid_varname((const char *)key)); if (len != -1) { @@ -3643,9 +3643,7 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) n1 = !n1; } } - } else if (rettv->v_type == VAR_FUNC || var2.v_type == VAR_FUNC - || rettv->v_type == VAR_PARTIAL - || var2.v_type == VAR_PARTIAL) { + } else if (tv_is_func(*rettv) || tv_is_func(var2)) { if (type != TYPE_EQUAL && type != TYPE_NEQUAL) { EMSG(_("E694: Invalid operation for Funcrefs")); tv_clear(rettv); @@ -8957,8 +8955,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv = &di->di_tv; } } - } else if (argvars[0].v_type == VAR_PARTIAL - || argvars[0].v_type == VAR_FUNC) { + } else if (tv_is_func(argvars[0])) { partial_T *pt; partial_T fref_pt; @@ -15994,7 +15991,7 @@ static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *const flg = tv_get_string_buf_chk(&argvars[3], flagsbuf); typval_T *expr = NULL; - if (argvars[2].v_type == VAR_FUNC || argvars[2].v_type == VAR_PARTIAL) { + if (tv_is_func(argvars[2])) { expr = &argvars[2]; } else { sub = tv_get_string_buf_chk(&argvars[2], subbuf); @@ -18229,8 +18226,7 @@ handle_subscript( while (ret == OK && (**arg == '[' || (**arg == '.' && rettv->v_type == VAR_DICT) - || (**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC - || rettv->v_type == VAR_PARTIAL))) + || (**arg == '(' && (!evaluate || tv_is_func(*rettv)))) && !ascii_iswhite(*(*arg - 1))) { if (**arg == '(') { partial_T *pt = NULL; @@ -18286,9 +18282,7 @@ handle_subscript( } // Turn "dict.Func" into a partial for "Func" bound to "dict". - if (selfdict != NULL - && (rettv->v_type == VAR_FUNC - || rettv->v_type == VAR_PARTIAL)) { + if (selfdict != NULL && tv_is_func(*rettv)) { set_selfdict(rettv, selfdict); } @@ -18794,8 +18788,7 @@ static void set_var(const char *name, const size_t name_len, typval_T *const tv, v = find_var_in_scoped_ht((const char *)name, name_len, true); } - if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL) - && !var_check_func_name(name, v == NULL)) { + if (tv_is_func(*tv) && !var_check_func_name(name, v == NULL)) { return; } @@ -19463,9 +19456,7 @@ void ex_function(exarg_T *eap) arg = name; else arg = fudi.fd_newkey; - if (arg != NULL && (fudi.fd_di == NULL - || (fudi.fd_di->di_tv.v_type != VAR_FUNC - && fudi.fd_di->di_tv.v_type != VAR_PARTIAL))) { + if (arg != NULL && (fudi.fd_di == NULL || !tv_is_func(fudi.fd_di->di_tv))) { int j = (*arg == K_SPECIAL) ? 3 : 0; while (arg[j] != NUL && (j == 0 ? eval_isnamec1(arg[j]) : eval_isnamec(arg[j]))) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index d9feb2d88e..620da0032e 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1250,8 +1250,7 @@ bool tv_dict_get_callback(dict_T *const d, return true; } - if (di->di_tv.v_type != VAR_FUNC && di->di_tv.v_type != VAR_STRING - && di->di_tv.v_type != VAR_PARTIAL) { + if (!tv_is_func(di->di_tv) && di->di_tv.v_type != VAR_STRING) { emsgf(_("E6000: Argument is not a function or function name")); return false; } @@ -1418,7 +1417,7 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, // Disallow replacing a builtin function in l: and g:. // Check the key to be valid when adding to any scope. if (d1->dv_scope == VAR_DEF_SCOPE - && di2->di_tv.v_type == VAR_FUNC + && tv_is_func(di2->di_tv) && !var_check_func_name((const char *)di2->di_key, di1 == NULL)) { break; } @@ -2101,9 +2100,7 @@ bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic, // TODO(ZyX-I): Make this not recursive static int recursive_cnt = 0; // Catch recursive loops. - if (!((tv1->v_type == VAR_FUNC || tv1->v_type == VAR_PARTIAL) - && (tv2->v_type == VAR_FUNC || tv2->v_type == VAR_PARTIAL)) - && tv1->v_type != tv2->v_type) { + if (!(tv_is_func(*tv1) && tv_is_func(*tv2)) && tv1->v_type != tv2->v_type) { return false; } diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index fa0105197f..7eab22bc12 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -408,6 +408,21 @@ static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) return QUEUE_DATA(q, DictWatcher, node); } +static inline bool tv_is_func(const typval_T tv) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_CONST; + +/// Check whether given typval_T contains a function +/// +/// That is, whether it contains VAR_FUNC or VAR_PARTIAL. +/// +/// @param[in] tv Typval to check. +/// +/// @return True if it is a function or a partial, false otherwise. +static inline bool tv_is_func(const typval_T tv) +{ + return tv.v_type == VAR_FUNC || tv.v_type == VAR_PARTIAL; +} + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/typval.h.generated.h" #endif From 4987850cacc9f0c5674bc4cb8197a6fa0a343167 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 13:43:25 +0300 Subject: [PATCH 0241/1671] unittests: Add tv_dict_clear tests --- test/unit/eval/typval_spec.lua | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index c710171779..b16e118053 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1887,5 +1887,23 @@ describe('typval.c', function() end) end) end) + describe('clear()', function() + itp('works', function() + local d = dict() + alloc_log:check({a.dict(d)}) + eq({}, dct2tbl(d)) + lib.tv_dict_clear(d) + eq({}, dct2tbl(d)) + lib.tv_dict_add_str(d, 'TEST', 3, 'tEsT') + local dis = dict_items(d) + local di = dis.TES + local di_s = di.di_tv.vval.v_string + alloc_log:check({a.di(di), a.str(di_s)}) + eq({TES='tEsT'}, dct2tbl(d)) + lib.tv_dict_clear(d) + alloc_log:check({a.freed(di_s), a.freed(di)}) + eq({}, dct2tbl(d)) + end) + end) end) end) From fa852e7cdc365b6fcd39d677f4067963274c44c3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 14:17:35 +0300 Subject: [PATCH 0242/1671] eval: Fix extend() behaviour with NULL lists and dictionaries Ref #4615 Ref vim/vim#768 --- src/nvim/eval.c | 34 ++++--- test/functional/eval/null_spec.lua | 147 ++++++++++++++--------------- 2 files changed, 94 insertions(+), 87 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index cab22c599a..9950f8b196 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8155,15 +8155,20 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) const size_t arg_errmsg_len = strlen(arg_errmsg); if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) { - list_T *l1, *l2; - listitem_T *item; long before; bool error = false; - l1 = argvars[0].vval.v_list; - l2 = argvars[1].vval.v_list; - if (l1 != NULL && !tv_check_lock(l1->lv_lock, arg_errmsg, arg_errmsg_len) - && l2 != NULL) { + list_T *const l1 = argvars[0].vval.v_list; + list_T *const l2 = argvars[1].vval.v_list; + if (l1 == NULL) { + const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, arg_errmsg_len); + (void)locked; + assert(locked == true); + } else if (l2 == NULL) { + // Do nothing + tv_copy(&argvars[0], rettv); + } else if (!tv_check_lock(l1->lv_lock, arg_errmsg, arg_errmsg_len)) { + listitem_T *item; if (argvars[2].v_type != VAR_UNKNOWN) { before = tv_get_number_chk(&argvars[2], &error); if (error) { @@ -8187,13 +8192,16 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT) { - dict_T *d1; - dict_T *d2; - - d1 = argvars[0].vval.v_dict; - d2 = argvars[1].vval.v_dict; - if (d1 != NULL && !tv_check_lock(d1->dv_lock, arg_errmsg, arg_errmsg_len) - && d2 != NULL) { + dict_T *const d1 = argvars[0].vval.v_dict; + dict_T *const d2 = argvars[1].vval.v_dict; + if (d1 == NULL) { + const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, arg_errmsg_len); + (void)locked; + assert(locked == true); + } else if (d2 == NULL) { + // Do nothing + tv_copy(&argvars[0], rettv); + } else if (!tv_check_lock(d1->dv_lock, arg_errmsg, arg_errmsg_len)) { const char *action = "force"; // Check the third argument. if (argvars[2].v_type != VAR_UNKNOWN) { diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index 3b361ceeda..6fd30caec9 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -20,120 +20,119 @@ describe('NULL', function() after_each(function() os.remove(tmpfname) end) + local null_test = function(name, cmd, err) + it(name, function() + eq(err, exc_exec(cmd)) + end) + end + local null_expr_test = function(name, expr, err, val, after) + it(name, function() + eq((err == 0) and ('') or ('\n' .. err), + redir_exec('let g:_var = ' .. expr)) + if val == nil then + eq(0, funcs.exists('g:_var')) + else + eq(val, meths.get_var('_var')) + end + if after ~= nil then + after() + end + end) + end describe('list', function() - local null_list_test = function(name, cmd, err) - it(name, function() - eq(err, exc_exec(cmd)) - end) - end - local null_list_expr_test = function(name, expr, err, val, after) - it(name, function() - eq((err == 0) and ('') or ('\n' .. err), - redir_exec('let g:_var = ' .. expr)) - if val == nil then - eq(0, funcs.exists('g:_var')) - else - eq(val, meths.get_var('_var')) - end - if after ~= nil then - after() - end - end) - end - -- Incorrect behaviour -- FIXME map() should not return 0 without error - null_list_expr_test('does not crash map()', 'map(L, "v:val")', 0, 0) + null_expr_test('does not crash map()', 'map(L, "v:val")', 0, 0) -- FIXME map() should not return 0 without error - null_list_expr_test('does not crash filter()', 'filter(L, "1")', 0, 0) + null_expr_test('does not crash filter()', 'filter(L, "1")', 0, 0) -- FIXME map() should at least return L - null_list_expr_test('makes map() return v:_null_list', 'map(L, "v:val") is# L', 0, 0) + null_expr_test('makes map() return v:_null_list', 'map(L, "v:val") is# L', 0, 0) -- FIXME filter() should at least return L - null_list_expr_test('makes filter() return v:_null_list', 'map(L, "1") is# L', 0, 0) + null_expr_test('makes filter() return v:_null_list', 'map(L, "1") is# L', 0, 0) -- FIXME add() should not return 1 at all - null_list_expr_test('does not crash add()', 'add(L, 0)', 0, 1) - -- FIXME extend() should not return 0 without error - null_list_expr_test('does not crash extend()', 'extend(L, [1])', 0, 0) - -- FIXME extend() should not return 0 at all - null_list_expr_test('does not crash extend() (second position)', 'extend([1], L)', 0, 0) + null_expr_test('does not crash add()', 'add(L, 0)', 0, 1) + null_expr_test('does not crash extend()', 'extend(L, [1])', 'E742: Cannot change value of extend() argument', 0) + null_expr_test('does not crash extend() (second position)', 'extend([1], L)', 0, {1}) -- FIXME should be accepted by inputlist() - null_list_expr_test('is accepted as an empty list by inputlist()', + null_expr_test('is accepted as an empty list by inputlist()', '[feedkeys("\\n"), inputlist(L)]', 'E686: Argument of inputlist() must be a List', {0, 0}) -- FIXME should be accepted by writefile(), return {0, {}} - null_list_expr_test('is accepted as an empty list by writefile()', + null_expr_test('is accepted as an empty list by writefile()', ('[writefile(L, "%s"), readfile("%s")]'):format(tmpfname, tmpfname), 'E484: Can\'t open file ' .. tmpfname, {0, {}}) -- FIXME should give error message - null_list_expr_test('does not crash remove()', 'remove(L, 0)', 0, 0) + null_expr_test('does not crash remove()', 'remove(L, 0)', 0, 0) -- FIXME should return 0 - null_list_expr_test('is accepted by setqflist()', 'setqflist(L)', 0, -1) + null_expr_test('is accepted by setqflist()', 'setqflist(L)', 0, -1) -- FIXME should return 0 - null_list_expr_test('is accepted by setloclist()', 'setloclist(1, L)', 0, -1) + null_expr_test('is accepted by setloclist()', 'setloclist(1, L)', 0, -1) -- FIXME should return 0 - null_list_expr_test('is accepted by setmatches()', 'setmatches(L)', 0, -1) + null_expr_test('is accepted by setmatches()', 'setmatches(L)', 0, -1) -- FIXME should return empty list or error out - null_list_expr_test('is accepted by sort()', 'sort(L)', 0, 0) + null_expr_test('is accepted by sort()', 'sort(L)', 0, 0) -- FIXME Should return 1 - null_list_expr_test('is accepted by sort()', 'sort(L) is L', 0, 0) + null_expr_test('is accepted by sort()', 'sort(L) is L', 0, 0) -- FIXME should not error out - null_list_test('is accepted by :cexpr', 'cexpr L', 'Vim(cexpr):E777: String or List expected') + null_test('is accepted by :cexpr', 'cexpr L', 'Vim(cexpr):E777: String or List expected') -- FIXME should not error out - null_list_test('is accepted by :lexpr', 'lexpr L', 'Vim(lexpr):E777: String or List expected') - null_list_test('is accepted by :for', 'for x in L|throw x|endfor', 0) + null_test('is accepted by :lexpr', 'lexpr L', 'Vim(lexpr):E777: String or List expected') + null_test('is accepted by :for', 'for x in L|throw x|endfor', 0) -- Subjectable behaviour -- FIXME Should return 1 - null_list_expr_test('is equal to empty list', 'L == []', 0, 0) + null_expr_test('is equal to empty list', 'L == []', 0, 0) -- FIXME Should return 1 - null_list_expr_test('is equal to empty list (reverse order)', '[] == L', 0, 0) + null_expr_test('is equal to empty list (reverse order)', '[] == L', 0, 0) -- FIXME Should return 1 - null_list_expr_test('is not locked', 'islocked("v:_null_list")', 0, 0) + null_expr_test('is not locked', 'islocked("v:_null_list")', 0, 0) -- Crashes - -- null_list_expr_test('does not crash setreg', 'setreg("x", L)', 0, 0) - -- null_list_expr_test('does not crash setline', 'setline(1, L)', 0, 0) - -- null_list_expr_test('does not crash system()', 'system("cat", L)', 0, '') - -- null_list_expr_test('does not crash systemlist()', 'systemlist("cat", L)', 0, {}) + -- null_expr_test('does not crash setreg', 'setreg("x", L)', 0, 0) + -- null_expr_test('does not crash setline', 'setline(1, L)', 0, 0) + -- null_expr_test('does not crash system()', 'system("cat", L)', 0, '') + -- null_expr_test('does not crash systemlist()', 'systemlist("cat", L)', 0, {}) -- Correct behaviour - null_list_expr_test('does not crash append()', 'append(1, L)', 0, 0, function() + null_expr_test('does not crash append()', 'append(1, L)', 0, 0, function() eq({''}, curbufmeths.get_lines(0, -1, false)) end) - null_list_expr_test('is identical to itself', 'L is L', 0, 1) - null_list_expr_test('can be sliced', 'L[:]', 0, {}) - null_list_expr_test('can be copied', 'copy(L)', 0, {}) - null_list_expr_test('can be deepcopied', 'deepcopy(L)', 0, {}) - null_list_expr_test('does not crash when indexed', 'L[1]', + null_expr_test('is identical to itself', 'L is L', 0, 1) + null_expr_test('can be sliced', 'L[:]', 0, {}) + null_expr_test('can be copied', 'copy(L)', 0, {}) + null_expr_test('can be deepcopied', 'deepcopy(L)', 0, {}) + null_expr_test('does not crash when indexed', 'L[1]', 'E684: list index out of range: 1\nE15: Invalid expression: L[1]', nil) - null_list_expr_test('does not crash call()', 'call("arglistid", L)', 0, 0) - null_list_expr_test('does not crash col()', 'col(L)', 0, 0) - null_list_expr_test('does not crash virtcol()', 'virtcol(L)', 0, 0) - null_list_expr_test('does not crash line()', 'line(L)', 0, 0) - null_list_expr_test('does not crash count()', 'count(L, 1)', 0, 0) - null_list_expr_test('does not crash cursor()', 'cursor(L)', 'E474: Invalid argument', -1) - null_list_expr_test('is empty', 'empty(L)', 0, 1) - null_list_expr_test('does not crash get()', 'get(L, 1, 10)', 0, 10) - null_list_expr_test('has zero length', 'len(L)', 0, 0) - null_list_expr_test('is accepted as an empty list by max()', 'max(L)', 0, 0) - null_list_expr_test('is accepted as an empty list by min()', 'min(L)', 0, 0) - null_list_expr_test('is stringified correctly', 'string(L)', 0, '[]') - null_list_expr_test('is JSON encoded correctly', 'json_encode(L)', 0, '[]') - null_list_test('does not crash lockvar', 'lockvar! L', 0) - null_list_expr_test('can be added to itself', '(L + L)', 0, {}) - null_list_expr_test('can be added to itself', '(L + L) is L', 0, 1) - null_list_expr_test('can be added to non-empty list', '([1] + L)', 0, {1}) - null_list_expr_test('can be added to non-empty list (reversed)', '(L + [1])', 0, {1}) - null_list_expr_test('is equal to itself', 'L == L', 0, 1) - null_list_expr_test('is not not equal to itself', 'L != L', 0, 0) - null_list_expr_test('counts correctly', 'count([L], L)', 0, 1) + null_expr_test('does not crash call()', 'call("arglistid", L)', 0, 0) + null_expr_test('does not crash col()', 'col(L)', 0, 0) + null_expr_test('does not crash virtcol()', 'virtcol(L)', 0, 0) + null_expr_test('does not crash line()', 'line(L)', 0, 0) + null_expr_test('does not crash count()', 'count(L, 1)', 0, 0) + null_expr_test('does not crash cursor()', 'cursor(L)', 'E474: Invalid argument', -1) + null_expr_test('is empty', 'empty(L)', 0, 1) + null_expr_test('does not crash get()', 'get(L, 1, 10)', 0, 10) + null_expr_test('has zero length', 'len(L)', 0, 0) + null_expr_test('is accepted as an empty list by max()', 'max(L)', 0, 0) + null_expr_test('is accepted as an empty list by min()', 'min(L)', 0, 0) + null_expr_test('is stringified correctly', 'string(L)', 0, '[]') + null_expr_test('is JSON encoded correctly', 'json_encode(L)', 0, '[]') + null_test('does not crash lockvar', 'lockvar! L', 0) + null_expr_test('can be added to itself', '(L + L)', 0, {}) + null_expr_test('can be added to itself', '(L + L) is L', 0, 1) + null_expr_test('can be added to non-empty list', '([1] + L)', 0, {1}) + null_expr_test('can be added to non-empty list (reversed)', '(L + [1])', 0, {1}) + null_expr_test('is equal to itself', 'L == L', 0, 1) + null_expr_test('is not not equal to itself', 'L != L', 0, 0) + null_expr_test('counts correctly', 'count([L], L)', 0, 1) end) describe('dict', function() it('does not crash when indexing NULL dict', function() eq('\nE716: Key not present in Dictionary: test\nE15: Invalid expression: v:_null_dict.test', redir_exec('echo v:_null_dict.test')) end) + null_expr_test('makes extend error out', 'extend(D, {})', 'E742: Cannot change value of extend() argument', 0) + null_expr_test('makes extend do nothing', 'extend({1: 2}, D)', 0, {['1']=2}) end) end) From 8b9a1fbf7a630b68b1428a39f25e1fa38fe0cc9f Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 14:35:53 +0300 Subject: [PATCH 0243/1671] unittests: Add tests for tv_dict_extend --- test/unit/eval/helpers.lua | 12 +++ test/unit/eval/tricks_spec.lua | 14 +--- test/unit/eval/typval_spec.lua | 129 ++++++++++++++++++++++++++++++++- 3 files changed, 144 insertions(+), 11 deletions(-) diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index 6909953022..a3cb062b7b 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -498,6 +498,16 @@ local function dict_watchers(d) return ret, qs, key_patterns end +local function eval0(expr) + local tv = ffi.gc(ffi.new('typval_T', {v_type=eval.VAR_UNKNOWN}), + eval.tv_clear) + if eval.eval0(to_cstr(expr), tv, nil, true) == 0 then + return nil + else + return tv + end +end + return { int=int, @@ -540,5 +550,7 @@ return { tbl2callback=tbl2callback, callback2tbl=callback2tbl, + eval0=eval0, + empty_list = {[type_key]=list_type}, } diff --git a/test/unit/eval/tricks_spec.lua b/test/unit/eval/tricks_spec.lua index 54029734fb..ae569bed11 100644 --- a/test/unit/eval/tricks_spec.lua +++ b/test/unit/eval/tricks_spec.lua @@ -1,4 +1,6 @@ local helpers = require('test.unit.helpers')(after_each) +local eval_helpers = require('test.unit.eval.helpers') + local itp = helpers.gen_itp(it) local cimport = helpers.cimport @@ -6,19 +8,11 @@ local to_cstr = helpers.to_cstr local ffi = helpers.ffi local eq = helpers.eq +local eval0 = eval_helpers.eval0 + local eval = cimport('./src/nvim/eval.h', './src/nvim/eval/typval.h', './src/nvim/memory.h') -local eval0 = function(expr) - local tv = ffi.gc(ffi.new('typval_T', {v_type=eval.VAR_UNKNOWN}), - eval.tv_clear) - if eval.eval0(to_cstr(expr), tv, nil, true) == 0 then - return nil - else - return tv - end -end - describe('NULL typval_T', function() itp('is produced by $XXX_UNEXISTENT_VAR_XXX', function() -- Required for various tests which need to check whether typval_T with NULL diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index b16e118053..00dc230e89 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1,6 +1,7 @@ +local bit = require('bit') local helpers = require('test.unit.helpers')(after_each) -local global_helpers = require('test.helpers') local eval_helpers = require('test.unit.eval.helpers') +local global_helpers = require('test.helpers') local itp = helpers.gen_itp(it) @@ -17,6 +18,7 @@ local a = eval_helpers.alloc_logging_helpers local int = eval_helpers.int local list = eval_helpers.list local dict = eval_helpers.dict +local eval0 = eval_helpers.eval0 local lst2tbl = eval_helpers.lst2tbl local dct2tbl = eval_helpers.dct2tbl local typvalt = eval_helpers.typvalt @@ -1905,5 +1907,130 @@ describe('typval.c', function() eq({}, dct2tbl(d)) end) end) + describe('extend()', function() + local function tv_dict_extend(d1, d2, action, emsg) + action = action or "force" + check_emsg(function() return lib.tv_dict_extend(d1, d2, action) end, emsg) + end + itp('works', function() + local d1 = dict() + alloc_log:check({a.dict(d1)}) + eq({}, dct2tbl(d1)) + local d2 = dict() + alloc_log:check({a.dict(d2)}) + eq({}, dct2tbl(d2)) + tv_dict_extend(d1, d2, 'error') + tv_dict_extend(d1, d2, 'keep') + tv_dict_extend(d1, d2, 'force') + alloc_log:check({}) + + d1 = dict({a='TEST'}) + eq({a='TEST'}, dct2tbl(d1)) + local dis1 = dict_items(d1) + local a1_s = dis1.a.di_tv.vval.v_string + alloc_log:clear_tmp_allocs() + alloc_log:check({ + a.dict(d1), + a.di(dis1.a), + a.str(a1_s), + }) + d2 = dict({a='TSET'}) + eq({a='TSET'}, dct2tbl(d2)) + local dis2 = dict_items(d2) + local a2_s = dis2.a.di_tv.vval.v_string + alloc_log:clear_tmp_allocs() + alloc_log:check({ + a.dict(d2), + a.di(dis2.a), + a.str(a2_s), + }) + + tv_dict_extend(d1, d2, 'error', 'E737: Key already exists: a') + eq({a='TEST'}, dct2tbl(d1)) + eq({a='TSET'}, dct2tbl(d2)) + alloc_log:clear() + + tv_dict_extend(d1, d2, 'keep') + alloc_log:check({}) + eq({a='TEST'}, dct2tbl(d1)) + eq({a='TSET'}, dct2tbl(d2)) + + tv_dict_extend(d1, d2, 'force') + alloc_log:check({ + a.freed(a1_s), + a.str(dis1.a.di_tv.vval.v_string), + }) + eq({a='TSET'}, dct2tbl(d1)) + eq({a='TSET'}, dct2tbl(d2)) + end) + itp('disallows overriding builtin or user functions', function() + local d = dict() + d.dv_scope = lib.VAR_DEF_SCOPE + local f_lua = { + [type_key]=func_type, + value='tr', + } + local f_tv = lua2typvalt(f_lua) + local p_lua = { + [type_key]=func_type, + value='tr', + args={1}, + } + local p_tv = lua2typvalt(p_lua) + eq(lib.VAR_PARTIAL, p_tv.v_type) + local d2 = dict({tr=f_tv}) + local d3 = dict({tr=p_tv}) + local d4 = dict({['TEST:THIS']=p_tv}) + local d5 = dict({Test=f_tv}) + local d6 = dict({Test=p_tv}) + eval0([[execute("function Test()\nendfunction")]]) + tv_dict_extend(d, d2, 'force', + 'E704: Funcref variable name must start with a capital: tr') + tv_dict_extend(d, d3, 'force', + 'E704: Funcref variable name must start with a capital: tr') + tv_dict_extend(d, d4, 'force', + 'E461: Illegal variable name: TEST:THIS') + tv_dict_extend(d, d5, 'force', + 'E705: Variable name conflicts with existing function: Test') + tv_dict_extend(d, d6, 'force', + 'E705: Variable name conflicts with existing function: Test') + eq({}, dct2tbl(d)) + d.dv_scope = lib.VAR_SCOPE + tv_dict_extend(d, d4, 'force', + 'E461: Illegal variable name: TEST:THIS') + eq({}, dct2tbl(d)) + tv_dict_extend(d, d2, 'force') + eq({tr=f_lua}, dct2tbl(d)) + tv_dict_extend(d, d3, 'force') + eq({tr=p_lua}, dct2tbl(d)) + tv_dict_extend(d, d5, 'force') + eq({tr=p_lua, Test=f_lua}, dct2tbl(d)) + tv_dict_extend(d, d6, 'force') + eq({tr=p_lua, Test=p_lua}, dct2tbl(d)) + end) + itp('cares about locks and read-only items', function() + local d_lua = {tv_locked=1, tv_fixed=2, di_ro=3, di_ro_sbx=4} + local d = dict(d_lua) + local dis = dict_items(d) + dis.tv_locked.di_tv.v_lock = lib.VAR_LOCKED + dis.tv_fixed.di_tv.v_lock = lib.VAR_FIXED + dis.di_ro.di_flags = bit.bor(dis.di_ro.di_flags, lib.DI_FLAGS_RO) + dis.di_ro_sbx.di_flags = bit.bor(dis.di_ro_sbx.di_flags, lib.DI_FLAGS_RO_SBX) + lib.sandbox = true + local d1 = dict({tv_locked=41}) + local d2 = dict({tv_fixed=42}) + local d3 = dict({di_ro=43}) + local d4 = dict({di_ro_sbx=44}) + tv_dict_extend(d, d1, 'force', 'E741: Value is locked: extend() argument') + tv_dict_extend(d, d2, 'force', 'E742: Cannot change value of extend() argument') + tv_dict_extend(d, d3, 'force', 'E46: Cannot change read-only variable "extend() argument"') + tv_dict_extend(d, d4, 'force', 'E794: Cannot set variable in the sandbox: "extend() argument"') + eq(d_lua, dct2tbl(d)) + lib.sandbox = false + tv_dict_extend(d, d4, 'force') + d_lua.di_ro_sbx = 44 + eq(d_lua, dct2tbl(d)) + end) + end) end) end) From 218fa1d8069c1f1d8c5154b1abc2b686adc0c742 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 14:39:34 +0300 Subject: [PATCH 0244/1671] charset: Remove useless condition from vim_iswordc_tab --- src/nvim/charset.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 6c22108853..ad0efa2c28 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -787,10 +787,9 @@ bool vim_iswordc(int c) bool vim_iswordc_tab(const int c, const uint64_t *const chartab) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - if (c >= 0x100) { - return utf_class(c) >= 2; - } - return c > 0 && c < 0x100 && GET_CHARTAB_TAB(chartab, c) != 0; + return (c >= 0x100 + ? (utf_class(c) >= 2) + : (c > 0 && GET_CHARTAB_TAB(chartab, c) != 0)); } /// Check that "c" is a keyword character: From 368a61c5259384d3c32cef4c482953ec318cf900 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 19:00:36 +0300 Subject: [PATCH 0245/1671] unittests: Add tv_dict_copy tests --- test/unit/eval/typval_spec.lua | 175 +++++++++++++++++++++++++++++++-- 1 file changed, 165 insertions(+), 10 deletions(-) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 00dc230e89..f81f7561b9 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -29,6 +29,7 @@ local func_type = eval_helpers.func_type local null_list = eval_helpers.null_list local null_dict = eval_helpers.null_dict local dict_items = eval_helpers.dict_items +local list_items = eval_helpers.list_items local empty_list = eval_helpers.empty_list local lua2typvalt = eval_helpers.lua2typvalt local typvalt2lua = eval_helpers.typvalt2lua @@ -43,16 +44,6 @@ local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h', './src/nvim/mbyte.h', './src/nvim/garray.h', './src/nvim/eval.h', './src/nvim/vim.h') -local function list_items(l) - local lis = {} - local li = l.lv_first - for i = 1, l.lv_len do - lis[i] = ffi.gc(li, nil) - li = li.li_next - end - return lis -end - local function list_watch_alloc(li) return ffi.cast('listwatch_T*', ffi.new('listwatch_T[1]', {{lw_item=li}})) end @@ -2032,5 +2023,169 @@ describe('typval.c', function() eq(d_lua, dct2tbl(d)) end) end) + describe('equal()', function() + local function tv_dict_equal(d1, d2, ic, recursive) + return lib.tv_dict_equal(d1, d2, ic or false, recursive or false) + end + itp('works', function() + eq(true, tv_dict_equal(nil, nil)) + local d1 = dict() + alloc_log:check({a.dict(d1)}) + eq(1, d1.dv_refcount) + eq(false, tv_dict_equal(nil, d1)) + eq(false, tv_dict_equal(d1, nil)) + eq(true, tv_dict_equal(d1, d1)) + eq(1, d1.dv_refcount) + alloc_log:check({}) + local d_upper = dict({a='TEST'}) + local dis_upper = dict_items(d_upper) + local d_lower = dict({a='test'}) + local dis_lower = dict_items(d_lower) + local d_kupper_upper = dict({A='TEST'}) + local dis_kupper_upper = dict_items(d_kupper_upper) + local d_kupper_lower = dict({A='test'}) + local dis_kupper_lower = dict_items(d_kupper_lower) + alloc_log:clear_tmp_allocs() + alloc_log:check({ + a.dict(d_upper), + a.di(dis_upper.a), + a.str(dis_upper.a.di_tv.vval.v_string), + + a.dict(d_lower), + a.di(dis_lower.a), + a.str(dis_lower.a.di_tv.vval.v_string), + + a.dict(d_kupper_upper), + a.di(dis_kupper_upper.A), + a.str(dis_kupper_upper.A.di_tv.vval.v_string), + + a.dict(d_kupper_lower), + a.di(dis_kupper_lower.A), + a.str(dis_kupper_lower.A.di_tv.vval.v_string), + }) + eq(true, tv_dict_equal(d_upper, d_upper)) + eq(true, tv_dict_equal(d_upper, d_upper, true)) + eq(false, tv_dict_equal(d_upper, d_lower, false)) + eq(true, tv_dict_equal(d_upper, d_lower, true)) + eq(true, tv_dict_equal(d_kupper_upper, d_kupper_lower, true)) + eq(false, tv_dict_equal(d_kupper_upper, d_lower, true)) + eq(false, tv_dict_equal(d_kupper_upper, d_upper, true)) + eq(true, tv_dict_equal(d_upper, d_upper, true, true)) + alloc_log:check({}) + end) + end) + describe('copy()', function() + local function tv_dict_copy(...) + return ffi.gc(lib.tv_dict_copy(...), lib.tv_dict_unref) + end + itp('copies NULL correctly', function() + eq(nil, lib.tv_dict_copy(nil, nil, true, 0)) + eq(nil, lib.tv_dict_copy(nil, nil, false, 0)) + eq(nil, lib.tv_dict_copy(nil, nil, true, 1)) + eq(nil, lib.tv_dict_copy(nil, nil, false, 1)) + end) + itp('copies dict correctly without converting items', function() + do + local v = {a={['«']='»'}, b={'„'}, ['1']=1, ['«»']='“', ns=null_string, nl=null_list, nd=null_dict} + local d_tv = lua2typvalt(v) + local d = d_tv.vval.v_dict + local dis = dict_items(d) + alloc_log:clear() + + eq(1, dis.a.di_tv.vval.v_dict.dv_refcount) + eq(1, dis.b.di_tv.vval.v_list.lv_refcount) + local d_copy1 = tv_dict_copy(nil, d, false, 0) + eq(2, dis.a.di_tv.vval.v_dict.dv_refcount) + eq(2, dis.b.di_tv.vval.v_list.lv_refcount) + local dis_copy1 = dict_items(d_copy1) + eq(dis.a.di_tv.vval.v_dict, dis_copy1.a.di_tv.vval.v_dict) + eq(dis.b.di_tv.vval.v_list, dis_copy1.b.di_tv.vval.v_list) + eq(v, dct2tbl(d_copy1)) + alloc_log:clear() + lib.tv_dict_free(ffi.gc(d_copy1, nil)) + alloc_log:clear() + + eq(1, dis.a.di_tv.vval.v_dict.dv_refcount) + eq(1, dis.b.di_tv.vval.v_list.lv_refcount) + local d_deepcopy1 = tv_dict_copy(nil, d, true, 0) + neq(nil, d_deepcopy1) + eq(1, dis.a.di_tv.vval.v_dict.dv_refcount) + eq(1, dis.b.di_tv.vval.v_list.lv_refcount) + local dis_deepcopy1 = dict_items(d_deepcopy1) + neq(dis.a.di_tv.vval.v_dict, dis_deepcopy1.a.di_tv.vval.v_dict) + neq(dis.b.di_tv.vval.v_list, dis_deepcopy1.b.di_tv.vval.v_list) + eq(v, dct2tbl(d_deepcopy1)) + local di_deepcopy1 = first_di(dis_deepcopy1.a.di_tv.vval.v_dict) + alloc_log:clear() + end + collectgarbage() + end) + itp('copies dict correctly and converts items', function() + local vc = ffi.gc(ffi.new('vimconv_T[1]'), function(vc) + lib.convert_setup(vc, nil, nil) + end) + -- UTF-8 ↔ latin1 conversions need no iconv + eq(OK, lib.convert_setup(vc, to_cstr('utf-8'), to_cstr('latin1'))) + + local v = {a={['«']='»'}, b={'„'}, ['1']=1, ['«»']='“', ns=null_string, nl=null_list, nd=null_dict} + local d_tv = lua2typvalt(v) + local d = d_tv.vval.v_dict + local dis = dict_items(d) + alloc_log:clear() + + eq(1, dis.a.di_tv.vval.v_dict.dv_refcount) + eq(1, dis.b.di_tv.vval.v_list.lv_refcount) + local d_deepcopy1 = tv_dict_copy(vc, d, true, 0) + neq(nil, d_deepcopy1) + eq(1, dis.a.di_tv.vval.v_dict.dv_refcount) + eq(1, dis.b.di_tv.vval.v_list.lv_refcount) + local dis_deepcopy1 = dict_items(d_deepcopy1) + neq(dis.a.di_tv.vval.v_dict, dis_deepcopy1.a.di_tv.vval.v_dict) + neq(dis.b.di_tv.vval.v_list, dis_deepcopy1.b.di_tv.vval.v_list) + eq({a={['\171']='\187'}, b={'\191'}, ['1']=1, ['\171\187']='\191', ns=null_string, nl=null_list, nd=null_dict}, + dct2tbl(d_deepcopy1)) + alloc_log:clear_tmp_allocs() + alloc_log:clear() + end) + itp('returns different/same containers with(out) copyID', function() + local d_inner_tv = lua2typvalt({}) + local d_tv = lua2typvalt({a=d_inner_tv, b=d_inner_tv}) + eq(3, d_inner_tv.vval.v_dict.dv_refcount) + local d = d_tv.vval.v_dict + local dis = dict_items(d) + eq(dis.a.di_tv.vval.v_dict, dis.b.di_tv.vval.v_dict) + + local d_copy1 = tv_dict_copy(nil, d, true, 0) + local dis_copy1 = dict_items(d_copy1) + neq(dis_copy1.a.di_tv.vval.v_dict, dis_copy1.b.di_tv.vval.v_dict) + eq({a={}, b={}}, dct2tbl(d_copy1)) + + local d_copy2 = tv_dict_copy(nil, d, true, 2) + local dis_copy2 = dict_items(d_copy2) + eq(dis_copy2.a.di_tv.vval.v_dict, dis_copy2.b.di_tv.vval.v_dict) + eq({a={}, b={}}, dct2tbl(d_copy2)) + + eq(3, d_inner_tv.vval.v_dict.dv_refcount) + end) + itp('works with self-referencing dict with copyID', function() + local d_tv = lua2typvalt({}) + local d = d_tv.vval.v_dict + eq(1, d.dv_refcount) + lib.tv_dict_add_dict(d, 'test', 4, d) + eq(2, d.dv_refcount) + + local d_copy1 = tv_dict_copy(nil, d, true, 2) + eq(2, d_copy1.dv_refcount) + local v = {} + v.test = v + eq(v, dct2tbl(d_copy1)) + + lib.tv_dict_clear(d) + eq(1, d.dv_refcount) + + lib.tv_dict_clear(d_copy1) + eq(1, d_copy1.dv_refcount) + end) + end) end) end) From e43de6bb3e1efb58669ab904c5672f4ae87003c0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 19:04:59 +0300 Subject: [PATCH 0246/1671] unittests: Add test for tv_dict_set_keys_readonly --- test/unit/eval/typval_spec.lua | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index f81f7561b9..0d894ff8b0 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -2187,5 +2187,18 @@ describe('typval.c', function() eq(1, d_copy1.dv_refcount) end) end) + describe('set_keys_readonly()', function() + itp('works', function() + local d = dict({a=true}) + local dis = dict_items(d) + alloc_log:check({a.dict(d), a.di(dis.a)}) + eq(0, bit.band(dis.a.di_flags, lib.DI_FLAGS_RO)) + eq(0, bit.band(dis.a.di_flags, lib.DI_FLAGS_FIX)) + lib.tv_dict_set_keys_readonly(d) + alloc_log:check({}) + eq(lib.DI_FLAGS_RO, bit.band(dis.a.di_flags, lib.DI_FLAGS_RO)) + eq(lib.DI_FLAGS_FIX, bit.band(dis.a.di_flags, lib.DI_FLAGS_FIX)) + end) + end) end) end) From f0bbd1e825841c55a1f75d66a9caeaa50cc2259c Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 19:35:12 +0300 Subject: [PATCH 0247/1671] unittests: Add tests for tv_clear() --- src/nvim/eval/typval.c | 5 ++- test/unit/eval/typval_spec.lua | 73 +++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 620da0032e..c245b9222e 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1840,10 +1840,11 @@ static inline void _nothing_conv_dict_end(typval_T *const tv, /// Free memory for a variable value and set the value to NULL or 0 /// /// @param[in,out] tv Value to free. -void tv_clear(typval_T *tv) +void tv_clear(typval_T *const tv) { if (tv != NULL && tv->v_type != VAR_UNKNOWN) { - const int evn_ret = encode_vim_to_nothing(NULL, tv, "tv_clear argument"); + const int evn_ret = encode_vim_to_nothing(NULL, tv, + _("tv_clear() argument")); (void)evn_ret; assert(evn_ret == OK); } diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 0d894ff8b0..8b0470d3b7 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -55,12 +55,16 @@ local function list_watch(l, li) end local function get_alloc_rets(exp_log, res) + setmetatable(res, { + __index={ + freed=function(r, n) return {func='free', args={r[n]}} end + } + }) for i = 1,#exp_log do if ({malloc=true, calloc=true})[exp_log[i].func] then res[#res + 1] = exp_log[i].ret end end - res.freed = function(r, n) return {func='free', args={r[n]}} end return exp_log end @@ -2201,4 +2205,71 @@ describe('typval.c', function() end) end) end) + describe('tv', function() + describe('alloc', function() + describe('list ret()', function() + itp('works', function() + local rettv = typvalt(lib.VAR_UNKNOWN) + local l = lib.tv_list_alloc_ret(rettv) + eq(empty_list, typvalt2lua(rettv)) + eq(rettv.vval.v_list, l) + end) + end) + describe('dict ret()', function() + itp('works', function() + local rettv = typvalt(lib.VAR_UNKNOWN) + lib.tv_dict_alloc_ret(rettv) + eq({}, typvalt2lua(rettv)) + end) + end) + end) + describe('clear()', function() + itp('works', function() + local function deffrees(alloc_rets) + local ret = {} + for i = #alloc_rets, 1, -1 do + ret[#alloc_rets - i + 1] = alloc_rets:freed(i) + end + return ret + end + local function defalloc() + return {} + end + alloc_log:check({}) + lib.tv_clear(nil) + alloc_log:check({}) + local ll = {} + local ll_l = nil + ll[1] = ll + local dd = {} + dd.dd = dd + for _, v in ipairs({ + {nil}, + {null_string, nil, function() return {a.freed(alloc_log.null)} end}, + {0}, + {int(0)}, + {true}, + {false}, + {{}, function(tv) return {a.dict(tv.vval.v_dict)} end}, + {empty_list, function(tv) return {a.list(tv.vval.v_list)} end}, + {ll, function(tv) + ll_l = tv.vval.v_list + return {a.list(tv.vval.v_list), a.li(tv.vval.v_list.lv_first)} + end, defalloc}, + {dd, function(tv) + dd_d = tv.vval.v_dict + return {a.dict(tv.vval.v_dict), a.di(first_di(tv.vval.v_dict))} + end, defalloc}, + }) do + local tv = lua2typvalt(v[1]) + local alloc_rets = {} + alloc_log:check(get_alloc_rets((v[2] or defalloc)(tv), alloc_rets)) + lib.tv_clear(tv) + alloc_log:check((v[3] or deffrees)(alloc_rets)) + end + eq(1, ll_l.lv_refcount) + eq(1, dd_d.dv_refcount) + end) + end) + end) end) From ed4948a93317bf801eb2454fd5597a4388730a7b Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 19:49:13 +0300 Subject: [PATCH 0248/1671] unittests: Test tv_copy() --- test/unit/eval/helpers.lua | 8 +++--- test/unit/eval/typval_spec.lua | 47 +++++++++++++++++++++++++++++++--- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index a3cb062b7b..fa76113756 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -132,10 +132,10 @@ local function typvalt2lua_tab_init() typvalt2lua_tab = { [tonumber(eval.VAR_SPECIAL)] = function(t) return ({ - [eval.kSpecialVarFalse] = false, - [eval.kSpecialVarNull] = nil_value, - [eval.kSpecialVarTrue] = true, - })[t.vval.v_special] + [tonumber(eval.kSpecialVarFalse)] = false, + [tonumber(eval.kSpecialVarNull)] = nil_value, + [tonumber(eval.kSpecialVarTrue)] = true, + })[tonumber(t.vval.v_special)] end, [tonumber(eval.VAR_NUMBER)] = function(t) return {[type_key]=int_type, value=tonumber(t.vval.v_number)} diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 8b0470d3b7..9745a432c3 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -25,6 +25,7 @@ local typvalt = eval_helpers.typvalt local type_key = eval_helpers.type_key local li_alloc = eval_helpers.li_alloc local first_di = eval_helpers.first_di +local nil_value = eval_helpers.nil_value local func_type = eval_helpers.func_type local null_list = eval_helpers.null_list local null_dict = eval_helpers.null_dict @@ -2223,6 +2224,9 @@ describe('typval.c', function() end) end) end) + local function defalloc() + return {} + end describe('clear()', function() itp('works', function() local function deffrees(alloc_rets) @@ -2232,9 +2236,6 @@ describe('typval.c', function() end return ret end - local function defalloc() - return {} - end alloc_log:check({}) lib.tv_clear(nil) alloc_log:check({}) @@ -2244,12 +2245,13 @@ describe('typval.c', function() local dd = {} dd.dd = dd for _, v in ipairs({ - {nil}, + {nil_value}, {null_string, nil, function() return {a.freed(alloc_log.null)} end}, {0}, {int(0)}, {true}, {false}, + {'true', function(tv) return {a.str(tv.vval.v_string)} end}, {{}, function(tv) return {a.dict(tv.vval.v_dict)} end}, {empty_list, function(tv) return {a.list(tv.vval.v_list)} end}, {ll, function(tv) @@ -2271,5 +2273,42 @@ describe('typval.c', function() eq(1, dd_d.dv_refcount) end) end) + describe('copy()', function() + itp('works', function() + local function strallocs(tv) + return {a.str(tv.vval.v_string)} + end + for _, v in ipairs({ + {nil_value}, + {null_string}, + {0}, + {int(0)}, + {true}, + {false}, + {{}, function(tv) return {a.dict(tv.vval.v_dict)} end, nil, function(from, to) + eq(2, to.vval.v_dict.dv_refcount) + eq(to.vval.v_dict, from.vval.v_dict) + end}, + {empty_list, function(tv) return {a.list(tv.vval.v_list)} end, nil, function(from, to) + eq(2, to.vval.v_list.lv_refcount) + eq(to.vval.v_list, from.vval.v_list) + end}, + {'test', strallocs, strallocs, function(from, to) + neq(to.vval.v_string, from.vval.v_string) + end}, + }) do + local from = lua2typvalt(v[1]) + alloc_log:check((v[2] or defalloc)(from)) + local to = typvalt(lib.VAR_UNKNOWN) + lib.tv_copy(from, to) + local res = v[1] + eq(res, typvalt2lua(to)) + alloc_log:check((v[3] or defalloc)(to)) + if v[4] then + v[4](from, to) + end + end + end) + end) end) end) From 630ff33dc144a64b5488b4132c0fc4351a5c84db Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 25 Mar 2017 19:52:20 +0300 Subject: [PATCH 0249/1671] unittests: Test locks section --- src/nvim/eval/typval.c | 7 +- test/unit/eval/typval_spec.lua | 114 +++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 3 deletions(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index c245b9222e..185dd0e86c 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1956,6 +1956,7 @@ void tv_copy(typval_T *const from, typval_T *const to) /// @param[in] deep Levels to (un)lock, -1 to (un)lock everything. /// @param[in] lock True if it is needed to lock an item, false to unlock. void tv_item_lock(typval_T *const tv, const int deep, const bool lock) + FUNC_ATTR_NONNULL_ALL { // TODO(ZyX-I): Make this not recursive static int recurse = 0; @@ -2031,13 +2032,13 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock) bool tv_islocked(const typval_T *const tv) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - return ((tv->v_lock & VAR_LOCKED) + return ((tv->v_lock == VAR_LOCKED) || (tv->v_type == VAR_LIST && tv->vval.v_list != NULL - && (tv->vval.v_list->lv_lock & VAR_LOCKED)) + && (tv->vval.v_list->lv_lock == VAR_LOCKED)) || (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL - && (tv->vval.v_dict->dv_lock & VAR_LOCKED))); + && (tv->vval.v_dict->dv_lock == VAR_LOCKED))); } /// Return true if typval is locked diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 9745a432c3..0619e43d08 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -2310,5 +2310,119 @@ describe('typval.c', function() end end) end) + describe('item_lock()', function() + itp('does not alter VAR_PARTIAL', function() + local p_tv = lua2typvalt({ + [type_key]=func_type, + value='tr', + dict={}, + }) + lib.tv_item_lock(p_tv, -1, true) + eq(lib.VAR_UNLOCKED, p_tv.vval.v_partial.pt_dict.dv_lock) + end) + itp('does not change VAR_FIXED values', function() + local d_tv = lua2typvalt({}) + local l_tv = lua2typvalt(empty_list) + alloc_log:clear() + d_tv.v_lock = lib.VAR_FIXED + d_tv.vval.v_dict.dv_lock = lib.VAR_FIXED + l_tv.v_lock = lib.VAR_FIXED + l_tv.vval.v_list.lv_lock = lib.VAR_FIXED + lib.tv_item_lock(d_tv, 1, true) + lib.tv_item_lock(l_tv, 1, true) + eq(lib.VAR_FIXED, d_tv.v_lock) + eq(lib.VAR_FIXED, l_tv.v_lock) + eq(lib.VAR_FIXED, d_tv.vval.v_dict.dv_lock) + eq(lib.VAR_FIXED, l_tv.vval.v_list.lv_lock) + lib.tv_item_lock(d_tv, 1, false) + lib.tv_item_lock(l_tv, 1, false) + eq(lib.VAR_FIXED, d_tv.v_lock) + eq(lib.VAR_FIXED, l_tv.v_lock) + eq(lib.VAR_FIXED, d_tv.vval.v_dict.dv_lock) + eq(lib.VAR_FIXED, l_tv.vval.v_list.lv_lock) + alloc_log:check({}) + end) + itp('works with NULL values', function() + local l_tv = lua2typvalt(null_list) + local d_tv = lua2typvalt(null_dict) + local s_tv = lua2typvalt(null_string) + alloc_log:clear() + lib.tv_item_lock(l_tv, 1, true) + lib.tv_item_lock(d_tv, 1, true) + lib.tv_item_lock(s_tv, 1, true) + eq(null_list, typvalt2lua(l_tv)) + eq(null_dict, typvalt2lua(d_tv)) + eq(null_string, typvalt2lua(s_tv)) + eq(lib.VAR_LOCKED, d_tv.v_lock) + eq(lib.VAR_LOCKED, l_tv.v_lock) + eq(lib.VAR_LOCKED, s_tv.v_lock) + alloc_log:check({}) + end) + end) + describe('islocked()', function() + itp('works with NULL values', function() + local l_tv = lua2typvalt(null_list) + local d_tv = lua2typvalt(null_dict) + eq(false, lib.tv_islocked(l_tv)) + eq(false, lib.tv_islocked(d_tv)) + end) + itp('works', function() + local tv = lua2typvalt() + local d_tv = lua2typvalt({}) + local l_tv = lua2typvalt(empty_list) + alloc_log:clear() + eq(false, lib.tv_islocked(tv)) + eq(false, lib.tv_islocked(l_tv)) + eq(false, lib.tv_islocked(d_tv)) + d_tv.vval.v_dict.dv_lock = lib.VAR_LOCKED + l_tv.vval.v_list.lv_lock = lib.VAR_LOCKED + eq(true, lib.tv_islocked(l_tv)) + eq(true, lib.tv_islocked(d_tv)) + tv.v_lock = lib.VAR_LOCKED + d_tv.v_lock = lib.VAR_LOCKED + l_tv.v_lock = lib.VAR_LOCKED + eq(true, lib.tv_islocked(tv)) + eq(true, lib.tv_islocked(l_tv)) + eq(true, lib.tv_islocked(d_tv)) + d_tv.vval.v_dict.dv_lock = lib.VAR_UNLOCKED + l_tv.vval.v_list.lv_lock = lib.VAR_UNLOCKED + eq(true, lib.tv_islocked(tv)) + eq(true, lib.tv_islocked(l_tv)) + eq(true, lib.tv_islocked(d_tv)) + tv.v_lock = lib.VAR_FIXED + d_tv.v_lock = lib.VAR_FIXED + l_tv.v_lock = lib.VAR_FIXED + eq(false, lib.tv_islocked(tv)) + eq(false, lib.tv_islocked(l_tv)) + eq(false, lib.tv_islocked(d_tv)) + d_tv.vval.v_dict.dv_lock = lib.VAR_LOCKED + l_tv.vval.v_list.lv_lock = lib.VAR_LOCKED + eq(true, lib.tv_islocked(l_tv)) + eq(true, lib.tv_islocked(d_tv)) + d_tv.vval.v_dict.dv_lock = lib.VAR_FIXED + l_tv.vval.v_list.lv_lock = lib.VAR_FIXED + eq(false, lib.tv_islocked(l_tv)) + eq(false, lib.tv_islocked(d_tv)) + alloc_log:check({}) + end) + end) + describe('check_lock()', function() + local function tv_check_lock(lock, name, name_len, emsg) + return check_emsg(function() + return lib.tv_check_lock(lock, name, name_len) + end, emsg) + end + itp('works', function() + eq(false, tv_check_lock(lib.VAR_UNLOCKED, 'test', 3)) + eq(true, tv_check_lock(lib.VAR_LOCKED, 'test', 3, + 'E741: Value is locked: tes')) + eq(true, tv_check_lock(lib.VAR_FIXED, 'test', 3, + 'E742: Cannot change value of tes')) + eq(true, tv_check_lock(lib.VAR_LOCKED, nil, 0, + 'E741: Value is locked: Unknown')) + eq(true, tv_check_lock(lib.VAR_FIXED, nil, 0, + 'E742: Cannot change value of Unknown')) + end) + end) end) end) From 389274bef77798af83013bda1e4f1c261db38de5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 25 Mar 2017 20:01:08 +0300 Subject: [PATCH 0250/1671] unittests: Add tv_equal() tests --- test/unit/eval/typval_spec.lua | 121 +++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 0619e43d08..74dba4377f 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -2424,5 +2424,126 @@ describe('typval.c', function() 'E742: Cannot change value of Unknown')) end) end) + describe('equal()', function() + itp('compares empty and NULL lists correctly', function() + local l = lua2typvalt(empty_list) + local l2 = lua2typvalt(empty_list) + local nl = lua2typvalt(null_list) + + -- NULL lists are not equal to empty lists + eq(false, lib.tv_equal(l, nl, true, false)) + eq(false, lib.tv_equal(nl, l, false, false)) + eq(false, lib.tv_equal(nl, l, false, true)) + eq(false, lib.tv_equal(l, nl, true, true)) + + -- Yet NULL lists are equal themselves + eq(true, lib.tv_equal(nl, nl, true, false)) + eq(true, lib.tv_equal(nl, nl, false, false)) + eq(true, lib.tv_equal(nl, nl, false, true)) + eq(true, lib.tv_equal(nl, nl, true, true)) + + -- As well as empty lists + eq(true, lib.tv_equal(l, l, true, false)) + eq(true, lib.tv_equal(l, l2, false, false)) + eq(true, lib.tv_equal(l2, l, false, true)) + eq(true, lib.tv_equal(l2, l2, true, true)) + end) + -- Must not use recursive=true argument in the following tests because it + -- indicates that tv_equal_recurse_limit and recursive_cnt were set which + -- is essential. This argument will be set when comparing inner lists. + itp('compares lists correctly when case is not ignored', function() + local l1 = lua2typvalt({'abc', {1, 2, 'Abc'}, 'def'}) + local l2 = lua2typvalt({'abc', {1, 2, 'Abc'}}) + local l3 = lua2typvalt({'abc', {1, 2, 'Abc'}, 'Def'}) + local l4 = lua2typvalt({'abc', {1, 2, 'Abc', 4}, 'def'}) + local l5 = lua2typvalt({'Abc', {1, 2, 'Abc'}, 'def'}) + local l6 = lua2typvalt({'abc', {1, 2, 'Abc'}, 'def'}) + local l7 = lua2typvalt({'abc', {1, 2, 'abc'}, 'def'}) + local l8 = lua2typvalt({'abc', nil, 'def'}) + local l9 = lua2typvalt({'abc', {1, 2, nil}, 'def'}) + + eq(true, lib.tv_equal(l1, l1, false, false)) + eq(false, lib.tv_equal(l1, l2, false, false)) + eq(false, lib.tv_equal(l1, l3, false, false)) + eq(false, lib.tv_equal(l1, l4, false, false)) + eq(false, lib.tv_equal(l1, l5, false, false)) + eq(true, lib.tv_equal(l1, l6, false, false)) + eq(false, lib.tv_equal(l1, l7, false, false)) + eq(false, lib.tv_equal(l1, l8, false, false)) + eq(false, lib.tv_equal(l1, l9, false, false)) + end) + itp('compares lists correctly when case is ignored', function() + local l1 = lua2typvalt({'abc', {1, 2, 'Abc'}, 'def'}) + local l2 = lua2typvalt({'abc', {1, 2, 'Abc'}}) + local l3 = lua2typvalt({'abc', {1, 2, 'Abc'}, 'Def'}) + local l4 = lua2typvalt({'abc', {1, 2, 'Abc', 4}, 'def'}) + local l5 = lua2typvalt({'Abc', {1, 2, 'Abc'}, 'def'}) + local l6 = lua2typvalt({'abc', {1, 2, 'Abc'}, 'def'}) + local l7 = lua2typvalt({'abc', {1, 2, 'abc'}, 'def'}) + local l8 = lua2typvalt({'abc', nil, 'def'}) + local l9 = lua2typvalt({'abc', {1, 2, nil}, 'def'}) + + eq(true, lib.tv_equal(l1, l1, true, false)) + eq(false, lib.tv_equal(l1, l2, true, false)) + eq(true, lib.tv_equal(l1, l3, true, false)) + eq(false, lib.tv_equal(l1, l4, true, false)) + eq(true, lib.tv_equal(l1, l5, true, false)) + eq(true, lib.tv_equal(l1, l6, true, false)) + eq(true, lib.tv_equal(l1, l7, true, false)) + eq(false, lib.tv_equal(l1, l8, true, false)) + eq(false, lib.tv_equal(l1, l9, true, false)) + end) + local function tv_equal(d1, d2, ic, recursive) + return lib.tv_equal(d1, d2, ic or false, recursive or false) + end + itp('works with dictionaries', function() + local nd = lua2typvalt(null_dict) + eq(true, tv_equal(nd, nd)) + alloc_log:check({}) + local d1 = lua2typvalt({}) + alloc_log:check({a.dict(d1.vval.v_dict)}) + eq(1, d1.vval.v_dict.dv_refcount) + eq(false, tv_equal(nd, d1)) + eq(false, tv_equal(d1, nd)) + eq(true, tv_equal(d1, d1)) + eq(1, d1.vval.v_dict.dv_refcount) + alloc_log:check({}) + local d_upper = lua2typvalt({a='TEST'}) + local dis_upper = dict_items(d_upper.vval.v_dict) + local d_lower = lua2typvalt({a='test'}) + local dis_lower = dict_items(d_lower.vval.v_dict) + local d_kupper_upper = lua2typvalt({A='TEST'}) + local dis_kupper_upper = dict_items(d_kupper_upper.vval.v_dict) + local d_kupper_lower = lua2typvalt({A='test'}) + local dis_kupper_lower = dict_items(d_kupper_lower.vval.v_dict) + alloc_log:clear_tmp_allocs() + alloc_log:check({ + a.dict(d_upper.vval.v_dict), + a.di(dis_upper.a), + a.str(dis_upper.a.di_tv.vval.v_string), + + a.dict(d_lower.vval.v_dict), + a.di(dis_lower.a), + a.str(dis_lower.a.di_tv.vval.v_string), + + a.dict(d_kupper_upper.vval.v_dict), + a.di(dis_kupper_upper.A), + a.str(dis_kupper_upper.A.di_tv.vval.v_string), + + a.dict(d_kupper_lower.vval.v_dict), + a.di(dis_kupper_lower.A), + a.str(dis_kupper_lower.A.di_tv.vval.v_string), + }) + eq(true, tv_equal(d_upper, d_upper)) + eq(true, tv_equal(d_upper, d_upper, true)) + eq(false, tv_equal(d_upper, d_lower, false)) + eq(true, tv_equal(d_upper, d_lower, true)) + eq(true, tv_equal(d_kupper_upper, d_kupper_lower, true)) + eq(false, tv_equal(d_kupper_upper, d_lower, true)) + eq(false, tv_equal(d_kupper_upper, d_upper, true)) + eq(true, tv_equal(d_upper, d_upper, true, true)) + alloc_log:check({}) + end) + end) end) end) From 49195063fd864960437b84cae5fc8b2ca9885d59 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 25 Mar 2017 20:15:37 +0300 Subject: [PATCH 0251/1671] =?UTF-8?q?unittests:=20Add=20tv=5Fcheck?= =?UTF-8?q?=E2=80=A6=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/unit/eval/typval_spec.lua | 98 ++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 3 deletions(-) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 74dba4377f..143f4544e6 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1381,7 +1381,7 @@ describe('typval.c', function() end) describe('dict', function() describe('watcher', function() - describe('add/remove', function() + describe('add()/remove()', function() itp('works with an empty key', function() local d = dict({}) eq({}, dict_watchers(d)) @@ -1491,7 +1491,7 @@ describe('typval.c', function() end) end) describe('item', function() - describe('alloc/free', function() + describe('alloc()/free()', function() local function check_tv_dict_item_alloc_len(s, len, tv, more_frees) local di if len == nil then @@ -1527,7 +1527,7 @@ describe('typval.c', function() check_tv_dict_item_alloc_len('', 0, tv, {a.freed(tv.vval.v_string)}) end) end) - describe('add/remove', function() + describe('add()/remove()', function() itp('works', function() local d = dict() eq({}, dct2tbl(d)) @@ -2545,5 +2545,97 @@ describe('typval.c', function() alloc_log:check({}) end) end) + describe('check', function() + describe('str_or_nr()', function() + itp('works', function() + local tv = typvalt() + local mem = lib.xmalloc(1) + tv.vval.v_list = mem -- Should crash when actually accessed + alloc_log:clear() + for _, v in ipairs({ + {lib.VAR_NUMBER, nil}, + {lib.VAR_FLOAT, 'E805: Expected a Number or a String, Float found'}, + {lib.VAR_PARTIAL, 'E703: Expected a Number or a String, Funcref found'}, + {lib.VAR_FUNC, 'E703: Expected a Number or a String, Funcref found'}, + {lib.VAR_LIST, 'E745: Expected a Number or a String, List found'}, + {lib.VAR_DICT, 'E728: Expected a Number or a String, Dictionary found'}, + {lib.VAR_SPECIAL, 'E5300: Expected a Number or a String'}, + {lib.VAR_UNKNOWN, 'E685: Internal error: tv_check_str_or_nr(UNKNOWN)'}, + }) do + local typ = v[1] + local emsg = v[2] + local ret = true + if emsg then ret = false end + tv.v_type = typ + eq(ret, check_emsg(function() return lib.tv_check_str_or_nr(tv) end, emsg)) + if emsg then + alloc_log:clear() + else + alloc_log:check({}) + end + end + end) + end) + describe('num()', function() + itp('works', function() + local tv = typvalt() + local mem = lib.xmalloc(1) + tv.vval.v_list = mem -- Should crash when actually accessed + alloc_log:clear() + for _, v in ipairs({ + {lib.VAR_NUMBER, nil}, + {lib.VAR_FLOAT, 'E805: Using a Float as a Number'}, + {lib.VAR_PARTIAL, 'E703: Using a Funcref as a Number'}, + {lib.VAR_FUNC, 'E703: Using a Funcref as a Number'}, + {lib.VAR_LIST, 'E745: Using a List as a Number'}, + {lib.VAR_DICT, 'E728: Using a Dictionary as a Number'}, + {lib.VAR_SPECIAL, nil}, + {lib.VAR_UNKNOWN, 'E685: using an invalid value as a Number'}, + }) do + local typ = v[1] + local emsg = v[2] + local ret = true + if emsg then ret = false end + tv.v_type = typ + eq(ret, check_emsg(function() return lib.tv_check_num(tv) end, emsg)) + if emsg then + alloc_log:clear() + else + alloc_log:check({}) + end + end + end) + end) + describe('str()', function() + itp('works', function() + local tv = typvalt() + local mem = lib.xmalloc(1) + tv.vval.v_list = mem -- Should crash when actually accessed + alloc_log:clear() + for _, v in ipairs({ + {lib.VAR_NUMBER, nil}, + {lib.VAR_FLOAT, 'E806: using Float as a String'}, + {lib.VAR_PARTIAL, 'E729: using Funcref as a String'}, + {lib.VAR_FUNC, 'E729: using Funcref as a String'}, + {lib.VAR_LIST, 'E730: using List as a String'}, + {lib.VAR_DICT, 'E731: using Dictionary as a String'}, + {lib.VAR_SPECIAL, nil}, + {lib.VAR_UNKNOWN, 'E908: using an invalid value as a String'}, + }) do + local typ = v[1] + local emsg = v[2] + local ret = true + if emsg then ret = false end + tv.v_type = typ + eq(ret, check_emsg(function() return lib.tv_check_str(tv) end, emsg)) + if emsg then + alloc_log:clear() + else + alloc_log:check({}) + end + end + end) + end) + end) end) end) From 4536c064e43dd93337d4809f6178c1ffc7034ebe Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 25 Mar 2017 20:17:00 +0300 Subject: [PATCH 0252/1671] unittests: Move tv_dict_add* tests to a proper describe() block --- test/unit/eval/typval_spec.lua | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 143f4544e6..101adf3de4 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1768,7 +1768,9 @@ describe('typval.c', function() {tv_dict_get_callback(d, 'testt', 5)}) end) end) - describe('dict_add()', function() + end) + describe('add', function() + describe('()', function() itp('works', function() local di = lib.tv_dict_item_alloc_len('t-est', 5) alloc_log:check({a.di(di, 't-est')}) @@ -1789,7 +1791,7 @@ describe('typval.c', function() 'E685: Internal error: hash_add()')) end) end) - describe('dict_add_list()', function() + describe('list()', function() itp('works', function() local l = list(1, 2, 3) alloc_log:clear() @@ -1815,7 +1817,7 @@ describe('typval.c', function() alloc_log:check({}) end) end) - describe('dict_add_dict()', function() + describe('dict()', function() itp('works', function() local d2 = dict({foo=42}) alloc_log:clear() @@ -1841,7 +1843,7 @@ describe('typval.c', function() alloc_log:check({}) end) end) - describe('dict_add_nr()', function() + describe('nr()', function() itp('works', function() local d = dict({test=10}) alloc_log:clear() @@ -1861,7 +1863,7 @@ describe('typval.c', function() alloc_log:check({}) end) end) - describe('dict_add_str()', function() + describe('str()', function() itp('works', function() local d = dict({test=10}) alloc_log:clear() From e08b27ba4acf22fe180c2a4a6b00f0ea0cfc3a79 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 25 Mar 2017 20:46:39 +0300 Subject: [PATCH 0253/1671] unittests: Add tv_get number tests --- src/nvim/eval/typval.c | 2 +- test/unit/eval/typval_spec.lua | 124 ++++++++++++++++++++++++++++++++- 2 files changed, 124 insertions(+), 2 deletions(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 185dd0e86c..48ff3ab623 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -2447,7 +2447,7 @@ float_T tv_get_float(const typval_T *const tv) break; } case VAR_UNKNOWN: { - emsgf(_(e_intern2), "get_tv_float(UNKNOWN)"); + emsgf(_(e_intern2), "tv_get_float(UNKNOWN)"); break; } } diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 101adf3de4..f9bb2a942c 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -10,6 +10,7 @@ local eq = helpers.eq local neq = helpers.neq local ffi = helpers.ffi local FAIL = helpers.FAIL +local NULL = helpers.NULL local cimport = helpers.cimport local to_cstr = helpers.to_cstr local alloc_log_new = helpers.alloc_log_new @@ -43,7 +44,8 @@ local concat_tables = global_helpers.concat_tables local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h', './src/nvim/mbyte.h', './src/nvim/garray.h', - './src/nvim/eval.h', './src/nvim/vim.h') + './src/nvim/eval.h', './src/nvim/vim.h', + './src/nvim/globals.h') local function list_watch_alloc(li) return ffi.cast('listwatch_T*', ffi.new('listwatch_T[1]', {{lw_item=li}})) @@ -2639,5 +2641,125 @@ describe('typval.c', function() end) end) end) + describe('get', function() + describe('number()', function() + itp('works', function() + for _, v in ipairs({ + {lib.VAR_NUMBER, {v_number=42}, nil, 42}, + {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, 100500}, + {lib.VAR_FLOAT, {v_float=42.53}, 'E805: Using a Float as a Number', 0}, + {lib.VAR_PARTIAL, {v_partial=NULL}, 'E703: Using a Funcref as a Number', 0}, + {lib.VAR_FUNC, {v_string=NULL}, 'E703: Using a Funcref as a Number', 0}, + {lib.VAR_LIST, {v_list=NULL}, 'E745: Using a List as a Number', 0}, + {lib.VAR_DICT, {v_dict=NULL}, 'E728: Using a Dictionary as a Number', 0}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 0}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarTrue}, nil, 1}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 0}, + {lib.VAR_UNKNOWN, nil, 'E685: Internal error: tv_get_number(UNKNOWN)', 0}, + }) do + local tv = typvalt(v[1], v[2]) + alloc_log:check({}) + local emsg = v[3] + local ret = v[4] + eq(ret, check_emsg(function() return lib.tv_get_number(tv) end, emsg)) + if emsg then + alloc_log:clear() + else + alloc_log:check({}) + end + end + end) + end) + describe('number_chk()', function() + itp('works', function() + for _, v in ipairs({ + {lib.VAR_NUMBER, {v_number=42}, nil, 42}, + {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, 100500}, + {lib.VAR_FLOAT, {v_float=42.53}, 'E805: Using a Float as a Number', 0}, + {lib.VAR_PARTIAL, {v_partial=NULL}, 'E703: Using a Funcref as a Number', 0}, + {lib.VAR_FUNC, {v_string=NULL}, 'E703: Using a Funcref as a Number', 0}, + {lib.VAR_LIST, {v_list=NULL}, 'E745: Using a List as a Number', 0}, + {lib.VAR_DICT, {v_dict=NULL}, 'E728: Using a Dictionary as a Number', 0}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 0}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarTrue}, nil, 1}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 0}, + {lib.VAR_UNKNOWN, nil, 'E685: Internal error: tv_get_number(UNKNOWN)', 0}, + }) do + local tv = typvalt(v[1], v[2]) + alloc_log:check({}) + local emsg = v[3] + local ret = {v[4], not not emsg} + eq(ret, check_emsg(function() + local err = ffi.new('bool[1]', {false}) + local res = lib.tv_get_number_chk(tv, err) + return {res, err[0]} + end, emsg)) + if emsg then + alloc_log:clear() + else + alloc_log:check({}) + end + end + end) + end) + describe('lnum()', function() + itp('works', function() + for _, v in ipairs({ + {lib.VAR_NUMBER, {v_number=42}, nil, 42}, + {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, 100500}, + {lib.VAR_STRING, {v_string=to_cstr('.')}, nil, 46}, + {lib.VAR_FLOAT, {v_float=42.53}, 'E805: Using a Float as a Number', -1}, + {lib.VAR_PARTIAL, {v_partial=NULL}, 'E703: Using a Funcref as a Number', -1}, + {lib.VAR_FUNC, {v_string=NULL}, 'E703: Using a Funcref as a Number', -1}, + {lib.VAR_LIST, {v_list=NULL}, 'E745: Using a List as a Number', -1}, + {lib.VAR_DICT, {v_dict=NULL}, 'E728: Using a Dictionary as a Number', -1}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 0}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarTrue}, nil, 1}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 0}, + {lib.VAR_UNKNOWN, nil, 'E685: Internal error: tv_get_number(UNKNOWN)', -1}, + }) do + lib.curwin.w_cursor.lnum = 46 + local tv = typvalt(v[1], v[2]) + alloc_log:check({}) + local emsg = v[3] + local ret = v[4] + eq(ret, check_emsg(function() return lib.tv_get_lnum(tv) end, emsg)) + if emsg then + alloc_log:clear() + else + alloc_log:check({}) + end + end + end) + end) + describe('float()', function() + itp('works', function() + for _, v in ipairs({ + {lib.VAR_NUMBER, {v_number=42}, nil, 42}, + {lib.VAR_STRING, {v_string=to_cstr('100500')}, 'E892: Using a String as a Float', 0}, + {lib.VAR_FLOAT, {v_float=42.53}, nil, 42.53}, + {lib.VAR_PARTIAL, {v_partial=NULL}, 'E891: Using a Funcref as a Float', 0}, + {lib.VAR_FUNC, {v_string=NULL}, 'E891: Using a Funcref as a Float', 0}, + {lib.VAR_LIST, {v_list=NULL}, 'E893: Using a List as a Float', 0}, + {lib.VAR_DICT, {v_dict=NULL}, 'E894: Using a Dictionary as a Float', 0}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, 'E907: Using a special value as a Float', 0}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarTrue}, 'E907: Using a special value as a Float', 0}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, 'E907: Using a special value as a Float', 0}, + {lib.VAR_UNKNOWN, nil, 'E685: Internal error: tv_get_float(UNKNOWN)', 0}, + }) do + local tv = typvalt(v[1], v[2]) + alloc_log:check({}) + local emsg = v[3] + local ret = v[4] + eq(ret, check_emsg(function() return lib.tv_get_float(tv) end, emsg)) + if emsg then + alloc_log:clear() + else + alloc_log:check({}) + end + end + end) + end) + end) end) end) From 7826ee1c035a99804dfeba80523c3f2992f7e6b6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 25 Mar 2017 20:59:59 +0300 Subject: [PATCH 0254/1671] unittests: Add tv_get_string* tests --- test/unit/eval/typval_spec.lua | 166 +++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index f9bb2a942c..d861f81105 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -2760,6 +2760,172 @@ describe('typval.c', function() end end) end) + describe('string()', function() + itp('works', function() + local buf = lib.tv_get_string(lua2typvalt(int(1))) + local buf_chk = lib.tv_get_string_chk(lua2typvalt(int(1))) + neq(buf, buf_chk) + for _, v in ipairs({ + {lib.VAR_NUMBER, {v_number=42}, nil, '42'}, + {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, '100500'}, + {lib.VAR_FLOAT, {v_float=42.53}, 'E806: using Float as a String', ''}, + {lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: using Funcref as a String', ''}, + {lib.VAR_FUNC, {v_string=NULL}, 'E729: using Funcref as a String', ''}, + {lib.VAR_LIST, {v_list=NULL}, 'E730: using List as a String', ''}, + {lib.VAR_DICT, {v_dict=NULL}, 'E731: using Dictionary as a String', ''}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'null'}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarTrue}, nil, 'true'}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 'false'}, + {lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', ''}, + }) do + local tv = typvalt(v[1], v[2]) + alloc_log:check({}) + local emsg = v[3] + local ret = v[4] + eq(ret, check_emsg(function() + local res = lib.tv_get_string(tv) + if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL then + eq(buf, res) + else + neq(buf, res) + end + if res ~= nil then + return ffi.string(res) + else + return nil + end + end, emsg)) + if emsg then + alloc_log:clear() + else + alloc_log:check({}) + end + end + end) + end) + describe('string_chk()', function() + itp('works', function() + local buf = lib.tv_get_string_chk(lua2typvalt(int(1))) + for _, v in ipairs({ + {lib.VAR_NUMBER, {v_number=42}, nil, '42'}, + {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, '100500'}, + {lib.VAR_FLOAT, {v_float=42.53}, 'E806: using Float as a String', nil}, + {lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: using Funcref as a String', nil}, + {lib.VAR_FUNC, {v_string=NULL}, 'E729: using Funcref as a String', nil}, + {lib.VAR_LIST, {v_list=NULL}, 'E730: using List as a String', nil}, + {lib.VAR_DICT, {v_dict=NULL}, 'E731: using Dictionary as a String', nil}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'null'}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarTrue}, nil, 'true'}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 'false'}, + {lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', nil}, + }) do + local tv = typvalt(v[1], v[2]) + alloc_log:check({}) + local emsg = v[3] + local ret = v[4] + eq(ret, check_emsg(function() + local res = lib.tv_get_string_chk(tv) + if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL then + eq(buf, res) + else + neq(buf, res) + end + if res ~= nil then + return ffi.string(res) + else + return nil + end + end, emsg)) + if emsg then + alloc_log:clear() + else + alloc_log:check({}) + end + end + end) + end) + describe('string_buf()', function() + itp('works', function() + for _, v in ipairs({ + {lib.VAR_NUMBER, {v_number=42}, nil, '42'}, + {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, '100500'}, + {lib.VAR_FLOAT, {v_float=42.53}, 'E806: using Float as a String', ''}, + {lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: using Funcref as a String', ''}, + {lib.VAR_FUNC, {v_string=NULL}, 'E729: using Funcref as a String', ''}, + {lib.VAR_LIST, {v_list=NULL}, 'E730: using List as a String', ''}, + {lib.VAR_DICT, {v_dict=NULL}, 'E731: using Dictionary as a String', ''}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'null'}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarTrue}, nil, 'true'}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 'false'}, + {lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', ''}, + }) do + local tv = typvalt(v[1], v[2]) + alloc_log:check({}) + local emsg = v[3] + local ret = v[4] + eq(ret, check_emsg(function() + local buf = ffi.new('char[?]', lib.NUMBUFLEN, {0}) + local res = lib.tv_get_string_buf(tv, buf) + if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL then + eq(buf, res) + else + neq(buf, res) + end + if res ~= nil then + return ffi.string(res) + else + return nil + end + end, emsg)) + if emsg then + alloc_log:clear() + else + alloc_log:check({}) + end + end + end) + end) + describe('string_buf_chk()', function() + itp('works', function() + for _, v in ipairs({ + {lib.VAR_NUMBER, {v_number=42}, nil, '42'}, + {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, '100500'}, + {lib.VAR_FLOAT, {v_float=42.53}, 'E806: using Float as a String', nil}, + {lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: using Funcref as a String', nil}, + {lib.VAR_FUNC, {v_string=NULL}, 'E729: using Funcref as a String', nil}, + {lib.VAR_LIST, {v_list=NULL}, 'E730: using List as a String', nil}, + {lib.VAR_DICT, {v_dict=NULL}, 'E731: using Dictionary as a String', nil}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'null'}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarTrue}, nil, 'true'}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 'false'}, + {lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', nil}, + }) do + local tv = typvalt(v[1], v[2]) + alloc_log:check({}) + local emsg = v[3] + local ret = v[4] + eq(ret, check_emsg(function() + local buf = ffi.new('char[?]', lib.NUMBUFLEN, {0}) + local res = lib.tv_get_string_buf_chk(tv, buf) + if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL then + eq(buf, res) + else + neq(buf, res) + end + if res ~= nil then + return ffi.string(res) + else + return nil + end + end, emsg)) + if emsg then + alloc_log:clear() + else + alloc_log:check({}) + end + end + end) + end) end) end) end) From 8daf756fb6f92bdeb39e473b34364afd5270dd99 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 25 Mar 2017 22:07:41 +0300 Subject: [PATCH 0255/1671] unittests: Fix linter errors --- test/unit/eval/tricks_spec.lua | 2 -- test/unit/eval/typval_spec.lua | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/test/unit/eval/tricks_spec.lua b/test/unit/eval/tricks_spec.lua index ae569bed11..7aa0f0f6e6 100644 --- a/test/unit/eval/tricks_spec.lua +++ b/test/unit/eval/tricks_spec.lua @@ -4,8 +4,6 @@ local eval_helpers = require('test.unit.eval.helpers') local itp = helpers.gen_itp(it) local cimport = helpers.cimport -local to_cstr = helpers.to_cstr -local ffi = helpers.ffi local eq = helpers.eq local eval0 = eval_helpers.eval0 diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index d861f81105..7436115945 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -2124,7 +2124,6 @@ describe('typval.c', function() neq(dis.a.di_tv.vval.v_dict, dis_deepcopy1.a.di_tv.vval.v_dict) neq(dis.b.di_tv.vval.v_list, dis_deepcopy1.b.di_tv.vval.v_list) eq(v, dct2tbl(d_deepcopy1)) - local di_deepcopy1 = first_di(dis_deepcopy1.a.di_tv.vval.v_dict) alloc_log:clear() end collectgarbage() @@ -2247,6 +2246,7 @@ describe('typval.c', function() local ll_l = nil ll[1] = ll local dd = {} + local dd_d = nil dd.dd = dd for _, v in ipairs({ {nil_value}, From 29bad04f9e2e70b7c1c198ca0d86d23282bee8a1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 25 Mar 2017 22:35:34 +0300 Subject: [PATCH 0256/1671] eval: Do not supply S_LEN to strncmp It may be a macro as well. --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 9950f8b196..0d7c585099 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7967,7 +7967,7 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (s == NULL) { return; } - if (strncmp(s, S_LEN("silent")) == 0) { + if (strncmp(s, "silent", 6) == 0) { msg_silent++; } if (strcmp(s, "silent!") == 0) { From f4256243dbdbd2858be716f9f52763bd9a76be4d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 25 Mar 2017 22:49:23 +0300 Subject: [PATCH 0257/1671] eval: Fix -Werror=unitialized from QB --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 0d7c585099..6890bdc522 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2107,7 +2107,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } int len = -1; - char_u *key; + char_u *key = NULL; if (*p == '.') { key = p + 1; for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) { From 58e34e8d99b01bf3937824fc50502e39a8c39eba Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 26 Mar 2017 03:42:23 +0300 Subject: [PATCH 0258/1671] eval/typval: Allow NULL dict as tv_dict_get_callback() argument Also removes NULL key input: tv_dict_find() does not allow this. --- src/nvim/eval/typval.c | 5 ++--- test/unit/eval/typval_spec.lua | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 48ff3ab623..da58cf5ca9 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -825,8 +825,7 @@ void tv_dict_watcher_add(dict_T *const dict, const char *const key_pattern, /// @param[in] cb2 Second callback to check. /// /// @return True if they are equal, false otherwise. -static bool tv_callback_equal(const Callback *const cb1, - const Callback *const cb2) +bool tv_callback_equal(const Callback *const cb1, const Callback *const cb2) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { if (cb1->type != cb2->type) { @@ -1240,7 +1239,7 @@ const char *tv_dict_get_string_buf(const dict_T *const d, const char *const key, bool tv_dict_get_callback(dict_T *const d, const char *const key, const ptrdiff_t key_len, Callback *const result) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_NONNULL_ARG(2, 4) FUNC_ATTR_WARN_UNUSED_RESULT { result->type = kCallbackNone; diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 7436115945..3a4ef0cb92 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1736,8 +1736,7 @@ describe('typval.c', function() return cb_lua, ret end itp('works with NULL dict', function() - eq({{type='none'}, true}, - {tv_dict_get_callback(nil, nil, 0)}) + eq({{type='none'}, true}, {tv_dict_get_callback(nil, '')}) end) itp('works', function() local lua_d = { From 114eaa15f009cbd3e3deb177a2a67affa430fbb8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 28 Mar 2017 01:07:34 +0300 Subject: [PATCH 0259/1671] eval/typval,api/buffer: Fix review comments --- src/nvim/api/buffer.c | 6 +++--- src/nvim/eval/typval.c | 12 ++++-------- test/unit/eval/typval_spec.lua | 3 +++ 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 037a6ee1da..26f9a6f592 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -296,7 +296,7 @@ void nvim_buf_set_lines(uint64_t channel_id, tabpage_T *save_curtab = NULL; size_t new_len = replacement.size; size_t old_len = (size_t)(end - start); - ssize_t extra = 0; // lines added to text, can be negative + ptrdiff_t extra = 0; // lines added to text, can be negative char **lines = (new_len != 0) ? xcalloc(new_len, sizeof(char *)) : NULL; for (size_t i = 0; i < new_len; i++) { @@ -342,8 +342,8 @@ void nvim_buf_set_lines(uint64_t channel_id, } } - if ((ssize_t)to_delete > 0) { - extra -= (ssize_t)to_delete; + if (to_delete > 0) { + extra -= (ptrdiff_t)to_delete; } // For as long as possible, replace the existing old_len with the diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index da58cf5ca9..779bb18175 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -164,9 +164,8 @@ void tv_list_free_contents(list_T *const l) } l->lv_len = 0; l->lv_idx_item = NULL; - for (listwatch_T *lw = l->lv_watch; lw != NULL; lw = lw->lw_next) { - lw->lw_item = NULL; - } + l->lv_last = NULL; + assert(l->lv_watch == NULL); } /// Free a list itself, ignoring items it contains @@ -230,13 +229,10 @@ void tv_list_remove_items(list_T *const l, listitem_T *const item, listitem_T *const item2) FUNC_ATTR_NONNULL_ALL { - // notify watchers - for (listitem_T *ip = item; ip != NULL; ip = ip->li_next) { + // Notify watchers. + for (listitem_T *ip = item; ip != item2->li_next; ip = ip->li_next) { l->lv_len--; tv_list_watch_fix(l, ip); - if (ip == item2) { - break; - } } if (item2->li_next == NULL) { diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 3a4ef0cb92..258b5c4c1f 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -255,6 +255,9 @@ describe('typval.c', function() eq({lis[3], lis[5], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) alloc_log:clear() + lib.tv_list_watch_remove(l, lws[2]) + lib.tv_list_watch_remove(l, lws[3]) + lib.tv_list_watch_remove(l, lws[1]) lib.tv_list_free(l) alloc_log:check({ a.freed(lis[3]), From a1d590a08bd9d40d0e20a9907381573c2d069738 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 28 Mar 2017 02:17:51 +0300 Subject: [PATCH 0260/1671] *: Use const char * in set_one_cmd_context Also renames functions added in master and renamed here. --- src/nvim/charset.c | 23 ++- src/nvim/eval.c | 8 +- src/nvim/ex_cmds2.c | 12 +- src/nvim/ex_docmd.c | 444 +++++++++++++++++++++++-------------------- src/nvim/ex_getln.c | 9 +- src/nvim/if_cscope.c | 29 ++- src/nvim/syntax.c | 69 ++++--- 7 files changed, 311 insertions(+), 283 deletions(-) diff --git a/src/nvim/charset.c b/src/nvim/charset.c index ad0efa2c28..99d3e2dd88 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1397,7 +1397,8 @@ void getvcols(win_T *wp, pos_T *pos1, pos_T *pos2, colnr_T *left, /// /// @return Pointer to character after the skipped whitespace. char_u *skipwhite(const char_u *q) - FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_RET { const char_u *p = q; while (ascii_iswhite(*p)) { @@ -1406,19 +1407,21 @@ char_u *skipwhite(const char_u *q) return (char_u *)p; } -/// skip over digits +/// Skip over digits /// -/// @param q +/// @param[in] q String to skip digits in. /// /// @return Pointer to the character after the skipped digits. -char_u* skipdigits(char_u *q) +char_u *skipdigits(const char_u *q) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_RET { - char_u *p = q; + const char_u *p = q; while (ascii_isdigit(*p)) { // skip to next non-digit p++; } - return p; + return (char_u *)p; } /// skip over binary digits @@ -1564,17 +1567,17 @@ int vim_tolower(int c) return TOLOWER_LOC(c); } -/// skiptowhite: skip over text until ' ' or '\t' or NUL. +/// Skip over text until ' ' or '\t' or NUL /// -/// @param p +/// @param[in] p Text to skip over. /// /// @return Pointer to the next whitespace or NUL character. -char_u* skiptowhite(char_u *p) +char_u *skiptowhite(const char_u *p) { while (*p != ' ' && *p != '\t' && *p != NUL) { p++; } - return p; + return (char_u *)p; } /// skiptowhite_esc: Like skiptowhite(), but also skip escaped chars diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 6890bdc522..80c2fe10d7 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -9462,8 +9462,8 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - if (STRCMP(get_tv_string(&argvars[1]), "cmdline") == 0) { - set_one_cmd_context(&xpc, get_tv_string(&argvars[0])); + if (strcmp(tv_get_string(&argvars[1]), "cmdline") == 0) { + set_one_cmd_context(&xpc, tv_get_string(&argvars[0])); xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); goto theend; } @@ -9484,7 +9484,7 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (xpc.xp_context == EXPAND_CSCOPE) { - set_context_in_cscope_cmd(&xpc, xpc.xp_pattern, CMD_cscope); + set_context_in_cscope_cmd(&xpc, (const char *)xpc.xp_pattern, CMD_cscope); xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); } @@ -14562,7 +14562,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) } else if (title_arg->v_type == VAR_STRING) { title = tv_get_string_chk(title_arg); if (!title) { - // Type error. Error already printed by get_tv_string_chk(). + // Type error. Error already printed by tv_get_string_chk(). return; } } else if (title_arg->v_type == VAR_DICT) { diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 213641667d..eeace789b2 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -957,23 +957,21 @@ char_u *get_profile_name(expand_T *xp, int idx) } /// Handle command line completion for :profile command. -void set_context_in_profile_cmd(expand_T *xp, char_u *arg) +void set_context_in_profile_cmd(expand_T *xp, const char *arg) { - char_u *end_subcmd; - // Default: expand subcommands. xp->xp_context = EXPAND_PROFILE; pexpand_what = PEXP_SUBCMD; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; - end_subcmd = skiptowhite(arg); + char_u *const end_subcmd = skiptowhite((const char_u *)arg); if (*end_subcmd == NUL) { return; } - if (end_subcmd - arg == 5 && STRNCMP(arg, "start", 5) == 0) { + if ((const char *)end_subcmd - arg == 5 && strncmp(arg, "start", 5) == 0) { xp->xp_context = EXPAND_FILES; - xp->xp_pattern = skipwhite(end_subcmd); + xp->xp_pattern = skipwhite((const char_u *)end_subcmd); return; } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 26cfec991f..0fd4ae48be 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -2633,32 +2633,27 @@ int cmd_exists(const char *const name) * perfectly compatible with each other, but then the command line syntax * probably won't change that much -- webb. */ -char_u * -set_one_cmd_context ( +const char * set_one_cmd_context( expand_T *xp, - char_u *buff /* buffer for command string */ + const char *buff // buffer for command string ) { - char_u *p; - char_u *cmd, *arg; - int len = 0; + size_t len = 0; exarg_T ea; - int compl = EXPAND_NOTHING; - int delim; - int forceit = FALSE; - int usefilter = FALSE; /* filter instead of file name */ + int context = EXPAND_NOTHING; + int forceit = false; + int usefilter = false; // Filter instead of file name. ExpandInit(xp); - xp->xp_pattern = buff; - xp->xp_context = EXPAND_COMMANDS; /* Default until we get past command */ + xp->xp_pattern = (char_u *)buff; + xp->xp_context = EXPAND_COMMANDS; // Default until we get past command ea.argt = 0; - /* - * 2. skip comment lines and leading space, colons or bars - */ - for (cmd = buff; vim_strchr((char_u *)" \t:|", *cmd) != NULL; cmd++) - ; - xp->xp_pattern = cmd; + // 2. skip comment lines and leading space, colons or bars + const char *cmd; + for (cmd = buff; strchr(" \t:|", *cmd) != NULL; cmd++) { + } + xp->xp_pattern = (char_u *)cmd; if (*cmd == NUL) return NULL; @@ -2670,14 +2665,15 @@ set_one_cmd_context ( /* * 3. parse a range specifier of the form: addr [,addr] [;addr] .. */ - cmd = skip_range(cmd, &xp->xp_context); + cmd = (const char *)skip_range((const char_u *)cmd, &xp->xp_context); /* * 4. parse command */ - xp->xp_pattern = cmd; - if (*cmd == NUL) + xp->xp_pattern = (char_u *)cmd; + if (*cmd == NUL) { return NULL; + } if (*cmd == '"') { xp->xp_context = EXPAND_NOTHING; return NULL; @@ -2693,6 +2689,7 @@ set_one_cmd_context ( * do accept "keepmarks", "keepalt" and "keepjumps". * - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r' */ + const char *p; if (*cmd == 'k' && cmd[1] != 'e') { ea.cmdidx = CMD_k; p = cmd + 1; @@ -2715,20 +2712,21 @@ set_one_cmd_context ( } } // check for non-alpha command - if (p == cmd && vim_strchr((char_u *)"@*!=><&~#", *p) != NULL) { + if (p == cmd && strchr("@*!=><&~#", *p) != NULL) { p++; } - len = (int)(p - cmd); + len = (size_t)(p - cmd); if (len == 0) { xp->xp_context = EXPAND_UNSUCCESSFUL; return NULL; } for (ea.cmdidx = (cmdidx_T)0; (int)ea.cmdidx < (int)CMD_SIZE; - ea.cmdidx = (cmdidx_T)((int)ea.cmdidx + 1)) - if (STRNCMP(cmdnames[(int)ea.cmdidx].cmd_name, cmd, - (size_t)len) == 0) + ea.cmdidx = (cmdidx_T)((int)ea.cmdidx + 1)) { + if (STRNCMP(cmdnames[(int)ea.cmdidx].cmd_name, cmd, len) == 0) { break; + } + } if (cmd[0] >= 'A' && cmd[0] <= 'Z') { while (ASCII_ISALNUM(*p) || *p == '*') { // Allow * wild card @@ -2745,16 +2743,15 @@ set_one_cmd_context ( return NULL; if (ea.cmdidx == CMD_SIZE) { - if (*cmd == 's' && vim_strchr((char_u *)"cgriI", cmd[1]) != NULL) { + if (*cmd == 's' && strchr("cgriI", cmd[1]) != NULL) { ea.cmdidx = CMD_substitute; p = cmd + 1; } else if (cmd[0] >= 'A' && cmd[0] <= 'Z') { - ea.cmd = cmd; - p = find_ucmd(&ea, p, NULL, xp, - &compl - ); - if (p == NULL) - ea.cmdidx = CMD_SIZE; /* ambiguous user command */ + ea.cmd = (char_u *)cmd; + p = (const char *)find_ucmd(&ea, (char_u *)p, NULL, xp, &context); + if (p == NULL) { + ea.cmdidx = CMD_SIZE; // Ambiguous user command. + } } } if (ea.cmdidx == CMD_SIZE) { @@ -2777,16 +2774,17 @@ set_one_cmd_context ( ea.argt = cmdnames[(int)ea.cmdidx].cmd_argt; } - arg = skipwhite(p); + const char *arg = (const char *)skipwhite((const char_u *)p); if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) { - if (*arg == '>') { /* append */ - if (*++arg == '>') - ++arg; - arg = skipwhite(arg); - } else if (*arg == '!' && ea.cmdidx == CMD_write) { /* :w !filter */ - ++arg; - usefilter = TRUE; + if (*arg == '>') { // Append. + if (*++arg == '>') { + arg++; + } + arg = (const char *)skipwhite((const char_u *)arg); + } else if (*arg == '!' && ea.cmdidx == CMD_write) { // :w !filter + arg++; + usefilter = true; } } @@ -2799,23 +2797,24 @@ set_one_cmd_context ( } if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift) { - while (*arg == *cmd) /* allow any number of '>' or '<' */ - ++arg; - arg = skipwhite(arg); + while (*arg == *cmd) { // allow any number of '>' or '<' + arg++; + } + arg = (const char *)skipwhite((const char_u *)arg); } /* Does command allow "+command"? */ if ((ea.argt & EDITCMD) && !usefilter && *arg == '+') { /* Check if we're in the +command */ p = arg + 1; - arg = skip_cmd_arg(arg, FALSE); + arg = (const char *)skip_cmd_arg((char_u *)arg, false); /* Still touching the command after '+'? */ if (*arg == NUL) return p; - /* Skip space(s) after +command to get to the real argument */ - arg = skipwhite(arg); + // Skip space(s) after +command to get to the real argument. + arg = (const char *)skipwhite((const char_u *)arg); } /* @@ -2844,19 +2843,18 @@ set_one_cmd_context ( } // no arguments allowed - if (!(ea.argt & EXTRA) && *arg != NUL - && vim_strchr((char_u *)"|\"", *arg) == NULL) { + if (!(ea.argt & EXTRA) && *arg != NUL && strchr("|\"", *arg) == NULL) { return NULL; } /* Find start of last argument (argument just before cursor): */ p = buff; - xp->xp_pattern = p; - len = (int)STRLEN(buff); + xp->xp_pattern = (char_u *)p; + len = strlen(buff); while (*p && p < buff + len) { if (*p == ' ' || *p == TAB) { - /* argument starts after a space */ - xp->xp_pattern = ++p; + // Argument starts after a space. + xp->xp_pattern = (char_u *)++p; } else { if (*p == '\\' && *(p + 1) != NUL) ++p; /* skip over escaped character */ @@ -2866,25 +2864,26 @@ set_one_cmd_context ( if (ea.argt & XFILE) { int c; - int in_quote = FALSE; - char_u *bow = NULL; /* Beginning of word */ + int in_quote = false; + const char *bow = NULL; // Beginning of word. /* * Allow spaces within back-quotes to count as part of the argument * being expanded. */ - xp->xp_pattern = skipwhite(arg); - p = xp->xp_pattern; + xp->xp_pattern = skipwhite((const char_u *)arg); + p = (const char *)xp->xp_pattern; while (*p != NUL) { - if (has_mbyte) - c = mb_ptr2char(p); - else - c = *p; - if (c == '\\' && p[1] != NUL) - ++p; - else if (c == '`') { + if (has_mbyte) { + c = mb_ptr2char((const char_u *)p); + } else { + c = (uint8_t)(*p); + } + if (c == '\\' && p[1] != NUL) { + p++; + } else if (c == '`') { if (!in_quote) { - xp->xp_pattern = p; + xp->xp_pattern = (char_u *)p; bow = p + 1; } in_quote = !in_quote; @@ -2897,22 +2896,26 @@ set_one_cmd_context ( || ascii_iswhite(c)) { len = 0; /* avoid getting stuck when space is in 'isfname' */ while (*p != NUL) { - if (has_mbyte) - c = mb_ptr2char(p); - else + if (has_mbyte) { + c = mb_ptr2char((const char_u *)p); + } else { c = *p; - if (c == '`' || vim_isfilec_or_wc(c)) + } + if (c == '`' || vim_isfilec_or_wc(c)) { break; - if (has_mbyte) - len = (*mb_ptr2len)(p); - else + } + if (has_mbyte) { + len = (size_t)(*mb_ptr2len)((const char_u *)p); + } else { len = 1; + } mb_ptr_adv(p); } - if (in_quote) + if (in_quote) { bow = p; - else - xp->xp_pattern = p; + } else { + xp->xp_pattern = (char_u *)p; + } p -= len; } mb_ptr_adv(p); @@ -2922,8 +2925,9 @@ set_one_cmd_context ( * If we are still inside the quotes, and we passed a space, just * expand from there. */ - if (bow != NULL && in_quote) - xp->xp_pattern = bow; + if (bow != NULL && in_quote) { + xp->xp_pattern = (char_u *)bow; + } xp->xp_context = EXPAND_FILES; /* For a shell command more chars need to be escaped. */ @@ -2931,33 +2935,36 @@ set_one_cmd_context ( #ifndef BACKSLASH_IN_FILENAME xp->xp_shell = TRUE; #endif - /* When still after the command name expand executables. */ - if (xp->xp_pattern == skipwhite(arg)) + // When still after the command name expand executables. + if (xp->xp_pattern == skipwhite((const char_u *)arg)) { xp->xp_context = EXPAND_SHELLCMD; + } } - /* Check for environment variable */ - if (*xp->xp_pattern == '$' - ) { - for (p = xp->xp_pattern + 1; *p != NUL; ++p) - if (!vim_isIDc(*p)) + // Check for environment variable. + if (*xp->xp_pattern == '$') { + for (p = (const char *)xp->xp_pattern + 1; *p != NUL; p++) { + if (!vim_isIDc((uint8_t)(*p))) { break; + } + } if (*p == NUL) { xp->xp_context = EXPAND_ENV_VARS; - ++xp->xp_pattern; - /* Avoid that the assignment uses EXPAND_FILES again. */ - if (compl != EXPAND_USER_DEFINED && compl != EXPAND_USER_LIST) - compl = EXPAND_ENV_VARS; + xp->xp_pattern++; + // Avoid that the assignment uses EXPAND_FILES again. + if (context != EXPAND_USER_DEFINED && context != EXPAND_USER_LIST) { + context = EXPAND_ENV_VARS; + } } } /* Check for user names */ if (*xp->xp_pattern == '~') { - for (p = xp->xp_pattern + 1; *p != NUL && *p != '/'; ++p) - ; - /* Complete ~user only if it partially matches a user name. - * A full match ~user will be replaced by user's home - * directory i.e. something like ~user -> /home/user/ */ - if (*p == NUL && p > xp->xp_pattern + 1 + for (p = (const char *)xp->xp_pattern + 1; *p != NUL && *p != '/'; p++) { + } + // Complete ~user only if it partially matches a user name. + // A full match ~user will be replaced by user's home + // directory i.e. something like ~user -> /home/user/ + if (*p == NUL && p > (const char *)xp->xp_pattern + 1 && match_user(xp->xp_pattern + 1) == 1) { xp->xp_context = EXPAND_USER; ++xp->xp_pattern; @@ -2987,7 +2994,7 @@ set_one_cmd_context ( break; case CMD_help: xp->xp_context = EXPAND_HELP; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; /* Command modifiers: return the argument. @@ -3030,13 +3037,14 @@ set_one_cmd_context ( if (*arg == NUL || !ends_excmd(*arg)) { /* also complete "None" */ set_context_in_echohl_cmd(xp, arg); - arg = skipwhite(skiptowhite(arg)); + arg = (const char *)skipwhite(skiptowhite((const char_u *)arg)); if (*arg != NUL) { xp->xp_context = EXPAND_NOTHING; - arg = skip_regexp(arg + 1, *arg, p_magic, NULL); + arg = (const char *)skip_regexp((char_u *)arg + 1, (uint8_t)(*arg), + p_magic, NULL); } } - return find_nextcmd(arg); + return (const char *)find_nextcmd((char_u *)arg); /* * All completion for the +cmdline_compl feature goes here. @@ -3045,15 +3053,15 @@ set_one_cmd_context ( case CMD_command: /* Check for attributes */ while (*arg == '-') { - arg++; /* Skip "-" */ - p = skiptowhite(arg); + arg++; // Skip "-". + p = (const char *)skiptowhite((const char_u *)arg); if (*p == NUL) { - /* Cursor is still in the attribute */ - p = vim_strchr(arg, '='); + // Cursor is still in the attribute. + p = strchr(arg, '='); if (p == NULL) { - /* No "=", so complete attribute names */ + // No "=", so complete attribute names. xp->xp_context = EXPAND_USER_CMD_FLAGS; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; return NULL; } @@ -3061,73 +3069,81 @@ set_one_cmd_context ( // their arguments as well. if (STRNICMP(arg, "complete", p - arg) == 0) { xp->xp_context = EXPAND_USER_COMPLETE; - xp->xp_pattern = p + 1; + xp->xp_pattern = (char_u *)p + 1; return NULL; } else if (STRNICMP(arg, "nargs", p - arg) == 0) { xp->xp_context = EXPAND_USER_NARGS; - xp->xp_pattern = p + 1; + xp->xp_pattern = (char_u *)p + 1; return NULL; } else if (STRNICMP(arg, "addr", p - arg) == 0) { xp->xp_context = EXPAND_USER_ADDR_TYPE; - xp->xp_pattern = p + 1; + xp->xp_pattern = (char_u *)p + 1; return NULL; } return NULL; } - arg = skipwhite(p); + arg = (const char *)skipwhite((char_u *)p); } - /* After the attributes comes the new command name */ - p = skiptowhite(arg); + // After the attributes comes the new command name. + p = (const char *)skiptowhite((const char_u *)arg); if (*p == NUL) { xp->xp_context = EXPAND_USER_COMMANDS; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; } - /* And finally comes a normal command */ - return skipwhite(p); + // And finally comes a normal command. + return (const char *)skipwhite((const char_u *)p); case CMD_delcommand: xp->xp_context = EXPAND_USER_COMMANDS; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_global: - case CMD_vglobal: - delim = *arg; /* get the delimiter */ - if (delim) - ++arg; /* skip delimiter if there is one */ + case CMD_vglobal: { + const int delim = (uint8_t)(*arg); // Get the delimiter. + if (delim) { + arg++; // Skip delimiter if there is one. + } - while (arg[0] != NUL && arg[0] != delim) { - if (arg[0] == '\\' && arg[1] != NUL) - ++arg; - ++arg; + while (arg[0] != NUL && (uint8_t)arg[0] != delim) { + if (arg[0] == '\\' && arg[1] != NUL) { + arg++; + } + arg++; } if (arg[0] != NUL) return arg + 1; break; + } case CMD_and: - case CMD_substitute: - delim = *arg; + case CMD_substitute: { + const int delim = (uint8_t)(*arg); if (delim) { - /* skip "from" part */ - ++arg; - arg = skip_regexp(arg, delim, p_magic, NULL); + // Skip "from" part. + arg++; + arg = (const char *)skip_regexp((char_u *)arg, delim, p_magic, NULL); } - /* skip "to" part */ - while (arg[0] != NUL && arg[0] != delim) { - if (arg[0] == '\\' && arg[1] != NUL) - ++arg; - ++arg; + // Skip "to" part. + while (arg[0] != NUL && (uint8_t)arg[0] != delim) { + if (arg[0] == '\\' && arg[1] != NUL) { + arg++; + } + arg++; } - if (arg[0] != NUL) /* skip delimiter */ - ++arg; - while (arg[0] && vim_strchr((char_u *)"|\"#", arg[0]) == NULL) - ++arg; - if (arg[0] != NUL) + if (arg[0] != NUL) { // Skip delimiter. + arg++; + } + while (arg[0] && strchr("|\"#", arg[0]) == NULL) { + arg++; + } + if (arg[0] != NUL) { return arg; + } break; + } case CMD_isearch: case CMD_dsearch: case CMD_ilist: @@ -3137,36 +3153,40 @@ set_one_cmd_context ( case CMD_djump: case CMD_isplit: case CMD_dsplit: - arg = skipwhite(skipdigits(arg)); /* skip count */ - if (*arg == '/') { /* Match regexp, not just whole words */ - for (++arg; *arg && *arg != '/'; arg++) - if (*arg == '\\' && arg[1] != NUL) + // Skip count. + arg = (const char *)skipwhite(skipdigits((const char_u *)arg)); + if (*arg == '/') { // Match regexp, not just whole words. + for (++arg; *arg && *arg != '/'; arg++) { + if (*arg == '\\' && arg[1] != NUL) { arg++; + } + } if (*arg) { - arg = skipwhite(arg + 1); + arg = (const char *)skipwhite((const char_u *)arg + 1); - /* Check for trailing illegal characters */ - if (*arg && vim_strchr((char_u *)"|\"\n", *arg) == NULL) + // Check for trailing illegal characters. + if (*arg && strchr("|\"\n", *arg) == NULL) { xp->xp_context = EXPAND_NOTHING; - else + } else { return arg; + } } } break; case CMD_autocmd: - return set_context_in_autocmd(xp, arg, FALSE); + return (const char *)set_context_in_autocmd(xp, (char_u *)arg, false); case CMD_doautocmd: case CMD_doautoall: - return set_context_in_autocmd(xp, arg, TRUE); + return (const char *)set_context_in_autocmd(xp, (char_u *)arg, true); case CMD_set: - set_context_in_set_cmd(xp, arg, 0); + set_context_in_set_cmd(xp, (char_u *)arg, 0); break; case CMD_setglobal: - set_context_in_set_cmd(xp, arg, OPT_GLOBAL); + set_context_in_set_cmd(xp, (char_u *)arg, OPT_GLOBAL); break; case CMD_setlocal: - set_context_in_set_cmd(xp, arg, OPT_LOCAL); + set_context_in_set_cmd(xp, (char_u *)arg, OPT_LOCAL); break; case CMD_tag: case CMD_stag: @@ -3178,15 +3198,16 @@ set_one_cmd_context ( case CMD_tjump: case CMD_stjump: case CMD_ptjump: - if (*p_wop != NUL) + if (*p_wop != NUL) { xp->xp_context = EXPAND_TAGS_LISTFILES; - else + } else { xp->xp_context = EXPAND_TAGS; - xp->xp_pattern = arg; + } + xp->xp_pattern = (char_u *)arg; break; case CMD_augroup: xp->xp_context = EXPAND_AUGROUP; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_syntax: set_context_in_syntax_cmd(xp, arg); @@ -3203,20 +3224,21 @@ set_one_cmd_context ( case CMD_echoerr: case CMD_call: case CMD_return: - set_context_for_expression(xp, arg, ea.cmdidx); + set_context_for_expression(xp, (char_u *)arg, ea.cmdidx); break; case CMD_unlet: - while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL) - arg = xp->xp_pattern + 1; + while ((xp->xp_pattern = (char_u *)strchr(arg, ' ')) != NULL) { + arg = (const char *)xp->xp_pattern + 1; + } xp->xp_context = EXPAND_USER_VARS; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_function: case CMD_delfunction: xp->xp_context = EXPAND_USER_FUNC; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_echohl: @@ -3231,33 +3253,37 @@ set_one_cmd_context ( set_context_in_cscope_cmd(xp, arg, ea.cmdidx); break; case CMD_sign: - set_context_in_sign_cmd(xp, arg); + set_context_in_sign_cmd(xp, (char_u *)arg); break; case CMD_bdelete: case CMD_bwipeout: case CMD_bunload: - while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL) - arg = xp->xp_pattern + 1; - /*FALLTHROUGH*/ + while ((xp->xp_pattern = (char_u *)strchr(arg, ' ')) != NULL) { + arg = (const char *)xp->xp_pattern + 1; + } + // FALLTHROUGH case CMD_buffer: case CMD_sbuffer: case CMD_checktime: xp->xp_context = EXPAND_BUFFERS; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_USER: case CMD_USER_BUF: - if (compl != EXPAND_NOTHING) { - /* XFILE: file names are handled above */ + if (context != EXPAND_NOTHING) { + // XFILE: file names are handled above. if (!(ea.argt & XFILE)) { - if (compl == EXPAND_MENUS) - return set_context_in_menu_cmd(xp, cmd, arg, forceit); - if (compl == EXPAND_COMMANDS) + if (context == EXPAND_MENUS) { + return (const char *)set_context_in_menu_cmd(xp, (char_u *)cmd, + (char_u *)arg, forceit); + } else if (context == EXPAND_COMMANDS) { return arg; - if (compl == EXPAND_MAPPINGS) - return set_context_in_map_cmd(xp, (char_u *)"map", - arg, forceit, FALSE, FALSE, CMD_map); - /* Find start of last argument. */ + } else if (context == EXPAND_MAPPINGS) { + return (const char *)set_context_in_map_cmd( + xp, (char_u *)"map", (char_u *)arg, forceit, false, false, + CMD_map); + } + // Find start of last argument. p = arg; while (*p) { if (*p == ' ') @@ -3267,9 +3293,9 @@ set_one_cmd_context ( ++p; /* skip over escaped character */ mb_ptr_adv(p); } - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; } - xp->xp_context = compl; + xp->xp_context = context; } break; case CMD_map: case CMD_noremap: @@ -3281,8 +3307,8 @@ set_one_cmd_context ( case CMD_lmap: case CMD_lnoremap: case CMD_smap: case CMD_snoremap: case CMD_xmap: case CMD_xnoremap: - return set_context_in_map_cmd(xp, cmd, arg, forceit, - FALSE, FALSE, ea.cmdidx); + return (const char *)set_context_in_map_cmd( + xp, (char_u *)cmd, (char_u *)arg, forceit, false, false, ea.cmdidx); case CMD_unmap: case CMD_nunmap: case CMD_vunmap: @@ -3292,18 +3318,18 @@ set_one_cmd_context ( case CMD_lunmap: case CMD_sunmap: case CMD_xunmap: - return set_context_in_map_cmd(xp, cmd, arg, forceit, - FALSE, TRUE, ea.cmdidx); + return (const char *)set_context_in_map_cmd( + xp, (char_u *)cmd, (char_u *)arg, forceit, false, true, ea.cmdidx); case CMD_abbreviate: case CMD_noreabbrev: case CMD_cabbrev: case CMD_cnoreabbrev: case CMD_iabbrev: case CMD_inoreabbrev: - return set_context_in_map_cmd(xp, cmd, arg, forceit, - TRUE, FALSE, ea.cmdidx); + return (const char *)set_context_in_map_cmd( + xp, (char_u *)cmd, (char_u *)arg, forceit, true, false, ea.cmdidx); case CMD_unabbreviate: case CMD_cunabbrev: case CMD_iunabbrev: - return set_context_in_map_cmd(xp, cmd, arg, forceit, - TRUE, TRUE, ea.cmdidx); + return (const char *)set_context_in_map_cmd( + xp, (char_u *)cmd, (char_u *)arg, forceit, true, true, ea.cmdidx); case CMD_menu: case CMD_noremenu: case CMD_unmenu: case CMD_amenu: case CMD_anoremenu: case CMD_aunmenu: case CMD_nmenu: case CMD_nnoremenu: case CMD_nunmenu: @@ -3313,47 +3339,49 @@ set_one_cmd_context ( case CMD_cmenu: case CMD_cnoremenu: case CMD_cunmenu: case CMD_tmenu: case CMD_tunmenu: case CMD_popup: case CMD_emenu: - return set_context_in_menu_cmd(xp, cmd, arg, forceit); + return (const char *)set_context_in_menu_cmd( + xp, (char_u *)cmd, (char_u *)arg, forceit); case CMD_colorscheme: xp->xp_context = EXPAND_COLORS; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_compiler: xp->xp_context = EXPAND_COMPILER; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_ownsyntax: xp->xp_context = EXPAND_OWNSYNTAX; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_setfiletype: xp->xp_context = EXPAND_FILETYPE; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_packadd: xp->xp_context = EXPAND_PACKADD; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; #ifdef HAVE_WORKING_LIBINTL case CMD_language: - p = skiptowhite(arg); + p = (const char *)skiptowhite((const char_u *)arg); if (*p == NUL) { xp->xp_context = EXPAND_LANGUAGE; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; } else { - if ( STRNCMP(arg, "messages", p - arg) == 0 - || STRNCMP(arg, "ctype", p - arg) == 0 - || STRNCMP(arg, "time", p - arg) == 0) { + if (strncmp(arg, "messages", p - arg) == 0 + || strncmp(arg, "ctype", p - arg) == 0 + || strncmp(arg, "time", p - arg) == 0) { xp->xp_context = EXPAND_LOCALES; - xp->xp_pattern = skipwhite(p); - } else + xp->xp_pattern = skipwhite((const char_u *)p); + } else { xp->xp_context = EXPAND_NOTHING; + } } break; #endif @@ -3362,16 +3390,16 @@ set_one_cmd_context ( break; case CMD_behave: xp->xp_context = EXPAND_BEHAVE; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_history: xp->xp_context = EXPAND_HISTORY; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_syntime: xp->xp_context = EXPAND_SYNTIME; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; @@ -3390,10 +3418,9 @@ set_one_cmd_context ( * Also skip white space and ":" characters. * Returns the "cmd" pointer advanced to beyond the range. */ -char_u * -skip_range ( - char_u *cmd, - int *ctx /* pointer to xp_context or NULL */ +char_u *skip_range( + const char_u *cmd, + int *ctx // pointer to xp_context or NULL ) { unsigned delim; @@ -3418,7 +3445,7 @@ skip_range ( while (*cmd == ':') cmd = skipwhite(cmd + 1); - return cmd; + return (char_u *)cmd; } /* @@ -4585,14 +4612,15 @@ int ends_excmd(int c) FUNC_ATTR_CONST * Return the next command, after the first '|' or '\n'. * Return NULL if not found. */ -char_u *find_nextcmd(char_u *p) +char_u *find_nextcmd(const char_u *p) { while (*p != '|' && *p != '\n') { - if (*p == NUL) + if (*p == NUL) { return NULL; - ++p; + } + p++; } - return p + 1; + return (char_u *)p + 1; } /* diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index e140dfa886..8810204c03 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -3636,7 +3636,6 @@ set_cmd_context ( ) { int old_char = NUL; - char_u *nextcomm; /* * Avoid a UMR warning from Purify, only save the character if it has been @@ -3645,7 +3644,7 @@ set_cmd_context ( if (col < len) old_char = str[col]; str[col] = NUL; - nextcomm = str; + const char *nextcomm = (const char *)str; if (use_ccline && ccline.cmdfirstc == '=') { // pass CMD_SIZE because there is no real command @@ -3654,9 +3653,11 @@ set_cmd_context ( xp->xp_context = ccline.xp_context; xp->xp_pattern = ccline.cmdbuff; xp->xp_arg = ccline.xp_arg; - } else - while (nextcomm != NULL) + } else { + while (nextcomm != NULL) { nextcomm = set_one_cmd_context(xp, nextcomm); + } + } /* Store the string here so that call_user_expand_func() can get to them * easily. */ diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index 550d256de5..b647b8146a 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -140,31 +140,30 @@ char_u *get_cscope_name(expand_T *xp, int idx) /* * Handle command line completion for :cscope command. */ -void set_context_in_cscope_cmd(expand_T *xp, char_u *arg, cmdidx_T cmdidx) +void set_context_in_cscope_cmd(expand_T *xp, const char *arg, cmdidx_T cmdidx) { - char_u *p; - - /* Default: expand subcommands */ + // Default: expand subcommands. xp->xp_context = EXPAND_CSCOPE; - xp->xp_pattern = arg; - expand_what = (cmdidx == CMD_scscope) - ? EXP_SCSCOPE_SUBCMD : EXP_CSCOPE_SUBCMD; + xp->xp_pattern = (char_u *)arg; + expand_what = ((cmdidx == CMD_scscope) + ? EXP_SCSCOPE_SUBCMD : EXP_CSCOPE_SUBCMD); /* (part of) subcommand already typed */ if (*arg != NUL) { - p = skiptowhite(arg); - if (*p != NUL) { /* past first word */ - xp->xp_pattern = skipwhite(p); - if (*skiptowhite(xp->xp_pattern) != NUL) + const char *p = (const char *)skiptowhite((const char_u *)arg); + if (*p != NUL) { // Past first word. + xp->xp_pattern = skipwhite((const char_u *)p); + if (*skiptowhite(xp->xp_pattern) != NUL) { xp->xp_context = EXPAND_NOTHING; - else if (STRNICMP(arg, "add", p - arg) == 0) + } else if (STRNICMP(arg, "add", p - arg) == 0) { xp->xp_context = EXPAND_FILES; - else if (STRNICMP(arg, "kill", p - arg) == 0) + } else if (STRNICMP(arg, "kill", p - arg) == 0) { expand_what = EXP_CSCOPE_KILL; - else if (STRNICMP(arg, "find", p - arg) == 0) + } else if (STRNICMP(arg, "find", p - arg) == 0) { expand_what = EXP_CSCOPE_FIND; - else + } else { xp->xp_context = EXPAND_NOTHING; + } } } } diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index a54e36a609..4a7b4a0eac 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -5574,43 +5574,42 @@ void reset_expand_highlight(void) * Handle command line completion for :match and :echohl command: Add "None" * as highlight group. */ -void set_context_in_echohl_cmd(expand_T *xp, char_u *arg) +void set_context_in_echohl_cmd(expand_T *xp, const char *arg) { xp->xp_context = EXPAND_HIGHLIGHT; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; include_none = 1; } /* * Handle command line completion for :syntax command. */ -void set_context_in_syntax_cmd(expand_T *xp, char_u *arg) +void set_context_in_syntax_cmd(expand_T *xp, const char *arg) { - char_u *p; - - /* Default: expand subcommands */ + // Default: expand subcommands. xp->xp_context = EXPAND_SYNTAX; expand_what = EXP_SUBCMD; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; include_link = 0; include_default = 0; /* (part of) subcommand already typed */ if (*arg != NUL) { - p = skiptowhite(arg); - if (*p != NUL) { /* past first word */ - xp->xp_pattern = skipwhite(p); - if (*skiptowhite(xp->xp_pattern) != NUL) + const char *p = (const char *)skiptowhite((const char_u *)arg); + if (*p != NUL) { // Past first word. + xp->xp_pattern = skipwhite((const char_u *)p); + if (*skiptowhite(xp->xp_pattern) != NUL) { xp->xp_context = EXPAND_NOTHING; - else if (STRNICMP(arg, "case", p - arg) == 0) + } else if (STRNICMP(arg, "case", p - arg) == 0) { expand_what = EXP_CASE; - else if ( STRNICMP(arg, "keyword", p - arg) == 0 + } else if (STRNICMP(arg, "keyword", p - arg) == 0 || STRNICMP(arg, "region", p - arg) == 0 || STRNICMP(arg, "match", p - arg) == 0 - || STRNICMP(arg, "list", p - arg) == 0) + || STRNICMP(arg, "list", p - arg) == 0) { xp->xp_context = EXPAND_HIGHLIGHT; - else + } else { xp->xp_context = EXPAND_NOTHING; + } } } } @@ -7474,41 +7473,41 @@ int highlight_changed(void) /* * Handle command line completion for :highlight command. */ -void set_context_in_highlight_cmd(expand_T *xp, char_u *arg) +void set_context_in_highlight_cmd(expand_T *xp, const char *arg) { - char_u *p; - - /* Default: expand group names */ + // Default: expand group names. xp->xp_context = EXPAND_HIGHLIGHT; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; include_link = 2; include_default = 1; /* (part of) subcommand already typed */ if (*arg != NUL) { - p = skiptowhite(arg); - if (*p != NUL) { /* past "default" or group name */ + const char *p = (const char *)skiptowhite((const char_u *)arg); + if (*p != NUL) { // Past "default" or group name. include_default = 0; - if (STRNCMP("default", arg, p - arg) == 0) { - arg = skipwhite(p); - xp->xp_pattern = arg; - p = skiptowhite(arg); + if (strncmp("default", arg, p - arg) == 0) { + arg = (const char *)skipwhite((const char_u *)p); + xp->xp_pattern = (char_u *)arg; + p = (const char *)skiptowhite((const char_u *)arg); } if (*p != NUL) { /* past group name */ include_link = 0; - if (arg[1] == 'i' && arg[0] == 'N') + if (arg[1] == 'i' && arg[0] == 'N') { highlight_list(); - if (STRNCMP("link", arg, p - arg) == 0 - || STRNCMP("clear", arg, p - arg) == 0) { - xp->xp_pattern = skipwhite(p); - p = skiptowhite(xp->xp_pattern); - if (*p != NUL) { /* past first group name */ - xp->xp_pattern = skipwhite(p); - p = skiptowhite(xp->xp_pattern); + } + if (strncmp("link", arg, p - arg) == 0 + || strncmp("clear", arg, p - arg) == 0) { + xp->xp_pattern = skipwhite((const char_u *)p); + p = (const char *)skiptowhite(xp->xp_pattern); + if (*p != NUL) { // Past first group name. + xp->xp_pattern = skipwhite((const char_u *)p); + p = (const char *)skiptowhite(xp->xp_pattern); } } - if (*p != NUL) /* past group name(s) */ + if (*p != NUL) { // Past group name(s). xp->xp_context = EXPAND_NOTHING; + } } } } From b9603218be3b7bf3fbc30eee6c7c458b01902584 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 28 Mar 2017 08:12:09 +0300 Subject: [PATCH 0261/1671] eval/executor: Fix check-single-includes --- src/nvim/eval/executor.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/nvim/eval/executor.h b/src/nvim/eval/executor.h index 19e2a75914..3d789f76a5 100644 --- a/src/nvim/eval/executor.h +++ b/src/nvim/eval/executor.h @@ -1,6 +1,8 @@ #ifndef NVIM_EVAL_EXECUTOR_H #define NVIM_EVAL_EXECUTOR_H +#include "nvim/eval/typval.h" + extern char *e_listidx; #ifdef INCLUDE_GENERATED_DECLARATIONS From 46efe14473fa803f84509592cc1e8fca4eb20640 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 28 Mar 2017 08:18:23 +0300 Subject: [PATCH 0262/1671] functests: Try sleeping a bit more --- test/functional/eval/timer_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/eval/timer_spec.lua b/test/functional/eval/timer_spec.lua index 4353619ff0..b3c4cd07eb 100644 --- a/test/functional/eval/timer_spec.lua +++ b/test/functional/eval/timer_spec.lua @@ -49,7 +49,7 @@ describe('timers', function() it('works with zero timeout', function() -- timer_start does still not invoke the callback immediately eq(0,eval("[timer_start(0, 'MyHandler', {'repeat': 1000}), g:val][1]")) - run(nil, nil, nil, 300) + run(nil, nil, nil, 400) eq(1000,eval("g:val")) end) From 2846d508b24eb9f8ab59d21a060fd8130906392f Mon Sep 17 00:00:00 2001 From: lonerover Date: Wed, 29 Mar 2017 22:48:50 +0800 Subject: [PATCH 0263/1671] vim-patch:7.4.2276 (#6393) * vim-patch:7.4.2276 Problem: Command line test fails on Windows when run twice. Solution: Wipe the buffer so that the directory can be deleted. https://github.com/vim/vim/commit/1773ddfdcd106fa3bbf479c9b62ccde03c2a86ba * version.c: mark vim-patch 7.4.2269 as included (#5659) --- src/nvim/testdir/test_cmdline.vim | 1 + src/nvim/version.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index f56227250c..0c9d1297d6 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -201,5 +201,6 @@ func Test_expand_star_star() call writefile(['asdfasdf'], 'a/b/fileXname') call feedkeys(":find **/fileXname\\", 'xt') call assert_equal('find a/b/fileXname', getreg(':')) + bwipe! call delete('a', 'rf') endfunc diff --git a/src/nvim/version.c b/src/nvim/version.c index dd583f6ffd..020737ac8c 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -165,14 +165,14 @@ static int included_patches[] = { 2279, // 2278 NA 2277, - // 2276, + 2276, 2275, 2274, 2273, 2272, // 2271 NA // 2270 NA - // 2269, + 2269, // 2268, // 2267 NA 2266, From 05b74399aa40c813b195d595aa3878ff42e61ab8 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 29 Mar 2017 17:39:06 +0200 Subject: [PATCH 0264/1671] build: remove unused get_preproc_output() call ref https://github.com/neovim/neovim/pull/6375#discussion_r108573471 --- src/nvim/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index e752f5d4df..bad43e7f72 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -461,10 +461,8 @@ foreach(hfile ${NVIM_HEADERS}) if(NOT ${hfile} MATCHES "[.]c[.]h$") set(tsource "${GENERATED_DIR}/${r}.test-include.c") - set(tresult "${GENERATED_DIR}/${r}.test-include.i") string(REPLACE "/" "-" texe "test-incl-${r}") write_file("${tsource}" "#include \"${hfile}\"\nint main(int argc, char **argv) { return 0; }") - get_preproc_output(PREPROC_OUTPUT ${tresult}) add_executable( ${texe} EXCLUDE_FROM_ALL From afacda046d4cce07cb80ba2cf43341c03131c683 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 29 Mar 2017 17:45:48 +0200 Subject: [PATCH 0265/1671] ci: Run check-single-includes in "lint" build only --- .ci/common/test.sh | 4 ---- .ci/run_tests.sh | 1 - Makefile | 4 ++-- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.ci/common/test.sh b/.ci/common/test.sh index 4137472385..b28e46a4df 100644 --- a/.ci/common/test.sh +++ b/.ci/common/test.sh @@ -109,10 +109,6 @@ run_oldtests() { check_core_dumps } -run_single_includes_tests() { - ${MAKE_CMD} -C "${BUILD_DIR}" check-single-includes -} - install_nvim() { ${MAKE_CMD} -C "${BUILD_DIR}" install diff --git a/.ci/run_tests.sh b/.ci/run_tests.sh index d994db471f..6347ac15d4 100755 --- a/.ci/run_tests.sh +++ b/.ci/run_tests.sh @@ -10,7 +10,6 @@ source "${CI_DIR}/common/test.sh" check_core_dumps --delete quiet prepare_build -run_single_includes_tests build_nvim if [ "$CLANG_SANITIZER" != "TSAN" ]; then diff --git a/Makefile b/Makefile index 47fb1e5edd..d2f6c11b19 100644 --- a/Makefile +++ b/Makefile @@ -132,9 +132,9 @@ clint: -DLINT_SUPPRESS_URL="$(DOC_DOWNLOAD_URL_BASE)$(CLINT_ERRORS_FILE_PATH)" \ -P cmake/RunLint.cmake -lint: clint testlint - check-single-includes: build/.ran-cmake +$(BUILD_CMD) -C build check-single-includes +lint: check-single-includes clint testlint + .PHONY: test testlint functionaltest unittest lint clint clean distclean nvim libnvim cmake deps install From 6964b67c00f9aa029791137f44f30ed4aef20ef4 Mon Sep 17 00:00:00 2001 From: relnod Date: Thu, 30 Mar 2017 00:50:11 +0200 Subject: [PATCH 0266/1671] refactor/single-include: buffer.h (#6396) --- src/nvim/CMakeLists.txt | 1 - src/nvim/buffer.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index c5bf79c0e5..a3bacaa9d2 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -394,7 +394,6 @@ target_link_libraries(nvim-test ${NVIM_LINK_LIBRARIES}) set_property(TARGET nvim-test APPEND_STRING PROPERTY COMPILE_FLAGS -DUNIT_TESTING) set(NO_SINGLE_CHECK_HEADERS - buffer cursor_shape diff digraph diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h index c915d373aa..016c5ce3b7 100644 --- a/src/nvim/buffer.h +++ b/src/nvim/buffer.h @@ -1,6 +1,7 @@ #ifndef NVIM_BUFFER_H #define NVIM_BUFFER_H +#include "nvim/vim.h" #include "nvim/window.h" #include "nvim/pos.h" // for linenr_T #include "nvim/ex_cmds_defs.h" // for exarg_T From 1f478cebeb929332e90c1b50de4b8a4f311a0df2 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 29 Mar 2017 18:29:46 +0200 Subject: [PATCH 0267/1671] win: tempname(): Use $TMPDIR if defined. --- runtime/doc/change.txt | 4 ++-- src/nvim/os/win_defs.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index c8576d83e8..e0974b103c 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -601,8 +601,8 @@ all files in it are deleted. When Vim has the setuid bit set this may cause problems, the temp file is owned by the setuid user but the filter command probably runs as the original user. Directory for temporary files is created in the first suitable directory of: -For Unix: $TMPDIR, /tmp, current-dir, $HOME. -For MS-Windows: $TMP, $TEMP, $USERPROFILE, current-dir. + Unix: $TMPDIR, /tmp, current-dir, $HOME. + Windows: $TMPDIR, $TMP, $TEMP, $USERPROFILE, current-dir. diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h index 827fb2f247..7c980c3768 100644 --- a/src/nvim/os/win_defs.h +++ b/src/nvim/os/win_defs.h @@ -19,7 +19,7 @@ #define NAME_MAX _MAX_PATH -#define TEMP_DIR_NAMES { "$TMP", "$TEMP", "$USERPROFILE", "" } +#define TEMP_DIR_NAMES { "$TMPDIR", "$TMP", "$TEMP", "$USERPROFILE", "" } #define TEMP_FILE_PATH_MAXLEN _MAX_PATH #define FNAME_ILLEGAL "\"*?><|" From 1ea9ebf112d6c9d6355038afb4aaee794187cb23 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 29 Mar 2017 20:07:39 +0200 Subject: [PATCH 0268/1671] test: Use workspace-local temp directory. Closes #6291 --- .gitignore | 6 ----- cmake/RunTests.cmake | 3 +++ test/functional/core/job_spec.lua | 3 ++- test/helpers.lua | 41 ++++++++++++++++++++----------- 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/.gitignore b/.gitignore index 68c0e9d17c..fb506305b6 100644 --- a/.gitignore +++ b/.gitignore @@ -39,9 +39,6 @@ tags # generated by luacheck during `make testlint' /test/.luacheckcache -# luarocks, not added as a subtree because of the large number of blobs -/third-party/luarocks - # local make targets local.mk @@ -49,6 +46,3 @@ local.mk /runtime/doc/*.html /runtime/doc/tags.ref /runtime/doc/errors.log - -# clint errors, generated by `make lint` -/errors.json diff --git a/cmake/RunTests.cmake b/cmake/RunTests.cmake index 38e0f35213..bd7b630708 100644 --- a/cmake/RunTests.cmake +++ b/cmake/RunTests.cmake @@ -25,6 +25,8 @@ if(DEFINED ENV{TEST_FILTER}) set(TEST_TAG "--filter=$ENV{TEST_FILTER}") endif() +execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${WORKING_DIR}/Xtest-tmpdir) +set(ENV{TMPDIR} ${WORKING_DIR}/Xtest-tmpdir) set(ENV{SYSTEM_NAME} ${SYSTEM_NAME}) execute_process( COMMAND ${BUSTED_PRG} ${TEST_TAG} ${TEST_FILTER} -v -o ${BUSTED_OUTPUT_TYPE} @@ -37,6 +39,7 @@ execute_process( file(REMOVE ${WORKING_DIR}/Xtest_rplugin_manifest) file(REMOVE_RECURSE ${WORKING_DIR}/Xtest_xdg) +file(REMOVE_RECURSE ${WORKING_DIR}/Xtest-tmpdir) if(NOT res EQUAL 0) message(STATUS "Output to stderr:\n${err}") diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index e442c2a317..9ee91f2fe9 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -8,6 +8,7 @@ local clear, eq, eval, exc_exec, execute, feed, insert, neq, next_msg, nvim, local command = helpers.command local wait = helpers.wait local iswin = helpers.iswin +local get_pathsep = helpers.get_pathsep local Screen = require('test.functional.ui.screen') describe('jobs', function() @@ -65,7 +66,7 @@ describe('jobs', function() end) it('changes to given `cwd` directory', function() - local dir = eval('resolve(tempname())') + local dir = eval("resolve(tempname())"):gsub("/", get_pathsep()) mkdir(dir) nvim('command', "let g:job_opts.cwd = '" .. dir .. "'") if iswin() then diff --git a/test/helpers.lua b/test/helpers.lua index 1a86effa1c..82451bc61d 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -106,20 +106,33 @@ local uname = (function() end) end)() -local function tmpname() - local fname = os.tmpname() - if uname() == 'Windows' and fname:sub(1, 2) == '\\s' then - -- In Windows tmpname() returns a filename starting with - -- special sequence \s, prepend $TEMP path - local tmpdir = os.getenv('TEMP') - return tmpdir..fname - elseif fname:match('^/tmp') and uname() == 'Darwin' then - -- In OS X /tmp links to /private/tmp - return '/private'..fname - else - return fname - end -end +local tmpname = (function() + local seq = 0 + local tmpdir = os.getenv('TMPDIR') and os.getenv('TMPDIR') or os.getenv('TEMP') + -- Is $TMPDIR defined local to the project workspace? + local in_workspace = not not (tmpdir and string.find(tmpdir, 'Xtest')) + return (function() + if in_workspace then + -- Cannot control os.tmpname() dir, so hack our own tmpname() impl. + seq = seq + 1 + local fname = tmpdir..'/nvim-test-lua-'..seq + io.open(fname, 'w'):close() + return fname + else + local fname = os.tmpname() + if uname() == 'Windows' and fname:sub(1, 2) == '\\s' then + -- In Windows tmpname() returns a filename starting with + -- special sequence \s, prepend $TEMP path + return tmpdir..fname + elseif fname:match('^/tmp') and uname() == 'Darwin' then + -- In OS X /tmp links to /private/tmp + return '/private'..fname + else + return fname + end + end + end) +end)() local function map(func, tab) local rettab = {} From 3116f870ba274862fa6d6643d9fa0870215fed12 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Wed, 29 Mar 2017 16:30:06 -0400 Subject: [PATCH 0269/1671] coverity/161195: Increase scope of exe_name Since exe_name is a stack allocated array, we need it to be in scope for the lifetime that vim_path points to it. --- src/nvim/os/env.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index a73d753e46..a10c835591 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -615,9 +615,9 @@ char *vim_getenv(const char *name) vim_path = (char *)p_hf; } + char exe_name[MAXPATHL]; // Find runtime path relative to the nvim binary: ../share/nvim/runtime if (vim_path == NULL) { - char exe_name[MAXPATHL]; size_t exe_name_len = MAXPATHL; if (os_exepath(exe_name, &exe_name_len) == 0) { char *path_end = (char *)path_tail_with_sep((char_u *)exe_name); From 1c6ae58fd1301bfe2b27ed168b5a117e92c9c4cd Mon Sep 17 00:00:00 2001 From: James McCoy Date: Wed, 29 Mar 2017 16:51:46 -0400 Subject: [PATCH 0270/1671] coverity/161194: Restore check for 'keywordprg' being ":help" 998d0ffc09d5c7358db62dc88c2e2b87622f60b5 removed the explicit check for ":help", relying instead on whether the user was in a help buffer. However, this breaks escaping the identifier for use in the lookup command. 2f54d6927cc02484b528a5e8b25b64c8d6580ddd tried to fix this by removing "!kp_ex" in "if (cmdchar == 'K' && !kp_ex)", but that causes shell escaping to be used instead of escaping for tag lookup. --- src/nvim/normal.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/nvim/normal.c b/src/nvim/normal.c index d4919dc3b6..388ddfc8bb 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -4670,6 +4670,7 @@ static void nv_ident(cmdarg_T *cap) char_u *kp = *curbuf->b_p_kp == NUL ? p_kp : curbuf->b_p_kp; // 'keywordprg' assert(*kp != NUL); // option.c:do_set() should default to ":help" if empty. bool kp_ex = (*kp == ':'); // 'keywordprg' is an ex command + bool kp_help = (STRCMP(kp, ":he") == 0 || STRCMP(kp, ":help") == 0); size_t buf_size = n * 2 + 30 + STRLEN(kp); char *buf = xmalloc(buf_size); buf[0] = NUL; @@ -4692,7 +4693,9 @@ static void nv_ident(cmdarg_T *cap) break; case 'K': - if (kp_ex) { + if (kp_help) { + STRCPY(buf, "he! "); + } else if (kp_ex) { if (cap->count0 != 0) { // Send the count to the ex command. snprintf(buf, buf_size, "%" PRId64, (int64_t)(cap->count0)); } @@ -4755,7 +4758,7 @@ static void nv_ident(cmdarg_T *cap) } // Now grab the chars in the identifier - if (cmdchar == 'K') { + if (cmdchar == 'K' && !kp_help) { ptr = vim_strnsave(ptr, n); if (kp_ex) { // Escape the argument properly for an Ex command From 91dfebf0506c4389af77071323798fdd7360c589 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Wed, 29 Mar 2017 20:45:22 -0400 Subject: [PATCH 0271/1671] ci: Update Coverity model for typval refactoring [ci skip] --- src/coverity-model.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coverity-model.c b/src/coverity-model.c index a01ea6d316..3c38e4ae4d 100644 --- a/src/coverity-model.c +++ b/src/coverity-model.c @@ -64,7 +64,7 @@ void *je_realloc(void *ptr, size_t size) // of the memory allocated for item. typedef struct {} dictitem_T; typedef struct {} dict_T; -int dict_add(dict_T *d, dictitem_T *item) +int tv_dict_add(dict_T *const d, dictitem_T *const item) { __coverity_escape__(item); } From 1222c82799b9a853c5adaf8761309b616e664c95 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Wed, 29 Mar 2017 21:34:04 -0400 Subject: [PATCH 0272/1671] coverity/16127: Verify lang is non-NULL before calling strlen --- src/nvim/option.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/nvim/option.c b/src/nvim/option.c index 9b31e14ea7..69c12e2cc7 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -1054,13 +1054,15 @@ void set_init_3(void) */ void set_helplang_default(const char *lang) { - int idx; - - const size_t lang_len = strlen(lang); - if (lang == NULL || lang_len < 2) { // safety check + if (lang == NULL) { return; } - idx = findoption("hlg"); + + const size_t lang_len = strlen(lang); + if (lang_len < 2) { // safety check + return; + } + int idx = findoption("hlg"); if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) { if (options[idx].flags & P_ALLOCED) free_string_option(p_hlg); From 75b98f7c3f23b18e30c362028af3bf3dae663a02 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Thu, 30 Mar 2017 10:09:33 -0400 Subject: [PATCH 0273/1671] Remove PVS-Studio cruft [ci skip] --- src/nvim/move.PVS-Studio.cfg | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 src/nvim/move.PVS-Studio.cfg diff --git a/src/nvim/move.PVS-Studio.cfg b/src/nvim/move.PVS-Studio.cfg deleted file mode 100644 index cb6da32f12..0000000000 --- a/src/nvim/move.PVS-Studio.cfg +++ /dev/null @@ -1,10 +0,0 @@ - -source-file=/home/zyx/a.a/Proj/c/neovim/src/nvim/move.c -i-file=/home/zyx/a.a/Proj/c/neovim/src/nvim/move.i -language=C -skip-cl-exe=yes -preprocessor=gcc -platform=linux64 -lic-file=/home/zyx/a.a/Proj/c/neovim/build/../PVS-Studio.lic -output-file=/home/zyx/a.a/Proj/c/neovim/build/../PVS-Studio.log.x -analysis-mode=4 From 66b336d89bd5b45e60587b3c1689c7435019d775 Mon Sep 17 00:00:00 2001 From: Matthew Malcomson Date: Thu, 30 Mar 2017 18:00:34 +0100 Subject: [PATCH 0274/1671] test: set 'nomore' by default (#6360) Escaping from a '-- More --' prompt in tests is awkward as it doesn't take keys from the typebuffer, requiring a call to `feed()` in lua at the correct time. Moreover, it's rarer that a test will want the '-- More --' prompt to be activated than not. --- test/functional/helpers.lua | 2 +- test/functional/legacy/051_highlight_spec.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 7ce95d0b7c..ab36508262 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -23,7 +23,7 @@ local nvim_prog = os.getenv('NVIM_PROG') or os.getenv('NVIM_PRG') or 'build/bin/ -- Default settings for the test session. local nvim_set = 'set shortmess+=I background=light noswapfile noautoindent' ..' laststatus=1 undodir=. directory=. viewdir=. backupdir=.' - ..' belloff= noshowcmd noruler' + ..' belloff= noshowcmd noruler nomore' local nvim_argv = {nvim_prog, '-u', 'NONE', '-i', 'NONE', '-N', '--cmd', nvim_set, '--embed'} diff --git a/test/functional/legacy/051_highlight_spec.lua b/test/functional/legacy/051_highlight_spec.lua index ef392d8c67..d4d9b7d997 100644 --- a/test/functional/legacy/051_highlight_spec.lua +++ b/test/functional/legacy/051_highlight_spec.lua @@ -16,7 +16,7 @@ describe(':highlight', function() local screen = Screen.new(35, 10) screen:attach() -- Basic test if ":highlight" doesn't crash - execute('highlight') + execute('set more', 'highlight') -- FIXME(tarruda): We need to be sure the prompt is displayed before -- continuing, or risk a race condition where some of the following input -- is discarded resulting in test failure From eb0e94f71b1f44cebf7ae5c1bcff348264af6cef Mon Sep 17 00:00:00 2001 From: Jakob Schnitzer Date: Thu, 30 Mar 2017 22:03:52 +0200 Subject: [PATCH 0275/1671] api: {get,set}_option should {get,set} global value of local options (#6405) - nvim_get_option should return the global default of a local option. - nvim_set_option should set the global default of a local option. --- src/nvim/api/private/helpers.c | 2 +- src/nvim/api/vim.c | 2 +- src/nvim/option.c | 24 +++++++++++++----------- src/nvim/option_defs.h | 6 +++--- test/functional/api/vim_spec.lua | 22 ++++++++++++++++++++++ 5 files changed, 40 insertions(+), 16 deletions(-) diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index b245132ba7..fe15b28041 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -289,7 +289,7 @@ void set_option_to(void *to, int type, String name, Object value, Error *err) } } - int opt_flags = (type ? OPT_LOCAL : OPT_GLOBAL); + int opt_flags = (type == SREQ_GLOBAL) ? OPT_GLOBAL : OPT_LOCAL; if (flags & SOPT_BOOL) { if (value.type != kObjectTypeBoolean) { diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 975446057c..6926436d2f 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -440,7 +440,7 @@ Object nvim_get_vvar(String name, Error *err) /// /// @param name Option name /// @param[out] err Error details, if any -/// @return Option value +/// @return Option value (global) Object nvim_get_option(String name, Error *err) FUNC_API_SINCE(1) { diff --git a/src/nvim/option.c b/src/nvim/option.c index 9b31e14ea7..0bf81b4d3a 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -4619,14 +4619,13 @@ int get_option_value_strict(char *name, } char_u *varp = NULL; - vimoption_T *p; int rv = 0; int opt_idx = findoption(name); if (opt_idx < 0) { return 0; } - p = &(options[opt_idx]); + vimoption_T *p = &options[opt_idx]; // Hidden option if (p->var == NULL) { @@ -4642,26 +4641,25 @@ int get_option_value_strict(char *name, } if (p->indir == PV_NONE) { - if (opt_type == SREQ_GLOBAL) + if (opt_type == SREQ_GLOBAL) { rv |= SOPT_GLOBAL; - else - return 0; // Did not request global-only option + } else { + return 0; // Did not request global-only option + } } else { if (p->indir & PV_BOTH) { rv |= SOPT_GLOBAL; - } else if (opt_type == SREQ_GLOBAL) { - return 0; // Requested global option } if (p->indir & PV_WIN) { if (opt_type == SREQ_BUF) { - return 0; // Did not request window-local option + return 0; // Requested buffer-local, not window-local option } else { rv |= SOPT_WIN; } } else if (p->indir & PV_BUF) { if (opt_type == SREQ_WIN) { - return 0; // Did not request buffer-local option + return 0; // Requested window-local, not buffer-local option } else { rv |= SOPT_BUF; } @@ -4673,7 +4671,11 @@ int get_option_value_strict(char *name, } if (opt_type == SREQ_GLOBAL) { - varp = p->var; + if (p->var == VAR_WIN) { + return 0; + } else { + varp = p->var; + } } else { if (opt_type == SREQ_BUF) { // Special case: 'modified' is b_changed, but we also want to @@ -4720,7 +4722,7 @@ int get_option_value_strict(char *name, /// @param[in] name Option name. /// @param[in] number New value for the number or boolean option. /// @param[in] string New value for string option. -/// @param[in] opt_flags Flags: OPT_LOCAL or 0 (both). +/// @param[in] opt_flags Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both). /// /// @return NULL on success, error message on error. char *set_option_value(const char *const name, const long number, diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 94c6361236..2475a0b6a1 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -16,9 +16,9 @@ #define SOPT_UNSET 0x40 // Option does not have local value set // Option types for various functions in option.c -#define SREQ_GLOBAL 0 // Request global option -#define SREQ_WIN 1 // Request window-local option -#define SREQ_BUF 2 // Request buffer-local option +#define SREQ_GLOBAL 0 // Request global option value +#define SREQ_WIN 1 // Request window-local option value +#define SREQ_BUF 2 // Request buffer-local option value /* * Default values for 'errorformat'. diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 3348368a36..8f9f155110 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -153,6 +153,28 @@ describe('api', function() nvim('set_option', 'equalalways', false) ok(not nvim('get_option', 'equalalways')) end) + + it('works to get global value of local options', function() + eq(false, nvim('get_option', 'lisp')) + eq(8, nvim('get_option', 'shiftwidth')) + end) + + it('works to set global value of local options', function() + nvim('set_option', 'lisp', true) + eq(true, nvim('get_option', 'lisp')) + eq(false, helpers.curbuf('get_option', 'lisp')) + eq(nil, nvim('command_output', 'setglobal lisp?'):match('nolisp')) + eq('nolisp', nvim('command_output', 'setlocal lisp?'):match('nolisp')) + nvim('set_option', 'shiftwidth', 20) + eq('20', nvim('command_output', 'setglobal shiftwidth?'):match('%d+')) + eq('8', nvim('command_output', 'setlocal shiftwidth?'):match('%d+')) + end) + + it('most window-local options have no global value', function() + local status, err = pcall(nvim, 'get_option', 'foldcolumn') + eq(false, status) + ok(err:match('Invalid option name') ~= nil) + end) end) describe('nvim_{get,set}_current_buf, nvim_list_bufs', function() From 831eb2a9bf9fdffc1b6942e4c43bc2458f4af794 Mon Sep 17 00:00:00 2001 From: Michael Ennen Date: Thu, 30 Mar 2017 16:07:39 -0700 Subject: [PATCH 0276/1671] vim-patch:7.4.2104 (#6332) Problem: Code duplication when unreferencing a function. Solution: De-duplicate. https://github.com/vim/vim/commit/97baee80f0906ee2f651ee1215ec033e84f866ad --- src/nvim/eval.c | 8 +------- src/nvim/version.c | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 80c2fe10d7..a83e93090a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -20711,13 +20711,7 @@ void func_unref(char_u *name) abort(); #endif } - if (fp != NULL && --fp->uf_refcount <= 0) { - // Only delete it when it's not being used. Otherwise it's done - // when "uf_calls" becomes zero. - if (fp->uf_calls == 0) { - func_clear_free(fp, false); - } - } + func_ptr_unref(fp); } /// Unreference a Function: decrement the reference count and free it when it diff --git a/src/nvim/version.c b/src/nvim/version.c index 9275a2e5bd..fdf5436a98 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -337,7 +337,7 @@ static const int included_patches[] = { 2107, 2106, // 2105 NA - // 2104, + 2104, 2103, // 2102 NA 2101, From 3a9dd13f9e6471215a738cf22bf180e60a2e0bcd Mon Sep 17 00:00:00 2001 From: Matthew Malcomson Date: Fri, 31 Mar 2017 00:21:26 +0100 Subject: [PATCH 0277/1671] fold.c: more edge-cases when updating (#6207) When foldUpdateIEMSRecurse() re-uses an existing fold, it misses the case where the existing fold spans from before startlnum to after firstlnum, the new fold does not span this range, and there is no "forced start" of a fold. We add a case for this in. Ensure that if there was no forced break in folds, we merge folds that now touch each other. Include testing for a tricky foldmethod=expr case that has never been a bug. This case works at the moment because of some effects that are not obvious when reading the code. A test for this could be useful to ensure a regression doesn't happen. vim-patch:8.0.0408 --- src/nvim/fold.c | 71 +++++++++++----- src/nvim/testdir/test_fold.vim | 118 +++++++++++++++++++++++---- test/functional/normal/fold_spec.lua | 112 ++++++++++++++++++++++++- 3 files changed, 263 insertions(+), 38 deletions(-) diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 36a5b0efd7..d810aee0ce 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -2232,32 +2232,51 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *gap, int level, * before where we started looking, extend it. If it * starts at another line, update nested folds to keep * their position, compensating for the new fd_top. */ - if (fp->fd_top >= startlnum && fp->fd_top != firstlnum) { - if (fp->fd_top > firstlnum) - /* like lines are inserted */ + if (fp->fd_top == firstlnum) { + // We have found a fold beginning exactly where we want one. + } else if (fp->fd_top >= startlnum) { + if (fp->fd_top > firstlnum) { + // We will move the start of this fold up, hence we move all + // nested folds (with relative line numbers) down. foldMarkAdjustRecurse(&fp->fd_nested, - (linenr_T)0, (linenr_T)MAXLNUM, - (long)(fp->fd_top - firstlnum), 0L); - else - /* like lines are deleted */ + (linenr_T)0, (linenr_T)MAXLNUM, + (long)(fp->fd_top - firstlnum), 0L); + } else { + // Will move fold down, move nested folds relatively up. foldMarkAdjustRecurse(&fp->fd_nested, - (linenr_T)0, - (long)(firstlnum - fp->fd_top - 1), - (linenr_T)MAXLNUM, - (long)(fp->fd_top - firstlnum)); + (linenr_T)0, + (long)(firstlnum - fp->fd_top - 1), + (linenr_T)MAXLNUM, + (long)(fp->fd_top - firstlnum)); + } fp->fd_len += fp->fd_top - firstlnum; fp->fd_top = firstlnum; - fold_changed = TRUE; - } else if (flp->start != 0 && lvl == level - && fp->fd_top != firstlnum) { - /* Existing fold that includes startlnum must stop - * if we find the start of a new fold at the same - * level. Split it. Delete contained folds at - * this point to split them too. */ - foldRemove(&fp->fd_nested, flp->lnum - fp->fd_top, - flp->lnum - fp->fd_top); + fold_changed = true; + } else if ((flp->start != 0 && lvl == level) + || (firstlnum != startlnum)) { + // Before there was a fold spanning from above startlnum to below + // firstlnum. This fold is valid above startlnum (because we are + // not updating that range), but there is now a break in it. + // If the break is because we are now forced to start a new fold + // at the level "level" at line fline->lnum, then we need to + // split the fold at fline->lnum. + // If the break is because the range [startlnum, firstlnum) is + // now at a lower indent than "level", we need to split the fold + // in this range. + // Any splits have to be done recursively. + linenr_T breakstart; + linenr_T breakend; + if (firstlnum != startlnum) { + breakstart = startlnum; + breakend = firstlnum; + } else { + breakstart = flp->lnum; + breakend = flp->lnum; + } + foldRemove(&fp->fd_nested, breakstart - fp->fd_top, + breakend - fp->fd_top); i = (int)(fp - (fold_T *)gap->ga_data); - foldSplit(gap, i, flp->lnum, flp->lnum - 1); + foldSplit(gap, i, breakstart, breakend - 1); fp = (fold_T *)gap->ga_data + i + 1; /* If using the "marker" or "syntax" method, we * need to continue until the end of the fold is @@ -2267,6 +2286,16 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *gap, int level, || getlevel == foldlevelSyntax) finish = TRUE; } + if (fp->fd_top == startlnum && concat) { + i = (int)(fp - (fold_T *)gap->ga_data); + if (i != 0) { + fp2 = fp - 1; + if (fp2->fd_top + fp2->fd_len == fp->fd_top) { + foldMerge(fp2, gap, fp); + fp = fp2; + } + } + } break; } if (fp->fd_top >= startlnum) { diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim index 976c6b5cd1..46c54e8614 100644 --- a/src/nvim/testdir/test_fold.vim +++ b/src/nvim/testdir/test_fold.vim @@ -100,22 +100,6 @@ func! Test_indent_fold2() bw! endfunc -func Test_folds_marker_in_comment() - new - call setline(1, ['" foo', 'bar', 'baz']) - setl fen fdm=marker - setl com=sO:\"\ -,mO:\"\ \ ,eO:\"\",:\" cms=\"%s - norm! zf2j - setl nofen - :1y - call assert_equal(['" foo{{{'], getreg(0,1,1)) - :+2y - call assert_equal(['baz"}}}'], getreg(0,1,1)) - - set foldmethod& - bwipe! -endfunc - func Test_manual_fold_with_filter() if !executable('cat') return @@ -138,6 +122,108 @@ func Test_manual_fold_with_filter() endfor endfunc +func! Test_indent_fold_with_read() + new + set foldmethod=indent + call setline(1, repeat(["\a"], 4)) + for n in range(1, 4) + call assert_equal(1, foldlevel(n)) + endfor + + call writefile(["a", "", "\a"], 'Xfile') + foldopen + 2read Xfile + %foldclose + call assert_equal(1, foldlevel(1)) + call assert_equal(2, foldclosedend(1)) + call assert_equal(0, foldlevel(3)) + call assert_equal(0, foldlevel(4)) + call assert_equal(1, foldlevel(5)) + call assert_equal(7, foldclosedend(5)) + + bwipe! + set foldmethod& + call delete('Xfile') +endfunc + +func Test_combining_folds_indent() + new + let one = "\a" + let zero = 'a' + call setline(1, [one, one, zero, zero, zero, one, one, one]) + set foldmethod=indent + 3,5d + %foldclose + call assert_equal(5, foldclosedend(1)) + + set foldmethod& + bwipe! +endfunc + +func Test_combining_folds_marker() + new + call setline(1, ['{{{', '}}}', '', '', '', '{{{', '', '}}}']) + set foldmethod=marker + 3,5d + %foldclose + call assert_equal(2, foldclosedend(1)) + + set foldmethod& + bwipe! +endfunc + +func Test_folds_marker_in_comment() + new + call setline(1, ['" foo', 'bar', 'baz']) + setl fen fdm=marker + setl com=sO:\"\ -,mO:\"\ \ ,eO:\"\",:\" cms=\"%s + norm! zf2j + setl nofen + :1y + call assert_equal(['" foo{{{'], getreg(0,1,1)) + :+2y + call assert_equal(['baz"}}}'], getreg(0,1,1)) + + set foldmethod& + bwipe! +endfunc + +func s:TestFoldExpr(lnum) + let thisline = getline(a:lnum) + if thisline == 'a' + return 1 + elseif thisline == 'b' + return 0 + elseif thisline == 'c' + return '<1' + elseif thisline == 'd' + return '>1' + endif + return 0 +endfunction + +func Test_update_folds_expr_read() + new + call setline(1, ['a', 'a', 'a', 'a', 'a', 'a']) + set foldmethod=expr + set foldexpr=s:TestFoldExpr(v:lnum) + 2 + foldopen + call writefile(['b', 'b', 'a', 'a', 'd', 'a', 'a', 'c'], 'Xfile') + read Xfile + %foldclose + call assert_equal(2, foldclosedend(1)) + call assert_equal(0, foldlevel(3)) + call assert_equal(0, foldlevel(4)) + call assert_equal(6, foldclosedend(5)) + call assert_equal(10, foldclosedend(7)) + call assert_equal(14, foldclosedend(11)) + + call delete('Xfile') + bwipe! + set foldmethod& foldexpr& +endfunc + func! Test_move_folds_around_manual() new let input = PrepIndent("a") + PrepIndent("b") + PrepIndent("c") diff --git a/test/functional/normal/fold_spec.lua b/test/functional/normal/fold_spec.lua index 5584db20ba..fc055c4e7a 100644 --- a/test/functional/normal/fold_spec.lua +++ b/test/functional/normal/fold_spec.lua @@ -6,12 +6,15 @@ local feed = helpers.feed local expect = helpers.expect local execute = helpers.execute local funcs = helpers.funcs -local foldlevel, foldclosedend = funcs.foldlevel, funcs.foldclosedend +local foldlevel = funcs.foldlevel +local foldclosedend = funcs.foldclosedend local eq = helpers.eq describe('Folds', function() + local tempfname = 'Xtest-fold.txt' clear() before_each(function() execute('enew!') end) + after_each(function() os.remove(tempfname) end) it('manual folding adjusts with filter', function() insert([[ 1 @@ -230,4 +233,111 @@ a]], '2,3m0') eq({1, 2, 0, 0, 0}, get_folds()) end) end) + it('updates correctly on :read', function() + -- luacheck: ignore 621 + helpers.write_file(tempfname, [[ + a + + + a]]) + insert([[ + a + a + a + a + ]]) + execute('set foldmethod=indent', '2', '%foldopen') + execute('read ' .. tempfname) + -- Just to check we have the correct file text. + expect([[ + a + a + a + + + a + a + a + ]]) + for i = 1,2 do + eq(1, funcs.foldlevel(i)) + end + for i = 3,5 do + eq(0, funcs.foldlevel(i)) + end + for i = 6,8 do + eq(1, funcs.foldlevel(i)) + end + end) + it('combines folds when removing separating space', function() + -- luacheck: ignore 621 + insert([[ + a + a + a + a + a + a + a + a + ]]) + execute('set foldmethod=indent', '3,5d') + eq(5, funcs.foldclosedend(1)) + end) + it("doesn't combine folds that have a specified end", function() + insert([[ + {{{ + }}} + + + + {{{ + + }}} + ]]) + execute('set foldmethod=marker', '3,5d', '%foldclose') + eq(2, funcs.foldclosedend(1)) + end) + it('splits folds according to >N and 1' + endif + return 0 + endfunction + ]]) + helpers.write_file(tempfname, [[ + b + b + a + a + d + a + a + c]]) + insert([[ + a + a + a + a + a + a + ]]) + execute('set foldmethod=expr', 'set foldexpr=TestFoldExpr(v:lnum)', '2', 'foldopen') + execute('read ' .. tempfname, '%foldclose') + eq(2, funcs.foldclosedend(1)) + eq(0, funcs.foldlevel(3)) + eq(0, funcs.foldlevel(4)) + eq(6, funcs.foldclosedend(5)) + eq(10, funcs.foldclosedend(7)) + eq(14, funcs.foldclosedend(11)) + end) end) From d92add8a4b00d5e87fd367ae2656ac5ba87a8f06 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Thu, 30 Mar 2017 10:35:48 -0400 Subject: [PATCH 0278/1671] third-party: Bump msgpack-c to 2.1.1 --- third-party/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index a5fd766aa8..ea1039f459 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -90,8 +90,8 @@ include(ExternalProject) set(LIBUV_URL https://github.com/libuv/libuv/archive/v1.9.1.tar.gz) set(LIBUV_SHA256 a6ca9f0648973d1463f46b495ce546ddcbe7cce2f04b32e802a15539e46c57ad) -set(MSGPACK_URL https://github.com/msgpack/msgpack-c/archive/cpp-1.0.0.tar.gz) -set(MSGPACK_SHA256 afda64ca445203bb7092372b822bae8b2539fdcebbfc3f753f393628c2bcfe7d) +set(MSGPACK_URL https://github.com/msgpack/msgpack-c/archive/cpp-2.1.1.tar.gz) +set(MSGPACK_SHA256 d6bef12d959816a39c7a6972f3f16c0724e4c7ff0927eb59a35247dc8267b609) set(LUAJIT_URL https://github.com/neovim/deps/raw/master/opt/LuaJIT-2.0.4.tar.gz) set(LUAJIT_SHA256 620fa4eb12375021bef6e4f237cbd2dd5d49e56beb414bee052c746beef1807d) From af2ee9c5d1118bb51cd6b5c1e1edbda91648712d Mon Sep 17 00:00:00 2001 From: James McCoy Date: Thu, 30 Mar 2017 11:05:48 -0400 Subject: [PATCH 0279/1671] cmake: Detect whether msgpack-c has MSGPACK_OBJECT_FLOAT{32,64} types --- CMakeLists.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a058f2bff..e8956c9074 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -310,6 +310,21 @@ include_directories(SYSTEM ${LIBUV_INCLUDE_DIRS}) find_package(Msgpack 1.0.0 REQUIRED) include_directories(SYSTEM ${MSGPACK_INCLUDE_DIRS}) +list(APPEND CMAKE_REQUIRED_INCLUDES "${MSGPACK_INCLUDE_DIRS}") +check_c_source_compiles(" +#include + +int +main(void) +{ + return MSGPACK_OBJECT_FLOAT32; +} +" MSGPACK_HAS_FLOAT32) + +if(MSGPACK_HAS_FLOAT32) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNVIM_MSGPACK_HAS_FLOAT32") +endif() + if(UNIX) option(FEAT_TUI "Enable the Terminal UI" ON) else() From f4a3a96b6852f2eb5cf68d26b2bf58123c39c602 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Thu, 30 Mar 2017 11:06:26 -0400 Subject: [PATCH 0280/1671] Add handling for MSGPACK_OBJECT_FLOAT{32,64} msgpack-c previously only had MSGPACK_OBJECT_FLOAT, which was a 64-bit value. Now, 32-bit and 64-bit floats are supported as distinct types, but we'll simply continue to treat everything as 64-bit types. --- src/nvim/eval/decode.c | 8 +++++++- src/nvim/msgpack_rpc/helpers.c | 13 ++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 3cb68e093b..fb31a65971 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -973,7 +973,13 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) } break; } - case MSGPACK_OBJECT_FLOAT: { +#ifdef NVIM_MSGPACK_HAS_FLOAT32 + case MSGPACK_OBJECT_FLOAT32: + case MSGPACK_OBJECT_FLOAT64: +#else + case MSGPACK_OBJECT_FLOAT: +#endif + { *rettv = (typval_T) { .v_type = VAR_FLOAT, .v_lock = VAR_UNLOCKED, diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index 808bb863fd..4d8a9984e1 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -114,7 +114,13 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg) } break; } - case MSGPACK_OBJECT_FLOAT: { +#ifdef NVIM_MSGPACK_HAS_FLOAT32 + case MSGPACK_OBJECT_FLOAT32: + case MSGPACK_OBJECT_FLOAT64: +#else + case MSGPACK_OBJECT_FLOAT: +#endif + { STATIC_ASSERT(sizeof(Float) == sizeof(cur.mobj->via.f64), "Msgpack floating-point size does not match API integer"); *cur.aobj = FLOATING_OBJ(cur.mobj->via.f64); @@ -181,7 +187,12 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg) case MSGPACK_OBJECT_BOOLEAN: case MSGPACK_OBJECT_POSITIVE_INTEGER: case MSGPACK_OBJECT_NEGATIVE_INTEGER: +#ifdef NVIM_MSGPACK_HAS_FLOAT32 + case MSGPACK_OBJECT_FLOAT32: + case MSGPACK_OBJECT_FLOAT64: +#else case MSGPACK_OBJECT_FLOAT: +#endif case MSGPACK_OBJECT_EXT: case MSGPACK_OBJECT_MAP: case MSGPACK_OBJECT_ARRAY: { From 193aa4c140781b5af1d9879385d6834d8e8dfbf0 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Thu, 30 Mar 2017 12:07:30 -0400 Subject: [PATCH 0281/1671] third-party: Avoid building msgpack-c examples --- third-party/cmake/BuildMsgpack.cmake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/third-party/cmake/BuildMsgpack.cmake b/third-party/cmake/BuildMsgpack.cmake index 6b38508b0b..779cb1ebfe 100644 --- a/third-party/cmake/BuildMsgpack.cmake +++ b/third-party/cmake/BuildMsgpack.cmake @@ -35,6 +35,7 @@ endfunction() set(MSGPACK_CONFIGURE_COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/msgpack -DMSGPACK_ENABLE_CXX=OFF -DMSGPACK_BUILD_TESTS=OFF + -DMSGPACK_BUILD_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR} -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} @@ -49,6 +50,7 @@ if(MINGW AND CMAKE_CROSSCOMPILING) set(MSGPACK_CONFIGURE_COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/msgpack -DMSGPACK_ENABLE_CXX=OFF -DMSGPACK_BUILD_TESTS=OFF + -DMSGPACK_BUILD_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR} # Pass toolchain -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN} @@ -60,6 +62,7 @@ elseif(MSVC) set(MSGPACK_CONFIGURE_COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/msgpack -DMSGPACK_ENABLE_CXX=OFF -DMSGPACK_BUILD_TESTS=OFF + -DMSGPACK_BUILD_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR} -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_COMPILER_ARG1}" From 338da727cdb19a15a0b001707e8778e10977e65c Mon Sep 17 00:00:00 2001 From: James McCoy Date: Thu, 30 Mar 2017 22:11:40 -0400 Subject: [PATCH 0282/1671] coverity/161216: Ensure buf is valid for lifetime of defstr Depending on the type of argument for input()/inputdialog()'s {text} argument, defstr may point to buf. Therefore it needs to be in scope for the lifetime of defstr. Also, use a different buffer for the handling of the 3rd argument to input()/inputdialog(). Although the buffer defstr points to is used immediately, it avoids potential mishaps if the code changes. --- src/nvim/eval.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 80c2fe10d7..c683fe4e10 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -11008,18 +11008,19 @@ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) cmdline_row = msg_row; const char *defstr = ""; + char buf[NUMBUFLEN]; if (argvars[1].v_type != VAR_UNKNOWN) { - char buf[NUMBUFLEN]; defstr = tv_get_string_buf_chk(&argvars[1], buf); if (defstr != NULL) { stuffReadbuffSpec(defstr); } if (!inputdialog && argvars[2].v_type != VAR_UNKNOWN) { + char buf2[NUMBUFLEN]; // input() with a third argument: completion rettv->vval.v_string = NULL; - const char *const xp_name = tv_get_string_buf_chk(&argvars[2], buf); + const char *const xp_name = tv_get_string_buf_chk(&argvars[2], buf2); if (xp_name == NULL) { return; } From a1c928e70cd995426449ac6ec6df3b5a492580e5 Mon Sep 17 00:00:00 2001 From: Nikolai Aleksandrovich Pavlov Date: Fri, 31 Mar 2017 15:32:58 +0300 Subject: [PATCH 0283/1671] ci: Do not hide ci directory (#6410) --- .travis.yml | 12 ++++++------ README.md | 2 +- appveyor.yml | 4 ++-- {.ci => ci}/after_success.sh | 0 {.ci => ci}/before_cache.sh | 0 {.ci => ci}/before_install.sh | 0 {.ci => ci}/before_script.sh | 0 {.ci => ci}/build.bat | 0 {.ci => ci}/common/build.sh | 0 {.ci => ci}/common/test.sh | 0 {.ci => ci}/install.sh | 0 {.ci => ci}/run_tests.sh | 0 {.ci => ci}/script.sh | 4 ++-- test/unit/helpers.lua | 2 +- 14 files changed, 12 insertions(+), 12 deletions(-) rename {.ci => ci}/after_success.sh (100%) rename {.ci => ci}/before_cache.sh (100%) rename {.ci => ci}/before_install.sh (100%) rename {.ci => ci}/before_script.sh (100%) rename {.ci => ci}/build.bat (100%) rename {.ci => ci}/common/build.sh (100%) rename {.ci => ci}/common/test.sh (100%) rename {.ci => ci}/install.sh (100%) rename {.ci => ci}/run_tests.sh (100%) rename {.ci => ci}/script.sh (84%) diff --git a/.travis.yml b/.travis.yml index d28fc9d7f1..a02d44ea47 100644 --- a/.travis.yml +++ b/.travis.yml @@ -83,12 +83,12 @@ matrix: - env: GCOV=gcov-5 CMAKE_FLAGS="$CMAKE_FLAGS -DUSE_GCOV=ON" fast_finish: true -before_install: .ci/before_install.sh -install: .ci/install.sh -before_script: .ci/before_script.sh -script: .ci/script.sh -before_cache: .ci/before_cache.sh -after_success: .ci/after_success.sh +before_install: ci/before_install.sh +install: ci/install.sh +before_script: ci/before_script.sh +script: ci/script.sh +before_cache: ci/before_cache.sh +after_success: ci/after_success.sh addons: apt: diff --git a/README.md b/README.md index dcead08331..bfa0216a0f 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Packages are in [Homebrew], [Debian], [Ubuntu], [Fedora], [Arch Linux], and Project layout -------------- -- `.ci/`: Build server scripts +- `ci/`: Build server scripts - `cmake/`: Build scripts - `runtime/`: Application files - [`src/`](src/nvim/README.md): Application source code diff --git a/appveyor.yml b/appveyor.yml index ed5e06e3ee..091e86583a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,9 +4,9 @@ configuration: - MINGW_32 install: [] build_script: -- call .ci\build.bat +- call ci\build.bat cache: -- C:\msys64\var\cache\pacman\pkg -> .ci\build.bat +- C:\msys64\var\cache\pacman\pkg -> ci\build.bat - .deps -> third-party/CMakeLists.txt artifacts: - path: build/Neovim.zip diff --git a/.ci/after_success.sh b/ci/after_success.sh similarity index 100% rename from .ci/after_success.sh rename to ci/after_success.sh diff --git a/.ci/before_cache.sh b/ci/before_cache.sh similarity index 100% rename from .ci/before_cache.sh rename to ci/before_cache.sh diff --git a/.ci/before_install.sh b/ci/before_install.sh similarity index 100% rename from .ci/before_install.sh rename to ci/before_install.sh diff --git a/.ci/before_script.sh b/ci/before_script.sh similarity index 100% rename from .ci/before_script.sh rename to ci/before_script.sh diff --git a/.ci/build.bat b/ci/build.bat similarity index 100% rename from .ci/build.bat rename to ci/build.bat diff --git a/.ci/common/build.sh b/ci/common/build.sh similarity index 100% rename from .ci/common/build.sh rename to ci/common/build.sh diff --git a/.ci/common/test.sh b/ci/common/test.sh similarity index 100% rename from .ci/common/test.sh rename to ci/common/test.sh diff --git a/.ci/install.sh b/ci/install.sh similarity index 100% rename from .ci/install.sh rename to ci/install.sh diff --git a/.ci/run_tests.sh b/ci/run_tests.sh similarity index 100% rename from .ci/run_tests.sh rename to ci/run_tests.sh diff --git a/.ci/script.sh b/ci/script.sh similarity index 84% rename from .ci/script.sh rename to ci/script.sh index 46c4eecf38..79a1bec201 100755 --- a/.ci/script.sh +++ b/ci/script.sh @@ -12,7 +12,7 @@ fi # as $USER, while retaining the environment variables defined and belonging # to secondary groups given above in usermod. if [[ "${TRAVIS_OS_NAME}" == osx ]]; then - sudo -E su "${USER}" -c ".ci/run_tests.sh" + sudo -E su "${USER}" -c "ci/run_tests.sh" else - .ci/run_tests.sh + ci/run_tests.sh fi diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 8aad3acd98..f4e6194fc7 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -574,7 +574,7 @@ local function gen_itp(it) if not err then if allow_failure then io.stderr:write('Errorred out:\n' .. tostring(emsg) .. '\n') - os.execute([[sh -c "source .ci/common/test.sh ; check_core_dumps --delete \"]] .. Paths.test_luajit_prg .. [[\""]]) + os.execute([[sh -c "source ci/common/test.sh ; check_core_dumps --delete \"]] .. Paths.test_luajit_prg .. [[\""]]) else error(emsg) end From 030c0588a04c7b201ed21ab6bcbc38c040e3d5b6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 31 Mar 2017 10:51:41 +0300 Subject: [PATCH 0284/1671] cmake: Add `clint` target to build Makefile Allows linting only modified files and linting multiple files in parallel. In the current state is rather slow because errors.json is a 6 MiB file and needs to be reparsed each time. Results on my system (6-core): # In build dir, actually parallel make -j5 clint 241.24s user 8.39s system 334% cpu 1:14.74 total # In root, one process make -j5 clint 60.69s user 0.37s system 93% cpu 1:05.19 total In both cases download time included. That is not well for travis (though I would keep travis as-is because new variant will fail before checking all files), but already good enough for regular development: total times are nearly identical and this is the *full* build, further `make -C build clint` will check only modified files. --- cmake/Download.cmake | 1 + src/nvim/CMakeLists.txt | 147 +++++++++++++++++++++++++--------------- 2 files changed, 93 insertions(+), 55 deletions(-) create mode 100644 cmake/Download.cmake diff --git a/cmake/Download.cmake b/cmake/Download.cmake new file mode 100644 index 0000000000..07ec174852 --- /dev/null +++ b/cmake/Download.cmake @@ -0,0 +1 @@ +file(DOWNLOAD "${URL}" "${FILE}") diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index a3bacaa9d2..68c0339dc5 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -10,6 +10,7 @@ if(USE_GCOV) endif() endif() +set(TOUCHES_DIR ${PROJECT_BINARY_DIR}/touches) set(GENERATED_DIR ${PROJECT_BINARY_DIR}/src/nvim/auto) set(DISPATCH_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gendispatch.lua) file(GLOB API_HEADERS api/*.h) @@ -34,11 +35,16 @@ set(UNICODE_TABLES_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genunicodetables.lua) set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/unicode) file(GLOB UNICODE_FILES ${UNICODE_DIR}/*.txt) set(GENERATED_UNICODE_TABLES ${GENERATED_DIR}/unicode_tables.generated.h) +set(LINT_SUPPRESS_FILE ${PROJECT_BINARY_DIR}/errors.json) +set(LINT_SUPPRESS_URL "https://raw.githubusercontent.com/neovim/doc/gh-pages/reports/clint/errors.json") +set(LINT_PRG ${PROJECT_SOURCE_DIR}/src/clint.py) +set(DOWNLOAD_SCRIPT ${PROJECT_SOURCE_DIR}/cmake/Download.cmake) include_directories(${GENERATED_DIR}) include_directories(${CACHED_GENERATED_DIR}) include_directories(${GENERATED_INCLUDES_DIR}) +file(MAKE_DIRECTORY ${TOUCHES_DIR}) file(MAKE_DIRECTORY ${GENERATED_DIR}) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}) @@ -73,6 +79,8 @@ file(GLOB UNIT_TEST_FIXTURES ${PROJECT_SOURCE_DIR}/test/unit/fixtures/*.c) list(SORT NVIM_SOURCES) list(SORT NVIM_HEADERS) +list(APPEND LINT_NVIM_SOURCES ${NVIM_SOURCES} ${NVIM_HEADERS}) + foreach(sfile ${NVIM_SOURCES}) get_filename_component(f ${sfile} NAME) if(${f} MATCHES "^(regexp_nfa.c)$") @@ -393,76 +401,82 @@ add_library(nvim-test MODULE EXCLUDE_FROM_ALL ${NVIM_GENERATED_FOR_SOURCES} ${NV target_link_libraries(nvim-test ${NVIM_LINK_LIBRARIES}) set_property(TARGET nvim-test APPEND_STRING PROPERTY COMPILE_FLAGS -DUNIT_TESTING) -set(NO_SINGLE_CHECK_HEADERS - cursor_shape - diff - digraph - ex_cmds - ex_getln - file_search - fold - getchar - hardcopy - if_cscope - if_cscope_defs - mark - mbyte - memfile_defs - memline - memline_defs - menu - misc2 - move - msgpack_rpc/server - ops - option - os/shell - os_unix - os/win_defs - popupmnu - quickfix - regexp - regexp_defs - screen - search - sha256 - sign_defs - spell - spellfile - syntax - syntax_defs - tag - terminal - tui/tui - ugrid - ui - ui_bridge - undo - undo_defs - version - window -) -foreach(hfile ${NVIM_HEADERS}) - get_filename_component(full_d ${hfile} PATH) +function(get_test_target prefix sfile relative_path_var target_var) + get_filename_component(full_d "${sfile}" PATH) file(RELATIVE_PATH d "${PROJECT_SOURCE_DIR}/src/nvim" "${full_d}") if(${d} MATCHES "^[.][.]") file(RELATIVE_PATH d "${GENERATED_DIR}" "${full_d}") endif() - get_filename_component(r ${hfile} NAME_WE) + get_filename_component(r "${sfile}" NAME) if(NOT ${d} EQUAL ".") set(r "${d}/${r}") endif() + string(REGEX REPLACE "[/.]" "-" suffix "${r}") + set(${relative_path_var} ${r} PARENT_SCOPE) + set(${target_var} "${prefix}-${suffix}" PARENT_SCOPE) +endfunction() + +set(NO_SINGLE_CHECK_HEADERS + cursor_shape.h + diff.h + digraph.h + ex_cmds.h + ex_getln.h + file_search.h + fold.h + getchar.h + hardcopy.h + if_cscope.h + if_cscope_defs.h + mark.h + mbyte.h + memfile_defs.h + memline.h + memline_defs.h + menu.h + misc2.h + move.h + msgpack_rpc/server.h + ops.h + option.h + os/shell.h + os_unix.h + os/win_defs.h + popupmnu.h + quickfix.h + regexp.h + regexp_defs.h + screen.h + search.h + sha256.h + sign_defs.h + spell.h + spellfile.h + syntax.h + syntax_defs.h + tag.h + terminal.h + tui/tui.h + ugrid.h + ui.h + ui_bridge.h + undo.h + undo_defs.h + version.h + window.h +) +foreach(hfile ${NVIM_HEADERS}) + get_test_target(test-includes "${hfile}" relative_path texe) if(NOT ${hfile} MATCHES "[.]c[.]h$") - set(tsource "${GENERATED_DIR}/${r}.test-include.c") - string(REPLACE "/" "-" texe "test-incl-${r}") + set(tsource "${GENERATED_DIR}/${relative_path}.test-include.c") write_file("${tsource}" "#include \"${hfile}\"\nint main(int argc, char **argv) { return 0; }") add_executable( ${texe} EXCLUDE_FROM_ALL ${tsource} ${NVIM_HEADERS} ${NVIM_GENERATED_FOR_HEADERS}) - list(FIND NO_SINGLE_CHECK_HEADERS "${r}" hfile_exclude_idx) + list(FIND NO_SINGLE_CHECK_HEADERS "${relative_path}" hfile_exclude_idx) if(${hfile_exclude_idx} EQUAL -1) list(APPEND HEADER_CHECK_TARGETS ${texe}) endif() @@ -470,4 +484,27 @@ foreach(hfile ${NVIM_HEADERS}) endforeach() add_custom_target(check-single-includes DEPENDS ${HEADER_CHECK_TARGETS}) +function(add_download output url) + add_custom_command( + OUTPUT "${output}" + COMMAND ${CMAKE_COMMAND} -DURL=${url} -DFILE=${output} -P ${DOWNLOAD_SCRIPT} + DEPENDS ${DOWNLOAD_SCRIPT} + ) +endfunction() + +add_download(${LINT_SUPPRESS_FILE} ${LINT_SUPPRESS_URL}) + +foreach(sfile ${LINT_NVIM_SOURCES}) + get_test_target("${TOUCHES_DIR}/ran-clint" "${sfile}" r touch_file) + add_custom_command( + OUTPUT ${touch_file} + COMMAND ${LINT_PRG} --suppress-errors=${LINT_SUPPRESS_FILE} src/nvim/${r} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMAND ${CMAKE_COMMAND} -E touch ${touch_file} + DEPENDS ${sfile} ${LINT_SUPPRESS_FILE} + ) + list(APPEND LINT_TARGETS ${touch_file}) +endforeach() +add_custom_target(clint DEPENDS ${LINT_TARGETS}) + add_subdirectory(po) From 0b528fc4b55a8875579d0a1a5dde852b6c3f55dc Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 31 Mar 2017 11:38:08 +0300 Subject: [PATCH 0285/1671] cmake: Use file-specific supprresses `make -C build clint` time is now make -j5 clint 95.29s user 1.86s system 409% cpu 23.751 total *without* downloading anything (much worse if something was not cached, still a bit better then top-level `make clint`). But since without neovim/bot-ci#95 it is downloading each file one-by-one total time with download (download also parallel!) is make -j5 -B clint 99.29s user 2.98s system 258% cpu 39.634 total Top-level makefile still gives make -j5 clint 59.33s user 0.28s system 95% cpu 1:02.41 total --- src/nvim/CMakeLists.txt | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 68c0339dc5..b4bc3afc05 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -36,9 +36,12 @@ set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/unicode) file(GLOB UNICODE_FILES ${UNICODE_DIR}/*.txt) set(GENERATED_UNICODE_TABLES ${GENERATED_DIR}/unicode_tables.generated.h) set(LINT_SUPPRESS_FILE ${PROJECT_BINARY_DIR}/errors.json) -set(LINT_SUPPRESS_URL "https://raw.githubusercontent.com/neovim/doc/gh-pages/reports/clint/errors.json") +set(LINT_SUPPRESS_URL_BASE "https://raw.githubusercontent.com/neovim/doc/gh-pages/reports/clint") +set(LINT_SUPPRESS_URL "${LINT_SUPPRESS_URL_BASE}/errors.json") set(LINT_PRG ${PROJECT_SOURCE_DIR}/src/clint.py) set(DOWNLOAD_SCRIPT ${PROJECT_SOURCE_DIR}/cmake/Download.cmake) +set(LINT_SUPPRESSES_ROOT ${PROJECT_BINARY_DIR}/errors) +set(LINT_SUPPRESSES_URL "https://raw.githubusercontent.com/neovim/doc/gh-pages/reports/clint/errors.tar.gz") include_directories(${GENERATED_DIR}) include_directories(${CACHED_GENERATED_DIR}) @@ -404,16 +407,20 @@ set_property(TARGET nvim-test APPEND_STRING PROPERTY COMPILE_FLAGS -DUNIT_TESTIN function(get_test_target prefix sfile relative_path_var target_var) get_filename_component(full_d "${sfile}" PATH) file(RELATIVE_PATH d "${PROJECT_SOURCE_DIR}/src/nvim" "${full_d}") - if(${d} MATCHES "^[.][.]") + if(d MATCHES "^[.][.]") file(RELATIVE_PATH d "${GENERATED_DIR}" "${full_d}") endif() get_filename_component(r "${sfile}" NAME) - if(NOT ${d} EQUAL ".") + if(NOT d MATCHES "^[.]?$") set(r "${d}/${r}") endif() string(REGEX REPLACE "[/.]" "-" suffix "${r}") set(${relative_path_var} ${r} PARENT_SCOPE) - set(${target_var} "${prefix}-${suffix}" PARENT_SCOPE) + if(prefix STREQUAL "") + set(${target_var} "${suffix}" PARENT_SCOPE) + else() + set(${target_var} "${prefix}-${suffix}" PARENT_SCOPE) + endif() endfunction() set(NO_SINGLE_CHECK_HEADERS @@ -495,13 +502,17 @@ endfunction() add_download(${LINT_SUPPRESS_FILE} ${LINT_SUPPRESS_URL}) foreach(sfile ${LINT_NVIM_SOURCES}) - get_test_target("${TOUCHES_DIR}/ran-clint" "${sfile}" r touch_file) + get_test_target("" "${sfile}" r suffix) + set(suppress_file ${LINT_SUPPRESSES_ROOT}/${suffix}.json) + set(suppress_url "${LINT_SUPPRESS_URL_BASE}/${suffix}.json") + add_download(${suppress_file} ${suppress_url}) + set(touch_file "${TOUCHES_DIR}/ran-clint-${suffix}") add_custom_command( OUTPUT ${touch_file} - COMMAND ${LINT_PRG} --suppress-errors=${LINT_SUPPRESS_FILE} src/nvim/${r} + COMMAND ${LINT_PRG} --suppress-errors=${suppress_file} src/nvim/${r} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${CMAKE_COMMAND} -E touch ${touch_file} - DEPENDS ${sfile} ${LINT_SUPPRESS_FILE} + DEPENDS ${sfile} ${suppress_file} ) list(APPEND LINT_TARGETS ${touch_file}) endforeach() From 8204eaea7f07c7fd2784bee2c2cfd495d49877df Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 31 Mar 2017 11:39:31 +0300 Subject: [PATCH 0286/1671] cmake: Make Download.cmake check for errors Copying from third-party/cmake/DownloadAndExtractFile.cmake. --- cmake/Download.cmake | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/cmake/Download.cmake b/cmake/Download.cmake index 07ec174852..470bb19a08 100644 --- a/cmake/Download.cmake +++ b/cmake/Download.cmake @@ -1 +1,16 @@ -file(DOWNLOAD "${URL}" "${FILE}") +file( + DOWNLOAD "${URL}" "${FILE}" + STATUS status + LOG log +) + +list(GET status 0 status_code) +list(GET status 1 status_string) + +if(NOT status_code EQUAL 0) + message(FATAL_ERROR "error: downloading '${URL}' failed + status_code: ${status_code} + status_string: ${status_string} + log: ${log} +") +endif() From 24fd125893203c667789cb574b289fc1c9eaf6bc Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 31 Mar 2017 11:49:18 +0300 Subject: [PATCH 0287/1671] cmake: Allow failing to download small suppress files --- cmake/Download.cmake | 12 +++++++----- src/nvim/CMakeLists.txt | 12 ++++++++---- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/cmake/Download.cmake b/cmake/Download.cmake index 470bb19a08..50a77816bc 100644 --- a/cmake/Download.cmake +++ b/cmake/Download.cmake @@ -8,9 +8,11 @@ list(GET status 0 status_code) list(GET status 1 status_string) if(NOT status_code EQUAL 0) - message(FATAL_ERROR "error: downloading '${URL}' failed - status_code: ${status_code} - status_string: ${status_string} - log: ${log} -") + if(NOT ALLOW_FAILURE) + message(FATAL_ERROR "error: downloading '${URL}' failed + status_code: ${status_code} + status_string: ${status_string} + log: ${log} + ") + endif() endif() diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index b4bc3afc05..f5f4173879 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -491,21 +491,25 @@ foreach(hfile ${NVIM_HEADERS}) endforeach() add_custom_target(check-single-includes DEPENDS ${HEADER_CHECK_TARGETS}) -function(add_download output url) +function(add_download output url allow_failure) add_custom_command( OUTPUT "${output}" - COMMAND ${CMAKE_COMMAND} -DURL=${url} -DFILE=${output} -P ${DOWNLOAD_SCRIPT} + COMMAND + ${CMAKE_COMMAND} + -DURL=${url} -DFILE=${output} + -DALLOW_FAILURE=${allow_failure} + -P ${DOWNLOAD_SCRIPT} DEPENDS ${DOWNLOAD_SCRIPT} ) endfunction() -add_download(${LINT_SUPPRESS_FILE} ${LINT_SUPPRESS_URL}) +add_download(${LINT_SUPPRESS_FILE} ${LINT_SUPPRESS_URL} off) foreach(sfile ${LINT_NVIM_SOURCES}) get_test_target("" "${sfile}" r suffix) set(suppress_file ${LINT_SUPPRESSES_ROOT}/${suffix}.json) set(suppress_url "${LINT_SUPPRESS_URL_BASE}/${suffix}.json") - add_download(${suppress_file} ${suppress_url}) + add_download(${suppress_file} ${suppress_url} on) set(touch_file "${TOUCHES_DIR}/ran-clint-${suffix}") add_custom_command( OUTPUT ${touch_file} From c61858a9978504c645f09de60daf4f9786c2220e Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 31 Mar 2017 12:02:59 +0300 Subject: [PATCH 0288/1671] cmake: Replace RunLint.cmake with code in src/nvim/CMakeLists.txt This also removes LINT_FILE environment variable, other then that functionality is kept. It is expected that developers needing partial linting will use `make lint`, touching interesting file before (if not done already by writing to them). --- Makefile | 12 ++++++------ cmake/RunLint.cmake | 32 -------------------------------- src/nvim/CMakeLists.txt | 13 ++++++++++++- 3 files changed, 18 insertions(+), 39 deletions(-) delete mode 100644 cmake/RunLint.cmake diff --git a/Makefile b/Makefile index d2f6c11b19..5e2bbb99d1 100644 --- a/Makefile +++ b/Makefile @@ -126,15 +126,15 @@ distclean: clean install: | nvim +$(BUILD_CMD) -C build install -clint: - $(CMAKE_PRG) -DLINT_PRG=./src/clint.py \ - -DLINT_DIR=src \ - -DLINT_SUPPRESS_URL="$(DOC_DOWNLOAD_URL_BASE)$(CLINT_ERRORS_FILE_PATH)" \ - -P cmake/RunLint.cmake +clint: build/.ran-cmake + +$(BUILD_CMD) -C build clint + +clint-full: build/.ran-cmake + +$(BUILD_CMD) -C build clint-full check-single-includes: build/.ran-cmake +$(BUILD_CMD) -C build check-single-includes -lint: check-single-includes clint testlint +lint: check-single-includes clint-full testlint .PHONY: test testlint functionaltest unittest lint clint clean distclean nvim libnvim cmake deps install diff --git a/cmake/RunLint.cmake b/cmake/RunLint.cmake deleted file mode 100644 index 306e938232..0000000000 --- a/cmake/RunLint.cmake +++ /dev/null @@ -1,32 +0,0 @@ -get_filename_component(LINT_DIR ${LINT_DIR} ABSOLUTE) -get_filename_component(LINT_PREFIX ${LINT_DIR} PATH) -set(LINT_SUPPRESS_FILE "${LINT_PREFIX}/errors.json") - -if(DEFINED ENV{LINT_FILE}) - file(GLOB_RECURSE LINT_FILES "$ENV{LINT_FILE}") -else() - file(GLOB_RECURSE LINT_FILES ${LINT_DIR}/*.c ${LINT_DIR}/*.h) -endif() - -set(LINT_ARGS) - -if(LINT_SUPPRESS_URL) - file(DOWNLOAD ${LINT_SUPPRESS_URL} ${LINT_SUPPRESS_FILE}) - list(APPEND LINT_ARGS "--suppress-errors=${LINT_SUPPRESS_FILE}") -endif() - -foreach(lint_file ${LINT_FILES}) - file(RELATIVE_PATH lint_file "${LINT_PREFIX}" "${lint_file}") - list(APPEND LINT_ARGS "${lint_file}") -endforeach() - -execute_process( - COMMAND ${LINT_PRG} ${LINT_ARGS} - RESULT_VARIABLE res - WORKING_DIRECTORY "${LINT_PREFIX}") - -file(REMOVE ${LINT_SUPPRESS_FILE}) - -if(NOT res EQUAL 0) - message(FATAL_ERROR "Linting failed: ${res}.") -endif() diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index f5f4173879..b19bdf2326 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -505,21 +505,32 @@ endfunction() add_download(${LINT_SUPPRESS_FILE} ${LINT_SUPPRESS_URL} off) +set(LINT_NVIM_REL_SOURCES) foreach(sfile ${LINT_NVIM_SOURCES}) get_test_target("" "${sfile}" r suffix) set(suppress_file ${LINT_SUPPRESSES_ROOT}/${suffix}.json) set(suppress_url "${LINT_SUPPRESS_URL_BASE}/${suffix}.json") + set(rsfile src/nvim/${r}) add_download(${suppress_file} ${suppress_url} on) set(touch_file "${TOUCHES_DIR}/ran-clint-${suffix}") add_custom_command( OUTPUT ${touch_file} - COMMAND ${LINT_PRG} --suppress-errors=${suppress_file} src/nvim/${r} + COMMAND ${LINT_PRG} --suppress-errors=${suppress_file} ${rsfile} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${CMAKE_COMMAND} -E touch ${touch_file} DEPENDS ${sfile} ${suppress_file} ) list(APPEND LINT_TARGETS ${touch_file}) + list(APPEND LINT_NVIM_REL_SOURCES ${rsfile}) endforeach() add_custom_target(clint DEPENDS ${LINT_TARGETS}) +add_custom_target( + clint-full + COMMAND + ${LINT_PRG} --suppress-errors=${LINT_SUPPRESS_FILE} ${LINT_NVIM_REL_SOURCES} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + DEPENDS ${LINT_NVIM_SOURCES} ${LINT_SUPPRESS_FILE} +) + add_subdirectory(po) From 4fc2be490cd075d08ef8532f954424051fe49300 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 31 Mar 2017 12:27:13 +0300 Subject: [PATCH 0289/1671] clint: Do not report zero errors --- src/clint.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clint.py b/src/clint.py index 61c53d128e..5174521fb8 100755 --- a/src/clint.py +++ b/src/clint.py @@ -571,7 +571,8 @@ class _CppLintState(object): for category, count in self.errors_by_category.items(): sys.stderr.write('Category \'%s\' errors found: %d\n' % (category, count)) - sys.stderr.write('Total errors found: %d\n' % self.error_count) + if self.error_count: + sys.stderr.write('Total errors found: %d\n' % self.error_count) def SuppressErrorsFrom(self, fname): """Open file and read a list of suppressed errors from it""" From 4d0f90f94db6040841b9987d02a021b785cfe0f3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 31 Mar 2017 12:28:10 +0300 Subject: [PATCH 0290/1671] cmake: Also depend on LINT_PRG --- src/nvim/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index b19bdf2326..5a5ebc4415 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -518,7 +518,7 @@ foreach(sfile ${LINT_NVIM_SOURCES}) COMMAND ${LINT_PRG} --suppress-errors=${suppress_file} ${rsfile} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${CMAKE_COMMAND} -E touch ${touch_file} - DEPENDS ${sfile} ${suppress_file} + DEPENDS ${LINT_PRG} ${sfile} ${suppress_file} ) list(APPEND LINT_TARGETS ${touch_file}) list(APPEND LINT_NVIM_REL_SOURCES ${rsfile}) @@ -530,7 +530,7 @@ add_custom_target( COMMAND ${LINT_PRG} --suppress-errors=${LINT_SUPPRESS_FILE} ${LINT_NVIM_REL_SOURCES} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - DEPENDS ${LINT_NVIM_SOURCES} ${LINT_SUPPRESS_FILE} + DEPENDS ${LINT_PRG} ${LINT_NVIM_SOURCES} ${LINT_SUPPRESS_FILE} ) add_subdirectory(po) From 2bf9d36ccd59f12e3895c885e8cf17e620bf191b Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 31 Mar 2017 16:02:53 +0300 Subject: [PATCH 0291/1671] ci: Refactor CI scripts 1. CI_TARGET now determines which run_${CI_TARGET}.sh script to use. Defaults to `tests`. 2. Build no longer halts on the first failing suit: e.g. if functional tests failed it will continue with unit tests, etc. 3. All ${MAKE_CMD} occurrences moved to `top_make` function, added `build_make` as an alias to `make -C build` (`"${MAKE_CMD}" -C "${BUILD_DIR}"`) which is too verbose. `suite.sh` was copied from powerline (tests/common.sh file), assumes running with POSIX shells (and actually uses dash in powerline). Then some convenience functions were added (run_test and below). --- .travis.yml | 1 + ci/common/build.sh | 16 ++++++++++---- ci/common/suite.sh | 55 ++++++++++++++++++++++++++++++++++++++++++++++ ci/common/test.sh | 8 ++++--- ci/run_lint.sh | 17 ++++++++++++++ ci/run_tests.sh | 17 +++++++++----- ci/script.sh | 9 ++------ 7 files changed, 104 insertions(+), 19 deletions(-) create mode 100644 ci/common/suite.sh create mode 100755 ci/run_lint.sh diff --git a/.travis.yml b/.travis.yml index a02d44ea47..fe58858e5b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,6 +50,7 @@ env: - SUCCESS_MARKER="$BUILD_DIR/.tests_successful" # default target name for functional tests - FUNCTIONALTEST=functionaltest + - CI_TARGET=tests matrix: include: diff --git a/ci/common/build.sh b/ci/common/build.sh index 44087513ee..19c23bdda8 100644 --- a/ci/common/build.sh +++ b/ci/common/build.sh @@ -1,3 +1,11 @@ +top_make() { + "${MAKE_CMD}" "$@" +} + +build_make() { + top_make -C "${BUILD_DIR}" "$@" +} + build_deps() { if [[ "${BUILD_32BIT}" == ON ]]; then DEPS_CMAKE_FLAGS="${DEPS_CMAKE_FLAGS} ${CMAKE_FLAGS_32BIT}" @@ -30,7 +38,7 @@ build_deps() { echo "Configuring with '${DEPS_CMAKE_FLAGS}'." CC= cmake ${DEPS_CMAKE_FLAGS} "${TRAVIS_BUILD_DIR}/third-party/" - if ! ${MAKE_CMD}; then + if ! top_make; then exit 1 fi @@ -53,18 +61,18 @@ prepare_build() { build_nvim() { echo "Building nvim." - if ! ${MAKE_CMD} nvim; then + if ! top_make nvim; then exit 1 fi if [ "$CLANG_SANITIZER" != "TSAN" ]; then echo "Building libnvim." - if ! ${MAKE_CMD} libnvim; then + if ! top_make libnvim; then exit 1 fi echo "Building nvim-test." - if ! ${MAKE_CMD} nvim-test; then + if ! top_make nvim-test; then exit 1 fi fi diff --git a/ci/common/suite.sh b/ci/common/suite.sh new file mode 100644 index 0000000000..ad7c30708f --- /dev/null +++ b/ci/common/suite.sh @@ -0,0 +1,55 @@ +FAILED=0 + +FAIL_SUMMARY="" + +enter_suite() { + local suite_name="$1" + export NVIM_TEST_CURRENT_SUITE="${NVIM_TEST_CURRENT_SUITE}/$suite_name" +} + +exit_suite() { + if test $FAILED -ne 0 ; then + echo "Suite ${NVIM_TEST_CURRENT_SUITE} failed, summary:" + echo "${FAIL_SUMMARY}" + fi + export NVIM_TEST_CURRENT_SUITE="${NVIM_TEST_CURRENT_SUITE%/*}" + if test "x$1" != "x--continue" ; then + exit $FAILED + fi +} + +fail() { + local allow_failure= + if test "x$1" = "x--allow-failure" ; then + shift + allow_failure=A + fi + local test_name="$1" + local fail_char="$allow_failure$2" + local message="$3" + + : ${fail_char:=F} + : ${message:=Test $test_name failed} + + local full_msg="$fail_char $NVIM_TEST_CURRENT_SUITE|$test_name :: $message" + FAIL_SUMMARY="${FAIL_SUMMARY}${NL}${full_msg}" + echo "Failed: $full_msg" + if test "x$allow_failure" = "x" ; then + FAILED=1 + fi +} + +run_test() { + local cmd="$1" + shift + local test_name="$1" + : ${test_name:=$cmd} + shift + if ! eval "$cmd" ; then + fail "${test_name}" "$@" + fi +} + +succeeded() { + return $FAILED +} diff --git a/ci/common/test.sh b/ci/common/test.sh index b28e46a4df..4936992cfd 100644 --- a/ci/common/test.sh +++ b/ci/common/test.sh @@ -1,3 +1,5 @@ +source "${CI_DIR}/common/build.sh" + print_core() { local app="$1" local core="$2" @@ -75,7 +77,7 @@ asan_check() { run_unittests() { ulimit -c unlimited - if ! ${MAKE_CMD} -C "${BUILD_DIR}" unittest ; then + if ! build_make unittest ; then check_core_dumps "$(which luajit)" exit 1 fi @@ -84,7 +86,7 @@ run_unittests() { run_functionaltests() { ulimit -c unlimited - if ! ${MAKE_CMD} -C "${BUILD_DIR}" ${FUNCTIONALTEST}; then + if ! build_make ${FUNCTIONALTEST}; then asan_check "${LOG_DIR}" valgrind_check "${LOG_DIR}" check_core_dumps @@ -110,7 +112,7 @@ run_oldtests() { } install_nvim() { - ${MAKE_CMD} -C "${BUILD_DIR}" install + build_make install "${INSTALL_PREFIX}/bin/nvim" --version "${INSTALL_PREFIX}/bin/nvim" -u NONE -e -c ':help' -c ':qall' || { diff --git a/ci/run_lint.sh b/ci/run_lint.sh new file mode 100755 index 0000000000..c7937930d1 --- /dev/null +++ b/ci/run_lint.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +set -e +set -o pipefail + +CI_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +source "${CI_DIR}/common/build.sh" +source "${CI_DIR}/common/suite.sh" + +enter_suite 'lint' + +run_test 'top_make clint-full' clint +run_test 'top_make testlint' testlint +run_test 'top_make check-single-includes' single-includes + +exit_suite diff --git a/ci/run_tests.sh b/ci/run_tests.sh index 6347ac15d4..b552e1f520 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -6,6 +6,9 @@ set -o pipefail CI_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "${CI_DIR}/common/build.sh" source "${CI_DIR}/common/test.sh" +source "${CI_DIR}/common/suite.sh" + +enter_suite tests check_core_dumps --delete quiet @@ -15,11 +18,15 @@ build_nvim if [ "$CLANG_SANITIZER" != "TSAN" ]; then # Additional threads are only created when the builtin UI starts, which # doesn't happen in the unit/functional tests - run_unittests - run_functionaltests + run_test run_unittests + run_test run_functionaltests fi -run_oldtests +run_test run_oldtests -install_nvim +run_test install_nvim -touch "${SUCCESS_MARKER}" +if succeeded ; then + touch "${SUCCESS_MARKER}" +fi + +exit_suite diff --git a/ci/script.sh b/ci/script.sh index 79a1bec201..a59c40cd2d 100755 --- a/ci/script.sh +++ b/ci/script.sh @@ -3,16 +3,11 @@ set -e set -o pipefail -if [[ -n "${CI_TARGET}" ]]; then - make "${CI_TARGET}" - exit 0 -fi - # This will pass the environment variables down to a bash process which runs # as $USER, while retaining the environment variables defined and belonging # to secondary groups given above in usermod. if [[ "${TRAVIS_OS_NAME}" == osx ]]; then - sudo -E su "${USER}" -c "ci/run_tests.sh" + sudo -E su "${USER}" -c "ci/run_${CI_TARGET}.sh" else - ci/run_tests.sh + ci/run_${CI_TARGET}.sh fi From 1e8706129476248ed3597bac72afacdf5a77eaeb Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 31 Mar 2017 16:08:02 +0300 Subject: [PATCH 0292/1671] makefile: Make `lint` target depend on `clint` --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5e2bbb99d1..0c057f5656 100644 --- a/Makefile +++ b/Makefile @@ -135,6 +135,6 @@ clint-full: build/.ran-cmake check-single-includes: build/.ran-cmake +$(BUILD_CMD) -C build check-single-includes -lint: check-single-includes clint-full testlint +lint: check-single-includes clint testlint .PHONY: test testlint functionaltest unittest lint clint clean distclean nvim libnvim cmake deps install From 2da3caef1b4d34de3403071b9bf54c7339f3c609 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 31 Mar 2017 16:17:38 +0300 Subject: [PATCH 0293/1671] ci: Do not quote MAKE_CMD --- ci/common/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/common/build.sh b/ci/common/build.sh index 19c23bdda8..129622b522 100644 --- a/ci/common/build.sh +++ b/ci/common/build.sh @@ -1,5 +1,5 @@ top_make() { - "${MAKE_CMD}" "$@" + ${MAKE_CMD} "$@" } build_make() { From 4c20733f6bbdcfe2a230766aa6b602681a399ac8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 31 Mar 2017 16:19:47 +0300 Subject: [PATCH 0294/1671] ci: Add ${NL} variable --- ci/common/suite.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ci/common/suite.sh b/ci/common/suite.sh index ad7c30708f..e8c6a2b07a 100644 --- a/ci/common/suite.sh +++ b/ci/common/suite.sh @@ -1,3 +1,7 @@ +# HACK: get newline for use in strings given that "\n" and $'' do not work. +NL="$(printf '\nE')" +NL="${NL%E}" + FAILED=0 FAIL_SUMMARY="" From 1b276be6e5881a8a24e7a7fb769b5e174b3f99ea Mon Sep 17 00:00:00 2001 From: James McCoy Date: Wed, 22 Mar 2017 14:03:12 -0400 Subject: [PATCH 0295/1671] ci: Use LLVM's trusty repo to match Travis' Ubuntu version --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a02d44ea47..3ecde08809 100644 --- a/.travis.yml +++ b/.travis.yml @@ -94,7 +94,7 @@ addons: apt: sources: - ubuntu-toolchain-r-test - - llvm-toolchain-precise-3.8 + - llvm-toolchain-trusty-3.8 packages: - autoconf - automake From 4bae3f48fefab86c45a8e2bee8e56bc6872c355a Mon Sep 17 00:00:00 2001 From: James McCoy Date: Wed, 22 Mar 2017 14:03:57 -0400 Subject: [PATCH 0296/1671] ci: Bump clang version to 3.9 --- .travis.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3ecde08809..a551bbe27d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ env: # http://docs.travis-ci.com/user/speeding-up-the-build/#Paralellizing-your-build-on-one-VM - MAKE_CMD="make -j2" # Update PATH for pip. - - PATH="$(python2.7 -c 'import site; print(site.getuserbase())')/bin:/usr/lib/llvm-symbolizer-3.8/bin:$PATH" + - PATH="$(python2.7 -c 'import site; print(site.getuserbase())')/bin:/usr/lib/llvm-symbolizer-3.9/bin:$PATH" # Build directory for Neovim. - BUILD_DIR="$TRAVIS_BUILD_DIR/build" # Build directory for third-party dependencies. @@ -68,10 +68,10 @@ matrix: compiler: gcc-5 -m32 env: BUILD_32BIT=ON - os: linux - compiler: clang-3.8 + compiler: clang-3.9 env: CLANG_SANITIZER=ASAN_UBSAN - os: linux - compiler: clang-3.8 + compiler: clang-3.9 env: CLANG_SANITIZER=TSAN - os: osx compiler: clang @@ -94,13 +94,13 @@ addons: apt: sources: - ubuntu-toolchain-r-test - - llvm-toolchain-trusty-3.8 + - llvm-toolchain-trusty-3.9 packages: - autoconf - automake - apport - build-essential - - clang-3.8 + - clang-3.9 - cmake - cscope - g++-5-multilib @@ -110,7 +110,7 @@ addons: - gdb - libc6-dev-i386 - libtool - - llvm-3.8-dev + - llvm-3.9-dev - pkg-config - unzip - valgrind From d9069b9fe490e3be3ac06985f7f8625af51d5129 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 31 Mar 2017 17:39:18 +0300 Subject: [PATCH 0297/1671] ci: Check for exact value of CI_TARGET, not its emptyness --- ci/before_install.sh | 2 +- ci/before_script.sh | 2 +- ci/install.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ci/before_install.sh b/ci/before_install.sh index 9aac37de12..5b36adaef2 100755 --- a/ci/before_install.sh +++ b/ci/before_install.sh @@ -3,7 +3,7 @@ set -e set -o pipefail -if [[ -n "${CI_TARGET}" ]]; then +if [[ "${CI_TARGET}" == lint ]]; then exit fi diff --git a/ci/before_script.sh b/ci/before_script.sh index 4a75e89fbe..445996a8df 100755 --- a/ci/before_script.sh +++ b/ci/before_script.sh @@ -3,7 +3,7 @@ set -e set -o pipefail -if [[ -n "${CI_TARGET}" ]]; then +if [[ "${CI_TARGET}" == lint ]]; then exit fi diff --git a/ci/install.sh b/ci/install.sh index 98d3dc01cb..4ee99e1e44 100755 --- a/ci/install.sh +++ b/ci/install.sh @@ -3,7 +3,7 @@ set -e set -o pipefail -if [[ -n "${CI_TARGET}" ]]; then +if [[ "${CI_TARGET}" == lint ]]; then exit fi From 929c398aab786473e6c28998862cbd1356de0166 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 31 Mar 2017 19:09:18 +0300 Subject: [PATCH 0298/1671] ci: Enable tracing --- ci/run_tests.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci/run_tests.sh b/ci/run_tests.sh index b552e1f520..92cb5a9fd8 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -8,6 +8,8 @@ source "${CI_DIR}/common/build.sh" source "${CI_DIR}/common/test.sh" source "${CI_DIR}/common/suite.sh" +set -x + enter_suite tests check_core_dumps --delete quiet From 86f5b1276bf444b164ac3a3b60b411afe4bd7ec4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 31 Mar 2017 19:42:18 +0300 Subject: [PATCH 0299/1671] ci: Add test watchdog and tracing for lint tests --- ci/common/suite.sh | 48 ++++++++++++++++++++++++++++++++++++++++++++++ ci/run_lint.sh | 4 +++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/ci/common/suite.sh b/ci/common/suite.sh index e8c6a2b07a..8c44e5f974 100644 --- a/ci/common/suite.sh +++ b/ci/common/suite.sh @@ -54,6 +54,54 @@ run_test() { fi } +run_test_wd() { + local timeout="$1" + shift + local cmd="$1" + shift + local test_name="$1" + : ${test_name:=$cmd} + shift + local output_file="$(mktemp)" + local status_file="$(mktemp)" + local restarts=5 + local prev_tmpsize=-1 + while test $restarts -gt 0 ; do + : > "${status_file}" + ( + if ! ( + set -o pipefail + eval "$cmd" 2>&1 | tee -a "$output_file" + ) ; then + fail "${test_name}" "$@" + fi + echo "$FAILED" > "$status_file" + ) & + local pid=$! + while test "$(stat -c "%s" "$status_file")" -eq 0 ; do + prev_tmpsize=$tmpsize + sleep $timeout + tmpsize="$(stat -c "%s" "$output_file")" + if test $tempsize -eq $prev_temsize ; then + # no output, assuming either hang or exit + break + fi + done + if test "$(stat -c "%s" "$status_file")" -eq 0 ; then + # status file not updated, assuming hang + kill -KILL $pid + echo "Test ${test_name} hang up, restarting" + else + local new_failed="$(cat "$status_file")" + if test "x$new_failed" != "x0" ; then + fail "${test_name}" F "Test failed in run_test_wd" + fi + return 0 + fi + restarts=$[ restarts - 1 ] + done +} + succeeded() { return $FAILED } diff --git a/ci/run_lint.sh b/ci/run_lint.sh index c7937930d1..5122ffc2b6 100755 --- a/ci/run_lint.sh +++ b/ci/run_lint.sh @@ -10,8 +10,10 @@ source "${CI_DIR}/common/suite.sh" enter_suite 'lint' +set -x + run_test 'top_make clint-full' clint run_test 'top_make testlint' testlint -run_test 'top_make check-single-includes' single-includes +run_test_wd 5s 'top_make check-single-includes' single-includes exit_suite From 6ddaace7ac9f2ed13d7b210d87c715c9f7209c7f Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 31 Mar 2017 20:52:05 +0300 Subject: [PATCH 0300/1671] ci: Do not shift if there are not enough arguments --- ci/common/suite.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ci/common/suite.sh b/ci/common/suite.sh index 8c44e5f974..a70cafc92c 100644 --- a/ci/common/suite.sh +++ b/ci/common/suite.sh @@ -45,10 +45,10 @@ fail() { run_test() { local cmd="$1" - shift + test $# -gt 0 && shift local test_name="$1" : ${test_name:=$cmd} - shift + test $# -gt 0 && shift if ! eval "$cmd" ; then fail "${test_name}" "$@" fi @@ -56,12 +56,12 @@ run_test() { run_test_wd() { local timeout="$1" - shift + test $# -gt 0 && shift local cmd="$1" - shift + test $# -gt 0 && shift local test_name="$1" : ${test_name:=$cmd} - shift + test $# -gt 0 && shift local output_file="$(mktemp)" local status_file="$(mktemp)" local restarts=5 From 8de53157b691dd4ce604a5be0e2d9c3b6014bfdb Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 1 Apr 2017 03:22:56 +0200 Subject: [PATCH 0301/1671] build: avoid cmake warning (#6417) --- third-party/cmake/DownloadAndExtractFile.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third-party/cmake/DownloadAndExtractFile.cmake b/third-party/cmake/DownloadAndExtractFile.cmake index 24e431b5e5..2fc6e0415f 100644 --- a/third-party/cmake/DownloadAndExtractFile.cmake +++ b/third-party/cmake/DownloadAndExtractFile.cmake @@ -39,7 +39,7 @@ if(TIMEOUT) set(timeout_args TIMEOUT ${timeout}) set(timeout_msg "${timeout} seconds") else() - set(timeout_args "# no TIMEOUT") + set(timeout_args "") set(timeout_msg "none") endif() From 933d60bc2356c1e22b8dbf8f8847c5323bc1f7a7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 1 Apr 2017 11:07:08 +0300 Subject: [PATCH 0302/1671] unittests: Do not hang when error message is too long --- test/README.md | 3 +++ test/unit/helpers.lua | 4 ++-- test/unit/testtest_spec.lua | 13 +++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 test/unit/testtest_spec.lua diff --git a/test/README.md b/test/README.md index df66f24626..dc83b1ceef 100644 --- a/test/README.md +++ b/test/README.md @@ -99,3 +99,6 @@ get backtrace from). approximately 90% of the tests. Should be used when finding cores is too hard for some reason. Normally (on OS X or when `NVIM_TEST_CORE_GLOB_DIRECTORY` is defined and this variable is not) cores are checked for after each test. + +`NVIM_TEST_RUN_TESTTEST` (U) (1): allows running `test/unit/testtest_spec.lua` +used to check how testing infrastructure works. diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index f4e6194fc7..74b5b4e8bd 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -554,8 +554,6 @@ local function gen_itp(it) end else sc.close(wr) - sc.wait(child_pid) - child_pid = nil local function check() local res = sc.read(rd, 2) eq(2, #res) @@ -570,6 +568,8 @@ local function gen_itp(it) assert.just_fail(err) end local err, emsg = pcall(check) + sc.wait(child_pid) + child_pid = nil sc.close(rd) if not err then if allow_failure then diff --git a/test/unit/testtest_spec.lua b/test/unit/testtest_spec.lua new file mode 100644 index 0000000000..2397081b09 --- /dev/null +++ b/test/unit/testtest_spec.lua @@ -0,0 +1,13 @@ +local helpers = require('test.unit.helpers')(after_each) +local assert = require('luassert') +local itp = helpers.gen_itp(it) + +-- All of the below tests must fail. Check how exactly they fail. +if os.getenv('NVIM_TEST_RUN_TESTTEST') ~= '1' then + return +end +describe('test code', function() + itp('does not hang when working with lengthy errors', function() + assert.just_fail(('x'):rep(65536)) + end) +end) From 8f7a48f46a39590dd29c702e00d56fcd7abe0208 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 1 Apr 2017 11:19:41 +0300 Subject: [PATCH 0303/1671] unittests: Split itp implementation into multiple functions --- test/unit/helpers.lua | 110 ++++++++++++++++++++---------------- test/unit/testtest_spec.lua | 1 + 2 files changed, 63 insertions(+), 48 deletions(-) diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 74b5b4e8bd..8f0d3fa429 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -511,6 +511,64 @@ if os.getenv('NVIM_TEST_PRINT_SYSCALLS') == '1' then end end +local function just_fail(_) + return false +end +say:set('assertion.just_fail.positive', '%s') +say:set('assertion.just_fail.negative', '%s') +assert:register('assertion', 'just_fail', just_fail, + 'assertion.just_fail.positive', + 'assertion.just_fail.negative') + +local function itp_child(wr, func) + init() + collectgarbage('stop') + local err, emsg = pcall(func) + collectgarbage('restart') + emsg = tostring(emsg) + if not err then + sc.write(wr, ('-\n%05u\n%s'):format(#emsg, emsg)) + deinit() + sc.close(wr) + sc.exit(1) + else + sc.write(wr, '+\n') + deinit() + sc.close(wr) + sc.exit(0) + end +end + +local function check_child_err(rd) + local res = sc.read(rd, 2) + eq(2, #res) + if res == '+\n' then + return + end + eq('-\n', res) + local len_s = sc.read(rd, 5) + local len = tonumber(len_s) + neq(0, len) + local err = sc.read(rd, len + 1) + assert.just_fail(err) +end + +local function itp_parent(rd, pid, allow_failure) + local err, emsg = pcall(check_child_err, rd) + sc.wait(pid) + sc.close(rd) + if not err then + if allow_failure then + io.stderr:write('Errorred out:\n' .. tostring(emsg) .. '\n') + os.execute([[ + sh -c "source ci/common/test.sh + check_core_dumps --delete \"]] .. Paths.test_luajit_prg .. [[\""]]) + else + error(emsg) + end + end +end + local function gen_itp(it) child_calls_mod = {} child_calls_mod_once = {} @@ -518,14 +576,6 @@ local function gen_itp(it) preprocess_cache_mod = map(function(v) return v end, preprocess_cache_init) previous_defines_mod = previous_defines_init cdefs_mod = cdefs_init:copy() - local function just_fail(_) - return false - end - say:set('assertion.just_fail.positive', '%s') - say:set('assertion.just_fail.negative', '%s') - assert:register('assertion', 'just_fail', just_fail, - 'assertion.just_fail.positive', - 'assertion.just_fail.negative') local function itp(name, func, allow_failure) if allow_failure and os.getenv('NVIM_TEST_RUN_FAILING_TESTS') ~= '1' then -- FIXME Fix tests with this true @@ -535,50 +585,13 @@ local function gen_itp(it) local rd, wr = sc.pipe() child_pid = sc.fork() if child_pid == 0 then - init() sc.close(rd) - collectgarbage('stop') - local err, emsg = pcall(func) - collectgarbage('restart') - emsg = tostring(emsg) - if not err then - sc.write(wr, ('-\n%05u\n%s'):format(#emsg, emsg)) - deinit() - sc.close(wr) - sc.exit(1) - else - sc.write(wr, '+\n') - deinit() - sc.close(wr) - sc.exit(0) - end + itp_child(wr, func) else sc.close(wr) - local function check() - local res = sc.read(rd, 2) - eq(2, #res) - if res == '+\n' then - return - end - eq('-\n', res) - local len_s = sc.read(rd, 5) - local len = tonumber(len_s) - neq(0, len) - local err = sc.read(rd, len + 1) - assert.just_fail(err) - end - local err, emsg = pcall(check) - sc.wait(child_pid) + saved_child_pid = child_pid child_pid = nil - sc.close(rd) - if not err then - if allow_failure then - io.stderr:write('Errorred out:\n' .. tostring(emsg) .. '\n') - os.execute([[sh -c "source ci/common/test.sh ; check_core_dumps --delete \"]] .. Paths.test_luajit_prg .. [[\""]]) - else - error(emsg) - end - end + itp_parent(rd, saved_child_pid, allow_failure) end end) end @@ -610,6 +623,7 @@ local module = { only_separate = only_separate, child_call_once = child_call_once, child_cleanup_once = child_cleanup_once, + sc = sc, } return function(after_each) if after_each then diff --git a/test/unit/testtest_spec.lua b/test/unit/testtest_spec.lua index 2397081b09..b469e7ed44 100644 --- a/test/unit/testtest_spec.lua +++ b/test/unit/testtest_spec.lua @@ -1,5 +1,6 @@ local helpers = require('test.unit.helpers')(after_each) local assert = require('luassert') + local itp = helpers.gen_itp(it) -- All of the below tests must fail. Check how exactly they fail. From 046d6a8dfe8ef5b319fdd7139303d46b56b5daa6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 1 Apr 2017 12:25:10 +0300 Subject: [PATCH 0304/1671] unittests: Collect traces Some benchmarks: MAIN_CDEFS + NO_TRACE: 3.81s user 1.65s system 33% cpu 16.140 total MAIN_CDEFS: 73.61s user 10.98s system 154% cpu 54.690 total NO_TRACE: 18.49s user 4.30s system 73% cpu 30.804 total (default): 77.11s user 14.74s system 126% cpu 1:12.79 total --- test/README.md | 5 ++ test/unit/helpers.lua | 102 +++++++++++++++++++++++++++++++++++- test/unit/testtest_spec.lua | 5 ++ 3 files changed, 111 insertions(+), 1 deletion(-) diff --git a/test/README.md b/test/README.md index dc83b1ceef..9a8701695a 100644 --- a/test/README.md +++ b/test/README.md @@ -102,3 +102,8 @@ defined and this variable is not) cores are checked for after each test. `NVIM_TEST_RUN_TESTTEST` (U) (1): allows running `test/unit/testtest_spec.lua` used to check how testing infrastructure works. + +`NVIM_TEST_NO_TRACE` (U) (1): omits getting traces from tests. This means that +if tests crashed without core dump you will have no clues regarding where, but +this makes tests run a lot faster. Combine with `NVIM_TEST_MAIN_CDEFS` for +maximal speed. diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 8f0d3fa429..c8296abb2a 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -520,13 +520,92 @@ assert:register('assertion', 'just_fail', just_fail, 'assertion.just_fail.positive', 'assertion.just_fail.negative') +local hook_fnamelen = 30 +local hook_sfnamelen = 30 +local hook_numlen = 5 +local hook_msglen = 1 + 1 + 1 + (1 + hook_fnamelen) + (1 + hook_sfnamelen) + (1 + hook_numlen) + 1 + +local function child_sethook(wr) + if os.getenv('NVIM_TEST_NO_TRACE') == '1' then + return + end + -- Message: + -- |> msg char (1) + -- ||> what char (1) + -- |||> namewhat char (1) + -- ||| |> source file name (30) + -- ||| | |> function name (30) + -- ||| | | |> line number (5) + -- CWN SSSSSSSSSSSSSSSSSSSSSSSSSSSSSS:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:LLLLL\n + local function hook(reason, lnum) + local msgchar = reason:sub(1, 1) + if reason == 'count' then + msgchar = 'C' + end + local info = nil + if reason ~= 'tail return' then -- tail return + info = debug.getinfo(2, 'nSl') + end + local whatchar = ' ' + local namewhatchar = ' ' + local funcname = '' + local source = '' + if info then + funcname = (info.name or ''):sub(1, hook_fnamelen) + whatchar = info.what:sub(1, 1) + namewhatchar = info.namewhat:sub(1, 1) + if namewhatchar == '' then + namewhatchar = ' ' + end + source = info.source + if source:sub(1, 1) == '@' then + if source:sub(-4, -1) == '.lua' then + source = source:sub(1, -5) + end + source = source:sub(-hook_sfnamelen, -1) + end + lnum = lnum or info.currentline + end + + -- assert(-1 <= lnum and lnum <= 99999) + local lnum_s + if lnum == -1 then + lnum_s = 'nknwn' + else + lnum_s = ('%u'):format(lnum) + end + local msg = ( -- lua does not support %* + '' + .. msgchar + .. whatchar + .. namewhatchar + .. ' ' + .. source .. (' '):rep(hook_sfnamelen - #source) + .. ':' + .. funcname .. (' '):rep(hook_fnamelen - #funcname) + .. ':' + .. ('0'):rep(hook_numlen - #lnum_s) .. lnum_s + .. '\n' + ) + -- eq(hook_msglen, #msg) + sc.write(wr, msg) + end + debug.sethook(hook, 'crl') +end + local function itp_child(wr, func) init() collectgarbage('stop') + child_sethook(wr) local err, emsg = pcall(func) + debug.sethook() collectgarbage('restart') emsg = tostring(emsg) + sc.write(wr, ('E%s\n'):format((' '):rep(hook_msglen - 2))) if not err then + if #emsg > 99999 then + emsg = emsg:sub(1, 99999) + end sc.write(wr, ('-\n%05u\n%s'):format(#emsg, emsg)) deinit() sc.close(wr) @@ -540,8 +619,29 @@ local function itp_child(wr, func) end local function check_child_err(rd) + local trace = {} + while true do + local traceline = sc.read(rd, hook_msglen) + if #traceline ~= hook_msglen then + if #traceline == 0 then + break + else + trace[#trace + 1] = 'Partial read: <' .. trace .. '>\n' + end + end + if traceline:sub(1, 1) == 'E' then + break + end + trace[#trace + 1] = traceline + end local res = sc.read(rd, 2) - eq(2, #res) + if #res ~= 2 then + local error = 'Test crashed, trace:\n' + for i = 1, #trace do + error = error .. trace[i] + end + assert.just_fail(error) + end if res == '+\n' then return end diff --git a/test/unit/testtest_spec.lua b/test/unit/testtest_spec.lua index b469e7ed44..d2f3632b6f 100644 --- a/test/unit/testtest_spec.lua +++ b/test/unit/testtest_spec.lua @@ -3,6 +3,8 @@ local assert = require('luassert') local itp = helpers.gen_itp(it) +local sc = helpers.sc + -- All of the below tests must fail. Check how exactly they fail. if os.getenv('NVIM_TEST_RUN_TESTTEST') ~= '1' then return @@ -11,4 +13,7 @@ describe('test code', function() itp('does not hang when working with lengthy errors', function() assert.just_fail(('x'):rep(65536)) end) + itp('shows trace after exiting abnormally', function() + sc.exit(0) + end) end) From 9dd0d4f8b9217f711fff32e9f47674e556a8fab0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 1 Apr 2017 12:52:28 +0300 Subject: [PATCH 0305/1671] unittests: Add trace description right to the error message --- test/functional/helpers.lua | 23 +---------------------- test/helpers.lua | 23 +++++++++++++++++++++++ test/unit/helpers.lua | 23 ++++++++++++++--------- 3 files changed, 38 insertions(+), 31 deletions(-) diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index ab36508262..335cf3c3ff 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -16,6 +16,7 @@ local eq = global_helpers.eq local ok = global_helpers.ok local map = global_helpers.map local filter = global_helpers.filter +local dedent = global_helpers.dedent local start_dir = lfs.currentdir() -- XXX: NVIM_PROG takes precedence, QuickBuild sets it. @@ -191,28 +192,6 @@ local function nvim_feed(input) end end -local function dedent(str) - -- find minimum common indent across lines - local indent = nil - for line in str:gmatch('[^\n]+') do - local line_indent = line:match('^%s+') or '' - if indent == nil or #line_indent < #indent then - indent = line_indent - end - end - if indent == nil or #indent == 0 then - -- no minimum common indent - return str - end - -- create a pattern for the indent - indent = indent:gsub('%s', '[ \t]') - -- strip it from the first line - str = str:gsub('^'..indent, '') - -- strip it from the remaining lines - str = str:gsub('[\n]'..indent, '\n') - return str -end - local function feed(...) for _, v in ipairs({...}) do nvim_feed(dedent(v)) diff --git a/test/helpers.lua b/test/helpers.lua index 82451bc61d..3fc10e9e30 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -251,6 +251,28 @@ local function concat_tables(...) return ret end +local function dedent(str) + -- find minimum common indent across lines + local indent = nil + for line in str:gmatch('[^\n]+') do + local line_indent = line:match('^%s+') or '' + if indent == nil or #line_indent < #indent then + indent = line_indent + end + end + if indent == nil or #indent == 0 then + -- no minimum common indent + return str + end + -- create a pattern for the indent + indent = indent:gsub('%s', '[ \t]') + -- strip it from the first line + str = str:gsub('^'..indent, '') + -- strip it from the remaining lines + str = str:gsub('[\n]'..indent, '\n') + return str +end + return { eq = eq, neq = neq, @@ -265,4 +287,5 @@ return { hasenv = hasenv, which = which, concat_tables = concat_tables, + dedent = dedent, } diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index c8296abb2a..048027692a 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -11,6 +11,7 @@ local posix = nil local syscall = nil local check_cores = global_helpers.check_cores +local dedent = global_helpers.dedent local neq = global_helpers.neq local map = global_helpers.map local eq = global_helpers.eq @@ -525,18 +526,22 @@ local hook_sfnamelen = 30 local hook_numlen = 5 local hook_msglen = 1 + 1 + 1 + (1 + hook_fnamelen) + (1 + hook_sfnamelen) + (1 + hook_numlen) + 1 +local tracehelp = dedent([[ + ┌ Trace type: _r_eturn from function , function _c_all, _l_ine executed, + │ _t_ail return, _C_ount (should not actually appear). + │┏ Function type: _L_ua function, _C_ function, _m_ain part of chunk, + │┃ function that did _t_ail call. + │┃┌ Function name type: _g_lobal, _l_ocal, _m_ethod, _f_ield, _u_pvalue, + │┃│ space for unknown. + │┃│ ┏ Source file name ┌ Function name ┏ Line + │┃│ ┃ (trunc to 30 bytes, no .lua) │ (truncated to last 30 bytes) ┃ number + CWN SSSSSSSSSSSSSSSSSSSSSSSSSSSSSS:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:LLLLL\n +]]) + local function child_sethook(wr) if os.getenv('NVIM_TEST_NO_TRACE') == '1' then return end - -- Message: - -- |> msg char (1) - -- ||> what char (1) - -- |||> namewhat char (1) - -- ||| |> source file name (30) - -- ||| | |> function name (30) - -- ||| | | |> line number (5) - -- CWN SSSSSSSSSSSSSSSSSSSSSSSSSSSSSS:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:LLLLL\n local function hook(reason, lnum) local msgchar = reason:sub(1, 1) if reason == 'count' then @@ -636,7 +641,7 @@ local function check_child_err(rd) end local res = sc.read(rd, 2) if #res ~= 2 then - local error = 'Test crashed, trace:\n' + local error = '\nTest crashed, trace:\n' .. tracehelp for i = 1, #trace do error = error .. trace[i] end From 708a55ee1584c22caadd6b17de8cfc1760b2dba3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 1 Apr 2017 13:16:25 +0300 Subject: [PATCH 0306/1671] unittests: Disable non-C-calls Some benchmarks: TRACE_EVERYTHING: 79.45s user 12.68s system 124% cpu 1:13.94 total (default): 30.26s user 5.30s system 89% cpu 39.663 total --- test/README.md | 4 ++++ test/unit/helpers.lua | 24 +++++++++++++++++------- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/test/README.md b/test/README.md index 9a8701695a..d0871711a8 100644 --- a/test/README.md +++ b/test/README.md @@ -107,3 +107,7 @@ used to check how testing infrastructure works. if tests crashed without core dump you will have no clues regarding where, but this makes tests run a lot faster. Combine with `NVIM_TEST_MAIN_CDEFS` for maximal speed. + +`NVIM_TEST_TRACE_EVERYTHING` (U) (1): by default unit test only record C calls +which is faster then recording everything. Set this variable to 1 if you want to +see all traces. diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 048027692a..7ef2fa22fc 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -542,19 +542,27 @@ local function child_sethook(wr) if os.getenv('NVIM_TEST_NO_TRACE') == '1' then return end + local trace_only_c = (os.getenv('NVIM_TEST_TRACE_EVERYTHING') ~= '1') local function hook(reason, lnum) - local msgchar = reason:sub(1, 1) - if reason == 'count' then - msgchar = 'C' - end local info = nil if reason ~= 'tail return' then -- tail return info = debug.getinfo(2, 'nSl') end + + if trace_only_c and (not info or info.what ~= 'C') then + return + end + local whatchar = ' ' local namewhatchar = ' ' local funcname = '' local source = '' + local msgchar = reason:sub(1, 1) + + if reason == 'count' then + msgchar = 'C' + end + if info then funcname = (info.name or ''):sub(1, hook_fnamelen) whatchar = info.what:sub(1, 1) @@ -595,9 +603,11 @@ local function child_sethook(wr) -- eq(hook_msglen, #msg) sc.write(wr, msg) end - debug.sethook(hook, 'crl') + debug.sethook(hook, trace_only_c and 'cr' or 'crl') end +local trace_end_msg = ('E%s\n'):format((' '):rep(hook_msglen - 2)) + local function itp_child(wr, func) init() collectgarbage('stop') @@ -606,7 +616,7 @@ local function itp_child(wr, func) debug.sethook() collectgarbage('restart') emsg = tostring(emsg) - sc.write(wr, ('E%s\n'):format((' '):rep(hook_msglen - 2))) + sc.write(wr, trace_end_msg) if not err then if #emsg > 99999 then emsg = emsg:sub(1, 99999) @@ -634,7 +644,7 @@ local function check_child_err(rd) trace[#trace + 1] = 'Partial read: <' .. trace .. '>\n' end end - if traceline:sub(1, 1) == 'E' then + if traceline == trace_end_msg then break end trace[#trace + 1] = traceline From 2d158dde025fc7752c9f52def8384a2fbb698652 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 1 Apr 2017 13:17:25 +0300 Subject: [PATCH 0307/1671] unittests: Fix linter error --- test/unit/helpers.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 7ef2fa22fc..2cea88c8c0 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -704,7 +704,7 @@ local function gen_itp(it) itp_child(wr, func) else sc.close(wr) - saved_child_pid = child_pid + local saved_child_pid = child_pid child_pid = nil itp_parent(rd, saved_child_pid, allow_failure) end From 53da57d27a8ee47fe42604ad07bb7c956d9012f5 Mon Sep 17 00:00:00 2001 From: lonerover Date: Wed, 22 Mar 2017 12:24:07 +0800 Subject: [PATCH 0308/1671] vim-patch:7.4.2236 Problem: The 'langnoremap' option leads to double negatives. And it does not work for the last character of a mapping. Solution: Add 'langremap' with the opposite value. Keep 'langnoremap' for backwards compatibility. Make it work for the last character of a mapping. Make the test work. https://github.com/vim/vim/commit/920694c1b60fac8017b8909efcc24f189804a9bb --- src/nvim/macros.h | 2 +- src/nvim/option.c | 6 +++ src/nvim/option_defs.h | 5 +- src/nvim/options.lua | 6 +++ src/nvim/testdir/test_mapping.vim | 78 ++++++++++++++++++++++++------- src/nvim/version.c | 2 +- 6 files changed, 78 insertions(+), 21 deletions(-) diff --git a/src/nvim/macros.h b/src/nvim/macros.h index 5042663041..a8df6322cf 100644 --- a/src/nvim/macros.h +++ b/src/nvim/macros.h @@ -94,7 +94,7 @@ do { \ if (*p_langmap \ && (condition) \ - && (!p_lnr || (p_lnr && typebuf_maplen() == 0)) \ + && (p_lrm || (!p_lrm && KeyTyped)) \ && !KeyStuffed \ && (c) >= 0) \ { \ diff --git a/src/nvim/option.c b/src/nvim/option.c index 0b4d9aae5d..b3b4dc1e0a 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -3630,6 +3630,12 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, } else if ((int *)varp == &p_force_off && p_force_off == true) { p_force_off = false; return (char *)e_unsupportedoption; + } else if ((int *)varp == &p_lrm) { + // 'langremap' -> !'langnoremap' + p_lnr = !p_lrm; + } else if ((int *)varp == &p_lnr) { + // 'langnoremap' -> !'langremap' + p_lrm = !p_lnr; // 'undofile' } else if ((int *)varp == &curbuf->b_p_udf || (int *)varp == &p_udf) { // Only take action when the option was set. When reset we do not diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 2475a0b6a1..4ee0f4f225 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -476,8 +476,9 @@ EXTERN char_u *p_isp; // 'isprint' EXTERN int p_js; // 'joinspaces' EXTERN char_u *p_kp; // 'keywordprg' EXTERN char_u *p_km; // 'keymodel' -EXTERN char_u *p_langmap; // 'langmap'*/ -EXTERN int p_lnr; // 'langnoremap'*/ +EXTERN char_u *p_langmap; // 'langmap' +EXTERN int p_lnr; // 'langnoremap' +EXTERN int p_lrm; // 'langremap' EXTERN char_u *p_lm; // 'langmenu' EXTERN char_u *p_lispwords; // 'lispwords' EXTERN long p_ls; // 'laststatus' diff --git a/src/nvim/options.lua b/src/nvim/options.lua index ee2b8a563d..9dff3410d6 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -1346,6 +1346,12 @@ return { varname='p_lnr', defaults={if_true={vi=false, vim=true}} }, + { + full_name='langremap', abbreviation='lrm', + type='bool', scope={'global'}, + varname='p_lrm', + defaults={if_true={vi=true, vim=false}} + }, { full_name='laststatus', abbreviation='ls', type='number', scope={'global'}, diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim index d937565ce5..3b6dcdccf5 100644 --- a/src/nvim/testdir/test_mapping.vim +++ b/src/nvim/testdir/test_mapping.vim @@ -35,29 +35,73 @@ func Test_map_ctrl_c_visual() endfunc func Test_map_langmap() - " langmap should not get remapped in insert mode - inoremap { FAIL_ilangmap - set langmap=+{ langnoremap + if !has('langmap') + return + endif + + " check langmap applies in normal mode + set langmap=+- nolangremap + new + call setline(1, ['a', 'b', 'c']) + 2 + call assert_equal('b', getline('.')) + call feedkeys("+", "xt") + call assert_equal('a', getline('.')) + + " check no remapping + map x + + 2 + call feedkeys("x", "xt") + call assert_equal('c', getline('.')) + + " check with remapping + set langremap + 2 + call feedkeys("x", "xt") + call assert_equal('a', getline('.')) + + unmap x + bwipe! + + " 'langnoremap' follows 'langremap' and vise versa + set langremap + set langnoremap + call assert_equal(0, &langremap) + set langremap + call assert_equal(0, &langnoremap) + set nolangremap + call assert_equal(1, &langnoremap) + + " langmap should not apply in insert mode, 'langremap' doesn't matter + set langmap=+{ nolangremap + call feedkeys("Go+\", "xt") + call assert_equal('+', getline('$')) + set langmap=+{ langremap call feedkeys("Go+\", "xt") call assert_equal('+', getline('$')) - " Insert-mode expr mapping with langmap - inoremap { "FAIL_iexplangmap" - call feedkeys("Go+\", "xt") - call assert_equal('+', getline('$')) - iunmap { - - " langmap should not get remapped in Command-line mode - cnoremap { FAIL_clangmap + " langmap used for register name in insert mode. + call setreg('a', 'aaaa') + call setreg('b', 'bbbb') + call setreg('c', 'cccc') + set langmap=ab langremap + call feedkeys("Go\a\", "xt") + call assert_equal('bbbb', getline('$')) + call feedkeys("Go\\a\", "xt") + call assert_equal('bbbb', getline('$')) + " mapping does not apply + imap c a + call feedkeys("Go\c\", "xt") + call assert_equal('cccc', getline('$')) + imap a c + call feedkeys("Go\a\", "xt") + call assert_equal('bbbb', getline('$')) + + " langmap should not apply in Command-line mode + set langmap=+{ nolangremap call feedkeys(":call append(line('$'), '+')\", "xt") call assert_equal('+', getline('$')) - cunmap { - " Command-line mode expr mapping with langmap - cnoremap { "FAIL_cexplangmap" - call feedkeys(":call append(line('$'), '+')\", "xt") - call assert_equal('+', getline('$')) - cunmap { set nomodified endfunc diff --git a/src/nvim/version.c b/src/nvim/version.c index fdf5436a98..ba1ebc8f2c 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -205,7 +205,7 @@ static const int included_patches[] = { // 2239, // 2238 NA 2237, - // 2236, + 2236, 2235, // 2234 NA 2233, From 45a13c4bbc4a7c4f62a299c832ecc192d8f3cd0a Mon Sep 17 00:00:00 2001 From: lonerover Date: Wed, 22 Mar 2017 12:52:15 +0800 Subject: [PATCH 0309/1671] vim-patch:7.4.2306 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Default value for 'langremap' is wrong. Solution: Set the right value. (Jürgen Krämer) Add a test. https://github.com/vim/vim/commit/da9ce2cde11ddd0e16cdfbab6d4ac4e8110218e1 --- src/nvim/testdir/test_mapping.vim | 8 ++++++++ src/nvim/version.c | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim index 3b6dcdccf5..6b313ff54f 100644 --- a/src/nvim/testdir/test_mapping.vim +++ b/src/nvim/testdir/test_mapping.vim @@ -72,6 +72,14 @@ func Test_map_langmap() set nolangremap call assert_equal(1, &langnoremap) + " check default values + set langnoremap& + call assert_equal(1, &langnoremap) + call assert_equal(0, &langremap) + set langremap& + call assert_equal(1, &langnoremap) + call assert_equal(0, &langremap) + " langmap should not apply in insert mode, 'langremap' doesn't matter set langmap=+{ nolangremap call feedkeys("Go+\", "xt") diff --git a/src/nvim/version.c b/src/nvim/version.c index ba1ebc8f2c..0ee0419849 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -135,7 +135,7 @@ static const int included_patches[] = { 2309, // 2308 NA 2307, - // 2306, + 2306, 2305, // 2304 NA 2303, From ac22238b6af1d37fab09fc2173d5ed2019652c41 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 1 Apr 2017 20:57:23 +0300 Subject: [PATCH 0310/1671] unittests: Replace two environment variables with one TRACE_LEVEL --- test/README.md | 17 +++++++---------- test/unit/helpers.lua | 21 ++++++++++++++++----- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/test/README.md b/test/README.md index d0871711a8..2857cc0ecf 100644 --- a/test/README.md +++ b/test/README.md @@ -27,8 +27,8 @@ groups by the semantic component they are testing. Test behaviour is affected by environment variables. Currently supported (Functional, Unit, Benchmarks) (when Defined; when set to _1_; when defined, -treated as Integer; when defined, treated as String; !must be defined to -function properly): +treated as Integer; when defined, treated as String; when defined, treated as +Number; !must be defined to function properly): `GDB` (F) (D): makes nvim instances to be run under `gdbserver`. It will be accessible on `localhost:7777`: use `gdb build/bin/nvim`, type `target remote @@ -103,11 +103,8 @@ defined and this variable is not) cores are checked for after each test. `NVIM_TEST_RUN_TESTTEST` (U) (1): allows running `test/unit/testtest_spec.lua` used to check how testing infrastructure works. -`NVIM_TEST_NO_TRACE` (U) (1): omits getting traces from tests. This means that -if tests crashed without core dump you will have no clues regarding where, but -this makes tests run a lot faster. Combine with `NVIM_TEST_MAIN_CDEFS` for -maximal speed. - -`NVIM_TEST_TRACE_EVERYTHING` (U) (1): by default unit test only record C calls -which is faster then recording everything. Set this variable to 1 if you want to -see all traces. +`NVIM_TEST_TRACE_LEVEL` (U) (N): specifies unit tests tracing level: `0` +disables tracing (the fastest, but you get no data if tests crash and there was +no core dump generated), `1` or empty/undefined leaves only C function cals and +returns in the trace (faster then recording everything), `2` records all +function calls, returns and lua source lines exuecuted. diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 2cea88c8c0..93e1e91173 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -539,10 +539,16 @@ local tracehelp = dedent([[ ]]) local function child_sethook(wr) - if os.getenv('NVIM_TEST_NO_TRACE') == '1' then + local trace_level = os.getenv('NVIM_TEST_TRACE_LEVEL') + if not trace_level or trace_level == '' then + trace_level = 1 + else + trace_level = tonumber(trace_level) + end + if trace_level <= 0 then return end - local trace_only_c = (os.getenv('NVIM_TEST_TRACE_EVERYTHING') ~= '1') + local trace_only_c = trace_level <= 1 local function hook(reason, lnum) local info = nil if reason ~= 'tail return' then -- tail return @@ -651,9 +657,14 @@ local function check_child_err(rd) end local res = sc.read(rd, 2) if #res ~= 2 then - local error = '\nTest crashed, trace:\n' .. tracehelp - for i = 1, #trace do - error = error .. trace[i] + local error + if #trace == 0 then + error = '\nTest crashed, no trace available\n' + else + error = '\nTest crashed, trace:\n' .. tracehelp + for i = 1, #trace do + error = error .. trace[i] + end end assert.just_fail(error) end From cc4523013f8e693f92de3b96ff36065895c60974 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 1 Apr 2017 21:13:21 +0300 Subject: [PATCH 0311/1671] eval,fileio: Omit additional fsync() call Fixes #6420 --- src/nvim/eval.c | 2 +- src/nvim/os/fileio.c | 31 +++++++++++++++++++++++-------- test/unit/os/fileio_spec.lua | 19 +++++++++++++++++++ 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 15b712e7de..a6774a3a0b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -17289,7 +17289,7 @@ static bool write_list(FileDescriptor *const fp, const list_T *const list, } } } - if ((error = file_fsync(fp)) != 0) { + if ((error = file_flush(fp)) != 0) { goto write_list_error; } return true; diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c index 775f2bd449..3c928363cc 100644 --- a/src/nvim/os/fileio.c +++ b/src/nvim/os/fileio.c @@ -143,21 +143,36 @@ int file_free(FileDescriptor *const fp) FUNC_ATTR_NONNULL_ALL /// @param[in,out] fp File to work with. /// /// @return 0 or error code. -int file_fsync(FileDescriptor *const fp) +int file_flush(FileDescriptor *const fp) FUNC_ATTR_NONNULL_ALL { if (!fp->wr) { return 0; } file_rb_write_full_cb(fp->rv, fp); - if (fp->_error != 0) { - const int error = fp->_error; - fp->_error = 0; - return error; + const int error = fp->_error; + fp->_error = 0; + return error; +} + +/// Flush file modifications to disk and run fsync() +/// +/// @param[in,out] fp File to work with. +/// +/// @return 0 or error code. +int file_fsync(FileDescriptor *const fp) + FUNC_ATTR_NONNULL_ALL +{ + if (!fp->wr) { + return 0; } - const int error = os_fsync(fp->fd); - if (error != UV_EINVAL && error != UV_EROFS) { - return error; + const int flush_error = file_flush(fp); + if (flush_error != 0) { + return flush_error; + } + const int fsync_error = os_fsync(fp->fd); + if (fsync_error != UV_EINVAL && fsync_error != UV_EROFS) { + return fsync_error; } return 0; } diff --git a/test/unit/os/fileio_spec.lua b/test/unit/os/fileio_spec.lua index 7a738ce85c..5e1b2523fa 100644 --- a/test/unit/os/fileio_spec.lua +++ b/test/unit/os/fileio_spec.lua @@ -80,6 +80,10 @@ local function file_read(fp, size) return ret1, ret2 end +local function file_flush(fp) + return m.file_flush(fp) +end + local function file_fsync(fp) return m.file_fsync(fp) end @@ -244,6 +248,21 @@ describe('file_fsync', function() end) end) +describe('file_flush', function() + itp('can flush writes to disk', function() + local err, fp = file_open(filec, m.kFileCreateOnly, 384) + eq(0, file_flush(fp)) + eq(0, err) + eq(0, lfs.attributes(filec).size) + local wsize = file_write(fp, 'test') + eq(4, wsize) + eq(0, lfs.attributes(filec).size) + eq(0, file_flush(fp)) + eq(wsize, lfs.attributes(filec).size) + eq(0, m.file_close(fp)) + end) +end) + describe('file_read', function() itp('can read small chunks of input until eof', function() local err, fp = file_open(file1, 0, 384) From 19690d4a25f15dfa752ac3723384f1d33f06329a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 1 Apr 2017 22:26:50 +0300 Subject: [PATCH 0312/1671] eval: Do not allocate FileDescriptor --- src/nvim/eval.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a6774a3a0b..f0d78a2508 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -17439,21 +17439,21 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (fname == NULL) { return; } - FileDescriptor *fp; + FileDescriptor fp; int error; rettv->vval.v_number = -1; if (*fname == NUL) { EMSG(_("E482: Can't open file with an empty name")); - } else if ((fp = file_open_new(&error, fname, - ((append ? kFileAppend : kFileTruncate) - | kFileCreate), 0666)) == NULL) { + } else if ((error = file_open(&fp, fname, + ((append ? kFileAppend : kFileTruncate) + | kFileCreate), 0666)) != 0) { emsgf(_("E482: Can't open file %s for writing: %s"), fname, os_strerror(error)); } else { - if (write_list(fp, argvars[0].vval.v_list, binary)) { + if (write_list(&fp, argvars[0].vval.v_list, binary)) { rettv->vval.v_number = 0; } - if ((error = file_free(fp)) != 0) { + if ((error = file_close(&fp)) != 0) { emsgf(_("E80: Error when closing file %s: %s"), fname, os_strerror(error)); } From 337b6179df852350b52409fd3806e4b47ab2875b Mon Sep 17 00:00:00 2001 From: Matthew Malcomson Date: Sat, 1 Apr 2017 20:50:29 +0100 Subject: [PATCH 0313/1671] 'pastetoggle': support value >1 char (#6421) If we `set pastetoggle=abcde`, and manually type it, then `vgetorpeek()` sees part of the option before it has all been inserted into the typebuffer. To signify this it sets `keylen = KEYLEN_PART_KEY`, but the condition about whether to return the current key from `vgetorpeek()` only checks for `keylen = KEYLEN_PART_MAP`. Add a check for `KEYLEN_PART_KEY` to account for the `'pastetoggle'` option. --- src/nvim/getchar.c | 2 +- test/functional/options/pastetoggle_spec.lua | 37 ++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 test/functional/options/pastetoggle_spec.lua diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 7143819e21..b83681ad01 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1903,7 +1903,7 @@ static int vgetorpeek(int advance) } if ((mp == NULL || max_mlen >= mp_match_len) - && keylen != KEYLEN_PART_MAP) { + && keylen != KEYLEN_PART_MAP && keylen != KEYLEN_PART_KEY) { // No matching mapping found or found a non-matching mapping that // matches at least what the matching mapping matched keylen = 0; diff --git a/test/functional/options/pastetoggle_spec.lua b/test/functional/options/pastetoggle_spec.lua new file mode 100644 index 0000000000..e449df31f5 --- /dev/null +++ b/test/functional/options/pastetoggle_spec.lua @@ -0,0 +1,37 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local feed = helpers.feed +local execute = helpers.execute +local eq = helpers.eq +local eval = helpers.eval +local sleep = helpers.sleep + +describe("'pastetoggle' option", function() + before_each(function() + clear() + execute('set nopaste') + execute('set pastetoggle=a') + end) + it("toggles 'paste'", function() + eq(eval('&paste'), 0) + feed('a') + -- Need another key so that the vgetorpeek() function returns. + feed('j') + eq(eval('&paste'), 1) + end) + it("multiple key 'pastetoggle' is waited for", function() + eq(eval('&paste'), 0) + local pastetoggle = 'lllll' + execute('set pastetoggle=' .. pastetoggle) + execute('set timeoutlen=1', 'set ttimoutlen=10000') + feed(pastetoggle:sub(0, 2)) + -- sleep() for long enough that vgetorpeek() is gotten into, but short + -- enough that ttimeoutlen is not reached. + sleep(200) + feed(pastetoggle:sub(3, -1)) + -- Need another key so that the vgetorpeek() function returns. + feed('j') + eq(eval('&paste'), 1) + end) +end) From dd4a5fcbb65ade08b5d2c7951b2924d2d04dc99e Mon Sep 17 00:00:00 2001 From: Matthieu Coudron Date: Mon, 20 Mar 2017 22:56:58 +0100 Subject: [PATCH 0314/1671] tui: 'guicursor' shape #6044 Closes #2583 --- src/nvim/api/ui.c | 10 ++ src/nvim/cursor_shape.c | 140 ++++++++++++++++++++-------- src/nvim/cursor_shape.h | 73 ++++++++------- src/nvim/syntax.c | 53 +++++------ src/nvim/tui/tui.c | 170 +++++++++++++++++++++++++--------- src/nvim/tui/tui.h | 2 + src/nvim/ui.c | 9 ++ src/nvim/ui.h | 1 + src/nvim/ui_bridge.c | 19 ++++ test/functional/ui/screen.lua | 6 ++ 10 files changed, 336 insertions(+), 147 deletions(-) diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 625bcc6b4b..a95be0fabb 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -12,6 +12,7 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/popupmnu.h" +#include "nvim/cursor_shape.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/ui.c.generated.h" @@ -69,6 +70,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, ui->clear = remote_ui_clear; ui->eol_clear = remote_ui_eol_clear; ui->cursor_goto = remote_ui_cursor_goto; + ui->cursor_style_set = remote_ui_cursor_style_set; ui->update_menu = remote_ui_update_menu; ui->busy_start = remote_ui_busy_start; ui->busy_stop = remote_ui_busy_stop; @@ -298,6 +300,14 @@ static void remote_ui_scroll(UI *ui, int count) push_call(ui, "scroll", args); } +static void remote_ui_cursor_style_set(UI *ui, Dictionary styles) +{ + Array args = ARRAY_DICT_INIT; + Object copy = copy_object(DICTIONARY_OBJ(styles)); + ADD(args, copy); + push_call(ui, "cursor_style_set", args); +} + static void remote_ui_highlight_set(UI *ui, HlAttrs attrs) { Array args = ARRAY_DICT_INIT; diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index b50462664c..9f0eb215ef 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -7,40 +7,84 @@ #include "nvim/charset.h" #include "nvim/strings.h" #include "nvim/syntax.h" +#include "nvim/api/private/helpers.h" +#include "nvim/ui.h" -/* - * Handling of cursor and mouse pointer shapes in various modes. - */ - +/// Handling of cursor and mouse pointer shapes in various modes. static cursorentry_T shape_table[SHAPE_IDX_COUNT] = { - /* The values will be filled in from the 'guicursor' and 'mouseshape' - * defaults when Vim starts. - * Adjust the SHAPE_IDX_ defines when making changes! */ - {0, 0, 0, 700L, 400L, 250L, 0, 0, "n", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "v", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "i", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "r", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "c", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "ci", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "cr", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "o", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "ve", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "e", SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "s", SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "sd", SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "vs", SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "vd", SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "m", SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "ml", SHAPE_MOUSE}, - {0, 0, 0, 100L, 100L, 100L, 0, 0, "sm", SHAPE_CURSOR}, + // The values will be filled in from the 'guicursor' and 'mouseshape' + // defaults when Vim starts. + // Adjust the SHAPE_IDX_ defines when making changes! + { "normal", + 0, 0, 0, 700L, 400L, 250L, 0, 0, "n", SHAPE_CURSOR+SHAPE_MOUSE }, + { "visual", + 0, 0, 0, 700L, 400L, 250L, 0, 0, "v", SHAPE_CURSOR+SHAPE_MOUSE }, + { "insert", + 0, 0, 0, 700L, 400L, 250L, 0, 0, "i", SHAPE_CURSOR+SHAPE_MOUSE }, + { "replace", + 0, 0, 0, 700L, 400L, 250L, 0, 0, "r", SHAPE_CURSOR+SHAPE_MOUSE }, + { "cmd_normal", + 0, 0, 0, 700L, 400L, 250L, 0, 0, "c", SHAPE_CURSOR+SHAPE_MOUSE }, + { "cmd_insert", 0, + 0, 0, 700L, 400L, 250L, 0, 0, "ci", SHAPE_CURSOR+SHAPE_MOUSE }, + { "cmd_replace", + 0, 0, 0, 700L, 400L, 250L, 0, 0, "cr", SHAPE_CURSOR+SHAPE_MOUSE }, + { "pending", + 0, 0, 0, 700L, 400L, 250L, 0, 0, "o", SHAPE_CURSOR+SHAPE_MOUSE }, + { "visual_select", + 0, 0, 0, 700L, 400L, 250L, 0, 0, "ve", SHAPE_CURSOR+SHAPE_MOUSE }, + { "cmd_line", 0, 0, 0, 0L, 0L, 0L, 0, 0, "e", SHAPE_MOUSE }, + { "statusline", 0, 0, 0, 0L, 0L, 0L, 0, 0, "s", SHAPE_MOUSE }, + { "drag_statusline", 0, 0, 0, 0L, 0L, 0L, 0, 0, "sd", SHAPE_MOUSE }, + { "vsep", 0, 0, 0, 0L, 0L, 0L, 0, 0, "vs", SHAPE_MOUSE }, + { "vdrag", 0, 0, 0, 0L, 0L, 0L, 0, 0, "vd", SHAPE_MOUSE }, + { "more", 0, 0, 0, 0L, 0L, 0L, 0, 0, "m", SHAPE_MOUSE }, + { "more_lastline", 0, 0, 0, 0L, 0L, 0L, 0, 0, "ml", SHAPE_MOUSE }, + { "match_paren", 0, 0, 0, 100L, 100L, 100L, 0, 0, "sm", SHAPE_CURSOR }, }; -/* - * Parse the 'guicursor' option ("what" is SHAPE_CURSOR) or 'mouseshape' - * ("what" is SHAPE_MOUSE). - * Returns error message for an illegal option, NULL otherwise. - */ +/// Converts cursor_shapes into a Dictionary of dictionaries +/// @return a dictionary of the form {"normal" : { "cursor_shape": ... }, ...} +Dictionary cursor_shape_dict(void) +{ + Dictionary all = ARRAY_DICT_INIT; + + for (int i = 0; i < SHAPE_IDX_COUNT; i++) { + Dictionary dic = ARRAY_DICT_INIT; + cursorentry_T *cur = &shape_table[i]; + if (cur->used_for & SHAPE_MOUSE) { + PUT(dic, "mouse_shape", INTEGER_OBJ(cur->mshape)); + } + if (cur->used_for & SHAPE_CURSOR) { + String shape_str; + switch (cur->shape) { + case SHAPE_BLOCK: shape_str = cstr_to_string("block"); break; + case SHAPE_VER: shape_str = cstr_to_string("vertical"); break; + case SHAPE_HOR: shape_str = cstr_to_string("horizontal"); break; + default: shape_str = cstr_to_string("unknown"); + } + PUT(dic, "cursor_shape", STRING_OBJ(shape_str)); + PUT(dic, "cell_percentage", INTEGER_OBJ(cur->percentage)); + PUT(dic, "blinkwait", INTEGER_OBJ(cur->blinkwait)); + PUT(dic, "blinkon", INTEGER_OBJ(cur->blinkon)); + PUT(dic, "blinkoff", INTEGER_OBJ(cur->blinkoff)); + PUT(dic, "hl_id", INTEGER_OBJ(cur->id)); + PUT(dic, "id_lm", INTEGER_OBJ(cur->id_lm)); + } + PUT(dic, "short_name", STRING_OBJ(cstr_to_string(cur->name))); + + PUT(all, cur->full_name, DICTIONARY_OBJ(dic)); + } + + return all; +} + +/// Parse the 'guicursor' option +/// +/// @param what either SHAPE_CURSOR or SHAPE_MOUSE ('mouseshape') +/// +/// @returns error message for an illegal option, NULL otherwise. char_u *parse_shape_opt(int what) { char_u *modep; @@ -71,19 +115,18 @@ char_u *parse_shape_opt(int what) return (char_u *)N_("E546: Illegal mode"); commap = vim_strchr(modep, ','); - /* - * Repeat for all mode's before the colon. - * For the 'a' mode, we loop to handle all the modes. - */ + // Repeat for all mode's before the colon. + // For the 'a' mode, we loop to handle all the modes. all_idx = -1; assert(modep < colonp); while (modep < colonp || all_idx >= 0) { if (all_idx < 0) { - /* Find the mode. */ - if (modep[1] == '-' || modep[1] == ':') + // Find the mode + if (modep[1] == '-' || modep[1] == ':') { len = 1; - else + } else { len = 2; + } if (len == 1 && TOLOWER_ASC(modep[0]) == 'a') { all_idx = SHAPE_IDX_COUNT - 1; @@ -100,11 +143,11 @@ char_u *parse_shape_opt(int what) modep += len + 1; } - if (all_idx >= 0) + if (all_idx >= 0) { idx = all_idx--; - else if (round == 2) { + } else if (round == 2) { { - /* Set the defaults, for the missing parts */ + // Set the defaults, for the missing parts shape_table[idx].shape = SHAPE_BLOCK; shape_table[idx].blinkwait = 700L; shape_table[idx].blinkon = 400L; @@ -208,6 +251,23 @@ char_u *parse_shape_opt(int what) shape_table[SHAPE_IDX_VE].id_lm = shape_table[SHAPE_IDX_V].id_lm; } } - + ui_cursor_style_set(); return NULL; } + + +/// Map cursor mode from string to integer +/// +/// @param mode Fullname of the mode whose id we are looking for +/// @return -1 in case of failure, else the matching SHAPE_ID* integer +int cursor_mode_str2int(const char *mode) +{ + for (int current_mode = 0; current_mode < SHAPE_IDX_COUNT; current_mode++) { + if (strcmp(shape_table[current_mode].full_name, mode) == 0) { + return current_mode; + } + } + ELOG("Unknown mode %s", mode); + return -1; +} + diff --git a/src/nvim/cursor_shape.h b/src/nvim/cursor_shape.h index 9ce1b6e0a0..127d0df555 100644 --- a/src/nvim/cursor_shape.h +++ b/src/nvim/cursor_shape.h @@ -1,32 +1,34 @@ #ifndef NVIM_CURSOR_SHAPE_H #define NVIM_CURSOR_SHAPE_H -/* - * struct to store values from 'guicursor' and 'mouseshape' - */ -/* Indexes in shape_table[] */ -#define SHAPE_IDX_N 0 /* Normal mode */ -#define SHAPE_IDX_V 1 /* Visual mode */ -#define SHAPE_IDX_I 2 /* Insert mode */ -#define SHAPE_IDX_R 3 /* Replace mode */ -#define SHAPE_IDX_C 4 /* Command line Normal mode */ -#define SHAPE_IDX_CI 5 /* Command line Insert mode */ -#define SHAPE_IDX_CR 6 /* Command line Replace mode */ -#define SHAPE_IDX_O 7 /* Operator-pending mode */ -#define SHAPE_IDX_VE 8 /* Visual mode with 'selection' exclusive */ -#define SHAPE_IDX_CLINE 9 /* On command line */ -#define SHAPE_IDX_STATUS 10 /* A status line */ -#define SHAPE_IDX_SDRAG 11 /* dragging a status line */ -#define SHAPE_IDX_VSEP 12 /* A vertical separator line */ -#define SHAPE_IDX_VDRAG 13 /* dragging a vertical separator line */ -#define SHAPE_IDX_MORE 14 /* Hit-return or More */ -#define SHAPE_IDX_MOREL 15 /* Hit-return or More in last line */ -#define SHAPE_IDX_SM 16 /* showing matching paren */ -#define SHAPE_IDX_COUNT 17 +/// struct to store values from 'guicursor' and 'mouseshape' +/// Indexes in shape_table[] +typedef enum { +SHAPE_IDX_N = 0, ///< Normal mode +SHAPE_IDX_V = 1, ///< Visual mode +SHAPE_IDX_I = 2, ///< Insert mode +SHAPE_IDX_R = 3, ///< Replace mode +SHAPE_IDX_C = 4, ///< Command line Normal mode +SHAPE_IDX_CI = 5, ///< Command line Insert mode +SHAPE_IDX_CR = 6, ///< Command line Replace mode +SHAPE_IDX_O = 7, ///< Operator-pending mode +SHAPE_IDX_VE = 8, ///< Visual mode with 'selection' exclusive +SHAPE_IDX_CLINE = 9, ///< On command line +SHAPE_IDX_STATUS = 10, ///< status line +SHAPE_IDX_SDRAG = 11, ///< dragging a status line +SHAPE_IDX_VSEP = 12, ///< A vertical separator line +SHAPE_IDX_VDRAG = 13, ///< dragging a vertical separator line +SHAPE_IDX_MORE = 14, ///< Hit-return or More +SHAPE_IDX_MOREL = 15, ///< Hit-return or More in last line +SHAPE_IDX_SM = 16, ///< showing matching paren +SHAPE_IDX_COUNT = 17 +} MouseMode; -#define SHAPE_BLOCK 0 /* block cursor */ -#define SHAPE_HOR 1 /* horizontal bar cursor */ -#define SHAPE_VER 2 /* vertical bar cursor */ +typedef enum { +SHAPE_BLOCK = 0, ///< block cursor +SHAPE_HOR = 1, ///< horizontal bar cursor +SHAPE_VER = 2 ///< vertical bar cursor +} CursorShape; #define MSHAPE_NUMBERED 1000 /* offset for shapes identified by number */ #define MSHAPE_HIDE 1 /* hide mouse pointer */ @@ -35,16 +37,17 @@ #define SHAPE_CURSOR 2 /* used for text cursor shape */ typedef struct cursor_entry { - int shape; /* one of the SHAPE_ defines */ - int mshape; /* one of the MSHAPE defines */ - int percentage; /* percentage of cell for bar */ - long blinkwait; /* blinking, wait time before blinking starts */ - long blinkon; /* blinking, on time */ - long blinkoff; /* blinking, off time */ - int id; /* highlight group ID */ - int id_lm; /* highlight group ID for :lmap mode */ - char *name; /* mode name (fixed) */ - char used_for; /* SHAPE_MOUSE and/or SHAPE_CURSOR */ + char *full_name; ///< mode full name + CursorShape shape; ///< cursor shape: one of the SHAPE_ defines + int mshape; ///< mouse shape: one of the MSHAPE defines + int percentage; ///< percentage of cell for bar + long blinkwait; ///< blinking, wait time before blinking starts + long blinkon; ///< blinking, on time + long blinkoff; ///< blinking, off time + int id; ///< highlight group ID + int id_lm; ///< highlight group ID for :lmap mode + char *name; ///< mode short name + char used_for; ///< SHAPE_MOUSE and/or SHAPE_CURSOR } cursorentry_T; diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 4a7b4a0eac..4f2f44ff86 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -42,29 +42,29 @@ static bool did_syntax_onoff = false; -// Structure that stores information about a highlight group. -// The ID of a highlight group is also called group ID. It is the index in -// the highlight_ga array PLUS ONE. +/// Structure that stores information about a highlight group. +/// The ID of a highlight group is also called group ID. It is the index in +/// the highlight_ga array PLUS ONE. struct hl_group { - char_u *sg_name; // highlight group name - char_u *sg_name_u; // uppercase of sg_name - int sg_attr; // Screen attr - int sg_link; // link to this highlight group ID - int sg_set; // combination of SG_* flags - scid_T sg_scriptID; // script in which the group was last set + char_u *sg_name; ///< highlight group name + char_u *sg_name_u; ///< uppercase of sg_name + int sg_attr; ///< Screen attr + int sg_link; ///< link to this highlight group ID + int sg_set; ///< combination of SG_* flags + scid_T sg_scriptID; ///< script in which the group was last set // for terminal UIs - int sg_cterm; // "cterm=" highlighting attr - int sg_cterm_fg; // terminal fg color number + 1 - int sg_cterm_bg; // terminal bg color number + 1 - int sg_cterm_bold; // bold attr was set for light color + int sg_cterm; ///< "cterm=" highlighting attr + int sg_cterm_fg; ///< terminal fg color number + 1 + int sg_cterm_bg; ///< terminal bg color number + 1 + int sg_cterm_bold; ///< bold attr was set for light color // for RGB UIs - int sg_gui; // "gui=" highlighting attributes - RgbValue sg_rgb_fg; // RGB foreground color - RgbValue sg_rgb_bg; // RGB background color - RgbValue sg_rgb_sp; // RGB special color - uint8_t *sg_rgb_fg_name; // RGB foreground color name - uint8_t *sg_rgb_bg_name; // RGB background color name - uint8_t *sg_rgb_sp_name; // RGB special color name + int sg_gui; ///< "gui=" highlighting attributes + RgbValue sg_rgb_fg; ///< RGB foreground color + RgbValue sg_rgb_bg; ///< RGB background color + RgbValue sg_rgb_sp; ///< RGB special color + uint8_t *sg_rgb_fg_name; ///< RGB foreground color name + uint8_t *sg_rgb_bg_name; ///< RGB background color name + uint8_t *sg_rgb_sp_name; ///< RGB special color name }; #define SG_CTERM 2 // cterm has been set @@ -7165,12 +7165,13 @@ int syn_namen2id(char_u *linep, int len) return id; } -/* - * Find highlight group name in the table and return it's ID. - * The argument is a pointer to the name and the length of the name. - * If it doesn't exist yet, a new entry is created. - * Return 0 for failure. - */ +/// Find highlight group name in the table and return it's ID. +/// If it doesn't exist yet, a new entry is created. +/// +/// @param pp Highlight group name +/// @param len length of \p pp +/// +/// @return 0 for failure else the id of the group int syn_check_group(char_u *pp, int len) { char_u *name = vim_strnsave(pp, len); diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index ebdfb1e7a1..ebcef33fa1 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -31,6 +31,8 @@ #include "nvim/ugrid.h" #include "nvim/tui/input.h" #include "nvim/tui/tui.h" +#include "nvim/cursor_shape.h" +#include "nvim/syntax.h" // Space reserved in the output buffer to restore the cursor to normal when // flushing. No existing terminal will require 32 bytes to do that. @@ -69,12 +71,12 @@ typedef struct { bool can_use_terminal_scroll; bool mouse_enabled; bool busy; + cursorentry_T cursor_shapes[SHAPE_IDX_COUNT]; HlAttrs print_attrs; int showing_mode; struct { int enable_mouse, disable_mouse; int enable_bracketed_paste, disable_bracketed_paste; - int set_cursor_shape_bar, set_cursor_shape_ul, set_cursor_shape_block; int set_rgb_foreground, set_rgb_background; int enable_focus_reporting, disable_focus_reporting; } unibi_ext; @@ -97,6 +99,7 @@ UI *tui_start(void) ui->clear = tui_clear; ui->eol_clear = tui_eol_clear; ui->cursor_goto = tui_cursor_goto; + ui->cursor_style_set = tui_cursor_style_set; ui->update_menu = tui_update_menu; ui->busy_start = tui_busy_start; ui->busy_stop = tui_busy_stop; @@ -131,9 +134,6 @@ static void terminfo_start(UI *ui) data->unibi_ext.disable_mouse = -1; data->unibi_ext.enable_bracketed_paste = -1; data->unibi_ext.disable_bracketed_paste = -1; - data->unibi_ext.set_cursor_shape_bar = -1; - data->unibi_ext.set_cursor_shape_ul = -1; - data->unibi_ext.set_cursor_shape_block = -1; data->unibi_ext.enable_focus_reporting = -1; data->unibi_ext.disable_focus_reporting = -1; data->out_fd = 1; @@ -147,7 +147,6 @@ static void terminfo_start(UI *ui) } fix_terminfo(data); // Initialize the cursor shape. - unibi_out(ui, data->unibi_ext.set_cursor_shape_block); // Set 't_Co' from the result of unibilium & fix_terminfo. t_colors = unibi_get_num(data->ut, unibi_max_colors); // Enter alternate screen and clear @@ -434,6 +433,64 @@ static void tui_cursor_goto(UI *ui, int row, int col) unibi_goto(ui, row, col); } +CursorShape tui_cursor_decode_shape(const char *shape_str) +{ + CursorShape shape = 0; + if (strcmp(shape_str, "block") == 0) { + shape = SHAPE_BLOCK; + } else if (strcmp(shape_str, "vertical") == 0) { + shape = SHAPE_VER; + } else if (strcmp(shape_str, "horizontal") == 0) { + shape = SHAPE_HOR; + } else { + EMSG2(_(e_invarg2), shape_str); + } + return shape; +} + +static cursorentry_T decode_cursor_entry(Dictionary args) +{ + cursorentry_T r; + + for (size_t i = 0; i < args.size; i++) { + char *keyStr = args.items[i].key.data; + Object value = args.items[i].value; + + if (strcmp(keyStr, "cursor_shape") == 0) { + r.shape = tui_cursor_decode_shape(args.items[i].value.data.string.data); + } else if (strcmp(keyStr, "blinkon") == 0) { + r.blinkon = (int)value.data.integer; + } else if (strcmp(keyStr, "blinkoff") == 0) { + r.blinkoff = (int)value.data.integer; + } else if (strcmp(keyStr, "hl_id") == 0) { + r.id = (int)value.data.integer; + } + } + return r; +} + +static void tui_cursor_style_set(UI *ui, Dictionary args) +{ + TUIData *data = ui->data; + + for (size_t i = 0; i < args.size; i++) { + char *mode_name = args.items[i].key.data; + const int mode_id = cursor_mode_str2int(mode_name); + + if (mode_id < 0) { + WLOG("Unknown mode '%s'", mode_name); + continue; + } + cursorentry_T r = decode_cursor_entry(args.items[i].value.data.dictionary); + r.full_name = mode_name; + data->cursor_shapes[mode_id] = r; + } + + // force redrawal + MouseMode cursor_mode = tui_mode2cursor(data->showing_mode); + tui_set_cursor(ui, cursor_mode); +} + static void tui_update_menu(UI *ui) { // Do nothing; menus are for GUI only @@ -467,33 +524,90 @@ static void tui_mouse_off(UI *ui) } } +/// @param mode one of SHAPE_XXX +static void tui_set_cursor(UI *ui, MouseMode mode) +{ + TUIData *data = ui->data; + cursorentry_T c = data->cursor_shapes[mode]; + int shape = c.shape; + bool inside_tmux = os_getenv("TMUX") != NULL; + unibi_var_t vars[26 + 26] = { { 0 } }; + +# define TMUX_WRAP(seq) (inside_tmux ? "\x1bPtmux;\x1b" seq "\x1b\\" : seq) + // Support changing cursor shape on some popular terminals. + const char *term_prog = os_getenv("TERM_PROGRAM"); + const char *vte_version = os_getenv("VTE_VERSION"); + + if ((term_prog && !strcmp(term_prog, "Konsole")) + || os_getenv("KONSOLE_DBUS_SESSION") != NULL) { + // Konsole uses a proprietary escape code to set the cursor shape + // and does not support DECSCUSR. + switch (shape) { + case SHAPE_BLOCK: shape = 0; break; + case SHAPE_VER: shape = 1; break; + case SHAPE_HOR: shape = 3; break; + default: WLOG("Unknown shape value %d", shape); break; + } + printf(TMUX_WRAP("\x1b]50;CursorShape=%d;BlinkingCursorEnabled=%d\x07"), + shape, (c.blinkon !=0)); + } else if (!vte_version || atoi(vte_version) >= 3900) { + // Assume that the terminal supports DECSCUSR unless it is an + // old VTE based terminal. This should not get wrapped for tmux, + // which will handle it via its Ss/Se terminfo extension - usually + // according to its terminal-overrides. + + switch (shape) { + case SHAPE_BLOCK: shape = 1; break; + case SHAPE_VER: shape = 5; break; + case SHAPE_HOR: shape = 3; break; + default: WLOG("Unknown shape value %d", shape); break; + } + data->params[0].i = shape + (c.blinkon ==0); + unibi_format(vars, vars + 26, "\x1b[%p1%d q", + data->params, out, ui, NULL, NULL); + } +} + +/// Returns cursor mode from edit mode +static MouseMode tui_mode2cursor(int mode) +{ + switch (mode) { + case INSERT: return SHAPE_IDX_I; + case CMDLINE: return SHAPE_IDX_C; + case REPLACE: return SHAPE_IDX_R; + case NORMAL: + default: return SHAPE_IDX_N; + } +} + +/// @param mode editor mode static void tui_mode_change(UI *ui, int mode) { TUIData *data = ui->data; if (mode == INSERT) { if (data->showing_mode != INSERT) { - unibi_out(ui, data->unibi_ext.set_cursor_shape_bar); + tui_set_cursor(ui, SHAPE_IDX_I); } } else if (mode == CMDLINE) { if (data->showing_mode != CMDLINE) { - unibi_out(ui, data->unibi_ext.set_cursor_shape_bar); + tui_set_cursor(ui, SHAPE_IDX_C); } } else if (mode == REPLACE) { if (data->showing_mode != REPLACE) { - unibi_out(ui, data->unibi_ext.set_cursor_shape_ul); + tui_set_cursor(ui, SHAPE_IDX_R); } } else { assert(mode == NORMAL); if (data->showing_mode != NORMAL) { - unibi_out(ui, data->unibi_ext.set_cursor_shape_block); + tui_set_cursor(ui, SHAPE_IDX_N); } } data->showing_mode = mode; } static void tui_set_scroll_region(UI *ui, int top, int bot, int left, - int right) + int right) { TUIData *data = ui->data; ugrid_set_scroll_region(&data->grid, top, bot, left, right); @@ -831,8 +945,6 @@ static void fix_terminfo(TUIData *data) goto end; } - bool inside_tmux = os_getenv("TMUX") != NULL; - #define STARTS_WITH(str, prefix) (!memcmp(str, prefix, sizeof(prefix) - 1)) if (STARTS_WITH(term, "rxvt")) { @@ -890,40 +1002,6 @@ static void fix_terminfo(TUIData *data) unibi_set_str(ut, unibi_set_a_background, XTERM_SETAB); } - const char * env_cusr_shape = os_getenv("NVIM_TUI_ENABLE_CURSOR_SHAPE"); - if (env_cusr_shape && strncmp(env_cusr_shape, "0", 1) == 0) { - goto end; - } - bool cusr_blink = env_cusr_shape && strncmp(env_cusr_shape, "2", 1) == 0; - -#define TMUX_WRAP(seq) (inside_tmux ? "\x1bPtmux;\x1b" seq "\x1b\\" : seq) - // Support changing cursor shape on some popular terminals. - const char *term_prog = os_getenv("TERM_PROGRAM"); - const char *vte_version = os_getenv("VTE_VERSION"); - - if ((term_prog && !strcmp(term_prog, "Konsole")) - || os_getenv("KONSOLE_DBUS_SESSION") != NULL) { - // Konsole uses a proprietary escape code to set the cursor shape - // and does not support DECSCUSR. - data->unibi_ext.set_cursor_shape_bar = (int)unibi_add_ext_str(ut, NULL, - TMUX_WRAP("\x1b]50;CursorShape=1\x07")); - data->unibi_ext.set_cursor_shape_ul = (int)unibi_add_ext_str(ut, NULL, - TMUX_WRAP("\x1b]50;CursorShape=2\x07")); - data->unibi_ext.set_cursor_shape_block = (int)unibi_add_ext_str(ut, NULL, - TMUX_WRAP("\x1b]50;CursorShape=0\x07")); - } else if (!vte_version || atoi(vte_version) >= 3900) { - // Assume that the terminal supports DECSCUSR unless it is an - // old VTE based terminal. This should not get wrapped for tmux, - // which will handle it via its Ss/Se terminfo extension - usually - // according to its terminal-overrides. - data->unibi_ext.set_cursor_shape_bar = - (int)unibi_add_ext_str(ut, NULL, cusr_blink ? "\x1b[5 q" : "\x1b[6 q"); - data->unibi_ext.set_cursor_shape_ul = - (int)unibi_add_ext_str(ut, NULL, cusr_blink ? "\x1b[3 q" : "\x1b[4 q"); - data->unibi_ext.set_cursor_shape_block = - (int)unibi_add_ext_str(ut, NULL, cusr_blink ? "\x1b[1 q" : "\x1b[2 q"); - } - end: // Fill some empty slots with common terminal strings data->unibi_ext.enable_mouse = (int)unibi_add_ext_str(ut, NULL, diff --git a/src/nvim/tui/tui.h b/src/nvim/tui/tui.h index 07523bc124..2915b0e2f8 100644 --- a/src/nvim/tui/tui.h +++ b/src/nvim/tui/tui.h @@ -1,6 +1,8 @@ #ifndef NVIM_TUI_TUI_H #define NVIM_TUI_TUI_H +#include "nvim/cursor_shape.h" + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "tui/tui.h.generated.h" #endif diff --git a/src/nvim/ui.c b/src/nvim/ui.c index ea42e3e357..babb4efa96 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -29,6 +29,7 @@ #include "nvim/screen.h" #include "nvim/syntax.h" #include "nvim/window.h" +#include "nvim/cursor_shape.h" #ifdef FEAT_TUI # include "nvim/tui/tui.h" #else @@ -179,6 +180,7 @@ void ui_refresh(void) row = col = 0; screen_resize(width, height); pum_set_external(pum_external); + ui_cursor_style_set(); } static void ui_refresh_event(void **argv) @@ -376,6 +378,13 @@ void ui_cursor_goto(int new_row, int new_col) pending_cursor_update = true; } +void ui_cursor_style_set(void) +{ + Dictionary style = cursor_shape_dict(); + UI_CALL(cursor_style_set, style); + api_free_dictionary(style); +} + void ui_update_menu(void) { UI_CALL(update_menu); diff --git a/src/nvim/ui.h b/src/nvim/ui.h index d14bc5812c..0af0c0db65 100644 --- a/src/nvim/ui.h +++ b/src/nvim/ui.h @@ -22,6 +22,7 @@ struct ui_t { void (*clear)(UI *ui); void (*eol_clear)(UI *ui); void (*cursor_goto)(UI *ui, int row, int col); + void (*cursor_style_set)(UI *ui, Dictionary cursor_shapes); void (*update_menu)(UI *ui); void (*busy_start)(UI *ui); void (*busy_stop)(UI *ui); diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c index 25861abc1b..c9bad6b254 100644 --- a/src/nvim/ui_bridge.c +++ b/src/nvim/ui_bridge.c @@ -13,6 +13,7 @@ #include "nvim/memory.h" #include "nvim/ui_bridge.h" #include "nvim/ugrid.h" +#include "nvim/api/private/helpers.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ui_bridge.c.generated.h" @@ -59,6 +60,7 @@ UI *ui_bridge_attach(UI *ui, ui_main_fn ui_main, event_scheduler scheduler) rv->bridge.clear = ui_bridge_clear; rv->bridge.eol_clear = ui_bridge_eol_clear; rv->bridge.cursor_goto = ui_bridge_cursor_goto; + rv->bridge.cursor_style_set = ui_bridge_cursor_styleset; rv->bridge.update_menu = ui_bridge_update_menu; rv->bridge.busy_start = ui_bridge_busy_start; rv->bridge.busy_stop = ui_bridge_busy_stop; @@ -178,6 +180,23 @@ static void ui_bridge_cursor_goto_event(void **argv) ui->cursor_goto(ui, PTR2INT(argv[1]), PTR2INT(argv[2])); } +static void ui_bridge_cursor_styleset(UI *b, Dictionary style) +{ + Object copy = copy_object(DICTIONARY_OBJ(style)); + Object *pobj = xmalloc(sizeof(copy)); + *pobj = copy; + UI_CALL(b, cursor_styleset, 2, b, pobj); +} +static void ui_bridge_cursor_styleset_event(void **argv) +{ + UI *ui = UI(argv[0]); + Object *styles = (Object *)argv[1]; + + ui->cursor_style_set(ui, styles->data.dictionary); + api_free_object(*styles); + xfree(styles); +} + static void ui_bridge_update_menu(UI *b) { UI_CALL(b, update_menu, 1, b); diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index 54f43387dc..2d04949bb3 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -313,6 +313,8 @@ function Screen:_redraw(updates) if handler ~= nil then handler(self, unpack(update[i])) else + assert(self._on_event, "Either add an Screen:_handle_XXX method ".. + " or call Screen:set_on_event_handler") self._on_event(method, update[i]) end end @@ -343,6 +345,10 @@ function Screen:_handle_resize(width, height) } end +function Screen:_handle_cursor_style_set(styles) + self._cursor_styles = styles +end + function Screen:_handle_clear() self:_clear_block(self._scroll_region.top, self._scroll_region.bot, self._scroll_region.left, self._scroll_region.right) From 54bab0019b3638f213608757b523062195be156b Mon Sep 17 00:00:00 2001 From: Matthieu Coudron Date: Tue, 21 Mar 2017 00:03:01 +0100 Subject: [PATCH 0315/1671] tui: 'guicursor' color For now only supports valid hex colors (does not check for the validity the hex color) when termguicolors is set, otherwise it won't attempt to change the cursor color. --- runtime/doc/options.txt | 3 ++- src/nvim/syntax.c | 45 ++++++++++++++++++++++++----------------- src/nvim/syntax.h | 10 +++++---- src/nvim/tui/tui.c | 19 +++++++++++++++-- 4 files changed, 52 insertions(+), 25 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 7d41162cfc..4c827d0749 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2802,7 +2802,8 @@ A jump table for the options with a short description can be found at |Q_op|. the height of the cursor can be changed. This can be done by specifying a block cursor, or a percentage for a vertical or horizontal cursor. - For a console the 't_SI' and 't_EI' escape sequences are used. + For a console, shape is taken into account and color as well if + 'termguicolors' is set. 't_SI' and 't_EI' are deprecated in neovim. The option is a comma separated list of parts. Each part consist of a mode-list and an argument-list: diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 4f2f44ff86..acda25e738 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -48,9 +48,9 @@ static bool did_syntax_onoff = false; struct hl_group { char_u *sg_name; ///< highlight group name char_u *sg_name_u; ///< uppercase of sg_name - int sg_attr; ///< Screen attr + int sg_attr; ///< Screen attr @see ATTR_ENTRY int sg_link; ///< link to this highlight group ID - int sg_set; ///< combination of SG_* flags + int sg_set; ///< combination of flags in \ref SG_SET scid_T sg_scriptID; ///< script in which the group was last set // for terminal UIs int sg_cterm; ///< "cterm=" highlighting attr @@ -59,6 +59,7 @@ struct hl_group { int sg_cterm_bold; ///< bold attr was set for light color // for RGB UIs int sg_gui; ///< "gui=" highlighting attributes + ///< (combination of \ref HL_ATTRIBUTES) RgbValue sg_rgb_fg; ///< RGB foreground color RgbValue sg_rgb_bg; ///< RGB background color RgbValue sg_rgb_sp; ///< RGB special color @@ -67,9 +68,12 @@ struct hl_group { uint8_t *sg_rgb_sp_name; ///< RGB special color name }; +/// \addtogroup SG_SET +/// @{ #define SG_CTERM 2 // cterm has been set #define SG_GUI 4 // gui has been set #define SG_LINK 8 // link has been set +/// @} // highlight groups for 'highlight' option static garray_T highlight_ga = GA_EMPTY_INIT_VALUE; @@ -6093,16 +6097,16 @@ int load_colors(char_u *name) return retval; } -/* - * Handle the ":highlight .." command. - * When using ":hi clear" this is called recursively for each group with - * "forceit" and "init" both TRUE. - */ -void -do_highlight ( + +/// Handle the ":highlight .." command. +/// When using ":hi clear" this is called recursively for each group with +/// "forceit" and "init" both TRUE. +/// @param init TRUE when called for initializing +void +do_highlight( char_u *line, int forceit, - int init /* TRUE when called for initializing */ + int init ) { char_u *name_end; @@ -6704,12 +6708,10 @@ static garray_T attr_table = GA_EMPTY_INIT_VALUE; #define ATTR_ENTRY(idx) ((attrentry_T *)attr_table.ga_data)[idx] -/* - * Return the attr number for a set of colors and font. - * Add a new entry to the term_attr_table, attr_table or gui_attr_table - * if the combination is new. - * Return 0 for error. - */ +/// Return the attr number for a set of colors and font. +/// Add a new entry to the term_attr_table, attr_table or gui_attr_table +/// if the combination is new. +/// @return 0 for error. int get_attr_entry(attrentry_T *aep) { garray_T *table = &attr_table; @@ -6932,7 +6934,7 @@ static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg /// Check whether highlight group has attribute /// -/// @param[in] id Highilght group to check. +/// @param[in] id Highlight group to check. /// @param[in] flag Attribute to check. /// @param[in] modec 'g' for GUI, 'c' for term. /// @@ -8245,7 +8247,14 @@ color_name_table_T color_name_table[] = { { NULL, 0 }, }; -RgbValue name_to_color(uint8_t *name) + +/// Translate to RgbValue if \p name is an hex value (e.g. #XXXXXX), +/// else look into color_name_table to translate a color name to its +/// hex value +/// +/// @param[in] name string value to convert to RGB +/// return the hex value or -1 if could not find a correct value +RgbValue name_to_color(const uint8_t *name) { if (name[0] == '#' && isxdigit(name[1]) && isxdigit(name[2]) diff --git a/src/nvim/syntax.h b/src/nvim/syntax.h index af2ac719c6..574e3372e2 100644 --- a/src/nvim/syntax.h +++ b/src/nvim/syntax.h @@ -5,10 +5,11 @@ #include "nvim/buffer_defs.h" -/* - * Terminal highlighting attribute bits. - * Attributes above HL_ALL are used for syntax highlighting. - */ + +/// Terminal highlighting attribute bits. +/// Attributes above HL_ALL are used for syntax highlighting. +/// \addtogroup HL_ATTRIBUTES +/// @{ #define HL_NORMAL 0x00 #define HL_INVERSE 0x01 #define HL_BOLD 0x02 @@ -16,6 +17,7 @@ #define HL_UNDERLINE 0x08 #define HL_UNDERCURL 0x10 #define HL_STANDOUT 0x20 +/// @} #define HL_CONTAINED 0x01 /* not used on toplevel */ #define HL_TRANSP 0x02 /* has no highlighting */ diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index ebcef33fa1..12281246fe 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -78,6 +78,7 @@ typedef struct { int enable_mouse, disable_mouse; int enable_bracketed_paste, disable_bracketed_paste; int set_rgb_foreground, set_rgb_background; + int set_cursor_color; int enable_focus_reporting, disable_focus_reporting; } unibi_ext; } TUIData; @@ -132,6 +133,7 @@ static void terminfo_start(UI *ui) data->showing_mode = 0; data->unibi_ext.enable_mouse = -1; data->unibi_ext.disable_mouse = -1; + data->unibi_ext.set_cursor_color = -1; data->unibi_ext.enable_bracketed_paste = -1; data->unibi_ext.disable_bracketed_paste = -1; data->unibi_ext.enable_focus_reporting = -1; @@ -548,8 +550,12 @@ static void tui_set_cursor(UI *ui, MouseMode mode) case SHAPE_HOR: shape = 3; break; default: WLOG("Unknown shape value %d", shape); break; } - printf(TMUX_WRAP("\x1b]50;CursorShape=%d;BlinkingCursorEnabled=%d\x07"), - shape, (c.blinkon !=0)); + data->params[0].i = shape; + data->params[1].i = (c.blinkon ==0); + + unibi_format(vars, vars + 26, + TMUX_WRAP("\x1b]50;CursorShape=%p1%d;BlinkingCursorEnabled=%p2%d\x07"), + data->params, out, ui, NULL, NULL); } else if (!vte_version || atoi(vte_version) >= 3900) { // Assume that the terminal supports DECSCUSR unless it is an // old VTE based terminal. This should not get wrapped for tmux, @@ -566,6 +572,13 @@ static void tui_set_cursor(UI *ui, MouseMode mode) unibi_format(vars, vars + 26, "\x1b[%p1%d q", data->params, out, ui, NULL, NULL); } + + if (c.id != 0 && ui->rgb) { + int attr = syn_id2attr(c.id); + attrentry_T *aep = syn_cterm_attr2entry(attr); + data->params[0].i = aep->rgb_bg_color; + unibi_out(ui, data->unibi_ext.set_cursor_color); + } } /// Returns cursor mode from edit mode @@ -1004,6 +1017,8 @@ static void fix_terminfo(TUIData *data) end: // Fill some empty slots with common terminal strings + data->unibi_ext.set_cursor_color = (int)unibi_add_ext_str( + ut, NULL, "\033]12;#%p1%06x\007"); data->unibi_ext.enable_mouse = (int)unibi_add_ext_str(ut, NULL, "\x1b[?1002h\x1b[?1006h"); data->unibi_ext.disable_mouse = (int)unibi_add_ext_str(ut, NULL, From c2826a7830ddba66261afdf45fcf4d0043506342 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 1 Apr 2017 13:08:42 +0200 Subject: [PATCH 0316/1671] 'guicursor': Empty means "block cursor in all modes". Also: update default 'guicursor' to match the documentation. --- man/nvim.1 | 21 ---- runtime/doc/msgpack_rpc.txt | 22 ++-- runtime/doc/options.txt | 25 ++-- runtime/doc/vim_diff.txt | 1 + src/nvim/cursor_shape.c | 13 ++- src/nvim/cursor_shape.h | 4 +- src/nvim/options.lua | 2 +- src/nvim/tui/tui.c | 1 - test/functional/ui/cursor_spec.lua | 180 +++++++++++++++++++++++++++++ test/functional/ui/mouse_spec.lua | 2 +- test/functional/ui/screen.lua | 4 +- 11 files changed, 214 insertions(+), 61 deletions(-) create mode 100644 test/functional/ui/cursor_spec.lua diff --git a/man/nvim.1 b/man/nvim.1 index 98d97c2d5a..d2a3ea5c43 100644 --- a/man/nvim.1 +++ b/man/nvim.1 @@ -371,27 +371,6 @@ See Used to set the 'shell' option, which determines the shell used by the .Ic :terminal command. -.It Ev NVIM_TUI_ENABLE_CURSOR_SHAPE -Set to 0 to prevent Nvim from changing the cursor shape. -Set to 1 to enable non-blinking mode-sensitive cursor (this is the default). -Set to 2 to enable blinking mode-sensitive cursor. -Host terminal must support the DECSCUSR CSI escape sequence. -.Pp -Depending on the terminal emulator, using this option with -.Nm -under -.Xr tmux 1 -might require adding the following to -.Pa ~/.tmux.conf : -.Bd -literal -offset indent -set -ga terminal-overrides ',*:Ss=\eE[%p1%d q:Se=\eE[2 q' -.Ed -.Pp -See -.Ic terminal-overrides -in the -.Xr tmux 1 -manual page for more information. .El .Sh FILES .Bl -tag -width "~/.config/nvim/init.vim" diff --git a/runtime/doc/msgpack_rpc.txt b/runtime/doc/msgpack_rpc.txt index 02086e24fd..3f3c41f566 100644 --- a/runtime/doc/msgpack_rpc.txt +++ b/runtime/doc/msgpack_rpc.txt @@ -250,23 +250,21 @@ connect to another with different type codes. ============================================================================== 6. Remote UIs *rpc-remote-ui* -Nvim allows Graphical user interfaces to be implemented by separate processes -communicating with Nvim over the RPC API. Currently the ui model conists of a -terminal-like grid with one single, monospace font size, with a few elements -that could be drawn separately from the grid (for the momemnt only the popup -menu) +GUIs can be implemented as external processes communicating with Nvim over the +RPC API. Currently the UI model consists of a terminal-like grid with one +single, monospace font size. Some elements (UI "widgets") can be drawn +separately from the grid. -After connecting to a nvim instance (typically a spawned, embedded instance) -use the |nvim_ui_attach|(width, height, options) API method to tell nvim that your -program wants to draw the nvim screen on a grid with "width" times -"height" cells. "options" should be a dictionary with the following (all -optional) keys: - `rgb`: Controls what color format to use. +After connecting to Nvim (usually a spawned, embedded instance) use the +|nvim_ui_attach| API method to tell Nvim that your program wants to draw the +Nvim screen on a grid of width × height cells. `options` must be +a dictionary with these (optional) keys: + `rgb` Controls what color format to use. Set to true (default) to use 24-bit rgb colors. Set to false to use terminal color codes (at most 256 different colors). - `popupmenu_external`: Instead of drawing the completion popupmenu on + `popupmenu_external` Instead of drawing the completion popupmenu on the grid, Nvim will send higher-level events to the ui and let it draw the popupmenu. Defaults to false. diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 4c827d0749..394e38f6e5 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2790,22 +2790,17 @@ A jump table for the options with a short description can be found at |Q_op|. i-ci:ver25-Cursor/lCursor, r-cr:hor20-Cursor/lCursor, sm:block-Cursor - -blinkwait175-blinkoff150-blinkon175", - for Windows console: - "n-v-c:block,o:hor50,i-ci:hor15, - r-cr:hor30,sm:block") + -blinkwait175-blinkoff150-blinkon175") global - {only available when compiled with GUI enabled, and - for Windows console} - This option tells Vim what the cursor should look like in different - modes. It fully works in the GUI. In a Windows console, only - the height of the cursor can be changed. This can be done by - specifying a block cursor, or a percentage for a vertical or - horizontal cursor. - For a console, shape is taken into account and color as well if - 'termguicolors' is set. 't_SI' and 't_EI' are deprecated in neovim. - - The option is a comma separated list of parts. Each part consist of a + Configures the cursor style for each mode. Works in the GUI and some + terminals. Empty means "non-blinking block cursor in all modes": > + :set guicursor= +< + With tmux you might need this in ~/.tmux.conf (see terminal-overrides + in the tmux(1) manual page): > + set -ga terminal-overrides ',*:Ss=\E[%p1%d q:Se=\E[2 q' +< + The option is a comma separated list of parts. Each part consists of a mode-list and an argument-list: mode-list:argument-list,mode-list:argument-list,.. The mode-list is a dash separated list of these modes: diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 41c0c41c80..bd43028806 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -113,6 +113,7 @@ Some `CTRL-SHIFT-...` key chords are distinguished from `CTRL-...` variants Options: 'cpoptions' flags: |cpo-_| + 'guicursor' works in the terminal 'inccommand' shows interactive results for |:substitute|-like commands 'statusline' supports unlimited alignment sections 'tabline' %@Func@foo%X can call any function on mouse-click diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index 9f0eb215ef..c78bcbc29d 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -45,7 +45,7 @@ static cursorentry_T shape_table[SHAPE_IDX_COUNT] = }; /// Converts cursor_shapes into a Dictionary of dictionaries -/// @return a dictionary of the form {"normal" : { "cursor_shape": ... }, ...} +/// @return dictionary of the form {"normal" : { "cursor_shape": ... }, ...} Dictionary cursor_shape_dict(void) { Dictionary all = ARRAY_DICT_INIT; @@ -82,7 +82,7 @@ Dictionary cursor_shape_dict(void) /// Parse the 'guicursor' option /// -/// @param what either SHAPE_CURSOR or SHAPE_MOUSE ('mouseshape') +/// @param what SHAPE_CURSOR or SHAPE_MOUSE ('mouseshape') /// /// @returns error message for an illegal option, NULL otherwise. char_u *parse_shape_opt(int what) @@ -103,10 +103,11 @@ char_u *parse_shape_opt(int what) * First round: check for errors; second round: do it for real. */ for (round = 1; round <= 2; ++round) { - /* - * Repeat for all comma separated parts. - */ + // Repeat for all comma separated parts. modep = p_guicursor; + if (*p_guicursor == NUL) { + modep = (char_u *)"a:block-blinkon0"; + } while (*modep != NUL) { colonp = vim_strchr(modep, ':'); if (colonp == NULL) @@ -115,7 +116,7 @@ char_u *parse_shape_opt(int what) return (char_u *)N_("E546: Illegal mode"); commap = vim_strchr(modep, ','); - // Repeat for all mode's before the colon. + // Repeat for all modes before the colon. // For the 'a' mode, we loop to handle all the modes. all_idx = -1; assert(modep < colonp); diff --git a/src/nvim/cursor_shape.h b/src/nvim/cursor_shape.h index 127d0df555..14ace2a861 100644 --- a/src/nvim/cursor_shape.h +++ b/src/nvim/cursor_shape.h @@ -14,7 +14,7 @@ SHAPE_IDX_CR = 6, ///< Command line Replace mode SHAPE_IDX_O = 7, ///< Operator-pending mode SHAPE_IDX_VE = 8, ///< Visual mode with 'selection' exclusive SHAPE_IDX_CLINE = 9, ///< On command line -SHAPE_IDX_STATUS = 10, ///< status line +SHAPE_IDX_STATUS = 10, ///< status line SHAPE_IDX_SDRAG = 11, ///< dragging a status line SHAPE_IDX_VSEP = 12, ///< A vertical separator line SHAPE_IDX_VDRAG = 13, ///< dragging a vertical separator line @@ -37,7 +37,7 @@ SHAPE_VER = 2 ///< vertical bar cursor #define SHAPE_CURSOR 2 /* used for text cursor shape */ typedef struct cursor_entry { - char *full_name; ///< mode full name + char *full_name; ///< mode description CursorShape shape; ///< cursor shape: one of the SHAPE_ defines int mshape; ///< mouse shape: one of the MSHAPE defines int percentage; ///< percentage of cell for bar diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 9dff3410d6..09f016cf5a 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -1000,7 +1000,7 @@ return { deny_duplicates=true, vi_def=true, varname='p_guicursor', - defaults={if_true={vi="n-v-c:block,o:hor50,i-ci:hor15,r-cr:hor30,sm:block"}} + defaults={if_true={vi="n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175"}} }, { full_name='guifont', abbreviation='gfn', diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 12281246fe..e1fec0f678 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -148,7 +148,6 @@ static void terminfo_start(UI *ui) data->ut = unibi_dummy(); } fix_terminfo(data); - // Initialize the cursor shape. // Set 't_Co' from the result of unibilium & fix_terminfo. t_colors = unibi_get_num(data->ut, unibi_max_colors); // Enter alternate screen and clear diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua new file mode 100644 index 0000000000..6f5fd244d5 --- /dev/null +++ b/test/functional/ui/cursor_spec.lua @@ -0,0 +1,180 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local clear, feed, meths = helpers.clear, helpers.feed, helpers.meths +local insert, execute = helpers.insert, helpers.execute +local eq, funcs = helpers.eq, helpers.funcs +local command = helpers.command + +if helpers.pending_win32(pending) then return end + +describe('ui/cursor', function() + local screen + + before_each(function() + clear() + screen = Screen.new(25, 5) + screen:attach() + end) + + after_each(function() + screen:detach() + end) + + it("'guicursor' is published as a UI event", function() + command('redraw') + screen:expect('', nil, nil, nil, true) -- Tickle the event-loop. + local expected_cursor_style = { + cmd_insert = { + blinkoff = 250, + blinkon = 400, + blinkwait = 700, + cell_percentage = 25, + cursor_shape = 'vertical', + hl_id = 45, + id_lm = 46, + mouse_shape = 0, + short_name = 'ci' }, + cmd_line = { + mouse_shape = 0, + short_name = 'e' }, + cmd_normal = { + blinkoff = 250, + blinkon = 400, + blinkwait = 700, + cell_percentage = 0, + cursor_shape = 'block', + hl_id = 45, + id_lm = 46, + mouse_shape = 0, + short_name = 'c' }, + cmd_replace = { + blinkoff = 250, + blinkon = 400, + blinkwait = 700, + cell_percentage = 20, + cursor_shape = 'horizontal', + hl_id = 45, + id_lm = 46, + mouse_shape = 0, + short_name = 'cr' }, + drag_statusline = { + mouse_shape = 0, + short_name = 'sd' }, + insert = { + blinkoff = 250, + blinkon = 400, + blinkwait = 700, + cell_percentage = 25, + cursor_shape = 'vertical', + hl_id = 45, + id_lm = 46, + mouse_shape = 0, + short_name = 'i' }, + match_paren = { + blinkoff = 150, + blinkon = 175, + blinkwait = 175, + cell_percentage = 0, + cursor_shape = 'block', + hl_id = 45, + id_lm = 45, + short_name = 'sm' }, + more = { + mouse_shape = 0, + short_name = 'm' }, + more_lastline = { + mouse_shape = 0, + short_name = 'ml' }, + normal = { + blinkoff = 250, + blinkon = 400, + blinkwait = 700, + cell_percentage = 0, + cursor_shape = 'block', + hl_id = 45, + id_lm = 46, + mouse_shape = 0, + short_name = 'n' }, + pending = { + blinkoff = 250, + blinkon = 400, + blinkwait = 700, + cell_percentage = 50, + cursor_shape = 'horizontal', + hl_id = 45, + id_lm = 45, + mouse_shape = 0, + short_name = 'o' }, + replace = { + blinkoff = 250, + blinkon = 400, + blinkwait = 700, + cell_percentage = 20, + cursor_shape = 'horizontal', + hl_id = 45, + id_lm = 46, + mouse_shape = 0, + short_name = 'r' }, + statusline = { + mouse_shape = 0, + short_name = 's' }, + vdrag = { + mouse_shape = 0, + short_name = 'vd' }, + visual = { + blinkoff = 250, + blinkon = 400, + blinkwait = 700, + cell_percentage = 0, + cursor_shape = 'block', + hl_id = 45, + id_lm = 46, + mouse_shape = 0, + short_name = 'v' }, + visual_select = { + blinkoff = 250, + blinkon = 400, + blinkwait = 700, + cell_percentage = 35, + cursor_shape = 'vertical', + hl_id = 45, + id_lm = 45, + mouse_shape = 0, + short_name = 've' }, + vsep = { + mouse_shape = 0, + short_name = 'vs' } + } + -- Default 'guicursor' published on startup. + eq(expected_cursor_style, screen._cursor_style) + eq('normal', screen.mode) + + -- Event is published ONLY if the cursor style changed. + screen._cursor_style = nil + command('redraw') + screen:expect('', nil, nil, nil, true) -- Tickle the event-loop. + eq(nil, screen._cursor_style) + + -- Change the cursor style. + meths.set_option('guicursor', 'n-v-c:ver35-blinkwait171-blinkoff172-blinkon173,ve:hor35,o:ver50,i-ci:block,r-cr:hor90,sm:ver42') + command('redraw') + screen:expect('', nil, nil, nil, true) -- Tickle the event-loop. + eq('vertical', screen._cursor_style.normal.cursor_shape) + eq('horizontal', screen._cursor_style.visual_select.cursor_shape) + eq('vertical', screen._cursor_style.pending.cursor_shape) + eq('block', screen._cursor_style.insert.cursor_shape) + eq('vertical', screen._cursor_style.match_paren.cursor_shape) + end) + + it("empty 'guicursor' sets cursor_shape=block in all modes", function() + meths.set_option('guicursor', '') + command('redraw') + screen:expect('', nil, nil, nil, true) -- Tickle the event-loop. + for _, m in ipairs({ 'cmd_insert', 'cmd_normal', 'cmd_replace', 'insert', + 'match_paren', 'normal', 'replace', 'visual', + 'visual_select', }) do + eq('block', screen._cursor_style[m].cursor_shape) + end + end) + +end) diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index b2fbedfb5e..ecbd5642d1 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -6,7 +6,7 @@ local eq, funcs = helpers.eq, helpers.funcs if helpers.pending_win32(pending) then return end -describe('Mouse input', function() +describe('ui/mouse/input', function() local screen before_each(function() diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index 2d04949bb3..f67a4abd29 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -345,8 +345,8 @@ function Screen:_handle_resize(width, height) } end -function Screen:_handle_cursor_style_set(styles) - self._cursor_styles = styles +function Screen:_handle_cursor_style_set(style) + self._cursor_style = style end function Screen:_handle_clear() From 3a69dbfca6642463ca8e19f814f71791f66332f3 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 1 Apr 2017 22:32:16 +0200 Subject: [PATCH 0317/1671] api/cursor_style_set: mode descriptions --- src/nvim/cursor_shape.c | 44 ++++++++------------- src/nvim/cursor_shape.h | 4 +- src/nvim/tui/tui.c | 4 +- test/functional/ui/cursor_spec.lua | 62 +++++++++++++++--------------- test/functional/ui/screen.lua | 4 +- 5 files changed, 55 insertions(+), 63 deletions(-) diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index c78bcbc29d..7ec70bb724 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -13,35 +13,25 @@ /// Handling of cursor and mouse pointer shapes in various modes. static cursorentry_T shape_table[SHAPE_IDX_COUNT] = { - // The values will be filled in from the 'guicursor' and 'mouseshape' - // defaults when Vim starts. - // Adjust the SHAPE_IDX_ defines when making changes! - { "normal", - 0, 0, 0, 700L, 400L, 250L, 0, 0, "n", SHAPE_CURSOR+SHAPE_MOUSE }, - { "visual", - 0, 0, 0, 700L, 400L, 250L, 0, 0, "v", SHAPE_CURSOR+SHAPE_MOUSE }, - { "insert", - 0, 0, 0, 700L, 400L, 250L, 0, 0, "i", SHAPE_CURSOR+SHAPE_MOUSE }, - { "replace", - 0, 0, 0, 700L, 400L, 250L, 0, 0, "r", SHAPE_CURSOR+SHAPE_MOUSE }, - { "cmd_normal", - 0, 0, 0, 700L, 400L, 250L, 0, 0, "c", SHAPE_CURSOR+SHAPE_MOUSE }, - { "cmd_insert", 0, - 0, 0, 700L, 400L, 250L, 0, 0, "ci", SHAPE_CURSOR+SHAPE_MOUSE }, - { "cmd_replace", - 0, 0, 0, 700L, 400L, 250L, 0, 0, "cr", SHAPE_CURSOR+SHAPE_MOUSE }, - { "pending", - 0, 0, 0, 700L, 400L, 250L, 0, 0, "o", SHAPE_CURSOR+SHAPE_MOUSE }, - { "visual_select", - 0, 0, 0, 700L, 400L, 250L, 0, 0, "ve", SHAPE_CURSOR+SHAPE_MOUSE }, - { "cmd_line", 0, 0, 0, 0L, 0L, 0L, 0, 0, "e", SHAPE_MOUSE }, - { "statusline", 0, 0, 0, 0L, 0L, 0L, 0, 0, "s", SHAPE_MOUSE }, - { "drag_statusline", 0, 0, 0, 0L, 0L, 0L, 0, 0, "sd", SHAPE_MOUSE }, - { "vsep", 0, 0, 0, 0L, 0L, 0L, 0, 0, "vs", SHAPE_MOUSE }, - { "vdrag", 0, 0, 0, 0L, 0L, 0L, 0, 0, "vd", SHAPE_MOUSE }, + // Values are set by 'guicursor' and 'mouseshape'. + // Adjust the SHAPE_IDX_ defines when changing this! + { "normal", 0, 0, 0, 700L, 400L, 250L, 0, 0, "n", SHAPE_CURSOR+SHAPE_MOUSE }, + { "visual", 0, 0, 0, 700L, 400L, 250L, 0, 0, "v", SHAPE_CURSOR+SHAPE_MOUSE }, + { "insert", 0, 0, 0, 700L, 400L, 250L, 0, 0, "i", SHAPE_CURSOR+SHAPE_MOUSE }, + { "replace", 0, 0, 0, 700L, 400L, 250L, 0, 0, "r", SHAPE_CURSOR+SHAPE_MOUSE }, + { "cmdline_normal", 0, 0, 0, 700L, 400L, 250L, 0, 0, "c", SHAPE_CURSOR+SHAPE_MOUSE }, + { "cmdline_insert", 0, 0, 0, 700L, 400L, 250L, 0, 0, "ci", SHAPE_CURSOR+SHAPE_MOUSE }, + { "cmdline_replace", 0, 0, 0, 700L, 400L, 250L, 0, 0, "cr", SHAPE_CURSOR+SHAPE_MOUSE }, + { "operator", 0, 0, 0, 700L, 400L, 250L, 0, 0, "o", SHAPE_CURSOR+SHAPE_MOUSE }, + { "visual_select", 0, 0, 0, 700L, 400L, 250L, 0, 0, "ve", SHAPE_CURSOR+SHAPE_MOUSE }, + { "cmdline_hover", 0, 0, 0, 0L, 0L, 0L, 0, 0, "e", SHAPE_MOUSE }, + { "statusline_hover", 0, 0, 0, 0L, 0L, 0L, 0, 0, "s", SHAPE_MOUSE }, + { "statusline_drag", 0, 0, 0, 0L, 0L, 0L, 0, 0, "sd", SHAPE_MOUSE }, + { "vsep_hover", 0, 0, 0, 0L, 0L, 0L, 0, 0, "vs", SHAPE_MOUSE }, + { "vsep_drag", 0, 0, 0, 0L, 0L, 0L, 0, 0, "vd", SHAPE_MOUSE }, { "more", 0, 0, 0, 0L, 0L, 0L, 0, 0, "m", SHAPE_MOUSE }, { "more_lastline", 0, 0, 0, 0L, 0L, 0L, 0, 0, "ml", SHAPE_MOUSE }, - { "match_paren", 0, 0, 0, 100L, 100L, 100L, 0, 0, "sm", SHAPE_CURSOR }, + { "showmatch", 0, 0, 0, 100L, 100L, 100L, 0, 0, "sm", SHAPE_CURSOR }, }; /// Converts cursor_shapes into a Dictionary of dictionaries diff --git a/src/nvim/cursor_shape.h b/src/nvim/cursor_shape.h index 14ace2a861..0006ede31d 100644 --- a/src/nvim/cursor_shape.h +++ b/src/nvim/cursor_shape.h @@ -14,9 +14,9 @@ SHAPE_IDX_CR = 6, ///< Command line Replace mode SHAPE_IDX_O = 7, ///< Operator-pending mode SHAPE_IDX_VE = 8, ///< Visual mode with 'selection' exclusive SHAPE_IDX_CLINE = 9, ///< On command line -SHAPE_IDX_STATUS = 10, ///< status line +SHAPE_IDX_STATUS = 10, ///< On status line SHAPE_IDX_SDRAG = 11, ///< dragging a status line -SHAPE_IDX_VSEP = 12, ///< A vertical separator line +SHAPE_IDX_VSEP = 12, ///< On vertical separator line SHAPE_IDX_VDRAG = 13, ///< dragging a vertical separator line SHAPE_IDX_MORE = 14, ///< Hit-return or More SHAPE_IDX_MOREL = 15, ///< Hit-return or More in last line diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index e1fec0f678..badc0cd870 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -487,7 +487,7 @@ static void tui_cursor_style_set(UI *ui, Dictionary args) data->cursor_shapes[mode_id] = r; } - // force redrawal + // force redraw MouseMode cursor_mode = tui_mode2cursor(data->showing_mode); tui_set_cursor(ui, cursor_mode); } @@ -550,7 +550,7 @@ static void tui_set_cursor(UI *ui, MouseMode mode) default: WLOG("Unknown shape value %d", shape); break; } data->params[0].i = shape; - data->params[1].i = (c.blinkon ==0); + data->params[1].i = (c.blinkon == 0); unibi_format(vars, vars + 26, TMUX_WRAP("\x1b]50;CursorShape=%p1%d;BlinkingCursorEnabled=%p2%d\x07"), diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua index 6f5fd244d5..8b42c193ab 100644 --- a/test/functional/ui/cursor_spec.lua +++ b/test/functional/ui/cursor_spec.lua @@ -5,8 +5,6 @@ local insert, execute = helpers.insert, helpers.execute local eq, funcs = helpers.eq, helpers.funcs local command = helpers.command -if helpers.pending_win32(pending) then return end - describe('ui/cursor', function() local screen @@ -24,7 +22,10 @@ describe('ui/cursor', function() command('redraw') screen:expect('', nil, nil, nil, true) -- Tickle the event-loop. local expected_cursor_style = { - cmd_insert = { + cmdline_hover = { + mouse_shape = 0, + short_name = 'e' }, + cmdline_insert = { blinkoff = 250, blinkon = 400, blinkwait = 700, @@ -34,10 +35,7 @@ describe('ui/cursor', function() id_lm = 46, mouse_shape = 0, short_name = 'ci' }, - cmd_line = { - mouse_shape = 0, - short_name = 'e' }, - cmd_normal = { + cmdline_normal = { blinkoff = 250, blinkon = 400, blinkwait = 700, @@ -47,7 +45,7 @@ describe('ui/cursor', function() id_lm = 46, mouse_shape = 0, short_name = 'c' }, - cmd_replace = { + cmdline_replace = { blinkoff = 250, blinkon = 400, blinkwait = 700, @@ -57,9 +55,6 @@ describe('ui/cursor', function() id_lm = 46, mouse_shape = 0, short_name = 'cr' }, - drag_statusline = { - mouse_shape = 0, - short_name = 'sd' }, insert = { blinkoff = 250, blinkon = 400, @@ -70,15 +65,6 @@ describe('ui/cursor', function() id_lm = 46, mouse_shape = 0, short_name = 'i' }, - match_paren = { - blinkoff = 150, - blinkon = 175, - blinkwait = 175, - cell_percentage = 0, - cursor_shape = 'block', - hl_id = 45, - id_lm = 45, - short_name = 'sm' }, more = { mouse_shape = 0, short_name = 'm' }, @@ -95,7 +81,7 @@ describe('ui/cursor', function() id_lm = 46, mouse_shape = 0, short_name = 'n' }, - pending = { + operator = { blinkoff = 250, blinkon = 400, blinkwait = 700, @@ -115,12 +101,21 @@ describe('ui/cursor', function() id_lm = 46, mouse_shape = 0, short_name = 'r' }, - statusline = { + showmatch = { + blinkoff = 150, + blinkon = 175, + blinkwait = 175, + cell_percentage = 0, + cursor_shape = 'block', + hl_id = 45, + id_lm = 45, + short_name = 'sm' }, + statusline_drag = { + mouse_shape = 0, + short_name = 'sd' }, + statusline_hover = { mouse_shape = 0, short_name = 's' }, - vdrag = { - mouse_shape = 0, - short_name = 'vd' }, visual = { blinkoff = 250, blinkon = 400, @@ -141,7 +136,10 @@ describe('ui/cursor', function() id_lm = 45, mouse_shape = 0, short_name = 've' }, - vsep = { + vsep_drag = { + mouse_shape = 0, + short_name = 'vd' }, + vsep_hover = { mouse_shape = 0, short_name = 'vs' } } @@ -161,19 +159,23 @@ describe('ui/cursor', function() screen:expect('', nil, nil, nil, true) -- Tickle the event-loop. eq('vertical', screen._cursor_style.normal.cursor_shape) eq('horizontal', screen._cursor_style.visual_select.cursor_shape) - eq('vertical', screen._cursor_style.pending.cursor_shape) + eq('vertical', screen._cursor_style.operator.cursor_shape) eq('block', screen._cursor_style.insert.cursor_shape) - eq('vertical', screen._cursor_style.match_paren.cursor_shape) + eq('vertical', screen._cursor_style.showmatch.cursor_shape) + eq(171, screen._cursor_style.normal.blinkwait) + eq(172, screen._cursor_style.normal.blinkoff) + eq(173, screen._cursor_style.normal.blinkon) end) it("empty 'guicursor' sets cursor_shape=block in all modes", function() meths.set_option('guicursor', '') command('redraw') screen:expect('', nil, nil, nil, true) -- Tickle the event-loop. - for _, m in ipairs({ 'cmd_insert', 'cmd_normal', 'cmd_replace', 'insert', - 'match_paren', 'normal', 'replace', 'visual', + for _, m in ipairs({ 'cmdline_insert', 'cmdline_normal', 'cmdline_replace', 'insert', + 'showmatch', 'normal', 'replace', 'visual', 'visual_select', }) do eq('block', screen._cursor_style[m].cursor_shape) + eq(0, screen._cursor_style[m].blinkon) end end) diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index f67a4abd29..3f8173c8e2 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -313,8 +313,8 @@ function Screen:_redraw(updates) if handler ~= nil then handler(self, unpack(update[i])) else - assert(self._on_event, "Either add an Screen:_handle_XXX method ".. - " or call Screen:set_on_event_handler") + assert(self._on_event, + "Add Screen:_handle_XXX method or call Screen:set_on_event_handler") self._on_event(method, update[i]) end end From 16babc66870b5579f3305fa1289f25e1dc496655 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Sat, 1 Apr 2017 18:00:42 -0400 Subject: [PATCH 0318/1671] tui: Only enable/disable mouse when there's something to do (#6411) If we get a mouse_on/mouse_off event, but the mouse is already in the corresponding state, there's no need to send the event up to the terminal. Closes #4394 --- src/nvim/tui/tui.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 55936ad58d..ebdfb1e7a1 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -452,15 +452,19 @@ static void tui_busy_stop(UI *ui) static void tui_mouse_on(UI *ui) { TUIData *data = ui->data; - unibi_out(ui, data->unibi_ext.enable_mouse); - data->mouse_enabled = true; + if (!data->mouse_enabled) { + unibi_out(ui, data->unibi_ext.enable_mouse); + data->mouse_enabled = true; + } } static void tui_mouse_off(UI *ui) { TUIData *data = ui->data; - unibi_out(ui, data->unibi_ext.disable_mouse); - data->mouse_enabled = false; + if (data->mouse_enabled) { + unibi_out(ui, data->unibi_ext.disable_mouse); + data->mouse_enabled = false; + } } static void tui_mode_change(UI *ui, int mode) From ddfa0359c638a4fd5eba5c339dc3e18e2b8aca35 Mon Sep 17 00:00:00 2001 From: Nikolai Aleksandrovich Pavlov Date: Sun, 2 Apr 2017 14:25:47 +0300 Subject: [PATCH 0319/1671] unittests: Make it easier to determine on which _spec line it crashed (#6424) Benchmarks: Before change: 17.78s user 3.48s system 94% cpu 22.525 total After change: 25.38s user 4.46s system 101% cpu 29.317 total --- test/unit/helpers.lua | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 93e1e91173..74f214a231 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -528,7 +528,8 @@ local hook_msglen = 1 + 1 + 1 + (1 + hook_fnamelen) + (1 + hook_sfnamelen) + (1 local tracehelp = dedent([[ ┌ Trace type: _r_eturn from function , function _c_all, _l_ine executed, - │ _t_ail return, _C_ount (should not actually appear). + │ _t_ail return, _C_ount (should not actually appear), + │ _s_aved from previous run for reference. │┏ Function type: _L_ua function, _C_ function, _m_ain part of chunk, │┃ function that did _t_ail call. │┃┌ Function name type: _g_lobal, _l_ocal, _m_ethod, _f_ield, _u_pvalue, @@ -549,15 +550,27 @@ local function child_sethook(wr) return end local trace_only_c = trace_level <= 1 - local function hook(reason, lnum) + local prev_info, prev_reason, prev_lnum + local function hook(reason, lnum, use_prev) local info = nil - if reason ~= 'tail return' then -- tail return + if use_prev then + info = prev_info + elseif reason ~= 'tail return' then -- tail return info = debug.getinfo(2, 'nSl') end - if trace_only_c and (not info or info.what ~= 'C') then + if trace_only_c and (not info or info.what ~= 'C') and not use_prev then + if info.source:sub(-9) == '_spec.lua' then + prev_info = info + prev_reason = 'saved' + prev_lnum = lnum + end return end + if trace_only_c and not use_prev and prev_reason then + hook(prev_reason, prev_lnum, true) + prev_reason = nil + end local whatchar = ' ' local namewhatchar = ' ' @@ -609,7 +622,7 @@ local function child_sethook(wr) -- eq(hook_msglen, #msg) sc.write(wr, msg) end - debug.sethook(hook, trace_only_c and 'cr' or 'crl') + debug.sethook(hook, 'crl') end local trace_end_msg = ('E%s\n'):format((' '):rep(hook_msglen - 2)) From b10880dadcbd3b3ad368621f95a0f4be7e30dc0d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Apr 2017 22:11:35 +0300 Subject: [PATCH 0320/1671] eval: Make writefile() able to disable fsync() --- runtime/doc/eval.txt | 5 +++ src/nvim/eval.c | 6 +++- src/nvim/os/fileio.c | 20 ++++++----- src/nvim/shada.c | 2 +- test/unit/os/fileio_spec.lua | 66 +++++++++++++++++++++++------------- 5 files changed, 66 insertions(+), 33 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 9a86e13d95..727199f742 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -7915,6 +7915,11 @@ writefile({list}, {fname} [, {flags}]) appended to the file: > :call writefile(["foo"], "event.log", "a") :call writefile(["bar"], "event.log", "a") +< + When {flags} contains "S" fsync() call is not used. This means + that writefile() will finish faster, but writes may be left in + OS buffers and not yet written to disk. Such changes will + disappear if system crashes before OS does writing. All NL characters are replaced with a NUL character. Inserting CR characters needs to be done before passing {list} diff --git a/src/nvim/eval.c b/src/nvim/eval.c index f0d78a2508..8a3e3f3e22 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -17421,6 +17421,7 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool binary = false; bool append = false; + bool do_fsync = true; if (argvars[2].v_type != VAR_UNKNOWN) { const char *const flags = tv_get_string_chk(&argvars[2]); if (flags == NULL) { @@ -17432,6 +17433,9 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (strchr(flags, 'a')) { append = true; } + if (strchr(flags, 'S')) { + do_fsync = false; + } } char buf[NUMBUFLEN]; @@ -17453,7 +17457,7 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (write_list(&fp, argvars[0].vval.v_list, binary)) { rettv->vval.v_number = 0; } - if ((error = file_close(&fp)) != 0) { + if ((error = file_close(&fp, do_fsync)) != 0) { emsgf(_("E80: Error when closing file %s: %s"), fname, os_strerror(error)); } diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c index 3c928363cc..4b7b53fc7f 100644 --- a/src/nvim/os/fileio.c +++ b/src/nvim/os/fileio.c @@ -113,27 +113,31 @@ FileDescriptor *file_open_new(int *const error, const char *const fname, /// Close file and free its buffer /// /// @param[in,out] fp File to close. +/// @param[in] do_fsync If true, use fsync() to write changes to disk. /// /// @return 0 or error code. -int file_close(FileDescriptor *const fp) FUNC_ATTR_NONNULL_ALL +int file_close(FileDescriptor *const fp, const bool do_fsync) + FUNC_ATTR_NONNULL_ALL { - const int error = file_fsync(fp); - const int error2 = os_close(fp->fd); + const int flush_error = (do_fsync ? file_fsync(fp) : file_flush(fp)); + const int close_error = os_close(fp->fd); rbuffer_free(fp->rv); - if (error2 != 0) { - return error2; + if (close_error != 0) { + return close_error; } - return error; + return flush_error; } /// Close and free file obtained using file_open_new() /// /// @param[in,out] fp File to close. +/// @param[in] do_fsync If true, use fsync() to write changes to disk. /// /// @return 0 or error code. -int file_free(FileDescriptor *const fp) FUNC_ATTR_NONNULL_ALL +int file_free(FileDescriptor *const fp, const bool do_fsync) + FUNC_ATTR_NONNULL_ALL { - const int ret = file_close(fp); + const int ret = file_close(fp, do_fsync); xfree(fp); return ret; } diff --git a/src/nvim/shada.c b/src/nvim/shada.c index f65fdaf1c0..c7b95958e0 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -811,7 +811,7 @@ static int open_shada_file_for_reading(const char *const fname, /// Wrapper for closing file descriptors static void close_file(void *cookie) { - const int error = file_free(cookie); + const int error = file_free(cookie, true); if (error != 0) { emsgf(_(SERR "System error while closing ShaDa file: %s"), os_strerror(error)); diff --git a/test/unit/os/fileio_spec.lua b/test/unit/os/fileio_spec.lua index 5e1b2523fa..6c1ae73847 100644 --- a/test/unit/os/fileio_spec.lua +++ b/test/unit/os/fileio_spec.lua @@ -98,7 +98,7 @@ describe('file_open', function() eq(0, err) local attrs = lfs.attributes(filec) eq('rwx------', attrs.permissions) - eq(0, m.file_close(fp)) + eq(0, m.file_close(fp, false)) end) itp('can create a rw------- file with kFileCreate', function() @@ -106,7 +106,7 @@ describe('file_open', function() eq(0, err) local attrs = lfs.attributes(filec) eq('rw-------', attrs.permissions) - eq(0, m.file_close(fp)) + eq(0, m.file_close(fp, false)) end) itp('can create a rwx------ file with kFileCreateOnly', function() @@ -114,7 +114,7 @@ describe('file_open', function() eq(0, err) local attrs = lfs.attributes(filec) eq('rwx------', attrs.permissions) - eq(0, m.file_close(fp)) + eq(0, m.file_close(fp, false)) end) itp('can create a rw------- file with kFileCreateOnly', function() @@ -122,7 +122,7 @@ describe('file_open', function() eq(0, err) local attrs = lfs.attributes(filec) eq('rw-------', attrs.permissions) - eq(0, m.file_close(fp)) + eq(0, m.file_close(fp, false)) end) itp('fails to open an existing file with kFileCreateOnly', function() @@ -141,35 +141,35 @@ describe('file_open', function() local err, fp = file_open(file1, m.kFileCreate, 384) eq(0, err) eq(true, fp.wr) - eq(0, m.file_close(fp)) + eq(0, m.file_close(fp, false)) end) itp('can open an existing file read-only with zero', function() local err, fp = file_open(file1, 0, 384) eq(0, err) eq(false, fp.wr) - eq(0, m.file_close(fp)) + eq(0, m.file_close(fp, false)) end) itp('can open an existing file read-only with kFileReadOnly', function() local err, fp = file_open(file1, m.kFileReadOnly, 384) eq(0, err) eq(false, fp.wr) - eq(0, m.file_close(fp)) + eq(0, m.file_close(fp, false)) end) itp('can open an existing file read-only with kFileNoSymlink', function() local err, fp = file_open(file1, m.kFileNoSymlink, 384) eq(0, err) eq(false, fp.wr) - eq(0, m.file_close(fp)) + eq(0, m.file_close(fp, false)) end) itp('can truncate an existing file with kFileTruncate', function() local err, fp = file_open(file1, m.kFileTruncate, 384) eq(0, err) eq(true, fp.wr) - eq(0, m.file_close(fp)) + eq(0, m.file_close(fp, false)) local attrs = lfs.attributes(file1) eq(0, attrs.size) end) @@ -178,7 +178,7 @@ describe('file_open', function() local err, fp = file_open(file1, m.kFileWriteOnly, 384) eq(0, err) eq(true, fp.wr) - eq(0, m.file_close(fp)) + eq(0, m.file_close(fp, false)) local attrs = lfs.attributes(file1) eq(4096, attrs.size) end) @@ -195,7 +195,7 @@ describe('file_open', function() local err, fp = file_open(linkf, m.kFileTruncate, 384) eq(0, err) eq(true, fp.wr) - eq(0, m.file_close(fp)) + eq(0, m.file_close(fp, false)) local attrs = lfs.attributes(file1) eq(0, attrs.size) end) @@ -221,7 +221,7 @@ describe('file_open_new', function() local err, fp = file_open_new(file1, 0, 384) eq(0, err) eq(false, fp.wr) - eq(0, m.file_free(fp)) + eq(0, m.file_free(fp, false)) end) itp('fails to open an existing file with kFileCreateOnly', function() @@ -231,7 +231,27 @@ describe('file_open_new', function() end) end) --- file_close is called above, so it is not tested directly +describe('file_close', function() + itp('can flush writes to disk also with true argument', function() + local err, fp = file_open(filec, m.kFileCreateOnly, 384) + local wsize = file_write(fp, 'test') + eq(4, wsize) + eq(0, lfs.attributes(filec).size) + eq(0, m.file_close(fp, true)) + eq(wsize, lfs.attributes(filec).size) + end) +end) + +describe('file_free', function() + itp('can flush writes to disk also with true argument', function() + local err, fp = file_open_new(filec, m.kFileCreateOnly, 384) + local wsize = file_write(fp, 'test') + eq(4, wsize) + eq(0, lfs.attributes(filec).size) + eq(0, m.file_free(fp, true)) + eq(wsize, lfs.attributes(filec).size) + end) +end) describe('file_fsync', function() itp('can flush writes to disk', function() @@ -244,7 +264,7 @@ describe('file_fsync', function() eq(0, lfs.attributes(filec).size) eq(0, file_fsync(fp)) eq(wsize, lfs.attributes(filec).size) - eq(0, m.file_close(fp)) + eq(0, m.file_close(fp, false)) end) end) @@ -259,7 +279,7 @@ describe('file_flush', function() eq(0, lfs.attributes(filec).size) eq(0, file_flush(fp)) eq(wsize, lfs.attributes(filec).size) - eq(0, m.file_close(fp)) + eq(0, m.file_close(fp, false)) end) end) @@ -281,7 +301,7 @@ describe('file_read', function() eq({exp_err, exp_s}, {file_read(fp, size)}) shift = shift + size end - eq(0, m.file_close(fp)) + eq(0, m.file_close(fp, false)) end) itp('can read the whole file at once', function() @@ -290,7 +310,7 @@ describe('file_read', function() eq(false, fp.wr) eq({#fcontents, fcontents}, {file_read(fp, #fcontents)}) eq({0, ('\0'):rep(#fcontents)}, {file_read(fp, #fcontents)}) - eq(0, m.file_close(fp)) + eq(0, m.file_close(fp, false)) end) itp('can read more then 1024 bytes after reading a small chunk', function() @@ -300,7 +320,7 @@ describe('file_read', function() eq({5, fcontents:sub(1, 5)}, {file_read(fp, 5)}) eq({#fcontents - 5, fcontents:sub(6) .. (('\0'):rep(5))}, {file_read(fp, #fcontents)}) - eq(0, m.file_close(fp)) + eq(0, m.file_close(fp, false)) end) itp('can read file by 768-byte-chunks', function() @@ -320,7 +340,7 @@ describe('file_read', function() eq({exp_err, exp_s}, {file_read(fp, size)}) shift = shift + size end - eq(0, m.file_close(fp)) + eq(0, m.file_close(fp, false)) end) end) @@ -331,7 +351,7 @@ describe('file_write', function() eq(true, fp.wr) local wr = file_write(fp, fcontents) eq(#fcontents, wr) - eq(0, m.file_close(fp)) + eq(0, m.file_close(fp, false)) eq(wr, lfs.attributes(filec).size) eq(fcontents, io.open(filec):read('*a')) end) @@ -348,7 +368,7 @@ describe('file_write', function() eq(wr, #s) shift = shift + size end - eq(0, m.file_close(fp)) + eq(0, m.file_close(fp, false)) eq(#fcontents, lfs.attributes(filec).size) eq(fcontents, io.open(filec):read('*a')) end) @@ -365,7 +385,7 @@ describe('file_write', function() eq(wr, #s) shift = shift + size end - eq(0, m.file_close(fp)) + eq(0, m.file_close(fp, false)) eq(#fcontents, lfs.attributes(filec).size) eq(fcontents, io.open(filec):read('*a')) end) @@ -380,6 +400,6 @@ describe('file_skip', function() local rd, s = file_read(fp, 3) eq(3, rd) eq(fcontents:sub(4, 6), s) - eq(0, m.file_close(fp)) + eq(0, m.file_close(fp, false)) end) end) From 364709bedb17bfde4eb12d8f2c1427fe958dc6fc Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Apr 2017 00:35:29 +0300 Subject: [PATCH 0321/1671] fileio: Refactor errmsg handling Adds os_strerror() result to a number of places. Also since I could not track where err\* variables are NULL and where they are not, using macros to make sure that all three variables are set at once. Removes #ifdef UNIX around the use of os_fsync, makes it use os_close in place of close in some places. --- runtime/doc/options.txt | 3 + src/nvim/fileio.c | 175 ++++++++++++++++++++++------------------ 2 files changed, 99 insertions(+), 79 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 394e38f6e5..eedb7ce34d 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2740,6 +2740,9 @@ A jump table for the options with a short description can be found at |Q_op|. mode, so it may be undesirable in some situations. Be warned that turning this off increases the chances of data loss after a crash. + Currently applies only to writing the buffer with e.g. |:w| and + |writefile()|. + *'gdefault'* *'gd'* *'nogdefault'* *'nogd'* 'gdefault' 'gd' boolean (default off) global diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index bd632b2755..fb44d2cc1d 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -2258,9 +2258,16 @@ buf_write ( int len; linenr_T lnum; long nchars; - char_u *errmsg = NULL; - int errmsg_allocated = FALSE; - char_u *errnum = NULL; +#define SET_ERRMSG_NUM(num, msg) \ + errnum = num ": ", errmsg = msg, errmsgarg = 0 +#define SET_ERRMSG_ARG(msg, error) \ + errnum = NULL, errmsg = msg, errmsgarg = error +#define SET_ERRMSG(msg) \ + errnum = NULL, errmsg = msg, errmsgarg = 0 + const char *errnum = NULL; + char *errmsg = NULL; + int errmsgarg = 0; + bool errmsg_allocated = false; char_u *buffer; char_u smallbuf[SMBUFSIZE]; char_u *backup_ext; @@ -2282,7 +2289,6 @@ buf_write ( /* writing everything */ int whole = (start == 1 && end == buf->b_ml.ml_line_count); linenr_T old_line_count = buf->b_ml.ml_line_count; - int attr; int fileformat; int write_bin; struct bw_info write_info; /* info for buf_write_bytes() */ @@ -2577,13 +2583,11 @@ buf_write ( perm = file_info_old.stat.st_mode; if (!S_ISREG(file_info_old.stat.st_mode)) { /* not a file */ if (S_ISDIR(file_info_old.stat.st_mode)) { - errnum = (char_u *)"E502: "; - errmsg = (char_u *)_("is a directory"); + SET_ERRMSG_NUM("E502", _("is a directory")); goto fail; } if (os_nodetype((char *)fname) != NODE_WRITABLE) { - errnum = (char_u *)"E503: "; - errmsg = (char_u *)_("is not a file or writable device"); + SET_ERRMSG_NUM("E503", _("is not a file or writable device")); goto fail; } /* It's a device of some kind (or a fifo) which we can write to @@ -2599,8 +2603,7 @@ buf_write ( */ c = os_nodetype((char *)fname); if (c == NODE_OTHER) { - errnum = (char_u *)"E503: "; - errmsg = (char_u *)_("is not a file or writable device"); + SET_ERRMSG_NUM("E503", _("is not a file or writable device")); goto fail; } if (c == NODE_WRITABLE) { @@ -2612,8 +2615,7 @@ buf_write ( if (perm < 0) { newfile = true; } else if (os_isdir(fname)) { - errnum = (char_u *)"E502: "; - errmsg = (char_u *)_("is a directory"); + SET_ERRMSG_NUM("E502", _("is a directory")); goto fail; } if (overwriting) { @@ -2632,11 +2634,9 @@ buf_write ( if (!forceit && file_readonly) { if (vim_strchr(p_cpo, CPO_FWRITE) != NULL) { - errnum = (char_u *)"E504: "; - errmsg = (char_u *)_(err_readonly); + SET_ERRMSG_NUM("E504", _(err_readonly)); } else { - errnum = (char_u *)"E505: "; - errmsg = (char_u *)_("is read-only (add ! to override)"); + SET_ERRMSG_NUM("E505", _("is read-only (add ! to override)")); } goto fail; } @@ -2904,23 +2904,27 @@ buf_write ( while ((write_info.bw_len = read_eintr(fd, copybuf, BUFSIZE)) > 0) { if (buf_write_bytes(&write_info) == FAIL) { - errmsg = (char_u *)_( - "E506: Can't write to backup file (add ! to override)"); + SET_ERRMSG(_( + "E506: Can't write to backup file (add ! to override)")); break; } os_breakcheck(); if (got_int) { - errmsg = (char_u *)_(e_interr); + SET_ERRMSG(_(e_interr)); break; } } - if (close(bfd) < 0 && errmsg == NULL) - errmsg = (char_u *)_( - "E507: Close error for backup file (add ! to override)"); - if (write_info.bw_len < 0) - errmsg = (char_u *)_( - "E508: Can't read file for backup (add ! to override)"); + int error; + if ((error = os_close(bfd)) != 0 && errmsg == NULL) { + SET_ERRMSG_ARG(_( + "E507: Close error for backup file (add ! to override): %s"), + error); + } + if (write_info.bw_len < 0) { + SET_ERRMSG(_( + "E508: Can't read file for backup (add ! to override)")); + } #ifdef UNIX set_file_time(backup, file_info_old.stat.st_atim.tv_sec, @@ -2937,18 +2941,19 @@ buf_write ( } } nobackup: - close(fd); /* ignore errors for closing read file */ + os_close(fd); /* ignore errors for closing read file */ xfree(copybuf); - if (backup == NULL && errmsg == NULL) - errmsg = (char_u *)_( - "E509: Cannot create backup file (add ! to override)"); - /* ignore errors when forceit is TRUE */ + if (backup == NULL && errmsg == NULL) { + SET_ERRMSG(_( + "E509: Cannot create backup file (add ! to override)")); + } + // Ignore errors when forceit is TRUE. if ((some_error || errmsg != NULL) && !forceit) { retval = FAIL; goto fail; } - errmsg = NULL; + SET_ERRMSG(NULL); } else { char_u *dirp; char_u *p; @@ -2963,8 +2968,7 @@ nobackup: * anyway, thus we need an extra check here. */ if (file_readonly && vim_strchr(p_cpo, CPO_FWRITE) != NULL) { - errnum = (char_u *)"E504: "; - errmsg = (char_u *)_(err_readonly); + SET_ERRMSG_NUM("E504", _(err_readonly)); goto fail; } @@ -3028,7 +3032,7 @@ nobackup: } } if (backup == NULL && !forceit) { - errmsg = (char_u *)_("E510: Can't make backup file (add ! to override)"); + SET_ERRMSG(_("E510: Can't make backup file (add ! to override)")); goto fail; } } @@ -3069,7 +3073,7 @@ nobackup: && !(exiting && backup != NULL)) { ml_preserve(buf, FALSE); if (got_int) { - errmsg = (char_u *)_(e_interr); + SET_ERRMSG(_(e_interr)); goto restore_backup; } } @@ -3140,8 +3144,8 @@ nobackup: */ if (*p_ccv != NUL) { wfname = vim_tempname(); - if (wfname == NULL) { /* Can't write without a tempfile! */ - errmsg = (char_u *)_("E214: Can't find temp file for writing"); + if (wfname == NULL) { // Can't write without a tempfile! + SET_ERRMSG(_("E214: Can't find temp file for writing")); goto restore_backup; } } @@ -3153,8 +3157,8 @@ nobackup: && wfname == fname ) { if (!forceit) { - errmsg = (char_u *)_( - "E213: Cannot convert (add ! to write without conversion)"); + SET_ERRMSG(_( + "E213: Cannot convert (add ! to write without conversion)")); goto restore_backup; } notconverted = TRUE; @@ -3189,11 +3193,10 @@ nobackup: if ((!newfile && os_fileinfo_hardlinks(&file_info) > 1) || (os_fileinfo_link((char *)fname, &file_info) && !os_fileinfo_id_equal(&file_info, &file_info_old))) { - errmsg = (char_u *)_("E166: Can't open linked file for writing"); - } else + SET_ERRMSG(_("E166: Can't open linked file for writing")); + } else { #endif - { - errmsg = (char_u *)_("E212: Can't open file for writing"); + SET_ERRMSG(_("E212: Can't open file for writing")); if (forceit && vim_strchr(p_cpo, CPO_FWRITE) == NULL && perm >= 0) { #ifdef UNIX @@ -3211,7 +3214,9 @@ nobackup: os_remove((char *)wfname); continue; } +#ifdef UNIX } +#endif } restore_backup: @@ -3253,7 +3258,7 @@ restore_backup: xfree(wfname); goto fail; } - errmsg = NULL; + SET_ERRMSG(NULL); write_info.bw_fd = fd; @@ -3373,7 +3378,6 @@ restore_backup: nchars += len; } -#if defined(UNIX) // On many journalling file systems there is a bug that causes both the // original and the backup file to be lost when halting the system right // after writing the file. That's because only the meta-data is @@ -3382,11 +3386,11 @@ restore_backup: // For a device do try the fsync() but don't complain if it does not work // (could be a pipe). // If the 'fsync' option is FALSE, don't fsync(). Useful for laptops. - if (p_fs && os_fsync(fd) != 0 && !device) { - errmsg = (char_u *)_("E667: Fsync failed"); + int error; + if (p_fs && (error = os_fsync(fd)) != 0 && !device) { + SET_ERRMSG_ARG(_("E667: Fsync failed: %s"), error); end = 0; } -#endif #ifdef HAVE_SELINUX /* Probably need to set the security context. */ @@ -3416,8 +3420,8 @@ restore_backup: } #endif - if (close(fd) != 0) { - errmsg = (char_u *)_("E512: Close failed"); + if ((error = os_close(fd)) != 0) { + SET_ERRMSG_ARG(_("E512: Close failed: %s"), error); end = 0; } @@ -3454,21 +3458,25 @@ restore_backup: if (end == 0) { if (errmsg == NULL) { if (write_info.bw_conv_error) { - if (write_info.bw_conv_error_lnum == 0) - errmsg = (char_u *)_( - "E513: write error, conversion failed (make 'fenc' empty to override)"); - else { - errmsg_allocated = TRUE; - errmsg = xmalloc(300); - vim_snprintf((char *)errmsg, 300, - _("E513: write error, conversion failed in line %" PRId64 - " (make 'fenc' empty to override)"), - (int64_t)write_info.bw_conv_error_lnum); + if (write_info.bw_conv_error_lnum == 0) { + SET_ERRMSG(_( + "E513: write error, conversion failed " + "(make 'fenc' empty to override)")); } - } else if (got_int) - errmsg = (char_u *)_(e_interr); - else - errmsg = (char_u *)_("E514: write error (file system full?)"); + else { + errmsg_allocated = true; + SET_ERRMSG(xmalloc(300)); + vim_snprintf( + errmsg, 300, + _("E513: write error, conversion failed in line %" PRIdLINENR + " (make 'fenc' empty to override)"), + write_info.bw_conv_error_lnum); + } + } else if (got_int) { + SET_ERRMSG(_(e_interr)); + } else { + SET_ERRMSG(_("E514: write error (file system full?)")); + } } /* @@ -3673,33 +3681,39 @@ nofail: #endif if (errmsg != NULL) { - int numlen = errnum != NULL ? (int)STRLEN(errnum) : 0; + const size_t numlen = (errnum != NULL ? strlen(errnum) : 0); - attr = hl_attr(HLF_E); /* set highlight for error messages */ - msg_add_fname(buf, + // Put file name in IObuff with quotes. #ifndef UNIX - sfname + msg_add_fname(buf, sfname); #else - fname + msg_add_fname(buf, fname); #endif - ); /* put file name in IObuff with quotes */ - if (STRLEN(IObuff) + STRLEN(errmsg) + numlen >= IOSIZE) - IObuff[IOSIZE - STRLEN(errmsg) - numlen - 1] = NUL; - /* If the error message has the form "is ...", put the error number in - * front of the file name. */ + const size_t errmsglen = strlen(errmsg); + if (STRLEN(IObuff) + errmsglen + numlen >= IOSIZE) { + IObuff[IOSIZE - errmsglen - numlen - 1] = NUL; + } + // If the error message has the form "is ...", put the error number in + // front of the file name. if (errnum != NULL) { STRMOVE(IObuff + numlen, IObuff); - memmove(IObuff, errnum, (size_t)numlen); + memmove(IObuff, errnum, numlen); } - STRCAT(IObuff, errmsg); - emsg(IObuff); - if (errmsg_allocated) + xstrlcat((char *)IObuff, errmsg, IOSIZE); + if (errmsgarg != 0) { + emsgf((const char *)IObuff, os_strerror(errmsgarg)); + } else { + emsgf((const char *)IObuff); + } + if (errmsg_allocated) { xfree(errmsg); + } retval = FAIL; if (end == 0) { + const int attr = hl_attr(HLF_E); // Set highlight for error messages. MSG_PUTS_ATTR(_("\nWARNING: Original file may be lost or damaged\n"), - attr | MSG_HIST); + attr | MSG_HIST); MSG_PUTS_ATTR(_( "don't quit the editor until the file is successfully written!"), attr | MSG_HIST); @@ -3759,6 +3773,9 @@ nofail: got_int |= prev_got_int; return retval; +#undef SET_ERRMSG +#undef SET_ERRMSG_ARG +#undef SET_ERRMSG_NUM } /* From 8dd9c6edd87403fb583b1e8b5567987e159fd7e2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Apr 2017 00:40:48 +0300 Subject: [PATCH 0322/1671] message: Do not use IObuff in emsgf --- src/nvim/message.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/nvim/message.c b/src/nvim/message.c index 83f2735b50..4423b430a5 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -573,16 +573,17 @@ void emsg_invreg(int name) /// Print an error message with unknown number of arguments bool emsgf(const char *const fmt, ...) { + static char errbuf[IOSIZE]; if (emsg_not_now()) { return true; } va_list ap; va_start(ap, fmt); - vim_vsnprintf((char *) IObuff, IOSIZE, fmt, ap, NULL); + vim_vsnprintf(errbuf, sizeof(errbuf), fmt, ap, NULL); va_end(ap); - return emsg(IObuff); + return emsg(errbuf); } static void msg_emsgf_event(void **argv) From 8eb598c08ea51731536184a4b3e98000ce073877 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Apr 2017 00:44:49 +0300 Subject: [PATCH 0323/1671] fixup! --- src/nvim/message.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/message.c b/src/nvim/message.c index 4423b430a5..1d3609291a 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -583,7 +583,7 @@ bool emsgf(const char *const fmt, ...) vim_vsnprintf(errbuf, sizeof(errbuf), fmt, ap, NULL); va_end(ap); - return emsg(errbuf); + return emsg((const char_u *)errbuf); } static void msg_emsgf_event(void **argv) From 1c41b9c77552618a5010ca69bee92033c4082748 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Apr 2017 01:39:09 +0300 Subject: [PATCH 0324/1671] fileio: Clean up IObuff-manipulation mess --- src/nvim/fileio.c | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index fb44d2cc1d..e382faf7a2 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -2259,7 +2259,7 @@ buf_write ( linenr_T lnum; long nchars; #define SET_ERRMSG_NUM(num, msg) \ - errnum = num ": ", errmsg = msg, errmsgarg = 0 + errnum = num, errmsg = msg, errmsgarg = 0 #define SET_ERRMSG_ARG(msg, error) \ errnum = NULL, errmsg = msg, errmsgarg = error #define SET_ERRMSG(msg) \ @@ -3681,29 +3681,21 @@ nofail: #endif if (errmsg != NULL) { - const size_t numlen = (errnum != NULL ? strlen(errnum) : 0); - - // Put file name in IObuff with quotes. #ifndef UNIX msg_add_fname(buf, sfname); #else msg_add_fname(buf, fname); #endif - const size_t errmsglen = strlen(errmsg); - if (STRLEN(IObuff) + errmsglen + numlen >= IOSIZE) { - IObuff[IOSIZE - errmsglen - numlen - 1] = NUL; - } - // If the error message has the form "is ...", put the error number in - // front of the file name. if (errnum != NULL) { - STRMOVE(IObuff + numlen, IObuff); - memmove(IObuff, errnum, numlen); - } - xstrlcat((char *)IObuff, errmsg, IOSIZE); - if (errmsgarg != 0) { - emsgf((const char *)IObuff, os_strerror(errmsgarg)); + if (errmsgarg != 0) { + emsgf("%s: %s%s: %s", errnum, IObuff, errmsg, os_strerror(errmsgarg)); + } else { + emsgf("%s: %s%s", errnum, IObuff, errmsg); + } + } else if (errmsgarg != 0) { + emsgf(errmsg, os_strerror(errmsgarg)); } else { - emsgf((const char *)IObuff); + emsgf(errmsg); } if (errmsg_allocated) { xfree(errmsg); @@ -3822,7 +3814,7 @@ static int set_rw_fname(char_u *fname, char_u *sfname) /* * Put file name into IObuff with quotes. */ -void msg_add_fname(buf_T *buf, char_u *fname) +static void msg_add_fname(buf_T *buf, char_u *fname) { if (fname == NULL) fname = (char_u *)"-stdin-"; From 5dcf2804455f45eac8aad7d900bf60464a4b2888 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Apr 2017 02:03:05 +0300 Subject: [PATCH 0325/1671] fileio: Refactor msg_add_fname to something which needs no comments --- src/nvim/buffer.c | 4 ++-- src/nvim/fileio.c | 58 ++++++++++++++++++++++++++--------------------- src/nvim/os/env.c | 7 +++--- src/nvim/path.c | 14 ++++++------ 4 files changed, 45 insertions(+), 38 deletions(-) diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index c9101c5b53..292eb03a16 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -2724,7 +2724,7 @@ fileinfo ( else name = curbuf->b_ffname; home_replace(shorthelp ? curbuf : NULL, name, p, - (int)(IOSIZE - (p - buffer)), TRUE); + (size_t)(IOSIZE - (p - buffer)), true); } vim_snprintf_add((char *)buffer, IOSIZE, "\"%s%s%s%s%s%s", @@ -2889,7 +2889,7 @@ void maketitle(void) buf[off++] = ' '; buf[off++] = '('; home_replace(curbuf, curbuf->b_ffname, - buf + off, SPACE_FOR_DIR - off, TRUE); + buf + off, (size_t)(SPACE_FOR_DIR - off), true); #ifdef BACKSLASH_IN_FILENAME /* avoid "c:/name" to be reduced to "c" */ if (isalpha(buf[off]) && buf[off + 1] == ':') diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index e382faf7a2..1ae73e787d 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -200,18 +200,14 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr) { int msg_scroll_save; - if (msg_silent != 0) + 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); - /* - * For the first message may have to start a new line. - * For further ones overwrite the previous one, reset msg_scroll before - * calling filemess(). - */ + } + add_quoted_fname((char *)IObuff, IOSIZE - 80, buf,(const char *)name); + xstrlcat((char *)IObuff, (const char *)s, IOSIZE); + // For the first message may have to start a new line. + // For further ones overwrite the previous one, reset msg_scroll before + // calling filemess(). msg_scroll_save = msg_scroll; if (shortmess(SHM_OVERALL) && !exiting && p_verbose == 0) msg_scroll = FALSE; @@ -1800,8 +1796,8 @@ failed: } if (!filtering && !(flags & READ_DUMMY)) { - msg_add_fname(curbuf, sfname); /* fname in IObuff with quotes */ - c = FALSE; + add_quoted_fname((char *)IObuff, IOSIZE, curbuf,(const char *)sfname); + c = false; #ifdef UNIX # ifdef S_ISFIFO @@ -3531,8 +3527,8 @@ restore_backup: fname = sfname; /* use shortname now, for the messages */ #endif if (!filtering) { - msg_add_fname(buf, fname); /* put fname in IObuff with quotes */ - c = FALSE; + add_quoted_fname((char *)IObuff, IOSIZE, buf,(const char *)fname); + c = false; if (write_info.bw_conv_error) { STRCAT(IObuff, _(" CONVERSION ERROR")); c = TRUE; @@ -3681,10 +3677,11 @@ nofail: #endif if (errmsg != NULL) { + // - 100 to save some space for further error message #ifndef UNIX - msg_add_fname(buf, sfname); + add_quoted_fname((char *)IObuff, IOSIZE - 100, buf, (const char *)sfname); #else - msg_add_fname(buf, fname); + add_quoted_fname((char *)IObuff, IOSIZE - 100, buf, (const char *)fname); #endif if (errnum != NULL) { if (errmsgarg != 0) { @@ -3811,16 +3808,25 @@ static int set_rw_fname(char_u *fname, char_u *sfname) return OK; } -/* - * Put file name into IObuff with quotes. - */ -static void msg_add_fname(buf_T *buf, char_u *fname) +/// Put file name into the specified buffer with quotes +/// +/// Replaces home directory at the start with `~`. +/// +/// @param[out] ret_buf Buffer to save results to. +/// @param[in] buf_len ret_buf length. +/// @param[in] buf buf_T file name is coming from. +/// @param[in] fname File name to write. +static void add_quoted_fname(char *const ret_buf, const size_t buf_len, + const buf_T *const buf, const char *fname) + FUNC_ATTR_NONNULL_ARG(1) { - if (fname == NULL) - fname = (char_u *)"-stdin-"; - home_replace(buf, fname, IObuff + 1, IOSIZE - 4, TRUE); - IObuff[0] = '"'; - STRCAT(IObuff, "\" "); + if (fname == NULL) { + fname = "-stdin-"; + } + ret_buf[0] = '"'; + home_replace(buf, (const char_u *)fname, (char_u *)ret_buf + 1, + (int)buf_len - 4, true); + xstrlcat(ret_buf, "\" ", buf_len); } /// Append message for text mode to IObuff. diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index a10c835591..1a97adfa21 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -703,7 +703,8 @@ char *vim_getenv(const char *name) /// @param dstlen Maximum length of the result /// @param one If true, only replace one file name, including spaces and commas /// in the file name -void home_replace(buf_T *buf, char_u *src, char_u *dst, int dstlen, bool one) +void home_replace(const buf_T *const buf, const char_u *src, + char_u *dst, size_t dstlen, bool one) { size_t dirlen = 0, envlen = 0; size_t len; @@ -717,7 +718,7 @@ void home_replace(buf_T *buf, char_u *src, char_u *dst, int dstlen, bool one) * If the file is a help file, remove the path completely. */ if (buf != NULL && buf->b_help) { - STRCPY(dst, path_tail(src)); + xstrlcpy((char *)dst, (char *)path_tail(src), dstlen); return; } @@ -809,7 +810,7 @@ char_u * home_replace_save(buf_T *buf, char_u *src) FUNC_ATTR_NONNULL_RET len += STRLEN(src); } char_u *dst = xmalloc(len); - home_replace(buf, src, dst, (int)len, true); + home_replace(buf, src, dst, len, true); return dst; } diff --git a/src/nvim/path.c b/src/nvim/path.c index d0248690d9..6bf42ed2fa 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -84,15 +84,15 @@ FileComparison path_full_compare(char_u *s1, char_u *s2, int checkname) /// /// @return pointer just past the last path separator (empty string, if fname /// ends in a slash), or empty string if fname is NULL. -char_u *path_tail(char_u *fname) +char_u *path_tail(const char_u *fname) FUNC_ATTR_NONNULL_RET { if (fname == NULL) { return (char_u *)""; } - char_u *tail = get_past_head(fname); - char_u *p = tail; + const char_u *tail = get_past_head(fname); + const char_u *p = tail; // Find last part of path. while (*p != NUL) { if (vim_ispathsep_nocolon(*p)) { @@ -100,7 +100,7 @@ char_u *path_tail(char_u *fname) } mb_ptr_adv(p); } - return tail; + return (char_u *)tail; } /// Get pointer to tail of "fname", including path separators. @@ -174,9 +174,9 @@ const char *path_next_component(const char *fname) /// Get a pointer to one character past the head of a path name. /// Unix: after "/"; Win: after "c:\" /// If there is no head, path is returned. -char_u *get_past_head(char_u *path) +char_u *get_past_head(const char_u *path) { - char_u *retval = path; + const char_u *retval = path; #ifdef WIN32 // May skip "c:" @@ -189,7 +189,7 @@ char_u *get_past_head(char_u *path) ++retval; } - return retval; + return (char_u *)retval; } /* From 97a7f4745dd1d75cd176dede1a4430bc4e28f8f7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Apr 2017 02:11:27 +0300 Subject: [PATCH 0326/1671] eval: Add s flag, use p_fs by default, error out on unknown flag --- runtime/doc/eval.txt | 9 +++++---- src/nvim/eval.c | 22 +++++++++++++--------- test/functional/eval/writefile_spec.lua | 9 +++++++++ 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 727199f742..7060cc4186 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -7916,10 +7916,11 @@ writefile({list}, {fname} [, {flags}]) :call writefile(["foo"], "event.log", "a") :call writefile(["bar"], "event.log", "a") < - When {flags} contains "S" fsync() call is not used. This means - that writefile() will finish faster, but writes may be left in - OS buffers and not yet written to disk. Such changes will - disappear if system crashes before OS does writing. + When {flags} contains "S" fsync() call is not used, with "s" + it is used, 'fsync' option applies by default. No fsync() + means that writefile() will finish faster, but writes may be + left in OS buffers and not yet written to disk. Such changes + will disappear if system crashes before OS does writing. All NL characters are replaced with a NUL character. Inserting CR characters needs to be done before passing {list} diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8a3e3f3e22..7ab07fe6a2 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -17421,20 +17421,24 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool binary = false; bool append = false; - bool do_fsync = true; + bool do_fsync = !!p_fs; if (argvars[2].v_type != VAR_UNKNOWN) { const char *const flags = tv_get_string_chk(&argvars[2]); if (flags == NULL) { return; } - if (strchr(flags, 'b')) { - binary = true; - } - if (strchr(flags, 'a')) { - append = true; - } - if (strchr(flags, 'S')) { - do_fsync = false; + for (const char *p = flags; *p; p++) { + switch (*p) { + case 'b': { binary = true; break; } + case 'a': { append = true; break; } + case 's': { do_fsync = true; break; } + case 'S': { do_fsync = false; break; } + default: { + // Using %s, p and not %c, *p to preserve multibyte characters + emsgf(_("E5060: Unknown flag: %s"), p); + return; + } + } } } diff --git a/test/functional/eval/writefile_spec.lua b/test/functional/eval/writefile_spec.lua index 3052c616e0..2f84114b9b 100644 --- a/test/functional/eval/writefile_spec.lua +++ b/test/functional/eval/writefile_spec.lua @@ -80,6 +80,13 @@ describe('writefile()', function() eq('a\0\0\0b', read_file(fname)) end) + it('writes with s and S', function() + eq(0, funcs.writefile({'\na\nb\n'}, fname, 'bs')) + eq('\0a\0b\0', read_file(fname)) + eq(0, funcs.writefile({'a\n\n\nb'}, fname, 'bS')) + eq('a\0\0\0b', read_file(fname)) + end) + it('correctly overwrites file', function() eq(0, funcs.writefile({'\na\nb\n'}, fname, 'b')) eq('\0a\0b\0', read_file(fname)) @@ -115,6 +122,8 @@ describe('writefile()', function() eq('\nE729: using Funcref as a String', redir_exec(('call writefile(%s)'):format(args:format('function("tr")')))) end + eq('\nE5060: Unknown flag: «»', + redir_exec(('call writefile([], "%s", "bs«»")'):format(fname))) eq('TEST', read_file(fname)) end) From 2dbd49f73cba0dac8d430f86b301b6e9c15f3a7f Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Apr 2017 03:02:17 +0300 Subject: [PATCH 0327/1671] fileio: Save details about E212 error --- src/nvim/fileio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 1ae73e787d..8e0e45aee6 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -3192,7 +3192,7 @@ nobackup: SET_ERRMSG(_("E166: Can't open linked file for writing")); } else { #endif - SET_ERRMSG(_("E212: Can't open file for writing")); + SET_ERRMSG_ARG(_("E212: Can't open file for writing: %s"), fd); if (forceit && vim_strchr(p_cpo, CPO_FWRITE) == NULL && perm >= 0) { #ifdef UNIX From 991204310399d4dcaf5f5ee7571eff178c93e793 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Apr 2017 03:04:10 +0300 Subject: [PATCH 0328/1671] functests: Test some :write errors --- test/functional/ex_cmds/write_spec.lua | 43 ++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/test/functional/ex_cmds/write_spec.lua b/test/functional/ex_cmds/write_spec.lua index 4ac9f312ef..9c2687971e 100644 --- a/test/functional/ex_cmds/write_spec.lua +++ b/test/functional/ex_cmds/write_spec.lua @@ -1,15 +1,28 @@ local helpers = require('test.functional.helpers')(after_each) +local lfs = require('lfs') local eq, eval, clear, write_file, execute, source, insert = helpers.eq, helpers.eval, helpers.clear, helpers.write_file, helpers.execute, helpers.source, helpers.insert +local redir_exec = helpers.redir_exec +local exc_exec = helpers.exc_exec +local command = helpers.command +local funcs = helpers.funcs +local meths = helpers.meths if helpers.pending_win32(pending) then return end +local fname = 'Xtest-functional-ex_cmds-write' +local fname_bak = fname .. '~' +local fname_broken = fname_bak .. 'broken' + describe(':write', function() local function cleanup() os.remove('test_bkc_file.txt') os.remove('test_bkc_link.txt') os.remove('test_fifo') + os.remove(fname) + os.remove(fname_bak) + os.remove(fname_broken) end before_each(function() clear() @@ -63,4 +76,34 @@ describe(':write', function() eq(text.."\n", fifo:read("*all")) fifo:close() end) + + it('errors out correctly', function() + command('let $HOME=""') + eq(funcs.fnamemodify('.', ':p:h'), funcs.fnamemodify('.', ':p:h:~')) + -- Message from check_overwrite + eq(('\nE17: "'..funcs.fnamemodify('.', ':p:h')..'" is a directory'), + redir_exec('write .')) + meths.set_option('writeany', true) + -- Message from buf_write + eq(('\nE502: "." is a directory'), + redir_exec('write .')) + funcs.mkdir(fname_bak) + meths.set_option('backupdir', '.') + meths.set_option('backup', true) + write_file(fname, 'content0') + eq(0, exc_exec('edit ' .. fname)) + funcs.setline(1, 'TTY') + eq('Vim(write):E510: Can\'t make backup file (add ! to override)', + exc_exec('write')) + meths.set_option('backup', false) + funcs.setfperm(fname, 'r--------') + eq('Vim(write):E505: "Xtest-functional-ex_cmds-write" is read-only (add ! to override)', + exc_exec('write')) + os.remove(fname) + os.remove(fname_bak) + write_file(fname_bak, 'TTYX') + lfs.link(fname_bak .. ('/xxxxx'):rep(20), fname, true) + eq('Vim(write):E166: Can\'t open linked file for writing', + exc_exec('write!')) + end) end) From dc75766081e143401ae28bea66f970ab005402fc Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Apr 2017 03:07:01 +0300 Subject: [PATCH 0329/1671] tests: Fix testlint errors --- test/functional/ui/cursor_spec.lua | 5 ++--- test/unit/os/fileio_spec.lua | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua index 8b42c193ab..56f02e4e7f 100644 --- a/test/functional/ui/cursor_spec.lua +++ b/test/functional/ui/cursor_spec.lua @@ -1,8 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') -local clear, feed, meths = helpers.clear, helpers.feed, helpers.meths -local insert, execute = helpers.insert, helpers.execute -local eq, funcs = helpers.eq, helpers.funcs +local clear, meths = helpers.clear, helpers.meths +local eq = helpers.eq local command = helpers.command describe('ui/cursor', function() diff --git a/test/unit/os/fileio_spec.lua b/test/unit/os/fileio_spec.lua index 6c1ae73847..e3c8e616ce 100644 --- a/test/unit/os/fileio_spec.lua +++ b/test/unit/os/fileio_spec.lua @@ -234,6 +234,7 @@ end) describe('file_close', function() itp('can flush writes to disk also with true argument', function() local err, fp = file_open(filec, m.kFileCreateOnly, 384) + eq(0, err) local wsize = file_write(fp, 'test') eq(4, wsize) eq(0, lfs.attributes(filec).size) @@ -245,6 +246,7 @@ end) describe('file_free', function() itp('can flush writes to disk also with true argument', function() local err, fp = file_open_new(filec, m.kFileCreateOnly, 384) + eq(0, err) local wsize = file_write(fp, 'test') eq(4, wsize) eq(0, lfs.attributes(filec).size) From ac87c7e5ae5fc15ca97150dfce1476408804ae5a Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Apr 2017 03:46:44 +0300 Subject: [PATCH 0330/1671] fileio: Fix most linter errors One error is still kept: buf_write function is too large. --- src/nvim/fileio.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 8e0e45aee6..c1b8203ed1 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -203,7 +203,7 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr) if (msg_silent != 0) { return; } - add_quoted_fname((char *)IObuff, IOSIZE - 80, buf,(const char *)name); + add_quoted_fname((char *)IObuff, IOSIZE - 80, buf, (const char *)name); xstrlcat((char *)IObuff, (const char *)s, IOSIZE); // For the first message may have to start a new line. // For further ones overwrite the previous one, reset msg_scroll before @@ -1796,7 +1796,7 @@ failed: } if (!filtering && !(flags & READ_DUMMY)) { - add_quoted_fname((char *)IObuff, IOSIZE, curbuf,(const char *)sfname); + add_quoted_fname((char *)IObuff, IOSIZE, curbuf, (const char *)sfname); c = false; #ifdef UNIX @@ -2913,9 +2913,9 @@ buf_write ( int error; if ((error = os_close(bfd)) != 0 && errmsg == NULL) { - SET_ERRMSG_ARG(_( - "E507: Close error for backup file (add ! to override): %s"), - error); + SET_ERRMSG_ARG(_("E507: Close error for backup file " + "(add ! to override): %s"), + error); } if (write_info.bw_len < 0) { SET_ERRMSG(_( @@ -2937,7 +2937,7 @@ buf_write ( } } nobackup: - os_close(fd); /* ignore errors for closing read file */ + os_close(fd); // Ignore errors for closing read file. xfree(copybuf); if (backup == NULL && errmsg == NULL) { @@ -3458,8 +3458,7 @@ restore_backup: SET_ERRMSG(_( "E513: write error, conversion failed " "(make 'fenc' empty to override)")); - } - else { + } else { errmsg_allocated = true; SET_ERRMSG(xmalloc(300)); vim_snprintf( @@ -3527,7 +3526,7 @@ restore_backup: fname = sfname; /* use shortname now, for the messages */ #endif if (!filtering) { - add_quoted_fname((char *)IObuff, IOSIZE, buf,(const char *)fname); + add_quoted_fname((char *)IObuff, IOSIZE, buf, (const char *)fname); c = false; if (write_info.bw_conv_error) { STRCAT(IObuff, _(" CONVERSION ERROR")); From ae7d8d8ffb86eefa45d8f59834eb0f088e93535d Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Apr 2017 03:47:42 +0300 Subject: [PATCH 0331/1671] ci: Do not mark test as failed if it is previous one which failed --- ci/common/suite.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/common/suite.sh b/ci/common/suite.sh index a70cafc92c..5c79ce2718 100644 --- a/ci/common/suite.sh +++ b/ci/common/suite.sh @@ -69,6 +69,7 @@ run_test_wd() { while test $restarts -gt 0 ; do : > "${status_file}" ( + FAILED=0 if ! ( set -o pipefail eval "$cmd" 2>&1 | tee -a "$output_file" From bc6d868d00a739050b683f33994f7493cf81bd61 Mon Sep 17 00:00:00 2001 From: Yichao Zhou Date: Sun, 26 Mar 2017 03:15:52 -0700 Subject: [PATCH 0332/1671] 'listchars': `Whitespace` highlight group #6367 --- runtime/doc/options.txt | 38 ++++----- runtime/doc/syntax.txt | 24 +++--- runtime/doc/vim_diff.txt | 1 + src/nvim/globals.h | 3 +- src/nvim/option.c | 3 +- src/nvim/screen.c | 8 +- src/nvim/syntax.c | 1 + test/functional/ui/cursor_spec.lua | 40 +++++----- test/functional/ui/highlight_spec.lua | 107 +++++++++----------------- 9 files changed, 93 insertions(+), 132 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index eedb7ce34d..bd9bc5820b 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -3171,29 +3171,17 @@ A jump table for the options with a short description can be found at |Q_op|. Think twice when using ":q!" or ":qa!". *'highlight'* *'hl'* -'highlight' 'hl' string (default (as a single string): - "8:SpecialKey,~:EndOfBuffer,z:TermCursor, - Z:TermCursorNC,@:NonText,d:Directory, - e:ErrorMsg,i:IncSearch,l:Search, - m:MoreMsg,M:ModeMsg,n:LineNr, - N:CursorLineNr,r:Question,s:StatusLine, - S:StatusLineNC,c:VertSplit,t:Title, - v:Visual,w:WarningMsg,W:WildMenu, - f:Folded,F:FoldColumn,A:DiffAdd, - C:DiffChange,D:DiffDelete,T:DiffText, - >:SignColumn,B:SpellBad,P:SpellCap, - R:SpellRare,L:SpellLocal,-:Conceal, - +:Pmenu,=:PmenuSel,x:PmenuSbar, - X:PmenuThumb") +'highlight' 'hl' string (default: string of "c:group,..." pairs) global This option can be used to set highlighting mode for various occasions. It is a comma separated list of character pairs. The first character in a pair gives the occasion, the second the mode to use for that occasion. The occasions are: |hl-SpecialKey| 8 Meta and special keys listed with ":map" - |hl-EndOfBuffer| ~ lines after the last line in the buffer + |hl-Whitespace| 0 + |hl-EndOfBuffer| ~ lines after the last line in the buffer |hl-TermCursor| z Cursor in a focused terminal - |hl-TermCursorNC| Z Cursor in an unfocused terminal + |hl-TermCursorNC| Z Cursor in an unfocused terminal |hl-NonText| @ '@' at the end of the window and characters from 'showbreak' |hl-Directory| d directories in CTRL-D listing and other special @@ -3205,11 +3193,11 @@ A jump table for the options with a short description can be found at |Q_op|. |hl-ModeMsg| M Mode (e.g., "-- INSERT --") |hl-LineNr| n line number for ":number" and ":#" commands, and when 'number' or 'relativenumber' option is set. - |hl-CursorLineNr| N like n for when 'cursorline' or 'relativenumber' is + |hl-CursorLineNr| N like n for when 'cursorline' or 'relativenumber' is set. |hl-Question| r |hit-enter| prompt and yes/no questions |hl-StatusLine| s status line of current window |status-line| - |hl-StatusLineNC| S status lines of not-current windows + |hl-StatusLineNC| S status lines of not-current windows |hl-Title| t Titles for output from ":set all", ":autocmd" etc. |hl-VertSplit| c column used to separate vertically split windows |hl-Visual| v Visual mode @@ -3233,6 +3221,15 @@ A jump table for the options with a short description can be found at |Q_op|. |hl-PmenuSbar| x popup menu scrollbar |hl-PmenuThumb| X popup menu scrollbar thumb + |hl-TabLine| * + |hl-TabLineFill| _ + |hl-TabLineSel| # + + |hl-ColorColumn| o + |hl-CursorColumn| ! + |hl-CursorLine| . + |hl-QuickFixLine| q + The display modes are: r reverse (termcap entry "mr" and "me") i italic (termcap entry "ZH" and "ZR") @@ -3917,9 +3914,8 @@ A jump table for the options with a short description can be found at |Q_op|. :set lcs=tab:>-,trail:- :set lcs=tab:>-,eol:<,nbsp:% :set lcs=extends:>,precedes:< -< The "NonText" highlighting will be used for "eol", "extends" and - "precedes". "SpecialKey" for "nbsp", "space", "tab" and "trail". - |hl-NonText| |hl-SpecialKey| +< |hl-NonText| highlighting will be used for "eol", "extends" and + "precedes". |hl-Whitespace| for "nbsp", "space", "tab" and "trail". *'lpl'* *'nolpl'* *'loadplugins'* *'noloadplugins'* 'loadplugins' 'lpl' boolean (default on) diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index b0b4cabd65..f7c2c0e120 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -4899,32 +4899,28 @@ PmenuThumb Popup menu: Thumb of the scrollbar. *hl-Question* Question |hit-enter| prompt and yes/no questions *hl-QuickFixLine* -QuickFixLine The selected |quickfix| item in the quickfix window. - |hl-CursorLine| is combined with this when the cursor is on - the currently selected quickfix item. +QuickFixLine Current |quickfix| item in the quickfix window. Combined with + |hl-CursorLine| when the cursor is there. *hl-Search* Search Last search pattern highlighting (see 'hlsearch'). - Also used for highlighting the current line in the quickfix - window and similar items that need to stand out. + Also used for similar items that need to stand out. *hl-SpecialKey* -SpecialKey Meta and special keys listed with ":map", also for text used - to show unprintable characters in the text, 'listchars'. - Generally: text that is displayed differently from what it - really is. +SpecialKey Unprintable characters: text displayed differently from what + it really is. But not 'listchars' whitespace. |hl-Whitespace| *hl-SpellBad* SpellBad Word that is not recognized by the spellchecker. |spell| - This will be combined with the highlighting used otherwise. + Combined with the highlighting used otherwise. *hl-SpellCap* SpellCap Word that should start with a capital. |spell| - This will be combined with the highlighting used otherwise. + Combined with the highlighting used otherwise. *hl-SpellLocal* SpellLocal Word that is recognized by the spellchecker as one that is used in another region. |spell| - This will be combined with the highlighting used otherwise. + Combined with the highlighting used otherwise. *hl-SpellRare* SpellRare Word that is recognized by the spellchecker as one that is hardly ever used. |spell| - This will be combined with the highlighting used otherwise. + Combined with the highlighting used otherwise. *hl-StatusLine* StatusLine status line of current window *hl-StatusLineNC* @@ -4943,6 +4939,8 @@ Title titles for output from ":set all", ":autocmd" etc. Visual Visual mode selection *hl-WarningMsg* WarningMsg warning messages + *hl-Whitespace* +Whitespace "nbsp", "space", "tab" and "trail" in 'listchars' *hl-WildMenu* WildMenu current match in 'wildmenu' completion diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index bd43028806..c84cea2b55 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -147,6 +147,7 @@ Highlight groups: |hl-Substitute| |hl-TermCursor| |hl-TermCursorNC| + |hl-Whitespace| highlights 'listchars' whitespace ============================================================================== 4. Changed features *nvim-features-changed* diff --git a/src/nvim/globals.h b/src/nvim/globals.h index c15287aa38..3c705d88a5 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -464,6 +464,7 @@ typedef enum { , HLF_CUL // 'cursurline' , HLF_MC // 'colorcolumn' , HLF_QFL // selected quickfix line + , HLF_0 // Whitespace , HLF_COUNT // MUST be the last one } hlf_T; @@ -472,7 +473,7 @@ typedef enum { #define HL_FLAGS { '8', '~', 'z', 'Z', '@', 'd', 'e', 'i', 'l', 'm', 'M', 'n', \ 'N', 'r', 's', 'S', 'c', 't', 'v', 'V', 'w', 'W', 'f', 'F', \ 'A', 'C', 'D', 'T', '-', '>', 'B', 'P', 'R', 'L', '+', '=', \ - 'x', 'X', '*', '#', '_', '!', '.', 'o', 'q' } + 'x', 'X', '*', '#', '_', '!', '.', 'o', 'q', '0' } EXTERN int highlight_attr[HLF_COUNT]; /* Highl. attr for each context. */ EXTERN int highlight_user[9]; /* User[1-9] attributes */ diff --git a/src/nvim/option.c b/src/nvim/option.c index b3b4dc1e0a..695d0edebf 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -245,7 +245,8 @@ typedef struct vimoption { "A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn,-:Conceal," \ "B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel," \ "x:PmenuSbar,X:PmenuThumb,*:TabLine,#:TabLineSel,_:TabLineFill," \ - "!:CursorColumn,.:CursorLine,o:ColorColumn,q:QuickFixLine" + "!:CursorColumn,.:CursorLine,o:ColorColumn,q:QuickFixLine," \ + "0:Whitespace" /* * options[] is initialized here. diff --git a/src/nvim/screen.c b/src/nvim/screen.c index cf460adb82..d9a21aa81f 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -3409,7 +3409,7 @@ win_line ( || (c == ' ' && lcs_space && ptr - line <= trailcol))) { c = (c == ' ') ? lcs_space : lcs_nbsp; n_attr = 1; - extra_attr = hl_attr(HLF_8); + extra_attr = hl_attr(HLF_0); saved_attr2 = char_attr; // save current attr mb_c = c; if (enc_utf8 && (*mb_char2len)(c) > 1) { @@ -3424,7 +3424,7 @@ win_line ( if (trailcol != MAXCOL && ptr > line + trailcol && c == ' ') { c = lcs_trail; n_attr = 1; - extra_attr = hl_attr(HLF_8); + extra_attr = hl_attr(HLF_0); saved_attr2 = char_attr; // save current attr mb_c = c; if (enc_utf8 && (*mb_char2len)(c) > 1) { @@ -3525,8 +3525,8 @@ win_line ( c_extra = lcs_tab2; } n_attr = tab_len + 1; - extra_attr = hl_attr(HLF_8); - saved_attr2 = char_attr; /* save current attr */ + extra_attr = hl_attr(HLF_0); + saved_attr2 = char_attr; // save current attr mb_c = c; if (enc_utf8 && (*mb_char2len)(c) > 1) { mb_utf8 = TRUE; diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index acda25e738..e36b00d770 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -5916,6 +5916,7 @@ static char *highlight_init_both[] = "default link EndOfBuffer NonText", "default link QuickFixLine Search", "default link Substitute Search", + "default link Whitespace NonText", NULL }; diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua index 56f02e4e7f..1e3a9fcb60 100644 --- a/test/functional/ui/cursor_spec.lua +++ b/test/functional/ui/cursor_spec.lua @@ -30,8 +30,8 @@ describe('ui/cursor', function() blinkwait = 700, cell_percentage = 25, cursor_shape = 'vertical', - hl_id = 45, - id_lm = 46, + hl_id = 46, + id_lm = 47, mouse_shape = 0, short_name = 'ci' }, cmdline_normal = { @@ -40,8 +40,8 @@ describe('ui/cursor', function() blinkwait = 700, cell_percentage = 0, cursor_shape = 'block', - hl_id = 45, - id_lm = 46, + hl_id = 46, + id_lm = 47, mouse_shape = 0, short_name = 'c' }, cmdline_replace = { @@ -50,8 +50,8 @@ describe('ui/cursor', function() blinkwait = 700, cell_percentage = 20, cursor_shape = 'horizontal', - hl_id = 45, - id_lm = 46, + hl_id = 46, + id_lm = 47, mouse_shape = 0, short_name = 'cr' }, insert = { @@ -60,8 +60,8 @@ describe('ui/cursor', function() blinkwait = 700, cell_percentage = 25, cursor_shape = 'vertical', - hl_id = 45, - id_lm = 46, + hl_id = 46, + id_lm = 47, mouse_shape = 0, short_name = 'i' }, more = { @@ -76,8 +76,8 @@ describe('ui/cursor', function() blinkwait = 700, cell_percentage = 0, cursor_shape = 'block', - hl_id = 45, - id_lm = 46, + hl_id = 46, + id_lm = 47, mouse_shape = 0, short_name = 'n' }, operator = { @@ -86,8 +86,8 @@ describe('ui/cursor', function() blinkwait = 700, cell_percentage = 50, cursor_shape = 'horizontal', - hl_id = 45, - id_lm = 45, + hl_id = 46, + id_lm = 46, mouse_shape = 0, short_name = 'o' }, replace = { @@ -96,8 +96,8 @@ describe('ui/cursor', function() blinkwait = 700, cell_percentage = 20, cursor_shape = 'horizontal', - hl_id = 45, - id_lm = 46, + hl_id = 46, + id_lm = 47, mouse_shape = 0, short_name = 'r' }, showmatch = { @@ -106,8 +106,8 @@ describe('ui/cursor', function() blinkwait = 175, cell_percentage = 0, cursor_shape = 'block', - hl_id = 45, - id_lm = 45, + hl_id = 46, + id_lm = 46, short_name = 'sm' }, statusline_drag = { mouse_shape = 0, @@ -121,8 +121,8 @@ describe('ui/cursor', function() blinkwait = 700, cell_percentage = 0, cursor_shape = 'block', - hl_id = 45, - id_lm = 46, + hl_id = 46, + id_lm = 47, mouse_shape = 0, short_name = 'v' }, visual_select = { @@ -131,8 +131,8 @@ describe('ui/cursor', function() blinkwait = 700, cell_percentage = 35, cursor_shape = 'vertical', - hl_id = 45, - id_lm = 45, + hl_id = 46, + id_lm = 46, mouse_shape = 0, short_name = 've' }, vsep_drag = { diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index 7a1b8c91e7..05cf3231ea 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -200,57 +200,30 @@ describe('Default highlight groups', function() it('insert mode text', function() feed('i') + screen:try_resize(53, 4) screen:expect([[ ^ | {0:~ }| {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| {1:-- INSERT --} | ]], {[0] = {bold=true, foreground=Screen.colors.Blue}, [1] = {bold = true}}) end) it('end of file markers', function() + screen:try_resize(53, 4) screen:expect([[ ^ | {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| {1:~ }| | ]], {[1] = {bold = true, foreground = Screen.colors.Blue}}) end) it('"wait return" text', function() + screen:try_resize(53, 4) feed(':ls') screen:expect([[ - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| {0:~ }| :ls | 1 %a "[No Name]" line 1 | @@ -259,23 +232,15 @@ describe('Default highlight groups', function() [1] = {bold = true, foreground = Screen.colors.SeaGreen}}) feed('') -- skip the "Press ENTER..." state or tests will hang end) + it('can be cleared and linked to other highlight groups', function() + screen:try_resize(53, 4) execute('highlight clear ModeMsg') feed('i') screen:expect([[ ^ | {0:~ }| {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| -- INSERT -- | ]], {[0] = {bold=true, foreground=Screen.colors.Blue}, [1] = {bold=true}}) @@ -287,37 +252,19 @@ describe('Default highlight groups', function() ^ | {0:~ }| {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| {1:-- INSERT --} | ]], {[0] = {bold=true, foreground=Screen.colors.Blue}, [1] = {foreground = Screen.colors.Red, background = Screen.colors.Green}}) end) + it('can be cleared by assigning NONE', function() + screen:try_resize(53, 4) execute('syn keyword TmpKeyword neovim') execute('hi link TmpKeyword ErrorMsg') insert('neovim') screen:expect([[ {1:neovi^m} | {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| {0:~ }| | ]], { @@ -330,18 +277,34 @@ describe('Default highlight groups', function() neovi^m | {0:~ }| {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| + | + ]], {[0] = {bold=true, foreground=Screen.colors.Blue}}) + end) + + it('Whitespace highlight', function() + screen:try_resize(53, 4) + execute('highlight NonText gui=NONE guifg=#FF0000') + execute('set listchars=space:.,tab:>-,trail:*,eol:¬ list') + insert(' ne \t o\tv im ') + screen:expect([[ + ne{0:.>----.}o{0:>-----}v{0:..}im{0:*^*¬} | {0:~ }| {0:~ }| | - ]], {[0] = {bold=true, foreground=Screen.colors.Blue}}) + ]], { + [0] = {foreground=Screen.colors.Red}, + [1] = {foreground=Screen.colors.Blue}, + }) + execute('highlight Whitespace gui=NONE guifg=#0000FF') + screen:expect([[ + ne{1:.>----.}o{1:>-----}v{1:..}im{1:*^*}{0:¬} | + {0:~ }| + {0:~ }| + :highlight Whitespace gui=NONE guifg=#0000FF | + ]], { + [0] = {foreground=Screen.colors.Red}, + [1] = {foreground=Screen.colors.Blue}, + }) end) end) @@ -510,7 +473,7 @@ describe("'listchars' highlight", function() }, }) execute('highlight clear ModeMsg') - execute('highlight SpecialKey guifg=#FF0000') + execute('highlight Whitespace guifg=#FF0000') execute('set cursorline') execute('set tabstop=8') execute('set listchars=space:.,eol:¬,tab:>-,extends:>,precedes:<,trail:* list') @@ -606,7 +569,7 @@ describe("'listchars' highlight", function() }, }) execute('highlight clear ModeMsg') - execute('highlight SpecialKey guifg=#FF0000') + execute('highlight Whitespace guifg=#FF0000') execute('set cursorline') execute('set tabstop=8') execute('set nowrap') @@ -653,7 +616,7 @@ describe("'listchars' highlight", function() [3] = {foreground=Screen.colors.Green1}, }) execute('highlight clear ModeMsg') - execute('highlight SpecialKey guifg=#FF0000') + execute('highlight Whitespace guifg=#FF0000') execute('highlight Error guifg=#00FF00') execute('set nowrap') feed('ia \t bc \t ') From 644db2165e3437be66d11e46624124c99cfb810e Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 4 Apr 2017 03:58:10 +0300 Subject: [PATCH 0333/1671] ci: Clean up when restarting single includes test --- ci/common/suite.sh | 9 +++++++++ ci/run_lint.sh | 7 ++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/ci/common/suite.sh b/ci/common/suite.sh index 5c79ce2718..44a560c50a 100644 --- a/ci/common/suite.sh +++ b/ci/common/suite.sh @@ -57,13 +57,21 @@ run_test() { run_test_wd() { local timeout="$1" test $# -gt 0 && shift + local cmd="$1" test $# -gt 0 && shift + + local restart_cmd="$1" + : ${restart_cmd:=true} + test $# -gt 0 && shift + local test_name="$1" : ${test_name:=$cmd} test $# -gt 0 && shift + local output_file="$(mktemp)" local status_file="$(mktemp)" + local restarts=5 local prev_tmpsize=-1 while test $restarts -gt 0 ; do @@ -92,6 +100,7 @@ run_test_wd() { # status file not updated, assuming hang kill -KILL $pid echo "Test ${test_name} hang up, restarting" + eval "$restart_cmd" else local new_failed="$(cat "$status_file")" if test "x$new_failed" != "x0" ; then diff --git a/ci/run_lint.sh b/ci/run_lint.sh index 5122ffc2b6..d2807e425e 100755 --- a/ci/run_lint.sh +++ b/ci/run_lint.sh @@ -12,8 +12,13 @@ enter_suite 'lint' set -x +csi_clean() { + rm "${BUILD_DIR}"/bin/test-includes-* + find "${BUILD_DIR}" -name '*test-include*.o' -delete +} + run_test 'top_make clint-full' clint run_test 'top_make testlint' testlint -run_test_wd 5s 'top_make check-single-includes' single-includes +run_test_wd 5s 'top_make check-single-includes' 'csi_clean' single-includes exit_suite From d59378a5cafd3408a1cbecfd8c4de1a96198c81d Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 4 Apr 2017 04:02:54 +0300 Subject: [PATCH 0334/1671] ci: Force make output coloring --- ci/run_lint.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ci/run_lint.sh b/ci/run_lint.sh index d2807e425e..82a8532850 100755 --- a/ci/run_lint.sh +++ b/ci/run_lint.sh @@ -19,6 +19,10 @@ csi_clean() { run_test 'top_make clint-full' clint run_test 'top_make testlint' testlint -run_test_wd 5s 'top_make check-single-includes' 'csi_clean' single-includes +CLICOLOR_FORCE=1 run_test_wd \ + 5s \ + 'top_make check-single-includes' \ + 'csi_clean' \ + single-includes exit_suite From dcad882256af21bb620f580ff8a112691cd149db Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 4 Apr 2017 04:17:40 +0300 Subject: [PATCH 0335/1671] ci: Do not fail csi_clean if there are no files to remove --- ci/run_lint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/run_lint.sh b/ci/run_lint.sh index 82a8532850..39a90102e7 100755 --- a/ci/run_lint.sh +++ b/ci/run_lint.sh @@ -13,7 +13,7 @@ enter_suite 'lint' set -x csi_clean() { - rm "${BUILD_DIR}"/bin/test-includes-* + find "${BUILD_DIR}/bin" -name 'test-includes-*' -delete find "${BUILD_DIR}" -name '*test-include*.o' -delete } From 017f64b9707955e66319e07e6e8f173b29583235 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 4 Apr 2017 04:59:30 +0300 Subject: [PATCH 0336/1671] ci: Also fail if last restart hang up --- ci/common/suite.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ci/common/suite.sh b/ci/common/suite.sh index 44a560c50a..46207754fa 100644 --- a/ci/common/suite.sh +++ b/ci/common/suite.sh @@ -96,11 +96,16 @@ run_test_wd() { break fi done + restarts=$[ restarts - 1 ] if test "$(stat -c "%s" "$status_file")" -eq 0 ; then # status file not updated, assuming hang kill -KILL $pid - echo "Test ${test_name} hang up, restarting" - eval "$restart_cmd" + if test $restarts -eq 0 ; then + fail "${test_name}" E "Test hang up" + else + echo "Test ${test_name} hang up, restarting" + eval "$restart_cmd" + fi else local new_failed="$(cat "$status_file")" if test "x$new_failed" != "x0" ; then @@ -108,7 +113,6 @@ run_test_wd() { fi return 0 fi - restarts=$[ restarts - 1 ] done } From 3ccd59ee8216f3da812c5cf81eb392e6a95b539a Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 3 Apr 2017 16:16:21 +0200 Subject: [PATCH 0337/1671] 'guicursor': enabled=false if 'guicursor' is empty Closes #6429 Closes #6430 --- src/nvim/api/ui.c | 6 ++-- src/nvim/cursor_shape.c | 6 ++-- src/nvim/tui/tui.c | 48 ++++++++++++++++-------------- src/nvim/ui.c | 3 +- src/nvim/ui.h | 2 +- src/nvim/ui_bridge.c | 22 +++++++------- test/functional/ui/cursor_spec.lua | 1 + test/functional/ui/screen.lua | 3 +- 8 files changed, 49 insertions(+), 42 deletions(-) diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index a95be0fabb..de60339e5f 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -300,11 +300,11 @@ static void remote_ui_scroll(UI *ui, int count) push_call(ui, "scroll", args); } -static void remote_ui_cursor_style_set(UI *ui, Dictionary styles) +static void remote_ui_cursor_style_set(UI *ui, bool enabled, Dictionary data) { Array args = ARRAY_DICT_INIT; - Object copy = copy_object(DICTIONARY_OBJ(styles)); - ADD(args, copy); + ADD(args, BOOLEAN_OBJ(enabled)); + ADD(args, copy_object(DICTIONARY_OBJ(data))); push_call(ui, "cursor_style_set", args); } diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index 7ec70bb724..34ee53bf75 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -89,10 +89,8 @@ char_u *parse_shape_opt(int what) int found_ve = false; /* found "ve" flag */ int round; - /* - * First round: check for errors; second round: do it for real. - */ - for (round = 1; round <= 2; ++round) { + // First round: check for errors; second round: do it for real. + for (round = 1; round <= 2; round++) { // Repeat for all comma separated parts. modep = p_guicursor; if (*p_guicursor == NUL) { diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index badc0cd870..f34f5f1bc4 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -84,6 +84,7 @@ typedef struct { } TUIData; static bool volatile got_winch = false; +static bool cursor_style_enabled = false; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "tui/tui.c.generated.h" @@ -151,6 +152,7 @@ static void terminfo_start(UI *ui) // Set 't_Co' from the result of unibilium & fix_terminfo. t_colors = unibi_get_num(data->ut, unibi_max_colors); // Enter alternate screen and clear + // NOTE: Do this *before* changing terminal settings. #6433 unibi_out(ui, unibi_enter_ca_mode); unibi_out(ui, unibi_clear_screen); // Enable bracketed paste @@ -437,11 +439,11 @@ static void tui_cursor_goto(UI *ui, int row, int col) CursorShape tui_cursor_decode_shape(const char *shape_str) { CursorShape shape = 0; - if (strcmp(shape_str, "block") == 0) { + if (strequal(shape_str, "block")) { shape = SHAPE_BLOCK; - } else if (strcmp(shape_str, "vertical") == 0) { + } else if (strequal(shape_str, "vertical")) { shape = SHAPE_VER; - } else if (strcmp(shape_str, "horizontal") == 0) { + } else if (strequal(shape_str, "horizontal")) { shape = SHAPE_HOR; } else { EMSG2(_(e_invarg2), shape_str); @@ -454,40 +456,41 @@ static cursorentry_T decode_cursor_entry(Dictionary args) cursorentry_T r; for (size_t i = 0; i < args.size; i++) { - char *keyStr = args.items[i].key.data; + char *key = args.items[i].key.data; Object value = args.items[i].value; - if (strcmp(keyStr, "cursor_shape") == 0) { + if (strequal(key, "cursor_shape")) { r.shape = tui_cursor_decode_shape(args.items[i].value.data.string.data); - } else if (strcmp(keyStr, "blinkon") == 0) { + } else if (strequal(key, "blinkon")) { r.blinkon = (int)value.data.integer; - } else if (strcmp(keyStr, "blinkoff") == 0) { + } else if (strequal(key, "blinkoff")) { r.blinkoff = (int)value.data.integer; - } else if (strcmp(keyStr, "hl_id") == 0) { + } else if (strequal(key, "hl_id")) { r.id = (int)value.data.integer; } } return r; } -static void tui_cursor_style_set(UI *ui, Dictionary args) +static void tui_cursor_style_set(UI *ui, bool enabled, Dictionary args) { + cursor_style_enabled = enabled; + if (!enabled) { + return; // Do not send cursor style control codes. + } TUIData *data = ui->data; + assert(args.size); + // Keys: as defined by `shape_table`. for (size_t i = 0; i < args.size; i++) { char *mode_name = args.items[i].key.data; const int mode_id = cursor_mode_str2int(mode_name); - - if (mode_id < 0) { - WLOG("Unknown mode '%s'", mode_name); - continue; - } + assert(mode_id >= 0); cursorentry_T r = decode_cursor_entry(args.items[i].value.data.dictionary); r.full_name = mode_name; data->cursor_shapes[mode_id] = r; } - // force redraw MouseMode cursor_mode = tui_mode2cursor(data->showing_mode); tui_set_cursor(ui, cursor_mode); } @@ -528,6 +531,9 @@ static void tui_mouse_off(UI *ui) /// @param mode one of SHAPE_XXX static void tui_set_cursor(UI *ui, MouseMode mode) { + if (!cursor_style_enabled) { + return; + } TUIData *data = ui->data; cursorentry_T c = data->cursor_shapes[mode]; int shape = c.shape; @@ -536,17 +542,15 @@ static void tui_set_cursor(UI *ui, MouseMode mode) # define TMUX_WRAP(seq) (inside_tmux ? "\x1bPtmux;\x1b" seq "\x1b\\" : seq) // Support changing cursor shape on some popular terminals. - const char *term_prog = os_getenv("TERM_PROGRAM"); const char *vte_version = os_getenv("VTE_VERSION"); - if ((term_prog && !strcmp(term_prog, "Konsole")) - || os_getenv("KONSOLE_DBUS_SESSION") != NULL) { + if (os_getenv("KONSOLE_PROFILE_NAME") || os_getenv("KONSOLE_DBUS_SESSION")) { // Konsole uses a proprietary escape code to set the cursor shape // and does not support DECSCUSR. switch (shape) { case SHAPE_BLOCK: shape = 0; break; case SHAPE_VER: shape = 1; break; - case SHAPE_HOR: shape = 3; break; + case SHAPE_HOR: shape = 2; break; default: WLOG("Unknown shape value %d", shape); break; } data->params[0].i = shape; @@ -1102,15 +1106,15 @@ static const char *tui_tk_ti_getstr(const char *name, const char *value, stty_erase = tui_get_stty_erase(); } - if (strcmp(name, "key_backspace") == 0) { + if (strequal(name, "key_backspace")) { ILOG("libtermkey:kbs=%s", value); if (stty_erase != NULL && stty_erase[0] != 0) { return stty_erase; } - } else if (strcmp(name, "key_dc") == 0) { + } else if (strequal(name, "key_dc")) { ILOG("libtermkey:kdch1=%s", value); // Vim: "If and are now the same, redefine ." - if (stty_erase != NULL && value != NULL && strcmp(stty_erase, value) == 0) { + if (stty_erase != NULL && value != NULL && strequal(stty_erase, value)) { return stty_erase[0] == DEL ? CTRL_H_STR : DEL_STR; } } diff --git a/src/nvim/ui.c b/src/nvim/ui.c index babb4efa96..28f71b7ef2 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -381,7 +381,8 @@ void ui_cursor_goto(int new_row, int new_col) void ui_cursor_style_set(void) { Dictionary style = cursor_shape_dict(); - UI_CALL(cursor_style_set, style); + bool enabled = (*p_guicursor != NUL); + UI_CALL(cursor_style_set, enabled, style); api_free_dictionary(style); } diff --git a/src/nvim/ui.h b/src/nvim/ui.h index 0af0c0db65..8ffc5a45a6 100644 --- a/src/nvim/ui.h +++ b/src/nvim/ui.h @@ -22,7 +22,7 @@ struct ui_t { void (*clear)(UI *ui); void (*eol_clear)(UI *ui); void (*cursor_goto)(UI *ui, int row, int col); - void (*cursor_style_set)(UI *ui, Dictionary cursor_shapes); + void (*cursor_style_set)(UI *ui, bool enabled, Dictionary cursor_styles); void (*update_menu)(UI *ui); void (*busy_start)(UI *ui); void (*busy_stop)(UI *ui); diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c index c9bad6b254..9f780663ac 100644 --- a/src/nvim/ui_bridge.c +++ b/src/nvim/ui_bridge.c @@ -60,7 +60,7 @@ UI *ui_bridge_attach(UI *ui, ui_main_fn ui_main, event_scheduler scheduler) rv->bridge.clear = ui_bridge_clear; rv->bridge.eol_clear = ui_bridge_eol_clear; rv->bridge.cursor_goto = ui_bridge_cursor_goto; - rv->bridge.cursor_style_set = ui_bridge_cursor_styleset; + rv->bridge.cursor_style_set = ui_bridge_cursor_style_set; rv->bridge.update_menu = ui_bridge_update_menu; rv->bridge.busy_start = ui_bridge_busy_start; rv->bridge.busy_stop = ui_bridge_busy_stop; @@ -180,19 +180,21 @@ static void ui_bridge_cursor_goto_event(void **argv) ui->cursor_goto(ui, PTR2INT(argv[1]), PTR2INT(argv[2])); } -static void ui_bridge_cursor_styleset(UI *b, Dictionary style) +static void ui_bridge_cursor_style_set(UI *b, bool enabled, Dictionary styles) { - Object copy = copy_object(DICTIONARY_OBJ(style)); - Object *pobj = xmalloc(sizeof(copy)); - *pobj = copy; - UI_CALL(b, cursor_styleset, 2, b, pobj); + bool *enabledp = xmalloc(sizeof(*enabledp)); + Object *stylesp = xmalloc(sizeof(*stylesp)); + *enabledp = enabled; + *stylesp = copy_object(DICTIONARY_OBJ(styles)); + UI_CALL(b, cursor_style_set, 3, b, enabledp, stylesp); } -static void ui_bridge_cursor_styleset_event(void **argv) +static void ui_bridge_cursor_style_set_event(void **argv) { UI *ui = UI(argv[0]); - Object *styles = (Object *)argv[1]; - - ui->cursor_style_set(ui, styles->data.dictionary); + bool *enabled = argv[1]; + Object *styles = argv[2]; + ui->cursor_style_set(ui, *enabled, styles->data.dictionary); + xfree(enabled); api_free_object(*styles); xfree(styles); } diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua index 1e3a9fcb60..c022a5649e 100644 --- a/test/functional/ui/cursor_spec.lua +++ b/test/functional/ui/cursor_spec.lua @@ -144,6 +144,7 @@ describe('ui/cursor', function() } -- Default 'guicursor' published on startup. eq(expected_cursor_style, screen._cursor_style) + eq(true, screen._cursor_style_enabled) eq('normal', screen.mode) -- Event is published ONLY if the cursor style changed. diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index 3f8173c8e2..ff71194dab 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -345,7 +345,8 @@ function Screen:_handle_resize(width, height) } end -function Screen:_handle_cursor_style_set(style) +function Screen:_handle_cursor_style_set(enabled, style) + self._cursor_style_enabled = enabled self._cursor_style = style end From e348e256f3ed93fe462971447ee79033307b2ddf Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 4 Apr 2017 02:37:43 +0200 Subject: [PATCH 0338/1671] 'guicursor': Disable by default for unknown terminals. User can still set guicursor explicitly in init.vim. Closes #5990 Closes #6403 --- runtime/doc/options.txt | 2 +- src/nvim/main.c | 2 +- src/nvim/option.c | 20 +++++++++++--------- src/nvim/os/env.c | 14 ++++++++++++++ test/functional/ui/cursor_spec.lua | 9 ++++++--- 5 files changed, 33 insertions(+), 14 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index bd9bc5820b..2d5551eafe 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2796,7 +2796,7 @@ A jump table for the options with a short description can be found at |Q_op|. -blinkwait175-blinkoff150-blinkon175") global Configures the cursor style for each mode. Works in the GUI and some - terminals. Empty means "non-blinking block cursor in all modes": > + terminals. Unset to disable: > :set guicursor= < With tmux you might need this in ~/.tmux.conf (see terminal-overrides diff --git a/src/nvim/main.c b/src/nvim/main.c index 33e1551351..7ad42d6776 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -283,7 +283,7 @@ int main(int argc, char **argv) cmdline_row = (int)(Rows - p_ch); msg_row = cmdline_row; screenalloc(false); /* allocate screen buffers */ - set_init_2(); + set_init_2(params.headless); TIME_MSG("inits 2"); msg_scroll = TRUE; diff --git a/src/nvim/option.c b/src/nvim/option.c index 695d0edebf..458d80716c 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -939,11 +939,8 @@ void free_all_options(void) #endif -/* - * Initialize the options, part two: After getting Rows and Columns and - * setting 'term'. - */ -void set_init_2(void) +/// Initialize the options, part two: After getting Rows and Columns. +void set_init_2(bool headless) { int idx; @@ -966,8 +963,12 @@ void set_init_2(void) p_window = Rows - 1; } set_number_default("window", Rows - 1); - parse_shape_opt(SHAPE_CURSOR); /* set cursor shapes from 'guicursor' */ - (void)parse_printoptions(); /* parse 'printoptions' default value */ + if (!headless && !os_term_is_nice()) { + set_string_option_direct((char_u *)"guicursor", -1, (char_u *)"", + OPT_GLOBAL, SID_NONE); + } + parse_shape_opt(SHAPE_CURSOR); // set cursor shapes from 'guicursor' + (void)parse_printoptions(); // parse 'printoptions' default value } /* @@ -2842,9 +2843,10 @@ did_set_string_option ( } } - /* 'guicursor' */ - else if (varp == &p_guicursor) + // 'guicursor' + else if (varp == &p_guicursor) { errmsg = parse_shape_opt(SHAPE_CURSOR); + } else if (varp == &p_popt) errmsg = parse_printoptions(); diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 1a97adfa21..839e0d1b51 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -889,3 +889,17 @@ bool os_setenv_append_path(const char *fname) } return false; } + +/// Returns true if the terminal can be assumed to silently ignore unknown +/// control codes. +bool os_term_is_nice(void) +{ +#if defined(__APPLE__) || defined(WIN32) + return true; +#else + const char *vte_version = os_getenv("VTE_VERSION"); + return (vte_version && atoi(vte_version) >= 3900) + || NULL != os_getenv("KONSOLE_PROFILE_NAME") + || NULL != os_getenv("KONSOLE_DBUS_SESSION"); +#endif +} diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua index c022a5649e..3ec3ffd08c 100644 --- a/test/functional/ui/cursor_spec.lua +++ b/test/functional/ui/cursor_spec.lua @@ -3,6 +3,7 @@ local Screen = require('test.functional.ui.screen') local clear, meths = helpers.clear, helpers.meths local eq = helpers.eq local command = helpers.command +local wait = helpers.wait describe('ui/cursor', function() local screen @@ -18,7 +19,7 @@ describe('ui/cursor', function() end) it("'guicursor' is published as a UI event", function() - command('redraw') + wait() screen:expect('', nil, nil, nil, true) -- Tickle the event-loop. local expected_cursor_style = { cmdline_hover = { @@ -149,13 +150,13 @@ describe('ui/cursor', function() -- Event is published ONLY if the cursor style changed. screen._cursor_style = nil - command('redraw') + wait() screen:expect('', nil, nil, nil, true) -- Tickle the event-loop. eq(nil, screen._cursor_style) -- Change the cursor style. meths.set_option('guicursor', 'n-v-c:ver35-blinkwait171-blinkoff172-blinkon173,ve:hor35,o:ver50,i-ci:block,r-cr:hor90,sm:ver42') - command('redraw') + wait() screen:expect('', nil, nil, nil, true) -- Tickle the event-loop. eq('vertical', screen._cursor_style.normal.cursor_shape) eq('horizontal', screen._cursor_style.visual_select.cursor_shape) @@ -171,6 +172,8 @@ describe('ui/cursor', function() meths.set_option('guicursor', '') command('redraw') screen:expect('', nil, nil, nil, true) -- Tickle the event-loop. + -- Empty 'guicursor' sets enabled=false. + eq(false, screen._cursor_style_enabled) for _, m in ipairs({ 'cmdline_insert', 'cmdline_normal', 'cmdline_replace', 'insert', 'showmatch', 'normal', 'replace', 'visual', 'visual_select', }) do From a7f34e199144bfb657c180cdc1413093fd34bdf9 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 4 Apr 2017 03:38:57 +0200 Subject: [PATCH 0339/1671] options: remove 'guiheadroom' --- runtime/doc/gui.txt | 37 ------------------------------------- runtime/doc/options.txt | 15 --------------- runtime/doc/quickref.txt | 1 - runtime/optwin.vim | 2 -- src/nvim/options.lua | 7 ------- 5 files changed, 62 deletions(-) diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt index 12b86e5d73..4b89cd0d35 100644 --- a/runtime/doc/gui.txt +++ b/runtime/doc/gui.txt @@ -83,28 +83,6 @@ Recommended place for your personal GUI initializations: The personal initialization files are searched in the order specified above and only the first one that is found is read. -There are a number of options which only have meaning in the GUI version of -Vim. These are 'guicursor', 'guifont', and 'guioptions'. They are -documented in |options.txt| with all the other options. - -Another way to set the colors for different occasions is with highlight -groups. The "Normal" group is used to set the background and foreground -colors. Example (which looks nice): > - - :highlight Normal guibg=grey90 - -The "guibg" and "guifg" settings override the normal background and -foreground settings. The other settings for the Normal highlight group are -not used. Use the 'guifont' option to set the font. - -Also check out the 'guicursor' option, to set the colors for the cursor in -various modes. - -Vim tries to make the window fit on the screen when it starts up. This avoids -that you can't see part of it. On the X Window System this requires a bit of -guesswork. You can change the height that is used for the window title and a -task bar with the 'guiheadroom' option. - *:winp* *:winpos* *E188* :winp[os] Display current position of the top left corner of the GUI vim @@ -124,21 +102,6 @@ task bar with the 'guiheadroom' option. :win[size] {width} {height} Set the window height to {width} by {height} characters. Obsolete, use ":set lines=11 columns=22". - If you get less lines than expected, check the 'guiheadroom' - option. - -If you are running the X Window System, you can get information about the -window Vim is running in with these commands: > - :!xwininfo -id $WINDOWID - :!xprop -id $WINDOWID - :execute '!xwininfo -id ' . v:windowid - :execute '!xprop -id ' . v:windowid -< - *gui-IME* *iBus* -Input methods for international characters in X that rely on the XIM -framework, most notably iBus, have been known to produce undesirable results -in gVim. These may include an inability to enter spaces, or long delays -between typing a character and it being recognized by the application. ============================================================================== 2. Scrollbars *gui-scrollbars* diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 2d5551eafe..d212e029aa 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2976,18 +2976,6 @@ A jump table for the options with a short description can be found at |Q_op|. If set and valid, 'guifontwide' is used for IME instead of 'guifont'. - *'guiheadroom'* *'ghr'* -'guiheadroom' 'ghr' number (default 50) - global - {only for X11 GUI} - The number of pixels subtracted from the screen height when fitting - the GUI window on the screen. Set this before the GUI is started, - e.g., in your |gvimrc| file. When zero, the whole screen height will - be used by the window. When positive, the specified number of pixel - lines will be left for window decorations and other items on the - screen. Set it to a negative value to allow windows taller than the - screen. - *'guioptions'* *'go'* 'guioptions' 'go' string (default "egmrLT" (MS-Windows)) global @@ -3812,9 +3800,6 @@ A jump table for the options with a short description can be found at |Q_op|. use this command to get the tallest window possible: > :set lines=999 < Minimum value is 2, maximum value is 1000. - If you get less lines than expected, check the 'guiheadroom' option. - When you set this option and Vim is unable to change the physical - number of lines of the display, the display may be messed up. *'linespace'* *'lsp'* 'linespace' 'lsp' number (default 0, 1 for Win32 GUI) diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt index 7de0bba118..a918a4d34a 100644 --- a/runtime/doc/quickref.txt +++ b/runtime/doc/quickref.txt @@ -715,7 +715,6 @@ Short explanation of each option: *option-list* 'guifont' 'gfn' GUI: Name(s) of font(s) to be used 'guifontset' 'gfs' GUI: Names of multi-byte fonts to be used 'guifontwide' 'gfw' list of font names for double-wide characters -'guiheadroom' 'ghr' GUI: pixels room for window decorations 'guioptions' 'go' GUI: Which components and options are used 'guitablabel' 'gtl' GUI: custom label for a tab page 'guitabtooltip' 'gtt' GUI: custom tooltip for a tab page diff --git a/runtime/optwin.vim b/runtime/optwin.vim index 64726937a0..2053b2d860 100644 --- a/runtime/optwin.vim +++ b/runtime/optwin.vim @@ -589,8 +589,6 @@ if has("gui") call append("$", "toolbariconsize\tsize of toolbar icons") call OptionG("tbis", &tbis) endif - call append("$", "guiheadroom\troom (in pixels) left above/below the window") - call append("$", " \tset ghr=" . &ghr) endif if has("browse") call append("$", "browsedir\t\"last\", \"buffer\" or \"current\": which directory used for the file browser") diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 09f016cf5a..4ca63f2efe 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -1025,13 +1025,6 @@ return { redraw={'everything'}, enable_if=false, }, - { - full_name='guiheadroom', abbreviation='ghr', - type='number', scope={'global'}, - vi_def=true, - enable_if=false, - defaults={if_true={vi=50}} - }, { full_name='guioptions', abbreviation='go', type='string', list='flags', scope={'global'}, From 3b558e5d7b8c641896a57c5c4e09d9b8f8535fd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Linse?= Date: Tue, 4 Apr 2017 17:47:23 +0200 Subject: [PATCH 0340/1671] tests: short form `screen:except(func)` expects condition only. #6440 - Use this to properly test cursor shape events. - tests: update screen_basic_spec to use `screen:expect` short form. Clearer than using `screen:wait` directy. --- test/functional/ui/cursor_spec.lua | 69 +++++++++++++----------- test/functional/ui/screen.lua | 32 ++++++----- test/functional/ui/screen_basic_spec.lua | 50 ++++++----------- 3 files changed, 75 insertions(+), 76 deletions(-) diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua index 3ec3ffd08c..02e9422781 100644 --- a/test/functional/ui/cursor_spec.lua +++ b/test/functional/ui/cursor_spec.lua @@ -3,7 +3,6 @@ local Screen = require('test.functional.ui.screen') local clear, meths = helpers.clear, helpers.meths local eq = helpers.eq local command = helpers.command -local wait = helpers.wait describe('ui/cursor', function() local screen @@ -19,8 +18,6 @@ describe('ui/cursor', function() end) it("'guicursor' is published as a UI event", function() - wait() - screen:expect('', nil, nil, nil, true) -- Tickle the event-loop. local expected_cursor_style = { cmdline_hover = { mouse_shape = 0, @@ -142,44 +139,54 @@ describe('ui/cursor', function() vsep_hover = { mouse_shape = 0, short_name = 'vs' } - } - -- Default 'guicursor' published on startup. - eq(expected_cursor_style, screen._cursor_style) - eq(true, screen._cursor_style_enabled) - eq('normal', screen.mode) + } + + screen:expect(function() + -- Default 'guicursor' published on startup. + eq(expected_cursor_style, screen._cursor_style) + eq(true, screen._cursor_style_enabled) + eq('normal', screen.mode) + end) -- Event is published ONLY if the cursor style changed. screen._cursor_style = nil - wait() - screen:expect('', nil, nil, nil, true) -- Tickle the event-loop. - eq(nil, screen._cursor_style) + command("echo 'test'") + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + test | + ]], nil, nil, function() + eq(nil, screen._cursor_style) + end) -- Change the cursor style. meths.set_option('guicursor', 'n-v-c:ver35-blinkwait171-blinkoff172-blinkon173,ve:hor35,o:ver50,i-ci:block,r-cr:hor90,sm:ver42') - wait() - screen:expect('', nil, nil, nil, true) -- Tickle the event-loop. - eq('vertical', screen._cursor_style.normal.cursor_shape) - eq('horizontal', screen._cursor_style.visual_select.cursor_shape) - eq('vertical', screen._cursor_style.operator.cursor_shape) - eq('block', screen._cursor_style.insert.cursor_shape) - eq('vertical', screen._cursor_style.showmatch.cursor_shape) - eq(171, screen._cursor_style.normal.blinkwait) - eq(172, screen._cursor_style.normal.blinkoff) - eq(173, screen._cursor_style.normal.blinkon) + screen:expect(function() + eq('vertical', screen._cursor_style.normal.cursor_shape) + eq('horizontal', screen._cursor_style.visual_select.cursor_shape) + eq('vertical', screen._cursor_style.operator.cursor_shape) + eq('block', screen._cursor_style.insert.cursor_shape) + eq('vertical', screen._cursor_style.showmatch.cursor_shape) + eq(171, screen._cursor_style.normal.blinkwait) + eq(172, screen._cursor_style.normal.blinkoff) + eq(173, screen._cursor_style.normal.blinkon) + end) end) it("empty 'guicursor' sets cursor_shape=block in all modes", function() meths.set_option('guicursor', '') - command('redraw') - screen:expect('', nil, nil, nil, true) -- Tickle the event-loop. - -- Empty 'guicursor' sets enabled=false. - eq(false, screen._cursor_style_enabled) - for _, m in ipairs({ 'cmdline_insert', 'cmdline_normal', 'cmdline_replace', 'insert', - 'showmatch', 'normal', 'replace', 'visual', - 'visual_select', }) do - eq('block', screen._cursor_style[m].cursor_shape) - eq(0, screen._cursor_style[m].blinkon) - end + screen:expect(function() + -- Empty 'guicursor' sets enabled=false. + eq(false, screen._cursor_style_enabled) + for _, m in ipairs({ 'cmdline_insert', 'cmdline_normal', 'cmdline_replace', 'insert', + 'showmatch', 'normal', 'replace', 'visual', + 'visual_select', }) do + eq('block', screen._cursor_style[m].cursor_shape) + eq(0, screen._cursor_style[m].blinkon) + end + end) end) end) diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index ff71194dab..2f2cc85dab 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -181,6 +181,7 @@ end -- expected: Expected screen state (string). Each line represents a screen -- row. Last character of each row (typically "|") is stripped. -- Common indentation is stripped. +-- Used as `condition` if NOT a string; must be the ONLY arg then. -- attr_ids: Expected text attributes. Screen rows are transformed according -- to this table, as follows: each substring S composed of -- characters having the same attributes will be substituted by @@ -191,18 +192,23 @@ end -- any: true: Succeed if `expected` matches ANY screen line(s). -- false (default): `expected` must match screen exactly. function Screen:expect(expected, attr_ids, attr_ignore, condition, any) - -- remove the last line and dedent - expected = dedent(expected:gsub('\n[ ]+$', '')) local expected_rows = {} - for row in expected:gmatch('[^\n]+') do - -- the last character should be the screen delimiter - row = row:sub(1, #row - 1) - table.insert(expected_rows, row) - end - if not any then - assert(self._height == #expected_rows, - "Expected screen state's row count(" .. #expected_rows - .. ') differs from configured height(' .. self._height .. ') of Screen.') + if type(expected) ~= "string" then + assert(not (attr_ids or attr_ignore or condition or any)) + condition = expected + expected = nil + else + -- Remove the last line and dedent. + expected = dedent(expected:gsub('\n[ ]+$', '')) + for row in expected:gmatch('[^\n]+') do + row = row:sub(1, #row - 1) -- Last char must be the screen delimiter. + table.insert(expected_rows, row) + end + if not any then + assert(self._height == #expected_rows, + "Expected screen state's row count(" .. #expected_rows + .. ') differs from configured height(' .. self._height .. ') of Screen.') + end end local ids = attr_ids or self._default_attr_ids local ignore = attr_ignore or self._default_attr_ignore @@ -218,7 +224,9 @@ function Screen:expect(expected, attr_ids, attr_ignore, condition, any) actual_rows[i] = self:_row_repr(self._rows[i], ids, ignore) end - if any then + if expected == nil then + return + elseif any then -- Search for `expected` anywhere in the screen lines. local actual_screen_str = table.concat(actual_rows, '\n') if nil == string.find(actual_screen_str, expected) then diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua index e511234e5e..21953ba294 100644 --- a/test/functional/ui/screen_basic_spec.lua +++ b/test/functional/ui/screen_basic_spec.lua @@ -73,33 +73,29 @@ describe('Screen', function() describe(':suspend', function() it('is forwarded to the UI', function() local function check() - if not screen.suspended then - return 'Screen was not suspended' - end + eq(true, screen.suspended) end execute('suspend') - screen:wait(check) + screen:expect(check) screen.suspended = false feed('') - screen:wait(check) + screen:expect(check) end) end) describe('bell/visual bell', function() it('is forwarded to the UI', function() feed('') - screen:wait(function() - if not screen.bell or screen.visual_bell then - return 'Bell was not sent' - end + screen:expect(function() + eq(true, screen.bell) + eq(false, screen.visual_bell) end) screen.bell = false execute('set visualbell') feed('') - screen:wait(function() - if not screen.visual_bell or screen.bell then - return 'Visual bell was not sent' - end + screen:expect(function() + eq(true, screen.visual_bell) + eq(false, screen.bell) end) end) end) @@ -109,22 +105,16 @@ describe('Screen', function() local expected = 'test-title' execute('set titlestring='..expected) execute('set title') - screen:wait(function() - local actual = screen.title - if actual ~= expected then - return 'Expected title to be "'..expected..'" but was "'..actual..'"' - end + screen:expect(function() + eq(expected, screen.title) end) end) it('has correct default title with unnamed file', function() local expected = '[No Name] - NVIM' execute('set title') - screen:wait(function() - local actual = screen.title - if actual ~= expected then - return 'Expected title to be "'..expected..'" but was "'..actual..'"' - end + screen:expect(function() + eq(expected, screen.title) end) end) @@ -132,11 +122,8 @@ describe('Screen', function() local expected = 'myfile (/mydir) - NVIM' execute('set title') execute('file /mydir/myfile') - screen:wait(function() - local actual = screen.title - if actual ~= expected then - return 'Expected title to be "'..expected..'" but was "'..actual..'"' - end + screen:expect(function() + eq(expected, screen.title) end) end) end) @@ -146,11 +133,8 @@ describe('Screen', function() local expected = 'test-icon' execute('set iconstring='..expected) execute('set icon') - screen:wait(function() - local actual = screen.icon - if actual ~= expected then - return 'Expected title to be "'..expected..'" but was "'..actual..'"' - end + screen:expect(function() + eq(expected, screen.icon) end) end) end) From 8863af28b8f756ab0e2063879adeb2b48871b61d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 6 Apr 2017 00:16:15 +0200 Subject: [PATCH 0341/1671] test: retry() works with asserts; error() not required. --- test/functional/autocmd/termclose_spec.lua | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/test/functional/autocmd/termclose_spec.lua b/test/functional/autocmd/termclose_spec.lua index ecda1bffb7..d4beab22e4 100644 --- a/test/functional/autocmd/termclose_spec.lua +++ b/test/functional/autocmd/termclose_spec.lua @@ -14,17 +14,11 @@ describe('TermClose event', function() nvim('set_option', 'shellcmdflag', 'EXE') end) - local function eq_err(expected, actual) - if expected ~= actual then - error('expected: '..tostring(expected)..', actual: '..tostring(actual)) - end - end - it('triggers when terminal job ends', function() command('autocmd TermClose * let g:test_termclose = 23') command('terminal') command('call jobstop(b:terminal_job_id)') - retry(nil, nil, function() eq_err(23, eval('g:test_termclose')) end) + retry(nil, nil, function() eq(23, eval('g:test_termclose')) end) end) it('reports the correct ', function() @@ -35,12 +29,12 @@ describe('TermClose event', function() eq(2, eval('bufnr("%")')) command('terminal') - retry(nil, nil, function() eq_err(3, eval('bufnr("%")')) end) + retry(nil, nil, function() eq(3, eval('bufnr("%")')) end) command('buffer 1') - retry(nil, nil, function() eq_err(1, eval('bufnr("%")')) end) + retry(nil, nil, function() eq(1, eval('bufnr("%")')) end) command('3bdelete!') - retry(nil, nil, function() eq_err('3', eval('g:abuf')) end) + retry(nil, nil, function() eq('3', eval('g:abuf')) end) end) end) From 210b013ce75b5ea8a897071e32decc1e1f88189e Mon Sep 17 00:00:00 2001 From: James McCoy Date: Wed, 5 Apr 2017 20:10:20 -0400 Subject: [PATCH 0342/1671] vim-patch: Update regex for included_patches array (#6449) 28dafe3ff const-ified the array without updating the regex. [ci skip] --- scripts/vim-patch.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh index d02eef6b44..c8ae36cc3b 100755 --- a/scripts/vim-patch.sh +++ b/scripts/vim-patch.sh @@ -321,7 +321,7 @@ list_vim_patches() { if [[ -n "${vim_tag}" ]]; then local patch_number="${vim_tag:5}" # Remove prefix like "v7.4." # Tagged Vim patch, check version.c: - is_missing="$(sed -n '/static int included_patches/,/}/p' "${NVIM_SOURCE_DIR}/src/nvim/version.c" | + is_missing="$(sed -n '/static const int included_patches/,/}/p' "${NVIM_SOURCE_DIR}/src/nvim/version.c" | grep -x -e "[[:space:]]*//[[:space:]]${patch_number} NA.*" -e "[[:space:]]*${patch_number}," >/dev/null && echo "false" || echo "true")" vim_commit="${vim_tag#v}" if (cd "${VIM_SOURCE_DIR}" && git --no-pager show --color=never --name-only "v${vim_commit}" 2>/dev/null) | grep -q ^runtime; then From 271df03fa4b507d8ec608abd530616cb4b57616e Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 4 Apr 2017 18:14:09 +0300 Subject: [PATCH 0343/1671] unittests: Force GC, fix GC failures in typval_spec --- test/unit/eval/typval_spec.lua | 37 +++++++++++++++++++++++++--------- test/unit/helpers.lua | 3 ++- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 258b5c4c1f..c477683038 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -460,6 +460,10 @@ describe('typval.c', function() eq(empty_list, typvalt2lua(l_tv)) eq({true, true, true}, {lws[1].lw_item == nil, lws[2].lw_item == nil, lws[3].lw_item == nil}) + lib.tv_list_watch_remove(l, lws[1]) + lib.tv_list_watch_remove(l, lws[2]) + lib.tv_list_watch_remove(l, lws[3]) + alloc_log:check({}) end) end) @@ -1090,9 +1094,13 @@ describe('typval.c', function() local function list_join(l, sep, ret) local ga = ga_alloc() eq(ret or OK, lib.tv_list_join(ga, l, sep)) - if ga.ga_data == nil then return '' - else return ffi.string(ga.ga_data) + local ret = '' + if ga.ga_data ~= nil then + ret = ffi.string(ga.ga_data) end + -- For some reason this is not working well in GC + lib.ga_clear(ffi.gc(ga, nil)) + return ret end itp('works', function() local l @@ -2659,7 +2667,8 @@ describe('typval.c', function() {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 0}, {lib.VAR_UNKNOWN, nil, 'E685: Internal error: tv_get_number(UNKNOWN)', 0}, }) do - local tv = typvalt(v[1], v[2]) + -- Using to_cstr, cannot free with tv_clear + local tv = ffi.gc(typvalt(v[1], v[2]), nil) alloc_log:check({}) local emsg = v[3] local ret = v[4] @@ -2687,7 +2696,8 @@ describe('typval.c', function() {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 0}, {lib.VAR_UNKNOWN, nil, 'E685: Internal error: tv_get_number(UNKNOWN)', 0}, }) do - local tv = typvalt(v[1], v[2]) + -- Using to_cstr, cannot free with tv_clear + local tv = ffi.gc(typvalt(v[1], v[2]), nil) alloc_log:check({}) local emsg = v[3] local ret = {v[4], not not emsg} @@ -2721,7 +2731,8 @@ describe('typval.c', function() {lib.VAR_UNKNOWN, nil, 'E685: Internal error: tv_get_number(UNKNOWN)', -1}, }) do lib.curwin.w_cursor.lnum = 46 - local tv = typvalt(v[1], v[2]) + -- Using to_cstr, cannot free with tv_clear + local tv = ffi.gc(typvalt(v[1], v[2]), nil) alloc_log:check({}) local emsg = v[3] local ret = v[4] @@ -2749,7 +2760,8 @@ describe('typval.c', function() {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, 'E907: Using a special value as a Float', 0}, {lib.VAR_UNKNOWN, nil, 'E685: Internal error: tv_get_float(UNKNOWN)', 0}, }) do - local tv = typvalt(v[1], v[2]) + -- Using to_cstr, cannot free with tv_clear + local tv = ffi.gc(typvalt(v[1], v[2]), nil) alloc_log:check({}) local emsg = v[3] local ret = v[4] @@ -2780,7 +2792,9 @@ describe('typval.c', function() {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 'false'}, {lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', ''}, }) do - local tv = typvalt(v[1], v[2]) + -- Using to_cstr in place of Neovim allocated string, cannot + -- tv_clear() that. + local tv = ffi.gc(typvalt(v[1], v[2]), nil) alloc_log:check({}) local emsg = v[3] local ret = v[4] @@ -2821,7 +2835,8 @@ describe('typval.c', function() {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 'false'}, {lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', nil}, }) do - local tv = typvalt(v[1], v[2]) + -- Using to_cstr, cannot free with tv_clear + local tv = ffi.gc(typvalt(v[1], v[2]), nil) alloc_log:check({}) local emsg = v[3] local ret = v[4] @@ -2861,7 +2876,8 @@ describe('typval.c', function() {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 'false'}, {lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', ''}, }) do - local tv = typvalt(v[1], v[2]) + -- Using to_cstr, cannot free with tv_clear + local tv = ffi.gc(typvalt(v[1], v[2]), nil) alloc_log:check({}) local emsg = v[3] local ret = v[4] @@ -2902,7 +2918,8 @@ describe('typval.c', function() {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 'false'}, {lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', nil}, }) do - local tv = typvalt(v[1], v[2]) + -- Using to_cstr, cannot free with tv_clear + local tv = ffi.gc(typvalt(v[1], v[2]), nil) alloc_log:check({}) local emsg = v[3] local ret = v[4] diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 74f214a231..6d0de5c651 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -632,8 +632,9 @@ local function itp_child(wr, func) collectgarbage('stop') child_sethook(wr) local err, emsg = pcall(func) - debug.sethook() collectgarbage('restart') + collectgarbage() + debug.sethook() emsg = tostring(emsg) sc.write(wr, trace_end_msg) if not err then From c501d7c432693705482f76a384a98b4b4c0ef1a9 Mon Sep 17 00:00:00 2001 From: Carlo Abelli Date: Thu, 6 Apr 2017 08:48:42 -0400 Subject: [PATCH 0344/1671] refactor/single-include: diff.h (#6443) --- src/nvim/CMakeLists.txt | 1 - src/nvim/diff.h | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 5a5ebc4415..d0f75a2b5b 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -425,7 +425,6 @@ endfunction() set(NO_SINGLE_CHECK_HEADERS cursor_shape.h - diff.h digraph.h ex_cmds.h ex_getln.h diff --git a/src/nvim/diff.h b/src/nvim/diff.h index f6cef1cafd..3624ce29bb 100644 --- a/src/nvim/diff.h +++ b/src/nvim/diff.h @@ -1,6 +1,9 @@ #ifndef NVIM_DIFF_H #define NVIM_DIFF_H +#include "nvim/pos.h" +#include "nvim/ex_cmds_defs.h" + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "diff.h.generated.h" #endif From 6a6bbbc6d8fa79a0c14fb913baa3ba2d7046419c Mon Sep 17 00:00:00 2001 From: James McCoy Date: Thu, 6 Apr 2017 08:54:30 -0400 Subject: [PATCH 0345/1671] vim-patch:7.4.2281 Problem: Timer test fails sometimes. Solution: Reduce minimum time by 1 msec. https://github.com/vim/vim/commit/0426bae2abede764d0dd366a28663d1c6e6ab0fe --- src/nvim/testdir/test_timers.vim | 6 +++--- src/nvim/version.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index db10f351ae..16c70b166b 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -20,7 +20,7 @@ func Test_oneshot() let slept = WaitFor('g:val == 1') call assert_equal(1, g:val) if has('reltime') - call assert_inrange(50, 100, slept) + call assert_inrange(49, 100, slept) else call assert_inrange(20, 100, slept) endif @@ -32,7 +32,7 @@ func Test_repeat_three() let slept = WaitFor('g:val == 3') call assert_equal(3, g:val) if has('reltime') - call assert_inrange(150, 250, slept) + call assert_inrange(149, 250, slept) else call assert_inrange(80, 200, slept) endif @@ -57,7 +57,7 @@ func Test_with_partial_callback() let slept = WaitFor('g:val == 1') call assert_equal(1, g:val) if has('reltime') - call assert_inrange(50, 130, slept) + call assert_inrange(49, 130, slept) else call assert_inrange(20, 100, slept) endif diff --git a/src/nvim/version.c b/src/nvim/version.c index ca520c7af5..3944551cb4 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -160,7 +160,7 @@ static const int included_patches[] = { 2284, 2283, // 2282 NA - // 2281 NA + 2281, 2280, 2279, // 2278 NA From 0f99645b8faf3e5970e46c185c0cbbd7a9cfe318 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Thu, 6 Apr 2017 08:55:51 -0400 Subject: [PATCH 0346/1671] vim-patch:7.4.2304 Problem: In a timer callback the timer itself can't be found or stopped. (Thinca) Solution: Do not remove the timer from the list, remember whether it was freed. https://github.com/vim/vim/commit/417ccd7138d4d230d328de8b0d3892dd82ff1bee --- src/nvim/testdir/test_timers.vim | 15 +++++++++++++++ src/nvim/version.c | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index 16c70b166b..6a8b09c898 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -125,4 +125,19 @@ func Test_paused() endif endfunc +func StopMyself(timer) + let g:called += 1 + if g:called == 2 + call timer_stop(a:timer) + endif +endfunc + +func Test_delete_myself() + let g:called = 0 + let t = timer_start(10, 'StopMyself', {'repeat': -1}) + call WaitFor('g:called == 2') + call assert_equal(2, g:called) + call assert_equal([], timer_info(t)) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/version.c b/src/nvim/version.c index 3944551cb4..beb23090ec 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -137,7 +137,7 @@ static const int included_patches[] = { 2307, 2306, 2305, - // 2304 NA + 2304, 2303, // 2302 NA // 2301 NA From 9edbeec07716cff5607202dbd20b81917416030f Mon Sep 17 00:00:00 2001 From: James McCoy Date: Thu, 6 Apr 2017 08:56:53 -0400 Subject: [PATCH 0347/1671] vim-patch:7.4.2332 Problem: Crash when stop_timer() is called in a callback of a callback. Vim hangs when the timer callback uses too much time. Solution: Set tr_id to -1 when a timer is to be deleted. Don't keep calling callbacks forever. (Ozaki Kiichi) https://github.com/vim/vim/commit/75537a93e985ef32e6c267b06ce93629855dd983 --- src/nvim/testdir/test_timers.vim | 30 ++++++++++++++++++++++++++++++ src/nvim/version.c | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index 6a8b09c898..2a768585ce 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -140,4 +140,34 @@ func Test_delete_myself() call assert_equal([], timer_info(t)) endfunc +func StopTimer1(timer) + let g:timer2 = timer_start(10, 'StopTimer2') + " avoid maxfuncdepth error + call timer_pause(g:timer1, 1) + sleep 40m +endfunc + +func StopTimer2(timer) + call timer_stop(g:timer1) +endfunc + +func Test_stop_in_callback() + let g:timer1 = timer_start(10, 'StopTimer1') + sleep 40m +endfunc + +func StopTimerAll(timer) + call timer_stopall() +endfunc + +func Test_stop_all_in_callback() + let g:timer1 = timer_start(10, 'StopTimerAll') + let info = timer_info() + call assert_equal(1, len(info)) + sleep 40m + let info = timer_info() + call assert_equal(0, len(info)) +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/version.c b/src/nvim/version.c index beb23090ec..d16eab7201 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -109,7 +109,7 @@ static const int included_patches[] = { 2335, 2334, 2333, - // 2332 NA + 2332, 2331, 2330, 2329, From 071f2da66bacfd5a2d4ab87bda275d3848ddcc0e Mon Sep 17 00:00:00 2001 From: James McCoy Date: Thu, 6 Apr 2017 08:58:18 -0400 Subject: [PATCH 0348/1671] vim-patch:7.4.2359 Problem: Memory leak in timer_start(). Solution: Check the right field to be NULL. https://github.com/vim/vim/commit/26fe0d56912e42c2b16a61b2480e19ba569aee98 --- src/nvim/testdir/test_timers.vim | 8 ++++---- src/nvim/version.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index 2a768585ce..fd2b50b495 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -48,12 +48,12 @@ endfunc func Test_with_partial_callback() let g:val = 0 - let s:meow = {} - function s:meow.bite(...) - let g:val += 1 + let meow = {'one': 1} + function meow.bite(...) + let g:val += self.one endfunction - call timer_start(50, s:meow.bite) + call timer_start(50, meow.bite) let slept = WaitFor('g:val == 1') call assert_equal(1, g:val) if has('reltime') diff --git a/src/nvim/version.c b/src/nvim/version.c index d16eab7201..9a5d7ce169 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -82,7 +82,7 @@ static const int included_patches[] = { 2362, // 2361 NA // 2360, - // 2359 NA + 2359, // 2358 NA 2357, // 2356, From 30e1cda8acb7bd8120348d1812cfd9ecd8be8528 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 6 Apr 2017 21:35:03 +0200 Subject: [PATCH 0349/1671] completion: fix segfault with ignorecase+infercase (#6452) Helped-by: Matthew Malcomson Closes #6451 --- src/nvim/edit.c | 4 ++-- test/functional/viml/completion_spec.lua | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/nvim/edit.c b/src/nvim/edit.c index da09aed3dc..b35504908e 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -2109,7 +2109,7 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int xfree(wca); - return ins_compl_add(IObuff, len, icase, fname, NULL, true, dir, flags, + return ins_compl_add(IObuff, len, icase, fname, NULL, false, dir, flags, false); } return ins_compl_add(str, len, icase, fname, NULL, false, dir, flags, false); @@ -2146,7 +2146,7 @@ static int ins_compl_add(char_u *const str, int len, os_breakcheck(); #define FREE_CPTEXT(cptext, cptext_allocated) \ do { \ - if (cptext_allocated) { \ + if (cptext != NULL && cptext_allocated) { \ for (size_t i = 0; i < CPT_COUNT; i++) { \ xfree(cptext[i]); \ } \ diff --git a/test/functional/viml/completion_spec.lua b/test/functional/viml/completion_spec.lua index 3c09d71eb7..5a37cb8f43 100644 --- a/test/functional/viml/completion_spec.lua +++ b/test/functional/viml/completion_spec.lua @@ -850,6 +850,22 @@ describe('completion', function() ]]) end) end) + + it("'ignorecase' 'infercase' CTRL-X CTRL-N #6451", function() + execute('set ignorecase infercase') + execute('edit BACKERS.md') + feed('oX') + screen:expect([[ + # Bountysource Backers | + Xnull^ | + {2:Xnull }{6: } | + {1:Xoxomoon }{6: }ryone who backed our [Bountysource fundraise| + {1:Xu }{6: }ountysource.com/teams/neovim/fundraiser)! | + {1:Xpayn }{2: } | + {1:Xinity }{2: }d URL in BACKERS.md. | + {3:-- Keyword Local completion (^N^P) }{4:match 1 of 7} | + ]]) + end) end) describe('External completion popupmenu', function() From c1416e066534363e35b8895c30d7d7ce90e8b509 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 4 Apr 2017 20:15:30 +0300 Subject: [PATCH 0350/1671] ci: Really continue tests on failure, print global summary --- .travis.yml | 5 ---- ci/before_cache.sh | 5 +++- ci/common/suite.sh | 58 ++++++++++++++++++++++++++++--------------- ci/common/test.sh | 62 +++++++++++++++++++++++----------------------- ci/run_lint.sh | 2 +- ci/run_tests.sh | 6 +---- 6 files changed, 75 insertions(+), 63 deletions(-) diff --git a/.travis.yml b/.travis.yml index 14899a1289..b8c4c0172f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,11 +43,6 @@ env: # If this file exists, we know that the cache contains compiled # dependencies and we can use it. - CACHE_MARKER="$HOME/.cache/nvim-deps/.travis_cache_marker" - # Test success marker. If this file exists, we know that all tests - # were successful. Required because we only want to update the cache - # if the tests were successful, but don't have this information - # available in before_cache (which is run before after_success). - - SUCCESS_MARKER="$BUILD_DIR/.tests_successful" # default target name for functional tests - FUNCTIONALTEST=functionaltest - CI_TARGET=tests diff --git a/ci/before_cache.sh b/ci/before_cache.sh index dd1fcf2bf7..3d7cc0ec5a 100755 --- a/ci/before_cache.sh +++ b/ci/before_cache.sh @@ -3,12 +3,15 @@ set -e set -o pipefail +CI_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${CI_DIR}/common/suite.sh" + # Don't cache pip's log and selfcheck. rm -rf "${HOME}/.cache/pip/log" rm -f "${HOME}/.cache/pip/selfcheck.json" # Update the third-party dependency cache only if the build was successful. -if [[ -f "${SUCCESS_MARKER}" ]]; then +if ended_successfully; then rm -rf "${HOME}/.cache/nvim-deps" mv "${DEPS_BUILD_DIR}" "${HOME}/.cache/nvim-deps" touch "${CACHE_MARKER}" diff --git a/ci/common/suite.sh b/ci/common/suite.sh index 46207754fa..e22252c985 100644 --- a/ci/common/suite.sh +++ b/ci/common/suite.sh @@ -2,11 +2,18 @@ NL="$(printf '\nE')" NL="${NL%E}" -FAILED=0 - FAIL_SUMMARY="" +# Test success marker. If END_MARKER file exists, we know that all tests +# finished. If FAIL_SUMMARY_FILE exists we know that some tests failed, this +# file will contain information about failed tests. Build is considered +# successful if tests ended without any of them failing. +END_MARKER="$BUILD_DIR/.tests_finished" +FAIL_SUMMARY_FILE="$BUILD_DIR/.test_errors" + enter_suite() { + FAILED=0 + rm -f "${END_MARKER}" local suite_name="$1" export NVIM_TEST_CURRENT_SUITE="${NVIM_TEST_CURRENT_SUITE}/$suite_name" } @@ -19,17 +26,16 @@ exit_suite() { export NVIM_TEST_CURRENT_SUITE="${NVIM_TEST_CURRENT_SUITE%/*}" if test "x$1" != "x--continue" ; then exit $FAILED + else + local saved_failed=$FAILED + FAILED=0 + return $saved_failed fi } fail() { - local allow_failure= - if test "x$1" = "x--allow-failure" ; then - shift - allow_failure=A - fi local test_name="$1" - local fail_char="$allow_failure$2" + local fail_char="$2" local message="$3" : ${fail_char:=F} @@ -37,10 +43,9 @@ fail() { local full_msg="$fail_char $NVIM_TEST_CURRENT_SUITE|$test_name :: $message" FAIL_SUMMARY="${FAIL_SUMMARY}${NL}${full_msg}" + echo "${full_msg}" >> "${FAIL_SUMMARY_FILE}" echo "Failed: $full_msg" - if test "x$allow_failure" = "x" ; then - FAILED=1 - fi + FAILED=1 } run_test() { @@ -77,14 +82,13 @@ run_test_wd() { while test $restarts -gt 0 ; do : > "${status_file}" ( - FAILED=0 - if ! ( - set -o pipefail - eval "$cmd" 2>&1 | tee -a "$output_file" - ) ; then - fail "${test_name}" "$@" + set -o pipefail + ret=0 + if ! eval "$cmd" 2>&1 | tee -a "$output_file" ; then + ret=1 fi - echo "$FAILED" > "$status_file" + echo "$ret" > "$status_file" + exit $ret ) & local pid=$! while test "$(stat -c "%s" "$status_file")" -eq 0 ; do @@ -116,6 +120,20 @@ run_test_wd() { done } -succeeded() { - return $FAILED +ended_successfully() { + if [[ -f "${FAIL_SUMMARY_FILE}" ]]; then + echo 'Test failed, complete summary:' + cat "${FAIL_SUMMARY_FILE}" + return 1 + fi + if ! [[ -f "${END_MARKER}" ]] ; then + echo 'ended_successfully called before end marker was touched' + return 1 + fi + return 0 +} + +end_tests() { + touch "${END_MARKER}" + ended_successfully } diff --git a/ci/common/test.sh b/ci/common/test.sh index 4936992cfd..d911d9bc18 100644 --- a/ci/common/test.sh +++ b/ci/common/test.sh @@ -1,4 +1,5 @@ source "${CI_DIR}/common/build.sh" +source "${CI_DIR}/common/suite.sh" print_core() { local app="$1" @@ -40,10 +41,9 @@ check_core_dumps() { print_core "$app" "$core" fi done - if test "$app" = quiet ; then - return 0 + if test "$app" != quiet ; then + fail 'cores' E 'Core dumps found' fi - exit 1 } check_logs() { @@ -62,8 +62,7 @@ check_logs() { err=1 done if [[ -n "${err}" ]]; then - echo "Runtime errors detected." - exit 1 + fail 'logs' E 'Runtime errors detected.' fi } @@ -75,50 +74,53 @@ asan_check() { check_logs "${1}" "*san.*" } -run_unittests() { +run_unittests() {( + enter_suite unittests ulimit -c unlimited if ! build_make unittest ; then - check_core_dumps "$(which luajit)" - exit 1 + fail 'unittests' F 'Unit tests failed' fi check_core_dumps "$(which luajit)" -} + exit_suite +)} -run_functionaltests() { +run_functionaltests() {( + enter_suite functionaltests ulimit -c unlimited if ! build_make ${FUNCTIONALTEST}; then - asan_check "${LOG_DIR}" - valgrind_check "${LOG_DIR}" - check_core_dumps - exit 1 + fail 'functionaltests' F 'Functional tests failed' fi asan_check "${LOG_DIR}" valgrind_check "${LOG_DIR}" check_core_dumps -} + exit_suite +)} -run_oldtests() { +run_oldtests() {( + enter_suite oldtests ulimit -c unlimited if ! make -C "${TRAVIS_BUILD_DIR}/src/nvim/testdir"; then reset - asan_check "${LOG_DIR}" - valgrind_check "${LOG_DIR}" - check_core_dumps - exit 1 + fail 'oldtests' F 'Legacy tests failed' fi asan_check "${LOG_DIR}" valgrind_check "${LOG_DIR}" check_core_dumps -} + exit_suite +)} -install_nvim() { - build_make install +install_nvim() {( + enter_suite 'install_nvim' + if ! build_make install ; then + fail 'install' E 'make install failed' + exit_suite + fi "${INSTALL_PREFIX}/bin/nvim" --version "${INSTALL_PREFIX}/bin/nvim" -u NONE -e -c ':help' -c ':qall' || { echo "Running ':help' in the installed nvim failed." echo "Maybe the helptags have not been generated properly." - exit 1 + fail 'help' F 'Failed running :help' } local genvimsynf=syntax/vim/generated.vim @@ -127,24 +129,22 @@ install_nvim() { cd runtime ; git ls-files | grep -e '.vim$' -e '.ps$' -e '.dict$' -e '.py$' -e '.tutor$' ) ; do if ! test -e "${INSTALL_PREFIX}/share/nvim/runtime/$file" ; then - echo "It appears that $file is not installed." - exit 1 + fail 'runtime-install' F "It appears that $file is not installed." fi done # Check that generated syntax file has function names, #5060. local gpat='syn keyword vimFuncName .*eval' if ! grep -q "$gpat" "${INSTALL_PREFIX}/share/nvim/runtime/$genvimsynf"; then - echo "It appears that $genvimsynf does not contain $gpat." - exit 1 + fail 'funcnames' F "It appears that $genvimsynf does not contain $gpat." fi for file in $( cd runtime ; git ls-files | grep -e '.awk$' -e '.sh$' -e '.bat$' ) ; do if ! test -x "${INSTALL_PREFIX}/share/nvim/runtime/$file" ; then - echo "It appears that $file is not installed or is not executable." - exit 1 + fail 'not-exe' F "It appears that $file is not installed or is not executable." fi done -} + exit_suite +)} diff --git a/ci/run_lint.sh b/ci/run_lint.sh index 39a90102e7..2c30615725 100755 --- a/ci/run_lint.sh +++ b/ci/run_lint.sh @@ -25,4 +25,4 @@ CLICOLOR_FORCE=1 run_test_wd \ 'csi_clean' \ single-includes -exit_suite +end_tests diff --git a/ci/run_tests.sh b/ci/run_tests.sh index 92cb5a9fd8..4abc9eea9f 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -27,8 +27,4 @@ run_test run_oldtests run_test install_nvim -if succeeded ; then - touch "${SUCCESS_MARKER}" -fi - -exit_suite +end_tests From 654dd15bb8e82538942b1933b7f69c63d51b6608 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 6 Apr 2017 07:21:00 +0300 Subject: [PATCH 0351/1671] unittests: Fix testlint failure --- test/unit/eval/typval_spec.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index c477683038..4abd51d46d 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1091,9 +1091,9 @@ describe('typval.c', function() end) end) describe('join()', function() - local function list_join(l, sep, ret) + local function list_join(l, sep, join_ret) local ga = ga_alloc() - eq(ret or OK, lib.tv_list_join(ga, l, sep)) + eq(join_ret or OK, lib.tv_list_join(ga, l, sep)) local ret = '' if ga.ga_data ~= nil then ret = ffi.string(ga.ga_data) From 1813076c448f1039db33e08e83b7f1f2011db0ee Mon Sep 17 00:00:00 2001 From: Nicolas Hillegeer Date: Fri, 7 Apr 2017 12:29:17 +0200 Subject: [PATCH 0352/1671] eval: delimit string with NUL byte (#6467) A recent refactor left cpy without a NUL terminator, simplify the code instead of patching over it. Instead of plain memcpy, it'd be better to employ harder to misuse string functions made for this purpose like xstrlcpy(), but path_tail() takes char_u arguments and returns them, leading to a lot of ugly casting. Fixes #6431. --- src/nvim/eval.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7ab07fe6a2..1636b490d5 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -13416,14 +13416,12 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) q = (char *)path_tail((char_u *)p); } if (q > p && !path_is_absolute_path((const char_u *)buf)) { - // Symlink is relative to directory of argument. + // Symlink is relative to directory of argument. Replace the + // symlink with the resolved name in the same directory. const size_t p_len = strlen(p); const size_t buf_len = strlen(buf); - cpy = xmalloc(p_len + buf_len + 1); - memcpy(cpy, p, p_len); - memcpy(path_tail((char_u *)cpy), buf, buf_len + 1); - xfree(p); - p = cpy; + p = xrealloc(p, p_len + buf_len + 1); + memcpy(path_tail((char_u *)p), buf, buf_len + 1); } else { xfree(p); p = xstrdup(buf); From 13352c00f1909d9296c5f276a3735f5e6f231b39 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 7 Apr 2017 19:46:33 +0200 Subject: [PATCH 0353/1671] win: os_get_hostname() #5416 (#6413) --- src/nvim/mbyte.c | 49 +++++++++++++------------- src/nvim/os/env.c | 24 ++++++++++--- src/nvim/os/fs.c | 2 +- test/functional/eval/hostname_spec.lua | 17 +++++++++ 4 files changed, 63 insertions(+), 29 deletions(-) create mode 100644 test/functional/eval/hostname_spec.lua diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index d96848754c..460528b85f 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -1304,6 +1304,7 @@ static int utf_strnicmp(const char_u *s1, const char_u *s2, size_t n1, # define CP_UTF8 65001 /* magic number from winnls.h */ #endif +/// Reassigns `strw` to a new, allocated pointer to a UTF16 string. int utf8_to_utf16(const char *str, WCHAR **strw) FUNC_ATTR_NONNULL_ALL { @@ -1345,40 +1346,40 @@ int utf8_to_utf16(const char *str, WCHAR **strw) return 0; } +/// Reassigns `str` to a new, allocated pointer to a UTF8 string. int utf16_to_utf8(const WCHAR *strw, char **str) FUNC_ATTR_NONNULL_ALL { // Compute the space required to store the string as UTF-8. - ssize_t utf8_len = WideCharToMultiByte(CP_UTF8, - 0, - strw, - -1, - NULL, - 0, - NULL, - NULL); + DWORD utf8_len = WideCharToMultiByte(CP_UTF8, + 0, + strw, + -1, + NULL, + 0, + NULL, + NULL); if (utf8_len == 0) { return GetLastError(); } - ssize_t buf_sz = utf8_len * sizeof(char); - char *buf = xmalloc(buf_sz); - char *pos = buf; + *str = xmalloc(utf8_len); - // Convert string to UTF-8. - int r = WideCharToMultiByte(CP_UTF8, - 0, - strw, - -1, - pos, - utf8_len, - NULL, - NULL); - assert(r == utf8_len); - if (r != utf8_len) { - EMSG2("WideCharToMultiByte failed: %d", r); + // Convert to UTF-8. + utf8_len = WideCharToMultiByte(CP_UTF8, + 0, + strw, + -1, + *str, + utf8_len, + NULL, + NULL); + if (utf8_len == 0) { + free(*str); + *str = NULL; + return GetLastError(); } - *str = pos; + (*str)[utf8_len] = '\0'; return 0; } diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 839e0d1b51..12c2da6152 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -118,7 +118,6 @@ char *os_getenvname_at_index(size_t index) return name; } - /// Get the process ID of the Neovim process. /// /// @return the process ID. @@ -145,10 +144,27 @@ void os_get_hostname(char *hostname, size_t size) } else { xstrlcpy(hostname, vutsname.nodename, size); } +#elif defined(WIN32) + WCHAR host_utf16[MAX_COMPUTERNAME_LENGTH + 1]; + DWORD host_wsize = sizeof(host_utf16) / sizeof(host_utf16[0]); + if (GetComputerNameW(host_utf16, &host_wsize) == 0) { + *hostname = '\0'; + DWORD err = GetLastError(); + EMSG2("GetComputerNameW failed: %d", err); + return; + } + host_utf16[host_wsize] = '\0'; + + char *host_utf8; + int conversion_result = utf16_to_utf8(host_utf16, &host_utf8); + if (conversion_result != 0) { + EMSG2("utf16_to_utf8 failed: %d", conversion_result); + return; + } + xstrlcpy(hostname, host_utf8, size); + xfree(host_utf8); #else - // TODO(unknown): Implement this for windows. - // See the implementation used in vim: - // https://code.google.com/p/vim/source/browse/src/os_win32.c?r=6b69d8dde19e32909f4ee3a6337e6a2ecfbb6f72#2899 + EMSG("os_get_hostname failed: missing uname()"); *hostname = '\0'; #endif } diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 3833a43f5f..c33e1140e8 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -1009,7 +1009,7 @@ char *os_resolve_shortcut(const char *fname) WCHAR *p; const int conversion_result = utf8_to_utf16(fname, &p); if (conversion_result != 0) { - EMSG2("utf8_to_utf16 failed: %s", uv_strerror(conversion_result)); + EMSG2("utf8_to_utf16 failed: %d", conversion_result); } if (p != NULL) { diff --git a/test/functional/eval/hostname_spec.lua b/test/functional/eval/hostname_spec.lua new file mode 100644 index 0000000000..f1867846c4 --- /dev/null +++ b/test/functional/eval/hostname_spec.lua @@ -0,0 +1,17 @@ +local helpers = require('test.functional.helpers')(after_each) +local ok = helpers.ok +local call = helpers.call +local clear = helpers.clear + +describe('hostname()', function() + before_each(clear) + + it('returns hostname string', function() + local actual = call('hostname') + ok(string.len(actual) > 1) + if call('executable', 'hostname') == 1 then + local expected = string.gsub(call('system', 'hostname'), '[\n\r]', '') + helpers.eq(expected, actual) + end + end) +end) From 19044a15f9d41a7424e94fb3f0e257537e7afa5e Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 6 Apr 2017 21:21:11 +0300 Subject: [PATCH 0354/1671] strings: Replace vim_strchr implementation with a saner one Removes dead code (enc_utf8, enc_dbcs and has_mbyte now have hardcoded values), relies on libc implementation being more optimized. Also where previously negative character just would never be found it is an assertion error now. Ref #1476 --- src/nvim/strings.c | 64 ++++++++++++++-------------------------------- 1 file changed, 19 insertions(+), 45 deletions(-) diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 5dcffe00e0..c4bc9b204a 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -401,54 +401,28 @@ int vim_strnicmp(const char *s1, const char *s2, size_t len) } #endif -/* - * Version of strchr() and strrchr() that handle unsigned char strings - * with characters from 128 to 255 correctly. It also doesn't return a - * pointer to the NUL at the end of the string. - */ -char_u *vim_strchr(const char_u *string, int c) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE +/// strchr() version which handles multibyte strings +/// +/// @param[in] string String to search in. +/// @param[in] c Character to search for. Must be a valid character. +/// +/// @return Pointer to the first byte of the found character in string or NULL +/// if it was not found. NUL character is never found, use `strlen()` +/// instead. +char_u *vim_strchr(const char_u *const string, const int c) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - int b; - - const char_u *p = string; - if (enc_utf8 && c >= 0x80) { - while (*p != NUL) { - int l = (*mb_ptr2len)(p); - - // Avoid matching an illegal byte here. - if (l > 1 && utf_ptr2char(p) == c) { - return (char_u *) p; - } - p += l; - } + assert(c >= 0); + if (c == 0) { return NULL; + } else if (c < 0x80) { + return (char_u *)strchr((const char *)string, c); + } else { + char u8char[MB_MAXBYTES + 1]; + const int len = utf_char2bytes(c, (char_u *)u8char); + u8char[len] = NUL; + return (char_u *)strstr((const char *)string, u8char); } - if (enc_dbcs != 0 && c > 255) { - int n2 = c & 0xff; - - c = ((unsigned)c >> 8) & 0xff; - while ((b = *p) != NUL) { - if (b == c && p[1] == n2) - return (char_u *) p; - p += (*mb_ptr2len)(p); - } - return NULL; - } - if (has_mbyte) { - while ((b = *p) != NUL) { - if (b == c) - return (char_u *) p; - p += (*mb_ptr2len)(p); - } - return NULL; - } - while ((b = *p) != NUL) { - if (b == c) - return (char_u *) p; - ++p; - } - return NULL; } /* From 171baaee93c8e257ef593b30c05405d03ac30c96 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 6 Apr 2017 21:31:37 +0300 Subject: [PATCH 0355/1671] strings: Remove vim_strbyte Ref #1476 --- src/nvim/ex_cmds.c | 13 ++++++------- src/nvim/getchar.c | 4 ++-- src/nvim/mbyte.c | 6 +++--- src/nvim/regexp.c | 42 +++++++++++++++--------------------------- src/nvim/regexp_nfa.c | 13 +++---------- src/nvim/strings.c | 18 ------------------ 6 files changed, 29 insertions(+), 67 deletions(-) diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 9a847a4c0a..8485a1ca66 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -5087,14 +5087,13 @@ static void helptags_one(char_u *dir, char_u *ext, char_u *tagfname, } p1 = vim_strchr(IObuff, '*'); /* find first '*' */ while (p1 != NULL) { - /* Use vim_strbyte() instead of vim_strchr() so that when - * 'encoding' is dbcs it still works, don't find '*' in the - * second byte. */ - p2 = vim_strbyte(p1 + 1, '*'); /* find second '*' */ - if (p2 != NULL && p2 > p1 + 1) { /* skip "*" and "**" */ - for (s = p1 + 1; s < p2; ++s) - if (*s == ' ' || *s == '\t' || *s == '|') + p2 = (char_u *)strchr((const char *)p1 + 1, '*'); // Find second '*'. + if (p2 != NULL && p2 > p1 + 1) { // Skip "*" and "**". + for (s = p1 + 1; s < p2; s++) { + if (*s == ' ' || *s == '\t' || *s == '|') { break; + } + } /* * Only accept a *tag* when it consists of valid diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index b83681ad01..56493a300d 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -3588,8 +3588,8 @@ int check_abbr(int c, char_u *ptr, int col, int mincol) char_u *q = mp->m_keys; int match; - if (vim_strbyte(mp->m_keys, K_SPECIAL) != NULL) { - /* might have CSI escaped mp->m_keys */ + if (strchr((const char *)mp->m_keys, K_SPECIAL) != NULL) { + // Might have CSI escaped mp->m_keys. q = vim_strsave(mp->m_keys); vim_unescape_csi(q); qlen = (int)STRLEN(q); diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index d96848754c..c31fee44af 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -356,10 +356,10 @@ int bomb_size(void) */ void remove_bom(char_u *s) { - char_u *p = s; + char *p = (char *)s; - while ((p = vim_strbyte(p, 0xef)) != NULL) { - if (p[1] == 0xbb && p[2] == 0xbf) { + while ((p = strchr(p, 0xef)) != NULL) { + if ((uint8_t)p[1] == 0xbb && (uint8_t)p[2] == 0xbf) { STRMOVE(p, p + 3); } else { p++; diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 9baa53d2a2..e9c9b491fd 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -3429,32 +3429,26 @@ static long bt_regexec_both(char_u *line, c = *prog->regmust; s = line + col; - /* - * This is used very often, esp. for ":global". Use three versions of - * the loop to avoid overhead of conditions. - */ - if (!ireg_ic - && !has_mbyte - ) - while ((s = vim_strbyte(s, c)) != NULL) { - if (cstrncmp(s, prog->regmust, &prog->regmlen) == 0) - break; /* Found it. */ - ++s; - } - else if (!ireg_ic || (!enc_utf8 && mb_char2len(c) > 1)) + // This is used very often, esp. for ":global". Use two versions of + // the loop to avoid overhead of conditions. + if (!ireg_ic) { while ((s = vim_strchr(s, c)) != NULL) { - if (cstrncmp(s, prog->regmust, &prog->regmlen) == 0) - break; /* Found it. */ + if (cstrncmp(s, prog->regmust, &prog->regmlen) == 0) { + break; // Found it. + } mb_ptr_adv(s); } - else + } else { while ((s = cstrchr(s, c)) != NULL) { - if (cstrncmp(s, prog->regmust, &prog->regmlen) == 0) - break; /* Found it. */ + if (cstrncmp(s, prog->regmust, &prog->regmlen) == 0) { + break; // Found it. + } mb_ptr_adv(s); } - if (s == NULL) /* Not present. */ + } + if (s == NULL) { // Not present. goto theend; + } } regline = line; @@ -3484,14 +3478,8 @@ static long bt_regexec_both(char_u *line, /* Messy cases: unanchored match. */ while (!got_int) { if (prog->regstart != NUL) { - /* Skip until the char we know it must start with. - * Used often, do some work to avoid call overhead. */ - if (!ireg_ic - && !has_mbyte - ) - s = vim_strbyte(regline + col, prog->regstart); - else - s = cstrchr(regline + col, prog->regstart); + // Skip until the char we know it must start with. + s = cstrchr(regline + col, prog->regstart); if (s == NULL) { retval = 0; break; diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 5b49ab38f0..a77978884e 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -4855,17 +4855,10 @@ static int failure_chance(nfa_state_T *state, int depth) */ static int skip_to_start(int c, colnr_T *colp) { - char_u *s; - - /* Used often, do some work to avoid call overhead. */ - if (!ireg_ic - && !has_mbyte - ) - s = vim_strbyte(regline + *colp, c); - else - s = cstrchr(regline + *colp, c); - if (s == NULL) + const char_u *const s = cstrchr(regline + *colp, c); + if (s == NULL) { return FAIL; + } *colp = (int)(s - regline); return OK; } diff --git a/src/nvim/strings.c b/src/nvim/strings.c index c4bc9b204a..ada4c108cf 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -425,24 +425,6 @@ char_u *vim_strchr(const char_u *const string, const int c) } } -/* - * Version of strchr() that only works for bytes and handles unsigned char - * strings with characters above 128 correctly. It also doesn't return a - * pointer to the NUL at the end of the string. - */ -char_u *vim_strbyte(const char_u *string, int c) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE -{ - const char_u *p = string; - - while (*p != NUL) { - if (*p == c) - return (char_u *) p; - ++p; - } - return NULL; -} - /* * Search for last occurrence of "c" in "string". * Return NULL if not found. From ac1cb1c72fa762b39ff457153c7fa6ecf1eaedc3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 6 Apr 2017 21:56:49 +0300 Subject: [PATCH 0356/1671] regexp: Refactor cstrchr Ref #1476 --- src/nvim/regexp.c | 59 ++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index e9c9b491fd..175aa1b970 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -6287,43 +6287,38 @@ static int cstrncmp(char_u *s1, char_u *s2, int *n) /* * cstrchr: This function is used a lot for simple searches, keep it fast! */ -static char_u *cstrchr(char_u *s, int c) +static inline char_u *cstrchr(const char_u *const s, const int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_ALWAYS_INLINE { - char_u *p; - int cc; - - if (!ireg_ic - || (!enc_utf8 && mb_char2len(c) > 1) - ) + if (!ireg_ic) { return vim_strchr(s, c); + } - /* tolower() and toupper() can be slow, comparing twice should be a lot - * faster (esp. when using MS Visual C++!). - * For UTF-8 need to use folded case. */ - if (enc_utf8 && c > 0x80) - cc = utf_fold(c); - else if (vim_isupper(c)) - cc = vim_tolower(c); - else if (vim_islower(c)) - cc = vim_toupper(c); - else - return vim_strchr(s, c); - - if (has_mbyte) { - for (p = s; *p != NUL; p += (*mb_ptr2len)(p)) { - if (enc_utf8 && c > 0x80) { - if (utf_fold(utf_ptr2char(p)) == cc) - return p; - } else if (*p == c || *p == cc) - return p; + // tolower() and toupper() can be slow, comparing twice should be a lot + // faster (esp. when using MS Visual C++!). + // For UTF-8 need to use folded case. + if (c > 0x80) { + const int folded_c = utf_fold(c); + for (const char_u *p = s; *p != NUL; p += utfc_ptr2len(p)) { + if (utf_fold(utf_ptr2char(p)) == folded_c) { + return (char_u *)p; + } } - } else - /* Faster version for when there are no multi-byte characters. */ - for (p = s; *p != NUL; ++p) - if (*p == c || *p == cc) - return p; + return NULL; + } - return NULL; + int cc; + if (vim_isupper(c)) { + cc = vim_tolower(c); + } else if (vim_islower(c)) { + cc = vim_toupper(c); + } else { + return vim_strchr(s, c); + } + + char tofind[] = { (char)c, (char)cc, NUL }; + return (char_u *)strpbrk((const char *)s, tofind); } /*************************************************************** From 20dc04470e00a369d2ba917a22b06ef2d173953f Mon Sep 17 00:00:00 2001 From: James McCoy Date: Fri, 7 Apr 2017 16:08:58 -0400 Subject: [PATCH 0357/1671] vim-patch:8.0.0499 Problem: taglist() does not prioritize tags for a buffer. Solution: Add an optional buffer argument. (Duncan McDougall, closes vim/vim#1194) https://github.com/vim/vim/commit/c6aafbaf3ea755e3ab4ee2e3045911126a08b038 --- runtime/doc/eval.txt | 9 +++++++-- src/nvim/eval.c | 10 +++++++--- src/nvim/eval.lua | 2 +- src/nvim/tag.c | 10 ++++------ src/nvim/testdir/test_alot.vim | 1 + src/nvim/testdir/test_taglist.vim | 21 +++++++++++++++++++++ 6 files changed, 41 insertions(+), 12 deletions(-) create mode 100644 src/nvim/testdir/test_taglist.vim diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 7060cc4186..e21f5357a2 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2291,7 +2291,7 @@ tabpagebuflist([{arg}]) List list of buffer numbers in tab page tabpagenr([{arg}]) Number number of current or last tab page tabpagewinnr({tabarg}[, {arg}]) Number number of current window in tab page -taglist({expr}) List list of tags matching {expr} +taglist({expr}[, {filename}]) List list of tags matching {expr} tagfiles() List tags files used tan({expr}) Float tangent of {expr} tanh({expr}) Float hyperbolic tangent of {expr} @@ -7427,8 +7427,13 @@ tagfiles() Returns a |List| with the file names used to search for tags for the current buffer. This is the 'tags' option expanded. -taglist({expr}) *taglist()* +taglist({expr}[, {filename}]) *taglist()* Returns a list of tags matching the regular expression {expr}. + + If {filename} is passed it is used to prioritize the results + in the same way that |:tselect| does. See |tag-priority|. + {filename} should be the full path of the file. + Each list item is a dictionary with at least the following entries: name Name of the tag. diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 1636b490d5..e15d6c0240 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8728,8 +8728,7 @@ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr) } fold_count = foldedCount(curwin, lnum, &foldinfo); if (fold_count > 0) { - text = get_foldtext(curwin, lnum, lnum + fold_count - 1, - &foldinfo, buf); + text = get_foldtext(curwin, lnum, lnum + fold_count - 1, &foldinfo, buf); if (text == buf) text = vim_strsave(text); rettv->vval.v_string = text; @@ -16436,7 +16435,12 @@ static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - (void)get_tags(tv_list_alloc_ret(rettv), (char_u *)tag_pattern); + const char *fname = NULL; + if (argvars[1].v_type != VAR_UNKNOWN) { + fname = tv_get_string(&argvars[1]); + } + (void)get_tags(tv_list_alloc_ret(rettv), (char_u *)tag_pattern, + (char_u *)fname); } /* diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index e3c5981b32..6f876e2a96 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -300,7 +300,7 @@ return { tabpagenr={args={0, 1}}, tabpagewinnr={args={1, 2}}, tagfiles={}, - taglist={args=1}, + taglist={args={1, 2}}, tan={args=1, func="float_op_wrapper", data="&tan"}, tanh={args=1, func="float_op_wrapper", data="&tanh"}, tempname={}, diff --git a/src/nvim/tag.c b/src/nvim/tag.c index b812dd2ffd..e4ff829807 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -2797,11 +2797,9 @@ add_tag_field ( return retval; } -/* - * Add the tags matching the specified pattern to the list "list" - * as a dictionary - */ -int get_tags(list_T *list, char_u *pat) +/// Add the tags matching the specified pattern "pat" to the list "list" +/// as a dictionary. Use "buf_fname" for priority, unless NULL. +int get_tags(list_T *list, char_u *pat, char_u *buf_fname) { int num_matches, i, ret; char_u **matches, *p; @@ -2811,7 +2809,7 @@ int get_tags(list_T *list, char_u *pat) bool is_static; ret = find_tags(pat, &num_matches, &matches, - TAG_REGEXP | TAG_NOIC, (int)MAXCOL, NULL); + TAG_REGEXP | TAG_NOIC, (int)MAXCOL, buf_fname); if (ret == OK && num_matches > 0) { for (i = 0; i < num_matches; ++i) { int parse_result = parse_match(matches[i], &tp); diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index baf49b7ff7..99d9835996 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -26,6 +26,7 @@ source test_tabline.vim " source test_tabpage.vim source test_tagcase.vim source test_tagjump.vim +source test_taglist.vim source test_true_false.vim source test_unlet.vim source test_utf8.vim diff --git a/src/nvim/testdir/test_taglist.vim b/src/nvim/testdir/test_taglist.vim new file mode 100644 index 0000000000..b89b25eae2 --- /dev/null +++ b/src/nvim/testdir/test_taglist.vim @@ -0,0 +1,21 @@ +" test 'taglist' function + +func Test_taglist() + call writefile([ + \ "FFoo\tXfoo\t1", + \ "FBar\tXfoo\t2", + \ "BFoo\tXbar\t1", + \ "BBar\tXbar\t2" + \ ], 'Xtags') + set tags=Xtags + split Xtext + + call assert_equal(['FFoo', 'BFoo'], map(taglist("Foo"), {i, v -> v.name})) + call assert_equal(['FFoo', 'BFoo'], map(taglist("Foo", "Xtext"), {i, v -> v.name})) + call assert_equal(['FFoo', 'BFoo'], map(taglist("Foo", "Xfoo"), {i, v -> v.name})) + call assert_equal(['BFoo', 'FFoo'], map(taglist("Foo", "Xbar"), {i, v -> v.name})) + + call delete('Xtags') + bwipe +endfunc + From caeff6e1aff227bb5826ad575362d2a24adebaa9 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 7 Apr 2017 23:18:36 +0300 Subject: [PATCH 0358/1671] regexp: Do not use locale-dependent functions in cstrchr --- src/nvim/regexp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 175aa1b970..893089f06d 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -6309,10 +6309,10 @@ static inline char_u *cstrchr(const char_u *const s, const int c) } int cc; - if (vim_isupper(c)) { - cc = vim_tolower(c); - } else if (vim_islower(c)) { - cc = vim_toupper(c); + if (ASCII_ISUPPER(c)) { + cc = TOLOWER_ASC(c); + } else if (ASCII_ISLOWER(c)) { + cc = TOUPPER_ASC(c); } else { return vim_strchr(s, c); } From 98dd9b801281bb6eb82817ba92c4f635bb5f45e0 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Fri, 7 Apr 2017 16:18:25 -0400 Subject: [PATCH 0359/1671] vim-patch:8.0.0550 Problem: Some etags format tags file use 0x01, breaking the parsing. Solution: Use 0x02 for TAG_SEP. (James McCoy, closes vim/vim#1614) https://github.com/vim/vim/commit/9585a1655ba0d34ea88574617112093a9bd4f2e9 --- src/nvim/tag.c | 10 ++++----- src/nvim/testdir/test_taglist.vim | 37 +++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/nvim/tag.c b/src/nvim/tag.c index e4ff829807..f01b8b8ab1 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -1847,14 +1847,14 @@ parse_line: } } } else { -#define TAG_SEP 0x01 +#define TAG_SEP 0x02 size_t tag_fname_len = STRLEN(tag_fname); // Save the tag in a buffer. - // Use 0x01 to separate fields (Can't use NUL, because the + // Use 0x02 to separate fields (Can't use NUL, because the // hash key is terminated by NUL). - // Emacs tag: - // other tag: - // without Emacs tags: + // Emacs tag: <0x02><0x02> + // other tag: <0x02><0x02> + // without Emacs tags: <0x02> // Here is the "mtt" value plus 1 to avoid NUL. len = (int)tag_fname_len + (int)STRLEN(lbuf) + 3; mfp = xmalloc(sizeof(char_u) + len + 1); diff --git a/src/nvim/testdir/test_taglist.vim b/src/nvim/testdir/test_taglist.vim index b89b25eae2..2d1557ebd9 100644 --- a/src/nvim/testdir/test_taglist.vim +++ b/src/nvim/testdir/test_taglist.vim @@ -19,3 +19,40 @@ func Test_taglist() bwipe endfunc +func Test_taglist_native_etags() + if !has('emacs_tags') + return + endif + call writefile([ + \ "\x0c", + \ "src/os_unix.c,13491", + \ "set_signals(\x7f1335,32699", + \ "reset_signals(\x7f1407,34136", + \ ], 'Xtags') + + set tags=Xtags + + call assert_equal([['set_signals', '1335,32699'], ['reset_signals', '1407,34136']], + \ map(taglist('set_signals'), {i, v -> [v.name, v.cmd]})) + + call delete('Xtags') +endfunc + +func Test_taglist_ctags_etags() + if !has('emacs_tags') + return + endif + call writefile([ + \ "\x0c", + \ "src/os_unix.c,13491", + \ "set_signals(void)\x7fset_signals\x011335,32699", + \ "reset_signals(void)\x7freset_signals\x011407,34136", + \ ], 'Xtags') + + set tags=Xtags + + call assert_equal([['set_signals', '1335,32699'], ['reset_signals', '1407,34136']], + \ map(taglist('set_signals'), {i, v -> [v.name, v.cmd]})) + + call delete('Xtags') +endfunc From 123931e65e8f6ca3ac13fff8279720c8328a018e Mon Sep 17 00:00:00 2001 From: James McCoy Date: Fri, 7 Apr 2017 16:38:06 -0400 Subject: [PATCH 0360/1671] lint --- src/nvim/eval.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index e15d6c0240..124d6acfe9 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8729,8 +8729,9 @@ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr) fold_count = foldedCount(curwin, lnum, &foldinfo); if (fold_count > 0) { text = get_foldtext(curwin, lnum, lnum + fold_count - 1, &foldinfo, buf); - if (text == buf) + if (text == buf) { text = vim_strsave(text); + } rettv->vval.v_string = text; } } From acc52a953b99f78435c34337b8ca9b6716a057a1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 8 Apr 2017 02:55:51 +0300 Subject: [PATCH 0361/1671] regexp: Update comment in cstrchr() --- src/nvim/regexp.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 893089f06d..96884aa87f 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -6295,9 +6295,8 @@ static inline char_u *cstrchr(const char_u *const s, const int c) return vim_strchr(s, c); } - // tolower() and toupper() can be slow, comparing twice should be a lot - // faster (esp. when using MS Visual C++!). - // For UTF-8 need to use folded case. + // Use folded case for UTF-8, slow! For ASCII use libc strpbrk which is + // expected to be highly optimized. if (c > 0x80) { const int folded_c = utf_fold(c); for (const char_u *p = s; *p != NUL; p += utfc_ptr2len(p)) { From a83511d1a19d6277f8258f2c5b970c936f0bc56e Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 6 Apr 2017 08:23:33 +0300 Subject: [PATCH 0362/1671] unittests: Move checking cores to check_child_err --- test/helpers.lua | 4 ++-- test/unit/helpers.lua | 16 ++++++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/test/helpers.lua b/test/helpers.lua index 3fc10e9e30..beef53b5a9 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -162,7 +162,7 @@ end local tests_skipped = 0 -local function check_cores(app) +local function check_cores(app, force) app = app or 'build/bin/nvim' local initial_path, re, exc_re local gdb_db_cmd = 'gdb -n -batch -ex "thread apply all bt full" "$_NVIM_TEST_APP" -c "$_NVIM_TEST_CORE"' @@ -188,7 +188,7 @@ local function check_cores(app) random_skip = true end -- Finding cores takes too much time on linux - if random_skip and math.random() < 0.9 then + if not force and random_skip and math.random() < 0.9 then tests_skipped = tests_skipped + 1 return end diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 6d0de5c651..4b9f185156 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -655,6 +655,7 @@ end local function check_child_err(rd) local trace = {} + local did_traceline = false while true do local traceline = sc.read(rd, hook_msglen) if #traceline ~= hook_msglen then @@ -665,6 +666,7 @@ local function check_child_err(rd) end end if traceline == trace_end_msg then + did_traceline = true break end trace[#trace + 1] = traceline @@ -680,6 +682,13 @@ local function check_child_err(rd) error = error .. trace[i] end end + if not did_traceline then + error = error .. '\nNo end of trace occurred' + end + local cc_err, cc_emsg = pcall(check_cores, Paths.test_luajit_prg, true) + if not cc_err then + error = error .. '\ncheck_cores failed: ' .. cc_emsg + end assert.just_fail(error) end if res == '+\n' then @@ -765,11 +774,6 @@ local module = { child_cleanup_once = child_cleanup_once, sc = sc, } -return function(after_each) - if after_each then - after_each(function() - check_cores(Paths.test_luajit_prg) - end) - end +return function() return module end From fd8f18bce25e6dcc66dec1fa4870a7ca5c106dec Mon Sep 17 00:00:00 2001 From: dedmass Date: Tue, 4 Apr 2017 20:30:32 -0400 Subject: [PATCH 0363/1671] refactor/single-include: cursor_shape.h #6442 --- src/nvim/CMakeLists.txt | 1 - src/nvim/cursor_shape.h | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index d0f75a2b5b..db3b406b4a 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -424,7 +424,6 @@ function(get_test_target prefix sfile relative_path_var target_var) endfunction() set(NO_SINGLE_CHECK_HEADERS - cursor_shape.h digraph.h ex_cmds.h ex_getln.h diff --git a/src/nvim/cursor_shape.h b/src/nvim/cursor_shape.h index 0006ede31d..7cf65cba3c 100644 --- a/src/nvim/cursor_shape.h +++ b/src/nvim/cursor_shape.h @@ -1,6 +1,9 @@ #ifndef NVIM_CURSOR_SHAPE_H #define NVIM_CURSOR_SHAPE_H +#include "nvim/types.h" +#include "nvim/api/private/defs.h" + /// struct to store values from 'guicursor' and 'mouseshape' /// Indexes in shape_table[] typedef enum { From f4e97fe49988b834fea8d8b7a62de0938325395a Mon Sep 17 00:00:00 2001 From: dedmass Date: Tue, 4 Apr 2017 20:53:26 -0400 Subject: [PATCH 0364/1671] refactor/single-include: digraph.h #6444 --- src/nvim/CMakeLists.txt | 1 - src/nvim/digraph.h | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index db3b406b4a..5524c4d87d 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -424,7 +424,6 @@ function(get_test_target prefix sfile relative_path_var target_var) endfunction() set(NO_SINGLE_CHECK_HEADERS - digraph.h ex_cmds.h ex_getln.h file_search.h diff --git a/src/nvim/digraph.h b/src/nvim/digraph.h index b623969e08..d0f10eaf78 100644 --- a/src/nvim/digraph.h +++ b/src/nvim/digraph.h @@ -1,6 +1,9 @@ #ifndef NVIM_DIGRAPH_H #define NVIM_DIGRAPH_H +#include "nvim/types.h" // for char_u +#include "nvim/ex_cmds_defs.h" // for exarg_T + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "digraph.h.generated.h" #endif From 3d4a2ee9c72e772113e69c9928fbc58e9dc4b09b Mon Sep 17 00:00:00 2001 From: dedmass Date: Thu, 6 Apr 2017 09:12:59 -0400 Subject: [PATCH 0365/1671] refactor/single-include: ex_cmds.h #6453 --- src/nvim/CMakeLists.txt | 1 - src/nvim/ex_cmds.h | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 5524c4d87d..843bff3655 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -424,7 +424,6 @@ function(get_test_target prefix sfile relative_path_var target_var) endfunction() set(NO_SINGLE_CHECK_HEADERS - ex_cmds.h ex_getln.h file_search.h fold.h diff --git a/src/nvim/ex_cmds.h b/src/nvim/ex_cmds.h index 65bbd8a99e..792e2f772f 100644 --- a/src/nvim/ex_cmds.h +++ b/src/nvim/ex_cmds.h @@ -6,6 +6,8 @@ #include "nvim/os/time.h" #include "nvim/pos.h" #include "nvim/eval/typval.h" +#include "nvim/buffer_defs.h" // for buf_T and win_T +#include "nvim/ex_cmds_defs.h" // for exarg_T // flags for do_ecmd() #define ECMD_HIDE 0x01 // don't free the current buffer From b47e1029a549d6563eeec17e099b82d781925f71 Mon Sep 17 00:00:00 2001 From: dedmass Date: Thu, 6 Apr 2017 21:56:27 -0400 Subject: [PATCH 0366/1671] refactor/single-include: move.h #6469 --- src/nvim/CMakeLists.txt | 1 - src/nvim/move.h | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 843bff3655..cdfc6367e5 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -438,7 +438,6 @@ set(NO_SINGLE_CHECK_HEADERS memline_defs.h menu.h misc2.h - move.h msgpack_rpc/server.h ops.h option.h diff --git a/src/nvim/move.h b/src/nvim/move.h index 3f3bf70929..1cdf1f6f52 100644 --- a/src/nvim/move.h +++ b/src/nvim/move.h @@ -2,6 +2,8 @@ #define NVIM_MOVE_H #include +#include "nvim/buffer_defs.h" // for win_T +#include "nvim/pos.h" // for linenr_T #ifdef INCLUDE_GENERATED_DECLARATIONS # include "move.h.generated.h" From 89deb6ff223ffef5c1e93e629b4df2cf666d9cd1 Mon Sep 17 00:00:00 2001 From: dedmass Date: Thu, 6 Apr 2017 17:16:56 -0400 Subject: [PATCH 0367/1671] refactor/single-include: memline_defs.h #6465 --- src/nvim/CMakeLists.txt | 1 - src/nvim/memfile_defs.h | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index cdfc6367e5..4d83e9b1ea 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -435,7 +435,6 @@ set(NO_SINGLE_CHECK_HEADERS mbyte.h memfile_defs.h memline.h - memline_defs.h menu.h misc2.h msgpack_rpc/server.h diff --git a/src/nvim/memfile_defs.h b/src/nvim/memfile_defs.h index cc71e1a7ff..57d8abbe30 100644 --- a/src/nvim/memfile_defs.h +++ b/src/nvim/memfile_defs.h @@ -3,8 +3,10 @@ #include #include +#include // for size_t #include "nvim/types.h" +#include "nvim/pos.h" // for linenr_T /// A block number. /// From fd69c3f561ea8862ce4b1ff67a5650acaf5f932d Mon Sep 17 00:00:00 2001 From: dedmass Date: Thu, 6 Apr 2017 09:22:44 -0400 Subject: [PATCH 0368/1671] refactor/single-include: ex_getln.h #6454 --- src/nvim/CMakeLists.txt | 1 - src/nvim/ex_getln.h | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 4d83e9b1ea..3d3d30081a 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -424,7 +424,6 @@ function(get_test_target prefix sfile relative_path_var target_var) endfunction() set(NO_SINGLE_CHECK_HEADERS - ex_getln.h file_search.h fold.h getchar.h diff --git a/src/nvim/ex_getln.h b/src/nvim/ex_getln.h index 5a1ca5213a..a29c8297d5 100644 --- a/src/nvim/ex_getln.h +++ b/src/nvim/ex_getln.h @@ -3,6 +3,9 @@ #include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" +#include "nvim/ex_cmds_defs.h" // for exarg_T +#include "nvim/os/time.h" // for Timestamp +#include "nvim/regexp_defs.h" // for regmatch_T /* Values for nextwild() and ExpandOne(). See ExpandOne() for meaning. */ #define WILD_FREE 1 From 3fd9b70c485c55adb95848aa0e2c67bfb3db786a Mon Sep 17 00:00:00 2001 From: dedmass Date: Thu, 6 Apr 2017 09:40:17 -0400 Subject: [PATCH 0369/1671] refactor/single-include: fold.h #6456 --- src/nvim/CMakeLists.txt | 1 - src/nvim/fold.h | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 3d3d30081a..24a6ec8b5e 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -425,7 +425,6 @@ endfunction() set(NO_SINGLE_CHECK_HEADERS file_search.h - fold.h getchar.h hardcopy.h if_cscope.h diff --git a/src/nvim/fold.h b/src/nvim/fold.h index 2ff10c0e91..2393f4ef47 100644 --- a/src/nvim/fold.h +++ b/src/nvim/fold.h @@ -1,7 +1,12 @@ #ifndef NVIM_FOLD_H #define NVIM_FOLD_H +#include // for FILE + #include "nvim/pos.h" +#include "nvim/garray.h" // for garray_T +#include "nvim/types.h" // for char_u +#include "nvim/buffer_defs.h" // for win_T /* * Info used to pass info about a fold from the fold-detection code to the From e586047a53d4145db9c797af7f42c71c35143896 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 8 Apr 2017 17:15:56 +0300 Subject: [PATCH 0370/1671] eval/decode,shada: Do not forget to clean up converters --- src/nvim/eval/decode.c | 1 + src/nvim/shada.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index fb31a65971..a4fc6c8eb1 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -892,6 +892,7 @@ json_decode_string_fail: tv_clear(&(kv_pop(stack).val)); } json_decode_string_ret: + convert_setup(&conv, NULL, NULL); kv_destroy(stack); kv_destroy(container_stack); return ret; diff --git a/src/nvim/shada.c b/src/nvim/shada.c index c7b95958e0..9ee364d962 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -709,6 +709,7 @@ static ptrdiff_t write_file(ShaDaWriteDef *const sd_writer, static void close_sd_reader(ShaDaReadDef *const sd_reader) FUNC_ATTR_NONNULL_ALL { + convert_setup(&sd_reader->sd_conv, NULL, NULL); close_file(sd_reader->cookie); } @@ -716,6 +717,7 @@ static void close_sd_reader(ShaDaReadDef *const sd_reader) static void close_sd_writer(ShaDaWriteDef *const sd_writer) FUNC_ATTR_NONNULL_ALL { + convert_setup(&sd_writer->sd_conv, NULL, NULL); close_file(sd_writer->cookie); } From b3587a456b4c235822006aae928667433ab85e66 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 8 Apr 2017 17:18:40 +0300 Subject: [PATCH 0371/1671] shada: Initialize vimconv_T --- src/nvim/shada.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 9ee364d962..3c4fd8b992 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -800,6 +800,7 @@ static int open_shada_file_for_reading(const char *const fname, .eof = false, .fpos = 0, .cookie = file_open_new(&error, fname, kFileReadOnly, 0), + .sd_conv.vc_type = CONV_NONE, }; if (sd_reader->cookie == NULL) { return error; @@ -2939,6 +2940,7 @@ int shada_write_file(const char *const file, bool nomerge) .write = &write_file, .close = &close_sd_writer, .error = NULL, + .sd_conv.vc_type = CONV_NONE, }; ShaDaReadDef sd_reader = { .close = NULL }; From 6006cb74ef7c43b58e8c06e5ecaa5823247b970a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 8 Apr 2017 17:36:02 +0300 Subject: [PATCH 0372/1671] eval/decode: Omit calling convert_setup for each string Uses the same trick eval/encode does. --- src/nvim/eval/decode.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index a4fc6c8eb1..e8c06b2927 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -606,6 +606,17 @@ parse_json_number_ret: } \ } while (0) +/// Last used p_enc value +/// +/// Generic pointer: it is not used as a string, only pointer comparisons are +/// performed. Must not be freed. +static const void *last_p_enc = NULL; + +/// Conversion setup for converting from UTF-8 to last_p_enc +static vimconv_T p_enc_conv = { + .vc_type = CONV_NONE, +}; + /// Convert JSON string into VimL object /// /// @param[in] buf String to convert. UTF-8 encoding is assumed. @@ -626,9 +637,12 @@ int json_decode_string(const char *const buf, const size_t buf_len, EMSG(_("E474: Attempt to decode a blank string")); return FAIL; } - vimconv_T conv = { .vc_type = CONV_NONE }; - convert_setup(&conv, (char_u *) "utf-8", p_enc); - conv.vc_fail = true; + if (last_p_enc != (const void *)p_enc) { + p_enc_conv.vc_type = CONV_NONE; + convert_setup(&p_enc_conv, (char_u *)"utf-8", p_enc); + p_enc_conv.vc_fail = true; + last_p_enc = p_enc; + } int ret = OK; ValuesStack stack = KV_INITIAL_VALUE; ContainerStack container_stack = KV_INITIAL_VALUE; @@ -774,9 +788,9 @@ json_decode_string_cycle_start: break; } case '"': { - if (parse_json_string(&conv, buf, buf_len, &p, &stack, &container_stack, - &next_map_special, &didcomma, &didcolon) - == FAIL) { + if (parse_json_string( + &p_enc_conv, buf, buf_len, &p, &stack, &container_stack, + &next_map_special, &didcomma, &didcolon) == FAIL) { // Error message was already given goto json_decode_string_fail; } @@ -892,7 +906,6 @@ json_decode_string_fail: tv_clear(&(kv_pop(stack).val)); } json_decode_string_ret: - convert_setup(&conv, NULL, NULL); kv_destroy(stack); kv_destroy(container_stack); return ret; From e81469522371837615d2e2ffbd578ef44f883d2a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 8 Apr 2017 18:03:56 +0300 Subject: [PATCH 0373/1671] eval/*code,shada: Drop support for converting UTF-8 from/to p_enc Not needed any longer since p_enc is always utf-8. --- src/nvim/eval/decode.c | 39 +------ src/nvim/eval/encode.c | 56 +++------- src/nvim/shada.c | 234 ++++++----------------------------------- 3 files changed, 45 insertions(+), 284 deletions(-) diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index e8c06b2927..c7ca3a8ce5 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -222,8 +222,6 @@ static inline int json_decoder_pop(ValuesStackItem obj, /// Parse JSON double-quoted string /// -/// @param[in] conv Defines conversion necessary to convert UTF-8 string to -/// &encoding. /// @param[in] buf Buffer being converted. /// @param[in] buf_len Length of the buffer. /// @param[in,out] pp Pointer to the start of the string. Must point to '"'. @@ -240,8 +238,7 @@ static inline int json_decoder_pop(ValuesStackItem obj, /// value when decoder is restarted, otherwise unused. /// /// @return OK in case of success, FAIL in case of error. -static inline int parse_json_string(vimconv_T *const conv, - const char *const buf, const size_t buf_len, +static inline int parse_json_string(const char *const buf, const size_t buf_len, const char **const pp, ValuesStack *const stack, ContainerStack *const container_stack, @@ -416,20 +413,6 @@ static inline int parse_json_string(vimconv_T *const conv, } PUT_FST_IN_PAIR(fst_in_pair, str_end); #undef PUT_FST_IN_PAIR - if (conv->vc_type != CONV_NONE) { - size_t str_len = (size_t) (str_end - str); - char *const new_str = (char *) string_convert(conv, (char_u *) str, - &str_len); - if (new_str == NULL) { - emsgf(_("E474: Failed to convert string \"%.*s\" from UTF-8"), - (int) str_len, str); - xfree(str); - goto parse_json_string_fail; - } - xfree(str); - str = new_str; - str_end = new_str + str_len; - } if (hasnul) { typval_T obj; list_T *const list = tv_list_alloc(); @@ -606,17 +589,6 @@ parse_json_number_ret: } \ } while (0) -/// Last used p_enc value -/// -/// Generic pointer: it is not used as a string, only pointer comparisons are -/// performed. Must not be freed. -static const void *last_p_enc = NULL; - -/// Conversion setup for converting from UTF-8 to last_p_enc -static vimconv_T p_enc_conv = { - .vc_type = CONV_NONE, -}; - /// Convert JSON string into VimL object /// /// @param[in] buf String to convert. UTF-8 encoding is assumed. @@ -637,12 +609,7 @@ int json_decode_string(const char *const buf, const size_t buf_len, EMSG(_("E474: Attempt to decode a blank string")); return FAIL; } - if (last_p_enc != (const void *)p_enc) { - p_enc_conv.vc_type = CONV_NONE; - convert_setup(&p_enc_conv, (char_u *)"utf-8", p_enc); - p_enc_conv.vc_fail = true; - last_p_enc = p_enc; - } + assert(STRCMP(p_enc, "utf-8") == 0); int ret = OK; ValuesStack stack = KV_INITIAL_VALUE; ContainerStack container_stack = KV_INITIAL_VALUE; @@ -789,7 +756,7 @@ json_decode_string_cycle_start: } case '"': { if (parse_json_string( - &p_enc_conv, buf, buf_len, &p, &stack, &container_stack, + buf, buf_len, &p, &stack, &container_stack, &next_map_special, &didcomma, &didcolon) == FAIL) { // Error message was already given goto json_decode_string_fail; diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index 26f9aaa27d..b64217f969 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -11,7 +11,7 @@ #include #include "nvim/eval/encode.h" -#include "nvim/buffer_defs.h" // vimconv_T +#include "nvim/buffer_defs.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/garray.h" @@ -29,10 +29,6 @@ #define utf_ptr2char(b) utf_ptr2char((char_u *)b) #define utf_ptr2len(b) ((size_t)utf_ptr2len((char_u *)b)) #define utf_char2len(b) ((size_t)utf_char2len(b)) -#define string_convert(a, b, c) \ - ((char *)string_convert((vimconv_T *)a, (char_u *)b, c)) -#define convert_setup(vcp, from, to) \ - (convert_setup(vcp, (char_u *)from, (char_u *)to)) const char *const encode_special_var_names[] = { [kSpecialVarNull] = "null", @@ -537,17 +533,6 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, } \ } while (0) -/// Last used p_enc value -/// -/// Generic pointer: it is not used as a string, only pointer comparisons are -/// performed. Must not be freed. -static const void *last_p_enc = NULL; - -/// Conversion setup for converting from last_p_enc to UTF-8 -static vimconv_T p_enc_conv = { - .vc_type = CONV_NONE, -}; - /// Escape sequences used in JSON static const char escapes[][3] = { [BS] = "\\b", @@ -579,33 +564,16 @@ static inline int convert_to_json_string(garray_T *const gap, } else { size_t utf_len = len; char *tofree = NULL; - if (last_p_enc != (const void *) p_enc) { - p_enc_conv.vc_type = CONV_NONE; - convert_setup(&p_enc_conv, p_enc, "utf-8"); - p_enc_conv.vc_fail = true; - last_p_enc = p_enc; - } - if (p_enc_conv.vc_type != CONV_NONE) { - tofree = string_convert(&p_enc_conv, buf, &utf_len); - if (tofree == NULL) { - emsgf(_("E474: Failed to convert string \"%.*s\" to UTF-8"), - utf_len, utf_buf); - return FAIL; - } - utf_buf = tofree; - } + assert(STRCMP(p_enc, "utf-8") == 0); size_t str_len = 0; - // Encode character as \u0000 if - // 1. It is an ASCII control character (0x0 .. 0x1F, 0x7F). - // 2. &encoding is not UTF-8 and code point is above 0x7F. - // 3. &encoding is UTF-8 and code point is not printable according to - // utf_printable(). - // This is done to make it possible to :echo values when &encoding is not - // UTF-8. -#define ENCODE_RAW(p_enc_conv, ch) \ - (ch >= 0x20 && (p_enc_conv.vc_type == CONV_NONE \ - ? utf_printable(ch) \ - : ch < 0x7F)) + // Encode character as \uNNNN if + // 1. It is an ASCII control character (0x0 .. 0x1F; 0x7F not + // utf_printable and thus not checked specially). + // 2. Code point is not printable according to utf_printable(). + // This is done to make resulting values displayable on screen also not from + // Neovim. +#define ENCODE_RAW(ch) \ + (ch >= 0x20 && utf_printable(ch)) for (size_t i = 0; i < utf_len;) { const int ch = utf_ptr2char(utf_buf + i); const size_t shift = (ch == 0? 1: utf_ptr2len(utf_buf + i)); @@ -636,7 +604,7 @@ static inline int convert_to_json_string(garray_T *const gap, utf_len - (i - shift), utf_buf + i - shift); xfree(tofree); return FAIL; - } else if (ENCODE_RAW(p_enc_conv, ch)) { + } else if (ENCODE_RAW(ch)) { str_len += shift; } else { str_len += ((sizeof("\\u1234") - 1) @@ -666,7 +634,7 @@ static inline int convert_to_json_string(garray_T *const gap, break; } default: { - if (ENCODE_RAW(p_enc_conv, ch)) { + if (ENCODE_RAW(ch)) { ga_concat_len(gap, utf_buf + i, shift); } else if (ch < SURROGATE_FIRST_CHAR) { ga_concat_len(gap, ((const char[]) { diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 3c4fd8b992..cfe0bd8774 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -73,15 +73,10 @@ KHASH_SET_INIT_STR(strset) (vim_rename((char_u *)a, (char_u *)b)) #define mb_strnicmp(a, b, c) \ (mb_strnicmp((char_u *)a, (char_u *)b, c)) -#define has_non_ascii(a) (has_non_ascii((char_u *)a)) -#define string_convert(a, b, c) \ - ((char *)string_convert((vimconv_T *)a, (char_u *)b, c)) #define path_shorten_fname_if_possible(b) \ ((char *)path_shorten_fname_if_possible((char_u *)b)) #define buflist_new(ffname, sfname, ...) \ (buflist_new((char_u *)ffname, (char_u *)sfname, __VA_ARGS__)) -#define convert_setup(vcp, from, to) \ - (convert_setup(vcp, (char_u *)from, (char_u *)to)) #define os_isdir(f) (os_isdir((char_u *) f)) #define regtilde(s, m) ((char *) regtilde((char_u *) s, m)) #define path_tail_with_sep(f) ((char *) path_tail_with_sep((char_u *)f)) @@ -413,8 +408,6 @@ typedef struct sd_read_def { const char *error; ///< Error message in case of error. uintmax_t fpos; ///< Current position (amount of bytes read since ///< reader structure initialization). May overflow. - vimconv_T sd_conv; ///< Structure used for converting encodings of some - ///< items. } ShaDaReadDef; struct sd_write_def; @@ -435,8 +428,6 @@ typedef struct sd_write_def { ShaDaWriteCloser close; ///< Close function. void *cookie; ///< Data describing object written to. const char *error; ///< Error message in case of error. - vimconv_T sd_conv; ///< Structure used for converting encodings of some - ///< items. } ShaDaWriteDef; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -709,7 +700,6 @@ static ptrdiff_t write_file(ShaDaWriteDef *const sd_writer, static void close_sd_reader(ShaDaReadDef *const sd_reader) FUNC_ATTR_NONNULL_ALL { - convert_setup(&sd_reader->sd_conv, NULL, NULL); close_file(sd_reader->cookie); } @@ -717,7 +707,6 @@ static void close_sd_reader(ShaDaReadDef *const sd_reader) static void close_sd_writer(ShaDaWriteDef *const sd_writer) FUNC_ATTR_NONNULL_ALL { - convert_setup(&sd_writer->sd_conv, NULL, NULL); close_file(sd_writer->cookie); } @@ -800,13 +789,12 @@ static int open_shada_file_for_reading(const char *const fname, .eof = false, .fpos = 0, .cookie = file_open_new(&error, fname, kFileReadOnly, 0), - .sd_conv.vc_type = CONV_NONE, }; if (sd_reader->cookie == NULL) { return error; } - convert_setup(&sd_reader->sd_conv, "utf-8", p_enc); + assert(STRCMP(p_enc, "utf-8") == 0); return 0; } @@ -1902,127 +1890,24 @@ shada_pack_entry_error: } #undef PACK_STRING -/// Write single ShaDa entry, converting it if needed +/// Write single ShaDa entry and free it afterwards /// -/// @warning Frees entry after packing. +/// Will not free if entry could not be freed. /// /// @param[in] packer Packer used to write entry. -/// @param[in] sd_conv Conversion definitions. -/// @param[in] entry Entry written. If entry.can_free_entry is false then -/// it assumes that entry was not converted, otherwise it -/// is assumed that entry was already converted. +/// @param[in] entry Entry written. /// @param[in] max_kbyte Maximum size of an item in KiB. Zero means no /// restrictions. -static ShaDaWriteResult shada_pack_encoded_entry(msgpack_packer *const packer, - const vimconv_T *const sd_conv, - PossiblyFreedShadaEntry entry, - const size_t max_kbyte) - FUNC_ATTR_NONNULL_ALL +static inline ShaDaWriteResult shada_pack_pfreed_entry( + msgpack_packer *const packer, PossiblyFreedShadaEntry entry, + const size_t max_kbyte) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE { ShaDaWriteResult ret = kSDWriteSuccessfull; + ret = shada_pack_entry(packer, entry.data, max_kbyte); if (entry.can_free_entry) { - ret = shada_pack_entry(packer, entry.data, max_kbyte); shada_free_shada_entry(&entry.data); - return ret; } -#define RUN_WITH_CONVERTED_STRING(cstr, code) \ - do { \ - bool did_convert = false; \ - if (sd_conv->vc_type != CONV_NONE && has_non_ascii((cstr))) { \ - char *const converted_string = string_convert(sd_conv, (cstr), NULL); \ - if (converted_string != NULL) { \ - (cstr) = converted_string; \ - did_convert = true; \ - } \ - } \ - code \ - if (did_convert) { \ - xfree((cstr)); \ - } \ - } while (0) - switch (entry.data.type) { - case kSDItemUnknown: - case kSDItemMissing: { - assert(false); - } - case kSDItemSearchPattern: { - RUN_WITH_CONVERTED_STRING(entry.data.data.search_pattern.pat, { - ret = shada_pack_entry(packer, entry.data, max_kbyte); - }); - break; - } - case kSDItemHistoryEntry: { - RUN_WITH_CONVERTED_STRING(entry.data.data.history_item.string, { - ret = shada_pack_entry(packer, entry.data, max_kbyte); - }); - break; - } - case kSDItemSubString: { - RUN_WITH_CONVERTED_STRING(entry.data.data.sub_string.sub, { - ret = shada_pack_entry(packer, entry.data, max_kbyte); - }); - break; - } - case kSDItemVariable: { - if (sd_conv->vc_type != CONV_NONE) { - typval_T tgttv; - var_item_copy(sd_conv, &entry.data.data.global_var.value, &tgttv, - true, 0); - tv_clear(&entry.data.data.global_var.value); - entry.data.data.global_var.value = tgttv; - } - ret = shada_pack_entry(packer, entry.data, max_kbyte); - break; - } - case kSDItemRegister: { - bool did_convert = false; - if (sd_conv->vc_type != CONV_NONE) { - size_t first_non_ascii = 0; - for (size_t i = 0; i < entry.data.data.reg.contents_size; i++) { - if (has_non_ascii(entry.data.data.reg.contents[i])) { - first_non_ascii = i; - did_convert = true; - break; - } - } - if (did_convert) { - entry.data.data.reg.contents = - xmemdup(entry.data.data.reg.contents, - (entry.data.data.reg.contents_size - * sizeof(entry.data.data.reg.contents[0]))); - for (size_t i = 0; i < entry.data.data.reg.contents_size; i++) { - if (i >= first_non_ascii) { - entry.data.data.reg.contents[i] = get_converted_string( - sd_conv, - entry.data.data.reg.contents[i], - strlen(entry.data.data.reg.contents[i])); - } else { - entry.data.data.reg.contents[i] = - xstrdup(entry.data.data.reg.contents[i]); - } - } - } - } - ret = shada_pack_entry(packer, entry.data, max_kbyte); - if (did_convert) { - for (size_t i = 0; i < entry.data.data.reg.contents_size; i++) { - xfree(entry.data.data.reg.contents[i]); - } - xfree(entry.data.data.reg.contents); - } - break; - } - case kSDItemHeader: - case kSDItemGlobalMark: - case kSDItemJump: - case kSDItemBufferList: - case kSDItemLocalMark: - case kSDItemChange: { - ret = shada_pack_entry(packer, entry.data, max_kbyte); - break; - } - } -#undef RUN_WITH_CONVERTED_STRING return ret; } @@ -2559,11 +2444,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, break; } typval_T tgttv; - if (sd_writer->sd_conv.vc_type != CONV_NONE) { - var_item_copy(&sd_writer->sd_conv, &vartv, &tgttv, true, 0); - } else { - tv_copy(&vartv, &tgttv); - } + tv_copy(&vartv, &tgttv); ShaDaWriteResult spe_ret; if ((spe_ret = shada_pack_entry(packer, (ShadaEntry) { .type = kSDItemVariable, @@ -2814,9 +2695,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, do { \ for (size_t i_ = 0; i_ < ARRAY_SIZE(wms_array); i_++) { \ if (wms_array[i_].data.type != kSDItemMissing) { \ - if (shada_pack_encoded_entry(packer, &sd_writer->sd_conv, \ - wms_array[i_], \ - max_kbyte) == kSDWriteFailed) { \ + if (shada_pack_pfreed_entry(packer, wms_array[i_], max_kbyte) \ + == kSDWriteFailed) { \ ret = kSDWriteFailed; \ goto shada_write_exit; \ } \ @@ -2826,8 +2706,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, PACK_WMS_ARRAY(wms->global_marks); PACK_WMS_ARRAY(wms->registers); for (size_t i = 0; i < wms->jumps_size; i++) { - if (shada_pack_encoded_entry(packer, &sd_writer->sd_conv, wms->jumps[i], - max_kbyte) == kSDWriteFailed) { + if (shada_pack_pfreed_entry(packer, wms->jumps[i], max_kbyte) + == kSDWriteFailed) { ret = kSDWriteFailed; goto shada_write_exit; } @@ -2835,8 +2715,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, #define PACK_WMS_ENTRY(wms_entry) \ do { \ if (wms_entry.data.type != kSDItemMissing) { \ - if (shada_pack_encoded_entry(packer, &sd_writer->sd_conv, wms_entry, \ - max_kbyte) == kSDWriteFailed) { \ + if (shada_pack_pfreed_entry(packer, wms_entry, max_kbyte) \ + == kSDWriteFailed) { \ ret = kSDWriteFailed; \ goto shada_write_exit; \ } \ @@ -2863,9 +2743,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, for (size_t i = 0; i < file_markss_to_dump; i++) { PACK_WMS_ARRAY(all_file_markss[i]->marks); for (size_t j = 0; j < all_file_markss[i]->changes_size; j++) { - if (shada_pack_encoded_entry(packer, &sd_writer->sd_conv, - all_file_markss[i]->changes[j], - max_kbyte) == kSDWriteFailed) { + if (shada_pack_pfreed_entry(packer, all_file_markss[i]->changes[j], + max_kbyte) == kSDWriteFailed) { ret = kSDWriteFailed; goto shada_write_exit; } @@ -2889,8 +2768,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, if (dump_one_history[i]) { hms_insert_whole_neovim_history(&wms->hms[i]); HMS_ITER(&wms->hms[i], cur_entry, { - if (shada_pack_encoded_entry( - packer, &sd_writer->sd_conv, (PossiblyFreedShadaEntry) { + if (shada_pack_pfreed_entry( + packer, (PossiblyFreedShadaEntry) { .data = cur_entry->data, .can_free_entry = cur_entry->can_free_entry, }, max_kbyte) == kSDWriteFailed) { @@ -2940,7 +2819,6 @@ int shada_write_file(const char *const file, bool nomerge) .write = &write_file, .close = &close_sd_writer, .error = NULL, - .sd_conv.vc_type = CONV_NONE, }; ShaDaReadDef sd_reader = { .close = NULL }; @@ -3042,7 +2920,7 @@ shada_write_file_nomerge: {} verbose_leave(); } - convert_setup(&sd_writer.sd_conv, p_enc, "utf-8"); + assert(STRCMP(p_enc, "utf-8") == 0); const ShaDaWriteResult sw_ret = shada_write(&sd_writer, (nomerge ? NULL @@ -3331,29 +3209,6 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, return kSDReadStatusSuccess; } -/// Convert or copy and return a string -/// -/// @param[in] sd_conv Conversion definition. -/// @param[in] str String to convert. -/// @param[in] len String length. -/// -/// @return [allocated] converted string or copy of the original string. -static inline char *get_converted_string(const vimconv_T *const sd_conv, - const char *const str, - const size_t len) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT -{ - if (!has_non_ascii_len(str, len)) { - return xmemdupz(str, len); - } - size_t new_len = len; - char *const new_str = string_convert(sd_conv, str, &new_len); - if (new_str == NULL) { - return xmemdupz(str, len); - } - return new_str; -} - #define READERR(entry_name, error_desc) \ RERR "Error while reading ShaDa file: " \ entry_name " entry at position %" PRIu64 " " \ @@ -3431,10 +3286,7 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv, sizeof(*unpacked.data.via.map.ptr)); \ ad_ga.ga_len++; \ } -#define CONVERTED(str, len) ( \ - sd_reader->sd_conv.vc_type != CONV_NONE \ - ? get_converted_string(&sd_reader->sd_conv, (str), (len)) \ - : xmemdupz((str), (len))) +#define CONVERTED(str, len) (xmemdupz((str), (len))) #define BIN_CONVERTED(b) CONVERTED(b.ptr, b.size) #define SET_ADDITIONAL_DATA(tgt, name) \ do { \ @@ -3807,30 +3659,14 @@ shada_read_next_item_start: (char) unpacked.data.via.array.ptr[2].via.u64; } size_t strsize; - if (sd_reader->sd_conv.vc_type == CONV_NONE - || !has_non_ascii_len(unpacked.data.via.array.ptr[1].via.bin.ptr, - unpacked.data.via.array.ptr[1].via.bin.size)) { -shada_read_next_item_hist_no_conv: - strsize = ( - unpacked.data.via.array.ptr[1].via.bin.size - + 1 // Zero byte - + 1); // Separator character - entry->data.history_item.string = xmalloc(strsize); - memcpy(entry->data.history_item.string, - unpacked.data.via.array.ptr[1].via.bin.ptr, - unpacked.data.via.array.ptr[1].via.bin.size); - } else { - size_t len = unpacked.data.via.array.ptr[1].via.bin.size; - char *const converted = string_convert( - &sd_reader->sd_conv, unpacked.data.via.array.ptr[1].via.bin.ptr, - &len); - if (converted != NULL) { - strsize = len + 2; - entry->data.history_item.string = xrealloc(converted, strsize); - } else { - goto shada_read_next_item_hist_no_conv; - } - } + strsize = ( + unpacked.data.via.array.ptr[1].via.bin.size + + 1 // Zero byte + + 1); // Separator character + entry->data.history_item.string = xmalloc(strsize); + memcpy(entry->data.history_item.string, + unpacked.data.via.array.ptr[1].via.bin.ptr, + unpacked.data.via.array.ptr[1].via.bin.size); entry->data.history_item.string[strsize - 2] = 0; entry->data.history_item.string[strsize - 1] = entry->data.history_item.sep; @@ -3863,16 +3699,6 @@ shada_read_next_item_hist_no_conv: "be converted to the VimL value")), initial_fpos); goto shada_read_next_item_error; } - if (sd_reader->sd_conv.vc_type != CONV_NONE) { - typval_T tgttv; - var_item_copy(&sd_reader->sd_conv, - &entry->data.global_var.value, - &tgttv, - true, - 0); - tv_clear(&entry->data.global_var.value); - entry->data.global_var.value = tgttv; - } SET_ADDITIONAL_ELEMENTS(unpacked.data.via.array, 2, entry->data.global_var.additional_elements, "variable"); From dc9722326e797453094b5d82decb5369b1086139 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 8 Apr 2017 19:06:03 +0300 Subject: [PATCH 0374/1671] unittests: Do not alter p_enc in decode unit test --- test/unit/eval/decode_spec.lua | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/test/unit/eval/decode_spec.lua b/test/unit/eval/decode_spec.lua index 0b2a423cd6..6145cf6358 100644 --- a/test/unit/eval/decode_spec.lua +++ b/test/unit/eval/decode_spec.lua @@ -72,7 +72,7 @@ describe('json_decode_string()', function() end itp('does not overflow in error messages', function() - local saved_p_enc = decode.p_enc + collectgarbage('restart') check_failure(']test', 1, 'E474: No container to close: ]') check_failure('[}test', 2, 'E474: Closing list with curly bracket: }') check_failure('{]test', 2, @@ -105,10 +105,6 @@ describe('json_decode_string()', function() check_failure('"\194"test', 3, 'E474: Only UTF-8 strings allowed: \194"') check_failure('"\252\144\128\128\128\128"test', 8, 'E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: \252\144\128\128\128\128"') check_failure('"test', 1, 'E474: Expected string end: "') - decode.p_enc = to_cstr('latin1') - check_failure('"\\uABCD"test', 8, - 'E474: Failed to convert string "ꯍ" from UTF-8') - decode.p_enc = saved_p_enc check_failure('-test', 1, 'E474: Missing number after minus sign: -') check_failure('-1.test', 3, 'E474: Missing number after decimal dot: -1.') check_failure('-1.0etest', 5, 'E474: Missing exponent: -1.0e') From ab19fa155203a4071cb8e780db7d3480b562aee0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 8 Apr 2017 19:11:42 +0300 Subject: [PATCH 0375/1671] *: Fix linter errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drops comments `// for …` that do not pass linter for them being unmaintainable and fast to becoming incomplete or even incorrect. Mention @dedmass --- src/nvim/digraph.h | 4 ++-- src/nvim/eval/decode.c | 6 +++--- src/nvim/ex_cmds.h | 4 ++-- src/nvim/ex_getln.h | 6 +++--- src/nvim/fold.h | 8 ++++---- src/nvim/memfile_defs.h | 4 ++-- src/nvim/move.h | 4 ++-- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/nvim/digraph.h b/src/nvim/digraph.h index d0f10eaf78..1b73ccaf3f 100644 --- a/src/nvim/digraph.h +++ b/src/nvim/digraph.h @@ -1,8 +1,8 @@ #ifndef NVIM_DIGRAPH_H #define NVIM_DIGRAPH_H -#include "nvim/types.h" // for char_u -#include "nvim/ex_cmds_defs.h" // for exarg_T +#include "nvim/types.h" +#include "nvim/ex_cmds_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "digraph.h.generated.h" diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index c7ca3a8ce5..f9889ca547 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -755,9 +755,9 @@ json_decode_string_cycle_start: break; } case '"': { - if (parse_json_string( - buf, buf_len, &p, &stack, &container_stack, - &next_map_special, &didcomma, &didcolon) == FAIL) { + if (parse_json_string(buf, buf_len, &p, &stack, &container_stack, + &next_map_special, &didcomma, &didcolon) + == FAIL) { // Error message was already given goto json_decode_string_fail; } diff --git a/src/nvim/ex_cmds.h b/src/nvim/ex_cmds.h index 792e2f772f..b564cde56c 100644 --- a/src/nvim/ex_cmds.h +++ b/src/nvim/ex_cmds.h @@ -6,8 +6,8 @@ #include "nvim/os/time.h" #include "nvim/pos.h" #include "nvim/eval/typval.h" -#include "nvim/buffer_defs.h" // for buf_T and win_T -#include "nvim/ex_cmds_defs.h" // for exarg_T +#include "nvim/buffer_defs.h" +#include "nvim/ex_cmds_defs.h" // flags for do_ecmd() #define ECMD_HIDE 0x01 // don't free the current buffer diff --git a/src/nvim/ex_getln.h b/src/nvim/ex_getln.h index a29c8297d5..051564fbe1 100644 --- a/src/nvim/ex_getln.h +++ b/src/nvim/ex_getln.h @@ -3,9 +3,9 @@ #include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" -#include "nvim/ex_cmds_defs.h" // for exarg_T -#include "nvim/os/time.h" // for Timestamp -#include "nvim/regexp_defs.h" // for regmatch_T +#include "nvim/ex_cmds_defs.h" +#include "nvim/os/time.h" +#include "nvim/regexp_defs.h" /* Values for nextwild() and ExpandOne(). See ExpandOne() for meaning. */ #define WILD_FREE 1 diff --git a/src/nvim/fold.h b/src/nvim/fold.h index 2393f4ef47..f35b328fb1 100644 --- a/src/nvim/fold.h +++ b/src/nvim/fold.h @@ -1,12 +1,12 @@ #ifndef NVIM_FOLD_H #define NVIM_FOLD_H -#include // for FILE +#include #include "nvim/pos.h" -#include "nvim/garray.h" // for garray_T -#include "nvim/types.h" // for char_u -#include "nvim/buffer_defs.h" // for win_T +#include "nvim/garray.h" +#include "nvim/types.h" +#include "nvim/buffer_defs.h" /* * Info used to pass info about a fold from the fold-detection code to the diff --git a/src/nvim/memfile_defs.h b/src/nvim/memfile_defs.h index 57d8abbe30..b3c2f3564c 100644 --- a/src/nvim/memfile_defs.h +++ b/src/nvim/memfile_defs.h @@ -3,10 +3,10 @@ #include #include -#include // for size_t +#include #include "nvim/types.h" -#include "nvim/pos.h" // for linenr_T +#include "nvim/pos.h" /// A block number. /// diff --git a/src/nvim/move.h b/src/nvim/move.h index 1cdf1f6f52..00fbcc580f 100644 --- a/src/nvim/move.h +++ b/src/nvim/move.h @@ -2,8 +2,8 @@ #define NVIM_MOVE_H #include -#include "nvim/buffer_defs.h" // for win_T -#include "nvim/pos.h" // for linenr_T +#include "nvim/buffer_defs.h" +#include "nvim/pos.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "move.h.generated.h" From cd0a436622d0eeafcbc79e0a6e53088b881ab5b1 Mon Sep 17 00:00:00 2001 From: dedmass Date: Thu, 6 Apr 2017 17:06:35 -0400 Subject: [PATCH 0376/1671] refactor/single-include Closes #6463 refactor/single-include: file_search.h Closes #6455 refactor/single-include: hardcopy.h Closes #6457 refactor/single-include: if_cscope.h Closes #6458 refactor/single-include: mark.h Closes #6461 refactor/single-include: mbyte.h Closes #6462 refactor/single-include: memline.h Closes #6464 refactor/single-include: menu.h Closes #6468 refactor/single-include: ops.h Closes #6470 --- src/nvim/CMakeLists.txt | 9 --------- src/nvim/file_search.h | 5 +++++ src/nvim/hardcopy.h | 4 ++++ src/nvim/if_cscope.h | 3 +++ src/nvim/mark.h | 1 + src/nvim/mbyte.h | 1 + src/nvim/memline.h | 2 ++ src/nvim/menu.h | 5 +++++ src/nvim/ops.h | 2 ++ 9 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 24a6ec8b5e..e0f4944762 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -424,19 +424,10 @@ function(get_test_target prefix sfile relative_path_var target_var) endfunction() set(NO_SINGLE_CHECK_HEADERS - file_search.h getchar.h - hardcopy.h - if_cscope.h if_cscope_defs.h - mark.h - mbyte.h - memfile_defs.h - memline.h - menu.h misc2.h msgpack_rpc/server.h - ops.h option.h os/shell.h os_unix.h diff --git a/src/nvim/file_search.h b/src/nvim/file_search.h index 833a1a05ff..b128029123 100644 --- a/src/nvim/file_search.h +++ b/src/nvim/file_search.h @@ -1,6 +1,11 @@ #ifndef NVIM_FILE_SEARCH_H #define NVIM_FILE_SEARCH_H +#include // for size_t + +#include "nvim/types.h" // for char_u +#include "nvim/globals.h" // for CdScope + /* Flags for find_file_*() functions. */ #define FINDFILE_FILE 0 /* only files */ #define FINDFILE_DIR 1 /* only directories */ diff --git a/src/nvim/hardcopy.h b/src/nvim/hardcopy.h index 4ead8dd5d4..a70b20e6f5 100644 --- a/src/nvim/hardcopy.h +++ b/src/nvim/hardcopy.h @@ -2,6 +2,10 @@ #define NVIM_HARDCOPY_H #include +#include // for size_t + +#include "nvim/types.h" // for char_u +#include "nvim/ex_cmds_defs.h" // for exarg_T /* * Structure to hold printing color and font attributes. diff --git a/src/nvim/if_cscope.h b/src/nvim/if_cscope.h index 351d9caef6..e20462576a 100644 --- a/src/nvim/if_cscope.h +++ b/src/nvim/if_cscope.h @@ -1,6 +1,9 @@ #ifndef NVIM_IF_CSCOPE_H #define NVIM_IF_CSCOPE_H +#include "nvim/types.h" // for char_u and expand_T +#include "nvim/ex_cmds_defs.h" // for exarg_T + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "if_cscope.h.generated.h" #endif diff --git a/src/nvim/mark.h b/src/nvim/mark.h index efba9708db..c22a102926 100644 --- a/src/nvim/mark.h +++ b/src/nvim/mark.h @@ -8,6 +8,7 @@ #include "nvim/memory.h" #include "nvim/pos.h" #include "nvim/os/time.h" +#include "nvim/ex_cmds_defs.h" // for exarg_T /// Set fmark using given value #define SET_FMARK(fmarkp_, mark_, fnum_) \ diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h index 3565202466..ad9e38004c 100644 --- a/src/nvim/mbyte.h +++ b/src/nvim/mbyte.h @@ -7,6 +7,7 @@ #include "nvim/iconv.h" #include "nvim/func_attr.h" #include "nvim/os/os_defs.h" // For WCHAR, indirect +#include "nvim/types.h" // for char_u /* * Return byte length of character that starts with byte "b". diff --git a/src/nvim/memline.h b/src/nvim/memline.h index f84e86fea0..a239c6a031 100644 --- a/src/nvim/memline.h +++ b/src/nvim/memline.h @@ -2,6 +2,8 @@ #define NVIM_MEMLINE_H #include "nvim/types.h" +#include "nvim/pos.h" // for pos_T, linenr_T, colnr_T +#include "nvim/buffer_defs.h" // for buf_T #ifdef INCLUDE_GENERATED_DECLARATIONS # include "memline.h.generated.h" diff --git a/src/nvim/menu.h b/src/nvim/menu.h index 3266c511b4..a84b7d812e 100644 --- a/src/nvim/menu.h +++ b/src/nvim/menu.h @@ -1,6 +1,11 @@ #ifndef NVIM_MENU_H #define NVIM_MENU_H +#include // for bool + +#include "nvim/types.h" // for char_u and expand_T +#include "nvim/ex_cmds_defs.h" // for exarg_T + /* Indices into vimmenu_T->strings[] and vimmenu_T->noremap[] for each mode */ #define MENU_INDEX_INVALID -1 #define MENU_INDEX_NORMAL 0 diff --git a/src/nvim/ops.h b/src/nvim/ops.h index 13d0142343..a8867e02ea 100644 --- a/src/nvim/ops.h +++ b/src/nvim/ops.h @@ -8,6 +8,8 @@ #include "nvim/types.h" #include "nvim/eval/typval.h" #include "nvim/os/time.h" +#include "nvim/normal.h" // for MotionType and oparg_T +#include "nvim/ex_cmds_defs.h" // for exarg_T typedef int (*Indenter)(void); From 467a02f88d278cb9e2d0d0ac716a3e228de51304 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 8 Apr 2017 19:34:01 +0300 Subject: [PATCH 0377/1671] doc: Update documentation --- runtime/doc/eval.txt | 4 ++-- runtime/doc/options.txt | 4 ++-- runtime/doc/starting.txt | 24 +++++------------------- 3 files changed, 9 insertions(+), 23 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index e21f5357a2..16f9a2ea6e 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -4944,8 +4944,8 @@ json_decode({expr}) *json_decode()* json_encode({expr}) *json_encode()* Convert {expr} into a JSON string. Accepts - |msgpack-special-dict| as the input. Will not convert |Funcref|s, - mappings with non-string keys (can be created as + |msgpack-special-dict| as the input. Will not convert + |Funcref|s, mappings with non-string keys (can be created as |msgpack-special-dict|), values with self-referencing containers, strings which contain non-UTF-8 characters, pseudo-UTF-8 strings which contain codepoints reserved for diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index d212e029aa..c30a88f48d 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -5151,8 +5151,8 @@ A jump table for the options with a short description can be found at |Q_op|. saved. When not included, the value of 'history' is used. *shada-c* c Dummy option, kept for compatibility reasons. Has no actual - effect. Current encoding state is described in - |shada-encoding|. + effect: ShaDa always uses UTF-8 and 'encoding' value is fixed + to UTF-8 as well. *shada-f* f Whether file marks need to be stored. If zero, file marks ('0 to '9, 'A to 'Z) are not stored. When not present or when diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index daf6ad9ca2..2d1dd22222 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -1097,23 +1097,6 @@ SHADA FILE NAME *shada-file-name* default and the name given with 'shada' or "-i" (unless it's NONE). -CHARACTER ENCODING *shada-encoding* - -The text in the ShaDa file is UTF-8-encoded. Normally you will always work -with the same 'encoding' value, and this works just fine. However, if you -read the ShaDa file with value for 'encoding' different from utf-8 and -'encoding' used when writing ShaDa file, some of the text (non-ASCII -characters) may be invalid as Neovim always attempts to convert the text in -the ShaDa file from the UTF-8 to the current 'encoding' value. Filenames are -never converted, affected elements are: - -- history strings; -- variable values; -- register values; -- last used search and substitute patterns; -- last used substitute replacement string. - - MANUALLY READING AND WRITING *shada-read-write* Two commands can be used to read and write the ShaDa file manually. This @@ -1221,8 +1204,11 @@ exactly four MessagePack objects: 3. Third goes the length of the fourth entry. Unsigned integer as well, used for fast skipping without parsing. 4. Fourth is actual entry data. All currently used ShaDa entries use - containers to hold data: either map or array. Exact format depends on the - entry type: + containers to hold data: either map or array. All string values in those + containers are either binary (applies to filenames) or UTF-8, yet parser + needs to expect that invalid bytes may be present in a UTF-8 string. + + Exact format depends on the entry type: Entry type (name) Entry data ~ 1 (Header) Map containing data that describes the generator From 7701014b659d3505c6d4f0de95c06042155c98d1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 8 Apr 2017 20:22:46 +0300 Subject: [PATCH 0378/1671] *: Remove useless asserts --- src/nvim/eval/decode.c | 1 - src/nvim/eval/encode.c | 1 - src/nvim/shada.c | 2 -- 3 files changed, 4 deletions(-) diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index f9889ca547..a7dc6b205d 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -609,7 +609,6 @@ int json_decode_string(const char *const buf, const size_t buf_len, EMSG(_("E474: Attempt to decode a blank string")); return FAIL; } - assert(STRCMP(p_enc, "utf-8") == 0); int ret = OK; ValuesStack stack = KV_INITIAL_VALUE; ContainerStack container_stack = KV_INITIAL_VALUE; diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index b64217f969..d74913a481 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -564,7 +564,6 @@ static inline int convert_to_json_string(garray_T *const gap, } else { size_t utf_len = len; char *tofree = NULL; - assert(STRCMP(p_enc, "utf-8") == 0); size_t str_len = 0; // Encode character as \uNNNN if // 1. It is an ASCII control character (0x0 .. 0x1F; 0x7F not diff --git a/src/nvim/shada.c b/src/nvim/shada.c index cfe0bd8774..8c5d6dff65 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -2920,8 +2920,6 @@ shada_write_file_nomerge: {} verbose_leave(); } - assert(STRCMP(p_enc, "utf-8") == 0); - const ShaDaWriteResult sw_ret = shada_write(&sd_writer, (nomerge ? NULL : &sd_reader)); From 7b6b629e1a1e3c9f49f75f3bf2c53bb76f77e209 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 8 Apr 2017 20:30:26 +0300 Subject: [PATCH 0379/1671] api: Add FUNC_API_SINCE(1) to new functions --- src/nvim/api/vim.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 511c87f408..05317b8d69 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -844,6 +844,7 @@ static void write_msg(String message, bool to_err) /// /// @return its argument. Object _vim_id(Object obj) + FUNC_API_SINCE(1) { return copy_object(obj); } @@ -857,6 +858,7 @@ Object _vim_id(Object obj) /// /// @return its argument. Array _vim_id_array(Array arr) + FUNC_API_SINCE(1) { return copy_object(ARRAY_OBJ(arr)).data.array; } @@ -870,6 +872,7 @@ Array _vim_id_array(Array arr) /// /// @return its argument. Dictionary _vim_id_dictionary(Dictionary dct) + FUNC_API_SINCE(1) { return copy_object(DICTIONARY_OBJ(dct)).data.dictionary; } @@ -883,6 +886,7 @@ Dictionary _vim_id_dictionary(Dictionary dct) /// /// @return its argument. Float _vim_id_float(Float flt) + FUNC_API_SINCE(1) { return flt; } From a40a969e9a4776f1e274dcf0e59c8f1ec1770ca0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 8 Apr 2017 20:33:48 +0300 Subject: [PATCH 0380/1671] api: Rename _vim_id functions to nvim__id --- src/nvim/api/vim.c | 8 +-- test/functional/lua/api_spec.lua | 104 +++++++++++++++---------------- 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 05317b8d69..9ee3827040 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -843,7 +843,7 @@ static void write_msg(String message, bool to_err) /// @param[in] obj Object to return. /// /// @return its argument. -Object _vim_id(Object obj) +Object nvim__id(Object obj) FUNC_API_SINCE(1) { return copy_object(obj); @@ -857,7 +857,7 @@ Object _vim_id(Object obj) /// @param[in] arr Array to return. /// /// @return its argument. -Array _vim_id_array(Array arr) +Array nvim__id_array(Array arr) FUNC_API_SINCE(1) { return copy_object(ARRAY_OBJ(arr)).data.array; @@ -871,7 +871,7 @@ Array _vim_id_array(Array arr) /// @param[in] dct Dictionary to return. /// /// @return its argument. -Dictionary _vim_id_dictionary(Dictionary dct) +Dictionary nvim__id_dictionary(Dictionary dct) FUNC_API_SINCE(1) { return copy_object(DICTIONARY_OBJ(dct)).data.dictionary; @@ -885,7 +885,7 @@ Dictionary _vim_id_dictionary(Dictionary dct) /// @param[in] flt Value to return. /// /// @return its argument. -Float _vim_id_float(Float flt) +Float nvim__id_float(Float flt) FUNC_API_SINCE(1) { return flt; diff --git a/test/functional/lua/api_spec.lua b/test/functional/lua/api_spec.lua index f6f65cb741..c74dfb90a0 100644 --- a/test/functional/lua/api_spec.lua +++ b/test/functional/lua/api_spec.lua @@ -73,84 +73,84 @@ describe('luaeval(vim.api.…)', function() end) it('correctly converts to API objects', function() - eq(1, funcs.luaeval('vim.api._vim_id(1)')) - eq('1', funcs.luaeval('vim.api._vim_id("1")')) - eq({1}, funcs.luaeval('vim.api._vim_id({1})')) - eq({foo=1}, funcs.luaeval('vim.api._vim_id({foo=1})')) - eq(1.5, funcs.luaeval('vim.api._vim_id(1.5)')) - eq(true, funcs.luaeval('vim.api._vim_id(true)')) - eq(false, funcs.luaeval('vim.api._vim_id(false)')) - eq(NIL, funcs.luaeval('vim.api._vim_id(nil)')) + eq(1, funcs.luaeval('vim.api.nvim__id(1)')) + eq('1', funcs.luaeval('vim.api.nvim__id("1")')) + eq({1}, funcs.luaeval('vim.api.nvim__id({1})')) + eq({foo=1}, funcs.luaeval('vim.api.nvim__id({foo=1})')) + eq(1.5, funcs.luaeval('vim.api.nvim__id(1.5)')) + eq(true, funcs.luaeval('vim.api.nvim__id(true)')) + eq(false, funcs.luaeval('vim.api.nvim__id(false)')) + eq(NIL, funcs.luaeval('vim.api.nvim__id(nil)')) - eq(0, eval([[type(luaeval('vim.api._vim_id(1)'))]])) - eq(1, eval([[type(luaeval('vim.api._vim_id("1")'))]])) - eq(3, eval([[type(luaeval('vim.api._vim_id({1})'))]])) - eq(4, eval([[type(luaeval('vim.api._vim_id({foo=1})'))]])) - eq(5, eval([[type(luaeval('vim.api._vim_id(1.5)'))]])) - eq(6, eval([[type(luaeval('vim.api._vim_id(true)'))]])) - eq(6, eval([[type(luaeval('vim.api._vim_id(false)'))]])) - eq(7, eval([[type(luaeval('vim.api._vim_id(nil)'))]])) + eq(0, eval([[type(luaeval('vim.api.nvim__id(1)'))]])) + eq(1, eval([[type(luaeval('vim.api.nvim__id("1")'))]])) + eq(3, eval([[type(luaeval('vim.api.nvim__id({1})'))]])) + eq(4, eval([[type(luaeval('vim.api.nvim__id({foo=1})'))]])) + eq(5, eval([[type(luaeval('vim.api.nvim__id(1.5)'))]])) + eq(6, eval([[type(luaeval('vim.api.nvim__id(true)'))]])) + eq(6, eval([[type(luaeval('vim.api.nvim__id(false)'))]])) + eq(7, eval([[type(luaeval('vim.api.nvim__id(nil)'))]])) - eq({foo=1, bar={42, {{baz=true}, 5}}}, funcs.luaeval('vim.api._vim_id({foo=1, bar={42, {{baz=true}, 5}}})')) + eq({foo=1, bar={42, {{baz=true}, 5}}}, funcs.luaeval('vim.api.nvim__id({foo=1, bar={42, {{baz=true}, 5}}})')) end) it('correctly converts container objects with type_idx to API objects', function() - eq(5, eval('type(luaeval("vim.api._vim_id({[vim.type_idx]=vim.types.float, [vim.val_idx]=0})"))')) - eq(4, eval([[type(luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.dictionary})'))]])) - eq(3, eval([[type(luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.array})'))]])) + eq(5, eval('type(luaeval("vim.api.nvim__id({[vim.type_idx]=vim.types.float, [vim.val_idx]=0})"))')) + eq(4, eval([[type(luaeval('vim.api.nvim__id({[vim.type_idx]=vim.types.dictionary})'))]])) + eq(3, eval([[type(luaeval('vim.api.nvim__id({[vim.type_idx]=vim.types.array})'))]])) - eq({}, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.array})')) + eq({}, funcs.luaeval('vim.api.nvim__id({[vim.type_idx]=vim.types.array})')) -- Presence of type_idx makes Vim ignore some keys - eq({42}, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) - eq({foo=2}, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) - eq(10, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) - eq({}, funcs.luaeval('vim.api._vim_id({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2})')) + eq({42}, funcs.luaeval('vim.api.nvim__id({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) + eq({foo=2}, funcs.luaeval('vim.api.nvim__id({[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) + eq(10, funcs.luaeval('vim.api.nvim__id({[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) + eq({}, funcs.luaeval('vim.api.nvim__id({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2})')) end) it('correctly converts arrays with type_idx to API objects', function() - eq(3, eval([[type(luaeval('vim.api._vim_id_array({[vim.type_idx]=vim.types.array})'))]])) + eq(3, eval([[type(luaeval('vim.api.nvim__id_array({[vim.type_idx]=vim.types.array})'))]])) - eq({}, funcs.luaeval('vim.api._vim_id_array({[vim.type_idx]=vim.types.array})')) + eq({}, funcs.luaeval('vim.api.nvim__id_array({[vim.type_idx]=vim.types.array})')) - eq({42}, funcs.luaeval('vim.api._vim_id_array({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) - eq({{foo=2}}, funcs.luaeval('vim.api._vim_id_array({{[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})')) - eq({10}, funcs.luaeval('vim.api._vim_id_array({{[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})')) - eq({}, funcs.luaeval('vim.api._vim_id_array({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2})')) + eq({42}, funcs.luaeval('vim.api.nvim__id_array({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) + eq({{foo=2}}, funcs.luaeval('vim.api.nvim__id_array({{[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})')) + eq({10}, funcs.luaeval('vim.api.nvim__id_array({{[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})')) + eq({}, funcs.luaeval('vim.api.nvim__id_array({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2})')) - eq({}, funcs.luaeval('vim.api._vim_id_array({})')) - eq(3, eval([[type(luaeval('vim.api._vim_id_array({})'))]])) + eq({}, funcs.luaeval('vim.api.nvim__id_array({})')) + eq(3, eval([[type(luaeval('vim.api.nvim__id_array({})'))]])) end) it('correctly converts dictionaries with type_idx to API objects', function() - eq(4, eval([[type(luaeval('vim.api._vim_id_dictionary({[vim.type_idx]=vim.types.dictionary})'))]])) + eq(4, eval([[type(luaeval('vim.api.nvim__id_dictionary({[vim.type_idx]=vim.types.dictionary})'))]])) - eq({}, funcs.luaeval('vim.api._vim_id_dictionary({[vim.type_idx]=vim.types.dictionary})')) + eq({}, funcs.luaeval('vim.api.nvim__id_dictionary({[vim.type_idx]=vim.types.dictionary})')) - eq({v={42}}, funcs.luaeval('vim.api._vim_id_dictionary({v={[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})')) - eq({foo=2}, funcs.luaeval('vim.api._vim_id_dictionary({[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) - eq({v=10}, funcs.luaeval('vim.api._vim_id_dictionary({v={[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})')) - eq({v={}}, funcs.luaeval('vim.api._vim_id_dictionary({v={[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2}})')) + eq({v={42}}, funcs.luaeval('vim.api.nvim__id_dictionary({v={[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})')) + eq({foo=2}, funcs.luaeval('vim.api.nvim__id_dictionary({[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})')) + eq({v=10}, funcs.luaeval('vim.api.nvim__id_dictionary({v={[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})')) + eq({v={}}, funcs.luaeval('vim.api.nvim__id_dictionary({v={[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2}})')) -- If API requests dictionary, then empty table will be the one. This is not -- the case normally because empty table is an empty arrray. - eq({}, funcs.luaeval('vim.api._vim_id_dictionary({})')) - eq(4, eval([[type(luaeval('vim.api._vim_id_dictionary({})'))]])) + eq({}, funcs.luaeval('vim.api.nvim__id_dictionary({})')) + eq(4, eval([[type(luaeval('vim.api.nvim__id_dictionary({})'))]])) end) it('errors out correctly when working with API', function() -- Conversion errors eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Cannot convert given lua type', - exc_exec([[call luaeval("vim.api._vim_id(vim.api._vim_id)")]])) + exc_exec([[call luaeval("vim.api.nvim__id(vim.api.nvim__id)")]])) eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Cannot convert given lua table', - exc_exec([[call luaeval("vim.api._vim_id({1, foo=42})")]])) + exc_exec([[call luaeval("vim.api.nvim__id({1, foo=42})")]])) eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Cannot convert given lua type', - exc_exec([[call luaeval("vim.api._vim_id({42, vim.api._vim_id})")]])) + exc_exec([[call luaeval("vim.api.nvim__id({42, vim.api.nvim__id})")]])) -- Errors in number of arguments eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Expected 1 argument', - exc_exec([[call luaeval("vim.api._vim_id()")]])) + exc_exec([[call luaeval("vim.api.nvim__id()")]])) eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Expected 1 argument', - exc_exec([[call luaeval("vim.api._vim_id(1, 2)")]])) + exc_exec([[call luaeval("vim.api.nvim__id(1, 2)")]])) eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Expected 2 arguments', exc_exec([[call luaeval("vim.api.nvim_set_var(1, 2, 3)")]])) -- Error in argument types @@ -163,19 +163,19 @@ describe('luaeval(vim.api.…)', function() exc_exec([[call luaeval("vim.api.nvim_buf_get_lines(0, 1.5, 1, false)")]])) eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Expected lua table', - exc_exec([[call luaeval("vim.api._vim_id_float('test')")]])) + exc_exec([[call luaeval("vim.api.nvim__id_float('test')")]])) eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Unexpected type', - exc_exec([[call luaeval("vim.api._vim_id_float({[vim.type_idx]=vim.types.dictionary})")]])) + exc_exec([[call luaeval("vim.api.nvim__id_float({[vim.type_idx]=vim.types.dictionary})")]])) eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Expected lua table', - exc_exec([[call luaeval("vim.api._vim_id_array(1)")]])) + exc_exec([[call luaeval("vim.api.nvim__id_array(1)")]])) eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Unexpected type', - exc_exec([[call luaeval("vim.api._vim_id_array({[vim.type_idx]=vim.types.dictionary})")]])) + exc_exec([[call luaeval("vim.api.nvim__id_array({[vim.type_idx]=vim.types.dictionary})")]])) eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Expected lua table', - exc_exec([[call luaeval("vim.api._vim_id_dictionary(1)")]])) + exc_exec([[call luaeval("vim.api.nvim__id_dictionary(1)")]])) eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string ""]:1: Unexpected type', - exc_exec([[call luaeval("vim.api._vim_id_dictionary({[vim.type_idx]=vim.types.array})")]])) + exc_exec([[call luaeval("vim.api.nvim__id_dictionary({[vim.type_idx]=vim.types.array})")]])) -- TODO: check for errors with Tabpage argument -- TODO: check for errors with Window argument -- TODO: check for errors with Buffer argument From 1ef98b34b3171452e55d656d26b96d803236bf65 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 8 Apr 2017 21:46:30 +0300 Subject: [PATCH 0381/1671] functests: Refactor 009_bufleave_autocommand_spec It was not testing anything actually: the `e yy` command simply failed because of unsaved changes, BufLeave never run thus. --- .../legacy/009_bufleave_autocommand_spec.lua | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/test/functional/legacy/009_bufleave_autocommand_spec.lua b/test/functional/legacy/009_bufleave_autocommand_spec.lua index 7481c639cf..e2b3dca685 100644 --- a/test/functional/legacy/009_bufleave_autocommand_spec.lua +++ b/test/functional/legacy/009_bufleave_autocommand_spec.lua @@ -1,19 +1,27 @@ -- Test for Bufleave autocommand that deletes the buffer we are about to edit. local helpers = require('test.functional.helpers')(after_each) -local clear, insert = helpers.clear, helpers.insert -local execute, expect = helpers.execute, helpers.expect + +local eq = helpers.eq +local clear = helpers.clear +local meths = helpers.meths +local expect = helpers.expect +local command = helpers.command +local exc_exec = helpers.exc_exec +local curbufmeths = helpers.curbufmeths describe('BufLeave autocommand', function() setup(clear) it('is working', function() - insert([[ - start of test file xx - end of test file xx]]) + meths.set_option('hidden', true) + curbufmeths.set_lines(0, 1, false, { + 'start of test file xx', + 'end of test file xx'}) - execute('au BufLeave * bwipe yy') - execute('e yy') + command('autocmd BufLeave * bwipeout yy') + eq('Vim(edit):E143: Autocommands unexpectedly deleted new buffer yy', + exc_exec('edit yy')) expect([[ start of test file xx From b0731290e89dae0a2957e18cfa9ab11ad7290af8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 8 Apr 2017 21:50:14 +0300 Subject: [PATCH 0382/1671] functests: Move test from legacy/009 to autocmd/autocmd --- test/functional/autocmd/autocmd_spec.lua | 27 +++++++++++++++++ .../legacy/009_bufleave_autocommand_spec.lua | 30 ------------------- 2 files changed, 27 insertions(+), 30 deletions(-) delete mode 100644 test/functional/legacy/009_bufleave_autocommand_spec.lua diff --git a/test/functional/autocmd/autocmd_spec.lua b/test/functional/autocmd/autocmd_spec.lua index c38bd75c69..9a0c5c2b0c 100644 --- a/test/functional/autocmd/autocmd_spec.lua +++ b/test/functional/autocmd/autocmd_spec.lua @@ -5,6 +5,15 @@ local command = helpers.command local eq = helpers.eq local eval = helpers.eval +local eq = helpers.eq +local eval = helpers.eval +local clear = helpers.clear +local meths = helpers.meths +local expect = helpers.expect +local command = helpers.command +local exc_exec = helpers.exc_exec +local curbufmeths = helpers.curbufmeths + describe('autocmds:', function() before_each(clear) @@ -33,4 +42,22 @@ describe('autocmds:', function() it('v:vim_did_enter is 1 after VimEnter', function() eq(1, eval('v:vim_did_enter')) end) + + describe('BufLeave autocommand', function() + it('can wipe out the buffer created by :edit which triggered autocmd', + function() + meths.set_option('hidden', true) + curbufmeths.set_lines(0, 1, false, { + 'start of test file xx', + 'end of test file xx'}) + + command('autocmd BufLeave * bwipeout yy') + eq('Vim(edit):E143: Autocommands unexpectedly deleted new buffer yy', + exc_exec('edit yy')) + + expect([[ + start of test file xx + end of test file xx]]) + end) + end) end) diff --git a/test/functional/legacy/009_bufleave_autocommand_spec.lua b/test/functional/legacy/009_bufleave_autocommand_spec.lua deleted file mode 100644 index e2b3dca685..0000000000 --- a/test/functional/legacy/009_bufleave_autocommand_spec.lua +++ /dev/null @@ -1,30 +0,0 @@ --- Test for Bufleave autocommand that deletes the buffer we are about to edit. - -local helpers = require('test.functional.helpers')(after_each) - -local eq = helpers.eq -local clear = helpers.clear -local meths = helpers.meths -local expect = helpers.expect -local command = helpers.command -local exc_exec = helpers.exc_exec -local curbufmeths = helpers.curbufmeths - -describe('BufLeave autocommand', function() - setup(clear) - - it('is working', function() - meths.set_option('hidden', true) - curbufmeths.set_lines(0, 1, false, { - 'start of test file xx', - 'end of test file xx'}) - - command('autocmd BufLeave * bwipeout yy') - eq('Vim(edit):E143: Autocommands unexpectedly deleted new buffer yy', - exc_exec('edit yy')) - - expect([[ - start of test file xx - end of test file xx]]) - end) -end) From 47b451c52b105dd4bdb06da82a5bc7359873036c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 8 Apr 2017 21:58:13 +0300 Subject: [PATCH 0383/1671] functests: Refactor legacy/012_directory_spec --- test/functional/legacy/012_directory_spec.lua | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/test/functional/legacy/012_directory_spec.lua b/test/functional/legacy/012_directory_spec.lua index cef31ae405..cec4f93737 100644 --- a/test/functional/legacy/012_directory_spec.lua +++ b/test/functional/legacy/012_directory_spec.lua @@ -3,12 +3,19 @@ -- - "./dir", in directory relative to file -- - "dir", in directory relative to current dir -local helpers = require('test.functional.helpers')(after_each) -local lfs = require('lfs') -local insert, eq = helpers.insert, helpers.eq -local neq, eval = helpers.neq, helpers.eval -local clear, execute = helpers.clear, helpers.execute -local wait, write_file = helpers.wait, helpers.write_file +local helpers = require('test.functional.helpers')(after_each) +local lfs = require('lfs') + +local eq = helpers.eq +local neq = helpers.neq +local wait = helpers.wait +local funcs = helpers.funcs +local meths = helpers.meths +local clear = helpers.clear +local insert = helpers.insert +local command = helpers.command +local write_file = helpers.write_file +local curbufmeths = helpers.curbufmeths local function ls_dir_sorted(dirname) local files = {} @@ -36,7 +43,7 @@ describe("'directory' option", function() clear() end) teardown(function() - execute('qall!') + command('qall!') helpers.rmdir('Xtest.je') helpers.rmdir('Xtest2') os.remove('Xtest1') @@ -49,21 +56,22 @@ describe("'directory' option", function() line 3 Abcdefghij end of testfile]]) - execute('set swapfile') - execute('set dir=.,~') + meths.set_option('swapfile', true) + curbufmeths.set_option('swapfile', true) + meths.set_option('directory', '.') -- sanity check: files should not exist yet. eq(nil, lfs.attributes('.Xtest1.swp')) - execute('e! Xtest1') + command('edit! Xtest1') wait() - eq('Xtest1', eval('buffer_name("%")')) + eq('Xtest1', funcs.buffer_name('%')) -- Verify that the swapfile exists. In the legacy test this was done by -- reading the output from :!ls. neq(nil, lfs.attributes('.Xtest1.swp')) - execute('set dir=./Xtest2,.,~') - execute('e Xtest1') + meths.set_option('directory', './Xtest2,.') + command('edit Xtest1') wait() -- swapfile should no longer exist in CWD. @@ -71,9 +79,9 @@ describe("'directory' option", function() eq({ "Xtest1.swp", "Xtest3" }, ls_dir_sorted("Xtest2")) - execute('set dir=Xtest.je,~') - execute('e Xtest2/Xtest3') - eq(1, eval('&swapfile')) + meths.set_option('directory', 'Xtest.je') + command('edit Xtest2/Xtest3') + eq(true, curbufmeths.get_option('swapfile')) wait() eq({ "Xtest3" }, ls_dir_sorted("Xtest2")) From e31aab8b61565766bbd411fae38550f9ae802dd9 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 8 Apr 2017 22:00:50 +0300 Subject: [PATCH 0384/1671] functests: Refactor legacy/029_join test --- test/functional/helpers.lua | 5 +- test/functional/legacy/029_join_spec.lua | 68 +++++++++++++----------- 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 335cf3c3ff..7793a9a739 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -304,7 +304,7 @@ end -- Executes an ex-command by user input. Because nvim_input() is used, VimL -- errors will not manifest as client (lua) errors. Use command() for that. -local function execute(...) +local function feed_command(...) for _, v in ipairs({...}) do if v:sub(1, 1) ~= '/' then -- not a search command, prefix with colon @@ -565,7 +565,8 @@ local M = { insert = insert, iswin = iswin, feed = feed, - execute = execute, + feed_command = feed_command, + execute = feed_command, -- FIXME Remove eval = nvim_eval, call = nvim_call, command = nvim_command, diff --git a/test/functional/legacy/029_join_spec.lua b/test/functional/legacy/029_join_spec.lua index 7a183fcbec..460b9291bf 100644 --- a/test/functional/legacy/029_join_spec.lua +++ b/test/functional/legacy/029_join_spec.lua @@ -1,8 +1,12 @@ -- Test for joining lines with marks in them (and with 'joinspaces' set/reset) local helpers = require('test.functional.helpers')(after_each) -local feed, insert = helpers.feed, helpers.insert -local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect + +local feed = helpers.feed +local clear = helpers.clear +local insert = helpers.insert +local expect = helpers.expect +local feed_command = helpers.feed_command describe('joining lines', function() before_each(clear) @@ -46,19 +50,19 @@ describe('joining lines', function() -- Switch off 'joinspaces', then join some lines in the buffer using "J". -- Also set a few marks and record their movement when joining lines. - execute('set nojoinspaces') - execute('/firstline/') + feed_command('set nojoinspaces') + feed_command('/firstline/') feed('j"td/^$/') feed('PJjJjJjJjJjJjJjJjJjJjJjJjJjJ') feed('j05lmx2j06lmy2k4Jy3l$p`xyl$p`yy2l$p') -- Do the same with 'joinspaces' on. - execute('set joinspaces') + feed_command('set joinspaces') feed('j"tp') feed('JjJjJjJjJjJjJjJjJjJjJjJjJjJ') feed('j05lmx2j06lmy2k4Jy3l$p`xyl$p`yy2l$po') - execute('1d') + feed_command('1d') expect([[ asdfasdf. asdf @@ -129,20 +133,20 @@ describe('joining lines', function() } ]]) - execute('/^{/+1') - execute('set comments=s1:/*,mb:*,ex:*/,://') - execute('set nojoinspaces') - execute('set backspace=eol,start') + feed_command('/^{/+1') + feed_command('set comments=s1:/*,mb:*,ex:*/,://') + feed_command('set nojoinspaces') + feed_command('set backspace=eol,start') -- With 'joinspaces' switched off, join lines using both "J" and :join and -- verify that comment leaders are stripped or kept as appropriate. - execute('.,+3join') + feed_command('.,+3join') feed('j4J') - execute('.,+2join') + feed_command('.,+2join') feed('j3J') - execute('.,+2join') + feed_command('.,+2join') feed('j3J') - execute('.,+2join') + feed_command('.,+2join') feed('jj3J') expect([[ @@ -180,22 +184,22 @@ describe('joining lines', function() -- As mentioned above, we mimic the wrong initial cursor position in the old -- test by advancing one line further. - execute([[/^\d\+ this]], '+1') + feed_command([[/^\d\+ this]], '+1') -- Test with the default 'backspace' setting. feed('Avim1') feed('Avim2u') - execute('set cpo-=<') - execute('inoremap ') + feed_command('set cpo-=<') + feed_command('inoremap ') feed('Avim3') - execute('iunmap ') + feed_command('iunmap ') feed('Avim4') -- Test with 'backspace' set to the compatible setting. - execute('set backspace=') + feed_command('set backspace=') feed('A vim5A') feed('A vim6Azweiu') - execute('inoremap ') + feed_command('inoremap ') feed('A vim7') expect([[ @@ -283,29 +287,29 @@ describe('joining lines', function() } ]]) - execute('/^{/+1') - execute([[set comments=sO:*\ -,mO:*\ \ ,exO:*/]]) - execute('set comments+=s1:/*,mb:*,ex:*/,://') - execute('set comments+=s1:>#,mb:#,ex:#<,:<') - execute('set backspace=eol,start') + feed_command('/^{/+1') + feed_command([[set comments=sO:*\ -,mO:*\ \ ,exO:*/]]) + feed_command('set comments+=s1:/*,mb:*,ex:*/,://') + feed_command('set comments+=s1:>#,mb:#,ex:#<,:<') + feed_command('set backspace=eol,start') -- With 'joinspaces' on (the default setting), again join lines and verify -- that comment leaders are stripped or kept as appropriate. - execute('.,+3join') + feed_command('.,+3join') feed('j4J') - execute('.,+8join') + feed_command('.,+8join') feed('j9J') - execute('.,+2join') + feed_command('.,+2join') feed('j3J') - execute('.,+2join') + feed_command('.,+2join') feed('j3J') - execute('.,+2join') + feed_command('.,+2join') feed('jj3J') feed('j') - execute('.,+2join') + feed_command('.,+2join') feed('jj3J') feed('j') - execute('.,+5join') + feed_command('.,+5join') feed('j6J') feed('oSome code!// Make sure backspacing does not remove this comment leader.0i') From 7766b24f3cba5eaebbdc2231bfd91a1594c8d209 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 8 Apr 2017 22:02:44 +0300 Subject: [PATCH 0385/1671] functests: Refactor legacy/018_unset_smart_indenting_spec --- .../legacy/018_unset_smart_indenting_spec.lua | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/test/functional/legacy/018_unset_smart_indenting_spec.lua b/test/functional/legacy/018_unset_smart_indenting_spec.lua index ba1eac02cb..94fbb283f4 100644 --- a/test/functional/legacy/018_unset_smart_indenting_spec.lua +++ b/test/functional/legacy/018_unset_smart_indenting_spec.lua @@ -1,11 +1,15 @@ -- Tests for not doing smart indenting when it isn't set. local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local execute, expect = helpers.execute, helpers.expect + +local feed = helpers.feed +local clear = helpers.clear +local insert = helpers.insert +local expect = helpers.expect +local feed_command = helpers.feed_command describe('unset smart indenting', function() - setup(clear) + before_each(clear) it('is working', function() insert([[ @@ -15,8 +19,8 @@ describe('unset smart indenting', function() test text test text]]) - execute('set nocin nosi ai') - execute('/some') + feed_command('set nocin nosi ai') + feed_command('/some') feed('2cc#test') expect([[ From cca029bc8d06fbf2f66719e4b15fd70e26c013d4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 8 Apr 2017 22:17:39 +0300 Subject: [PATCH 0386/1671] functests: Refactor legacy/003_cindent_spec and legacy/increment_spec --- test/functional/legacy/003_cindent_spec.lua | 1134 +++++++++---------- test/functional/legacy/increment_spec.lua | 6 +- 2 files changed, 570 insertions(+), 570 deletions(-) diff --git a/test/functional/legacy/003_cindent_spec.lua b/test/functional/legacy/003_cindent_spec.lua index 83388bd1eb..27835fea28 100644 --- a/test/functional/legacy/003_cindent_spec.lua +++ b/test/functional/legacy/003_cindent_spec.lua @@ -5,14 +5,14 @@ local helpers = require('test.functional.helpers')(after_each) local feed, insert = helpers.feed, helpers.insert -local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect -- Inserts text as usual, and additionally positions the cursor on line 1 and -- sets 'cindent' and tab settings. (In the original "test3.in" the modeline at -- the top of the file takes care of this.) local function insert_(content) insert(content) - execute('1', 'set cin ts=4 sw=4') + feed_command('1', 'set cin ts=4 sw=4') end describe('cindent', function() @@ -20,21 +20,21 @@ describe('cindent', function() it('1 is working', function() insert_([=[ - + /* start of AUTO matically checked vim: set ts=4 : */ { if (test) cmd1; cmd2; } - + { if (test) cmd1; else cmd2; } - + { if (test) { @@ -42,7 +42,7 @@ describe('cindent', function() cmd2; } } - + { if (test) { @@ -50,14 +50,14 @@ describe('cindent', function() else } } - + { while (this) if (test) cmd1; cmd2; } - + { while (this) if (test) @@ -65,25 +65,25 @@ describe('cindent', function() else cmd2; } - + { if (test) { cmd; } - + if (test) cmd; } - + { if (test) { cmd; } - + if (test) cmd; } - + { cmd1; for (blah) @@ -92,7 +92,7 @@ describe('cindent', function() cmd2; cmd3; } - + { cmd1; for (blah) @@ -100,7 +100,7 @@ describe('cindent', function() if (test) cmd2; cmd3; - + if (test) { cmd1; @@ -108,23 +108,23 @@ describe('cindent', function() cmd3; } } - - + + /* Test for 'cindent' do/while mixed with if/else: */ - + { do if (asdf) asdfasd; while (cond); - + do if (asdf) while (asdf) asdf; while (asdf); } - + /* Test for 'cindent' with two ) on a continuation line */ { if (asdfasdf;asldkfj asdlkfj as;ldkfj sal;d @@ -132,14 +132,14 @@ describe('cindent', function() al;sdjfka ;slkdf ) sa;ldkjfsa dlk;) line up here; } - - + + /* C++ tests: */ - + // foo() these three lines should remain in column 0 // { // } - + /* Test for continuation and unterminated lines: */ { i = 99 + 14325 + @@ -151,12 +151,12 @@ describe('cindent', function() 1234; c = 1; } - + /* testje for indent with empty line - + here */ - + { if (testing && not a joke || @@ -171,8 +171,8 @@ describe('cindent', function() line up here)) hay; } - - + + { switch (c) { @@ -191,7 +191,7 @@ describe('cindent', function() testing; } } - + { if (cond) { foo; @@ -201,7 +201,7 @@ describe('cindent', function() bar; } } - + { if (alskdfj ;alsdkfjal;skdjf (;sadlkfsa ;dlkf j;alksdfj ;alskdjf alsdkfj (asldk;fj @@ -210,7 +210,7 @@ describe('cindent', function() asdfasdf;))) asdfasdf; } - + int func(a, b) int a; @@ -223,7 +223,7 @@ describe('cindent', function() (c2 || c3) ) } - + { while (asd) { @@ -245,13 +245,13 @@ describe('cindent', function() asdf; } } - + { s = "/*"; b = ';' s = "/*"; b = ';'; a = b; } - + { switch (a) { @@ -285,7 +285,7 @@ describe('cindent', function() break; } } - + { if (!(vim_strchr(p_cpo, CPO_BUFOPTGLOB) != NULL && entering) && (bp_to->b_p_initialized || @@ -297,57 +297,57 @@ describe('cindent', function() asdf = asdf ? asdf: asdf; } - + /* Special Comments : This function has the added complexity (compared */ /* : to addtolist) of having to check for a detail */ /* : texture and add that to the list first. */ - + char *(array[100]) = { "testje", "foo", "bar", } - + enum soppie { yes = 0, no, maybe }; - + typedef enum soppie { yes = 0, no, maybe }; - + static enum { yes = 0, no, maybe } soppie; - + public static enum { yes = 0, no, maybe } soppie; - + static private enum { yes = 0, no, maybe } soppie; - + { int a, b; } - + { struct Type { @@ -360,7 +360,7 @@ describe('cindent', function() 2, "two", 3, "three" }; - + float matrix[3][3] = { { @@ -380,14 +380,14 @@ describe('cindent', function() } }; } - + { /* blah ( blah */ /* where does this go? */ - + /* blah ( blah */ cmd; - + func(arg1, /* comment */ arg2); @@ -398,7 +398,7 @@ describe('cindent', function() c; /* Hey, NOW it indents?! */ } } - + { func(arg1, arg2, @@ -406,7 +406,7 @@ describe('cindent', function() /* Hey, what am I doing here? Is this coz of the ","? */ } } - + main () { if (cond) @@ -420,7 +420,7 @@ describe('cindent', function() a = d; return; } - + { case 2: if (asdf && asdfasdf) @@ -431,42 +431,42 @@ describe('cindent', function() a = 9; case 4: x = 1; y = 2; - + label: if (asdf) here; - + label: if (asdf && asdfasdf) { } - + label: if (asdf && asdfasdf) { there; } - + label: if (asdf && asdfasdf) there; } - + { /* hello with ":set comments= cino=c5" */ - + /* hello with ":set comments= cino=" */ } - - + + { if (a < b) { a = a + 1; } else a = a + 2; - + if (a) do { testing; @@ -474,7 +474,7 @@ describe('cindent', function() a = b + 1; asdfasdf } - + { for ( int i = 0; i < 10; i++ ) @@ -482,13 +482,13 @@ describe('cindent', function() } i = 0; } - + class bob { int foo() {return 1;} int bar; } - + main() { while(1) @@ -501,32 +501,32 @@ describe('cindent', function() } misplacedline; } - + { if (clipboard.state == SELECT_DONE && ((row == clipboard.start.lnum && col >= clipboard.start.col) || row > clipboard.start.lnum)) } - + { if (1) {i += 4;} where_am_i; return 0; } - + { { } // sdf(asdf if (asdf) asd; } - + { label1: label2: } - + { int fooRet = foo(pBar1, false /*fKB*/, true /*fPTB*/, 3 /*nT*/, false /*fDF*/); @@ -538,12 +538,12 @@ describe('cindent', function() } } } - + { f1(/*comment*/); f2(); } - + { do { if (foo) { @@ -552,25 +552,25 @@ describe('cindent', function() } while (foo); foo(); // was wrong } - + int x; // no extra indent because of the ; void func() { } - + char *tab[] = {"aaa", "};", /* }; */ NULL} int indented; {} - + char *a[] = {"aaa", "bbb", "ccc", NULL}; // here - + char *tab[] = {"aaa", "xx", /* xx */}; /* asdf */ int not_indented; - + { do { switch (bla) @@ -581,23 +581,23 @@ describe('cindent', function() } while (boo); wrong; } - + int foo, bar; int foo; - + #if defined(foo) \ && defined(bar) char * xx = "asdf\ foo\ bor"; int x; - + char *foo = "asdf\ asdf\ asdf", *bar; - + void f() { #if defined(foo) \ @@ -616,19 +616,19 @@ describe('cindent', function() #endif } #endif - + int y; // comment // comment - + // comment - + { Constructor(int a, int b ) : BaseClass(a) { } } - + void foo() { char one, @@ -645,13 +645,13 @@ describe('cindent', function() kees, jan; } - + { t(int f, int d); // ) d(); } - + Constructor::Constructor(int a, int b ) : @@ -661,33 +661,33 @@ describe('cindent', function() mMember(b), { } - + Constructor::Constructor(int a, int b ) : BaseClass(a) { } - + Constructor::Constructor(int a, int b ) /*x*/ : /*x*/ BaseClass(a), member(b) { } - + A::A(int a, int b) : aa(a), bb(b), cc(c) { } - + class CAbc : public BaseClass1, protected BaseClass2 { int Test() { return FALSE; } int Test1() { return TRUE; } - + CAbc(int a, int b ) : BaseClass(a) { @@ -696,24 +696,24 @@ describe('cindent', function() case abc: asdf(); break; - + case 999: baer(); break; } } - + public: // <-- this was incoreectly indented before!! void testfall(); protected: void testfall(); }; - + class CAbc : public BaseClass1, protected BaseClass2 { }; - + static struct { int a; @@ -729,7 +729,7 @@ describe('cindent', function() 456 } }; - + static struct { int a; @@ -739,7 +739,7 @@ describe('cindent', function() { 123, 456 }, { 123, 456 } }; - + void asdf() /* ind_maxparen may cause trouble here */ { if ((0 @@ -769,17 +769,17 @@ describe('cindent', function() && 1 && 1)) break; } - + foo() { a = cond ? foo() : asdf + asdf; - + a = cond ? foo() : asdf + asdf; } - + int main(void) { if (a) @@ -788,7 +788,7 @@ describe('cindent', function() else 3; next_line_of_code(); } - + barry() { Foo::Foo (int one, @@ -796,14 +796,14 @@ describe('cindent', function() : something(4) {} } - + barry() { Foo::Foo (int one, int two) : something(4) {} } - + Constructor::Constructor(int a, int b ) : @@ -822,7 +822,7 @@ describe('cindent', function() && lele); lulu; } - + int main () { switch (c) @@ -832,13 +832,13 @@ describe('cindent', function() } } } - + main() { (void) MyFancyFuasdfadsfnction( argument); } - + main() { char foo[] = "/*"; @@ -846,7 +846,7 @@ describe('cindent', function() df */ hello } - + /* valid namespaces with normal indent */ namespace { @@ -885,7 +885,7 @@ describe('cindent', function() 22222222222222222; } } - + /* invalid namespaces use block indent */ namespace test test2 { 111111111111111111111; @@ -925,7 +925,7 @@ describe('cindent', function() } )foo"; } - + { int a[4] = { [0] = 0, @@ -934,12 +934,12 @@ describe('cindent', function() [3] = 3, }; } - + { a = b[2] + 3; } - + { if (1) /* aaaaa @@ -947,7 +947,7 @@ describe('cindent', function() */ a = 1; } - + void func() { switch (foo) @@ -974,29 +974,29 @@ describe('cindent', function() break; } } - + /* end of AUTO */ ]=]) - execute('/start of AUTO') + feed_command('/start of AUTO') feed('=/end of AUTO') expect([=[ - + /* start of AUTO matically checked vim: set ts=4 : */ { if (test) cmd1; cmd2; } - + { if (test) cmd1; else cmd2; } - + { if (test) { @@ -1004,7 +1004,7 @@ describe('cindent', function() cmd2; } } - + { if (test) { @@ -1012,14 +1012,14 @@ describe('cindent', function() else } } - + { while (this) if (test) cmd1; cmd2; } - + { while (this) if (test) @@ -1027,25 +1027,25 @@ describe('cindent', function() else cmd2; } - + { if (test) { cmd; } - + if (test) cmd; } - + { if (test) { cmd; } - + if (test) cmd; } - + { cmd1; for (blah) @@ -1054,7 +1054,7 @@ describe('cindent', function() cmd2; cmd3; } - + { cmd1; for (blah) @@ -1062,7 +1062,7 @@ describe('cindent', function() if (test) cmd2; cmd3; - + if (test) { cmd1; @@ -1070,23 +1070,23 @@ describe('cindent', function() cmd3; } } - - + + /* Test for 'cindent' do/while mixed with if/else: */ - + { do if (asdf) asdfasd; while (cond); - + do if (asdf) while (asdf) asdf; while (asdf); } - + /* Test for 'cindent' with two ) on a continuation line */ { if (asdfasdf;asldkfj asdlkfj as;ldkfj sal;d @@ -1094,14 +1094,14 @@ describe('cindent', function() al;sdjfka ;slkdf ) sa;ldkjfsa dlk;) line up here; } - - + + /* C++ tests: */ - + // foo() these three lines should remain in column 0 // { // } - + /* Test for continuation and unterminated lines: */ { i = 99 + 14325 + @@ -1113,12 +1113,12 @@ describe('cindent', function() 1234; c = 1; } - + /* testje for indent with empty line - + here */ - + { if (testing && not a joke || @@ -1133,8 +1133,8 @@ describe('cindent', function() line up here)) hay; } - - + + { switch (c) { @@ -1153,7 +1153,7 @@ describe('cindent', function() testing; } } - + { if (cond) { foo; @@ -1163,7 +1163,7 @@ describe('cindent', function() bar; } } - + { if (alskdfj ;alsdkfjal;skdjf (;sadlkfsa ;dlkf j;alksdfj ;alskdjf alsdkfj (asldk;fj @@ -1172,7 +1172,7 @@ describe('cindent', function() asdfasdf;))) asdfasdf; } - + int func(a, b) int a; @@ -1185,7 +1185,7 @@ describe('cindent', function() (c2 || c3) ) } - + { while (asd) { @@ -1207,13 +1207,13 @@ describe('cindent', function() asdf; } } - + { s = "/*"; b = ';' s = "/*"; b = ';'; a = b; } - + { switch (a) { @@ -1247,7 +1247,7 @@ describe('cindent', function() break; } } - + { if (!(vim_strchr(p_cpo, CPO_BUFOPTGLOB) != NULL && entering) && (bp_to->b_p_initialized || @@ -1259,57 +1259,57 @@ describe('cindent', function() asdf = asdf ? asdf: asdf; } - + /* Special Comments : This function has the added complexity (compared */ /* : to addtolist) of having to check for a detail */ /* : texture and add that to the list first. */ - + char *(array[100]) = { "testje", "foo", "bar", } - + enum soppie { yes = 0, no, maybe }; - + typedef enum soppie { yes = 0, no, maybe }; - + static enum { yes = 0, no, maybe } soppie; - + public static enum { yes = 0, no, maybe } soppie; - + static private enum { yes = 0, no, maybe } soppie; - + { int a, b; } - + { struct Type { @@ -1322,7 +1322,7 @@ describe('cindent', function() 2, "two", 3, "three" }; - + float matrix[3][3] = { { @@ -1342,14 +1342,14 @@ describe('cindent', function() } }; } - + { /* blah ( blah */ /* where does this go? */ - + /* blah ( blah */ cmd; - + func(arg1, /* comment */ arg2); @@ -1360,7 +1360,7 @@ describe('cindent', function() c; /* Hey, NOW it indents?! */ } } - + { func(arg1, arg2, @@ -1368,7 +1368,7 @@ describe('cindent', function() /* Hey, what am I doing here? Is this coz of the ","? */ } } - + main () { if (cond) @@ -1382,7 +1382,7 @@ describe('cindent', function() a = d; return; } - + { case 2: if (asdf && asdfasdf) @@ -1393,42 +1393,42 @@ describe('cindent', function() a = 9; case 4: x = 1; y = 2; - + label: if (asdf) here; - + label: if (asdf && asdfasdf) { } - + label: if (asdf && asdfasdf) { there; } - + label: if (asdf && asdfasdf) there; } - + { /* hello with ":set comments= cino=c5" */ - + /* hello with ":set comments= cino=" */ } - - + + { if (a < b) { a = a + 1; } else a = a + 2; - + if (a) do { testing; @@ -1436,7 +1436,7 @@ describe('cindent', function() a = b + 1; asdfasdf } - + { for ( int i = 0; i < 10; i++ ) @@ -1444,13 +1444,13 @@ describe('cindent', function() } i = 0; } - + class bob { int foo() {return 1;} int bar; } - + main() { while(1) @@ -1463,32 +1463,32 @@ describe('cindent', function() } misplacedline; } - + { if (clipboard.state == SELECT_DONE && ((row == clipboard.start.lnum && col >= clipboard.start.col) || row > clipboard.start.lnum)) } - + { if (1) {i += 4;} where_am_i; return 0; } - + { { } // sdf(asdf if (asdf) asd; } - + { label1: label2: } - + { int fooRet = foo(pBar1, false /*fKB*/, true /*fPTB*/, 3 /*nT*/, false /*fDF*/); @@ -1500,12 +1500,12 @@ describe('cindent', function() } } } - + { f1(/*comment*/); f2(); } - + { do { if (foo) { @@ -1514,25 +1514,25 @@ describe('cindent', function() } while (foo); foo(); // was wrong } - + int x; // no extra indent because of the ; void func() { } - + char *tab[] = {"aaa", "};", /* }; */ NULL} int indented; {} - + char *a[] = {"aaa", "bbb", "ccc", NULL}; // here - + char *tab[] = {"aaa", "xx", /* xx */}; /* asdf */ int not_indented; - + { do { switch (bla) @@ -1543,23 +1543,23 @@ describe('cindent', function() } while (boo); wrong; } - + int foo, bar; int foo; - + #if defined(foo) \ && defined(bar) char * xx = "asdf\ foo\ bor"; int x; - + char *foo = "asdf\ asdf\ asdf", *bar; - + void f() { #if defined(foo) \ @@ -1578,19 +1578,19 @@ describe('cindent', function() #endif } #endif - + int y; // comment // comment - + // comment - + { Constructor(int a, int b ) : BaseClass(a) { } } - + void foo() { char one, @@ -1607,13 +1607,13 @@ describe('cindent', function() kees, jan; } - + { t(int f, int d); // ) d(); } - + Constructor::Constructor(int a, int b ) : @@ -1623,33 +1623,33 @@ describe('cindent', function() mMember(b), { } - + Constructor::Constructor(int a, int b ) : BaseClass(a) { } - + Constructor::Constructor(int a, int b ) /*x*/ : /*x*/ BaseClass(a), member(b) { } - + A::A(int a, int b) : aa(a), bb(b), cc(c) { } - + class CAbc : public BaseClass1, protected BaseClass2 { int Test() { return FALSE; } int Test1() { return TRUE; } - + CAbc(int a, int b ) : BaseClass(a) { @@ -1658,24 +1658,24 @@ describe('cindent', function() case abc: asdf(); break; - + case 999: baer(); break; } } - + public: // <-- this was incoreectly indented before!! void testfall(); protected: void testfall(); }; - + class CAbc : public BaseClass1, protected BaseClass2 { }; - + static struct { int a; @@ -1691,7 +1691,7 @@ describe('cindent', function() 456 } }; - + static struct { int a; @@ -1701,7 +1701,7 @@ describe('cindent', function() { 123, 456 }, { 123, 456 } }; - + void asdf() /* ind_maxparen may cause trouble here */ { if ((0 @@ -1731,17 +1731,17 @@ describe('cindent', function() && 1 && 1)) break; } - + foo() { a = cond ? foo() : asdf + asdf; - + a = cond ? foo() : asdf + asdf; } - + int main(void) { if (a) @@ -1750,7 +1750,7 @@ describe('cindent', function() else 3; next_line_of_code(); } - + barry() { Foo::Foo (int one, @@ -1758,14 +1758,14 @@ describe('cindent', function() : something(4) {} } - + barry() { Foo::Foo (int one, int two) : something(4) {} } - + Constructor::Constructor(int a, int b ) : @@ -1784,7 +1784,7 @@ describe('cindent', function() && lele); lulu; } - + int main () { switch (c) @@ -1794,13 +1794,13 @@ describe('cindent', function() } } } - + main() { (void) MyFancyFuasdfadsfnction( argument); } - + main() { char foo[] = "/*"; @@ -1808,7 +1808,7 @@ describe('cindent', function() df */ hello } - + /* valid namespaces with normal indent */ namespace { @@ -1847,7 +1847,7 @@ describe('cindent', function() 22222222222222222; } } - + /* invalid namespaces use block indent */ namespace test test2 { 111111111111111111111; @@ -1887,7 +1887,7 @@ describe('cindent', function() } )foo"; } - + { int a[4] = { [0] = 0, @@ -1896,12 +1896,12 @@ describe('cindent', function() [3] = 3, }; } - + { a = b[2] + 3; } - + { if (1) /* aaaaa @@ -1909,7 +1909,7 @@ describe('cindent', function() */ a = 1; } - + void func() { switch (foo) @@ -1936,16 +1936,16 @@ describe('cindent', function() break; } } - + /* end of AUTO */ ]=]) end) it('2 is working', function() insert_([=[ - + { - + /* this is * a real serious important big * comment @@ -1954,14 +1954,14 @@ describe('cindent', function() } ]=]) - execute('set tw=0 wm=60 columns=80 noai fo=croq') - execute('/serious/e') + feed_command('set tw=0 wm=60 columns=80 noai fo=croq') + feed_command('/serious/e') feed('a about life, the universe, and the rest') expect([=[ - + { - + /* this is * a real serious * about life, the @@ -1976,48 +1976,48 @@ describe('cindent', function() it('3 is working', function() insert_([=[ - + { /* * Testing for comments, without 'cin' set */ - + /* * what happens here? */ - + /* the end of the comment, try inserting a line below */ - + /* how about this one */ } ]=]) - execute('set nocin') - execute('/comments') + feed_command('set nocin') + feed_command('/comments') feed('joabout life/happens') feed('jothere/below') feed('oline/this') feed('Ohello') expect([=[ - + { /* * Testing for comments, without 'cin' set */ about life - + /* * what happens here? */ there - + /* the end of the comment, try inserting a line below */ line - + /* how about hello this one */ @@ -2027,19 +2027,19 @@ describe('cindent', function() it('4 is working', function() insert_([=[ - + { var = this + that + vec[0] * vec[0] + vec[1] * vec[1] + vec2[2] * vec[2]; } ]=]) - execute('set cin') - execute('/vec2') + feed_command('set cin') + feed_command('/vec2') feed('==') expect([=[ - + { var = this + that + vec[0] * vec[0] + vec[1] * vec[1] @@ -2050,7 +2050,7 @@ describe('cindent', function() it('5 is working', function() insert_([=[ - + { asdf asdflkajds f; if (tes & ting) { @@ -2067,14 +2067,14 @@ describe('cindent', function() } ]=]) - execute('set cin') - execute('set cino=}4') - execute('/testing1') + feed_command('set cin') + feed_command('set cino=}4') + feed_command('/testing1') feed('k2==/testing2') feed('k2==') expect([=[ - + { asdf asdflkajds f; if (tes & ting) { @@ -2094,7 +2094,7 @@ describe('cindent', function() it('6 is working', function() insert_([=[ - + main ( int first_par, /* * Comment for * first par @@ -2114,17 +2114,17 @@ describe('cindent', function() * second par */ ); - + } ]=]) - execute('set cin') - execute('set cino=(0,)20') - execute('/main') + feed_command('set cin') + feed_command('set cino=(0,)20') + feed_command('/main') feed('=][') expect([=[ - + main ( int first_par, /* * Comment for * first par @@ -2144,14 +2144,14 @@ describe('cindent', function() * second par */ ); - + } ]=]) end) it('7 is working', function() insert_([=[ - + main(void) { /* Make sure that cino=X0s is not parsed like cino=Xs. */ @@ -2164,13 +2164,13 @@ describe('cindent', function() } ]=]) - execute('set cin') - execute('set cino=es,n0s') - execute('/main') + feed_command('set cin') + feed_command('set cino=es,n0s') + feed_command('/main') feed('=][') expect([=[ - + main(void) { /* Make sure that cino=X0s is not parsed like cino=Xs. */ @@ -2186,7 +2186,7 @@ describe('cindent', function() it('8 is working', function() insert_([=[ - + { do { @@ -2202,12 +2202,12 @@ describe('cindent', function() } ]=]) - execute('set cin') - execute('set cino=') + feed_command('set cin') + feed_command('set cino=') feed(']]=][') expect([=[ - + { do { @@ -2226,16 +2226,16 @@ describe('cindent', function() it('9 is working', function() insert_([=[ - + void f() { if ( k() ) { l(); - + } else { /* Start (two words) end */ m(); } - + n(); } ]=]) @@ -2243,16 +2243,16 @@ describe('cindent', function() feed(']]=][') expect([=[ - + void f() { if ( k() ) { l(); - + } else { /* Start (two words) end */ m(); } - + n(); } ]=]) @@ -2267,7 +2267,7 @@ describe('cindent', function() -- indented properly. And that's why we've had to add one explicitly. insert_([=[ { <= THIS IS THE CURLY BRACKET EXPLAINED IN THE COMMENT. - + void f() { if ( k() ) @@ -2280,12 +2280,12 @@ describe('cindent', function() } ]=]) - execute('set cino={s,e-s') + feed_command('set cino={s,e-s') feed(']]=][') expect([=[ { <= THIS IS THE CURLY BRACKET EXPLAINED IN THE COMMENT. - + void f() { if ( k() ) @@ -2301,7 +2301,7 @@ describe('cindent', function() it('11 is working', function() insert_([=[ - + void bar(void) { static array[2][2] = @@ -2309,12 +2309,12 @@ describe('cindent', function() { 1, 2 }, { 3, 4 }, } - + while (a) { foo(&a); } - + { int a; { @@ -2323,7 +2323,7 @@ describe('cindent', function() } b = a; } - + void func(void) { a = 1; @@ -2336,11 +2336,11 @@ describe('cindent', function() /* foo */ ]=]) - execute('set cino={s,fs') + feed_command('set cino={s,fs') feed(']]=/ foo') expect([=[ - + void bar(void) { static array[2][2] = @@ -2348,12 +2348,12 @@ describe('cindent', function() { 1, 2 }, { 3, 4 }, } - + while (a) { foo(&a); } - + { int a; { @@ -2362,7 +2362,7 @@ describe('cindent', function() } b = a; } - + void func(void) { a = 1; @@ -2378,7 +2378,7 @@ describe('cindent', function() it('12 is working', function() insert_([=[ - + a() { do { @@ -2390,12 +2390,12 @@ describe('cindent', function() } ]=]) - execute('set cino=') - execute('/while') + feed_command('set cino=') + feed_command('/while') feed('ohere') expect([=[ - + a() { do { @@ -2411,7 +2411,7 @@ describe('cindent', function() it('13 is working', function() insert_([=[ - + a() { label1: @@ -2420,12 +2420,12 @@ describe('cindent', function() } ]=]) - execute('set cino= com=') - execute('/comment') + feed_command('set cino= com=') + feed_command('/comment') feed('olabel2: b();label3 /* post */:/* pre */ label4:f(/*com*/);if (/*com*/)cmd();') expect([=[ - + a() { label1: @@ -2443,26 +2443,26 @@ describe('cindent', function() it('14 is working', function() insert_([=[ - + /* * A simple comment */ - + /* ** A different comment */ ]=]) - execute('set comments& comments^=s:/*,m:**,ex:*/') - execute('/simple') + feed_command('set comments& comments^=s:/*,m:**,ex:*/') + feed_command('/simple') feed('=5j') expect([=[ - + /* * A simple comment */ - + /* ** A different comment */ @@ -2471,26 +2471,26 @@ describe('cindent', function() it('15 is working', function() insert_([=[ - - + + void f() { - + /********* A comment. *********/ } ]=]) - execute('set cino=c0') - execute('set comments& comments-=s1:/* comments^=s0:/*') + feed_command('set cino=c0') + feed_command('set comments& comments-=s1:/* comments^=s0:/*') feed('2kdd]]=][') expect([=[ - + void f() { - + /********* A comment. *********/ @@ -2500,26 +2500,26 @@ describe('cindent', function() it('16 is working', function() insert_([=[ - - + + void f() { - + /********* A comment. *********/ } ]=]) - execute('set cino=c0,C1') - execute('set comments& comments-=s1:/* comments^=s0:/*') + feed_command('set cino=c0,C1') + feed_command('set comments& comments-=s1:/* comments^=s0:/*') feed('2kdd]]=][') expect([=[ - + void f() { - + /********* A comment. *********/ @@ -2529,7 +2529,7 @@ describe('cindent', function() it('17 is working', function() insert_([=[ - + void f() { c = c1 && @@ -2540,11 +2540,11 @@ describe('cindent', function() } ]=]) - execute('set cino=') + feed_command('set cino=') feed(']]=][') expect([=[ - + void f() { c = c1 && @@ -2558,8 +2558,8 @@ describe('cindent', function() it('18 is working', function() insert_([=[ - - + + void f() { c = c1 && @@ -2570,11 +2570,11 @@ describe('cindent', function() } ]=]) - execute('set cino=(s') + feed_command('set cino=(s') feed('2kdd]]=][') expect([=[ - + void f() { c = c1 && @@ -2588,8 +2588,8 @@ describe('cindent', function() it('19 is working', function() insert_([=[ - - + + void f() { c = c1 && @@ -2600,11 +2600,11 @@ describe('cindent', function() } ]=]) - execute('set cino=(s,U1 ') + feed_command('set cino=(s,U1 ') feed('2kdd]]=][') expect([=[ - + void f() { c = c1 && @@ -2618,8 +2618,8 @@ describe('cindent', function() it('20 is working', function() insert_([=[ - - + + void f() { if ( c1 @@ -2629,11 +2629,11 @@ describe('cindent', function() } ]=]) - execute('set cino=(0') + feed_command('set cino=(0') feed('2kdd]]=][') expect([=[ - + void f() { if ( c1 @@ -2646,8 +2646,8 @@ describe('cindent', function() it('21 is working', function() insert_([=[ - - + + void f() { if ( c1 @@ -2657,11 +2657,11 @@ describe('cindent', function() } ]=]) - execute('set cino=(0,w1 ') + feed_command('set cino=(0,w1 ') feed('2kdd]]=][') expect([=[ - + void f() { if ( c1 @@ -2674,8 +2674,8 @@ describe('cindent', function() it('22 is working', function() insert_([=[ - - + + void f() { c = c1 && ( @@ -2689,11 +2689,11 @@ describe('cindent', function() } ]=]) - execute('set cino=(s') + feed_command('set cino=(s') feed('2kdd]]=][') expect([=[ - + void f() { c = c1 && ( @@ -2710,8 +2710,8 @@ describe('cindent', function() it('23 is working', function() insert_([=[ - - + + void f() { c = c1 && ( @@ -2725,11 +2725,11 @@ describe('cindent', function() } ]=]) - execute('set cino=(s,m1 ') + feed_command('set cino=(s,m1 ') feed('2kdd]]=][') expect([=[ - + void f() { c = c1 && ( @@ -2746,8 +2746,8 @@ describe('cindent', function() it('24 is working', function() insert_([=[ - - + + void f() { switch (x) @@ -2762,11 +2762,11 @@ describe('cindent', function() } ]=]) - execute('set cino=b1') + feed_command('set cino=b1') feed('2kdd]]=][') expect([=[ - + void f() { switch (x) @@ -2784,8 +2784,8 @@ describe('cindent', function() it('25 is working', function() insert_([=[ - - + + void f() { invokeme( @@ -2801,11 +2801,11 @@ describe('cindent', function() } ]=]) - execute('set cino=(0,W5') + feed_command('set cino=(0,W5') feed('2kdd]]=][') expect([=[ - + void f() { invokeme( @@ -2824,8 +2824,8 @@ describe('cindent', function() it('26 is working', function() insert_([=[ - - + + void f() { statement; @@ -2834,11 +2834,11 @@ describe('cindent', function() } ]=]) - execute('set cino=/6') + feed_command('set cino=/6') feed('2kdd]]=][') expect([=[ - + void f() { statement; @@ -2850,8 +2850,8 @@ describe('cindent', function() it('27 is working', function() insert_([=[ - - + + void f() { statement; @@ -2860,12 +2860,12 @@ describe('cindent', function() } ]=]) - execute('set cino=') + feed_command('set cino=') feed('2kdd]]/comment 1/+1') feed('==') expect([=[ - + void f() { statement; @@ -2877,12 +2877,12 @@ describe('cindent', function() it('28 is working', function() insert_([=[ - - + + class CAbc { int Test() { return FALSE; } - + public: // comment void testfall(); protected: @@ -2890,15 +2890,15 @@ describe('cindent', function() }; ]=]) - execute('set cino=g0') + feed_command('set cino=g0') feed('2kdd]]=][') expect([=[ - + class CAbc { int Test() { return FALSE; } - + public: // comment void testfall(); protected: @@ -2909,8 +2909,8 @@ describe('cindent', function() it('29 is working', function() insert_([=[ - - + + class Foo : public Bar { public: @@ -2921,11 +2921,11 @@ describe('cindent', function() }; ]=]) - execute('set cino=(0,gs,hs') + feed_command('set cino=(0,gs,hs') feed('2kdd]]=][') expect([=[ - + class Foo : public Bar { public: @@ -2939,8 +2939,8 @@ describe('cindent', function() it('30 is working', function() insert_([=[ - - + + void foo() { @@ -2951,11 +2951,11 @@ describe('cindent', function() } ]=]) - execute('set cino=+20') + feed_command('set cino=+20') feed('2kdd]]=][') expect([=[ - + void foo() { @@ -2969,8 +2969,8 @@ describe('cindent', function() it('31 is working', function() insert_([=[ - - + + { averylongfunctionnamelongfunctionnameaverylongfunctionname()->asd( asdasdf, @@ -2978,9 +2978,9 @@ describe('cindent', function() asdfadsf), asdfasdf ); - + /* those are ugly, but consequent */ - + func()->asd(asdasdf, averylongfunctionname( abc, @@ -2994,7 +2994,7 @@ describe('cindent', function() ), asdasdf ); - + averylongfunctionnameaverylongfunctionnameavery()->asd(fasdf( abc, dec)->asdfasdfasdf( @@ -3009,11 +3009,11 @@ describe('cindent', function() } ]=]) - execute('set cino=(0,W2s') + feed_command('set cino=(0,W2s') feed('2kdd]]=][') expect([=[ - + { averylongfunctionnamelongfunctionnameaverylongfunctionname()->asd( asdasdf, @@ -3021,9 +3021,9 @@ describe('cindent', function() asdfadsf), asdfasdf ); - + /* those are ugly, but consequent */ - + func()->asd(asdasdf, averylongfunctionname( abc, @@ -3037,7 +3037,7 @@ describe('cindent', function() ), asdasdf ); - + averylongfunctionnameaverylongfunctionnameavery()->asd(fasdf( abc, dec)->asdfasdfasdf( @@ -3055,8 +3055,8 @@ describe('cindent', function() it('32 is working', function() insert_([=[ - - + + int main () { if (cond1 && @@ -3066,11 +3066,11 @@ describe('cindent', function() } ]=]) - execute('set cino=M1') + feed_command('set cino=M1') feed('2kdd]]=][') expect([=[ - + int main () { if (cond1 && @@ -3083,8 +3083,8 @@ describe('cindent', function() it('33 is working', function() insert_([=[ - - + + void func(int a #if defined(FOO) , int b @@ -3095,11 +3095,11 @@ describe('cindent', function() } ]=]) - execute('set cino=(0,ts') + feed_command('set cino=(0,ts') feed('2kdd2j=][') expect([=[ - + void func(int a #if defined(FOO) , int b @@ -3113,9 +3113,9 @@ describe('cindent', function() it('34 is working', function() insert_([=[ - - - + + + void func(int a #if defined(FOO) @@ -3127,12 +3127,12 @@ describe('cindent', function() } ]=]) - execute('set cino=(0') + feed_command('set cino=(0') feed('2kdd2j=][') expect([=[ - - + + void func(int a #if defined(FOO) @@ -3147,8 +3147,8 @@ describe('cindent', function() it('35 is working', function() insert_([=[ - - + + void func(void) { if(x==y) @@ -3159,7 +3159,7 @@ describe('cindent', function() } printf("Foo!\n"); } - + void func1(void) { char* tab[] = {"foo", "bar", @@ -3167,37 +3167,37 @@ describe('cindent', function() "this line used", "to be indented incorrectly"}; foo(); } - + void func2(void) { int tab[] = {1, 2, 3, 4, 5, 6}; - + printf("This line used to be indented incorrectly.\n"); } - + int foo[] #ifdef BAR - + = { 1, 2, 3, 4, 5, 6 } - + #endif ; int baz; - + void func3(void) { int tab[] = { 1, 2, 3, 4, 5, 6}; - + printf("Don't you dare indent this line incorrectly!\n"); } - + void func4(a, b, c) @@ -3206,14 +3206,14 @@ describe('cindent', function() int c; { } - + void func5( int a, int b) { } - + void func6( int a) @@ -3221,11 +3221,11 @@ describe('cindent', function() } ]=]) - execute('set cino&') + feed_command('set cino&') feed('2kdd2j=7][') expect([=[ - + void func(void) { if(x==y) @@ -3236,7 +3236,7 @@ describe('cindent', function() } printf("Foo!\n"); } - + void func1(void) { char* tab[] = {"foo", "bar", @@ -3244,37 +3244,37 @@ describe('cindent', function() "this line used", "to be indented incorrectly"}; foo(); } - + void func2(void) { int tab[] = {1, 2, 3, 4, 5, 6}; - + printf("This line used to be indented incorrectly.\n"); } - + int foo[] #ifdef BAR - + = { 1, 2, 3, 4, 5, 6 } - + #endif ; int baz; - + void func3(void) { int tab[] = { 1, 2, 3, 4, 5, 6}; - + printf("Don't you dare indent this line incorrectly!\n"); } - + void func4(a, b, c) @@ -3283,14 +3283,14 @@ describe('cindent', function() int c; { } - + void func5( int a, int b) { } - + void func6( int a) @@ -3301,17 +3301,17 @@ describe('cindent', function() it('36 is working', function() insert_([=[ - - + + void func(void) { int tab[] = { 1, 2, 3, 4, 5, 6}; - + printf("Indent this line correctly!\n"); - + switch (foo) { case bar: @@ -3328,21 +3328,21 @@ describe('cindent', function() } ]=]) - execute('set cino&') - execute('set cino+=l1') + feed_command('set cino&') + feed_command('set cino+=l1') feed('2kdd2j=][') expect([=[ - + void func(void) { int tab[] = { 1, 2, 3, 4, 5, 6}; - + printf("Indent this line correctly!\n"); - + switch (foo) { case bar: @@ -3362,8 +3362,8 @@ describe('cindent', function() it('37 is working', function() insert_([=[ - - + + void func(void) { cout << "a" @@ -3373,11 +3373,11 @@ describe('cindent', function() } ]=]) - execute('set cino&') + feed_command('set cino&') feed('2kdd2j=][') expect([=[ - + void func(void) { cout << "a" @@ -3390,7 +3390,7 @@ describe('cindent', function() it('38 is working', function() insert_([=[ - + void func(void) { /* @@ -3399,11 +3399,11 @@ describe('cindent', function() } ]=]) - execute('set com=s1:/*,m:*,ex:*/') + feed_command('set com=s1:/*,m:*,ex:*/') feed(']]3jofoo();') expect([=[ - + void func(void) { /* @@ -3416,8 +3416,8 @@ describe('cindent', function() it('39 is working', function() insert_([=[ - - + + void func(void) { for (int i = 0; i < 10; ++i) @@ -3429,11 +3429,11 @@ describe('cindent', function() } ]=]) - execute('set cino&') + feed_command('set cino&') feed('2kdd2j=][') expect([=[ - + void func(void) { for (int i = 0; i < 10; ++i) @@ -3448,8 +3448,8 @@ describe('cindent', function() it('40 is working', function() insert_([=[ - - + + void func(void) { if (condition1 @@ -3457,7 +3457,7 @@ describe('cindent', function() action(); function(argument1 && argument2); - + if (c1 && (c2 || c3)) foo; @@ -3465,7 +3465,7 @@ describe('cindent', function() (c2 || c3)) { } - + if ( c1 && ( c2 || c3)) @@ -3477,11 +3477,11 @@ describe('cindent', function() } ]=]) - execute('set cino=k2s,(0') + feed_command('set cino=k2s,(0') feed('2kdd3j=][') expect([=[ - + void func(void) { if (condition1 @@ -3489,7 +3489,7 @@ describe('cindent', function() action(); function(argument1 && argument2); - + if (c1 && (c2 || c3)) foo; @@ -3497,7 +3497,7 @@ describe('cindent', function() (c2 || c3)) { } - + if ( c1 && ( c2 || c3)) @@ -3512,8 +3512,8 @@ describe('cindent', function() it('41 is working', function() insert_([=[ - - + + void func(void) { if (condition1 @@ -3521,7 +3521,7 @@ describe('cindent', function() action(); function(argument1 && argument2); - + if (c1 && (c2 || c3)) foo; @@ -3529,7 +3529,7 @@ describe('cindent', function() (c2 || c3)) { } - + if ( c1 && ( c2 || c3)) @@ -3541,11 +3541,11 @@ describe('cindent', function() } ]=]) - execute('set cino=k2s,(s') + feed_command('set cino=k2s,(s') feed('2kdd3j=][') expect([=[ - + void func(void) { if (condition1 @@ -3553,7 +3553,7 @@ describe('cindent', function() action(); function(argument1 && argument2); - + if (c1 && (c2 || c3)) foo; @@ -3561,7 +3561,7 @@ describe('cindent', function() (c2 || c3)) { } - + if ( c1 && ( c2 || c3)) @@ -3576,8 +3576,8 @@ describe('cindent', function() it('42 is working', function() insert_([=[ - - + + void func(void) { if (condition1 @@ -3585,7 +3585,7 @@ describe('cindent', function() action(); function(argument1 && argument2); - + if (c1 && (c2 || c3)) foo; @@ -3597,7 +3597,7 @@ describe('cindent', function() && (c22345 || c3)) printf("foo\n"); - + c = c1 && ( c2 || @@ -3606,11 +3606,11 @@ describe('cindent', function() } ]=]) - execute('set cino=k2s,(s,U1') + feed_command('set cino=k2s,(s,U1') feed('2kdd3j=][') expect([=[ - + void func(void) { if (condition1 @@ -3618,7 +3618,7 @@ describe('cindent', function() action(); function(argument1 && argument2); - + if (c1 && (c2 || c3)) foo; @@ -3630,7 +3630,7 @@ describe('cindent', function() && (c22345 || c3)) printf("foo\n"); - + c = c1 && ( c2 || @@ -3642,8 +3642,8 @@ describe('cindent', function() it('43 is working', function() insert_([=[ - - + + void func(void) { if (condition1 @@ -3651,7 +3651,7 @@ describe('cindent', function() action(); function(argument1 && argument2); - + if (c1 && (c2 || c3)) foo; @@ -3663,12 +3663,12 @@ describe('cindent', function() && (c22345 || c3)) printf("foo\n"); - + if ( c1 && ( c2 || c3)) foo; - + a_long_line( argument, argument); @@ -3677,11 +3677,11 @@ describe('cindent', function() } ]=]) - execute('set cino=k2s,(0,W4') + feed_command('set cino=k2s,(0,W4') feed('2kdd3j=][') expect([=[ - + void func(void) { if (condition1 @@ -3689,7 +3689,7 @@ describe('cindent', function() action(); function(argument1 && argument2); - + if (c1 && (c2 || c3)) foo; @@ -3701,12 +3701,12 @@ describe('cindent', function() && (c22345 || c3)) printf("foo\n"); - + if ( c1 && ( c2 || c3)) foo; - + a_long_line( argument, argument); @@ -3718,8 +3718,8 @@ describe('cindent', function() it('44 is working', function() insert_([=[ - - + + void func(void) { if (condition1 @@ -3727,7 +3727,7 @@ describe('cindent', function() action(); function(argument1 && argument2); - + if (c1 && (c2 || c3)) foo; @@ -3742,11 +3742,11 @@ describe('cindent', function() } ]=]) - execute('set cino=k2s,u2') + feed_command('set cino=k2s,u2') feed('2kdd3j=][') expect([=[ - + void func(void) { if (condition1 @@ -3754,7 +3754,7 @@ describe('cindent', function() action(); function(argument1 && argument2); - + if (c1 && (c2 || c3)) foo; @@ -3772,8 +3772,8 @@ describe('cindent', function() it('45 is working', function() insert_([=[ - - + + void func(void) { if (condition1 @@ -3781,7 +3781,7 @@ describe('cindent', function() action(); function(argument1 && argument2); - + if (c1 && (c2 || c3)) foo; @@ -3793,7 +3793,7 @@ describe('cindent', function() && (c22345 || c3)) printf("foo\n"); - + if ( c1 && ( c2 || c3)) @@ -3805,11 +3805,11 @@ describe('cindent', function() } ]=]) - execute('set cino=k2s,(0,w1') + feed_command('set cino=k2s,(0,w1') feed('2kdd3j=][') expect([=[ - + void func(void) { if (condition1 @@ -3817,7 +3817,7 @@ describe('cindent', function() action(); function(argument1 && argument2); - + if (c1 && (c2 || c3)) foo; @@ -3829,7 +3829,7 @@ describe('cindent', function() && (c22345 || c3)) printf("foo\n"); - + if ( c1 && ( c2 || c3)) @@ -3844,8 +3844,8 @@ describe('cindent', function() it('46 is working', function() insert_([=[ - - + + void func(void) { if (condition1 @@ -3853,7 +3853,7 @@ describe('cindent', function() action(); function(argument1 && argument2); - + if (c1 && (c2 || c3)) foo; @@ -3864,11 +3864,11 @@ describe('cindent', function() } ]=]) - execute('set cino=k2,(s') + feed_command('set cino=k2,(s') feed('2kdd3j=][') expect([=[ - + void func(void) { if (condition1 @@ -3876,7 +3876,7 @@ describe('cindent', function() action(); function(argument1 && argument2); - + if (c1 && (c2 || c3)) foo; @@ -3890,7 +3890,7 @@ describe('cindent', function() it('47 is working', function() insert_([=[ - + NAMESPACESTART /* valid namespaces with normal indent */ namespace @@ -3930,7 +3930,7 @@ describe('cindent', function() 22222222222222222; } } - + /* invalid namespaces use block indent */ namespace test test2 { 111111111111111111111; @@ -3956,12 +3956,12 @@ describe('cindent', function() NAMESPACEEND ]=]) - execute('set cino=N-s') - execute('/^NAMESPACESTART') + feed_command('set cino=N-s') + feed_command('/^NAMESPACESTART') feed('=/^NAMESPACEEND') expect([=[ - + NAMESPACESTART /* valid namespaces with normal indent */ namespace @@ -4001,7 +4001,7 @@ describe('cindent', function() 22222222222222222; } } - + /* invalid namespaces use block indent */ namespace test test2 { 111111111111111111111; @@ -4030,7 +4030,7 @@ describe('cindent', function() it('48 is working', function() insert_([=[ - + JSSTART var bar = { foo: { @@ -4047,12 +4047,12 @@ describe('cindent', function() JSEND ]=]) - execute('set cino=j1,J1') - execute('/^JSSTART') + feed_command('set cino=j1,J1') + feed_command('/^JSSTART') feed('=/^JSEND') expect([=[ - + JSSTART var bar = { foo: { @@ -4072,7 +4072,7 @@ describe('cindent', function() it('49 is working', function() insert_([=[ - + JSSTART var foo = [ 1, @@ -4082,12 +4082,12 @@ describe('cindent', function() JSEND ]=]) - execute('set cino=j1,J1') - execute('/^JSSTART') + feed_command('set cino=j1,J1') + feed_command('/^JSSTART') feed('=/^JSEND') expect([=[ - + JSSTART var foo = [ 1, @@ -4100,7 +4100,7 @@ describe('cindent', function() it('50 is working', function() insert_([=[ - + JSSTART function bar() { var foo = [ @@ -4112,12 +4112,12 @@ describe('cindent', function() JSEND ]=]) - execute('set cino=j1,J1') - execute('/^JSSTART') + feed_command('set cino=j1,J1') + feed_command('/^JSSTART') feed('=/^JSEND') expect([=[ - + JSSTART function bar() { var foo = [ @@ -4132,10 +4132,10 @@ describe('cindent', function() it('51 is working', function() insert_([=[ - + JSSTART (function($){ - + if (cond && cond) { stmt; @@ -4143,18 +4143,18 @@ describe('cindent', function() window.something.left = (width - 50 + offset) + "px"; var class_name='myclass'; - + function private_method() { } - + var public_method={ method: function(options,args){ private_method(); } } - + function init(options) { - + $(this).data(class_name+'_public',$.extend({},{ foo: 'bar', bar: 2, @@ -4168,19 +4168,19 @@ describe('cindent', function() } }, options||{})); } - + $.fn[class_name]=function() { - + var _arguments=arguments; return this.each(function(){ - + var options=$(this).data(class_name+'_public'); if (!options) { init.apply(this,_arguments); - + } else { var method=public_method[_arguments[0]]; - + if (typeof(method)!='function') { console.log(class_name+' has no method "'+_arguments[0]+'"'); return false; @@ -4190,20 +4190,20 @@ describe('cindent', function() } }); } - + })(jQuery); JSEND ]=]) - execute('set cino=j1,J1') - execute('/^JSSTART') + feed_command('set cino=j1,J1') + feed_command('/^JSSTART') feed('=/^JSEND') expect([=[ - + JSSTART (function($){ - + if (cond && cond) { stmt; @@ -4211,18 +4211,18 @@ describe('cindent', function() window.something.left = (width - 50 + offset) + "px"; var class_name='myclass'; - + function private_method() { } - + var public_method={ method: function(options,args){ private_method(); } } - + function init(options) { - + $(this).data(class_name+'_public',$.extend({},{ foo: 'bar', bar: 2, @@ -4236,19 +4236,19 @@ describe('cindent', function() } }, options||{})); } - + $.fn[class_name]=function() { - + var _arguments=arguments; return this.each(function(){ - + var options=$(this).data(class_name+'_public'); if (!options) { init.apply(this,_arguments); - + } else { var method=public_method[_arguments[0]]; - + if (typeof(method)!='function') { console.log(class_name+' has no method "'+_arguments[0]+'"'); return false; @@ -4258,7 +4258,7 @@ describe('cindent', function() } }); } - + })(jQuery); JSEND ]=]) @@ -4266,7 +4266,7 @@ describe('cindent', function() it('52 is working', function() insert_([=[ - + JSSTART function init(options) { $(this).data(class_name+'_public',$.extend({},{ @@ -4285,12 +4285,12 @@ describe('cindent', function() JSEND ]=]) - execute('set cino=j1,J1') - execute('/^JSSTART') + feed_command('set cino=j1,J1') + feed_command('/^JSSTART') feed('=/^JSEND') expect([=[ - + JSSTART function init(options) { $(this).data(class_name+'_public',$.extend({},{ @@ -4312,7 +4312,7 @@ describe('cindent', function() it('53 is working', function() insert_([=[ - + JSSTART (function($){ function init(options) { @@ -4333,12 +4333,12 @@ describe('cindent', function() JSEND ]=]) - execute('set cino=j1,J1') - execute('/^JSSTART') + feed_command('set cino=j1,J1') + feed_command('/^JSSTART') feed('=/^JSEND') expect([=[ - + JSSTART (function($){ function init(options) { @@ -4362,7 +4362,7 @@ describe('cindent', function() it('javascript indent / vim-patch 7.4.670', function() insert_([=[ - + JSSTART // Results of JavaScript indent // 1 @@ -4379,7 +4379,7 @@ describe('cindent', function() 'i' ]; }()) - + // 2 (function(){ var a = [ @@ -4400,7 +4400,7 @@ describe('cindent', function() 'i' ]; }()) - + // 3 (function(){ var a = [ @@ -4423,7 +4423,7 @@ describe('cindent', function() 'i' ]; }()) - + // 4 { var a = [ @@ -4433,7 +4433,7 @@ describe('cindent', function() var b; var c; } - + // 5 { var a = [ @@ -4444,7 +4444,7 @@ describe('cindent', function() 3 ]; } - + // 6 { var a = [ @@ -4456,7 +4456,7 @@ describe('cindent', function() 3 ]; } - + // 7 { var a = [ @@ -4468,7 +4468,7 @@ describe('cindent', function() 3 ]; } - + // 8 var x = [ (function(){ @@ -4483,7 +4483,7 @@ describe('cindent', function() i; }) ]; - + // 9 var a = [ 0 + @@ -4502,7 +4502,7 @@ describe('cindent', function() 'h', 'i' ]; - + // 10 var a, b, @@ -4517,12 +4517,12 @@ describe('cindent', function() ]=]) -- :set cino=j1,J1,+2 - execute('set cino=j1,J1,+2') - execute('/^JSSTART') + feed_command('set cino=j1,J1,+2') + feed_command('/^JSSTART') feed('=/^JSEND') expect([=[ - + JSSTART // Results of JavaScript indent // 1 @@ -4539,7 +4539,7 @@ describe('cindent', function() 'i' ]; }()) - + // 2 (function(){ var a = [ @@ -4560,7 +4560,7 @@ describe('cindent', function() 'i' ]; }()) - + // 3 (function(){ var a = [ @@ -4583,7 +4583,7 @@ describe('cindent', function() 'i' ]; }()) - + // 4 { var a = [ @@ -4593,7 +4593,7 @@ describe('cindent', function() var b; var c; } - + // 5 { var a = [ @@ -4604,7 +4604,7 @@ describe('cindent', function() 3 ]; } - + // 6 { var a = [ @@ -4616,7 +4616,7 @@ describe('cindent', function() 3 ]; } - + // 7 { var a = [ @@ -4628,7 +4628,7 @@ describe('cindent', function() 3 ]; } - + // 8 var x = [ (function(){ @@ -4643,7 +4643,7 @@ describe('cindent', function() i; }) ]; - + // 9 var a = [ 0 + @@ -4662,7 +4662,7 @@ describe('cindent', function() 'h', 'i' ]; - + // 10 var a, b, diff --git a/test/functional/legacy/increment_spec.lua b/test/functional/legacy/increment_spec.lua index a76718ed8e..15273a4ad5 100644 --- a/test/functional/legacy/increment_spec.lua +++ b/test/functional/legacy/increment_spec.lua @@ -1,7 +1,7 @@ -- Tests for using Ctrl-A/Ctrl-X on visual selections local helpers = require('test.functional.helpers')(after_each) -local source, execute = helpers.source, helpers.execute +local source, command = helpers.source, helpers.command local call, clear = helpers.call, helpers.clear local eq, nvim = helpers.eq, helpers.meths @@ -742,14 +742,14 @@ describe('Ctrl-A/Ctrl-X on visual selections', function() local id = string.format('%02d', i) it('works on Test ' .. id, function() - execute('set nrformats&vi') -- &vi makes Vim compatible + command('set nrformats&vi') -- &vi makes Vim compatible call('Test_visual_increment_' .. id) eq({}, nvim.get_vvar('errors')) end) end it('does not drop leading zeroes', function() - execute('set nrformats&vi') -- &vi makes Vim compatible + command('set nrformats&vi') -- &vi makes Vim compatible call('Test_normal_increment_01') eq({}, nvim.get_vvar('errors')) end) From dd93733e521d759e9e7c9303b278be28997b3e43 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 8 Apr 2017 22:33:02 +0300 Subject: [PATCH 0387/1671] functests: Refactor legacy/054_buffer_local_autocommands_spec --- .../054_buffer_local_autocommands_spec.lua | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/test/functional/legacy/054_buffer_local_autocommands_spec.lua b/test/functional/legacy/054_buffer_local_autocommands_spec.lua index 1f7c4dee6a..c8b9dfa98f 100644 --- a/test/functional/legacy/054_buffer_local_autocommands_spec.lua +++ b/test/functional/legacy/054_buffer_local_autocommands_spec.lua @@ -1,33 +1,37 @@ -- Some tests for buffer-local autocommands local helpers = require('test.functional.helpers')(after_each) -local clear, execute, eq = helpers.clear, helpers.execute, helpers.eq -local curbuf_contents = helpers.curbuf_contents + +local clear = helpers.clear +local expect = helpers.expect +local command = helpers.command + +local fname = 'Xtest-functional-legacy-054' describe('BufLeave ', function() setup(clear) it('is working', function() - execute('w! xx') - execute('au BufLeave norm Ibuffer-local autocommand') - execute('au BufLeave update') - + command('write! ' .. fname) + command('autocmd BufLeave normal! Ibuffer-local autocommand') + command('autocmd BufLeave update') + -- Here, autocommand for xx shall append a line -- But autocommand shall not apply to buffer named - execute('e somefile') + command('edit somefile') -- Here, autocommand shall be auto-deleted - execute('bwipe xx') - - -- Nothing shall be written - execute('e xx') - execute('e somefile') - execute('e xx') + command('bwipeout ' .. fname) - eq('buffer-local autocommand', curbuf_contents()) + -- Nothing shall be written + command('edit ' .. fname) + command('edit somefile') + command('edit ' .. fname) + + expect('buffer-local autocommand') end) teardown(function() - os.remove('xx') + os.remove(fname) end) end) From 9158cc171f46ebae0a0d3d1721aa5b7d829bcba5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 8 Apr 2017 23:48:22 +0300 Subject: [PATCH 0388/1671] functests: Refactor options/pastetoggle Note: typo, ttimeoutlen not set ever. Mention @hardenedapple --- test/functional/options/pastetoggle_spec.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/functional/options/pastetoggle_spec.lua b/test/functional/options/pastetoggle_spec.lua index e449df31f5..ec3c60fe37 100644 --- a/test/functional/options/pastetoggle_spec.lua +++ b/test/functional/options/pastetoggle_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear = helpers.clear local feed = helpers.feed -local execute = helpers.execute +local command = helpers.command local eq = helpers.eq local eval = helpers.eval local sleep = helpers.sleep @@ -10,8 +10,8 @@ local sleep = helpers.sleep describe("'pastetoggle' option", function() before_each(function() clear() - execute('set nopaste') - execute('set pastetoggle=a') + command('set nopaste') + command('set pastetoggle=a') end) it("toggles 'paste'", function() eq(eval('&paste'), 0) @@ -23,8 +23,8 @@ describe("'pastetoggle' option", function() it("multiple key 'pastetoggle' is waited for", function() eq(eval('&paste'), 0) local pastetoggle = 'lllll' - execute('set pastetoggle=' .. pastetoggle) - execute('set timeoutlen=1', 'set ttimoutlen=10000') + command('set pastetoggle=' .. pastetoggle) + command('set timeoutlen=1 ttimeoutlen=10000') feed(pastetoggle:sub(0, 2)) -- sleep() for long enough that vgetorpeek() is gotten into, but short -- enough that ttimeoutlen is not reached. From a34408ef7f5ce25462beedff44db552f11424f3f Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 9 Apr 2017 02:11:08 +0200 Subject: [PATCH 0389/1671] test: retry(): Report number of retries. (#6475) tui_spec.lua: Retry the terminal-mode test. --- test/functional/helpers.lua | 7 +- test/functional/terminal/tui_spec.lua | 106 ++++++++++++++------------ 2 files changed, 61 insertions(+), 52 deletions(-) diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 335cf3c3ff..42ed1800d3 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -246,12 +246,13 @@ local function retry(max, max_ms, fn) return result end if (max and tries >= max) or (luv.now() - start_time > timeout) then - break + if type(result) == "string" then + result = "\nretry() attempts: "..tostring(tries).."\n"..result + end + error(result) end tries = tries + 1 end - -- Do not use pcall() for the final attempt, let the failure bubble up. - return fn() end local function clear(...) diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 90051b8cd5..e56fd2db5c 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -2,9 +2,10 @@ -- as a simple way to send keys and assert screen state. local helpers = require('test.functional.helpers')(after_each) local thelpers = require('test.functional.terminal.helpers') -local feed = thelpers.feed_data +local feed_data = thelpers.feed_data local execute = helpers.execute local nvim_dir = helpers.nvim_dir +local retry = helpers.retry if helpers.pending_win32(pending) then return end @@ -34,7 +35,7 @@ describe('tui', function() end) it('accepts basic utf-8 input', function() - feed('iabc\ntest1\ntest2') + feed_data('iabc\ntest1\ntest2') screen:expect([[ abc | test1 | @@ -44,7 +45,7 @@ describe('tui', function() {3:-- INSERT --} | {3:-- TERMINAL --} | ]]) - feed('\027') + feed_data('\027') screen:expect([[ abc | test1 | @@ -60,7 +61,7 @@ describe('tui', function() local keys = 'dfghjkl' for c in keys:gmatch('.') do execute('nnoremap ialt-'..c..'') - feed('\027'..c) + feed_data('\027'..c) end screen:expect([[ alt-j | @@ -71,7 +72,7 @@ describe('tui', function() | {3:-- TERMINAL --} | ]]) - feed('gg') + feed_data('gg') screen:expect([[ {1:a}lt-d | alt-f | @@ -90,7 +91,7 @@ describe('tui', function() -- Example: for input ALT+j: -- * Vim (Nvim prior to #3982) sets high-bit, inserts "ê". -- * Nvim (after #3982) inserts "j". - feed('i\027j') + feed_data('i\027j') screen:expect([[ j{1: } | {4:~ }| @@ -103,10 +104,10 @@ describe('tui', function() end) it('accepts ascii control sequences', function() - feed('i') - feed('\022\007') -- ctrl+g - feed('\022\022') -- ctrl+v - feed('\022\013') -- ctrl+m + feed_data('i') + feed_data('\022\007') -- ctrl+g + feed_data('\022\022') -- ctrl+v + feed_data('\022\013') -- ctrl+m screen:expect([[ {9:^G^V^M}{1: } | {4:~ }| @@ -119,7 +120,7 @@ describe('tui', function() end) it('automatically sends for bracketed paste sequences', function() - feed('i\027[200~') + feed_data('i\027[200~') screen:expect([[ {1: } | {4:~ }| @@ -129,7 +130,7 @@ describe('tui', function() {3:-- INSERT (paste) --} | {3:-- TERMINAL --} | ]]) - feed('pasted from terminal') + feed_data('pasted from terminal') screen:expect([[ pasted from terminal{1: } | {4:~ }| @@ -139,7 +140,7 @@ describe('tui', function() {3:-- INSERT (paste) --} | {3:-- TERMINAL --} | ]]) - feed('\027[201~') + feed_data('\027[201~') screen:expect([[ pasted from terminal{1: } | {4:~ }| @@ -157,7 +158,7 @@ describe('tui', function() for i = 1, 3000 do t[i] = 'item ' .. tostring(i) end - feed('i\027[200~'..table.concat(t, '\n')..'\027[201~') + feed_data('i\027[200~'..table.concat(t, '\n')..'\027[201~') screen:expect([[ item 2997 | item 2998 | @@ -180,7 +181,7 @@ describe('tui with non-tty file descriptors', function() it('can handle pipes as stdout and stderr', function() local screen = thelpers.screen_setup(0, '"'..helpers.nvim_prog ..' -u NONE -i NONE --cmd \'set noswapfile noshowcmd noruler\' --cmd \'normal iabc\' > /dev/null 2>&1 && cat testF && rm testF"') - feed(':w testF\n:q\n') + feed_data(':w testF\n:q\n') screen:expect([[ :w testF | :q | @@ -200,12 +201,13 @@ describe('tui focus event handling', function() helpers.clear() screen = thelpers.screen_setup(0, '["'..helpers.nvim_prog ..'", "-u", "NONE", "-i", "NONE", "--cmd", "set noswapfile noshowcmd noruler"]') - execute('autocmd FocusGained * echo "gained"') - execute('autocmd FocusLost * echo "lost"') + feed_data(":autocmd FocusGained * echo 'gained'\n") + feed_data(":autocmd FocusLost * echo 'lost'\n") + feed_data("\034\016") -- CTRL-\ CTRL-N end) it('can handle focus events in normal mode', function() - feed('\027[I') + feed_data('\027[I') screen:expect([[ {1: } | {4:~ }| @@ -216,7 +218,7 @@ describe('tui focus event handling', function() {3:-- TERMINAL --} | ]]) - feed('\027[O') + feed_data('\027[O') screen:expect([[ {1: } | {4:~ }| @@ -230,8 +232,8 @@ describe('tui focus event handling', function() it('can handle focus events in insert mode', function() execute('set noshowmode') - feed('i') - feed('\027[I') + feed_data('i') + feed_data('\027[I') screen:expect([[ {1: } | {4:~ }| @@ -241,7 +243,7 @@ describe('tui focus event handling', function() gained | {3:-- TERMINAL --} | ]]) - feed('\027[O') + feed_data('\027[O') screen:expect([[ {1: } | {4:~ }| @@ -254,8 +256,8 @@ describe('tui focus event handling', function() end) it('can handle focus events in cmdline mode', function() - feed(':') - feed('\027[I') + feed_data(':') + feed_data('\027[I') screen:expect([[ | {4:~ }| @@ -265,7 +267,7 @@ describe('tui focus event handling', function() g{1:a}ined | {3:-- TERMINAL --} | ]]) - feed('\027[O') + feed_data('\027[O') screen:expect([[ | {4:~ }| @@ -278,30 +280,36 @@ describe('tui focus event handling', function() end) it('can handle focus events in terminal mode', function() - execute('set shell='..nvim_dir..'/shell-test') - execute('set laststatus=0') - execute('set noshowmode') - execute('terminal') - feed('\027[I') - screen:expect([[ - ready $ | - [Process exited 0]{1: } | - | - | - | - gained | - {3:-- TERMINAL --} | - ]]) - feed('\027[O') - screen:expect([[ - ready $ | - [Process exited 0]{1: } | - | - | - | - lost | - {3:-- TERMINAL --} | - ]]) + feed_data(':set shell='..nvim_dir..'/shell-test\n') + feed_data(':set noshowmode laststatus=0\n') + + retry(2, 3 * screen.timeout, function() + feed_data(':terminal\n') + feed_data('\027[I') + screen:expect([[ + ready $ | + [Process exited 0]{1: } | + | + | + | + gained | + {3:-- TERMINAL --} | + ]]) + feed_data('\027[O') + screen:expect([[ + ready $ | + [Process exited 0]{1: } | + | + | + | + lost | + {3:-- TERMINAL --} | + ]]) + + -- If retry is needed... + feed_data("\034\016") -- CTRL-\ CTRL-N + feed_data(':bwipeout!\n') + end) end) end) From 65fb622000af8e3dbb65480e1581758ecf4ba3e2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 9 Apr 2017 00:12:26 +0300 Subject: [PATCH 0390/1671] functests: Replace execute with either command or feed_command Hope this will make people using feed_command less likely: this hides bugs. Already found at least two: 1. msgpackparse() will show internal error: hash_add() in case of duplicate keys, though it will still work correctly. Currently silenced. 2. ttimeoutlen was spelled incorrectly, resulting in option not being set when expected. Test was still functioning somehow though. Currently fixed. --- test/functional/api/buffer_spec.lua | 6 +- .../api/server_notifications_spec.lua | 8 +- test/functional/autocmd/bufenter_spec.lua | 4 +- test/functional/autocmd/textyankpost_spec.lua | 30 +- .../clipboard/clipboard_provider_spec.lua | 86 ++--- test/functional/core/job_spec.lua | 12 +- test/functional/eval/api_functions_spec.lua | 22 +- test/functional/eval/glob_spec.lua | 4 +- test/functional/eval/json_functions_spec.lua | 103 +++--- test/functional/eval/modeline_spec.lua | 4 +- .../eval/msgpack_functions_spec.lua | 149 ++++---- test/functional/eval/reltime_spec.lua | 4 +- test/functional/eval/setpos_spec.lua | 9 +- test/functional/eval/special_vars_spec.lua | 6 +- test/functional/eval/system_spec.lua | 16 +- test/functional/eval/timer_spec.lua | 26 +- test/functional/ex_cmds/arg_spec.lua | 8 +- test/functional/ex_cmds/bang_filter_spec.lua | 4 +- test/functional/ex_cmds/cd_spec.lua | 54 +-- test/functional/ex_cmds/ctrl_c_spec.lua | 4 +- test/functional/ex_cmds/drop_spec.lua | 22 +- test/functional/ex_cmds/edit_spec.lua | 9 +- test/functional/ex_cmds/encoding_spec.lua | 6 +- test/functional/ex_cmds/grep_spec.lua | 10 +- test/functional/ex_cmds/menu_spec.lua | 16 +- test/functional/ex_cmds/oldfiles_spec.lua | 22 +- test/functional/ex_cmds/recover_spec.lua | 18 +- test/functional/ex_cmds/undojoin_spec.lua | 8 +- test/functional/ex_cmds/write_spec.lua | 15 +- test/functional/ex_cmds/wundo_spec.lua | 15 +- test/functional/ex_cmds/wviminfo_spec.lua | 12 +- test/functional/helpers.lua | 1 - .../legacy/002_filename_recognition_spec.lua | 12 +- .../004_bufenter_with_modelines_spec.lua | 21 +- .../005_bufleave_delete_buffer_spec.lua | 27 +- .../legacy/006_argument_list_spec.lua | 42 ++- .../legacy/007_ball_buffer_list_spec.lua | 32 +- .../legacy/008_autocommands_spec.lua | 30 +- .../legacy/011_autocommands_spec.lua | 60 +-- test/functional/legacy/015_alignment_spec.lua | 30 +- .../legacy/019_smarttab_expandtab_spec.lua | 18 +- .../legacy/020_blockwise_visual_spec.lua | 13 +- .../functional/legacy/021_control_wi_spec.lua | 11 +- .../legacy/022_line_ending_spec.lua | 6 +- .../legacy/023_edit_arguments_spec.lua | 34 +- .../legacy/025_jump_tag_hidden_spec.lua | 24 +- .../legacy/026_execute_while_if_spec.lua | 6 +- .../legacy/028_source_ctrl_v_spec.lua | 6 +- .../legacy/030_fileformats_spec.lua | 343 +++++++++-------- .../legacy/031_close_commands_spec.lua | 66 ++-- .../legacy/033_lisp_indent_spec.lua | 27 +- .../legacy/034_user_function_spec.lua | 22 +- .../035_increment_and_decrement_spec.lua | 12 +- .../036_regexp_character_classes_spec.lua | 6 +- .../legacy/038_virtual_replace_spec.lua | 12 +- .../039_visual_block_mode_commands_spec.lua | 24 +- ...writing_and_reading_hundred_kbyte_spec.lua | 25 +- .../legacy/043_magic_settings_spec.lua | 21 +- .../044_099_regexp_multibyte_magic_spec.lua | 34 +- test/functional/legacy/045_folding_spec.lua | 84 ++--- test/functional/legacy/051_highlight_spec.lua | 75 ++-- .../legacy/055_list_and_dict_types_spec.lua | 146 ++++---- test/functional/legacy/057_sort_spec.lua | 147 +++++--- .../legacy/059_utf8_spell_checking_spec.lua | 242 ++++++------ test/functional/legacy/061_undo_tree_spec.lua | 46 +-- test/functional/legacy/062_tab_pages_spec.lua | 70 ++-- .../legacy/063_match_and_matchadd_spec.lua | 90 ++--- .../065_float_and_logic_operators_spec.lua | 6 +- .../legacy/066_visual_block_tab_spec.lua | 16 +- .../legacy/067_augroup_exists_spec.lua | 42 +-- .../legacy/068_text_formatting_spec.lua | 154 ++++---- .../legacy/069_multibyte_formatting_spec.lua | 44 +-- test/functional/legacy/072_undo_file_spec.lua | 48 +-- .../legacy/074_global_var_in_viminfo_spec.lua | 12 +- test/functional/legacy/075_maparg_spec.lua | 46 +-- .../legacy/077_mf_hash_grow_spec.lua | 18 +- .../functional/legacy/080_substitute_spec.lua | 28 +- .../legacy/081_coptions_movement_spec.lua | 8 +- .../legacy/082_string_comparison_spec.lua | 10 +- test/functional/legacy/084_curswant_spec.lua | 4 +- .../legacy/088_conceal_tabs_spec.lua | 20 +- .../092_mksession_cursor_cols_utf8_spec.lua | 22 +- .../093_mksession_cursor_cols_latin1_spec.lua | 22 +- .../legacy/094_visual_mode_operators_spec.lua | 86 ++--- .../legacy/096_location_list_spec.lua | 138 +++---- test/functional/legacy/097_glob_path_spec.lua | 28 +- test/functional/legacy/101_hlsearch_spec.lua | 74 ++-- .../legacy/102_fnameescape_spec.lua | 12 +- .../legacy/104_let_assignment_spec.lua | 6 +- .../legacy/106_errorformat_spec.lua | 16 +- .../107_adjust_window_and_contents_spec.lua | 56 +-- .../108_backtrace_debug_commands_spec.lua | 48 +-- test/functional/legacy/arglist_spec.lua | 246 ++++++------ test/functional/legacy/assert_spec.lua | 8 +- test/functional/legacy/autochdir_spec.lua | 8 +- .../functional/legacy/autocmd_option_spec.lua | 74 ++-- .../legacy/autoformat_join_spec.lua | 15 +- test/functional/legacy/breakindent_spec.lua | 250 ++++++------- test/functional/legacy/changelist_spec.lua | 10 +- test/functional/legacy/charsearch_spec.lua | 18 +- test/functional/legacy/close_count_spec.lua | 206 ++++++----- test/functional/legacy/command_count_spec.lua | 60 +-- test/functional/legacy/comparators_spec.lua | 4 +- test/functional/legacy/delete_spec.lua | 30 +- test/functional/legacy/eval_spec.lua | 350 +++++++++--------- test/functional/legacy/fixeol_spec.lua | 40 +- test/functional/legacy/function_sort_spec.lua | 26 +- test/functional/legacy/getcwd_spec.lua | 46 +-- test/functional/legacy/glob2regpat_spec.lua | 7 +- test/functional/legacy/insertcount_spec.lua | 4 +- test/functional/legacy/join_spec.lua | 14 +- test/functional/legacy/lispwords_spec.lua | 4 +- test/functional/legacy/listchars_spec.lua | 40 +- test/functional/legacy/listlbr_spec.lua | 168 ++++----- test/functional/legacy/mapping_spec.lua | 62 ++-- test/functional/legacy/marks_spec.lua | 12 +- .../legacy/nested_function_spec.lua | 4 +- test/functional/legacy/packadd_spec.lua | 14 +- test/functional/legacy/search_mbyte_spec.lua | 16 +- test/functional/legacy/searchpos_spec.lua | 4 +- test/functional/legacy/set_spec.lua | 18 +- test/functional/legacy/signs_spec.lua | 14 +- test/functional/legacy/textobjects_spec.lua | 10 +- test/functional/legacy/utf8_spec.lua | 18 +- test/functional/legacy/wordcount_spec.lua | 87 +++-- test/functional/legacy/writefile_spec.lua | 24 +- test/functional/normal/count_spec.lua | 4 +- test/functional/normal/fold_spec.lua | 34 +- test/functional/normal/put_spec.lua | 24 +- test/functional/normal/undo_spec.lua | 4 +- test/functional/options/defaults_spec.lua | 4 +- test/functional/options/shortmess_spec.lua | 8 +- test/functional/plugin/health_spec.lua | 10 +- test/functional/plugin/matchparen_spec.lua | 4 +- test/functional/terminal/buffer_spec.lua | 30 +- test/functional/terminal/cursor_spec.lua | 4 +- test/functional/terminal/ex_terminal_spec.lua | 18 +- test/functional/terminal/helpers.lua | 8 +- test/functional/terminal/highlight_spec.lua | 16 +- test/functional/terminal/scrollback_spec.lua | 6 +- test/functional/terminal/tui_spec.lua | 20 +- .../terminal/window_split_tab_spec.lua | 8 +- test/functional/ui/bufhl_spec.lua | 8 +- test/functional/ui/highlight_spec.lua | 138 +++---- test/functional/ui/inccommand_spec.lua | 130 +++---- test/functional/ui/input_spec.lua | 6 +- test/functional/ui/mouse_spec.lua | 72 ++-- test/functional/ui/quickfix_spec.lua | 34 +- test/functional/ui/screen_basic_spec.lua | 46 ++- test/functional/ui/searchhl_spec.lua | 34 +- test/functional/ui/sign_spec.lua | 12 +- test/functional/ui/syntax_conceal_spec.lua | 44 +-- test/functional/ui/wildmode_spec.lua | 14 +- test/functional/viml/completion_spec.lua | 52 +-- test/functional/viml/lang_spec.lua | 5 +- 155 files changed, 3174 insertions(+), 3075 deletions(-) diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index 552e3a8564..c3002618b0 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -5,7 +5,7 @@ local curbufmeths, ok = helpers.curbufmeths, helpers.ok local funcs = helpers.funcs local request = helpers.request local exc_exec = helpers.exc_exec -local execute = helpers.execute +local feed_command = helpers.feed_command local insert = helpers.insert local NIL = helpers.NIL local meth_pcall = helpers.meth_pcall @@ -246,7 +246,7 @@ describe('api/buf', function() end) it("set_line on alternate buffer does not access invalid line (E315)", function() - execute('set hidden') + feed_command('set hidden') insert('Initial file') command('enew') insert([[ @@ -257,7 +257,7 @@ describe('api/buf', function() The Other Buffer]]) - execute('$') + feed_command('$') local retval = exc_exec("call nvim_buf_set_lines(1, 0, 1, v:false, ['test'])") eq(0, retval) end) diff --git a/test/functional/api/server_notifications_spec.lua b/test/functional/api/server_notifications_spec.lua index 78639d7ed7..9d7cfb9b78 100644 --- a/test/functional/api/server_notifications_spec.lua +++ b/test/functional/api/server_notifications_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) -local eq, clear, eval, execute, nvim, next_message = - helpers.eq, helpers.clear, helpers.eval, helpers.execute, helpers.nvim, +local eq, clear, eval, command, nvim, next_message = + helpers.eq, helpers.clear, helpers.eval, helpers.command, helpers.nvim, helpers.next_message local meths = helpers.meths @@ -16,8 +16,8 @@ describe('notify', function() it('sends the notification/args to the corresponding channel', function() eval('rpcnotify('..channel..', "test-event", 1, 2, 3)') eq({'notification', 'test-event', {1, 2, 3}}, next_message()) - execute('au FileType lua call rpcnotify('..channel..', "lua!")') - execute('set filetype=lua') + command('au FileType lua call rpcnotify('..channel..', "lua!")') + command('set filetype=lua') eq({'notification', 'lua!', {}}, next_message()) end) end) diff --git a/test/functional/autocmd/bufenter_spec.lua b/test/functional/autocmd/bufenter_spec.lua index ccbcdf5c5e..2758be0b13 100644 --- a/test/functional/autocmd/bufenter_spec.lua +++ b/test/functional/autocmd/bufenter_spec.lua @@ -4,7 +4,7 @@ local clear = helpers.clear local command = helpers.command local eq = helpers.eq local eval = helpers.eval -local execute = helpers.execute +local command = helpers.command local request = helpers.request local source = helpers.source @@ -28,7 +28,7 @@ describe('autocmd BufEnter', function() endtry endfunction ]]) - execute("call Test()") + command("call Test()") eq(1, eval("exists('g:dir_bufenter')")) -- Did BufEnter for the directory. eq(2, eval("bufnr('%')")) -- Switched to the dir buffer. end) diff --git a/test/functional/autocmd/textyankpost_spec.lua b/test/functional/autocmd/textyankpost_spec.lua index bd5f1912c5..a34d7ad53c 100644 --- a/test/functional/autocmd/textyankpost_spec.lua +++ b/test/functional/autocmd/textyankpost_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local clear, eval, eq = helpers.clear, helpers.eval, helpers.eq -local feed, execute, expect, command = helpers.feed, helpers.execute, helpers.expect, helpers.command +local feed, command, expect, command = helpers.feed, helpers.command, helpers.expect, helpers.command local curbufmeths, funcs, neq = helpers.curbufmeths, helpers.funcs, helpers.neq describe('TextYankPost', function() @@ -8,11 +8,11 @@ describe('TextYankPost', function() clear() -- emulate the clipboard so system clipboard isn't affected - execute('let &rtp = "test/functional/fixtures,".&rtp') + command('let &rtp = "test/functional/fixtures,".&rtp') - execute('let g:count = 0') - execute('autocmd TextYankPost * let g:event = copy(v:event)') - execute('autocmd TextYankPost * let g:count += 1') + command('let g:count = 0') + command('autocmd TextYankPost * let g:event = copy(v:event)') + command('autocmd TextYankPost * let g:count += 1') curbufmeths.set_lines(0, -1, true, { 'foo\0bar', @@ -61,27 +61,27 @@ describe('TextYankPost', function() regtype = 'V' }, eval('g:event')) - execute('set debug=msg') + command('set debug=msg') -- the regcontents should not be changed without copy. local status, err = pcall(command,'call extend(g:event.regcontents, ["more text"])') eq(status,false) neq(nil, string.find(err, ':E742:')) -- can't mutate keys inside the autocommand - execute('autocmd! TextYankPost * let v:event.regcontents = 0') + command('autocmd! TextYankPost * let v:event.regcontents = 0') status, err = pcall(command,'normal yy') eq(status,false) neq(nil, string.find(err, ':E46:')) -- can't add keys inside the autocommand - execute('autocmd! TextYankPost * let v:event.mykey = 0') + command('autocmd! TextYankPost * let v:event.mykey = 0') status, err = pcall(command,'normal yy') eq(status,false) neq(nil, string.find(err, ':E742:')) end) it('is not invoked recursively', function() - execute('autocmd TextYankPost * normal "+yy') + command('autocmd TextYankPost * normal "+yy') feed('yy') eq({ operator = 'y', @@ -134,7 +134,7 @@ describe('TextYankPost', function() feed('"_yy') eq(0, eval('g:count')) - execute('delete _') + command('delete _') eq(0, eval('g:count')) end) @@ -155,7 +155,7 @@ describe('TextYankPost', function() regtype = 'V' }, eval('g:event')) - execute("set clipboard=unnamed") + command("set clipboard=unnamed") -- regname still shows the name the user requested feed('yy') @@ -176,7 +176,7 @@ describe('TextYankPost', function() end) it('works with Ex commands', function() - execute('1delete +') + command('1delete +') eq({ operator = 'd', regcontents = { 'foo\nbar' }, @@ -185,7 +185,7 @@ describe('TextYankPost', function() }, eval('g:event')) eq(1, eval('g:count')) - execute('yank') + command('yank') eq({ operator = 'y', regcontents = { 'baz text' }, @@ -194,7 +194,7 @@ describe('TextYankPost', function() }, eval('g:event')) eq(2, eval('g:count')) - execute('normal yw') + command('normal yw') eq({ operator = 'y', regcontents = { 'baz ' }, @@ -203,7 +203,7 @@ describe('TextYankPost', function() }, eval('g:event')) eq(3, eval('g:count')) - execute('normal! dd') + command('normal! dd') eq({ operator = 'd', regcontents = { 'baz text' }, diff --git a/test/functional/clipboard/clipboard_provider_spec.lua b/test/functional/clipboard/clipboard_provider_spec.lua index d969d4a487..eb2eeee0da 100644 --- a/test/functional/clipboard/clipboard_provider_spec.lua +++ b/test/functional/clipboard/clipboard_provider_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local execute, expect, eq, eval = helpers.execute, helpers.expect, helpers.eq, helpers.eval +local feed_command, expect, eq, eval = helpers.feed_command, helpers.expect, helpers.eq, helpers.eval local function basic_register_test(noblock) insert("some words") @@ -95,7 +95,7 @@ describe('clipboard usage', function() before_each(function() reset() - execute('call getreg("*")') -- force load of provider + feed_command('call getreg("*")') -- force load of provider end) it('has independent "* and unnamed registers per default', function() @@ -140,8 +140,8 @@ describe('clipboard usage', function() end) it('support autodectection of regtype', function() - execute("let g:test_clip['*'] = ['linewise stuff','']") - execute("let g:test_clip['+'] = ['charwise','stuff']") + feed_command("let g:test_clip['*'] = ['linewise stuff','']") + feed_command("let g:test_clip['+'] = ['charwise','stuff']") eq("V", eval("getregtype('*')")) eq("v", eval("getregtype('+')")) insert("just some text") @@ -156,7 +156,7 @@ describe('clipboard usage', function() insert([[ much text]]) - execute("let g:test_clip['*'] = [['very','block'],'b']") + feed_command("let g:test_clip['*'] = [['very','block'],'b']") feed('gg"*P') expect([[ very much @@ -170,15 +170,15 @@ describe('clipboard usage', function() end) it('supports setreg', function() - execute('call setreg("*", "setted\\ntext", "c")') - execute('call setreg("+", "explicitly\\nlines", "l")') + feed_command('call setreg("*", "setted\\ntext", "c")') + feed_command('call setreg("+", "explicitly\\nlines", "l")') feed('"+P"*p') expect([[ esetted textxplicitly lines ]]) - execute('call setreg("+", "blocky\\nindeed", "b")') + feed_command('call setreg("+", "blocky\\nindeed", "b")') feed('"+p') expect([[ esblockyetted @@ -188,13 +188,13 @@ describe('clipboard usage', function() end) it('supports let @+ (issue #1427)', function() - execute("let @+ = 'some'") - execute("let @* = ' other stuff'") + feed_command("let @+ = 'some'") + feed_command("let @* = ' other stuff'") eq({{'some'}, 'v'}, eval("g:test_clip['+']")) eq({{' other stuff'}, 'v'}, eval("g:test_clip['*']")) feed('"+p"*p') expect('some other stuff') - execute("let @+ .= ' more'") + feed_command("let @+ .= ' more'") feed('dd"+p') expect('some more') end) @@ -202,7 +202,7 @@ describe('clipboard usage', function() it('pastes unnamed register if the provider fails', function() insert('the text') feed('yy') - execute("let g:cliperror = 1") + feed_command("let g:cliperror = 1") feed('"*p') expect([[ the text @@ -214,7 +214,7 @@ describe('clipboard usage', function() -- the basic behavior of unnamed register should be the same -- even when handled by clipboard provider before_each(function() - execute('set clipboard=unnamed') + feed_command('set clipboard=unnamed') end) it('works', function() @@ -222,7 +222,7 @@ describe('clipboard usage', function() end) it('works with pure text clipboard', function() - execute("let g:cliplossy = 1") + feed_command("let g:cliplossy = 1") -- expect failure for block mode basic_register_test(true) end) @@ -237,7 +237,7 @@ describe('clipboard usage', function() -- "+ shouldn't have changed eq({''}, eval("g:test_clip['+']")) - execute("let g:test_clip['*'] = ['linewise stuff','']") + feed_command("let g:test_clip['*'] = ['linewise stuff','']") feed('p') expect([[ words @@ -247,7 +247,7 @@ describe('clipboard usage', function() it('does not clobber "0 when pasting', function() insert('a line') feed('yy') - execute("let g:test_clip['*'] = ['b line','']") + feed_command("let g:test_clip['*'] = ['b line','']") feed('"0pp"0p') expect([[ a line @@ -258,20 +258,20 @@ describe('clipboard usage', function() it('supports v:register and getreg() without parameters', function() eq('*', eval('v:register')) - execute("let g:test_clip['*'] = [['some block',''], 'b']") + feed_command("let g:test_clip['*'] = [['some block',''], 'b']") eq('some block', eval('getreg()')) eq('\02210', eval('getregtype()')) end) it('yanks visual selection when pasting', function() insert("indeed visual") - execute("let g:test_clip['*'] = [['clipboard'], 'c']") + feed_command("let g:test_clip['*'] = [['clipboard'], 'c']") feed("viwp") eq({{'visual'}, 'v'}, eval("g:test_clip['*']")) expect("indeed clipboard") -- explicit "* should do the same - execute("let g:test_clip['*'] = [['star'], 'c']") + feed_command("let g:test_clip['*'] = [['star'], 'c']") feed('viw"*p') eq({{'clipboard'}, 'v'}, eval("g:test_clip['*']")) expect("indeed star") @@ -280,7 +280,7 @@ describe('clipboard usage', function() it('unamed operations work even if the provider fails', function() insert('the text') feed('yy') - execute("let g:cliperror = 1") + feed_command("let g:cliperror = 1") feed('p') expect([[ the text @@ -294,11 +294,11 @@ describe('clipboard usage', function() match text ]]) - execute('g/match/d') + feed_command('g/match/d') eq('match\n', eval('getreg("*")')) feed('u') eval('setreg("*", "---")') - execute('g/test/') + feed_command('g/test/') feed('') eq('---', eval('getreg("*")')) end) @@ -307,7 +307,7 @@ describe('clipboard usage', function() describe('with clipboard=unnamedplus', function() before_each(function() - execute('set clipboard=unnamedplus') + feed_command('set clipboard=unnamedplus') end) it('links the "+ and unnamed registers', function() @@ -320,13 +320,13 @@ describe('clipboard usage', function() -- "* shouldn't have changed eq({''}, eval("g:test_clip['*']")) - execute("let g:test_clip['+'] = ['three']") + feed_command("let g:test_clip['+'] = ['three']") feed('p') expect('twothree') end) it('and unnamed, yanks to both', function() - execute('set clipboard=unnamedplus,unnamed') + feed_command('set clipboard=unnamedplus,unnamed') insert([[ really unnamed text]]) @@ -340,8 +340,8 @@ describe('clipboard usage', function() -- unnamedplus takes predecence when pasting eq('+', eval('v:register')) - execute("let g:test_clip['+'] = ['the plus','']") - execute("let g:test_clip['*'] = ['the star','']") + feed_command("let g:test_clip['+'] = ['the plus','']") + feed_command("let g:test_clip['*'] = ['the star','']") feed("p") expect([[ text @@ -356,11 +356,11 @@ describe('clipboard usage', function() match text ]]) - execute('g/match/d') + feed_command('g/match/d') eq('match\n', eval('getreg("+")')) feed('u') eval('setreg("+", "---")') - execute('g/test/') + feed_command('g/test/') feed('') eq('---', eval('getreg("+")')) end) @@ -375,13 +375,13 @@ describe('clipboard usage', function() it('supports :put', function() insert("a line") - execute("let g:test_clip['*'] = ['some text']") - execute("let g:test_clip['+'] = ['more', 'text', '']") - execute(":put *") + feed_command("let g:test_clip['*'] = ['some text']") + feed_command("let g:test_clip['+'] = ['more', 'text', '']") + feed_command(":put *") expect([[ a line some text]]) - execute(":put +") + feed_command(":put +") expect([[ a line some text @@ -392,9 +392,9 @@ describe('clipboard usage', function() it('supports "+ and "* in registers', function() local screen = Screen.new(60, 10) screen:attach() - execute("let g:test_clip['*'] = ['some', 'star data','']") - execute("let g:test_clip['+'] = ['such', 'plus', 'stuff']") - execute("registers") + feed_command("let g:test_clip['*'] = ['some', 'star data','']") + feed_command("let g:test_clip['+'] = ['such', 'plus', 'stuff']") + feed_command("registers") screen:expect([[ ~ | ~ | @@ -418,17 +418,17 @@ describe('clipboard usage', function() insert('s/s/t/') feed('gg"*y$:*') expect('t/s/t/') - execute("let g:test_clip['*'] = ['s/s/u']") + feed_command("let g:test_clip['*'] = ['s/s/u']") feed(':*') expect('t/u/t/') end) it('supports :redir @*>', function() - execute("let g:test_clip['*'] = ['stuff']") - execute('redir @*>') + feed_command("let g:test_clip['*'] = ['stuff']") + feed_command('redir @*>') -- it is made empty eq({{''}, 'v'}, eval("g:test_clip['*']")) - execute('let g:test = doesnotexist') + feed_command('let g:test = doesnotexist') feed('') eq({{ '', @@ -436,7 +436,7 @@ describe('clipboard usage', function() 'E121: Undefined variable: doesnotexist', 'E15: Invalid expression: doesnotexist', }, 'v'}, eval("g:test_clip['*']")) - execute(':echo "Howdy!"') + feed_command(':echo "Howdy!"') eq({{ '', '', @@ -448,7 +448,7 @@ describe('clipboard usage', function() end) it('handles middleclick correctly', function() - execute('set mouse=a') + feed_command('set mouse=a') local screen = Screen.new(30, 5) screen:attach() @@ -471,7 +471,7 @@ describe('clipboard usage', function() the a target]]) -- on error, fall back to unnamed register - execute("let g:cliperror = 1") + feed_command("let g:cliperror = 1") feed('<6,1>') expect([[ the source diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 9ee91f2fe9..b98169b067 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -1,7 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) -local clear, eq, eval, exc_exec, execute, feed, insert, neq, next_msg, nvim, +local clear, eq, eval, exc_exec, feed_command, feed, insert, neq, next_msg, nvim, nvim_dir, ok, source, write_file, mkdir, rmdir = helpers.clear, - helpers.eq, helpers.eval, helpers.exc_exec, helpers.execute, helpers.feed, + helpers.eq, helpers.eval, helpers.exc_exec, helpers.feed_command, helpers.feed, helpers.insert, helpers.neq, helpers.next_message, helpers.nvim, helpers.nvim_dir, helpers.ok, helpers.source, helpers.write_file, helpers.mkdir, helpers.rmdir @@ -94,7 +94,7 @@ describe('jobs', function() it('returns 0 when it fails to start', function() eq("", eval("v:errmsg")) - execute("let g:test_jobid = jobstart([])") + feed_command("let g:test_jobid = jobstart([])") eq(0, eval("g:test_jobid")) eq("E474:", string.match(eval("v:errmsg"), "E%d*:")) end) @@ -470,7 +470,7 @@ describe('jobs', function() end) it('will return -2 when interrupted', function() - execute('call rpcnotify(g:channel, "ready") | '.. + feed_command('call rpcnotify(g:channel, "ready") | '.. 'call rpcnotify(g:channel, "wait", '.. 'jobwait([jobstart("sleep 10; exit 55")]))') eq({'notification', 'ready', {}}, next_msg()) @@ -514,7 +514,7 @@ describe('jobs', function() \ ]) endfunction ]]) - execute('call Run()') + feed_command('call Run()') local r for i = 10, 1, -1 do r = next_msg() @@ -668,7 +668,7 @@ describe("pty process teardown", function() it("does not prevent/delay exit. #4798 #4900", function() if helpers.pending_win32(pending) then return end -- Use a nested nvim (in :term) to test without --headless. - execute(":terminal '"..helpers.nvim_prog + feed_command(":terminal '"..helpers.nvim_prog -- Use :term again in the _nested_ nvim to get a PTY process. -- Use `sleep` to simulate a long-running child of the PTY. .."' +terminal +'!(sleep 300 &)' +qa") diff --git a/test/functional/eval/api_functions_spec.lua b/test/functional/eval/api_functions_spec.lua index 21dd228145..7f6f53d226 100644 --- a/test/functional/eval/api_functions_spec.lua +++ b/test/functional/eval/api_functions_spec.lua @@ -1,7 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local lfs = require('lfs') -local neq, eq, execute = helpers.neq, helpers.eq, helpers.execute +local neq, eq, command = helpers.neq, helpers.eq, helpers.command local clear, curbufmeths = helpers.clear, helpers.curbufmeths local exc_exec, expect, eval = helpers.exc_exec, helpers.expect, helpers.eval local insert = helpers.insert @@ -10,17 +10,17 @@ describe('api functions', function() before_each(clear) it("work", function() - execute("call nvim_command('let g:test = 1')") + command("call nvim_command('let g:test = 1')") eq(1, eval("nvim_get_var('test')")) local buf = eval("nvim_get_current_buf()") - execute("call nvim_buf_set_lines("..buf..", 0, -1, v:true, ['aa', 'bb'])") + command("call nvim_buf_set_lines("..buf..", 0, -1, v:true, ['aa', 'bb'])") expect([[ aa bb]]) - execute("call nvim_win_set_cursor(0, [1, 1])") - execute("call nvim_input('ax')") + command("call nvim_win_set_cursor(0, [1, 1])") + command("call nvim_input('ax')") expect([[ aax bb]]) @@ -57,7 +57,7 @@ describe('api functions', function() eq(bnr, bhnd) eq(wid, whnd) - execute("new") -- creates new buffer and new window + command("new") -- creates new buffer and new window local bnr2 = eval("bufnr('')") local bhnd2 = eval("nvim_get_current_buf()") local wid2 = eval("win_getid()") @@ -69,7 +69,7 @@ describe('api functions', function() -- 0 is synonymous to the current buffer eq(bnr2, eval("nvim_buf_get_number(0)")) - execute("bn") -- show old buffer in new window + command("bn") -- show old buffer in new window eq(bnr, eval("nvim_get_current_buf()")) eq(bnr, eval("bufnr('')")) eq(bnr, eval("nvim_buf_get_number(0)")) @@ -81,7 +81,7 @@ describe('api functions', function() curbufmeths.set_lines(0, -1, true, {"aa\0", "b\0b"}) eq({'aa\n', 'b\nb'}, eval("nvim_buf_get_lines(0, 0, -1, 1)")) - execute('call nvim_buf_set_lines(0, 1, 2, v:true, ["xx", "\\nyy"])') + command('call nvim_buf_set_lines(0, 1, 2, v:true, ["xx", "\\nyy"])') eq({'aa\0', 'xx', '\0yy'}, curbufmeths.get_lines(0, -1, 1)) end) @@ -124,9 +124,9 @@ describe('api functions', function() [5] = {bold = true, foreground = Screen.colors.Blue}, }) - execute("set ft=vim") - execute("let &rtp='build/runtime/,'.&rtp") - execute("syntax on") + command("set ft=vim") + command("let &rtp='build/runtime/,'.&rtp") + command("syntax on") insert([[ call bufnr('%') call nvim_input('typing...') diff --git a/test/functional/eval/glob_spec.lua b/test/functional/eval/glob_spec.lua index 599b3dcdc3..b8807ecfcc 100644 --- a/test/functional/eval/glob_spec.lua +++ b/test/functional/eval/glob_spec.lua @@ -1,13 +1,13 @@ local lfs = require('lfs') local helpers = require('test.functional.helpers')(after_each) -local clear, execute, eval, eq = helpers.clear, helpers.execute, helpers.eval, helpers.eq +local clear, command, eval, eq = helpers.clear, helpers.command, helpers.eval, helpers.eq before_each(function() clear() lfs.mkdir('test-glob') -- Long path might cause "Press ENTER" prompt; use :silent to avoid it. - execute('silent cd test-glob') + command('silent cd test-glob') end) after_each(function() diff --git a/test/functional/eval/json_functions_spec.lua b/test/functional/eval/json_functions_spec.lua index fc0a19bdfa..4d34cde849 100644 --- a/test/functional/eval/json_functions_spec.lua +++ b/test/functional/eval/json_functions_spec.lua @@ -4,16 +4,17 @@ local funcs = helpers.funcs local meths = helpers.meths local eq = helpers.eq local eval = helpers.eval -local execute = helpers.execute +local command = helpers.command local exc_exec = helpers.exc_exec local redir_exec = helpers.redir_exec local NIL = helpers.NIL +local source = helpers.source describe('json_decode() function', function() local restart = function(...) clear(...) - execute('language C') - execute([[ + source([[ + language C function Eq(exp, act) let act = a:act let exp = a:exp @@ -45,8 +46,6 @@ describe('json_decode() function', function() endif return 1 endfunction - ]]) - execute([[ function EvalEq(exp, act_expr) let act = eval(a:act_expr) if Eq(a:exp, act) @@ -441,7 +440,7 @@ describe('json_decode() function', function() local sp_decode_eq = function(expected, json) meths.set_var('__json', json) speq(expected, 'json_decode(g:__json)') - execute('unlet! g:__json') + command('unlet! g:__json') end it('parses strings with NUL properly', function() @@ -527,7 +526,7 @@ end) describe('json_encode() function', function() before_each(function() clear() - execute('language C') + command('language C') end) it('dumps strings', function() @@ -576,94 +575,94 @@ describe('json_encode() function', function() it('cannot dump generic mapping with generic mapping keys and values', function() - execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') - execute('let todumpv1 = {"_TYPE": v:msgpack_types.map, "_VAL": []}') - execute('let todumpv2 = {"_TYPE": v:msgpack_types.map, "_VAL": []}') - execute('call add(todump._VAL, [todumpv1, todumpv2])') + command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') + command('let todumpv1 = {"_TYPE": v:msgpack_types.map, "_VAL": []}') + command('let todumpv2 = {"_TYPE": v:msgpack_types.map, "_VAL": []}') + command('call add(todump._VAL, [todumpv1, todumpv2])') eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) end) it('cannot dump generic mapping with ext key', function() - execute('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}') - execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') + command('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}') + command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) end) it('cannot dump generic mapping with array key', function() - execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}') - execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') + command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}') + command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) end) it('cannot dump generic mapping with UINT64_MAX key', function() - execute('let todump = {"_TYPE": v:msgpack_types.integer}') - execute('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]') - execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') + command('let todump = {"_TYPE": v:msgpack_types.integer}') + command('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]') + command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) end) it('cannot dump generic mapping with floating-point key', function() - execute('let todump = {"_TYPE": v:msgpack_types.float, "_VAL": 0.125}') - execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') + command('let todump = {"_TYPE": v:msgpack_types.float, "_VAL": 0.125}') + command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) end) it('can dump generic mapping with STR special key and NUL', function() - execute('let todump = {"_TYPE": v:msgpack_types.string, "_VAL": ["\\n"]}') - execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') + command('let todump = {"_TYPE": v:msgpack_types.string, "_VAL": ["\\n"]}') + command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') eq('{"\\u0000": 1}', eval('json_encode(todump)')) end) it('can dump generic mapping with BIN special key and NUL', function() - execute('let todump = {"_TYPE": v:msgpack_types.binary, "_VAL": ["\\n"]}') - execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') + command('let todump = {"_TYPE": v:msgpack_types.binary, "_VAL": ["\\n"]}') + command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') eq('{"\\u0000": 1}', eval('json_encode(todump)')) end) it('can dump STR special mapping with NUL and NL', function() - execute('let todump = {"_TYPE": v:msgpack_types.string, "_VAL": ["\\n", ""]}') + command('let todump = {"_TYPE": v:msgpack_types.string, "_VAL": ["\\n", ""]}') eq('"\\u0000\\n"', eval('json_encode(todump)')) end) it('can dump BIN special mapping with NUL and NL', function() - execute('let todump = {"_TYPE": v:msgpack_types.binary, "_VAL": ["\\n", ""]}') + command('let todump = {"_TYPE": v:msgpack_types.binary, "_VAL": ["\\n", ""]}') eq('"\\u0000\\n"', eval('json_encode(todump)')) end) it('cannot dump special ext mapping', function() - execute('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}') + command('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}') eq('Vim(call):E474: Unable to convert EXT string to JSON', exc_exec('call json_encode(todump)')) end) it('can dump special array mapping', function() - execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}') + command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}') eq('[5, [""]]', eval('json_encode(todump)')) end) it('can dump special UINT64_MAX mapping', function() - execute('let todump = {"_TYPE": v:msgpack_types.integer}') - execute('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]') + command('let todump = {"_TYPE": v:msgpack_types.integer}') + command('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]') eq('18446744073709551615', eval('json_encode(todump)')) end) it('can dump special INT64_MIN mapping', function() - execute('let todump = {"_TYPE": v:msgpack_types.integer}') - execute('let todump._VAL = [-1, 2, 0, 0]') + command('let todump = {"_TYPE": v:msgpack_types.integer}') + command('let todump._VAL = [-1, 2, 0, 0]') eq('-9223372036854775808', eval('json_encode(todump)')) end) it('can dump special BOOLEAN true mapping', function() - execute('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 1}') + command('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 1}') eq('true', eval('json_encode(todump)')) end) it('can dump special BOOLEAN false mapping', function() - execute('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 0}') + command('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 0}') eq('false', eval('json_encode(todump)')) end) it('can dump special NIL mapping', function() - execute('let todump = {"_TYPE": v:msgpack_types.nil, "_VAL": 0}') + command('let todump = {"_TYPE": v:msgpack_types.nil, "_VAL": 0}') eq('null', eval('json_encode(todump)')) end) @@ -673,7 +672,7 @@ describe('json_encode() function', function() end) it('fails to dump a partial', function() - execute('function T() dict\nendfunction') + command('function T() dict\nendfunction') eq('Vim(call):E474: Error while dumping encode_tv2json() argument, itself: attempt to dump function reference', exc_exec('call json_encode(function("T", [1, 2], {}))')) end) @@ -684,56 +683,56 @@ describe('json_encode() function', function() end) it('fails to dump a recursive list', function() - execute('let todump = [[[]]]') - execute('call add(todump[0][0], todump)') + command('let todump = [[[]]]') + command('call add(todump[0][0], todump)') eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', exc_exec('call json_encode(todump)')) end) it('fails to dump a recursive dict', function() - execute('let todump = {"d": {"d": {}}}') - execute('call extend(todump.d.d, {"d": todump})') + command('let todump = {"d": {"d": {}}}') + command('call extend(todump.d.d, {"d": todump})') eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', exc_exec('call json_encode([todump])')) end) it('can dump dict with two same dicts inside', function() - execute('let inter = {}') - execute('let todump = {"a": inter, "b": inter}') + command('let inter = {}') + command('let todump = {"a": inter, "b": inter}') eq('{"a": {}, "b": {}}', eval('json_encode(todump)')) end) it('can dump list with two same lists inside', function() - execute('let inter = []') - execute('let todump = [inter, inter]') + command('let inter = []') + command('let todump = [inter, inter]') eq('[[], []]', eval('json_encode(todump)')) end) it('fails to dump a recursive list in a special dict', function() - execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') - execute('call add(todump._VAL, todump)') + command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') + command('call add(todump._VAL, todump)') eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', exc_exec('call json_encode(todump)')) end) it('fails to dump a recursive (val) map in a special dict', function() - execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') - execute('call add(todump._VAL, ["", todump])') + command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') + command('call add(todump._VAL, ["", todump])') eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', exc_exec('call json_encode([todump])')) end) it('fails to dump a recursive (val) map in a special dict, _VAL reference', function() - execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [["", []]]}') - execute('call add(todump._VAL[0][1], todump._VAL)') + command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [["", []]]}') + command('call add(todump._VAL[0][1], todump._VAL)') eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', exc_exec('call json_encode(todump)')) end) it('fails to dump a recursive (val) special list in a special dict', function() - execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') - execute('call add(todump._VAL, ["", todump._VAL])') + command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') + command('call add(todump._VAL, ["", todump._VAL])') eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', exc_exec('call json_encode(todump)')) end) diff --git a/test/functional/eval/modeline_spec.lua b/test/functional/eval/modeline_spec.lua index 0be7210a76..c5bb798f4a 100644 --- a/test/functional/eval/modeline_spec.lua +++ b/test/functional/eval/modeline_spec.lua @@ -1,5 +1,5 @@ local helpers = require('test.functional.helpers')(after_each) -local clear, execute, write_file = helpers.clear, helpers.execute, helpers.write_file +local clear, command, write_file = helpers.clear, helpers.command, helpers.write_file local eq, eval = helpers.eq, helpers.eval describe("modeline", function() @@ -12,7 +12,7 @@ describe("modeline", function() it('does not crash with a large version number', function() write_file(tempfile, 'vim100000000000000000000000') - execute('e! ' .. tempfile) + command('e! ' .. tempfile) eq(2, eval('1+1')) -- Still alive? end) diff --git a/test/functional/eval/msgpack_functions_spec.lua b/test/functional/eval/msgpack_functions_spec.lua index 44c01d2226..c5520deb73 100644 --- a/test/functional/eval/msgpack_functions_spec.lua +++ b/test/functional/eval/msgpack_functions_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear = helpers.clear local funcs = helpers.funcs local eval, eq = helpers.eval, helpers.eq -local execute = helpers.execute +local command = helpers.command local nvim = helpers.nvim local exc_exec = helpers.exc_exec @@ -331,9 +331,9 @@ describe('msgpack*() functions', function() obj_test('are able to dump and restore floating-point value', {0.125}) it('can restore and dump UINT64_MAX', function() - execute('let dumped = ["\\xCF" . repeat("\\xFF", 8)]') - execute('let parsed = msgpackparse(dumped)') - execute('let dumped2 = msgpackdump(parsed)') + command('let dumped = ["\\xCF" . repeat("\\xFF", 8)]') + command('let parsed = msgpackparse(dumped)') + command('let dumped2 = msgpackdump(parsed)') eq(1, eval('type(parsed[0]) == type(0) ' .. '|| parsed[0]._TYPE is v:msgpack_types.integer')) if eval('type(parsed[0]) == type(0)') == 1 then @@ -345,9 +345,9 @@ describe('msgpack*() functions', function() end) it('can restore and dump INT64_MIN', function() - execute('let dumped = ["\\xD3\\x80" . repeat("\\n", 7)]') - execute('let parsed = msgpackparse(dumped)') - execute('let dumped2 = msgpackdump(parsed)') + command('let dumped = ["\\xD3\\x80" . repeat("\\n", 7)]') + command('let parsed = msgpackparse(dumped)') + command('let dumped2 = msgpackdump(parsed)') eq(1, eval('type(parsed[0]) == type(0) ' .. '|| parsed[0]._TYPE is v:msgpack_types.integer')) if eval('type(parsed[0]) == type(0)') == 1 then @@ -359,33 +359,33 @@ describe('msgpack*() functions', function() end) it('can restore and dump BIN string with zero byte', function() - execute('let dumped = ["\\xC4\\x01\\n"]') - execute('let parsed = msgpackparse(dumped)') - execute('let dumped2 = msgpackdump(parsed)') + command('let dumped = ["\\xC4\\x01\\n"]') + command('let parsed = msgpackparse(dumped)') + command('let dumped2 = msgpackdump(parsed)') eq({{_TYPE={}, _VAL={'\n'}}}, eval('parsed')) eq(1, eval('parsed[0]._TYPE is v:msgpack_types.binary')) eq(1, eval('dumped ==# dumped2')) end) it('can restore and dump STR string with zero byte', function() - execute('let dumped = ["\\xA1\\n"]') - execute('let parsed = msgpackparse(dumped)') - execute('let dumped2 = msgpackdump(parsed)') + command('let dumped = ["\\xA1\\n"]') + command('let parsed = msgpackparse(dumped)') + command('let dumped2 = msgpackdump(parsed)') eq({{_TYPE={}, _VAL={'\n'}}}, eval('parsed')) eq(1, eval('parsed[0]._TYPE is v:msgpack_types.string')) eq(1, eval('dumped ==# dumped2')) end) it('can restore and dump BIN string with NL', function() - execute('let dumped = ["\\xC4\\x01", ""]') - execute('let parsed = msgpackparse(dumped)') - execute('let dumped2 = msgpackdump(parsed)') + command('let dumped = ["\\xC4\\x01", ""]') + command('let parsed = msgpackparse(dumped)') + command('let dumped2 = msgpackdump(parsed)') eq({"\n"}, eval('parsed')) eq(1, eval('dumped ==# dumped2')) end) it('dump and restore special mapping with floating-point value', function() - execute('let todump = {"_TYPE": v:msgpack_types.float, "_VAL": 0.125}') + command('let todump = {"_TYPE": v:msgpack_types.float, "_VAL": 0.125}') eq({0.125}, eval('msgpackparse(msgpackdump([todump]))')) end) end) @@ -394,52 +394,53 @@ describe('msgpackparse() function', function() before_each(clear) it('restores nil as v:null', function() - execute('let dumped = ["\\xC0"]') - execute('let parsed = msgpackparse(dumped)') + command('let dumped = ["\\xC0"]') + command('let parsed = msgpackparse(dumped)') eq('[v:null]', eval('string(parsed)')) end) it('restores boolean false as v:false', function() - execute('let dumped = ["\\xC2"]') - execute('let parsed = msgpackparse(dumped)') + command('let dumped = ["\\xC2"]') + command('let parsed = msgpackparse(dumped)') eq({false}, eval('parsed')) end) it('restores boolean true as v:true', function() - execute('let dumped = ["\\xC3"]') - execute('let parsed = msgpackparse(dumped)') + command('let dumped = ["\\xC3"]') + command('let parsed = msgpackparse(dumped)') eq({true}, eval('parsed')) end) it('restores FIXSTR as special dict', function() - execute('let dumped = ["\\xa2ab"]') - execute('let parsed = msgpackparse(dumped)') + command('let dumped = ["\\xa2ab"]') + command('let parsed = msgpackparse(dumped)') eq({{_TYPE={}, _VAL={'ab'}}}, eval('parsed')) eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.string')) end) it('restores BIN 8 as string', function() - execute('let dumped = ["\\xC4\\x02ab"]') + command('let dumped = ["\\xC4\\x02ab"]') eq({'ab'}, eval('msgpackparse(dumped)')) end) it('restores FIXEXT1 as special dictionary', function() - execute('let dumped = ["\\xD4\\x10", ""]') - execute('let parsed = msgpackparse(dumped)') + command('let dumped = ["\\xD4\\x10", ""]') + command('let parsed = msgpackparse(dumped)') eq({{_TYPE={}, _VAL={0x10, {"", ""}}}}, eval('parsed')) eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.ext')) end) it('restores MAP with BIN key as special dictionary', function() - execute('let dumped = ["\\x81\\xC4\\x01a\\xC4\\n"]') - execute('let parsed = msgpackparse(dumped)') + command('let dumped = ["\\x81\\xC4\\x01a\\xC4\\n"]') + command('let parsed = msgpackparse(dumped)') eq({{_TYPE={}, _VAL={{'a', ''}}}}, eval('parsed')) eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.map')) end) it('restores MAP with duplicate STR keys as special dictionary', function() - execute('let dumped = ["\\x82\\xA1a\\xC4\\n\\xA1a\\xC4\\n"]') - execute('let parsed = msgpackparse(dumped)') + command('let dumped = ["\\x82\\xA1a\\xC4\\n\\xA1a\\xC4\\n"]') + -- FIXME Internal error bug + command('silent! let parsed = msgpackparse(dumped)') eq({{_TYPE={}, _VAL={ {{_TYPE={}, _VAL={'a'}}, ''}, {{_TYPE={}, _VAL={'a'}}, ''}}} }, eval('parsed')) eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.map')) @@ -448,8 +449,8 @@ describe('msgpackparse() function', function() end) it('restores MAP with MAP key as special dictionary', function() - execute('let dumped = ["\\x81\\x80\\xC4\\n"]') - execute('let parsed = msgpackparse(dumped)') + command('let dumped = ["\\x81\\x80\\xC4\\n"]') + command('let parsed = msgpackparse(dumped)') eq({{_TYPE={}, _VAL={{{}, ''}}}}, eval('parsed')) eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.map')) end) @@ -494,7 +495,7 @@ describe('msgpackparse() function', function() end) it('fails to parse a partial', function() - execute('function T() dict\nendfunction') + command('function T() dict\nendfunction') eq('Vim(call):E686: Argument of msgpackparse() must be a List', exc_exec('call msgpackparse(function("T", [1, 2], {}))')) end) @@ -514,10 +515,10 @@ describe('msgpackdump() function', function() end) it('can dump generic mapping with generic mapping keys and values', function() - execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') - execute('let todumpv1 = {"_TYPE": v:msgpack_types.map, "_VAL": []}') - execute('let todumpv2 = {"_TYPE": v:msgpack_types.map, "_VAL": []}') - execute('call add(todump._VAL, [todumpv1, todumpv2])') + command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') + command('let todumpv1 = {"_TYPE": v:msgpack_types.map, "_VAL": []}') + command('let todumpv2 = {"_TYPE": v:msgpack_types.map, "_VAL": []}') + command('call add(todump._VAL, [todumpv1, todumpv2])') eq({'\129\128\128'}, eval('msgpackdump([todump])')) end) @@ -530,130 +531,130 @@ describe('msgpackdump() function', function() end) it('can v:null', function() - execute('let todump = v:null') + command('let todump = v:null') end) it('can dump special bool mapping (true)', function() - execute('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 1}') + command('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 1}') eq({'\195'}, eval('msgpackdump([todump])')) end) it('can dump special bool mapping (false)', function() - execute('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 0}') + command('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 0}') eq({'\194'}, eval('msgpackdump([todump])')) end) it('can dump special nil mapping', function() - execute('let todump = {"_TYPE": v:msgpack_types.nil, "_VAL": 0}') + command('let todump = {"_TYPE": v:msgpack_types.nil, "_VAL": 0}') eq({'\192'}, eval('msgpackdump([todump])')) end) it('can dump special ext mapping', function() - execute('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}') + command('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}') eq({'\212\005', ''}, eval('msgpackdump([todump])')) end) it('can dump special array mapping', function() - execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}') + command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}') eq({'\146\005\145\196\n'}, eval('msgpackdump([todump])')) end) it('can dump special UINT64_MAX mapping', function() - execute('let todump = {"_TYPE": v:msgpack_types.integer}') - execute('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]') + command('let todump = {"_TYPE": v:msgpack_types.integer}') + command('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]') eq({'\207\255\255\255\255\255\255\255\255'}, eval('msgpackdump([todump])')) end) it('can dump special INT64_MIN mapping', function() - execute('let todump = {"_TYPE": v:msgpack_types.integer}') - execute('let todump._VAL = [-1, 2, 0, 0]') + command('let todump = {"_TYPE": v:msgpack_types.integer}') + command('let todump._VAL = [-1, 2, 0, 0]') eq({'\211\128\n\n\n\n\n\n\n'}, eval('msgpackdump([todump])')) end) it('fails to dump a function reference', function() - execute('let Todump = function("tr")') + command('let Todump = function("tr")') eq('Vim(call):E5004: Error while dumping msgpackdump() argument, index 0, itself: attempt to dump function reference', exc_exec('call msgpackdump([Todump])')) end) it('fails to dump a partial', function() - execute('function T() dict\nendfunction') - execute('let Todump = function("T", [1, 2], {})') + command('function T() dict\nendfunction') + command('let Todump = function("T", [1, 2], {})') eq('Vim(call):E5004: Error while dumping msgpackdump() argument, index 0, itself: attempt to dump function reference', exc_exec('call msgpackdump([Todump])')) end) it('fails to dump a function reference in a list', function() - execute('let todump = [function("tr")]') + command('let todump = [function("tr")]') eq('Vim(call):E5004: Error while dumping msgpackdump() argument, index 0, index 0: attempt to dump function reference', exc_exec('call msgpackdump([todump])')) end) it('fails to dump a recursive list', function() - execute('let todump = [[[]]]') - execute('call add(todump[0][0], todump)') + command('let todump = [[[]]]') + command('call add(todump[0][0], todump)') eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in index 0, index 0, index 0', exc_exec('call msgpackdump([todump])')) end) it('fails to dump a recursive dict', function() - execute('let todump = {"d": {"d": {}}}') - execute('call extend(todump.d.d, {"d": todump})') + command('let todump = {"d": {"d": {}}}') + command('call extend(todump.d.d, {"d": todump})') eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in key \'d\', key \'d\', key \'d\'', exc_exec('call msgpackdump([todump])')) end) it('can dump dict with two same dicts inside', function() - execute('let inter = {}') - execute('let todump = {"a": inter, "b": inter}') + command('let inter = {}') + command('let todump = {"a": inter, "b": inter}') eq({"\130\161a\128\161b\128"}, eval('msgpackdump([todump])')) end) it('can dump list with two same lists inside', function() - execute('let inter = []') - execute('let todump = [inter, inter]') + command('let inter = []') + command('let todump = [inter, inter]') eq({"\146\144\144"}, eval('msgpackdump([todump])')) end) it('fails to dump a recursive list in a special dict', function() - execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') - execute('call add(todump._VAL, todump)') + command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') + command('call add(todump._VAL, todump)') eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in index 0', exc_exec('call msgpackdump([todump])')) end) it('fails to dump a recursive (key) map in a special dict', function() - execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') - execute('call add(todump._VAL, [todump, 0])') + command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') + command('call add(todump._VAL, [todump, 0])') eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in index 1', exc_exec('call msgpackdump([todump])')) end) it('fails to dump a recursive (val) map in a special dict', function() - execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') - execute('call add(todump._VAL, [0, todump])') + command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') + command('call add(todump._VAL, [0, todump])') eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in key 0 at index 0 from special map', exc_exec('call msgpackdump([todump])')) end) it('fails to dump a recursive (key) map in a special dict, _VAL reference', function() - execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[[], []]]}') - execute('call add(todump._VAL[0][0], todump._VAL)') + command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[[], []]]}') + command('call add(todump._VAL[0][0], todump._VAL)') eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in key [[[[...@0], []]]] at index 0 from special map, index 0', exc_exec('call msgpackdump([todump])')) end) it('fails to dump a recursive (val) map in a special dict, _VAL reference', function() - execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[[], []]]}') - execute('call add(todump._VAL[0][1], todump._VAL)') + command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[[], []]]}') + command('call add(todump._VAL[0][1], todump._VAL)') eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in key [] at index 0 from special map, index 0', exc_exec('call msgpackdump([todump])')) end) it('fails to dump a recursive (val) special list in a special dict', function() - execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') - execute('call add(todump._VAL, [0, todump._VAL])') + command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') + command('call add(todump._VAL, [0, todump._VAL])') eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in index 0, index 1', exc_exec('call msgpackdump([todump])')) end) @@ -689,7 +690,7 @@ describe('msgpackdump() function', function() end) it('fails to dump a partial', function() - execute('function T() dict\nendfunction') + command('function T() dict\nendfunction') eq('Vim(call):E686: Argument of msgpackdump() must be a List', exc_exec('call msgpackdump(function("T", [1, 2], {}))')) end) diff --git a/test/functional/eval/reltime_spec.lua b/test/functional/eval/reltime_spec.lua index 0b19d372ec..0181f09024 100644 --- a/test/functional/eval/reltime_spec.lua +++ b/test/functional/eval/reltime_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local clear, eq, ok = helpers.clear, helpers.eq, helpers.ok -local neq, execute, funcs = helpers.neq, helpers.execute, helpers.funcs +local neq, command, funcs = helpers.neq, helpers.command, helpers.funcs local reltime, reltimestr, reltimefloat = funcs.reltime, funcs.reltimestr, funcs.reltimefloat describe('reltimestr(), reltimefloat()', function() @@ -8,7 +8,7 @@ describe('reltimestr(), reltimefloat()', function() it('Checks', function() local now = reltime() - execute('sleep 10m') + command('sleep 10m') local later = reltime() local elapsed = reltime(now) diff --git a/test/functional/eval/setpos_spec.lua b/test/functional/eval/setpos_spec.lua index 2e27cd8ac0..6a8b3a8732 100644 --- a/test/functional/eval/setpos_spec.lua +++ b/test/functional/eval/setpos_spec.lua @@ -3,7 +3,7 @@ local setpos = helpers.funcs.setpos local getpos = helpers.funcs.getpos local insert = helpers.insert local clear = helpers.clear -local execute = helpers.execute +local command = helpers.command local eval = helpers.eval local eq = helpers.eq local exc_exec = helpers.exc_exec @@ -16,7 +16,7 @@ describe('setpos() function', function() First line of text Second line of text Third line of text]]) - execute('new') + command('new') insert([[ Line of text 1 Line of text 2 @@ -34,7 +34,8 @@ describe('setpos() function', function() it('can set lowercase marks in the current buffer', function() setpos("'d", {0, 2, 1, 0}) eq(getpos("'d"), {0, 2, 1, 0}) - execute('undo', 'call setpos("\'d", [2, 3, 1, 0])') + command('undo') + command('call setpos("\'d", [2, 3, 1, 0])') eq(getpos("'d"), {0, 3, 1, 0}) end) it('can set lowercase marks in other buffers', function() @@ -42,7 +43,7 @@ describe('setpos() function', function() eq(0, retval) setpos("'d", {1, 2, 1, 0}) eq(getpos("'d"), {0, 0, 0, 0}) - execute('wincmd w') + command('wincmd w') eq(eval('bufnr("%")'), 1) eq(getpos("'d"), {0, 2, 1, 0}) end) diff --git a/test/functional/eval/special_vars_spec.lua b/test/functional/eval/special_vars_spec.lua index 4c5d63ce23..3d9358447e 100644 --- a/test/functional/eval/special_vars_spec.lua +++ b/test/functional/eval/special_vars_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local exc_exec = helpers.exc_exec -local execute = helpers.execute +local command = helpers.command local funcs = helpers.funcs local clear = helpers.clear local eval = helpers.eval @@ -12,7 +12,7 @@ describe('Special values', function() before_each(clear) it('do not cause error when freed', function() - execute([[ + command([[ function Test() try return v:true @@ -109,7 +109,7 @@ describe('Special values', function() it('does not work with +=/-=/.=', function() meths.set_var('true', true) meths.set_var('false', false) - execute('let null = v:null') + command('let null = v:null') eq('Vim(let):E734: Wrong variable type for +=', exc_exec('let true += 1')) eq('Vim(let):E734: Wrong variable type for +=', exc_exec('let false += 1')) diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua index ee75b593ff..0b7d3dce21 100644 --- a/test/functional/eval/system_spec.lua +++ b/test/functional/eval/system_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) -local eq, call, clear, eval, execute, feed, nvim = - helpers.eq, helpers.call, helpers.clear, helpers.eval, helpers.execute, +local eq, call, clear, eval, feed_command, feed, nvim = + helpers.eq, helpers.call, helpers.clear, helpers.eval, helpers.feed_command, helpers.feed, helpers.nvim local Screen = require('test.functional.ui.screen') @@ -43,10 +43,10 @@ describe('system()', function() it('parameter validation does NOT modify v:shell_error', function() -- 1. Call system() with invalid parameters. -- 2. Assert that v:shell_error was NOT set. - execute('call system({})') + feed_command('call system({})') eq('E475: Invalid argument: expected String or List', eval('v:errmsg')) eq(0, eval('v:shell_error')) - execute('call system([])') + feed_command('call system([])') eq('E474: Invalid argument', eval('v:errmsg')) eq(0, eval('v:shell_error')) @@ -57,9 +57,9 @@ describe('system()', function() -- 1. Call system() with invalid parameters. -- 2. Assert that v:shell_error was NOT modified. - execute('call system({})') + feed_command('call system({})') eq(old_val, eval('v:shell_error')) - execute('call system([])') + feed_command('call system([])') eq(old_val, eval('v:shell_error')) end) @@ -182,7 +182,7 @@ describe('system()', function() end) it('to backgrounded command does not crash', function() -- This is indeterminate, just exercise the codepath. May get E5677. - execute('call system("echo -n echoed &")') + feed_command('call system("echo -n echoed &")') local v_errnum = string.match(eval("v:errmsg"), "^E%d*:") if v_errnum then eq("E5677:", v_errnum) @@ -197,7 +197,7 @@ describe('system()', function() end) it('to backgrounded command does not crash', function() -- This is indeterminate, just exercise the codepath. May get E5677. - execute('call system("cat - &")') + feed_command('call system("cat - &")') local v_errnum = string.match(eval("v:errmsg"), "^E%d*:") if v_errnum then eq("E5677:", v_errnum) diff --git a/test/functional/eval/timer_spec.lua b/test/functional/eval/timer_spec.lua index b3c4cd07eb..2dd9968a01 100644 --- a/test/functional/eval/timer_spec.lua +++ b/test/functional/eval/timer_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local ok, feed, eq, eval = helpers.ok, helpers.feed, helpers.eq, helpers.eval local source, nvim_async, run = helpers.source, helpers.nvim_async, helpers.run -local clear, execute, funcs = helpers.clear, helpers.execute, helpers.funcs +local clear, command, funcs = helpers.clear, helpers.command, helpers.funcs local curbufmeths = helpers.curbufmeths describe('timers', function() @@ -17,14 +17,14 @@ describe('timers', function() end) it('works one-shot', function() - execute("call timer_start(50, 'MyHandler')") + command("call timer_start(50, 'MyHandler')") eq(0,eval("g:val")) run(nil, nil, nil, 200) eq(1,eval("g:val")) end) it('works one-shot when repeat=0', function() - execute("call timer_start(50, 'MyHandler', {'repeat': 0})") + command("call timer_start(50, 'MyHandler', {'repeat': 0})") eq(0,eval("g:val")) run(nil, nil, nil, 200) eq(1,eval("g:val")) @@ -32,14 +32,14 @@ describe('timers', function() it('works with repeat two', function() - execute("call timer_start(50, 'MyHandler', {'repeat': 2})") + command("call timer_start(50, 'MyHandler', {'repeat': 2})") eq(0,eval("g:val")) run(nil, nil, nil, 300) eq(2,eval("g:val")) end) it('are triggered during sleep', function() - execute("call timer_start(50, 'MyHandler', {'repeat': 2})") + command("call timer_start(50, 'MyHandler', {'repeat': 2})") nvim_async("command", "sleep 10") eq(0,eval("g:val")) run(nil, nil, nil, 300) @@ -63,12 +63,12 @@ describe('timers', function() end) it('are paused when event processing is disabled', function() - execute("call timer_start(50, 'MyHandler', {'repeat': -1})") + command("call timer_start(50, 'MyHandler', {'repeat': -1})") run(nil, nil, nil, 100) local count = eval("g:val") -- shows two line error message and thus invokes the return prompt. -- if we start to allow event processing here, we need to change this test. - execute("throw 'fatal error'") + feed(':throw "fatal error"') run(nil, nil, nil, 300) feed("") local diff = eval("g:val") - count @@ -76,7 +76,7 @@ describe('timers', function() end) it('are triggered in blocking getchar() call', function() - execute("call timer_start(50, 'MyHandler', {'repeat': -1})") + command("call timer_start(50, 'MyHandler', {'repeat': -1})") nvim_async("command", "let g:c = getchar()") run(nil, nil, nil, 300) feed("c") @@ -157,7 +157,7 @@ describe('timers', function() endif endfunc ]]) - execute("call timer_start(50, 'MyHandler', {'repeat': -1})") + command("call timer_start(50, 'MyHandler', {'repeat': -1})") eq(0,eval("g:val")) run(nil, nil, nil, 300) eq(3,eval("g:val")) @@ -170,8 +170,8 @@ describe('timers', function() let g:val2 += 1 endfunc ]]) - execute("call timer_start(50, 'MyHandler', {'repeat': 3})") - execute("call timer_start(100, 'MyHandler2', {'repeat': 2})") + command("call timer_start(50, 'MyHandler', {'repeat': 3})") + command("call timer_start(100, 'MyHandler2', {'repeat': 2})") run(nil, nil, nil, 300) eq(3,eval("g:val")) eq(2,eval("g:val2")) @@ -186,7 +186,7 @@ describe('timers', function() let g:val += 1 endfunc ]]) - execute("call timer_start(5, 'MyHandler', {'repeat': 1})") + command("call timer_start(5, 'MyHandler', {'repeat': 1})") run(nil, nil, nil, 300) eq(1,eval("g:val")) end) @@ -201,7 +201,7 @@ describe('timers', function() echo "evil" endfunc ]]) - execute("call timer_start(100, 'MyHandler', {'repeat': 1})") + command("call timer_start(100, 'MyHandler', {'repeat': 1})") feed(":good") screen:sleep(200) screen:expect([[ diff --git a/test/functional/ex_cmds/arg_spec.lua b/test/functional/ex_cmds/arg_spec.lua index e11b90532f..6d31f05c2a 100644 --- a/test/functional/ex_cmds/arg_spec.lua +++ b/test/functional/ex_cmds/arg_spec.lua @@ -1,5 +1,5 @@ local helpers = require("test.functional.helpers")(after_each) -local eq, execute, funcs = helpers.eq, helpers.execute, helpers.funcs +local eq, command, funcs = helpers.eq, helpers.command, helpers.funcs local ok = helpers.ok local clear = helpers.clear @@ -9,15 +9,15 @@ describe(":argument", function() end) it("does not restart :terminal buffer", function() - execute("terminal") + command("terminal") helpers.feed([[]]) - execute("argadd") + command("argadd") helpers.feed([[]]) local bufname_before = funcs.bufname("%") local bufnr_before = funcs.bufnr("%") helpers.ok(nil ~= string.find(bufname_before, "^term://")) -- sanity - execute("argument 1") + command("argument 1") helpers.feed([[]]) local bufname_after = funcs.bufname("%") diff --git a/test/functional/ex_cmds/bang_filter_spec.lua b/test/functional/ex_cmds/bang_filter_spec.lua index a320e6d018..aaec983b73 100644 --- a/test/functional/ex_cmds/bang_filter_spec.lua +++ b/test/functional/ex_cmds/bang_filter_spec.lua @@ -1,7 +1,7 @@ -- Specs for bang/filter commands local helpers = require('test.functional.helpers')(after_each) -local feed, execute, clear = helpers.feed, helpers.execute, helpers.clear +local feed, command, clear = helpers.feed, helpers.command, helpers.clear local mkdir, write_file, rmdir = helpers.mkdir, helpers.write_file, helpers.rmdir if helpers.pending_win32(pending) then return end @@ -28,7 +28,7 @@ describe('issues', function() end) it('#3269 Last line of shell output is not truncated', function() - execute([[nnoremap \l :!ls bang_filter_spec]]) + command([[nnoremap \l :!ls bang_filter_spec]]) feed([[\l]]) screen:expect([[ ~ | diff --git a/test/functional/ex_cmds/cd_spec.lua b/test/functional/ex_cmds/cd_spec.lua index 5bf4d22d0f..059cb26d5d 100644 --- a/test/functional/ex_cmds/cd_spec.lua +++ b/test/functional/ex_cmds/cd_spec.lua @@ -6,7 +6,7 @@ local helpers = require('test.functional.helpers')(after_each) local eq = helpers.eq local call = helpers.call local clear = helpers.clear -local execute = helpers.execute +local command = helpers.command local exc_exec = helpers.exc_exec if helpers.pending_win32(pending) then return end @@ -58,7 +58,7 @@ for _, cmd in ipairs {'cd', 'chdir'} do eq(0, lwd(globalwin)) eq(0, lwd(globalwin, tabnr)) - execute('bot split') + command('bot split') local localwin = call('winnr') -- Initial window is still using globalDir eq(globalDir, cwd(localwin)) @@ -66,7 +66,7 @@ for _, cmd in ipairs {'cd', 'chdir'} do eq(0, lwd(globalwin)) eq(0, lwd(globalwin, tabnr)) - execute('silent l' .. cmd .. ' ' .. directories.window) + command('silent l' .. cmd .. ' ' .. directories.window) -- From window with local dir, the original window -- is still reporting the global dir eq(globalDir, cwd(globalwin)) @@ -80,7 +80,7 @@ for _, cmd in ipairs {'cd', 'chdir'} do eq(1, lwd(localwin)) eq(1, lwd(localwin, tabnr)) - execute('tabnew') + command('tabnew') -- From new tab page, original window reports global dir eq(globalDir, cwd(globalwin, tabnr)) eq(0, lwd(globalwin, tabnr)) @@ -100,8 +100,8 @@ for _, cmd in ipairs {'cd', 'chdir'} do eq(0, lwd(-1, 0)) eq(0, lwd(-1, globaltab)) - execute('tabnew') - execute('silent t' .. cmd .. ' ' .. directories.tab) + command('tabnew') + command('silent t' .. cmd .. ' ' .. directories.tab) local localtab = call('tabpagenr') -- From local tab page, original tab reports globalDir @@ -114,7 +114,7 @@ for _, cmd in ipairs {'cd', 'chdir'} do eq(1, lwd(-1, 0)) eq(1, lwd(-1, localtab)) - execute('tabnext') + command('tabnext') -- From original tab page, local reports as such eq(globalDir .. '/' .. directories.tab, cwd(-1, localtab)) eq(1, lwd(-1, localtab)) @@ -128,13 +128,13 @@ for _, cmd in ipairs {'cd', 'chdir'} do end) it('works with tab-local pwd', function() - execute('silent t' .. cmd .. ' ' .. directories.tab) + command('silent t' .. cmd .. ' ' .. directories.tab) eq(directories.start, cwd(-1, -1)) eq(0, lwd(-1, -1)) end) it('works with window-local pwd', function() - execute('silent l' .. cmd .. ' ' .. directories.window) + command('silent l' .. cmd .. ' ' .. directories.window) eq(directories.start, cwd(-1, -1)) eq(0, lwd(-1, -1)) end) @@ -145,18 +145,18 @@ for _, cmd in ipairs {'cd', 'chdir'} do local globalDir = directories.start -- Create a new tab and change directory - execute('tabnew') - execute('silent t' .. cmd .. ' ' .. directories.tab) + command('tabnew') + command('silent t' .. cmd .. ' ' .. directories.tab) eq(globalDir .. '/' .. directories.tab, tcwd()) -- Create a new tab and verify it has inherited the directory - execute('tabnew') + command('tabnew') eq(globalDir .. '/' .. directories.tab, tcwd()) -- Change tab and change back, verify that directories are correct - execute('tabnext') + command('tabnext') eq(globalDir, tcwd()) - execute('tabprevious') + command('tabprevious') eq(globalDir .. '/' .. directories.tab, tcwd()) end) end) @@ -164,7 +164,7 @@ for _, cmd in ipairs {'cd', 'chdir'} do it('works', function() local globalDir = directories.start -- Create a new tab first and verify that is has the same working dir - execute('tabnew') + command('tabnew') eq(globalDir, cwd()) eq(globalDir, tcwd()) -- has no tab-local directory eq(0, tlwd()) @@ -172,7 +172,7 @@ for _, cmd in ipairs {'cd', 'chdir'} do eq(0, wlwd()) -- Change tab-local working directory and verify it is different - execute('silent t' .. cmd .. ' ' .. directories.tab) + command('silent t' .. cmd .. ' ' .. directories.tab) eq(globalDir .. '/' .. directories.tab, cwd()) eq(cwd(), tcwd()) -- working directory maches tab directory eq(1, tlwd()) @@ -180,46 +180,46 @@ for _, cmd in ipairs {'cd', 'chdir'} do eq(0, wlwd()) -- Create a new window in this tab to test `:lcd` - execute('new') + command('new') eq(1, tlwd()) -- Still tab-local working directory eq(0, wlwd()) -- Still no window-local working directory eq(globalDir .. '/' .. directories.tab, cwd()) - execute('silent l' .. cmd .. ' ../' .. directories.window) + command('silent l' .. cmd .. ' ../' .. directories.window) eq(globalDir .. '/' .. directories.window, cwd()) eq(globalDir .. '/' .. directories.tab, tcwd()) eq(1, wlwd()) -- Verify the first window still has the tab local directory - execute('wincmd w') + command('wincmd w') eq(globalDir .. '/' .. directories.tab, cwd()) eq(globalDir .. '/' .. directories.tab, tcwd()) eq(0, wlwd()) -- No window-local directory -- Change back to initial tab and verify working directory has stayed - execute('tabnext') + command('tabnext') eq(globalDir, cwd() ) eq(0, tlwd()) eq(0, wlwd()) -- Verify global changes don't affect local ones - execute('silent ' .. cmd .. ' ' .. directories.global) + command('silent ' .. cmd .. ' ' .. directories.global) eq(globalDir .. '/' .. directories.global, cwd()) - execute('tabnext') + command('tabnext') eq(globalDir .. '/' .. directories.tab, cwd()) eq(globalDir .. '/' .. directories.tab, tcwd()) eq(0, wlwd()) -- Still no window-local directory in this window -- Unless the global change happened in a tab with local directory - execute('silent ' .. cmd .. ' ..') + command('silent ' .. cmd .. ' ..') eq(globalDir, cwd() ) eq(0 , tlwd()) eq(0 , wlwd()) -- Which also affects the first tab - execute('tabnext') + command('tabnext') eq(globalDir, cwd()) -- But not in a window with its own local directory - execute('tabnext | wincmd w') + command('tabnext | wincmd w') eq(globalDir .. '/' .. directories.window, cwd() ) eq(0 , tlwd()) eq(globalDir .. '/' .. directories.window, wcwd()) @@ -280,8 +280,8 @@ describe("getcwd()", function () end) it("returns empty string if working directory does not exist", function() - execute("cd "..directories.global) - execute("call delete('../"..directories.global.."', 'd')") + command("cd "..directories.global) + command("call delete('../"..directories.global.."', 'd')") eq("", helpers.eval("getcwd()")) end) end) diff --git a/test/functional/ex_cmds/ctrl_c_spec.lua b/test/functional/ex_cmds/ctrl_c_spec.lua index 072fd2ad10..33affb26a5 100644 --- a/test/functional/ex_cmds/ctrl_c_spec.lua +++ b/test/functional/ex_cmds/ctrl_c_spec.lua @@ -1,7 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear, feed, source = helpers.clear, helpers.feed, helpers.source -local execute = helpers.execute +local command = helpers.command describe("CTRL-C (mapped)", function() before_each(function() @@ -20,7 +20,7 @@ describe("CTRL-C (mapped)", function() nnoremap ]]) - execute("silent edit! test/functional/fixtures/bigfile.txt") + command("silent edit! test/functional/fixtures/bigfile.txt") local screen = Screen.new(52, 6) screen:attach() screen:set_default_attr_ids({ diff --git a/test/functional/ex_cmds/drop_spec.lua b/test/functional/ex_cmds/drop_spec.lua index 99db5ea333..9105b84367 100644 --- a/test/functional/ex_cmds/drop_spec.lua +++ b/test/functional/ex_cmds/drop_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') -local clear, feed, execute = helpers.clear, helpers.feed, helpers.execute +local clear, feed, feed_command = helpers.clear, helpers.feed, helpers.feed_command describe(":drop", function() local screen @@ -15,7 +15,7 @@ describe(":drop", function() [2] = {reverse = true}, [3] = {bold = true}, }) - execute("set laststatus=2") + feed_command("set laststatus=2") end) after_each(function() @@ -23,7 +23,7 @@ describe(":drop", function() end) it("works like :e when called with only one window open", function() - execute("drop tmp1.vim") + feed_command("drop tmp1.vim") screen:expect([[ ^ | {0:~ }| @@ -39,10 +39,10 @@ describe(":drop", function() end) it("switches to an open window showing the buffer", function() - execute("edit tmp1") - execute("vsplit") - execute("edit tmp2") - execute("drop tmp1") + feed_command("edit tmp1") + feed_command("vsplit") + feed_command("edit tmp2") + feed_command("drop tmp1") screen:expect([[ {2:|}^ | {0:~ }{2:|}{0:~ }| @@ -58,11 +58,11 @@ describe(":drop", function() end) it("splits off a new window when a buffer can't be abandoned", function() - execute("edit tmp1") - execute("vsplit") - execute("edit tmp2") + feed_command("edit tmp1") + feed_command("vsplit") + feed_command("edit tmp2") feed("iABC") - execute("drop tmp3") + feed_command("drop tmp3") screen:expect([[ ^ {2:|} | {0:~ }{2:|}{0:~ }| diff --git a/test/functional/ex_cmds/edit_spec.lua b/test/functional/ex_cmds/edit_spec.lua index 3cc5f5fb95..6ed500a293 100644 --- a/test/functional/ex_cmds/edit_spec.lua +++ b/test/functional/ex_cmds/edit_spec.lua @@ -1,7 +1,8 @@ local helpers = require("test.functional.helpers")(after_each) -local eq, execute, funcs = helpers.eq, helpers.execute, helpers.funcs +local eq, command, funcs = helpers.eq, helpers.command, helpers.funcs local ok = helpers.ok local clear = helpers.clear +local feed = helpers.feed describe(":edit", function() before_each(function() @@ -9,13 +10,13 @@ describe(":edit", function() end) it("without arguments does not restart :terminal buffer", function() - execute("terminal") - helpers.feed([[]]) + command("terminal") + feed([[]]) local bufname_before = funcs.bufname("%") local bufnr_before = funcs.bufnr("%") helpers.ok(nil ~= string.find(bufname_before, "^term://")) -- sanity - execute("edit") + command("edit") local bufname_after = funcs.bufname("%") local bufnr_after = funcs.bufnr("%") diff --git a/test/functional/ex_cmds/encoding_spec.lua b/test/functional/ex_cmds/encoding_spec.lua index 87ed7a2d0a..0769259be4 100644 --- a/test/functional/ex_cmds/encoding_spec.lua +++ b/test/functional/ex_cmds/encoding_spec.lua @@ -1,5 +1,5 @@ local helpers = require('test.functional.helpers')(after_each) -local clear, execute, feed = helpers.clear, helpers.execute, helpers.feed +local clear, feed_command, feed = helpers.clear, helpers.feed_command, helpers.feed local eq, neq, eval = helpers.eq, helpers.neq, helpers.eval describe('&encoding', function() @@ -12,7 +12,7 @@ describe('&encoding', function() end) it('cannot be changed after setup', function() - execute('set encoding=latin1') + feed_command('set encoding=latin1') -- error message expected feed('') neq(nil, string.find(eval('v:errmsg'), '^E474:')) @@ -31,7 +31,7 @@ describe('&encoding', function() end) it('can be set to utf-8 without error', function() - execute('set encoding=utf-8') + feed_command('set encoding=utf-8') eq("", eval('v:errmsg')) clear('--cmd', 'set enc=utf-8') diff --git a/test/functional/ex_cmds/grep_spec.lua b/test/functional/ex_cmds/grep_spec.lua index 13f88b7e03..43ef1bd424 100644 --- a/test/functional/ex_cmds/grep_spec.lua +++ b/test/functional/ex_cmds/grep_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) -local clear, execute, feed, ok, eval = - helpers.clear, helpers.execute, helpers.feed, helpers.ok, helpers.eval +local clear, feed_command, feed, ok, eval = + helpers.clear, helpers.feed_command, helpers.feed, helpers.ok, helpers.eval describe(':grep', function() before_each(clear) @@ -11,10 +11,10 @@ describe(':grep', function() return end - execute([[set grepprg=grep\ -r]]) + feed_command([[set grepprg=grep\ -r]]) -- Change to test directory so that the test does not run too long. - execute('cd test') - execute('grep a **/*') + feed_command('cd test') + feed_command('grep a **/*') feed('') -- Press ENTER ok(eval('len(getqflist())') > 9000) -- IT'S OVER 9000!!1 end) diff --git a/test/functional/ex_cmds/menu_spec.lua b/test/functional/ex_cmds/menu_spec.lua index 52df9e1592..8c249d6918 100644 --- a/test/functional/ex_cmds/menu_spec.lua +++ b/test/functional/ex_cmds/menu_spec.lua @@ -1,5 +1,5 @@ local helpers = require('test.functional.helpers')(after_each) -local clear, execute, nvim = helpers.clear, helpers.execute, helpers.nvim +local clear, command, nvim = helpers.clear, helpers.command, helpers.nvim local expect, feed, command = helpers.expect, helpers.feed, helpers.command local eq, eval = helpers.eq, helpers.eval @@ -7,17 +7,17 @@ describe(':emenu', function() before_each(function() clear() - execute('nnoremenu Test.Test inormal') - execute('inoremenu Test.Test insert') - execute('vnoremenu Test.Test x') - execute('cnoremenu Test.Test cmdmode') + command('nnoremenu Test.Test inormal') + command('inoremenu Test.Test insert') + command('vnoremenu Test.Test x') + command('cnoremenu Test.Test cmdmode') - execute('nnoremenu Edit.Paste p') - execute('cnoremenu Edit.Paste "') + command('nnoremenu Edit.Paste p') + command('cnoremenu Edit.Paste "') end) it('executes correct bindings in normal mode without using API', function() - execute('emenu Test.Test') + command('emenu Test.Test') expect('normal') end) diff --git a/test/functional/ex_cmds/oldfiles_spec.lua b/test/functional/ex_cmds/oldfiles_spec.lua index a161e49fc6..656b3f9bae 100644 --- a/test/functional/ex_cmds/oldfiles_spec.lua +++ b/test/functional/ex_cmds/oldfiles_spec.lua @@ -1,7 +1,7 @@ local Screen = require('test.functional.ui.screen') local helpers = require('test.functional.helpers')(after_each) -local buf, eq, execute = helpers.curbufmeths, helpers.eq, helpers.execute +local buf, eq, feed_command = helpers.curbufmeths, helpers.eq, helpers.feed_command local feed, nvim_prog, wait = helpers.feed, helpers.nvim_prog, helpers.wait local ok, set_session, spawn = helpers.ok, helpers.set_session, helpers.spawn @@ -27,12 +27,12 @@ describe(':oldfiles', function() it('shows most recently used files', function() local screen = Screen.new(100, 5) screen:attach() - execute('edit testfile1') - execute('edit testfile2') - execute('wshada ' .. shada_file) - execute('rshada! ' .. shada_file) + feed_command('edit testfile1') + feed_command('edit testfile2') + feed_command('wshada ' .. shada_file) + feed_command('rshada! ' .. shada_file) local oldfiles = helpers.meths.get_vvar('oldfiles') - execute('oldfiles') + feed_command('oldfiles') screen:expect([[ testfile2 | 1: ]].. add_padding(oldfiles[1]) ..[[ | @@ -50,14 +50,14 @@ describe(':browse oldfiles', function() before_each(function() _clear() - execute('edit testfile1') + feed_command('edit testfile1') filename = buf.get_name() - execute('edit testfile2') + feed_command('edit testfile2') filename2 = buf.get_name() - execute('wshada ' .. shada_file) + feed_command('wshada ' .. shada_file) wait() _clear() - execute('rshada! ' .. shada_file) + feed_command('rshada! ' .. shada_file) -- Ensure nvim is out of "Press ENTER..." prompt. feed('') @@ -70,7 +70,7 @@ describe(':browse oldfiles', function() ok(filename == oldfiles[1] or filename == oldfiles[2]) ok(filename2 == oldfiles[1] or filename2 == oldfiles[2]) - execute('browse oldfiles') + feed_command('browse oldfiles') end) after_each(function() diff --git a/test/functional/ex_cmds/recover_spec.lua b/test/functional/ex_cmds/recover_spec.lua index af1296c94c..36bf85015a 100644 --- a/test/functional/ex_cmds/recover_spec.lua +++ b/test/functional/ex_cmds/recover_spec.lua @@ -2,8 +2,8 @@ local helpers = require('test.functional.helpers')(after_each) local lfs = require('lfs') -local execute, eq, clear, eval, feed, expect, source = - helpers.execute, helpers.eq, helpers.clear, helpers.eval, helpers.feed, +local feed_command, eq, clear, eval, feed, expect, source = + helpers.feed_command, helpers.eq, helpers.clear, helpers.eval, helpers.feed, helpers.expect, helpers.source if helpers.pending_win32(pending) then return end @@ -13,7 +13,7 @@ describe(':recover', function() it('fails if given a non-existent swapfile', function() local swapname = 'bogus-swapfile' - execute('recover '..swapname) -- This should not segfault. #2117 + feed_command('recover '..swapname) -- This should not segfault. #2117 eq('E305: No swap file found for '..swapname, eval('v:errmsg')) end) @@ -40,12 +40,12 @@ describe(':preserve', function() ]] source(init) - execute('set swapfile fileformat=unix undolevels=-1') + feed_command('set swapfile fileformat=unix undolevels=-1') -- Put swapdir at the start of the 'directory' list. #1836 - execute('set directory^='..swapdir..'//') - execute('edit '..testfile) + feed_command('set directory^='..swapdir..'//') + feed_command('edit '..testfile) feed('isometext') - execute('preserve') + feed_command('preserve') source('redir => g:swapname | swapname | redir END') local swappath1 = eval('g:swapname') @@ -59,8 +59,8 @@ describe(':preserve', function() source(init) -- Use the "SwapExists" event to choose the (R)ecover choice at the dialog. - execute('autocmd SwapExists * let v:swapchoice = "r"') - execute('silent edit '..testfile) + feed_command('autocmd SwapExists * let v:swapchoice = "r"') + feed_command('silent edit '..testfile) source('redir => g:swapname | swapname | redir END') local swappath2 = eval('g:swapname') diff --git a/test/functional/ex_cmds/undojoin_spec.lua b/test/functional/ex_cmds/undojoin_spec.lua index ba1e46ceb3..7803906619 100644 --- a/test/functional/ex_cmds/undojoin_spec.lua +++ b/test/functional/ex_cmds/undojoin_spec.lua @@ -5,7 +5,7 @@ local clear = helpers.clear local insert = helpers.insert local feed = helpers.feed local expect = helpers.expect -local execute = helpers.execute +local feed_command = helpers.feed_command local exc_exec = helpers.exc_exec describe(':undojoin command', function() @@ -14,10 +14,10 @@ describe(':undojoin command', function() insert([[ Line of text 1 Line of text 2]]) - execute('goto 1') + feed_command('goto 1') end) it('joins changes in a buffer', function() - execute('undojoin | delete') + feed_command('undojoin | delete') expect([[ Line of text 2]]) feed('u') @@ -26,7 +26,7 @@ describe(':undojoin command', function() end) it('does not corrupt undolist when connected with redo', function() feed('ixx') - execute('undojoin | redo') + feed_command('undojoin | redo') expect([[ xxLine of text 1 Line of text 2]]) diff --git a/test/functional/ex_cmds/write_spec.lua b/test/functional/ex_cmds/write_spec.lua index 9c2687971e..ea8b41a578 100644 --- a/test/functional/ex_cmds/write_spec.lua +++ b/test/functional/ex_cmds/write_spec.lua @@ -1,11 +1,12 @@ local helpers = require('test.functional.helpers')(after_each) local lfs = require('lfs') -local eq, eval, clear, write_file, execute, source, insert = +local eq, eval, clear, write_file, command, source, insert = helpers.eq, helpers.eval, helpers.clear, helpers.write_file, - helpers.execute, helpers.source, helpers.insert + helpers.command, helpers.source, helpers.insert local redir_exec = helpers.redir_exec local exc_exec = helpers.exc_exec local command = helpers.command +local feed_command = helpers.feed_command local funcs = helpers.funcs local meths = helpers.meths @@ -33,9 +34,9 @@ describe(':write', function() end) it('&backupcopy=auto preserves symlinks', function() - execute('set backupcopy=auto') + command('set backupcopy=auto') write_file('test_bkc_file.txt', 'content0') - execute("silent !ln -s test_bkc_file.txt test_bkc_link.txt") + command("silent !ln -s test_bkc_file.txt test_bkc_link.txt") source([[ edit test_bkc_link.txt call setline(1, ['content1']) @@ -46,9 +47,9 @@ describe(':write', function() end) it('&backupcopy=no replaces symlink with new file', function() - execute('set backupcopy=no') + command('set backupcopy=no') write_file('test_bkc_file.txt', 'content0') - execute("silent !ln -s test_bkc_file.txt test_bkc_link.txt") + command("silent !ln -s test_bkc_file.txt test_bkc_link.txt") source([[ edit test_bkc_link.txt call setline(1, ['content1']) @@ -69,7 +70,7 @@ describe(':write', function() insert(text) -- Blocks until a consumer reads the FIFO. - execute("write >> test_fifo") + feed_command("write >> test_fifo") -- Read the FIFO, this will unblock the :write above. local fifo = assert(io.open("test_fifo")) diff --git a/test/functional/ex_cmds/wundo_spec.lua b/test/functional/ex_cmds/wundo_spec.lua index e1216fa5d4..b6fcae0cf4 100644 --- a/test/functional/ex_cmds/wundo_spec.lua +++ b/test/functional/ex_cmds/wundo_spec.lua @@ -1,20 +1,21 @@ -- Specs for :wundo and underlying functions local helpers = require('test.functional.helpers')(after_each) -local execute, clear, eval, feed, spawn, nvim_prog, set_session = - helpers.execute, helpers.clear, helpers.eval, helpers.feed, helpers.spawn, +local command, clear, eval, spawn, nvim_prog, set_session = + helpers.command, helpers.clear, helpers.eval, helpers.spawn, helpers.nvim_prog, helpers.set_session describe(':wundo', function() before_each(clear) + after_each(function() + os.remove(eval('getcwd()') .. '/foo') + end) it('safely fails on new, non-empty buffer', function() - feed('iabc') - execute('wundo foo') -- This should not segfault. #1027 + command('normal! iabc') + command('wundo foo') -- This should not segfault. #1027 --TODO: check messages for error message - - os.remove(eval('getcwd()') .. '/foo') --cleanup end) end) @@ -23,7 +24,7 @@ describe('u_* functions', function() local session = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed', '-c', 'set undodir=. undofile'}) set_session(session) - execute('echo "True"') -- Should not error out due to crashed Neovim + command('echo "True"') -- Should not error out due to crashed Neovim session:close() end) end) diff --git a/test/functional/ex_cmds/wviminfo_spec.lua b/test/functional/ex_cmds/wviminfo_spec.lua index 37f45da2d4..eebbd70f2b 100644 --- a/test/functional/ex_cmds/wviminfo_spec.lua +++ b/test/functional/ex_cmds/wviminfo_spec.lua @@ -1,8 +1,8 @@ local helpers = require('test.functional.helpers')(after_each) local lfs = require('lfs') -local execute, eq, neq, spawn, nvim_prog, set_session, wait, write_file - = helpers.execute, helpers.eq, helpers.neq, helpers.spawn, - helpers.nvim_prog, helpers.set_session, helpers.wait, helpers.write_file +local command, eq, neq, spawn, nvim_prog, set_session, write_file = + helpers.command, helpers.eq, helpers.neq, helpers.spawn, + helpers.nvim_prog, helpers.set_session, helpers.write_file describe(':wshada', function() local shada_file = 'wshada_test' @@ -24,8 +24,7 @@ describe(':wshada', function() it('creates a shada file', function() -- file should _not_ exist eq(nil, lfs.attributes(shada_file)) - execute('wsh! '..shada_file) - wait() + command('wsh! '..shada_file) -- file _should_ exist neq(nil, lfs.attributes(shada_file)) end) @@ -40,8 +39,7 @@ describe(':wshada', function() eq(text, io.open(shada_file):read()) neq(nil, lfs.attributes(shada_file)) - execute('wsh! '..shada_file) - wait() + command('wsh! '..shada_file) -- File should have been overwritten with a shada file. local fp = io.open(shada_file, 'r') diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 7793a9a739..adabfde9f7 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -566,7 +566,6 @@ local M = { iswin = iswin, feed = feed, feed_command = feed_command, - execute = feed_command, -- FIXME Remove eval = nvim_eval, call = nvim_call, command = nvim_command, diff --git a/test/functional/legacy/002_filename_recognition_spec.lua b/test/functional/legacy/002_filename_recognition_spec.lua index 5a833281e7..26a62d92fe 100644 --- a/test/functional/legacy/002_filename_recognition_spec.lua +++ b/test/functional/legacy/002_filename_recognition_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local execute, expect = helpers.execute, helpers.expect +local feed_command, expect = helpers.feed_command, helpers.expect describe('filename recognition', function() setup(clear) @@ -17,17 +17,17 @@ describe('filename recognition', function() fourth test for URL:\\machine.name\tmp\vimtest2d, and other text]]) -- Go to the first URL and append it to the beginning - execute('/^first', '/tmp', 'call append(0, expand(""))') + feed_command('/^first', '/tmp', 'call append(0, expand(""))') -- Repeat for the second URL -- this time, navigate to the word "URL" instead of "tmp" - execute('/^second', '/URL', 'call append(1, expand(""))') + feed_command('/^second', '/URL', 'call append(1, expand(""))') -- Repeat for the remaining URLs. This time, the 'isfname' option must be -- set to allow '\' in filenames - execute('set isf=@,48-57,/,.,-,_,+,,,$,:,~,\\') - execute('/^third', '/name', 'call append(2, expand(""))') - execute('/^fourth', '/URL', 'call append(3, expand(""))') + feed_command('set isf=@,48-57,/,.,-,_,+,,,$,:,~,\\') + feed_command('/^third', '/name', 'call append(2, expand(""))') + feed_command('/^fourth', '/URL', 'call append(3, expand(""))') -- Delete the initial text, which now starts at line 5 feed('5GdG') diff --git a/test/functional/legacy/004_bufenter_with_modelines_spec.lua b/test/functional/legacy/004_bufenter_with_modelines_spec.lua index 3e5cdd2ff2..9b0df024c8 100644 --- a/test/functional/legacy/004_bufenter_with_modelines_spec.lua +++ b/test/functional/legacy/004_bufenter_with_modelines_spec.lua @@ -1,10 +1,9 @@ --- vim: set foldmethod=marker foldmarker=[[,]] : -- Test for autocommand that changes current buffer on BufEnter event. -- Check if modelines are interpreted for the correct buffer. local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local execute, expect = helpers.execute, helpers.expect +local feed_command, expect = helpers.feed_command, helpers.expect describe('BufEnter with modelines', function() setup(clear) @@ -20,34 +19,34 @@ describe('BufEnter with modelines', function() this is a test end of test file Xxx]]) - execute('au BufEnter Xxx brew') + feed_command('au BufEnter Xxx brew') -- Write test file Xxx - execute('/start of') - execute('.,/end of/w! Xxx') - execute('set ai modeline modelines=3') + feed_command('/start of') + feed_command('.,/end of/w! Xxx') + feed_command('set ai modeline modelines=3') -- Split to Xxx, autocmd will do :brew - execute('sp Xxx') + feed_command('sp Xxx') -- Append text with autoindent to this file feed('G?this is a') feed('othis should be auto-indented') -- Go to Xxx, no autocmd anymore - execute('au! BufEnter Xxx') - execute('buf Xxx') + feed_command('au! BufEnter Xxx') + feed_command('buf Xxx') -- Append text without autoindent to Xxx feed('G?this is a') feed('othis should be in column 1') - execute('wq') + feed_command('wq') -- Include Xxx in the current file feed('G:r Xxx') -- Vim issue #57 do not move cursor on when autoindent is set - execute('set fo+=r') + feed_command('set fo+=r') feed('G') feed('o# abcdef2hid0') feed('o# abcdef2hid0') diff --git a/test/functional/legacy/005_bufleave_delete_buffer_spec.lua b/test/functional/legacy/005_bufleave_delete_buffer_spec.lua index 895f4ad181..417842c52d 100644 --- a/test/functional/legacy/005_bufleave_delete_buffer_spec.lua +++ b/test/functional/legacy/005_bufleave_delete_buffer_spec.lua @@ -3,7 +3,8 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local execute, expect = helpers.execute, helpers.expect +local command, expect = helpers.command, helpers.expect +local wait = helpers.wait describe('test5', function() setup(clear) @@ -18,35 +19,37 @@ describe('test5', function() this is a test end of test file Xxx]]) - execute('w! Xxx0') - execute('au BufLeave Xxx bwipe') - execute('/start of') + command('w! Xxx0') + command('au BufLeave Xxx bwipe') + command('/start of') -- Write test file Xxx. - execute('.,/end of/w! Xxx') + command('.,/end of/w! Xxx') -- Split to Xxx. - execute('sp Xxx') + command('sp Xxx') -- Delete buffer Xxx, now we're back here. - execute('bwipe') + command('bwipe') feed('G?this is a') feed('othis is some more text') + wait() -- Append some text to this file. -- Write current file contents. - execute('?start?,$yank A') + command('?start?,$yank A') -- Delete current buffer, get an empty one. - execute('bwipe!') + command('bwipe!') -- Append an extra line to the output register. feed('ithis is another test line:yank A') + wait() -- Output results - execute('%d') - execute('0put a') - execute('$d') + command('%d') + command('0put a') + command('$d') -- Assert buffer contents. expect([[ diff --git a/test/functional/legacy/006_argument_list_spec.lua b/test/functional/legacy/006_argument_list_spec.lua index 764854314f..dac58df8a5 100644 --- a/test/functional/legacy/006_argument_list_spec.lua +++ b/test/functional/legacy/006_argument_list_spec.lua @@ -2,8 +2,9 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local execute, dedent, eq = helpers.execute, helpers.dedent, helpers.eq +local command, dedent, eq = helpers.command, helpers.dedent, helpers.eq local curbuf_contents = helpers.curbuf_contents +local wait = helpers.wait describe('argument list', function() setup(clear) @@ -16,10 +17,11 @@ describe('argument list', function() this is a test this is a test end of test file Xxx]]) + wait() + + command('au BufReadPost Xxx2 next Xxx2 Xxx1') + command('/^start of') - execute('au BufReadPost Xxx2 next Xxx2 Xxx1') - execute('/^start of') - -- Write test file Xxx1 feed('A1:.,/end of/w! Xxx1') @@ -28,29 +30,31 @@ describe('argument list', function() -- Write test file Xxx3 feed('$r3:.,/end of/w! Xxx3') + wait() -- Redefine arglist; go to Xxx1 - execute('next! Xxx1 Xxx2 Xxx3') - + command('next! Xxx1 Xxx2 Xxx3') + -- Open window for all args - execute('all') - + command('all') + -- Write contents of Xxx1 - execute('%yank A') + command('%yank A') -- Append contents of last window (Xxx1) feed('') - execute('%yank A') - - -- should now be in Xxx2 - execute('rew') - - -- Append contents of Xxx2 - execute('%yank A') + wait() + command('%yank A') - execute('%d') - execute('0put=@a') - execute('$d') + -- should now be in Xxx2 + command('rew') + + -- Append contents of Xxx2 + command('%yank A') + + command('%d') + command('0put=@a') + command('$d') eq(dedent([[ start of test file Xxx1 diff --git a/test/functional/legacy/007_ball_buffer_list_spec.lua b/test/functional/legacy/007_ball_buffer_list_spec.lua index e54525fd06..8501faabec 100644 --- a/test/functional/legacy/007_ball_buffer_list_spec.lua +++ b/test/functional/legacy/007_ball_buffer_list_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local execute, expect = helpers.execute, helpers.expect +local feed_command, expect = helpers.feed_command, helpers.expect describe(':ball', function() setup(clear) @@ -14,44 +14,44 @@ describe(':ball', function() this is a test end of test file Xxx]]) - execute('w! Xxx0') + feed_command('w! Xxx0') feed('gg') -- Write test file Xxx1 feed('A1:.,/end of/w! Xxx1') - execute('sp Xxx1') - execute('close') + feed_command('sp Xxx1') + feed_command('close') -- Write test file Xxx2 feed('$r2:.,/end of/w! Xxx2') - execute('sp Xxx2') - execute('close') + feed_command('sp Xxx2') + feed_command('close') -- Write test file Xxx3 feed('$r3:.,/end of/w! Xxx3') - execute('sp Xxx3') - execute('close') + feed_command('sp Xxx3') + feed_command('close') - execute('au BufReadPost Xxx2 bwipe') + feed_command('au BufReadPost Xxx2 bwipe') -- Open window for all args, close Xxx2 feed('$r4:ball') -- Write contents of this file - execute('%yank A') + feed_command('%yank A') -- Append contents of second window (Xxx1) feed('') - execute('%yank A') + feed_command('%yank A') -- Append contents of last window (this file) feed('') - execute('%yank A') + feed_command('%yank A') - execute('bf') - execute('%d') - execute('0put=@a') - execute('$d') + feed_command('bf') + feed_command('%d') + feed_command('0put=@a') + feed_command('$d') expect([[ start of test file Xxx4 diff --git a/test/functional/legacy/008_autocommands_spec.lua b/test/functional/legacy/008_autocommands_spec.lua index 2c398d3c73..7474f1e068 100644 --- a/test/functional/legacy/008_autocommands_spec.lua +++ b/test/functional/legacy/008_autocommands_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local feed, source = helpers.feed, helpers.source -local clear, execute, expect, eq, eval = helpers.clear, helpers.execute, helpers.expect, helpers.eq, helpers.eval +local clear, feed_command, expect, eq, eval = helpers.clear, helpers.feed_command, helpers.expect, helpers.eq, helpers.eval local write_file, wait, dedent = helpers.write_file, helpers.wait, helpers.dedent local io = require('io') @@ -25,15 +25,15 @@ describe('autocommands that delete and unload buffers:', function() before_each(clear) it('BufWritePre, BufUnload', function() - execute('au BufWritePre Xxx1 bunload') - execute('au BufWritePre Xxx2 bwipe') - execute('e Xxx2') + feed_command('au BufWritePre Xxx1 bunload') + feed_command('au BufWritePre Xxx2 bwipe') + feed_command('e Xxx2') eq('Xxx2', eval('bufname("%")')) - execute('e Xxx1') + feed_command('e Xxx1') eq('Xxx1', eval('bufname("%")')) -- The legacy test file did not check the error message. - execute('let v:errmsg = "no error"') - execute('write') + feed_command('let v:errmsg = "no error"') + feed_command('write') -- Discard all "hit enter" prompts and messages. feed('') eq('E203: Autocommands deleted or unloaded buffer to be written', @@ -41,11 +41,11 @@ describe('autocommands that delete and unload buffers:', function() eq('Xxx2', eval('bufname("%")')) expect(text2) -- Start editing Xxx2. - execute('e! Xxx2') + feed_command('e! Xxx2') -- The legacy test file did not check the error message. - execute('let v:errmsg = "no error"') + feed_command('let v:errmsg = "no error"') -- Write Xxx2, will delete the buffer and give an error msg. - execute('w') + feed_command('w') -- Discard all "hit enter" prompts and messages. feed('') eq('E203: Autocommands deleted or unloaded buffer to be written', @@ -73,17 +73,17 @@ describe('autocommands that delete and unload buffers:', function() au BufUnload * call CloseAll() au VimLeave * call WriteToOut() ]]) - execute('e Xxx2') + feed_command('e Xxx2') -- Discard all "hit enter" prompts and messages. feed('') - execute('e Xxx1') + feed_command('e Xxx1') -- Discard all "hit enter" prompts and messages. feed('') - execute('e Makefile') -- an existing file + feed_command('e Makefile') -- an existing file feed('') - execute('sp new2') + feed_command('sp new2') feed('') - execute('q') + feed_command('q') wait() eq('VimLeave done', string.match(io.open('test.out', 'r'):read('*all'), "^%s*(.-)%s*$")) diff --git a/test/functional/legacy/011_autocommands_spec.lua b/test/functional/legacy/011_autocommands_spec.lua index ba899f8119..e01af4583b 100644 --- a/test/functional/legacy/011_autocommands_spec.lua +++ b/test/functional/legacy/011_autocommands_spec.lua @@ -14,8 +14,8 @@ local helpers= require('test.functional.helpers')(after_each) local lfs = require('lfs') -local clear, execute, expect, eq, neq, dedent, write_file, feed = - helpers.clear, helpers.execute, helpers.expect, helpers.eq, helpers.neq, +local clear, feed_command, expect, eq, neq, dedent, write_file, feed = + helpers.clear, helpers.feed_command, helpers.expect, helpers.eq, helpers.neq, helpers.dedent, helpers.write_file, helpers.feed if helpers.pending_win32(pending) then return end @@ -66,26 +66,26 @@ describe('file reading, writing and bufnew and filter autocommands', function() it('FileReadPost (using gzip)', function() prepare_gz_file('Xtestfile', text1) - execute('let $GZIP = ""') + feed_command('let $GZIP = ""') --execute('au FileChangedShell * echo "caught FileChangedShell"') - execute('set bin') - execute("au FileReadPost *.gz '[,']!gzip -d") + feed_command('set bin') + feed_command("au FileReadPost *.gz '[,']!gzip -d") -- Read and decompress the testfile. - execute('$r Xtestfile.gz') + feed_command('$r Xtestfile.gz') expect('\n'..text1) end) it('BufReadPre, BufReadPost (using gzip)', function() prepare_gz_file('Xtestfile', text1) local gzip_data = io.open('Xtestfile.gz'):read('*all') - execute('let $GZIP = ""') + feed_command('let $GZIP = ""') -- Setup autocommands to decompress before reading and re-compress afterwards. - execute("au BufReadPre *.gz exe '!gzip -d ' . shellescape(expand(''))") - execute("au BufReadPre *.gz call rename(expand(':r'), expand(''))") - execute("au BufReadPost *.gz call rename(expand(''), expand(':r'))") - execute("au BufReadPost *.gz exe '!gzip ' . shellescape(expand(':r'))") + feed_command("au BufReadPre *.gz exe '!gzip -d ' . shellescape(expand(''))") + feed_command("au BufReadPre *.gz call rename(expand(':r'), expand(''))") + feed_command("au BufReadPost *.gz call rename(expand(''), expand(':r'))") + feed_command("au BufReadPost *.gz exe '!gzip ' . shellescape(expand(':r'))") -- Edit compressed file. - execute('e! Xtestfile.gz') + feed_command('e! Xtestfile.gz') -- Discard all prompts and messages. feed('') -- Expect the decompressed file in the buffer. @@ -96,11 +96,11 @@ describe('file reading, writing and bufnew and filter autocommands', function() it('FileReadPre, FileReadPost', function() prepare_gz_file('Xtestfile', text1) - execute('au! FileReadPre *.gz exe "silent !gzip -d " . shellescape(expand(""))') - execute('au FileReadPre *.gz call rename(expand(":r"), expand(""))') - execute("au! FileReadPost *.gz '[,']s/l/L/") + feed_command('au! FileReadPre *.gz exe "silent !gzip -d " . shellescape(expand(""))') + feed_command('au FileReadPre *.gz call rename(expand(":r"), expand(""))') + feed_command("au! FileReadPost *.gz '[,']s/l/L/") -- Read compressed file. - execute('$r Xtestfile.gz') + feed_command('$r Xtestfile.gz') -- Discard all prompts and messages. feed('') expect([[ @@ -121,17 +121,17 @@ describe('file reading, writing and bufnew and filter autocommands', function() end it('FileAppendPre, FileAppendPost', function() - execute('au BufNewFile *.c read Xtest.c') + feed_command('au BufNewFile *.c read Xtest.c') -- Will load Xtest.c. - execute('e! foo.c') - execute("au FileAppendPre *.out '[,']s/new/NEW/") - execute('au FileAppendPost *.out !cat Xtest.c >>test.out') + feed_command('e! foo.c') + feed_command("au FileAppendPre *.out '[,']s/new/NEW/") + feed_command('au FileAppendPost *.out !cat Xtest.c >>test.out') -- Append it to the output file. - execute('w>>test.out') + feed_command('w>>test.out') -- Discard all prompts and messages. feed('') -- Expect the decompressed file in the buffer. - execute('e test.out') + feed_command('e test.out') expect([[ /* @@ -166,18 +166,18 @@ describe('file reading, writing and bufnew and filter autocommands', function() * Here is a new .c file */]])) -- Need temp files here. - execute('set shelltemp') - execute('au FilterReadPre *.out call rename(expand(""), expand("") . ".t")') - execute('au FilterReadPre *.out exe "silent !sed s/e/E/ " . shellescape(expand("")) . ".t >" . shellescape(expand(""))') - execute('au FilterReadPre *.out exe "silent !rm " . shellescape(expand("")) . ".t"') - execute("au FilterReadPost *.out '[,']s/x/X/g") + feed_command('set shelltemp') + feed_command('au FilterReadPre *.out call rename(expand(""), expand("") . ".t")') + feed_command('au FilterReadPre *.out exe "silent !sed s/e/E/ " . shellescape(expand("")) . ".t >" . shellescape(expand(""))') + feed_command('au FilterReadPre *.out exe "silent !rm " . shellescape(expand("")) . ".t"') + feed_command("au FilterReadPost *.out '[,']s/x/X/g") -- Edit the output file. - execute('e! test.out') - execute('23,$!cat') + feed_command('e! test.out') + feed_command('23,$!cat') -- Discard all prompts and messages. feed('') -- Remove CR for when sed adds them. - execute([[23,$s/\r$//]]) + feed_command([[23,$s/\r$//]]) expect([[ startstart start of testfile diff --git a/test/functional/legacy/015_alignment_spec.lua b/test/functional/legacy/015_alignment_spec.lua index 48d4042ff2..8423aa3d11 100644 --- a/test/functional/legacy/015_alignment_spec.lua +++ b/test/functional/legacy/015_alignment_spec.lua @@ -4,7 +4,7 @@ local helpers = require('test.functional.helpers')(after_each) local feed, insert = helpers.feed, helpers.insert -local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect describe('alignment', function() setup(clear) @@ -19,7 +19,7 @@ describe('alignment', function() asdfa a xasdfa a asxxdfa a - + test for :center a a fa afd asdf @@ -28,7 +28,7 @@ describe('alignment', function() asdfa a xasdfa asdfasdfasdfasdfasdf asxxdfa a - + test for :right a a fa a @@ -111,34 +111,34 @@ describe('alignment', function() asxxdfa axxxoikey asxa;ofa axxxoikey asdfaqwer axxxoikey - + xxxxx xx xxxxxx xxxxxxx xxxxxxxxx xxx xxxx xxxxx xxxxx xxx xx xxxxxxxxxxxxxxxxxx xxxxx xxxx, xxxx xxxx xxxx xxxx xxx xx xx xx xxxxxxx. xxxx xxxx. - + > xx xx, xxxx xxxx xxx xxxx xxx xxxxx xxx xxx xxxxxxx xxx xxxxx > xxxxxx xxxxxxx: xxxx xxxxxxx, xx xxxxxx xxxx xxxxxxxxxx - + aa aa aa aa bb bb bb bb cc cc cc cc]]) - execute('set tw=65') + feed_command('set tw=65') feed([[:/^\s*test for :left/,/^\s*test for :center/ left]]) feed([[:/^\s*test for :center/,/^\s*test for :right/ center]]) feed([[:/^\s*test for :right/,/^xxx/-1 right]]) - execute('set fo+=tcroql tw=72') + feed_command('set fo+=tcroql tw=72') feed('/xxxxxxxx$') feed('0gq6kk') -- Undo/redo here to make the next undo only work on the following changes. feed('u') - execute('map gg :.,.+2s/^/x/kk:set tw=3gqq') - execute('/^aa') + feed_command('map gg :.,.+2s/^/x/kk:set tw=3gqq') + feed_command('/^aa') feed('ggu') -- Assert buffer contents. @@ -151,7 +151,7 @@ describe('alignment', function() asdfa a xasdfa a asxxdfa a - + test for :center a a fa afd asdf @@ -160,7 +160,7 @@ describe('alignment', function() asdfa a xasdfa asdfasdfasdfasdfasdf asxxdfa a - + test for :right a a fa a @@ -243,14 +243,14 @@ describe('alignment', function() asxxdfa axxxoikey asxa;ofa axxxoikey asdfaqwer axxxoikey - + xxxxx xx xxxxxx xxxxxxx xxxxxxxxx xxx xxxx xxxxx xxxxx xxx xx xxxxxxxxxxxxxxxxxx xxxxx xxxx, xxxx xxxx xxxx xxxx xxx xx xx xx xxxxxxx. xxxx xxxx. - + > xx xx, xxxx xxxx xxx xxxx xxx xxxxx xxx xxx xxxxxxx xxx xxxxx xxxxxx > xxxxxxx: xxxx xxxxxxx, xx xxxxxx xxxx xxxxxxxxxx - + aa aa aa aa bb bb bb bb cc cc cc cc]]) diff --git a/test/functional/legacy/019_smarttab_expandtab_spec.lua b/test/functional/legacy/019_smarttab_expandtab_spec.lua index 2287a9f786..ecb24885bb 100644 --- a/test/functional/legacy/019_smarttab_expandtab_spec.lua +++ b/test/functional/legacy/019_smarttab_expandtab_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local feed, insert = helpers.feed, helpers.insert -local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect describe([[performing "r" with 'smarttab' and 'expandtab' set/not set, and "dv_"]], function() setup(clear) @@ -19,24 +19,24 @@ describe([[performing "r" with 'smarttab' and 'expandtab' set/not set, and test text Second line beginning with whitespace]]) - execute('set smarttab expandtab ts=8 sw=4') + feed_command('set smarttab expandtab ts=8 sw=4') -- Make sure that backspace works, no matter what termcap is used. - execute('set t_kD=x7f t_kb=x08') + feed_command('set t_kD=x7f t_kb=x08') - execute('/some') + feed_command('/some') feed('r ') - execute('set noexpandtab') - execute('/other') + feed_command('set noexpandtab') + feed_command('/other') feed('r ') -- Test replacing with Tabs and then backspacing to undo it. feed('0wR ') -- Test replacing with Tabs. feed('0wR ') -- Test that copyindent works with expandtab set. - execute('set expandtab smartindent copyindent ts=8 sw=8 sts=8') + feed_command('set expandtab smartindent copyindent ts=8 sw=8 sts=8') feed('o{x') - execute('set nosol') - execute('/Second line/') + feed_command('set nosol') + feed_command('/Second line/') -- Test "dv_" feed('fwdv_') diff --git a/test/functional/legacy/020_blockwise_visual_spec.lua b/test/functional/legacy/020_blockwise_visual_spec.lua index 660348a792..8d90b1c77d 100644 --- a/test/functional/legacy/020_blockwise_visual_spec.lua +++ b/test/functional/legacy/020_blockwise_visual_spec.lua @@ -1,11 +1,10 @@ --- vim: set foldmethod=marker foldmarker=[[,]] : -- Tests Blockwise Visual when there are TABs before the text. -- First test for undo working properly when executing commands from a register. -- Also test this in an empty buffer. local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local execute, expect = helpers.execute, helpers.expect +local feed_command, expect = helpers.feed_command, helpers.expect describe('blockwise visual', function() setup(clear) @@ -26,15 +25,15 @@ Ox jAy kdd]]) feed(":let @a = 'OxjAykdd'") feed('G0k@au') - execute('new') + feed_command('new') feed('@auY') - execute('quit') + feed_command('quit') feed('GP') - execute('/start here') + feed_command('/start here') feed('"by$jjlld') - execute('/456') + feed_command('/456') feed('jj"bP') - execute('$-3,$d') + feed_command('$-3,$d') expect([[ 123start here56 diff --git a/test/functional/legacy/021_control_wi_spec.lua b/test/functional/legacy/021_control_wi_spec.lua index 787a384fca..87d9deed7a 100644 --- a/test/functional/legacy/021_control_wi_spec.lua +++ b/test/functional/legacy/021_control_wi_spec.lua @@ -1,9 +1,8 @@ --- vim: set foldmethod=marker foldmarker=[[,]] : -- Tests for [ CTRL-I with a count and CTRL-W CTRL-I with a count local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local execute, expect = helpers.execute, helpers.expect +local feed_command, expect = helpers.feed_command, helpers.expect describe('CTRL-W CTRL-I', function() setup(clear) @@ -20,18 +19,18 @@ describe('CTRL-W CTRL-I', function() test text]]) -- Search for the second occurence of start and append to register - execute('/start') + feed_command('/start') feed('2[') - execute('yank A') + feed_command('yank A') -- Same as above but using different keystrokes. feed('?start') feed('2') - execute('yank A') + feed_command('yank A') -- Clean buffer and put register feed('ggdG"ap') - execute('1d') + feed_command('1d') -- The buffer should now contain: expect([[ diff --git a/test/functional/legacy/022_line_ending_spec.lua b/test/functional/legacy/022_line_ending_spec.lua index 092440bb16..fb4b782011 100644 --- a/test/functional/legacy/022_line_ending_spec.lua +++ b/test/functional/legacy/022_line_ending_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed = helpers.clear, helpers.feed -local execute, expect = helpers.execute, helpers.expect +local feed_command, expect = helpers.feed_command, helpers.expect describe('line ending', function() setup(clear) @@ -14,8 +14,8 @@ describe('line ending', function() this one does and the last one doesn't]], '') - execute('set ta tx') - execute('e!') + feed_command('set ta tx') + feed_command('e!') expect("this lines ends in a\r\n".. "this one doesn't\n".. diff --git a/test/functional/legacy/023_edit_arguments_spec.lua b/test/functional/legacy/023_edit_arguments_spec.lua index 95ab983d24..e705397a2b 100644 --- a/test/functional/legacy/023_edit_arguments_spec.lua +++ b/test/functional/legacy/023_edit_arguments_spec.lua @@ -2,7 +2,8 @@ local helpers = require('test.functional.helpers')(after_each) local clear, insert = helpers.clear, helpers.insert -local execute, expect = helpers.execute, helpers.expect +local command, expect = helpers.command, helpers.expect +local wait = helpers.wait describe(':edit', function() setup(clear) @@ -12,31 +13,32 @@ describe(':edit', function() The result should be in Xfile1: "fooPIPEbar", in Xfile2: "fooSLASHbar" foo|bar foo/bar]]) + wait() -- Prepare some test files - execute('$-1w! Xfile1') - execute('$w! Xfile2') - execute('w! Xfile0') + command('$-1w! Xfile1') + command('$w! Xfile2') + command('w! Xfile0') -- Open Xfile using '+' range - execute('edit +1 Xfile1') - execute('s/|/PIPE/') - execute('yank A') - execute('w! Xfile1') + command('edit +1 Xfile1') + command('s/|/PIPE/') + command('yank A') + command('w! Xfile1') -- Open Xfile2 using '|' range - execute('edit Xfile2|1') - execute("s/\\//SLASH/") - execute('yank A') - execute('w! Xfile2') + command('edit Xfile2|1') + command("s/\\//SLASH/") + command('yank A') + command('w! Xfile2') -- Clean first buffer and put @a - execute('bf') - execute('%d') - execute('0put a') + command('bf') + command('%d') + command('0put a') -- Remove empty line - execute('$d') + command('$d') -- The buffer should now contain expect([[ diff --git a/test/functional/legacy/025_jump_tag_hidden_spec.lua b/test/functional/legacy/025_jump_tag_hidden_spec.lua index 99224f9e08..0d51b4da26 100644 --- a/test/functional/legacy/025_jump_tag_hidden_spec.lua +++ b/test/functional/legacy/025_jump_tag_hidden_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local execute, expect = helpers.execute, helpers.expect +local feed_command, expect = helpers.feed_command, helpers.expect if helpers.pending_win32(pending) then return end @@ -21,30 +21,30 @@ describe('jump to a tag with hidden set', function() SECTION_OFF]]) - execute('w! Xxx') - execute('set hidden') + feed_command('w! Xxx') + feed_command('set hidden') -- Create a link from test25.dir to the current directory. - execute('!rm -f test25.dir') - execute('!ln -s . test25.dir') + feed_command('!rm -f test25.dir') + feed_command('!ln -s . test25.dir') -- Create tags.text, with the current directory name inserted. - execute('/tags line') - execute('r !pwd') + feed_command('/tags line') + feed_command('r !pwd') feed('d$/test') feed('hP:.w! tags.test') -- Try jumping to a tag in the current file, but with a path that contains a -- symbolic link. When wrong, this will give the ATTENTION message. The next -- space will then be eaten by hit-return, instead of moving the cursor to 'd'. - execute('set tags=tags.test') + feed_command('set tags=tags.test') feed('G x:yank a') - execute('!rm -f Xxx test25.dir tags.test') + feed_command('!rm -f Xxx test25.dir tags.test') -- Put @a and remove empty line - execute('%d') - execute('0put a') - execute('$d') + feed_command('%d') + feed_command('0put a') + feed_command('$d') -- Assert buffer contents. expect("#efine SECTION_OFF 3") diff --git a/test/functional/legacy/026_execute_while_if_spec.lua b/test/functional/legacy/026_execute_while_if_spec.lua index 74ef34bb20..ea8abed7ae 100644 --- a/test/functional/legacy/026_execute_while_if_spec.lua +++ b/test/functional/legacy/026_execute_while_if_spec.lua @@ -1,9 +1,11 @@ -- Test for :execute, :while and :if local helpers = require('test.functional.helpers')(after_each) + local clear = helpers.clear -local execute, expect = helpers.execute, helpers.expect +local expect = helpers.expect local source = helpers.source +local command = helpers.command describe(':execute, :while and :if', function() setup(clear) @@ -37,7 +39,7 @@ describe(':execute, :while and :if', function() ]]) -- Remove empty line - execute('1d') + command('1d') -- Assert buffer contents. expect([[ diff --git a/test/functional/legacy/028_source_ctrl_v_spec.lua b/test/functional/legacy/028_source_ctrl_v_spec.lua index a8c43260be..fabf831341 100644 --- a/test/functional/legacy/028_source_ctrl_v_spec.lua +++ b/test/functional/legacy/028_source_ctrl_v_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local execute, expect = helpers.execute, helpers.expect +local feed_command, expect = helpers.feed_command, helpers.expect describe('CTRL-V at the end of the line', function() setup(clear) @@ -24,8 +24,8 @@ describe('CTRL-V at the end of the line', function() feed(':%s/X//g') feed(':/firstline/+1,/lastline/-1w! Xtestfile') - execute('so Xtestfile') - execute('%d') + feed_command('so Xtestfile') + feed_command('%d') feed('Gmm__1__2__3__4__5') feed(":'m,$s//0/g") diff --git a/test/functional/legacy/030_fileformats_spec.lua b/test/functional/legacy/030_fileformats_spec.lua index 5fd78b2c59..aadc6d60c2 100644 --- a/test/functional/legacy/030_fileformats_spec.lua +++ b/test/functional/legacy/030_fileformats_spec.lua @@ -1,8 +1,9 @@ -- Test for a lot of variations of the 'fileformats' option local helpers = require('test.functional.helpers')(after_each) -local feed, clear, execute = helpers.feed, helpers.clear, helpers.execute +local feed, clear, command = helpers.feed, helpers.clear, helpers.command local eq, write_file = helpers.eq, helpers.write_file +local wait = helpers.wait if helpers.pending_win32(pending) then return end @@ -45,198 +46,214 @@ describe('fileformats option', function() it('is working', function() -- Try reading and writing with 'fileformats' empty. - execute('set fileformats=') - execute('set fileformat=unix') - execute('e! XXUnix') - execute('w! test.out') - execute('e! XXDos') - execute('w! XXtt01') - execute('e! XXMac') - execute('w! XXtt02') - execute('bwipe XXUnix XXDos XXMac') - execute('set fileformat=dos') - execute('e! XXUnix') - execute('w! XXtt11') - execute('e! XXDos') - execute('w! XXtt12') - execute('e! XXMac') - execute('w! XXtt13') - execute('bwipe XXUnix XXDos XXMac') - execute('set fileformat=mac') - execute('e! XXUnix') - execute('w! XXtt21') - execute('e! XXDos') - execute('w! XXtt22') - execute('e! XXMac') - execute('w! XXtt23') - execute('bwipe XXUnix XXDos XXMac') + command('set fileformats=') + command('set fileformat=unix') + command('e! XXUnix') + command('w! test.out') + command('e! XXDos') + command('w! XXtt01') + command('e! XXMac') + command('w! XXtt02') + command('bwipe XXUnix XXDos XXMac') + command('set fileformat=dos') + command('e! XXUnix') + command('w! XXtt11') + command('e! XXDos') + command('w! XXtt12') + command('e! XXMac') + command('w! XXtt13') + command('bwipe XXUnix XXDos XXMac') + command('set fileformat=mac') + command('e! XXUnix') + command('w! XXtt21') + command('e! XXDos') + command('w! XXtt22') + command('e! XXMac') + command('w! XXtt23') + command('bwipe XXUnix XXDos XXMac') -- Try reading and writing with 'fileformats' set to one format. - execute('set fileformats=unix') - execute('e! XXUxDsMc') - execute('w! XXtt31') - execute('bwipe XXUxDsMc') - execute('set fileformats=dos') - execute('e! XXUxDsMc') - execute('w! XXtt32') - execute('bwipe XXUxDsMc') - execute('set fileformats=mac') - execute('e! XXUxDsMc') - execute('w! XXtt33') - execute('bwipe XXUxDsMc') + command('set fileformats=unix') + command('e! XXUxDsMc') + command('w! XXtt31') + command('bwipe XXUxDsMc') + command('set fileformats=dos') + command('e! XXUxDsMc') + command('w! XXtt32') + command('bwipe XXUxDsMc') + command('set fileformats=mac') + command('e! XXUxDsMc') + command('w! XXtt33') + command('bwipe XXUxDsMc') -- Try reading and writing with 'fileformats' set to two formats. - execute('set fileformats=unix,dos') - execute('e! XXUxDsMc') - execute('w! XXtt41') - execute('bwipe XXUxDsMc') - execute('e! XXUxMac') - execute('w! XXtt42') - execute('bwipe XXUxMac') - execute('e! XXDosMac') - execute('w! XXtt43') - execute('bwipe XXDosMac') - execute('set fileformats=unix,mac') - execute('e! XXUxDs') - execute('w! XXtt51') - execute('bwipe XXUxDs') - execute('e! XXUxDsMc') - execute('w! XXtt52') - execute('bwipe XXUxDsMc') - execute('e! XXDosMac') - execute('w! XXtt53') - execute('bwipe XXDosMac') - execute('e! XXEol') + command('set fileformats=unix,dos') + command('e! XXUxDsMc') + command('w! XXtt41') + command('bwipe XXUxDsMc') + command('e! XXUxMac') + command('w! XXtt42') + command('bwipe XXUxMac') + command('e! XXDosMac') + command('w! XXtt43') + command('bwipe XXDosMac') + command('set fileformats=unix,mac') + command('e! XXUxDs') + command('w! XXtt51') + command('bwipe XXUxDs') + command('e! XXUxDsMc') + command('w! XXtt52') + command('bwipe XXUxDsMc') + command('e! XXDosMac') + command('w! XXtt53') + command('bwipe XXDosMac') + command('e! XXEol') feed('ggO=&ffs:=&ff') - execute('w! XXtt54') - execute('bwipe XXEol') - execute('set fileformats=dos,mac') - execute('e! XXUxDs') - execute('w! XXtt61') - execute('bwipe XXUxDs') - execute('e! XXUxMac') + wait() + command('w! XXtt54') + command('bwipe XXEol') + command('set fileformats=dos,mac') + command('e! XXUxDs') + command('w! XXtt61') + command('bwipe XXUxDs') + command('e! XXUxMac') feed('ggO=&ffs:=&ff') - execute('w! XXtt62') - execute('bwipe XXUxMac') - execute('e! XXUxDsMc') - execute('w! XXtt63') - execute('bwipe XXUxDsMc') - execute('e! XXMacEol') + wait() + command('w! XXtt62') + command('bwipe XXUxMac') + command('e! XXUxDsMc') + command('w! XXtt63') + command('bwipe XXUxDsMc') + command('e! XXMacEol') feed('ggO=&ffs:=&ff') - execute('w! XXtt64') - execute('bwipe XXMacEol') + wait() + command('w! XXtt64') + command('bwipe XXMacEol') -- Try reading and writing with 'fileformats' set to three formats. - execute('set fileformats=unix,dos,mac') - execute('e! XXUxDsMc') - execute('w! XXtt71') - execute('bwipe XXUxDsMc') - execute('e! XXEol') + command('set fileformats=unix,dos,mac') + command('e! XXUxDsMc') + command('w! XXtt71') + command('bwipe XXUxDsMc') + command('e! XXEol') feed('ggO=&ffs:=&ff') - execute('w! XXtt72') - execute('bwipe XXEol') - execute('set fileformats=mac,dos,unix') - execute('e! XXUxDsMc') - execute('w! XXtt81') - execute('bwipe XXUxDsMc') - execute('e! XXEol') + wait() + command('w! XXtt72') + command('bwipe XXEol') + command('set fileformats=mac,dos,unix') + command('e! XXUxDsMc') + command('w! XXtt81') + command('bwipe XXUxDsMc') + command('e! XXEol') feed('ggO=&ffs:=&ff') - execute('w! XXtt82') - execute('bwipe XXEol') + wait() + command('w! XXtt82') + command('bwipe XXEol') -- Try with 'binary' set. - execute('set fileformats=mac,unix,dos') - execute('set binary') - execute('e! XXUxDsMc') - execute('w! XXtt91') - execute('bwipe XXUxDsMc') - execute('set fileformats=mac') - execute('e! XXUxDsMc') - execute('w! XXtt92') - execute('bwipe XXUxDsMc') - execute('set fileformats=dos') - execute('e! XXUxDsMc') - execute('w! XXtt93') + command('set fileformats=mac,unix,dos') + command('set binary') + command('e! XXUxDsMc') + command('w! XXtt91') + command('bwipe XXUxDsMc') + command('set fileformats=mac') + command('e! XXUxDsMc') + command('w! XXtt92') + command('bwipe XXUxDsMc') + command('set fileformats=dos') + command('e! XXUxDsMc') + command('w! XXtt93') -- Append "END" to each file so that we can see what the last written -- char was. - execute('set fileformat=unix nobin') + command('set fileformat=unix nobin') feed('ggdGaEND') - execute('w >>XXtt01') - execute('w >>XXtt02') - execute('w >>XXtt11') - execute('w >>XXtt12') - execute('w >>XXtt13') - execute('w >>XXtt21') - execute('w >>XXtt22') - execute('w >>XXtt23') - execute('w >>XXtt31') - execute('w >>XXtt32') - execute('w >>XXtt33') - execute('w >>XXtt41') - execute('w >>XXtt42') - execute('w >>XXtt43') - execute('w >>XXtt51') - execute('w >>XXtt52') - execute('w >>XXtt53') - execute('w >>XXtt54') - execute('w >>XXtt61') - execute('w >>XXtt62') - execute('w >>XXtt63') - execute('w >>XXtt64') - execute('w >>XXtt71') - execute('w >>XXtt72') - execute('w >>XXtt81') - execute('w >>XXtt82') - execute('w >>XXtt91') - execute('w >>XXtt92') - execute('w >>XXtt93') + wait() + command('w >>XXtt01') + command('w >>XXtt02') + command('w >>XXtt11') + command('w >>XXtt12') + command('w >>XXtt13') + command('w >>XXtt21') + command('w >>XXtt22') + command('w >>XXtt23') + command('w >>XXtt31') + command('w >>XXtt32') + command('w >>XXtt33') + command('w >>XXtt41') + command('w >>XXtt42') + command('w >>XXtt43') + command('w >>XXtt51') + command('w >>XXtt52') + command('w >>XXtt53') + command('w >>XXtt54') + command('w >>XXtt61') + command('w >>XXtt62') + command('w >>XXtt63') + command('w >>XXtt64') + command('w >>XXtt71') + command('w >>XXtt72') + command('w >>XXtt81') + command('w >>XXtt82') + command('w >>XXtt91') + command('w >>XXtt92') + command('w >>XXtt93') -- Concatenate the results. -- Make fileformat of test.out the native fileformat. -- Add a newline at the end. - execute('set binary') - execute('e! test.out') - execute('$r XXtt01') - execute('$r XXtt02') + command('set binary') + command('e! test.out') + command('$r XXtt01') + command('$r XXtt02') feed('Go1') - execute('$r XXtt11') - execute('$r XXtt12') - execute('$r XXtt13') + wait() + command('$r XXtt11') + command('$r XXtt12') + command('$r XXtt13') feed('Go2') - execute('$r XXtt21') - execute('$r XXtt22') - execute('$r XXtt23') + wait() + command('$r XXtt21') + command('$r XXtt22') + command('$r XXtt23') feed('Go3') - execute('$r XXtt31') - execute('$r XXtt32') - execute('$r XXtt33') + wait() + command('$r XXtt31') + command('$r XXtt32') + command('$r XXtt33') feed('Go4') - execute('$r XXtt41') - execute('$r XXtt42') - execute('$r XXtt43') + wait() + command('$r XXtt41') + command('$r XXtt42') + command('$r XXtt43') feed('Go5') - execute('$r XXtt51') - execute('$r XXtt52') - execute('$r XXtt53') - execute('$r XXtt54') + wait() + command('$r XXtt51') + command('$r XXtt52') + command('$r XXtt53') + command('$r XXtt54') feed('Go6') - execute('$r XXtt61') - execute('$r XXtt62') - execute('$r XXtt63') - execute('$r XXtt64') + wait() + command('$r XXtt61') + command('$r XXtt62') + command('$r XXtt63') + command('$r XXtt64') feed('Go7') - execute('$r XXtt71') - execute('$r XXtt72') + wait() + command('$r XXtt71') + command('$r XXtt72') feed('Go8') - execute('$r XXtt81') - execute('$r XXtt82') + wait() + command('$r XXtt81') + command('$r XXtt82') feed('Go9') - execute('$r XXtt91') - execute('$r XXtt92') - execute('$r XXtt93') + wait() + command('$r XXtt91') + command('$r XXtt92') + command('$r XXtt93') feed('Go10') - execute('$r XXUnix') - execute('set nobinary ff&') + wait() + command('$r XXUnix') + command('set nobinary ff&') -- Assert buffer contents. This has to be done manually as -- helpers.expect() calls helpers.dedent() which messes up the white space diff --git a/test/functional/legacy/031_close_commands_spec.lua b/test/functional/legacy/031_close_commands_spec.lua index d41eadaa00..64c67c9882 100644 --- a/test/functional/legacy/031_close_commands_spec.lua +++ b/test/functional/legacy/031_close_commands_spec.lua @@ -16,7 +16,7 @@ local clear = helpers.clear local source = helpers.source local insert = helpers.insert local expect = helpers.expect -local execute = helpers.execute +local feed_command = helpers.feed_command describe('Commands that close windows and/or buffers', function() local function cleanup() @@ -38,40 +38,40 @@ describe('Commands that close windows and/or buffers', function() feed('GA 1:$w! Xtest1') feed('$r2:$w! Xtest2') feed('$r3:$w! Xtest3') - execute('n! Xtest1 Xtest2') + feed_command('n! Xtest1 Xtest2') feed('A 1:set hidden') -- Test for working :n when hidden set - execute('n') + feed_command('n') expect('testtext 2') -- Test for failing :rew when hidden not set - execute('set nohidden') + feed_command('set nohidden') feed('A 2:rew') expect('testtext 2 2') -- Test for working :rew when hidden set - execute('set hidden') - execute('rew') + feed_command('set hidden') + feed_command('rew') expect('testtext 1 1') -- Test for :all keeping a buffer when it's modified - execute('set nohidden') + feed_command('set nohidden') feed('A 1:sp') - execute('n Xtest2 Xtest3') - execute('all') - execute('1wincmd w') + feed_command('n Xtest2 Xtest3') + feed_command('all') + feed_command('1wincmd w') expect('testtext 1 1 1') -- Test abandoning changed buffer, should be unloaded even when 'hidden' set - execute('set hidden') + feed_command('set hidden') feed('A 1:q!') expect('testtext 2 2') - execute('unhide') + feed_command('unhide') expect('testtext 2 2') -- Test ":hide" hides anyway when 'hidden' not set - execute('set nohidden') + feed_command('set nohidden') feed('A 2:hide') expect('testtext 3') @@ -80,42 +80,42 @@ describe('Commands that close windows and/or buffers', function() expect('testtext 3 3') -- Test ":edit" working in modified buffer when 'hidden' set - execute('set hidden') - execute('e Xtest1') + feed_command('set hidden') + feed_command('e Xtest1') expect('testtext 1') -- Test ":close" not hiding when 'hidden' not set in modified buffer - execute('sp Xtest3') - execute('set nohidden') + feed_command('sp Xtest3') + feed_command('set nohidden') feed('A 3:close') expect('testtext 3 3 3') -- Test ":close!" does hide when 'hidden' not set in modified buffer feed('A 3:close!') - execute('set nohidden') + feed_command('set nohidden') expect('testtext 1') -- Test ":all!" hides changed buffer - execute('sp Xtest4') + feed_command('sp Xtest4') feed('GA 4:all!') - execute('1wincmd w') + feed_command('1wincmd w') expect('testtext 2 2 2') -- Test ":q!" and hidden buffer. - execute('bw! Xtest1 Xtest2 Xtest3 Xtest4') - execute('sp Xtest1') - execute('wincmd w') - execute('bw!') - execute('set modified') - execute('bot sp Xtest2') - execute('set modified') - execute('bot sp Xtest3') - execute('set modified') - execute('wincmd t') - execute('hide') - execute('q!') + feed_command('bw! Xtest1 Xtest2 Xtest3 Xtest4') + feed_command('sp Xtest1') + feed_command('wincmd w') + feed_command('bw!') + feed_command('set modified') + feed_command('bot sp Xtest2') + feed_command('set modified') + feed_command('bot sp Xtest3') + feed_command('set modified') + feed_command('wincmd t') + feed_command('hide') + feed_command('q!') expect('testtext 3') - execute('q!') + feed_command('q!') feed('') expect('testtext 1') source([[ diff --git a/test/functional/legacy/033_lisp_indent_spec.lua b/test/functional/legacy/033_lisp_indent_spec.lua index b4abb02ac2..2b79ee024b 100644 --- a/test/functional/legacy/033_lisp_indent_spec.lua +++ b/test/functional/legacy/033_lisp_indent_spec.lua @@ -1,10 +1,10 @@ --- vim: set foldmethod=marker foldmarker=[[,]] : -- Test for 'lisp' -- If the lisp feature is not enabled, this will fail! local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local execute, expect = helpers.execute, helpers.expect +local command, expect = helpers.command, helpers.expect +local wait = helpers.wait describe('lisp indent', function() setup(clear) @@ -13,7 +13,7 @@ describe('lisp indent', function() insert([[ (defun html-file (base) (format nil "~(~A~).html" base)) - + (defmacro page (name title &rest body) (let ((ti (gensym))) `(with-open-file (*standard-output* @@ -26,29 +26,30 @@ describe('lisp indent', function() (as h2 (string-upcase ,ti))) (brs 3) ,@body)))) - + ;;; Utilities for generating links - + (defmacro with-link (dest &rest body) `(progn (format t "" (html-file ,dest)) ,@body (princ "")))]]) - execute('set lisp') - execute('/^(defun') + command('set lisp') + command('/^(defun') feed('=G:/^(defun/,$yank A') + wait() -- Put @a and clean empty line - execute('%d') - execute('0put a') - execute('$d') + command('%d') + command('0put a') + command('$d') -- Assert buffer contents. expect([[ (defun html-file (base) (format nil "~(~A~).html" base)) - + (defmacro page (name title &rest body) (let ((ti (gensym))) `(with-open-file (*standard-output* @@ -61,9 +62,9 @@ describe('lisp indent', function() (as h2 (string-upcase ,ti))) (brs 3) ,@body)))) - + ;;; Utilities for generating links - + (defmacro with-link (dest &rest body) `(progn (format t "" (html-file ,dest)) diff --git a/test/functional/legacy/034_user_function_spec.lua b/test/functional/legacy/034_user_function_spec.lua index 38989cd982..0b7dfc4f0e 100644 --- a/test/functional/legacy/034_user_function_spec.lua +++ b/test/functional/legacy/034_user_function_spec.lua @@ -5,7 +5,7 @@ local helpers = require('test.functional.helpers')(after_each) local feed, insert, source = helpers.feed, helpers.insert, helpers.source -local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect describe('user functions, expr-mappings, overwrite protected builtin functions and regression on calling expressions', function() setup(clear) @@ -72,19 +72,19 @@ describe('user functions, expr-mappings, overwrite protected builtin functions a feed('(one') feed('(two') feed('[(one again') - execute('call append(line("$"), max([1, 2, 3]))') - execute('call extend(g:, {"max": function("min")})') - execute('call append(line("$"), max([1, 2, 3]))') - execute('try') + feed_command('call append(line("$"), max([1, 2, 3]))') + feed_command('call extend(g:, {"max": function("min")})') + feed_command('call append(line("$"), max([1, 2, 3]))') + feed_command('try') -- Regression: the first line below used to throw "E110: Missing ')'" -- Second is here just to prove that this line is correct when not -- skipping rhs of &&. - execute([[ $put =(0&&(function('tr'))(1, 2, 3))]]) - execute([[ $put =(1&&(function('tr'))(1, 2, 3))]]) - execute('catch') - execute([[ $put ='!!! Unexpected exception:']]) - execute(' $put =v:exception') - execute('endtry') + feed_command([[ $put =(0&&(function('tr'))(1, 2, 3))]]) + feed_command([[ $put =(1&&(function('tr'))(1, 2, 3))]]) + feed_command('catch') + feed_command([[ $put ='!!! Unexpected exception:']]) + feed_command(' $put =v:exception') + feed_command('endtry') -- Assert buffer contents. expect([[ diff --git a/test/functional/legacy/035_increment_and_decrement_spec.lua b/test/functional/legacy/035_increment_and_decrement_spec.lua index 3b9f7a9d85..84eb9c0eee 100644 --- a/test/functional/legacy/035_increment_and_decrement_spec.lua +++ b/test/functional/legacy/035_increment_and_decrement_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local execute, expect = helpers.execute, helpers.expect +local feed_command, expect = helpers.feed_command, helpers.expect describe('increment and decrement commands', function() setup(clear) @@ -19,25 +19,25 @@ describe('increment and decrement commands', function() -- Increment and decrement numbers in the first row, interpreting the -- numbers as decimal, octal or hexadecimal. - execute('set nrformats=bin,octal,hex', '1') + feed_command('set nrformats=bin,octal,hex', '1') feed('63l102ll64128$') -- For the second row, treat the numbers as decimal or octal. -- 0x100 should be interpreted as decimal 0, the character x, and decimal 100. - execute('set nrformats=octal', '2') + feed_command('set nrformats=octal', '2') feed('0w102l2w65129blx6lD') -- For the third row, treat the numbers as decimal or hexadecimal. -- 077 should be interpreted as decimal 77. - execute('set nrformats=hex', '3') + feed_command('set nrformats=hex', '3') feed('0101l257Txldt   ') -- For the fourth row, interpret all numbers as decimal. - execute('set nrformats=', '4') + feed_command('set nrformats=', '4') feed('0200l100w78') -- For the last row, interpret as binary and hexadecimal. - execute('set nrformats=bin,hex', '5') + feed_command('set nrformats=bin,hex', '5') feed('010065l6432') expect([[ diff --git a/test/functional/legacy/036_regexp_character_classes_spec.lua b/test/functional/legacy/036_regexp_character_classes_spec.lua index 15287b9901..110f7dd852 100644 --- a/test/functional/legacy/036_regexp_character_classes_spec.lua +++ b/test/functional/legacy/036_regexp_character_classes_spec.lua @@ -1,7 +1,7 @@ -- Test character classes in regexp using regexpengine 0, 1, 2. local helpers = require('test.functional.helpers')(after_each) -local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local clear, command, expect = helpers.clear, helpers.command, helpers.expect local source, write_file = helpers.source, helpers.write_file local function sixlines(text) @@ -14,7 +14,7 @@ end local function diff(text, nodedent) local fname = helpers.tmpname() - execute('w! '..fname) + command('w! '..fname) helpers.wait() local data = io.open(fname):read('*all') if nodedent then @@ -45,7 +45,7 @@ describe('character classes in regexp', function() end) before_each(function() clear() - execute('e test36.in') + command('e test36.in') end) teardown(function() os.remove('test36.in') diff --git a/test/functional/legacy/038_virtual_replace_spec.lua b/test/functional/legacy/038_virtual_replace_spec.lua index dcbc9c39f7..2dfc959a8c 100644 --- a/test/functional/legacy/038_virtual_replace_spec.lua +++ b/test/functional/legacy/038_virtual_replace_spec.lua @@ -2,31 +2,31 @@ local helpers = require('test.functional.helpers')(after_each) local feed = helpers.feed -local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect describe('Virtual replace mode', function() setup(clear) it('is working', function() -- Make sure that backspace works, no matter what termcap is used. - execute('set t_kD=x7f t_kb=x08') + feed_command('set t_kD=x7f t_kb=x08') -- Use vi default for 'smarttab' - execute('set nosmarttab') + feed_command('set nosmarttab') feed('ggdGa') feed('abcdefghi') feed('jklmn') feed('opqrst') feed('uvwxyz') feed('gg') - execute('set ai') - execute('set bs=2') + feed_command('set ai') + feed_command('set bs=2') feed('gR0 1') feed('A') feed('BCDEFGHIJ') feed('KL') feed('MNO') feed('PQRG') - execute('ka') + feed_command('ka') feed('o0') feed('abcdefghi') feed('jklmn') diff --git a/test/functional/legacy/039_visual_block_mode_commands_spec.lua b/test/functional/legacy/039_visual_block_mode_commands_spec.lua index 63335985cc..dffef50950 100644 --- a/test/functional/legacy/039_visual_block_mode_commands_spec.lua +++ b/test/functional/legacy/039_visual_block_mode_commands_spec.lua @@ -5,14 +5,14 @@ local helpers = require('test.functional.helpers')(after_each) local nvim, eq = helpers.meths, helpers.eq local insert, feed = helpers.insert, helpers.feed local clear, expect = helpers.clear, helpers.expect -local execute = helpers.execute +local feed_command = helpers.feed_command describe('Visual block mode', function() before_each(function() clear() - execute('set ts&vi sw&vi sts&vi noet') -- Vim compatible + feed_command('set ts&vi sw&vi sts&vi noet') -- Vim compatible end) it('should shift, insert, replace and change a block', function() @@ -55,9 +55,9 @@ describe('Visual block mode', function() cccc dddd]]) - execute('/^aa') + feed_command('/^aa') feed('ljjjlllI ') - execute('/xaaa$') + feed_command('/xaaa$') feed('jjjI>p') expect([[ @@ -84,13 +84,13 @@ describe('Visual block mode', function() 4567]]) -- Test for Visual block was created with the last $. - execute('/^A23$/') + feed_command('/^A23$/') feed('lj$Aab') -- Test for Visual block was created with the middle $ (1). - execute('/^B23$/') + feed_command('/^B23$/') feed('lj$hAab') -- Test for Visual block was created with the middle $ (2). - execute('/^C23$/') + feed_command('/^C23$/') feed('lj$hhAab') expect([[ @@ -112,8 +112,8 @@ describe('Visual block mode', function() ]]) -- Test for Visual block insert when virtualedit=all and utf-8 encoding. - execute('set ve=all') - execute('/\t\tline') + feed_command('set ve=all') + feed_command('/\t\tline') feed('07ljjIx') expect([[ @@ -199,10 +199,10 @@ describe('Visual block mode', function() 98765]]) -- Test cursor position. When virtualedit=block and Visual block mode and $gj. - execute('set ve=block') + feed_command('set ve=block') feed('G2l') feed('2k$gj') - execute([[let cpos=getpos("'>")]]) + feed_command([[let cpos=getpos("'>")]]) local cpos = nvim.get_var('cpos') local expected = { col = 4, @@ -223,7 +223,7 @@ describe('Visual block mode', function() #define BO_CRSR 0x0004]]) -- Block_insert when replacing spaces in front of the block with tabs. - execute('set ts=8 sts=4 sw=4') + feed_command('set ts=8 sts=4 sw=4') feed('ggf02jI') expect([[ diff --git a/test/functional/legacy/041_writing_and_reading_hundred_kbyte_spec.lua b/test/functional/legacy/041_writing_and_reading_hundred_kbyte_spec.lua index b6451eb720..b526d82519 100644 --- a/test/functional/legacy/041_writing_and_reading_hundred_kbyte_spec.lua +++ b/test/functional/legacy/041_writing_and_reading_hundred_kbyte_spec.lua @@ -1,8 +1,10 @@ -- Test for writing and reading a file of over 100 Kbyte local helpers = require('test.functional.helpers')(after_each) + local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local execute, expect = helpers.execute, helpers.expect +local command, expect = helpers.command, helpers.expect +local wait = helpers.wait describe('writing and reading a file of over 100 Kbyte', function() setup(clear) @@ -16,17 +18,18 @@ describe('writing and reading a file of over 100 Kbyte', function() This is the end]]) feed('kY3000p2GY3000p') + wait() - execute('w! test.out') - execute('%d') - execute('e! test.out') - execute('yank A') - execute('3003yank A') - execute('6005yank A') - execute('%d') - execute('0put a') - execute('$d') - execute('w!') + command('w! test.out') + command('%d') + command('e! test.out') + command('yank A') + command('3003yank A') + command('6005yank A') + command('%d') + command('0put a') + command('$d') + command('w!') expect([[ This is the start diff --git a/test/functional/legacy/043_magic_settings_spec.lua b/test/functional/legacy/043_magic_settings_spec.lua index f174751de2..a88ccc2b42 100644 --- a/test/functional/legacy/043_magic_settings_spec.lua +++ b/test/functional/legacy/043_magic_settings_spec.lua @@ -1,9 +1,8 @@ --- vim: set foldmethod=marker foldmarker=[[,]] : -- Tests for regexp with various magic settings. local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local execute, expect = helpers.execute, helpers.expect +local feed_command, expect = helpers.feed_command, helpers.expect describe('regexp with magic settings', function() setup(clear) @@ -21,27 +20,27 @@ describe('regexp with magic settings', function() 9 foobar ]]) - execute('/^1') - execute([[/a*b\{2}c\+/e]]) + feed_command('/^1') + feed_command([[/a*b\{2}c\+/e]]) feed([[x/\Md\*e\{2}f\+/e]]) feed('x:set nomagic') - execute([[/g\*h\{2}i\+/e]]) + feed_command([[/g\*h\{2}i\+/e]]) feed([[x/\mj*k\{2}l\+/e]]) feed([[x/\vm*n{2}o+/e]]) feed([[x/\V^aa$]]) feed('x:set magic') - execute([[/\v(a)(b)\2\1\1/e]]) + feed_command([[/\v(a)(b)\2\1\1/e]]) feed([[x/\V[ab]\(\[xy]\)\1]]) feed('x:$') - execute('set undolevels=100') + feed_command('set undolevels=100') feed('dv?bar?') feed('Yup:') - execute('?^1?,$yank A') + feed_command('?^1?,$yank A') -- Put @a and clean empty line - execute('%d') - execute('0put a') - execute('$d') + feed_command('%d') + feed_command('0put a') + feed_command('$d') -- Assert buffer contents. expect([[ diff --git a/test/functional/legacy/044_099_regexp_multibyte_magic_spec.lua b/test/functional/legacy/044_099_regexp_multibyte_magic_spec.lua index c6883e4902..074ee094b4 100644 --- a/test/functional/legacy/044_099_regexp_multibyte_magic_spec.lua +++ b/test/functional/legacy/044_099_regexp_multibyte_magic_spec.lua @@ -5,7 +5,7 @@ local helpers = require('test.functional.helpers')(after_each) local feed, insert = helpers.feed, helpers.insert -local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect -- Runs the test protocol with the given 'regexpengine' setting. In the old test -- suite the test protocol was duplicated in test44 and test99, the only @@ -32,19 +32,19 @@ local function run_test_with_regexpengine(regexpengine) k combinations l ä ö ü ᾱ̆́]]) - execute('set re=' .. regexpengine) + feed_command('set re=' .. regexpengine) -- Lines 1-8. Exercise regexp search with various magic settings. On each -- line the character on which the cursor is expected to land is deleted. feed('/^1') feed([[/a*b\{2}c\+/ex]]) feed([[/\Md\*e\{2}f\+/ex]]) - execute('set nomagic') + feed_command('set nomagic') feed([[/g\*h\{2}i\+/ex]]) feed([[/\mj*k\{2}l\+/ex]]) feed([[/\vm*n{2}o+/ex]]) feed([[/\V^aa$x]]) - execute('set magic') + feed_command('set magic') feed([[/\v(a)(b)\2\1\1/ex]]) feed([[/\V[ab]\(\[xy]\)\1x]]) @@ -57,7 +57,7 @@ local function run_test_with_regexpengine(regexpengine) -- Line b. Find word by change of word class. -- (The "<" character in this test step seemed to confuse our "feed" test -- helper, which is why we've resorted to "execute" here.) - execute([[/ち\<カヨ\>は]]) + feed_command([[/ち\<カヨ\>は]]) feed('x') -- Lines c-i. Test \%u, [\u], and friends. @@ -73,28 +73,28 @@ local function run_test_with_regexpengine(regexpengine) -- Line k. Test substitution with combining characters by executing register -- contents. - execute([[let @w=':%s#comb[i]nations#œ̄ṣ́m̥̄ᾱ̆́#g']]) - execute('@w') + feed_command([[let @w=':%s#comb[i]nations#œ̄ṣ́m̥̄ᾱ̆́#g']]) + feed_command('@w') -- Line l. Ex command ":s/ \?/ /g" should NOT split multi-byte characters -- into bytes (fixed by vim-7.3.192). - execute([[/^l]]) - execute([[s/ \?/ /g]]) + feed_command([[/^l]]) + feed_command([[s/ \?/ /g]]) -- Additional tests. Test matchstr() with multi-byte characters. feed('G') - execute([[put =matchstr(\"אבגד\", \".\", 0, 2)]]) -- ב - execute([[put =matchstr(\"אבגד\", \"..\", 0, 2)]]) -- בג - execute([[put =matchstr(\"אבגד\", \".\", 0, 0)]]) -- א - execute([[put =matchstr(\"אבגד\", \".\", 4, -1)]]) -- ג + feed_command([[put =matchstr(\"אבגד\", \".\", 0, 2)]]) -- ב + feed_command([[put =matchstr(\"אבגד\", \"..\", 0, 2)]]) -- בג + feed_command([[put =matchstr(\"אבגד\", \".\", 0, 0)]]) -- א + feed_command([[put =matchstr(\"אבגד\", \".\", 4, -1)]]) -- ג -- Test that a search with "/e" offset wraps around at the end of the buffer. - execute('new') - execute([[$put =['dog(a', 'cat('] ]]) + feed_command('new') + feed_command([[$put =['dog(a', 'cat('] ]]) feed('/(/e+') feed('"ayn') - execute('bd!') - execute([[$put ='']]) + feed_command('bd!') + feed_command([[$put ='']]) feed('G"ap') -- Assert buffer contents. diff --git a/test/functional/legacy/045_folding_spec.lua b/test/functional/legacy/045_folding_spec.lua index 5c8292c324..6ca1176aea 100644 --- a/test/functional/legacy/045_folding_spec.lua +++ b/test/functional/legacy/045_folding_spec.lua @@ -2,8 +2,8 @@ local Screen = require('test.functional.ui.screen') local helpers = require('test.functional.helpers')(after_each) -local feed, insert, execute, expect_any = - helpers.feed, helpers.insert, helpers.execute, helpers.expect_any +local feed, insert, feed_command, expect_any = + helpers.feed, helpers.insert, helpers.feed_command, helpers.expect_any describe('folding', function() local screen @@ -28,15 +28,15 @@ describe('folding', function() -- Basic test if a fold can be created, opened, moving to the end and -- closed. - execute('1') + feed_command('1') feed('zf2j') - execute('call append("$", "manual " . getline(foldclosed(".")))') + feed_command('call append("$", "manual " . getline(foldclosed(".")))') feed('zo') - execute('call append("$", foldclosed("."))') + feed_command('call append("$", foldclosed("."))') feed(']z') - execute('call append("$", getline("."))') + feed_command('call append("$", getline("."))') feed('zc') - execute('call append("$", getline(foldclosed(".")))') + feed_command('call append("$", getline(foldclosed(".")))') expect_any([[ manual 1 aa @@ -52,15 +52,15 @@ describe('folding', function() ee {{{ }}} ff }}} ]]) - execute('set fdm=marker fdl=1') - execute('2') - execute('call append("$", "line 2 foldlevel=" . foldlevel("."))') + feed_command('set fdm=marker fdl=1') + feed_command('2') + feed_command('call append("$", "line 2 foldlevel=" . foldlevel("."))') feed('[z') - execute('call append("$", foldlevel("."))') + feed_command('call append("$", foldlevel("."))') feed('jo{{ r{jj') -- writes '{{{' and moves 2 lines bot - execute('call append("$", foldlevel("."))') + feed_command('call append("$", foldlevel("."))') feed('kYpj') - execute('call append("$", foldlevel("."))') + feed_command('call append("$", foldlevel("."))') helpers.wait() screen:expect([[ @@ -80,15 +80,15 @@ describe('folding', function() it("foldmethod=indent", function() screen:try_resize(20, 8) - execute('set fdm=indent sw=2') + feed_command('set fdm=indent sw=2') insert([[ aa bb cc last ]]) - execute('call append("$", "foldlevel line3=" . foldlevel(3))') - execute('call append("$", foldlevel(2))') + feed_command('call append("$", "foldlevel line3=" . foldlevel(3))') + feed_command('call append("$", foldlevel(2))') feed('zR') helpers.wait() @@ -119,23 +119,23 @@ describe('folding', function() a jj b kk last]]) - execute('set fdm=syntax fdl=0') - execute('syn region Hup start="dd" end="ii" fold contains=Fd1,Fd2,Fd3') - execute('syn region Fd1 start="ee" end="ff" fold contained') - execute('syn region Fd2 start="gg" end="hh" fold contained') - execute('syn region Fd3 start="commentstart" end="commentend" fold contained') + feed_command('set fdm=syntax fdl=0') + feed_command('syn region Hup start="dd" end="ii" fold contains=Fd1,Fd2,Fd3') + feed_command('syn region Fd1 start="ee" end="ff" fold contained') + feed_command('syn region Fd2 start="gg" end="hh" fold contained') + feed_command('syn region Fd3 start="commentstart" end="commentend" fold contained') feed('Gzk') - execute('call append("$", "folding " . getline("."))') + feed_command('call append("$", "folding " . getline("."))') feed('k') - execute('call append("$", getline("."))') + feed_command('call append("$", getline("."))') feed('jAcommentstart Acommentend') - execute('set fdl=1') + feed_command('set fdl=1') feed('3j') - execute('call append("$", getline("."))') - execute('set fdl=0') + feed_command('call append("$", getline("."))') + feed_command('set fdl=0') feed('zOj') -- redraws screen - execute('call append("$", getline("."))') - execute('set fdl=0') + feed_command('call append("$", getline("."))') + feed_command('set fdl=0') expect_any([[ folding 9 ii 3 cc @@ -158,7 +158,7 @@ describe('folding', function() b kk last ]]) - execute([[ + feed_command([[ fun Flvl() let l = getline(v:lnum) if l =~ "bb$" @@ -173,15 +173,15 @@ describe('folding', function() return "=" endfun ]]) - execute('set fdm=expr fde=Flvl()') - execute('/bb$') - execute('call append("$", "expr " . foldlevel("."))') - execute('/hh$') - execute('call append("$", foldlevel("."))') - execute('/ii$') - execute('call append("$", foldlevel("."))') - execute('/kk$') - execute('call append("$", foldlevel("."))') + feed_command('set fdm=expr fde=Flvl()') + feed_command('/bb$') + feed_command('call append("$", "expr " . foldlevel("."))') + feed_command('/hh$') + feed_command('call append("$", foldlevel("."))') + feed_command('/ii$') + feed_command('call append("$", foldlevel("."))') + feed_command('/kk$') + feed_command('call append("$", foldlevel("."))') expect_any([[ expr 2 @@ -199,11 +199,11 @@ describe('folding', function() Test fdm=indent START line3 line4]]) - execute('set noai nosta ') - execute('set fdm=indent') - execute('1m1') + feed_command('set noai nosta ') + feed_command('set fdm=indent') + feed_command('1m1') feed('2jzc') - execute('m0') + feed_command('m0') feed('zR') expect_any([[ diff --git a/test/functional/legacy/051_highlight_spec.lua b/test/functional/legacy/051_highlight_spec.lua index d4d9b7d997..b98c1ac2d5 100644 --- a/test/functional/legacy/051_highlight_spec.lua +++ b/test/functional/legacy/051_highlight_spec.lua @@ -1,10 +1,9 @@ --- vim: set foldmethod=marker foldmarker=[[,]] : -- Tests for ":highlight". local Screen = require('test.functional.ui.screen') local helpers = require('test.functional.helpers')(after_each) local clear, feed = helpers.clear, helpers.feed -local execute, expect = helpers.execute, helpers.expect +local command, expect = helpers.command, helpers.expect local wait = helpers.wait if helpers.pending_win32(pending) then return end @@ -16,7 +15,7 @@ describe(':highlight', function() local screen = Screen.new(35, 10) screen:attach() -- Basic test if ":highlight" doesn't crash - execute('set more', 'highlight') + command('set more', 'highlight') -- FIXME(tarruda): We need to be sure the prompt is displayed before -- continuing, or risk a race condition where some of the following input -- is discarded resulting in test failure @@ -34,64 +33,64 @@ describe(':highlight', function() ]]) feed('q') wait() -- wait until we're back to normal - execute('hi Search') + command('hi Search') -- Test setting colors. -- Test clearing one color and all doesn't generate error or warning - execute('hi NewGroup cterm=italic ctermfg=DarkBlue ctermbg=Grey gui=NONE guifg=#00ff00 guibg=Cyan') - execute('hi Group2 cterm=NONE') - execute('hi Group3 cterm=bold') - execute('redir! @a') - execute('hi NewGroup') - execute('hi Group2') - execute('hi Group3') - execute('hi clear NewGroup') - execute('hi NewGroup') - execute('hi Group2') - execute('hi Group2 NONE') - execute('hi Group2') - execute('hi clear') - execute('hi Group3') - execute([[hi Crash cterm='asdf]]) - execute('redir END') + command('hi NewGroup cterm=italic ctermfg=DarkBlue ctermbg=Grey gui=NONE guifg=#00ff00 guibg=Cyan') + command('hi Group2 cterm=NONE') + command('hi Group3 cterm=bold') + command('redir! @a') + command('hi NewGroup') + command('hi Group2') + command('hi Group3') + command('hi clear NewGroup') + command('hi NewGroup') + command('hi Group2') + command('hi Group2 NONE') + command('hi Group2') + command('hi clear') + command('hi Group3') + command([[hi Crash cterm='asdf]]) + command('redir END') -- Filter ctermfg and ctermbg, the numbers depend on the terminal - execute('0put a') - execute([[%s/ctermfg=\d*/ctermfg=2/]]) - execute([[%s/ctermbg=\d*/ctermbg=3/]]) + command('0put a') + command([[%s/ctermfg=\d*/ctermfg=2/]]) + command([[%s/ctermbg=\d*/ctermbg=3/]]) -- Filter out possibly translated error message - execute('%s/E475: [^:]*:/E475:/') + command('%s/E475: [^:]*:/E475:/') -- Fix the fileformat - execute('set ff&') - execute('$d') + command('set ff&') + command('$d') -- Assert buffer contents. expect([[ - - + + NewGroup xxx cterm=italic ctermfg=2 ctermbg=3 guifg=#00ff00 guibg=Cyan - + Group2 xxx cleared - + Group3 xxx cterm=bold - - + + NewGroup xxx cleared - + Group2 xxx cleared - - + + Group2 xxx cleared - - + + Group3 xxx cleared - + E475: cterm='asdf]]) screen:detach() end) diff --git a/test/functional/legacy/055_list_and_dict_types_spec.lua b/test/functional/legacy/055_list_and_dict_types_spec.lua index dbe9e1bc7f..e84c415eb0 100644 --- a/test/functional/legacy/055_list_and_dict_types_spec.lua +++ b/test/functional/legacy/055_list_and_dict_types_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local feed, source = helpers.feed, helpers.source -local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect describe('list and dictionary types', function() before_each(clear) @@ -20,7 +20,7 @@ describe('list and dictionary types', function() $put =v:exception[:14] endtry]]) expect([[ - + [1, 'as''d', [1, 2, function('strlen')], {'a': 1}] {'a': 1} 1 @@ -38,7 +38,7 @@ describe('list and dictionary types', function() $put =string(l[0:8]) $put =string(l[8:-1])]]) expect([=[ - + [1, 'as''d', [1, 2, function('strlen')], {'a': 1}] ['as''d', [1, 2, function('strlen')], {'a': 1}] [1, 'as''d', [1, 2, function('strlen')]] @@ -84,7 +84,7 @@ describe('list and dictionary types', function() call filter(d, 'v:key =~ ''[ac391]''') $put =string(d)]]) expect([[ - + {'1': 'asd', 'b': [1, 2, function('strlen')], '-1': {'a': 1}}asd ['-1', '1', 'b'] ['asd', [1, 2, function('strlen')], {'a': 1}] @@ -134,7 +134,7 @@ describe('list and dictionary types', function() unlet d[-1] $put =string(d)]]) expect([[ - + [1, 'as''d', {'a': 1}] [4] {'1': 99, '3': 33}]]) @@ -142,42 +142,42 @@ describe('list and dictionary types', function() it("removing items out of range: silently skip items that don't exist", function() -- We can not use source() here as we want to ignore all errors. - execute('lang C') - execute('let l = [0, 1, 2, 3]') - execute('unlet l[2:1]') - execute('$put =string(l)') - execute('let l = [0, 1, 2, 3]') - execute('unlet l[2:2]') - execute('$put =string(l)') - execute('let l = [0, 1, 2, 3]') - execute('unlet l[2:3]') - execute('$put =string(l)') - execute('let l = [0, 1, 2, 3]') - execute('unlet l[2:4]') - execute('$put =string(l)') - execute('let l = [0, 1, 2, 3]') - execute('unlet l[2:5]') - execute('$put =string(l)') - execute('let l = [0, 1, 2, 3]') - execute('unlet l[-1:2]') - execute('$put =string(l)') - execute('let l = [0, 1, 2, 3]') - execute('unlet l[-2:2]') - execute('$put =string(l)') - execute('let l = [0, 1, 2, 3]') - execute('unlet l[-3:2]') - execute('$put =string(l)') - execute('let l = [0, 1, 2, 3]') - execute('unlet l[-4:2]') - execute('$put =string(l)') - execute('let l = [0, 1, 2, 3]') - execute('unlet l[-5:2]') - execute('$put =string(l)') - execute('let l = [0, 1, 2, 3]') - execute('unlet l[-6:2]') - execute('$put =string(l)') + feed_command('lang C') + feed_command('let l = [0, 1, 2, 3]') + feed_command('unlet l[2:1]') + feed_command('$put =string(l)') + feed_command('let l = [0, 1, 2, 3]') + feed_command('unlet l[2:2]') + feed_command('$put =string(l)') + feed_command('let l = [0, 1, 2, 3]') + feed_command('unlet l[2:3]') + feed_command('$put =string(l)') + feed_command('let l = [0, 1, 2, 3]') + feed_command('unlet l[2:4]') + feed_command('$put =string(l)') + feed_command('let l = [0, 1, 2, 3]') + feed_command('unlet l[2:5]') + feed_command('$put =string(l)') + feed_command('let l = [0, 1, 2, 3]') + feed_command('unlet l[-1:2]') + feed_command('$put =string(l)') + feed_command('let l = [0, 1, 2, 3]') + feed_command('unlet l[-2:2]') + feed_command('$put =string(l)') + feed_command('let l = [0, 1, 2, 3]') + feed_command('unlet l[-3:2]') + feed_command('$put =string(l)') + feed_command('let l = [0, 1, 2, 3]') + feed_command('unlet l[-4:2]') + feed_command('$put =string(l)') + feed_command('let l = [0, 1, 2, 3]') + feed_command('unlet l[-5:2]') + feed_command('$put =string(l)') + feed_command('let l = [0, 1, 2, 3]') + feed_command('unlet l[-6:2]') + feed_command('$put =string(l)') expect([=[ - + [0, 1, 2, 3] [0, 1, 3] [0, 1] @@ -208,7 +208,7 @@ describe('list and dictionary types', function() $put =v:exception[:14] endtry]]) expect([[ - + 2 3 Vim(let):E687: @@ -257,7 +257,7 @@ describe('list and dictionary types', function() " Must be almost empty now. $put =string(d)]]) expect([[ - + 3000 2900 2001 1600 1501 Vim(let):E716: 1500 NONE 2999 @@ -277,7 +277,7 @@ describe('list and dictionary types', function() let Fn = dict.func call Fn('xxx')]]) expect([[ - + len: 3 again: 3 xxx3]]) @@ -324,7 +324,7 @@ describe('list and dictionary types', function() let l3 = deepcopy(l2) $put ='same list: ' . (l3[1] is l3[2])]]) expect([[ - + Vim(let):E698: same list: 1]]) end) @@ -394,7 +394,7 @@ describe('list and dictionary types', function() endfor endfor]=]) expect([[ - + depth is 0 0000-000 ppppppp @@ -499,7 +499,7 @@ describe('list and dictionary types', function() endfor endfor]=]) expect([[ - + depth is 0 0000-000 ppppppp @@ -647,7 +647,7 @@ describe('list and dictionary types', function() $put =string(l)]]) expect([=[ - + Locks and commands or functions: No :unlet after lock on dict: Vim(unlet):E741: @@ -676,7 +676,7 @@ describe('list and dictionary types', function() end) it('locked variables (part 2)', function() - execute( + feed_command( 'let l = [1, 2, 3, 4]', 'lockvar! l', '$put =string(l)', @@ -691,7 +691,7 @@ describe('list and dictionary types', function() 'let l[1:2] = [0, 1]', '$put =string(l)') expect([=[ - + [1, 2, 3, 4] [1, 2, 3, 4] [1, 2, 3, 4] @@ -708,7 +708,7 @@ describe('list and dictionary types', function() $put ='exists g:footest#x:'.exists('g:footest#x') $put ='g:footest#x: '.g:footest#x]]) expect([[ - + locked g:footest#x:-1 exists g:footest#x:0 g:footest#x: 1]]) @@ -749,9 +749,9 @@ describe('list and dictionary types', function() $put ='caught ' . v:exception endtry endfunction]]) - execute('call Test(1, 2, [3, 4], {5: 6})') + feed_command('call Test(1, 2, [3, 4], {5: 6})') expect([=[ - + caught a:000 caught a:000[0] caught a:000[2] @@ -779,7 +779,7 @@ describe('list and dictionary types', function() $put =string(sort(copy(l), 'i')) $put =string(sort(copy(l)))]=]) expect([=[ - + ['-0', 'A11', 2, 'xaaa', 4, 'foo', 'foo6', 'foo', [0, 1, 2], 'x8', [0, 1, 2], 1.5] [1.5, [0, 1, 2], 'x8', [0, 1, 2], 'foo', 'foo6', 'foo', 4, 'xaaa', 2, 2, 'A11', '-0'] [1.5, [0, 1, 2], 'x8', [0, 1, 2], 'foo', 'foo6', 'foo', 4, 'xaaa', 2, 2, 'A11', '-0'] @@ -805,7 +805,7 @@ describe('list and dictionary types', function() $put =string(split('abc', '\zs')) $put =string(split('abc', '\zs', 1))]]) expect([=[ - + ['aa', 'bb'] ['aa', 'bb'] ['', 'aa', 'bb', ''] @@ -827,7 +827,7 @@ describe('list and dictionary types', function() $put =(l != deepcopy(l)) $put =(d != deepcopy(d))]]) expect([[ - + 1 1 0 @@ -845,7 +845,7 @@ describe('list and dictionary types', function() $put =(l == lcopy) $put =(dict4 == dict4copy)]]) expect([[ - + 1 1]]) end) @@ -856,7 +856,7 @@ describe('list and dictionary types', function() call extend(l, l) $put =string(l)]]) expect([=[ - + [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]]=]) end) @@ -866,7 +866,7 @@ describe('list and dictionary types', function() call extend(d, d) $put =string(d)]]) expect([[ - + {'a': {'b': 'B'}}]]) end) @@ -881,7 +881,7 @@ describe('list and dictionary types', function() endtry $put =string(d)]]) expect([[ - + Vim(call):E737: a {'a': {'b': 'B'}}]]) end) @@ -892,29 +892,29 @@ describe('list and dictionary types', function() let l[:] = [1, 2] $put =string(l)]]) expect([=[ - + [1, 2]]=]) end) it('vim patch 7.3.637', function() - execute('let a = "No error caught"') - execute('try') - execute(' foldopen') - execute('catch') - execute(" let a = matchstr(v:exception,'^[^ ]*')") - execute('endtry') + feed_command('let a = "No error caught"') + feed_command('try') + feed_command(' foldopen') + feed_command('catch') + feed_command(" let a = matchstr(v:exception,'^[^ ]*')") + feed_command('endtry') feed('o=a') - execute('lang C') - execute('redir => a') + feed_command('lang C') + feed_command('redir => a') -- The test failes if this is not in one line. - execute("try|foobar|catch|let a = matchstr(v:exception,'^[^ ]*')|endtry") - execute('redir END') + feed_command("try|foobar|catch|let a = matchstr(v:exception,'^[^ ]*')|endtry") + feed_command('redir END') feed('o=a') expect([[ - + Vim(foldopen):E490: - - + + Error detected while processing : E492: Not an editor command: foobar|catch|let a = matchstr(v:exception,'^[^ ]*')|endtry ]]) diff --git a/test/functional/legacy/057_sort_spec.lua b/test/functional/legacy/057_sort_spec.lua index 6984ad0de2..b3343d3af0 100644 --- a/test/functional/legacy/057_sort_spec.lua +++ b/test/functional/legacy/057_sort_spec.lua @@ -1,9 +1,11 @@ -- Tests for :sort command. local helpers = require('test.functional.helpers')(after_each) -local insert, execute, clear, expect, eq, eval, source = helpers.insert, - helpers.execute, helpers.clear, helpers.expect, helpers.eq, helpers.eval, - helpers.source + +local insert, command, clear, expect, eq, eval, wait = helpers.insert, + helpers.command, helpers.clear, helpers.expect, helpers.eq, helpers.eval, + helpers.wait +local exc_exec = helpers.exc_exec describe(':sort', function() local text = [[ @@ -26,9 +28,10 @@ describe(':sort', function() it('alphabetical', function() insert(text) - execute('sort') + wait() + command('sort') expect([[ - + 123b a a122 @@ -65,12 +68,13 @@ describe(':sort', function() b321 b321b ]]) - execute('sort n') + wait() + command('sort n') expect([[ abc ab a - + -24 x-22 0 @@ -89,9 +93,10 @@ describe(':sort', function() it('hexadecimal', function() insert(text) - execute('sort x') + wait() + command('sort x') expect([[ - + a ab abc @@ -110,9 +115,10 @@ describe(':sort', function() it('alphabetical, unique', function() insert(text) - execute('sort u') + wait() + command('sort u') expect([[ - + 123b a a122 @@ -130,7 +136,8 @@ describe(':sort', function() it('alphabetical, reverse', function() insert(text) - execute('sort!') + wait() + command('sort!') expect([[ c321d c123d @@ -151,7 +158,8 @@ describe(':sort', function() it('numerical, reverse', function() insert(text) - execute('sort! n') + wait() + command('sort! n') expect([[ b322b b321b @@ -164,7 +172,7 @@ describe(':sort', function() b123 a123 a122 - + a ab abc]]) @@ -172,7 +180,8 @@ describe(':sort', function() it('unique, reverse', function() insert(text) - execute('sort! u') + wait() + command('sort! u') expect([[ c321d c123d @@ -192,12 +201,13 @@ describe(':sort', function() it('octal', function() insert(text) - execute('sort o') + wait() + command('sort o') expect([[ abc ab a - + a122 a123 b123 @@ -213,7 +223,8 @@ describe(':sort', function() it('reverse, hexadecimal', function() insert(text) - execute('sort! x') + wait() + command('sort! x') expect([[ c321d c123d @@ -234,10 +245,11 @@ describe(':sort', function() it('alphabetical, skip first character', function() insert(text) - execute('sort/./') + wait() + command('sort/./') expect([[ a - + a122 a123 b123 @@ -255,11 +267,12 @@ describe(':sort', function() it('alphabetical, skip first 2 characters', function() insert(text) - execute('sort/../') + wait() + command('sort/../') expect([[ ab a - + a321 b321 b321 @@ -276,11 +289,12 @@ describe(':sort', function() it('alphabetical, unique, skip first 2 characters', function() insert(text) - execute('sort/../u') + wait() + command('sort/../u') expect([[ ab a - + a321 b321 b321b @@ -296,12 +310,13 @@ describe(':sort', function() it('numerical, skip first character', function() insert(text) - execute('sort/./n') + wait() + command('sort/./n') expect([[ abc ab a - + a122 a123 b123 @@ -317,9 +332,10 @@ describe(':sort', function() it('alphabetical, sort on first character', function() insert(text) - execute('sort/./r') + wait() + command('sort/./r') expect([[ - + 123b abc ab @@ -338,10 +354,11 @@ describe(':sort', function() it('alphabetical, sort on first 2 characters', function() insert(text) - execute('sort/../r') + wait() + command('sort/../r') expect([[ a - + 123b a123 a122 @@ -359,7 +376,8 @@ describe(':sort', function() it('numerical, sort on first character', function() insert(text) - execute('sort/./rn') + wait() + command('sort/./rn') expect([[ abc ab @@ -380,12 +398,13 @@ describe(':sort', function() it('alphabetical, skip past first digit', function() insert(text) - execute([[sort/\d/]]) + wait() + command([[sort/\d/]]) expect([[ abc ab a - + a321 b321 b321 @@ -401,12 +420,13 @@ describe(':sort', function() it('alphabetical, sort on first digit', function() insert(text) - execute([[sort/\d/r]]) + wait() + command([[sort/\d/r]]) expect([[ abc ab a - + a123 a122 b123 @@ -422,12 +442,13 @@ describe(':sort', function() it('numerical, skip past first digit', function() insert(text) - execute([[sort/\d/n]]) + wait() + command([[sort/\d/n]]) expect([[ abc ab a - + a321 b321 c321d @@ -443,12 +464,13 @@ describe(':sort', function() it('numerical, sort on first digit', function() insert(text) - execute([[sort/\d/rn]]) + wait() + command([[sort/\d/rn]]) expect([[ abc ab a - + a123 a122 b123 @@ -464,12 +486,13 @@ describe(':sort', function() it('alphabetical, skip past first 2 digits', function() insert(text) - execute([[sort/\d\d/]]) + wait() + command([[sort/\d\d/]]) expect([[ abc ab a - + a321 b321 b321 @@ -485,12 +508,13 @@ describe(':sort', function() it('numerical, skip past first 2 digits', function() insert(text) - execute([[sort/\d\d/n]]) + wait() + command([[sort/\d\d/n]]) expect([[ abc ab a - + a321 b321 c321d @@ -506,12 +530,13 @@ describe(':sort', function() it('hexadecimal, skip past first 2 digits', function() insert(text) - execute([[sort/\d\d/x]]) + wait() + command([[sort/\d\d/x]]) expect([[ abc ab a - + a321 b321 b321 @@ -527,12 +552,13 @@ describe(':sort', function() it('alpha, on first 2 digits', function() insert(text) - execute([[sort/\d\d/r]]) + wait() + command([[sort/\d\d/r]]) expect([[ abc ab a - + a123 a122 b123 @@ -548,12 +574,13 @@ describe(':sort', function() it('numeric, on first 2 digits', function() insert(text) - execute([[sort/\d\d/rn]]) + wait() + command([[sort/\d\d/rn]]) expect([[ abc ab a - + a123 a122 b123 @@ -569,12 +596,13 @@ describe(':sort', function() it('hexadecimal, on first 2 digits', function() insert(text) - execute([[sort/\d\d/rx]]) + wait() + command([[sort/\d\d/rx]]) expect([[ abc ab a - + a123 a122 b123 @@ -591,13 +619,7 @@ describe(':sort', function() it('fails with wrong arguments', function() insert(text) -- This should fail with "E474: Invalid argument". - source([[ - try - sort no - catch - let tmpvar = v:exception - endtry]]) - eq('Vim(sort):E474: Invalid argument', eval('tmpvar')) + eq('Vim(sort):E474: Invalid argument', exc_exec('sort no')) expect(text) end) @@ -617,7 +639,8 @@ describe(':sort', function() 0b100010 0b100100 0b100010]]) - execute([[sort b]]) + wait() + command([[sort b]]) expect([[ 0b000000 0b001000 @@ -651,7 +674,8 @@ describe(':sort', function() 0b101010 0b000000 b0b111000]]) - execute([[sort b]]) + wait() + command([[sort b]]) expect([[ 0b000000 a0b001000 @@ -677,7 +701,8 @@ describe(':sort', function() 1.15e-6 -1.1e3 -1.01e3]]) - execute([[sort f]]) + wait() + command([[sort f]]) expect([[ -1.1e3 -1.01e3 diff --git a/test/functional/legacy/059_utf8_spell_checking_spec.lua b/test/functional/legacy/059_utf8_spell_checking_spec.lua index 2fb8f3557d..120e469ab2 100644 --- a/test/functional/legacy/059_utf8_spell_checking_spec.lua +++ b/test/functional/legacy/059_utf8_spell_checking_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local feed, insert, source = helpers.feed, helpers.insert, helpers.source -local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect local write_file, call = helpers.write_file, helpers.call if helpers.pending_win32(pending) then return end @@ -15,44 +15,44 @@ end describe("spell checking with 'encoding' set to utf-8", function() setup(function() clear() - execute("syntax off") + feed_command("syntax off") write_latin1('Xtest1.aff',[[ SET ISO8859-1 TRY esianrtolcdugmphbyfvkwjkqxz-ëéèêïîäàâöüû'ESIANRTOLCDUGMPHBYFVKWJKQXZ - + FOL àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ LOW àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ UPP ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßÿ - + SOFOFROM abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ¿ SOFOTO ebctefghejklnnepkrstevvkesebctefghejklnnepkrstevvkeseeeeeeeceeeeeeeedneeeeeeeeeeepseeeeeeeeceeeeeeeedneeeeeeeeeeep? - + MIDWORD '- - + KEP = RAR ? BAD ! - + PFX I N 1 PFX I 0 in . - + PFX O Y 1 PFX O 0 out . - + SFX S Y 2 SFX S 0 s [^s] SFX S 0 es s - + SFX N N 3 SFX N 0 en [^n] SFX N 0 nen n SFX N 0 n . - + REP 3 REP g ch REP ch g REP svp s.v.p. - + MAP 9 MAP aàáâãäå MAP eèéêë @@ -79,39 +79,39 @@ describe("spell checking with 'encoding' set to utf-8", function() ]]) write_latin1('Xtest2.aff', [[ SET ISO8859-1 - + FOL àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ LOW àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ UPP ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßÿ - + PFXPOSTPONE - + MIDWORD '- - + KEP = RAR ? BAD ! - + PFX I N 1 PFX I 0 in . - + PFX O Y 1 PFX O 0 out [a-z] - + SFX S Y 2 SFX S 0 s [^s] SFX S 0 es s - + SFX N N 3 SFX N 0 en [^n] SFX N 0 nen n SFX N 0 n . - + REP 3 REP g ch REP ch g REP svp s.v.p. - + MAP 9 MAP aàáâãäå MAP eèéêë @@ -125,7 +125,7 @@ describe("spell checking with 'encoding' set to utf-8", function() ]]) write_latin1('Xtest3.aff', [[ SET ISO8859-1 - + COMPOUNDMIN 3 COMPOUNDRULE m* NEEDCOMPOUND x @@ -139,21 +139,21 @@ describe("spell checking with 'encoding' set to utf-8", function() ]]) write_latin1('Xtest4.aff', [[ SET ISO8859-1 - + FOL àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ LOW àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ UPP ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßÿ - + COMPOUNDRULE m+ COMPOUNDRULE sm*e COMPOUNDRULE sm+ COMPOUNDMIN 3 COMPOUNDWORDMAX 3 COMPOUNDFORBIDFLAG t - + COMPOUNDSYLMAX 5 SYLLABLE aáeéiíoóöõuúüûy/aa/au/ea/ee/ei/ie/oa/oe/oo/ou/uu/ui - + MAP 9 MAP aàáâãäå MAP eèéêë @@ -164,23 +164,23 @@ describe("spell checking with 'encoding' set to utf-8", function() MAP cç MAP yÿý MAP sß - + NEEDAFFIX x - + PFXPOSTPONE - + MIDWORD '- - + SFX q N 1 SFX q 0 -ok . - + SFX a Y 2 SFX a 0 s . SFX a 0 ize/t . - + PFX p N 1 PFX p 0 pre . - + PFX P N 1 PFX P 0 nou . ]]) @@ -196,28 +196,28 @@ describe("spell checking with 'encoding' set to utf-8", function() ]]) write_latin1('Xtest5.aff', [[ SET ISO8859-1 - + FLAG long - + NEEDAFFIX !! - + COMPOUNDRULE ssmm*ee - + NEEDCOMPOUND xx COMPOUNDPERMITFLAG pp - + SFX 13 Y 1 SFX 13 0 bork . - + SFX a1 Y 1 SFX a1 0 a1 . - + SFX aé Y 1 SFX aé 0 aé . - + PFX zz Y 1 PFX zz 0 pre/pp . - + PFX yy Y 1 PFX yy 0 nou . ]]) @@ -231,26 +231,26 @@ describe("spell checking with 'encoding' set to utf-8", function() ]]) write_latin1('Xtest6.aff', [[ SET ISO8859-1 - + FLAG caplong - + NEEDAFFIX A! - + COMPOUNDRULE sMm*Ee - + NEEDCOMPOUND Xx - + COMPOUNDPERMITFLAG p - + SFX N3 Y 1 SFX N3 0 bork . - + SFX A1 Y 1 SFX A1 0 a1 . - + SFX Aé Y 1 SFX Aé 0 aé . - + PFX Zz Y 1 PFX Zz 0 pre/p . ]]) @@ -264,29 +264,29 @@ describe("spell checking with 'encoding' set to utf-8", function() ]]) write_latin1('Xtest7.aff', [[ SET ISO8859-1 - + FOL àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ LOW àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ UPP ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßÿ - + FLAG num - + NEEDAFFIX 9999 - + COMPOUNDRULE 2,77*123 - + NEEDCOMPOUND 1 COMPOUNDPERMITFLAG 432 - + SFX 61003 Y 1 SFX 61003 0 meat . - + SFX 391 Y 1 SFX 391 0 a1 . - + SFX 111 Y 1 SFX 111 0 aé . - + PFX 17 Y 1 PFX 17 0 pre/432 . ]]) @@ -300,7 +300,7 @@ describe("spell checking with 'encoding' set to utf-8", function() ]]) write_latin1('Xtest8.aff', [[ SET ISO8859-1 - + NOSPLITSUGS ]]) write_latin1('Xtest8.dic', [[ @@ -319,37 +319,37 @@ describe("spell checking with 'encoding' set to utf-8", function() write_latin1('Xtest-sal.aff', [[ SET ISO8859-1 TRY esianrtolcdugmphbyfvkwjkqxz-ëéèêïîäàâöüû'ESIANRTOLCDUGMPHBYFVKWJKQXZ - + FOL àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ LOW àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ UPP ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßÿ - + MIDWORD '- - + KEP = RAR ? BAD ! - + PFX I N 1 PFX I 0 in . - + PFX O Y 1 PFX O 0 out . - + SFX S Y 2 SFX S 0 s [^s] SFX S 0 es s - + SFX N N 3 SFX N 0 en [^n] SFX N 0 nen n SFX N 0 n . - + REP 3 REP g ch REP ch g REP svp s.v.p. - + MAP 9 MAP aàáâãäå MAP eèéêë @@ -360,7 +360,7 @@ describe("spell checking with 'encoding' set to utf-8", function() MAP cç MAP yÿý MAP sß - + SAL AH(AEIOUY)-^ *H SAL AR(AEIOUY)-^ *R SAL A(HR)^ * @@ -550,60 +550,60 @@ describe("spell checking with 'encoding' set to utf-8", function() 1good: wrong OK puts. Test the end bad: inputs comment ok Ok. test déôl end the badend - + test2: elequint test elekwint test elekwent asdf ]]) test_one(1, 1) - execute([[$put =soundfold('goobledygoook')]]) - execute([[$put =soundfold('kóopërÿnôven')]]) - execute([[$put =soundfold('oeverloos gezwets edale')]]) + feed_command([[$put =soundfold('goobledygoook')]]) + feed_command([[$put =soundfold('kóopërÿnôven')]]) + feed_command([[$put =soundfold('oeverloos gezwets edale')]]) -- And now with SAL instead of SOFO items; test automatic reloading. os.execute('cp -f Xtest-sal.aff Xtest.aff') - execute('mkspell! Xtest Xtest') - execute([[$put =soundfold('goobledygoook')]]) - execute([[$put =soundfold('kóopërÿnôven')]]) - execute([[$put =soundfold('oeverloos gezwets edale')]]) + feed_command('mkspell! Xtest Xtest') + feed_command([[$put =soundfold('goobledygoook')]]) + feed_command([[$put =soundfold('kóopërÿnôven')]]) + feed_command([[$put =soundfold('oeverloos gezwets edale')]]) -- Also use an addition file. - execute('mkspell! Xtest.utf-8.add.spl Xtest.utf-8.add') - execute('set spellfile=Xtest.utf-8.add') - execute('/^test2:') + feed_command('mkspell! Xtest.utf-8.add.spl Xtest.utf-8.add') + feed_command('set spellfile=Xtest.utf-8.add') + feed_command('/^test2:') feed(']s') - execute('let [str, a] = spellbadword()') - execute('$put =str') - execute('set spl=Xtest_us.utf-8.spl') - execute('/^test2:') + feed_command('let [str, a] = spellbadword()') + feed_command('$put =str') + feed_command('set spl=Xtest_us.utf-8.spl') + feed_command('/^test2:') feed(']smm') - execute('let [str, a] = spellbadword()') - execute('$put =str') + feed_command('let [str, a] = spellbadword()') + feed_command('$put =str') feed('`m]s') - execute('let [str, a] = spellbadword()') - execute('$put =str') - execute('set spl=Xtest_gb.utf-8.spl') - execute('/^test2:') + feed_command('let [str, a] = spellbadword()') + feed_command('$put =str') + feed_command('set spl=Xtest_gb.utf-8.spl') + feed_command('/^test2:') feed(']smm') - execute('let [str, a] = spellbadword()') - execute('$put =str') + feed_command('let [str, a] = spellbadword()') + feed_command('$put =str') feed('`m]s') - execute('let [str, a] = spellbadword()') - execute('$put =str') - execute('set spl=Xtest_nz.utf-8.spl') - execute('/^test2:') + feed_command('let [str, a] = spellbadword()') + feed_command('$put =str') + feed_command('set spl=Xtest_nz.utf-8.spl') + feed_command('/^test2:') feed(']smm') - execute('let [str, a] = spellbadword()') - execute('$put =str') + feed_command('let [str, a] = spellbadword()') + feed_command('$put =str') feed('`m]s') - execute('let [str, a] = spellbadword()') - execute('$put =str') - execute('set spl=Xtest_ca.utf-8.spl') - execute('/^test2:') + feed_command('let [str, a] = spellbadword()') + feed_command('$put =str') + feed_command('set spl=Xtest_ca.utf-8.spl') + feed_command('/^test2:') feed(']smm') - execute('let [str, a] = spellbadword()') - execute('$put =str') + feed_command('let [str, a] = spellbadword()') + feed_command('$put =str') feed('`m]s') - execute('let [str, a] = spellbadword()') - execute('$put =str') - execute('1,/^test 1-1/-1d') + feed_command('let [str, a] = spellbadword()') + feed_command('$put =str') + feed_command('1,/^test 1-1/-1d') expect([[ test 1-1 # file: Xtest.utf-8.spl @@ -667,7 +667,7 @@ describe("spell checking with 'encoding' set to utf-8", function() ]]) -- Postponed prefixes. test_one(2, 1) - execute('1,/^test 2-1/-1d') + feed_command('1,/^test 2-1/-1d') expect([=[ test 2-1 # file: Xtest.utf-8.spl @@ -711,13 +711,13 @@ describe("spell checking with 'encoding' set to utf-8", function() it('part 3-3', function() insert([[ Test rules for compounding. - + 3good: foo mï foobar foofoobar barfoo barbarfoo bad: bar la foomï barmï mïfoo mïbar mïmï lala mïla lamï foola labar badend ]]) test_one(3, 3) - execute('1,/^test 3-3/-1d') + feed_command('1,/^test 3-3/-1d') expect([=[ test 3-3 # file: Xtest.utf-8.spl @@ -755,7 +755,7 @@ describe("spell checking with 'encoding' set to utf-8", function() it('part 4-4', function() insert([[ Tests for compounding. - + 4good: word util bork prebork start end wordutil wordutils pro-ok bork borkbork borkborkbork borkborkborkbork borkborkborkborkbork tomato tomatotomato startend startword startwordword startwordend @@ -770,7 +770,7 @@ describe("spell checking with 'encoding' set to utf-8", function() badend ]]) test_one(4, 4) - execute('1,/^test 4-4/-1d') + feed_command('1,/^test 4-4/-1d') expect([=[ test 4-4 # file: Xtest.utf-8.spl @@ -823,7 +823,7 @@ describe("spell checking with 'encoding' set to utf-8", function() it('part 5-5', function() insert([[ Test affix flags with two characters - + 5good: fooa1 fooaé bar prebar barbork prebarbork startprebar start end startend startmiddleend nouend bad: foo fooa2 prabar probarbirk middle startmiddle middleend endstart @@ -831,7 +831,7 @@ describe("spell checking with 'encoding' set to utf-8", function() badend ]]) test_one(5, 5) - execute('1,/^test 5-5/-1d') + feed_command('1,/^test 5-5/-1d') expect([=[ test 5-5 # file: Xtest.utf-8.spl @@ -878,7 +878,7 @@ describe("spell checking with 'encoding' set to utf-8", function() badend ]]) test_one(6, 6) - execute('1,/^test 6-6/-1d') + feed_command('1,/^test 6-6/-1d') expect([=[ test 6-6 # file: Xtest.utf-8.spl @@ -924,7 +924,7 @@ describe("spell checking with 'encoding' set to utf-8", function() -- Compound words. test_one(7, 7) -- Assert buffer contents. - execute('1,/^test 7-7/-1d') + feed_command('1,/^test 7-7/-1d') expect([=[ test 7-7 # file: Xtest.utf-8.spl @@ -968,7 +968,7 @@ describe("spell checking with 'encoding' set to utf-8", function() -- NOSPLITSUGS test_one(8, 8) -- Assert buffer contents. - execute('1,/^test 8-8/-1d') + feed_command('1,/^test 8-8/-1d') expect([=[ test 8-8 # file: Xtest.utf-8.spl @@ -992,7 +992,7 @@ describe("spell checking with 'encoding' set to utf-8", function() -- NOSPLITSUGS test_one(9, 9) -- Assert buffer contents. - execute('1,/^test 9-9/-1d') + feed_command('1,/^test 9-9/-1d') expect([=[ test 9-9 # file: Xtest.utf-8.spl diff --git a/test/functional/legacy/061_undo_tree_spec.lua b/test/functional/legacy/061_undo_tree_spec.lua index aeb2001d11..1a8ef067d0 100644 --- a/test/functional/legacy/061_undo_tree_spec.lua +++ b/test/functional/legacy/061_undo_tree_spec.lua @@ -1,8 +1,8 @@ -- Tests for undo tree and :earlier and :later. local helpers = require('test.functional.helpers')(after_each) +local feed_command = helpers.feed_command local write_file = helpers.write_file -local execute = helpers.execute local command = helpers.command local source = helpers.source local expect = helpers.expect @@ -44,7 +44,7 @@ describe('undo tree:', function() -- function to allow multiple attempts. local function test_earlier_later() clear() - execute('e Xtest') + feed_command('e Xtest') -- Assert that no undo history is present. eq({}, eval('undotree().entries')) -- Delete three characters and undo. @@ -88,13 +88,13 @@ describe('undo tree:', function() feed('Ab') feed('Ac') expect_line('123456abc') - execute('earlier 1s') + feed_command('earlier 1s') expect_line('123456') - execute('earlier 3s') + feed_command('earlier 3s') expect_line('123456789') - execute('later 1s') + feed_command('later 1s') expect_line('123456') - execute('later 1h') + feed_command('later 1h') expect_line('123456abc') end @@ -103,28 +103,28 @@ describe('undo tree:', function() it('file-write specifications', function() feed('ione one one') - execute('w Xtest') + feed_command('w Xtest') feed('otwo') feed('otwo') - execute('w') + feed_command('w') feed('othree') - execute('earlier 1f') + feed_command('earlier 1f') expect([[ one one one two two]]) - execute('earlier 1f') + feed_command('earlier 1f') expect('one one one') - execute('earlier 1f') + feed_command('earlier 1f') expect_empty_buffer() - execute('later 1f') + feed_command('later 1f') expect('one one one') - execute('later 1f') + feed_command('later 1f') expect([[ one one one two two]]) - execute('later 1f') + feed_command('later 1f') expect([[ one one one two @@ -193,20 +193,20 @@ describe('undo tree:', function() feed('ob') feed([[o1a2=setline('.','1234')]]) expect([[ - + a b 12034]]) feed('uu') expect([[ - + a b 1]]) feed('oc') feed([[o1a2=setline('.','1234')]]) expect([[ - + a b 1 @@ -214,16 +214,16 @@ describe('undo tree:', function() 12034]]) feed('u') expect([[ - + a b 1 c 12]]) feed('od') - execute('so! Xtest.source') + feed_command('so! Xtest.source') expect([[ - + a b 1 @@ -233,7 +233,7 @@ describe('undo tree:', function() 12123]]) feed('u') expect([[ - + a b 1 @@ -246,7 +246,7 @@ describe('undo tree:', function() -- interactive use (even in Vim; see ":help :undojoin"): feed(normal_commands) expect([[ - + a b 1 @@ -256,7 +256,7 @@ describe('undo tree:', function() 12123]]) feed('u') expect([[ - + a b 1 diff --git a/test/functional/legacy/062_tab_pages_spec.lua b/test/functional/legacy/062_tab_pages_spec.lua index 71a0a77354..9435913b53 100644 --- a/test/functional/legacy/062_tab_pages_spec.lua +++ b/test/functional/legacy/062_tab_pages_spec.lua @@ -1,17 +1,17 @@ -- Tests for tab pages local helpers = require('test.functional.helpers')(after_each) -local feed, insert, source, clear, execute, expect, eval, eq = +local feed, insert, source, clear, command, expect, eval, eq = helpers.feed, helpers.insert, helpers.source, helpers.clear, - helpers.execute, helpers.expect, helpers.eval, helpers.eq + helpers.command, helpers.expect, helpers.eval, helpers.eq describe('tab pages', function() before_each(clear) it('can be opened and closed', function() - execute('tabnew') + command('tabnew') eq(2, eval('tabpagenr()')) - execute('quit') + command('quit') eq(1, eval('tabpagenr()')) end) @@ -25,7 +25,7 @@ describe('tab pages', function() tabrewind ]]) eq('this is tab page 1', eval("getline('$')")) - execute('tablast') + command('tablast') eq('this is tab page 4', eval("getline('$')")) end) @@ -44,7 +44,7 @@ describe('tab pages', function() eq(100, eval('gettabvar(2, "val_num")')) eq('SetTabVar test', eval('gettabvar(2, "val_str")')) eq({'red', 'blue', 'green'}, eval('gettabvar(2, "val_list")')) - execute('tabnext 2') + command('tabnext 2') eq(100, eval('t:val_num')) eq('SetTabVar test', eval('t:val_str')) eq({'red', 'blue', 'green'}, eval('t:val_list')) @@ -52,8 +52,8 @@ describe('tab pages', function() it('work together with the drop feature and loaded buffers', function() -- Test for ":tab drop exist-file" to keep current window. - execute('sp test1') - execute('tab drop test1') + command('sp test1') + command('tab drop test1') eq(1, eval('tabpagenr("$")')) eq(2, eval('winnr("$")')) eq(1, eval('winnr()')) @@ -61,8 +61,8 @@ describe('tab pages', function() it('work together with the drop feature and new files', function() -- Test for ":tab drop new-file" to keep current window of tabpage 1. - execute('split') - execute('tab drop newfile') + command('split') + command('tab drop newfile') eq(2, eval('tabpagenr("$")')) eq(2, eval('tabpagewinnr(1, "$")')) eq(1, eval('tabpagewinnr(1)')) @@ -71,52 +71,52 @@ describe('tab pages', function() it('work together with the drop feature and multi loaded buffers', function() -- Test for ":tab drop multi-opend-file" to keep current tabpage and -- window. - execute('new test1') - execute('tabnew') - execute('new test1') - execute('tab drop test1') + command('new test1') + command('tabnew') + command('new test1') + command('tab drop test1') eq(2, eval('tabpagenr()')) eq(2, eval('tabpagewinnr(2, "$")')) eq(1, eval('tabpagewinnr(2)')) end) it('can be navigated with :tabmove', function() - execute('lang C') - execute('for i in range(9) | tabnew | endfor') + command('lang C') + command('for i in range(9) | tabnew | endfor') feed('1gt') eq(1, eval('tabpagenr()')) - execute('tabmove 5') + command('tabmove 5') eq(5, eval('tabpagenr()')) - execute('.tabmove') + command('.tabmove') eq(5, eval('tabpagenr()')) - execute('tabmove -') + command('tabmove -') eq(4, eval('tabpagenr()')) - execute('tabmove +') + command('tabmove +') eq(5, eval('tabpagenr()')) - execute('tabmove -2') + command('tabmove -2') eq(3, eval('tabpagenr()')) - execute('tabmove +4') + command('tabmove +4') eq(7, eval('tabpagenr()')) - execute('tabmove') + command('tabmove') eq(10, eval('tabpagenr()')) - execute('0tabmove') + command('0tabmove') eq(1, eval('tabpagenr()')) - execute('$tabmove') + command('$tabmove') eq(10, eval('tabpagenr()')) - execute('tabmove 0') + command('tabmove 0') eq(1, eval('tabpagenr()')) - execute('tabmove $') + command('tabmove $') eq(10, eval('tabpagenr()')) - execute('3tabmove') + command('3tabmove') eq(4, eval('tabpagenr()')) - execute('7tabmove 5') + command('7tabmove 5') eq(5, eval('tabpagenr()')) - execute('let a="No error caught."') - execute('try') - execute('tabmove foo') - execute('catch E474') - execute('let a="E474 caught."') - execute('endtry') + command('let a="No error caught."') + command('try') + command('tabmove foo') + command('catch E474') + command('let a="E474 caught."') + command('endtry') eq('E474 caught.', eval('a')) end) diff --git a/test/functional/legacy/063_match_and_matchadd_spec.lua b/test/functional/legacy/063_match_and_matchadd_spec.lua index 5818bb6b3a..34163d5a55 100644 --- a/test/functional/legacy/063_match_and_matchadd_spec.lua +++ b/test/functional/legacy/063_match_and_matchadd_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local feed, insert = helpers.feed, helpers.insert -local eval, clear, execute = helpers.eval, helpers.clear, helpers.execute +local eval, clear, command = helpers.eval, helpers.clear, helpers.command local eq, neq = helpers.eq, helpers.neq describe('063: Test for ":match", "matchadd()" and related functions', function() @@ -15,12 +15,12 @@ describe('063: Test for ":match", "matchadd()" and related functions', function( -- Check that "matcharg()" returns the correct group and pattern if a match -- is defined. - execute("highlight MyGroup1 term=bold ctermbg=red guibg=red") - execute("highlight MyGroup2 term=italic ctermbg=green guibg=green") - execute("highlight MyGroup3 term=underline ctermbg=blue guibg=blue") - execute("match MyGroup1 /TODO/") - execute("2match MyGroup2 /FIXME/") - execute("3match MyGroup3 /XXX/") + command("highlight MyGroup1 term=bold ctermbg=red guibg=red") + command("highlight MyGroup2 term=italic ctermbg=green guibg=green") + command("highlight MyGroup3 term=underline ctermbg=blue guibg=blue") + command("match MyGroup1 /TODO/") + command("2match MyGroup2 /FIXME/") + command("3match MyGroup3 /XXX/") eq({'MyGroup1', 'TODO'}, eval('matcharg(1)')) eq({'MyGroup2', 'FIXME'}, eval('matcharg(2)')) eq({'MyGroup3', 'XXX'}, eval('matcharg(3)')) @@ -31,18 +31,18 @@ describe('063: Test for ":match", "matchadd()" and related functions', function( eq({}, eval('matcharg(4)')) -- Check that "matcharg()" returns ['', ''] if a match is not defined. - execute("match") - execute("2match") - execute("3match") + command("match") + command("2match") + command("3match") eq({'', ''}, eval('matcharg(1)')) eq({'', ''}, eval('matcharg(2)')) eq({'', ''}, eval('matcharg(3)')) -- Check that "matchadd()" and "getmatches()" agree on added matches and -- that default values apply. - execute("let m1 = matchadd('MyGroup1', 'TODO')") - execute("let m2 = matchadd('MyGroup2', 'FIXME', 42)") - execute("let m3 = matchadd('MyGroup3', 'XXX', 60, 17)") + command("let m1 = matchadd('MyGroup1', 'TODO')") + command("let m2 = matchadd('MyGroup2', 'FIXME', 42)") + command("let m3 = matchadd('MyGroup3', 'XXX', 60, 17)") eq({{group = 'MyGroup1', pattern = 'TODO', priority = 10, id = 4}, {group = 'MyGroup2', pattern = 'FIXME', priority = 42, id = 5}, {group = 'MyGroup3', pattern = 'XXX', priority = 60, id = 17}}, @@ -50,55 +50,55 @@ describe('063: Test for ":match", "matchadd()" and related functions', function( -- Check that "matchdelete()" deletes the matches defined in the previous -- test correctly. - execute("call matchdelete(m1)") - execute("call matchdelete(m2)") - execute("call matchdelete(m3)") + command("call matchdelete(m1)") + command("call matchdelete(m2)") + command("call matchdelete(m3)") eq({}, eval('getmatches()')) --- Check that "matchdelete()" returns 0 if successful and otherwise -1. - execute("let m = matchadd('MyGroup1', 'TODO')") + command("let m = matchadd('MyGroup1', 'TODO')") eq(0, eval('matchdelete(m)')) -- matchdelete throws error and returns -1 on failure neq(true, pcall(function() eval('matchdelete(42)') end)) - execute("let r2 = matchdelete(42)") + command("let r2 = matchdelete(42)") eq(-1, eval('r2')) -- Check that "clearmatches()" clears all matches defined by ":match" and -- "matchadd()". - execute("let m1 = matchadd('MyGroup1', 'TODO')") - execute("let m2 = matchadd('MyGroup2', 'FIXME', 42)") - execute("let m3 = matchadd('MyGroup3', 'XXX', 60, 17)") - execute("match MyGroup1 /COFFEE/") - execute("2match MyGroup2 /HUMPPA/") - execute("3match MyGroup3 /VIM/") - execute("call clearmatches()") + command("let m1 = matchadd('MyGroup1', 'TODO')") + command("let m2 = matchadd('MyGroup2', 'FIXME', 42)") + command("let m3 = matchadd('MyGroup3', 'XXX', 60, 17)") + command("match MyGroup1 /COFFEE/") + command("2match MyGroup2 /HUMPPA/") + command("3match MyGroup3 /VIM/") + command("call clearmatches()") eq({}, eval('getmatches()')) -- Check that "setmatches()" restores a list of matches saved by -- "getmatches()" without changes. (Matches with equal priority must also -- remain in the same order.) - execute("let m1 = matchadd('MyGroup1', 'TODO')") - execute("let m2 = matchadd('MyGroup2', 'FIXME', 42)") - execute("let m3 = matchadd('MyGroup3', 'XXX', 60, 17)") - execute("match MyGroup1 /COFFEE/") - execute("2match MyGroup2 /HUMPPA/") - execute("3match MyGroup3 /VIM/") - execute("let ml = getmatches()") + command("let m1 = matchadd('MyGroup1', 'TODO')") + command("let m2 = matchadd('MyGroup2', 'FIXME', 42)") + command("let m3 = matchadd('MyGroup3', 'XXX', 60, 17)") + command("match MyGroup1 /COFFEE/") + command("2match MyGroup2 /HUMPPA/") + command("3match MyGroup3 /VIM/") + command("let ml = getmatches()") local ml = eval("ml") - execute("call clearmatches()") - execute("call setmatches(ml)") + command("call clearmatches()") + command("call setmatches(ml)") eq(ml, eval('getmatches()')) -- Check that "setmatches()" can correctly restore the matches from matchaddpos() - execute("call clearmatches()") - execute("call setmatches(ml)") + command("call clearmatches()") + command("call setmatches(ml)") eq(ml, eval('getmatches()')) -- Check that "setmatches()" will not add two matches with the same ID. The -- expected behaviour (for now) is to add the first match but not the -- second and to return -1. - execute("let r1 = setmatches([{'group': 'MyGroup1', 'pattern': 'TODO', 'priority': 10, 'id': 1}, {'group': 'MyGroup2', 'pattern': 'FIXME', 'priority': 10, 'id': 1}])") + command("let r1 = setmatches([{'group': 'MyGroup1', 'pattern': 'TODO', 'priority': 10, 'id': 1}, {'group': 'MyGroup2', 'pattern': 'FIXME', 'priority': 10, 'id': 1}])") feed("") eq(-1, eval("r1")) eq({{group = 'MyGroup1', pattern = 'TODO', priority = 10, id = 1}}, eval('getmatches()')) @@ -108,18 +108,18 @@ describe('063: Test for ":match", "matchadd()" and related functions', function( -- return values.) eq(0,eval("setmatches([])")) eq(0,eval("setmatches([{'group': 'MyGroup1', 'pattern': 'TODO', 'priority': 10, 'id': 1}])")) - execute("call clearmatches()") - execute("let rf1 = setmatches(0)") + command("call clearmatches()") + command("let rf1 = setmatches(0)") eq(-1, eval('rf1')) - execute("let rf2 = setmatches([0])") + command("let rf2 = setmatches([0])") eq(-1, eval('rf2')) - execute("let rf3 = setmatches([{'wrong key': 'wrong value'}])") + command("let rf3 = setmatches([{'wrong key': 'wrong value'}])") feed("") eq(-1, eval('rf3')) -- Check that "matchaddpos()" positions matches correctly insert('abcdefghijklmnopq') - execute("call matchaddpos('MyGroup1', [[1, 5], [1, 8, 3]], 10, 3)") + command("call matchaddpos('MyGroup1', [[1, 5], [1, 8, 3]], 10, 3)") screen:expect([[ abcd{1:e}fg{1:hij}klmnop^q | ~ | @@ -128,9 +128,9 @@ describe('063: Test for ":match", "matchadd()" and related functions', function( | ]], {[1] = {background = Screen.colors.Red}}, {{bold = true, foreground = Screen.colors.Blue}}) - execute("call clearmatches()") - execute("call setline(1, 'abcdΣabcdef')") - execute("call matchaddpos('MyGroup1', [[1, 4, 2], [1, 9, 2]])") + command("call clearmatches()") + command("call setline(1, 'abcdΣabcdef')") + command("call matchaddpos('MyGroup1', [[1, 4, 2], [1, 9, 2]])") screen:expect([[ abc{1:dΣ}ab{1:cd}e^f | ~ | diff --git a/test/functional/legacy/065_float_and_logic_operators_spec.lua b/test/functional/legacy/065_float_and_logic_operators_spec.lua index d12ea502f3..615dff1f3b 100644 --- a/test/functional/legacy/065_float_and_logic_operators_spec.lua +++ b/test/functional/legacy/065_float_and_logic_operators_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local insert, source = helpers.insert, helpers.source -local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local clear, command, expect = helpers.clear, helpers.command, helpers.expect describe('floating point and logical operators', function() setup(clear) @@ -49,7 +49,7 @@ describe('floating point and logical operators', function() -- The test will throw an error if this line is included in a source() -- call. The vim expression throws a exception "E745: Using a List as a -- Number" which is fatal in a source() call but not in a execute() call. - execute([[$put =printf('%d', abs([1, 2, 3]))]]) + command([[$put =printf('%d', abs([1, 2, 3]))]]) source([[ $put =printf('%g', abs(14.56)) @@ -104,7 +104,7 @@ describe('floating point and logical operators', function() -- This line can not be included in a source() call. It throws a "E805: -- Using a Float as a Number". Also compare comment above. - execute('$put =invert(1.0)') + command('$put =invert(1.0)') -- Assert buffer contents. expect([=[ diff --git a/test/functional/legacy/066_visual_block_tab_spec.lua b/test/functional/legacy/066_visual_block_tab_spec.lua index 72fa7d881b..7c4984362f 100644 --- a/test/functional/legacy/066_visual_block_tab_spec.lua +++ b/test/functional/legacy/066_visual_block_tab_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local execute, expect = helpers.execute, helpers.expect +local feed_command, expect = helpers.feed_command, helpers.expect describe('visual block shift and tab characters', function() setup(clear) @@ -24,23 +24,23 @@ describe('visual block shift and tab characters', function() feed('gg') feed([[fe4jRugvr1:','>yank A]]) - execute('/^abcdefgh') + feed_command('/^abcdefgh') feed('4jI j11|D') feed('j7|a ') feed('j7|a ') feed('j7|a 4k13|4j') - execute('$-5,$yank A') - execute([[$-4,$s/\s\+//g]]) + feed_command('$-5,$yank A') + feed_command([[$-4,$s/\s\+//g]]) feed('4kI j') feed('j7|a ') feed('j7|a ') feed('j7|a 4k13|4j3') - execute('$-4,$yank A') + feed_command('$-4,$yank A') -- Put @a and clean empty lines - execute('%d') - execute('0put a') - execute('$d') + feed_command('%d') + feed_command('0put a') + feed_command('$d') -- Assert buffer contents. expect([[ diff --git a/test/functional/legacy/067_augroup_exists_spec.lua b/test/functional/legacy/067_augroup_exists_spec.lua index 8f6b881ed8..a0d8d3940b 100644 --- a/test/functional/legacy/067_augroup_exists_spec.lua +++ b/test/functional/legacy/067_augroup_exists_spec.lua @@ -3,32 +3,32 @@ local helpers = require('test.functional.helpers')(after_each) local clear = helpers.clear -local execute, expect = helpers.execute, helpers.expect +local command, expect = helpers.command, helpers.expect describe('augroup when calling exists()', function() setup(clear) it('is working', function() - execute('let results=[]') - execute('call add(results, "##BufEnter: " . exists("##BufEnter"))') - execute('call add(results, "#BufEnter: " . exists("#BufEnter"))') - execute('au BufEnter * let g:entered=1') - execute('call add(results, "#BufEnter: " . exists("#BufEnter"))') - execute('call add(results, "#auexists#BufEnter: " . exists("#auexists#BufEnter"))') - execute('augroup auexists', 'au BufEnter * let g:entered=1', 'augroup END') - execute('call add(results, "#auexists#BufEnter: " . exists("#auexists#BufEnter"))') - execute('call add(results, "#BufEnter#*.test: " . exists("#BufEnter#*.test"))') - execute('au BufEnter *.test let g:entered=1') - execute('call add(results, "#BufEnter#*.test: " . exists("#BufEnter#*.test"))') - execute('edit testfile.test') - execute('call add(results, "#BufEnter#: " . exists("#BufEnter#"))') - execute('au BufEnter let g:entered=1') - execute('call add(results, "#BufEnter#: " . exists("#BufEnter#"))') - execute('edit testfile2.test') - execute('call add(results, "#BufEnter#: " . exists("#BufEnter#"))') - execute('bf') - execute('call append(0, results)') - execute('$d') + command('let results=[]') + command('call add(results, "##BufEnter: " . exists("##BufEnter"))') + command('call add(results, "#BufEnter: " . exists("#BufEnter"))') + command('au BufEnter * let g:entered=1') + command('call add(results, "#BufEnter: " . exists("#BufEnter"))') + command('call add(results, "#auexists#BufEnter: " . exists("#auexists#BufEnter"))') + command('augroup auexists', 'au BufEnter * let g:entered=1', 'augroup END') + command('call add(results, "#auexists#BufEnter: " . exists("#auexists#BufEnter"))') + command('call add(results, "#BufEnter#*.test: " . exists("#BufEnter#*.test"))') + command('au BufEnter *.test let g:entered=1') + command('call add(results, "#BufEnter#*.test: " . exists("#BufEnter#*.test"))') + command('edit testfile.test') + command('call add(results, "#BufEnter#: " . exists("#BufEnter#"))') + command('au BufEnter let g:entered=1') + command('call add(results, "#BufEnter#: " . exists("#BufEnter#"))') + command('edit testfile2.test') + command('call add(results, "#BufEnter#: " . exists("#BufEnter#"))') + command('bf') + command('call append(0, results)') + command('$d') -- Assert buffer contents. expect([[ diff --git a/test/functional/legacy/068_text_formatting_spec.lua b/test/functional/legacy/068_text_formatting_spec.lua index e232e5073d..772dbc14cf 100644 --- a/test/functional/legacy/068_text_formatting_spec.lua +++ b/test/functional/legacy/068_text_formatting_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local feed = helpers.feed local clear = helpers.clear local insert = helpers.insert -local execute = helpers.execute +local feed_command = helpers.feed_command local expect = helpers.expect describe('text formatting', function() @@ -15,195 +15,195 @@ describe('text formatting', function() -- mode so it has to be escaped with . insert([[ Results of test68: - - + + { - - + + } - - + + { a b - + a } - - + + { a  } - - + + { a b #a b } - - + + { 1 a # 1 a } - - + + { - + x a b c - + } - - + + { # 1 a b } - - + + { # x # a b } - - + + { 1aa 2bb } - - + + /* abc def ghi jkl * mno pqr stu */ - - + + # 1 xxxxx ]]) - execute('/^{/+1') - execute('set noai tw=2 fo=t') + feed_command('/^{/+1') + feed_command('set noai tw=2 fo=t') feed('gRa b') - execute('/^{/+1') - execute('set ai tw=2 fo=tw') + feed_command('/^{/+1') + feed_command('set ai tw=2 fo=tw') feed('gqgqjjllab') - execute('/^{/+1') - execute('set tw=3 fo=t') + feed_command('/^{/+1') + feed_command('set tw=3 fo=t') feed('gqgqo') feed('a ') - execute('/^{/+1') - execute('set tw=2 fo=tcq1 comments=:#') + feed_command('/^{/+1') + feed_command('set tw=2 fo=tcq1 comments=:#') feed('gqgqjgqgqo') feed('a b') feed('#a b') - execute('/^{/+1') - execute('set tw=5 fo=tcn comments=:#') + feed_command('/^{/+1') + feed_command('set tw=5 fo=tcn comments=:#') feed('A bjA b') - execute('/^{/+3') - execute('set tw=5 fo=t2a si') + feed_command('/^{/+3') + feed_command('set tw=5 fo=t2a si') feed('i A_') - execute('/^{/+1') - execute('set tw=5 fo=qn comments=:#') + feed_command('/^{/+1') + feed_command('set tw=5 fo=qn comments=:#') feed('gwap') - execute('/^{/+1') - execute('set tw=5 fo=q2 comments=:#') + feed_command('/^{/+1') + feed_command('set tw=5 fo=q2 comments=:#') feed('gwap') - execute('/^{/+2') - execute('set tw& fo=a') + feed_command('/^{/+2') + feed_command('set tw& fo=a') feed('I^^') - execute('/mno pqr/') - execute('setl tw=20 fo=an12wcq comments=s1:/*,mb:*,ex:*/') + feed_command('/mno pqr/') + feed_command('setl tw=20 fo=an12wcq comments=s1:/*,mb:*,ex:*/') feed('A vwx yz') - execute('/^#/') - execute('setl tw=12 fo=tqnc comments=:#') + feed_command('/^#/') + feed_command('setl tw=12 fo=tqnc comments=:#') feed('A foobar') -- Assert buffer contents. expect([[ Results of test68: - - + + { a b } - - + + { a b - + a b } - - + + { a  - + a  } - - + + { a b #a b - + a b #a b } - - + + { 1 a b # 1 a # b } - - + + { - + x a b_ c - + } - - + + { # 1 a # b } - - + + { # x a # b } - - + + { 1aa ^^2bb } - - + + /* abc def ghi jkl * mno pqr stu * vwx yz */ - - + + # 1 xxxxx # foobar ]]) diff --git a/test/functional/legacy/069_multibyte_formatting_spec.lua b/test/functional/legacy/069_multibyte_formatting_spec.lua index 6edcd8b7f2..38ca25d57a 100644 --- a/test/functional/legacy/069_multibyte_formatting_spec.lua +++ b/test/functional/legacy/069_multibyte_formatting_spec.lua @@ -4,8 +4,8 @@ -- Also test byteidx() and byteidxcomp() local helpers = require('test.functional.helpers')(after_each) -local feed, insert, eq, eval, clear, execute, expect = helpers.feed, - helpers.insert, helpers.eq, helpers.eval, helpers.clear, helpers.execute, +local feed, insert, eq, eval, clear, feed_command, expect = helpers.feed, + helpers.insert, helpers.eq, helpers.eval, helpers.clear, helpers.feed_command, helpers.expect describe('multibyte text', function() @@ -17,8 +17,8 @@ describe('multibyte text', function() XYZ abc XYZ }]]) - execute('/^{/+1') - execute('set tw=2 fo=t') + feed_command('/^{/+1') + feed_command('set tw=2 fo=t') feed('gqgqjgqgqo') feed('XYZ') feed('abc XYZ') @@ -43,8 +43,8 @@ describe('multibyte text', function() XY X Y }]]) - execute('/^{/+1') - execute('set tw=1 fo=tm') + feed_command('/^{/+1') + feed_command('set tw=1 fo=tm') feed('gqgqjgqgqjgqgqjgqgqjgqgqo') feed('X') feed('Xa') @@ -89,8 +89,8 @@ describe('multibyte text', function() abX c abXY }]]) - execute('/^{/+1') - execute('set tw=2 fo=tm') + feed_command('/^{/+1') + feed_command('set tw=2 fo=tm') feed('gqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqo') feed('X') feed('Xa') @@ -156,8 +156,8 @@ describe('multibyte text', function() X Xa }]]) - execute('/^{/+1') - execute('set ai tw=2 fo=tm') + feed_command('/^{/+1') + feed_command('set ai tw=2 fo=tm') feed('gqgqjgqgqo') feed('X') feed('Xa') @@ -179,8 +179,8 @@ describe('multibyte text', function() X Xa }]]) - execute('/^{/+1') - execute('set noai tw=2 fo=tm') + feed_command('/^{/+1') + feed_command('set noai tw=2 fo=tm') feed('gqgqjgqgqo') -- Literal spaces will be trimmed from the by feed(). feed('') @@ -211,8 +211,8 @@ describe('multibyte text', function() XXa XXY }]]) - execute('/^{/+1') - execute('set tw=2 fo=cqm comments=n:X') + feed_command('/^{/+1') + feed_command('set tw=2 fo=cqm comments=n:X') feed('gqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqo') feed('X') feed('Xa') @@ -261,8 +261,8 @@ describe('multibyte text', function() { }]]) - execute('/^{/+1') - execute('set tw=2 fo=tm') + feed_command('/^{/+1') + feed_command('set tw=2 fo=tm') feed('RXa') expect([[ { @@ -276,8 +276,8 @@ describe('multibyte text', function() { ‘ two three ’ four }]]) - execute('/^{/+1') - execute('set mps+=‘:’') + feed_command('/^{/+1') + feed_command('set mps+=‘:’') feed('d%') expect([[ { @@ -299,8 +299,8 @@ describe('multibyte text', function() insert([[ á x]]) - execute('set whichwrap+=h') - execute('/^x') + feed_command('set whichwrap+=h') + feed_command('/^x') feed('dh') expect([[ áx]]) @@ -308,9 +308,9 @@ describe('multibyte text', function() it('can be queried with byteidx() and byteidxcomp()', function() -- One char of two bytes. - execute("let a = '.é.'") + feed_command("let a = '.é.'") -- Normal e with composing char. - execute("let b = '.é.'") + feed_command("let b = '.é.'") eq(0, eval('byteidx(a, 0)')) eq(1, eval('byteidx(a, 1)')) eq(3, eval('byteidx(a, 2)')) diff --git a/test/functional/legacy/072_undo_file_spec.lua b/test/functional/legacy/072_undo_file_spec.lua index 4682a82008..b4927e779e 100644 --- a/test/functional/legacy/072_undo_file_spec.lua +++ b/test/functional/legacy/072_undo_file_spec.lua @@ -4,7 +4,7 @@ local helpers = require('test.functional.helpers')(after_each) local feed, insert = helpers.feed, helpers.insert -local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect describe('72', function() setup(clear) @@ -13,34 +13,34 @@ describe('72', function() insert([[ 1111 ----- 2222 ----- - + 123456789]]) -- Test 'undofile': first a simple one-line change. - execute('set visualbell') - execute('set ul=100 undofile undodir=. nomore') - execute('e! Xtestfile') + feed_command('set visualbell') + feed_command('set ul=100 undofile undodir=. nomore') + feed_command('e! Xtestfile') feed('ggdGithis is one line:set ul=100') - execute('s/one/ONE/') - execute('set ul=100') - execute('w') - execute('bwipe!') - execute('e Xtestfile') + feed_command('s/one/ONE/') + feed_command('set ul=100') + feed_command('w') + feed_command('bwipe!') + feed_command('e Xtestfile') feed('u:.w! test.out') -- Test 'undofile', change in original file fails check. - execute('set noundofile') - execute('e! Xtestfile') - execute('s/line/Line/') - execute('w') - execute('set undofile') - execute('bwipe!') - execute('e Xtestfile') + feed_command('set noundofile') + feed_command('e! Xtestfile') + feed_command('s/line/Line/') + feed_command('w') + feed_command('set undofile') + feed_command('bwipe!') + feed_command('e Xtestfile') ---- TODO: this beeps. feed('u:.w >>test.out') -- Test 'undofile', add 10 lines, delete 6 lines, undo 3. - execute('set undofile') + feed_command('set undofile') feed('ggdGione') feed('two') feed('three') @@ -57,20 +57,20 @@ describe('72', function() feed('dd:set ul=100') feed('dd:set ul=100') feed('dd:set ul=100') - execute('w') - execute('bwipe!') - execute('e Xtestfile') + feed_command('w') + feed_command('bwipe!') + feed_command('e Xtestfile') feed('uuu:w >>test.out') -- Test that reading the undofiles when setting undofile works. - execute('set noundofile ul=0') + feed_command('set noundofile ul=0') feed('i') feed('u:e! Xtestfile') - execute('set undofile ul=100') + feed_command('set undofile ul=100') feed('uuuuuu:w >>test.out') ---- Open the output to see if it meets the expections - execute('e! test.out') + feed_command('e! test.out') -- Assert buffer contents. expect([[ diff --git a/test/functional/legacy/074_global_var_in_viminfo_spec.lua b/test/functional/legacy/074_global_var_in_viminfo_spec.lua index e8292db8c1..e160f69592 100644 --- a/test/functional/legacy/074_global_var_in_viminfo_spec.lua +++ b/test/functional/legacy/074_global_var_in_viminfo_spec.lua @@ -2,8 +2,8 @@ local helpers = require('test.functional.helpers')(after_each) local lfs = require('lfs') -local clear, execute, eq, neq, eval, wait, spawn = - helpers.clear, helpers.execute, helpers.eq, helpers.neq, helpers.eval, +local clear, command, eq, neq, eval, wait, spawn = + helpers.clear, helpers.command, helpers.eq, helpers.neq, helpers.eval, helpers.wait, helpers.spawn describe('storing global variables in ShaDa files', function() @@ -26,7 +26,7 @@ describe('storing global variables in ShaDa files', function() 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100} - execute( + command( -- This will cause a few errors, do it silently. 'set visualbell', 'set shada+=!', @@ -39,18 +39,18 @@ describe('storing global variables in ShaDa files', function() eq(test_dict, eval('MY_GLOBAL_DICT')) eq(test_list, eval('MY_GLOBAL_LIST')) - execute('wsh! ' .. tempname) + command('wsh! ' .. tempname) wait() -- Assert that the shada file exists. neq(nil, lfs.attributes(tempname)) - execute('unlet MY_GLOBAL_DICT', + command('unlet MY_GLOBAL_DICT', 'unlet MY_GLOBAL_LIST') -- Assert that the variables where deleted. eq(0, eval('exists("MY_GLOBAL_DICT")')) eq(0, eval('exists("MY_GLOBAL_LIST")')) - execute('rsh! ' .. tempname) + command('rsh! ' .. tempname) eq(test_list, eval('MY_GLOBAL_LIST')) eq(test_dict, eval('MY_GLOBAL_DICT')) diff --git a/test/functional/legacy/075_maparg_spec.lua b/test/functional/legacy/075_maparg_spec.lua index e9d2acdaf5..fcfd33ec46 100644 --- a/test/functional/legacy/075_maparg_spec.lua +++ b/test/functional/legacy/075_maparg_spec.lua @@ -3,46 +3,48 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed = helpers.clear, helpers.feed -local execute, expect = helpers.execute, helpers.expect +local command, expect = helpers.command, helpers.expect +local wait = helpers.wait describe('maparg()', function() setup(clear) it('is working', function() - execute('set cpo-=<') + command('set cpo-=<') -- Test maparg() with a string result - execute('map foo isfoo') - execute('vnoremap