Find file Copy path
c32ee5a Nov 9, 2018
1 contributor

Users who have contributed to this file

2031 lines (1505 sloc) 55.8 KB

Emacs Configuration

Use sensible-defaults.el

Use sensible-defaults.el for some basic settings.

(load-file "~/code/personal/sensible-defaults.el/sensible-defaults.el")

Configure use-package

I’m currently using Cask to manage my dependencies, but I’m gradually moving to use-package. I’m planning on converting packages lazily, as I need them, so expect to see a somewhat inconsistent state for a while. =)

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

(setq use-package-verbose t)
(setq use-package-always-ensure t)

(require 'use-package)

Always compile packages, and use the newest version available.

(use-package auto-compile
  :config (auto-compile-on-load-mode))
(setq load-prefer-newer t)

Set personal information

Who am I? Where am I?

(setq user-full-name "Harry R. Schwartz"
      user-mail-address ""
      calendar-latitude 37.4
      calendar-longitude -122.1
      calendar-location-name "Mountain View, CA")

Access my netrc data

(require 'netrc)

(setq netrc-file "~/.netrc")

(defun netrc-username (machine)
  "Helper function to extract a username from my netrc."
  (car (netrc-credentials machine)))

(defun netrc-password (machine)
  "Helper function to extract a password from my netrc."
  (cadr (netrc-credentials machine)))

Add resources to load-path

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


I’d prefer not to expand abbrevs when I hit escape. That’s always jarring and usually not what I want. In particular, it makes working with Coq really frustrating.

(setq evil-want-abbrev-expand-on-insert-exit nil)

Use evil.

(evil-mode 1)

Enable surround everywhere.

(global-evil-surround-mode 1)

Bind C-p to fuzzy-finding files in the current project.

(define-key evil-normal-state-map (kbd "C-p") 'projectile-find-file)

Use evil with Org agendas.

(use-package evil-org
  :ensure t
  :after org
  (add-hook 'org-mode-hook 'evil-org-mode)
  (add-hook 'evil-org-mode-hook
            (lambda () (evil-org-set-key-theme)))
  (require 'evil-org-agenda)

Utility functions

Define a big ol’ bunch of handy utility functions.

(defun hrs/rename-file (new-name)
  (interactive "FNew name: ")
  (let ((filename (buffer-file-name)))
    (if filename
          (when (buffer-modified-p)
          (rename-file filename new-name t)
          (kill-buffer (current-buffer))
          (find-file new-name)
          (message "Renamed '%s' -> '%s'" filename new-name))
      (message "Buffer '%s' isn't backed by a file!" (buffer-name)))))

(defun hrs/generate-scratch-buffer ()
  "Create and switch to a temporary scratch buffer with a random
  (switch-to-buffer (make-temp-name "scratch-")))

(defun hrs/kill-current-buffer ()
  "Kill the current buffer without prompting."
  (kill-buffer (current-buffer)))

(defun hrs/visit-last-dired-file ()
  "Open the last file in an open dired buffer."

(defun hrs/visit-last-migration ()
  "Open the last file in 'db/migrate/'. Relies on projectile. Pretty sloppy."
  (dired (expand-file-name "db/migrate" (projectile-project-root)))
  (kill-buffer "migrate"))

(defun hrs/add-auto-mode (mode &rest patterns)
  "Add entries to `auto-mode-alist' to use `MODE' for all given file `PATTERNS'."
  (dolist (pattern patterns)
    (add-to-list 'auto-mode-alist (cons pattern mode))))

(defun hrs/find-file-as-sudo ()
  (let ((file-name (buffer-file-name)))
    (when file-name
      (find-alternate-file (concat "/sudo::" file-name)))))

(defun hrs/region-or-word ()
  (if mark-active
      (buffer-substring-no-properties (region-beginning)
    (thing-at-point 'word)))

(defun hrs/append-to-path (path)
  "Add a path both to the $PATH variable and to Emacs' exec-path."
  (setenv "PATH" (concat (getenv "PATH") ":" path))
  (add-to-list 'exec-path path))

UI preferences

Tweak window chrome

I don’t usually use the menu or scroll bar, and they take up useful space.

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

There’s a tiny scroll bar that appears in the minibuffer window. This disables that:

(set-window-scroll-bars (minibuffer-window) nil nil)

The default frame title isn’t useful. This binds it to the name of the current project:

(setq frame-title-format '((:eval (projectile-project-name))))

Use fancy lambdas

Why not?

(global-prettify-symbols-mode t)

Load up a theme

I’m currently using the “nord” theme. I’ve got a scenic wallpaper, so just a hint of transparency looks lovely and isn’t distracting or hard to read.

(defun transparency (value)
  "Sets the transparency of the frame window. 0=transparent/100=opaque."
  (interactive "nTransparency Value 0 - 100 opaque:")
  (set-frame-parameter (selected-frame) 'alpha value))

(defun hrs/apply-theme ()
  "Apply the `nord' theme and make frames just slightly transparent."
  (load-theme 'nord t)
  (transparency 90))

If this code is being evaluated by emacs --daemon, ensure that each subsequent frame is themed appropriately.

(if (daemonp)
    (add-hook 'after-make-frame-functions
              (lambda (frame)
                (with-selected-frame frame (hrs/apply-theme))))

Configure powerline-evil

(use-package powerline-evil
  :ensure t

Disable visual bell

sensible-defaults replaces the audible bell with a visual one, but I really don’t even want that (and my Emacs/Mac pair renders it poorly). This disables the bell altogether.

(setq ring-bell-function 'ignore)

Scroll conservatively

When point goes outside the window, Emacs usually recenters the buffer point. I’m not crazy about that. This changes scrolling behavior to only scroll as far as point goes.

(setq scroll-conservatively 100)

Set default font and configure font resizing

I’m partial to Inconsolata.

The standard text-scale- functions just resize the text in the current buffer; I’d generally like to resize the text in every buffer, and I usually want to change the size of the modeline, too (this is especially helpful when presenting). These functions and bindings let me resize everything all together!

Note that this overrides the default font-related keybindings from sensible-defaults.

(setq hrs/default-font "Inconsolata")
(setq hrs/default-font-size 8)
(setq hrs/current-font-size hrs/default-font-size)

(setq hrs/font-change-increment 1.1)

(defun hrs/font-code ()
  "Return a string representing the current font (like \"Inconsolata-14\")."
  (concat hrs/default-font "-" (number-to-string hrs/current-font-size)))

(defun hrs/set-font-size ()
  "Set the font to `hrs/default-font' at `hrs/current-font-size'.
Set that for the current frame, and also make it the default for
other, future frames."
  (let ((font-code (hrs/font-code)))
    (add-to-list 'default-frame-alist (cons 'font font-code))
    (set-frame-font font-code)))

(defun hrs/reset-font-size ()
  "Change font size back to `hrs/default-font-size'."
  (setq hrs/current-font-size hrs/default-font-size)

(defun hrs/increase-font-size ()
  "Increase current font size by a factor of `hrs/font-change-increment'."
  (setq hrs/current-font-size
        (ceiling (* hrs/current-font-size hrs/font-change-increment)))

(defun hrs/decrease-font-size ()
  "Decrease current font size by a factor of `hrs/font-change-increment', down to a minimum size of 1."
  (setq hrs/current-font-size
        (max 1
             (floor (/ hrs/current-font-size hrs/font-change-increment))))

(define-key global-map (kbd "C-)") 'hrs/reset-font-size)
(define-key global-map (kbd "C-+") 'hrs/increase-font-size)
(define-key global-map (kbd "C-=") 'hrs/increase-font-size)
(define-key global-map (kbd "C-_") 'hrs/decrease-font-size)
(define-key global-map (kbd "C--") 'hrs/decrease-font-size)


Highlight the current line

global-hl-line-mode softly highlights the background color of the line containing point. It makes it a bit easier to find point, and it’s useful when pairing or presenting code.


Hide certain modes from the modeline

I’d rather have only a few necessary mode identifiers on my modeline. This either hides or “renames” a variety of major or minor modes using the diminish package.

(defmacro diminish-minor-mode (filename mode &optional abbrev)
  `(eval-after-load (symbol-name ,filename)
     '(diminish ,mode ,abbrev)))

(defmacro diminish-major-mode (mode-hook abbrev)
  `(add-hook ,mode-hook
             (lambda () (setq mode-name ,abbrev))))

(diminish-minor-mode 'abbrev 'abbrev-mode)
(diminish-minor-mode 'simple 'auto-fill-function)
(diminish-minor-mode 'company 'company-mode)
(diminish-minor-mode 'eldoc 'eldoc-mode)
(diminish-minor-mode 'flycheck 'flycheck-mode)
(diminish-minor-mode 'flyspell 'flyspell-mode)
(diminish-minor-mode 'global-whitespace 'global-whitespace-mode)
(diminish-minor-mode 'org-indent 'org-indent-mode)
(diminish-minor-mode 'projectile 'projectile-mode)
(diminish-minor-mode 'ruby-end 'ruby-end-mode)
(diminish-minor-mode 'subword 'subword-mode)
(diminish-minor-mode 'undo-tree 'undo-tree-mode)
(diminish-minor-mode 'yard-mode 'yard-mode)
(diminish-minor-mode 'yasnippet 'yas-minor-mode)
(diminish-minor-mode 'wrap-region 'wrap-region-mode)

(diminish-minor-mode 'paredit 'paredit-mode " π")

(diminish-major-mode 'emacs-lisp-mode-hook "el")
(diminish-major-mode 'haskell-mode-hook "λ=")
(diminish-major-mode 'lisp-interaction-mode-hook "λ")
(diminish-major-mode 'python-mode-hook "Py")

Highlight uncommitted changes

Use the diff-hl package to highlight changed-and-uncommitted lines when programming.

(require 'diff-hl)

(add-hook 'prog-mode-hook 'turn-on-diff-hl-mode)
(add-hook 'vc-dir-mode-hook 'turn-on-diff-hl-mode)

Programming environments

I like shallow indentation, but tabs are displayed as 8 characters by default. This reduces that.

(setq-default tab-width 2)

Treating terms in CamelCase symbols as separate words makes editing a little easier for me, so I like to use subword-mode everywhere.

(global-subword-mode 1)

Compilation output goes to the *compilation* buffer. I rarely have that window selected, so the compilation output disappears past the bottom of the window. This automatically scrolls the compilation window so I can always see the output.

(setq compilation-scroll-output t)


Use company-coq-mode, which really helps make Proof General a more useful IDE.

I bind the right and left arrow keys to evaluating and retracting the next and previous statements. This is more convenient than the default bindings of C-c C-n and C-c C-u.

I also like to disable abbrev-mode; it has a ton of abbreviations for Coq, but they’ve always been unpleasant surprises for me.

(add-hook 'coq-mode-hook
          (lambda ()
            (evil-define-key 'normal coq-mode-map (kbd "<down>") 'proof-assert-next-command-interactive)
            (evil-define-key 'normal coq-mode-map (kbd "<up>") 'proof-undo-last-successful-command)
            (evil-define-key 'normal coq-mode-map (kbd "<return>") 'company-coq-proof-goto-point)
            (abbrev-mode 0)))

The default Proof General layout stacks the code, goal, and response buffers on top of each other. I like to keep my code on one side and my goal and response buffers on the other.

(setq proof-three-window-mode-policy 'hybrid)

Don’t move point when asserting and undoing proof commands.

(setq proof-follow-mode 'ignore)

The Proof General splash screen’s pretty cute, but I don’t need to see it every time.

(setq proof-splash-enable nil)

CSS and Sass

Indent by 2 spaces.

(use-package css-mode
  (setq css-indent-offset 2))

Don’t compile the current SCSS file every time I save.

(use-package scss-mode
  (setq scss-compile-at-save nil))


Define my $GOPATH and tell Emacs where to find the Go binaries.

(setenv "GOPATH" "/home/hrs/code/go")
(hrs/append-to-path (concat (getenv "GOPATH") "/bin"))

Run goimports on every file when saving, which formats the file and automatically updates the list of imports. This requires that the goimports binary is installed.

(setq gofmt-command "goimports")
(add-hook 'before-save-hook 'gofmt-before-save)

When I open a Go file,

  • Start up company-mode with the Go backend. This requires that the gocode binary is installed,
  • Redefine the default compile command to something Go-specific, and
  • Enable flycheck.
(add-hook 'go-mode-hook
          (lambda ()
            (set (make-local-variable 'company-backends)
            (if (not (string-match "go" compile-command))
                (set (make-local-variable 'compile-command)
                     "go build -v && go test -v && go vet"))


Enable haskell-doc-mode, which displays the type signature of a function, and use smart indentation.

(hrs/append-to-path "~/.cabal/bin")
(add-hook 'haskell-mode-hook
          (lambda ()

JavaScript and CoffeeScript

Indent everything by 2 spaces.

(setq js-indent-level 2)

(add-hook 'coffee-mode-hook
          (lambda ()
            (yas-minor-mode 1)
            (setq coffee-tab-width 2)))


All the lisps have some shared features, so we want to do the same things for all of them. That includes using paredit-mode to balance parentheses (and more!), rainbow-delimiters to color matching parentheses, and highlighting the whole expression when point is on a paren.

(setq lispy-mode-hooks

(dolist (hook lispy-mode-hooks)
  (add-hook hook (lambda ()
                   (setq show-paren-style 'expression)

If I’m writing in Emacs lisp I’d like to use eldoc-mode to display documentation.

(add-hook 'emacs-lisp-mode-hook 'eldoc-mode)


I bring up the status menu with C-x g.

Use evil keybindings with magit.

The default behavior of magit is to ask before pushing. I haven’t had any problems with accidentally pushing, so I’d rather not confirm that every time.

Per tpope’s suggestions, highlight commit text in the summary line that goes beyond 50 characters.

Enable spellchecking when writing commit messages.

I sometimes use git from the terminal, and I’ll use emacsclient --tty to write commits. I’d like to be in the insert state when my editor pops open for that.

I’d like to start in the insert state when writing a commit message.

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

  (use-package evil-magit)
  (setq magit-push-always-verify nil)
  (setq git-commit-summary-max-length 50)
  (add-hook 'git-commit-mode-hook 'turn-on-flyspell)
  (add-hook 'with-editor-mode-hook 'evil-insert-state))


Projectile’s default binding of projectile-ag to C-c p s s is clunky enough that I rarely use it (and forget it when I need it). This binds the easier-to-type C-c C-v and C-c v to useful searches.

(defun hrs/search-project-for-symbol-at-point ()
  "Use `projectile-ag' to search the current project for `symbol-at-point'."
  (projectile-ag (projectile-symbol-at-point)))

(global-set-key (kbd "C-c v") 'projectile-ag)
(global-set-key (kbd "C-c C-v") 'hrs/search-project-for-symbol-at-point)

When I visit a project with projectile-switch-project, the default action is to search for a file in that project. I’d rather just open up the top-level directory of the project in dired and find (or create) new files from there.

(setq projectile-switch-project-action 'projectile-dired)

I’d like to always be able to recursively fuzzy-search for files, not just when I’m in a Projecile-defined project. This uses the current directory as a project root (if I’m not in a “real” project).

(setq projectile-require-project-root nil)


Add ~/.local/bin to load path. That’s where virtualenv is installed, and we’ll need that for jedi.

(hrs/append-to-path "~/.local/bin")

Enable elpy. This provides automatic indentation, auto-completion, syntax checking, etc.


Use flycheck for syntax checking:

(add-hook 'elpy-mode-hook 'flycheck-mode)

Format code according to PEP8 on save:

(require 'py-autopep8)
(add-hook 'elpy-mode-hook 'py-autopep8-enable-on-save)

Configure Jedi

(add-to-list 'company-backends 'company-jedi)
(add-hook 'python-mode-hook 'jedi:setup)
(setq jedi:complete-on-dot t)

Ruby and RSpec

I use chruby to switch between versions of Ruby. This sets a default version to use within Emacs (for things like xmp or rspec).

(setq hrs/ruby-version "2.5.1")

(use-package chruby
  (chruby hrs/ruby-version))

Ruby executables are installed in ~/.gem/ruby/<version>/bin. This ensures that that’s included in the path. In particular, we want that directory to be included because it contains the xmpfilter executable, which is used below.

(hrs/append-to-path (format "~/.gem/ruby/%s/bin" hrs/ruby-version))

rcodetools provides xmp, which lets me evaluate a Ruby buffer and display the results in “magic” (# =>) comments.

I disable warnings in Ruby because I disagree with a few of them (complaining about private attr_reader, especially) and they gunk up my buffer.

(setq xmpfilter-command-name
      "ruby -S xmpfilter --no-warnings --dev --fork --detect-rbtest")
(require 'rcodetools)

I like running Rubocop through Flycheck, but it also invokes Reek, which I’ve found to be more of a nuisance than a help. This disables the ruby-reek checker:

(setq-default flycheck-disabled-checkers '(ruby-reek))

When assigning the result of a conditional, I like to align the expression to match the beginning of the statement instead of indenting it all the way to the if.

(setq ruby-align-to-stmt-keywords '(def if))

There are a bunch of things I’d like to do when I open a Ruby buffer:

  • I don’t want to insert an encoding comment.
  • I want to enable yas, rspec, yard, flycheck, and projectile-rails.
  • I’d like my RSpec tests to be run in a random order, and I’d like the output to be colored.
  • Chruby should automatically determine the correct version for me.
  • C-c C-c should run xmp, to do that nifty “eval into comments” trick.
(add-hook 'ruby-mode-hook
          (lambda ()
            (setq ruby-insert-encoding-magic-comment nil)
            (local-set-key "\r" 'newline-and-indent)
            (setq rspec-command-options "--color --order random")
            (define-key ruby-mode-map (kbd "C-c C-c") 'xmp)

I associate ruby-mode with Gemfiles, gemspecs, Rakefiles, and Vagrantfiles.


When running RSpec tests I’d like to scroll to the first error.

(add-hook 'rspec-compilation-mode-hook
          (lambda ()
            (make-local-variable 'compilation-scroll-output)
            (setq compilation-scroll-output 'first-error)))


Indent with 2 spaces.

(add-hook 'sh-mode-hook
          (lambda ()
            (setq sh-basic-offset 2
                  sh-indentation 2)))


Ensure that scala-mode and sbt-mode are installed.

(use-package scala-mode
  ("scala" . scala-mode))
(use-package sbt-mode)

Don’t show the startup message with launching ENSIME:

(setq ensime-startup-notification nil)

Bind a few keys to common operations:

(evil-define-key 'normal ensime-mode-map (kbd "C-t") 'ensime-type-at-point)
(evil-define-key 'normal ensime-mode-map (kbd "M-.") 'ensime-edit-definition)


If I’m editing Slim templates I’m probably in a Rails project. In that case, I’d like to still be able to run my tests from a Slim buffer.

(add-hook 'slim-mode-hook 'rspec-mode)


If I’m in web-mode, I’d like to:

  • Color color-related words with rainbow-mode.
  • Still be able to run RSpec tests from web-mode buffers.
  • Indent everything with 2 spaces.
(add-hook 'web-mode-hook
          (lambda ()
            (setq web-mode-markup-indent-offset 2)))

Use web-mode with embedded Ruby files, regular HTML, and PHP.



If I’m editing YAML I’m usually in a Rails project. I’d like to be able to run the tests from any buffer.

(add-hook 'yaml-mode-hook 'rspec-mode)


I use multi-term to manage my shell sessions. It’s bound to C-c t.

(global-set-key (kbd "C-c t") 'multi-term)

Use a login shell:

(setq multi-term-program-switches "--login")

I’d rather not use Evil in the terminal. It’s not especially useful (I don’t use vi bindings in xterm) and it shadows useful keybindings (C-d for EOF, for example).

(evil-set-initial-state 'term-mode 'emacs)

I add a bunch of hooks to term-mode:

  • I’d like links (URLs, etc) to be clickable.
  • Yanking in term-mode doesn’t quite work. The text from the paste appears in the buffer but isn’t sent to the shell process. This correctly binds C-y and middle-click to yank the way we’d expect.
  • I bind M-o to quickly change windows. I’d like that in terminals, too.
  • I don’t want to perform yasnippet expansion when tab-completing.
(defun hrs/term-paste (&optional string)
   (get-buffer-process (current-buffer))
   (if string string (current-kill 0))))

(add-hook 'term-mode-hook
          (lambda ()
            (define-key term-raw-map (kbd "C-y") 'hrs/term-paste)
            (define-key term-raw-map (kbd "<mouse-2>") 'hrs/term-paste)
            (define-key term-raw-map (kbd "M-o") 'other-window)
            (setq yas-dont-activate t)))

Publishing and task management with Org-mode

Display preferences

I like to see an outline of pretty bullets instead of a list of asterisks.

(use-package org-bullets
  (add-hook 'org-mode-hook #'org-bullets-mode))

I like seeing a little downward-pointing arrow instead of the usual ellipsis (...) that org displays when there’s stuff under a header.

(setq org-ellipsis "")

Use syntax highlighting in source blocks while editing.

(setq org-src-fontify-natively t)

Make TAB act as if it were issued in a buffer of the language’s major mode.

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

When editing a code snippet, use the current window rather than popping open a new one (which shows the same information).

(setq org-src-window-setup 'current-window)

Quickly insert a block of elisp:

(add-to-list 'org-structure-template-alist
             '("el" "#+BEGIN_SRC emacs-lisp\n?\n#+END_SRC"))

Enable spell-checking in Org-mode.

(add-hook 'org-mode-hook 'flyspell-mode)

Task and org-capture management

Store my org files in ~/documents/org, maintain an inbox in Dropbox, define the location of an index file (my main todo list), and archive finished tasks in ~/documents/org/

(setq org-directory "~/documents/org")

(defun org-file-path (filename)
  "Return the absolute address of an org file, given its relative name."
  (concat (file-name-as-directory org-directory) filename))

(setq org-inbox-file "~/Dropbox/")
(setq org-index-file (org-file-path ""))
(setq org-archive-location
      (concat (org-file-path "") "::* From %s"))

I use Drafts to create new tasks, format them according to a template, and append them to an “” file in my Dropbox. This function lets me import them easily from that inbox file to my index.

(defun hrs/copy-tasks-from-inbox ()
  (when (file-exists-p org-inbox-file)
      (find-file org-index-file)
      (goto-char (point-max))
      (insert-file-contents org-inbox-file)
      (delete-file org-inbox-file))))

I store all my todos in ~/documents/org/, so I’d like to derive my agenda from there.

(setq org-agenda-files (list org-index-file))

Hitting C-c C-x C-s will mark a todo as done and move it to an appropriate place in the archive.

(defun hrs/mark-done-and-archive ()
  "Mark the state of an org-mode item as DONE and archive it."
  (org-todo 'done)

(define-key org-mode-map (kbd "C-c C-x C-s") 'hrs/mark-done-and-archive)

Record the time that a todo was archived.

(setq org-log-done 'time)

Capturing tasks

Define a few common tasks as capture templates. Specifically, I frequently:

  • Record ideas for future blog posts in ~/documents/notes/,
  • Maintain a todo list in ~/documents/org/
  • Convert emails into todos to maintain an empty inbox.
(setq org-capture-templates
      '(("b" "Blog idea"
         (file "~/documents/notes/")
         "* %?\n")

        ("e" "Email" entry
         (file+headline org-index-file "Inbox")
         "* TODO %?\n\n%a\n\n")

        ("f" "Finished book"
         table-line (file "~/documents/notes/")
         "| %^{Title} | %^{Author} | %u |")

        ("r" "Reading"
         (file (org-file-path "")))

        ("s" "Subscribe to an RSS feed"
         (file "~/documents/rss/urls")
         "%^{Feed URL} \"~%^{Feed name}\"")

        ("t" "Todo"
         (file+headline org-index-file "Inbox")
         "* TODO %?\n")))

When I’m starting an Org capture template I’d like to begin in insert mode. I’m opening it up in order to start typing something, so this skips a step.

(add-hook 'org-capture-mode-hook 'evil-insert-state)

When refiling an item, I’d like to use ido for completion.

(setq org-refile-use-outline-path t)
(setq org-outline-path-complete-in-steps nil)


Bind a few handy keys.

(define-key global-map "\C-cl" 'org-store-link)
(define-key global-map "\C-ca" 'org-agenda)
(define-key global-map "\C-cc" 'org-capture)

Hit C-c i to quickly open up my todo list.

(defun hrs/open-index-file ()
  "Open the master org TODO list."
  (find-file org-index-file)
  (flycheck-mode -1)

(global-set-key (kbd "C-c i") 'hrs/open-index-file)

Hit M-n to quickly open up a capture template for a new todo.

(defun org-capture-todo ()
  (org-capture :keys "t"))

(global-set-key (kbd "M-n") 'org-capture-todo)
(add-hook 'gfm-mode-hook
          (lambda () (local-set-key (kbd "M-n") 'org-capture-todo)))
(add-hook 'haskell-mode-hook
          (lambda () (local-set-key (kbd "M-n") 'org-capture-todo)))


Allow export to markdown and beamer (for presentations).

(require 'ox-md)
(require 'ox-beamer)

Allow babel to evaluate Emacs lisp, Ruby, dot, or Gnuplot code.

 '((emacs-lisp . t)
   (ruby . t)
   (dot . t)
   (gnuplot . t)))

Don’t ask before evaluating code blocks.

(setq org-confirm-babel-evaluate nil)

Associate the “dot” language with the graphviz-dot major mode.

(add-to-list 'org-src-lang-modes '("dot" . graphviz-dot))

Translate regular ol’ straight quotes to typographically-correct curly quotes when exporting.

(setq org-export-with-smart-quotes t)

Exporting to HTML

Don’t include a footer with my contact and publishing information at the bottom of every exported HTML document.

(setq org-html-postamble nil)

Exporting to HTML and opening the results triggers /usr/bin/sensible-browser, which checks the $BROWSER environment variable to choose the right browser. I’d like to always use Firefox, so:

(setq browse-url-browser-function 'browse-url-generic
      browse-url-generic-program "firefox")

(setenv "BROWSER" "firefox")

Exporting to PDF

I want to produce PDFs with syntax highlighting in the code. The best way to do that seems to be with the minted package, but that package shells out to pygments to do the actual work. pdflatex usually disallows shell commands; this enables that.

(setq org-latex-pdf-process
      '("xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"
        "xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"
        "xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"))

Include the minted package in all of my LaTeX exports.

(add-to-list 'org-latex-packages-alist '("" "minted"))
(setq org-latex-listings 'minted)

Exporting projects

I have a few Org project definitions that I maintain in a separate elisp file.

(load-file ".emacs.d/projects.el")

TeX configuration

I rarely write LaTeX directly any more, but I often export through it with org-mode, so I’m keeping them together.

Automatically parse the file after loading it.

(setq TeX-parse-self t)

Always use pdflatex when compiling LaTeX documents. I don’t really have any use for DVIs.

(setq TeX-PDF-mode t)

Open compiled PDFs in zathura instead of in the editor.

(add-hook 'org-mode-hook
      '(lambda ()
         (delete '("\\.pdf\\'" . default) org-file-apps)
         (add-to-list 'org-file-apps '("\\.pdf\\'" . "zathura %s"))))

Enable a minor mode for dealing with math (it adds a few useful keybindings), and always treat the current file as the “main” file. That’s intentional, since I’m usually actually in an org document.

(add-hook 'LaTeX-mode-hook
          (lambda ()
            (setq TeX-master t)))

Add links to Instapaper

I sometimes use Instapaper to store articles I want to read later. The instapaper.el library sends my URLs there.

(require 'instapaper)

(setq instapaper-username (netrc-username "")
      instapaper-password (netrc-password ""))


I maintain a blog written in Jekyll. There are plenty of command-line tools to automate creating a new post, but staying in my editor minimizes friction and encourages me to write.

This defines a hrs/new-blog-post function, which prompts the user for a title and creates a new post (with a timestamped and slugged file name) in the blog’s _posts/ directory. The new post includes appropriate YAML header information.

(defvar hrs/jekyll-posts-directory "~/documents/blog/_posts/")
(defvar hrs/jekyll-post-extension ".md")

(defun hrs/replace-whitespace-with-hyphens (s)
  (replace-regexp-in-string " " "-" s))

(defun hrs/replace-nonalphanumeric-with-whitespace (s)
  (replace-regexp-in-string "[^A-Za-z0-9 ]" " " s))

(defun hrs/remove-quotes (s)
  (replace-regexp-in-string "[\'\"]" "" s))

(defun hrs/replace-unusual-characters (title)
  "Remove quotes, downcase everything, and replace characters
that aren't alphanumeric with hyphens."
      (hrs/remove-quotes title))))))

(defun hrs/slug-for (title)
  "Given a blog post title, return a convenient URL slug.
   Downcase letters and remove special characters."
  (let ((slug (hrs/replace-unusual-characters title)))
    (while (string-match "--" slug)
      (setq slug (replace-regexp-in-string "--" "-" slug)))

(defun hrs/timestamped-slug-for (title)
  "Turn a string into a slug with a timestamp and title."
  (concat (format-time-string "%Y-%m-%d")
          (hrs/slug-for title)))

(defun hrs/jekyll-yaml-template (title)
  "Return the YAML header information appropriate for a blog
   post. Include the title, the current date, the post layout,
   and an empty list of tags."
   "title: " title "\n"
   "date: " (format-time-string "%Y-%m-%d") "\n"
   "layout: post\n"
   "# pdf_file: " (hrs/slug-for title) ".pdf\n"
   "tags: []\n"

(defun hrs/new-blog-post (title)
  "Create a new blog post in Jekyll."
  (interactive "sPost title: ")
  (let ((post (concat hrs/jekyll-posts-directory
                      (hrs/timestamped-slug-for title)
    (if (file-exists-p post)
        (find-file post)
      (find-file post)
      (insert (hrs/jekyll-yaml-template title)))))

This selects and inserts a tag:

(defun hrs/existing-blog-tags ()
  "Return a list of all the tags currently used in my blog."
  (split-string (shell-command-to-string "cd ~/documents/blog && rake tags")))

(defun hrs/insert-blog-tag ()
  "Select one of the current tags and insert it at point."
   (ido-completing-read "Insert tag at point: " (hrs/existing-blog-tags))))

Daily checklist

There are certain things I want to do regularly. I store those in a checklist. Because different things happen on different days, the checklist is an Org document generated by a Ruby script.

Running hrs/today either opens today’s existing checklist (if it exists), or renders today’s new checklist, copies it into an Org file in /tmp, and opens it.

(setq hrs/checklist-script "~/bin/daily-checklist")

(defun hrs/today-checklist-filename ()
  "The filename of today's checklist."
  (concat "/home/hrs/documents/checklists/daily-checklist-" (format-time-string "%Y-%m-%d") ".org"))

(defun hrs/today ()
  "Take a look at today's checklist."
  (let ((filename (hrs/today-checklist-filename)))
    (if (file-exists-p filename)
        (find-file filename)
        (shell-command (concat hrs/checklist-script " > " filename))
        (find-file filename)))))

Open the checklist and my TODO list side-by-side:

(defun hrs/dashboard ()

(global-set-key (kbd "C-c d") 'hrs/dashboard)

Email with mu4e

Use the evil bindings for navigation. They’re very similar to the mutt bindings, which matches my muscle memory nicely. =)

(require 'evil-mu4e)

Where’s my mail? Who am I?

I keep my mail in ~/.mail. The default maildir would be ~/Maildir, but I’d rather hide it; I don’t poke around in there manually very often.

This setting matches the paths in my mbsync configuration.

(setq mu4e-maildir "~/.mail")

I only have one context at the moment. If I had another email account, though, I’d define it in here with an additional make-mu4e-context block.

My full name is defined earlier in this configuration file.

(setq mu4e-contexts
          :name "personal"
          :match-func (lambda (msg)
                        (when msg
                          (string-prefix-p "/personal" (mu4e-message-field msg :maildir))))
          :vars '((user-mail-address . "")
                  (mu4e-trash-folder . "/personal/archive")
                  (mu4e-refile-folder . "/personal/archive")
                  (mu4e-sent-folder . "/personal/sent")
                  (mu4e-drafts-folder . "/personal/drafts")))))

Fetching new mail

I fetch my email with mbsync. I’ve also bound “o” to fetch new mail.

(setq mu4e-get-mail-command "killall --quiet mbsync; mbsync inboxes")

(define-key mu4e-headers-mode-map (kbd "o") 'mu4e-update-mail-and-index)

Rename files when moving them between directories. mbsync supposedly prefers this; I’m cargo-culting.

(setq mu4e-change-filenames-when-moving t)

Poll the server for new mail every 5 minutes.

(setq mu4e-update-interval 300)

Viewing mail

I check my email pretty often! Probably more than I should. This binds C-c m to close any other windows and open my personal inbox.

In practice, I keep an *mu4e-headers* buffer in its own frame, full-screen, on a dedicated i3 workspace.

(defun hrs/visit-inbox ()
  (mu4e~headers-jump-to-maildir "/personal/inbox"))

(global-set-key (kbd "C-c m") 'hrs/visit-inbox)

Open my inbox and sent messages folders with J-i and J-s, respectively. These are the only two folders I visit regularly enough to warrant shortcuts.

(setq mu4e-maildir-shortcuts '(("/personal/inbox" . ?i)
                               ("/personal/sent" . ?s)))

mu4e starts approximately instantaneously, so I don’t know why I’d want to reconsider quitting it.

(setq mu4e-confirm-quit nil)

Composing a new message

When I’m composing a new email, default to using the first context.

(setq mu4e-compose-context-policy 'pick-first)

Compose new messages (as with C-x m) using mu4e-user-agent.

(setq mail-user-agent 'mu4e-user-agent)

Enable Org-style tables and list manipulation.

(add-hook 'message-mode-hook 'turn-on-orgtbl)
(add-hook 'message-mode-hook 'turn-on-orgstruct++)

Check my spelling while I’m writing.

(add-hook 'mu4e-compose-mode-hook 'flyspell-mode)

Once I’ve sent an email, kill the associated buffer instead of just burying it.

(setq message-kill-buffer-on-exit t)

Reading an email

Display the sender’s email address along with their name.

(setq mu4e-view-show-addresses t)

Save attachments in my ~/downloads directory, not my home directory.

(setq mu4e-attachment-dir "~/downloads")

Hit C-c C-o to open a URL in the browser.

(define-key mu4e-view-mode-map (kbd "C-c C-o") 'mu4e~view-browse-url-from-binding)

While HTML emails are undeniably sinful, we often have to read them. That’s sometimes best done in a browser. This effectively binds a h to open the current email in my default Web browser.

(add-to-list 'mu4e-view-actions '("html in browser" . mu4e-action-view-in-browser) t)


If a message is encrypted, my reply should always be encrypted, too.

(defun hrs/encrypt-responses ()
  (let ((msg mu4e-compose-parent-message))
    (when msg
      (when (member 'encrypted (mu4e-message-field msg :flags))

(add-hook 'mu4e-compose-mode-hook 'hrs/encrypt-responses)

Sending mail over SMTP

I send my email through msmtp. It’s very fast, and I’ve already got it configured from using mutt. These settings describe how to send a message:

  • Use a sendmail program instead of sending directly from Emacs,
  • Tell msmtp to infer the correct account from the From: address,
  • Don’t add a ”-f username” flag to the msmtp command, and
  • Use /usr/bin/msmtp!
(setq message-send-mail-function 'message-send-mail-with-sendmail)
(setq message-sendmail-extra-arguments '("--read-envelope-from"))
(setq message-sendmail-f-is-evil 't)
(setq sendmail-program "msmtp")

Org integration

org-mu4e lets me store links to emails. I use this to reference emails in my TODO list while keeping my inbox empty.

(require 'org-mu4e)

When storing a link to a message in the headers view, link to the message instead of the search that resulted in that view.

(setq org-mu4e-link-query-in-headers-mode nil)

Configure BBDB with mu4e

Use BBDB to handle my address book.

(require 'bbdb-mu4e)

Don’t try to do address completion with mu4e. Use BBDB instead:

(setq mu4e-compose-complete-addresses nil)

RSS with elfeed

(use-package elfeed-org
    (setq rmh-elfeed-org-files (list "~/"))))
(use-package elfeed)

Writing prose

Look up definitions in Webster 1913

I look up definitions by hitting C-x w, which shells out to sdcv. I’ve loaded that with the (beautifully lyrical) 1913 edition of Webster’s dictionary, so these definitions are a lot of fun.

(defun hrs/dictionary-prompt ()
   (format "Word (%s): " (or (hrs/region-or-word) ""))

(defun hrs/dictionary-define-word ()
  (let* ((word (hrs/dictionary-prompt))
         (buffer-name (concat "Definition: " word)))
    (with-output-to-temp-buffer buffer-name
      (shell-command (format "sdcv -n %s" word) buffer-name))))

(define-key global-map (kbd "C-x w") 'hrs/dictionary-define-word)

Look up words in a thesaurus

Synosaurus is hooked up to wordnet to provide access to a thesaurus. Hitting C-x s searches for synonyms.

(setq-default synosaurus-backend 'synosaurus-backend-wordnet)
(add-hook 'after-init-hook #'synosaurus-mode)
(define-key global-map "\C-xs" 'synosaurus-lookup)

Editing with Markdown

Because I can’t always use org.

  • Associate .md files with GitHub-flavored Markdown.
  • I’d like spell-checking running when editing Markdown.
  • Use pandoc to render the results.
  • Leave the code block font unchanged.
(use-package markdown-mode
  :commands gfm-mode

  :mode (("\\.md$" . gfm-mode))

  (setq markdown-command "pandoc --standalone --mathjax --from=markdown")
  (add-hook 'gfm-mode-hook 'flyspell-mode)
   '(markdown-code-face ((t nil)))))

Wrap paragraphs automatically

AutoFillMode automatically wraps paragraphs, kinda like hitting M-q. I wrap a lot of paragraphs, so this automatically wraps ‘em when I’m writing text, Markdown, or Org.

(add-hook 'text-mode-hook 'turn-on-auto-fill)
(add-hook 'gfm-mode-hook 'turn-on-auto-fill)
(add-hook 'org-mode-hook 'turn-on-auto-fill)

Sometimes, though, I don’t wanna wrap text. This toggles wrapping with C-c q:

(global-set-key (kbd "C-c q") 'auto-fill-mode)

Cycle between spacing alternatives

Successive calls to cycle-spacing rotate between changing the whitespace around point to:

  • A single space,
  • No spaces, or
  • The original spacing.

Binding this to M-SPC is strictly better than the original binding of just-one-space.

(global-set-key (kbd "M-SPC") 'cycle-spacing)

Linting prose

I use proselint to check my prose for common errors. This creates a flycheck checker that runs proselint in texty buffers and displays my errors.

(require 'flycheck)

(flycheck-define-checker proselint
  "A linter for prose."
  :command ("proselint" source-inplace)
  ((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)))
  :modes (text-mode markdown-mode gfm-mode org-mode))

(add-to-list 'flycheck-checkers 'proselint)

Use flycheck in the appropriate buffers:

(add-hook 'markdown-mode-hook #'flycheck-mode)
(add-hook 'gfm-mode-hook #'flycheck-mode)
(add-hook 'text-mode-hook #'flycheck-mode)
(add-hook 'org-mode-hook #'flycheck-mode)

Enable region case modification

(put 'downcase-region 'disabled nil)
(put 'upcase-region 'disabled nil)

Quickly explore my “notes” directory with deft

(use-package deft
  :bind ("C-c n" . deft)
  :commands (deft)

  (setq deft-directory "~/documents/notes"
        deft-recursive t
        deft-use-filename-as-title t)

  (evil-set-initial-state 'deft-mode 'emacs))


Load up the assorted dired extensions.

(use-package dired-details)
(use-package dired+)

Open media with the appropriate programs.

(use-package dired-open
  (setq dired-open-extensions
        '(("pdf" . "zathura")
          ("mkv" . "vlc")
          ("mp4" . "vlc")
          ("avi" . "vlc"))))

These are the switches that get passed to ls when dired gets a list of files. We’re using:

  • l: Use the long listing format.
  • h: Use human-readable sizes.
  • v: Sort numbers naturally.
  • A: Almost all. Doesn’t include ”.” or ”..”.
(setq-default dired-listing-switches "-lhvA")

Use “j” and “k” to move around in dired.

(evil-define-key 'normal dired-mode-map (kbd "j") 'dired-next-line)
(evil-define-key 'normal dired-mode-map (kbd "k") 'dired-previous-line)

Kill buffers of files/directories that are deleted in dired.

(setq dired-clean-up-buffers-too t)

Always copy directories recursively instead of asking every time.

(setq dired-recursive-copies 'always)

Ask before recursively deleting a directory, though.

(setq dired-recursive-deletes 'top)

Open a file with an external program (that is, through xdg-open) by hitting C-c C-o.

(defun dired-xdg-open ()
  "In dired, open the file named on this line."
  (let* ((file (dired-get-filename nil t)))
    (call-process "xdg-open" nil 0 nil file)))

(define-key dired-mode-map (kbd "C-c C-o") 'dired-xdg-open)

Editing settings

Quickly visit Emacs configuration

I futz around with my dotfiles a lot. This binds C-c e to quickly open my Emacs configuration file.

(defun hrs/visit-emacs-config ()
  (find-file "~/.emacs.d/"))

(global-set-key (kbd "C-c e") 'hrs/visit-emacs-config)

Always kill current buffer

Assume that I always want to kill the current buffer when hitting C-x k.

(global-set-key (kbd "C-x k") 'hrs/kill-current-buffer)

Set up helpful

The helpful package provides, among other things, more context in Help buffers.

(use-package helpful)

(global-set-key (kbd "C-h f") #'helpful-callable)
(global-set-key (kbd "C-h v") #'helpful-variable)
(global-set-key (kbd "C-h k") #'helpful-key)

Look for executables in /usr/local/bin.

(hrs/append-to-path "/usr/local/bin")

Use company-mode everywhere

(add-hook 'after-init-hook 'global-company-mode)

Use M-/ for completion.

(global-set-key (kbd "M-/") 'company-complete-common)

Save my location within a file

Using save-place-mode saves the location of point for every file I visit. If I close the file or close the editor, then later re-open it, point will be at the last place I visited.

(save-place-mode t)

Always indent with spaces

Never use tabs. Tabs are the devil’s whitespace.

(setq-default indent-tabs-mode nil)

Install and configure which-key

which-key displays the possible completions for a long keybinding. That’s really helpful for some modes (like projectile, for example).

(use-package which-key

Configure yasnippet

I keep my snippets in ~/.emacs/snippets/text-mode, and I always want yasnippet enabled.

(setq yas-snippet-dirs '("~/.emacs.d/snippets/text-mode"))
(yas-global-mode 1)

I don’t want ido to automatically indent the snippets it inserts. Sometimes this looks pretty bad (when indenting org-mode, for example, or trying to guess at the correct indentation for Python).

(setq yas/indent-line nil)

Configure ido

(setq ido-enable-flex-matching t)
(setq ido-everywhere t)
(ido-mode 1)
(flx-ido-mode 1) ; better/faster matching
(setq ido-create-new-buffer 'always) ; don't confirm to create new buffers
(ido-vertical-mode 1)
(setq ido-vertical-define-keys 'C-n-and-C-p-only)

Use smex to handle M-x with ido


(global-set-key (kbd "M-x") 'smex)
(global-set-key (kbd "M-X") 'smex-major-mode-commands)

Switch and rebalance windows when splitting

When splitting a window, I invariably want to switch to the new window. This makes that automatic.

(defun hrs/split-window-below-and-switch ()
  "Split the window horizontally, then switch to the new pane."
  (other-window 1))

(defun hrs/split-window-right-and-switch ()
  "Split the window vertically, then switch to the new pane."
  (other-window 1))

(global-set-key (kbd "C-x 2") 'hrs/split-window-below-and-switch)
(global-set-key (kbd "C-x 3") 'hrs/split-window-right-and-switch)

Mass editing of grep results

I like the idea of mass editing grep results the same way I can edit filenames in dired. These keybindings allow me to use C-x C-q to start editing grep results and C-c C-c to stop, just like in dired.

(eval-after-load 'grep
  '(define-key grep-mode-map
    (kbd "C-x C-q") 'wgrep-change-to-wgrep-mode))

(eval-after-load 'wgrep
  '(define-key grep-mode-map
    (kbd "C-c C-c") 'wgrep-finish-edit))

(setq wgrep-auto-save-buffer t)

Use projectile everywhere


Add a bunch of engines for engine-mode

Enable engine-mode and define a few useful engines.

(require 'engine-mode)

(defengine duckduckgo
  :keybinding "d")

(defengine github
  :keybinding "g")

(defengine google

(defengine rfcs

(defengine stack-overflow
  :keybinding "s")

(defengine wikipedia
  :keybinding "w")

(defengine wiktionary

(defengine youtube

(engine-mode t)

Set custom keybindings

Just a few handy functions.

(global-set-key (kbd "C-w") 'backward-kill-word)
(global-set-key (kbd "M-o") 'other-window)

Remap when working in terminal Emacs.

(define-key input-decode-map "\e[1;2A" [S-up])