Skip to content

Instantly share code, notes, and snippets.

@manasthakur
Last active September 24, 2022 13:30
Show Gist options
  • Star 44 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save manasthakur/5afd3166a14bbadc1dc0f42d070bd746 to your computer and use it in GitHub Desktop.
Save manasthakur/5afd3166a14bbadc1dc0f42d070bd746 to your computer and use it in GitHub Desktop.
Vim: Creating your own ack.vim/ag.vim

Creating your own ag.vim

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.

Set ag as the default grep program

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

Open quickfix window by default

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.

Custom mappings

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.

Grep the word under cursor

There are two ways to achieve this:

  1. Vim allows the word under the cursor to be put in the command-line mode using Ctrl-R followed by Ctrl-W. Thus, typing this sequence after :Search<CR>, and pressing Enter, will allow you to search the word under the cursor.
  2. 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.

@voyeg3r
Copy link

voyeg3r commented Jan 3, 2017

Following your tip I figured out that I did not have a snippet for new vim commands, and here we are:
#ultisnip for new vim commands

snippet 'com(mand)?' "define a new vim command" !r
command! -nargs=${1:0} ${2:command name} :${3:execution}
endsnippet

By the way, I was wondering how can I change the search function to give it a patter at once, like this:
:Search grep

@manasthakur
Copy link
Author

manasthakur commented Jan 3, 2017

Hi @voyeg3r,

snippet 'com(mand)?'

Cool 👍

:Search grep

If I understood your question correctly, you can supply the term to search by modifying
(a) the Search command to:

command! -nargs=1 Search call MySearch(<q-args>)

(b) the MySearch function to:

function! MySearch(grep_term)
" upper logic
  execute 'silent grep' a:grep_term | copen
" lower logic
endfunction

@vcavallo
Copy link

vcavallo commented Jan 3, 2017

👍 This serves as a really nice introduction to the basics of vim functions and commands, as well :)

@bjoernwibben
Copy link

bjoernwibben commented Jun 23, 2017

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.

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