How I work in Emacs
Posted on
This is about the way I use and work in Emacs, which I think departs from the more mainstream usage.
This is not so much about my Emacs setup. This is more about how I use Emacs that happens to include some of my configuration as well.init.el
Generally, I try to keep my Emacs configuration as small as possible and leverage all the built-in functionality and this write-up is about recording all the things I find really useful in Emacs. I have tried configuration frameworks like Spacemacshttps://www.spacemacs.org/ and Doom Emacshttps://github.com/doomemacs/doomemacs, especially when I was switching back from Vim to Emacs. The issue I had with these configuration frameworks is that it adds another layer of things on top of vanilla Emacs that I need to know how to work with. Another reason for dropping Doom was the community living in DiscordI really liked Doom Emacs and have cribbed some of the speed improvements, but I stopped using the framework. Today, my philosophy on Emacs configuration is more aligned with System Craftershttps://systemcrafters.net/, namely starting from vanilla Emacs, maintaining my own configuration and preferring built-in tools over external packages.
Variable-width fonts
I have written about writing code in a variable-with font beforeTaking Acme for a spin (update) and this is still the default setup. I used the Go fonts, but have switched to the Liberation Sans fonts. Sometimes, I switch to a monospace font and when I do I roll with Fira Code.https://github.com/tonsky/FiraCode
In terms of styling I now use the default Emacs theme. In the past I had settled on a slightly modified version of nofrils-acme.https://gitlab.com/esessoms/nofrils-theme I also have nyan-modehttps://github.com/TeMPOraL/nyan-mode/ just because it’s cute. Aside from a handful of other minor UI modifications I use the defaults.
Compilation mode
In short: compilation modehttps://www.gnu.org/software/emacs/manual/html_node/emacs/Compilation-Mode.html rules! During development, I tend to follow this pattern:
-
Set up a command with M-x compile. For example
make
ordune build
ordune build && ./run-tests
-
F9 is set up as a shortcut for M-x recompile, so repeating the command is just one keystroke
-
If there is an error in the output, it’s nicely highlighted and M-g n takes me directly to the file and line with an error. (This only works if the compiler produces error output with file names and line numbers.)
The ability to jump from directly to file that needs attention without having to work out manually the file name and line number is nothing short of amazing. This is something I could never get in a multi-terminal workflow and I don’t want to work without it now.
I also prefer to have precise control over when I re-run my command. I have explored automatic re-running of commands in all my work setups: in Acme,Watch by Russ Cox in the terminalentr(1) and in EmacsFile Notifications in Emacs, but I find that re-running a command on every save is too much. Often I’m just saving a file as a reflex, but I don’t want to run my command. Being in control of when the re-run happens works much better for me.
My overall configuration of compilation-mode is quite barebones:
(setq compilation-scroll-output 'first-error)
(setq compilation-always-kill t)
(global-set-key [f9] 'recompile)
(add-hook 'compilation-filter-hook 'ansi-color-compilation-filter)
Shells
I also run shells in Emacs. I started with M-x shell, and have slowly graduated to invoking C-x p s, to get a shell for the current project (which tends to be a git repo). I have experimented with using Emacs’ eshell but haven’t so far found a compelling reason to use it.
Using a shell within Emacs has a couple of side effects: I tend not to repeat commands when I need to inspect its output because I can just scroll back and search in the buffer. I don’t need to pipe output through less(1) to inspect it. Copying from the shell is the same as copying from any other buffer.
Running shells in Emacs is slower than running a terminal like Alacritty or some other terminal emulator that’s built for speed. This is only really an issue if I happen to produce a lot of output in a short amount of time, but I try to avoid that.
Being as comfortable using shells as I am let’s me use all the commands that I already know. Many of the tools I use are CLI tools, such as…
Git
I use Git in the shell.
I don’t use Magit.https://magit.vc/ Which might be a shame because it’s supposedly great. I tried and found it difficult to navigate and remember all the shortcuts. I also suffer from fat finger syndrome and many times experienced hitting some random key that turned out to be bound to a Magit function, then the UI changed and I was immediately lost. The only part of Magit that I use (and like) is the git-commit package.
Mostly I found that the terminology used in Magit is just slightly off from Git’s own and that causes me a lot of confusion. Plus the interface is (understandably) quite different from Git’s CLI which also requires some effort to get used to.
I already knew Git and its CLI quite well before encountering Magit and I am comfortable with most of the subcommands and their usage. All that being said, Magit appears to be great for a lot of developers, I probably just haven’t put in the time to get used to it.
I use Emacs as the editor for commit messages and have core.editor = emacsclient
in my ~/.gitconfig and export EDITOR=emacsclient
in my
environment.
I have settled on mu4ehttps://www.djcbsoftware.nl/code/mu/mu4e.html. It just works. Fetching and indexing mail is done with isynchttps://isync.sourceforge.io/
With mu4e I rely on the menu items for actions that I can never remember the keybindings for. Attaching a file to an email is not something I do often, so I don’t have the shortcut memorised.
Package management
Gentoo has a lot of app-emacs/ packages. I decided to use those instead of the built-in package manager. Whether this is a good or bad decision remains to be seen. Any packages that don’t exist in the Gentoo repository, I just package in my own overlay.
This extra “hump” that is needed to pull in a dependency is a great tool to prevent me from installing any random package I come by.
One of my favorite packages is app-emacs/ebuild-modehttps://gitweb.gentoo.org/proj/ebuild-mode.git/, which is specific to writing Gentoo ebuilds.
Man pages
To browse manual pages, M-x man has you covered.https://www.gnu.org/software/emacs/manual/html_node/emacs/Man-Page.html I find it useful to have the manual page in a buffer next to the window I’m working in. This goes for both looking up documentation for shell commands but also looking up C stdlib functions. If the cursor is placed on a word, that word becomes the default suggestion: for example memcpy, then M-x man will suggest memcpy and all I have to do is hit RET.
Window management
I use tab barshttps://www.gnu.org/software/emacs/manual/html_node/emacs/Tab-Bars.html and generally have two tabs active - one for development and another for email. I have tried using multiple frames but found unwieldy to manage.
I create windows in the current tab with C-x 3 (split-window-right) and C-x 2 (split-window-below). Getting rid of windows in the current tab is either with C-x 0 or C-x 1 I switch between windows with C-x o or just use the mouse to select a window. I have tried other packages that implement different ways of navigating windows and tabs, like ace-windowhttps://github.com/abo-abo/ace-window, but none of them have stuck.
Other tools
I like browse-url and use it frequently (M-x br-u). I like ffap as well. I am almost ready to rebind C-x C-f to ffap.
I started using C-x p g to grep for words in the current project. I grep around in codebases quite frequently and xref provides a nice interface. Emacs’ builtin project awarenesshttps://www.gnu.org/software/emacs/manual/html_node/emacs/Projects.html provides a few other useful shortcuts that I don’t use frequently, but I really should.
I try to use dired as much as I can, but often revert to browsing the file system in the shell.
Overall, for me Emacs hits the right spot between Acme and Vim (as odd as that might sound). Everything (almost) being text in a buffer makes searching through buffers, be it code, man pages, email, shell history behave the same, and copying from one location to another is a breeze.