vim-patch:9.1.1872: Cmdline history not updated when mapping <Up> and <CR> (#36334)

Problem:  Cmdline history not updated when mapping both <Up> and <CR>.
Solution: Consider the command typed when in Cmdline mode and there is
          no pending input (zeertzjq).

Although the existing behavior technically does match documentation, the
"completely come from mappings" part is a bit ambiguous, because one may
argue that the command doesn't completely come from mappings as long as
the user has typed a key in Cmdline mode.  I'm not entirely sure if this
change will cause problems, but it seems unlikely.

fixes: vim/vim#2771
related: neovim/neovim#36256
closes: vim/vim#18607

97b6e8b424
This commit is contained in:
zeertzjq
2025-10-26 22:19:32 +08:00
committed by GitHub
parent 07461bac27
commit 2407833ba1
4 changed files with 78 additions and 3 deletions

View File

@@ -1288,7 +1288,7 @@ and ":put" commands and with CTRL-R.
"@:" to repeat the previous command-line command.
The command-line is only stored in this register when at least
one character of it was typed. Thus it remains unchanged if
the command was completely from a mapping.
the command was executed completely from a mapping.
*quote_#* *quote#*
6. Alternate file register "#

View File

@@ -57,8 +57,8 @@ Notes:
- When you enter a command-line that is exactly the same as an older one, the
old one is removed (to avoid repeated commands moving older commands out of
the history).
- Only commands that are typed are remembered. Ones that completely come from
mappings are not put in the history.
- Only commands that are typed are remembered. A command executed completely
from a mapping is not put in the history.
- All searches are put in the search history, including the ones that come
from commands like "*" and "#". But for a mapping, only the last search is
remembered (to avoid that long mappings trash the history).

View File

@@ -1038,6 +1038,13 @@ static int command_line_check(VimState *state)
// that occurs while typing a command should
// cause the command not to be executed.
if (stuff_empty() && typebuf.tb_len == 0) {
// There is no pending input from sources other than user input, so
// Vim is going to wait for the user to type a key. Consider the
// command line typed even if next key will trigger a mapping.
s->some_key_typed = true;
}
// Trigger SafeState if nothing is pending.
may_trigger_safestate(s->xpc.xp_numfiles <= 0);

View File

@@ -2676,6 +2676,74 @@ func Test_recalling_cmdline()
cunmap <Plug>(save-cmdline)
endfunc
func Test_recalling_cmdline_with_mappings()
CheckFeature cmdline_hist
cnoremap <F2> <Cmd>let g:cmdline = getcmdline()<CR>
cnoremap <CR> <CR>
cnoremap <Up> <Up>
let save_a = ['a', getreg('a'), getregtype('a')]
call feedkeys(":echo 'foo'\<CR>", 'tx')
call assert_equal("echo 'foo'", @:)
call feedkeys(":echo 'bar'\<CR>", 'tx')
call assert_equal("echo 'bar'", @:)
call assert_equal("echo 'bar'", histget(':', -1))
call assert_equal("echo 'foo'", histget(':', -2))
" This command comes completely from a mapping.
nmap <F3> :echo 'baz'<F2><CR>
call feedkeys("\<F3>", 'tx')
call assert_equal('baz', Screenline(&lines)->trim())
call assert_equal("echo 'baz'", g:cmdline)
call assert_equal("echo 'bar'", @:)
call assert_equal("echo 'bar'", histget(':', -1))
call assert_equal("echo 'foo'", histget(':', -2))
if has('unix')
new
call setline(1, ['aaa'])
setlocal formatprg=cat
" Formatting with non-typed "gq" should not change cmdline history.
normal! gqgq
call assert_equal(":.!cat", Screenline(&lines)->trim())
call assert_equal("echo 'bar'", @:)
call assert_equal("echo 'bar'", histget(':', -1))
call assert_equal("echo 'foo'", histget(':', -2))
bwipe!
endif
" This case can still be considered a typed command.
call timer_start(1, {-> feedkeys("\<CR>", 't')})
call feedkeys(":\<Up>\<Up>", 'tx!')
call assert_equal('foo', Screenline(&lines)->trim())
call assert_equal("echo 'foo'", @:)
call assert_equal("echo 'foo'", histget(':', -1))
call assert_equal("echo 'bar'", histget(':', -2))
call feedkeys(":\<Up>\<F2>\<Esc>", 'tx')
call assert_equal("echo 'foo'", g:cmdline)
call assert_equal("echo 'foo'", @:)
" A command from an executed register is also ignored in the history.
call feedkeys(':let @a=":echo ''zzz''\<cr>"' .. "\<CR>", 'tx')
call feedkeys(":norm @a\<cr>", 'tx')
call assert_equal('zzz', Screenline(&lines)->trim())
call assert_equal('norm @a', @:)
call assert_equal('norm @a', histget(':', -1))
call assert_equal('let @a=":echo ''zzz''\<cr>"', histget(':', -2))
call assert_equal("echo 'foo'", histget(':', -3))
call assert_equal("echo 'bar'", histget(':', -4))
unlet g:cmdline
call call('setreg', save_a)
cunmap <F2>
cunmap <CR>
cunmap <Up>
nunmap <F3>
endfunc
func Test_cmd_map_cmdlineChanged()
let g:log = []
cnoremap <F1> l<Cmd><CR>s