diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index 3cc9515fef..496c047a4a 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -267,7 +267,7 @@ int nextwild(expand_T *xp, int type, int options, bool escape) char *p1; if (cmdline_fuzzy_completion_supported(xp)) { // If fuzzy matching, don't modify the search string - p1 = xstrdup(xp->xp_pattern); + p1 = xstrnsave(xp->xp_pattern, xp->xp_pattern_len); } else { p1 = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context); } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index c52937b6c1..98da044bb4 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -4241,7 +4241,8 @@ int expand_argopt(char *pat, expand_T *xp, regmatch_T *rmp, char ***matches, int // Special handling of "ff" which acts as a short form of // "fileformat", as "ff" is not a substring of it. - if (strcmp(xp->xp_pattern, "ff") == 0) { + if (xp->xp_pattern_len == 2 + && strncmp(xp->xp_pattern, "ff", xp->xp_pattern_len) == 0) { *matches = xmalloc(sizeof(char *)); *numMatches = 1; (*matches)[0] = xstrdup("fileformat="); diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 2759dc9bb5..9d0e3d542a 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -861,6 +861,16 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear cmdmsg_rl = false; + // We could have reached here without having a chance to clean up wild menu + // if certain special keys like or were used as wildchar. Make + // sure to still clean up to avoid memory corruption. + if (cmdline_pum_active()) { + cmdline_pum_remove(); + } + wildmenu_cleanup(&ccline); + s->did_wild_list = false; + s->wim_index = 0; + ExpandCleanup(&s->xpc); ccline.xpc = NULL; @@ -1247,13 +1257,14 @@ static int command_line_execute(VimState *state, int key) s->c = wildmenu_translate_key(&ccline, s->c, &s->xpc, s->did_wild_list); } - if (cmdline_pum_active() || s->did_wild_list) { + int wild_type = 0; + const bool key_is_wc = (s->c == p_wc && KeyTyped) || s->c == p_wcm; + if ((cmdline_pum_active() || s->did_wild_list) && !key_is_wc) { // Ctrl-Y: Accept the current selection and close the popup menu. // Ctrl-E: cancel the cmdline popup menu and return the original text. if (s->c == Ctrl_E || s->c == Ctrl_Y) { - const int wild_type = (s->c == Ctrl_E) ? WILD_CANCEL : WILD_APPLY; + wild_type = (s->c == Ctrl_E) ? WILD_CANCEL : WILD_APPLY; (void)nextwild(&s->xpc, wild_type, WILD_NO_BEEP, s->firstc != '@'); - s->c = Ctrl_E; } } @@ -1262,7 +1273,7 @@ static int command_line_execute(VimState *state, int key) // 'wildcharm' or Ctrl-N or Ctrl-P or Ctrl-A or Ctrl-L). // If the popup menu is displayed, then PageDown and PageUp keys are // also used to navigate the menu. - bool end_wildmenu = (!(s->c == p_wc && KeyTyped) && s->c != p_wcm && s->c != Ctrl_Z + bool end_wildmenu = (!key_is_wc && s->c != Ctrl_Z && s->c != Ctrl_N && s->c != Ctrl_P && s->c != Ctrl_A && s->c != Ctrl_L); end_wildmenu = end_wildmenu && (!cmdline_pum_active() @@ -1366,6 +1377,12 @@ static int command_line_execute(VimState *state, int key) } s->do_abbr = true; // default: check for abbreviation + + // If already used to cancel/accept wildmenu, don't process the key further. + if (wild_type == WILD_CANCEL || wild_type == WILD_APPLY) { + return command_line_not_changed(s); + } + return command_line_handle_key(s); } diff --git a/test/functional/fixtures/wildpum/Xdir/XdirA/XdirB/XfileC b/test/functional/fixtures/wildpum/Xnamedir/XdirA/XdirB/XfileC similarity index 100% rename from test/functional/fixtures/wildpum/Xdir/XdirA/XdirB/XfileC rename to test/functional/fixtures/wildpum/Xnamedir/XdirA/XdirB/XfileC diff --git a/test/functional/fixtures/wildpum/Xdir/XdirA/XfileB b/test/functional/fixtures/wildpum/Xnamedir/XdirA/XfileB similarity index 100% rename from test/functional/fixtures/wildpum/Xdir/XdirA/XfileB rename to test/functional/fixtures/wildpum/Xnamedir/XdirA/XfileB diff --git a/test/functional/fixtures/wildpum/Xdir/XfileA b/test/functional/fixtures/wildpum/Xnamedir/XfileA similarity index 100% rename from test/functional/fixtures/wildpum/Xdir/XfileA rename to test/functional/fixtures/wildpum/Xnamedir/XfileA diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index 38649a2be3..bfa4b7f14e 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -3096,7 +3096,7 @@ describe('builtin popupmenu', function() :sign define culhl= culhl=^ | ]]) - feed('e Xdi') + feed('e Xnamedi') screen:expect([[ | {1:~ }| @@ -3105,9 +3105,9 @@ describe('builtin popupmenu', function() {1:~ }| {1:~ }| {1:~ }| - {1:~ }{s: XdirA/ }{1: }| - {1:~ }{n: XfileA }{1: }| - :e Xdir/XdirA/^ | + {1:~ }{s: XdirA/ }{1: }| + {1:~ }{n: XfileA }{1: }| + :e Xnamedir/XdirA/^ | ]]) -- Pressing on a directory name should go into that directory @@ -3120,9 +3120,9 @@ describe('builtin popupmenu', function() {1:~ }| {1:~ }| {1:~ }| - {1:~ }{s: XdirB/ }{1: }| - {1:~ }{n: XfileB }{1: }| - :e Xdir/XdirA/XdirB/^ | + {1:~ }{s: XdirB/ }{1: }| + {1:~ }{n: XfileB }{1: }| + :e Xnamedir/XdirA/XdirB/^ | ]]) -- Pressing on a directory name should go to the parent directory @@ -3135,9 +3135,9 @@ describe('builtin popupmenu', function() {1:~ }| {1:~ }| {1:~ }| - {1:~ }{s: XdirA/ }{1: }| - {1:~ }{n: XfileA }{1: }| - :e Xdir/XdirA/^ | + {1:~ }{s: XdirA/ }{1: }| + {1:~ }{n: XfileA }{1: }| + :e Xnamedir/XdirA/^ | ]]) -- Pressing when the popup menu is displayed should list all the @@ -3511,6 +3511,49 @@ describe('builtin popupmenu', function() :sign define^ | ]]) + -- pressing to end completion should work in middle of the line too + feed(':set wildchazz') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }{s: wildchar }{1: }| + {1:~ }{n: wildcharm }{1: }| + :set wildchar^zz | + ]]) + feed('') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :set wildcha^zz | + ]]) + + -- pressing should select the current match and end completion + feed(':set wildchazz') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :set wildchar^zz | + ]]) + feed('') -- check positioning with multibyte char in pattern @@ -3726,13 +3769,13 @@ describe('builtin popupmenu', function() end) -- oldtest: Test_wildmenu_pum_clear_entries() - it('wildoptions=pum when using Ctrl-E as wildchar vim-patch:9.0.1030', function() + it('wildoptions=pum when using odd wildchar', function() screen:try_resize(30, 10) exec([[ set wildoptions=pum set wildchar= ]]) - feed(':sign ') + feed(':sign ') screen:expect([[ | {1:~ }| diff --git a/test/old/testdir/test_cmdline.vim b/test/old/testdir/test_cmdline.vim index 00c894058e..61ee59068d 100644 --- a/test/old/testdir/test_cmdline.vim +++ b/test/old/testdir/test_cmdline.vim @@ -82,38 +82,38 @@ func Test_complete_list() endfunc func Test_complete_wildmenu() - call mkdir('Xdir1/Xdir2', 'p') - call writefile(['testfile1'], 'Xdir1/Xtestfile1') - call writefile(['testfile2'], 'Xdir1/Xtestfile2') - call writefile(['testfile3'], 'Xdir1/Xdir2/Xtestfile3') - call writefile(['testfile3'], 'Xdir1/Xdir2/Xtestfile4') + call mkdir('Xwilddir1/Xdir2', 'pR') + call writefile(['testfile1'], 'Xwilddir1/Xtestfile1') + call writefile(['testfile2'], 'Xwilddir1/Xtestfile2') + call writefile(['testfile3'], 'Xwilddir1/Xdir2/Xtestfile3') + call writefile(['testfile3'], 'Xwilddir1/Xdir2/Xtestfile4') set wildmenu " Pressing completes, and moves to next files when pressing again. - call feedkeys(":e Xdir1/\\\", 'tx') + call feedkeys(":e Xwilddir1/\\\", 'tx') call assert_equal('testfile1', getline(1)) - call feedkeys(":e Xdir1/\\\\", 'tx') + call feedkeys(":e Xwilddir1/\\\\", 'tx') call assert_equal('testfile2', getline(1)) " is like but begin with the last match and then go to " previous. - call feedkeys(":e Xdir1/Xtest\\", 'tx') + call feedkeys(":e Xwilddir1/Xtest\\", 'tx') call assert_equal('testfile2', getline(1)) - call feedkeys(":e Xdir1/Xtest\\\", 'tx') + call feedkeys(":e Xwilddir1/Xtest\\\", 'tx') call assert_equal('testfile1', getline(1)) " / to move to previous/next file. - call feedkeys(":e Xdir1/\\\", 'tx') + call feedkeys(":e Xwilddir1/\\\", 'tx') call assert_equal('testfile1', getline(1)) - call feedkeys(":e Xdir1/\\\\", 'tx') + call feedkeys(":e Xwilddir1/\\\\", 'tx') call assert_equal('testfile2', getline(1)) - call feedkeys(":e Xdir1/\\\\\", 'tx') + call feedkeys(":e Xwilddir1/\\\\\", 'tx') call assert_equal('testfile1', getline(1)) " / to go up/down directories. - call feedkeys(":e Xdir1/\\\", 'tx') + call feedkeys(":e Xwilddir1/\\\", 'tx') call assert_equal('testfile3', getline(1)) - call feedkeys(":e Xdir1/\\\\\", 'tx') + call feedkeys(":e Xwilddir1/\\\\\", 'tx') call assert_equal('testfile1', getline(1)) " this fails in some Unix GUIs, not sure why @@ -123,9 +123,9 @@ func Test_complete_wildmenu() set wildcharm= cnoremap cnoremap - call feedkeys(":e Xdir1/\\\", 'tx') + call feedkeys(":e Xwilddir1/\\\", 'tx') call assert_equal('testfile3', getline(1)) - call feedkeys(":e Xdir1/\\\\", 'tx') + call feedkeys(":e Xwilddir1/\\\\", 'tx') call assert_equal('testfile1', getline(1)) set wildcharm=0 cunmap @@ -134,21 +134,21 @@ func Test_complete_wildmenu() " Test for canceling the wild menu by adding a character redrawstatus - call feedkeys(":e Xdir1/\x\\"\", 'xt') - call assert_equal('"e Xdir1/Xdir2/x', @:) + call feedkeys(":e Xwilddir1/\x\\"\", 'xt') + call assert_equal('"e Xwilddir1/Xdir2/x', @:) " Completion using a relative path - cd Xdir1/Xdir2 + cd Xwilddir1/Xdir2 call feedkeys(":e ../\\\\\\"\", 'tx') call assert_equal('"e Xtestfile3 Xtestfile4', @:) cd - " test for wildmenumode() cnoremap wildmenumode() - call feedkeys(":cd Xdir\\\\"\", 'tx') - call assert_equal('"cd Xdir1/0', @:) - call feedkeys(":e Xdir1/\\\\"\", 'tx') - call assert_equal('"e Xdir1/Xdir2/1', @:) + call feedkeys(":cd Xwilddir\\\\"\", 'tx') + call assert_equal('"cd Xwilddir1/0', @:) + call feedkeys(":e Xwilddir1/\\\\"\", 'tx') + call assert_equal('"e Xwilddir1/Xdir2/1', @:) cunmap " Test for canceling the wild menu by pressing or . @@ -159,9 +159,15 @@ func Test_complete_wildmenu() call feedkeys(":sign \\\\\\\"\", 'tx') call assert_equal('"TestWildMenu', @:) + " Test for Ctrl-E/Ctrl-Y being able to cancel / accept a match + call feedkeys(":sign un zz\\\\\ yy\\"\", 'tx') + call assert_equal('"sign un yy zz', @:) + + call feedkeys(":sign un zz\\\\\\ yy\\"\", 'tx') + call assert_equal('"sign unplace yy zz', @:) + " cleanup %bwipe - call delete('Xdir1', 'rf') set nowildmenu endfunc @@ -1046,6 +1052,9 @@ func Test_cmdline_complete_argopt() call assert_equal('edit', getcompletion('read ++bin ++edi', 'cmdline')[0]) call assert_equal(['fileformat='], getcompletion('edit ++ff', 'cmdline')) + " Test ++ff in the middle of the cmdline + call feedkeys(":edit ++ff zz\\\\\\"\", 'xt') + call assert_equal("\"edit ++fileformat= zz", @:) call assert_equal('dos', getcompletion('write ++ff=d', 'cmdline')[0]) call assert_equal('mac', getcompletion('args ++fileformat=m', 'cmdline')[0]) @@ -2513,7 +2522,7 @@ func Test_wildmenu_pum() call feedkeys(":edit $VIMRUNTIME/\\\ab\") endfunc [CODE] - call writefile(commands, 'Xtest') + call writefile(commands, 'Xtest', 'D') let buf = RunVimInTerminal('-S Xtest', #{rows: 10}) @@ -2571,12 +2580,12 @@ func Test_wildmenu_pum() call VerifyScreenDump(buf, 'Test_wildmenu_pum_13', {}) " Directory name completion - call mkdir('Xdir/XdirA/XdirB', 'p') - call writefile([], 'Xdir/XfileA') - call writefile([], 'Xdir/XdirA/XfileB') - call writefile([], 'Xdir/XdirA/XdirB/XfileC') + call mkdir('Xnamedir/XdirA/XdirB', 'pR') + call writefile([], 'Xnamedir/XfileA') + call writefile([], 'Xnamedir/XdirA/XfileB') + call writefile([], 'Xnamedir/XdirA/XdirB/XfileC') - call term_sendkeys(buf, "\e Xdi\\") + call term_sendkeys(buf, "\e Xnamedi\\") call VerifyScreenDump(buf, 'Test_wildmenu_pum_14', {}) " Pressing on a directory name should go into that directory @@ -2651,13 +2660,13 @@ func Test_wildmenu_pum() call VerifyScreenDump(buf, 'Test_wildmenu_pum_31', {}) " Tests a directory name contained full-width characters. - call mkdir('Xdir/あいう', 'p') - call writefile([], 'Xdir/あいう/abc') - call writefile([], 'Xdir/あいう/xyz') - call writefile([], 'Xdir/あいう/123') + call mkdir('Xnamedir/あいう', 'p') + call writefile([], 'Xnamedir/あいう/abc') + call writefile([], 'Xnamedir/あいう/xyz') + call writefile([], 'Xnamedir/あいう/123') call term_sendkeys(buf, "\set wildmode&\") - call term_sendkeys(buf, ":\e Xdir/あいう/\") + call term_sendkeys(buf, ":\e Xnamedir/あいう/\") call VerifyScreenDump(buf, 'Test_wildmenu_pum_32', {}) " Pressing when the popup menu is displayed should list all the @@ -2679,7 +2688,7 @@ func Test_wildmenu_pum() " After using to expand all the filename matches, pressing " should not open the popup menu again. - call term_sendkeys(buf, "\\:cd Xdir/XdirA\") + call term_sendkeys(buf, "\\:cd Xnamedir/XdirA\") call term_sendkeys(buf, ":e \\\") call VerifyScreenDump(buf, 'Test_wildmenu_pum_36', {}) call term_sendkeys(buf, "\\:cd -\") @@ -2737,10 +2746,18 @@ func Test_wildmenu_pum() call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_wildmenu_pum_50', {}) + " pressing to end completion should work in middle of the line too + call term_sendkeys(buf, "\:set wildchazz\\\") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_51', {}) + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_52', {}) + + " pressing should select the current match and end completion + call term_sendkeys(buf, "\:set wildchazz\\\\") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_53', {}) + call term_sendkeys(buf, "\\") call StopVimInTerminal(buf) - call delete('Xtest') - call delete('Xdir', 'rf') endfunc " Test for wildmenumode() with the cmdline popup menu @@ -3406,6 +3423,17 @@ func Test_fuzzy_completion_custom_func() set wildoptions& endfunc +" Test for fuzzy completion in the middle of a cmdline instead of at the end +func Test_fuzzy_completion_in_middle() + set wildoptions=fuzzy + call feedkeys(":set ildar wrap\\\\\\\\"\", 'tx') + call assert_equal("\"set wildchar wildcharm wrap", @:) + + call feedkeys(":args ++odng zz\\\\\\"\", 'tx') + call assert_equal("\"args ++encoding= zz", @:) + set wildoptions& +endfunc + " Test for :breakadd argument completion func Test_cmdline_complete_breakadd() call feedkeys(":breakadd \\\"\", 'tx') diff --git a/test/old/testdir/test_options.vim b/test/old/testdir/test_options.vim index cc3f21b86b..1a3a909344 100644 --- a/test/old/testdir/test_options.vim +++ b/test/old/testdir/test_options.vim @@ -621,6 +621,9 @@ func Test_set_completion_string_values() " \ {idx, val -> val != ':'}), " \ '') " call assert_equal([], getcompletion('set hl+=8'..hl_display_modes, 'cmdline')) + " Test completion in middle of the line + " call feedkeys(":set hl=8b i\\\\\"\", 'xt') + " call assert_equal("\"set hl=8bi i", @:) " " Test flag lists