Skip to content

Instantly share code, notes, and snippets.

@tmcw tmcw/

Last active Jul 11, 2019
What would you like to do?
Mediocre vim code intelligence with ctags

Though I love vim, it has one pretty big flaw: it doesn't know JavaScript. There are systems that know JavaScript pretty well, like all of Visual Studio Code’s code intelligence features. I've tried all of them: languageserver, tern, typescript server… and they've all broken my heart. And then I went back to the opposite extreme: no autocomplete, no fancy stuff, not even the supertab plugin. Now I've settled in the middle, and am loving mediocre vim. The combination is:


Ctags is a beautifully ugly system, with extreme performance and simplicity. Just enough intelligence to pluck out the exports and declarations in your files to make jump to definition work mostly. It's basically a set of regular expressions that tries to find keywords and then puts them in a file that your editor can use as an index.

brew install ctags-exuberant

Next, you want to generate those tags. Doing it manually is annoying. There's a great plugin, gutentags, that does it automatically, and just works:

In your vim-plug dependencies:

Plug 'ludovicchabant/vim-gutentags'

Next, you want to make those tags better, so configure ctags. This means a ~/.ctags file to configure. Here's my configuration:



--regex-js=/([A-Za-z0-9._$]+)[ \t]*=[ \t]*\(function\(\)/\1/c,class/
--regex-js=/['"]*([A-Za-z0-9_$]+)['"]*:[ \t]*\(function\(\)/\1/c,class/
--regex-js=/class[ \t]+([A-Za-z0-9._$]+)[ \t]*/\1/c,class/
--regex-js=/([A-Z][A-Za-z0-9_$]+)[ \t]*=[ \t]*[A-Za-z0-9_$]*[ \t]*[{(]/\1/c,class/
--regex-js=/([A-Z][A-Za-z0-9_$]+)[ \t]*:[ \t]*[A-Za-z0-9_$]*[ \t]*[{(]/\1/c,class/
--regex-js=/([A-Za-z$][A-Za-z0-9_$]+)[ \t]*=[ \t]*function[ \t]*\(/\1/f,function/
--regex-js=/function[ \t]*([A-Za-z$_][A-Za-z0-9_$]+)[ \t]*\([^)]*\)[ \t]*\{/\1/f,function/
--regex-js=/^[ \t]*(export)?[ \t]*function[ \t]+([a-zA-Z0-9_]+)/\2/f,functions/
--regex-js=/['"]*([A-Za-z$][A-Za-z0-9_$]+)['"]*:[ \t]*function[ \t]*\(/\1/m,method/
--regex-js=/([A-Za-z0-9_$]+)\[["']([A-Za-z0-9_$]+)["']\][ \t]*=[ \t]*function[ \t]*\(/\2/m,method/
--regex-js=/^[ \t]*(export)?[ \t]*class[ \t]+([a-zA-Z0-9_]+)/\2/c,classes/
--regex-js=/^[ \t]*(export)?[ \t]*module[ \t]+([a-zA-Z0-9_]+)/\2/n,modules/
--regex-js=/^[ \t]*export[ \t]+(var|let|const)[ \t]+([a-zA-Z0-9_]+)/\2/v,variables/
--regex-js=/^[ \t]*(var|let|const)[ \t]+([a-zA-Z0-9_]+)[ \t]*=[ \t]*function[ \t]*\(\)/\2/v,varlambdas/
--regex-js=/^[ \t]*(export)?[ \t]*(public|private)[ \t]+(static)?[ \t]*([a-zA-Z0-9_]+)/\4/m,members/
--regex-js=/^[ \t]*(export)?[ \t]*interface[ \t]+([a-zA-Z0-9_]+)/\2/i,interfaces/
--regex-js=/^[ \t]*(export)?[ \t]*enum[ \t]+([a-zA-Z0-9_]+)/\2/e,enums/

Then, you can navigate tags by putting your cursor over something and hitting Ctrl-] to navigate to its definition.

Then, you can navigate to any tag, just like you'd navigate to any file, using Fzf:

In your vim-plug dependencies:

Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' }
Plug 'junegunn/fzf.vim'

In vim configuration:

nnoremap <Leader>t :Tags<CR>

Voila! A simple, debuggable, non-magic setup but it's practical as all hell. I adapted the .ctags configuration from a modern ctags configuration. I dropped some of the typescript bits and added better ES6 support.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.