There are a lot of blog-posts how to add C++ support to Emacs. But i would like to share my personal configuration, because it took my some time to get it working properly. I tested this tutorial on macOS, but added some notes on how to do it on linux based systems.

First of all you need the following packages/tools:

  • ycmd (the server-part)

And the following Emacs packages:

  • emacs-ycmd (the client)

  • company-mode (text completion framework for Emacs)
  • company-ycmd (company-mode client for emacs-ycmd)

  • yasnippet (for snippets)

  • flycheck (on-the-fly syntax checking extension for Emacs)
  • flycheck-ycmd (flycheck client for emacs-ycmd)

  • eldoc (to show the argument list in the echo area)

Install requirements

So let’s install ycmd first:

  • Install the dependencies
    • On debian-based systems: sudo apt-get install build-essential cmake python-dev
    • On macOS: xcode-select --install and install cmake with homebrew
  • Clone the ycmd-repo and run git submodule update --init --recursive in the repo-folder.

  • Now you can build ycmd for C-family languages: ./build.py --clang-completer

Configure Emacs

To make completion work in Emacs (including snippet-completion), we need the following configuration in our init.el.

;; Snippets
(use-package yasnippet
  :ensure t
  :diminish yas-minor-mode
  :init (yas-global-mode t))

;; Autocomplete
(use-package company
  :defer 10
  :diminish company-mode
  :bind (:map company-active-map
              ("M-j" . company-select-next)
              ("M-k" . company-select-previous))
  :preface
  ;; enable yasnippet everywhere
  (defvar company-mode/enable-yas t
    "Enable yasnippet for all backends.")
  (defun company-mode/backend-with-yas (backend)
    (if (or 
         (not company-mode/enable-yas) 
         (and (listp backend) (member 'company-yasnippet backend)))
        backend
      (append (if (consp backend) backend (list backend))
              '(:with company-yasnippet))))

  :init (global-company-mode t)
  :config
  ;; no delay no autocomplete
  (validate-setq
   company-idle-delay 0
   company-minimum-prefix-length 2
   company-tooltip-limit 20)

  (validate-setq company-backends 
                 (mapcar #'company-mode/backend-with-yas company-backends)))

Now you should have some basic auto-completion in all buffers.

To get C++-completion you now need to install the ycmd-client for emacs.

;; Code-comprehension server
(use-package ycmd
  :ensure t
  :init (add-hook 'c++-mode-hook #'ycmd-mode)
  :config
  (set-variable 'ycmd-server-command '("python2" "/path/to/ycmd/ycmd"))
  (set-variable 'ycmd-global-config (expand-file-name "~/path/to/ycmd/ycm_conf.py"))

  (set-variable 'ycmd-extra-conf-whitelist '("~/Repos/*"))

  (use-package company-ycmd
    :ensure t
    :init (company-ycmd-setup)
    :config (add-to-list 'company-backends (company-mode/backend-with-yas 'company-ycmd))))

This configuration adds a hook to c++-mode and enables ycmd-mode. You need to adjust your paths to ycmd (the server-part which you installed earlier) and the ycm_conf.py inside the server-repo.

You may want to configure a whitelist of paths, where ycmd is allowed to load project-specific configurations, but we will get to this later.

c++-completion

For on-the-fly syntax checking, you need to install flycheck and flycheck-ycmd.

;; On-the-fly syntax checking
(use-package flycheck
  :ensure t
  :diminish flycheck-mode
  :init (global-flycheck-mode t))

(use-package flycheck-ycmd
  :commands (flycheck-ycmd-setup)
  :init (add-hook 'ycmd-mode-hook 'flycheck-ycmd-setup))

on-the-fly syntax checking

ElDoc shows the argument list of a function in the echo area.

;; Show argument list in echo area
(use-package eldoc
  :diminish eldoc-mode
  :init (add-hook 'ycmd-mode-hook 'ycmd-eldoc-setup))

argument-list

Configure ycmd for a complex project

ycmd now works just fine for simple project. But as soon as you start using external libraries or something like this, you need a specific configuration-file for ycmd. Using YCM-Generator, you can generate such a configuration for you project.

Clone the repository and add config_gen.py in you $PATH.

Now you can change into your project and run config_gen.py . to generate a .ycm_extra_conf.py Take a look into this configuration file and make sure the generator did a good job. There is an array with flags at the top of the file. For C++ projects you may want to have something like this:

flags = [
    '-std=c++14', 
    '-x',
    'c++',
    /// '-I /path/to/include'  ...
]

library completion

You can run M-x ycmd-show-debug-info to check, if your setup works.

ycmd-debug

For other configurations, take a look at my init.el. If have trouble configuring you emacs using this blog-post, please let me know!