Thursday, October 09, 2008

Make Vim and Ctag play nice together

CTags and Vim are a killer combination for developing all kinds of programs in all kinds of languages. These tools are very flexible by themselves and combined we have so many options on how to use them together that it becomes very difficult to do efficiently.

For example you can configure Vim to auto generate ctags based on programming language and to search for tags files in every imaginable part of your hard disk. So how should one decide were to put the ctag files, how to generate them and how to access them?.

Here I talk about how I personally handle ctag files and how I configure vim to auto generate and search them.

Pre requisites

The following instructions were tested on a Linux machine installed using Kubuntu 8.10, 9.04 and 9.10. The first step is of course to make sure you have installed Vim and exuberant-ctags in you machine. For instruction on how to install Vim with minimal configuration refer to this link. To install exuberant-ctags simply execute the following command in a konsole window:


sudo aptitude install exuberant-ctags


Generate system wide ctags

I usually generate ctag files for the languages and libraries I use in my projects and save them inside my ~/.vim directory. This way I can easily jump to these library definitions.

To do so create some folders to store the ctag files:


mkdir -p ~/.vim/tags/linux
mkdir -p ~/.vim/tags/local


Then generate ctag files for each library and language you intent to use:


# Create system tags for linux kernel. This will allow you to jump 
# to the linux header files:

sudo aptitude install linux-headers-`uname -r`
ctags -R -f ~/.vim/tags/linux/ctags /usr/src/linux-headers-`uname -r`

# Create system tags for ruby

ctags -R -f ~/.vim/tags/linux/rbtags /usr/lib/ruby

# Create system tags for Java

# For the java tags to work you need to install java source package.
# In Kubuntu/Ubuntu follow these instructions:

sudo aptitude install sun-java6-source
sudo mkdir -p /usr/lib/jvm/java-6-sun/src
sudo unzip -d /usr/lib/jvm/java-6-sun/ /usr/lib/jvm/java-6-sun/src.zip
ctags -R -f ~/.vim/tags/linux/javatags /usr/lib/jvm/java-6-sun/src

# You may create additional ctag files for other libraries like QT,
# SDL, etc.



Configure Vim to generate/update your local ctag files

Creating ctag files for libraries can be done when a new version is available or when you modify/replace it. On the other hand your own projects may change in a daily or even hour basis so you need to update your ctag files all the time in order to use them during your project development.

To tackle this I create some local ctag files, one for each programming language I use, and tell Vim to update them every time a file I edit changes. This is very simple to do by adding the following lines to the vim configuration file (i.e. ~/.vimrc):



let _ctag_ = "ctags -a -f ~/.vim/tags/local/ctags --extra=+q "
au BufWritePost *.cpp,*.h,*.c,*.rl,*.def call system(_ctag_ . expand("%:p"))

let _rbtag_ = "ctags -a -f ~/.vim/tags/local/rbtags --extra=+q "
au BufWritePost *.rb call system(_rbtag_ . expand("%:p"))

let _pytag_ = "ctags -a -f ~/.vim/tags/local/pytags --extra=+q "
au BufWritePost *.py call system(_pytag_ . expand("%:p"))

let _jtag_ = "ctags -a -f ~/.vim/tags/local/javatags --extra=+q "
au BufWritePost *.java call system(_jtag_ . expand("%:p"))



Finally we must tell vim where to look for these tags by adding the following to the ~/.vimrc configuration file:

au BufRead,BufNewFile *.rb setlocal tags+=~/.vim/tags/local/rbtags,~/.vim/tags/linux/rbtags
au BufRead,BufNewFile *.cpp,*.h,*.c setlocal tags+=~/.vim/tags/local/ctags,~/.vim/tags/linux/ctags
au BufRead,BufNewFile *.rl,*.def setlocal tags+=~/.vim/tags/local/ctags,~/.vim/tags/linux/ctags
au BufRead,BufNewFile *.py setlocal tags+=~/.vim/tags/local/pytags,~/.vim/tags/linux/pytags
au BufRead,BufNewFile *.java setlocal tags+=~/.vim/tags/local/javatags,~/.vim/tags/linux/javatags

set tags=./.tags;${HOME}


Note that we use setlocal instead of simply set so the tag files are added to the current edit buffer only. This is done so we do not add all tag files to every file as we do not need for example the ruby tag file when editing a python script.

The last set command is to tell vim to search for ".tags" files inside the current folder recursively up to your HOME directory. We do not want vim tracking all the tag files up to ROOT do we?

Additionally I recommend you to use the TagList plugin that will provide a nice list with all your tags so you can easily jump from tag to tag.

Here is the complete vim configuration. Simply copy paste this in your own .vimrc file and follow the instructions in the comments.



"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""

"" Make vim work nice with exuberant ctags

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""

" Install exuberant-ctags in your system

"    - i.e. for Kubuntu:  sudo aptitude install exuberant-ctags

"



" Vim Tip #1299 - Update local tags when files are saved

"

"    Create a folder to store the tag files: mkdir -p ~/.vim/tags/local



let _ctag_ = "ctags -a -f ~/.vim/tags/local/ctags --extra=+q "

au BufWritePost *.cpp,*.h,*.c,*.rl,*.def call system(_ctag_ . expand("%:p"))



let _rbtag_ = "ctags -a -f ~/.vim/tags/local/rbtags --extra=+q "

au BufWritePost *.rb call system(_rbtag_ . expand("%:p"))



let _pytag_ = "ctags -a -f ~/.vim/tags/local/pytags --extra=+q "

au BufWritePost *.py call system(_pytag_ . expand("%:p"))



let _jtag_ = "ctags -a -f ~/.vim/tags/local/javatags --extra=+q "

au BufWritePost *.java call system(_jtag_ . expand("%:p"))



" Vim Tip #804 - Generate System Tags for your favorite languages

"

"    Create a folder to store the system tag files: mkdir -p ~/.vim/tags/linux

"

"    Create system tags for linux kernel.

"

"       sudo aptitude install linux-headers-`uname -r`

"       ctags -R -f ~/.vim/tags/linux/ctags /usr/src/linux-headers-`uname -r`

"

"    Create system tags for ruby

"

"       ctags -R -f ~/.vim/tags/linux/rbtags /usr/lib/ruby

"

"    Create system tags for Java

"

"       sudo aptitude install sun-java6-source

"       cd /usr/lib/jvm/java-6-sun

"       sudo mkdir src

"       sudo unzip -d src src.zip

"       ctags -R -f ~/.vim/tags/linux/javatags /usr/lib/jvm/java-6-sun/src

"

"     Finally configure vim to read these tags depending of the buffer filetype.

"     Also you may add other system tags for perl and/or python

"

au BufRead,BufNewFile *.rb setlocal tags+=~/.vim/tags/local/rbtags,~/.vim/tags/linux/rbtags

au BufRead,BufNewFile *.cpp,*.h,*.c setlocal tags+=~/.vim/tags/local/ctags,~/.vim/tags/linux/ctags

au BufRead,BufNewFile *.rl,*.def setlocal tags+=~/.vim/tags/local/ctags,~/.vim/tags/linux/ctags

au BufRead,BufNewFile *.py setlocal tags+=~/.vim/tags/local/pytags,~/.vim/tags/linux/pytags

au BufRead,BufNewFile *.java setlocal tags+=~/.vim/tags/local/javatags,~/.vim/tags/linux/javatags



" Vim Tip #94 - Set tags search path recursive up to $HOME

set tags=./.tags;${HOME}



"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""

"" Tag list features

"" Install the all powerfull taglist plugin so you can browse using the ctags

""

""   Get taglist_45.zip from http://vim-taglist.sourceforge.net/

""   Uncompress to your vim directory:  unzip -d ~/.vim taglist_45.zip

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""

let Tlist_Auto_Open=1

let Tlist_Auto_Update=1

let Tlist_Use_Horiz_Window = 0

"let Tlist_Inc_Winwidth=0

"let Tlist_Show_One_File=1

let Tlist_Exist_OnlyWindow=1

let Tlist_Use_Right_Window = 1

let Tlist_Sort_Type="name"

let Tlist_Display_Prototype=0

let Tlist_Compact_Format=1 " Compact?

let Tlist_GainFocus_On_ToggleOpen=1

let Tlist_Display_Tag_Scope=1

let Tlist_Close_On_Select=1

let Tlist_Enable_Fold_Column=1

"let TList_WinWidth=25




[get this widget]

1 comments:

Jesus said...

Thank you for this post, it has been a life saver!