on GitHub, with this version updated periodically.
Not that we needed all that for the trip, but once you get locked into a serious drug collection, the tendency is to push it as far as you can. Hunter S. Thompson, Fear and Loathing in Las Vegas
Add repositories from which we'll load packages. I prefer to live on the bleeding edge so have only enabled melpa. Setting package-enable-at-startup
to nil
prevents a second package load and slightly improves startup time.
(setq package-enable-at-startup nil)
(setq package-archives '(("gnu" . "http://mirrors.163.com/elpa/gnu/")
("melpa" . "https://melpa.org/packages/")
("org" . "http://orgmode.org/elpa/")))
If use-package
is not installed, install it.
(unless (package-installed-p 'use-package)
(package-refresh-contents)
(package-install 'use-package)
(eval-when-compile (require 'use-package)))
By default all packages should be installed from package manager as that's the usual path. This is equivalent to setting :ensure t
on each call to use-package
. To disable set :ensure nil
(this is done automatically for any packages using :load-path
so shouldn't generally be needed).
(setq use-package-always-ensure t)
benchmark-init
records startup time by package so we can debug. It only records things after it's initialised, so put as early in config as possible.
(use-package benchmark-init
:config
;; To disable collection of benchmark data after init is done.
(add-hook 'after-init-hook 'benchmark-init/deactivate))
(add-hook 'after-init-hook
(lambda () (message "loaded in %s" (emacs-init-time))))
The default garbage collection threshold is 800kB, increasing this to 10MB for startup increases speed (from 11.0s -> 9.7s when I tested).
(setq gc-cons-threshold 10000000)
;; Restore after startup
(add-hook 'after-init-hook
(lambda ()
(setq gc-cons-threshold 1000000)
(message "gc-cons-threshold restored to %S"
gc-cons-threshold)))
(defun find-config ()
"Edit config.org"
(interactive)
(find-file "~/dotfiles/config.org"))
(global-set-key (kbd "C-c I") 'find-config)
This stops emacs adding customised settings to init.el
. I try to avoid using customize anyway, preferring programmatic control of variables. Creating it as a temporary file effectively disables it (i.e. any changes are session local).
(setq custom-file (make-temp-file "emacs-custom"))
By default Emacs only includes files directly under user-emacs-directory
(usually ~/.emacs.d/
), so we need to add any folders containing custom packages.
I put my scripts under ~/dotfiles/lisp/
and symlink it with ln -s ~/dotfiles/lisp ~/.emacs.d/lisp
.
(add-to-list 'load-path "~/.emacs.d/lisp/")
This is useful to find out what I use a lot. I plan to then change bindings to improve ergonomics on commonly used functions.
(use-package keyfreq
:config
(keyfreq-mode 1)
(keyfreq-autosave-mode 1))
Don't display the help screen on startup.
(setq inhibit-startup-screen t)
On I use ⌘ as meta
and prefer ⌥ to do nothing so I can still insert special characters easily.
(setq mac-command-modifier 'meta
mac-option-modifier 'none)
I prefer lines to wrap.
(global-visual-line-mode 1)
Let's turn off unwanted window decoration.
(tool-bar-mode -1)
(scroll-bar-mode -1)
I don't want the error bell.
(setq ring-bell-function 'ignore)
Make the yes or no
prompts shorter.
(defalias 'yes-or-no-p 'y-or-n-p)
A common frustration with new Emacs users is the filename#
files created. This centralises the backup files created as you edit.
(setq backup-directory-alist '(("." . "~/.emacs.d/backup"))
backup-by-copying t ; Don't delink hardlinks
version-control t ; Use version numbers on backups
delete-old-versions t ; Automatically delete excess backups
kept-new-versions 20 ; how many of the newest versions to keep
kept-old-versions 5 ; and how many of the old
)
I usually don't want tabs, if I do I can set this buffer-local to t
. If I just want one tab then use C-q
(quoted-insert
) to insert as a literal.
(setq-default indent-tabs-mode nil)
crux
has useful functions extracted from Emacs Prelude. Set C-a
to move to the first non-whitespace character on a line, and then to toggle between that and the beginning of the line.
(use-package crux
:bind (("C-a" . crux-move-beginning-of-line)))
I never want whitespace at the end of lines. Remove it on save.
(add-hook 'before-save-hook 'delete-trailing-whitespace)
Evil-mode emulates Vim in Emacs.
(use-package evil
:config
;; (evil-mode 1)
(evil-set-initial-state 'NeoTree 'emacs))
Todo: evil leader etc?
God-mode is a sort-of alternative to Vim - it's a modal interface to emacs existing commands, so in essence when enabled you don't need to chord Ctrl / Meta. As an example C-x C-s
(save) becomes xs
.
(use-package god-mode
:disabled
:bind (("<escape>" . god-local-mode)
("C-x C-1" . delete-other-windows)
("C-x C-2" . split-window-below)
("C-x C-3" . split-window-right)
("C-x C-0" . delete-window)))
(defun my-update-cursor ()
(setq cursor-type (if (or god-local-mode buffer-read-only)
'box
'bar)))
(add-hook 'god-mode-enabled-hook 'my-update-cursor)
(add-hook 'god-mode-disabled-hook 'my-update-cursor)
Todo: update window-divider
on god-mode status?
Sometimes it's useful to step to the last changes in a buffer.
(use-package goto-last-change
:bind (("C-;" . goto-last-change)))
ivy
is a generic completion framework which uses the minibuffer. Turning on ivy-mode
enables replacement of lots of built in ido
functionality.
(use-package ivy
:config
(ivy-mode t))
By default ivy
starts filters with ^
. I don't normally want that and can easily type it manually when I do.
(setq ivy-initial-inputs-alist nil)
counsel
is a collection of ivy
enhanced versions of common Emacs commands. I haven't bound much as ivy-mode
takes care of most things.
(use-package counsel
:bind (("M-x" . counsel-M-x)))
prescient
sorts and filters candidate lists for avy/counsel.
(use-package prescient)
(use-package ivy-prescient
:config
(ivy-prescient-mode t))
swiper
is an ivy
enhanced version of isearch.
(use-package swiper
:bind (("M-s" . counsel-grep-or-swiper)))
hydra
presents menus for ivy
commands.
(use-package ivy-hydra)
major-mode-hydra
binds a single key to open a context sensitive hydra based on current major mode. Hydras can be defined in use-package
definitions via the :mode-hydra
integration.
(use-package major-mode-hydra
:bind
("C-M-SPC" . major-mode-hydra)
:config
(major-mode-hydra-define org-mode
()
("Tools"
(("l" org-lint "lint")))))
Suggest next keys to me based on currently entered key combination.
(use-package which-key
:config
(add-hook 'after-init-hook 'which-key-mode))
undo-tree
visualises undo history as a tree for easy navigation.
(use-package undo-tree
:defer 5
:config
(global-undo-tree-mode 1))
One of the most important features of an advanced editor is quick text navigation. avy
lets us jump to any character or line quickly.
(use-package avy)
ace-window
lets us navigate between windows in the same way as avy
. Once activated it has useful sub-modes like x
to switch into window deletion mode.
(use-package ace-window
:config
(setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)))
expand-region
expands the region around the cursor semantically depending on mode. Hard to describe but a killer feature.
(use-package expand-region
:bind ("C-=" . er/expand-region))
This function toggles the frame-parameter
fullscreen
so that I can maximise Emacs from within rather than relying on the external MacOS controls.
(defun mac-toggle-max-window ()
(interactive)
(set-frame-parameter
nil
'fullscreen
(if (frame-parameter nil 'fullscreen)
nil
'fullboth)))
I'm now using my own translation of Panda Theme (now on melpa!).
(use-package panda-theme
:disabled
:config
(load-theme 'panda t))
I also like Solarized.
(use-package solarized-theme
:config
(load-theme 'solarized-light t))
Set a nice font.
(set-frame-font "Operator Mono 12" nil t)
;; (set-frame-font "Inconsolata 13" nil t)
;; (set-frame-font "SF Mono 12" nil t)
feebleline
is a minimalist mode line replacement.
(use-package feebleline
:config
(feebleline-mode 't))
Add emoji support. This is useful when working with html.
(use-package emojify)
Improve look and feel of titlebar on Macos. Set ns-appearance
to dark
for white title text and nil
for black title text.
(add-to-list 'default-frame-alist '(ns-transparent-titlebar . t))
(add-to-list 'default-frame-alist '(ns-appearance . dark))
When programming I like my editor to try to help me with keeping parentheses balanced.
(use-package smartparens
:config
(add-hook 'prog-mode-hook 'smartparens-mode))
Highlight parens etc. for improved readability.
(use-package rainbow-delimiters
:config
(add-hook 'prog-mode-hook 'rainbow-delimiters-mode))
Highlight strings which represent colours. I only want this in programming modes, and I don't want colour names to be highlighted (x-colors
).
(use-package rainbow-mode
:config
(setq rainbow-x-colors nil)
(add-hook 'prog-mode-hook 'rainbow-mode))
Expand parentheses for me.
(add-hook 'prog-mode-hook 'electric-pair-mode)
fzf
is a fuzzy file finder which is very quick.
(use-package fzf)
deadgrep
uses rg
to search for strings, project.el
means it will automatically use the project root if (e.g.) it's a git repository, which is my usual use case.
(use-package deadgrep)
By default Emacs doesn't read from the same environment variables set in your terminal. This package fixes that.
(use-package exec-path-from-shell
:config
(exec-path-from-shell-initialize))
Individual language packages often support IDE features like jump to source, but dumb-jump
attempts to support many languages by simple searching. It's quite effective even with dynamic libraries like JS and Python.
(use-package dumb-jump
:bind (("C-M-g" . dumb-jump-go)
("C-M-p" . dumb-jump-back)
("C-M-q" . dumb-jump-quick-look)))
Magit is an awesome interface to git. Summon it with `C-x g`.
(use-package magit
:bind ("C-x g" . magit-status))
Display line changes in gutter based on git history. Enable it everywhere.
(use-package git-gutter
:config
(global-git-gutter-mode 't))
Flycheck
is a general syntax highlighting framework which other packages hook into. It's an improvment on the built in flymake
.
Setup is pretty simple - we just enable globally and turn on a custom eslint function, and also add a custom checker for proselint.
(use-package flycheck
:config
(add-hook 'after-init-hook 'global-flycheck-mode)
(add-hook 'flycheck-mode-hook 'jc/use-eslint-from-node-modules)
(add-to-list 'flycheck-checkers 'proselint)
(setq-default flycheck-highlighting-mode 'lines)
;; Define fringe indicator / warning levels
(define-fringe-bitmap 'flycheck-fringe-bitmap-ball
(vector #b00000000
#b00000000
#b00000000
#b00000000
#b00000000
#b00000000
#b00000000
#b00011100
#b00111110
#b00111110
#b00111110
#b00011100
#b00000000
#b00000000
#b00000000
#b00000000
#b00000000))
(flycheck-define-error-level 'error
:severity 2
:overlay-category 'flycheck-error-overlay
:fringe-bitmap 'flycheck-fringe-bitmap-ball
:fringe-face 'flycheck-fringe-error)
(flycheck-define-error-level 'warning
:severity 1
:overlay-category 'flycheck-warning-overlay
:fringe-bitmap 'flycheck-fringe-bitmap-ball
:fringe-face 'flycheck-fringe-warning)
(flycheck-define-error-level 'info
:severity 0
:overlay-category 'flycheck-info-overlay
:fringe-bitmap 'flycheck-fringe-bitmap-ball
:fringe-face 'flycheck-fringe-info))
Proselint is a syntax checker for English language. This defines a custom checker which will run in texty modes.
Proselint is an external program, install it with pip install proselint
for this to work.
(flycheck-define-checker proselint
"A linter for prose."
:command ("proselint" source-inplace)
:error-patterns
((warning line-start (file-name) ":" line ":" column ": "
(id (one-or-more (not (any " "))))
(message (one-or-more not-newline)
(zero-or-more "\n" (any " ") (one-or-more not-newline)))
line-end))
:modes (text-mode markdown-mode gfm-mode org-mode))
Eglot
is a client to Language Server Protocol servers.
(use-package eglot
:commands eglot
:config
(add-to-list 'eglot-server-programs '(elm-mode . ("elm-language-server" "--stdio"))))
Unlike autocomplete which suggests words / symbols, snippets are pre-prepared templates which you fill in.
Type the shortcut and press TAB
to complete, or M-/
to autosuggest a snippet.
(use-package yasnippet
:config
(add-to-list 'yas-snippet-dirs "~/.emacs.d/snippets")
(yas-global-mode 1))
Install some premade snippets (in addition to personal ones stored above)
(use-package yasnippet-snippets)
In JS indent to 2 spaces.
(setq-default js-indent-level 2)
JS2 mode improves on the built in JS mode.
(use-package js2-mode
:mode "\\.js\\'"
:config
(setq-default js2-ignored-warnings '("msg.extra.trailing.comma")))
js2-refactor
supports some useful refactoring options and builds on top of js2-mode
.
(use-package js2-refactor
:config
(js2r-add-keybindings-with-prefix "C-c C-m")
(add-hook 'js2-mode-hook 'js2-refactor-mode))
RJSX mode makes JSX work well.
(use-package rjsx-mode)
Prettier-js autoformats JS code - much like `gofmt` - and we hook it into JS2 and RJSX modes.
(use-package prettier-js
:config
(setq prettier-js-args '(
"--trailing-comma" "es5"
"--single-quote" "true"
"--print-width" "100"
))
(add-hook 'js2-mode-hook 'prettier-js-mode)
(add-hook 'rjsx-mode-hook 'prettier-js-mode))
js-doc
makes it easy to add jsdoc comments via Ctrl+c i
.
(use-package js-doc
:bind (:map js2-mode-map
("C-c i" . js-doc-insert-function-doc)
("@" . js-doc-insert-tag))
:config
(setq js-doc-mail-address "jamiecollinson@gmail.com"
js-doc-author (format "Jamie Collinson <%s>" js-doc-mail-address)
js-doc-url "jamiecollinson.com"
js-doc-license "MIT License"))
Sometimes it's useful to use the local eslint provided by a project's node_modules directory. We call this function from a flycheck hook to enable it automatically.
(defun jc/use-eslint-from-node-modules ()
"Set local eslint if available."
(let* ((root (locate-dominating-file
(or (buffer-file-name) default-directory)
"node_modules"))
(eslint (and root
(expand-file-name "node_modules/eslint/bin/eslint.js"
root))))
(when (and eslint (file-executable-p eslint))
(setq-local flycheck-javascript-eslint-executable eslint))))
We often want to use local packages instead of global ones.
(use-package add-node-modules-path)
Web mode handles html/css/js.
(use-package web-mode
:mode ("\\.html\\'")
:config
(setq web-mode-markup-indent-offset 2)
(setq web-mode-engines-alist
'(("django" . "focus/.*\\.html\\'")
("ctemplate" . "realtimecrm/.*\\.html\\'"))))
Web beautify prettifies html / css / js using js-beautify - install with npm install -g js-beautify
.
(use-package web-beautify
:bind (:map web-mode-map
("C-c b" . web-beautify-html)
:map js2-mode-map
("C-c b" . web-beautify-js)))
Markdown support isn't built into Emacs, add it with markdown-mode
.
(use-package markdown-mode
:commands (markdown-mode gfm-mode)
:mode (("README\\.md\\'" . gfm-mode)
("\\.md\\'" . markdown-mode)
("\\.markdown\\'" . markdown-mode))
:init (setq markdown-command "multimarkdown"))
Go-mode provides basic language support, we call gofmt
on each save to keep code tidy.
(use-package go-mode
:config
(add-hook 'before-save-hook 'gofmt-before-save))
Install haskell mode.
(use-package haskell-mode)
Code formatting is easier with hindent
.
(use-package hindent)
Pyvenv handles virtual environment support.
(use-package pyvenv)
Black is an opinionated pyton formatter. Install with pip install black
so the command line tool is available.
(use-package blacken
:config
(add-hook 'python-mode-hook 'blacken-mode))
Elixir highlighting is not built into emacs at present. Elixir-mode gives all the usual niceties, and alchemist improves interaction with tools like iex
, mix
and elixir-format
.
(use-package elixir-mode
:config
(use-package alchemist))
Open .v files with Proof General's Coq mode
(use-package proof-general)
Elm is a delightful language for reliable webapps. It compiles to JS. First install elm with npm install -g elm elm-format
.
(use-package elm-mode
:config
(setq elm-format-on-save t))
Dotnet core runs on linux / macos. Let's get syntax highlighting.
(use-package csharp-mode)
Rust is a zero-cost abstraction systems language.
(use-package rust-mode)
I should comment on these more…
(setq org-startup-indented 'f)
(setq org-directory "~/org")
(setq org-special-ctrl-a/e 't)
(setq org-default-notes-file (concat org-directory "/notes.org"))
(define-key global-map "\C-cc" 'org-capture)
(setq org-mobile-directory "~/Dropbox/Apps/MobileOrg")
(setq org-src-fontify-natively 't)
(setq org-src-tab-acts-natively t)
(setq org-src-window-setup 'current-window)
Customize appearance.
(let*
((base-font-color (face-foreground 'default nil 'default))
(headline `(:foreground ,base-font-color)))
(custom-theme-set-faces 'user
`(org-level-8 ((t (,@headline))))
`(org-level-7 ((t (,@headline))))
`(org-level-6 ((t (,@headline))))
`(org-level-5 ((t (,@headline))))
`(org-level-4 ((t (,@headline))))
`(org-level-3 ((t (,@headline :height 1.3))))
`(org-level-2 ((t (,@headline :height 1.3))))
`(org-level-1 ((t (,@headline :height 1.3 ))))
`(org-document-title ((t (,@headline :height 1))))))
writegood-mode
highlights bad word choices and has functions for calculating readability.
(use-package writegood-mode
:bind ("C-c g" . writegood-mode)
:config
(add-to-list 'writegood-weasel-words "actionable"))
notmuch
is a fast mail client. Install it externally, e.g. with brew install notmuch
and then use it within emacs.
(use-package notmuch)
I hesitate to say this is the emacs way, it's just what I felt necessary.