58 KiB
Emacs Configuration
- Basic config settings
- Configure
use-package
&discover.el
- Use
sensible-defaults.el
- Set personal information
- Add
resources
toload-path
evil-mode
- Utility functions
- UI preferences
- Project management
- Programming environments
- Terminal
- Org-mode
- Blogging
- Music
- Applications
- Writing prose
- Use
variable-pitch
fonts when writing prose - Enable spell-checking in the usual places
- Wrap paragraphs automatically
- Use Org-style lists and tables everywhere
- Linting prose
- Look up definitions
- Look up words in a thesaurus
- Editing with Markdown
- Cycle between spacing alternatives
- Enable region case modification
- Quickly explore my "notes" directory with
deft
- Use
- File management with
dired
- Editing settings
- Quickly visit Emacs configuration
- Always kill current buffer
- Set up
helpful
- Look for executables in
/usr/local/bin
- Save my location within a file
- Always indent with spaces
- Install and configure
which-key
- Configure
yasnippet
- Configure
ivy
andcounsel
- Switch and rebalance windows when splitting
- Mass editing of
grep
results - Use projectile everywhere
- Add a bunch of engines for
engine-mode
- Usenet
Basic config settings
Lexically bind the config
Read the wiki for more info about dynamic binding vs lexical binding.
;; -*- lexical-binding: t; -*-
Follow source-controlled symlinks
My configs are version-controlled and stored in a repo elsewhere on my system. I
use dotgit">dotgit
to manage them and it symlinks the configs in the repo to their
appropriate locations on-disk. For Emacs, that's ~/.emacs.d
. On startup, Emacs
will ask the user whether or not to follow that symlink and edit the "source"
file or the link. If you choose the former, you'll be able to use
version-control-related tools on the file. If you choose the latter, editing
should work fine but no VC tools will work. I want to follow the symlink.
(setq vc-follow-symlinks t)
Configure use-package
& discover.el
I use use-package
to install and configure my packages. My init.el
includes the
initial setup for package.el
and ensures that use-package
is installed, since I
wanna do that right away.
This makes sure that use-package
will install the package if it's not already
available. It also means that I should be able to open Emacs for the first time
on a fresh Debian box and have my whole environment automatically installed. I'm
not totally sure about that, but we're gettin' close.
(require 'use-package-ensure)
(setq use-package-always-ensure t)
Always compile packages and use the newest version available.
(use-package auto-compile
:config (auto-compile-on-load-mode))
(setq load-prefer-newer t)
I also use discover.el">discover.el
with compliant packages to discover their features.
(use-package discover)
Use sensible-defaults.el
Use sensible-defaults.el for some basic settings.
(load-file "~/.emacs.d/resources/sensible-defaults.el")
(sensible-defaults/use-all-settings)
(sensible-defaults/use-all-keybindings)
(sensible-defaults/backup-to-temp-directory)
Set personal information
Who am I? Where am I?
(setq user-full-name "Amolith"
user-mail-address "amolith@secluded.site")
Add resources
to load-path
(add-to-list 'load-path "~/.emacs.d/resources/")
evil-mode
Load and configure evil-mode
.
I'd prefer not to expand abbrevs when I hit escape. That's always jarring and usually not what I want. In particular, it makes working with Coq really frustrating.
Don't automatically load Evil bindings in different modes.
(use-package evil
:ensure t
:init
(setq evil-want-integration t)
(setq evil-want-abbrev-expand-on-insert-exit nil)
(setq evil-want-keybinding nil)
(setq evil-undo-system 'undo-tree)
:config
(evil-mode 1))
Install evil-collection
, which provides evil-friendly bindings for many modes.
(use-package evil-collection
:after evil
:ensure t
:config
(evil-collection-init))
Enable surround
everywhere.
(use-package evil-surround
:config
(global-evil-surround-mode 1))
Use evil
with Org agendas.
(use-package evil-org
:after org
:config
(add-hook 'org-mode-hook 'evil-org-mode)
(add-hook 'evil-org-mode-hook
(lambda () (evil-org-set-key-theme)))
(require 'evil-org-agenda)
(evil-org-agenda-set-keys))
evil-goggles
evil-goggles">evil-goggles
is a nice package to visually show what's happening while
editing but my theme fucks it up.
;(use-package evil-goggles
; :ensure t
; :config
; (evil-goggles-mode)
; (evil-goggles-use-diff-faces))
Utility functions
Define a big ol' bunch of handy utility functions.
(defun amo/rename-file (new-name)
(interactive "FNew name: ")
(let ((filename (buffer-file-name)))
(if filename
(progn
(when (buffer-modified-p)
(save-buffer))
(rename-file filename new-name t)
(kill-buffer (current-buffer))
(find-file new-name)
(message "Renamed '%s' -> '%s'" filename new-name))
(message "Buffer '%s' isn't backed by a file!" (buffer-name)))))
(defun amo/generate-scratch-buffer ()
"Create and switch to a temporary scratch buffer with a random
name."
(interactive)
(switch-to-buffer (make-temp-name "scratch-")))
(defun amo/visit-last-migration ()
"Open the most recent Rails migration. Relies on projectile."
(interactive)
(let ((migrations
(directory-files
(expand-file-name "db/migrate" (projectile-project-root)) t)))
(find-file (car (last migrations)))))
(defun amo/add-auto-mode (mode &rest patterns)
"Add entries to `auto-mode-alist' to use `MODE' for all given file `PATTERNS'."
(dolist (pattern patterns)
(add-to-list 'auto-mode-alist (cons pattern mode))))
(defun amo/find-file-as-sudo ()
(interactive)
(let ((file-name (buffer-file-name)))
(when file-name
(find-alternate-file (concat "/sudo::" file-name)))))
(defun amo/region-or-word ()
(if mark-active
(buffer-substring-no-properties (region-beginning)
(region-end))
(thing-at-point 'word)))
(defun amo/append-to-path (path)
"Add a path both to the $PATH variable and to Emacs' exec-path."
(setenv "PATH" (concat (getenv "PATH") ":" path))
(add-to-list 'exec-path path))
(defun amo/insert-password ()
(interactive)
(shell-command "pwgen 30 -1" t))
(defun amo/notify-send (title message)
"Display a desktop notification by shelling out to `notify-send'."
(call-process-shell-command
(format "notify-send -t 2000 \"%s\" \"%s\"" title message)))
UI preferences
Auto-centre windows
I have a wide display and don't really like keeping my head turned to the left
when reading or writing something. perfect-margin">perfect-margin
is a package that keeps
windows centred. I've also set fill-column to 72 to keep text within that field.
(setq-default fill-column 72)
(use-package perfect-margin
:custom
(perfect-margin-visible-width 80)
:config
(perfect-margin-mode 1))
(add-to-list 'perfect-margin-ignore-filters #'window-minibuffer-p)
(setq which-key-popup-type 'minibuffer)
(defcustom perfect-margin-ignore-regexps
'("^minibuf", "\\*which-key\\*")
"List of strings to determine if window is ignored.
Each string is used as regular expression to match the
window buffer name."
:group 'perfect-margin)
Keep point in the ~center of the screen
I like seeing context around what I'm writing but, if the cursor is at the
bottom of the screen while scrolling, you have to scroll further and then back
to edit what you want. smooth-scrolling">smooth-scrolling
fixes that 👍
(use-package smooth-scrolling
:ensure t
:config
(smooth-scrolling-mode 1)
(setq smooth-scroll-margin 15))
Tweak window chrome
I don't usually use the menu or scroll bar, and they take up useful space.
(tool-bar-mode 0)
(menu-bar-mode 0)
(scroll-bar-mode -1)
There's a tiny scroll bar that appears in the minibuffer window. This disables that:
(set-window-scroll-bars (minibuffer-window) nil nil)
The default frame title isn't useful. This binds it to the name of the current project:
(setq frame-title-format '((:eval (projectile-project-name))))
Use fancy lambdas
Why not?
(global-prettify-symbols-mode t)
Load a theme
Apparently I switch themes a lot ¯\_(ツ)_/¯
;(use-package base16-theme
; :ensure t
; :config
; (load-theme 'base16-unikitty-light t))
I'm seeing how I like gruvbox right now
;(use-package gruvbox-theme
; :ensure t
; :config
; (load-theme 'gruvbox-light-medium t))
Maybe this one
(use-package doom-themes
:ensure t
:config
; (load-theme 'doom-wilmersdorf t))
(load-theme 'doom-dracula t))
#
#
#
Highlight email files
I use OfflineIMAP, notmuch
, afew
, NeoMutt, and msmtp
for email. Emacs doesn't
autodetect mail filetype when editing replies in NeoMutt and this enables that.
(add-to-list 'auto-mode-alist '("/tmp/neomutt-*" . mail-mode))
Set new modeline
Use doom-modeline">doom-modeline
instead of default.
(use-package doom-modeline
:ensure t
:hook (after-init . doom-modeline-mode))
Disable visual bell
sensible-defaults
replaces the audible bell with a visual one, but I really
don't even want that. This disables the bell altogether.
(setq ring-bell-function 'ignore)
Scroll conservatively
When point goes outside the window, Emacs usually recenters the buffer point. I'm not crazy about that. This changes scrolling behavior to only scroll as far as point goes.
(setq scroll-conservatively 100)
Set default font and configure font resizing
I'm partial to Recursive Mono for code and Valkyrie for prose.
The standard text-scale-
functions just resize the text in the current buffer;
I'd generally like to resize the text in every buffer, and I usually want to
change the size of the modeline, too (this is especially helpful when
presenting). These functions and bindings let me resize everything all together!
Note that this overrides the default font-related keybindings from
sensible-defaults
.
(set-face-attribute 'default nil :height 120 :family "Triplicate T4c")
(set-face-attribute 'fixed-pitch nil :height 120 :family "Triplicate T4c")
(set-face-attribute 'variable-pitch nil :height 140 :family "Concourse T4")
(setq amo/default-font "Triplicate T4c")
(setq amo/default-font-size 12)
(setq amo/current-font-size amo/default-font-size)
(setq amo/font-change-increment 1.1)
(defun amo/font-code ()
"Return a string representing the current font (like \"Inconsolata-14\")."
(concat amo/default-font "-" (number-to-string amo/current-font-size)))
(defun amo/set-font-size ()
"Set the font to `amo/default-font' at `amo/current-font-size'.
Set that for the current frame, and also make it the default for
other, future frames."
(let ((font-code (amo/font-code)))
(if (assoc 'font default-frame-alist)
(setcdr (assoc 'font default-frame-alist) font-code)
(add-to-list 'default-frame-alist (cons 'font font-code)))
(set-frame-font font-code)))
(defun amo/reset-font-size ()
"Change font size back to `amo/default-font-size'."
(interactive)
(setq amo/current-font-size amo/default-font-size)
(amo/set-font-size))
(defun amo/increase-font-size ()
"Increase current font size by a factor of `amo/font-change-increment'."
(interactive)
(setq amo/current-font-size
(ceiling (* amo/current-font-size amo/font-change-increment)))
(amo/set-font-size))
(defun amo/decrease-font-size ()
"Decrease current font size by a factor of `amo/font-change-increment', down to a minimum size of 1."
(interactive)
(setq amo/current-font-size
(max 1
(floor (/ amo/current-font-size amo/font-change-increment))))
(amo/set-font-size))
(define-key global-map (kbd "C-)") 'amo/reset-font-size)
(define-key global-map (kbd "C-+") 'amo/increase-font-size)
(define-key global-map (kbd "C-=") 'amo/increase-font-size)
(amo/reset-font-size)
Highlight the current line
global-hl-line-mode
softly highlights the background color of the line
containing point. It makes it a bit easier to find point, and it's useful when
pairing or presenting code.
(global-hl-line-mode)
Highlight uncommitted changes
Use the diff-hl
package to highlight changed-and-uncommitted lines when
programming.
(use-package diff-hl
:config
(add-hook 'prog-mode-hook 'turn-on-diff-hl-mode)
(add-hook 'vc-dir-mode-hook 'turn-on-diff-hl-mode))
Project management
I use a few packages in virtually every programming or writing environment to manage the project, handle auto-completion, search for terms, and deal with version control. That's all in here.
ag
Install ag">ag
to provide search within projects (usually through
projectile-ag
).
(use-package ag)
avy
Install avy">avy
to skip around the screen quickly.
(use-package avy
:bind*
("C-;" . evil-avy-goto-char-2))
company
Use company-mode">company-mode
everywhere.
(use-package company)
(add-hook 'after-init-hook 'global-company-mode)
Use C-n
for completion.
(global-set-key (kbd "C-n") 'company-complete-common)
dumb-jump
The dumb-jump">dumb-jump
package works well enough in a ton of environments, and it
doesn't require any additional setup. I've bound its most useful command to
M-.
.
(use-package dumb-jump
:config
(add-hook 'xref-backend-functions #'dumb-jump-xref-activate)
(define-key evil-normal-state-map (kbd "M-.") 'xref-find-definitions)
(setq dumb-jump-selector 'ivy))
flycheck
I'd like to enable flycheck all kinds of places.
(use-package let-alist)
(use-package flycheck
:init (global-flycheck-mode))
magit
I use magit">magit
to handle version control. It's lovely, but I tweak a few things:
- I bring up the status menu with
C-x g
. - Use
evil
keybindings withmagit
. - The default behavior of
magit
is to ask before pushing. I haven't had any problems with accidentally pushing, so I'd rather not confirm that every time. - Per tpope's suggestions, highlight commit text in the summary line that goes beyond 50 characters.
-
I'd like to start in the insert state when writing a commit message.
(use-package magit :bind ("C-x g" . magit-status) :config (use-package with-editor) (setq magit-push-always-verify nil git-commit-summary-max-length 50) (add-hook 'with-editor-mode-hook 'evil-insert-state))
forge">
forge
and ghub">ghub
provide ways to interact with various Git forges such as GitHub and Gitea from within Emacs.(use-package ghub) (use-package forge)
I'm also partial to git-timemachine">
git-timemachine
, which lets you quickly page through the history of a file.(use-package git-timemachine)
projectile
Projectile's default binding of projectile-ag
to C-c p s s
is clunky enough
that I rarely use it (and forget it when I need it). This binds it to the
easier-to-type C-c v
for useful searches.
Bind C-p
to fuzzy-finding files in the current project. We also need to
explicitly set that in a few other modes.
I use ivy">ivy
as my completion system.
When I visit a project with projectile-switch-project
, the default action is
to search for a file in that project. I'd rather just open up the top-level
directory of the project in dired
and find (or create) new files from there.
I'd like to always be able to recursively fuzzy-search for files, not just when I'm in a Projectile-defined project. I use the current directory as a project root (if I'm not in a "real" project).
(use-package projectile
:bind
("C-c v" . projectile-ag)
:config
(define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)
(define-key evil-normal-state-map (kbd "C-p") 'projectile-find-file)
(evil-define-key 'motion ag-mode-map (kbd "C-p") 'projectile-find-file)
(evil-define-key 'motion rspec-mode-map (kbd "C-p") 'projectile-find-file)
(evil-define-key 'motion rspec-compilation-mode-map (kbd "C-p") 'projectile-find-file)
(setq projectile-completion-system 'ivy
projectile-switch-project-action 'projectile-dired
projectile-require-project-root nil))
undo-tree
I like tree-based undo management. I only rarely need it, but when I do, oh boy. Docs are in ELPA.
(use-package undo-tree
:config (global-undo-tree-mode 1))
Programming environments
I like shallow indentation, but tabs are displayed as 8 characters by default. This reduces that.
(setq-default tab-width 4)
Treating terms in CamelCase symbols as separate words makes editing a little
easier for me, so I like to use subword-mode
everywhere. Info about what
"subword" means can be found on gnu.org.
(use-package subword
:config (global-subword-mode 1))
Compilation output goes to the *compilation*
buffer. I rarely have that window
selected, so the compilation output disappears past the bottom of the window.
This automatically scrolls the compilation window so I can always see the
output.
(setq compilation-scroll-output t)
I use eglot
as my LSP client.
(use-package eglot)
CSS, Sass, and Less
Indent by 4 spaces.
(use-package css-mode
:config
(setq css-indent-offset 4))
Don't compile the current SCSS file every time I save.
(use-package scss-mode
:config
(setq scss-compile-at-save nil))
Install Less.
(use-package less-css-mode)
Golang
Install go-mode">go-mode
, go-errcheck">go-errcheck
, and company-go">company-go
:
(use-package go-mode)
(use-package go-errcheck)
(use-package company-go)
Define my $GOPATH
and tell Emacs where to find the Go binaries.
(setenv "GOPATH" "~/go")
(amo/append-to-path (concat (getenv "GOPATH") "/bin"))
Run goimports
on every file when saving, which formats the file and
automatically updates the list of imports. This requires that the goimports
binary be installed.
(setq gofmt-command "goimports")
(add-hook 'before-save-hook 'gofmt-before-save)
When I open a Go file,
- Start up
company-mode
with the Go backend. This requires that thegocode
binary is installed, - Redefine the default
compile
command to something Go-specific, and -
Enable
flycheck
.(add-hook 'go-mode-hook (lambda () (set (make-local-variable 'company-backends) '(company-go)) (company-mode) (if (not (string-match "go" compile-command)) (set (make-local-variable 'compile-command) "go build -v && go test -v && go vet")) (flycheck-mode)))
Haskell
Install haskell-mode">haskell-mode
for various niceties.
(use-package haskell-mode)
Enable haskell-doc-mode
, which displays the type signature of a function, and
use smart indentation.
(add-hook 'haskell-mode-hook
(lambda ()
(haskell-doc-mode)
(turn-on-haskell-indent)))
(amo/append-to-path "~/.cabal/bin")
Lisps
All Lisps
I like to use paredit">paredit
in Lisp modes to balance parentheses (and more!).
(use-package paredit)
rainbow-delimiters">rainbow-delimiters
is convenient for coloring matching parentheses.
(use-package rainbow-delimiters)
All the lisps have some shared features, so we want to do the same things for
all of them. That includes using paredit
, rainbow-delimiters
, and
highlighting the whole expression when point is on a parenthesis.
(setq lispy-mode-hooks
'(clojure-mode-hook
emacs-lisp-mode-hook
lisp-mode-hook
scheme-mode-hook))
(dolist (hook lispy-mode-hooks)
(add-hook hook (lambda ()
(setq show-paren-style 'expression)
(paredit-mode)
(rainbow-delimiters-mode))))
If I'm writing in Emacs lisp I'd like to use eldoc-mode">eldoc-mode
to display
documentation.
(use-package eldoc
:config
(add-hook 'emacs-lisp-mode-hook 'eldoc-mode))
I also like using flycheck-package">flycheck-package
to ensure that my Elisp packages are
correctly formatted.
(use-package flycheck-package)
(eval-after-load 'flycheck
'(flycheck-package-setup))
Python
(use-package python-mode)
Add ~/.local/bin
to load path. That's where virtualenv
is installed, and
we'll need that for jedi
.
(amo/append-to-path "~/.local/bin")
Enable elpy
. This provides automatic indentation, auto-completion, syntax
checking, etc.
(use-package elpy)
(elpy-enable)
Use flycheck
for syntax checking:
(add-hook 'elpy-mode-hook 'flycheck-mode)
Format code according to PEP8 on save:
(use-package py-autopep8)
(require 'py-autopep8)
(add-hook 'elpy-mode-hook 'py-autopep8-enable-on-save)
Configure Jedi along with the associated company
mode:
(use-package company-jedi)
(add-to-list 'company-backends 'company-jedi)
(add-hook 'python-mode-hook 'jedi:setup)
(setq jedi:complete-on-dot t)
Rust
Use rust-mode">rust-mode
to edit Rust code.
Run rustfmt
automatically when saving a file.
(use-package rust-mode
:config
(amo/append-to-path "~/.cargo/bin")
(setq rust-format-on-save t))
web-mode
(use-package web-mode
:config
(setq web-mode-markup-indent-offset 2
web-mode-css-indent-offset 2
web-mode-code-indent-offset 2
web-mode-indent-style 2))
I'd like to see colors with rainbow-mode
, so we'll need to install that, too:
(use-package rainbow-mode
:hook web-mode)
Use web-mode
with HTML and PHP.
(amo/add-auto-mode
'web-mode
"\\.html$"
"\\.php$")
YAML
Ensure that we always use fixed-pitch
fonts for YAML. The ocs are on MELPA.
(use-package yaml-mode
:config
(add-hook 'yaml-mode-hook (lambda () (variable-pitch-mode 0))))
Terminal
I use multi-term
to manage my shell sessions. It's bound to C-c t
.
(use-package multi-term)
(global-set-key (kbd "C-c t") 'multi-term)
Use a login bash
shell:
(setq multi-term-program-switches "--login")
I'd rather not use Evil in the terminal. It's not especially useful (I don't use
vi bindings in xterm) and it shadows useful keybindings (C-d
for EOF, for
example).
(evil-set-initial-state 'term-mode 'emacs)
I add a bunch of hooks to term-mode
:
- I'd like links (URLs, etc) to be clickable.
- Yanking in
term-mode
doesn't quite work. The text from the paste appears in the buffer but isn't sent to the shell process. This correctly bindsC-y
and middle-click to yank the way we'd expect. - I bind
M-o
to quickly change windows. I'd like that in terminals, too. - I don't want to perform
yasnippet
expansion when tab-completing.
(defun amo/term-paste (&optional string)
(interactive)
(process-send-string
(get-buffer-process (current-buffer))
(if string string (current-kill 0))))
(add-hook 'term-mode-hook
(lambda ()
(goto-address-mode)
(define-key term-raw-map (kbd "C-y") 'amo/term-paste)
(define-key term-raw-map (kbd "<mouse-2>") 'amo/term-paste)
(define-key term-raw-map (kbd "M-o") 'other-window)
(setq yas-dont-activate t)))
Org-mode
Including org-tempo
restores the <s
-style easy-templates that were
deprecated in Org 9.2.
I'd like to open file:
links in Org with the applications defined in my
mailcap. This clears the existing MIME mapping, parses my personal mailcap, and
tells Org to open those links with the mailcap-defined applications.
(use-package org
:ensure org-plus-contrib
:config
(require 'org-tempo)
(add-hook 'org-mode-hook
'(lambda ()
(setq mailcap-mime-data '())
(mailcap-parse-mailcap "~/.mailcap")
(setq org-file-apps
'((remote . emacs)
("mobi" . "fbreader %s")
(system . mailcap)
("md" . emacs)
("org" . emacs)
(t . mailcap))))))
I'd like the initial scratch buffer to be in Org:
(setq initial-major-mode 'org-mode)
Display preferences
I like to see an outline of pretty bullets instead of a list of asterisks.
(use-package org-bullets
:init
(add-hook 'org-mode-hook 'org-bullets-mode))
I like seeing a little downward-pointing arrow instead of the usual ellipsis
(...
) that org displays when there's stuff under a header.
(setq org-ellipsis " ⤵")
(setq org-hide-emphasis-markers t)
Use syntax highlighting in source blocks while editing.
(setq org-src-fontify-natively t)
Make TAB act as if it were issued in a buffer of the language's major mode.
(setq org-src-tab-acts-natively t)
When editing a code snippet, use the current window rather than popping open a new one (which shows the same information).
(setq org-src-window-setup 'current-window)
Quickly insert a block of elisp:
(add-to-list 'org-structure-template-alist
'("el" . "src emacs-lisp"))
Don't indent newly expanded blocks, even if they're under a heading.
(setq org-adapt-indentation nil)
Task management and agenda views
Store my org files in ~/Org/
, define the location of an index file (my main todo
list), and archive finished tasks in ~/Org/archive.org
.
(setq org-directory "~/Org")
(defun org-file-path (filename)
"Return the absolute address of an org file, given its relative name."
(concat (file-name-as-directory org-directory) filename))
(setq org-inbox-file "~/Org/Inbox.org")
(setq org-index-file (org-file-path "Index.org"))
(setq org-archive-location
(concat (org-file-path "archive/Archive.org") "::* From %s"))
I store most of my personal tasks in my index and maintain a separate file for
work-related tasks, so I'd like to derive my agenda from those files. I've also
got some annual OKRs in Goals.org
.
I also keep a schedule in Events.org
. Plus some recurring events in,
reasonably, a Recurring-Events.org
file. Those are (mostly) structured as
org-habit
items so they can recur according to a schedule.
(setq org-agenda-files (list org-index-file
(org-file-path "Events.org")
(org-file-path "Goals.org")
(org-file-path "Recurring-Events.org")
(org-file-path "Work.org")))
Hitting C-c C-x C-s
will mark a todo as done and move it to an appropriate
place in the archive.
(defun amo/mark-done-and-archive ()
"Mark the state of an org-mode item as DONE and archive it."
(interactive)
(org-todo 'done)
(org-archive-subtree))
(define-key org-mode-map (kbd "C-c C-x C-s") 'amo/mark-done-and-archive)
Record the time that a todo was archived.
(setq org-log-done 'time)
Ensure that a task can't be marked as done if it contains unfinished subtasks or checklist items. This is handy for organizing "blocking" tasks hierarchically.
(setq org-enforce-todo-dependencies t)
(setq org-enforce-todo-checkbox-dependencies t)
Begin weeks today, not on the last Monday.
(setq org-agenda-start-on-weekday nil)
Hide the category prefix from tasks. In practice, I've usually only got one or two files of tasks, so prefixing tasks with the file they're stored in is mostly redundant.
(setq org-agenda-prefix-format '((agenda . " %i %?-12t% s")
(todo . " %i ")
(tags . " %i ")
(search . " %i ")))
I use org-habit
to schedule recurring events and reminders for myself.
(require 'org-habit)
#
I've got some custom agenda views that I use to visualize what I'm working on.
The "Personal agenda" view is a bit complicated; it displays:
- My agenda for the next few days, including any recurring habits or scheduled events,
- All the other, not-high-priority non-habit TODO items,
- All the PENDING tasks, which are awaiting a response from someone else but still require my attention (I might need to ping someone, say), and
- All the BLOCKED tasks, which are pending other tasks (but might be worth keeping an eye on anyway).
I effectively use the "A" priority to decide where I want to focus my attention on a given day (I'll assign those priorities the night before, if I'm really on top of things), so displaying them at the top makes sense.
A lot of this is based on Aaron Bieber's agenda configuration, including the
skip-subtree-if-priority
and skip-subtree-if-habit
functions, which I've
shamelessly stolen and re-prefixed.
(defun amo/org-skip-subtree-if-priority (priority)
"Skip an agenda subtree if it has a priority of PRIORITY.
PRIORITY may be one of the characters ?A, ?B, or ?C."
(let ((subtree-end (save-excursion (org-end-of-subtree t)))
(pri-value (* 1000 (- org-lowest-priority priority)))
(pri-current (org-get-priority (thing-at-point 'line t))))
(if (= pri-value pri-current)
subtree-end
nil)))
(defun amo/org-skip-subtree-if-habit ()
"Skip an agenda entry if it has a STYLE property equal to \"habit\"."
(let ((subtree-end (save-excursion (org-end-of-subtree t))))
(if (string= (org-entry-get nil "STYLE") "habit")
subtree-end
nil)))
(setq org-agenda-custom-commands
'(("p" "Personal agenda"
((agenda "")
(todo "TODO"
((org-agenda-skip-function '(or (amo/org-skip-subtree-if-priority ?A)
(amo/org-skip-subtree-if-habit)))
(org-agenda-overriding-header "Other tasks:")))
(todo "PENDING"
((org-agenda-skip-function '(amo/org-skip-subtree-if-priority ?A))
(org-agenda-overriding-header "Pending:")))
(todo "BLOCKED"
((org-agenda-skip-function '(amo/org-skip-subtree-if-priority ?A))
(org-agenda-overriding-header "Blocked:")))))))
(setq org-todo-keywords '((sequence "TODO(t)" "INPROGRESS(i)" "WAITING(w)" "|" "DONE(d)" "CANCELLED(c)")))
I consult my agenda pretty often, so I bind C-c d
to open it a it faster. This
also copies any files I've sent through Drafts into my index file before
displaying the index, so they'll be in the agenda view, too.
(defun amo/dashboard ()
(interactive)
(find-file org-index-file)
(org-agenda nil "p"))
(global-set-key (kbd "C-c d") 'amo/dashboard)
Calendaring UI
Ensure emacs-calfw">emacs-calfw
is installed.
(require 'calfw-org)
Some simple configuration
(setq ; Use pretty lines
cfw:fchar-junction ?╋
cfw:fchar-vertical-line ?┃
cfw:fchar-horizontal-line ?-
cfw:fchar-left-junction ?┣
cfw:fchar-right-junction ?┫
cfw:fchar-top-junction ?┯
cfw:fchar-top-left-corner ?┏
cfw:fchar-top-right-corner ?┓
; Improve line-breaking
cfw:render-line-breaker 'cfw:render-line-breaker-wordwrap)
Capturing tasks
Define a few common tasks as capture templates. Specifically, I frequently:
- Record ideas for future blog posts in
~/Org/Documents/Blog-Ideas.org
, - Maintain a todo list in
~/Org/Documents/Inbox.org
.
(setq org-capture-templates
'(("b" "Blog idea"
entry
(file "~/Org/Blog-Ideas.org")
"* %?\n")
("c" "Contact"
entry
(file "~/Org/Contacts.org")
"* %(org-contacts-template-name)
:PROPERTIES:
:ADDRESS: %^{123 Fake St., City, ST 12345}
:PHONE: %^{555-555-5555}
:EMAIL: %(org-contacts-template-email)
:NOTE: %^{note}
:END:")
("t" "ToDo"
entry
(file+headline org-index-file "Inbox")
"* TODO %?\n")
("e" "Event"
entry
(file+headline org-index-file "Events")
"* %?\n")))
When I'm starting an Org capture template I'd like to begin in insert mode. I'm opening it up in order to start typing something, so this skips a step.
(add-hook 'org-capture-mode-hook 'evil-insert-state)
Refiling according to the document's hierarchy.
(setq org-refile-use-outline-path t)
(setq org-outline-path-complete-in-steps nil)
Keybindings
Bind a few handy keys.
(define-key global-map "\C-cl" 'org-store-link)
(define-key global-map "\C-ca" 'org-agenda)
(define-key global-map "\C-cc" 'org-capture)
Hit C-c i
to quickly open up my todo list.
(defun amo/open-index-file ()
"Open the master org TODO list."
(interactive)
(find-file org-index-file)
(flycheck-mode -1)
(end-of-buffer))
(global-set-key (kbd "C-c i") 'amo/open-index-file)
Hit M-n
to quickly open up a capture template for a new todo.
(defun org-capture-todo ()
(interactive)
(org-capture :keys "t"))
(global-set-key (kbd "M-n") 'org-capture-todo)
(add-hook 'gfm-mode-hook
(lambda () (local-set-key (kbd "M-n") 'org-capture-todo)))
(add-hook 'haskell-mode-hook
(lambda () (local-set-key (kbd "M-n") 'org-capture-todo)))
Hit C-c w
to quickly open up my work todo list.
(defun amo/open-work-file ()
"Open the work TODO list."
(interactive)
(find-file (org-file-path "work.org"))
(flycheck-mode -1)
(end-of-buffer))
(global-set-key (kbd "C-c w") 'amo/open-work-file)
Exporting
Allow export to markdown and beamer (for presentations).
(require 'ox-md)
(require 'ox-beamer)
Allow babel
to evaluate Emacs lisp, Ruby, ditaa
, Graphviz, or Gnuplot code.
(use-package gnuplot)
(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
(ruby . t)
(ditaa . t)
(dot . t)
(gnuplot . t)))
Don't ask before evaluating code blocks.
(setq org-confirm-babel-evaluate nil)
Use htmlize
to ensure that exported code blocks use syntax highlighting.
(use-package htmlize)
Associate the "dot" language with the graphviz-dot
major mode.
(use-package graphviz-dot-mode)
(add-to-list 'org-src-lang-modes '("dot" . graphviz-dot))
Translate regular ol' straight quotes to typographically-correct curly quotes when exporting.
(setq org-export-with-smart-quotes t)
Exporting to HTML
Exporting to HTML and opening the results triggers /usr/bin/sensible-browser
,
which checks the $BROWSER
environment variable to choose the right browser.
I'd like to always use Firefox, so:
(setq browse-url-browser-function 'browse-url-generic
browse-url-generic-program "firefox-developer-edition")
(setenv "BROWSER" "firefox-developer-edition")
Exporting to PDF
I want to produce PDFs with syntax highlighting in the code. The best way to do
that seems to be with the minted
package, but that package shells out to
pygments
to do the actual work. pdflatex
usually disallows shell commands;
this enables that.
(setq org-latex-pdf-process
'("xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"
"xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"
"xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"))
Include the minted
package in all of my LaTeX exports.
(add-to-list 'org-latex-packages-alist '("" "minted"))
(setq org-latex-listings 'minted)
TeX configuration
I rarely write LaTeX directly any more, but I often export through it with org-mode, so I'm keeping them together.
Automatically parse the file after loading it.
(setq TeX-parse-self t)
Always use pdflatex
when compiling LaTeX documents. I don't really have any
use for DVIs.
(setq TeX-PDF-mode t)
Enable a minor mode for dealing with math (it adds a few useful keybindings), and always treat the current file as the "main" file. That's intentional, since I'm usually actually in an org document.
(add-hook 'LaTeX-mode-hook
(lambda ()
(LaTeX-math-mode)
(setq TeX-master t)))
PlantUML Support
(use-package plantuml-mode
:init
(setq
org-plantuml-jar-path (expand-file-name "~/.emacs.d/resources/plantuml.jar"))
(add-to-list 'org-src-lang-modes '("plantuml" . plantuml))
(org-babel-do-load-languages 'org-babel-load-languages
'((plantuml . t))))
Blogging
Building sites with org-publish
I kinda still suck with Emacs so let me know if this can be improved.
(setq org-publish-project-alist
'(("NixNet Mail Pages"
:base-directory "~/repos/mail/"
:base-extension "org"
:publishing-directory "~/repos/mail/build"
:publishing-function org-html-publish-to-html
:recursive t
:section-number nil
:html-postamble nil
:html-head "<link rel='stylesheet' type='text/css' href='/styles.css' />"
:html-html5-fancy t
:with-toc nil
:section-numbers nil)
("NixNet Mail Assets"
:base-directory "~/repos/mail/"
:base-extension "css"
:publishing-directory "/tmp/nnm/"
:publishing-function org-publish-attachment)
("NixNet Mail" :components ("NixNet Mail Pages" "NixNet Mail Assets"))))
Hugo
I maintain a blog generated with Hugo. There are plenty of command-line tools to automate creating a new post, but staying in my editor minimizes friction and encourages me to write.
I use ox-hugo">ox-hugo
for writing and publishing posts. Since moving to Org, I don't
ever want to leave and its deep integration is absolutely awesome.
(use-package ox-hugo
:ensure t
:after ox)
Might be switching to org-roam
(use-package org-roam
:ensure t
:hook
(after-init . org-roam-mode)
:custom
(org-roam-directory "~/Org/Roam")
(org-roam-buffer-position 'right)
(org-roam-db-update-method 'immediate)
:bind (:map org-roam-mode-map
(("C-c n l" . org-roam)
("C-c n f" . org-roam-find-file)
("C-c n g" . org-roam-graph))
:map org-mode-map
(("C-c n i" . org-roam-insert))
(("C-c n I" . org-roam-insert-immediate))))
Music
I've taken to charting my guitar music out with ChordPro. Chordpro mode just adds a major mode for syntax highlighting and some keybinds.
(setq auto-mode-alist (cons '("\\.cho$" . chordpro-mode) auto-mode-alist))
(autoload 'chordpro-mode "chordpro-mode")
I use LilyPond for writing standard sheet music. When you install the package, it comes with some elisp files that Emacs needs to load and configure.
(require 'lilypond-mode)
(add-to-list 'auto-mode-alist '("\\.ly$" . LilyPond-mode))
(setq LilyPond-pdf-command "zathura")
Applications
Gemini browser
(use-package elpher :ensure t)
ledger-mode
I've started doing my accounting with ledger
and ledger-mode facilitates that
from within Emacs.
(use-package ledger-mode :ensure t)
Writing prose
I write prose in several modes: I might be editing an Org document, or a commit message, or an email. These are the main ones, with sub-items being derived from their parents:
git-commit-mode
-
text-mode
markdown-mode
-
gfm-mode
message-mode
-
mail-mode
org-mode
Recall that derived modes "inherit" their parent's hooks, so a hook added onto
e.g. text-mode
will also be executed by mu4e-compose-mode
.
There are some exceptions, but I can usually associate a hook with every prose-related mode, so I store those in a list:
(defvar prose-modes
'(gfm-mode
git-commit-mode
markdown-mode
message-mode
mail-mode
org-mode
text-mode))
(defvar prose-mode-hooks
(mapcar (lambda (mode) (intern (format "%s-hook" mode)))
prose-modes))
Use variable-pitch
fonts when writing prose
I personally much prefer writing prose in serif fonts. At the beginning of the
config, I set variable-pitch
to Valkyrie; this is a commercial font created by
Matthew Butterick of Practical Typography and I absolutely love it.
;(defun amo/enable-variable-pitch-mode ()
; (variable-pitch-mode 1))
;(dolist (hook prose-mode-hooks)
; (add-hook hook 'amo/enable-variable-pitch-mode))
;(add-hook 'mu4e-view-mode-hook 'amo/enable-variable-pitch-mode)
However much I love serif fonts, tables, links, and code blocks in Org should still be monospaced. They look quite odd otherwise.
;(set-face-attribute 'org-table nil :inherit 'fixed-pitch)
;(set-face-attribute 'org-block nil :inherit 'fixed-pitch)
;(set-face-attribute 'org-code nil :inherit 'fixed-pitch)
;(set-face-attribute 'org-date nil :inherit 'fixed-pitch)
;(set-face-attribute 'org-special-keyword nil :inherit 'fixed-pitch)
Enable spell-checking in the usual places
I want to make sure that I've enabled spell-checking if I'm editing text, composing an email, or authoring a Git commit.
(use-package flyspell
:config
(dolist (hook prose-mode-hooks)
(add-hook hook 'flyspell-mode)))
Wrap paragraphs automatically
AutoFillMode automatically wraps paragraphs, kinda like hitting M-q
. I wrap a
lot of paragraphs, so this automatically wraps 'em when I'm writing text,
Markdown, or Org.
(dolist (hook prose-mode-hooks)
(add-hook hook 'turn-on-auto-fill))
Use Org-style lists and tables everywhere
Enable Org-style tables.
(add-hook 'git-commit-mode-hook 'orgtbl-mode)
(add-hook 'markdown-mode-hook 'orgtbl-mode)
(add-hook 'message-mode-hook 'orgtbl-mode)
Use the orgalist">orgalist
package for more convenient list manipulation.
(use-package orgalist
:config
(add-hook 'git-commit-mode-hook 'orgalist-mode)
(add-hook 'markdown-mode-hook 'orgalist-mode)
(add-hook 'message-mode-hook 'orgalist-mode))
Linting prose
I use proselint to check my prose for common errors. This creates a flycheck checker that runs proselint in texty buffers and displays my errors.
(require 'flycheck)
(flycheck-def-executable-var proselint "proselint")
(flycheck-define-command-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 prose-modes
:next-checkers 'nil
:standard-input 'nil
:working-directory 'nil)
(add-to-list 'flycheck-checkers 'proselint)
Use flycheck in the appropriate buffers:
(dolist (hook prose-mode-hooks)
(add-hook hook 'flycheck-mode))
Look up definitions
I look up definitions by hitting C-x w
, which shells out to dict">dict
with
dict-wn">dict-wn
for English definitions. It can be a bit finicky but it works well enough.
(defun amo/dictionary-prompt ()
(read-string
(format "Word (%s): " (or (amo/region-or-word) ""))
nil
nil
(amo/region-or-word)))
(defun amo/dictionary-define-word ()
(interactive)
(let* ((word (amo/dictionary-prompt))
(buffer-name (concat "Definition: " word)))
(with-output-to-temp-buffer buffer-name
(shell-command (format "dict -d wn %s" word) buffer-name))))
(define-key global-map (kbd "C-x w") 'amo/dictionary-define-word)
Look up words in a thesaurus
Hitting C-x s
searches for synonyms for the word at point.
(use-package powerthesaurus
:bind
("C-x s" . powerthesaurus-lookup-word-dwim))
Editing with Markdown
Because I can't always use org
.
- Associate
.md
files with GitHub-flavored Markdown. - Use
pandoc
to render the results. - Apply syntax highlighting in code blocks.
(use-package markdown-mode
:commands gfm-mode
:mode (("\\.md$" . gfm-mode))
:config
(custom-set-faces
'(markdown-pre-face ((t nil))))
(setq markdown-command "pandoc --standalone --mathjax --from=markdown"
markdown-fontify-code-blocks-natively t))
Cycle between spacing alternatives
Successive calls to cycle-spacing
rotate between changing the whitespace
around point to:
- A single space,
- No spaces, or
- The original spacing.
Binding this to M-SPC
is strictly better than the original binding of
just-one-space
.
(global-set-key (kbd "M-SPC") 'cycle-spacing)
Enable region case modification
(put 'downcase-region 'disabled nil)
(put 'upcase-region 'disabled nil)
Quickly explore my "notes" directory with deft
(use-package deft
:bind ("C-c n" . deft)
:commands (deft)
:config
(setq deft-directory "~/Org/notes"
deft-recursive t
deft-use-filename-as-title t)
(evil-set-initial-state 'deft-mode 'emacs))
File management with dired
Hide dotfiles by default, but toggle their visibility with .
.
(use-package dired-hide-dotfiles
:config
(dired-hide-dotfiles-mode)
(define-key dired-mode-map "." 'dired-hide-dotfiles-mode))
Open media with the appropriate programs.
(use-package dired-open
:config
(setq dired-open-extensions
'(("avi" . "mpv")
("cbr" . "comix")
("doc" . "abiword")
("docx" . "abiword")
("gif" . "ffplay")
("gnumeric" . "gnumeric")
("jpeg" . "s")
("jpg" . "s")
("mkv" . "mpv")
("mov" . "mpv")
("mp3" . "mpv")
("mp4" . "mpv")
("pdf" . "zathura")
("png" . "s")
("webm" . "mpv")
("xls" . "gnumeric")
("xlsx" . "gnumeric"))))
These are the switches that get passed to ls
when dired
gets a list of
files. We're using:
l
: Use the long listing format.h
: Use human-readable sizes.v
: Sort numbers naturally.A
: Almost all. Doesn't include ".
" or "..
".
That said, I'd usually like to hide those extra details.
dired-hide-details-mode
can be toggled with (
.
(setq-default dired-listing-switches "-lhvA")
(add-hook 'dired-mode-hook (lambda () (dired-hide-details-mode 1)))
Set up DWIM ("do what I mean") for dired
. When I've got two dired
windows
side-by-side, and I move or copy files in one window, this sets the default
location to the other window.
(setq dired-dwim-target t)
Kill buffers of files/directories that are deleted in dired
.
(setq dired-clean-up-buffers-too t)
Always copy directories recursively instead of asking every time.
(setq dired-recursive-copies 'always)
Ask before recursively deleting a directory, though.
(setq dired-recursive-deletes 'top)
Files are normally moved and copied synchronously. This is fine for small or local files, but copying a large file or moving a file across a mounted network drive blocks Emacs until the process is completed. Unacceptable!
This uses emacs-async
to make dired
perform actions asynchronously.
(use-package async
:config
(dired-async-mode 1))
Use "j" and "k" to move around in dired
.
(evil-define-key 'normal dired-mode-map (kbd "j") 'dired-next-line)
(evil-define-key 'normal dired-mode-map (kbd "k") 'dired-previous-line)
I'm often browsing directories of photos and images, so this binds "v" to view a
slideshow of the current directory with s
(a custom feh
wrapper defined
elsewhere in this repo).
(defun hrs/dired-slideshow ()
(interactive)
(start-process "dired-slideshow" nil "s" (dired-current-directory)))
(evil-define-key 'normal dired-mode-map (kbd "v") 'hrs/dired-slideshow)
Editing settings
Quickly visit Emacs configuration
I futz around with my dotfiles a lot. This binds C-c e
to quickly open my
Emacs configuration file.
(defun amo/visit-emacs-config ()
(interactive)
(find-file "~/.emacs.d/configuration.org"))
(global-set-key (kbd "C-c e") 'amo/visit-emacs-config)
Always kill current buffer
Assume that I always want to kill the current buffer when hitting C-x k
.
(defun amo/kill-current-buffer ()
"Kill the current buffer without prompting."
(interactive)
(kill-buffer (current-buffer)))
(global-set-key (kbd "C-x k") 'amo/kill-current-buffer)
Set up helpful
The helpful
package provides, among other things, more context in Help
buffers.
(use-package helpful)
(global-set-key (kbd "C-h f") #'helpful-callable)
(global-set-key (kbd "C-h v") #'helpful-variable)
(global-set-key (kbd "C-h k") #'helpful-key)
(evil-define-key 'normal helpful-mode-map (kbd "q") 'quit-window)
Look for executables in /usr/local/bin
(amo/append-to-path "/usr/local/bin")
Save my location within a file
Using save-place-mode
saves the location of point for every file I visit. If I
close the file or close the editor, then later re-open it, point will be at the
last place I visited.
(save-place-mode t)
Always indent with spaces
Never use tabs. Tabs are the devil’s whitespace.
(setq-default indent-tabs-mode nil)
Install and configure which-key
which-key= displays the possible completions for a long keybinding. That's
really helpful for some modes (like projectile
, for example).
(use-package which-key
:config (which-key-mode))
Configure yasnippet
(use-package yasnippet)
I keep my snippets in ~/.emacs/snippets/text-mode
, and I always want yasnippet
enabled.
(setq yas-snippet-dirs '("~/.emacs.d/snippets/text-mode"))
(yas-global-mode 1)
I don’t want yas
to automatically indent the snippets it inserts. Sometimes
this looks pretty bad (when indenting org-mode, for example, or trying to guess
at the correct indentation for Python).
(setq yas-indent-line 'auto)
Configure ivy
and counsel
I use ivy
and counsel
as my completion framework.
This configuration:
- Uses
counsel-M-x
for command completion, - Replaces
isearch
withswiper
, - Uses
smex
to maintain history, - Enables fuzzy matching everywhere except swiper (where it's thoroughly unhelpful), and
- Includes recent files in the switch buffer.
(use-package counsel
:bind
("M-x" . 'counsel-M-x)
("C-s" . 'swiper)
:config
(use-package flx)
(use-package smex)
(ivy-mode 1)
(setq ivy-use-virtual-buffers t)
(setq ivy-count-format "(%d/%d) ")
(setq ivy-initial-inputs-alist nil)
(setq ivy-re-builders-alist
'((swiper . ivy--regex-plus)
(t . ivy--regex-fuzzy))))
Switch and rebalance windows when splitting
When splitting a window, I invariably want to switch to the new window. This makes that automatic.
(defun amo/split-window-below-and-switch ()
"Split the window horizontally, then switch to the new pane."
(interactive)
(split-window-below)
(balance-windows)
(other-window 1))
(defun amo/split-window-right-and-switch ()
"Split the window vertically, then switch to the new pane."
(interactive)
(split-window-right)
(balance-windows)
(other-window 1))
(global-set-key (kbd "C-x 2") 'amo/split-window-below-and-switch)
(global-set-key (kbd "C-x 3") 'amo/split-window-right-and-switch)
Mass editing of grep
results
I like the idea of mass editing grep
results the same way I can edit filenames
in dired
. These keybindings allow me to use C-x C-q
to start editing grep
results and C-c C-c
to stop, just like in dired
.
(use-package wgrep)
(eval-after-load 'grep
'(define-key grep-mode-map
(kbd "C-x C-q") 'wgrep-change-to-wgrep-mode))
(eval-after-load 'wgrep
'(define-key grep-mode-map
(kbd "C-c C-c") 'wgrep-finish-edit))
(setq wgrep-auto-save-buffer t)
Use projectile everywhere
(projectile-global-mode)
Add a bunch of engines for engine-mode
Enable engine-mode and define a few useful engines.
(use-package engine-mode)
(require 'engine-mode)
(defengine duckduckgo
"https://duckduckgo.com/?q=%s")
(defengine github
"https://github.com/search?ref=simplesearch&q=%s"
:keybinding "g")
(defengine rfcs
"http://pretty-rfc.herokuapp.com/search?q=%s")
(defengine stack-overflow
"https://stackoverflow.com/search?q=%s"
:keybinding "s")
(defengine wikipedia
"http://www.wikipedia.org/search-redirect.php?language=en&go=Go&search=%s"
:keybinding "w")
(defengine wiktionary
"https://www.wikipedia.org/search-redirect.php?family=wiktionary&language=en&go=Go&search=%s")
(defengine youtube
"https://www.youtube.com/results?search_query=%s")
(engine-mode t)
#
#
#
#
Usenet
(setq gnus-select-method '(nntp "news.usenet.farm"))