When I asked him how he did that, he talked about how he had been using those techniques for 20 years. I had watched other people use emacs, but they were still moving their hands away from the home row. That alone motivated me to pick and learn Vi. (Yeah yeah, I know, a proficient emacs user could have been just as impressive. More on that later.)
Learning Vim turned out to be a great investment, as I worked on several unix and embedded systems where it was normal to have some form of Vi installed. It's even usable in the Dvorak layout! (I'll have to save those details for another post).
Back to the topic at hand: Vim is also great for reading and navigating source code. I would see people using an IDE that would push a button and it would take them to the declaration of a variable, function, etc... "How did you do that!?" See, I've never used a "real IDE" on a regular basis, and was unaccustomed to such features (feel free to flog me for this). I had to figure out how to get those features inside of Vim. (Another sidenote: I can't use anything besides Vim now. Even my MS words docs often have rows of jjjjj kkkkk sporadically placed. Another sidenote #2: One of the eclipse Vim plugins I tried last year made eclipse usable, but I still prefer Vim... I'll have to save that one for another post as well.)
Turns out there were several options to get source code navigation working in Vim. ctags, cscope, an eclipse ipc method, and one I stumbled on because of a co-worker: gnu global. Anthony swore by global, and indeed, his source code navigation was impressive... even while using emacs!
In any case, after trying all the above tools, I settled on gnu global, although it took some work to get it working with Vim and my setup. That's what this post is really about: documenting my usage of Vim and gnu global.
First, some settings for .bashrc:
alias maketags='mkdir -p $MAKEOBJDIRPREFIX/$(pwd -P) && gtags -i $MAKEOBJDIRPREFIX/$(pwd -P)'
alias maketags_cpp='GTAGSFORCECPP=1 $(maketags)'
That will set up the command I use to generate the tags: maketags_cpp
global is great in that you can store the generated tags outside the source tree (in this case $HOME/wa/globaltags/). Plus, you can be anywhere in your source tree and it correctly finds the tags. You can also have automatic per project tag databases. These are some of the main reasons I chose gnu global over the alternatives. I don't want to jump to spots in an unrelated code base.
Now, for Vim. First, .vimrc:
let GtagsCscope_Auto_Load = 1
let GtagsCscope_Auto_Map = 1
let GtagsCscope_Quiet = 1
Apparently Vim doesn't have a plugin architecture for tagging systems. So, global provides a cscope adapter in order to make Vim think it's using cscope, when it's really using global.
Next, drop gtags-cscope.vim into $HOME/.vim/plugins. You may need to make sure you have the correct version for the version of global you're using. global also has "gtags.vim", but like I said, Vim doesn't expose an api to allow different tagging systems, making the tag stack unavailable (which, is one of the best features of this setup).
Another sidenote/tip from Anthony: if you ever google for it, make sure you google "gnu global" and not "global" or "gtags". I know... "gnu global" is a terrible name marketing wise.
So, after installing global, the basic workflow is like this:
- enter source dir root and run "maketags_cpp"
- open a file with Vim
Some of the common operations I use:
- go to function/variable definition of identifier under cursor (pushes onto the tag stack): CTRL-]
- go back (pops off the tag stack): CTRL-t
- search for all instances of this identifier: CTRL-\ and then e
- this brings up a list, and you can enter the number to go to that spot. You can then do ":tn" and ":tN" from the Vim command line to go forward and backwards through the tag list.
- global also has a method to find all functions that call the function in question. I don't remember the shortcut, but honestly, I normally use the above item instead.
- several other search methods that I can't remember right now
There are some warts that I haven't figured out: sometimes the first "common operation" can't find the definition like it should. In those cases, I use the 3rd "common operation" as a backup.
There are support for other languages, and some have configured global to work with unsupported languages, but googling for those is left as an exercise for the reader.
That's about it. Just as my co-worker warned, it's pretty hard to work without this functionality after having enjoyed it.