Skip to content

Instantly share code, notes, and snippets.

@romainl
Last active October 29, 2022 19:26
Show Gist options
  • Save romainl/5b2cfb2b81f02d44e1d90b74ef555e31 to your computer and use it in GitHub Desktop.
Save romainl/5b2cfb2b81f02d44e1d90b74ef555e31 to your computer and use it in GitHub Desktop.
Make various list-like commands more intuitive
" Background here: https://gist.github.com/romainl/047aca21e338df7ccf771f96858edb86
" with help from https://github.com/teoljungberg
function! CCR()
let cmdline = getcmdline()
let filter_stub = '\v\C^((filt|filte|filter) .+ )*'
command! -bar Z silent set more|delcommand Z
if getcmdtype() !~ ':'
return "\<CR>"
endif
if cmdline =~ filter_stub . '(ls|files|buffers)$'
" like :ls but prompts for a buffer command
return "\<CR>:b"
elseif cmdline =~ '\v\C/(#|nu|num|numb|numbe|number|l|li|lis|list)$'
" like :g//# but prompts for a command
return "\<CR>:"
elseif cmdline =~ filter_stub . '(\%)*(#|nu|num|numb|numbe|number|l|li|lis|list)$'
" like :g//# but prompts for a command
return "\<CR>:"
elseif cmdline =~ '\v\C^(dli|il)'
" like :dlist or :ilist but prompts for a count for :djump or :ijump
return "\<CR>:" . cmdline[0] . "j " . split(cmdline, " ")[1] . "\<S-Left>\<Left>"
elseif cmdline =~ filter_stub . '(cli)'
" like :clist or :llist but prompts for an error/location number
return "\<CR>:sil cc\<Space>"
elseif cmdline =~ filter_stub . '(lli)'
" like :clist or :llist but prompts for an error/location number
return "\<CR>:sil ll\<Space>"
elseif cmdline =~ filter_stub . 'old'
" like :oldfiles but prompts for an old file to edit
set nomore
return "\<CR>:Z|e #<"
elseif cmdline =~ filter_stub . 'changes'
" like :changes but prompts for a change to jump to
set nomore
return "\<CR>:Z|norm! g;\<S-Left>"
elseif cmdline =~ filter_stub . 'ju'
" like :jumps but prompts for a position to jump to
set nomore
return "\<CR>:Z|norm! \<C-o>\<S-Left>"
elseif cmdline =~ filter_stub . 'marks'
" like :marks but prompts for a mark to jump to
return "\<CR>:norm! `"
elseif cmdline =~ '\v\C^undol'
" like :undolist but prompts for a change to undo
return "\<CR>:u "
elseif cmdline =~ '\v\C^tabs'
set nomore
return "\<CR>:Z| tabnext\<S-Left>"
elseif cmdline =~ '^\k\+$'
" handle cabbrevs gracefully
" https://www.reddit.com/r/vim/comments/jgyqhl/nightly_hack_for_vimmers/
return "\<C-]>\<CR>"
else
return "\<CR>"
endif
endfunction
cnoremap <expr> <CR> CCR()
@Gavinok
Copy link

Gavinok commented Dec 19, 2019

Not sure if I am missing somthing but you may want to add somthing like

if getcmdtype() isnot# ":" | return "\<CR>" | endif`

To avoid using the mappings in / or ? commands. e.g. searching for ls in a file

@romainl
Copy link
Author

romainl commented Dec 19, 2019

Looks like I've never had to search for any of those words. Thank you.

@sparkcanon
Copy link

This is really neat, thanks!
I have found a caveat with this in relation to cnoreabbrev. I have one set up like so cnoreabbrev w update, which only writes the buffer if modified. So if I :w<CR> works just like :write<CR>. The above snippet forces you to :w<space><CR> which is just extra <space> and not a big deal but I was hoping if you have some advice to achieve the previous effect where an extra <space> won't be necessary.

@romainl
Copy link
Author

romainl commented Mar 25, 2020

That trailing space is part of how abbreviations work: you press <Space> to expand your abbreviation and you get that annoying trailing <Space>. You can use <C-]> as an alternative trigger if you don't want that pesky <Space>:

Example:
   :ab hh	hello
 	    "hh<Space>" is expanded to "hello<Space>"
	    "hh<C-]>" is expanded to "hello"

@sparkcanon
Copy link

Apologies if I have not explained myself very well.
With the abbreviation cnoreabbrev w update set, If I type :w<CR>, it executes :update immediately without any additional <space>/<C-]>. No need for expansion in this case.

@romainl
Copy link
Author

romainl commented Mar 26, 2020

Are you sure :w<CR> actually executes :update? :write and :update are exactly equivalent if the buffer is changed.

Does the update time change on the file even if there was no change?

I tried your abbreviation on my end and :w<CR> is just :write<CR>: the abbreviation is never expanded.

@sparkcanon
Copy link

Yes, in my case :w<CR> just executes :update and as the :h :update suggest it only writes if the buffer is modified.

  • If the buffer is modified, :w<CR> expands to :update and writes the buffer, :ls shows accurate update time.
  • If the buffer is not modified, :w<CR> expands to :update does not write the buffer, :ls shows accurate update time.

I tried this with vim -u NONE and can reproduce.
I am running 8.2.400.

@romainl
Copy link
Author

romainl commented Mar 26, 2020

I can't reproduce your issue (8.2.319 and 8.0.503).

w is never expanded when I press <CR> so :w<CR> is just regular :w<CR>. Could there be a trailing/leading <Space> somewhere?

@sparkcanon
Copy link

If you've got your <CR> mapped to the snippet from this gist, try disabling it. Because that's what causes this small issue with cnoreabbrev.

Nope, no spaces involved. I tried wiping my entire vimrc as well as vim -u NONE, to the same effect. Auto expansion on <CR>.

That's okay, perhaps, I am missing something on my part. I'll spend some time figuring this out and report back once I find out what the hell is going on.

@sparkcanon
Copy link

sparkcanon commented Apr 12, 2020

I changed line 40 to return "\<C-]>\<CR>" to get around my issue. Tested it for a while, haven't noticed any issues.
Also added space on line 15 '\v\C^(dli|il)\ ' so that if I accidentally don't input a search term after li/dli, it won't be stuck in an error loop.

@lamtt77
Copy link

lamtt77 commented Dec 9, 2020

Thank you for a very nice ccr.vim command line consolidation!

I have just found that the latest changes:
if getcmdtype() != ':' return "\<CR>" endif
caused a conflict with this insert mode mapping:
inoremap <expr> <cr> pumvisible() ? "\<C-y>\<cr>" : "\<cr>"

Comment this out will work fine, except that now it will have same issue with /ls .

Change to the following seems fix both issues:
if getcmdtype() != ':' return "\<Esc>" endif

@craigmac
Copy link

To list tabpages and then setup the command line to jump to a tabpagenr add another elseif condition with:

elseif cmdline=~# '\C^tabs | set nomore | return "\<CR>:Z| tabnext\<S-Left>"

@romainl
Copy link
Author

romainl commented Feb 22, 2022

@craigmac added, thank you.

I also added support for :list to that silly n|nu|… pattern that I love and is getting sillier and sillier.

FWIW, my local version also supports :filter for most cases and also deals with abbreviations more gracefully. I should update the gist to reflect those changes.

@romainl
Copy link
Author

romainl commented Mar 9, 2022

Latest update:

  • handles :filter for supported commands, think :filt rb old,
  • :clist and :llist are now on separate branches to make handling:filter easier,
  • command-line abbreviations are now handled properly.

Thank you all for your comments.

@craigmac
Copy link

craigmac commented Apr 4, 2022

Another little annoyance: if you use alternate file # in, for example, :buffer #<CR> it switches the buffer then puts : on the command-line, which is not what we want. Same thing happens with :edit #<CR>. I see that the issue is one condition which checks for #|num|... and returns "\<CR>:". I'm trying to think why the # is checked for there, and if it breaks any cases if I remove it. On further examination, is that line missing a ^ so that it wouldn't pick up a # in the case of :buffer #<CR>?

@romainl
Copy link
Author

romainl commented Apr 4, 2022

@craigmac :# is a synonym of :number so it has to be taken into account. As of now, line 14 is triggered by commands like :g/foo/#, which I use quite often.
The pattern could definitely be improved or I could !! a second pattern.

@romainl
Copy link
Author

romainl commented Apr 18, 2022

@craigmac looking at it again, :buffer #<CR> and :edit #<CR> seem to work fine on my end.

  • The pattern on line 14 has a / before the # so it shouldn't match against :<command> #.
  • The pattern on line 17 starts with \v\C^((filt|filte|filter) .+ )* so it shouldn't match either.

Do you use the latest version of this gist?

@craigmac
Copy link

@romainl I re-copied and tried it out, only to realize I had my own version of the command overriding this one (which had a syntax error causing my bug). Apologies, it works fine as is!

@romainl
Copy link
Author

romainl commented Apr 19, 2022

@craigmac No problem!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment