I am always curious about other people’s vim workflow, especially when it comes to project management and goto definitions with ctags. I have now used vim quite some time and want to share my personal workflow. This is about how to create custom local vim configuration files per project and how to manage all of your ctag files easily.
TL;DR
So basically what I do is:
- Go to my project root
- Create vim project file
- Create ctag files
- Start coding
All done automatically with a one-liner:
$ make-vim-project all
1. The whole story
1.1 Install Dependencies
As I am currently using a MacBook due to work, I have to deal with OSX and therefore of course use homebrew
to install my stuff. So first I need to get the exuberant version of ctags.
brew install ctags-exuberant
1.2 Vim and ctags
I am trying to separate programming languages into different ctag files. For example, one file for c/c++
, one file for shell-scripts
, one file for javascript
and so on. For this to work, I need to tell vim
where to look for the files. The vim section looks like this:
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" CTAGS/CSCOPE
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Default/Generic tag file
set tags=tags,.tags
" Filetype specific tag files (This is used for global IDE tags)
autocmd FileType c set tags=.tags_cpp,$HOME/.vim/tags/cpp
autocmd FileType cpp set tags=.tags_cpp,$HOME/.vim/tags/cpp
autocmd FileType css set tags=.tags_css,$HOME/.vim/tags/css
autocmd FileType java set tags=.tags_java,$HOME/.vim/tags/java
autocmd FileType javascript set tags=.tags_js,$HOME/.vim/tags/js
autocmd FileType html set tags=.tags_html,$HOME/.vim/tags/html
autocmd FileType php set tags=.tags_php,$HOME/.vim/tags/php
autocmd FileType sh set tags=.tags_sh,$HOME/.vim/tags/sh
1.3 Vim and project files
Now I need a way to always tell vim where my project root is in order for it to look for the project specific ctag files. For this I am using local_vimrc via NeoBundle. Here is how to get it into vim.
" ---- PROJECT vimrc
NeoBundle 'LucHermitte/lh-vim-lib', {
\ 'name': 'lh-vim-lib'
\}
NeoBundle 'LucHermitte/local_vimrc', {
\ 'depends': 'lh-vim-lib'
\}
This plugin will check the root directory for a file called _vimrc_local.vim
. The only thing I want to place into this file is the cd path, so it know the root of the project directory.:
$ cat /path/to/project/_vimrc_local.vim
:cd /path/to/project
Whenever I open vim from within this project path, it will check if there a ctag files as defined in vim and ctags
above.
1.4 Creating project and ctag files
The setup is almost complete and I just need to create the project and ctag files for every project in its root. So first creating the project file:
$ cd /path/to/project && echo ":cd $(pwd)" > _vimrc_local.vim
And then I will add the ctag files. Here is an example for a c/c++
project:
$ ctags -R -f .tags_cpp \
--file-scope=yes \
--sort=yes \
--c++-kinds=+p \
--fields=+iaS \
--extra=+q \
2>/dev/null
This kind of sucks as I don’t want to issue those long commands every time I create a new project or update my ctags. So it needs to be automated or at least simplified.
1.5 Using a bash functions for project and ctag files
On the most simple form I just want to issue a single command which does everything for me. So I wrote a bash function make-vim-project
:
$ make-vim-project
Usage: make-vim-project <type>
all Create ctags for every filetype
web Create ctags for php, js, css and html
cpp Create ctags for c/c++
shell Create ctags for bash/sh
Now I can create a c/c++
project easily by just typing this:
$ make-vim-project cpp
It will automatically create the _vimrc_local.vim
as shown above and all c/c++ relevant ctag files. I also use this command once I update my project. So how does the function look and where do I put it?
First, it can be put anywhere in .bash_profile
, .bashrc
or any other custom bash file that is sourced by the main bash configuration file. Let’s have a look at the function itself:
#------------------------------------------------------
#-------- Vim Project
make-vim-project() {
local name dir
name="_vimrc_local.vim"
dir="$(pwd)"
read -r -d '' USAGE <<-'EOF'
Usage: make-vim-project <type>
all Create ctags for every filetype
web Create ctags for php, js, css and html
cpp Create ctags for c/c++
shell Create ctags for bash/sh
EOF
if [ $# -ne 1 ]; then
echo "$USAGE"
return
fi
# CTAGS
echo "Building ctags"
if [ "$1" == "all" ]; then
make-ctags
make-ctags-css
make-ctags-js
make-ctags-html
make-ctags-php
make-ctags-sql
make-ctags-shell
make-ctags-cpp
elif [ "$1" == "web" ]; then
make-ctags-php
make-ctags-html
make-ctags-js
make-ctags-css
make-ctags-sql
elif [ "$1" == "cpp" ]; then
make-ctags-cpp
elif [ "$1" == "shell" ]; then
make-ctags-shell
else
echo "$USAGE"
return
fi
# Vimrc
echo "Creating local vimrc"
echo ":cd ${dir}" >> "${name}"
}
As you can see, the function just prints its usage, calls other make-ctags-*
functions and creates the _vimrc_local.vim
file. Have a look at the gist for the complete source of all other make-ctags-*
functions:
Just for clarification, here is how one of the ctag functions will look:
make-ctags-cpp() {
ctags -R -f .tags_cpp \
--file-scope=yes \
--sort=yes \
--c++-kinds=+p \
--fields=+iaS \
--extra=+q \
2>/dev/null
}
1.6 Project root
Let’s have a look what files are inside my project root after using make-vim-project all
:
$ ls -la
...
-rw-r--r-- 1 cytopia 1286676289 73381097 Oct 17 12:01 .tags
-rw-r--r-- 1 cytopia 1286676289 72893221 Oct 17 12:02 .tags_cpp
-rw-r--r-- 1 cytopia 1286676289 1776509 Oct 17 12:01 .tags_css
-rw-r--r-- 1 cytopia 1286676289 409973 Oct 17 12:01 .tags_html
-rw-r--r-- 1 cytopia 1286676289 64329626 Oct 17 12:01 .tags_js
-rw-r--r-- 1 cytopia 1286676289 8989441 Oct 17 12:01 .tags_php
-rw-r--r-- 1 cytopia 1286676289 6223 Oct 17 12:01 .tags_sh
-rw-r--r-- 1 cytopia 1286676289 52748 Oct 17 12:01 .tags_sql
-rw-r--r-- 1 cytopia 1286676289 32 Oct 17 12:02 _vimrc_local.vim
2. What next?
This workflow has evolved during over a year of vim experience and as my personal preference. I am still not quite satisfied with some manual work, especially for updating the ctags once you have added code. If any of you have some better workflows and/or can recommend other vim plugins that do the trick more automated, please let me know and share.
_
2 comments on “vim workflow: go to definition with ctags”