]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.1872: Cmdline history not updated when mapping <Up> and <CR> v9.1.1872
authorzeertzjq <zeertzjq@outlook.com>
Sun, 26 Oct 2025 13:29:09 +0000 (13:29 +0000)
committerChristian Brabandt <cb@256bit.org>
Sun, 26 Oct 2025 13:29:09 +0000 (13:29 +0000)
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: #2771
related: neovim/neovim#36256
closes: #18607

Signed-off-by: zeertzjq <zeertzjq@outlook.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
runtime/doc/change.txt
runtime/doc/cmdline.txt
src/ex_getln.c
src/testdir/test_cmdline.vim
src/version.c

index ff64628e07f50b502ef21999b72d7c4183f389aa..878d38851d3032bb33c5f1c298b9ad395f88a3c7 100644 (file)
@@ -1,4 +1,4 @@
-*change.txt*    For Vim version 9.1.  Last change: 2025 Oct 14
+*change.txt*    For Vim version 9.1.  Last change: 2025 Oct 26
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -1324,7 +1324,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.
                {not available when compiled without the |+cmdline_hist|
                feature}
                                                        *quote_#* *quote#*
index ef13d72715bc92bec2a12b0a256aa850ea121cf2..ede9a5ec033ca16dcfabc2ca8c1fe1bdcb917307 100644 (file)
@@ -1,4 +1,4 @@
-*cmdline.txt*   For Vim version 9.1.  Last change: 2025 Oct 12
+*cmdline.txt*   For Vim version 9.1.  Last change: 2025 Oct 26
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -61,8 +61,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).
index 73f8ca22d9b64623f68ba6de5b6a1e164180ce2c..5fc9330bc9ab9aefc58953e3095a01bc6df3d4be 100644 (file)
@@ -1871,6 +1871,12 @@ getcmdline_int(
                                // 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.
+           some_key_typed = TRUE;
+
        // Trigger SafeState if nothing is pending.
        may_trigger_safestate(xpc.xp_numfiles <= 0);
 
index 58629b5fddbeef5f63e315ab50a075e3904568d2..d0987c40a0603d41f01c91e4bb47bc4afb20c22b 100644 (file)
@@ -2606,6 +2606,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
index 2526c09330fe4a2eb9c0277624d519371b9c7ba2..d20067856f8911aabbaff44b74055522d4e2f23d 100644 (file)
@@ -734,6 +734,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1872,
 /**/
     1871,
 /**/