Skip to content

bradwright/emacs.d

Repository files navigation

Bradley Wright’s Emacs Configuration

Preamble

As my editor of choice I run Emacs. One of the benefits of Emacs is that it’ll install basically anywhere.

This configuration is designed to run in shells as well as on desktops. There are some platform specific optimisations as well.

What is this?

It’s built using Org mode’s tangling functionality.

My entire Emacs configuration is thus written in a literate programming style, and is contained entirely in this file.

Old version

The previous version can be found at bradleywright/emacs-d. This version has been refactored to use traditional Emacs loading techniques and macros for installing packages.

Installing

Just check it out straight to the right directory, and use Make to install it:

cd ~/Projects/emacs.d && make

Platform specific configuration

OS X desktop (Emacs.app)

  • Left option key is remapped to M-
  • M-3 prints a literal # (UK Mac keyboards being weird)
  • We use the Solarized theme
  • Font is Inconsolata

OS X CLI in iTerm2

  • When the Solarized Dark iTerm2 theme is installed, we use the solarized-dark color theme
  • Cut/paste are made to write/read from the clipboard (via pbcopy and pbpaste)
  • Mouse highlighting works via xTerm capabilities
Required iTerm 2 Configuration
  • Re-map left option key to +Esc
  • Untick Smart cursor color

Included libraries

The following libraries are included in non-attributable ways, i.e not via package install or via a Git submodule:

License and copyright

Copyright 2010 Bradley Wright.

Files are licensed under the same license as Emacs (GPL) unless otherwise specified. See the COPYING file for more information.

Any external/third party works included in this work are licensed under their own licenses - refer to the submodules or packages for more information.

Conventions

Functions and variables defined exclusively for my use are prefixed with my initials and a slash bw/ to namespace them.

Setup

Emacs looks in load-path for Emacs lisp files. require and other loading constructs use this when looking for implicit names.

First we define a convenience function bw/add-to-load-path that adds the passed in directory to load-path:

(defun bw/add-to-load-path (dir)
  "Adds `dir' to load-path"
  (add-to-list 'load-path dir))

and a convenience function for making a proper path out of two strings:

(defun bw/join-dirs (prefix suffix)
  "Joins `prefix' and `suffix' into a directory"
  (file-name-as-directory (concat prefix suffix)))

Base load path

Define a base directory bw/dotfiles-dir that’s relative to the currently loading file (this file). This means if I deliberately start Emacs with a file loaded:

emacs -q -l ~/src/emacs/init.el

then bw/dotfiles-dir will be ~/src/emacs.

(defconst bw/dotfiles-dir
  (file-name-directory
   (or (buffer-file-name) load-file-name))
  "Base path for customised Emacs configuration")

This variable is important because all other directories I load things from are relative to it, which means my Emacs config doesn’t need to live in user-emacs-directory.

Temporary directory

Emacs has many packages which need to store state in files. Generally these are in ~ or user-emacs-directory - since my entire ~/.emacs.d is versioned, I’d rather all temporary files were stored in a known place, bw/tmp-local-dir. This directory is created if it doesn’t exist.

(make-directory
 (setq bw/tmp-local-dir
       (bw/join-dirs bw/dotfiles-dir ".tmp")) t)

Backups

Emacs automatically backs up files while you’re editing them. The default configuration isn’t great though.

First, set up some directories to keep backups:

(make-directory
 (setq bw/tmp-backups-dir
       (bw/join-dirs bw/tmp-local-dir "backups")) t)
(make-directory
 (setq bw/tmp-autosaves-dir
       (bw/join-dirs bw/tmp-local-dir "autosaves")) t)

Now use those directories for backups and autosave files:

(setq backup-directory-alist `((".*" . ,bw/tmp-backups-dir))
      auto-save-file-name-transforms `((".*" ,bw/tmp-autosaves-dir)))

Always copy files when backing up to avoid breaking symlinks:

(setq backup-by-copying t)

Delete old versions automatically, and keep a limited number around:

(setq delete-old-versions t
      kept-new-versions 2
      kept-old-versions 2)

Finally, use version numbers in the filenames:

(setq version-control t)

Client/server

Emacs has a client/server model for editing. The client is invoked via the emacsclient command. More information on configuration is available on the EmacsWiki EmacsClient page.

We make sure the server is running, additionally guarded to check if the version of Emacs we’re using supports the server package:

(when (require 'server nil t)
  (unless (server-running-p)
    (server-start)))

Editing defaults

Emacs comes with a collection of strange defaults. See Magnar Sveen’s sane-defaults.el file for some commentary.

Line widths and wrapping

The default wrap width (known as filling) for Emacs is 70 characters. Modern conventions state that 80 characters is the standard:

(setq-default fill-column 80)

I don’t type double-space sentences, so make sure that Emacs doesn’t look for double-spaces after periods to fill paragraphs correctly:

(setq-default sentence-end-double-space nil)

Trailing whitespace

Most UNIX tools work best when there’s a trailing newline on all files. Enable that option:

(setq require-final-newline t)

I don’t want to leave trailing whitespace in files I touch, so set up a hook that automatically deletes trailing whitespace after every line when saving a file:

(add-hook 'write-file-hooks 'delete-trailing-whitespace)

Emacs has lots of other options for managing superfluous whitespace.

Indentation

I don’t use tabstops in files, and my default tab width is 4 characters.

It’s worth noting that Emacs can override either of those on a per-file/mode basis, so Makefiles, Ruby etc. will still get the correct indentation rules.

(setq-default
 indent-tabs-mode nil
 tab-width 4)

Auto-indentation

Electric indent mode was added in Emacs 24.1, and it enables automatic indentation when typing a newline. More about electric indent mode on Emacs Redux.

First we define convenience toggling functions we can use in a hook (or interactively):

(defun bw/turn-on-electric-indent-mode ()
  "Turns on electric-indent-mode"
  (interactive)
  (electric-indent-mode 1))

(defun bw/turn-off-electric-indent-mode ()
  "Turns off electric-indent-mode"
  (interactive)
  (electric-indent-mode -1))

then we enable it for the generic abstract programming mode prog-mode, introduced in Emacs 24.1 (more about prog-mode on Emacs Redux):

(add-hook 'prog-mode-hook 'bw/turn-on-electric-indent-mode)

Encoding

I want to have UTF-8 by default. Emacs unfortunately has a few settings that govern encoding, so we should set them all at once:

(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(prefer-coding-system 'utf-8)

Smart beginning-of-line

C-a is mapped to beginning-of-line by default, which moves point to position 0 on the current line. The irreal blog suggests a smarter alternative that moves the point to the first non-whitespace character first, and then position 0, with extra presses toggling the position:

(defadvice move-beginning-of-line (around smarter-bol activate)
  ;; Move to requested line if needed.
  (let ((arg (or (ad-get-arg 0) 1)))
    (when (/= arg 1)
      (forward-line (1- arg))))
  ;; Move to indentation on first call, then to actual BOL on second.
  (let ((pos (point)))
    (back-to-indentation)
    (when (= pos (point))
      ad-do-it)))

This functionality uses the Emacs concept of advice, which is a way of modifying existing functions in-place without redefining the entire thing.

Fix minibuffer behaviour

When changing focus to the minibuffer, stop allowing point to move over the prompt. Code taken from ergoemacs.

(setq minibuffer-prompt-properties (add-to-list 'minibuffer-prompt-properties 'minibuffer-avoid-prompt))
(setq minibuffer-prompt-properties (add-to-list 'minibuffer-prompt-properties 'point-entered))

Keyboard

Modifier keys

  • C- means Control in combination with another key, eg C-x means Ctrl + x
  • M- means Meta in combination with another key. This is usually Alt, or on OS X (by default). Esc also serves as Meta if it’s not separately bound. On OS X I want to use left for Meta, and leave right alone:
(when (and (eq system-type 'darwin) (display-graphic-p))
  (setq ns-alternate-modifier 'meta)
  (setq ns-right-alternate-modifier nil))
  • s- means super key. On OS X I want this to be :
(when (and (eq system-type 'darwin) (display-graphic-p))
  (setq ns-command-modifier 'super))
  • H- means hyper key. On OS X I want this to be ctrl to prevent fat fingers:
(when (and (eq system-type 'darwin) (display-graphic-p))
  (setq ns-function-modifier 'ctrl))

Basic remappings

The below are some remappings I got from Steve Yegge’s Effective Emacs article. They’re designed to map some slightly difficult but very common mappings to things that are easier to type.

Invoke M-x without the Alt key

As per Yegge’s Item 2. This unmaps the difficult M-x (usually Alt+x) to C-x m, and then add a fat-finger combination of C-x C-m:

(global-unset-key (kbd "C-x m"))
(global-unset-key (kbd "M-x"))
(global-set-key (kbd "C-x m") 'execute-extended-command)
(global-set-key (kbd "C-x C-m") 'execute-extended-command)

Prefer backward-kill-word over Backspace

As per Yegge’s Item 3. This copies readline’s C-w command to backward-kill-word, remaps the command that used to live there (kill-region), and then enables a fat-finger version of the new kill=region mapping:

(global-set-key (kbd "C-w") 'backward-kill-word)
(global-set-key (kbd "C-x C-k") 'kill-region)
(global-set-key (kbd "C-c C-k") 'kill-region)

Quick window switching

Usually one must type C-x o to switch between windows - make that quicker by also mapping M-o:

(global-set-key (kbd "M-o") 'other-window)

Buffer management

Burying a buffer (removing it from the current window and sending it to the bottom of the stack) is very common for dismissing buffers. Add a mapping for it:

(global-set-key (kbd "C-c y") 'bury-buffer)

Add a key combination to revert the current buffer (re-read the contents from disk):

(global-set-key (kbd "C-c r") 'revert-buffer)

Use ibuffer instead of the feature-lacking list-buffers:

(global-set-key (kbd "C-x C-b") 'ibuffer)

On OS X, make sure M-3 is remapped to hash:

(when (eq system-type 'darwin)
  (fset 'insert-pound "#")
  (define-key global-map "\M-3" #'insert-pound))

Launcher keymap

This trick I got from a blog post on launcher keymaps. I define my launcher combo as C-x C-l, which is normally downcase-region - a command I use so infrequently I didn’t even know there was a key binding for it.

Also use s-/ (Apple + / on OSX).

(define-prefix-command 'bw/launcher-map)
(define-key ctl-x-map (kbd "C-l") 'bw/launcher-map)
(when (eq system-type 'darwin)
  (define-key global-map (kbd "s-/") 'bw/launcher-map))

rather than remembering that it’s bw/launcher-map, just make a function:

(defun bw/add-launcher (key function)
  "Maps FUNCTION to KEY under the `bw/launcher-map' prefix"
  (define-key bw/launcher-map key function))

Interface

Remove chrome

To ensure that all scrollbars, toolbars etc. are turned off, we run this as early as possible.

(dolist (mode '(menu-bar-mode tool-bar-mode scroll-bar-mode))
  (when (fboundp mode) (funcall mode -1)))

Startup buffers

Turn off the startup screen, and always show *scratch*.

;; inhibit startup screen
(setq inhibit-startup-screen t
      ;; Show *scratch* on start
      initial-buffer-choice t)

Font

I use Inconsolata as my default coding font. It’s set to render at 18pt:

(when
  (display-graphic-p)
  (when (find-font (font-spec :name "Inconsolata"))
    (set-frame-font "Inconsolata-18" t t)))

Syntax highlighting

Syntax highlighting in Emacs is called font locking. It’s enabled by font-lock-mode. This turned on by default in modern Emacs systems, but it’s worth keeping around:

(global-font-lock-mode t)

Emacs also supports multiple levels of complexity for highlighting. Setting this value to t forces it to pick the maximum available (also the default):

(setq font-lock-maximum-decoration t)

Line and column numbers

Emacs doesn’t display line numbers by the code by default. For that you want Linum mode.

I want to display the current line number in the mode line, and also the current column number:

(line-number-mode 1)
(column-number-mode 1)

Tooltips

Emacs convention is to show help and other inline documentation in the message area. Show help there instead of using an OS tooltip:

(when (display-graphic-p)
  (tooltip-mode -1))

Dialogue boxes and windows

Just don’t show them. Use native Emacs controls:

(when (display-graphic-p)
  (setq use-dialog-box nil))

Make the window title display the full path of the file I’m currently editing:

(when (display-graphic-p)
  (setq frame-title-format
        '((:eval (if (buffer-file-name)
                     (abbreviate-file-name (buffer-file-name))
                   "%b")))))

Aside: Emacs calls OS windows frames and divisions within frames windows. More information on frame titles.

Cursor

On modern operating systems, a vertical bar is used as a cursor:

(when (display-graphic-p)
  (setq-default cursor-type 'bar))

Make the cursor blink (interestingly in Emacs 24.4+ the cursor automatically stops blinking after a period to conserve CPU).

Make the cursor blink every second:

(when (display-graphic-p)
  (setq blink-cursor-interval 1.0)
  (blink-cursor-mode 1))

Typing

Show the modifier combinations I just typed almost immediately:

(setq echo-keystrokes 0.1)

Don’t make me type yes or no to boolean interface questions:

(defalias 'yes-or-no-p 'y-or-n-p)

Bells

Don’t make a sound when ringing a bell - flash a visual bell instead:

(setq visible-bell t)

Override the ring-bell-function to conditionally ring the bell only when it’s not a valid quit case like hitting esc or C-g. Generally this means the bell will only ring when there’s actually an error raised somehow:

(setq ring-bell-function
      (lambda ()
        "Only rings the bell if it's not a valid quit case, e.g
keyboard-quit"
        (unless (memq this-command
                      '(isearch-abort abort-recursive-edit exit-minibuffer keyboard-quit))
          (ding))))

Buffer naming

By default Emacs resolves conflicting buffer names by appending a number to them. For instance, if I open ~/src/thing/init.el and ~/src/other-thing/init.el they’ll be named init.el and init.el<2> respectively.

We can use Uniquify library to name them thing/init.el and other-thing/init.el, which is much easier to make sense of.

(require 'uniquify)
(setq uniquify-buffer-name-style 'forward)

Themes

(defun bw/toggle-solarized ()
  "Toggles Solarized tone"
  (interactive)
  (when
      (or (custom-theme-enabled-p 'solarized-dark) (custom-theme-enabled-p 'solarized-light))
    (cond ((custom-theme-enabled-p 'solarized-dark) (progn
                                                      (enable-theme 'solarized-light)
                                                      (disable-theme 'solarized-dark)))
          ((custom-theme-enabled-p 'solarized-light) (progn
                                                       (enable-theme 'solarized-dark)
                                                       (disable-theme 'solarized-light))))))

Clipboard management

Make sure that out-of-Emacs clipboard gets into the kill ring:

(setq save-interprogram-paste-before-kill t)

OS X specific configuration

Besides the keyboard configuration above, there are some other specific things I do on OS X. On OS X system-type is the symbol darwin.

Host name mangling

Typically OS X hosts are called things like hostname.localconfig or hostname.local. Make Emacs report that without the extra suffix:

(when (eq system-type 'darwin)
  (setq system-name (car (split-string system-name "\\."))))

Spelling correction

ispell isn’t generally available on OS X. aspell is available via Homebrew, so let’s use that if we can find it:

(when (and (eq system-type 'darwin) (executable-find "aspell"))
    (setq ispell-program-name (executable-find "aspell")))

dired fixes

OS X’s bundled version of ls isn’t the GNU one, so it doesn’t support the --dired flag. Emacs caters for that use case:

(setq dired-use-ls-dired nil)

sRGB display fixes

As of Emacs 24.4, Emacs natively supports proper sRGB values on OS X:

(setq ns-use-srgb-colorspace t)

If you’re not using Emacs 24.4 this variable setting will have no effect. See Homebrew’s Emacs recipe for details of how to get this behaviour in earlier Emacs versions.

Terminal integration

Using this configuration, Emacs runs best in iTerm2.

On the desktop, Emacs integrates with the OS X clipboard, so kill etc. copy to the clipboard, and yank copies from the clipboard.

Obviously this doesn’t work in the terminal, so we need to use the interprogram-(cut|paste)-function variables to copy/paste. Most of this code gotten from this blog comment.

(when (and (not (display-graphic-p)) (eq system-type 'darwin))
  (defun bw/copy-from-osx ()
    "Copies the current clipboard content using the `pbcopy` command"
    (shell-command-to-string "pbpaste"))

  (defun bw/paste-to-osx (text &optional push)
    "Copies the top of the kill ring stack to the OSX clipboard"
    (let ((process-connection-type nil))
      (let ((proc (start-process "pbcopy" "*Messages*" "pbcopy")))
        (process-send-string proc text)
        (process-send-eof proc))))

  (setq interprogram-cut-function 'bw/paste-to-osx)
  (setq interprogram-paste-function 'bw/copy-from-osx))

Fullscreen support

On Emacs 24.4 and above, Lion-style fullscreen display is supported. Bind it to ⌘-<return>.

(when (and (eq system-type 'darwin) (fboundp 'toggle-frame-fullscreen))
  (global-set-key (kbd "s-<return>") 'toggle-frame-fullscreen))

On the Yosemite beta, ns-use-native-fullscreen is nil.

(when (eq system-type 'darwin)
  (setq ns-use-native-fullscreen t))

Maximise window

Like Iterm2:

(when (eq system-type 'darwin)
  (global-set-key (kbd "S-s-<return>") 'delete-other-windows))

Utility functions

Rename modeline

Renames the major-mode lighter in the modeline. Lifted from What the emacs.d.

(defmacro rename-modeline (package-name mode new-name)
  `(eval-after-load ,package-name
     '(defadvice ,mode (after rename-modeline activate)
        (setq mode-name ,new-name))))

Get keychain password

If I’m on OS X, I can fetch passwords etc. from my Keychain. This is much more secure than storing them in configuration on disk:

(defun bw/chomp (str)
  "Chomp leading and tailing whitespace from `str'."
  (while (string-match "\\`\n+\\|^\\s-+\\|\\s-+$\\|\n+\\'" str)
    (setq str (replace-match "" t t str))) str)

(defun bw/get-keychain-password (account-name)
  "Get `account-name' keychain password from OS X Keychain"
  (interactive "sAccount name: ")
  (when (executable-find "security")
    (bw/chomp
     (shell-command-to-string
      (concat
       "security find-generic-password -wa "
       account-name)))))

Conditionally kill Emacs

When I’m in an emacsclient, I probably just want the client to die rather than the entire server. And, when I kill my server, I want Emacs to confirm this with me:

(defun bw/kill-emacs ()
  "If this buffer is a client, just kill it, otherwise confirm
the quit."
  (interactive)
  (if server-buffer-clients
      (server-edit)
    (if (= (length (frame-list)) 1)
        (cond ((y-or-n-p "Quit Emacs? ")
               (save-buffers-kill-terminal)))
      (save-buffers-kill-terminal))))

Enable this, and override the default command Emacs assigns to kill itself:

(define-key (current-global-map) [remap save-buffers-kill-terminal] 'bw/kill-emacs)

Other modes

Emacs comes with hundreds of major and minor modes to do many many things. These are the ones I commonly use and have configured.

First let’s define a convenient macro that wraps typical eval-after-load in such a way that we don’t need to use progn to contain the callback logic. This macro was gotten from Steve Purcell’s emacs.d repo:

(defmacro after-load (feature &rest body)
  "After FEATURE is loaded, evaluate BODY."
  (declare (indent defun))
  `(eval-after-load ,feature
     '(progn ,@body)))

occur

occur-mode is a search minor-mode that shows a buffer with all matching results in a popup buffer.

Use the occur-dwim (do what I mean) function from (or emacs:

(defun occur-dwim ()
  "Call `occur' with a sane default."
  (interactive)
  (push (if (region-active-p)
            (buffer-substring-no-properties
             (region-beginning)
             (region-end))
          (let ((sym (thing-at-point 'symbol)))
            (when (stringp sym)
              (regexp-quote sym))))
        regexp-history)
  (call-interactively 'occur))

Bind it to a launcher.

(bw/add-launcher "o" 'occur-dwim)

org

org-mode is a plain text system for organising information and notes.

Don’t auto-fold my documents:

(setq org-startup-folded nil)

When editing nested source code, always accept Emacs Lisp:

(org-babel-do-load-languages
 'org-babel-load-languages
 '((emacs-lisp . t)))

and automatically apply syntax highlighting:

(setq org-src-fontify-natively t)
(setq org-src-tab-acts-natively t)

When using imenu, make sure I can follow the outline to the full available depth:

(setq org-imenu-depth 6)

ido

ido is a mode for narrowing candidates as you type. It has excellent integration with buffer switching and finding files. Mastering Emacs has a good guide to Ido.

First we enable ido-mode globally and enable ido-everywhere, which enables Ido for buffer and file reading:

(after-load 'ido
  (ido-mode t)
  (ido-everywhere t))

Force Ido to ignore Dropbox cruft:

(after-load 'ido
  (add-to-list 'ido-ignore-files "Icon\n"))

Configure Ido (see comments for more information):

(after-load 'ido
  (setq
   ;; Speed up ido by using less candidates
   ido-max-prospects 10
   ;; Match arbitrary points in strings
   ido-enable-prefix nil
   ;; Match across entire string
   ido-enable-flex-matching t
   ;; Create a new buffer if there's no match candidate
   ido-create-new-buffer 'always
   ;; Don't try and guess if the string under point is a file
   ido-use-filename-at-point nil
   ;; case-insensitive matching
   ido-case-fold t
   ;; don't store old files as virtual buffers
   ido-use-virtual-buffers nil))

Store ido temporary directory cache elsewhere:

(setq ido-save-directory-list-file (expand-file-name ".ido.last" bw/tmp-local-dir))

Finally load Ido:

(require 'ido)

bookmarks

Emacs has robust bookmarking functionality. It uses a file to persit the list of bookmarks, so make sure that file is in my custom temporary directory:

(after-load 'bookmark
  (setq bookmark-default-file (expand-file-name ".emacs.bmk" bw/tmp-local-dir)))

eldoc-mode

eldoc-mode is a minor mode that displays context-sensitive help when editing Emacs lisp (eg information about arity of functions). Enable that for emacs-lisp-mode:

(add-hook 'emacs-lisp-mode-hook 'turn-on-eldoc-mode)
(add-hook 'lisp-interaction-mode-hook 'turn-on-eldoc-mode)

python-mode

As of 24.2, Emacs ships with a robust Python mode. However, when navigating SnakeCase words (eg class names), forward-word etc don’t work correctly.

We can work around that using subword-mode:

(add-hook 'python-mode-hook (lambda () (subword-mode 1)))

Google’s Bazel project uses BUILD files that closely resemble Python:

(add-to-list 'auto-mode-alist '("BUILD\\'" . python-mode))

ruby-mode

As of 24.4, Emacs comes with a much better Ruby mode. However it doesn’t come with subword-mode enabled by default:

(after-load 'ruby-mode
  (add-hook 'ruby-mode-hook (lambda () (subword-mode 1))))

Add Puppetfile files to ruby-mode:

(add-to-list 'auto-mode-alist '("[pP]uppetfile\\'" . ruby-mode))

hippie

Hippie expand is a more feature complete completion engine than the default dabbrev engine. The main feature I use over dabbrev is that it supports a wide range of backends for finding completions - dabbrev only looks at currently open buffers.

First we customise the types of things it looks for:

(setq hippie-expand-try-functions-list
      '(try-expand-dabbrev
        try-expand-dabbrev-all-buffers
        try-expand-dabbrev-from-kill
        try-complete-file-name-partially
        try-complete-file-name
        try-expand-all-abbrevs
        try-expand-list
        try-expand-line
        try-complete-lisp-symbol-partially
        try-complete-lisp-symbol))

Then we override dabbrev-expand’s keybinding to use hippie-expand instead (normally this is M-/):

(define-key (current-global-map) [remap dabbrev-expand] 'hippie-expand)

tramp mode

tramp-mode is a package that provides remote file editing, eg find-file /user@host:file. This allows one to edit files on other servers using your local Emacs (rather than the Vim user’s equivalent of editing the file on the server).

All of the below are wrapped in an after-load construct because tramp-mode isn’t loaded by default on older versions of Emacs.

First we set the default mode to be ssh (it’s normally scp). There are two reasons for this choice:

  • ssh takes a port number as an argument, whereas scp doesn’t
  • It’s apparently faster for smaller files
(after-load 'tramp
  (setq tramp-default-method "ssh"))

We also want to alter the list of allowed proxies (tramp uses a whitelist for patterns that it can remotely access) so I can edit remote files as sudo, eg find-file /sudo:example.com/etc/something-owned-by-root.

I got this code from the Multi-hops section of the tramp manual.

(after-load 'tramp
  (add-to-list 'tramp-default-proxies-alist
               '(nil "\\`root\\'" "/ssh:%h:")))

Also make sure we can edit local files as sudo - this is normally disallowed for security reasons:

(after-load 'tramp
  (add-to-list 'tramp-default-proxies-alist
               '((regexp-quote (system-name)) nil nil)))

More on the last two incantations at emacs-fu’s guide to editing files owned by root.

eshell

eshell is a shell-like command interpreter built with Emacs lisp. It integrates well with Emacs, and can be a convenient way to get a shell without invoking bash or similar (provided you don’t want any interactive commands).

There’s a great guide to mastering eshell on Mastering Emacs.

eshell has a directory where it stores bookmarks and other temporary cruft - move that out of the way:

(setq eshell-directory-name (bw/join-dirs bw/tmp-local-dir "eshell"))

When using the ssh command (or vagrant ssh, which is really the same thing), we’ll want to jump into something that’s an actual terminal emulator like ansi-term (eshell won’t be able to deal with the login on the remote machine):

(after-load 'esh-opt
  (require 'em-term)
  (add-to-list 'eshell-visual-commands "ssh")
  (when (fboundp 'eshell-visual-subcommands)
    (add-to-list 'eshell-visual-subcommands '("vagrant" "ssh"))))

Define a keybinding to get an eshell buffer anywhere:

(global-set-key (kbd "C-c C-t e") 'eshell)

ansi-term

ansi-term is a terminal emulator written in Emacs Lisp. It’s more like a traditional terminal emulator than eshell.

Force ansi-term to be UTF-8 after it launches:

(defadvice ansi-term (after bw/advise-ansi-term-coding-system activate)
  (set-buffer-process-coding-system 'utf-8-unix 'utf-8-unix))

When exiting a terminal buffer (either with exit or EOF), automatically kill the buffer:

(defadvice term-sentinel (around bw/advice-term-sentinel (proc msg) activate)
  (if (memq (process-status proc) '(signal exit))
      (let ((buffer (process-buffer proc)))
        ad-do-it
        (kill-buffer buffer))
    ad-do-it))

Always pick my default shell, rather than prompting me (from: @echosa’s improving ansi-term):

(defadvice ansi-term (before use-default-shell activate)
  (interactive (list (getenv "SHELL"))))

Make sure that yank always works in ansi-term:

(defun bw/ensure-term-paste (&optional string)
 (interactive)
 (process-send-string
  (get-buffer-process (current-buffer))
  (if string string (current-kill 0))))

(add-hook 'term-mode-hook (lambda ()
                            (define-key term-raw-map [remap yank] #'bw/ensure-term-paste)))

recentf

recentf stores a list of recently opened files.

Never clean up the list:

(after-load 'recentf
  (setq recentf-auto-cleanup 'never))

The list of files contains any files Emacs has read, not just files I’ve explicitly opened. Clean that list to exclude Emacs metafiles, package cruft etc.

TODO: refactor to use recentf-keep: http://www.emacswiki.org/emacs/RecentFiles#toc18

(after-load 'recentf
  (setq recentf-exclude '("[/\\]\\.elpa/" "[/\\]\\.ido\\.last\\'" "[/\\]\\.git/" ".*\\.gz\\'" ".*-autoloads\\.el\\'" "[/\\]archive-contents\\'" "[/\\]\\.loaddefs\\.el\\'" "url/cookies" ".*\\emacs.bmk\\'")))

Save the most recent 100 items (this is manily to keep the list low for ido):

(after-load 'recentf
  (setq recentf-max-saved-items 100))

Customise the place recentf persists its list of items:

(after-load 'recentf
  (setq recentf-save-file (expand-file-name ".recentf" bw/tmp-local-dir)))

Strip $HOME from the front of recentf candidate files:

(after-load 'recentf
  (add-to-list 'recentf-filename-handlers 'abbreviate-file-name))

I want easy access to my recent files, so define a function that lets me use ido to search over them. Bind this to C-x C-r (C-c C-r is used in modes like =org=mode):

(after-load 'recentf
  (after-load 'ido
    (defun bw/recentf-ido-find-file ()
      "Find a recent file using ido."
      (interactive)
      (let ((file (ido-completing-read "Recently: " recentf-list nil t)))
        (when file
          (find-file file))))
    (bw/add-launcher "r" 'bw/recentf-ido-find-file)))

Now enable recentf:

(after-load 'recentf
  (recentf-mode 1))
(require 'recentf)

ediff

ediff is a full-featured visual diff and merge tool, built into Emacs.

Make sure that the window split is always side-by-side:

(setq ediff-split-window-function 'split-window-horizontally)

Ignore whitespace changes:

(setq ediff-diff-options "-w")

Only ever use one set of windows in one frame:

(setq ediff-window-setup-function 'ediff-setup-windows-plain)

Third-party packages

Emacs has a built-in package manager.

Rather than using Git submodules or similar my Emacs configuration is set up to automatically download and install any required packages at load time. This makes my configuration fully portable.

First set up convenience function (borrowed from Steve Purcell’s emacs config) that installs a package if it’s not already installed:

(defun require-package (package &optional min-version no-refresh)
  "Install given PACKAGE, optionally requiring MIN-VERSION.
If NO-REFRESH is non-nil, the available package lists will not be
re-downloaded in order to locate PACKAGE."
  (if (package-installed-p package min-version)
      t
    (if (or (assoc package package-archive-contents) no-refresh)
        (package-install package)
      (progn
        (package-refresh-contents)
        (require-package package min-version t)))))

Configure package manager

Custom package install location

The default value for package-user-dir is ~/.emacs.d/elpa - since these are third-party packages that are dynamically installed I’d prefer them to be in a hidden directory.

Packages are also byte compiled upon installation, so namespace the install directory to the version of Emacs I’m using.

Final result should be something like ~/.emacs.d/.elpa/24.3.93.1/.

(after-load 'package
  (setq package-user-dir
        (bw/join-dirs (bw/join-dirs bw/dotfiles-dir ".elpa") emacs-version)))

Customise package repositories to install from

By default Emacs only installs files from ELPA. Some of these packages are old or out of date, and they don’t track GitHub repositories.

I want to also add:

  • MELPA (tracks GitHub repositories, is much more comprehensive)
  • MELPA stable (like MELPA, but pinned to specific versions)
(after-load 'package
  (setq package-archives
        '(("gnu"          . "http://elpa.gnu.org/packages/")
          ("melpa"        . "http://melpa.org/packages/")
          ("melpa-stable" . "http://stable.melpa.org/packages/"))))

If we’re using Emacs 25.1 or greater, configure the relative priority of those archives:

(after-load 'package
  (setq package-archive-priorities
      '(("melpa-stable" . 20)
        ("gnu" . 10)
        ("melpa" . 0))))

Pin packages

Some packages need to be pinned to ensure they come from the right place:

(setq package-pinned-packages '((php-mode . "melpa")
                                (ivy . "melpa")
                                (swiper . "melpa")
                                (go-mode . "melpa")
                                (solarized-theme . "melpa")
                                (smartparens . "melpa")
                                (projectile . "melpa")
                                (editorconfig . "melpa")
                                (magit . "melpa-stable")
                                (rjsx-mode . "melpa")))

Initialise package manager

Finally we initialise the package manager:

(package-initialize)

diminish

diminish removes or abbreviates the minor mode indicators that can clutter up one’s modeline.

(require-package 'diminish)

Diminish subword-mode, eldoc-mode, and auto-revert-mode:

(after-load 'diminish
  (after-load 'subword
    (diminish 'subword-mode))
  (after-load 'eldoc
    (diminish 'eldoc-mode))
  (after-load 'autorevert
    (diminish 'auto-revert-mode)))

paradox

paradox is an advanced package.el frontend with GitHub integration.

(require-package 'paradox)

Force paradox into a fullframe:

(after-load 'fullframe
  (fullframe paradox-list-packages paradox-quit-and-close nil))

Automatically ‘star’ packages on GitHub after I install them (so I can easily follow changes to them). Note that this requires a Personal Access Token with public_repo permission, added to the MacOS Keychain with an account name of paradox-github-token:

(let ((github-token (bw/get-keychain-password "paradox-github-token")))
  (setq paradox-automatically-star github-token)
  (setq paradox-github-token github-token))

Add launch command:

(bw/add-launcher "p" 'paradox-list-packages)

async mode

Paradox now supports an asynchronous mode which requires the async package:

(require-package 'async)
(setq paradox-execute-asynchronously t)

exec-path-from-shell

OS X doesn’t use the environment variables available in a shell in a GUI environment (more here).

Since Emacs runs shell commands regularly it’s important that the same PATH is available to my editor as Homebrew etc. set and use.

exec-path-from-shell is a package that copies across PATH and other variables to the Emacs environment.

I only want this to be installed and enabled on OS X.

(when (and (eq system-type 'darwin) (display-graphic-p))
  (require-package 'exec-path-from-shell)
  (setq exec-path-from-shell-check-startup-files nil)
  (setq exec-path-from-shell-variables '("PATH"  "MANPATH" "SHELL"))
  (exec-path-from-shell-initialize))

smex

smex is an advanced completion mode for execute-extended-command (usually known as M-x).

(require-package 'smex)

Replace execute-extended-command’s keyboard shortcuts:

(define-key (current-global-map) [remap execute-extended-command] 'smex)

Make sure we stop the annoying “click this menubar” advice in the buffer:

(setq-default smex-key-advice-ignore-menu-bar t)

Move smex’s cache file out of the home directory:

(setq smex-save-file (expand-file-name ".smex-items" bw/tmp-local-dir))

fullframe

fullframe is a minor mode which allows certain buffers to take over the full frame using advice.

(require-package 'fullframe)

magit

Magit is an Emacs interface to Git. It’s very feature-rich and I find it intuitive.

Keyboard shortcuts

magit-status is the main command to launch Magit. It’s autoloaded so I don’t need to load Magit first.

(global-set-key (kbd "C-c g") 'magit-status)
(bw/add-launcher "g" 'magit-status)

magit-grep isn’t autoloaded, so I need to explicitly load it before binding it:

(autoload 'magit-grep "magit" "Grep for files" t)
(global-set-key (kbd "C-c f") 'magit-grep)

VC-mode integration

Since I use Magit I don’t need to use Emacs’s native vc-mode:

(delete 'Git vc-handled-backends)

Configuration

When performing a completing-read within Magit, I’d like to use IDO:

(setq magit-completing-read-function 'magit-ido-completing-read)

Open the magit-status buffer in the same window as the current buffer:

(setq magit-display-buffer-function 'magit-display-buffer-fullframe-status-v1)

Highlight individual word and letter changes when showing hunk diff overlays:

(setq magit-diff-refine-hunk t)

Don’t tell me when Magit reverts buffers:

(setq magit-revert-buffers 'silent)

When Magit takes a while to call out to Git, pop the process buffer after 10 seconds so I can look for issues:

(setq magit-process-popup-time 10)

Always show the verbose diff in commit windows:

(setq magit-commit-arguments '("--verbose"))

Always set the upstream when pushing:

(setq magit-push-arguments '("--set-upstream"))

Load Magit

Finally we can install Magit:

(require-package 'magit)

evil integration

When launching magit-blame-mode, we want to switch into emacs-mode:

(defadvice magit-blame-mode (after switch-to-emacs-mode-if-evil activate)
  (when evil-mode
    (if magit-blame-mode
        (evil-emacs-state 1)
      (evil-normal-state 1))))

gitignore-mode

gitignore-mode is a major mode for editing gitignore files:

(require-package 'gitignore-mode)

gitconfig-mode

gitconfig-mode is a major more for editing gitconfig files:

(require-package 'gitconfig-mode)

ido-ubiquitous

ido-ubiquitous mode enables ido in many more places than the default ido setup:

(require-package 'ido-completing-read+)
(ido-ubiquitous-mode 1)

ido-vertical

ido-vertical mode renders the ido prompt vertically instead of horizontally. I find this easier to read.

(require-package 'ido-vertical-mode)
(ido-vertical-mode) ;; autoloaded

Because it’s displayed vertically and I want to save screen real estate, I want to reduce the maximum number of candidates ido displays:

(setq ido-max-prospects 5)

Make C-n and C-p work in vertical mode:

(setq ido-vertical-define-keys 'C-n-and-C-p-only)

flx-ido

flx-ido is an advanced flex-matching algorithm that’s significantly faster and more accurate than the built-in method.

(require-package 'flx-ido)

The flx-ido documentation suggests upping the threshold at which GC occurs within Emacs so that flx can cache its candidate lists for longer:

(setq gc-cons-threshold 20000000)

Finally we cause flx-ido-mode to take over ido:

(flx-ido-mode 1)

avy

avy allows one to jump around the buffer to named characters (it’s easier to watch the video on that link than explain).

(require-package 'avy)

Bind it:

(global-set-key (kbd "C-c SPC") 'avy-goto-word-or-subword-1)
(global-set-key (kbd "C-c j") 'avy-goto-word-or-subword-1)
(global-set-key (kbd "C-<return>") 'avy-goto-line)
(bw/add-launcher "j" 'avy-goto-word-or-subword-1)
(bw/add-launcher "J" 'avy-goto-line)

popwin

popwin is a popup window manager that helps make the behaviour of compilation buffers, search buffers etc. a bit more sane.

(require-package 'popwin)

As well as the defaults, I want ag, magit, flycheck and occur to ‘pop’. I don’t want to auto-select the Magit process buffer as it’s for information only.

(after-load 'popwin
  (add-to-list 'popwin:special-display-config `"*ag search*")
  (add-to-list 'popwin:special-display-config `("*magit-process*" :noselect t))
  (add-to-list 'popwin:special-display-config `"*Flycheck errors*")
  (add-to-list 'popwin:special-display-config `"*Occur*")
  (add-to-list 'popwin:special-display-config `("*Compile-Log*" :noselect t))
  (add-to-list 'popwin:special-display-config `("*Paradox Report*" :noselect t))
  (add-to-list 'popwin:special-display-config `("\\*godoc" :regexp t)))

Load popwin and configure keyboard shortcuts:

(require 'popwin)
(popwin-mode 1)
(global-set-key (kbd "C-c P") 'popwin:popup-last-buffer)
(when (eq system-type 'darwin)
  (global-set-key (kbd "s-P") 'popwin:popup-last-buffer))

ag

ag is an Emacs frontend to the ag command, a grep-like code-searching tool. It’s installed via Homebrew on my Mac.

(require-package 'ag)

Set up some key bindings:

(global-set-key (kbd "C-c f") 'ag-project)
(global-set-key (kbd "C-c a") 'ag)
(when (eq system-type 'darwin)
  (global-set-key (kbd "s-F") 'ag-project)
  (global-set-key (kbd "s-A") 'ag))
(bw/add-launcher "a" 'ag-project)
(bw/add-launcher "A" 'ag)

Make sure that we re-use the ag buffers - without this my buffer list is full of buffers named after the project root.

(setq ag-reuse-buffers t)

Highlight search results using isearch highlight faces (otherwise it just copies them from the shell):

(setq ag-highlight-search t)
(add-hook 'ag-mode-hook
          (lambda ()
            (copy-face 'lazy-highlight 'ag-match-face)))

wgrep-ag

wgrep-ag allows me to edit ag results directly from the buffer.

(require-package 'wgrep-ag)

Always save the buffers on exit:

(setq wgrep-auto-save-buffer t)

projectile

projectile is a minor mode for performing commands over a single ‘project’ or grouping of files.

Install and initialise projectile from the main repo:

(require-package 'projectile)
(projectile-global-mode)

I want my keyboard shortcuts to be the same in Projectile as in non-Projectile buffers, so do some remapping:

(after-load 'projectile
  (define-key projectile-mode-map [remap magit-find-file-completing-read] 'projectile-find-file)
  (define-key projectile-mode-map [remap ag-project] 'projectile-ag))

Since I use ag, always use that instead of grep:

(after-load 'projectile
  (define-key projectile-mode-map [remap projectile-grep] 'projectile-ag))

Also define a convenience keyboard shortcut to switch between buffers from the same project:

(after-load 'projectile
  (global-set-key (kbd "s-b") 'projectile-switch-to-buffer)
  (global-set-key (kbd "C-x 4 s-b") 'projectile-switch-to-buffer-other-window)
  (bw/add-launcher "s" 'projectile-switch-project))

Provide some advice to projectile-current-project-files so it uses the magit-find-file library when we’re in a Git repository - using Magit’s process manager is significantly faster than Projectile’s own Git interaction, which creates a new shell process each time. Since magit-find-file might be loaded at any time, just make sure this advice runs after everything has finished:

(add-hook 'after-init-hook
          (lambda ()
            (when (fboundp 'magit-find-file-completing-read)
              (defadvice projectile-current-project-files (around bw/use-magit-find-file activate)
                "If magit-find-file-completing-read is available use that to
call the files instead of Projectile's native caller - this is
much much faster"
                (autoload 'magit-find-file-files "magit-find-file")
                (autoload 'magit-git-repo-p "magit-git")
                (if (magit-git-repo-p (projectile-project-root))
                  (setq ad-return-value (magit-find-file-files))
                  ad-do-it)))))

solarized-theme

I use the solarized dark theme in my editor. I use bbatsoz’s variant as it is more up to date and has better compatibility with most Emacs packages.

solarized-theme doesn’t seem to follow package versioning, so just pull the latest.

Install and configure solarized-theme:

(require-package 'solarized-theme)
(setq x-underline-at-descent-line t)
(setq solarized-use-variable-pitch nil)
(load-theme 'solarized-dark t t)
(load-theme 'solarized-light t t)
(add-hook 'after-init-hook (lambda ()
                             (enable-theme 'solarized-dark)))

magit-find-file

magit-find-file is a package that uses Magit’s process buffers to emulate SublimeText’s Command+p functionality within Git repositories.

(require-package 'magit-find-file)

Set up keybindings for it, particularly the SublimeText equivalent:

(global-set-key (kbd "C-c t") 'magit-find-file-completing-read)
(global-set-key (kbd "M-p") 'magit-find-file-completing-read)
(when (eq system-type 'darwin)
    (global-set-key (kbd "s-p") 'magit-find-file-completing-read))
(bw/add-launcher "t" 'magit-find-file-completing-read)

evil

evil is a very full-featured Vim emulator for Emacs.

(require-package 'evil)

evil-leader

evil-leader is a way of using Vim’s leader key concept in Emacs. Since Emacs already supports nested key bindings, this is really just for convenience.

Install evil-leader, and enable it globally:

(require-package 'evil-leader)
(global-evil-leader-mode 1)

and set it to , (the default is \):

(evil-leader/set-leader ",")

Now set up all the evil-leader powered shortcuts I want:

(evil-leader/set-key
  "b" 'ido-switch-buffer
  "d" 'dired-jump
  "k" 'kill-this-buffer
  "K" 'kill-buffer
  "l" 'linum-mode
  "o" 'occur
  "O" 'browse-url
  "P" 'popwin:popup-last-buffer
  "w" 'save-buffer
  "x" 'smex
  "y" 'bury-buffer)

Now we automatically copy across everything from bw/launcher-map to ensure I easily retain muscle memory:

(dolist (entry (cdr bw/launcher-map))
  (evil-leader/set-key
    (key-description (vector (car entry))) (cdr entry)))

basic evil configuration

Set up some defaults for evil.

Firstly, stop Evil from making the cursor color invisible sometimes:

(after-load 'evil
  (setq evil-default-cursor t))

Make sure that sideways motion keys (h, l etc.) wrap around to the next/previous lines:

(after-load 'evil
  (setq evil-cross-lines t))

When starting Evil, start in normal mode:

(after-load 'evil
  (setq evil-default-state 'normal))

Include the first and last character when moving to the start or end of lines:

(after-load 'evil
  (setq evil-want-visual-char-semi-exclusive t))

When exiting insert mode, don’t move the cursor:

(after-load 'evil
  (setq evil-move-cursor-back nil))

Use ido-find-file when using the :e ex-command - but only after typing a space (e by itself will still reload):

(define-key evil-ex-map "e " 'ido-find-file)

diminish abbrev-mode, which is bundled with evil:

(after-load 'diminish
  (diminish 'abbrev-mode))

Also diminish undo-tree-mode, a mode which allows one to visualise their undo/redo history. This mode is bundled with evil:

(after-load 'undo-tree
  (diminish 'undo-tree-mode))

Finally, invoke evil-mode using my launcher:

(bw/add-launcher "e" 'evil-mode)

fix annoying defaults

Evil has some annoying defaults that I don’t want to unlearn.

C-e is normally evil-copy-from-below in insert mode - I’d rather it was end-of-line:

(define-key evil-insert-state-map "\C-e" 'end-of-line)

C-g is the default ‘exit everything’ key in Emacs - make it do the same thing in Evil (these mappings have been manually copied from evil-maps.el):

(define-key evil-normal-state-map "\C-g" 'evil-force-normal-state)
(define-key evil-visual-state-map "\C-g" 'evil-exit-visual-state)
(define-key evil-insert-state-map "\C-g" 'evil-normal-state)
(define-key evil-replace-state-map "\C-g" 'evil-normal-state)
(define-key evil-ex-completion-map "\C-g" 'abort-recursive-edit)

On OS X, evil copies every single visual state move to the kill ring, which in turns copies it to my system clipboard. I don’t want that to happen.

This has been filed as a bug against evil, and most of the code has come from this StackOverflow question:

(defadvice evil-visual-update-x-selection (around clobber-x-select-text activate)
  (unless (featurep 'ns)
    ad-do-it))

Default mode state for evil

evil supports a default starting state for different major modes. I often want evil to start in emacs-mode, for example:

(dolist (mode-map '((ag-mode . emacs)
                    (cider-repl-mode . emacs)
                    (comint-mode . emacs)
                    (eshell-mode . emacs)
                    (fundamental-mode . emacs)
                    (git-commit-mode . insert)
                    (git-rebase-mode . emacs)
                    (help-mode . emacs)
                    (paradox-menu-mode . emacs)
                    (term-mode . emacs)
                    (ag-mode . emacs)))
  (evil-set-initial-state `,(car mode-map) `,(cdr mode-map)))

smartparens

smartparens is a paredit like minor-mode for many more things than just Lisp.

show-smartparens-mode is a replacement mode for show-paren-mode.

(require-package 'smartparens)
(require 'smartparens-config)
(show-smartparens-global-mode 1)

company

company-mode is a modern and modular completion framework (the other one Emacs people use is autocomplete. I chose company because it’s well-maintained and has better code for integrating with).

configuration

I don’t want company to auto-start - it should only pop when I ask for it:

(after-load 'company
  (setq company-idle-delay nil))

I want it to attempt a completion immediately after a . character - without this I need a few characters before it’ll show candidates:

(after-load 'company
  (setq company-minimum-prefix-length 0))

Show candidates immediately rather than waiting:

(after-load 'company
  (setq company-echo-delay 0))

Make the lighter shorter:

(after-load 'company
  (after-load 'diminish
    (diminish 'company-mode "com")))

enable company mode in programming buffers only

Define a function to enable company-mode and overwrite hippie’s key binding for the local buffer only (this means I can use the same key binding for completion no matter which minor mode I’m using):

(defun bw/enable-company-mode ()
  "Enables company-mode and overloads hippie-expand's binding"
  (company-mode 1)
  (define-key (current-local-map) [remap dabbrev-expand] 'company-complete))

Add this function to any modes derived from prog-mode:

(after-load 'company
  (add-hook 'prog-mode-hook 'bw/enable-company-mode))

evil integration

If I’m using evil, I’ll need to create a new function which acts like a lambda (and ignores arguments):

(defun bw/company-complete-lambda (arg)
  "Ignores passed in arg like a lambda and runs company-complete"
  (company-complete))

And then bind that to the functions evil uses to complete:

(after-load 'evil
  (setq
   evil-complete-next-func 'bw/company-complete-lambda
   evil-complete-previous-func 'bw/company-complete-lambda))

Install and load company

(require-package 'company)
(require 'company)

flycheck

flycheck is a modern, more easily customisable version of flymake. It’s used to perform on-the-fly syntax checking and linting.

(require-package 'flycheck)
(after-load 'flycheck
  (setq
   ;; don't show anything in the left fringe
   flycheck-indication-mode nil))
(require 'flycheck)

javascript

I use js2-mode for editing JavaScript. js2-mode is a JavaScript major mode that includes a full syntax parser written in Emacs Lisp.

js2-mode

(require-package 'js2-mode)
(add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode))

js2-mode isn’t auto-loaded for *.js files, as Emacs ships with a default JavaScript major mode, so I need the final line to make sure I use the right mode.

configure js2-mode

The default lighter for js2-mode Javascript-IDE, which is too long. Rename it:

(rename-modeline "js2-mode" js2-mode "JS2")

JavaScript classes are typically written in CamelCase, so enabled subword-mode:

(add-hook 'js2-mode-hook (lambda () (subword-mode 1)))

Highlight everything:

(after-load 'js2-mode
  (setq js2-highlight-level 3))

Always indent by 4 spaces:

(after-load 'js2-mode
  (setq js2-basic-offset 3))

Make the closing bracket position indent itself idiomatically:

(after-load 'js2-mode
  (setq js2-consistent-level-indent-inner-bracket-p t))

Allow for multi-line var declaration indenting:

(after-load 'js2-mode
  (setq js2-pretty-multiline-decl-indentation-p t))

flycheck integration

I use jslint on the command line (via node.js) to provide syntax checking/linting, and this is integrated with flycheck.

This repository includes an npm manifest that installs a local copy of JSLint. Add the node_modules binary path to my exec path.

(after-load 'flycheck
  (add-to-list 'exec-path (concat bw/dotfiles-dir "node_modules/.bin/")))

Define a custom checker that supports the output of the --terse flag to JSLint, and enable it only for js2-mode.

(after-load 'flycheck
  (flycheck-define-checker javascript-jslint-reporter
      "JSLint based checker"
      :command ("jslint" "--terse" source)
      :error-patterns
      ((warning line-start (1+ nonl) ":" line ":" column ":" blank (message) line-end))
      :modes js2-mode))

Finally, enable my custom checker when js2-mode is enabled. This also dynamically disables js2-mode’s built-in linting functionality so it doesn’t clash.

(after-load 'flycheck
  (defun bw/turn-on-flycheck-mode-js2 ()
    "Turn on and define JS2 mode checker"
    (set (make-local-variable 'js2-highlight-external-variables) nil)
    (set (make-local-variable 'js2-strict-missing-semi-warning) nil)
    (flycheck-select-checker 'javascript-jslint-reporter)
    (flycheck-mode 1))

  (add-hook 'js2-mode-hook 'bw/turn-on-flycheck-mode-js2))

jsx

Install rjsx-mode:

(package-install 'rjsx-mode)

go

Go is an open source language created by Google.

go-mode

go-mode is a major mode for editing .go files. It has excellent integration with the Go compiler and toolchain.

Install go-mode from latest:

(require-package 'go-mode)

Make sure we run gofmt before saving any Go files:

(after-load 'go-mode
  (add-hook 'before-save-hook 'gofmt-before-save))

Make sure we capture the GOPATH environment variable on OS X:

(when (eq system-type 'darwin)
  (after-load 'exec-path-from-shell
    (exec-path-from-shell-copy-env "GOPATH")))

Make sure that if we’re using goenv or gom we can detect that with projectile:

(after-load 'projectile
  (add-to-list 'projectile-project-root-files ".go-version")
  (add-to-list 'projectile-project-root-files "Gomfile"))

Because I use eldoc, which shows the function signatures in the message area, I never actually use godef-describe (it’s the same info). Instead, let’s allow me to jump to the godoc info for the symbol at point.

(after-load 'go-mode
  (define-key go-mode-map [remap godef-describe] 'godoc-at-point))

flycheck integration

flycheck has excellent golang integration, so enable it:

(after-load 'go-mode
  (after-load 'flycheck
    (add-hook 'go-mode-hook 'flycheck-mode-on-safe)))

eldoc integration

Because Go is a compiled language, we can inspect functions statically and provide documentation in the editor using eldoc:

(require-package 'go-eldoc)
(after-load 'go-mode
  (add-hook 'go-mode-hook 'go-eldoc-setup))

company integration

Because of libraries like gocode, Go has very good completion. I use company for completion, so let’s integrate with that. Note this requires gocode to be installed on the system before it’ll work.

First I define a hook lambda that deletes all other company backends - gocode is so accurate that I don’t need any other suggestions:

(defun bw/setup-company-go ()
  "Hook for running on company-go"
  (set (make-local-variable 'company-backends) '(company-go)))

Now add the hook:

(after-load 'company-go
  (add-hook 'go-mode-hook 'bw/setup-company-go))

and install and load company-go:

(after-load 'go-mode
  (after-load 'company
    (require-package 'company-go)
    (require 'company-go)))

puppet-mode

puppet-mode is a major mode for editing .pp files.

(require-package 'puppet-mode)

markdown-mode

markdown-mode is a major mode for editing Markdown files.

(require-package 'markdown-mode)

There’s no official Markdown file extension, so support all the unofficial ones:

(add-to-list 'auto-mode-alist '("\\.md$" . markdown-mode))
(add-to-list 'auto-mode-alist '("\\.markdown$" . markdown-mode))
(add-to-list 'auto-mode-alist '("\\.ft$" . markdown-mode)) ;; FoldingText

paredit

paredit is a minor mode for editing S-expressions in a balanced way. It’s a very good way to edit Lisp, Clojure etc. files. More on EmacsWiki.

(require-package 'paredit)

Enable it for Emacs Lisp files:

(add-hook 'emacs-lisp-mode-hook 'enable-paredit-mode)
(add-hook 'lisp-interaction-mode-hook 'enable-paredit-mode)

Conditionally enable it in the minibuffer when entering an expression:

(defun bw/conditionally-enable-paredit-mode ()
  (if (eq this-command 'eval-expression)
      (enable-paredit-mode)))
(add-hook 'minibuffer-setup-hook 'bw/conditionally-enable-paredit-mode)

cider

CIDER (Clojure IDE and REPL) is the best way to develop Clojure in Emacs.

(require-package 'cider)

Automatically enable eldoc-mode and paredit in CIDER buffers:

(after-load 'cider-mode
  (add-hook 'cider-mode-hook 'cider-turn-on-eldoc-mode)
  (add-hook 'cider-mode-hook 'enable-paredit-mode)
  (add-hook 'cider-repl-mode-hook 'enable-paredit-mode))

Finally enable the CIDER minor mode in Clojure buffers:

(after-load 'clojure-mode
  (add-hook 'clojure-mode-hook 'clojure-enable-cider))

web-mode

web-mode is a major mode for editing templates and HTML. It supports a very broad range of template languages and is highly configurable.

(require-package 'web-mode)

Jinja templates are mostly like Django templates, so just force them to behave like that:

(after-load 'web-mode
  (setq web-mode-engines-alist
        '(("\\.jinja\\'" . "django"))))

Enable web-mode by default for several common file extensions:

(dolist (alist '(("\\.html$'" . web-mode)
                 ("\\.html\\.erb$" . web-mode)
                 ("\\.mustache$" . web-mode)
                 ("\\.jinja$" . web-mode)
                 ("\\.twig$" . web-mode)))
  (add-to-list 'auto-mode-alist alist))

idomenu

idomenu offers ido completion over imenu candidates. It allows me to navigate through classes etc. using completion for methods.

(require-package 'idomenu)

Add it to my launcher:

(bw/add-launcher "i" 'idomenu)

Automatically rescan the current file so imenu is up to date:

(setq imenu-auto-rescan t)

know-your-http-well

know-your-http-well is a documentation set for HTTP, linked out to all the various RFCs and specifications.

(require-package 'know-your-http-well)
(require 'know-your-http-well)

yaml-mode

Regrettably I need to occasionally edit YAML.

(require-package 'yaml-mode)

which-key

which-key displays key bindings based on your incomplete command entered so far.

(require-package 'which-key)
(which-key-mode 1)

diminish which-key-mode:

(diminish 'which-key-mode)

browse-kill-ring

browse-kill-ring allows one to browse the kill ring history when yanking.

(require-package 'browse-kill-ring)

remap yank-pop:

(define-key (current-global-map) [remap yank-pop] 'browse-kill-ring)

make browse-kill-ring act like yank-pop by overwriting the previous yank:

(after-load 'browse-kill-ring
  (setq browse-kill-ring-replace-yank t))

projectile-rails

projectile-rails provides some convenience functions and hooks to make working with Ruby on Rails projects easier:

(require-package 'projectile-rails)

and make sure it’s loaded during projectile’s loading phase:

(add-hook 'projectile-mode-hook 'projectile-rails-on)

swiper

swiper adds inline preview to an isearch like incremental search.

Install swiper from latest code (and also Ivy, as otherwise I get an old version):

(require-package 'swiper)

Remap isearch to swiper:

(define-key (current-global-map) [remap isearch-forward-regexp] 'swiper)
(define-key (current-global-map) [remap isearch-backward-regexp] 'swiper)

Add some advice to recenter the window when exiting from swiper (gotten from Pragmatic Emacs):

(defun bw/recenter (&rest args)
  "recenter display after"
  (recenter))
(advice-add 'swiper :after #'bw/recenter)

anzu

anzu provides enhancements to isearch and related tasks, particularly providing counts and better replacement visualisation.

(require-package 'anzu)

Enable anzu globally:

(global-anzu-mode 1)

Use anzu’s better query-replace:

(define-key (current-global-map) [remap query-replace] 'anzu-query-replace)

Hide anzu’s lighter:

(after-load 'diminish
  (diminish 'anzu-mode))

smart-mode-line

Install smart-mode-line, a better mode-line that has a bunch of nice functionality.

(require-package 'smart-mode-line)

Let the currently set theme override sml/theme - since solarized-theme is installed, this provides the theme:

(setq sml/theme nil)

Finally, init smart-mode-line:

(sml/setup)

If projectile is installed, diminish it:

(after-load 'diminish
  (diminish 'projectile-mode))

sane-term

sane-term is a simple wrapper around ansi-term that allows cycling between ansi-term buffers (instead of ansi-term’s default, which is to create a new buffer):

(package-install 'sane-term)

Add a launcher:

(bw/add-launcher "n" 'sane-term)

php-mode

Use a modern php-mode.

Pin a more modern version than the 2015 version that comes with Emacs, and install php-mode:

(package-install 'php-mode)

Use flycheck to check PHP syntax:

(after-load 'php-mode
  (after-load 'flycheck
    (add-hook 'php-mode-hook 'flycheck-mode-on-safe)))

Local and custom configuration

Local overrides

So I can configure my Emacs per computer/user, I attempted to automatically load some configuration.

First set up a directory to hold the files:

(setq bw/local-dotfiles-dir (bw/join-dirs bw/dotfiles-dir "local"))

Now try to load a file named after the current user:

(load (concat bw/local-dotfiles-dir user-login-name ".el") t)

and try to load a file named after the local system:

(load (concat bw/local-dotfiles-dir system-name ".el") t)

Finally, try loading a default file:

(load (concat bw/local-dotfiles-dir "local-overrides.el") t)

Customize-d configuration

Make sure anything saved using customize goes into a consistent (and ignored) place:

(load (setq custom-file (concat bw/dotfiles-dir "custom.el")) t)

(note that this works because setq returns the value it’s set to)

About

My Emacs configuration

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages