Thursday, 18 April 2013

Working with Vim on large projects

I've been learning to use Vim for past two weeks and I'm finally getting to grips with it. Learning the basics is the easy part (there are plenty of tutorials) but to get it to work in a real project is a bit more work as it involves installing a host of plugins etc.

I'm getting there as well and this is a sort of first look at what I have found so far. I might follow-up on this if there are any new interesting findings.

Firstly, I tried Janus, which is a collection of plugins rolled into one plus some modifications. Not a good idea, it adds far too much stuff and you have no idea what.

Now I think I have a usable setup although there are still lots of small things to improve. And I know there are useful plugins that I should add.

Plugins

To start with, a plugin manager is a good idea. Pathogen is the older one, Vundle newer. I opted for the latter. Vundle helps with the installation etc. issues although I did find that using the built-in installer doesn't seem to work reliably. It says it installs the plugin, but you can't use it. So it's better to find the address for the plugin and adding this to .vimrc.

So far I have added the following:

  • NERDTree - File explorer. I mapped F5  to toggle.
  • vim-fugitive - Plugin for Git.
  • vim-gitgutter - Shows changes made to file in the left column compared to HEAD. I toggle this with F4.
  • CRefVim - C reference helper. You can get reference info for standard C functions. 
  • cvim - C plugin for Vim. Includes a host of useful stuff for C coding, like file templates, commenting (mark an area and hit \cc to comment and \co to uncomment for instance) etc.
  • Gundo - Vim undo explorer. Vim has a more complex undo system than most (it can branch) and this visualises the undo path. I have mapped this to F7.
  • Tagbar - Shows tags for current file in a window. I have mapped this to F6.
  • CCTree - Call graph visualizer. This shows the call graph of selected item, either forward or reverse. I have mapped this to F8.
  • Powerline - Makes the status bar much better. Looks better, includes stuff like current git branch and colours it depending on vim mode. 
  • Minibufexpl - A very compact buffer explorer.
  • OmniCppComplete - Code completion.
  • SuperTab - Improves tab. I use this with OmniCppComplete.
There are some other stuff I have done as well, but those add the most value. There have been a few hickups on the way and I still do. Plus I have only started configuring the key mappings.

UPDATE: I have found that so far I haven't used cvim or Gundo at all. Both are theoretically nice ideas but I find the C standard functions with Google and Gundo just doesn't seem to fit with my way of thinking.

Handling files in multiple directories

To make CCTree, tags and completion to be useful was to be able to add 3rd party sources to the search tree and in a way that I can actually have more than one project. There are some project oriented plugins but I haven't looked at them yet. I managed this more easily in the following manner.

First of all I wrote a script that creates a file with all the files I'm interested in. It looks something like

find . -name "*.[ch] -print
find [dir someplace else, e.g. /opt/CodeSourcery] -path "*/backward/*" -prune -print

Which would leave out any files in a "backward" directory. This works for C files, C++ would just search for cpp and hpp files.

I already have the paths defined in my build environment so this was simple. I save the output to cscope.files in my main source directory (=basically root directory of my own project).

Then I can just call 

ctags --C-kinds=+p --fields=+aS --extra=+q -L cscope.files 

or

ctags --C++-kinds=+pl --fields=+iaS --extra=+q -L cscope.files

for C++ files. l in the "kinds" parameter is not probably needed, it includes info for local variables. Nice if you need to browse through code with long methods.

To generate the tags file. For cscope I'm so far fine with just cscope -R. 

For CCTree I need to do :CCTreeLoadDB

This way I can have separate tags files for separate projects and I only need to add 
tags+=./tags

into my .vimrc to get OmniCppComplete to see all the tags in this project.

I have omitted basic installation of these plugins from this post because it would make this very long and this info is easily available already. The stuff I have explained here needed a bit more digging than just following the instructions even though it wasn't that hard either. 

Powerline

Powerline needed a bit of tweaking before it started working. Here's how.

First of all, the Vundle line in .vimrc needs to be

Bundle 'Lokaltog/powerline', {'rtp': 'powerline/bindings/vim/'}

Then I cloned the patched fonts repository to ~/.fonts and run fc-cache -fv in it. I originally tried to save just one font with Save As in my browser but this somehow messed the file and fc-cache wouldn't even recognize it.

Lastly I added these lines to .vimrc:

" For Powerline
set encoding=utf-8
if has("gui_running")
  set guifont=Droid\ Sans\ Mono\ for\ Powerline\ 11
endif
let g:Powerline_symbols = 'fancy'

I liked Droid the most of the fonts available. It's possible to add your own patched fonts as well, but I didn't find any need for that.

Minor tweaks

I have made a couple of other minor tweaks (and this is something I guess vim users never stop doing). Firstly, I like to see which files I have edited but not saved so I added this to .vimrc.


It's also nice to be able to move between windows easily, especially when you work with some of the plugins I have installed.

" quick moving between windows
map j
map k
map h
map l

It's really annoying that Vim keeps creating all these temporary and swap files in the current directory so

set backupdir=$HOME/.vim/_backup//
set directory=$HOME/.vim/_temp//

Usually I want Vim's current dir to be the one I opened the file in so I added a mapping just for changing that:

nnoremap ,cd :cd %:p:h:pwd

And what does all this look then? Below is an example screenshot. Obviously I don't keep all those windows open all the time, this is just for demonstration. Left-most panel is Gundo. Top part shows the undo tree and bottom part the changes done in this revision. Next is NERDTree, then code window and the right-most window is TagBar. You can also see OmniCppComplete in action, the window above the code window is showing info about the selected struct member. Also check out the status line


Second example shows more stuff. Left-most window is the CCTree window. It shows a forward trace from main(). Above the code window is vim-fugitive's window after :Gstatus, which shows the same output as "git status" on the command line. Top-most small area is the minibufexplorer. In the code window you can also see that I have added the file header comment.  This is shown by gitgutter.