Integrated development environments (IDEs) abstract too much away from the developer for my liking so I work with a text editor. I used Vim for a long time but about a year ago I started casting envious eyes at spacemacs and gave it a try. But updates sometimes changed keybindings I'd only just become comfortable with so I felt I was giving too much control of my configuration to others. Here's how I've started putting together my own Emacs configuration for a spacemacs-like experience to my taste.
In this post we'll look at:
- managing a simple configuration;
- installing packages in a repeatable way; and
- using spacemacs-style keybindings that you control.
Starting your configuration
You should keep anything that's important to you under revision control. My editor configuration definitely comes under that category. I follow the convention of creating a revision controlled
dotfiles subdirectory of my
home directory, then symlinking to files in that from the locations in which your tools expect to find their configurations. Since we're talking Emacs, we'll use that as the example. This post assumes you're using a Linux-like file system because that's what I'm using, so you may need to adapt it for your own operating system.
Emacs Lisp is a complete programming language so there's no end to the ways you can configure Emacs. But I like to keep things as simple as possible so I keep everything in one file,
Create a file in
~/dotfiles/init.el and save it blank for now. The create a symlink to your new file with
ln -s ~/.emacs.d/init.el ~/dotfiles/init.el. Emacs will now run through your new configuration file each time it starts.
Most people don't want installing new packages to be a pain. We'll use use-package to take that pain away. Enter the following in your
;; Bootstrap `use-package' (require 'package) (setq package-enable-at-startup nil) (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/")) (package-initialize) (unless (package-installed-p 'use-package) (package-refresh-contents) (package-install 'use-package))
Now when you add new packages to your
use-package wil install them from MELPA. You can add other package sources by adding to the
'package-archives list, but I prefer to stick to things on MELPA.
Setting some nice defaults
Next I like to make myself comfortable by setting some defaults to my tastes. Adding the following few lines lets me:
- start Emacs on the scratch buffer instead of the startup message;
- insert four spaces instead of
\twhen I use the tab key;
- start up with the frame maximized and toolbars removed (I have a small screen); and
save my command history between sessions.
;; Emacs configuration
(setq inhibit-startup-message t) (setq-default indent-tabs-mode nil) (setq-default tab-width 4) (setq indent-line-function 'insert-tab) (add-to-list 'default-frame-alist '(fullscreen . maximized))
(tool-bar-mode -1) (savehist-mode 1)
Some recommended packages
Emacs has some really nice look-and-feel customizations, which also often have a good deal of utility. Vim users needn't miss out on powerline, which has been ported. By using
flycheck-color-mode-line you can also get a visual cue in the modeline when there's errors or warnings in the current buffer. There are also plenty of themes on MELPA. I like a muted, light theme so I use Leuven.
;; Appearance (use-package powerline :ensure t :config (powerline-center-evil-theme) (use-package flycheck-color-mode-line :ensure t :config (add-hook 'flycheck-mode-hook 'flycheck-color-mode-line-mode)) ) (use-package leuven-theme :ensure t)
If, like me, you're coming to Emacs from a Spacemacs or Vim background there's a few things you'll need to get your keybindings how you like them. If you're happy to use Emacs keybindings you can skip this section.
The first thing you need is evil mode. Evil is the Vim emulation layer and it houses all of your Vim-like configuration.
After using Spacemacs I got used to using a leader key. The evil-leader package, which I'll demonstrate how to configure shortly, provides this. I've also always remapped the Escape key to something closer to my fingers. For that you need the evil-escape package.
All these sub-packages rely on Evil so I nest their configurations inside the overall Evil one like so:
;; Evil mode (use-package evil :ensure t :config (evil-mode 1) (use-package evil-escape :ensure t :config (evil-escape-mode 1) (setq-default evil-escape-key-sequence "fd") (setq-default evil-escape-delay 0.2)) (use-package evil-leader :ensure t :config (global-evil-leader-mode) (evil-leader/set-leader "<SPC>") (evil-leader/set-key "e" 'find-file "bb" 'switch-to-buffer "bd" 'kill-buffer-and-window "by" 'copy-whole-buffer "cy" 'clipboard-kill-ring-save "cp" 'clipboard-yank "fs" 'save-buffer "gs" 'magit-status "hs" 'split-window-horizontally "iu" 'insert-char "lf" 'load-file "ne" 'flycheck-next-error "pe" 'flycheck-previous-error "rm" 'notmuch "sm" 'message-send-and-exit "si" 'whitespace-mode "tn" 'linum-mode "w1" 'delete-other-windows "wk" 'windmove-left "wj" 'windmove-right "qq" 'save-buffers-kill-emacs "zp" 'zeal-at-point ) ) (use-package evil-surround :ensure t :config (global-evil-surround-mode)) (use-package evil-indent-textobject :ensure t))
This sets the space bar as my leader key then maps a series of key combinations to Emacs functions. You may notice I'm being lazy my defining all my key combinations globally. I'm comfortable with this but, if you'd prefer to make them specific to certain major modes, you can do that using
evil-leader/set-key-for-mode instead of
evil-leader/set-key. Remember to pass an additional argument of the mode you want something to work with like so:
(evil-leader/set-key-for-mode 'python-mode "ne" 'flycheck-next-error )
Setting up programming environments to suit my needs is very simple. The lines below gave me all the features I needed to be happy writing Haskell and Python code.
;; Programming and writing environments (use-package elpy :ensure t :config (elpy-enable) (setq elpy-rpc-python-command "python3") (setq elpy-rpc-backend "jedi") (elpy-use-cpython "/usr/bin/python3") (setq python-check-command "~/.local/bin/pyflakes") (add-hook 'python-mode-hook (lambda () (show-paren-mode 1)))) (use-package haskell-mode :ensure t) (use-package intero :ensure t :config (add-hook 'haskell-mode-hook 'intero-mode)) (use-package markdown-mode :ensure t) (use-package yaml-mode :ensure t)
I use Zeal for quick and reliable access to documentation. I round off my Emacs setup by giving myself quick access to it from any file I'm working on.
;; Zeal setup (use-package zeal-at-point :ensure t) (add-to-list 'zeal-at-point-mode-alist '(haskell-mode . "haskell")) (add-to-list 'zeal-at-point-mode-alist '(python-mode . "python")) (provide 'init) ;;; init.el ends here
I've made some ommissions here where explaining setup would take me too far away from the purpose of showing how to build up a simple Emacs configuration. You can find my complete configuration on github.