neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

commit d75410b0918c456ed766a043704071e0073554d7
parent 43f3c4a48f6cc3a4c4173591a59b8e3fcdb96759
Author: zeertzjq <zeertzjq@outlook.com>
Date:   Tue, 29 Apr 2025 06:50:53 +0800

vim-patch:9.1.1349: CmdlineLeavePre may trigger twice

Problem:  CmdlineLeavePre may trigger twice
          (after v9.1.1329)
Solution: check that the key was typed, trigger it when it wasn't before
          (Girish Palya)

There are two problems:
- CmdlineLeavePre may be triggered twice when a cabbr is present.
- CmdlineLeavePre fails to trigger when exiting the command-line via
  <Backspace>.

Check if the Carriage Return (Enter) key was actually typed.
Trigger the event when the command-line is exited using Backspace and
other keys.

closes: vim/vim#17214

https://github.com/vim/vim/commit/46755e6b52b0c4346c1c7eaa311c204fdd9185df

Co-authored-by: Girish Palya <girishji@gmail.com>

Diffstat:
Msrc/nvim/ex_getln.c | 19++++++++++++++-----
Mtest/functional/legacy/autocmd_spec.lua | 10++++++++++
Mtest/old/testdir/test_autocmd.vim | 50++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 74 insertions(+), 5 deletions(-)

diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c @@ -142,6 +142,8 @@ typedef struct { expand_T xpc; OptInt *b_im_ptr; buf_T *b_im_ptr_buf; ///< buffer where b_im_ptr is valid + int cmdline_type; + bool event_cmdlineleavepre_triggered; } CommandLineState; typedef struct { @@ -795,9 +797,10 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear setmouse(); setcursor(); + s->cmdline_type = firstc > 0 ? firstc : '-'; Error err = ERROR_INIT; char firstcbuf[2]; - firstcbuf[0] = (char)(firstc > 0 ? firstc : '-'); + firstcbuf[0] = (char)s->cmdline_type; firstcbuf[1] = 0; if (has_event(EVENT_CMDLINEENTER)) { @@ -947,6 +950,11 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear need_wait_return = false; } + // Trigger CmdlineLeavePre autocommands if not already triggered. + if (!s->event_cmdlineleavepre_triggered) { + trigger_cmd_autocmd(s->cmdline_type, EVENT_CMDLINELEAVEPRE); + } + set_option_direct(kOptInccommand, CSTR_AS_OPTVAL(s->save_p_icm), 0, SID_NONE); State = s->save_State; if (cmdpreview != save_cmdpreview) { @@ -1304,9 +1312,10 @@ static int command_line_execute(VimState *state, int key) } // Trigger CmdlineLeavePre autocommand - if (s->c == '\n' || s->c == '\r' || s->c == K_KENTER - || s->c == ESC || s->c == Ctrl_C) { - trigger_cmd_autocmd(get_cmdline_type(), EVENT_CMDLINELEAVEPRE); + if ((KeyTyped && (s->c == '\n' || s->c == '\r' || s->c == K_KENTER || s->c == ESC)) + || s->c == Ctrl_C) { + trigger_cmd_autocmd(s->cmdline_type, EVENT_CMDLINELEAVEPRE); + s->event_cmdlineleavepre_triggered = true; } // The wildmenu is cleared if the pressed key is not used for @@ -2234,7 +2243,7 @@ end: static void may_trigger_cursormovedc(CommandLineState *s) { if (ccline.cmdpos != s->prev_cmdpos) { - trigger_cmd_autocmd(get_cmdline_type(), EVENT_CURSORMOVEDC); + trigger_cmd_autocmd(s->cmdline_type, EVENT_CURSORMOVEDC); s->prev_cmdpos = ccline.cmdpos; ccline.redraw_state = MAX(ccline.redraw_state, kCmdRedrawPos); } diff --git a/test/functional/legacy/autocmd_spec.lua b/test/functional/legacy/autocmd_spec.lua @@ -91,3 +91,13 @@ it('WinScrolled and WinResized events can be ignored in a window', function() feed(':echo win_getid() g:afile g:resized g:scrolled<CR>') screen:expect({ any = '1000 1001 1 1.*' }) end) + +-- oldtest: Test_CmdlineLeavePre_cabbr() +it(':cabbr does not cause a spurious CmdlineLeavePre', function() + command('let g:a = 0') + command('cabbr v v') + command('command! -nargs=* Foo echo') + command('au! CmdlineLeavePre * let g:a += 1') + feed(':Foo v<CR>') + eq(1, api.nvim_get_var('a')) +end) diff --git a/test/old/testdir/test_autocmd.vim b/test/old/testdir/test_autocmd.vim @@ -1923,51 +1923,101 @@ endfunc func Test_Cmdline_Trigger() autocmd CmdlineLeavePre : let g:log = "CmdlineLeavePre" + autocmd CmdlineLeavePre : let g:log2 = "CmdlineLeave" new let g:log = '' + let g:log2 = '' nnoremap <F1> <Cmd>echo "hello"<CR> call feedkeys("\<F1>", 'x') call assert_equal('', g:log) + call assert_equal('', g:log2) nunmap <F1> + let g:log = '' + let g:log2 = '' nnoremap <F1> :echo "hello"<CR> call feedkeys("\<F1>", 'x') call assert_equal('CmdlineLeavePre', g:log) + call assert_equal('CmdlineLeave', g:log2) nunmap <F1> + + let g:log = '' + let g:log2 = '' + call feedkeys(":\<bs>", "tx") + call assert_equal('CmdlineLeavePre', g:log) + call assert_equal('CmdlineLeave', g:log2) + let g:log = '' + let g:log2 = '' split call assert_equal('', g:log) call feedkeys(":echo hello", "tx") call assert_equal('CmdlineLeavePre', g:log) + call assert_equal('CmdlineLeave', g:log2) + let g:log = '' + let g:log2 = '' close call assert_equal('', g:log) call feedkeys(":echo hello", "tx") call assert_equal('CmdlineLeavePre', g:log) + call assert_equal('CmdlineLeave', g:log2) + let g:log = '' + let g:log2 = '' tabnew call assert_equal('', g:log) call feedkeys(":echo hello", "tx") call assert_equal('CmdlineLeavePre', g:log) + call assert_equal('CmdlineLeave', g:log2) + let g:log = '' + let g:log2 = '' split call assert_equal('', g:log) call feedkeys(":echo hello", "tx") call assert_equal('CmdlineLeavePre', g:log) + call assert_equal('CmdlineLeave', g:log2) + let g:log = '' + let g:log2 = '' tabclose call assert_equal('', g:log) call feedkeys(":echo hello", "tx") call assert_equal('CmdlineLeavePre', g:log) + call assert_equal('CmdlineLeave', g:log2) + let g:count = 0 autocmd CmdlineLeavePre * let g:count += 1 call feedkeys(":let c = input('? ')\<cr>B\<cr>", "tx") call assert_equal(2, g:count) unlet! g:count unlet! g:log + unlet! g:log2 bw! endfunc +" Ensure :cabbr does not cause a spurious CmdlineLeavePre. +func Test_CmdlineLeavePre_cabbr() + CheckFeature terminal + let buf = term_start([GetVimProg(), '--clean', '-c', 'set noswapfile'], {'term_rows': 3}) + call assert_equal('running', term_getstatus(buf)) + call term_sendkeys(buf, ":let g:a=0\<cr>") + call term_wait(buf, 50) + call term_sendkeys(buf, ":cabbr v v\<cr>") + call term_wait(buf, 50) + call term_sendkeys(buf, ":command! -nargs=* Foo echo\<cr>") + call term_wait(buf, 50) + call term_sendkeys(buf, ":au! CmdlineLeavePre * :let g:a+=1\<cr>") + call term_wait(buf, 50) + call term_sendkeys(buf, ":Foo v\<cr>") + call term_wait(buf, 50) + call term_sendkeys(buf, ":echo g:a\<cr>") + call term_wait(buf, 50) + call WaitForAssert({-> assert_match('^2.*$', term_getline(buf, 3))}) + bwipe! +endfunc + func Test_Cmdline() au! CmdlineChanged : let g:text = getcmdline() let g:text = 0