vim with ctags for multi-level directory hierarchy

April 24th, 2008 mysurface Posted in ctags, Developer, Text Manipulation, vi, vim | Hits: 116011 | 11 Comments »

I have wrote a post regarding vim with ctags, introduces how ctags allows you to travel across source codes, searching for function, objects, variables definition. To jump from function call to function definition, I usually do ctrl+] in vim. It should works fine as it should have already configured by default from most of the linux distros.

Usually c/c++ projects may have multiple directories, for example, lets look at this project hierarchy:

  |       `...
          |       `-----hdrs
          |       `-----lib
          |       `-----src
          |       `-----test
          |       `-----hdrs
          |       `-----lib
          |       `-----src
          |       `-----test

I Copied it from Exuberant Ctags FAQ

Let say source code call in sysint/client/src may make a function call where the function definition is in sysint/common/hdrs. The simplest way to make use of ctags, you have to station yourself in sysint folder, and do this:

ctags -R *

The command line above will generate a file name tags in sysint folder. To edit let say in client/src, you will need to issue vim at sysint directory, like this:

vim client/src/

So that if the function call definition is at common/hdrs, you may able to travel from there, because vim will always try to find the tags file in the base folder you issue vim. Therefore, if you change directory to client/src like this

cd client/src

And you try to do ctrl+], it will complain it can’t find the tags file and refused to work! Because inside sysctl/client/src folder doesn’t contain tags file. ctags -R * at sysctl folder does not generates tags in any of its subfolders.

Exuberant Ctags FAQ guiding through you on how to generates tags file on every single sub folders, so you can travel freely at any point. The steps some how – tedious! I don’t feel that I can remember the steps very well everytime I create a new c/c++ project. So I created a bash script to does all those tedious steps.


# gentags v0.2 -- generate tags using ctags for all subfolders
# Written by Hean Kuan Ong (
# April 24, 2008
# Copyright (C) 2008 Hean Kuan Ong All rights reserved.
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

# IGNORE contains the directories to ignore in find statement
# for more info, man find.
IGNORE=" ! -wholename *.svn* ! -wholename *.git* ! -wholename .*log* ! -wholename .*Log* "


    echo "gentags v0.1"
    echo "USAGE: gentags [OPTION]"
    echo ""
    echo "OPTION:"
    echo " --help   display me"
    echo " --info   simple steps for vim user"
    echo " --test   shows the list of target tags directories"
    echo " --init   generate tags to all subfolders besides .svn and .git"
    echo " --ask    ask for confirmation for tags generation"
    echo " --clean  clean all the tags files"
    echo " --show   locate all the tags files"
    echo ""
    exit 2

    read -p "ctags * $1  [y/n] " ANS
    if [[ $ANS = "y" || $ANS = "Y" ]]
        gentags $1

    echo ""
    echo "run 'gentags --init' without quotes at your base project folder"
    echo "paste the lines below to your ~/.vimrc"
    echo ":nmap ,t :!(cd %:p:h;ctags *)&"
    echo ":set tags=./tags,./../tags,./../../tags,./../../../tags,tags"
    echo ""

if [[ $1 = "--help" || $1 = "-h" ]]
elif [[ $1 = "--info" ]]
elif [[ $1 = "--test" ]]
    find . -type d $IGNORE 
elif [[ $1 = "--init" ]]
    ctags --file-scope=no -R
    find . -type d $IGNORE -exec `basename $0` {} \;
elif [[ $1 = "--ask" ]]
    ctags --file-scope=no -R
    find . -type d $IGNORE -exec `basename $0` --ask2 {} \;
elif [[ $1 = "--ask2" ]]
    ask $2
elif [[ $1 = "--clean" ]]
    find . -name "tags" -exec rm {} \;
elif [[ $1 = "--show" ]]
    find . -name "tags"
    if [[ $1 = "." ]]
    cd $TARGET
    ctags *
    find * -type d -prune -print | ctags -aR --file-scope=no -L-

Copy the script above to /usr/bin/gentags, for now, you can trigger gentags at your c/c++ project base directory like this:

gentags --init

From the script above, you can edit the IGNORE variable, where what folder you usually want to exclude from being targeted. For example, you don’t want to mess up your .svn or .git folders, because generating tags in such folders are useless and may harmful to your svn or git structures. You can trigger a dry run with –test before performing a real gentags, it will list all the targeted directories, that helps you to tweak your IGNORE variables.

In case there are too many subfolders that you wanna it to be excluded from gentags, you can use –ask. gentags will ask you for confirmation apon generating tags for each folders. You can locate all the existing tags files by –show, and you can clean all the tags by –clean. For more options gentags support, you can refers to the –help.

Last thing you wanna do is adding two lines in your ~/.vimrc. You want vim to refers to the tags file at project base first, that allows you to travel to other sibling folders.

:set tags=./tags,./../tags,./../../tags,./../../../tags,tags

And you may want to update your tags when you have modified your class structure, adding new function etc. You can map a key for you to trigger the update.

:nmap ,t :!(cd %:p:h;ctags *)&

The line above map the updates of tags to key ,t.

Hope you enjoy coding with vim and ctags!

11 Responses to “vim with ctags for multi-level directory hierarchy”

  1. you can do this instead:

    cd project
    ctags -R

    then add this to .vimrc
    set tags=tags;

    the magic is the ‘;’ at end. it will make vim tags file search go up from current directory until it finds one.

  2. KFC fan: Thanks dude, it is so simple and it works perfectly!

  3. Hey surface,

    At last i know how to make function on shell scripting.
    I’ve been looking for example and you include sample.

    a bit off topic anyway.
    But thanks.

    p/s: I cant use vim cuz my keyboard had problem with certain keys

  4. @Gunbladeiv: Simple and straight forward right?

  5. I came across this article because my gvim 7.xx was complaining about not having ctags. Which related to my prolem.

    I’ve been installing ruby/rails and getting gvim going on my Windows XP system.
    Basically followed Fabio Akita’s instructions, and downloaded snippet collection from
    your site. The built-in rails.vim snippets work fine, but none of the ones I downloaded do.

    I Installed to ~/vimfiles/snippets, and also ~/vimfiles/after/plugin/snippets.vim

    So is there something I have missed in making this work, or anything more I may need to put in

    Thank you.

    Jerrold Eric Thompson

  6. The method proposed by KFC fan is working on my code except for one drawback. Whenever I search for the definition of a function it duplicates each result, i.e. there will be two lines corresponding to the same file in the results screen.

    Does anyone know why is it happening?

  7. Hey: This website is the best vim with ctags for multi-level directory hierarchy » Linux by Examples … Just how did one make it look this great ! Enjoy your day Company Website Design

  8. hey KFC fan.. thats the most elegant solution i’ve come accross…!

  9. Thanks a lot. It saved a lot of time. Its very elegant and works perfectly.

  10. Thanks a lot…

  11. Timeless comment, KFC fan, thank you! :D

Leave a Reply