diff options
| author | zeertzjq <zeertzjq@outlook.com> | 2025-07-20 21:51:10 +0800 |
|---|---|---|
| committer | zeertzjq <zeertzjq@outlook.com> | 2025-07-21 07:15:51 +0800 |
| commit | bbc368dfce2547ff28698f4a2a7d629ee83ffba6 (patch) | |
| tree | 5f2ab4da7f0d69cc5ef50fa83ecadda95385dc5c | |
| parent | c3991b8ef4f086b479ae3bc0f65aecaa07271a0a (diff) | |
| download | rneovim-bbc368dfce2547ff28698f4a2a7d629ee83ffba6.tar.gz rneovim-bbc368dfce2547ff28698f4a2a7d629ee83ffba6.tar.bz2 rneovim-bbc368dfce2547ff28698f4a2a7d629ee83ffba6.zip | |
vim-patch:9.1.1571: CmdlineChanged triggered to often
Problem: The CmdlineChanged event was firing unnecessarily, even when
the command line's content hadn't actually changed.
Solution: I've added a check to compare the command-line buffer's state
before and after key processing. The `CmdlineChanged` event
now only triggers if the buffer's contents are genuinely
different (Girish Palya).
closes: vim/vim#17803
https://github.com/vim/vim/commit/239c4e4abe79c0f3611f0b4776ee34fbf0ac53cf
Co-authored-by: Girish Palya <girishji@gmail.com>
| -rw-r--r-- | src/nvim/ex_getln.c | 20 | ||||
| -rw-r--r-- | test/old/testdir/test_cmdline.vim | 49 |
2 files changed, 66 insertions, 3 deletions
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index eaee6c07a3..a156b83c7d 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -134,6 +134,7 @@ typedef struct { int save_msg_scroll; int save_State; // remember State when called int prev_cmdpos; + char *prev_cmdbuff; char *save_p_icm; bool some_key_typed; // one of the keys was typed // mouse drag and release events are ignored, unless they are @@ -1013,12 +1014,14 @@ theend: ccline.cmdbuff = NULL; } + xfree(s->prev_cmdbuff); return (uint8_t *)p; } static int command_line_check(VimState *state) { CommandLineState *s = (CommandLineState *)state; + s->prev_cmdpos = ccline.cmdpos; redir_off = true; // Don't redirect the typed command. // Repeated, because a ":redir" inside @@ -1029,6 +1032,10 @@ static int command_line_check(VimState *state) // that occurs while typing a command should // cause the command not to be executed. + if (ccline.cmdbuff != NULL) { + s->prev_cmdbuff = xmemdupz(ccline.cmdbuff, (size_t)ccline.cmdpos); + } + // Trigger SafeState if nothing is pending. may_trigger_safestate(s->xpc.xp_numfiles <= 0); @@ -2288,7 +2295,6 @@ static void may_trigger_cursormovedc(CommandLineState *s) { if (ccline.cmdpos != s->prev_cmdpos) { trigger_cmd_autocmd(s->cmdline_type, EVENT_CURSORMOVEDC); - s->prev_cmdpos = ccline.cmdpos; ccline.redraw_state = MAX(ccline.redraw_state, kCmdRedrawPos); } } @@ -2296,12 +2302,14 @@ static void may_trigger_cursormovedc(CommandLineState *s) static int command_line_not_changed(CommandLineState *s) { may_trigger_cursormovedc(s); + s->prev_cmdpos = ccline.cmdpos; // Incremental searches for "/" and "?": // Enter command_line_not_changed() when a character has been read but the // command line did not change. Then we only search and redraw if something // changed in the past. // Enter command_line_changed() when the command line did change. if (!s->is_state.incsearch_postponed) { + XFREE_CLEAR(s->prev_cmdbuff); return 1; } return command_line_changed(s); @@ -2772,8 +2780,14 @@ static void do_autocmd_cmdlinechanged(int firstc) static int command_line_changed(CommandLineState *s) { - // Trigger CmdlineChanged autocommands. - do_autocmd_cmdlinechanged(s->firstc > 0 ? s->firstc : '-'); + if (ccline.cmdpos != s->prev_cmdpos + || (s->prev_cmdbuff != NULL + && strncmp(s->prev_cmdbuff, ccline.cmdbuff, (size_t)s->prev_cmdpos) != 0)) { + // Trigger CmdlineChanged autocommands. + do_autocmd_cmdlinechanged(s->firstc > 0 ? s->firstc : '-'); + } + + XFREE_CLEAR(s->prev_cmdbuff); may_trigger_cursormovedc(s); diff --git a/test/old/testdir/test_cmdline.vim b/test/old/testdir/test_cmdline.vim index 3e8592a7d8..a49603311e 100644 --- a/test/old/testdir/test_cmdline.vim +++ b/test/old/testdir/test_cmdline.vim @@ -4719,4 +4719,53 @@ func Test_pum_scroll_noselect() call StopVimInTerminal(buf) endfunc +" CmdlineChanged shouldn't trigger if command-line text is unchanged +func Test_cmdline_changed() + let g:cmdchg_count = 0 + let g:cmdprefix = '' + augroup test_CmdlineAugrp | autocmd! + autocmd CmdlineChanged * if getcmdline() =~ g:cmdprefix | let g:cmdchg_count += 1 | endif + augroup END + + new + set wildmenu + set wildmode=full + + let g:cmdprefix = 'echomsg' + let g:cmdchg_count = 0 + call feedkeys(":echomsg\<Tab>", "tx") + call assert_equal(1, g:cmdchg_count) " once only for 'g', not again for <Tab> + + let g:cmdchg_count = 0 + let g:cmdprefix = 'echo' + call feedkeys(":ech\<Tab>", "tx") + call assert_equal(1, g:cmdchg_count) " (once for 'h' and) once for 'o' + + set wildmode=noselect,full + let g:cmdchg_count = 0 + let g:cmdprefix = 'ech' + call feedkeys(":ech\<Tab>", "tx") + call assert_equal(1, g:cmdchg_count) " once for 'h', not again for <tab> + + command! -nargs=+ -complete=custom,TestComplete Test echo + + func TestComplete(arglead, cmdline, cursorpos) + return "AbC" + endfunc + + set wildoptions=fuzzy wildmode=full + let g:cmdchg_count = 0 + let g:cmdprefix = 'Test \(AbC\|abc\)' + call feedkeys(":Test abc\<Tab>", "tx") + call assert_equal(2, g:cmdchg_count) " once for 'c', again for 'AbC' + + bw! + set wildmode& wildmenu& wildoptions& + augroup test_CmdlineAugrp | autocmd! | augroup END + unlet g:cmdchg_count + unlet g:cmdprefix + delfunc TestComplete + delcommand Test +endfunc + " vim: shiftwidth=2 sts=2 expandtab |