---
title: Silver Searcher Tab Completion with Exuberant Ctags
teaser: 'Improve your Silver Searcher game by introducing tab completion in zsh based
  on the tags file of the project.

  '
tags: unix,shell
author: Josh Clayton
published_on: 2014-08-11
---

I'm a heavy Vim user and demand speedy navigation between files. I rely on
[Exuberant Ctags][ctags] and [tag navigation][tag-navigation] (usually
<kbd>Ctrl-]</kbd>) to move quickly around the codebase.

There were times, however, when I wasn't in Vim but wanted to use tags to
access information quickly; most noticeable was time spent in my shell,
searching the codebase with [`ag`][ag].

As a [`zsh`][zsh] user, I was already aware of introducing tab completion by way
of [`compdef`][compdef] and [`compadd`][compadd]:

```zsh
_fn_completion() {
  if (( CURRENT == 2 )); then
    compadd foo bar baz
  fi
}

compdef _fn_completion fn
```

In this example, `fn` is the binary we want to add tab completion to, and we
only attempt to complete after typing `fn ` and then <kbd>TAB</kbd>. By checking
`CURRENT == 2`, we're verifying the position of the cursor as the second field
in the command. This will complete with options `foo`, `bar`, and `baz`, and
filter the options accordingly as you start typing and hit <kbd>TAB</kbd> again.

Now that we understand how to configure tab completion for commands, next up is
determining how to extract useful information from the `tags` file. Here's the
first few lines of the file from a project I worked on recently:

```ctags
==      ../app/models/week.rb   /^  def ==(other)$/;"   f       class:Week
AccessToken     ../app/models/access_token.rb   /^class AccessToken < ActiveRecord::Base$/;"    c
AccessTokensController  ../app/controllers/access_tokens_controller.rb  /^class AccessTokensController < ApplicationController$/;"      c
```

The tokens we want to use for tab completion are the first set of characters per
line, so we can use `cut -f 1 path/to/tags` to grab the first field. We then use
`grep -v` to ignore autogenerated ctags metadata we don't care about.  With a
bit of extra work (like writing `stderr` to `/dev/null` in the instance where
the `tags` file doesn't exist yet), the end result looks like this:

```zsh
_ag() {
  if (( CURRENT == 2 )); then
    compadd $(cut -f 1 .git/tags tmp/tags 2>/dev/null | grep -v '!_TAG')
  fi
}

compdef _ag ag
```

With this in place, we can now `ag` a project and tab complete from the
generated `tags` file. With `ag Acc`<kbd>TAB</kbd>:

    $ ag AccessToken
    AccessToken             AccessTokensController

And the result:

    [ ~/dev/thoughtbot/project master ] ✔ ag AccessToken
    app/controllers/access_tokens_controller.rb
    1:class AccessTokensController < ApplicationController
    15:    @project = AccessToken.find_project(params[:id])

    app/models/access_token.rb
    1:class AccessToken < ActiveRecord::Base

    db/migrate/20140416195446_create_access_tokens.rb
    1:class CreateAccessTokens < ActiveRecord::Migration

    db/migrate/20140718175701_add_index_on_access_tokens_project_id.rb
    1:class AddIndexOnAccessTokensProjectId < ActiveRecord::Migration

    spec/models/access_token_spec.rb
    3:describe AccessToken, 'Associations' do
    7:describe AccessToken, '.find_project' do
    12:      result = AccessToken.find_project(access_token.to_param)
    20:      expect { AccessToken.find_project('unknown') }.
    26:describe AccessToken, '.generate' do
    40:describe AccessToken, '#to_param' do
    50:    expect(AccessToken.find(access_token.to_param)).to eq(access_token)

Voila! Tab completion with `ag` based on the `tags` file.

If you're using [thoughtbot's dotfiles][dotfiles], you already have [this
behavior][ag-completion].

[ctags]: http://ctags.sourceforge.net/
[tag-navigation]: http://vim.wikia.com/wiki/Browsing_programs_with_tags
[ag]: http://geoff.greer.fm/ag/
[compdef]: http://zsh.sourceforge.net/Doc/Release/Completion-System.html
[compadd]: http://zsh.sourceforge.net/Doc/Release/Completion-Widgets.html
[zsh]: http://www.zsh.org/
[dotfiles]: https://github.com/thoughtbot/dotfiles
[ag-completion]: https://github.com/thoughtbot/dotfiles/blob/master/zsh/completion/_ag
