Emacs configuration for C++/CMake/git
Table of Contents
Intro
I hope this emacs configuration file will help explain a little elisp and how to write your own.
The most important take-away is understanding how emacs can help read and learn from other people's configurations. Once you know how to navigate the documentation you can pick apart any config file and expand your own
Note that I wrote this knowing no lisp at all - so if there are mistakes or you would like to make some suggestions I'd really appreciate you telling me by adding an issue on the github repo
General Settings
Package managment
To begin extending Emacs we will want to be able to add external packages. Emacs comes with a built in package manager called package which we will use to get external package. We load it by using require and then we initialize it.
(require 'package) (package-initialize)
To see more specifically what the require function does type C-h f require RET. You'll find that virtually every function will have some associted documentation.
Note the use of a leading quote before the word package; This tells emacs that the word package is a symbol. If you ommit the quote then emacs will try to evaluate package as a function and give you an error.
Next we need to tell the package manager where to look for new packages. There is a limited GNU-blessed repository called ELPA, but we will also want to add another larger repository called MELPA. To do that we we just need to add it to the list called package-archives
To look up a variable, use C-h v [variable] RET
So type C-h v package-archives RET to see what it's for
(add-to-list 'package-archives
'("melpa" . "https://melpa.org/packages/"))
This C-h [something] [something] pattern repeats for a lot of features. To see the complete list just enter C-h C-h. These key-bindings will be your way of exploring configurations you find online.
The newer fancy way of managing package is using use-package. I've included it as a git submodule in my .emacs.d directory which you can see in the repository (be sure to clone recursively if you decide to use my version). You can get it yourself manually from: https://github.com/jwiegley/use-package
Once we have it in our directory we just need to add it to the load-path and load it in using require like last time
(setq load-path (cons (concat (file-name-directory load-file-name) "use-package") load-path)) (require 'use-package)
setq is the function that sets a variable to a value. The q indicates that we don't need to add a quote to the first arguement. There is also the more cumbersome (set 'arg1 'arg2). The rest of the code is there to append the new path to use-package correctly to the existing load-path.
Indentation - Change the default indentation from 2 spaces to 4
Indentation is generally govered by two variables
default-tab-width - this is the variable for any text document when you normally type in a TAB
c-basic-offset - when working with source code the indentation is done automatically and based on this offset value (a lot of modes derive from c-mode, hence the name)
More info: https://kb.iu.edu/d/abde
(setq c-basic-offset 4)
Line wrap
Next we need to enable line-wrap in org mode. By default, as you keep typing the page scrolls to the right. So a whole paragraph will appear as one line making it difficult to navigate
(add-hook 'org-mode-hook (lambda () (setq truncate-lines nil)))
again, don't hesitate to look up all the variables and function. Hooks are in general places where you can add function to be called at designated times. Here it's a function that sets a variable each time org-mode is enabled.
Theme
This is the only decent light theme I could find. The advantage over the default theme is that it will color a more things in more modes. The most important to me me is that it will color code blocks in org-mode
(use-package moe-theme :ensure t :config (moe-light))
Git
For using git we want to have a couple of tools
Magit
This is the tool for inspecting and updating out git repository. It's a little complicated to use, so look up documentation for it. It is a must for development in emacs if you use git - so make the investment and learn to use it.
(use-package magit :ensure t)
Projectile
This will manage our workspaces. Each workspace will be tied to a git repository. This makes it so that our buffer list doesn't get really crowded when we are working on multiple projects
(use-package projectile :ensure t :config (projectile-mode))
I sometimes use this - and other times I just run separate emacs sessions for different projects.
Orgmode
Some adjustments to org-mode
see here for reference
(setq org-confirm-babel-evaluate nil ;; don't prompt for confirmation about executing a block org-src-tab-acts-natively t org-use-sub-superscripts '{} org-src-fontify-natively t) (use-package htmlize :ensure t)
1 - Turns off the annoying "are you sure?" prompts on tangle export
2 - Makes tabs work in the source code blocks the same as it would in a buffer with that source code
3 - Makes it so underscores aren't interpreted as subscripts unless used with braces
(I often need underscores for file/variable names)
4 - Make source code gets colored based on the language
5 - htmlize will colorize orgmode code-blocks code in the exported HTML
For more info on any of these variables, again, use C-h v [variable] RET
C++
Here we'll setup a development environment as feature rich as an IDE
I based it on this guide - but I've really pared it down. I still seem to get all the same autocomplete/navigation and error squigglies that the author gets just by using this subset of packages, so I don't really know what I'm missing here
RTags
The backbone of most modern C++ dev environments leverage libclang to parse the C++ codebase. In Emacs we do the same using a independent system called rtags which runs as a deamon parsing your codebase in the background. Emacs has a rtags package for launching and communicating with the deamon which will then feed all the information available to the compiler front-end. So to get started you need to go to the rtags github and install rtags. The rtags documentation is extensive, but for our purposes we just need to have rtags in the system path. The rest will be handles by Emacs. Once you're done installing, if you want to sanity check you can run rdm from the command line and see some process boot up (you can kill it - Emacs will be launching rdm itself by the end of this configuration)
Once you have it installed somewhere in your path we need to do the following:
1 - enable rtags
2 - enable standard key binding
2 - enable diagnostics (needed for code completion!)
3 - enable code completion
4 - launch the rtags deamon
(use-package rtags :config (rtags-enable-standard-keybindings) (setq rtags-autostart-diagnostics t) (rtags-diagnostics) (setq rtags-completions-enabled t) (rtags-start-process-unless-running))
There are c++ code base parsers like
ctagsggtagsetc. which are much easier to set up but they will not ultimately give you the same level of robustness and information b/c onlyrtagsleverages a compiler front
Even if you're not building with
clang(say you're targetting embedded and are using something like gcc),rtagsshould be able to index your project. So don't feel like it ties you toclang
Company
Next we turn on company. The package that will do autocompletion for us (it standards for COMPlete ANYthing)
(use-package company :config (push 'company-rtags company-backends) (global-company-mode) (define-key c-mode-base-map (kbd "<C-tab>") (function company-complete)))
looking at the documentation we see that push will take the 1st argument and add it to the beginning of the list provided in the 2nd argument. company-backends is "a list of active backends (completion engines)". company-rtags is a backend provided by the rtags guys. See the documentation for more info :)
CMake-IDE
The last part is adding cmake-ide
(cmake-ide-setup)
This is the glue that will point rtags to the right location and make everything work. When you are in a buffer for a c++ file it will look for it's corresponding CMakeLists.txt and run cmake and hook rtags up.
Using the environment
Now that everything is set up there are a couple of small caveats before you can use it all:
1 - You need to give cmake-ide a build directory by setting cmake-ide-build-dir
(setq cmake-ide-build-dir '"/some/build/directory/")
2 - You need to run cmake at least once manually in that directory with all the flags you want
cd /path/to/build/directory cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -D[your flags/options.. like build release/debug] /path/to/source/directory
The first flag makes cmake export a .json file describing the build. rtags uses this file to understand which source files to feed into libclang (so this is happening independent of your actual build toochain whether it be gcc or something else)
These last two steps are a bit clunky but only have to be done once. After that rtags will run in the background reindexing the project when appropriate. You can now jump to definitions with C-c r ., things will autcomplete with C-TAB, errors will get underlined (when your cursor rests over the error it'll print in the minibuffer), and things will compile when you run M-x cmake-ide-compile. rtags in particular has a ton of different features - so look at their github for more info
Note that things sometimes get a little wonky when rtags is not finished indexing in the background. So sometimes you just need to give it a little time to reindex! This is especially true after the first time you build the project. So give it a minute to settle down before you frantically try to debug things
Wishlist/TODOs
- Make the last clunky part into an elisp function that prompts for a build directory
- Find a way to inspect the Rtags backend's state so that you can have something in the mode-line that tells you if it's still busy
This webpage is generated from an org-document (at
./index.org) that also generates all the files described.Once opened in Emacs:
C-c C-e h hgenerates the webpage
C-c C-v C-texports the code blocks into the appropriate files