Matthew BauerBlogRSS

05 Nov 2017

bauer: an Emacs+Nix IDE

Note: the updated version of this is available at

I’m publishing my IDE as a blog post. It’s all written in Org Babel.

This file generates an Emacs configuration. It can be considered an ‘Emacs+Nix’ IDE. That is, the Emacs configuration is integrated with hardcoded Nix store paths. This provides a kind of functional Emacs configuration.



If you already have Nix installed, you can demo this out really easily. Just run the following,

url="" \
expr='import (builtins.fetchTarball "$url")' \
   nix-shell -p nix-bundle --run 'nix-run "$expr"'


Run this from your shell:

curl | sh

Alternatively, you can accomplish the same thing from Emacs by downloading installer.el, loading it (M‑x load‑file<RET>) and running ‘install’ (M‑x install<RET>). This process will take a little bit as everything is downloaded. After you restart Emacs, everything should be working.


To make changes to the IDE, it is recommended you setup your environment like so,

git clone ~/bauer
cd ~/bauer

The last line will spawn an Emacs frame in the Git repo. Make any changes you want to this document or any of the files in the list folder. Make sure you commit your changes afterward by typing C-c p v, then c-ac (using Magit, of course). If you have forked this repo on GitHub, you can add it by typing Mg then 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 welcome through GitHub!

w/o Nix usage

You can use bauer without Nix. This just gives you the unintegrated Emacs configuration. To get started, run the following.

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

Then, add the following to your Emacs init file,

(defvar bauer-dir (expand-file-name ".nixpkgs" (getenv "HOME")))
(defvar bauer-org (expand-file-name "" bauer-dir))
(setq package-enable-at-startup nil)
(autoload 'org-babel-tangle-file "ob-tangle")
(if (locate-library "default")
    (unless (featurep 'default) (load "default" t))
  (let ((default-directory bauer-dir))
    (add-to-list 'load-path (expand-file-name "lisp" bauer-dir))
    (org-babel-tangle-file bauer-org "README.el" "emacs-lisp")
    (load-file (expand-file-name "README.el" bauer-dir))))

Emacs Init file

This is the main file of the IDE. It will be loaded by Emacs on startup.

Verify Emacs version ≥ 25

Emacs 24 is unsupported currently. This will check to make sure Emacs 25+ is available.

(unless (>= emacs-major-version 25)
  (error "Need Emacs 25+ to work properly"))

EXPERIMENTAL Emacs 24 support

If Nix is unavailable and only Emacs 24 is install for you, then you can try the experimental v24 branch. From the Git root of this repository, just run:

git checkout v24

and make sure you have setup the Nix-less version in your init.el file (see w/o Nix usage directions).

Increase GC

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


(autoload 'tramp-tramp-file-p "tramp")
(eval-and-compile (autoload 'use-package-autoload-keymap "use-package"))
(autoload 'package-installed-p "package")

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).

 '(TeX-auto-save t)
 '(TeX-engine 'xetex)
 '(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)
 '(backward-delete-char-untabify-method 'hungry)
 '(backup-directory-alist `(("." .
                             ,(expand-file-name "backup"
 '(bookmark-save-flag t)
 '(c-syntactic-indentation nil)
 '(comint-process-echoes t)
 '(comint-input-ignoredups t)
 '(comint-prompt-read-only t)
 '(comint-scroll-show-maximum-output nil)
 '(company-auto-complete (lambda () (and (company-tooltip-visible-p)
 '(company-frontends '(company-pseudo-tooltip-unless-just-one-frontend
   '(not save-buffer
 '(company-require-match nil)
 '(company-selection-wrap-around t)
 '(compilation-always-kill t)
 '(compilation-ask-about-save nil)
 '(compilation-auto-jump-to-first-error nil)
 '(compilation-environment '("TERM=xterm-256color"))
 '(compilation-scroll-output nil)
 '(compilation-skip-threshold 2)
 '(completions-format 'vertical)
 '(completion-cycle-threshold 5)
 '(counsel-find-file-at-point t)
 '(counsel-mode-override-describe-bindings t)
 '(create-lockfiles nil)
 '(cursor-in-non-selected-windows nil)
 '(custom-safe-themes t)
 '(custom-buffer-done-kill t)
 '(custom-file (expand-file-name "settings.el" user-emacs-directory))
 '(custom-search-field nil)
 '(create-lockfiles nil)
 '(checkdoc-spellcheck-documentation-flag t)
 '(delete-old-versions t)
 '(delete-by-moving-to-trash t)
 '(dired-auto-revert-buffer t)
 '(dired-hide-details-hide-symlink-targets nil)
 '(dired-dwim-target t)
 '(dired-listing-switches "-alhv")
 '(dired-omit-verbose nil)
 '(dired-omit-files "^\\.")
 '(dired-recursive-copies 'always)
 '(dired-recursive-deletes 'always)
 '(dired-subtree-line-prefix " ")
 '(dtrt-indent-verbosity 0)
 '(disabled-command-function nil)
 '(display-buffer-reuse-frames t)
 '(echo-keystrokes 0)
 '(enable-recursive-minibuffers t)
 '(erc-autoaway-idle-seconds 600)
 '(erc-autojoin-timing 'ident)
 '(erc-fill-prefix "          ")
 '(erc-insert-timestamp-function 'erc-insert-timestamp-left)
 '(erc-interpret-mirc-color t)
 '(erc-kill-buffer-on-part t)
 '(erc-kill-queries-on-quit t)
 '(erc-kill-server-buffer-on-quit t)
 '(erc-prompt (lambda nil (concat "[" (buffer-name) "]")))
 '(erc-prompt-for-password nil)
 '(erc-query-display 'buffer)
 '(erc-server-coding-system '(utf-8 . utf-8))
 '(erc-timestamp-format "%H:%M ")
 '(erc-timestamp-only-if-changed-flag nil)
 '(erc-try-new-nick-p nil)
 '(eshell-banner-message "")
 '(eshell-cd-on-directory t)
 '(eshell-cmpl-autolist t)
 '(eshell-cmpl-cycle-completions nil)
 '(eshell-cmpl-cycle-cutoff-length 2)
 '(eshell-cmpl-ignore-case t)
 '(eshell-cp-interactive-query t)
 '(eshell-cp-overwrite-files nil)
 '(eshell-default-target-is-dot t)
 '(eshell-destroy-buffer-when-process-dies t)
 '(eshell-highlight-prompt t)
 '(eshell-hist-ignoredups t)
 '(eshell-history-size 10000)
 '(eshell-list-files-after-cd t)
 '(eshell-ln-interactive-query t)
 '(eshell-mv-interactive-query t)
 '(eshell-output-filter-functions '(eshell-handle-ansi-color
 '(eshell-plain-echo-behavior nil)
 '(eshell-review-quick-commands t)
 '(eshell-rm-interactive-query t)
   (lambda () (concat
               (when (tramp-tramp-file-p default-directory)
                   (tramp-dissect-file-name default-directory))
                  (tramp-file-name-real-host (tramp-dissect-file-name
                  " "))
               (let ((dir (eshell/pwd)))
                 (if (string= dir (getenv "HOME")) "~"
                   (let ((dirname (file-name-nondirectory dir)))
                     (if (string= dirname "") "/" dirname))))
               (if (= (user-uid) 0) " # " " $ "))))
   '("vi" "screen" "top" "less" "more" "lynx" "ncftp" "pine" "tin" "trn" "elm"
     "nano" "nethack" "telnet" "emacs" "emacsclient" "htop" "w3m" "links" "lynx"
     "elinks" "irrsi" "mutt" "finch" "newsbeuter" "pianobar"))
 '(eval-expression-print-length 20)
 '(eval-expression-print-level nil)
 '(explicit-shell-args '("-c" "export EMACS= INSIDE_EMACS=; stty echo; shell"))
 '(expand-region-contract-fast-key "j")
 '(fill-column 80)
 '(flycheck-check-syntax-automatically '(save
 '(flycheck-idle-change-delay 0.001)
 '(flycheck-standard-error-navigation nil)
 '(flycheck-global-modes '(not erc-mode
 '(flyspell-abbrev-p nil)
 '(flyspell-auto-correct nil)
 '(flyspell-highlight-properties nil)
 '(flyspell-incorrect-hook nil)
 '(flyspell-issue-welcome-flag nil)
 '(frame-title-format '(:eval
                        (if (buffer-file-name)
                            (abbreviate-file-name (buffer-file-name))
 '(global-auto-revert-non-file-buffers t)
 '(highlight-nonselected-windows nil)
 '(hideshowvis-ignore-same-line nil)
 '(history-delete-duplicates t)
 '(history-length 20000)
 '(hippie-expand-verbose nil)
 '(iedit-toggle-key-default nil)
 '(imenu-auto-rescan t)
 '(indicate-empty-lines t)
 '(indent-tabs-mode nil)
 '(inhibit-startup-screen t)
 '(inhibit-startup-echo-area-message t)
 '(initial-major-mode 'fundamental-mode)
 '(initial-scratch-message "")
 '(ispell-extra-args '("--sug-mode=ultra"))
 '(ispell-silently-savep t)
 '(ispell-quietly t)
 '(ivy-count-format "\"\"")
 '(ivy-display-style nil)
 '(ivy-minibuffer-faces nil)
 '(ivy-use-virtual-buffers t)
 '(ivy-fixed-height-minibuffer 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-option-key-is-meta nil)
 '(mac-option-modifier 'super)
 '(mac-right-option-modifier nil)
 '(mac-frame-tabbing t)
 '(mac-system-move-file-to-trash-use-finder t)
 '(magit-log-auto-more t)
 '(magit-clone-set-remote\.pushDefault t)
 '(magit-diff-options nil)
 '(magit-display-buffer-function 'magit-display-buffer-fullframe-status-v1)
 '(magit-ediff-dwim-show-on-hunks t)
 '(magit-fetch-arguments nil)
 '(magit-highlight-trailing-whitespace nil)
 '(magit-highlight-whitespace nil)
 '(magit-no-confirm t)
 '(magit-process-connection-type nil)
 '(magit-process-find-password-functions '(magit-process-password-auth-source))
 '(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)
 '(minibuffer-prompt-properties '(read-only t
                                            cursor-intangible t
                                            face minibuffer-prompt))
 '(mwim-beginning-of-line-function 'beginning-of-line)
 '(mwim-end-of-line-function 'end-of-line)
 '(neo-theme 'arrow)
 '(neo-fixed-size nil)
 '(next-error-recenter t)
 '(notmuch-show-logo nil)
 '(nrepl-log-messages t)
 '(nsm-save-host-names t)
 '(ns-function-modifier 'hyper)
 '(ns-pop-up-frames nil)
 '(org-blank-before-new-entry '((heading) (plain-list-item)))
 '(org-export-in-background nil)
 '(org-log-done 'time)
 '(org-return-follows-link t)
 '(org-special-ctrl-a/e t)
 '(org-src-fontify-natively t)
 '(org-src-preserve-indentation t)
 '(org-src-tab-acts-natively t)
 '(org-support-shift-select t)
 '(parens-require-spaces t)
 '(package-archives '(("melpa-stable" . "")
                      ("melpa" . "")
                      ("org" . "")
                      ("gnu" . "")
 '(proof-splash-enable nil)
 '(projectile-globally-ignored-files '(".DS_Store" "TAGS"))
 '(projectile-enable-caching t)
   '(:eval (if (and (projectile-project-p)
                    (not (file-remote-p default-directory)))
               (format " Projectile[%s]" (projectile-project-name)) "")))
 '(projectile-ignored-project-function 'file-remote-p)
 '(projectile-switch-project-action 'projectile-dired)
 '(projectile-do-log nil)
 '(projectile-verbose nil)
 '(reb-re-syntax 'string)
 '(require-final-newline t)
 '(resize-mini-windows t)
 '(ring-bell-function 'ignore)
 '(rtags-completions-enabled t)
 '(rtags-imenu-syntax-highlighting 10)
 '(ruby-insert-encoding-magic-comment nil)
 '(sh-guess-basic-offset t)
   '("*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)
 '(auto-window-vscroll nil)
 '(hscroll-margin 5)
 '(hscroll-step 5)
 '(scroll-preserve-screen-position 'always)
 '(send-mail-function 'smtpmail-send-it)
 '(sentence-end-double-space nil)
 '(set-mark-command-repeat-pop t)
 '(shell-completion-execonly nil)
 '(shell-input-autoexpand nil)
 '(sp-autoskip-closing-pair 'always)
 '(sp-hybrid-kill-entire-symbol nil)
 '(truncate-lines nil)
 '(tab-always-indent 'complete)
 '(term-input-autoexpand t)
 '(term-input-ignoredups t)
 '(term-input-ring-file-name t)
 '(tramp-default-proxies-alist '(((regexp-quote (system-name)) nil nil)
                                 (nil "\\`root\\'" "/ssh:%h:")
                                 (".*" "\\`root\\'" "/ssh:%h:")))
 '(tramp-default-user nil)
 '(text-quoting-style 'quote)
 '(tls-checktrust t)
 '(undo-limit 800000)
 '(uniquify-after-kill-buffer-p t)
 '(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)
 '(use-package-expand-minimally nil)
 '(version-control t)
 '(vc-allow-async-revert t)
 '(vc-command-messages nil)
 '(vc-git-diff-switches '("-w" "-U3"))
 '(vc-follow-symlinks nil)
   (concat "\\(\\(\\`"
 '(view-read-only t)
 '(view-inhibit-help-message t)
 '(visible-bell nil)
 '(visible-cursor nil)
 '(woman-imenu t)
 '(whitespace-line-column 80)
 '(whitespace-auto-cleanup t)
 '(whitespace-rescan-timer-time nil)
 '(whitespace-silent t)
 '(whitespace-style '(face

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 derivations. Everything should work fine if you don’t have this available, though. If you are in Emacs and already have the IDE install you can inspect this file by typing M-: (find-file (locate-library "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" :noerror)

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 -nw")
 '("LANG" "en_US.UTF-8")
 '("LC_ALL" "en_US.UTF-8")
 '("PAGER" "cat")

Load custom file

This file allows users to override above defaults.

(load custom-file 'noerror)

Setup use-package

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.

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

Actually require use-package,

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

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

  (setq use-package-always-ensure 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).

(bind-key "C-c C-u"         'rename-uniquely)
(bind-key "C-x ~"           (lambda () (interactive) (find-file "~")))
(bind-key "C-x /"           (lambda () (interactive) (find-file "/")))
(bind-key "C-c C-o"         'browse-url-at-point)
(bind-key "H-l"             'browse-url-at-point)
(bind-key "C-x 5 3"         'iconify-frame)
(bind-key "C-x 5 4"         'toggle-frame-fullscreen)
(bind-key "s-SPC"           'cycle-spacing)
(bind-key "C-c w w"         'whitespace-mode)

(bind-key "<C-return>"      'other-window)
(bind-key "C-z"             'delete-other-windows)
(bind-key "M-g l"           'goto-line)
(bind-key "<C-M-backspace>" 'backward-kill-sexp)
(bind-key "C-x t"           'toggle-truncate-lines)
(bind-key "C-x v H"         'vc-region-history)
(bind-key "C-c SPC"         'just-one-space)
(bind-key "C-c f"           'flush-lines)
(bind-key "C-c o"           'customize-option)
(bind-key "C-c O"           'customize-group)
(bind-key "C-c F"           'customize-face)
(bind-key "C-c q"           'fill-region)
(bind-key "C-c s"           'replace-string)
(bind-key "C-c u"           'rename-uniquely)
(bind-key "C-c z"           'clean-buffer-list)
(bind-key "C-c ="           'count-matches)
(bind-key "C-c ;"           'comment-or-uncomment-region)
(bind-key "C-c n"           'clean-up-buffer-or-region)
(bind-key "C-c d"           'duplicate-current-line-or-region)
(bind-key "M-+"             'text-scale-increase)
(bind-key "M-_"             'text-scale-decrease)

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

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

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


These utils are needed at init stage and should always appear before other use-package declarations.

(use-package add-hooks
  :commands (add-hooks add-hooks-pair))
(use-package hook-helpers
  :commands (create-hook-helper
  :functions (make-hook-helper


Alphabetical listing of all Emacs packages needed by the IDE.

Rules: No packages on the top level should have the :demand keyword. Each package should be setup as either commands, hooks, modes, or key bindings. Defer timers are allowed but should be used sparingly. Currently, these packages need defer timers:

  • autorevert (1)
  • company (2)
  • delsel (2)
  • dtrt-indent (3)
  • flycheck (3)
  • savehist (4)
  • save-place (5)
  • which-key (3)
  • apropostriate (2)

    To resort, go to one of the package group headings and type C-c ^ (the shortcut for org-sort).


Some of these are included in Emacs, others aren’t. All of them are necessary for using Emacs as a full featured IDE.

  • ace window
    (use-package ace-window
      :bind (("M-o" . other-window)
             ([remap next-multiframe-window] . ace-window)))
  • aggressive-indent

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

    (use-package aggressive-indent
      :commands aggressive-indent-mode
      :init (add-hooks '(((emacs-lisp-mode
                           slime-repl-mode) . aggressive-indent-mode))))
  • 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)))
  • Company
    (use-package company
      :bind (:map company-active-map
                  ("TAB" .
                  ("<tab>" .
                  ("S-TAB" . company-select-previous)
                  ("<backtab>" . company-select-previous)
                  ("C-n" . company-select-next)
                  ("C-p" . company-select-previous)
      :commands (company-mode
      (setq company-backends
            '((company-css :with company-dabbrev)
              (company-nxml :with company-dabbrev)
              (company-elisp :with company-capf)
              (company-eshell-history :with company-capf company-files)
              (company-capf :with company-files company-keywords)
              (company-etags company-gtags company-clang company-cmake
                             :with company-dabbrev)
              (company-semantic :with company-dabbrev company-capf)
              (company-abbrev company-dabbrev company-keywords)
      (global-company-mode 1)
      (add-hook 'minibuffer-setup-hook 'company-mode)
      (add-hook 'minibuffer-setup-hook
                (lambda () (setq-local company-frontends
      (advice-add 'completion-at-point :override 'company-complete-common-or-cycle))
    • company-eshell-history
      (use-package company-eshell-history
        :ensure nil
        :commands company-eshell-history
    • company-statistics
      (use-package company-statistics
        :commands company-statistics-mode
        :init (add-hook 'company-mode-hook 'company-statistics-mode))
  • compile
    (use-package compile
      :ensure nil
      :bind (("C-c C-c" . compile)
             ("M-O" . show-compilation)
             :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))))
      (create-hook-helper compilation-ansi-color-process-output ()
        :hooks (compilation-filter-hook)
        (ansi-color-process-output nil)
        (set (make-local-variable 'comint-last-output-start)
  • Counsel
    (use-package counsel
      :commands (counsel-descbinds counsel-grep-or-swiper)
      :bind* (([remap execute-extended-command] . counsel-M-x)
              ([remap find-file] . counsel-find-file)
              ([remap describe-function] . counsel-describe-function)
              ([remap describe-variable] . counsel-describe-variable)
              ([remap info-lookup-symbol] . counsel-info-lookup-symbol)
              ("<f1> l" . counsel-find-library)
              ("C-c j" . counsel-git-grep)
              ("C-c k" . counsel-rg)
              ("C-x l" . counsel-locate)
              ("C-M-i" . counsel-imenu)
              ("M-y" . counsel-yank-pop)
              ("C-c i 8" . counsel-unicode-char)
      (bind-key* [remap isearch-forward] 'counsel-grep-or-swiper
                 (executable-find "grep"))
  • diff-hl
    (use-package diff-hl
      :commands (diff-hl-dir-mode diff-hl-mode diff-hl-magit-post-refresh
      :bind (:map diff-hl-mode-map
                  ("<left-fringe> <mouse-1>" . diff-hl-diff-goto-hunk))
      (add-hook 'prog-mode-hook 'diff-hl-mode)
      (add-hook 'vc-dir-mode-hook 'diff-hl-mode)
      (add-hook 'dired-mode-hook 'diff-hl-dir-mode)
      (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh)
  • dired
    (use-package dired
      :ensure nil
      :init (require 'dired)
      :bind (("C-c J" . dired-double-jump)
             :map dired-mode-map
             ("C-c C-c" . compile)
             ("r" . browse-url-of-dired-file)))
    • dired-column
      (use-package dired-column
        :ensure nil
        :after dired
        :bind (:map dired-mode-map
                    ("o" . dired-column-find-file)))
    • dired-imenu
      (use-package dired-imenu
        :after dired)
    • dired-subtree
      (use-package dired-subtree
        :after dired
        :bind (:map dired-mode-map
                    ("<tab>" . dired-subtree-toggle)
                    ("<backtab>" . dired-subtree-cycle)))
    • dired-x
      (use-package dired-x
        :ensure nil
        :after dired
        :commands (dired-omit-mode dired-hide-details-mode)
        (add-hook 'dired-mode-hook 'dired-omit-mode)
        (add-hook 'dired-mode-hook 'dired-hide-details-mode)
        :bind (("s-\\" . dired-jump-other-window)
               :map dired-mode-map
               (")" . dired-omit-mode)))
  • dtrt-indent
    (use-package dtrt-indent
      :commands dtrt-indent-mode
      :config (dtrt-indent-mode 1))
  • eldoc

    Provides some info for the thing at the point.

    (use-package eldoc
      :ensure nil
      :commands eldoc-mode
      (add-hooks '(((emacs-lisp-mode
                     typescript-mode) . eldoc-mode))))
  • Emacs shell
    (use-package eshell
      :ensure nil
      :bind (("C-c M-t" . eshell)
             ("C-c x" . eshell))
      :commands (eshell eshell-command eshell-bol)
      (use-package em-rebind
        (defun eshell-eol ()
          "Goes to the end of line."
        :ensure nil
        (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)] . eshell-eol))))
      (setq eshell-modules-list
    • esh-help
      (use-package esh-help
        :commands esh-help-eldoc-command
        :init (create-hook-helper esh-help-setup ()
                :hooks (eshell-mode-hook)
                (make-local-variable 'eldoc-documentation-function)
                (setq eldoc-documentation-function 'esh-help-eldoc-command)
    • em-dired
      (use-package em-dired
        :ensure nil
        :commands (em-dired-mode em-dired-new)
        :bind (:map dired-mode-map
                    ("e" . em-dired))
        (add-hook 'eshell-mode-hook 'em-dired-mode)
        (advice-add 'eshell :before 'em-dired-new))
  • Emacs speaks statistics
    (use-package ess-site
      :ensure ess
      :commands R)
  • esup
    (use-package esup
      :commands esup
      (defun init-profile ()
        (esup (locate-library "default"))))
  • flycheck
    (use-package flycheck
      :commands global-flycheck-mode
      :config (global-flycheck-mode))
    • flycheck-irony

      This is currently disabled.

      (use-package flycheck-irony
        :commands flycheck-irony-setup
        :init (add-hook 'flycheck-mode-hook 'flycheck-irony-setup))
  • flyspell
    (use-package flyspell
      :ensure nil
      :preface (require 'ispell)
      :when (executable-find ispell-program-name)
      :commands (flyspell-mode flyspell-prog-mode)
      (setq flyspell-use-meta-tab nil)
      (add-hook 'text-mode-hook 'flyspell-mode)
      (add-hook 'prog-mode-hook 'flyspell-prog-mode))
  • gnus
    (use-package gnus
      :ensure nil
      :commands gnus
      (add-hook 'gnus-group-mode-hook 'gnus-topic-mode)
      (add-hook 'dired-mode-hook 'turn-on-gnus-dired-mode))
  • god-mode
    (use-package god-mode
      :bind (("<escape>" . god-local-mode)))
  • gud
    (use-package gud
      :ensure nil
      :commands gud-gdb
  • help
    (use-package help
      :ensure nil
      :bind (:map help-map
                  ("C-v" . find-variable)
                  ("C-k" . find-function-on-key)
                  ("C-f" . find-function)
                  ("C-l" . find-library)
                  :map help-mode-map
                  ("g" . revert-buffer-no-confirm))
      (defun revert-buffer-no-confirm (&optional ignore-auto)
        "Revert current buffer without asking."
        (interactive (list (not current-prefix-arg)))
        (revert-buffer ignore-auto t nil)))
  • helpful
    (use-package helpful
      :bind (("C-h f" . helpful-callable)
             ("C-h v" . helpful-variable)))
  • ivy
    (use-package ivy
      :bind (("<f6>" . ivy-resume)
             ([remap list-buffers] . ivy-switch-buffer)
             :map ivy-minibuffer-map
             ("<escape>" . abort-recursive-edit))
      :commands ivy-mode
      (require 'ivy nil t)
      (defvar projectile-completion-system)
      (defvar magit-completing-read-function)
      (defvar dumb-jump-selector)
      (defvar rtags-display-result-backend)
      (defvar projector-completion-system)
      (setq projectile-completion-system 'ivy
            magit-completing-read-function 'ivy-completing-read
            dumb-jump-selector 'ivy
            rtags-display-result-backend 'ivy
            projector-completion-system 'ivy)
      :config (ivy-mode 1))
  • kill-or-bury-alive
    (use-package kill-or-bury-alive
      :bind (([remap kill-buffer] . kill-or-bury-alive)))
  • 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))))
      :commands (magit-clone
      :bind (("C-x g" . magit-status)
             ("C-x G" . magit-dispatch-popup)
             :map magit-mode-map
             ("C-o" . magit-dired-other-window))
      (defvar magit-last-seen-setup-instructions "1.4.0")
      (create-hook-helper magit-github-hook ()
        :hooks (magit-mode-hook)
        (magit-define-popup-action 'magit-remote-popup
          ?g "Add remote from github user name" #'magit-remote-github)))
  • mb-depth
    (use-package mb-depth
      :ensure nil
      :commands minibuffer-depth-indicate-mode
      :init (add-hook 'minibuffer-setup-hook 'minibuffer-depth-indicate-mode))
  • mmm-mode
    (use-package mmm-mode
      :commands mmm-mode
      (use-package mmm-auto
        :ensure nil
  • multiple-cursors
    (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)))
  • 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)))
  • org-mode
    (use-package org
      :ensure org-plus-contrib
      :commands org-capture
      :bind* (("C-c c" . org-capture)
              ("C-c a" . org-agenda)
              ("C-c l" . org-store-link)
              ("C-c b" . org-iswitchb))
      (defun org-completion-symbols ()
        (when (looking-back "=[a-zA-Z]+")
          (let (cands)
                (goto-char (point-min))
                (while (re-search-forward "=\\([a-zA-Z]+\\)=" nil t)
                   (match-string-no-properties 0) cands :test 'equal))
            (when cands
              (list (match-beginning 0) (match-end 0) cands)))))
    (defun org-completion-refs ()
      (when (looking-back "\\\\\\(?:ref\\|label\\){\\([^\n{}]\\)*")
        (let (cands beg end)
            (goto-char (point-min))
            (while (re-search-forward "\\label{\\([^}]+\\)}" nil t)
              (push (match-string-no-properties 1) cands)))
            (setq end (1- (point)))
            (setq beg (1+ (point))))
          (list beg end
                (delete (buffer-substring-no-properties beg end)
                        (nreverse cands))))))
      (add-hook 'org-mode-hook 'auto-fill-mode)
      (add-hook 'org-mode-hook
                (lambda ()
                  (setq-local completion-at-point-functions
      (use-package ob-dot
        :ensure nil
      (use-package ox-rss
        :ensure nil
      (use-package ox-latex
        :ensure nil
      (use-package ox-beamer
        :ensure nil
      (use-package ox-md
        :ensure nil
      (use-package ox-reveal
      (use-package ox-pandoc
      (use-package ob-http
      (use-package org-brain
      (use-package org-projectile
      (use-package org-present
      (use-package org-ref
      (use-package org-autolist
      (use-package ox-tufte
      (use-package org-static-blog
      (org-babel-do-load-languages 'org-babel-load-languages
                                   '((sh . t)
                                     (emacs-lisp . t)
                                     (dot . t)
                                     (latex . t)
  • Projectile

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

    (use-package projectile
      :bind-keymap* (("C-c p" . projectile-command-map)
                     ("s-p" . projectile-command-map))
      :bind (:map projectile-command-map
             ("s r" . projectile-rg))
      (defun projectile-rg ()
        "Run ripgrep in projectile."
        (counsel-rg "" (projectile-project-root)))
      :commands (projectile-mode)
      :defer 1
      (put 'projectile-project-run-cmd 'safe-local-variable #'stringp)
      (put 'projectile-project-compilation-cmd 'safe-local-variable
           (lambda (a) (and (stringp a) (or (not (boundp 'compilation-read-command))
      (use-package easymenu
        :ensure nil
        (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]
  • Proof General
    (use-package proof-site
      :ensure proofgeneral
      :disabled needs-package-init
      :commands (proofgeneral proof-mode proof-shell-mode))
  • Ripgrep
    (use-package rg
      :commands rg)
  • Shell
    (use-package shell
      :ensure nil
      :commands (shell shell-mode)
      :bind ("C-c C-s" . shell)
      (add-hook 'shell-mode-hook 'ansi-color-for-comint-mode-on)
      (add-hook 'shell-mode-hook 'dirtrack-mode)
      (create-hook-helper use-histfile ()
        :hooks (shell-mode-hook)
        (turn-on-comint-history (getenv "HISTFILE"))))
  • smart-hungry-delete
    (use-package smart-hungry-delete
      :commands (smart-hungry-delete-default-c-mode-common-hook
      :bind (:map prog-mode-map
                  ("<backspace>" . smart-hungry-delete-backward-char)
                  ("C-d" . smart-hungry-delete-forward-char))
      (add-hook 'prog-mode-hook 'smart-hungry-delete-default-prog-mode-hook)
      (add-hook 'c-mode-common-hook 'smart-hungry-delete-default-c-mode-common-hook)
      (add-hook 'python-mode-hook 'smart-hungry-delete-default-c-mode-common-hook)
      (add-hook 'text-mode-hook 'smart-hungry-delete-default-text-mode-hook))
  • Smartparens
    (use-package smartparens
      :commands (smartparens-mode
      :bind (: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)
                  :map smartparens-strict-mode-map
                  ([remap c-electric-backspace] . sp-backward-delete-char)
                  :map emacs-lisp-mode-map
                  (";" . sp-comment))
      (add-hooks '(((emacs-lisp-mode
                     eval-expression-minibuffer-setup) . smartparens-strict-mode)))
      (add-hooks '(((emacs-lisp-mode
                     slime-repl-mode) . show-smartparens-mode)))
      (add-hooks '(((web-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 "«" "»"))
          '(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-local-pair 'org-mode "~" "~" :actions '(wrap))
      (sp-local-pair 'org-mode "/" "/" :actions '(wrap))
      (sp-local-pair 'org-mode "*" "*" :actions '(wrap)))
  • sudo-edit
    (use-package sudo-edit
      :bind (("C-c C-r" . sudo-edit)))
  • swiper
    (use-package swiper)
  • term
    (use-package term
      :ensure nil
      :commands (term-mode term-char-mode term-set-escape-char)
      (add-hook 'term-mode-hook (lambda ()
                                  (setq term-prompt-regexp "^[^#$%>\n]*[#$%>] *")
                                  (setq-local transient-mark-mode nil)
                                  (auto-fill-mode -1)))
      (defun my-term ()
        (set-buffer (make-term "my-term" "zsh"))
        (term-set-escape-char ?\C-x)
        (switch-to-buffer "*my-term*"))
      :bind ("C-c t" . my-term))
  • tramp
    (use-package tramp
      :ensure nil
      :commands (tramp-tramp-file-p
  • transpose-frame
    (use-package transpose-frame
      :bind ("H-t" . transpose-frame))
  • try
    (use-package try
      :commands try)
  • which-func
    (use-package which-func
      :commands which-function-mode
      :ensure nil
      :config (which-function-mode))
  • which-key
    (use-package which-key
      :commands which-key-mode
      :config (which-key-mode))
  • whitespace-cleanup-mode
    (use-package whitespace-cleanup-mode
      :commands whitespace-cleanup-mode
      :init (add-hook 'prog-mode-hook 'whitespace-cleanup-mode))
  • whitespace
    (use-package whitespace
      :ensure nil
      :commands whitespace-mode
      :init (add-hook 'prog-mode-hook 'whitespace-mode))
  • yafolding
    (use-package yafolding
      :commands yafolding-mode
      :init (add-hook 'prog-mode-hook 'yafolding-mode))


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

  • align
    (use-package align
      :bind (("C-c [" . align-regexp))
      :commands align
      :ensure nil)
  • ansi-color

    Get color/ansi codes in compilation mode.

    (use-package ansi-color
      :ensure nil
      :commands ansi-color-apply-on-region
      :init (create-hook-helper colorize-compilation-buffer ()
              :hooks (compilation-filter-hook)
              (let ((inhibit-read-only t))
                (ansi-color-apply-on-region (point-min) (point-max)))))
  • autorevert
    (use-package autorevert
      :ensure nil
      :commands auto-revert-mode
      (add-hook 'dired-mode-hook 'auto-revert-mode)
      (global-auto-revert-mode t))
  • bug-reference
    (use-package bug-reference
      :ensure nil
      :commands bug-reference-prog-mode
      :init (add-hook 'prog-mode-hook 'bug-reference-prog-mode))
    • bug-reference-github
      (use-package bug-reference-github
        :commands bug-reference-github-set-url-format
        :init (add-hook 'prog-mode-hook 'bug-reference-github-set-url-format))
  • comint
    (use-package comint
      :ensure nil
      (: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))
      :commands (comint-next-prompt
      (defun turn-on-comint-history (history-file)
        (setq comint-input-ring-file-name history-file)
        (comint-read-input-ring 'silent))
      (add-hook 'kill-buffer-hook 'comint-write-input-ring)
      (create-hook-helper save-history ()
        :hooks (kill-emacs-hook)
        (dolist (buffer (buffer-list))
          (with-current-buffer buffer (comint-write-input-ring)))))
  • delsel
    (use-package delsel
      :ensure nil
      :config (delete-selection-mode t))
  • edebug
    (use-package edebug
      :ensure nil)
  • electric

    Setup these modes:

    • electric-quote
    • electric-indent
    • electric-layout
    (use-package electric
      :ensure nil
      :commands (electric-quote-mode electric-indent-mode electric-layout-mode)
      (add-hook 'prog-mode-hook 'electric-quote-mode)
      (add-hook 'prog-mode-hook 'electric-indent-mode)
      (add-hook 'prog-mode-hook '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
        :commands electric-pair-mode
        (add-hook 'prog-mode-hook 'electric-pair-mode)
        (add-hook 'smartparens-mode-hook (lambda () (electric-pair-mode -1))))
  • etags
    (use-package etags
      :ensure nil
      :commands (tags-completion-table))
  • executable
    (use-package executable
      :ensure nil
      :commands executable-make-buffer-file-executable-if-script-p
      (add-hook 'after-save-hook
  • ffap
    (use-package ffap
      :ensure nil
  • goto-addr
    (use-package goto-addr
      :ensure nil
      :commands (goto-address-prog-mode goto-address-mode)
      (add-hook 'prog-mode-hook 'goto-address-prog-mode)
      (add-hook 'git-commit-mode-hook 'goto-address-mode))
  • grep
    (use-package grep
      :ensure nil
      :bind (("M-s d" . find-grep-dired)
             ("M-s F" . find-grep)
             ("M-s G" . grep)))
  • hippie-exp
    (use-package hippie-exp
      :ensure nil
      :bind* (("M-/". hippie-expand)))
  • ibuffer
    (use-package ibuffer
      :ensure nil
      :bind ([remap switch-to-buffer] . ibuffer))
  • imenu
    • imenu-anywhere
      (use-package imenu-anywhere
        :bind (("C-c i" . imenu-anywhere)
               ("s-i" . imenu-anywhere)))
    • imenu-list
      (use-package imenu-list
        :commands imenu-list)
  • newcomment
    (use-package newcomment
      :ensure nil
      :bind ("s-/" . comment-or-uncomment-region))
  • notmuch
    (use-package notmuch
      :commands notmuch)
  • pp
    (use-package pp
      :ensure nil
      :commands pp-eval-last-sexp
      :bind (([remap eval-expression] . pp-eval-expression))
      (global-unset-key (kbd "C-x C-e"))
      (create-hook-helper always-eval-sexp ()
        :hooks (lisp-mode-hook emacs-lisp-mode-hook)
        (define-key (current-local-map) (kbd "C-x C-e") 'pp-eval-last-sexp)))
  • prog-mode
    (use-package prog-mode
      :ensure nil
      :commands (prettify-symbols-mode global-prettify-symbols-mode)
      (add-hook 'prog-mode-hook 'prettify-symbols-mode)
      (create-hook-helper prettify-symbols-prog ()
        :hooks (prog-mode-hook)
        (push '("<=" . ?≤) prettify-symbols-alist)
        (push '(">=" . ?≥) prettify-symbols-alist))
      (create-hook-helper prettify-symbols-lisp ()
        :hooks (lisp-mode-hook)
        (push '("/=" . ?≠) prettify-symbols-alist)
        (push '("sqrt" . ?√) prettify-symbols-alist)
        (push '("not" . ?¬) prettify-symbols-alist)
        (push '("and" . ?∧) prettify-symbols-alist)
        (push '("or" . ?∨) prettify-symbols-alist))
      (create-hook-helper prettify-symbols-c ()
        :hooks (c-mode-hook)
        (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))
      (create-hook-helper prettify-symbols-c++ ()
        :hooks (c++-mode-hook)
        (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))
      (create-hook-helper prettify-symbols-js ()
        :hooks (js2-mode-hook js-mode-hook)
        (push '("function" . ?λ) prettify-symbols-alist)
        (push '("=>" . ?⇒) prettify-symbols-alist)))
  • savehist
    (use-package savehist
      :ensure nil
      :commands savehist-mode
      :config (savehist-mode 1))
  • simple
    (use-package simple
      :ensure nil
      (("C-`" . list-processes)
       :map minibuffer-local-map
       ("<escape>"  . abort-recursive-edit)
       ("M-TAB"     . previous-complete-history-element)
       ("<M-S-tab>" . next-complete-history-element))
      :commands visual-line-mode
      (add-hook 'text-mode-hook 'visual-line-mode)
  • subword
    (use-package subword
      :ensure nil
      :commands subword-mode
      :init (add-hook 'java-mode-hook 'subword-mode))
  • time
    (use-package time
  • tooltip
    (use-package tooltip
      :ensure nil
      (tooltip-mode -1))
  • view
    (use-package view
      :ensure nil
      :bind (: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)))
  • windmove
    (use-package windmove
      :ensure nil
      :bind (("<s-down>" . windmove-down)
             ("<s-up>" . windmove-up)

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.)

  • 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))
      (use-package c-eldoc
        :commands c-turn-on-eldoc-mode
        :init (add-hook 'c-mode-common-hook 'c-turn-on-eldoc-mode)))
    • irony
      (use-package irony
        :commands irony-mode
        :init (add-hooks '(((c++-mode c-mode objc-mode) . irony-mode))))
    • irony-eldoc
      (use-package irony-eldoc
        :commands irony-eldoc
        :init (add-hook 'irony-mode-hook 'irony-eldoc))
  • CoffeeScript
    (use-package coffee-mode
      :mode (("\\.coffee\\'" . coffee-mode)))
  • CSS
    (use-package css-mode
      :ensure nil
      :mode "\\.css\\'"
      :commands css-mode
      (use-package css-eldoc
  • CSV
    (use-package csv-mode
      :mode "\\.csv\\'")
  • ELF
    (use-package elf-mode
      :commands elf-mode
      :init (add-to-list 'magic-mode-alist (cons "ELF" 'elf-mode)))
  • Go
    (use-package go-mode
      :mode "\\.go\\'")
    • go-eldoc
      (use-package go-eldoc
        :commands go-eldoc-setup
        :init (add-hook 'go-mode-hook 'go-eldoc-setup))
  • HAML
    (use-package haml-mode
      :mode "\\.haml\\'")
  • Haskell
    • intero
      (use-package intero
        :commands intero-mode
        (defun intero-mode-unless-global-project ()
          "Run intero-mode iff we're in a project with a stack.yaml"
          (unless (string-match-p
                   (regexp-quote ".stack/global-project")
                    "stack path --project-root --verbosity silent"))
        (add-hook 'haskell-mode-hook 'intero-mode-unless-global-project)
    • ghc
      (use-package ghc)
    • haskell-mode
      (use-package haskell-mode
        :mode (("\\.hs\\'" . haskell-mode)
               ("\\.cabal\\'" . haskell-cabal-mode))
        :commands haskell-indentation-moe
        (add-hook 'haskell-mode-hook 'haskell-indentation-mode)
        (use-package haskell-doc
          :ensure nil
  • Java
    • jdee
      (use-package jdee
        :mode ("\\.java\\'" . jdee-mode)
        :commands jdee-mode
        :bind (:map jdee-mode-map
                    ("<s-mouse-1>" . jdee-open-class-at-event)))
  • JavaScript
    • indium
      (use-package indium
        :mode ("\\.js\\'" . indium-mode)
        :commands (indium-mode indium-interaction-mode indium-scratch))
    • js2-mode
      (use-package js2-mode
        :mode (("\\.js\\'" . js2-mode))
        :commands js2-imenu-extras-mode
        (add-hook 'js2-mode-hook 'js2-imenu-extras-mode))
    • js3-mode
      (use-package js3-mode
        :commands js3-mode)
    • tern
      (use-package tern
        :commands tern-mode
        :init (add-hook 'js2-mode-hook 'tern-mode))
  • JSON
    (use-package json-mode
      :mode (("\\.bowerrc$"     . json-mode)
             ("\\.jshintrc$"    . json-mode)
             ("\\.json_schema$" . json-mode)
             ("\\.json\\'" . json-mode))
      (make-local-variable 'js-indent-level))
  • LaTeX
    • auctex

      Auctex provides some helpful tools for working with LaTeX.

      (use-package tex-site
        :ensure auctex
        :commands (TeX-latex-mode
        :mode ("\\.tex\\'" . TeX-latex-mode))
  • Lisp
    (use-package elisp-mode
      :ensure nil
      :interpreter (("emacs" . emacs-lisp-mode)))
    • cider
      (use-package cider)
    • slime
      (use-package slime)
    • ielm
      (use-package ielm
        :ensure nil
        :bind ("C-c :" . ielm))

    This is currently disabled.

    (use-package llvm-mode
      :mode "\\.ll\\'")
  • Lua
    • lua-mode
      (use-package lua-mode
        :mode "\\.lua\\'")
  • Mach-O
    (use-package macho-mode
      :commands macho-mode
      :ensure nil
      (add-to-list 'magic-mode-alist '("\xFE\xED\xFA\xCE" . macho-mode))
      (add-to-list 'magic-mode-alist '("\xFE\xED\xFA\xCF" . macho-mode))
      (add-to-list 'magic-mode-alist '("\xCE\xFA\xED\xFE" . macho-mode))
      (add-to-list 'magic-mode-alist '("\xCF\xFA\xED\xFE" . macho-mode)))
  • Makefile
    • make-mode
      (use-package make-mode
        :ensure nil
        (add-hook 'makefile-mode-hook 'indent-tabs-mode))
  • Markdown
    • markdown-mode
      (use-package markdown-mode
        (("\\.md\\'" . gfm-mode)
         ("\\.markdown\\'" . gfm-mode))
        (bind-key "'" "’" markdown-mode-map
                  (not (or (markdown-code-at-point-p)
                           (memq 'markdown-pre-face
                                 (face-at-point nil 'mult))))))
  • Nix
    (use-package nix-mode
      :mode "\\.nix\\'")
    • nix-buffer
      (use-package nix-buffer
        :commands nix-buffer)
    (use-package nroff-mode
      :ensure nil
      :commands nroff-mode)
  • PHP
    (use-package php-mode
      :mode "\\.php\\'")
  • Python
    • Anaconda
      (use-package anaconda-mode
        :commands (anaconda-mode anaconda-eldoc-mode)
        (add-hook 'python-mode-hook 'anaconda-mode)
        (add-hook 'python-mode-hook 'anaconda-eldoc-mode))
    • python-mode
      (use-package python
        :ensure nil
        :mode ("\\.py\\'" . python-mode)
        :interpreter ("python" . python-mode))
    • 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
      (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 (: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
        :commands (tide-setup tide-hl-identifier-mode)
        (add-hook 'typescript-mode-hook 'tide-setup)
        (add-hook 'typescript-mode-hook 'tide-hl-identifier-mode))
  • Web
    (use-package web-mode
      :mode (("\\.erb\\'" . web-mode)
             ("\\.mustache\\'" . web-mode)
             ("\\.html?\\'" . web-mode)
             ("\\.php\\'" . web-mode)
             ("\\.jsp\\'" . web-mode)))
  • XML
    (use-package nxml-mode
      :ensure nil
      :commands nxml-mode
      (defalias 'xml-mode 'nxml-mode))
  • YAML
    (use-package yaml-mode
      :mode "\\.ya?ml\\'")


These are all available in ./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.

  • gnuplot
    (use-package gnuplot)
  • logview
    (use-package logview)
  • anything
    (use-package anything
      :commands anything)
  • apropospriate-theme

    This is the theme I use. This has to be defered for some reason.

    (use-package apropospriate-theme
      :config (load-theme 'apropospriate-dark t))
  • bm
    (use-package bm)
  • bool-flip
    (use-package bool-flip
      :bind ("C-c C-b" . bool-flip-do-flip))
  • browse-at-remote
    (use-package browse-at-remote
      :commands browse-at-remote)
  • 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)))
  • elfeed
    (use-package elfeed
      :commands elfeed)
  • expand-region
    (use-package expand-region
      :bind (("C-=" . er/expand-region)))
  • firestarter
    (use-package firestarter
      :bind ("C-c m s" . firestarter-mode))
  • focus
    (use-package focus
      :bind ("C-c m f" . focus-mode))
  • hl-todo
    (use-package hl-todo
      :commands hl-todo-mode
      :init (add-hook 'prog-mode-hook 'hl-todo-mode))
  • hookify
    (use-package hookify
      :commands hookify)
  • htmlize
    (use-package htmlize)
  • mediawiki
    (use-package mediawiki)
  • minimap
    (use-package minimap
      :commands minimap-mode)
  • multi-term
    (use-package multi-term
      :bind (("C-. t" . multi-term-next)
             ("C-. T" . multi-term)))
  • page-break-lines
    (use-package page-break-lines
      :commands page-break-lines-mode
      :init (add-hooks '(((doc-mode
                           emacs-lisp-mode) . page-break-lines-mode))))
  • pandoc-mode
    (use-package pandoc-mode
      :commands (pandoc-mode pandoc-load-default-settings)
      (add-hook 'markdown-mode-hook 'pandoc-mode)
      (add-hook 'pandoc-mode-hook 'pandoc-load-default-settings))
  • rainbow-delimiters
    (use-package rainbow-delimiters
      :commands rainbow-delimiters-mode
      :init (add-hooks '(((emacs-lisp-mode
                           slime-repl-mode) . rainbow-delimiters-mode))))
  • rainbow-mode
    (use-package rainbow-mode
      :commands rainbow-mode
      :init (add-hooks '(((emacs-lisp-mode
                           slime-repl-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))
  • smart-shift
    (use-package smart-shift
      :bind (("C-c <left>" . smart-shift-left)
             ("C-c <right>" . smart-shift-right)
             ("C-c <up>" . smart-shift-up)
             ("C-c <down>" . smart-shift-down)))
  • 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)))
  • vkill
    (use-package vkill
      :bind ("C-x L" . vkill))
  • xah-math-input
    (use-package xah-math-input
      :commands xah-math-input-mode)
  • xterm-color
    (use-package xterm-color
      :commands xterm-color-filter
      (add-hook 'comint-preoutput-filter-functions 'xterm-color-filter)
      (setq comint-output-filter-functions
            (remove 'ansi-color-process-output comint-output-filter-functions)))


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 LANG=en_US.UTF-8 \
       LC_ALL=en_US.UTF-8 \
       INFOPATH=$PREFIX/share/info \
       MANPATH=$PREFIX/share/man \
       DICPATH=$PREFIX/share/hunspell \
       CLICOLOR=1 \
       GREP_OPTIONS='--color=auto' \
       GREP_COLOR='3;33' \
       LC_COLLATE=C \
       HISTFILE=$HOME/.history \
       HISTSIZE=16000 \
       HISTFILESIZE=16000 \
       HISTCONTROL=ignoreboth \
       SAVEHIST=15000 \

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"


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


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

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")
 `("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")
 '(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")
 '(explicit-shell-file-name "@bash@/bin/bash")
 '(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-python-flake8-executable "@flake8@/bin/flake8")
 '(flycheck-asciidoc-executable "@asciidoc@/bin/asciidoc")
 '(flycheck-less-executable "@lessc@/bin/lessc")
 '(flycheck-haskell-stack-ghc-executable "@stack@/bin/stack")
 '(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")
 '(ispell-program-name "@aspell@/bin/aspell")
 '(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")
;; '(jdee-server-dir "@jdeeserver@")
 '(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")
 '(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")
 '(intero-stack-executable "@intero@/bin/intero-nix-shim")
 '(notmuch-command "@notmuch@/bin/notmuch")
 '(org-export-async-init-file "@orginit@")

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)

Provide site-paths,

(provide 'site-paths)


Install Nix.

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

Pull into Git repo.

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

Clone if default.nix doesn’t exist.

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

Install current directory

nix-env -if .

Cross-platform script to execute app.

#!/usr/bin/env sh

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 ${./lisp} $out/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.

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.

customEmacsPackages = emacsPackagesNg.overrideScope (super: self: {
  inherit emacs;

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-genericrecommended collection-langenglish
    collection-latex collection-latexrecommended collection-luatex
    collection-mathextra collection-metapost collection-plainextra
    collection-texworks collection-xetex capt-of;

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 emacs 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;
    inherit (pythonPackages) flake8;
    inherit (nodePackages) jshint eslint;
    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 = with customEmacsPackages.melpaPackages;
        runCommand "package-list" { buildInputs = [ emacs ]; } ''
    emacs --batch --quick \
                    -L ${bind-key}/share/emacs/site-lisp/elpa/bind-key-* \
                    -L ${use-package}/share/emacs/site-lisp/elpa/use-package-* \
                    -L ${./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 = [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 ${./lisp/em-dired.el} $out/share/emacs/site-lisp/em-dired.el
    cp ${./lisp/dired-column.el} \
    cp ${./lisp/macho-mode.el} $out/share/emacs/site-lisp/macho-mode.el
    cp ${./lisp/nethack.el} $out/share/emacs/site-lisp/nethack.el
    cp ${./lisp/set-defaults.el} \
    cp ${./lisp/installer.el} $out/share/emacs/site-lisp/installer.el
    cp ${./lisp/restart-emacs.el} \
    cp ${./lisp/company-eshell-history.el} \
    cp ${./lisp/use-package-list.el} \

    Ideally, we could compile the .el file into a .elc, but it doesn’t work so well. The below block should do it, but it gives lots of errors.

    cd $out/share/emacs/site-lisp
    export HOME=$PWD
    emacs --batch --quick \
          --eval "(let ((default-directory \"${emacsWrapper
                    ((requiredPackages customEmacsPackages myPackages) ++
    }/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 emacs explicitRequires; inherit (xorg) lndir; }
       mkdir -p $out/bin
       mkdir -p $out/share/emacs/site-lisp
       local requires
       for pkg in $explicitRequires; do
         findInputs $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.

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 @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
chmod +x $out/bin/run
  meta = {
    priority = -10;
  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 @cacert@ ${cacert}
  ] ++ [bashInteractive zsh coreutils git
        gawk gnused gzip gnutar gnupg1compat xz cacert
  ] ++ [nox nix nix-index nix-repl]
    ++ [nodePackages.tern isync notmuch graphviz]
    ++ [stack ghc];
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
  - sh result

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
    - env:
    - env:

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 \"\")"

This file gives us some info on the built derivation. Only arg is optional and should be a path to a Nix store derivation.


if [ -z "$out" ]; then

if [ -L "$out" ]; then
  out=$(readlink -f $out)

if [ -e $out ]; then
  echo Dependencies:
  du -scl $(nix-store -qR $out) | sort -n


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;


(require 'site-paths nil t)
(require 'use-package)
(setq use-package-always-demand t
      use-package-always-ensure nil)
(use-package tex-site)
(use-package emacs-lisp)
(use-package sh-script)
(use-package org
  (setq org-export-in-background t
        org-src-fontify-natively t
        org-src-preserve-indentation t
        ;; org-html-htmlize-output-type (quote css)
        org-latex-listings (quote minted))
  (use-package ox-latex)
  (use-package ox-beamer)
  (use-package ox-md)
  (use-package ox-rss))