bauer: an Emacs+Nix IDE
Bauer’s Automated Unified Emacs Realm

This ORG-mode file generates an Emacs configuration. I am calling it an Emacs+Nix IDE. That is, the Emacs configuration is integrated with hardcoded Nix store paths. This provides a kind of functional Emacs configuration. The integration between Emacs and Nix comes with lots of useful side effects. The raw Org file can always be downloaded here and you can create pull requests and issues on the GitHub repo.

Table of Contents



This is the script that I use to setup all of my new machines but it’s portable enough for anyone to use.

To install, just run this command from your shell:

curl | sh

Once it’s installed, you can open Emacs at any time with this command:


If you don’t like it, it’s also very easy to uninstall. Just run:

nix-env -e bauer
nix-collect-garbage -d


After you’ve installed it, it’s easy to make changes. By default, the configuration lives in ~/.local/share/bauer. To make changes just follow this process,

cd ~/.local/share/bauer
./result/run ./

The last line will spawn an Emacs frame in the Git repo. Anything in that file can be change. Once you’ve made a change, just run M-x dev-restart to rebuild the configuration and restart Emacs. Make any changes you want to the file or any of the files in the site-lisp folder. Make sure you commit your changes afterward by typing C-c p v, then c-ac. If you have already forked this repo on GitHub, you can add it as a remote by typing Mg followed by your GitHub username within Magit. To push to it, just type Pr then find your username in the list and press enter. Pull requests are always welcome through GitHub!

w/o Nix

You can also use this configuration without Nix. This just gives you the Emacs configuration without any of the Nix integrations. All of the binaries will have to be provided externally. To get started, run the following:

mkdir -p ~/.emacs.d
git clone ~/.emacs.d/bauer

Then, add the following to your Emacs init file:

(load (expand-file-name "settings.el" user-emacs-directory) t)
(defvar bauer-dir (expand-file-name ".nixpkgs" (getenv "HOME")))
(defvar bauer-org (expand-file-name "" bauer-dir))
(add-to-list 'load-path (expand-file-name "site-lisp" bauer-dir))
(unless (file-exists-p (expand-file-name "README.el" bauer-dir))
  (let ((default-directory bauer-dir))
    (autoload 'org-babel-tangle-file "ob-tangle")
    (org-babel-tangle-file bauer-org "README.el" "emacs-lisp")))
(load (expand-file-name "README.el" bauer-dir) t)

Emacs Init file

This is the main part of the IDE. It is written in Emacs lisp and will be loaded every time Emacs is started.

Increase GC

Increasing GC is a common way to speed up Emacs. gc-cons-threshold sets at what point Emacs should invoke its garbage collector Some people set it to a really larger number permanently. This works well until the garbage is actually collected (then you have to wait a long time). I’ve decided to just set it temporarily to a large number so we only garbage collect once on startup. After that we reset it to the standard value. Read @bling’s post for more info on this.

(setq gc-cons-threshold most-positive-fixnum)
(add-hook 'after-init-hook
          (lambda ()
            (setq gc-cons-threshold
                  (car (get 'gc-cons-threshold 'standard-value)))))

Autoloads & Misc.

Setup some initial aliases for Emacs. These give us an easy way to use these functions without actually require’ing them. Ideally, Emacs should pick these up through the automatic autoloading method, but that sometimes conflicts with the compiling phases used later.

  (autoload 'package-installed-p "package")
  (autoload 'use-package-autoload-keymap "use-package")
  (autoload 'pcomplete-arg   "pcomplete")
  (autoload 'pcomplete--here "pcomplete"))

Now we are going to setup some basic Emacs GUI elements. Under normal circumstances, we want GUI elements hidden from the user so that we only see the text buffer. This gives us a minimalist look that works well with the theme. We will end up disabling tool-bar-mode, scroll-bar-mode, blink-cursor-mode, and menu-bar-mode here. The one exception to this is when we are running macOS where the system provides a builtin menubar system that Emacs can use.

(when window-system
  (tool-bar-mode -1)
  (scroll-bar-mode -1)
  (blink-cursor-mode -1))

(when (not (memq window-system '(mac ns)))
  (menu-bar-mode -1))

When we are within a terminal we want to be able to use the mouse, so xterm-mouse-mode is enabled here.

(when (not window-system)
  (xterm-mouse-mode 1))

Custom config

set-defaults provides an easy way to override the default custom files. This means that when you customize a variable it will appear as ‘standard’ even though it’s not what the package originally defined as the default. This is useful for an Emacs distribution to provide better defaults while still letting the user override them. Look through the lispdoc of the package for documentation on how this works. Eventually, this will be added to MELPA for use in other Emacs distributions.

(require 'set-defaults)

Better defaults

These are some better defaults for Emacs. They shouldn’t require any packages to be installed to work (those go in use-package). In addition, they should take almost no time to run (meaning they probably shouldn’t have custom init hooks). The format of arguments to set-defaults is identical to the one used by custom-set-variables.

 '(TeX-auto-save t)
 '(TeX-auto-untabify t)
 '(TeX-electric-escape t)
 '(TeX-engine 'xetex)
 '(TeX-parse-self t)
 '(ad-redefinition-action 'accept)
 '(apropos-do-all t)
 '(async-shell-command-buffer 'new-buffer)
 '(auth-source-save-behavior t)
 '(auto-revert-check-vc-info t)
 '(auto-revert-verbose nil)
 '(auto-save-visited-file-name t)
 '(ange-ftp-default-user t)
 '(auto-window-vscroll nil)
 '(backward-delete-char-untabify-method 'hungry)
 '(backup-directory-alist `(("." . ,(expand-file-name "backup"
 '(checkdoc-spellcheck-documentation-flag t)
 '(company-auto-complete (lambda () (and (company-tooltip-visible-p)
   '(not save-buffer
 '(company-require-match nil)
 '(company-selection-wrap-around t)
 '(company-backends '(company-elisp
                       company-keywords company-dabbrev
 '(comint-input-ignoredups t)
 '(comint-process-echoes t)
 '(comint-prompt-read-only t)
 '(comint-scroll-show-maximum-output nil)
 '(compilation-always-kill t)
 '(compilation-ask-about-save nil)
 '(compilation-environment '("TERM=xterm-256color"))
 '(compilation-skip-threshold 2)
 '(completion-styles '(basic partial-completion emacs22 substring))
 '(completions-cycle-threshold t)
 '(completions-format 'vertical)
 '(counsel-find-file-at-point t)
 '(counsel-mode-override-describe-bindings t)
 '(create-lockfiles nil)
 '(cursor-in-non-selected-windows nil)
 '(custom-buffer-done-kill t)
 '(custom-file (expand-file-name "settings.el" user-emacs-directory))
 '(custom-safe-themes t)
 '(custom-search-field nil)
 '(delete-by-moving-to-trash t)
 '(delete-old-versions t)
 '(dired-auto-revert-buffer t)
 '(dired-dwim-target t)
 '(dired-hide-details-hide-symlink-targets nil)
 '(dired-listing-switches "-alhv")
 '(dired-omit-files "^\\.\\|^#.*#$")
 '(dired-omit-verbose nil)
 '(dired-recursive-copies 'always)
 '(dired-recursive-deletes 'always)
 '(dired-subtree-line-prefix " ")
 '(disabled-command-function nil)
 '(display-buffer-reuse-frames t)
 '(dtrt-indent-verbosity 0)
 '(echo-keystrokes 0)
 '(enable-recursive-minibuffers t)
 '(erc-autojoin-timing 'ident)
 '(erc-insert-timestamp-function 'erc-insert-timestamp-left)
 '(erc-interpret-mirc-color t)
 '(erc-join-buffer 'bury)
 '(erc-kill-buffer-on-part t)
 '(erc-kill-queries-on-quit t)
 '(erc-kill-server-buffer-on-quit t)
 '(erc-log-write-after-send t)
 '(erc-lurker-hide-list '("JOIN" "NICK" "PART" "QUIT" "MODE"))
 '(erc-prompt (lambda nil (concat "[" (buffer-name) "]")))
 '(erc-prompt-for-password nil)
 '(erc-query-display 'buffer)
 '(erc-rename-buffers t)
 '(erc-timestamp-format "%H:%M ")
 '(erc-timestamp-only-if-changed-flag nil)
 '(erc-try-new-nick-p nil)
 '(erc-user-full-name 'user-full-name)
 '(eshell-banner-message "")
 '(eshell-cmpl-autolist t)
 '(eshell-cmpl-cycle-completions nil)
 '(eshell-cmpl-cycle-cutoff-length 2)
 '(eshell-cmpl-ignore-case t)
 '(eshell-cp-overwrite-files nil)
 '(eshell-default-target-is-dot t)
 '(eshell-destroy-buffer-when-process-dies t)
 '(eshell-hist-ignoredups t)
 '(eshell-history-size nil)
 '(eshell-list-files-after-cd t)
 '(eshell-ls-dired-initial-args '("-h"))
 '(eshell-ls-initial-args "-h")
 '(eshell-review-quick-commands t)
   (lambda () (concat (when (tramp-tramp-file-p default-directory)
                        (concat (tramp-file-name-user
                                 (tramp-dissect-file-name default-directory))
                                " "))
                      (let ((dir (eshell/pwd)))
                        (if (string= dir (getenv "HOME")) "~"
                          (let ((dirname (file-name-nondirectory dir)))
                            (if (string= dirname "") "/" dirname))))
                      (if (= (user-uid) 0) " # " " $ "))))
 '(eval-expression-print-level nil)
 '(expand-region-contract-fast-key "j")
   '("-c" "export EMACS= INSIDE_EMACS=; stty echo; shell"))
 '(explicit-shell-file-name "/bin/bash")
 '(find-ls-option '("-print0 | xargs -P4 -0 ls -ldN" . "-ldN"))
 '(find-ls-subdir-switches "-ldN")
 '(flycheck-global-modes '(not erc-mode
 '(flycheck-standard-error-navigation nil)
 '(flyspell-highlight-properties nil)
 '(flyspell-issue-welcome-flag nil)
 '(frame-title-format '(:eval (if (buffer-file-name)
                                  (abbreviate-file-name (buffer-file-name))
 '(gnuplot-inline-image-mode 'dedicated)
 '(haskell-ask-also-kill-buffers nil)
 '(haskell-interactive-mode-scroll-to-bottom t)
 '(haskell-process-load-or-reload-prompt t)
 '(haskell-process-prompt-restart-on-cabal-change nil)
 '(haskell-process-show-debug-tips nil)
 '(haskell-process-suggest-haskell-docs-imports t)
 '(haskell-process-suggest-hoogle-imports t)
 '(haskell-process-suggest-remove-import-lines t)
 '(haskell-process-suggest-restart nil)
 '(haskell-process-use-presentation-mode nil)
 '(help-window-select t)
 '(hideshowvis-ignore-same-line nil)
 '(highlight-nonselected-windows nil)
 '(history-delete-duplicates t)
 '(ibuffer-default-display-maybe-show-predicates t)
 '(ibuffer-expert t)
 '(ibuffer-formats '((mark modified read-only " " (name 16 -1) " "
                           (size 6 -1 :right) " " (mode 16 16) " " filename)
                     (mark " " (name 16 -1) " " filename)))
 '(ibuffer-maybe-show-regexps nil)
 '(ibuffer-show-empty-filter-groups nil)
 '(ibuffer-shrink-to-minimum-size t)
 '(ibuffer-use-other-window t)
 '(iedit-toggle-key-default nil)
 '(imenu-auto-rescan t)
 '(indicate-empty-lines t)
 '(inhibit-startup-echo-area-message t)
 '(inhibit-startup-screen t)
 '(initial-scratch-message "")
 '(initial-major-mode 'fundamental-mode)
 '(ispell-extra-args '("--sug-mode=ultra"))
 '(ispell-quietly t)
 '(ispell-silently-savep t)
 '(jit-lock-defer-time 0.01)
 '(js2-mode-show-parse-errors nil)
 '(js2-mode-show-strict-warnings nil)
 '(js2-strict-missing-semi-warning nil)
 '(kill-do-not-save-duplicates t)
 '(kill-whole-line t)
 '(load-prefer-newer t)
 '(mac-allow-anti-aliasing t)
 '(mac-command-key-is-meta t)
 '(mac-command-modifier 'meta)
 '(mac-frame-tabbing t)
 '(mac-option-key-is-meta nil)
 '(mac-option-modifier 'super)
 '(mac-right-option-modifier nil)
 '(mac-system-move-file-to-trash-use-finder t)
 '(magit-clone-set-remote\.pushDefault t)
 '(magit-diff-options nil)
 '(magit-ediff-dwim-show-on-hunks t)
 '(magit-highlight-trailing-whitespace nil)
 '(magit-highlight-whitespace nil)
 '(magit-log-auto-more t)
 '(magit-no-confirm t)
 '(magit-process-popup-time 15)
 '(magit-push-always-verify nil)
 '(magit-save-repository-buffers 'dontask)
 '(magit-stage-all-confirm nil)
 '(magit-unstage-all-confirm nil)
 '(mmm-global-mode 'buffers-with-submode-classes)
 '(mmm-submode-decoration-level 2)
   '(read-only t cursor-intangible t face minibuffer-prompt))
 '(next-error-recenter t)
 '(notmuch-show-logo nil)
 '(nrepl-log-messages t)
 '(ns-function-modifier 'hyper)
 '(ns-pop-up-frames nil)
 '(nsm-save-host-names t)
 '(nxml-sexp-element-flag t)
 '(nxml-slash-auto-complete-flag t)
 '(org-confirm-babel-evaluate nil)
 '(org-edit-src-turn-on-auto-save t)
 '(org-export-with-toc nil)
 '(org-html-htmlize-output-type (quote css))
 '(org-latex-listings (quote minted))
 ;; '(org-list-allow-alphabetical t)
 '(org-log-done 'time)
 '(org-special-ctrl-a/e t)
 '(org-support-shift-select t)
 '(package-archives '(("melpa" . "")
                      ("org" . "")
                      ("gnu" . "")))
 '(pcomplete-compare-entries-function 'file-newer-than-file-p)
 '(projectile-globally-ignored-files '(".DS_Store" "TAGS"))
 '(projectile-ignored-project-function 'file-remote-p)
 '(projectile-mode-line '(:eval
                          (if (and (ignore-errors (projectile-project-p))
                                   (not (file-remote-p default-directory)))
                              (format " Projectile[%s]"
                                      (projectile-project-name)) "")))
 '(projectile-switch-project-action 'projectile-dired)
 '(projectile-verbose nil)
 '(proof-auto-action-when-deactivating-scripting 'retract)
 '(proof-autosend-enable nil)
 '(proof-electric-terminator-enable t)
 '(proof-fast-process-buffer nil)
 '(proof-script-fly-past-comments t)
 '(proof-shell-fiddle-frames nil)
 '(proof-splash-enable nil)
 '(proof-sticky-errors t)
 '(proof-tidy-response t)
 '(reb-re-syntax 'string)
 '(resize-mini-windows t)
 '(ring-bell-function 'ignore)
 '(ruby-insert-encoding-magic-comment nil)
 ;; '(same-window-buffer-names '("*eshell*"
 ;;                              "*shell*"
 ;;                              "*mail*"
 ;;                              "*inferior-lisp*"
 ;;                              "*ielm*"
 ;;                              "*scheme*"))
 '(save-abbrevs 'silently)
 '(save-interprogram-paste-before-kill t)
 '(savehist-additional-variables '(search-ring
 '(savehist-autosave-interval 60)
 '(savehist-ignored-variables '(load-history
                                flyspell-auto-correct-ring kill-ring))
 '(scroll-preserve-screen-position 'always)
 '(scroll-conservatively 101)
 '(sentence-end-double-space nil)
 '(set-mark-command-repeat-pop t)
 '(shell-completion-execonly nil)
 '(shell-input-autoexpand nil)
 '(sh-learn-basic-offset t)
 ;; '(show-paren-delay 0)
 '(sp-autoskip-closing-pair 'always)
 '(sp-highlight-pair-overlay nil)
 '(switch-to-buffer-preserve-window-point t)
 '(tab-always-indent 'complete)
 '(term-input-autoexpand t)
 '(term-input-ignoredups t)
 '(term-input-ring-file-name t)
 '(text-quoting-style 'quote)
 '(tramp-default-proxies-alist '(((regexp-quote (system-name)) nil nil)
                                 (nil "\\`root\\'" "/ssh:%h:")
                                 (".*" "\\`root\\'" "/ssh:%h:")))
 ;; '(truncate-lines nil)
 '(tls-checktrust t)
 '(undo-limit 800000)
 '(uniquify-buffer-name-style 'forward)
 '(uniquify-ignore-buffers-re "^\\*")
 '(uniquify-separator "/")
 '(use-dialog-box nil)
 '(use-file-dialog nil)
 '(use-package-always-defer t)
 ;; '(use-package-enable-imenu-support t)
 '(version-control t)
 '(vc-allow-async-revert t)
 '(vc-command-messages t)
 '(vc-git-diff-switches '("-w" "-U3"))
 '(vc-follow-symlinks nil)
   (concat "\\(\\(\\`"
 '(view-read-only t)
 '(view-inhibit-help-message t)
 '(visible-bell t)
 '(woman-imenu t)
 '(x-stretch-cursor t)
 '(which-key-lighter "")
 '(whitespace-action '(cleanup auto-cleanup))
 '(whitespace-style '(face trailing lines space-before-tab empty lines-style))

Site paths

Now, pull in generated paths from site-paths.el. Nix will generate this file automatically for us and different Emacs variables will be set to their Nix store derivation paths. Everything should work fine if you don’t have this available, though. If you are in Emacs and already have the IDE installed you can inspect this file by typing C-h C-l site-paths. It will look similar to a settings.el file where each line corresponds to a customizable variable. Unlike settings.el, each entry is path in the Nix store and we verify it exists before setting it.

(load "site-paths" t)

Set environment

set-envs is provided by set-defaults. We can use it like custom-set-variables, just it calls setenv instead of setq. All of these entries correspond to environment variables that we want to always be set in the Emacs process.

 '("EDITOR" "emacsclient")
 '("LANG" "en_US.UTF-8")
 '("LC_ALL" "en_US.UTF-8")
 '("PAGER" "cat")
 '("PS1" "\\W > ")

Load custom file

This file allows users to override the above defaults. This will mean you can use custom as you normally would in vanilla Emacs.

(load custom-file t)

Setup use-package

use-package is an Emacs package by John Weigley allowing users to easily configure other Emacs packages. It’s quite useful and it will be used extensively in this project.

Now to get use-package we will require package.el and initialize it if site-paths is not setup (meaning we’re outside the Nix expression). Because site-paths should be available (unless you don’t have Nix), we can skip this step. All of this is marked ‘eval-and-compile’ to make sure the compiler picks it up on build phase.

So, there are basically two modes for using this configuration. One when packages are installed externally (through Nix) and another where they are installed internally. This is captured in the variable ‘needs-package-init’ which will be t when we want to use the builtin package.el and will be nli when we want to just assume everything is available.

  (setq needs-package-init (and (not (locate-library "site-paths"))
                                (not (and (boundp 'use-package-list--is-running)

First handle using package.el. We will do all of the work of bootstrapping here including running package-initialize and ensuring use-package and delight are installed.

(when needs-package-init
  (require 'package)
  (unless (package-installed-p 'use-package)
    (package-install 'use-package))
  (unless (package-installed-p 'delight)
    (package-install 'delight)))

Actually require use-package,

  (require 'use-package)
  (require 'delight)
  (require 'bind-key)

  ;; remove once PR jwiegley/use-package#633 is merged
  (defun use-package-normalize-binder-override (name keyword args)
    (let ((arg args)
      (while arg
        (let ((x (car arg)))
           ;; (KEY . COMMAND)
           ((and (consp x)
                 (or (stringp (car x))
                     (vectorp (car x)))
                 (or (use-package-recognize-function (cdr x) t #'stringp)))
            (setq args* (nconc args* (list x)))
            (setq arg (cdr arg)))
           ;; KEYWORD
           ;;   :map KEYMAP
           ;;   :prefix-docstring STRING
           ;;   :prefix-map SYMBOL
           ;;   :prefix STRING
           ;;   :filter SEXP
           ;;   :menu-name STRING
           ;;   :package SYMBOL
           ((or (and (eq x :map) (symbolp (cadr arg)))
                (and (eq x :prefix) (stringp (cadr arg)))
                (and (eq x :prefix-map) (symbolp (cadr arg)))
                (and (eq x :prefix-docstring) (stringp (cadr arg)))
                (eq x :filter)
                (and (eq x :menu-name) (stringp (cadr arg)))
                (and (eq x :package) (symbolp (cadr arg))))
            (setq args* (nconc args* (list x (cadr arg))))
            (setq arg (cddr arg)))
           ((listp x)
            (setq args*
                  (nconc args* (use-package-normalize-binder name keyword x)))
            (setq arg (cdr arg)))
            ;; Error!
             (concat (symbol-name name)
                     " wants arguments acceptable to the `bind-keys' macro,"
                     " or a list of such values"))))))

  (advice-add 'use-package-normalize-binder
              :override 'use-package-normalize-binder-override))

Now let’s handle the case where all of the packages are already provided. Basically, we’ll prevent use-package from running ‘ensure’ on anything.

  (setq use-package-always-ensure needs-package-init)
  ;; (setq use-package-expand-minimally (not needs-package-init))
  (when (not needs-package-init)
    (setq use-package-ensure-function 'ignore
          package-enable-at-startup nil)))

Key bindings

Using bind-key, setup some simple key bindings. None of these should overwrite Emacs’ default keybindings. Also, they should only require vanilla Emacs to work (non-vanilla Emacs key bindings should be put in their use-package declaration). These are meant to all be as close to vanilla Emacs as possible. I try to avoid extremely specific key binds here.

What is overwritten can be seen with M-x describe-personal-keybindings. The goal is to overwrite as little as possible. When it is necessary to overwrite Emacs keybinds, documentation on why should be provided.

First we include a library that provides some nice helper functions that will be used as key bindings.

(require 'bauer)

Now we will call bind-keys and give it keys to bind and what function to run when those keys are pressed. Note on syntax of bind-keys: if you are unfamiliar with how Emacs key binding works, you should read through this article.

   ("C-c C-u" . rename-uniquely)
   ("C-x ~" . (lambda () (interactive) (find-file "~")))
   ("C-x /" . (lambda () (interactive) (find-file "/")))
   ("C-c C-o" . browse-url-at-point)
   ("H-l" . browse-url-at-point)
   ("C-x 5 3" . iconify-frame)
   ("C-x 5 4" . toggle-frame-fullscreen)
   ("s-SPC" . cycle-spacing)
   ("C-c w w" . whitespace-mode)
   ("<C-return>" . other-window)
   ("s-o" . other-window)
   ("C-z" . delete-other-windows)
   ("M-g l" . goto-line)
   ("<C-M-backspace>" . backward-kill-sexp)
   ("C-x t" . toggle-truncate-lines)
   ("C-x v H" . vc-region-history)
   ("C-c SPC" . just-one-space)
   ("C-c f" . flush-lines)
   ("C-c o" . customize-option)
   ("C-c O" . customize-group)
   ("C-c F" . customize-face)
   ("C-c q" . fill-region)
   ("C-c s" . replace-string)
   ("C-c u" . rename-uniquely)
   ("C-c z" . clean-buffer-list)
   ("C-c =" . count-matches)
   ("C-c ;" . comment-or-uncomment-region)
   ("M-+" . text-scale-increase)
   ("M-_" . text-scale-decrease)

   ("H-c" . compile)
   ("s-1" . other-frame)
   ("<s-return>" . toggle-frame-fullscreen)

   ("s-C-<left>" . enlarge-window-horizontally)
   ("s-C-<right>" . shrink-window-horizontally)
   ("s-C-<down>" . shrink-window)
   ("s-C-<up>" . enlarge-window)

   ("<S-s-up>" . shrink-window)
   ("<S-s-down>" . enlarge-window)

   ("<s-down>" . windmove-down)
   ("<s-up>" . windmove-up)
   ("<s-left>" . windmove-left)
   ("<s-right>" . windmove-right)

   ("C-c [" . align-regexp)
   ("M-s d" . find-grep-dired)
   ("M-s F" . find-grep)
   ("M-s G" . grep)
   ("s-/" . comment-or-uncomment-region)

   ("C-x M-p" . (lambda () (interactive) (save-excursion (other-window 1)

   ("C-M--" . (lambda () (interactive) (update-font-size -1 t)))
   ("C-M-=" . (lambda () (interactive) (update-font-size 1 t)))
   ("C-M-0" . (lambda () (interactive) (update-font-size 12 nil)))

   ("M-n" . next-error)
   ("M-p" . previous-error)

;;   ("s-r" . revert-buffer)
;;   ("s-u" . revert-buffer)

  ("s-c m" . man)
  ("s-c i" . imenu)
  ("s-i" . imenu)

   ("C-c m b" . eval-buffer)
   ("C-c m e" . eval-last-sexp)
   ("C-c m i" . eval-expression)
   ("C-c m d" . eval-defun)
   ("C-c m n" . eval-print-last-sexp)
   ("C-c m r" . eval-region))

   :package view
   :map view-mode-map
   ("n" . next-line)
   ("p" . previous-line)
   ("j" . next-line)
   ("k" . previous-line)
   ("l" . forward-char)
   ("f" . forward-char)
   ("b" . backward-char)
   ("e" . end-of-line)
   ("a" . beginning-of-line))

   ;; :package help
   ;; :map help-map
   ;; ("C-v" . find-variable)
   ;; ("C-k" . find-function-on-key)
   ;; ("C-f" . find-function)
   ;; ("C-l" . find-library)

   :package iso-transl
   :map iso-transl-ctl-x-8-map
   ("' /" . "′")
   ("\" /" . "″")
   ("\" (" . "“")
   ("\" )" . "”")
   ("' (" . "‘" )
   ("' )" . "’" )
   ("4 < -" . "←")
   ("4 - >" . "→")
   ("4 b" . "←")
   ("4 f" . "→")
   ("4 p" . "↑")
   ("4 n" . "↓")
   ("<down>" . "⇓")
   ("<S-down>" . "↓")
   ("<left>" . "⇐")
   ("<S-left>" . "←")
   ("<right>" . "⇒")
   ("<S-right>" . "→")
   ("<up>" . "⇑")
   ("<S-up>" .  "↑")
   ("," .  "…"))

   :prefix-map bauer-git
   :prefix "s-g"
   ("l" . magit-clone)

   :prefix-map bauer-help
   :prefix "s-h"
   ("k" . describe-personal-keybindings)
   ("p" . ffap))

Setup installer

Installer provides installation and upgrading functionality. You can upgrade the IDE at any time by typing M-x upgrade from within Emacs. You may have to restart Emacs for the upgrade to take place. See installer.el for documentation.

(require 'installer nil t)


Each of these entries are use-package calls that will both install and load the package for us. The most important are listed first in “Essentials”. “Built-in“ Emacs packages are also configured. Next comes the “Programming Language” modes. Finally, we list some miscellaneous modes.

This is an alphabetized listing of all Emacs packages needed by the IDE. To resort, go to one of the package group headings and type C-c ^ a.


These are the best and most useful modes available to us in Emacs world.

  • aggressive-indent


    Automatically indent code as you type. Only enabled for Lisp currently.

    (use-package aggressive-indent
      :hook ((emacs-lisp-mode
              slime-repl-mode) . aggressive-indent-mode))
  • Apropospriate Theme


    This is the theme I use and it works well for this configuration. It is dark with high contrast. We will only enable it when we are running with GUI Emacs.

    (use-package apropospriate-theme
      :if window-system
      :config (load-theme 'apropospriate-dark t))
  • Company


    Company provides completions in Emacs. Activate them by pressing C-M-i.

    (load "company-autoloads" t t)
    (use-package company
      :commands global-company-mode
      (defun company-complete-common-or-cycle-backward ()
        "Complete common prefix or cycle backward."
        (company-complete-common-or-cycle -1))
      :bind (:map company-mode-map
                   ("C-M-i" . company-complete-common-or-cycle)
              :map company-active-map
                  ("RET" . company-complete-selection)
                  ([return] . company-complete-selection)
                  ("C-j" . company-complete-selection)
                  ("TAB" . company-complete-common-or-cycle)
                  ("<tab>" . company-complete-common-or-cycle)
                  ("S-TAB" . company-complete-common-or-cycle-backward)
                  ("<backtab>" . company-complete-common-or-cycle-backward)
                  ("C-n" . company-select-next)
                  ("C-p" . company-select-previous)
                  ("C-/" . company-search-candidates)
                  ("C-M-/" . company-filter-candidates)
                  ("C-d" . company-show-doc-buffer)
      :hook ((minibuffer-setup . company-mode)
             (minibuffer-setup . (lambda ()
                                   (setq-local company-frontends
             (after-init . global-company-mode))
      (advice-add 'completion-at-point :override 'company-complete-common-or-cycle)
    (use-package readline-complete
      :commands company-readline
      :hook (rlc-no-readline . (lambda () (company-mode -1)))
      :init (push 'company-readline company-backends))
    (use-package company-irony
      :commands company-irony
      :init (add-to-list 'company-backends 'company-irony))
    (use-package company-restclient
      :commands company-restclient
      :init (add-to-list 'company-backends 'company-restclient))
    (use-package company-anaconda
      :commands company-anaconda
      (add-to-list 'company-backends '(company-anaconda :with company-capf)))
    (use-package company-jedi
      :commands company-jedi
      :hook (python-mode . (lambda ()
                               (add-to-list 'company-backends 'company-jedi))))
    (use-package company-tern
      :commands company-jedi
      :init (add-to-list 'company-backends 'company-jedi))
    (use-package company-ghc
      :commands company-ghc
      :init (add-to-list 'company-backends 'company-ghc))
    (use-package company-auctex
      :commands (company-auctex company-auctext-labels
                                company-auctest-bibs company-auctex-macros
      (add-to-list 'company-backends 'company-auctex-labels)
      (add-to-list 'company-backends 'company-auctex-bibs)
      (add-to-list 'company-backends
      (autoload 'company-web-html "company-web-html")
      (autoload 'company-web-jade "company-web-jade")
      (autoload 'company-web-slim "company-web-slim")
    (use-package company-web
      (add-to-list 'company-backends 'company-web-html)
      (add-to-list 'company-backends 'company-web-jade)
      (add-to-list 'company-backends 'company-web-slim))
    (use-package company-math
      :hook (TeX-mode . (lambda ()
                            '((company-math-symbols-latex company-latex-commands))
  • Counsel


    Counsel provides a better selection experience to the default Emacs.

    Counsel is only enabled on non-Windows systems. This is due to an issue in counsel-find-file, see for more info.

    (use-package counsel
        :commands (counsel-mode counsel-descbinds counsel-grep-or-swiper)
        ;; counsel doesn’t work well with windows drives
        ;; see
        ;; :if (not (string= system-type "windows-nt"))
        :bind* (([remap execute-extended-command] . counsel-M-x)
                ;; ("s-c s-f" . counsel-find-file)
                ;; ([remap find-file] . counsel-find-file)
                ([remap find-library] . counsel-find-library)
                ;; ([remap describe-function] . counsel-describe-function)
                ;; ([remap describe-variable] . counsel-describe-variable)
                ([remap describe-bindings]  . counsel-descbinds)
                ([remap describe-face]  . counsel-describe-faces)
                ([remap list-faces-display] . counsel-faces)
                ([remap imenu] . counsel-imenu)
                ([remap load-library] . counsel-load-library)
                ([remap load-theme] . counsel-load-theme)
                ([remap yank-pop] . counsel-yank-pop)
                ([remap info-lookup-symbol] . counsel-info-lookup-symbol)
                ([remap pop-to-mark-command] . counsel-mark-ring)
                ([remap bookmark-jump] . counsel-bookmark)
                ("C-c j" . counsel-git-grep)
                ("C-c k" . counsel-rg)
                ("C-x l" . counsel-locate)
                ("M-y" . counsel-yank-pop)
                ("C-c i 8" . counsel-unicode-char)
                :map help-map
                ("C-v" . counsel-find-symbol)
                ("C-k" . counsel-find-function-on-key)
                ;; ("C-f" . counsel-find-function)
                ("C-l" . counsel-find-library)
    • ivy
      (use-package ivy
        :bind (([remap list-buffers] . ivy-switch-buffer)
               ([remap switch-to-buffer] . ivy-switch-buffer)
               ([remap switch-to-buffer-other-window] .
               :package ivy
               :map ivy-minibuffer-map
               ("<escape>" . abort-recursive-edit))
        (defvar projectile-completion-system)
        (defvar magit-completing-read-function)
        (defvar projector-completion-system)
        (setq projectile-completion-system 'ivy
              magit-completing-read-function 'ivy-completing-read
              ;; completing-read-function 'ivy-completing-read
              ;; completion-in-region-function 'ivy-completion-in-region
        :commands (ivy-completing-read ivy-completion-in-region))
  • diff-hl


    This mode provides indicators at the right fringe of the Emacs buffer. These indications show where a file has been edited from the last Git commit.

    (use-package diff-hl
      :bind (:package diff-hl
             :map diff-hl-mode-map
                  ("<left-fringe> <mouse-1>" . diff-hl-diff-goto-hunk))
      :hook ((prog-mode . diff-hl-mode)
             (vc-dir-mode . diff-hl-mode)
             (dired-mode . diff-hl-dir-mode)
             (magit-post-refresh . diff-hl-magit-post-refresh)
             (org-mode . diff-hl-mode)))
  • dtrt-indent


    This mode will try to

    (use-package dtrt-indent
      :hook (prog-mode . dtrt-indent-mode))
  • Emacs shell

    Emacs shell provides . Run eshell by typing C-c e or M-x eshell.

    (use-package eshell
      :ensure nil
      :bind (("C-c M-t" . eshell)
             ("C-c x" . eshell)
             ("C-c e" . eshell))
      :hook ((eshell-mode . eshell-read-history))
      (defun pcomplete/sudo ()
        (let ((prec (pcomplete-arg 'last -1)))
          (cond ((string= "sudo" prec)
                 (while (pcomplete-here*
                         (funcall pcomplete-command-completion-function)
                         (pcomplete-arg 'last) t))))))
      (use-package em-rebind
        :ensure nil
        ;; TODO move this back to customize
        (setq eshell-rebind-keys-alist
              '(([(control 97)] . eshell-bol)
                ([home] . eshell-bol)
                ([(control 100)] . eshell-delchar-or-maybe-eof)
                ([backspace] . eshell-delete-backward-char)
                ([delete] . eshell-delete-backward-char)
                ([(control 119)] . backward-kill-word)
                ([(control 117)] . eshell-kill-input)
                ([tab] . completion-at-point)
                ([(control 101)] . (lambda () (interactive) (end-of-line)))))
      ;; TODO move this back to customize
      (setq eshell-modules-list
    (use-package esh-autosuggest
      :commands esh-autosuggest
      :hook (eshell-mode . (lambda ()
                             (add-to-list 'company-backends
    • esh-help
      (use-package esh-help
        :hook (eshell-mode . (lambda ()
                               (autoload 'esh-help-eldoc-command "esh-help")
                               (setq-local eldoc-documentation-function
    • em-dired
      (autoload 'em-dired-new "em-dired")
      (use-package em-dired
        :ensure nil
        :bind (:package dired
               :map dired-mode-map
                    ("e" . em-dired))
        :hook (eshell-mode . em-dired-mode)
        (advice-add 'eshell :before 'em-dired-new))
  • ESUP


    emacs-init-time gives good readings for Emacs startup time. Currently my emacs-init-time is 4.5 seconds. It fluctuates based on what’s been enabled but I aim to never let it go above 5. This is good but it includes some things we don’t have control over (window system and Emacs C internals).

    ESUP provides good info on what is taking a long time during startup. ESUP is a startup profiler for Emacs. I’ve provided “startup-profile” so that you can just profile what is in “default.el” (this script) and not any other miscellaneous scripts you have around. This will be part of our effort to get quick startup times. Slowdowns happen for various reasons but right now autorevert, apropospriate, and flycheck are the biggest offenders.

    (use-package esup
      :commands esup
      (defun startup-profile ()
        (esup (locate-library "default"))))
  • Flycheck


    Flycheck will annotate code with errors from the compiler or interpreter. It supports many languages and give us a lot of features right out of the box.

    (use-package flycheck
      :hook (prog-mode . flycheck-mode))
  • Gnus


    Gnus is an infamous email client and news reader.

    (use-package gnus
      :ensure nil
      :commands gnus
      :hook ((gnus-group-mode . gnus-topic-mode)
             (dired-mode . turn-on-gnus-dired-mode)))
  • God Mode


    God Mode makes it easier to type Emacs shortcuts involving lots of modifier keys. Activate it by pressing Escape (Notice “God” at the bottom of the screen). You no longer have to press and hold the control key!

    Note that god-mode overwrites escape key. This can cause some issues for certain Emacs keybinds.

    (use-package god-mode
      :bind (("<escape>" . god-local-mode)))
  • Hippie Expand

    Hippie provides dynamic expansions. Try it out by pressing M-/.

    (use-package hippie-exp
      :ensure nil
      :bind* (("M-/" . hippie-expand)
              ("s-?" . hippie-expand-line))
      :hook ((emacs-lisp-mode ielm-mode) .
             (lambda ()
               (setq-local hippie-expand-try-functions-list
                    (append '(try-complete-lisp-symbol-partially
  • Magit


    Magit is a Git porcelain for Emacs. All of the features from the Git command line are available in an intuitive Emacs buffer.

    (use-package git-commit
      :hook ((git-commit-mode . flyspell-mode)
             (git-commit-mode . git-commit-save-message)
             (git-commit-mode . turn-on-auto-fill)))
      (autoload 'magit-toplevel "magit")
      (autoload 'magit-read-string-ns "magit")
      (autoload 'magit-get "magit")
      (autoload 'magit-define-popup-action "magit")
      (autoload 'magit-remote-arguments "magit"))
    (use-package magit
      (defun magit-dired-other-window ()
        (dired-other-window (magit-toplevel)))
      (defun magit-remote-github (username &optional args)
        (interactive (list (magit-read-string-ns "User name")
        (let* ((url (magit-get "remote.origin.url"))
               (match (string-match "^https?://github\.com/[^/]*/\\(.*\\)" url)))
          (unless match
            (error "Not a github remote"))
          (let ((repo (match-string 1 url)))
            (apply 'magit-remote-add username (format ""
                                                      username repo) args))))
      (defun magit-github-hook ()
        "Add to remote popup to add from github username."
        (magit-define-popup-action 'magit-remote-popup
          ?g "Add remote from github user name" #'magit-remote-github))
      :hook (magit-mode . magit-github-hook)
      :commands magit-clone
      :if (locate-file "git" exec-path)
      :bind (("C-x g" . magit-status)
             ("C-x G" . magit-dispatch-popup)
             :package magit
             :map magit-mode-map
             ("C-o" . magit-dired-other-window)))
  • MMM Mode


    MMM mode lets you edit multiple languages within one buffer.

    (use-package mmm-mode
      :commands mmm-mode
      (use-package mmm-auto
        :ensure nil))
  • multiple-cursors


    Multiple cursors give you more cursors. It is bound to C-> and C-<.

    (use-package multiple-cursors
      (("<C-S-down>" . mc/mark-next-like-this)
       ("<C-S-up>" . mc/mark-previous-like-this)
       ("C->" . mc/mark-next-like-this)
       ("C-<" . mc/mark-previous-like-this)
       ("M-<mouse-1>" . mc/add-cursor-on-click)
       ("C-c C-<"     . mc/mark-all-like-this)
       ("C-!"         . mc/mark-next-symbol-like-this)
       ("C-S-c C-S-c" . mc/edit-lines)))
  • Org


    Org mode is an impressive suite of text editing solutions. It gives you an outliner but also much much more.

    (use-package org
      ;; just use builtin org-mode for now
      ;; versions are mismatched so can cause a conflict
      :ensure org-plus-contrib
      ;; :ensure nil
      :hook ((message-mode . turn-on-orgstruct++)
             (org-mode . (lambda ()
                           (add-hook 'completion-at-point-functions
                                     'pcomplete-completions-at-point nil t)))
             (org-mode . auto-fill-mode)
             (org-mode . (lambda () (setq-local scroll-margin 3)))
             (message-mode . turn-on-orgtbl)
             (org-mode . (lambda ()
                           (autoload 'org-eldoc-documentation-function "esh-help")
                           (setq-local eldoc-documentation-function
      :bind* (("C-c c" . org-capture)
              ("C-c a" . org-agenda)
              ("C-c l" . org-store-link)
              ("C-c b" . org-iswitchb))
      (use-package ob-dot
        :ensure nil
      (use-package ox-latex
        :ensure nil
      (use-package ox-beamer
        :ensure nil
      (use-package ox-md
        :ensure nil
      (use-package org-static-blog
      (org-babel-do-load-languages 'org-babel-load-languages
                                   '((shell . t)
                                     (emacs-lisp . t)
                                     (dot . t)
                                     (latex . t))))
    (use-package org-cliplink
      :bind (:map org-mode-map ("C-c M-l" . org-cliplink)))
    (use-package toc-org
      :hook (org-mode . toc-org-enable))
  • Projectile


    Setup projectile and link it with some other packages. This also adds an easymenu to make the “Projectile” modeline clickable.

      (autoload 'projectile-project-vcs "projectile")
      (autoload 'projectile-project-root "projectile")
      (autoload 'easy-menu-define "easymenu" "" nil 'macro))
    (use-package projectile
      :commands projectile-mode
      :bind-keymap* (("C-c p" . projectile-command-map)
                     ("s-p" . projectile-command-map))
      :bind (("C-c C-f" . projectile-find-file)
             :map projectile-command-map
             ("s r" . projectile-rg))
      (defun projectile-rg ()
        "Run ripgrep in projectile."
        (counsel-rg "" (projectile-project-root)))
      ;; projectile is global
      ;; needed to recognize project files
      (easy-menu-define projectile-menu projectile-mode-map "Projectile"
          :active nil
          ["Find file" projectile-find-file]
          ["Find file in known projects" projectile-find-file-in-known-projects]
          ["Find test file" projectile-find-test-file]
          ["Find directory" projectile-find-dir]
          ["Find file in directory" projectile-find-file-in-directory]
          ["Find other file" projectile-find-other-file]
          ["Switch to buffer" projectile-switch-to-buffer]
          ["Jump between implementation file and test file"
          ["Kill project buffers" projectile-kill-buffers]
          ["Recent files" projectile-recentf]
          ["Edit .dir-locals.el" projectile-edit-dir-locals]
          ["Open project in dired" projectile-dired]
          ["Switch to project" projectile-switch-project]
          ["Switch to open project" projectile-switch-open-project]
          ["Discover projects in directory"
          ["Search in project (grep)" projectile-grep]
          ["Search in project (ag)" projectile-ag]
          ["Replace in project" projectile-replace]
          ["Multi-occur in project" projectile-multi-occur]
          ["Browse dirty projects" projectile-browse-dirty-projects]
          ["Run shell" projectile-run-shell]
          ["Run eshell" projectile-run-eshell]
          ["Run term" projectile-run-term]
          ["Cache current file" projectile-cache-current-file]
          ["Invalidate cache" projectile-invalidate-cache]
          ["Regenerate [e|g]tags" projectile-regenerate-tags]
          ["Compile project" projectile-compile-project]
          ["Test project" projectile-test-project]
          ["Run project" projectile-run-project]
          ["Project info" projectile-project-info]
          ["About" projectile-version]))
  • smart-hungry-delete


    Smart hungry delete automatically delete lots of whitespace in a row.

    (use-package smart-hungry-delete
      :disabled (< emacs-major-version 25)
      :bind (:map prog-mode-map
                  ("<backspace>" . smart-hungry-delete-backward-char)
                  ("C-d" . smart-hungry-delete-forward-char))
      :hook ((prog-mode . smart-hungry-delete-default-prog-mode-hook)
             (c-mode-common . smart-hungry-delete-default-c-mode-common-hook)
             (python-mode . smart-hungry-delete-default-c-mode-common-hook)
             (text-mode . smart-hungry-delete-default-text-mode-hook)))
  • Smartparens


    Smartparens is helpful in closing parenthesis when editing Lisp code.

      (autoload 'sp-with-modes "smartparens" "" nil 'macro)
      (autoload 'sp-local-pair "smartparens")
      (autoload 'sp-local-tag  "smartparens"))
    (use-package smartparens
      :bind (:package smartparens
             :map smartparens-mode-map
                  ("C-M-k" . sp-kill-sexp)
                  ("C-M-f" . sp-forward-sexp)
                  ("C-M-b" . sp-backward-sexp)
                  ("C-M-n" . sp-up-sexp)
                  ("C-M-d" . sp-down-sexp)
                  ("C-M-u" . sp-backward-up-sexp)
                  ("C-M-p" . sp-backward-down-sexp)
                  ("C-M-w" . sp-copy-sexp)
                  ("M-s" . sp-splice-sexp)
                  ("C-}" . sp-forward-barf-sexp)
                  ("C-{" . sp-backward-barf-sexp)
                  ("M-S" . sp-split-sexp)
                  ("M-J" . sp-join-sexp)
                  ("C-M-t" . sp-transpose-sexp)
                  ("C-M-<right>" . sp-forward-sexp)
                  ("C-M-<left>" . sp-backward-sexp)
                  ("M-F" . sp-forward-sexp)
                  ("M-B" . sp-backward-sexp)
                  ("C-M-a" . sp-backward-down-sexp)
                  ("C-S-d" . sp-beginning-of-sexp)
                  ("C-S-a" . sp-end-of-sexp)
                  ("C-M-e" . sp-up-sexp)
                  ("C-(" . sp-forward-barf-sexp)
                  ("C-)" . sp-forward-slurp-sexp)
                  ("M-(" . sp-forward-barf-sexp)
                  ("M-)" . sp-forward-slurp-sexp)
                  ("M-D" . sp-splice-sexp)
                  ("C-<down>" . sp-down-sexp)
                  ("C-<up>"   . sp-up-sexp)
                  ("M-<down>" . sp-splice-sexp-killing-forward)
                  ("M-<up>"   . sp-splice-sexp-killing-backward)
                  ("C-<right>" . sp-forward-slurp-sexp)
                  ("M-<right>" . sp-forward-barf-sexp)
                  ("C-<left>"  . sp-backward-slurp-sexp)
                  ("M-<left>"  . sp-backward-barf-sexp)
                  ("C-k"   . sp-kill-hybrid-sexp)
                  ("M-k"   . sp-backward-kill-sexp)
                  ("M-<backspace>" . backward-kill-word)
                  ("C-<backspace>" . sp-backward-kill-word)
                  ([remap sp-backward-kill-word] . backward-kill-word)
                  ("M-[" . sp-backward-unwrap-sexp)
                  ("M-]" . sp-unwrap-sexp)
                  ("C-x C-t" . sp-transpose-hybrid-sexp)
                  :package smartparens
                  :map smartparens-strict-mode-map
                  ([remap c-electric-backspace] . sp-backward-delete-char)
                  :map emacs-lisp-mode-map
                  (";" . sp-comment))
      :hook (((emacs-lisp-mode
               eval-expression-minibuffer-setup) . smartparens-strict-mode)
               org-mode) . show-smartparens-mode)
               html-mode) . smartparens-mode))
      (use-package smartparens-html
        :ensure nil)
      (use-package smartparens-config
        :ensure nil)
      (sp-with-modes 'org-mode
        (sp-local-pair "*" "*"
                       :actions '(insert wrap)
                       :unless '(sp-point-after-word-p sp-point-at-bol-p)
                       :wrap "C-*" :skip-match 'sp--org-skip-asterisk)
        (sp-local-pair "_" "_" :unless '(sp-point-after-word-p) :wrap "C-_")
        (sp-local-pair "/" "/" :unless '(sp-point-after-word-p)
                       :post-handlers '(("[d1]" "SPC")))
        (sp-local-pair "~" "~" :unless '(sp-point-after-word-p)
                       :post-handlers '(("[d1]" "SPC")))
        (sp-local-pair "=" "=" :unless '(sp-point-after-word-p)
                       :post-handlers '(("[d1]" "SPC")))
        (sp-local-pair "«" "»"))
      (sp-with-modes '(java-mode c++-mode)
        (sp-local-pair "{" nil :post-handlers '(("||\n[i]" "RET")))
        (sp-local-pair "/*" "*/" :post-handlers '((" | " "SPC")
                                                  ("* ||\n[i]" "RET"))))
      (sp-with-modes '(markdown-mode gfm-mode rst-mode)
        (sp-local-pair "*" "*" :bind "C-*")
        (sp-local-tag "2" "**" "**")
        (sp-local-tag "s" "```scheme" "```")
        (sp-local-tag "<"  "<_>" "</_>" :transform 'sp-match-sgml-tags))
      (sp-local-pair 'emacs-lisp-mode "`" nil :when '(sp-in-string-p))
      (sp-local-pair 'clojure-mode "`" "`" :when '(sp-in-string-p))
      (sp-local-pair 'minibuffer-inactive-mode "'" nil :actions nil)
      (sp-with-modes 'nix-mode
        (sp-local-pair "'" "'" :unless '(sp-in-comment-p sp-in-string-quotes-p))
        (sp-local-pair "\"" "\"")
        (sp-local-pair "''" "''" :unless '(sp-in-comment-p sp-in-string-quotes-p))))
  • sudo-edit


    Sudo-edit lets you open a file using sudo (it actually goes through TRAMP to achieve this).

    (use-package sudo-edit
      :bind (("C-c C-r" . sudo-edit)))
  • which-key

    Which-key will tell you what key bindings are available give a prefix. Test it out by pressing C-x and waiting a few seconds. Each key listed is bound to a function.

    (use-package which-key
      :commands which-key-mode
      :config (which-key-mode))


These are available automatically, so these use-package blocks just configure them.

  • ansi-color

    Get color/ansi codes in compilation mode.

    (use-package ansi-color
      :ensure nil
      :hook (compilation-filter . colorize-compilation-buffer)
      (defun colorize-compilation-buffer ()
        (let ((inhibit-read-only t))
          (ansi-color-apply-on-region (point-min) (point-max)))))
  • autorevert
    (use-package autorevert
      :ensure nil
      :commands global-auto-revert-mode
      :config (global-auto-revert-mode t))
  • bug-reference
    (use-package bug-reference
      :ensure nil
      :hook ((prog-mode . bug-reference-prog-mode)
             (text-mode . bug-reference-mode)))
    • bug-reference-github
      (use-package bug-reference-github
        :commands bug-reference-github-set-url-format
        (defun bug-reference-github-projectile ()
          (when (and projectile-mode
                     (eq (projectile-project-vcs (projectile-project-root)) 'git))
        :hook (projectile-mode . bug-reference-github-projectile))
  • comint
    (use-package comint
      :ensure nil
      ;; :bind
      ;; (:map comint-mode-map
      ;;       ("C-r"       . comint-history-isearch-backward-regexp)
      ;;       ("s-k"       . comint-clear-buffer)
      ;;       ("M-TAB"     . comint-previous-matching-input-from-input)
      ;;       ("<M-S-tab>" . comint-next-matching-input-from-input))
      ;; :hook ((kill-buffer . comint-write-input-ring)
      ;;     (kill-buffer . save-history))
      (defun turn-on-comint-history (history-file)
        (setq comint-input-ring-file-name history-file)
        (comint-read-input-ring 'silent))
      (defun save-history ()
        (dolist (buffer (buffer-list))
          (with-current-buffer buffer (comint-write-input-ring)))))
  • compile
    (use-package compile
      :ensure nil
      :bind (("C-c C-c" . compile)
             :map compilation-mode-map
             ("o" . compile-goto-error))
      (defun show-compilation ()
        (let ((compile-buf
               (catch 'found
                 (dolist (buf (buffer-list))
                   (if (string-match "\\*compilation\\*" (buffer-name buf))
                       (throw 'found buf))))))
          (if compile-buf
              (switch-to-buffer-other-window compile-buf)
            (call-interactively 'compile))))
      (defun compilation-ansi-color-process-output ()
        (ansi-color-process-output nil)
        (set (make-local-variable 'comint-last-output-start)
      :hook (compilation-filter . compilation-ansi-color-process-output))
  • delsel
    (use-package delsel
      :ensure nil
      :commands delete-selection-mode
      :config (delete-selection-mode t))
  • dired
    (use-package dired
      :ensure nil
      (defun dired-run-command (&optional filename)
        "Run file at point in a new buffer."
        (unless filename
          (setq filename (expand-file-name
                          (dired-get-filename t t)
        (let ((buffer (make-term (file-name-nondirectory filename) filename))
              (buffer-read-only nil))
          (with-current-buffer buffer
            ;; (term-mode)
            (term-set-escape-char ?\C-x))
          (set-process-sentinel (get-buffer-process buffer)
                                (lambda (proc event)
                                  (when (not (process-live-p proc))
                                    (kill-buffer (process-buffer proc)))))
          (switch-to-buffer buffer)))
      :bind (("C-c J" . dired-double-jump)
             :package dired
             :map dired-mode-map
             ("C-c C-c" . compile)
             ("r" . term)
             ("M-@" . shell)
             ("M-*" . eshell)
             ("W" . browse-url-of-dired-file)
             ("@" . dired-run-command)))
    • dired-column
      (use-package dired-column
        :ensure nil
        :bind (:package dired
               :map dired-mode-map
                    ("o" . dired-column-find-file)))
    • dired-subtree
      (use-package dired-subtree
        :bind (:package dired
               :map dired-mode-map
                    ("<tab>" . dired-subtree-toggle)
                    ("<backtab>" . dired-subtree-cycle)))
    • dired-x
      (use-package dired-x
        :ensure nil
        :hook ((dired-mode . dired-omit-mode)
               (dired-mode . dired-hide-details-mode))
        :bind (("s-\\" . dired-jump-other-window)
               :package dired
               :map dired-mode-map
               (")" . dired-omit-mode)))
  • eldoc

    Provides some info for the thing at the point.

    (use-package eldoc
      :ensure nil
      :hook ((emacs-lisp-mode . eldoc-mode)
             (eval-expression-minibuffer-setup . eldoc-mode)
             (lisp-mode-interactive-mode . eldoc-mode)
             (typescript-mode . eldoc-mode)
             (haskell-mode . eldoc-mode)
             (python-mode . eldoc-mode)
             (eshell-mode . eldoc-mode)
             (org-mode . eldoc-mode)))
  • electric

    Setup these modes:

    • electric-quote
    • electric-indent
    • electric-layout
    (use-package electric
      :ensure nil
      :disabled (< emacs-major-version 25)
      :hook ((prog-mode . electric-quote-mode)
             (text-mode . electric-quote-mode)
             (prog-mode . electric-indent-mode)
             (prog-mode . electric-layout-mode)))
    • elec-pair

      Setup electric-pair-mode for prog-modes. Also disable it when smartparens is setup.

      (use-package elec-pair
        :ensure nil
        :hook ((prog-mode . electric-pair-mode)
               (smartparens-mode . (lambda () (electric-pair-mode -1)))))
  • eww
    (use-package eww
      :ensure nil
      :if (not window-system)
      :commands eww-browse-url
      (setq browse-url-browser-function 'eww-browse-url))
  • executable
    (use-package executable
      :ensure nil
      :hook ((after-save . executable-make-buffer-file-executable-if-script-p)))
  • ffap
    (use-package ffap
      :bind (("C-x C-f" . find-file-at-point)
             ("C-x C-r" . ffap-read-only)
             ("C-x C-v" . ffap-alternate-file)
             ("C-x 4 f" . ffap-other-window)
             ("C-x 5 f" . ffap-other-frame)
             ("C-x 4 r" . ffap-read-only-other-window)
             ("C-x 5 r" . ffap-read-only-other-frame)
             ("C-x d"  . dired-at-point)
             ("C-x 4 d" . ffap-dired-other-window)
             ("C-x 5 d" . ffap-dired-other-frame)
             ("C-x C-d" . ffap-list-directory))
      :hook ((gnus-summary-mode . ffap-gnus-hook)
             (gnus-article-mode . ffap-gnus-hook)
             (vm-mode . ffap-ro-mode-hook)
             (rmail-mode . ffap-ro-mode-hook))
      :ensure nil)
  • files
    (use-package files
      :ensure nil
      (defun find-file--line-number (orig-fun filename &optional wildcards)
        "Turn files like file.cpp:14 into file.cpp and going to the 14-th line."
          (let* ((matched (string-match "^\\(.*\\):\\([0-9]+\\):?$" filename))
                 (line-number (and matched
                                   (match-string 2 filename)
                                   (string-to-number (match-string 2 filename))))
                 (filename (if matched (match-string 1 filename) filename)))
            (apply orig-fun (list filename wildcards))
            (when line-number
              ;; goto-line is for interactive use
              (goto-char (point-min))
              (forward-line (1- line-number))))))
      (advice-add 'find-file :around #'find-file--line-number))
  • flyspell
    (use-package flyspell
      :ensure nil
      :if (locate-file
           (if (boundp 'ispell-program-name) ispell-program-name "ispell")
      :hook ((text-mode . flyspell-mode)
             (prog-mode . flyspell-prog-mode))
      :bind (:map flyspell-mode-map
             ("C-M-i" . nil))
      ;; :init
      ;; (define-key flyspell-mode-map [(control ?\.)] nil)
  • goto-addr
    (use-package goto-addr
      :ensure nil
      :hook ((prog-mode . goto-address-prog-mode)
             (git-commit-mode . goto-address-mode)))
  • hl-line
    (use-package hl-line
      :ensure nil
      :hook ((prog-mode . hl-line-mode)
             (org-mode . hl-line-mode)
             (dired-mode . hl-line-mode)))
  • paren
    (use-package paren
      :ensure nil
      :hook ((prog-mode . show-paren-mode)
             (prog-mode . (lambda () (show-paren-mode -1)))))
  • pp
    (use-package pp
      :ensure nil
      :commands pp-eval-last-sexp
      :bind (([remap eval-expression] . pp-eval-expression))
      ;; :init
      ;;(global-unset-key (kbd "C-x C-e"))
      :hook ((lisp-mode emacs-lisp-mode) . always-eval-sexp)
      (defun always-eval-sexp ()
        (define-key (current-local-map) (kbd "C-x C-e") 'pp-eval-last-sexp)))
  • prog-mode
    (use-package prog-mode
      :ensure nil
      :hook ((prog-mode . prettify-symbols-mode)
             (lisp-mode . prettify-symbols-lisp)
             (c-mode . prettify-symbols-c)
             (c++-mode . prettify-symbols-c++)
             ((js-mode js2-mode) . prettify-symbols-js)
             (prog-mode . (lambda () (setq-local scroll-margin 3))))
      (defun prettify-symbols-prog ()
        (push '("<=" . ?≤) prettify-symbols-alist)
        (push '(">=" . ?≥) prettify-symbols-alist))
      (defun prettify-symbols-lisp ()
        (push '("/=" . ?≠) prettify-symbols-alist)
        (push '("sqrt" . ?√) prettify-symbols-alist)
        (push '("not" . ?¬) prettify-symbols-alist)
        (push '("and" . ?∧) prettify-symbols-alist)
        (push '("or" . ?∨) prettify-symbols-alist))
      (defun prettify-symbols-c ()
        (push '("<=" . ?≤) prettify-symbols-alist)
        (push '(">=" . ?≥) prettify-symbols-alist)
        (push '("!=" . ?≠) prettify-symbols-alist)
        (push '("&&" . ?∧) prettify-symbols-alist)
        (push '("||" . ?∨) prettify-symbols-alist)
        (push '(">>" . ?») prettify-symbols-alist)
        (push '("<<" . ?«) prettify-symbols-alist))
      (defun prettify-symbols-c++ ()
        (push '("<=" . ?≤) prettify-symbols-alist)
        (push '(">=" . ?≥) prettify-symbols-alist)
        (push '("!=" . ?≠) prettify-symbols-alist)
        (push '("&&" . ?∧) prettify-symbols-alist)
        (push '("||" . ?∨) prettify-symbols-alist)
        (push '(">>" . ?») prettify-symbols-alist)
        (push '("<<" . ?«) prettify-symbols-alist)
        (push '("->" . ?→) prettify-symbols-alist))
      (defun prettify-symbols-js ()
        (push '("function" . ?λ) prettify-symbols-alist)
        (push '("=>" . ?⇒) prettify-symbols-alist)))
  • savehist-mode
    (use-package savehist
      :ensure nil
      :hook (after-init . savehist-mode))
  • saveplace-mode
    (use-package saveplace
      :ensure nil
      :disabled (< emacs-major-version 25)
      :hook (after-init . save-place-mode))
  • Shell
    (use-package shell
      :ensure nil
      :bind ("C-c C-s" . shell)
      :hook ((shell-mode . ansi-color-for-comint-mode-on)
             (shell-mode . dirtrack-mode)
             (shell-mode . pcomplete-shell-setup)
             ;; (shell-mode . use-histfile)
      (defun use-histfile ()
        (turn-on-comint-history (getenv "HISTFILE"))))
  • simple
    (use-package simple
      :ensure nil
      :commands (column-number-mode auto-fill-mode)
      (("C-`" . list-processes)
       :map minibuffer-local-map
       ("<escape>"  . abort-recursive-edit)
       ("M-TAB"     . previous-complete-history-element)
       ("<M-S-tab>" . next-complete-history-element))
      :hook ((text-mode . visual-line-mode))
      :config (column-number-mode))
  • subword
    (use-package subword
      :ensure nil
      :hook ((java-mode . subword-mode)))
  • term
    (use-package term
      :ensure nil
      :commands (term-mode term-char-mode)
      :hook (term-mode . (lambda ()
                           (setq term-prompt-regexp "^[^#$%>\n]*[#$%>] *")
                           (setq-local transient-mark-mode nil)
                           (auto-fill-mode -1)))
      (defun my-term (&optional path name)
        (set-buffer (make-term "my-term" "zsh"))
        (term-set-escape-char ?\C-x)
        (switch-to-buffer "*my-term*"))
      (defun term-remote (&optional path name)
        "Opens an ansi terminal at PATH. If no PATH is given, it uses
    the value of `default-directory'. PATH may be a tramp remote path.
    The ansi-term buffer is named based on `name' "
        (unless path (setq path default-directory))
        (unless name (setq name "ansi-term"))
        (ansi-term "/bin/bash" name)
        (let ((path (replace-regexp-in-string "^file:" "" path))
               "fn=%s; if test ! -d $fn; then fn=$(dirname $fn); fi; cd $fn;")
              (bufname (concat "*" name "*" )))
          (if (tramp-tramp-file-p path)
              (let ((tstruct (tramp-dissect-file-name path)))
                 ((equal (tramp-file-name-method tstruct) "ssh")
                  (process-send-string bufname (format
                                                (concat  "ssh -t %s '"
                                                         "exec bash'; exec bash; clear\n")
                                                (tramp-file-name-host tstruct)
                                                (tramp-file-name-localname tstruct))))
                 (t (error "not implemented for method %s"
                           (tramp-file-name-method tstruct)))))
            (process-send-string bufname (format (concat cd-str " exec bash;clear\n")
      :bind ("C-c t" . my-term))
    (use-package tramp-term
      :commands tramp-term)
  • text-mode
    (use-package text-mode
      :ensure nil
      :hook ((text-mode . turn-on-auto-fill)))
  • time
    (use-package time
      :config (display-time-mode))
  • url-handlers
    (use-package url-handlers
      :ensure nil
      :commands url-handler-mode
      :config (url-handler-mode))
  • which-func
    (use-package which-func
      :ensure nil
      :config (which-function-mode))
  • whitespace
    (use-package whitespace
      :ensure nil
      :hook (prog-mode . whitespace-mode))

Programming languages

Each use-package declaration corresponds to major modes in Emacs lingo. Each language will at least one of these major modes as well as associated packages (for completion, syntax checking, etc.)

  • Emacs speaks statistics


    (use-package ess-site
      :ensure ess
      :interpreter (("Rscript" . r-mode)
                    ("r" . r-mode))
      :mode (("\\.sp\\'"          . S-mode)
             ("/R/.*\\.q\\'"      . R-mode)
             ("\\.[qsS]\\'"       . S-mode)
             ("\\.ssc\\'"         . S-mode)
             ("\\.SSC\\'"         . S-mode)
             ("\\.[rR]\\'"        . R-mode)
             ("\\.[rR]nw\\'"      . Rnw-mode)
             ("\\.[sS]nw\\'"      . Snw-mode)
             ("\\.[rR]profile\\'" . R-mode)
             ("NAMESPACE\\'"      . R-mode)
             ("CITATION\\'"       . R-mode)
             ("\\.omg\\'"         . omegahat-mode)
             ("\\.hat\\'"         . omegahat-mode)
             ("\\.lsp\\'"         . XLS-mode)
             ("\\.do\\'"          . STA-mode)
             ("\\.ado\\'"         . STA-mode)
             ("\\.[Ss][Aa][Ss]\\'"        . SAS-mode)
             ("\\.[Ss]t\\'"       . S-transcript-mode)
             ("\\.Sout"           . S-transcript-mode)
             ("\\.[Rr]out"        . R-transcript-mode)
             ("\\.Rd\\'"          . Rd-mode)
             ("\\.[Bb][Uu][Gg]\\'"         . ess-bugs-mode)
             ("\\.[Bb][Oo][Gg]\\'"         . ess-bugs-mode)
             ("\\.[Bb][Mm][Dd]\\'"         . ess-bugs-mode)
             ("\\.[Jj][Aa][Gg]\\'"         . ess-jags-mode)
             ("\\.[Jj][Oo][Gg]\\'"         . ess-jags-mode)
             ("\\.[Jj][Mm][Dd]\\'"         . ess-jags-mode)
  • Proof General


    (use-package proof-site
      :disabled (< emacs-major-version 25)
      :ensure proofgeneral
      :if (not needs-package-init)
      :commands (proofgeneral proof-mode proof-shell-mode))
  • C/C++
    (use-package cc-mode
      :ensure nil
      :mode (("\\.h\\(h?\\|xx\\|pp\\)\\'" . c++-mode)
             ("\\.m\\'" . c-mode)
             ("\\.c\\'" . c-mode)
             ("\\.cpp\\'" . c++-mode)
             ("\\.c++\\'" . c++-mode)
             ("\\.mm\\'" . c++-mode)))
    • Irony
      (use-package irony
        (defun irony-mode-disable-remote ()
          "Disabled irony in remote buffers."
          (when (and buffer-file-name (file-remote-p buffer-file-name))
            (irony-mode -1)))
        :hook (((c++-mode c-mode objc-mode) . irony-mode-disable-remote)
               ((c++-mode c-mode objc-mode) . irony-mode)))
      (use-package irony-cdb
        :ensure nil
        :hook (irony-mode . irony-cdb-autosetup-compile-options))
      • flycheck-irony
        (use-package flycheck-irony
          :hook (flycheck-mode . flycheck-irony-setup))
      • irony-eldoc
        (use-package irony-eldoc
          :hook (irony-mode . irony-eldoc))
  • CoffeeScript
    (use-package coffee-mode
      :mode (("\\.coffee\\'" . coffee-mode)))
  • CSS
    (use-package css-mode
      :ensure nil
      :mode "\\.css\\'")
  • CSV
    (use-package csv-mode
      :mode "\\.csv\\'")
  • ELF
    (use-package elf-mode
      :magic ("ELF" . elf-mode))
  • Go
    (use-package go-mode
      :mode "\\.go\\'")
  • HAML
    (use-package haml-mode
      :mode "\\.haml\\'")
  • Haskell
    • ghc

      Note: this needs ghc-mod to be in PATH to work properly.

      (use-package ghc
        :if (locate-file "ghc-mod" exec-path)
        :hook ((haskell-mode . ghc-init)
               (haskell-mode . ghc-comp-init)))
    • haskell-mode
      (load "haskell-mode-autoloads" t t)
      (use-package haskell
        :ensure haskell-mode
        :mode (("\\.hs\\'" . haskell-mode)
               ("\\.cabal\\'" . haskell-cabal-mode))
        :hook ((haskell-mode . subword-mode)
               (haskell-mode . flyspell-prog-mode)
               (haskell-mode . haskell-indentation-mode)
               (haskell-mode . imenu-add-menubar-index)
               (haskell-mode . (lambda ()
                                  (autoload 'haskell-doc-current-info "haskell-doc")
                                  (setq-local eldoc-documentation-function
        (add-to-list 'completion-ignored-extensions ".hi"))
    • haskell-interactive-mode
      (use-package haskell-interactive-mode
        :ensure nil
        :hook (haskell-mode . interactive-haskell-mode))
  • Java
    • jdee
      (use-package jdee
        :mode ("\\.java\\'" . jdee-mode)
        :bind (:package jdee
               :map jdee-mode-map
                    ("<s-mouse-1>" . jdee-open-class-at-event)))
  • JavaScript
    • indium
      (use-package indium
        :disabled (< emacs-major-version 25)
        :mode ("\\.js\\'" . indium-mode))
    • js2-mode
      (use-package js2-mode
        :mode (("\\.js\\'" . js2-mode)
               ("\\.es6\\'" . js2-mode)
               ("\\.ejs\\'" . js2-mode))
        :interpreter "node"
         js2-mode-indent-ignore-first-tab t
         js2-strict-inconsistent-return-warning nil
         '("module" "require" "__dirname" "process" "console" "JSON" "$" "_")))
      (use-package js2-imenu-extras
        :ensure nil
        :hook (js2-mode . js2-imenu-extras-mode))
    • tern
      (use-package tern
        :hook (js2-mode . tern-mode))
  • JSON
    (use-package json-mode
      :mode (("\\.bowerrc$"     . json-mode)
             ("\\.jshintrc$"    . json-mode)
             ("\\.json_schema$" . json-mode)
             ("\\.json\\'" . json-mode))
      :bind (:package json-mode-map
             :map json-mode-map ("C-c <tab>" . json-mode-beautify))
      (make-local-variable 'js-indent-level))
  • LaTeX
    • auctex

      Auctex provides some helpful tools for working with LaTeX.

      (use-package tex-site
        :ensure auctex
        :mode ("\\.tex\\'" . TeX-latex-mode))
  • Lisp
    (use-package elisp-mode
      :ensure nil
      :interpreter (("emacs" . emacs-lisp-mode)))
    • ielm
      (use-package ielm
        :ensure nil
        :bind ("C-c :" . ielm))
  • Mach-O
    (use-package macho-mode
      :ensure nil
      :magic (("\xFE\xED\xFA\xCE" . macho-mode)
              ("\xFE\xED\xFA\xCF" . macho-mode)
              ("\xCE\xFA\xED\xFE" . macho-mode)
              ("\xCF\xFA\xED\xFE" . macho-mode)))
  • Markdown
    • markdown-mode
      (use-package markdown-mode
        :mode (("\\.md\\'" . gfm-mode)
               ("\\.markdown\\'" . gfm-mode)))
  • Nix
    (use-package nix-mode
      :mode "\\.nix\\'")
    (use-package nix-shell
      :ensure nil
      :commands (nix-shell nix-unpack))
    • nix-buffer
      (use-package nix-buffer
        :commands nix-buffer
        (defun turn-on-nix-buffer ()
              (when (and (not noninteractive)
                                 (not (eq (aref (buffer-name) 0) ?\s))
                                 (not (file-remote-p default-directory)))
        :hook (after-change-major-mode . turn-on-nix-buffer))
    • nix-update
      (use-package nix-update-el
        :bind (("C-. u" . nix-update-fetch)))
  • PHP
    (use-package php-mode
      :mode "\\.php\\'")
  • Python
    • Anaconda
      (use-package anaconda-mode
        :hook ((python-mode . anaconda-mode)
               (python-mode . anaconda-eldoc-mode)))
    • python-mode
      (use-package python
        :ensure nil
        :mode ("\\.py\\'" . python-mode)
        :interpreter ("python" . python-mode))
    • jedi
      (use-package jedi
        :hook ((python-mode . jedi:setup))
        (setq jedi:complete-on-dot t))
    • elpy
      (use-package elpy
        :mode ("\\.py\\'" . elpy-mode))
  • Ruby
    (use-package ruby-mode
      :ensure nil
      :mode ("\\.rb\\'" . ruby-mode)
      :interpreter ("ruby" . ruby-mode))
  • Rust
    (use-package rust-mode
      :mode "\\.rs\\'")
  • SASS
    (use-package sass-mode
      :mode "\\.sass\\'")
  • Scala
    (use-package scala-mode
      :interpreter ("scala" . scala-mode))
  • SCSS
    (use-package scss-mode
      :mode "\\.scss\\'")
  • Shell
    (use-package sh-script
      :ensure nil
      :commands shell-command
      (defun shell-command-at-point ()
        (let ((start-point (save-excursion
          (shell-command (buffer-substring start-point (point)))))
      :mode (("\\.*shellrc$" . sh-mode)
             ("\\.*shell_profile" . sh-mode)
             ("\\.zsh\\'" . sh-mode))
      :bind (:package sh-script
             :map sh-mode-map
                  ("C-x C-e" . shell-command-at-point)))
  • texinfo
    (use-package texinfo
      :mode ("\\.texi\\'" . texinfo-mode))
  • TypeScript
    (use-package typescript-mode
      :mode "\\.ts\\'")
    • tide
      (use-package tide
        :hook ((typescript-mode . tide-setup)
               (typescript-mode . tipe-hl-identifier-mode)))
  • Web
    (use-package web-mode
      :mode (("\\.erb\\'" . web-mode)
             ("\\.mustache\\'" . web-mode)
             ("\\.html?\\'" . web-mode)
             ("\\.php\\'" . web-mode)
             ("\\.jsp\\'" . web-mode)
             ("\\.jsx?$" . web-mode)
             ("\\.es6\\'" . web-mode)
             ("\\.ejs\\'" . web-mode)
             ("\\.phtml\\'" . web-mode)
             ("\\.tpl\\.php\\'" . web-mode)
             ("\\.[agj]sp\\'" . web-mode)
             ("\\.as[cp]x\\'" . web-mode)
             ("\\.djhtml\\'" . web-mode)))
  • YAML
    (use-package yaml-mode
      :mode "\\.ya?ml\\'")


These are all available in ./site-lisp. Eventually they should go into separate repositories.

  • dired-column
  • em-dired
  • installer
  • macho-mode
  • nethack
    (use-package nethack
      :commands nethack
      :ensure nil)
  • nix-fontify
  • set-defaults
  • use-package-list


These should correspond to minor modes or helper functions. Some of them are more helpful than others but none are essential.

Most of these are available in MELPA.

  • delight
    (use-package delight)
  • browse-at-remote
    (use-package browse-at-remote
      :bind ("C-c g g" . browse-at-remote))
  • buffer-move


    (use-package buffer-move
      (("<M-S-up>" . buf-move-up)
       ("<M-S-down>" . buf-move-down)
       ("<M-S-left>" . buf-move-left)
       ("<M-S-right>" . buf-move-right)))
  • copy-as-format
    (use-package copy-as-format
      :bind (("C-c w s" . copy-as-format-slack)
             ("C-c w g" . copy-as-format-github)))
  • crux
    (use-package crux
      :bind (("C-c D" . crux-delete-file-and-buffer)
             ("C-c C-e" . crux-eval-and-replace)
             ([shift return] . crux-smart-open-line)))
  • expand-region
    (use-package expand-region
      :bind (("C-=" . er/expand-region)))
  • git-attr
    (use-package git-attr-linguist
      :ensure git-attr
      (require 'cl-lib)
      (defalias 'caddr 'cl-caddr)
      :hook ((find-file . git-attr-linguist)))
  • git-modes
    (use-package gitattributes-mode
      :mode (("/\\.gitattributes\\'" . gitattributes-mode)
             ("/info/attributes\\'" . gitattributes-mode)
             ("/git/attributes\\'" . gitattributes-mode)))
    (use-package gitconfig-mode
      :mode (("/\\.gitconfig\\'" . gitconfig-mode)
             ("/\\.git/config\\'" . gitconfig-mode)
             ("/modules/.*/config\\'" . gitconfig-mode)
             ("/git/config\\'" . gitconfig-mode)
             ("/\\.gitmodules\\'" . gitconfig-mode)
             ("/etc/gitconfig\\'" . gitconfig-mode)))
    (use-package gitignore-mode
      :mode (("/\\.gitignore\\'" . gitignore-mode)
             ("/info/exclude\\'" . gitignore-mode)
             ("/git/ignore\\'" . gitignore-mode)))
  • github-clone
    (use-package github-clone
      :if (locate-file "git" exec-path)
      :bind ("C-c g c" . github-clone))
  • helpful
    (use-package helpful
      :disabled (< emacs-major-version 25)
      :bind (([remap describe-function] . helpful-callable)
             ([remap describe-variable] . helpful-variable)
             ([remap describe-key] . helpful-key)))
  • hl-todo
    (use-package hl-todo
      :hook (prog-mode . hl-todo-mode))
  • htmlize
    (use-package htmlize :no-require)
  • multi-term
    (use-package multi-term
      :bind (("C-. t" . multi-term-next)
             ("C-. T" . multi-term)))
  • mwim


    (use-package mwim
      :bind (([remap move-beginning-of-line] . mwim-beginning-of-code-or-line)
             ([remap move-end-of-line] . mwim-end-of-code-or-line)))
  • notmuch
    (use-package notmuch
      :bind ("s-m" . notmuch))
  • page-break-lines
    (use-package page-break-lines
      :hook ((doc-mode
              magit-mode) . page-break-lines-mode))
  • pandoc-mode
    (use-package pandoc-mode
      :hook ((markdown-mode . pandoc-mode)
             (pandoc-mode . pandoc-load-default-settings)))
  • rainbow-delimiters
    (use-package rainbow-delimiters
      :hook ((emacs-lisp-mode
              slime-repl-mode) . rainbow-delimiters-mode))
  • rainbow-mode
    (use-package rainbow-mode
      :hook ((emacs-lisp-mode
              css-mode) . rainbow-mode))
  • restclient
    (use-package restclient
      :mode (("\\.rest\\'" . restclient-mode)
             ("\\.restclient\\'" . restclient-mode)))
  • shrink-whitespace
    (use-package shrink-whitespace
      :bind ("H-SPC" . shrink-whitespace))
  • string-inflection
    (use-package string-inflection
      :bind (("C-c r r" . string-inflection-all-cycle)
             ("C-c r c" . string-inflection-camelcase)
             ("C-c r l" . string-inflection-lower-camelcase)
             ("C-c r u" . string-inflection-underscore)
             ("C-c r k" . string-inflection-kebab-case)
             ("C-c r J" . string-inflection-java-style-cycle)))
  • try


    (use-package try
      :commands try)
  • vkill
    (use-package vkill
      :bind ("C-x L" . vkill))
  • volatile-highlights
    (use-package volatile-highlights
      :commands volatile-highlights-mode
      :config (volatile-highlights-mode t)


All of these files live outside of Emacs but are necessary for a usable developer environment. They are basic shell profile and some git configuration scripts as well.


To use this, you must create a short ~/.profile file. Here is an example,

[ -f $bootstrap ] && source $bootstrap

Here we setup .profile. First, setup exports.

export MAN_PATH

export LANG=en_US.UTF-8 \
       LC_ALL=en_US.UTF-8 \
       INFOPATH=$PREFIX/share/info \
       DICPATH=$PREFIX/share/hunspell \
       CLICOLOR=1 \
       GREP_COLOR='3;33' \
       LC_COLLATE=C \
       HISTFILE=$HOME/.history \
       HISTSIZE=2000 \
       SAVEHIST=2000 \
       HISTFILESIZE=2000 \
       HISTTIMEFORMAT="[%F %T] " \
       HISTCONTROL=ignoreboth \

Then setup aliases.

alias ls="TERM=ansi ls --color=always" \
      l="ls -lF" \
      ..="cd .." \
      ...="cd ../.." \
      ....="cd ../../.." \
      .....="cd ../../../.." \
      tree='tree -Csuh' \
      grep="grep --color=auto"


if [ "$TERM" = dumb ] && ! [ -z "$INSIDE_EMACS" ]; then
    export TERM=dumb-emacs-ansi \

Define update_terminal_cwd function.

update_terminal_cwd () {
    local SEARCH=' '
    local REPLACE='%20'
    local PWD_URL="file://$HOSTNAME${PWD//$SEARCH/$REPLACE}"
    printf '\e]7;%s\a' "$PWD_URL"


This is a profile for use with GNU Bash. To use this, you must create a short ~/.bash_profile file. Here is an example,

[ -f $bootstrap ] && source $bootstrap

Source profile found above.

source @out@/etc/profile

When TERM=dumb, just do a simple prompt.

case "$TERM" in
        PS1="\W > "

Setup some bash-specific features.

shopt -s cdspell \
         cdable_vars \
         checkhash \
         checkwinsize \
         cmdhist \
         dotglob \
         extglob \
         histappend \
         histreedit \
         histverify \
         nocaseglob \
         no_empty_cmd_completion \

Turn on notify, noclobber, ignoreeof, emacs. These are bash-specific.

set -o notify \
    -o noclobber \
    -o ignoreeof \
    -o emacs

Setup prompt.

PS1='\e[0;34m\u@\h:\e[0;36m\w \e[0;33m$ \e[0m'

Bind keys when we’re interactive.

if [[ $- == *i* ]]; then
    bind '"\e/": dabbrev-expand'
    bind '"\ee": edit-and-execute-command'

Run the update_terminal_cwd command when we’re in Apple_Terminal. This will give us the working directory in the title window.

if [ "$TERM_PROGRAM" = Apple_Terminal ] && [ -z "$INSIDE_EMACS" ]; then
function set-eterm-dir {
    echo -e "\033AnSiTc" "$(pwd)"
    echo -e "\033AnSiTh" "$(hostname -f)"
    echo -e "\033AnSiTu" "$LOGNAME"
if [ "$TERM" = "eterm-color" ]; then


This is a profile for use with Zsh. It is closely based off of oh-my-zsh. To use this, you must create a short ~/.zshrc file. Here is an example,

[ -f $bootstrap ] && source $bootstrap

Setup ZSH profile. First, we just source the global profile.

source @out@/etc/profile

Handle dumb options.

case "$TERM" in
        unsetopt zle \
                 prompt_cr \
        if whence -w precmd >/dev/null; then
            unfunction precmd
        if whence -w preexec >/dev/null; then
            unfunction preexec
        PS1='$ '

Load up site-functions in ZSH.


Setup ZSH auto suggestions.

. @zsh-autosuggestions@/share/zsh-autosuggestions/zsh-autosuggestions.zsh

Turn on colors.

autoload -U colors && colors

Turn on ZSH-specific options.

setopt always_to_end \
       append_history \
       auto_cd \
       auto_menu \
       auto_name_dirs \
       auto_pushd \
       cdablevarS \
       complete_in_word \
       correct \
       correctall \
       extended_glob \
       extended_history \
       hist_expire_dups_first \
       hist_find_no_dups \
       hist_ignore_dups \
       hist_ignore_space \
       hist_reduce_blanks \
       hist_verify \
       inc_append_history \
       interactive_comments \
       long_list_jobs \
       multios \
       no_beep \
       prompt_subst \
       pushd_ignore_dups \
       pushdminus \
       share_history \

Setup completions.

autoload -U compaudit compinit && compinit -d "${ZSH_COMPDUMP}"
zmodload -i zsh/complist

Zstyle completions.

zstyle ':vcs_info:*' actionformats \
       '%F{5}(%f%s%F{5})%F{3}-%F{5}[%F{2}%b%F{3}|%F{1}%a%F{5}]%f '
zstyle ':vcs_info:*' formats '%F{5}(%f%s%F{5})%F{3}-%F{5}[%F{2}%b%F{5}]%f '
zstyle ':vcs_info:*' enable git

zstyle ':completion:*' matcher-list 'r:|=*' 'l:|=* r:|=*'
zstyle ':completion:*' matcher-list 'm:{a-zA-Z-_}={A-Za-z_-}' 'r:|=*' 'l:|=*'
zstyle ':completion:*' list-colors ''
zstyle ':completion:*:*:kill:*:processes' list-colors \
       '=(#b) #([0-9]#) ([0-9a-z-]#)*=01;34=0=01'
zstyle ':completion:*:*:*:*:processes' command \
       "ps -u $USER -o pid,user,comm -w -w"
zstyle ':completion:*:cd:*' tag-order local-directories directory-stack \
zstyle ':completion::complete:*' use-cache 1
zstyle ':completion::complete:*' cache-path ~/.zsh/cache/$HOST
zstyle ':completion:*' select-prompt \
       '%SScrolling active: current selection at %p%s'
zstyle ':completion:*::::' completer _expand _complete _ignored _approximate
zstyle ':completion:*' menu select=1 _complete _ignored _approximate
zstyle ':completion:*:*:-subscript-:*' tag-order indexes parameters
zstyle ':completion:*' verbose yes
zstyle ':completion:*:descriptions' format '%B%d%b'
zstyle ':completion:*:messages' format '%d'
zstyle ':completion:*:warnings' format 'No matches for: %d'
zstyle ':completion:*:corrections' format '%B%d (errors: %e)%b'
zstyle ':completion:*' group-name ''
zstyle ':completion:*:functions' ignored-patterns '_*'
zstyle ':completion:*:scp:*' tag-order files users \
       'hosts:-host hosts:-domain:domain hosts:-ipaddr"IP\ Address *'
zstyle ':completion:*:scp:*' group-order files all-files users hosts-domain \
       hosts-host hosts-ipaddr
zstyle ':completion:*:ssh:*' tag-order users \
       'hosts:-host hosts:-domain:domain hosts:-ipaddr"IP\ Address *'
zstyle ':completion:*:ssh:*' group-order hosts-domain hosts-host users \
zstyle '*' single-ignored show

Turn on prompt with colors.

PROMPT='%F{blue}%n@%m:%F{cyan}%c%F{yellow} $ %F{reset}'

ZSH key bindings.

if [ "$TERM" = xterm-256color ]; then
    bindkey "^[[H" beginning-of-line
    bindkey "^[[F" end-of-line
    bindkey "^[[3~" delete-char

Setup Apple Terminal so that CWD is shown.

if [ "$TERM_PROGRAM" = Apple_Terminal ] && [ -z "$INSIDE_EMACS" ]; then
    autoload add-zsh-hook
    add-zsh-hook chpwd update_terminal_cwd
if [ -n "$INSIDE_EMACS" ]; then
    # function to set the dired and host for ansiterm
    set_eterm_dir() {
        print -P "\033AnSiTu %n"
        print -P "\033AnSiTh" "$(hostname -f)"
        print -P "\033AnSiTc %d"

    # call prmptcmd whenever prompt is redrawn
    precmd_functions=($precmd_functions set_eterm_dir)

This just sources everything in the /etc/profile.d dir. PREFIX can be used to reference the Nix output dir.

export PREFIX=@out@

This will source everything in /etc/profile.d.

if [ -d @out@/etc/profile.d ]; then
  for i in @out@/etc/profile.d/*.sh; do
    if [ -r $i ]; then
      source $i


Some basic gitignore paths.



        editor = emacsclient
        excludesfile = @gitignore@

        gpgSign = true

        program = "@gnupg@/bin/gpg"

        default = simple

        rebase = true

        amend = commit --amend

        autcorrect = 1

        ui = true


This file provides site-specific paths. However, it must be substituted in Nix before we can actually run it in Emacs. To prevent Emacs from trying to run this, I’ve set the syntax to text.

(require 'set-defaults)
(require 'subr-x)

output-directory points to the nix-profile directory created by Nix. Ideally, this could point to a Nix store path, but the order of building means that we don’t know this until too late.

(defvar output-directory (expand-file-name ".nix-profile" (getenv "HOME")))

Setup exec-path.

(setq exec-path (append `(,(expand-file-name "bin" output-directory)
                          "/usr/sbin" "/usr/bin" "/sbin" "/bin")

Setup man-path.

(defvar man-path `("/usr/share/man"
                   ,(expand-file-name "share/man" output-directory)))

This will setup cacert-file var,

(defcustom cacert-file "/etc/ssl/certs/ca-bundle.crt"
  "Path for SSL certificates."
  :group 'environment)

Set env vars provided by Nix,

 ;; `("NIX_SSL_CERT_FILE" ,cacert-file)
 `("NIX_PATH" "nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixpkgs")
 `("NIX_REMOTE" "daemon")
 `("PATH" ,(string-join exec-path ":"))
 `("MANPATH" ,(string-join man-path ":"))

Set paths provided by Nix,

 '(company-cmake-executable "@cmake@/bin/cmake")
 '(doc-view-dvipdf-program "@ghostscript@/bin/dvipdf")
 '(cacert-file "@cacert@/etc/ssl/certs/ca-bundle.crt")
 '(calc-gnuplot-name "@gnuplot@/bin/gnuplot")
 '(gnuplot-program "@gnuplot@/bin/gnuplot")
 '(doc-view-ps2pdf-program "@ghostscript@/bin/ps2pdf")
 '(dired-touch-program "@coreutils@/bin/touch")
 '(dired-chmod-program "@coreutils@/bin/chmod")
 '(dired-chown-program "@coreutils@/bin/chown")
 '(dired-free-space-program "@coreutils@/bin/df")
 '(diff-command "@diffutils@/bin/diff")
 '(find-program "@findutils@/bin/find")
 '(epg-gpg-program "@gpg@/bin/gpg")
 '(epg-gpgconf-program "@gpg@/bin/gpgconf")
 '(epg-gpgsm-program "@gpg@/bin/gpgsm")
 '(flycheck-sh-bash-executable "@bash@/bin/bash")
 '(flycheck-sh-zsh-executable "@zsh@/bin/zsh")
 '(flycheck-perl-executable "@perl@/bin/perl")
 '(flycheck-go-golint-executable "@golint@/bin/golint")
 '(flycheck-haskell-hlint-executable "@hlint@/bin/hlint")
 '(flycheck-python-flake8-executable "@flake8@/bin/flake8")
 '(flycheck-asciidoc-executable "@asciidoc@/bin/asciidoc")
 '(flycheck-less-executable "@lessc@/bin/lessc")
 '(flycheck-c/c++-gcc-executable "@gcc@/bin/gcc")
 '(flycheck-javascript-eslint-executable "@eslint@/bin/eslint")
 '(flycheck-javascript-jshint-executable "@jshint@/bin/jshint")
 '(flycheck-go-build-executable "@go@/bin/go")
 '(flycheck-go-test-executable "@go@/bin/go")
 '(flycheck-lua-executable "@lua@/bin/luac")
 '(flycheck-xml-xmllint-executable "@libxml2@/bin/xmllint")
 '(flycheck-perl-perlcritic-executable "@perlcritic@/bin/perlcritic")
 '(flycheck-html-tidy-executable "@tidy@/bin/tidy")
 '(fortune-dir "@fortune@/share/games/fortunes")
 '(fortune-file "@fortune@/share/games/fortunes/food")
 '(grep-program "@gnugrep@/bin/grep")
 '(haskell-check-command "@hlint@/bin/hlint")
 '(haskell-hoogle-command "@hoogle@/bin/hoogle")
 '(haskell-hasktags-path "@hasktags@/bin/hasktags")
 '(haskell-mode-stylish-haskell-path "@stylish@/bin/stylish-haskell")
 '(ispell-program-name "@aspell@/bin/aspell")
 '(ispell-grep-command "@gnugrep@/bin/grep")
 '(irony-cmake-executable "@cmake@/bin/cmake")
 '(jka-compr-info-compress-program "@ncompress@/bin/compress")
 '(jka-compr-info-uncompress-program "@ncompress@/bin/uncompress")
 '(irony-server-install-prefix "@irony@")
 '(jka-compr-dd-program "@coreutils@/bin/dd")
 '(magit-git-executable "@git@/bin/git")
 '(markdown-command "@markdown2@/bin/markdown2")
 '(manual-program "@man@/bin/man")
 '(man-awk-command "@gawk@/bin/awk")
 '(man-sed-command "@gnused@/bin/sed")
 '(man-untabify-command "@coreutils@/bin/pr")
 '(nethack-executable "@nethack@/bin/nethack")
 '(org-pandoc-command "@pandoc@/bin/pandoc")
 '(pandoc-binary "@pandoc@/bin/pandoc")
 '(remote-shell-program "@openssh@/bin/ssh")
 '(ripgrep-executable "@ripgrep@/bin/rg")
 '(rtags-path "@rtags@/bin")
 '(sql-ingres-program "@parallel@/bin/sql")
 '(sql-interbase-program "@unixODBC@/bin/isql")
 '(sql-mysql-program "@mariadb@/bin/mysql")
 '(sql-ms-program "@freetds@/bin/osql")
 '(sql-postgres-program "@freetds@/bin/osql")
 '(sql-sqlite-program "@sqliteInteractive@/bin/sqlite3")
 '(tramp-encoding-shell "@bash@/bin/sh")
 '(tex-shell "@bash@/bin/sh")
 '(xargs-program "@findutils@/bin/xargs")
 '(vc-git-program "@git@/bin/git")
 '(gnutls "@gnutls@/bin/gnutls-cli")
 '(pdf2dsc-command "@ghostscript@/bin/pdf2dsc")
 '(preview-gs-command "@texlive@/bin/rungs")
 '(TeX-command "@texlive@/bin/tex")
 '(LaTeX-command "@texlive@/bin/latex")
 '(latex-run-command "@texlive@/bin/latex")
 '(tex-run-command "@texlive@/bin/tex")
 '(luatex-command "@texlive@/bin/luatex")
 '(xetex-command "@texlive@/bin/xetex")
 '(xelatex-command "@texlive@/bin/xelatex")
 '(makeinfo-command "@texinfoInteractive@/bin/makeinfo")
 '(pdftex-command "@texlive@/bin/pdftex")
 '(context-command "@texlive@/bin/context")
 '(bibtex-command "@texlive@/bin/bibtex")
 '(dvipdfmx-command "@texlive@/bin/dvipdfmx")
 '(makeindex-command "@texlive@/bin/makeindex")
 '(chktex-command "@texlive@/bin/chktex")
 '(lacheck-command "@texlive@/bin/lacheck")
 '(dvipdfmx-command "@texlive@/bin/dvipdfmx")
 '(dvips-command "@texlive@/bin/dvips")
 '(dvipng-command "@texlive@/bin/dvipng")
 '(ps2pdf-command "@ghostscript@/bin/ps2pdf")
 '(locate-executable "@findutils@/bin/locate")
 '(ag-executable "@ag@/bin/ag")
 '(notmuch-command "@notmuch@/bin/notmuch")

Set some defaults that depend on the path variables below,

 '(imap-ssl-program `(,(concat gnutls " --tofu -p %p %s")))
 '(tls-program (concat gnutls " --tofu -p %p %h"))
   (concat pdf2dsc-command " %s.pdf %m/preview.dsc"))
   (concat dvips-command " -Pwww %d -o %m/"))
   (concat dvips-command " -Pwww %d -o %m/"))
   (concat dvipng-command
           " -picky -noghostscript %d -o \"%m/prev%%03d.png\""))
 '(TeX-engine-alist `((xetex "XeTeX" xetex-command xelatex-command
                      (luatex "LuaTeX" luatex-command
                              ,(concat luatex-command " --jobname=%s")
      "%(PDF)%(tex) %(file-line-error) %(extraopts) %`%S%(PDFout)%(mode)%' %t"
      TeX-run-TeX nil
      (plain-tex-mode ams-tex-mode texinfo-mode)
      :help "Run plain TeX")
     ("LaTeX" "%`%l%(mode)%' %t" TeX-run-TeX nil
      (latex-mode doctex-mode)
      :help "Run LaTeX")
     ("Makeinfo" ,(concat makeinfo-command " %(extraopts) %t")
      TeX-run-compile nil
      :help "Run Makeinfo with Info output")
     ("Makeinfo HTML" ,(concat makeinfo-command " %(extraopts) --html %t")
      TeX-run-compile nil
      :help "Run Makeinfo with HTML output")
      ,(concat pdftex-command " %(PDFout) %(extraopts) %`%S%(mode)%' %t")
      TeX-run-TeX nil
      :help "Run AMSTeX")
      ,(concat context-command " --once --texutil %(extraopts) %(execopts)%t")
      TeX-run-TeX nil
      :help "Run ConTeXt once")
     ("ConTeXt Full" ,(concat context-command " %(extraopts) %(execopts)%t")
      TeX-run-TeX nil
      :help "Run ConTeXt until completion")
     ("BibTeX" ,(concat bibtex-command " %s")
      TeX-run-BibTeX nil t :help "Run BibTeX")
     ("Biber" "biber %s" TeX-run-Biber nil t :help "Run Biber")
     ("View" "%V" TeX-run-discard-or-function t t :help "Run Viewer")
     ("Print" "%p" TeX-run-command t t :help "Print the file")
     ("Queue" "%q" TeX-run-background nil t
      :help "View the printer queue" :visible TeX-queue-command)
     ("File" ,(concat dvips-command " %d -o %f ")
      TeX-run-dvips t t :help "Generate PostScript file")
     ("Dvips" ,(concat dvips-command " %d -o %f ")
      TeX-run-dvips nil t :help "Convert DVI file to PostScript")
     ("Dvipdfmx" ,(concat dvipdfmx-command " %d")
      TeX-run-dvipdfmx nil t :help "Convert DVI file to PDF with dvipdfmx")
     ("Ps2pdf" ,(concat ps2pdf-command " %f")
      TeX-run-ps2pdf nil t :help "Convert PostScript file to PDF")
     ("Index" ,(concat makeindex-command " %s")
      TeX-run-index nil t :help "Run makeindex to create index file")
     ("upMendex" "upmendex %s"
      TeX-run-index t t :help "Run mendex to create index file")
     ("Xindy" "xindy %s"
      TeX-run-command nil t :help "Run xindy to create index file")
     ("Check" ,(concat lacheck-command " %s") TeX-run-compile nil
      :help "Check LaTeX file for correctness")
     ("ChkTeX" ,(concat chktex-command " -v6 %s") TeX-run-compile nil
      :help "Check LaTeX file for common mistakes")
     ("Spell" "(TeX-ispell-document \"\")"
      TeX-run-function nil t :help "Spell-check the document")
     ("Clean" "TeX-clean"
      TeX-run-function nil t :help "Delete generated intermediate files")
     ("Clean All" "(TeX-clean t)" TeX-run-function nil t
      :help "Delete generated intermediate and output files")
     ("Other" "" TeX-run-command t t :help "Run an arbitrary command")))
   (concat ripgrep-executable
           " -i -M 120 --no-heading --line-number --color never '%s' %s"))
   (concat ripgrep-executable " -i --no-heading --line-number %s ."))
 '(counsel-ag-base-command (concat ag-executable " --nocolor --nogroup %s"))
   `((dvipng :programs ("latex" "dvipng")
             :description "dvi > png"
             :message ""
             :image-input-type "dvi"
             :image-output-type "png"
             :image-size-adjust (1.0 . 1.0)
             (,(concat LaTeX-command
                       " -interaction nonstopmode -output-directory %o %f"))
             (,(concat dvipng-command
                       " -fg %F -bg %B -D %D -T tight -o %O %f")))))
 '(Info-directory-list `(,(expand-file-name "share/info" output-directory)))
 '(tramp-remote-path `(tramp-own-remote-path
                       ,(expand-file-name "bin" output-directory)
 '(woman-manpath man-path)
   `("@sysheaders@/include" "/usr/include" "/usr/local/include"))
 '(ffap-c-path '("@sysheaders@/include" "/usr/include" "/usr/local/include"))
 '(rng-schema-locating-files `("schemas.xml"
                                 "schema/schemas.xml" data-directory)
 '(irony-additional-clang-options '("-I@sysheaders@/include"

This is the bootstrap script that is mentioned above. We use it to install the IDE. It ensures Nix is installed and that the Git repo is available and up-to-date.

First we install Nix if it isn’t already.

if ! command -v nix-env >/dev/null 2>&1; then
    curl -s > $nix_installer
    sh $nix_installer

If we are in a Git repo already, we’ll pull to get latest updates.

if [ -d .git ] && command -v git >/dev/null 2>&1; then
    git pull origin master || true

If we can’t find default.nix then we’ll clone from GitHub. This will be stored in ~/.local/share/bauer.

if ! [ -f default.nix ] && command -v git >/dev/null 2>&1; then
    repo_dir=$(mktemp -d)
    git clone $repo_dir
    cd $repo_dir

The last action is to install our new derivation.

nix-env -if .

Cross-platform script to execute app.

case $(uname) in
        open @out@/Applications/


default.nix: the tangler

{ nixpkgs-url ? ""
, nixpkgs ? builtins.fetchTarball nixpkgs-url
, nixpkgs' ? import nixpkgs {}
}: with nixpkgs';

Now let’s tangle…

import (runCommand "README" { buildInputs = [ emacs ]; } ''
  mkdir -p $out
  cd $out
  cp -r ${./site-lisp} site-lisp
  cp ${./}
  emacs --batch --quick \
  -l ob-tangle \
  --eval "(org-babel-tangle-file \"\")"
  cp bauer.nix default.nix
'') {inherit nixpkgs';}

bauer.nix: the build script

nixpkgs should just be a Nix path to the Nixpkgs package set. nixpkgs' will be nixpkgs instantiated.

{ nixpkgs-url ? ""
, nixpkgs ? builtins.fetchTarball nixpkgs-url
, nixpkgs' ? import nixpkgs {}
}: with nixpkgs'; let

We start out by defining the Nix version to use. nixStable corresponds to the last released version, 1.11. nixUnstable is currently at Nix 2.0 which should soon be released.

nix = nixStable;

Next we start defining some packages. R is one of the simpler ones right now, so let’s start with that.

rEnv = pkgs.rWrapper.override {
  packages = with pkgs.rPackages; [

Here we define our package set. This will just give us access to all of the Emacs packages defined in Nixpkgs.

We also define our Emacs version to use. Mitsuharo’s Emacs package is much better for MacOS so we use that when we’re on Darwin systems. Otherwise, just default to ‘emacs’ which should be the latest (Nixpkgs-unstable has version 25.3 currently).

Emacs 26 is provided courtesy of @jwiegley’s nix-config repo.

inherit (import ((fetchFromGitHub {
  owner = "jwiegley";
  repo = "nix-config";
  rev = "e5649602dc89f944e0444a88d8526b19b965bccb";
  sha256 = "1i6bj41ddvlfwyi32gp917lapq1pb9zpa5z5qskwngd5s4j9vv5b";
}) + "/overlays/10-emacs.nix") self pkgs) emacs26;
customEmacsPackages = emacsPackagesNg.overrideScope (super: self: {
  emacs = emacs26;

Next, we define aspell with the English language. This is used by Emacs ispell.

myAspell = aspellWithDicts (ps : with ps; [ en ]);

Tex live provides some LaTeX commads for us.

myTex = texlive.combine {
  inherit (texlive) xetex setspace
    fontspec chktex enumitem xifthen
    ifmtarg filehook wrapfig inconsolata
    upquote minted lastpage collection-basic
    collection-binextra collection-context
    collection-fontsrecommended collection-fontutils
    collection-langenglish collection-latex
    collection-latexrecommended collection-luatex
    collection-metapost collection-texworks
    collection-xetex capt-of ulem hyperref titlesec;

Emacs configuration

Here, we start building up the site-paths.el file. This does a simple substitution of all the attributes set.

  site-paths = runCommand "site-paths.el" {
    inherit rtags ripgrep ag ant nethack fortune gnutls
      coreutils findutils openssh git bash
      zsh perl golint perlcritic
      go asciidoc lessc stack
      lua gcc bashInteractive diffutils
      pandoc clang cmake ghostscript
      gnugrep man gawk gnused
      sqliteInteractive freetds mariadb
      parallel unixODBC ncompress
      texinfoInteractive cacert notmuch gnuplot;
    inherit (customEmacsPackages) emacs;
    inherit (pythonPackages) flake8;
    inherit (nodePackages) jshint eslint;
    inherit (haskellPackages) hoogle hlint hasktags;
    inherit manpages sysheaders sysframeworks schemas;
    stylish = haskellPackages.stylish-haskell;
    # intero = haskellPackages.intero-nix-shim;
    texlive = myTex;
    markdown2 = pythonPackages.markdown2;
    tidy = html-tidy;
    irony = irony-server;
    libxml2 = libxml2.bin;
    gpg = gnupg1compat;
    # jdeeserver = jdee-server;
    aspell = myAspell;
    # orginit = ./org-init.el;
  } ''
cp ${./} $out
substituteAllInPlace $out

Emacs building can be divided into phases. Each phase will run through the Elisp once.

  • Phase 1: picking up dependencies

    myPackages gets a listing of all of the packages that are needed by the Emacs configuration. use-package-list generates this list automatically.

    package-list =
      runCommand "package-list" {
        buildInputs = [ customEmacsPackages.emacs ];
      } ''
    emacs --batch --quick \
          -L ${customEmacsPackages.melpaPackages.use-package}/share/emacs/site-lisp/elpa/use-package-* \
          -L ${customEmacsPackages.elpaPackages.delight}/share/emacs/site-lisp/elpa/delight-* \
          -L ${./site-lisp} -l use-package-list \
          --eval "(use-package-list \"${./README.el}\")" > $out
    myPackages = builtins.fromJSON (builtins.readFile package-list);
  • Phase 2: byte compiling
    default = runCommand "default.el" {
      buildInputs = [customEmacsPackages.emacs];
    } ''
    mkdir -p $out/share/emacs/site-lisp
    cp ${./README.el} $out/share/emacs/site-lisp/default.el
    cp ${site-paths} $out/share/emacs/site-lisp/site-paths.el
    cp ${./site-lisp/em-dired.el} $out/share/emacs/site-lisp/em-dired.el
    cp ${./site-lisp/dired-column.el} \
    cp ${./site-lisp/macho-mode.el} $out/share/emacs/site-lisp/macho-mode.el
    cp ${./site-lisp/nethack.el} $out/share/emacs/site-lisp/nethack.el
    cp ${./site-lisp/set-defaults.el} \
    cp ${./site-lisp/installer.el} $out/share/emacs/site-lisp/installer.el
    cp ${./site-lisp/restart-emacs.el} \
    cp ${./site-lisp/use-package-list.el} \
    cp ${./site-lisp/bauer.el} \
    cp ${./site-lisp/bind-key.el} \

    This is fairly complicated. What happens is we batch compile all of the .el files. The problem is the .el files all are going to depend on dependencies that we have just found in package-list. The solution is that complex eval below where we add all of the paths (and their requisites) to the load path. This works but is hacky and I am interested in fixing it.

    cd $out/share/emacs/site-lisp
    export HOME=$PWD
    emacs --batch --quick \
          --eval "(let ((default-directory \"${emacsWrapper
        ((requiredPackages customEmacsPackages myPackages) ++
          (with customEmacsPackages.melpaPackages;
           with customEmacsPackages.elpaPackages;
          [use-package delight]))
    }/share/emacs/site-lisp\")) (normal-top-level-add-subdirs-to-load-path))" \
          -L . -f batch-byte-compile *.el
  • Phase 3: wrapping into Emacs
    emacsWrapper = explicitRequires: runCommand "emacs-packages-deps"
     { inherit explicitRequires;
       inherit (xorg) lndir;
       inherit (customEmacsPackages) emacs; } ''
    findInputsOld() {
        local pkg="$1"; shift
        local var="$1"; shift
        local propagatedBuildInputsFiles=("$@")
        local varSlice="$var[*]"
        # ''${..-} to hack around old bash empty array problem
        case "''${!varSlice-}" in
      *" $pkg "*) return 0 ;;
        unset -v varSlice
        eval "$var"'+=("$pkg")'
        if ! [ -e "$pkg" ]; then
      echo "build input $pkg does not exist" >&2
      exit 1
        local file
        for file in "''${propagatedBuildInputsFiles[@]}"; do
      [[ -f "$file" ]] || continue
      local pkgNext
      for pkgNext in $(< "$file"); do
          findInputsOld "$pkgNext" "$var" \
    mkdir -p $out/bin
    mkdir -p $out/share/emacs/site-lisp
    local requires
    for pkg in $explicitRequires; do
      findInputsOld $pkg requires propagated-user-env-packages
    linkPath() {
      local pkg=$1
      local origin_path=$2
      local dest_path=$3
      # Add the path to the search path list, but only if it exists
      if [[ -d "$pkg/$origin_path" ]]; then
        $lndir/bin/lndir -silent "$pkg/$origin_path" "$out/$dest_path"
    linkEmacsPackage() {
      linkPath "$1" "bin" "bin"
      linkPath "$1" "share/emacs/site-lisp" "share/emacs/site-lisp"
    # Iterate over the array of inputs (avoiding nix's own interpolation)
    for pkg in "''${requires[@]}"; do
      linkEmacsPackage $pkg
    # Byte-compiling improves start-up time only slightly, but costs nothing.
    $emacs/bin/emacs --batch -f batch-byte-compile "$siteStart"

    requiredPackages is a function that takes two arguments.

    requiredPackages = epkgs: map (x:
      if builtins.hasAttr x epkgs.melpaPackages
        then builtins.getAttr x epkgs.melpaPackages
        else if builtins.hasAttr x epkgs then builtins.getAttr x epkgs
        else if builtins.hasAttr x emacsPackages
        then builtins.getAttr x emacsPackages
        else if builtins.hasAttr x epkgs.elpaPackages
        then builtins.getAttr x epkgs.elpaPackages
        else builtins.getAttr x pkgs);

    Now we build our Emacs distribution.

    myEmacs = customEmacsPackages.emacsWithPackages (epkgs:
      (requiredPackages epkgs myPackages)
      ++ [default epkgs.melpaPackages.use-package]

The environment

Finally, we can actually build the environment.

manpages = buildEnv {
  name = "man-pages";
  paths = [posix_man_pages] # stdmanpages
    ++ stdenv.lib.optional stdenv.isDarwin darwin.apple_sdk.sdk
    ++ stdenv.lib.optional stdenv.isLinux man-pages;

sysheaders = buildEnv {
  name = "headers";
  paths = [ libcxx ]
    ++ stdenv.lib.optional stdenv.isDarwin darwin.apple_sdk.sdk;

sysframeworks = buildEnv {
  name = "frameworks";
  paths = stdenv.lib.optional stdenv.isDarwin darwin.apple_sdk.sdk;

schemas = writeText "schemas.xml" ''
<locatingRules xmlns="">
  <documentElement localName="section" typeId="DocBook"/>
  <documentElement localName="chapter" typeId="DocBook"/>
  <documentElement localName="article" typeId="DocBook"/>
  <documentElement localName="book" typeId="DocBook"/>
  <typeId id="DocBook" uri="${docbook5}/xml/rng/docbook/docbookxi.rnc" />
myEnv = buildEnv {
  buildInputs = [ makeWrapper ];
  postBuild = ''
if [ -w $out/share/info ]; then
  shopt -s nullglob
  for i in $out/share/info/*.info $out/share/info/*.info.gz; do # */
    ${texinfoInteractive}/bin/install-info $i $out/share/info/dir

mkdir -p $out/etc

cp ${./gitconfig} $out/etc/gitconfig
substituteInPlace $out/etc/gitconfig \
  --replace @gitignore@ ${./gitignore} \
  --replace @gnupg@ ${gnupg1compat}/bin/gpg \
  --replace @out@ $out

cp ${./} $out/etc/bashrc
substituteInPlace $out/etc/bashrc \
  --replace @fortune@ ${fortune} \
  --replace @out@ $out

cp ${./} $out/etc/.zshrc
substituteInPlace $out/etc/.zshrc \
  --replace @zsh-autosuggestions@ ${zsh-autosuggestions} \
  --replace @fortune@ ${fortune} \
  --replace @out@ $out
cp $out/etc/.zshrc $out/etc/zshrc

cp ${./} $out/etc/profile
substituteInPlace $out/etc/profile \
  --replace @manpages@ ${manpages} \
  --replace @out@ $out

wrapProgram $out/bin/bash \
  --add-flags "--rcfile $out/etc/bashrc"

wrapProgram $out/bin/zsh \
  --set ZDOTDIR $out/etc

cp ${./} $out/bin/run
substituteInPlace $out/bin/run \
  --replace @out@ $out
ln -s $out/bin/run $out/bin/bauer
  pathsToLink = [

  extraOutputsToInstall = [ "man" "info" "doc" "devdoc" "devman" ];
  name = "bauer";

  paths = [
    (runCommand "my-profile" { buildInputs = [makeWrapper]; } ''
mkdir -p $out/etc/profile.d
cp ${./} $out/etc/profile.d/
substituteInPlace $out/etc/profile.d/ \
  --replace @emacs@ ${myEmacs} \
  --replace @fortune@ ${fortune} \
  --replace @manpages@ ${manpages} \
  --replace @cacert@ ${cacert}
    ++ [bashInteractive zsh coreutils git wget
  gawk gnused gzip gnutar gnupg1compat xz cacert gnuplot]
    # info pages
    ++ [ed lzip libmpc flex autoconf bison
  which bc gnum4 time gnutls aspell]
    ++ [nox nix nix-index nix-zsh-completions nix-bash-completions]
    ++ [gitAndTools.hub]
    ++ [nodePackages.tern isync notmuch graphviz]
    ++ [stack ghc]
#   ++ (with haskellPackages; [ghc-mod])
in myEnv

Invoking it

We can build it with nix-build.


Continuous integration

We’ll set up Travis support here. We start by configuring .travis.yml.


language: nix

Next we’ll set up nix-build and pass the URL of Nixpkgs with the NIXPKGS.

  - nix-build -Q --argstr nixpkgs-url $NIXPKGS

Setup the OSs. Sadly no Windows support yet.

  depth: 1
sudo: false
  - linux
  - osx

Setup some values for NIXPKGS variables.


Configure the matrix…

    - os: linux
    - os: osx
    - os: osx

Setup the cache.

    - /nix/store

Turn off those annoying Travis notifications.

  email: false


This is a simple script that I use to make sure I’ve updated the generated files.

emacs --batch \
      -l ob-tangle --eval "(org-babel-tangle-file \"\")"


If you end up with generated files, they’re easy to remove with Git. Just run git clean -xdf and it will remove all of the files that match the .gitignore rules (which should never be added to the git tree).

These set up some paths for .gitignore that we don’t want getting put in the repo. Start with Emacs/org-mode/LaTeX stuff.


Nix-related stuff.

# nix stuff

These are all tangled by



This file is just for convenience if you wanted to build with the Nixpkgs in your channel. Having this file available means you can place this repo in ~/.nixpkgs and Nixpkgs will have the package userPackages available.

  packageOverrides = pkgs: with pkgs; rec {
    bauer = import ./default.nix { nixpkgs' = pkgs; };
    userPackages = bauer;


config.nix linguist-generated=true
default.nix linguist-generated=true linguist-generated=true linguist-generated=true
.travis.yml linguist-generated=true
.gitattributes linguist-generated=true
.gitignore linguist-generated=true

Author: Matthew Bauer


Created: 2018-03-01 Thu 11:43

Emacs 24.5.1 (Org mode 9.1.7)