3

As the title stated, remap ':' to maintain last command, and erase all char if any key other than <CR> was pressed.

possible use cases:

  • I have typed a long command, and I may or may not modified it, then re-run it.
  • I save a lot, so I want type :, then visually verify :w is there, then type <CR> to proceed. -- I have remapped :saveas<CR> to <Leader>s.

things that are not what I pursue:

  • I know about q: or <C-f> in cmdline mode to open the mini buffer to edit command.
  • I know type @: then @@ to repeat command.
  • I know type <C-p> or <Up> in cmdline mode.

a little more on motivation:

Repeat with register/macro is great and is really handy for some task.

But here I want a more consistent and safer (visually verification/feedback) way to re-run last command, with fewer key stroke.

Is that possible? or not trivially implementable at all??

======== EDIT ========

Based on @Karl's comment, I think the problem could be implemented via a function definition and a nnoremap. The function consist three parts:

  1. echo last command getreg(':'), then get char with getchar

  2. if char == '^M', re-execute last command.

  3. otherwise, open command line and act accordingly.

My first attempt is as below: -- (based on @grodzik's answer and @Karl's idea in the comment)

func! Show_last_command_and_exec()
    echo ':'.getreg(':')
    try
        let char = getchar()
    catch /^Vim:Interrupt$/
        let char = "\<Esc>"
    endtry
    if char == "^M"
        exec getreg(':')
        " not work, don't know why, tried with previous comand be `:pwd`
    else
        " not work, don't know which function or command should be used.
        " below are failed attempt.
        " normal! :
        " call feedkeys(char)
        " exec ':'.char
    endif
endfunc

nnore : :call Show_last_command_and_exec()<CR>

But unfortunately, except the trivial first echo part, neither work yet. I post my attempt (though failed currently) here, hope someone interested may implement a better one that work nicely.

P.S. as @grodzik said in his answer, the "^M" should be replaced with real <CR> sign.

8
  • P.P.S. I observed the stated behavior in evil-mode of emacs, and thought, cool, how can I re-implement it with vimscript so I can use it in vim.
    – qeatzy
    Commented Apr 20, 2017 at 12:03
  • First part is quite easy: nnoremap : :<C-p>. Not sure about erasing it when pressing something else than <CR>
    – grodzik
    Commented Apr 20, 2017 at 12:12
  • Your are right, the first part is quite easy. For erase part, I have no clue yet.
    – qeatzy
    Commented Apr 20, 2017 at 12:41
  • How about using <C-u> to erase whole line in Ex-mode? I know it's extra key press, but auto erasing seems impossible.
    – grodzik
    Commented Apr 20, 2017 at 12:57
  • yeah, I also doubt I was over-engineer here. Or I should just be happy with <C-p>, <C-u> and <C-f> in cmdline mode.
    – qeatzy
    Commented Apr 20, 2017 at 13:00

3 Answers 3

3

The following should solve your issue:

nnoremap <expr> : ShowLastCmd()

function! ShowLastCmd()
  echohl ModeMsg
  echo ':' . getreg(':')
  echohl None

  while 1
    try
      let char = getchar()
      if char == '^\d\+$' || type(char) == 0
        let char = nr2char(char)
      endif
    catch /^Vim:Interrupt$/
      return ''
    endtry

    if char == "\<cursorhold>"
      continue
    elseif char == "\<cr>"
      return ':' . getreg(':') . "\<cr>"
    else
      return ':' . char
    endif
  endwhile
endfunction

The while loops is there to catch the <cursorhold> key event.

5
  • thx Karl, It works like a charm, really nice!! A minor note, when last command's echo contents exceed one page, eg reg, then press : do not show last command. -- is that a case of <cursorhold> or vim in a different mode other than normal mode? P.S. I do read :h <cursorhold> and CursorHold-example, and find it not very helpful here for me.
    – qeatzy
    Commented Apr 22, 2017 at 3:03
  • I don't quite understand what you mean. If I do :reg, enter, then do :, there is not problem. I've updated the code to fix the problem with <cursorhold> (i.e., if you did : and waited with the old version it would not do what you wanted). Commented Apr 22, 2017 at 7:03
  • sorry for the unclearness, the problem has nothing to do with :reg, the problem is when last command echo many contents -- there is --More-- at the bottom of echo erea -- eg, open 100+ file then type :ls. then it fails. P.S. or some option matters here?
    – qeatzy
    Commented Apr 22, 2017 at 10:31
  • besides that, it works fine. -- though, it has basically no practical problem for me. thanks.
    – qeatzy
    Commented Apr 22, 2017 at 10:33
  • Ok, I think I've fixed that minor issue now. :) Commented Apr 22, 2017 at 17:23
1

After a bit of hacking, I actually found a way to achive that:

function! AutoErase()
  setlocal cursorline
  normal Gk
  redraw!
  try
    let char = getchar()
  catch /^Vim:Interrupt$/
    let char = "\<Esc>"
  endtry
  if char == '^\d\+$' || type(char) == 0
    let char = nr2char(char)
  endif " It is the ascii code.
  if char == "^M"
    call feedkeys('^M')
  else
    execute "normal Gi" . char
    startinsert!
  endif
endfunction

autocmd CmdwinEnter * call AutoErase()
nnoremap : q:

This will show Command Window though and command to be executed when pressed Enter is the one highlited. Also, it may be required to change in .vimrc signs with ^M into real <CR> signs with CtrlvEnter.

5
  • thanks for your reply, I copy the code into .vimrc and start vim with vim -u .vimrc, also I do change ^M to true ascii <CR>, and it not work correctly. type : open Command Window immediately.
    – qeatzy
    Commented Apr 20, 2017 at 14:00
  • then I type :pwd and excute. after that, when I type : again, last command was not show, just the Command Window open.
    – qeatzy
    Commented Apr 20, 2017 at 14:01
  • thanks for your help, really helpful and eye-opening, but for this specific problem, I think I should keep it simple, and do with builtin key, since vim is already efficient enough, at least for my own need.
    – qeatzy
    Commented Apr 20, 2017 at 14:04
  • Try adding ` -N` to the command, which will activate nocompatible mode
    – grodzik
    Commented Apr 20, 2017 at 14:05
  • my bad, the compatible mode!! thanks, works as stated, nice job! P.S,, for the use case, I might still stay away from that, since IMO open Command Window as the default is too intrusive.
    – qeatzy
    Commented Apr 20, 2017 at 14:11
0

Here's a very short mapping that will achieve roughly what you're asking, by using the command-line window and select mode:

nnoremap : :<Up><C-F>gH

It recalls the previous command with <Up>, opens the command-line window with <C-F> and then enters linewise select mode with gH.

If you then press Enter then the command is run, but if you type anything else, the current selection will be replaced.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.