Towards a Vim-like Emacs
- First, some terminology
- Package archives
- Binding keys
- Lisp navigation
- Managing buffers
- Separating your init files
- Daemonizing Emacs
- Tips and tricks
- My config
I’ve previously written about my own process of transitioning from Vim to Emacs. It’s fairly high-level, however, and doesn’t cover the nitty-gritty: all those long, painful hours spent trying to smash Emacs into reasonable keybindings are lost. You get the retrospective, the analysis, but not the same benefit of hindsight I’ve gathered since I switched. Unfortunately, there aren’t many resources out there on how to do this right. People use Emacs. It’s a tool. Most of its users don’t write about it, and the percentage of Emacs users who try to emulate Vim and also write about it is even smaller.
There is Bling, the vim-airline developer, who switched and ultimately convinced me to give Emacs a shot. But his tips just scratch the surface of what’s necessary to replicate the finger-friendliness of a modern Vim workflow.
It’s a well-known and overstated joke that the default Emacs bindings are bad. If you’re reading this post, you probably already agree with me here, but for the uninitiated: key combos are the devil. Any time you are pressing two keys at once, with the same hand, hundreds of times per day, you are setting yourself up for repetitive stress injury. As programmers, we need to take care of our hands or our careers will be over.
This is going to be part-tutorial, part describing my configuration. With a little determination, this can take you from ground-zero to a working Evil configuration and generic development environment.
This post is geared at the determined Vim user who is willing to give Evil a shot and likes having a heavily customized editor. It has a number of tasks that are intended to teach the right attitude and mindset required to keep working with Emacs on your own. These will get you familiar enough with reading documentation that you know where to look when you want to do something. It is also Elisp-focused, with no emphasis on the more
modern customization features of Emacs that ultimately prevent new users from groking its internals.
First, some terminology
When you first fire up Emacs, you need to learn how they reference certain objects and keybindings. This is pretty simple, but can be confusing if you don’t have the initial introduction.
press the. This brings up the Emacs command prompt, which gives you access to any of the functions that are declared interactive - that is, Elisp functions that may be run interactively by the user.
<ALT>key, then press
xwhile still holding that down
- If someone gives you a command sequence like
M-x package-install <RET> evil <RET>, that means to do the
package-install, press enter, then type
eviland press enter. Often the second
<RET>will be omitted and taken as implicit.
- A buffer is a place where text may go. This is distinct from a window which is a visual area on screen which displays a buffer.
- A frame is another Emacs window in your window manager that is attached to the same Emacs process.
That’s it for the basics. Let’s see where we can go from here.
In order to install packages from things besides the default repos, you need to define a variable called
package-archives with the URLs of the package sources in it. You do this in your
init.el file, which is located at
~/.emacs.d/init.el by default.
(setq package-archives '(("melpa" . "http://melpa.milkbox.net/packages/") ("org" . "http://orgmode.org/elpa/") ("gnu" . "http://elpa.gnu.org/packages/")))
MELPA is the most important source for up-to-date Emacs packages. It’s kind of like the Arch Linux of Emacs package archives - it builds directly from upstream all the time, and it works with the builtin Emacs package manager.
Also, below the above code, you’ll need to initialize the package manager.
(require 'package) (package-initialize)
Once you’ve added the above to that file, you can evaluate each of these expressions inside Emacs with
C-x C-e. To evaluate an expression, position the cursor outside and following the expression and hit that keybinding. This runs the command
eval-last-sexp, which evaluates the symbolic expression before your cursor and pretty-prints the output.
You’ll need to do this in order (i.e., evaluate the
setq expression first), or it won’t work.
Once the above is finished, you can install
M-x package-install <RET> evil <RET>. The compilation log that pops up at the bottom of the screen can be killed with
C-x 4 0. That runs the function
kill-buffer-and-window, which you can also call interactively at the
M-x prompt. More on that later.
After that, Evil will be installed into the
~/.emacs.d directory. To enable it, type
M-x evil-mode. Now all your Vim bindings work, as long as you don’t kill Emacs.
You probably want to enable Evil more permanently. Add the following to the bottom of your
(require 'evil) (evil-mode 1)
This will automatically enable
evil-mode every time you start Emacs. It’s important that it goes at the bottom of the file. Evil relies on starting up after the rest of your packages, so that it can detect them and overlay its keybindings appropriately.
If you break your configuration, restart Emacs with:
$ emacs --debug-init
to get debug information on your init files. Don’t feel bad if you have to use Vim to fix them in the meantime. Don’t get worried thinking Evil is not a
first-class citizen in Emacs: this is an editor built to be ripped to pieces by the competent user.
The interesting part of Emacs is its Lisp engine. It’s the scores of reusable functions for automating your interaction with code. Keybindings are just one way of calling these things.
Like Vim, Emacs uses the word
modes to refer to an element of state in its editor. Unlike Vim, this is not referring to modal editing, but rather to the composition of various modules that make up your current editing environment.
A major mode defines your primary interaction with a buffer in Emacs. There can only be one of these active at once, and they are not all necessarily for editing text. For example,
dired is a major mode for editing directories, and
magit is a Git interaction interface. An important major mode to be aware of is
fundamental mode, which is a major mode with no discerning features besides the ability to type text into a buffer.
Minor modes are compositional - there can be more than one of them, and minor modes loaded after others can override the settings of a previous one. Evil is one such example of a minor mode.
Evil uses the term state to refer to what Vim calls a
mode. There are more states than these, but here are a few to get you going:
Each of these can be called interactively with
M-x to enable them. The way Evil works is each of these states is bound to a different keymap, and those keymaps change the meanings of your keys to call different functions when they are pressed. This affords you the ability to remap bindings across different states, where they will only work when
evil-mode is enabled and Evil is also in the right state to activate the keymap.
v, etc. as you would in Vim to switch between Evil states. Then play around with using
M-x to toggle the different states before moving on.
Let’s get started by adding some bindings to Evil. We want switching between windows to be easy, so we can bind the following:
(define-key evil-normal-state-map (kbd "C-h") 'evil-window-left) (define-key evil-normal-state-map (kbd "C-j") 'evil-window-down) (define-key evil-normal-state-map (kbd "C-k") 'evil-window-up) (define-key evil-normal-state-map (kbd "C-l") 'evil-window-right)
<CTRL>-hjkl to window movement commands when Evil is in normal state.
There are a number of interesting things about these expressions. First, position your cursor over the word
define-key and call
M-x describe-function. It will provide the default value of the thing under your cursor, so you can just press
RET again to bring up a buffer with help information. Read the help information it gives you, then move on. The buffer that appears will be in
help-mode, which does not have Evil bindings, but you can use
q to close it and keep working.
The proper term in Emacs for
your cursor would be point. This is used extensively in the help documentation, so it’s worth remembering. The
thing at point that provided the default argument for
describe-function was a symbol.2 A symbol is basically any thing in Emacs Lisp which has a name and can be hashed into a lookup table, where it then becomes an object in Lisp. These objects could be anything: functions, data, or text, for example, but symbols are interesting because we can give them names and refer to them later somehow.
From the official manual:
You can test whether an arbitrary Lisp object is a symbol with
— Function: symbolp object
This function returns
tif object is a symbol,
M-x apropos RET point RET and skim through the list that pops up. If you have Evil working properly, you can use
:q to close that window when you’re done with it.
Readers with keen eyes may have noticed that one of the arguments in the above code has an apostrophe (
') before it. This is because
evil-window-left is a function’s name, and we want to be able to call it later by binding it to a key. Putting an apostrophe before it when passing it as an argument to a function means we’re passing the quoted form of
define-key, not what it evaluates to.
Since our intent is to pass the symbol
define-key, and not the evaluated form of
evil-window-left itself, this is the right thing to do.
All arguments in Elisp expressions, without quoting, will be evaluated before they are passed into a function call. Quoting lets us avoid that and pass their symbols instead.
Maps and keys
The two other arguments to each of those keybindings were unquoted. The keymap,
evil-normal-state-map, goes directly into the function, since the purpose of
define-key is to modify that map. The expression
(kbd "C-h") is actually another separate function call that happens before the outside expression is evaluated. If we use
M-x describe-function to look at the help documentation for
kbd, we see:
kbd is a compiled Lisp function in `subr.el'. (kbd KEYS) Convert KEYS to the internal Emacs key representation. KEYS should be a string constant in the format used for saving keyboard macros (see `edmacro-mode').
We need to use the
kbd function to describe our keybindings, since they contain an extra operator (the
<CTRL> key). While single character bindings can be looked up in keymaps directly, a key sequence like
C-h is not stored in the map in that form.
You can look at the value of
(kbd "C-h") by typing it on a line below those bindings, and then evaluating it with
C-x C-e as before. In the minibuffer, the text area with a blank line at the bottom of the screen,
"^H" is displayed. That tells you that the expression
(define-key evil-normal-state-map (kbd "C-h") 'evil-window-left)
is internally binding
evil-window-left, but don’t doing that yourself—the way it’s shown in the minibuffer is still a displayed form, which is different from the hashed value it eventually becomes.
You can list the buffers that are currently open by typing
M-x list-buffers. Using the bindings we defined earlier, you can switch to the window that is opened and hit
q to close it when you’re done. To switch to one of these buffers, typing
M-x switch-to-buffer will bring up a minibuffer prompt where you can type the name of a buffer to switch to it.
For a better listing, you can use
The default bindings you should care about right now are:
pto go to the next and previous buffers.
d(marking the buffer for deletion), then
xon a buffer will make it go away.
RETwhile a buffer name is at point will take you to that buffer.
IBuffer has all sorts of useful features for browsing, filtering, and editing open buffers. But at least for a Vim user, its keybindings won’t at all be intuitive. Typing
j accidentally will call
jump-to-buffer, which is almost certainly not what you want. If you felt like typing the name of the buffer at a prompt, you probably wouldn’t have opened IBuffer to begin with!
Aside from being unnatural for Vim users, the interface to IBuffer isn’t actually that bad. The keybindings are mostly single-key sequences, unlike more poorly-designed Emacs modes, and the functions allow for quick actions that will save you tons of work.
Stealing IBuffer’s keymap
When I previously wrote about my experience switching to Evil, I mentioned there are a couple of different ways to deal with major mode keymaps being inconsistent with Evil. I’ll be covering the full remapping approach here, since it has a few benefits:
- Your major modes will be more tightly integrated with Evil.
- You’ll have the ability to redefine keys at will, easily and quickly.
- Remapping keys this way offers a chance at better understanding the functions in a major mode, which might just be missed otherwise. For core components of Emacs, like IBuffer and dired, building a more useful keymap can be a rewarding learning experience in itself.
ibuffer open, type
M-x describe-mode to pull up the help info below.
Following that link6 will take you to the source code for IBuffer. Somewhere in there, you’ll find the keymap:
Any one of these major mode keymaps is bound to be really large, so I won’t reproduce it here. The idea is to redefine these into a map within Evil, so IBuffer will respect your Evil keymaps and still have its functionality. The
evil-define-key function we used earlier has the option of taking an unlimited number of arguments. Now, copy lines like the following out of
(define-key map (kbd "m") 'ibuffer-mark-forward) (define-key map (kbd "t") 'ibuffer-toggle-marks) (define-key map (kbd "u") 'ibuffer-unmark-forward) (define-key map (kbd "=") 'ibuffer-diff-with-file) (define-key map (kbd "j") 'ibuffer-jump-to-buffer) (define-key map (kbd "M-g") 'ibuffer-jump-to-buffer) (define-key map (kbd "M-s a C-s") 'ibuffer-do-isearch) (define-key map (kbd "M-s a M-C-s") 'ibuffer-do-isearch-regexp) ;; ...
and drop them into
(require 'evil), like follows:
(evil-define-key 'normal ibuffer-mode-map (kbd "m") 'ibuffer-mark-forward (kbd "t") 'ibuffer-toggle-marks (kbd "u") 'ibuffer-unmark-forward (kbd "=") 'ibuffer-diff-with-file (kbd "j") 'ibuffer-jump-to-buffer (kbd "M-g") 'ibuffer-jump-to-buffer (kbd "M-s a C-s") 'ibuffer-do-isearch (kbd "M-s a M-C-s") 'ibuffer-do-isearch-regexp ;; ... )
I used a Vim macro and visual block selection to do the rewrite into
evil-define-key form for me, and it only took about a minute to perform.
To ensure IBuffer will use Evil’s keybindings, add the line
(evil-set-initial-state 'ibuffer-mode 'normal)
to your init file. You can use this to set Evil’s initial state for any major mode - before you know what to bind in a mode, sometimes using Emacs state isn’t a bad idea.
Now, if you try to actually use this configuration, it won’t work.7 The reason is we need to ensure the Evil bindings are not applied until after IBuffer is loaded, since it needs to do some special setup. The solution here is to surround the bindings in an
eval-after-load block, like follows:
(eval-after-load 'ibuffer '(progn (evil-set-initial-state 'ibuffer-mode 'normal) (evil-define-key 'normal ibuffer-mode-map (kbd "m") 'ibuffer-mark-forward (kbd "t") 'ibuffer-toggle-marks (kbd "u") 'ibuffer-unmark-forward (kbd "=") 'ibuffer-diff-with-file (kbd "j") 'ibuffer-jump-to-buffer (kbd "M-g") 'ibuffer-jump-to-buffer (kbd "M-s a C-s") 'ibuffer-do-isearch (kbd "M-s a M-C-s") 'ibuffer-do-isearch-regexp ;; ... ) ) )
This ensures your custom keymap does not try to set itself until after ibuffer has already initialized it properly.
Your goal after this section is to read the help information for
eval-after-load, as well as for some of the IBuffer bindings. Just pick ones that seem interesting, and again - don’t worry if you don’t understand things. Just read a few of them anyway.
Once you’ve done your reading, get the custom keymap to work by evaluating the block with
C-x C-e, and ensure it’s functional in
evil-normal-state by calling
M-x evil-normal-state manually within IBuffer.
You might think it’s crazy to place all of this configuration in one lone init file. We’ll get to that in the next section. For now, keeping things in
init.el will get the job done and let us focus on keybindings.
The obvious thing to do, now that we’ve copied the keymap, is to add proper
hjkl bindings. Usually in major modes like this you want to bind
Jwasn’t taken, I changed
- The binding for
kis less obvious: it’s tied to
Looking at the help documentation:
ibuffer-do-kill-linesis an interactive autoloaded compiled Lisp function in
Hide all of the currently marked lines.
IBuffer has a marking system that lets you select multiple buffers on which to perform actions. Personally, I don’t care very much for hiding marked lines, so I simply removed that binding. Now we have
(kbd "j") 'evil-next-line (kbd "k") 'evil-previous-line
bound, which covers the absolute basics. But what about
l? I’m used to ranger, a Vim-inspired file manager, where
l takes you
into the current item. In IBuffer,
l is bound to
ibuffer-redisplay, a useless function that redisplays the current buffers without loading new ones.8 Deleting that line lets us rebind it to
(kbd "l") 'ibuffer-visit-buffer
and now we can navigate IBuffer much more naturally.
Another Ranger replication: by default,
ibuffer-toggle-marks is bound to
v is bound to
ibuffer-do-view, which opens the buffer at point fullscreen and hides the others. I never need to do that, so I’ve added
(kbd "v") 'ibuffer-toggle-marks
to my config.
I’ll add that it’s not really necessary to duplicate the entire keymap in your init file. As you’d guess, you only need to explicitly define keys in Evil if:
- Evil would overwrite them otherwise, replacing them with something useless
- You need to change them
If neither of these apply, then you can get rid of the bindings. But pasting them in to start with, then trimming the bindings down to what you’re actually interested in gives you the chance to examine the default keymap and rebind things that sound useful.
Handling lots of buffers
Over time, when you use Emacs you’ll end up with a lot of garbage buffers. Tons of different commands will open scratch buffers to show you things and never kill them. Eventually you’ll need to learn to filter IBuffer’s output so it’s actually relevant to you, and quickly group and delete buffers that aren’t.
/ in the IBuffer window starts a filter command. I’ll list the default filter commands here, but remember that you can rebind them.
Filter by name
Filter by extension
Filter by filename
Make filters into group
Save filter groups
Switch to saved filters
Switch to saved filter group
Separating your init files
As I said before, putting everything in one init file gets old after a while. Emacs has a variable called
load-path that you can use to customize where it looks for files. This is similar to the
$PATH variable in most UNIX-derived shells.
Adding the following to the beginning of your
~/.emacs.d/init.el file will make Elisp files in the directory
~/.emacs.d/config available for loading:
(add-to-list 'load-path (concat user-emacs-directory "config"))
If you use
K in Evil’s normal state to look up the definition of
concat, you’ll see:
(concat &rest SEQUENCES)
Concatenate all the arguments and make the result a string. The result is a string whose elements are the elements of all the arguments.
Since this won’t be the last time you see it, I suggest reading the docs for
add-to-list as well.
You can use
M-x describe-variable to see the definition of
load-path. It will also tell you the variable’s value. Hitting
K with the symbol at point will also take you to this, but it’s good to do it the hard way at least once.
Now you can create a file,
~/.emacs.d/config/my-ibuffer.el, and move your IBuffer configuration over there. It won’t be loadable by default, though, unless you declare it at the bottom of the file:
;; code goes here (provide 'my-ibuffer)
~/.emacs.d/init.el, you’ll need to
require the file, as we did with Evil and Slime, for it to be loaded automatically.
Now your IBuffer settings will be loaded at startup, but they won’t clutter your main init file.
Dired is one of Emacs’ killer apps - a full-featured file manager built in to the editor. No package installation necessary. But the default bindings in Evil get some core things wrong, and not just in respect to my Vim-like preferences.
Dired is very good about using single-key bindings for the most important things. Changing them to be better is easy: you just have to know what functions to bind.
First, opening a subdirectory in Dired spawns a new Dired buffer. The sane action,
dired-find-alternate-file, is locked behind a feature gate that will prompt you the first time you use it. I rebound
dired-find-alternate-file, which means that moving
forward into a new Dired buffer happens in the same buffer (or window) as the current one. This does wonders in not cluttering up my buffer list with dead Dired exploration.
Repeating the steps used to build a new keymap for IBuffer, we can hack apart the Dired maps similarily so.
(evil-define-key 'normal dired-mode-map "h" 'dired-up-directory) (evil-define-key 'normal dired-mode-map "l" 'dired-find-alternate-file) (evil-define-key 'normal dired-mode-map "o" 'dired-sort-toggle-or-edit) (evil-define-key 'normal dired-mode-map "v" 'dired-toggle-marks) (evil-define-key 'normal dired-mode-map "m" 'dired-mark) (evil-define-key 'normal dired-mode-map "u" 'dired-unmark) (evil-define-key 'normal dired-mode-map "U" 'dired-unmark-all-marks) (evil-define-key 'normal dired-mode-map "c" 'dired-create-directory) (evil-define-key 'normal dired-mode-map "n" 'evil-search-next) (evil-define-key 'normal dired-mode-map "N" 'evil-search-previous) (evil-define-key 'normal dired-mode-map "q" 'kill-this-buffer)
You’ll notice the mark toggling is rebound to
v here as well, which gives me a somewhat consistent interface between
ranger, Dired, and IBuffer.
n is another important thing here. By default,
p are used for navigation within Dired, but with
hjkl bindings set that’s not really useful. Changing
N to Evil’s search commands gives us Vim-like searching within the file manager.
Another thing to note about the above code is that I’ve made each binding an explicit expression. This is useful when you’re just testing new keybindings - if you might want to change them and play around until you find something that works, you want to be able to evaluate each expression independently. If we feel like evaluating these all at the same time, we can just surround them in a
(progn) block, or visual select the code and use
M-x eval-region to do so.
One problem with this config is that while
l will use
h will keep the old Dired buffers around. To fix this, we need to write a function that will jump up one directory, and close the old Dired buffer.
(defun my-dired-up-directory () "Take dired up one directory, but behave like dired-find-alternate-file" (interactive) (let ((old (current-buffer))) (dired-up-directory) (kill-buffer old) ))
h to our new function makes buffer navigation behave much closer to other Vim-like file browsers.
(evil-define-key 'normal dired-mode-map "h" 'my-dired-up-directory)
Many incredibly useful Dired features are disabled by default, like
dired-jump, which jumps to a Dired buffer in the same place as the current file.
Adding the following to your init file will enable these
advanced features, making Dired a much nicer environment to work in:
Some of my Dired-related keybindings, like
dired-jump below, require
dired-x loaded to work.
Evil-Leader lets you define a leader key within Evil. I use
, as my Evil leader key, where
\ is reserved as the leader key for Emacs (per Bling’s Emacs as my <Leader> post series). As usual,
M-x package-install RET evil-leader will install this for you.
require Evil-Leader and run
(global-evil-leader-mode) before Evil is loaded, or otherwise it won’t work in buffers like
The first obvious thing to do is to bind
q to save and quit functions, as many people do in Vim:
(evil-leader/set-leader ",") (evil-leader/set-key "w" 'save-buffer) (evil-leader/set-key "q" 'kill-buffer-and-window)
Some other useful Evil-Leader bindings that work with all the packages we’ve seen so far:
(evil-leader/set-key "h" 'dired-jump) (evil-leader/set-key "v" 'split-window-right) (evil-leader/set-key "e" 'pp-eval-last-sexp) (evil-leader/set-key "," 'other-window) (evil-leader/set-key "b" 'ibuffer) (evil-leader/set-key "x" 'helm-M-x)
With these set, we can eliminate two-key combos in most of our daily editing tasks.
As you start collecting Emacs plugins, your startup times will begin to grow. In addition, your ability to deploy on multiple machines (which may not have those plugins installed) and handle dependencies properly will decrease significantly over time.
Use-package lets you avoid that problem by automating the bad parts of configuring a package, and establishing deferred bindings so that you can evaluate code after a given package is loaded, and trigger a package loading on the evaluation of arbitrary commands or opening of arbitrary filetypes.
With it, the top section of my init file looks like this:
(require 'package) (package-initialize) (setq package-enable-at-startup nil) (add-to-list 'load-path (concat user-emacs-directory "config")) (setq package-archives '(("melpa" . "http://melpa.milkbox.net/packages/") ("org" . "http://orgmode.org/elpa/") ("gnu" . "http://elpa.gnu.org/packages/"))) (unless (package-installed-p 'use-package) (package-refresh-contents) (package-install 'use-package)) (require 'use-package)
The effect of this is that it automatically downloads and installs
use-package from MELPA if it isn’t already present. Then you can use
:ensure within a
use-package declaration to do the same for packages you would normally install manually.
(use-package evil-leader :commands (evil-leader-mode) :ensure evil-leader :demand evil-leader :init (global-evil-leader-mode) :config (progn (evil-leader/set-leader ",") ;; bindings from earlier ) )
There’s pretty detailed help in the README on GitHub, but I’ll summarize some of it here:
Automatically fetch and install the package if it is not already installed.
Defer the loading until a given file extension is found.
Define the given commands, but don’t load the package until they are called.
Command to be executed immediatley when the expression is found.
Commands to be executed after the package is loaded.
The colons are necessary because these are optional, named arguments to an Elisp expression. You could use
use-package with nothing but the first argument, although that wouldn’t be very different from using
Also note that you generally want
:config to be wrapped in a
(progn), since they only take one command normally.
Your task for this section is to go get the previous code into Use-Package.
I use the following systemd unit to start an Emacs daemon when my machine boots:
[Unit] Description=Emacs daemon [Service] Type=forking WorkingDirectory=%h ExecStart=/usr/bin/emacs --daemon Restart=no [Install] WantedBy=console.target
Then you can make a simple script like
#!/bin/sh /usr/bin/emacsclient -c $@
And use that to spawn Emacs. It will just attach itself to the server process, allowing for instant startup.
Non-systemd users can still use
emacs --daemon in an init script somewhere for the same effect.
I’ve talked about Magit before. It’s the only Git interface I’ve ever liked for doing actual work, and I found it 3 years after I started using Git.
Giving it proper
jk bindings is simple:
(use-package magit :ensure magit :config (progn (evil-set-initial-state 'magit-mode 'normal) (evil-set-initial-state 'magit-status-mode 'normal) (evil-set-initial-state 'magit-diff-mode 'normal) (evil-set-initial-state 'magit-log-mode 'normal) (evil-define-key 'normal magit-mode-map "j" 'magit-goto-next-section "k" 'magit-goto-previous-section) (evil-define-key 'normal magit-log-mode-map "j" 'magit-goto-next-section "k" 'magit-goto-previous-section) (evil-define-key 'normal magit-diff-mode-map "j" 'magit-goto-next-section "k" 'magit-goto-previous-section)))
Tips and tricks
Avoid using customize
Easy customization is a
feature where Emacs autogenerates lisp code for you, then places that in a specific file with your graphical customizations.
Since this breaks customizing Emacs programmatically, I recommend avoiding it in most cases. It’s better to use Customize to browse for useful settings and copy the generated code into your real init files, that way you’re free to modify it.
Ditch the terminal
It’s a little-known fact among hardcore tmux users that window managers serve a useful purpose besides displaying a web browser. That was how I felt, at least. I used Vim inside the shell, since the graphical version was annoying: it had a different font, clunky menus, and I couldn’t use tmux’s copy-paste functions to move text around.
If you’re moving to Emacs full-time, be prepared to change that mindset. Emacs is better to use in a graphical window. It will actually play nicely with your graphical environment.
M-x new-frame opens a new window (in your window manager, not Emacs) that is attached to the same Emacs process. This behavior gives you the best of both worlds: you can use Emacs and split it up however you’d like to, but you can take advantage of the fancy features of your window manager.
Just trust me. Give it a shot. I promise it’s better.
Hide the startup messages
(setq inhibit-splash-screen t inhibit-startup-echo-area-message t inhibit-startup-message t)
Useful when managing package dependencies and controlling load order. Taken from milkbox:
(defmacro after (feature &rest body) "After FEATURE is loaded, evaluate BODY." (declare (indent defun)) `(eval-after-load ,feature '(progn ,@body)))
Better word wrapping
Emacs has a long history of being terrible at word wrapping. The situation has improved lately, however, and visual line mode gives you sane word wrapping in almost all cases.
Enforce trailing newlines
Some software (e.g., newsbeuter) breaks if you don’t provide a trailing newline.
(setq require-final-newline t)
Hide the toolbar
The toolbar is useful at first, but once you’re familiar with the major mode you’re working in, you might want to disable it to preserve vertical space.
My config files are on GitHub if you want to explore where I’ve gone with this. Caution that they don’t follow this post exactly, and they may not work on everyone’s system or be broken due to versioning (I build Emacs from the latest upstream source every couple weeks).
evil-emacs-stateuses Emacs keybindings exclusively.↩
Note that not everything in a given buffer is a symbol, even though everything in Elisp is an object. Since you were looking up a function definition, it was a symbol, but symbol refers exclusively to those objects that have names.↩
Often you’ll see people defining hooks using a
lambdaconstruct. This is counterproductive, since once you add a hook to a list it can be complicated to get it out. If you messed anything up with your lambda, you can have a hard time removing it. Giving all of your hooks explicit names lets you remove them easily with
remove-hookwithout restarting your Emacs session.↩
As of Emacs 24.4,
turn-on-eldoc-modeis a deprecated alias for
eldoc-mode, which should be used instead.↩
One reader wrote to me, finding that the code I provide here did not work for him.
I can see the elisp-slime description appearing at the bottom section, but when I press K it seems like it’s running another function. I’ve also tried mapping it to
T, but I’m not able to get it working (but by running elisp-slime-nav-describe-elisp-thing-at-point from the M-x, (or : when in evil-normal-mode) it works perfectly)
When he changed it to the following, it worked:
(evil-define-key 'normal emacs-lisp-mode-map "K" 'elisp-slime-nav-describe-elisp-thing-at-point)
So readers who have issue with my code might want to try binding to
A friend of mine pointed out that his version of Emacs (24.4, I believe) did not incldue a jump-to-source link in his
describe-modehelp window. While this was reported as a bug back in 2011, I was fairly certain that it has since been resolved …
I tried this out and, indeed, the same was true for me: either the feature had been removed, or our Emacs’ packages were built incorrectly (e.g., without the source files). I think it’s possible to fix this by modifying the package source for whatever distro one is using to not strip the
.el.gzfiles, but trying this on my own system has thus far not solved the issue.↩
I got errors about the
R E Tnot being a valid prefix key when I was testing this. YMMV.↩
ibuffer does not refresh itself by default. You need to call
ibuffer-updatefor new buffers to be shown.↩