Matthew BauerBlogRSS

05 Nov 2017

bauer: an Emacs+Nix IDE

Note: the updated version of this is available at https://matthewbauer.us/bauer/

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.

Usage

Demo

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

url="github.com/matthewbauer/bauer/archive/master.tar.gz" \
expr='import (builtins.fetchTarball "$url")' \
   nix-shell -p nix-bundle --run 'nix-run "$expr"'

Installing/upgrading

Run this from your shell:

curl https://matthewbauer.us/bootstrap.sh | 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.

Developing

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

git clone https://github.com/matthewbauer/bauer ~/bauer
cd ~/bauer
nix-build
./result/run

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 https://github.com/matthewbauer/bauer ~/.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 "README.org" 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 ()
            (garbage-collect)
            (setq gc-cons-threshold
                  (car (get 'gc-cons-threshold 'standard-value)))))

Autoloads

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

(set-defaults
 '(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"
                                                user-emacs-directory))))
 '(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-explicit-action-p))))
 '(company-frontends '(company-pseudo-tooltip-unless-just-one-frontend
                       company-preview-frontend
                       company-echo-metadata-frontend))
 '(company-continue-commands
   '(not save-buffer
         save-some-buffers
         save-buffers-kill-terminal
         save-buffers-kill-emacs
         comint-previous-matching-input-from-input
         comint-next-matching-input-from-input))
 '(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-handle-control-codes
                                    eshell-watch-for-password-prompt
                                    eshell-truncate-buffer))
 '(eshell-plain-echo-behavior nil)
 '(eshell-review-quick-commands t)
 '(eshell-rm-interactive-query t)
 '(eshell-prompt-function
   (lambda () (concat
               (when (tramp-tramp-file-p default-directory)
                 (concat
                  (tramp-file-name-user
                   (tramp-dissect-file-name default-directory))
                  "@"
                  (tramp-file-name-real-host (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) " # " " $ "))))
 '(eshell-visual-commands
   '("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
                                         idle-change
                                         mode-enabled
                                         new-line))
 '(flycheck-display-errors-function
   'flycheck-display-error-messages-unless-error-list)
 '(flycheck-idle-change-delay 0.001)
 '(flycheck-standard-error-navigation nil)
 '(flycheck-global-modes '(not erc-mode
                               message-mode
                               git-commit-mode
                               view-mode
                               outline-mode
                               text-mode
                               org-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))
                          "%b")))
 '(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" . "http://stable.melpa.org/packages/")
                      ("melpa" . "https://melpa.org/packages/")
                      ("org" . "http://orgmode.org/elpa/")
                      ("gnu" . "https://elpa.gnu.org/packages/")
                      ))
 '(proof-splash-enable nil)
 '(projectile-globally-ignored-files '(".DS_Store" "TAGS"))
 '(projectile-enable-caching t)
 '(projectile-mode-line
   '(: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)
 '(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
                                   regexp-search-ring
                                   kill-ring
                                   comint-input-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)
 '(vc-ignore-dir-regexp
   (concat "\\(\\(\\`"
           "\\(?:[\\/][\\/][^\\/]+[\\/]\\|/\\(?:net\\|afs\\|\\.\\.\\.\\)/\\)"
            "\\'\\)\\|\\(\\`/[^/|:][^/|]*:\\)\\)\\|\\(\\`/[^/|:][^/|]*:\\)"))
 '(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
                      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 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.

(set-envs
 '("EDITOR" "emacsclient -nw")
 '("LANG" "en_US.UTF-8")
 '("LC_ALL" "en_US.UTF-8")
 '("NODE_NO_READLINE" "1")
 '("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.

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

First handle using package.el.

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

Actually require use-package,

(eval-and-compile
  (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.

(eval-and-compile
  (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)

Helpers

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
             define-hook-helper
             hkhlp-normalize-hook-spec)
  :functions (make-hook-helper
              add-hook-helper
              hkhlp-update-helper))

Packages

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

Essentials

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
                           inferior-emacs-lisp-mode
                           ielm-mode
                           lisp-mode
                           inferior-lisp-mode
                           lisp-interaction-mode
                           slime-repl-mode) . aggressive-indent-mode))))
    
  • buffer-move
    (use-package buffer-move
      :bind
      (("<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
      :demand
      :bind (:map company-active-map
                  ("TAB" .
                   company-select-next-if-tooltip-visible-or-complete-selection)
                  ("<tab>" .
                   company-select-next-if-tooltip-visible-or-complete-selection)
                  ("S-TAB" . company-select-previous)
                  ("<backtab>" . company-select-previous)
                  ("C-n" . company-select-next)
                  ("C-p" . company-select-previous)
                  )
      :commands (company-mode
                 global-company-mode
                 company-auto-begin
                 company-complete-common-or-cycle)
      :config
      (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
                                       '(company-preview-frontend))))
      (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))
      :preface
      (defun show-compilation ()
        (interactive)
        (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))))
    
      :config
      (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)
             (point-marker))))
    
  • 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)
              )
      :init
      (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
                                  diff-hl-diff-goto-hunk)
      :bind (:map diff-hl-mode-map
                  ("<left-fringe> <mouse-1>" . diff-hl-diff-goto-hunk))
      :init
      (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)
        :init
        (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
      :demand
      :config (dtrt-indent-mode 1))
    
  • eldoc

    Provides some info for the thing at the point.

    (use-package eldoc
      :ensure nil
      :commands eldoc-mode
      :init
      (add-hooks '(((emacs-lisp-mode
                     eval-expression-minibuffer-setup
                     lisp-mode-interactive-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)
      :init
      (use-package em-rebind
        :preface
        (defun eshell-eol ()
          "Goes to the end of line."
          (interactive)
          (end-of-line))
        :ensure nil
        :demand
        :config
        (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
            '(eshell-alias
              eshell-banner
              eshell-basic
              eshell-cmpl
              eshell-dirs
              eshell-glob
              eshell-hist
              eshell-ls
              eshell-pred
              eshell-prompt
              eshell-rebind
              eshell-script
              eshell-smart
              eshell-term
              eshell-tramp
              eshell-unix
              eshell-xtra)))
    
    • 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)
                (eldoc-mode)))
      
    • em-dired
      (use-package em-dired
        :ensure nil
        :commands (em-dired-mode em-dired-new)
        :bind (:map dired-mode-map
                    ("e" . em-dired))
        :init
        (add-hook 'eshell-mode-hook 'em-dired-mode)
        (advice-add 'eshell :before 'em-dired-new))
      
  • Emacs speaks statistics
    (use-package ess-site
      :ensure ess
      :no-require
      :commands R)
    
  • esup
    (use-package esup
      :commands esup
      :preface
      (defun init-profile ()
        (interactive)
        (esup (locate-library "default"))))
    
  • flycheck
    (use-package flycheck
      :demand
      :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)
      :config
      (setq flyspell-use-meta-tab nil)
      :init
      (add-hook 'text-mode-hook 'flyspell-mode)
      (add-hook 'prog-mode-hook 'flyspell-prog-mode))
    
  • gnus
    (use-package gnus
      :ensure nil
      :commands gnus
      :init
      (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))
      :preface
      (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
      :init
      (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
      :preface
      (defun magit-dired-other-window ()
        (interactive)
        (dired-other-window (magit-toplevel)))
    
      (defun magit-remote-github (username &optional args)
        (interactive (list (magit-read-string-ns "User name")
                           (magit-remote-arguments)))
        (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 "https://github.com/%s/%s"
                                                      username repo) args))))
    
      :commands (magit-clone
                 magit-toplevel
                 magit-read-string-ns
                 magit-remote-arguments
                 magit-get
                 magit-remote-add
                 magit-define-popup-action)
    
      :bind (("C-x g" . magit-status)
             ("C-x G" . magit-dispatch-popup)
             :map magit-mode-map
             ("C-o" . magit-dired-other-window))
      :init
      (defvar magit-last-seen-setup-instructions "1.4.0")
      :config
      (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
      :config
      (use-package mmm-auto
        :ensure nil
        :demand))
    
  • multiple-cursors
    (use-package multiple-cursors
      :bind
      (("<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))
      :preface
      (defun org-completion-symbols ()
        (when (looking-back "=[a-zA-Z]+")
          (let (cands)
            (save-match-data
              (save-excursion
                (goto-char (point-min))
                (while (re-search-forward "=\\([a-zA-Z]+\\)=" nil t)
                  (cl-pushnew
                   (match-string-no-properties 0) cands :test 'equal))
                cands))
            (when cands
              (list (match-beginning 0) (match-end 0) cands)))))
    (defun org-completion-refs ()
      (when (looking-back "\\\\\\(?:ref\\|label\\){\\([^\n{}]\\)*")
        (let (cands beg end)
          (save-excursion
            (goto-char (point-min))
            (while (re-search-forward "\\label{\\([^}]+\\)}" nil t)
              (push (match-string-no-properties 1) cands)))
          (save-excursion
            (up-list)
            (setq end (1- (point)))
            (backward-list)
            (setq beg (1+ (point))))
          (list beg end
                (delete (buffer-substring-no-properties beg end)
                        (nreverse cands))))))
      :init
      (add-hook 'org-mode-hook 'auto-fill-mode)
      (add-hook 'org-mode-hook
                (lambda ()
                  (setq-local completion-at-point-functions
                              '(org-completion-symbols
                                org-completion-refs
                                pcomplete-completions-at-point))))
      :config
      (use-package ob-dot
        :ensure nil
        :demand)
      (use-package ox-rss
        :ensure nil
        :demand)
      (use-package ox-latex
        :ensure nil
        :demand)
      (use-package ox-beamer
        :ensure nil
        :demand)
      (use-package ox-md
        :ensure nil
        :demand)
      (use-package ox-reveal
        :disabled
        :demand)
      (use-package ox-pandoc
        :disabled
        :demand)
      (use-package ob-http
        :disabled
        :demand)
      (use-package org-brain
        :disabled
        :demand)
      (use-package org-projectile
        :disabled
        :demand)
      (use-package org-present
        :disabled
        :demand)
      (use-package org-ref
        :disabled
        :demand)
      (use-package org-autolist
        :disabled
        :demand)
      (use-package ox-tufte
        :disabled
        :demand)
      (use-package org-static-blog
        :demand)
      (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))
      :preface
      (defun projectile-rg ()
        "Run ripgrep in projectile."
        (interactive)
        (counsel-rg "" (projectile-project-root)))
      :commands (projectile-mode)
      :defer 1
      :config
      (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))
                                            compilation-read-command))))
    
      (projectile-mode)
    
      (use-package easymenu
        :ensure nil
        :config
    
        (easy-menu-define projectile-menu projectile-mode-map "Projectile"
          '("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"
             projectile-toggle-between-implementation-and-test]
            ["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"
             projectile-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
      :no-require
      :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)
      :init
      (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
                 smart-hungry-delete-default-prog-mode-hook
                 smart-hungry-delete-default-text-mode-hook)
      :bind (:map prog-mode-map
                  ("<backspace>" . smart-hungry-delete-backward-char)
                  ("C-d" . smart-hungry-delete-forward-char))
      :init
      (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
                 show-smartparens-mode
                 smartparens-strict-mode
                 sp-local-tag
                 sp-local-pair)
      :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))
      :init
      (add-hooks '(((emacs-lisp-mode
                     inferior-emacs-lisp-mode
                     ielm-mode
                     lisp-mode
                     inferior-lisp-mode
                     lisp-interaction-mode
                     slime-repl-mode
                     eval-expression-minibuffer-setup) . smartparens-strict-mode)))
      (add-hooks '(((emacs-lisp-mode
                     inferior-emacs-lisp-mode
                     ielm-mode
                     lisp-mode
                     inferior-lisp-mode
                     lisp-interaction-mode
                     slime-repl-mode) . show-smartparens-mode)))
      (add-hooks '(((web-mode
                     nxml-mode
                     html-mode) . smartparens-mode)))
      :config
      (use-package smartparens-html
        :ensure nil
        :demand)
      (use-package smartparens-config
        :ensure nil
        :demand)
    
      (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-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)
      :init
      (add-hook 'term-mode-hook (lambda ()
                                  (setq term-prompt-regexp "^[^#$%>\n]*[#$%>] *")
                                  (setq-local transient-mark-mode nil)
                                  (auto-fill-mode -1)))
      :preface
      (defun my-term ()
        (interactive)
        (set-buffer (make-term "my-term" "zsh"))
        (term-mode)
        (term-char-mode)
        (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
                 tramp-file-name-user
                 tramp-file-name-real-host
                 tramp-dissect-file-name))
    
  • 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
      :demand
      :config (which-function-mode))
    
  • which-key
    (use-package which-key
      :commands which-key-mode
      :demand
      :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))
    

Built-ins

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
      :demand
      :commands auto-revert-mode
      :init
      (add-hook 'dired-mode-hook 'auto-revert-mode)
      :config
      (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
      :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))
      :commands (comint-next-prompt
                 comint-write-input-ring
                 comint-after-pmark-p
                 comint-read-input-ring
                 comint-send-input)
      :preface
      (defun turn-on-comint-history (history-file)
        (setq comint-input-ring-file-name history-file)
        (comint-read-input-ring 'silent))
      :config
      (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
      :demand
      :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)
      :init
      (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
        :init
        (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
      :init
      (add-hook 'after-save-hook
                'executable-make-buffer-file-executable-if-script-p))
    
  • ffap
    (use-package ffap
      :ensure nil
      )
    
  • goto-addr
    (use-package goto-addr
      :ensure nil
      :commands (goto-address-prog-mode goto-address-mode)
      :init
      (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))
      :init
      (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)
      :init
      (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
      :demand
      :commands savehist-mode
      :config (savehist-mode 1))
    
  • simple
    (use-package simple
      :ensure nil
      :demand
      :bind
      (("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
      :init
      (add-hook 'text-mode-hook 'visual-line-mode)
      :config
      (column-number-mode))
    
  • subword
    (use-package subword
      :ensure nil
      :commands subword-mode
      :init (add-hook 'java-mode-hook 'subword-mode))
    
  • time
    (use-package time
      :demand
      :config
      (display-time-mode)
      )
    
  • tooltip
    (use-package tooltip
      :ensure nil
      :demand
      :config
      (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))
      :config
      (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
      :config
      (use-package css-eldoc
        :demand)
      )
    
  • 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
        :preface
        (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")
                   (shell-command-to-string
                    "stack path --project-root --verbosity silent"))
            (intero-mode)))
        :init
        (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
        :init
        (add-hook 'haskell-mode-hook 'haskell-indentation-mode)
        :config
        (use-package haskell-doc
          :ensure nil
          :demand))
      
  • 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
        :init
        (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))
      :config
      (make-local-variable 'js-indent-level))
    
  • LaTeX
    • auctex

      Auctex provides some helpful tools for working with LaTeX.

      (use-package tex-site
        :ensure auctex
        :no-require
        :commands (TeX-latex-mode
                   TeX-mode
                   tex-mode
                   LaTeX-mode
                   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))
      
  • LLVM IR

    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
      :init
      (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
        :init
        (add-hook 'makefile-mode-hook 'indent-tabs-mode))
      
  • Markdown
    • markdown-mode
      (use-package markdown-mode
        :mode
        (("\\.md\\'" . gfm-mode)
         ("\\.markdown\\'" . gfm-mode))
        :config
        (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)
      
  • NROFF
    (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)
        :init
        (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
      :preface
      (defun shell-command-at-point ()
        (interactive)
        (let ((start-point (save-excursion
                             (beginning-of-line)
                             (point))))
          (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)
        :init
        (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
      :init
      (defalias 'xml-mode 'nxml-mode))
    
  • YAML
    (use-package yaml-mode
      :mode "\\.ya?ml\\'")
    

Personal

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

Other

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
      :demand
      :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)
      :init
      (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
                           inferior-emacs-lisp-mode
                           ielm-mode
                           lisp-mode
                           inferior-lisp-mode
                           lisp-interaction-mode
                           slime-repl-mode) . rainbow-delimiters-mode))))
    
  • rainbow-mode
    (use-package rainbow-mode
      :commands rainbow-mode
      :init (add-hooks '(((emacs-lisp-mode
                           inferior-emacs-lisp-mode
                           ielm-mode
                           lisp-mode
                           inferior-lisp-mode
                           lisp-interaction-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
      :init
      (add-hook 'comint-preoutput-filter-functions 'xterm-color-filter)
      (setq comint-output-filter-functions
            (remove 'ansi-color-process-output comint-output-filter-functions)))
    

Profiles

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.

.profile

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

bootstrap=$HOME/.nix-profile/etc/profile
[ -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 \
       SHELL_SESSION_HISTORY=1

Then setup aliases.

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

Configure INSIDE_EMACS.

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

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"
}

.bashrc

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

bootstrap=$HOME/.nix-profile/etc/bashrc
[ -f $bootstrap ] && source $bootstrap

Source profile found above.

source @out@/etc/profile

When TERM=dumb, just do a simple prompt.

case "$TERM" in
    dumb)
        PS1="\W > "
        return
        ;;
esac

Setup some bash-specific features.

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

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'
fi

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
    PROMPT_COMMAND="update_terminal_cwd;$PROMPT_COMMAND"
    update_terminal_cwd
fi

.zshrc

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

bootstrap=$HOME/.nix-profile/etc/zshrc
[ -f $bootstrap ] && source $bootstrap

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

source @out@/etc/profile

Handle dumb options.

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

Load up site-functions in ZSH.

fpath+=(@out@/share/zsh/site-functions)

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 \
       transient_rprompt

Setup completions.

ZSH_COMPDUMP="${HOME}/.zcompdump-${SHORT_HOST}-${ZSH_VERSION}"
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 \
       path-directories
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 \
       hosts-ipaddr
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
fi

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
    update_terminal_cwd
fi

etc-profile.sh

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
    fi
  done
fi

.gitignore

Some basic gitignore paths.

*~
\#*\#
*.DS_Store

.gitconfig

[core]
        editor = emacsclient
        excludesfile = @gitignore@

[commit]
        gpgSign = true

[gpg]
        program = "@gnupg@/bin/gpg"

[push]
        default = simple

[pull]
        rebase = true

[alias]
        amend = commit --amend

[help]
        autcorrect = 1

[color]
        ui = true

Bootstrapping

site-paths.el.in

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")
                        exec-path))

Setup man-path.

(defvar man-path `("/usr/share/man"
                   "/usr/local/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,

(set-envs
 ;; `("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,

(set-paths
 '(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,

(set-defaults
 '(imap-ssl-program `(,(concat gnutls " --tofu -p %p %s")))
 '(tls-program (concat gnutls " --tofu -p %p %h"))
 '(preview-pdf2dsc-command
   (concat pdf2dsc-command " %s.pdf %m/preview.dsc"))
 '(preview-dvips-command
   (concat dvips-command " -Pwww %d -o %m/preview.ps"))
 '(preview-fast-dvips-command
   (concat dvips-command " -Pwww %d -o %m/preview.ps"))
 '(preview-dvipng-command
   (concat dvipng-command
           " -picky -noghostscript %d -o \"%m/prev%%03d.png\""))
 '(TeX-engine-alist `((xetex "XeTeX" xetex-command xelatex-command
                             xetex-command)
                      (luatex "LuaTeX" luatex-command
                              ,(concat luatex-command " --jobname=%s")
                              luatex-command)))
 '(TeX-command-list
   `(("TeX"
      "%(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
      (texinfo-mode)
      :help "Run Makeinfo with Info output")
     ("Makeinfo HTML" ,(concat makeinfo-command " %(extraopts) --html %t")
      TeX-run-compile nil
      (texinfo-mode)
      :help "Run Makeinfo with HTML output")
     ("AmSTeX"
      ,(concat pdftex-command " %(PDFout) %(extraopts) %`%S%(mode)%' %t")
      TeX-run-TeX nil
      (ams-tex-mode)
      :help "Run AMSTeX")
     ("ConTeXt"
      ,(concat context-command " --once --texutil %(extraopts) %(execopts)%t")
      TeX-run-TeX nil
      (context-mode)
      :help "Run ConTeXt once")
     ("ConTeXt Full" ,(concat context-command " %(extraopts) %(execopts)%t")
      TeX-run-TeX nil
      (context-mode)
      :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
      (latex-mode)
      :help "Check LaTeX file for correctness")
     ("ChkTeX" ,(concat chktex-command " -v6 %s") TeX-run-compile nil
      (latex-mode)
      :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")))
 '(counsel-grep-base-command
   (concat ripgrep-executable
           " -i -M 120 --no-heading --line-number --color never '%s' %s"))
 '(counsel-rg-base-command
   (concat ripgrep-executable " -i --no-heading --line-number %s ."))
 '(counsel-ag-base-command (concat ag-executable " --nocolor --nogroup %s"))
 '(org-preview-latex-process-alist
   `((dvipng :programs ("latex" "dvipng")
             :description "dvi > png"
             :message ""
             :image-input-type "dvi"
             :image-output-type "png"
             :image-size-adjust (1.0 . 1.0)
             :latex-compiler
             (,(concat LaTeX-command
                       " -interaction nonstopmode -output-directory %o %f"))
             :image-converter
             (,(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
                       "/run/current-system/sw/bin"
                       tramp-default-remote-path
                       "/bin"
                       "/usr/bin"
                       "/sbin"
                       "/usr/sbin"
                       "/usr/local/bin"
                       "/usr/local/sbin"
                       "/opt/bin"
                       "/opt/sbin"
                       ,(expand-file-name "bin" output-directory)
                       ))
 '(woman-manpath man-path)
 )

Provide site-paths,

(provide 'site-paths)

bootstrap.sh

#!/bin/sh

Install Nix.

if ! command -v nix-env >/dev/null 2>&1; then
    nix_installer=$(mktemp)
    curl -s https://nixos.org/nix/install > $nix_installer
    sh $nix_installer
fi

Pull into Git repo.

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

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 https://github.com/matthewbauer/bauer $repo_dir
    cd $repo_dir
fi

Install current directory

nix-env -if .

runemacs.sh

Cross-platform script to execute app.

#!/usr/bin/env sh

case $(uname) in
    Darwin)
        open @out@/Applications/Emacs.app
    ;;
    *)
        @out@/bin/emacs
    ;;
esac

Building

default.nix: the tangler

{ nixpkgs-url ? "nixos.org/channels/nixpkgs-unstable/nixexprs.tar.xz"
, nixpkgs ? builtins.fetchTarball nixpkgs-url
, nixpkgs' ? import nixpkgs {}
}: with nixpkgs';

Now let’s tangle README.org…

import (runCommand "README" { buildInputs = [ emacs ]; } ''
mkdir -p $out
cd $out
cp -r ${./lisp} $out/lisp
cp ${./README.org} README.org
emacs --batch --quick \
      -l ob-tangle \
      --eval "(org-babel-tangle-file \"README.org\")"
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 ? "nixos.org/channels/nixpkgs-unstable/nixexprs.tar.xz"
, 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; [
    RCurl
  ];
};

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 ${./site-paths.el.in} $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} \
      $out/share/emacs/site-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} \
      $out/share/emacs/site-lisp/set-defaults.el
    cp ${./lisp/installer.el} $out/share/emacs/site-lisp/installer.el
    cp ${./lisp/restart-emacs.el} \
      $out/share/emacs/site-lisp/restart-emacs.el
    cp ${./lisp/company-eshell-history.el} \
      $out/share/emacs/site-lisp/company-eshell-history.el
    cp ${./lisp/use-package-list.el} \
      $out/share/emacs/site-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) ++
          [customEmacsPackages.melpaPackages.use-package])
    }/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
       done
       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"
         fi
       }
       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
       done
       # 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
  done
fi

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 ${./bashrc.sh} $out/etc/bashrc
substituteInPlace $out/etc/bashrc \
  --replace @fortune@ ${fortune} \
  --replace @out@ $out

cp ${./zshrc.sh} $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 ${./etc-profile.sh} $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 ${./runemacs.sh} $out/bin/run
substituteInPlace $out/bin/run \
  --replace @out@ $out
chmod +x $out/bin/run
  '';
  meta = {
    priority = -10;
  };
  pathsToLink = [
    "/bin"
    "/etc/profile.d"
    "/etc/bash_completion.d"
    "/etc/ssl"
    "/Applications"
    "/share/doc"
    "/share/man"
    "/share/info"
    "/share/zsh"
    "/share/bash-completion"
    "/share/mime"
  ];

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

  paths = [
    myEmacs
    myTex
    rEnv
    (runCommand "my-profile" { buildInputs = [makeWrapper]; } ''
mkdir -p $out/etc/profile.d
cp ${./profile.sh} $out/etc/profile.d/my-profile.sh
substituteInPlace $out/etc/profile.d/my-profile.sh \
  --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.

nix-build
./result/bin/run

Continuous integration

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

.travis.yml

language: nix

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

script:
  - nix-build -Q --argstr nixpkgs-url $NIXPKGS
  - sh info.sh result

Setup the OSs. Sadly no Windows support yet.

git:
  depth: 1
sudo: false
os:
  - linux
  - osx

Setup some values for NIXPKGS variables.

env:
  - NIXPKGS=nixos.org/channels/nixos-17.09/nixexprs.tar.xz
  - NIXPKGS=nixos.org/channels/nixpkgs-17.09-darwin/nixexprs.tar.xz
  - NIXPKGS=nixos.org/channels/nixos-unstable/nixexprs.tar.xz
  - NIXPKGS=nixos.org/channels/nixpkgs-unstable/nixexprs.tar.xz

Configure the matrix…

matrix:
  exclude:
    - os: linux
      env: NIXPKGS=nixos.org/channels/nixpkgs-17.09-darwin/nixexprs.tar.xz
    - os: osx
      env: NIXPKGS=nixos.org/channels/nixos-17.09/nixexprs.tar.xz
    - os: osx
      env: NIXPKGS=nixos.org/channels/nixos-unstable/nixexprs.tar.xz
  allow_failures:
    - env: NIXPKGS=nixos.org/channels/nixos-unstable/nixexprs.tar.xz
    - env: NIXPKGS=nixos.org/channels/nixpkgs-unstable/nixexprs.tar.xz

Setup the cache.

cache:
  directories:
    - /nix/store

Turn off those annoying Travis notifications.

notifications:
  email: false

Extra

update.sh

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

#!/bin/sh
emacs --batch \
      -l ob-tangle --eval "(org-babel-tangle-file \"README.org\")"

info.sh

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

#!/bin/sh
out=$1

if [ -z "$out" ]; then
  out=$(nix-build)
fi

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

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

.gitignore

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

flycheck_*.el
*.elc
*.pdf
*.html
*.tex
*.log
*.aux
*.out
*.toc

Nix-related stuff.

# nix stuff
result
result-*

These are all tangled by README.org.

README.el
bashrc.sh
bauer.nix
zshrc.sh
etc-profile.sh
runemacs.sh
gitconfig
gitignore
default.el
profile.sh
site-paths.el.in
init.el
org-init.el

config.nix

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;
  };
}

org-init.el

(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
  :config
  (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))
Other posts