Git Hooks and Haskell Tags

Categories

git hooks and tags

I’ve been using Tim Pope’s effortless ctags with git blog post to keep my tags file up to date. The gist of it is to keep your tags file in .git/tags (if you have the excellent vim-fugitive installed vim is configured to look there) and update your tags file whenever the working tree changes. This comes in two parts: a script to update .git/tags, and hooks to call that script.

Following Tim Pope’s advice, and the advice of some of his commenters, I ended up with a script to update the tags that looked something like this:

#! /bin/sh

GITDIR=$(git rev-parse --git-dir)
TAGSFILE=tags.$$

mkdir $GITDIR/tags_lock 2>/dev/null || exit 0
trap "rmdir $GITDIR/tags_lock; rm $GITDIR/$TAGSFILE" EXIT

ctags --tag-relative -Rf $GITDIR/$TAGSFILE --exclude=$GITDIR

mv $GITDIR/$TAGSFILE $GITDIR/tags

This uses exuberant ctags to generate the tags file, and uses directory based locking1 so that a large rebase does not start tons of processes. git rev-parse --git-dir gets the git directory, and using it makes this work with submodules2.

generating haskell tags

This works great for the languages that exuberant ctags supports, but unfortunately haskell is not one of those languages. This means we need to add in support for haskell tags.

The haskell wiki lists some possible solutions.

  • GHCi can generate tags files. But to generate the tags, GHCi needs to know what language extensions to use and what modules to load. This information might be stored in a cabal file or a .ghci file, but we can’t rely on that.
  • gasbag does not understand all Haskell extensions, and cannot index haskell files that don’t compile.
  • hothasktags hothasktags needs to know language extensions (like GHCi), and cannot index haskell files that don’t compile (like gasbag).
  • hasktags uses its own parser and therefore does not care about language extensions or valid Haskell. Hasktags does have some problems (see this stack overflow post), but I went with it.
  • fast-tags Fast-tags addresses some of the problems with hasktags in the above stack overflow post, but it’s under-documented and I have yet to run into hasktags’ issues anyway.

combining hasktags and ctags

We could simply append the tags produced by hasktags to the tags from ctags, but vim expects tags files to be sorted (so it can binary search them). We must append then sort (using LC_COLLATE=C as advised here).

# exuberant ctags
ctags --tag-relative -Rf $GITDIR/$TAGSFILE --exclude=$GITDIR

# hasktags
if which hasktags > /dev/null ; then
    OLD_DIR=$(pwd)
    (cd $GITDIR && hasktags -c -x --ignore-close-implementation -a -f $TAGSFILE $OLD_DIR)
    LC_COLLATE=C sort $GITDIR/$TAGSFILE -o $GITDIR/$TAGSFILE
fi

Explanation of hasktags options:

  • -c means generate a vim (not emacs) format tags file,
  • -x means generate additional information,
  • --ignore-close-implementation avoids tagging both type signatures and implementations if the implementation is near the type signature.
  • -a means append
  • -f specifies output file

Full script available as a gist


  1. Thanks Rich Healy.↩︎

  2. Thanks Paul.↩︎