So, this past week I decided to redo my .emacs file. Previously, I was using the (somewhat common?) literate programming approach with org-mode, but I decided to ditch that, go back to a flat file and embrace John Wiegley’s excellent use-package.
My .emacs is at https://github.com/elbeno/dotemacs if you want to follow along or just peruse it at your leisure. I’m not going to cover every detail, but there is some useful stuff so I’ll call it out. This post is going to be a bit of a link-dump…
First, since I keep everything in a git repo, I usually structure it so that I just symlink ~/.emacs to the .emacs file in the repo. Everything “fixed” stays in the repo, and ephemeral stuff like caches gets automatically put in ~/.emacs.d. So the top of the file looks like this:
;; this file's true directory (setq dotfile-dir (file-name-directory (file-chase-links (or load-file-name (buffer-file-name))))) ;; my stuff is in .emacs.d (add-to-list 'load-path (concat dotfile-dir ".emacs.d/")) ;; 3rd party stuff is in site-lisp (add-to-list 'load-path (concat dotfile-dir ".emacs.d/site-lisp/")) ;; packages (setq package-user-dir (concat dotfile-dir ".emacs.d/packages/")) ;; extra binaries (if needed) are in site-bin (add-to-list 'exec-path (concat dotfile-dir ".emacs.d/site-bin/"))
The package system
Next, the package system gets set up, and use-package is loaded. Previously using org-mode, startup time was appreciable, and now, using use-package it’s in the sub-second range: most packages are deferred until needed.
(setq package-archives '(("org" . "http://orgmode.org/elpa/") ("gnu" . "http://elpa.gnu.org/packages/") ("elpa" . "http://tromey.com/elpa/") ("marmalade" . "http://marmalade-repo.org/packages/") ("melpa" . "http://melpa.org/packages/"))) (package-initialize) (setq package-enable-at-startup nil) (unless package-archive-contents (message "Refreshing package archives...") (package-refresh-contents)) (unless (package-installed-p 'use-package) (message "`use-package' not found. Installing...") (package-install 'use-package)) (require 'use-package) (setq use-package-minimum-reported-time 0 use-package-verbose t use-package-always-ensure t)
Then I have a bunch of stuff that sets fonts (10-pt Inconsolata is my font of choice), and turns off everything people usually turn off – toolbar, menu, scrollbar, startup screen message, etc. I have a few functions to wrangle frames, and a bunch of top-level key bindings I’m used to. Then it gets to the important stuff: what helps me code.
Generally useful packages
- smartparens – for doing stuff with anything surrounded with stuff, whether that stuff is parens, square brackets, angle brackets, quotes, or something else user-defined. Takes a little getting used to, and really shines in lisp, of course, but the notion of hybrid s-expressions is really useful also.
- rainbow-delimiters – subtly colours each pair of brackets/parens/whatever differently.
- rainbow-mode – colourizes colour names and codes in the buffer. I had a Greenspunned version of this before, I threw it out when I found the package. Nice.
- bm – just a really nice simple bookmarking solution. I had it before, I’m keeping it now.
- smex, flx-ido, ido-ubiquitous, ido-vertical-mode – this time around I added the vertical mode, which I’m finding pretty nice, especially with
(setq ido-vertical-define-keys 'C-n-C-p-up-and-down)
- undo-tree – the best package for navigating Emacs’ undo system.
Of course, there’s magit. That goes without saying. But there are also the mode packages: gitconfig-mode, gitignore-mode, git-commit-mode and git-rebase-mode. They just ease the everyday.
I already knew about git-gutter-fringe+ and mo-git-blame. But mo-git-blame is a little heavyweight… and I discovered git-messenger, much lighter weight. Cool. I also found git-timemachine which allows stepping back/forward through history. Awesome!
I program in several languages semi-regularly and Emacs supports them all, but mostly I use C++, and C++ is a tricky beast to wrangle. So I really wanted to improve things. There’s the behemoth that is CEDET, and it can do a load of things, but I’ve found it slow to do most of them.
When it comes down to it, the following are really the features I want/need:
- Quick switch between header and implementation file: for this, I use Projectile‘s
- Open file in project – Projectile covers that too with
- Open header at point – Projectile does that with
- Grep files in project – you guessed it,
- Autocomplete – this is the big one. This is the one that’s really slow on a big codebase. And there are no good solutions on Windows, really. CEDET (semantic) isn’t a good solution either – it’s too slow. But a mix of company and (on Linux) irony-mode works really well, using libclang. For Windows, I just use the company-dabbrev-code backend because I’m in a Microsoft ecosystem.
- For header completion, company-c-headers does the job.
- Flycheck and flycheck-irony give me in-buffer errors/warnings on Linux. That’s nice.
- For go-to-definition, I use plain old
etags-table, but with a nice addition to keep tags automatically up-to-date with git interactions.
- One more thing: automatic header insertion with my own hacked-up variant of cpp-auto-include. I updated it to separate and alphabetize the STL headers and deal with all the new C++11/14 stuff. So with a keypress, Emacs can automatically pull in the headers I need.