Using multiple tag files at once in vim / Tag organisation in general

TheIronKnuckle picture TheIronKnuckle · Jun 27, 2013 · Viewed 8k times · Source

(Apologies for the C tag, I did it for the syntax highlighting. This is more of a vim question. If someone more learned than I thinks the tag should be removed please do so)

Say I've got this directory structure:

Directory ~/Code/Test/     containing file1.c file2.c file4.c and Sub 
Directory ~/Code/Test/Sub/ containing file3.c

file1.c:

#include <stdio.h>
#include "file2.c"
#include "Sub/file3.c"

void function1();

int main (int argc, char *argv[]) {
   function1();
   function2();
   function3();
   return 0;
}

void function1() {
   printf("1\n");
}

file2.c:

#include <stdio.h>

void function2();

void function2() {
   printf("2\n");
}

Sub/file3.c:

#include "../file4.c"

void function3();

void function3() {
   printf("3\n");
   function4();
}

file4.c:

#include <stdio.h>

void function4();

void function4() {
   printf("4\n");
}

In any one of those files it should be possible to jump to the definition of the functions it uses from the other files. So for example, file1 should be able to jump across to file2, file1 should be able to jump down a directory to file3, file3 should be able to jump up a directory to file4, and here's the kicker; all of the files should be able to jump to the definition of printf. I also shouldn't have to copy the tags for the c library implementation into the Test directory in order to do this.

I'm wondering how I could go about doing this. I'm really not keen on monolithic tags files. Having a vim-wide tags file horrifies me. A tags file per directory annoys me. A tags file per project is bearable. But what I'd really like is a tags file per source file, and then a way to specify which tags files vim should be referring to.

Ideally I'd like to be able to just ctrl-] on anything and have vim jump to the correct definition based on what's in scope, just like visual studio. I'm beginning to suspect this can't be done, and if it is (via some combination of plugins) it would be extremely slow, which is really annoying because I was totally on the "Vim can do anything your newfangled IDEs can do" bandwagon for a couple of weeks there. Yes it's definitely the most powerful text editor I've come across, but as an IDE it's extremely unpolished. When I use the "Go to definition" command I expect to be taken to the correct definition whether it's a local variable, in a different file, in a standard library etc. Vim so far has given me hilarious results such as jumping across from a java file to a c file. And you have to use a separate command to jump to the definition of a local variable... What? (If there's a reason behind this I'd be interested in knowing)

I'm aware of wacking set tags=./tags in my .vimrc and that's what I've done so far. But this won't scale if I work on something massive that links separate assemblies and source files from separate projects together.

(To be fair to vim, visual studio doesn't let you jump across assemblies to find definitions either, but it does at least have the good grace to serve up a header file from which you can "load assembly" and navigate to the actual source code you're looking for)

Answer

romainl picture romainl · Jun 27, 2013

First things first: Vim has never been, is not and will probably never be a proper alternative to an IDE. Whoever made you believe that should be shot and you should be shot too for believing such nonsense.

I'm only half-joking.

Vim is a very powerful programming-oriented text editor but the simple fact that you need to run a dumb external code indexer to get a dumb "jump to definition" or another code indexer to get another dumb "jump to usage" should be a hint that Vim can't realistically be compared to an IDE. Hint: the I in IDE means "Integrated" and the E means "Environment". Since you can't get proper integration and would be hard-pressed to consider Vim as an environment, there's no IDE, here. Only a text editor with many plugins doing different things in different ways and, above all, no serious way to understand your code which is the #1 feature of a descent IDE.

Many users/bloggers claim they are using "Vim as an IDE" or that you could, too, turn Vim into a Python or whatever IDE but the truth is that Vim lacks all the low-level features that would make such a thing possible. You can turn it into something that looks like an IDE, if you are somehow able to believe in your own lies, but it will probably never be an IDE.

Whatever…

  • The default behavior (which can't be altered in your configuration) of <C-]> or :tag foo is to jump to the first hit in your tags file(s). Neither Vim nor Ctags know about scope. At best, you can be treated with a list from which to choose the correct tag (:ts foo or g]) but that's how far you can go.

    Depending on the languages you work with, Cscope might be better at indexing your code but the general principle is the same as with Ctags. Cscope offers a "jump to usage" feature so it might be worth switching just for it.

  • Making sure the correct tags file(s) are used can be a pain and the doc is surprisingly not very helpful in that regard. There are a bunch of plugins designed to make it simpler that you could try, EasyTags comes to mind.

    I admit I don't work on very large projects and not even in C so maybe this won't seem useful but this line in my ~/.vimrc makes working with tags easier:

    set tags=./tags;/,tags;/
    

    With this setting, Vim looks up and up (non-recursively) until / for tags files. The main point of this is to have a single tags file at the root of each of my projects that can be used from every file in that project without ever needing to tell Vim where to look for a tags file.

    One way to deal with your Java/C mixups could be to put your projects into language-specific directories:

    C/
      c.tags
      proj1/
        tags
        …
      proj2/
        tags
        …
    Java/
      j.tags
      proj3/
        tags
        …
      proj4/
        tags
        …
    

and put you "global" tags files at their root as well as project-specific tags files at the root of their respective projects.

Another way to deal with that issue could be to instruct Vim to load specific tags files depending on the filetype:

    autocmd FileType c setlocal tags=/path/to/your/global/c/tags,./tags;/,tags;/