December 24, 2016

Ctags and linux source browsing

Anyone will tell you that using "grep" to dig around in the linux sources is a bad way to do things. It is an ugly business when one file calls routines in another file (in some totally unknown place in the file hierarch). Macro definitions can also be in any number of places. Grep might work for a small project, but it is out of the question (or at least extremely painful) for studying linux.

However, you need to suffer with grep (and grep -r xyz .) to have the motivation to learn ctags (which is pretty simple actually) and to tackle the business of Vim plugins.

Ctags

Apparently what you want is "Exuberant ctags", not some older version. On my Fedora system, this is installed by default as revealed by:
[tom@trona ~]$ which ctags
/usr/bin/ctags
[tom@trona ~]$ ctags --version
Exuberant Ctags 5.8, Copyright (C) 1996-2009 Darren Hiebert
  Compiled: Feb  3 2016, 19:43:56
  Addresses: , http://ctags.sourceforge.net
  Optional compiled features: +wildcards, +regex
If you are OSX, you probably have the old ctags, so beware.

You run ctags to prepare a "tags" file. I have the sources for linux 4.2.1 that I want to be able to browse, so I do this:

cd /path/linux-4.2.1
ctags -R .
The resulting "tags" file has 2760268 lines.

Apparently you can sort of hide the tags file if it offends your sense of tidiness by using "ctags -R -f ./.git/tags ." This shoves the tags file into the .git directory for your project.

Also you should put a line like this in your .vimrc. With this, vim will run up through directories looking for a tags file, so you get access to tags even when working down inside a project.

set tags=tags;

Ack and Ag

There are Vim plugins for these (there are at least 4000 Vim plugins available). These are improvements over Grep for searching through source code. Something else to study and learn about. Ack is written in Perl, which would make you worry about its performance on a big source tree.
On Fedora
dnf install ack
dnf install the_silver_searcher

Vim tricks

This has nothing to do with ctags, but a trick worth knowing is that if you have the cursor on a word you want to search for, you can just type "*" (hit the star key) and vim will go to the next occurence of the word in the same file. Very handy; I need to break my habit of using slash searches and use this more.

Two keys handle most of what you want to do with ctags inside of vim.

Control-] -- goes to the definition of whatever the cursor is on.
Control-t -- unwinds the above one level (a "back" button).
Sometimes there are choices about where to go and you are presented with a list and need to type a number to make a choice.

The ":tag" command provides additional functionality. Using ":help tag" will be instructive.

One fine example is using ":tag mdelay" to find out where "mdelay" is defined. This is the way to go if you know the name of the function of interest, but are not in a file referencing it (or are starting from the top level). Even better in this case is to type "vi -t mdelay"

Since we are diving off the deep end into vim customization, this article is worth careful reading.

Links and Vim plugins

TagList is the top rated and most often downloaded vim plugin. This gets us into the whole business of vim plugins, for which see below. Ctags helps you find where things are defined. If you want to find where things are referenced, you need Cscope, which sort of complements Ctags. One thing at a time for me. Ctrlp is a plugin that allows you to directly search the tags file.
Tagbar is another plugin that gives you a tag list on the right side of the screen.

Fedora "vi" is not "vim" !!

I will mention this in case it saves somebody grief. On Fedora (this is still true in 12/2016 with Fedora 24) there are two different things: /bin/vi and /bin/vim. I like to invoke "vim" by typing "vi" as I have done since time immemorial. I pulled my hair out wondering why my .vimrc was being ignored. Now I have an alias in my .bashrc that makes "vi" run "/bin/vim". I can sort of understand what they were thinking (in the same way that I can understand but not embrace why people shoot heroin or commit suicide).

Vim plugins

Moving beyond Ctags now and tackling the general issue of installing vim plugins. The overwhelming recommendations is to not just install plugins willy-nilly but to use a plugin manager. At one time I used Pathogen, but the current recommendation is to use something called Vundle. Here is the drill to install Vundle. This is explained on the Github page. I had no previous ".vim/bundle" so there was no confusion about doing the following:
git clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vim
After doing this, you add a bunch of stuff to your .vimrc file as shown on the Github page. You should go there to find out the most recent stuff that is needed. After adding "the stuff" you can launch the editor and type ":PluginInstall" or give the following command.
vim +PluginInstall +qall
I have no clue what this does, but this is what you are told to do. My impression is that it does nothing at all, unless you added "Plugin" lines for not yet installed plugins when you added "the stuff" to your vimrc.

The "central idea" in all of this seems to be that a single "Plugin" line in your .vimrc file enables (or disables if commented out) a given plugin. This was also the central idea in Pathogen, but Vundle adds a management interface to vim, which we will discuss next.

Once you have Vundle installed you can use :PluginList to list all plugins. Not ":Plugins" as some apparently out of date documentation indicates.

You can also launch the management interface via ":PluginInstall". This opens a new vim buffer (so if you are not used to using multiple buffers in Vim, this will be surprising). You close down this thing via :bdelete. It gives you some help up top (there are single letter keystrokes). You can also continue to do things using colon commands, like:

:PluginSearch! taglist
When I type this, it gives me the Vundle buffer with two choices loaded. I want the classic "taglist.vim", so I move the cursor to it and type "i" to install it. This simply fetches taglist.vim and places it into .vim/bundle. Nothing has been done to the .vimrc yet, you have to do that by hand, adding a line like:
Plugin 'taglist.vim'

Using the taglist.vim plugin

You can set a variable so that the taglist window comes up automatically each time you start vim. Tjere are a bunch of variables you can set in your .vimrc to control the behavior of taglist. Here is one guys set that I find myself pulling more and more tricks from.
" TagList options
let Tlist_Close_On_Select = 1 "close taglist window once we selected something
let Tlist_Exit_OnlyWindow = 1 "if taglist window is the only window left, exit vim
let Tlist_Show_Menu = 1 "show Tags menu in gvim
let Tlist_Show_One_File = 1 "show tags of only one file
let Tlist_GainFocus_On_ToggleOpen = 1 "automatically switch to taglist window
let Tlist_Highlight_Tag_On_BufEnter = 1 "highlight current tag in taglist window
let Tlist_Process_File_Always = 1 "even without taglist window, create tags file, required for displaying tag in statusline
let Tlist_Use_Right_Window = 1 "display taglist window on the right
let Tlist_Display_Prototype = 1 "display full prototype instead of just function name
"let Tlist_Ctags_Cmd = /path/to/exuberant/ctags

nnoremap  :TlistToggle
nnoremap  :TlistShowPrototype

set statusline=[%n]\ %<%f\ %([%1*%M%*%R%Y]%)\ \ \ [%{Tlist_Get_Tagname_By_Line()}]\ %=%-19(\LINE\ [%l/%L]\ COL\ [%02c%03V]%)\ %P
In particular note the mapping for F5 (There is no way I am going to use taglist if I have to type :TlistToggle all the time). Putting the "CR" on the end makes it happen with one keystroke, which is just what I want. Also the "GainFocus" option gives sane behavior.

To use this effectively, you need a trick to move focus between windows. Apparently (Ctrl-w)w moves to the "other" window, which is kind of hard to remember, but gets the job done. Note that the usual vim "hjkl" motions work along with (Crtl-w) to move among windows.

Another plugin called NERDtree is quite popular, and I have started using it along with taglist. I followed the advice of several people and activated the setting to put the taglist column on the right, leaving room on the left for the column from NERDtree. I also added a mapping to let the F4 key map to the :NERDTreeToggle command. One follow says he bings this to the "t" key, which is a seldom used vim motion command. Another fellow says:

"I put project.vim, taglist, and NERDTree on , , and , respectively."
More on project.vim after we get into NERDtree

So, what about NERDtree

I try using the Vundle ":PluginSearch! nerd" and get the following choices:
Plugin 'nerdtree-ack'
Plugin 'NERD_Tree-and-ack'
Plugin 'FindInNERDTree'
Plugin 'NERD_tree-Project'
Plugin 'The-NERD-tree'
Plugin 'The-NERD-Commenter'
Nothing is just called NERDtree, but near as I can tell "The-NERD_tree" is what I want. This comes from vim.org. I use the Vundle searcher, then fall back on Google to try to figure out what all these darn things are. After this, I exit the editor and do an "ls .vimrc/bundle" to find out what this is called, then add the following to my .vimrc. I am ignoring these Ack augmented NERDtree things for now.
Plugin 'The-NERD-tree'
Nowwhere do I find anything entitled (or functionally equivalent to) "how do I use this darn thing?". The FAQ suggests mapping Control-n to the nerd tree toggle (which is sort of logical, "n" for nerdtree).
map  :NERDTreeToggle
nnoremap  :NERDTreeToggle
I actually do both, but use nnoremap for both. Also, I add the following to put the taglist stuff on the right:
let Tlist_Use_Right_Window   = 1
Also note that there are switches available for both taglist and nerdtree so they will come up whenever vim is launched. With a large (24 inch) wide aspect display (like most folks have these days) there is plenty of room for both of these and an editing windown in the middle. This leads to a variety of other composite plugins and talk about "IDE-like behavior", as follows:

And, what about project.vim

Installing this is the now familiar process of using :PluginSearch to find and install the plugin and then adding a line to the .vimrc.

One fellow says he uses this along wth taglist and NERDtree.

It is not clear yet what keybindings I might want to use. Installed by default this comes alive every time I start vim, so I have disabled this for now.


Have any comments? Questions? Drop me a line!

Tom's electronics pages / tom@mmto.org