My literate Emacs configuration
Emacs Lisp
Switch branches/tags
Nothing to show
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
.gitignore
init.el
readme.org

readme.org

About

This is my literate configuration for Emacs. This document includes a collection of configuration snippets to express how I personally like to use Emacs, each accompanied by some reasoning. I think it’s important to include reasoning for each part so I can understand why I use it. Plus the added benefit of others being able to peruse and borrow parts, just as I have from others.

The combination of literacy and functionality is achieved using the amazing org-mode, with org-babel.

Throughout this document, you’ll notice heavy use of the brilliant use-package. For anyone who hasn’t tried out use-package; I emplore you to do so - it truly makes managing your configuration an absolute joy.

General

This section covers many different types of configuration for native Emacs capabilities

Start the server

Start the Emacs server so other clients can connect and use the same session. This is useful for when you may be oprating Emacs from the GUI usually, but want to use the same session from a TTY/terminal. Also handy for when you have your EDITOR set to emacsclient.

(server-start)

Personal stuff

Pretty self explanatory: just setting some personal details about who’s using Emacs.

(setq user-full-name "Calum MacRae"
      user-mail-address "calum0macrae@gmail.com")

Deactivation

Deactivation of functionality I don’t tend to use:

  • Backup files
  • Autosaving
  • Start-up message
  • Audible bell
(setq
  make-backup-files nil
  auto-save-default nil
  inhibit-startup-message t
  ring-bell-function 'ignore)

UTF-8

Configure Emacs for full UTF-8 compatability

(set-charset-priority 'unicode)
(setq locale-coding-system   'utf-8)
(set-terminal-coding-system  'utf-8)
(set-keyboard-coding-system  'utf-8)
(set-selection-coding-system 'utf-8)
(prefer-coding-system        'utf-8)
(setq default-process-coding-system '(utf-8-unix . utf-8-unix))

Global :ensure for use-package statements

use-package has an :ensure keyword which dictates whether packages are installed or not. As most of my use-package configurations are for external packages, I set this to always ensure. Then, in cases where I don’t want this to be true, I simply set :ensure nil

(setq use-package-always-ensure t)

Discard customizations

Emacs has a comprehensive customization system that allows configuration changes interactively. Personally, I opt to ensure all the configuration I use for my environment is fully declarative. As such, the following configuration sets the custom-file to be a random temporary file created each time Emacs starts. This means any customizations made interactively are discarded entirely.

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

Just use ‘y’ or ‘n’ instead of ‘yes’ or ‘no’

You’ll find yes-or-no prompts coming up in Emacs a lot. I’d much rather just type y or n than yes or no every time…

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

Set the scratch buffer string

Set the scratch buffer’s initial contents to include a comment with a timestamp of creation. Not really all that useful, but cleaner than the default comment, and I like having something there.

(setq initial-scratch-message (format ";; Scratch buffer - started on %s\n\n" (current-time-string)))

Confirm quit

This adds a confirmation prompt when quitting Emacs - because I’m only human.

(setq confirm-kill-emacs 'yes-or-no-p)

Follow symlinks in version control

If there are any symlinks in version controlled repositories, follow them

(setq vc-follow-symlinks t)

Use ‘root’ user by default for SSH connections using TRAMP

When connecting to a remote system over SSH via TRAMP, use the root user by default

(set-default 'tramp-default-proxies-alist (quote ((".*" "\\`root\\'" "/ssh:%h:"))))

Set TRAMP shell prompt pattern (fix for some fancy prompts)

When connecting to some remote systems over SSH via TRAMP, you may run into some shells which use some different encoding for their prompt. This can result in a malformed prompt on the client side. This little snippet fixes that

(setq shell-prompt-pattern "\\(?:^\\|\r\\)[^]#$%>\n]*#?[]#$%>].* *\\(^[\\[[0-9;]*[a-zA-Z] *\\)*")

Set explicit shell binary

Set the filepath to the binary to run when invoking term (or any of its siblings).

(setq explicit-shell-file-name "/run/current-system/sw/bin/zsh")

Use M-3 to insert an octothorp

I’m usually on a British keyboard, so when doing M-3: insert an octothorp, not a GBP sign

(global-set-key (kbd "M-3") '(lambda () (interactive) (insert "#")))

Configure FlySpell to use aspell

I use aspell, so this simply sets Flyspell to use it and passes a couple extra arguments

(setq ispell-program-name "aspell")
(setq ispell-extra-args '("--sug-mode=ultra" "--lang=en_GB"))

Kill term buffers upon exit

If I’m using an interactive terminal, it’s nice to just ^D out of it and have the buffer disappear

(defadvice term-handle-exit
  (after term-kill-buffer-on-exit activate)
(kill-buffer))

Calendar/Diary

Set the start of the week for the calendar to be Monday. Sort entries when viewing diary items.

(setq calendar-week-start-day 1)
(setq diary-file "~/org/diary")
(add-hook 'diary-list-entries-hook 'diary-sort-entries t)

Packages

This section covers external packages I use and their configuration, in no particular order

Ivy/Counsel/Swiper

Absolutely brilliant interactive interface and completion frameworks. These packages improve the Emacs experience so much. As you can see from the :bind sections, I use these to replace some of the most used actions.

Ivy

  • Suppress count visibility for ivy-read
  • Set initial input chars to nil
(use-package ivy
  :init
  (setq ivy-count-format "")
  (setq ivy-initial-inputs-alist nil)
  :bind
  ("C-s" . swiper)
  ("M-x" . counsel-M-x)
  ("C-x C-f" . counsel-find-file)
  :config
  (ivy-mode 1))

Counsel

  • Set a prettier candidate delimiter for killring
  • Bind common functions
  • Bind common org functions
(use-package counsel
  :init
  (setq counsel-yank-pop-separator
    (concat "\n\n"
      (concat (apply 'concat (make-list 50 "---")) "\n")))
  :bind (
  ("M-y" . counsel-yank-pop)
  ("C-h f" . counsel-describe-function)
  ("C-h v" . counsel-describe-variable)

  :map org-mode-map
  ("C-c  C-j" . counsel-org-goto)
  ("C-c  C-q" . counsel-org-tag)))

Magit

The one true Git porcelain! Truely a joy to use - it surfaces the power of Git in such a fluent manner. Anyone using Git and Emacs needs Magit in their life!

(use-package magit
  :bind ("C-c m" . magit-status)
  :init
  (setq magit-completing-read-function 'ivy-completing-read))

GitHub integration

This package integrates Magit with GitHub to allow the user to perform pull request actions. As mentioned previously, I’m usually using a British keyboard, so I’ve mapped the popup to the press of “£”. I’ve added an argument so that when raising a new PR, it’s automatically opened in my web-browser. If my web-browser is already running, this’ll simply open a new tab - if not, it’ll spawn a new instance. Another handy functionality here is that the URL of the PR is automatically copied to the kill ring, so you can paste it wherever necessary.

(use-package magit-gh-pulls
  :bind (:map magit-gh-pulls-mode-map
  ("£" . magit-gh-pulls-popup))
  :init
  (add-hook 'magit-mode-hook 'turn-on-magit-gh-pulls)
  (setq magit-gh-pulls-arguments (quote ("--open-new-in-browser"))))

Projectile

Project management based on version control repositories. Absolutely essential package for me. This makes hopping around and between various projects really easy. Not only that, but it allows project-wide actions. Like killing all buffers for a project, performing a project-wide find-and-replace, or a grep, etc.

Some configuration I use:

  • Custom function for switching buffers based on status of current project: If I’m in a project and I do C-x b, it calls counsel-projectile-switch-to-buffer. If I’m not in a project, it falls back to ivy-switch-buffer.
  • Setting the completion system to ivy
  • Adding an action to invoke neotree upon switching projects
(use-package projectile
  :init
  (setq projectile-completion-system 'ivy)
  (setq projectile-switch-project-action 'neotree-projectile-action)
  (defun smart-switch-buffer ()
    "Call `counsel-projectile-switch-to-buffer` if `projectile-project-p`, otherwise
     fallback to `ivy-switch-buffer`."
    (interactive)
    (if (projectile-project-p)
        (counsel-projectile-switch-to-buffer)
      (ivy-switch-buffer)))
  :bind
  ("C-x b" . smart-switch-buffer)
  :config
  (projectile-global-mode))

counsel-projectile

Further integration of Counsel with Projectile than what’s provided natively. As I use counsel-projectile-on to remap a bunch of Projectile’s functions to their Counsel equivilents, but I want to use Perspective functionality, I remap projectile-switch-project, after counsel-projectile-on has been called, to projectile-persp-switch-project. This then masks counsel-projectile-switch-project and integrates Perspective when switching projects.

(use-package counsel-projectile
    :bind
    ("C-c p s r" . counsel-projectile-rg)
    :init
    (counsel-projectile-on)
    :config
    (define-key projectile-mode-map [remap projectile-switch-project] #'projectile-persp-switch-project))

Perspective

Workspaces! Indespensible if you work on a lot of projects. Perspective is like workspaces (virtual desktops) for Emacs. It’s a means of namespacing a group of tangible buffers. When combined with Projectile, this becomes a really nice combination as projects then seemlessly translate to workspaces.

Here, I’ve also added a hydra for various Perspective actions.

(use-package perspective
  :init (persp-mode))

(use-package persp-projectile
  :bind
  ("C-c x" . hydra-persp/body)
  :config
  (require 'persp-projectile)
  (defhydra hydra-persp (:columns 4
                         :color blue)
    "Perspective"
    ("a" persp-add-buffer "Add Buffer")
    ("i" persp-import "Import")
    ("c" persp-kill "Close")
    ("n" persp-next "Next")
    ("p" persp-prev "Prev")
    ("k" persp-remove-buffer "Kill Buffer")
    ("r" persp-rename "Rename")
    ("A" persp-set-buffer "Set Buffer")
    ("s" persp-switch "Switch")
    ("C-x" persp-switch-last "Switch Last")
    ("b" persp-switch-to-buffer "Switch to Buffer")
    ("P" projectile-persp-switch-project "Switch Project")
    ("q" nil "Quit")))

NeoTree

Awesome little filetree as a sidebar, ầ la NerdTree for Vim

(use-package neotree
  :bind ("C-;" . neotree-toggle)
  :config
  (setq neo-theme (if window-system 'icons 'arrows)))

popwin

Some windows in Emacs can be quite obtrusive. popwin aims to manage this. By using popwin windows that could be deemed “temporary” only take up a small amount of realestate, which is reclaimed upon said window closing. This is handy for things like grep results, help/compile buffers, etc.

You can also define your own “pop-up” actions. As you can see here, I’ve defined a little “pop-up” terminal. This will spawn a little terminal buffer at the top of my Emacs frame. Then, when I’m done with it and I exit the process/kill the buffer, the space is automatically reclaimed.

(use-package popwin
  :bind
  ("C-x t" . popwin-term:term)
  :config
  (popwin-mode 1)

  (defun popwin-term:term ()
  (interactive)
  (popwin:display-buffer-1
   (or (get-buffer "*terminal*")
       (save-window-excursion
         (call-interactively 'term)))
     :default-config-keywords '(:position :top))
     (provide 'popwin-term)))

Flycheck

Have Flycheck turned on for everything - checking stuff is always good!

(use-package flycheck
  :init
  (add-hook 'after-init-hook #'global-flycheck-mode))

ace-window

Jump around Emacs windows & frames using character prefixes. I use this constantly - it even works across multiple frames.

(use-package ace-window
  :bind ("M-p" . ace-window)
  :config
  (setq aw-dispatch-always t)
  (setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)))

Smartparens

Brilliant automatic balancing of pairs. Makes for a really nice experience when typing in any language - programming or not. Just check out some of the gifs in the project’s README.

(use-package smartparens
  :config
  (progn
    (smartparens-global-mode)
    (show-smartparens-global-mode t)))

eyebrowse

Layout management! Very handy for switching betweeen layout configurations. Think of it as a tiling window manager, except for panes within Emacs. It allows the user to seemlessly switch back and forth between favourable splits.

(use-package eyebrowse
  :config
  (eyebrowse-mode t))

erc-hl-nicks

Nickname highlighting for ERC (IRC in Emacs)

(use-package erc-hl-nicks)

GitGutter

Hints and actions in the buffer/fringe for bits being followed by Git. The configuration bellow gives little diff highlights in the fringe for changes.

(use-package git-gutter
  :init
  (setq
    git-gutter:modified-sign " "
    git-gutter:added-sign " "
    git-gutter:deleted-sign " ")
  (global-git-gutter-mode t)
  :config
  (add-hook 'window-setup-hook (lambda ()
    (set-face-background 'git-gutter:modified "#da8548")
    (set-face-background 'git-gutter:added "#98be65")
    (set-face-background 'git-gutter:deleted "#ff6c6b"))))

YAML & Ansible

YAML’s great - so support is obviously nice to have. I also spend quite a bit of my time working with Ansible. ansible-doc is a handy little package to pull up Ansible module documentation within Emacs. I’ve bound C-c h a for the YAML mode keymap to spawn ansible-doc

(use-package ansible-doc)
(use-package yaml-mode
  :bind (:map yaml-mode-map
    ("C-c h a" . ansible-doc)))

multiple-cursors

Having multiple cursors can be very powerful. This allows you to perform simultaneous actions at multiple positions within the buffer. This can be based on arbitrary regions (n amount of lines, as chosen manually), for each ocurrance of a pattern/selection, etc.

(use-package multiple-cursors
  :bind
  ("C-S-c C-S-c" . mc/edit-lines)
  ("C->" . mc/mark-next-like-this)
  ("C-<" . mc/mark-previous-like-this)
  ("C-c C->" . mc/mark-all-like-this))

Set exec/man PATH from shell

When looking for executables/man-pages, Emacs will inherit these properties from the OS environment. This package provides the ability to do so from the user’s shell, where they may have some more complex logic to determine such paths.

(use-package exec-path-from-shell
  :config
  (setq exec-path-from-shell-check-startup-files nil)
  (exec-path-from-shell-initialize))

Expand region

Select regions by semantic units. Really handy for selecting regions of data - just repeat keypress to expand selection further.

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

json-mode

No reasoning needed here! Everyone needs JSON

(use-package json-mode)

Aggressive indent

Keeps code indented when making disruptive changes

(use-package aggressive-indent
  :config
  (global-aggressive-indent-mode 1))

MoveText

Easily move text up and down. I’ve tied this into a little hydra for more natural repeated movement.

(use-package move-text
  :bind ("C-c t" . hydra-move-text/body)
  :config
  ;; Move Text
  (defhydra hydra-move-text ()
    "Move text"
    ("k" move-text-up "Up")
    ("j" move-text-down "Down")
    ("q" nil "Quit" :color blue)))

Docker Integration

Various docker integrations:

  • dockerfile-mode is pretty self explanatory
  • docker-tramp allows TRAMP connections into running containers
  • docker, with a hydra, allows for interaction with the Docker distribution
(use-package dockerfile-mode
  :mode "\\Dockerfile\\'")

(use-package docker-tramp)
(use-package docker
  :bind ("C-c d" . hydra-docker/body)
  :config
  (defhydra hydra-docker (:columns 5 :color blue)
    "Docker"
    ("c" docker-containers "Containers")
    ("v" docker-volumes "Volumes")
    ("i" docker-images "Images")
    ("n" docker-networks "Networks")
    ("b" dockerfile-build-buffer "Build Buffer")
    ("q" nil "Quit")))

Corral

Quickly surround text with delimiters, along with a hydra

(use-package corral
  :bind
  ("M-9" . corral-parentheses-backward)
  ("M-0" . corral-parentheses-forward)
  ("M-[" . corral-brackets-backward)
  ("M-]" . corral-brackets-forward)
  ("M-{" . corral-braces-backward)
  ("M-}" . corral-braces-forward)
  ("M-\"" . corral-double-quotes-backward)
  ("C-c v" . hydra-corral/body)
  :config
  (setq corral-preserve-point t)
  (defhydra hydra-corral (:columns 5)
    "Corral"
    ("(" corral-parentheses-backward "Back")
    (")" corral-parentheses-forward "Forward")
    ("[" corral-brackets-backward "Back")
    ("]" corral-brackets-forward "Forward")
    ("{" corral-braces-backward "Back")
    ("}" corral-braces-forward "Forward")
    ("\"" corral-double-quotes-backward "Back")
    ("'" corral-single-quotes-backward "Back")
    ("." hydra-repeat "Repeat")))

Focus

Makes the current function at the point the only syntax-highlighted construct in the buffer. All other buffer contents are “subdued” to look like comments.

(use-package focus)

Dumb Jump

Jump to definitions

(use-package dumb-jump
  :bind
  ("C-c j" . hydra-dumb-jump/body)
  :config
  (setq dumb-jump-selector 'ivy)
  (defhydra hydra-dumb-jump (:color blue)
  "Dumb Jump"
  ("g" dumb-jump-go "Jump to def")
  ("p" dumb-jump-back "Jump back")
  ("q" dumb-jump-quick-look "Quick look")
  ("o" dumb-jump-go-other-window "Jump in other window")
  ("q" nil "Quit")))

undo-tree

Powerful undo actions formulated in a tree structure

(use-package undo-tree
  :config
  (global-undo-tree-mode))

ivy-pass

I use pass to manage my passwords. This is a handy little package for interfacing with it.

(use-package ivy-pass
  :init (setq password-store-password-length 30)
  :bind ("C-c M-p" . ivy-pass))

Nix/NixOS

Various packages for working with Nix/NixOS

Turn off aggressive-indent-mode as it doesn’t play nice.

(use-package nix-mode
  :config
  (add-hook 'nix-mode-hook #'(lambda ()
                             (when (and (stringp buffer-file-name)
            (string-match "\\.nix\\'" buffer-file-name))
                               (aggressive-indent-mode 0)))))

Configure company-mode completions for NixOS options. I’ve made this conditional based on the shell output of uname, as conventional means of determining this won’t work (it just reports as a generic Linux distro).

(use-package nixos-options
  :if
  (string-match-p "NixOS"
    (shell-command-to-string "uname -v | awk '{print substr($1,4);}'")))

(use-package company-nixos-options
  :if
  (string-match-p "NixOS"
    (shell-command-to-string "uname -v | awk '{print substr($1,4);}'"))
  :config
  (add-hook 'nix-mode-hook (lambda ()
                            (set (make-local-variable 'company-backends) '(company-nixos-options))
                            (company-mode))))

restclient

REST client for Emacs! Really cool package. Kinda like Postman/Insomnia.

(use-package restclient
  :mode ("\\.http\\'" . restclient-mode))

Note/TODO highlighting

It’s nice to have some note/todo highlighting :)

(use-package hl-todo
  :config
  (global-hl-todo-mode)
  (add-hook 'yaml-mode-hook 'hl-todo-mode))

Hydras

Great package to tie tangible actions together into convenient keybinding landscapes. Here, you’ll find some “general” hydras - other hydras that are centric around packages will be found with that package’s configuration.

General hydras:

  • Zoom: increase/decrease current buffer text size
  • Transpose: transpose various constructs of text
  • Toggle mode: turn frequently “toggled” modes on and off
(use-package hydra
  :bind
  ("C-c z" . hydra-zoom/body)
  ("C-c T" . hydra-transpose/body)
  ("C-c M" . hydra-toggle-mode/body)

  :config
  ;; Zoom
  (defhydra hydra-zoom ()
    "Zoom"
    ("i" text-scale-increase "In")
    ("o" text-scale-decrease "Out")
    ("q" nil "Quit" :color blue))

  ;; Transpose
  (defhydra hydra-transpose (:color red)
    "Transpose"
    ("c" transpose-chars "Characters")
    ("w" transpose-words "Words")
    ("l" transpose-lines "Lines")
    ("s" transpose-sentences "Sentences")
    ("p" transpose-paragraphs "Paragraphs")
    ("q" nil "Quit" :color blue))

  ;; Toggle mode
  (defhydra hydra-toggle-mode (:color blue)
    "Toggle"
    ("c" centered-window-mode "Centered Buffer")
    ("w" whitespace-mode "Whitespace")
    ("f" focus-mode "Focus")
    ("i" aggressive-indent-mode "Aggressive indent")
    ("s" flyspell-mode "FlySpell")
    ("S" flyspell-prog-mode "FlySpell Prog")
    ("q" nil "Quit")))

Evil

Vim emulation in Emacs. Because: yes, you can have the best of both worlds!

Below you’ll find various extensions to my Evil layer that generally improve the quality of life. This first configuration block is simply to turn Evil on at start and add some NeoTree bindings for compatability.

(use-package evil
  :init (evil-mode)
  :config
  (evil-define-key 'normal neotree-mode-map (kbd "TAB") 'neotree-enter)
  (evil-define-key 'normal neotree-mode-map (kbd "SPC") 'neotree-quick-look)
  (evil-define-key 'normal neotree-mode-map (kbd "q") 'neotree-hide)
  (evil-define-key 'normal neotree-mode-map (kbd "RET") 'neotree-enter))

Compatibility

Make some things play nicer with Evil

Magit

(use-package evil-magit)

smartparens

(use-package evil-smartparens
  :config
  (add-hook 'smartparens-enabled-hook #'evil-smartparens-mode))

Org

(use-package evil-org
  :after org
  :config
  (add-hook 'org-mode-hook 'evil-org-mode)
  (add-hook 'evil-org-mode-hook
            (lambda ()
              (evil-org-set-key-theme))))

Surround

Easily surround, emulating surround.vim

(use-package evil-surround
  :config
  (global-evil-surround-mode 1))

Goggles

Visual hints when performing Evil operations (dd, yy, cw, p, etc.)

(use-package evil-goggles
  :config
  (evil-goggles-mode)
  (evil-goggles-use-diff-faces))

Lion

Align operators (gl & gL), emulating lion.vim

(use-package evil-lion
  :config
  (evil-lion-mode))

Traversal

EasyMotion

Buffer traversal made easy! Emulates easymotion.vim

(use-package evil-easymotion
  :config
  (evilem-default-keybindings "SPC"))

Snipe

2-char searching with f, F, t, T operators. Like seek.vim/sneak.vim

(use-package evil-snipe
  :after evil-quickscope
  :config
  (evil-snipe-mode 1)
  (evil-snipe-override-mode 1))

Quickscope

Highlight targets for f, F, t, T operators. Emulates quick_scope.vim

(use-package evil-quickscope
  :config
  (global-evil-quickscope-mode 1))

Commentary

Easily comment lines/blocks. Emulates commentary.vim

(use-package evil-commentary
  :config
  (evil-commentary-mode))

Exchange

Exchange operator for exchanging constructs of text. Emulates exchange.vim

(use-package evil-exchange
  :config
  (evil-exchange-install))

Custom functions

Useful functions gathered that don’t quite require an entire package.

Sort words

Taken from here; just a handy little function to sort words in a region alphabetically

(defun sort-words (reverse beg end)
  "Sort words in region alphabetically, in REVERSE if negative.
    Prefixed with negative \\[universal-argument], sorts in reverse.

    The variable `sort-fold-case' determines whether alphabetic case
    affects the sort order.

    See `sort-regexp-fields'."
  (interactive "*P\nr")
  (sort-regexp-fields reverse "\\w+" "\\&" beg end))

Sensible beginning of line

Taken from here, I use this to replace move-beginning-of-line (C-a). It will take your point back to the first column of the line you’re on, as per the indentation. A second press will then take your point back to the very beginning of the line. Pressing again will take you back to the indented column.

(defun sensible-move-beginning-of-line (arg)
  "Move point back to indentation of beginning of line.

  Move point to the first non-whitespace character on this line.
  If point is already there, move to the beginning of the line.
  Effectively toggle between the first non-whitespace character and
  the beginning of the line.

  If ARG is not nil or 1, move forward ARG - 1 lines first.  If
  point reaches the beginning or end of the buffer, stop there."
  (interactive "^p")
  (setq arg (or arg 1))

  ;; Move lines first
  (when (/= arg 1)
    (let ((line-move-visual nil))
      (forward-line (1- arg))))

  (let ((orig-point (point)))
    (back-to-indentation)
    (when (= orig-point (point))
      (move-beginning-of-line 1))))

(global-set-key [remap move-beginning-of-line]
                'sensible-move-beginning-of-line)

Appearance

Configuration related to the appearance of Emacs

Hide stuff

Hide various elements of the Emacs GUI:

  • toolbar
  • tooltips
  • scrollbar
  • menubar
  • blinking cursor
(dolist (mode
  '(tool-bar-mode
    tooltip-mode
    scroll-bar-mode
    menu-bar-mode
    blink-cursor-mode))
  (funcall mode 0))

Fringes

Fringes always looked too fat to me by default, and take up too much space. This just makes them a bit thinner and turns the fringe off completely where I don’t feel it’s necessary.

(fringe-mode '(4 . 0))

(defun hide-fringes ()
  (set-window-fringes (selected-window) 0 0))

(add-hook 'eshell-mode 'hide-fringes)

Centered buffers

A really simple package that will centre your buffer contents in the frame. Purely cosmetic, but I do find it helps with focus from time to time. If I’m working on something that only needs one buffer, I’ll usually centre it. I have this bound to a key in my toggle-mode hydra so I can switch it on/off easily.

(use-package centered-window-mode)

Current line highlighting

Highlights the current line of the point. Just helps to visualise where you are in the buffer. I turn it on globally, but explicitly turn it off where I don’t deem it necessary.

(global-hl-line-mode t)

(make-variable-buffer-local 'global-hl-line-mode)
(defvar my-ghd-modes '(
                       shell-mode-hook
                       git-commit-mode-hook
                       term-mode-hook
                      )
  "Modes to ensure global-hl-line-mode is disabled for.")
  (dolist (m my-ghd-modes)
    (add-hook m (lambda () (setq global-hl-line-mode nil))))

Indent guides

Cool little package to provide indentation guides. This will display a line of | characters with a comment face to indicate the indentation of the current block.

(use-package indent-guide
  :config
  (indent-guide-global-mode))

Rainbow Delimiters

So handy! This will colourize delimiters differently based on their depth. Really helps you not get burried when you’re in deep.

(use-package rainbow-delimiters
  :config
  (defvar my-rainbow-modes '(
                            yaml-mode-hook
			       prog-mode-hook
                            )
    "Modes to ensure rainbow-delimiters-mode is enabled for.")
  (dolist (m my-rainbow-modes)
      (add-hook m 'rainbow-delimiters-mode)))

All the icons

Fancy! Just a bit of extra prettiness. This places little glyphs around to better convey some things where text may be a bit cluttered. That, and it makes things look nice! We’re visual creatures, after-all.

In this first block, I’ve added a conditional call to the downloading of the all-the-icons font, based on the OS environment.

(use-package all-the-icons
  :init
  (cond
   ((string-equal system-type "gnu/linux")
    (if (not
      (file-exists-p (concat (getenv "XDG_DATA_HOME") "/fonts/all-the-icons.ttf")))
      (all-the-icons-install-fonts "t")))
   ((string-equal system-type "darwin")
     (if (not
      (file-exists-p (concat (getenv "HOME") "/Library/Fonts/all-the-icons.ttf")))
      (all-the-icons-install-fonts "t")))))

Dired

Makes dired buffers a little more easy on the eyes. Actually very helpful when trying to pick some files out manually.

(use-package all-the-icons-dired
  :init
  (add-hook 'dired-mode-hook 'all-the-icons-dired-mode))

Ivy

Icons in some ivy operations (file icons in counsel-find-file, etc.)

(use-package all-the-icons-ivy
  :init
  (all-the-icons-ivy-setup))

Theme

Fashion First!

Right now, I’m using the beautiful doom-one theme from hlissner’s doom-themes. It’s inspired by Atom’s “One Dark” themes. It’s high contrast, and easy on the eyes. Bright enough to easily distinguish between different constructs, but not sickening. It’s also got some nice hinting for textual faces and NeoTree icons.

(use-package doom-themes
  :init
  (load-theme 'doom-one t)
  :config
  (doom-themes-neotree-config)

  (setq
      doom-themes-enable-bold t
      doom-themes-enable-italic t
      doom-one-brighter-comments t
      doom-neotree-file-icons t))

Make file visiting buffers stand out

The following expression adds a little flair to buffers visiting files. I have it activate upon visiting files and after switching perspectives.

(use-package solaire-mode
  :init
  (add-hook 'after-change-major-mode-hook #'turn-on-solaire-mode)
  (advice-add #'persp-load-state-from-file :after #'solaire-mode-restore-persp-mode-buffers)
  :config
  (require 'solaire-mode)
  (solaire-mode-swap-bg))

Modeline

The ever important modeline! Making your modeline look good and express useful information is vital, in my opinion. There’s a lot of info you can cram in there - but to do so tastefully and efficiently is key.

I did build a custom modeline from scratch before, but since trying out Spaceline, taken from Spacemacs, I’ve not gone back. There are a few things I turn off, and I also stuff some more all-the-icons love in - so all in all, it’s very clean and clear.

(use-package spaceline
  :init
  (require 'spaceline-config)
  (spaceline-emacs-theme))
(use-package spaceline-all-the-icons
  :after spaceline
  :config
  (spaceline-all-the-icons-theme)
  (spaceline-all-the-icons--setup-git-ahead)
  (spaceline-toggle-all-the-icons-buffer-size-off)
  (spaceline-toggle-all-the-icons-hud-off)
  (spaceline-toggle-all-the-icons-vc-icon-off)
  (setq spaceline-all-the-icons-separator-type 'wave))

Font

Some configuration for fonts

Use Fira Code as the default font

(when (window-system)
   (set-default-font "Fira Code Retina"))

Configure ligatures

Ligatures are nice to have. They make things easier to read and ever so slightly more compact

(let ((alist '((33 . ".\\(?:\\(?:==\\|!!\\)\\|[!=]\\)")
               (35 . ".\\(?:###\\|##\\|_(\\|[#(?[_{]\\)")
               (36 . ".\\(?:>\\)")
               (37 . ".\\(?:\\(?:%%\\)\\|%\\)")
               (38 . ".\\(?:\\(?:&&\\)\\|&\\)")
               (42 . ".\\(?:\\(?:\\*\\*/\\)\\|\\(?:\\*[*/]\\)\\|[*/>]\\)")
               (43 . ".\\(?:\\(?:\\+\\+\\)\\|[+>]\\)")
               (45 . ".\\(?:\\(?:-[>-]\\|<<\\|>>\\)\\|[<>}~-]\\)")
               (46 . ".\\(?:\\(?:\\.[.<]\\)\\|[.=-]\\)")
               (47 . ".\\(?:\\(?:\\*\\*\\|//\\|==\\)\\|[*/=>]\\)")
               (48 . ".\\(?:x[a-zA-Z]\\)")
               (58 . ".\\(?:::\\|[:=]\\)")
               (59 . ".\\(?:;;\\|;\\)")
               (60 . ".\\(?:\\(?:!--\\)\\|\\(?:~~\\|->\\|\\$>\\|\\*>\\|\\+>\\|--\\|<[<=-]\\|=[<=>]\\||>\\)\\|[*$+~/<=>|-]\\)")
               (61 . ".\\(?:\\(?:/=\\|:=\\|<<\\|=[=>]\\|>>\\)\\|[<=>~]\\)")
               (62 . ".\\(?:\\(?:=>\\|>[=>-]\\)\\|[=>-]\\)")
               (63 . ".\\(?:\\(\\?\\?\\)\\|[:=?]\\)")
               (91 . ".\\(?:]\\)")
               (92 . ".\\(?:\\(?:\\\\\\\\\\)\\|\\\\\\)")
               (94 . ".\\(?:=\\)")
               (119 . ".\\(?:ww\\)")
               (123 . ".\\(?:-\\)")
               (124 . ".\\(?:\\(?:|[=|]\\)\\|[=>|]\\)")
               (126 . ".\\(?:~>\\|~~\\|[>=@~-]\\)")
               )
             ))
  (dolist (char-regexp alist)
    (set-char-table-range composition-function-table (car char-regexp)
                          `([,(cdr char-regexp) 0 font-shape-gstring]))))

Emoji

Because this is the world we live in: don’t hate, appreciate! Emojis can be fun in READMEs (and maybe Git commits where machine readability doesn’t matter all that much)

(use-package company-emoji
  :config
  (add-to-list 'company-backends 'company-emoji)
  (add-hook 'markdown-mode-hook 'company-mode)
  (add-hook 'git-commit-mode-hook 'company-mode)
  (cond
   ((string-equal system-type "darwin")
   (set-fontset-font
     t 'symbol
     (font-spec :family "Apple Color Emoji") nil 'prepend))))

(use-package emojify
  :config
  (add-hook 'markdown-mode-hook 'emojify-mode)
  (add-hook 'git-commit-mode-hook 'emojify-mode))

Language Config

Configuration specific to languages I tend to use

Go ❤

This configuration for Go does a few things:

  • Use goimports instead of go-fmt for formatting buffers
  • Add \~/code/go/bin to the executable path, so built binaries can be found
  • Format before saving (adhering to go fmt)
  • Use compilation to determine problems
  • GOROOT & GOPATH interpretation based on /etc/zshrc: I use declarative configuration in Nix for my workstations, so this will inherit the right values for these variables.
  • Add some bindings for:
    • Removing unused imports
    • Navigating to imports
    • Navigating to definitions
  • Set up company-mode with a Go backend for completion
  • Custom compilation operation:
    • Use gometalinter to check for issues
    • Build
    • Test/Vet
  • ElDoc integration
  • Use gometalinter for live linting
(use-package go-mode
  :config
  ; Use goimports instead of go-fmt
  (setq gofmt-command "goimports")
  (add-to-list 'exec-path "~/code/go/bin")
  (add-hook 'before-save-hook 'gofmt-before-save)
  (add-hook 'go-mode-hook 'setup-go-mode-compile)
  (setenv "GOROOT" (shell-command-to-string ". /etc/zshrc; echo -n $GOROOT"))
  (setenv "GOPATH" (shell-command-to-string ". /etc/zshrc; echo -n $GOPATH"))
  (add-hook 'go-mode-hook '(lambda ()
                             (local-set-key (kbd "C-c C-r") 'go-remove-unused-imports)))
  (add-hook 'go-mode-hook '(lambda ()
                             (local-set-key (kbd "C-c C-g") 'go-goto-imports)))
  (add-hook 'go-mode-hook (lambda ()
                            (set (make-local-variable 'company-backends) '(company-go))
                            (company-mode))))

;; Ensure all linting passes, then use 'go build' to compile, then test/vet
(defun setup-go-mode-compile ()
  (if (not (string-match "go" compile-command))
      (set (make-local-variable 'compile-command)
           "gometalinter.v1 --deadline 10s && go build -v && go test -v && go vet")))

;; Completion integration
(use-package company-go
  :after go
  :config
  (setq tab-width 4)

  :bind (:map go-mode-map
  ("M-." . godef-jump)))

;; ElDoc integration
(use-package go-eldoc
  :config
  (add-hook 'go-mode-hook 'go-eldoc-setup))

;; Linting
(use-package flycheck-gometalinter
  :config
  (progn
    (flycheck-gometalinter-setup))
    ;; skip linting for vendor dirs
    (setq flycheck-gometalinter-vendor t)
    ;; use in test files
    (setq flycheck-gometalinter-test t)
    ;; only use fast linters
    (setq flycheck-gometalinter-fast t)
    ;; explicitly disable 'gotype' linter
    (setq flycheck-gometalinter-disable-linters '("gotype")))

Markdown

Markdown compatability. Activate markdown-mode for .md files and turn on flyspell

(use-package markdown-mode
  :mode "\\.md\\'"
  :config
  (add-hook 'markdown-mode-hook 'flyspell-mode))

Jinja2

Jinja2 compatability. Activate jinja2-mode for .j2 files

(use-package jinja2-mode
  :mode "\\.j2\\'")

JavaScript

JavaScript compatability. Activate js2-mode for .js files

(use-package js2-mode
  :mode "\\.js\\'")

HashiCorp

Compatability with HCL and Terraform syntax. Activate hcl-mode for .nomad files.

(use-package hcl-mode
  :mode "\\.nomad\\'")

(use-package terraform-mode)

Org Config

Configuration for the brilliant Org mode!

General

  • A few keybindings for captures, agenda, etc.
  • Follow filesystem links for Org files
  • Agenda files directory
  • Custom capture templates
(global-set-key "\C-cl" 'org-store-link)
(global-set-key "\C-cc" 'org-capture)
(global-set-key "\C-ca" 'org-agenda)
(global-set-key "\C-cb" 'org-iswitchb)
(setq org-return-follows-link t)
(setq org-agenda-files '("~/org"))
(setq org-capture-templates
      '(("t" "Todo" entry (file+headline "~/org/gtd.org" "Tasks")
         "* TODO %^{Brief Description} %^g\n%?\tAdded: %U")
        ("r" "ToRead" entry (file+headline "~/org/gtd.org" "Tasks")
         "* TOREAD %^{Title} %^g\n%?\tLink: %c")
        ("p" "Project" entry (file+headline "~/org/gtd.org" "Projects")
         "* %^{Brief Description} %^g\n%?\tAdded: %U")
        ("m" "Maybe" entry (file+headline "~/org/gtd.org" "Maybe/Some Day")
         "* %^{Brief Description} %^g\n%?\tAdded: %U")))

org-bullets

Make Org headings look a bit fancier

(use-package org-bullets
  :config
  (add-hook 'org-mode-hook (lambda () (org-bullets-mode 1))))