# The situation Searching can be an efficient way to navigate the current buffer. The first search commands you learn are usually `/` and `?`. These are seriously cool, especially with the `incsearch` option enabled which lets us keep typing to refine our search pattern. But `/` and `?` really shine when all you want is to jump to something you already have your eyeballs on but they are not fit for *every* situation: * when you want to search something that's not directly there, those two commands can make you loose context very quickly, * when you need to be able to compare the matches. # A better way A better candidate for those situations would be the awesome `:g[lobal]/pattern/command` but it uses the `:p[rint]` command by default, which is not that useful: :g/foo fqjsfd foo foo dhqdgqs // foo shdjfksgdf Press ENTER or type command to continue With `:#` (or `:nu[mber]`), we ask Vim to also display the line numbers that we can then use at the prompt: :g/foo/# 3 fqjsfd foo 12 foo dhqdgqs 13 // foo shdjfksgdf Press ENTER or type command to continue :12 to jump to the desired line. This doesn't look like much but `:g/pattern/#` is an immensely useful tool that deserves its place in everyone's toolbox. No setup, no dependency, no third-party plugin… it just works! And will always do. # A *better* better way As is, this command requires us to type: * `:g/` before our `pattern`, * `/#` after our `pattern`, * and `:` followed by 1 to infinite digits, folowed by ``. The pattern and the line number can't be known in advance, of course, but we can certainly simplify the rest. Starting with `:g/pattern/#`, it's relatively easy to come up with a simple mapping: nnoremap :g//# that populates the command-line with a command stub and moves the cursor between the slashes, ready for us to type the pattern and press ``: :g/|/# Executing our search is now reduced to `` + pattern + ``, which is not bad at all. But what about the prompt? What if we could reduce `:` + digits + ``? This problem is a bit more complex but it's very doable with a bit of straightforward vimscript: function! CCR() let cmdline = getcmdline() if cmdline =~ '\v\C/(#|nu|num|numb|numbe|number)$' return "\:" else return "\" endif endfunction cnoremap CCR() The idea is to automatize the `` then `:` sequence when the command we typed (or ran through a mapping) ends with `/#` or any abbreviation of `/nu[mber]`. We are now down to `` + pattern + `` + digits + ``. Yes, that's a "core" of only **three** motherfucking keystrokes for the whole process! # Generalizing Sure, streamlining that boring (but mighty) `:g/foo/#:17` was quite an achievement but we learned a lot of similar list-like commands in the meantime: `:ls`, `:changes`, `:ilist` and so on. What if we could add an automatic prompt for *all of them*? Well, we already have a solid foundation, so let's augment it a bit: function! CCR() let cmdline = getcmdline() if cmdline =~ '\v\C/(#|nu|num|numb|numbe|number)$' return "\:" elseif cmdline =~ '\v\C^(ls|files|buffers|dli|il|cli|lli|old|changes|ju|marks|undol)' return "\:" else return "\" endif endfunction This seems to work: pressing `` after any of those commands executes the command *and* populates the command-line with a minimalist prompt. Another achievement unloc… Wait! Wait! Wait! Nope. That's acually a super dumb way to treat our "problem"! Why? Because all of those commands actually have different prompts. Sure `#` being all about listing lines, the only reasonable prompt is a colon, but `:ls` lists buffers so the right prompt is `:b`, `:changes` lists entries in the change list so the right prompt is `:norm! g;`, and so on. Don't panic! We only have to add a bunch of conditions to our test. That's all. Hmm… and figure out the right prompt for each command: | Command | Prompt (`*` marks the desired cursor position) | |-----------------------------|------------------------------------------------| | `:#`, `:nu[mber]` | `:*` | | `:ls`, `:files`, `:buffers` | `:b*` | | `:il[ist]` | `:ij[ump] * pattern` | | `:dli[st]` | `:dj[ump] * pattern` | | `:cl[ist]` | `:cc *` | | `:lli[st]` | `:ll *` | | `:old[files]` | `:e[dit] #<*` | | `:changes` | `:norm[al]! *g;` | | `:ju[mps]` | `:norm[al]! *` | | `:marks` | `:norm[al]! '*` | | `:undol[ist]` | `:u[ndo] *` | Yeah, some of those are a bit unintuitive. That's one more reason for streamlining the whole thing, right? And now, the glorious (and commented) result: " make list-like commands more intuitive function! CCR() let cmdline = getcmdline() if cmdline =~ '\v\C^(ls|files|buffers)' " like :ls but prompts for a buffer command return "\:b" elseif cmdline =~ '\v\C/(#|nu|num|numb|numbe|number)$' " like :g//# but prompts for a command return "\:" elseif cmdline =~ '\v\C^(dli|il)' " like :dlist or :ilist but prompts for a count for :djump or :ijump return "\:" . cmdline[0] . "j " . split(cmdline, " ")[1] . "\\" elseif cmdline =~ '\v\C^(cli|lli)' " like :clist or :llist but prompts for an error/location number return "\:sil " . repeat(cmdline[0], 2) . "\" elseif cmdline =~ '\C^old' " like :oldfiles but prompts for an old file to edit set nomore return "\:sil se more|e #<" elseif cmdline =~ '\C^changes' " like :changes but prompts for a change to jump to set nomore return "\:sil se more|norm! g;\" elseif cmdline =~ '\C^ju' " like :jumps but prompts for a position to jump to set nomore return "\:sil se more|norm! \\" elseif cmdline =~ '\C^marks' " like :marks but prompts for a mark to jump to return "\:norm! `" elseif cmdline =~ '\C^undol' " like :undolist but prompts for a change to undo return "\:u " else return "\" endif endfunction cnoremap CCR() Basically, that mapping and its associated function don't really change anything fundamental. We still use `:ilist` or `:oldfiles` as we used to, **but we don't have to type a different prompt for every command anymore.** All we did was reducing friction and have a lot of fun in the process. OK, but we still have only **one** mapping, for `:g//#`. Why don't we create mappings for all those commands? Sure you can go on a hunt for available keys and create mappings for just about every command but that will make a lot to remember for commands you don't use that much anyway.