Skip to content

NateEag/.emacs.d

Repository files navigation

Nate Eagleson's Emacs Config

Ever since Dr. Bui told our C++ programming class to use it for programming in the Sun lab, Emacs has been my editor.

On that first encounter, I was put off by its user interface, which is obscurantist at best. I discovered that the default install is incredibly unwieldy, and I was not impressed with how little it believed in graphical environments.

Nonetheless, I learned the basic keybindings and used it for assignments, often via a terminal over SSH.

After a naive search for the perfect editor in my early twenties (I narrowly avoided wasting years writing one), in 2009 I decided to learn Emacs well, since I knew a lot of its shortcuts and had decided to stick with an editor.

A year or two into the project, I realized my dream editor had been sitting in my lap all along, if only I had tried to learn it.

Why Emacs (or: Hokey Religions and Ancient Weapons)

Like all art forms, programming has technique.

A musician's technique is how he makes the instrument produce sound.

A painter's technique is how she puts paint on the canvas.

A programmer's technique is how he gives the computer instructions.

Despite the grumbling from the graphical language crowd, most programming comes down to entering, reading, and changing plain text in files.

Thus, a programmer should manipulate text fluidly and effortlessly, the way a pianist plays arpeggios or a painter wields a brush.

Changing editors for each language complicates technique. Eclipse for Java, PyCharm for Python, Sublime for JavaScript... The user interface, keyboard shortcuts, and mental model for editing a program are different in each of these, and over a lifetime adds cognitive burden.

Instead of changing editors for each language, a programmer's editor should adapt itself to each language, so that the technique of programming remains unchanged.

In the same way, the programmer should not adapt herself to the editor -the editor should adapt to her.

For these purposes, Emacs reigns supreme.

It has been honed over decades to a razor-sharp edge, it runs almost everywhere, and it can be rewritten without restarting it.

My Setup

Emacs' default keybindings are powerful but verbose. They're hard to type and may be why RMS has had wrist problems. I use evil-mode for vim-style modal editing, which fits my brain and makes my hands hurt less. evil-leader, evil-commentary, evil-surround, and evil-avy make it even better.

I'm slowly moving to use-package for configuring and loading packages on-demand. It's a huge improvement over my accumulated piles of ad-hoc code.

I use flycheck for style-checking just about everything, and install the relevant checkers whenever I start working with a new language.

I use smartparens to insert paired characters ((), [], {}, etc). It also has powerful navigation features I haven't really learned to use.

I use yasnippet to insert code snippets, so I don't have to type as much in verbose languages. I mostly use it for language constructs, since anything more is usually bad for codebase health (one-time code generation makes for WET code, and fixes in the generator rarely make it to previously-generated code).

I use Solarized as my color theme and flip between light and dark variants based on lighting conditions.

I use undo-tree to make Emacs' infinite-undo feature more usable. I have occasionally been bitten by a bug that loses part of undo history, but have not been able to reproduce it consistently - it may relate to undo in region. I just upgraded to Emacs 25.2.1, which the author says should mean this stops happening. Alas, I have seen it happen since then, as have some people in this thread: syl20bnr/spacemacs#298. Some on the net have proposed that setting undo-tree-enable-undo-in-region to nil might solve the issue, which at least one person in that thread says did not work. Others have suggested it seems to relate to persistent undo history or perhaps to working in version control repos, and certainly it seems to be more common for evil-mode users than others. I have turned off undo in region and undo tree's support for saving undo history to see if that helps (as of 2018-11-05). I just discovered this bug report that has a plausible-sounding analysis of the bug's root cause: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=27214 I saw the bug for the first time in a long while on 2019-11-20.

I am trying out replacing Helm with Ivy to find files and commands quickly and easily. Swiper has accordingly replaced helm-swoop.

I use magit for version control with git. It really is magical and hides a lot of Git's worst UI flaws. I sometimes use its forge plugin for interacting with external Git hosting services like GitHub and GitLab. Note that getting it set up with GitLab is a minor pain - you must create an API token manually and drop it in your ~/.authsource.gpg file per the docs.

I use backup-walker to search through a file's backups when I need an older version I didn't commit. I store all backups in autosaves/ and back up on every save, even in version control repositories. I have not lost a line of code since setting this up.

I have a decent Python setup, with Jedi for auto-complete, jump-to-definition and docstring display. I taught it to auto-detect virtualenvs in a project's directory with jedi-force.el.

I don't write much C these days, but I do use it to customize my ErgoDox EZ's keymap, and I do read C codebases occasionally. Thus, I have c-mode set up to use cquery so I can jump to definitions easily (paired with intercept-build to generate compile_commands.json) .

I use a combination of js2-mode, Tern.js, eslint (via flycheck), js2-refactor and skewer-mode for live-editing browser-based JavaScript. In theory this should be awesome, and it's not bad, but there are some kinks to work out.

My PHP setup has undergone bitrot over the past few years, but I'm starting to pay attention to it again. Felix Becker's excellent php-language-server means good PHP intelligence in Emacs is possible (though there are now several other more-maintained OSS options I want to check out). After some tinkering and a PR to lsp-mode I have jump-to-definition and completion-at-point working. It's a huge step up from my past setup, but it can certainly be much better.

For editing web templates, I use the awesome web-mode with several extensions, including emmet-mode.

I use mbsync to retrieve received emails from servers (and follow this handy guide for using it with corporate SSO 2FA Gmail accounts when needed). I use notmuch and associated Emacs modes for organizing and reading my email. I use notmuch-message-mode for writing email and auth-source-pass for giving emacs my credentials when sending email (for reasons unclear to me, I've had to use the domain.tld:<port>.gpg naming format to get auth-source-pass-search to find my creds - including username@domain.tld has caused it to not find my credentials).

I use css-mode for CSS, which is somewhat lacking, but I use skewer-reload-stylesheets to live-edit, and that makes life better.

I've been doing a decent amount of devops work using Ansible lately, so I've got yaml-mode set up with an Ansible-specific poly-mode and lsp-mode + yaml-language-server. There's definitely some massaging to do, but it makes Ansible development much less painful.

I'm starting an experiment in using chemacs to let me swap between profiles, so I can try out different setups. I'm curious about Doom, and I'd also like a simple way to write bug reproduction scripts starting with the huge collection of packages my heavily-customized setup uses, and I think it might just be the ticket.

Look in todo.txt and tell me about entries that can be resolved by turning on built-in features. I've found a few and would not mind finding more.

Layout

elpa/ - elisp packages installed via package.el. I keep this under version control to make installing my config simpler, so I can always return to a known-good state if an upgrade has unwanted effects, and so my configuration is less dependent on third-party services. This is a good example of why I do this - none of the reporters in that thread would have had a problem if they kept this dir in their config repos.

githooks/ - a few git hooks to aid in hacking on this config, mostly useful when pushing config changes between multiple machines.

init.el - where the magic begins.

site-lisp/ - elisp packages I update manually. Some are not available via package.el while others are my own. There are probably some third-party libraries that I never realized are on package.el, too.

snippets/ - my personal yasnippets, augmenting the base yasnippet collection.

OS X Setup

I used to use the Emacs for OS X build (I now build my own emacs to get support for automatic image resizing via ImageMagick), and had a hard time making it play nicely with command-line tools that use emacs like Cask.

I eventually solved it by copying the shell script at Emacs.app/Contents/MacOS/Emacs to Emacs.app/Contents/MacOS/emacs, since Emacs.app/Contents/MacOS/ was already on my $PATH.

I'm not sure why just putting a symlink on $PATH didn't work for me, but it didn't.

Notes On Debugging Emacs Lisp

debug-on-entry and cancel-debug-on-entry are gold for debugging right in your current Emacs. Figure out what function's breaking and use debug-on-entry to jump into debugging when you run your reproduction recipe. Press '?' after the debugger starts to see what keys do what.

Don't try to debug compiled functions, because you can't see much useful that way. Since I compile everything that means I need to manually eval function definitions before I debug - there must be a better way. Maybe I could advise debug-on-entry so it evals the function before starting debugging, if a compiled version is loaded?