shell

Vim for programmers (II)

In the first part of this series, we visited some Vim features that help us in programming. In this second issue, I will show you some other important things you should know to really appreciate the power of Vim.

Completion
Completion is not a programming specific feature in Vim, however it is in programming where I find it to be more useful. There are several completion options, but I will explain the ones I find more interesting. Completion is a sub mode of insert mode. This means the commands are applied while being in insert mode.

  • Line completion: Ctrl-x Ctrl-l completes the whole line. This is very useful as in programming, yo usually need to write exactly the same line you wrote before or a line very similar to that one.
  • Word completion: Ctrl-p completes a word
  • File completion: Ctrl-x Ctrl-f Extremely useful in #include like sentences as it completes file names.

Tags
Tags are a used to rapidly navigate source code. Having the cursor placed at a function invocation, it lets you jump to the function definition. Vim has support for tags based on the ctags utility. Ctags scans your source files and creates an index file with information about the structures and objects found in the code. Vim uses this file to know about your source code (functions, variables, etc.)
The first step is to run ctags. Change to the top level source directory of your project and run the following command:

$ ctags -R

This runs ctags recursively in all directories. Note that the BSD ctags version does not provide this flag, so it could be convenient to install the GNU version from ports (/usr/ports/devel/ctags) that gets named exctags upon installation.
Ctags just creates a “tags” file we can inspect (it is a text file):

$ less tags

!_TAG_FILE_FORMAT       2       /extended format; --format=1 will not append ;" to lines/
!_TAG_FILE_SORTED       1       /0=unsorted, 1=sorted, 2=foldcase/
!_TAG_PROGRAM_AUTHOR    Darren Hiebert  /dhiebert@users.sourceforge.net/
!_TAG_PROGRAM_NAME      Exuberant Ctags //
!_TAG_PROGRAM_URL       http://ctags.sourceforge.net    /official site/
!_TAG_PROGRAM_VERSION   5.5.4   //
__UTILS_H__     utils.h 2;"     d
main    main.c  /^int main(int argc, char **argv)$/;"   f
multiply        utils.c /^int multiply(const int a, const int b)$/;"    f

Vim uses a tags file in the current directory if present, but it can be instructed to load an alternative one using the -t flag:

$ vim -t /path/to/tags_file

Let us create a project with three files:

$ cat main.c

#include "utils.h"

int main(int argc, char **argv)
{
        int result;

        resultxx = multiply(3, 4);  /* resultxx is not defined */

        printf("Result = %d\n", result)   /* ; missing */

        retrun 0;   /* this should be 'return' */
}

$ cat utils.c
int multiply(const int a, const int b)
{
        return a * b;
}

$ cat utils.h
#ifndef __UTILS_H__
#define __UTILS_H__

int multiply(const int a, const int b);

#endif /* __UTILS_H__ */

After running ctags * (we do not need the -R flag in this example) if we place the cursor at the ‘multiply’ invocation and press Ctrl-], we jump to the function’s code. Once there, we can go back using Ctrl-t. Vim stacks the jumps so you can move forward / backwards using these commands no matter how deep you go into your code. At any time, you can check the state of the stack with:

:tags
 # TO tag         FROM line  in file/text
  1  1 multiply            8  main.c
>

Finding the declaration
The embedded gD command applied on a variable or function name moves the cursor to the declaration of that variable / function. If the prototype of the function does not exist, then it takes the cursor to the definition of the function.

Omni completion
This is known by Microsoft as “Intellisense”. It basically autocompletes in an intelligent way. For instance, if you have something like this:

struct person {
      char* name;
      unsigned char children
      unsigned char age;
};

struct person john;

Now, if you type john. and then Ctrl-X Ctrl-O, Vim will autocomplete with one of the fields of the struct. This would be in a perfect world, however this is not as easy. First off, omni completion is based on a ctags file. If you want a “full” C language completion, you need to first create a tags file scanning /usr/include, /usr/local/include and so on. In my laptop (AMD 64 900 MHz), executing this:

exctags -R -f ~/.vim/systags /usr/include /usr/local/include

takes almost 6 minutes and the generated file is as big as 791M. In addition if you install a new development library, you will need to scan the .h files to add them to you tags file. Personally I do not use omni completion as I do not think it is mature enough yet.

Compiling from inside Vim
Let us assume you are editing main.c from the project above. Usually, you would leave the editor and compile the sources by hand, typing commands in the shell. From inside Vim, you can do the same with:

$ :!gcc -Wall -o program main.c utils.c

But there is a more efficient mechanism to compile a project. This method implies the creation of a Makefile. Probably you should have a Makefile for all your projects as it improves compilation times and helps managing other tasks as installation. How to write a Makefile is out of the scope of this post, you can find more information here. Assuming you have created your Makefile, you can compile your project with:

:make

f00993z4@cipres:~/prueba/dummy> vim main.c

gcc -o program main.c utils.c
main.c: In function 'main':
main.c:8: error: 'resultxx' undeclared (first use in this function)
main.c:8: error: (Each undeclared identifier is reported only once
main.c:8: error: for each function it appears in.)
main.c:12: error: expected ';' before 'retrun'
make: *** [all] Error 1

Press ENTER or type command to continue

So far, the behavior is similar to the shell way. However, Vim parsed the output of the make command so you can see a list of errors directly from Vim:

:clist
 3 main.c:8: error: 'resultxx' undeclared (first use in this function)
 4 main.c:8: error: (Each undeclared identifier is reported only once
 5 main.c:8: error: for each function it appears in.)
 6 main.c:12: error: expected ';' before 'retrun'
Pulse INTRO o escriba una orden para continuar

The example above shows the output of make with the lines in which the errors occurred. In addition the messages are pretty nice formatted and colored ;)

After typing ENTER, Vim places the cursor at the line in which the first error occurred (line 8).
We fix the error (the name of the variable is wrong) and move on:

:cn

We have to do this several times because Vim shows you all of make’s output, so you have the whole description instead of just the first line of the error message (in this case, the error was pretty obvious, but in cases in which C++ and STL are involved, I really appreciate the complete message).
Finally, Vim places the cursor at other line:

(6 de 7): error: expected ';' before 'retrun'

We missed the ‘;’ at the previous line. We fix the error and move on:

:cn

There are no more errors. How is this possible if we did not fix the ‘retrun’ issue? It is because the compiler did not reach that point. It realized the ‘;’ was missing and after that it could not tell what the ‘retrun’ thing was about. This is how compilers work, we can not blame Vim for it :)
Let us compile again:

:make

gcc -o program main.c utils.c
main.c: In function 'main':
main.c:12: error: 'retrun' undeclared (first use in this function)
main.c:12: error: (Each undeclared identifier is reported only once
main.c:12: error: for each function it appears in.)
main.c:12: error: expected ';' before numeric constant
make: *** [all] Error 1

We fix the error, and compile for the last time:

:make

Press ENTER or type command to continue
gcc -o program main.c utils.c

Press ENTER or type command to continue

Here you are!, your project successfully compiled!.

I moved forward fixing a bug at a time, but you can use the :cp (cprevious) command to move to a previous error line.

Note that the :make command does not actually execute the ‘make’ program. It executes the program the variable makeprg is pointing to. In my BSD box, I usually do this:

:set makeprg=gmake

in order to use the GNU make version instead of the BSD licensed one.

Conclusion
Vim provides many features useful for programming. Completion is specially useful as Vim learns as you type. The other very useful thing is the possibility of executing Makefiles from inside the editor. The parsing of the output makes the edit-compile-debug cycle more efficient.

Vim Sheet (VI)

  • Ctrl-x Ctrl-l / Ctrl-p / Ctrl-f Autocomplete the whole line / word / file name
  • vim -t file_name Open Vim with the file_name tags file loaded.
  • Ctrl-] / Ctrl-t Jump to the definition of that object / Jump back. (The objects must be scanned and present in the tags file)
  • gD Go to the declaration / definition of the variable / function name
  • :make Execute make in the current directory
  • :clist Show the error and warning list
  • :cn / :cp Move to the next / previous error line.

2 Comments

  • On 07.30.10 commandliners » Vim. Saving keystrokes: abbreviations said:

    […] posts, Vim is the perfect tool if you want to save time in your daily work. It helps you in the write-compile-debug cycle, it indents and autoindents code, it is extremely powerful for searching, replacing and many other […]

  • On 01.09.12 commandliners » Greping from inside Vim said:

    […] grep command and the results will be integrated into the error list, the one we wrote about in compilation errors. This way, one can move through the matching list using both :cp and :cn We can instruct Vim to use […]

speak up

Add your comment below, or trackback from your own site.

Subscribe to these comments.

Be nice. Keep it clean. Stay on topic. No spam.

You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*Required Fields