ZNHOO Whatever you are, be a good one!

Emacs C/C++ IDE

  1. Tag
  2. Semantics and Completion
  3. GNU BLOBAL
  4. Installation
    1. Universal Ctags
    2. Emerge GLOBAL
    3. ggtags
    4. pygments
  5. GLOBAL tag database
  6. Usage
    1. Multiple matches
  7. Code Completion
    1. Header file completion
  8. References

This tutorial demonstrates the process of setting up a C/C++ development environment within Emacs. It mainly involves GNU GLOBAL and ggtags.

Tag

Tag is just an index file storing language objects from project sources, which allows these objects to be quickly and easily located and queried by text editors or other utilities.

In regard to tag environment, we should differentiate concepts of command and format. etags generates 'TAGS' files understood by Emacs while ctags generate 'tags' files for Vi.

'tags' format contains far richer metadata than 'TAGS' format that cannot be interpreted by Emacs. In spite of that, both formats are more or less the same.

Emacs package has built-in etags and ctags commands. There exist other standalone package providing these commands, such as universal-ctags and exuberant-ctags(deprecated).

Nowadays, ctags from universal-ctags can generate TAGS format with -e option. Hence, we can concentrate on ctags. Besides, ctags supports more languages.

Another package cscope that is more powerful than ctags is, in reality, dead. Originally, it encompasses much more features, especially when C and C++ are concerned. Moreover, it has its own tag database unlike plain-text format. You will find, from this tutorial, that cscope is silimar to GNU GLOBAL but support fewer languages.

Semantics and Completion

Tag only cares about sources navigation. Upon writing code, we want semantic parser to understand source objects and then achieve auto completion.

There exists a host of auto-completion packages, like company-mode.

GNU BLOBAL

GNU GLOBAL is an universal source code tagging system across diverse platforms and environments, such as Emacs, Vim, Bash Shell, various web browsers.

Through the command global, we can easily locate various objects, such as functions, macros, structs, classes etc. The gtags command is similar to etags and ctags, but is different from them in the following two points:

  1. Independence of any editor;
  2. Capability to treat both definition and reference;
  3. Tag database support.

GLOBAL supports 6 langages as C, C++, Yacc, Java, PHP4 and assembly by built-in semantic parser. Actually, GLOBAL brings along gtags-cscope support which, though outdated, can replace the default parser when parsing C/C++.

To support a wider range, we can combine GLOBAL, Pygments, etags/ctags plug-in parser. As mentioned above, ctags is a superset of etags. Ideally, we prefer ctags of universal-ctags over the built-in one of Emacs.

Installation

Universal Ctags

Before make GLOBAL, we should build universal-ctags ctags. The goal is to replace the default /usr/bin/ctags from Emacs.

Then supply

./configure --prefix=<PREFIX> --with-exuberant-ctags=/usr/local/bin/ctags
# -or-
./configure --prefix=<PREFIX> --with-universal-ctags=/usr/local/bin/ctags

argument, telling GLOBAL where universal-ctags locates.

I won't discuss building universal-ctags in this post and maybe get back to topic later on. Instead, the default GLOBAL built-in parser is enough for C/C++.

Emerge GLOBAL

Since GLOBAL is independent of editors, it requires imtermediate bridges. To interact with Emacs, it has gtags.el. We should enable the emacs USE:

root@tux ~ # echo 'dev-util/global emacs' >> /etc/portage/package.use/global (opt)
root@tux ~ # emerge -avt dev-util/global

Actually, Emacs also has etags.el for default etags and ctags.el for default ctags.

ggtags

However, gtags.el is quite primitve, we would like the improved version ggtags. So just install ggtags.el through ELPA list-packages.

After installing ggtags, add to init.el:

(require 'ggtags)
(add-hook 'c-mode-common-hook
          (lambda ()
            (when (derived-mode-p 'c-mode 'c++-mode 'java-mode 'asm-mode)
              (ggtags-mode 1))))

Another replacement is helm-gtags. I will leave it to yourself.

pygments

In order to support syntax highlighting, we need pygments. It can be installed through package manager or within Python virtual environment.

After installing pygments, we also require an intermediate plugin for GLOBAL, namely Pygments Plug-in Parser for GNU GLOBAL.

Since 6.3.2, GLOBAL includes it by default. For older version, we should install it manually.

GLOBAL tag database

A tag is a name of an object/entity in source code. An object can be a variable, a method definition, an include-operator …

A tag contains information like name of the entity, location in the source code, and which source file it belongs to.

GNU GLOBAL makes use of three tag databases:

GTAGS definition database
GRTAGS reference database
GPATH path name database

A definition of a tag is where the tag is implemented (not declaration). For example, a function definition is the block where the function is actually implemented.

On the contrary, a reference of a tag is where it is used in a source tree, but not where it is defined.

Usage

Up to now, we have almost finished the installation and configuration process. Next, we will examine how these components collaborate with each other to achieve an intelligent development environment.

The most commonly used command is ggtags-find-tag-dwim bound to M-., which find a tag by context. It will jump to a reference if the tag at point is a definition. Rather, it will jump to a definition if the tag at point is a reference. The the tag at point is a include header, it jumps to that header file.

Other useful functions are:

ggtags-find-definition find definition tags
ggtags-find-reference find reference tags
ggtags-find-other-symbol find tags that have no definitions
ggtags-find-tag-regexp find definition tags by regexp
ggtags-query-replace do a query & replace in all files found in a search
ggtags-find-file find a file from all the files indexed by gtags
ggtags-visit-project-root open the project root in dired

Multiple matches

When a search find multiple matches, a new buffer named ggtags-global is created and ggtags-navigation-mode is enabled to faciliate entry selection by following commands:

M-n move to the next match
M-p move to the previous match
M-} move to the next file
M-{ move to the previous file
M-= move to the file where navigation session starts
M-< move to the first match
M-> move to the last match
C-M-s, M-s s use isearch to find a match
M-, abort and go back to the location where search was started

Code Completion

Install company-mode and add to init.el:

(require 'company) ; optional
(add-hook 'after-init-hook 'global-company-mode)

By default, company-clang backend to retrieve standard libraries (/usr/include/) but not our project files. We can make use of company-complete that encompasses company-clang.

(setq company-backends (delete 'company-semantic company-backends)) ; optional
(define-key c-mode-map  [(tab)] 'company-complete)
(define-key c++-mode-map  [(tab)] 'company-complete)

By this snippet, we use 'company-complete' which calls 'company-clang' on demand while completes project entities. The very first reference suggests deleting company-semantic from company-backends. However, as far as I know, it is unneccessary.

Then we resort to GNU Emacs Manual - Per-Directory Local Variables, creating a special file .dir-locals.el under our project root. This file is usually used to define directory-local variables.

Emacs will read it when it visits any file in that directory or any of its subdirectories, and apply the settings it specifies to the file' s buffer.

We set company-clang-arguments in this file, specifying the project header files' location, namly the include path.

((nil . ((company-clang-arguments . ("-I/home/<user>/project_root/include1/"
                                     "-I/home/<user>/project_root/include2/")))))

That is enough! company-clang is able to retrieve completion candidates from our project sources.

Header file completion

company-c-headers helps complete #include statements by searching header filenames.

After installation, add to init.el:

(add-to-list 'company-backends 'company-c-headers)

By default, company-c-headers only covers C headers under /usr/include/ and /usr/local/include/. To support C++ headers, we should:

(add-to-list 'company-c-headers-path-system "/usr/include/c++/4.8/")

Simiarly, to complete projects header files, set company-c-headers-path-user in .dir-locals.el.

References

  1. C/C++ Development Environment for Emacs
  2. Universal Ctags
  3. ggtags
  4. GNU GLOBAL