4

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.

  • 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 Apr 20 '17 at 12:03
  • First part is quite easy: nnoremap : :<C-p>. Not sure about erasing it when pressing something else than <CR> – grodzik Apr 20 '17 at 12:12
  • Your are right, the first part is quite easy. For erase part, I have no clue yet. – qeatzy Apr 20 '17 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 Apr 20 '17 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 Apr 20 '17 at 13:00
4

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.

  • 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 Apr 22 '17 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). – Karl Yngve Lervåg Apr 22 '17 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 Apr 22 '17 at 10:31
  • besides that, it works fine. -- though, it has basically no practical problem for me. thanks. – qeatzy Apr 22 '17 at 10:33
  • Ok, I think I've fixed that minor issue now. :) – Karl Yngve Lervåg Apr 22 '17 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.

  • 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 Apr 20 '17 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 Apr 20 '17 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 Apr 20 '17 at 14:04
  • Try adding ` -N` to the command, which will activate nocompatible mode – grodzik Apr 20 '17 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 Apr 20 '17 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, privacy policy and cookie policy

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