Listing pattern-matches in the current file
:global
command
Option 1: The We can search for a pattern in the current file and list out the matching lines using the powerful global
command:
:g[lobal]/pattern/#
This prints the matching lines, along with their line numbers (#
), and prompts to enter a command.
We can then jump to one of the matches using :n<CR>
, where n
is the line-number.
However, what if we wanted to just list out the matches, and not immediately jump to any matching line? For example, a popular mapping to view the structure of a markdown file is:
nnoremap <key> :g/^#/
Now if we simply press <CR>
or <Esc>
, we find that we are lost! Our cursor is actually at the last line that matches our specified pattern.
To get back to where we were, we need to press <C-O>
. Though this doesn't sound much troublesome, it's sometimes enough to lose the context.
Option 2: Include lists
We can also use the :ilist
command to achieve a similar goal, without the above issue:
:il[ist] /pattern/#
(Note that :ilist
also lists matches from the included files by default.)
However, there is still one annoyance for our use-case: apart from the line-numbers, there is an additional entry-number listed, which, in this case, just serves one purpose: confuse us regarding which number to type, to jump to the desired entry.
Thus, for the simple need of searching a pattern, and optionally jumping to an entry, :global
seems a better friend, with one little annoyance (jump to the last match on an empty command).
:global
command
A wrapped The following function prompts for a pattern, invokes :global
, prompts for a line-number and jumps to it; and in case the user doesn't supply a line-number, it restores the cursor-position.
function! GlobalSearch() abort
" Prompt for a pattern as usual
let pattern = input(':g/')
if !empty(pattern)
" Print lines matching the pattern, with line-numbers
execute "g/" . pattern . "/#"
" The valid value of 'choice' is a line-number
let choice = input(':')
if !empty(choice)
" Jump to the entered line-number
execute choice
else
" If no choice was entered, restore the cursor position
execute "normal! \<C-O>"
endif
endif
endfunction
To call the above function using ,g
, create a map as follows:
nnoremap <silent> ,g :call GlobalSearch()<CR>
One last thing. What about the mapping we had above to list out the structure of a markdown file?
That requires the ability to pass a predefined pattern to our GlobalSearch()
function.
Let's do the same:
function! GlobalSearch(...) abort
" If no pattern was supplied, prompt for one
if a:0 == 0
let pattern = input(':g/')
else
let pattern = a:1
endif
if !empty(pattern)
" Print lines matching the pattern, with line-numbers
execute "g/" . pattern . "/#"
" The valid value of 'choice' is a line-number
let choice = input(':')
if !empty(choice)
" Jump to the entered line-number
execute choice
else
" If no choice was entered, restore the cursor position
execute "normal! \<C-O>"
endif
endif
endfunction
" Call without arguments using ,g
nnoremap <silent> ,g :call GlobalSearch()<CR>
" List the structure of a markdown file; add to ftplugin/markdown.vim
nnoremap <buffer> <key> :call GlobalSearch("^#")<CR>
" Bonus regex (magic-mode); add to ftplugin/java.vim
" list all classes and methods in a Java file (assuming each method has an explicit access-modifier)
nnoremap <buffer> <key> :call GlobalSearch("^\\s*\\(class\\\|public\\\|private\\\|protected\\).*{")<CR>
The new GlobalSearch()
function uses unnamed arguments (see :help function-argument
) to take different actions based on whether an argument was passed or not;
a:0
gives the number of arguments and a:1
gives the first argument.
Note that :global
can be used to perform a large number of things apart from the simple search-and-jump feature we targetted here.
For example, to delete the matching lines, we can use :g/pattern/d
.
The pattern can be any regular-expression in vim's regex format, and the default command is p
for 'print'. (Thus, :g/regular-expression/p
is equivalent to :g/regular-expression
).
In fact, :global/regular-expression/print
is the command from which the famous multi-file search command grep
derives its name!