Using ctags and cscope in Vim
My philosophy has always been to maximize productivity with minimal effort. I think this is why I prefer Vim over Emacs :smile:. Find a tool that works, stick with it, that’s what I do. I haven’t distro hopped in 10 years and I’ve been using vim as my text editor and tcsh as my login shell for more than 10 years. If it ain’t broken, don’t fix it!
My job has me working on dinosaur code bases where running
grep to find stuff isn’t exactly the most practical thing to do.
Some of the tools I’ve integrated into my environment over the years are
cscope, which, while requiring me
to learn a couple of new tricks, do make the archaeologia cōdicis phase go a lot faster.
So let’s see how
cscope work with Vim.
You need to have some sort of
ctags package installed. E.g.
pkg_add ectags pkg_add universal-ctags apt install exuberant-ctags ...
Note: Once a package is installed, the actual binary name may vary. Replace
ctags in the shell invocations below with the actual binary name of the thing you have installed. In my case, I run *
ectags which is the exubernat ctags implementation.*
Vim used to ship with exuberant ctags, which has somewhat decent C++ support, plus a whole bunch of features related to local symbols, who calls what, etc.
If exuberant ctags isn’t available, there’s universal ctags, which is mostly CLI compatible (though it might complain that some
--c-kinds are not available). The much older UNIX-y
ctags is still floating around, that one doesn’t do much. It can still be useful if it’s all you got; it supports basically no flags.
For a lot of people, running
ctags . is enough. But I like having a giant database for an entire source tree (sometimes for multiple source trees if
I’m working on a tool that uses some library). I invoke it like this:
# go to command ctags --extra=+fq --fields=+amnStail --c-kinds=+cdefglpx --c++-kinds=+lpx -f tags -R . # or, for a more exuberant version: # --tag-relative=yes <-- files in tags are relative to tags file and not to cwd # --fields: enable everything I know of # --extra: extra stuff for C++ # --c&c++-kinds: enable everything I know of ctags -f tags --tag-relative=yes --fields=+a+f+i+k+l+m+n+s+S+z+t --extra=+f+q --c-kinds=+c+d+e+f+g+l+m+n+p+s+t+u+v+x --c++-kinds=+c+d+e+f+g+l+m+n+p+s+t+u+v+x -R .
I remember going through the exuberant ctags man page and picking everything that I might ever find interesting. I may have enabled all flags, I don’t remember.
Vim has neat
ctags integration, and I use almost none of it.
To add tags, set your
set tags=tags. Note, this is stored as a string and possibly looked up relative to
cwd. This feature is meant to support having a
bunch of ctags databases and only loading the subset for the current folder (or whatever). You can have absolute paths.
I prefer to have a giant database with an entire source tree, so I tend to
The magical command you need to remember is
:ts (tag-select). There’s a shortcut
g] which takes the string under the cursor and passes it to
:ts. It then
brings up a menu with all the matches.
And if you weren’t aware,
^I are Vim’s back and forward buttons (like a web browser). I recall ctags support in Vim having a concept of a tag stack , but that’s
one of the many features I don’t use :smile:. I just stick with ol’ reliable
^O. Just go see
:help tags and
:help tagsrch.txt, there’s a lot of functionality there…
sometimes ctags isn’t enough; for example, there are many projects that provide helpful macros to help devs define or use stuff, and that confuses C parsers.
Again, you need
cscope installed, e.g.
pkg_add cscope apt install cscope
cscope comes in and provides its own search tools, and the features I mostly use are “who calls this”, “where is this text string”, “where is this regex”, “where is this macro defined”.
cscope -bqR cscope -bqR -s../include -s../drv # additional include dirs cscope -bqR -u # force update the database
cscope isn’t as neatly integrated into Vim, but it’s good enough.
cscope actually runs as a background process and Vim drives it.
:cs add cscope.out to enable.
:cs to show the help.
Then, you can use
:cs f g symbol,
:cs f c who_calls_this,
:cs f t text and so on.
If you want to pass a complicated and long symbol name, and you don’t want to type it (I usually don’t), you can use Vim registers to hold it.
Interactively, yank the thing in a register (e.g.
"qye or visual select +
"qy etc), then type
:cs f t ^Rq. You can automate e.g. in visual mode
:vremap or related functions.
I don’t believe
cscope works well with C++, but since I mostly use it as a complementary text or grep tool to
ctags, it works well enough :smile:.
Vim is a powerful IDE, but we knew that already. Especially when you pair
cscope with the somewhat recent
:Termdebug feature which makes Vim be a front-end to
gdb; now, while debugging, if you stumble over a macro like
LIBPATH, you can jump to it to see what it was compiled as :smile:.
The thing is, you can use these tools without Vim.
cscope in particular has a menu drive text interface (one of the
q flags disables this). You can use them
with any text editor / IDE.
The provide a massive amount of value on large or old or complicated projects/products. And they are considerably faster than running
grep repeatedly, or trying
to guess where everything should be. There’s just the initial setup time that can be annoying, but it usually finishes by the time you grab some coffee; and if the code base is large enough,
ctags should have more than enough time to finish while the product builds :smile:. Keep these tools handy.
There is at least one more tool, GNU GLOBAL, but I haven’t had fun with that one, so I never use it. Visual Studio Code also tries to do similar things to
cscope, and it mostly works, but who wants to run a text editor that eats up as much RAM as Chrome does? And I’m willing to wager someone wrote a cool clang-* thing that does the same thing as these two tools, but I’m too lazy to check (or to change my ways).