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))
- 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)))
- dired-column
- 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))
- esh-help
- 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))
- 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))
- 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))
- 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
- 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)))
- 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\\'")
- 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))
- intero
- Java
- 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))
- indium
- 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
- Lisp
(use-package elisp-mode :ensure nil :interpreter (("emacs" . emacs-lisp-mode)))
- LLVM IR
This is currently disabled.
(use-package llvm-mode :mode "\\.ll\\'")
- 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
- Markdown
- Nix
(use-package nix-mode :mode "\\.nix\\'")
- 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))
- Anaconda
- 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\\'")
- 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.
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
<a href=https://travis-ci.org/matthewbauer/bauer“> <img src=”https://travis-ci.org/matthewbauer/bauer.svg?branch=master“></img> </a>
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))