C/C++ Completion in Emacs

05 Aug 2015 Gregory J. Stein
Tags Emacs C/C++

I do a lot of work using C/C++ for my research. As projects grow in size and the number of contributors increases, it becomes increasingly difficult to keep track of even the most commonly used classes and their parameters and methods. I rely on emacs to do most of my code editing, so ensuring that I have a code-completion setup within it is essential. I rely on company-mode, short for ‘complete anything’ for completion using additional software behind the scenes. Company is a fantastic and rather popular framework for completion within emacs. Users can create functions, known as ‘company-backends’, which each return a list of completion matches.

For my needs, I’ve become fond of using both irony, which relies on libclang, and gtags, which is a part of GNU GLOBAL. Irony is the smarter engine, capable of completing variables defined within the function and, to some extent, classes in included header files. By contrast, gtags searches the entire project directory, resulting in a cruder yet more comprehensive search. Between the two of these, I haven’t needed any others.

Continue reading for more.

Emacs Configuration

Getting emacs to work with these backends involves installing a few packages: company, irony, and company-irony. All of these are available through the package manager and ‘melpa’, and, in the configuration file I’ve provided at the github repo for this post, I rely on use-package to install them automatically. If you’ve never heard of use-package (which I have a separate post about), I highly recommend that you check it out. The configuration file in the repo includes code for installing/activating use-package if you have not already done so

The bulk of the source code I use/provide for configuring these packages is shown here:

Emacs Lisp for Code Completion Lisp
;; == irony-mode ==
(use-package irony
  :ensure t
  :defer t
  :init
  (add-hook 'c++-mode-hook 'irony-mode)
  (add-hook 'c-mode-hook 'irony-mode)
  (add-hook 'objc-mode-hook 'irony-mode)
  :config
  ;; replace the `completion-at-point' and `complete-symbol' bindings in
  ;; irony-mode's buffers by irony-mode's function
  (defun my-irony-mode-hook ()
    (define-key irony-mode-map [remap completion-at-point]
      'irony-completion-at-point-async)
    (define-key irony-mode-map [remap complete-symbol]
      'irony-completion-at-point-async))
  (add-hook 'irony-mode-hook 'my-irony-mode-hook)
  (add-hook 'irony-mode-hook 'irony-cdb-autosetup-compile-options)
  )

;; == company-mode ==
(use-package company
  :ensure t
  :defer t
  :init (add-hook 'after-init-hook 'global-company-mode)
  :config
  (use-package company-irony :ensure t :defer t)
  (setq company-idle-delay              nil
	company-minimum-prefix-length   2
	company-show-numbers            t
	company-tooltip-limit           20
	company-dabbrev-downcase        nil
	company-backends                '((company-irony company-gtags))
	)
  :bind ("C-;" . company-complete-common)
  )

Configuring the Completion Engines

Despite the power of emacs, the completion backends installed here leverage the power of additional tools which must be configured on their own, namely clang and GNU GLOBAL. This section shows how to install these tools and how to set them up for use alongside emacs. [Ubuntu is currently the only procedure listed here, but I’ve gotten OS X working, and I will include it as well in the future.]

Ubuntu 14.04 Installation

On Ubuntu, the installation of the required packages is extremely simple, since it relies on the apt package manager. Simply run the following:

$ sudo apt-get install libclang-3.5-dev
$ sudo apt-get install global

Note that libclang-3.5-dev, the requirement for Irony, is the latest version as of this writing, but feel free to use a later version if one exists. Finally, gtags is included in the install of global.

Activating Irony and GNU Global

Once installed, the different packages must be properly set-up (outside of emacs) in order to get them to work properly, though this process is extremely simple for both.

For Irony, activation requires simply calling a command from emacs, namely:

M-x irony-install-server

In which file this code is run does not matter. The command creates an irony folder in your ~/.emacs.d directory, and the resulting irony server is shared across emacs. However, Irony is quite intelligent, and can determine what classes have been imported into the file of interest.

For GTAGS, one must use the command line. Simply move to the parent directory of your project using cd and then run the gtags command. This will parse all of the source files in the directory and create a list of “tags” which are then used for code completion. It should be noted that, I cannot seem to figure out if GTAGS updates as files are saved. Though Irony will asynchronously re-parse files it has already searched, you may periodically need to rerun the gtags command on the directory as files are substantially changed. However, this should not be an overwhelming inconvenience for large projects, for which large portions of the code remain static for extended periods of time.

An Example

I’ve included some (very basic) example code at the github repo associated with this post. In it, besides the emacs lisp configuration file shown above, are a few C++ files which can be used to test the code. Clone this repo and then use C-x C-f to enter the included main.cpp file. The contents of the file include a few simple variable definitions. In addition, the GTAGS files are not included, so you will have to run gtags on the directory.

The first set of variables defined are foobar and foobar_d. To call completion, type foo on a new line and, with your cursor placed directly after, run company-complete-common using the C-; keybinding and you should be presented with a drop-down menu as follows:

Selecting the option you’d like can be done in a number of ways:

In addition, the completion engines can be used to complete the class members of an object. In this case, I’ve defined square, an instance of the included rect class. It has two integer properties, height and width and a function area(). Run company-complete-common after typing square. (note the period) and the following should appear:

Notice that some of the options appear multiple times. This is because both completion engines are capable of finding some of the same candidates. This simple example should be enough to get you started with your own projects.

Further Reading

If you wish to configure these packages further, I recommend that you take a look at the github repositories for each:

Finally, this tutorial was inspired by this incredible post/tutorial: Setup C/C++ Development Environment for Emacs. If you’re seriously looking at using emacs for C/C++, you can pick up a lot of tricks by looking through it.