My Emacs Config

Setup

Configure package sources

Add repositories we'll load packages from. I prefer to live on the bleeding edge so have only enabled melpa. Setting package-enable-at-startup to nil prevents a second package load and slightly improves startup time.

  (add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/"))
  (setq package-enable-at-startup nil)

Bootstrap use-package

If use-package is not installed, install it.

  (unless (package-installed-p 'use-package)
    (package-refresh-contents)
    (package-install 'use-package))

Set custom settings to load in own file

This stops emacs adding customised settings to init.el. I try to avoid using customize anyway, preferring programmatic control of variables. Creating it as a temporary file effectively disables it (i.e. any changes are session local).

  (setq custom-file (make-temp-file "emacs-custom"))

Add custom packages to load path

By default Emacs only includes files directly under user-emacs-directory (usually ~/.emacs.d/), so we need to add any folders containing custom packages.

I put my scripts under ~/dotfiles/lisp/ and symlink it with ln -s ~/dotfiles/lisp ~/.emacs.d/lisp.

  (add-to-list 'load-path "~/.emacs.d/lisp/")

Preferences

Don't display the help screen on startup.

  (setq inhibit-startup-screen t)

On  I use ⌘ as meta and prefer ⌥ to do nothing so I can still insert special characters like easily.

  (setq mac-command-modifier 'meta
        mac-option-modifier 'none)

I prefer lines to wrap.

  (global-visual-line-mode 1)

Let's turn off unwanted window decoration.

  (tool-bar-mode -1)
  (scroll-bar-mode -1)

I don't want the error bell.

  (setq ring-bell-function 'ignore)

Make the yes or no prompts shorter.

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

A common frustration with new Emacs users is the filename# files created. This centralises the backup files created as you edit.

  (setq backup-directory-alist '(("." . "~/.emacs.d/backup"))
    backup-by-copying t    ; Don't delink hardlinks
    version-control t      ; Use version numbers on backups
    delete-old-versions t  ; Automatically delete excess backups
    kept-new-versions 20   ; how many of the newest versions to keep
    kept-old-versions 5    ; and how many of the old
    )

Interface

Basics

crux has useful functions extracted from Emacs Prelude. Set C-a to move to the first non-whitespace character on a line, and then to toggle between that and the beginning of the line.

  (use-package crux
    :ensure t
    :bind (("C-a" . crux-move-beginning-of-line)))

Key chords

Key chords let us bind functions to sequential key presses like jj. It makes evil mode being turned off much more palatable.

  (use-package use-package-chords
    :ensure t
    :config
    (key-chord-mode 1))

We bind individual mode chords via use-package but some globals are useful like JJ to jump to the previous buffer.

  (defun jc/switch-to-previous-buffer ()
    "Switch to previously open buffer.
  Repeated invocations toggle between the two most recently open buffers."
    (interactive)
    (switch-to-buffer (other-buffer (current-buffer) 1)))

  (key-chord-define-global "JJ" 'jc/switch-to-previous-buffer)

Command completion

smart M-x suggests M-x commands based on recency and frequency. I don't tend to use it directly but counsel uses it to order suggestions.

  (use-package smex
    :ensure t)

ivy is a generic completion framework which uses the minibuffer. Turning on ivy-mode enables replacement of lots of built in ido functionality.

  (use-package ivy
      :ensure t
      :diminish ivy-mode
      :config
      (ivy-mode t))

By default ivy starts filters with ^. I don't normally want that and can easily type it manually when I do.

  (setq ivy-initial-inputs-alist nil)

counsel is a collection of ivy enhanced versions of common Emacs commands. I haven't bound much as ivy-mode takes care of most things.

  (use-package counsel
    :ensure t
    :bind (("M-x" . counsel-M-x))
    :chords (("yy" . counsel-yank-pop)))

swiper is an ivy enhanced version of isearch.

  (use-package swiper
    :ensure t
    :bind (("M-s" . swiper)))

hydra presents menus for ivy commands.

  (use-package ivy-hydra
    :ensure t)

Suggest next key

Suggest next keys to me based on currently entered key combination.

  (use-package which-key
    :ensure t
    :diminish which-key-mode
    :config
    (add-hook 'after-init-hook 'which-key-mode))

Better undo

undo-tree visualises undo history as a tree for easy navigation.

  (use-package undo-tree
    :ensure t
    :chords (("uu" . undo-tree-visualize))
    :diminish undo-tree-mode)

One of the most important features of an advanced editor is quick text navigation. avy let's us jump to any character or line quickly.

  (use-package avy
    :ensure t
    :chords (("jj" . avy-goto-char-2)
             ("jl" . avy-goto-line)))

ace-window lets us navigate between windows in the same way as avy. Once activated it has useful sub-modes like x to switch into window deletion mode.

 (use-package ace-window
    :ensure t
    :chords ("jk" . ace-window)
    :config
    (setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)))

Easier selection

expand-region expands the region around the cursor semantically depending on mode. Hard to describe but a killer feature.

  (use-package expand-region
    :ensure t
    :bind ("C-=" . er/expand-region))

File tree

I don't use this a whole lot, preferring to navigate via searches such as counsel-find-file, but it's occasionally useful to have a file tree.

  (use-package neotree
    :ensure t
    :config
    (global-set-key (kbd "C-c t") 'neotree-toggle))

When I open the tree try to jump to current file.

  (setq neo-smart-open t)

Use a simple theme for the file tree.

  (setq neo-theme 'arrow)

Appearance

I'm trying out solarized. Not sure I'll be able to get used to a light theme, but worth a try.

  (use-package solarized-theme
    :ensure t
    :config
    (setq solarized-emphasize-indicators nil))
  (load-theme 'solarized-light t)

Set a nice font.

  (set-frame-font "FuraCode Nerd Font 12" nil t)

Set a preferred unicode font.

  (set-fontset-font t 'unicode "STIXGeneral" nil 'prepend)

Display pretty symbols for things like lambda.

  (setq prettify-symbols-unprettify-at-point 'right-edge)
  (global-prettify-symbols-mode 0)

  (add-hook
   'python-mode-hook
   (lambda ()
     (mapc (lambda (pair) (push pair prettify-symbols-alist))
           '(("def" . "𝒇")
             ("class" . "𝑪")
             ("and" . "∧")
             ("or" . "∨")
             ("not" . "¬")
             ("in" . "∈")
             ("not in" . "∉")
             ("return" . "⟼")
             ("yield" . "⟻")
             ("for" . "∀")
             ("!=" . "≠")
             ("==" . "=")
             (">=" . "≥")
             ("<=" . "≤")
             ("[]" . "⃞")
             ("=" . "≝")))))

Powerline is a port from vim, and improves the modeline. Without specifying powerline-default-separator the separators don't show correctly for me.

  (use-package powerline
    :ensure t
    :config
    (setq powerline-default-separator 'utf-8))

Add emoji support. This is useful when working with html.

  (use-package emojify
    :ensure t)

Sometimes it helps to focus on the thing currently under the cursor. This turns off syntax highlighting for everything but the current thing. It's useful sometimes but a bit buggy in certain modes. I wonder if I could improve the config / find a better alternative?

  (use-package focus
    :ensure t)

Sometimes I like to use a nyan cat to indicate progress through the buffer.

  (use-package nyan-mode
      :ensure t)

Highlight the current line.

  (global-hl-line-mode 1)

Coding

Programming specific interface improvements

When programming I like my editor to try to help me with keeping parentheses balanced.

  (use-package smartparens
    :ensure t
    :diminish smartparens-mode
    :config
    (add-hook 'prog-mode-hook 'smartparens-mode))

Highlight parens etc. for improved readability.

  (use-package rainbow-delimiters
    :ensure t
    :config
    (add-hook 'prog-mode-hook 'rainbow-delimiters-mode))

Highlight strings which represent colours.

  (use-package rainbow-mode
    :ensure t
    :config
    (add-hook 'prog-mode-hook 'rainbow-mode))

Keep things indented correctly for me.

  (use-package aggressive-indent
      :ensure t)

Expand parentheses for me.

  (add-hook 'prog-mode-hook 'electric-pair-mode)

Smart dash guesses _ vs - depending on context.

  (use-package smart-dash
    :ensure t
    :config
    (add-hook 'python-mode-hook 'smart-dash-mode))

Project management

Projectile handles folders which are in version control.

  (use-package projectile
    :ensure t
    :config
    (projectile-mode))

Tell projectile to integrate with ivy for completion.

  (setq projectile-completion-system 'ivy)

Add some extra completion options via integration with counsel. In particular this enables C-c p SPC for smart buffer / file search, and C-c p s s for search via ag.

There is no function for projectile-grep, but we could use counsel-git-grep which is similar. Should I bind that to C-c p s g?

  (use-package counsel-projectile
    :ensure t
    :config
    (add-hook 'after-init-hook 'counsel-projectile-on))

fzf is a fuzzy file finder which is very quick.

  (use-package fzf
    :ensure t)

Environment management

By default Emacs doesn't read from the same environment variables set in your terminal. This package fixes that.

  (use-package exec-path-from-shell
    :ensure t
    :config
    (exec-path-from-shell-initialize))

Jump to source

Individual language packages often support IDE features like jump to source, but dumb-jump attempts to support many languages by simple searching. It's quite effective even with dynamic libraries like JS and Python.

  (use-package dumb-jump
    :ensure t
    :diminish dumb-jump-mode
    :bind (("C-M-g" . dumb-jump-go)
           ("C-M-p" . dumb-jump-back)
           ("C-M-q" . dumb-jump-quick-look)))

Git

Magit is an awesome interface to git. Summon it with `C-x g`.

  (use-package magit
    :ensure t
    :bind ("C-x g" . magit-status))

Display line changes in gutter based on git history. Enable it everywhere.

  (use-package git-gutter
    :ensure t
    :config
    (global-git-gutter-mode 't)
    :diminish git-gutter-mode)

TimeMachine lets us step through the history of a file as recorded in git.

  (use-package git-timemachine
    :ensure t)

Syntax checking

Flycheck is a general syntax highlighting framework which other packages hook into. It's an improvment on the built in flymake.

Setup is pretty simple - we just enable globally and turn on a custom eslint function, and also add a custom checker for proselint.

  (use-package flycheck
    :ensure t
    :config
    (add-hook 'after-init-hook 'global-flycheck-mode)
    (add-hook 'flycheck-mode-hook 'jc/use-eslint-from-node-modules)
    (add-to-list 'flycheck-checkers 'proselint))

Proselint is a syntax checker for English language. This defines a custom checker which will run in texty modes.

Proselint is an external program, install it with pip install proselint for this to work.

  (flycheck-define-checker proselint
    "A linter for prose."
    :command ("proselint" source-inplace)
    :error-patterns
    ((warning line-start (file-name) ":" line ":" column ": "
              (id (one-or-more (not (any " "))))
              (message (one-or-more not-newline)
                       (zero-or-more "\n" (any " ") (one-or-more not-newline)))
              line-end))
    :modes (text-mode markdown-mode gfm-mode org-mode))

Autocomplete

Company mode provides good autocomplete options. Perhaps I should add company-quickhelp for documentation (https://github.com/expez/company-quickhelp)?

It would also be good to improve integration with yasnippet as I don't feel I'm making the best use there.

  (use-package company
    :ensure t
    :diminish
    :config
    (add-hook 'after-init-hook 'global-company-mode)

    (setq company-idle-delay t)

    (use-package company-go
      :ensure t
      :config
      (add-to-list 'company-backends 'company-go))

    (use-package company-anaconda
      :ensure t
      :config
      (add-to-list 'company-backends 'company-anaconda)))

Snippets

Unlike autocomplete which suggests words / symbols, snippets are pre-prepared templates which you fill in.

I'm using a community library ([[https://github.com/AndreaCrotti/yasnippet-snippets]]) with lots of ready made options, and have my own directory of custom snippets I've added. Not sure if I should unify these by forking yasnippet-snippets.

Type the shortcut and press TAB to complete, or M-/ to autosuggest a snippet.

  (use-package yasnippet
      :ensure t
      :diminish yas-minor-mode
      :config
      (add-to-list 'yas-snippet-dirs "~/.emacs.d/yasnippet-snippets")
      (add-to-list 'yas-snippet-dirs "~/.emacs.d/snippets")
      (yas-global-mode)
      (global-set-key (kbd "M-/") 'company-yasnippet))

Javascript

In JS indent to 2 spaces.

  (setq-default js-indent-level 2)

JS2 mode improves on the built in JS mode.

  (use-package js2-mode
    :ensure t
    :mode "\\.js\\'"
    :config
    (setq-default js2-ignored-warnings '("msg.extra.trailing.comma")))

js2-refactor supports some useful refactoring options and builds on top of js2-mode.

  (use-package js2-refactor
    :ensure t
    :config
    (js2r-add-keybindings-with-prefix "C-c C-m")
    (add-hook 'js2-mode-hook 'js2-refactor-mode))

RJSX mode makes JSX work well.

  (use-package rjsx-mode
    :ensure t)

Prettier-js autoformats JS code - much like `gofmt` - and we hook it into JS2 and RJSX modes.

  (use-package prettier-js
    :ensure t
    :config
    (setq prettier-js-args '(
                          "--trailing-comma" "es5"
                          "--single-quote" "true"
                          "--print-width" "100"
                          ))
    (add-hook 'js2-mode-hook 'prettier-js-mode)
    (add-hook 'rjsx-mode-hook 'prettier-js-mode))

Sometimes it's useful to use the local eslint provided by a project's node_modules directory. We call this function from a flycheck hook to enable it automatically.

  (defun jc/use-eslint-from-node-modules ()
    "Set local eslint if available."
    (let* ((root (locate-dominating-file
                  (or (buffer-file-name) default-directory)
                  "node_modules"))
           (eslint (and root
                        (expand-file-name "node_modules/eslint/bin/eslint.js"
                                          root))))
      (when (and eslint (file-executable-p eslint))
        (setq-local flycheck-javascript-eslint-executable eslint))))

Web mode

Web mode handles html/css/js.

  (use-package web-mode
    :ensure t)

Markdown

Markdown support isn't built into Emacs, add it with markdown-mode.

  (use-package markdown-mode
    :ensure t
    :commands (markdown-mode gfm-mode)
    :mode (("README\\.md\\'" . gfm-mode)
           ("\\.md\\'" . markdown-mode)
           ("\\.markdown\\'" . markdown-mode))
    :init (setq markdown-command "multimarkdown"))

Golang

Go-mode provides basic language support, we call gofmt on each save to keep code tidy, use eldoc to display documentation and add guru / doctor for IDE functionality.

  (use-package go-mode
    :ensure t
    :config
    (add-hook 'before-save-hook 'gofmt-before-save)

    (use-package go-eldoc
      :ensure t
      :config
      (add-hook 'go-mode-hook 'go-eldoc-setup))

    (use-package godoctor
      :ensure t)

    (use-package go-guru
      :ensure t))

Go guru needs a scope to look at, this function sets it to the current package.

  (defun jc/go-guru-set-current-package-as-main ()
    "GoGuru requires the scope to be set to a go package which
     contains a main, this function will make the current package the
     active go guru scope, assuming it contains a main"
    (interactive)
    (let* ((filename (buffer-file-name))
           (gopath-src-path (concat (file-name-as-directory (go-guess-gopath)) "src"))
           (relative-package-path (directory-file-name (file-name-directory (file-relative-name filename gopath-src-path)))))
      (setq go-guru-scope relative-package-path)))

Haskell

Install haskell mode.

  (use-package haskell-mode
    :ensure t)

Python

Emacs handles python quite well, but we can improve things with anaconda mode.

  (use-package anaconda-mode
    :ensure t
    :config
    (add-hook 'python-mode-hook 'anaconda-mode)
    (add-hook 'python-mode-hook 'anaconda-eldoc-mode))

Sometimes I use kivy.

  (use-package kivy-mode
    :ensure t
    :mode ("\\.kv\\'" . kivy-mode))

Elixir

Elixir highlighting is not built into emacs at present. Elixir-mode gives all the usual niceties, and alchemist improves interaction with tools like iex, mix and elixir-format.

  (use-package elixir-mode
    :ensure t
    :config

    (use-package alchemist
      :ensure t))

Cucumber

Cucumber (gherkin) is a syntax for specifying behaviour driven development tests.

  (use-package feature-mode
    :ensure t)

Rebol / Red

Red is an updated open source implementation of Rebol.

  (use-package rebol)

Idris

  (use-package idris-mode
    :ensure t)

Org

General settings.

I should comment on these more...

  (setq org-startup-indented 'f)
  (setq org-directory "~/org")
  (setq org-special-ctrl-a/e 't)
  (setq org-default-notes-file (concat org-directory "/notes.org"))
  (define-key global-map "\C-cc" 'org-capture)
  (setq org-mobile-directory "~/Dropbox/Apps/MobileOrg")
  (setq org-src-fontify-natively 't)
  (setq org-src-tab-acts-natively t)

Appearance

Improve the display of bullet points.

  (use-package org-bullets
    :ensure t
    :config
    (setq org-bullets-bullet-list '("∙"))
    (add-hook 'org-mode-hook 'org-bullets-mode))

Customize appearance.

  (let*
      ((variable-tuple (cond
                        ((x-list-fonts "Source Sans Pro") '(:font "Source Sans Pro"))
                        ((x-list-fonts "Lucida Grande")   '(:font "Lucida Grande"))
                        ((x-list-fonts "Verdana")         '(:font "Verdana"))
                        ((x-family-fonts "Sans Serif")    '(:family "Sans Serif"))
                        (nil (warn "Cannot find a Sans Serif Font.  Install Source Sans Pro."))))
       (base-font-color     (face-foreground 'default nil 'default))
       (headline           `(:inherit default :weight normal :foreground ,base-font-color)))

    (custom-theme-set-faces 'user
                            `(org-level-8 ((t (,@headline ,@variable-tuple))))
                            `(org-level-7 ((t (,@headline ,@variable-tuple))))
                            `(org-level-6 ((t (,@headline ,@variable-tuple))))
                            `(org-level-5 ((t (,@headline ,@variable-tuple))))
                            `(org-level-4 ((t (,@headline ,@variable-tuple))))
                            `(org-level-3 ((t (,@headline ,@variable-tuple :height 1.33))))
                            `(org-level-2 ((t (,@headline ,@variable-tuple :height 1.33))))
                            `(org-level-1 ((t (,@headline ,@variable-tuple :height 1.33))))
                            `(org-document-title ((t (,@headline ,@variable-tuple :height 1.33 :underline nil))))))

Export

Add bootstrap styled export.

  (use-package ox-twbs
    :ensure t)

Extras

Writing

writegood-mode highlights bad word choices and has functions for calculating readability.

  (use-package writegood-mode
    :ensure t
    :bind ("C-c g" . writegood-mode)
    :config
    (add-to-list 'writegood-weasel-words "actionable"))

Web browsing

w3m is a terminal based browser. Emacs now has eww built in, but I prefer w3m. It needs to be installed separately, e.g. with brew install w3m.

There's an emacs package to interface with it.

  (use-package w3m
    :ensure t)

Email

notmuch is a fast mail client. It needs to be installed externally, e.g. with brew install notmuch but can then be used within emacs.

  (use-package notmuch
    :ensure t)

Rest client

Sometimes I need to explore REST services. Why not Emacs?

  (use-package restclient
    :ensure t)