Vim provides built-in mechanisms to search through projects in the form of the grep
command.
However, on large projects, grep is known to be slow; and hence people have been switching to simpler searchers like ack, and faster, parallel (metal?) searchers like ag and pt.
Correspondingly, several plugins have been created that integrate these tools in vim: ack.vim, ag.vim, etc.
However, it's actually very easy to get the functionalities these plugins provide (faster search, results in quickfix-window, jumps, previews, and so on) in vanilla Vim itself; in fact, Vim already populates the grep-search results in a quickfix window. We just need to tell Vim to do the following things (use-case: ag):
- Use ag as the default grep program
- Open quickfix window by default
- Create mappings to easily navigate among the search-results
So let us build our ag-based lightweight grepper. Note that ag (aka 'the silver searcher') needs to be installed on your PC.
This just requires adding the following simple lines to your vimrc:
if executable('ag')
set grepprg=ag\ --nogroup\ --nocolor
endif
That's it! The next time you do a 'grep', Vim will actually use 'ag' (if installed on your PC) and show the results blazingly fast.
If you wish to display column numbers like vimgrep
(a built-in but slightly slower grep), you can add --vimgrep
to the grepprg
option, and modify the grepformat
option to display the column numbers properly.
Thus, the updated version will look as follows:
if executable('ag')
set grepprg=ag\ --nogroup\ --nocolor\ --vimgrep
set grepformat^=%f:%l:%c:%m " file:line:column:message
endif
Let us do this in an elegant way using a function.
Say we wish to define a new command called Search
that takes a word, greps it, and shows the results in a quickfix-window.
The function to do the same can be something like this:
function! MySearch()
let grep_term = input("Enter search term: ")
if !empty(grep_term)
execute 'silent grep' grep_term | copen
else
echo "Empty search term"
endif
redraw!
endfunction
The function MySearch
:
- prompts for the term to be grepped
- checks whether the user pressed Enter without giving a search term; if yes, displays an error
- if the search-term is valid, greps it using the default grep-program
- opens the result in a quickfix-window
- redraws the screen in order to avoid any distortion
Note that you can navigate across the results of the quickfix-window using :cnext
and :cprevious
, and close it using :cclose
.
Or, you can move to the quickfix-window and jump to any of the results using the arrow (or any vim-movement) keys and pressing Enter.
The next task is to define a command to call the above function:
command! Search call MySearch()
Done! Now just type :Search
in Vim's command-line, and press Enter to grep a term using ag.
You can also create a custom mapping for the Search command:
nnoremap <leader>s :Search<CR> " Default leader is '\' (backslash)
If you have used ack.vim and ag.vim, and want custom mappings for the quickfix-window, such as closing it using q
and jumping to a result directly whie closing the quickfix-window using O
, then add the following mappings to the file .vim/ftplugin/qf.vim:
nnoremap <buffer><silent> q :cclose<CR>
nnoremap <buffer><silent> O <CR>:cclose<CR>
You can similarly create maps to open a result in a new tab, in a new split, etc., as the plugins do, if you need.
There are two ways to achieve this:
- Vim allows the word under the cursor to be put in the command-line mode using
Ctrl-R
followed byCtrl-W
. Thus, typing this sequence after:Search<CR>
, and pressing Enter, will allow you to search the word under the cursor. - The word under the cursor can be referred using
<cword>
in Vimscript. Thus, you can grep the word under the cursor using the map<leader>*
as follows:
nnoremap <leader>* :silent grep <cword> \| copen<CR><C-l> " <C-l> redraws the screen
This finishes your personal, Vim-native, lightweight ag.vim script.
References: :help grepprg
, :help grepformat
, :help quickfix
.
If you get the Error E194, for example if you search for a CSS color with a #-Sign you have to escape the grep_term:
execute 'silent grep' escape(grep_term, "%#!") | copen
Now you can search for special characters too.