;;; ------------------------------------------
;;; Do not edit this file. It was tangled from
;;; an org file.
;;; ------------------------------------------
I’ve declared Emacs config bankruptcy for the new year, and it couldn’t have
come at a better time. All at once there are a bunch of new tools for
Emacs configuration: use-package
(and req-package
, used here) and
bind-key
simplify a lot of the annoying bits. Dependency management was hard
before Emacs 24, but with these new tools we can vastly simplify the process.
We even get a simpler keybinding mechanism in the bargain.
Pallet is a wonderful little tool built on Cask, a dependency management tool
for Emacs packages. Pallet adds automatic updating of the Caskfile
when
packages are installed and deleted.
Just run this command in your terminal of choice:
$ curl -fsSkL https://raw.github.com/cask/cask/master/go | python
then add ~/.cask/bin
to your PATH
so that you can use cask
.
For now, we just need a minimal Cask
to get Pallet set up. Mine looks
like this:
(source gnu)
(source melpa)
(depends-on "pallet")
(depends-on "req-package")
(depends-on "moe-theme")
(depends-on "evil")
Then run the following command in your .emacs.d
directory to set up Pallet:
cask install
Finally, we add the following lines to our init file:
(require 'cask "~/.cask/cask.el")
(cask-initialize)
req-package is a wrapper on top of use-package, a package dependency
management tool. The documentation for use-package
is immensely helpful for
figuring out how to describe package dependencies and settings. req-package
adds the :require
keyword which allows us to define dependencies between
related packages.
With the preceding process complete, we just need to add the following line
to our init file to begin using req-package
:
(require 'req-package)
(setq custom-file "~/.emacs.d/custom.el")
(load custom-file)
There are a number of ways to bind keys in Emacs, but I find
bind-key
, bundled with use-package
, easier to work with and,
more importantly, easier to read. bind-key
takes a key sequence, a
command, and an optional keymap. bind-key*
overrides any minor
mode which sets the keybinding. unbind-key
takes a key sequence
and a keymap and removes that binding. Invoking
describe-personal-keybindings
prints a summary of your keybindings
through bind-key
and any overrides or conflicts. This is really
the killer convenience of using bind-key
.
I was a diehard Vim user for a long time, and I still prefer the Vim way of interacting with text. Evil is a (mostly feature-complete) Vim emulation mode for Emacs. It has sane defaults and I rarely find weirdness or lacking features.
Here we have our first package with dependencies. Historically, I’ve had
little annoyances when evil
loads before evil-leader
, so we’ll make sure
it gets loaded first. By default, evil
uses its own cursor which shows up
black regardless of face settings. Setting evil-default-cursor
lets us use
the cursor we’re used to. I also redefine a couple of ex
commands for
convenience.
(req-package evil
:require (helm-config undo-tree surround ace-jump-mode)
:ensure evil
:init
(progn
(setq evil-default-cursor t)
(evil-mode 1)
(setq evil-motion-state-modes
(append evil-emacs-state-modes evil-motion-state-modes))
(setq evil-emacs-state-modes '(magit-mode dired-mode)))
:config
(progn
(evil-ex-define-cmd "e[dit]" 'helm-find-files)
(evil-ex-define-cmd "b[uffer]" 'helm-buffers-list)
(bind-key "[escape]" 'keyboard-escape-quit evil-normal-state-map)
(bind-key "[escape]" 'keyboard-escape-quit evil-visual-state-map)
(bind-key "<escape>" 'keyboard-escape-quit)
(bind-key "\"" 'ace-jump-mode evil-normal-state-map)
(evil-define-key 'normal
tern-mode-keymap "gd" 'tern-find-definition)))
<leader>
is a really useful shorthand in Vim, and evil-leader brings the
same facility to Evil. For really common commands, leader bindings can save
those precious keystrokes.
(req-package evil-leader
:require evil
:ensure evil-leader
:init
(progn
(evil-leader/set-leader "<SPC>")
(global-evil-leader-mode 1)
(evil-leader/set-key
"l" 'org-insert-link
"o" 'other-window
"d" 'delete-window
"D" 'delete-other-windows
"h" 'split-window-below
"v" 'split-window-right
"k" 'kill-buffer
"K" 'kill-buffer-and-window
"f" 'dired
"gs" 'magit-status)))
One of the little Vim conveniences not found in vanilla Evil is incrementing
and decrementing numbers with C-a
and C-x
, respectively, likely because
these are already important bindings in Emacs. However, by limiting the
effect to normal mode, we can use evil-numbers to bring this functionality
back without stomping all over existing bindings.
(req-package evil-numbers
:require evil
:config
(progn
(bind-key "C-a" 'evil-numbers/inc-at-pt evil-normal-state-map)
(bind-key "C-x" 'evil-numbers/dec-at-pt evil-normal-state-map)))
Working with delimiter pairs is common enough that Tim Pope wrote a plugin
to ease working with them in Vim, called vim-surround. evil-surround
emulates this functionality in evil
. It’s quite extensible, but the
defaults seem to cover all my needs. Check out the README for some examples.
(req-package surround
:init (global-surround-mode 1))
Standard Emacs undo is kind of confusing. undo-tree replaces this with a simpler tree structure. It also allows us to visualize the tree directly.
(req-package undo-tree
:diminish ""
:init
(progn
(setq undo-tree-auto-save-history t)
(global-undo-tree-mode)))
(req-package ace-jump-mode)
Coming from Vim, I was very used to the fuzzy matching of CtrlP. Ido is a
popular choice when one wishes to add this functionality to Emacs since it is
built in and there is a fairly significant ecosystem built around it. I used
Ido for a while, but after experimenting with Helm, I’ve decided to migrate.
Think of Helm as CtrlP for every minibuffer. From describing functions and
variables to interacting with org
, Helm covers just about anything.
Helm offers a command called helm-mini
that opens a helm
buffer populated
with recent files and currently open buffers. I want Helm everywhere, so
instead we’ll activate helm-mode
and work from there.
(req-package helm-config
:require popwin
:diminish (helm-mode . "")
:init
(progn
(setq helm-ff-auto-update-initial-value)
(setq popwin:special-display-config
(append helm-popwin
popwin:special-display-config))
(bind-key* "M-x" 'helm-M-x)
(bind-key* "C-x C-f" 'helm-find-files))
:config
(helm-mode 1))
The one annoying thing about helm
is that the window it opens to show
results is kinda huge. I use popwin to limit the height of most of the
Helm buffers.
(req-package popwin
:init
(progn
(popwin-mode 1)
(setq helm-popwin
'(("*Helm Find Files*" :height 10)
("^\*helm.+\*$" :regexp t :height 10)))))
(req-package org
:config
(progn
(add-hook 'org-mode-hook
'(lambda ()
(setq mode-name " ꙮ ")))
(bind-key* "C-c c" 'org-capture)
(bind-key* "C-c l" 'org-store-link)
(bind-key* "C-c a" 'org-agenda)
(bind-key* "C-c b" 'org-iswitch)))
I’m pretty picky about how I want my editor to look, so there’s a fair bit of configuration that goes here.
I’ve switched entirely to dark themes to make working with Structured Haskell Mode easier, and I like the colors of moe-theme. It’s bright and has good default faces for most modes. It also has dark and light versions, which is convenient.
I also advise load-theme
to fully unload the previous theme
before loading a new one.
(defadvice load-theme
(before theme-dont-propagate activate)
(mapc #'disable-theme custom-enabled-themes))
(req-package moe-theme)
(req-package moe-theme-switcher
:require moe-theme)
Powerline is very popular in Vim (and with Evil users), but I much prefer smart-mode-line. It’s compatible with just about anything you can imagine, and it’s easy to set up.
(req-package smart-mode-line
:require nyan-mode
:init (sml/setup))
nyan-mode is a goofy way to display one’s location in a file.
(req-package nyan-mode
:init
(progn
(nyan-mode)
(setq nyan-wavy-trail t))
:config (nyan-start-animation))
(req-package powerline)
(req-package faces
:config
(progn
(set-face-attribute 'default nil :family "DejaVu Sans Mono")
(set-face-attribute 'default nil :height 120)))
Who wants all that toolbars and scrollbars noise?
(req-package scroll-bar
:config
(scroll-bar-mode -1))
(req-package tool-bar
:config
(tool-bar-mode -1))
(req-package menu-bar
:config
(menu-bar-mode -1))
I also use diminish to clean up the modeline.
(req-package diminish)
(req-package server
:diminish (server-buffer-clients . ""))
A few conveniences that I like to have in all my prog-mode
buffers.
Flycheck has helped me write more programs than I’m totally comfortable admitting.
(req-package flycheck
:diminish (global-flycheck-mode . " ✓ ")
:config
(add-hook 'after-init-hook 'global-flycheck-mode))
(req-package helm-flycheck
:require flycheck
:commands helm-flycheck
:config
(bind-key "C-c ! h"
'helm-flycheck
flycheck-mode-map))
The only git wrapper that matters.
(req-package magit
:diminish magit-auto-revert-mode)
(req-package linum
:config
(add-hook 'prog-mode-hook
'(lambda () (linum-mode 1))))
I was a little spoiled by this feature in Vim, and not having it just doesn’t sit well with me.
(req-package linum-relative
:init (setq linum-relative-current-symbol ""))
I like my delimiters matched and visually distinct. I used paredit for a long time, but I’m currently experimenting with smartparens. As for the visual element, I quite like rainbow-delimiters.
(req-package smartparens-config
:ensure smartparens
:diminish (smartparens-mode . "()")
:init (smartparens-global-mode t))
(req-package rainbow-delimiters
:config
(add-hook 'prog-mode-hook 'rainbow-delimiters-mode))
I’ve had to work with colors in a fair bit of code, so having them displayed in buffer is convenient.
(req-package rainbow-mode
:diminish (rainbow-mode . "")
:config (add-hook 'prog-mode-hook 'rainbow-mode))
There’s also an interesting mode for uniquely coloring identifiers in code so that they are easy to scan for. It’s still a bit iffy, but it’s fun to try.
(req-package color-identifiers-mode
:diminish (color-identifiers-mode . "")
:init
(setq color-identifiers:num-colors 50)
:config
(progn
(add-hook 'emacs-lisp-mode-hook 'color-identifiers-mode)
(add-hook 'ruby-mode-hook 'color-identifiers-mode)))
(req-package auto-complete-config
:ensure auto-complete
:init
(progn
(ac-config-default)
(setq ac-auto-start 3))
:config
(progn
(require 'ac-math)
(require 'auto-complete-auctex)))
(req-package ggtags)
Except really I’m =ag=ging.
(req-package helm-ag
:require evil-leader)
(req-package haskell-mode
:require (flycheck flycheck-haskell)
:commands haskell-mode
:init
(add-to-list 'auto-mode-alist '("\\.l?hs$" . haskell-mode))
:config
(progn
(req-package inf-haskell)
(req-package hs-lint)
(bind-key "C-x C-d" nil haskell-mode-map)
(bind-key "C-c C-z" 'haskell-interactive-switch haskell-mode-map)
(bind-key "C-c C-l" 'haskell-process-load-file haskell-mode-map)
(bind-key "C-c C-b" 'haskell-interactive-switch haskell-mode-map)
(bind-key "C-c C-t" 'haskell-process-do-type haskell-mode-map)
(bind-key "C-c C-i" 'haskell-process-do-info haskell-mode-map)
(bind-key "C-c M-." nil haskell-mode-map)
(bind-key "C-c C-d" nil haskell-mode-map)
(defun my-haskell-hook ()
(setq mode-name " λ ")
(turn-on-haskell-doc)
(diminish 'haskell-doc-mode "")
(capitalized-words-mode)
(diminish 'capitalized-words-mode "")
(turn-on-eldoc-mode)
(diminish 'eldoc-mode "")
(turn-on-haskell-decl-scan)
(setq evil-auto-indent nil))
(setq haskell-font-lock-symbols 'unicode)
(setq haskell-literate-default 'tex)
(setq haskell-stylish-on-save t)
(setq haskell-tags-on-save t)
(add-hook 'haskell-mode-hook 'my-haskell-hook)))
(req-package flycheck-haskell
:config (add-hook 'flycheck-mode-hook #'flycheck-haskell-setup))
(req-package shm
:require haskell-mode
:commands structured-haskell-mode
:init (add-hook 'haskell-mode-hook
'structured-haskell-mode))
(req-package ghc
:init (add-hook 'haskell-mode-hook (lambda () (ghc-init))))
(req-package lisp-mode
:init
(add-hook 'emacs-lisp-mode-hook
(lambda ()
(setq mode-name " ξ "))))
All you need is AUCTeX, end of story.
(req-package tex-site
:require auto-complete-config
:ensure auctex)
(req-package ac-math
:require auto-complete-config)
(req-package auto-complete-auctex
:require auto-complete-config)
(req-package ess-site
:ensure ess)
(req-package idris-mode)
drupal-mode has php-mode as a dependency, so we could conceivably
get away with just including the former here, but just in case we
want a bit more control or we decide that drupal-mode
isn’t worth
it, we’ll make separate req-package
blocks.
(req-package php-mode
:init (setq php-template-compatibility nil))
(req-package web-mode)
(req-package drupal-mode
:require (php-mode ggtags))
(req-package tern
:require tern-auto-complete
:init
(progn
(add-hook 'js-mode-hook
(lambda ()
(tern-mode t))))
:config
(progn
(tern-ac-setup)))
(req-package tern-auto-complete)
Fixing a couple of gripes I have with Emacs.
(req-package exec-path-from-shell
:init
(when (memq window-system '(mac ns))
(exec-path-from-shell-initialize)))
These things end up everywhere, so let’s stick them all in a temporary directory.
(req-package files
:init
(progn
(setq backup-directory-alist
`((".*" . ,temporary-file-directory)))
(setq auto-save-file-name-transforms
`((".*" ,temporary-file-directory t)))))
Keep it short.
(defalias 'yes-or-no-p 'y-or-n-p)
cus-edit+ is a really handy way to keep your customizations up to
date, especially if you set your custom-file
.
(req-package cus-edit+
:init (customize-toggle-outside-change-updates))
At long last we need only call the following function to send req-package
on
its merry way.
(req-package-finish)