diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index 89556a350d..8a703fc3a2 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -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 "# diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index 7ddd472ef0..208e2b926a 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -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). diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index fa2773cea0..3f1b1dfe62 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -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); diff --git a/test/old/testdir/test_cmdline.vim b/test/old/testdir/test_cmdline.vim index 08518af1ed..a5704c1b30 100644 --- a/test/old/testdir/test_cmdline.vim +++ b/test/old/testdir/test_cmdline.vim @@ -2676,6 +2676,74 @@ func Test_recalling_cmdline() cunmap (save-cmdline) endfunc +func Test_recalling_cmdline_with_mappings() + CheckFeature cmdline_hist + + cnoremap let g:cmdline = getcmdline() + cnoremap + cnoremap + let save_a = ['a', getreg('a'), getregtype('a')] + + call feedkeys(":echo 'foo'\", 'tx') + call assert_equal("echo 'foo'", @:) + call feedkeys(":echo 'bar'\", '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 :echo 'baz' + call feedkeys("\", '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("\", 't')}) + call feedkeys(":\\", '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(":\\\", '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''\"' .. "\", 'tx') + call feedkeys(":norm @a\", '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''\"', 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 + cunmap + cunmap + nunmap +endfunc + func Test_cmd_map_cmdlineChanged() let g:log = [] cnoremap ls