Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Vim autoreply

A modified version of Romain's gist.

Instead we take the first expansion of what looks to be the command* with getcompletion(). This gives us the proper command name which we can match against literally. Note this is first filtered to ensure the match is case sensitive as getcompletion() respects ignorecase.

This also fixes what could be considered a minor bug at the time of writting in the original the dlist and ilist mapping. The pattern may have spaces so rather than increment the cursor backwards move it to the start then increment it forwards to the correct destination.

Rather than always having to return a carriage return and whatever else in an expression I've opted to use an autocmd.

* This doesn't work with things like :g/foo/# and to make that work would require a lot more logic. Instead I take the easy way out and just do nnoremap <key> :global //#<Left><Left> instead.

function! AutoReply(cmdline)
  let previous_cmd  = getcompletion(a:cmdline[0], 'command')
  if &ignorecase
    call filter(previous_cmd, "v:val !~# previous[0]")
  endif
  let previous_cmd  = get(previous_cmd, 0, '')
  let previous_args = a:cmdline[1:]

  if previous_cmd ==# 'global'
    call feedkeys(':', 'n')
  elseif previous_cmd ==# 'undolist'
    call feedkeys(':undo' . ' ', 'n')
  elseif previous_cmd ==# 'oldfiles'
    call feedkeys(':edit #<', 'n')
  elseif previous_cmd ==# 'marks'
    call feedkeys(':normal! `', 'n')

  elseif previous_cmd ==# 'changes'
    call feedkeys(':normal! g;', 'n')
    call feedkeys("\<S-Left>", 'n')
  elseif previous_cmd ==# 'jumps'
    call feedkeys(':normal!' . ' ', 'n')
    call feedkeys("\<C-O>\<S-Left>", 'n')

  elseif index(['ls', 'files', 'buffers'], previous_cmd) != -1
    call feedkeys(':buffer' . ' ', 'n')
  elseif index(['clist', 'llist'], previous_cmd) != -1
    call feedkeys(':silent' . ' ' . repeat(previous_cmd[0], 2) . ' ', 'n')
  elseif index(['dlist', 'ilist'], previous_cmd) != -1
    call feedkeys(':' . previous_cmd[0] . 'jump' . ' ' . join(previous_args), 'n')
    call feedkeys("\<Home>\<S-Right>\<Space>", 'n')
  endif
endfunction

augroup AutoReply
  autocmd!
  autocmd CmdlineLeave :
    \ let s:cmdline = getcmdline()
    \ | if !empty(s:cmdline)
    \ |   call AutoReply(split(s:cmdline))
    \ | endif
augroup END
@benknoble

This comment has been minimized.

Copy link

benknoble commented Jan 5, 2020

One way to handle :g/foo/# is to take split(previous_cmdline, '\A')[0]. Technically, a :command may have digits in the name, but no builtins that I'm aware of do, and no plugins that I use do.

The only problem is that entering, say, :23 fails, since 'split('23', '\A') == []

@benknoble

This comment has been minimized.

Copy link

benknoble commented Jan 5, 2020

Ok, so I added

  • :tags and :registers replies
  • cancellable replies (i.e., if, after :g /foo/#, you don't want to do :23, you can just use <Esc> or <C-c> as normal, and we won't stick you in an infinite loop)

https://gist.github.com/benknoble/d53208c6f1ad8f2130fd706c9cdbd006

@zekzekus

This comment has been minimized.

Copy link

zekzekus commented Apr 24, 2020

It seems neovim implemented this CmdlineLeave differently because histget('cmd', -1) does not return the same thing with vim. I tried to use getcmdline() and as far as I tried, it worked for both vim and neovim. For your information.

@george-b

This comment has been minimized.

Copy link
Owner Author

george-b commented Apr 25, 2020

Thanks for the report @zekzekus, I've updated the gist as getcmdline() better reflects the fact that CmdLineLeave actually fires just before leaving the command-line.

As a result of this in my personal config I've had to alter a "chained" mapping. Before I had the rather common.

nnoremap gb :ls<CR>:buffer<Space>

But with the update the autocmd now fires for this, whereas before it did not. As such I've since changed it to the following.

nnoremap :noautocmd ls<CR>:buffer<Space>

Note I've not opted to have my mapping just execute :ls and rely on this gist to reply with the pre-populated command-line of :buffer. This may be something to bare in mind.

@josefson

This comment has been minimized.

Copy link

josefson commented May 25, 2020

Tried using it as opposite to romain's CCR because it allowed me to nnoremap ,c :changes<CR>. Whereas with romain's i didn't know how to do that.
However, it collided with other stuff that used CmdLineLeave such as the builtin method motions for python filetypes for instance.
So if in visual mode i try to any method motion [m [M ]m ]M, it will jump to that motion but also it will stop visual selection in its tracks by ':' in command-line-mode.

@george-b

This comment has been minimized.

Copy link
Owner Author

george-b commented May 25, 2020

This should already be fixed in the latest revision.

I suspect you have an older copy where get() was still returning its default of 0 in the failed case, rather than the empty string as it does now. The former results in the the first comparison against previous_cmd evaluating to true. This is because in Vimscript when comparing a number to a string the string is converted to the value 0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.