Built-in features from low to high level.
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 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
By using :# (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
:12
to jump to the desired line.
This doesn't look like much but :g/pattern/# is an immensely useful tool to have in anyone's toolbox. No setup, no dependency, no third-party plugin… it just works!
As is, this command requires us to type:
:g/before ourpattern,/#<CR>after ourpattern,- and
:followed by 1 to infinite digits, folowed by<CR>.
The pattern and the line number can't be known in advance, of course, be we can certainly simplify the rest.
Starting with :g/pattern/#<CR>, it's relatively easy to come up with a simple mapping:
nnoremap <key> :g//#<Left><Left>
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 <CR>:
:g/|/#
Executing our search is now reduced to <key> + pattern + <CR>, which is not bad at all. But what about the prompt? What if we could reduce : + 10000 + <CR>?
This problem is a bit more complex than the other but it's very doable with a bit of vimscript:
function! CCR()
let cmdline = getcmdline()
if cmdline =~ '\v\C/(#|nu|num|numb|numbe|number)$'
return "\<CR>:"
else
return "\<CR>"
endif
endfunction
cnoremap <expr> <CR> CCR()
The idea, here, is to automatize the <CR> then : sequence when the command we typed (or ran through a mapping) ends with /# or any abbreviation of /number.
We are now down to <key> + pattern + <CR> + digits + <CR>. Yes. That's only three motherfucking keystrokes for the whole process!
" make list-like commands more intuitive
function! ccr#CCR()
let cmdline = getcmdline()
if cmdline =~ '\v\C^(ls|files|buffers)'
" like :ls but prompts for a buffer command
return "\<CR>:b"
elseif cmdline =~ '\v\C/(#|nu|num|numb|numbe|number)$'
" 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 =~ '\v\C^(cli|lli)'
" like :clist or :llist but prompts for an error/location number
return "\<CR>:sil " . repeat(cmdline[0], 2) . "\<Space>"
elseif cmdline =~ '\C^old'
" like :oldfiles but prompts for an old file to edit
set nomore
return "\<CR>:sil se more|e #<"
elseif cmdline =~ '\C^changes'
" like :changes but prompts for a change to jump to
set nomore
return "\<CR>:sil se more|norm! g;\<S-Left>"
elseif cmdline =~ '\C^ju'
" like :jumps but prompts for a position to jump to
set nomore
return "\<CR>:sil se more|norm! \<C-o>\<S-Left>"
elseif cmdline =~ '\C^marks'
" like :marks but prompts for a mark to jump to
return "\<CR>:norm! `"
elseif cmdline =~ '\C^undol'
" like :undolist but prompts for a change to undo
return "\<CR>:u "
else
return "\<CR>"
endif
endfunction
:g/foo<CR>is already displaying line numbers for me, even without appending/#afterwards..