Jethro’s Emacs.d Configuration
Introduction
This document is a constant work-in-progress, and will contain the latest updates to my Emacs configuration.
Notes:
- I use a Dvorak 60% keyboard, so some of the keybindings may not be suitable for you.
- I use StumpWM, and many of my keybindings are designed around that
About the Author
Typically this section is unimportant, but knowing who I am and what I do may help you understand why my configuration is the way it is.
I’m currently a Computer Science undergraduate, who does primarily back-end development (python), and some web development (HTML/CSS/JS, React). I also dabble in Lisps, and run most of my life in Org-mode.
Preinit: use-package
Use-package allows for isolation of package configuration, while maintaining tidiness and performance.
(unless (package-installed-p 'use-package)
(package-refresh-contents)
(package-install 'use-package))
(eval-and-compile
(defvar use-package-verbose t)
(require 'cl)
(require 'use-package)
(require 'bind-key)
(require 'diminish)
(setq use-package-always-ensure t))EXWM
;; Shrink fringes to 1 pixel
(fringe-mode 1)
;; You may want Emacs to show you the time
(setq display-time-default-load-average nil)
(display-time-mode t)
;; Load EXWM
(require 'exwm)
;; Set the initial number of workspaces.
(setq exwm-workspace-number 4)
;; All buffers created in EXWM mode are named "*EXWM*". You may want to change
;; it in `exwm-update-class-hook' and `exwm-update-title-hook', which are run
;; when a new window class name or title is available. Here's some advice on
;; this subject:
;; + Always use `exwm-workspace-rename-buffer` to avoid naming conflict.
;; + Only renaming buffer in one hook and avoid it in the other. There's no
;; guarantee on the order in which they are run.
;; + For applications with multiple windows (e.g. GIMP), the class names of all
;; windows are probably the same. Using window titles for them makes more
;; sense.
;; + Some application change its title frequently (e.g. browser, terminal).
;; Its class name may be more suitable for such case.
;; In the following example, we use class names for all windows expect for
;; Java applications and GIMP.
(add-hook 'exwm-update-class-hook
(lambda ()
(unless (or (string-prefix-p "sun-awt-X11-" exwm-instance-name)
(string= "gimp" exwm-instance-name))
(exwm-workspace-rename-buffer exwm-class-name))))
(add-hook 'exwm-update-title-hook
(lambda ()
(when (or (not exwm-instance-name)
(string-prefix-p "sun-awt-X11-" exwm-instance-name)
(string= "gimp" exwm-instance-name))
(exwm-workspace-rename-buffer exwm-title))))
;; `exwm-input-set-key' allows you to set a global key binding (available in
;; any case). Following are a few examples.
;; + We always need a way to go back to line-mode from char-mode
(exwm-input-set-key (kbd "s-r") #'exwm-reset)
;; + Bind a key to switch workspace interactively
(exwm-input-set-key (kbd "s-w") #'exwm-workspace-switch)
;; + Bind "s-0" to "s-9" to switch to the corresponding workspace.
(dotimes (i 10)
(exwm-input-set-key (kbd (format "s-%d" i))
`(lambda ()
(interactive)
(exwm-workspace-switch-create ,i))))
;; + Application launcher ('M-&' also works if the output buffer does not
;; bother you). Note that there is no need for processes to be created by
;; Emacs.
(exwm-input-set-key (kbd "s-SPC")
(lambda ()
(interactive)
(shell-command "rofi -show run")))
(exwm-input-set-key (kbd "s-p")
(lambda ()
(interactive)
(shell-command "rofi-pass")))
;; The following example demonstrates how to set a key binding only available
;; in line mode. It's simply done by first push the prefix key to
;; `exwm-input-prefix-keys' and then add the key sequence to `exwm-mode-map'.
;; The example shorten 'C-c q' to 'C-q'.
(push ?\C-q exwm-input-prefix-keys)
(define-key exwm-mode-map [?\C-q] #'exwm-input-send-next-key)
;; The following example demonstrates how to use simulation keys to mimic the
;; behavior of Emacs. The argument to `exwm-input-set-simulation-keys' is a
;; list of cons cells (SRC . DEST), where SRC is the key sequence you press and
;; DEST is what EXWM actually sends to application. Note that SRC must be a key
;; sequence (of type vector or string), while DEST can also be a single key.
(exwm-input-set-simulation-keys
'(([?\C-b] . left)
([?\C-f] . right)
([?\C-p] . up)
([?\C-n] . down)
([?\C-a] . home)
([?\C-e] . end)
([?\M-v] . prior)
([?\C-v] . next)
([?\C-d] . delete)
([?\C-k] . (S-end delete))))
;; You can hide the mode-line of floating X windows by uncommenting the
;; following lines
(add-hook 'exwm-floating-setup-hook #'exwm-layout-hide-mode-line)
(add-hook 'exwm-floating-exit-hook #'exwm-layout-show-mode-line)
;; You can hide the minibuffer and echo area when they're not used, by
;; uncommenting the following line
;; (setq exwm-workspace-minibuffer-position 'bottom)
;; Do not forget to enable EXWM. It will start by itself when things are ready.
(exwm-enable)Core Layer
User Configuration
First, some information about me:
(setq user-full-name "Jethro Kuan"
user-mail-address "jethrokuan95@gmail.com")Emacs Server
Load the emacs server, if it is not running. This allows for almost-instant emacs “startup”.
(require 'server)
(unless (server-running-p)
(server-start))No littering
This package sets out to fix this by changing the values of path variables to put files in either no-littering-etc-directory (defaulting to ~/.emacs.d/etc/) or no-littering-var-directory (defaulting to ~/.emacs.d/var/), and by using descriptive file names and subdirectories when appropriate. This is similar to a color-theme; a “path-theme” if you will.
(use-package no-littering
:config
(require 'recentf)
(add-to-list 'recentf-exclude no-littering-var-directory)
(add-to-list 'recentf-exclude no-littering-etc-directory)
(setq auto-save-file-name-transforms
`((".*" ,(no-littering-expand-var-file-name "auto-save/") t))))jethro-mode-map
I defined a minor mode for my own keybindings. This allows me to:
- Inspect what keybindings I have customized to my own liking
- Swap back to vanilla emacs keybindings if I disable
jethro-mode
(defvar jethro-mode-map (make-sparse-keymap)
"Keymap for `jethro-mode'.")
(define-minor-mode jethro-mode
"A minor mode so that my key settings override annoying major modes."
;; If init-value is not set to t, this mode does not get enabled in
;; `fundamental-mode' buffers even after doing \"(global-jethro-mode 1)\".
;; More info: http://emacs.stackexchange.com/q/16693/115
:init-value t
:lighter " j"
:keymap jethro-mode-map)
(define-globalized-minor-mode global-jethro-mode jethro-mode jethro-mode)
(add-to-list 'emulation-mode-map-alists `((jethro-mode . ,jethro-mode-map)))
;; Turn off the minor mode in the minibuffer
(defun turn-off-jethro-mode ()
"Turn off jethro-mode."
(jethro-mode -1))
(add-hook 'minibuffer-setup-hook #'turn-off-jethro-mode)Reloading Emacs Config
I want an easy way to reload my configuration when I change it. I bind
it to <f11>.
(defun reload-init ()
(interactive)
(load-file "~/.emacs.d/init.el"))
(bind-key "<f11>" 'reload-init jethro-mode-map)Emacs GC settings
Reduce the frequency of garbage collection by making it happen on each 50MB of allocated data (the default is on every 0.76MB). Also, warn when opening large files.
(setq gc-cons-threshold 50000000)
(setq large-file-warning-threshold 100000000)Auto Revert
Often when switching git branches, files tend to change. By default,
Emacs does not revert the buffers affected, which can lead to some
confusion. Turn on auto-revert-mode globally, so that when the files
change, the buffers reflect the latest editions as well.
NOTE: This can be quite slow, when the changes are massive across branches.
(diminish 'auto-revert-mode)
(global-auto-revert-mode 1)Custom file
Using the customize interface can be nice, but it tends to pollute
init.el. Move all customizations to a separate file.
(setq custom-file "~/.emacs.d/custom.el")Use y/n over yes/no
y/n is easier to type than yes/no
(defalias 'yes-or-no-p 'y-or-n-p)Replace region when typing
Type over a selected region, instead of deleting before typing.
(add-hook 'after-init-hook 'delete-selection-mode)Recentf
When I’m using Emacs via emacsclient, my recent files don’t get
saved because I never ever quit Emacs. Instead, now I run the function
every 5 minutes. Inhibit recentf from printing messages into the
minibuffer.
(require 'recentf)
(run-at-time (* 5 60) nil
(lambda ()
(let ((inhibit-message t))
(recentf-save-list))))Editing Preferences
Emacs uses double-spaces by default. Use single spaces instead:
(setq sentence-end-double-space nil)Also, use 2 spaces for tabs. Death to tabs!
(setq-default tab-width 2)
(setq-default js-indent-level 2)
(setq-default indent-tabs-mode nil)Line wrapping for text modes
Don’t wrap lines for coding. Create a hook that enables wrapping, for text-modes like org-mode and markdown-mode.
(setq-default truncate-lines t)
(defun jethro/truncate-lines-hook ()
(setq truncate-lines nil))
(add-hook 'text-mode-hook 'jethro/truncate-lines-hook)Backup directory
(setq backup-directory-alist
`((".*" . ,temporary-file-directory)))
(setq auto-save-file-name-transforms
`((".*" ,temporary-file-directory t)))Load secrets
Store secrets in a different file, not committed into the git repository.
(load "~/.emacs.d/secrets.el" t)Custom Commands
Nuke all buffers with C-c !
(defun jethro/nuke-all-buffers ()
(interactive)
(mapcar 'kill-buffer (buffer-list))
(delete-other-windows))
(bind-key "C-c !" 'jethro/nuke-all-buffers jethro-mode-map)compile with <f9>
(defun jethro/compile ()
(interactive)
(setq-local compilation-read-command nil)
(call-interactively 'compile))
(bind-key "<f9>" 'jethro/compile jethro-mode-map)Autosaving
Auto save all open buffers, when Emacs loses focus.
(add-hook 'focus-out-hook
(lambda () (save-some-buffers t)))Appearance
Font
I use Iosevka. Other good free alternatives include Source Code Pro, Office Code Pro and the Powerline font families.
(setq default-frame-alist '((font . "Iosevka-16")))Removing UI Cruft
Remove the useless toolbars and splash screens.
(tooltip-mode -1)
(tool-bar-mode -1)
(menu-bar-mode -1)
(scroll-bar-mode -1)
(setq inhibit-splash-screen t)
(setq inhibit-startup-message t)Theme
Challenger Deep
(use-package challenger-deep
:init
(load-theme 'challenger-deep t))Zenburn
(use-package zenburn-theme
:init
(load-theme 'zenburn t))Tomorrow (Eighties)
(use-package color-theme-sanityinc-tomorrow
:init
(load-theme 'sanityinc-tomorrow-eighties t))tao
(use-package tao-theme
:init
(load-theme 'tao-yang t))color-identifiers-mode
(use-package color-identifiers-mode
:diminish color-identifiers-mode
:init
(add-hook 'after-init-hook 'global-color-identifiers-mode)
:config
(let ((faces '(font-lock-comment-face font-lock-comment-delimiter-face font-lock-constant-face font-lock-type-face font-lock-function-name-face font-lock-variable-name-face font-lock-keyword-face font-lock-string-face font-lock-builtin-face font-lock-preprocessor-face font-lock-warning-face font-lock-doc-face)))
(dolist (face faces)
(set-face-attribute face nil :foreground nil :weight 'normal :slant 'normal)))
(set-face-attribute 'font-lock-comment-delimiter-face nil :slant 'italic)
(set-face-attribute 'font-lock-comment-face nil :slant 'italic)
(set-face-attribute 'font-lock-doc-face nil :slant 'italic)
(set-face-attribute 'font-lock-keyword-face nil :weight 'bold)
(set-face-attribute 'font-lock-builtin-face nil :weight 'bold)
(set-face-attribute 'font-lock-preprocessor-face nil :weight 'bold))Rainbow-delimiters-mode
We use rainbow delimiters to show imbalanced parenthesis.
(use-package rainbow-delimiters
:ensure t
:init
(add-hook 'after-init-hook 'rainbow-delimiters-mode)
:config
(set-face-attribute 'rainbow-delimiters-unmatched-face nil
:foreground 'unspecified
:inherit 'error))Remove blinking cursor
(blink-cursor-mode 0)Additional Code Highlighting
(use-package highlight-numbers
:init
(add-hook 'prog-mode-hook #'highlight-numbers-mode))
(use-package highlight-quoted
:init
(add-hook 'prog-mode-hook #'highlight-quoted-mode))
(use-package highlight-defined
:init
(add-hook 'prog-mode-hook #'highlight-defined-mode))
(use-package highlight-operators
:init
(add-hook 'prog-mode-hook #'highlight-operators-mode))
(use-package highlight-escape-sequences
:init
(add-hook 'prog-mode-hook #'hes-mode))Shell
(require 'eshell)Set default shell to bash
Because fish doesn’t play well with Emacs.
(setq-default explicit-shell-file-name "/run/current-system/sw/bin/bash")
(setq-default shell-file-name "/run/current-system/sw/bin/bash")Add PATH to shell
(use-package exec-path-from-shell
:config
(exec-path-from-shell-initialize))Eshell configuration
(require 'em-smart)
(setq eshell-scroll-to-bottom-on-input 'all
eshell-hist-ignoredups t
eshell-save-history-on-exit t
eshell-prefer-lisp-functions nil
eshell-destroy-buffer-when-process-dies t
eshell-glob-case-insensitive nil
eshell-error-if-no-glob nil
eshell-where-to-jump 'begin
eshell-review-quick-commands nil
eshell-smart-space-goes-to-end t)Fish-like auto-completion
This was obtained from http://whyarethingsthewaytheyare.com/fishlike-autosuggestions-in-eshell/.
(with-eval-after-load 'company
(defun company-eshell-autosuggest-candidates (prefix)
(let* ((history
(cl-remove-duplicates
(mapcar (lambda (str)
(string-trim (substring-no-properties str)))
(ring-elements eshell-history-ring))
:from-end t
:test #'string=))
(most-similar (cl-find-if
(lambda (str)
(string-prefix-p prefix str))
history)))
(when most-similar
`(,most-similar))))
(defun company-eshell-autosuggest--prefix ()
(let ((prefix
(string-trim-left
(buffer-substring-no-properties
(save-excursion
(eshell-bol))
(save-excursion (end-of-line) (point))))))
(if (not (string-empty-p prefix))
prefix
'stop)))
(defun company-eshell-autosuggest (command &optional arg &rest ignored)
(interactive (list 'interactive))
(cl-case command
(interactive (company-begin-backend 'company-eshell))
(prefix (and (eq major-mode 'eshell-mode)
(company-eshell-autosuggest--prefix)))
(candidates (company-eshell-autosuggest-candidates arg)))))
(defun setup-eshell-autosuggest ()
(require 'company)
(setq-local company-backends '(company-eshell-autosuggest))
(setq-local company-frontends '(company-preview-frontend)))
(add-hook 'eshell-mode-hook 'setup-eshell-autosuggest)Eshell theme
(use-package eshell-git-prompt
:config
(eshell-git-prompt-use-theme 'powerline))Open eshell in current/project directory
(defun jethro/eshell-here ()
"Opens up a new shell in projectile root. If a prefix argument is
passed, use the buffer's directory."
(interactive)
(let* ((projectile-name (projectile-project-name))
(current-directory (car
(last
(split-string
(if (buffer-file-name)
(file-name-directory (buffer-file-name))
default-directory) "/" t)))))
(split-window-vertically)
(other-window 1)
(if (equal projectile-name "-")
(progn
(eshell "new")
(rename-buffer (concat "*eshell: " current-directory "*")))
(projectile-with-default-dir (projectile-project-root)
(eshell "new")
(rename-buffer (concat "*eshell: " projectile-name "*"))))))
(bind-key "C-x m" 'jethro/eshell-here jethro-mode-map)Exiting eshell
(defun eshell/x ()
(unless (one-window-p)
(delete-window))
(eshell/exit))Isearch
(bind-key "C-s" 'eshell-isearch-forward eshell-mode-map)
(bind-key "C-r" 'eshell-isearch-backward eshell-mode-map)Quitting Eshell
(defun eshell/x ()
(delete-window)
(eshell/exit))with-editor
Use with-editor to use current Emacs to open everything that invokes
$EDITOR.
(use-package with-editor
:ensure t
:init
(progn
(add-hook 'shell-mode-hook 'with-editor-export-editor)
(add-hook 'eshell-mode-hook 'with-editor-export-editor)))Web Browsing
(use-package eww
:defer t
:init
(setq browse-url-browser-function
'((".*google.*maps.*" . browse-url-generic)
;; Github goes to firefox, but not gist
("http.*\/\/github.com" . browse-url-generic)
("groups.google.com" . browse-url-generic)
("docs.google.com" . browse-url-generic)
("melpa.org" . browse-url-generic)
("build.*\.elastic.co" . browse-url-generic)
(".*-ci\.elastic.co" . browse-url-generic)
("internal-ci\.elastic\.co" . browse-url-generic)
("zendesk\.com" . browse-url-generic)
("salesforce\.com" . browse-url-generic)
("stackoverflow\.com" . browse-url-generic)
("apache\.org\/jira" . browse-url-generic)
("thepoachedegg\.net" . browse-url-generic)
("zoom.us" . browse-url-generic)
("t.co" . browse-url-generic)
("twitter.com" . browse-url-generic)
("\/\/a.co" . browse-url-generic)
("youtube.com" . browse-url-generic)
("." . eww-browse-url)))
(setq shr-external-browser 'browse-url-generic)
(setq browse-url-browser-function 'browse-url-firefox
browse-url-new-window-flag t
browse-url-firefox-new-window-is-tab t)
(add-hook 'eww-mode-hook #'toggle-word-wrap)
(add-hook 'eww-mode-hook #'visual-line-mode)
:config
(use-package s :ensure t)
(define-key eww-mode-map "o" 'eww)
(define-key eww-mode-map "O" 'eww-browse-with-external-browser)
(define-key eww-mode-map "j" 'next-line)
(define-key eww-mode-map "k" 'previous-line)
(use-package eww-lnum
:bind (:map eww-mode-map
("f" . eww-lnum-follow)
("U" . eww-lnum-universal))))elfeed
(use-package elfeed
:bind ("<f6>" . elfeed))elfeed-org
(use-package elfeed-org
:config
(elfeed-org)
(setq rmh-elfeed-org-files '("~/.org/deft/feeds.org")))Core Utilities
Dash
Dash is a library used to simplify Emacs-lisp development. Some custom elisp code use Dash, so I load it first here anyway.
(use-package dash)Hydra
(use-package hydra)Ivy
I’ve recently switched over from helm to ivy. Ivy is simpler, and
easier to extend.
flx
Flx is required for fuzzy-matching.
(use-package flx)Fuzzy Isearch
(use-package flx-isearch
:bind (:map jethro-mode-map
("C-M-s" . flx-isearch-forward)
("C-M-r" . flx-isearch-backward)))Counsel
Counsel contains ivy enhancements for commonly-used functions.
(use-package counsel
:diminish ivy-mode
:bind
(:map jethro-mode-map
("C-c C-r" . ivy-resume)
("M-a" . counsel-M-x)
("C-s" . counsel-grep-or-swiper)
("C-r" . counsel-grep-or-swiper)
("C-c i" . counsel-imenu)
("C-x C-f" . counsel-find-file)
("C-x j" . counsel-dired-jump)
("C-x l" . counsel-locate)
("C-c j" . counsel-git)
("C-c f" . counsel-recentf)
("M-y" . counsel-yank-pop)
:map swiper-map
("C-r" . ivy-previous-line)
:map help-map
("f" . counsel-describe-function)
("v" . counsel-describe-variable)
("l" . counsel-info-lookup-symbol)
:map ivy-minibuffer-map
("C-d" . ivy-dired)
("C-o" . ivy-occur)
("<return>" . ivy-alt-done)
("M-<return>" . ivy-immediate-done)
:map read-expression-map
("C-r" . counsel-expression-history))
:init
(add-hook 'after-init-hook 'ivy-mode)
:config
(setq counsel-grep-swiper-limit 20000)
(defun ivy-dired ()
(interactive)
(if ivy--directory
(ivy-quit-and-run
(dired ivy--directory)
(when (re-search-forward
(regexp-quote
(substring ivy--current 0 -1)) nil t)
(goto-char (match-beginning 0))))
(user-error
"Not completing files currently")))
(setq counsel-grep-base-command
"rg -i -M 120 --no-heading --line-number --color never '%s' %s")
(setq counsel-find-file-at-point t)
(setq ivy-use-virtual-buffers t)
(setq ivy-display-style 'fancy)
(setq ivy-initial-inputs-alist nil)
(setq ivy-re-builders-alist
'((ivy-switch-buffer . ivy--regex-plus)
(swiper . ivy--regex-plus)
(t . ivy--regex-fuzzy)))
(ivy-set-actions
t
'(("I" insert "insert"))))wgrep
(use-package wgrep)rg
(use-package rg
:bind (:map jethro-mode-map
("M-s" . rg)))Visual Enhancements
Whitespace-mode
(require 'whitespace)
(setq whitespace-line-column 80) ;; limit line length
(setq whitespace-style '(face lines-tail))
(diminish 'whitespace-mode)
(add-hook 'prog-mode-hook 'whitespace-mode)Modeline
Flycheck
Show flycheck status in modeline.
(defface jethro/ok-face
'((t :foreground "#bfebbf"))
"Face for ok status in the mode-line.")
(defface jethro/warning-face
'((t :foreground "#dfaf8f"))
"Face for warning status in the mode-line.")
(defface jethro/error-face
'((t :foreground "#cc9393"))
"Face for error status in the mode-line.")
(defun jethro/modeline-flycheck-status ()
"Return the status of flycheck to be displayed in the mode-line."
(when flycheck-mode
(let* ((text (pcase flycheck-last-status-change
(`finished (if flycheck-current-errors
(let ((count (let-alist (flycheck-count-errors flycheck-current-errors)
(+ (or .warning 0) (or .error 0)))))
(propertize (format "✖ %s Issue%s" count (if (eq 1 count) "" "s"))
'face 'jethro/warning-face))
(propertize "✔ No Issues"
'face 'jethro/ok-face)))
(`running (propertize "⟲ Running"
'face 'jethro/warning-face))
(`no-checker (propertize "⚠ No Checker"
'face 'jethro/warning-face))
(`not-checked "✖ Disabled")
(`errored (propertize "⚠ Error"
'face 'jethro/error-face))
(`interrupted (propertize "⛔ Interrupted"
'face 'jethro/error-face))
(`suspicious ""))))
(propertize text
'help-echo "Show Flycheck Errors"
'local-map (make-mode-line-mouse-map
'mouse-1 #'flycheck-list-errors)))))Eyebrowse
(defun jethro/modeline-eyebrowse ()
(when eyebrowse-mode
(eyebrowse-mode-line-indicator)))Modeline Format
(defun jethro/org-mode-line-string ()
"Returns org-mode-line-string if bound, empty
string if not"
(if (boundp 'org-mode-line-string)
org-mode-line-string
""))
(defun jethro/org-pomodoro-mode-line ()
"Returns org-pomodoro-mode-line string if bound, empty
string if not"
(if (boundp 'org-pomodoro-mode-line)
org-pomodoro-mode-line
""))
(setq-default mode-line-format
`("%e"
mode-line-front-space mode-line-mule-info mode-line-client mode-line-modified mode-line-remote mode-line-frame-identification mode-line-buffer-identification
" "
(:eval (jethro/modeline-flycheck-status))
" "
(:eval (jethro/org-mode-line-string))
" "
(:eval (jethro/org-pomodoro-mode-line))
" "
(vc-mode vc-mode)
" "
(:eval (jethro/modeline-eyebrowse))))Thick modeline bar
(custom-set-faces
'(mode-line ((t (:background "#2B2B2B" :foreground "#DCDCCC" :box (:line-width 4 :color "#2B2B2B"))))))smart-mode-line
(use-package smart-mode-line
:init
(add-hook 'after-init-hook 'sml/setup)
:config
(setq sml/theme 'respectful)
(setq sml/name-width 44)
(setq sml/shorten-directory t)
(setq sml/shorten-modes nil)
(setq sml/mode-width 'full)
(setq sml/replacer-regexp-list
'(("^~/.org/" ":O:")
("^~/\\.emacs\\.d/" ":ED:")))
(setq rm-blacklist
(format "^ \\(%s\\)$"
(mapconcat #'identity
'("j"
"FlyC.*"
"Fill"
"Projectile.*"
"GitGutter"
"ivy"
"company"
""
"OrgSrc"
","
"ElDoc")
"\\|"))))Zooming
(with-eval-after-load 'hydra
(defhydra jethro/hydra-zoom ()
"zoom"
("i" text-scale-increase "in")
("o" text-scale-decrease "out"))
(bind-key "C-c h z" 'jethro/hydra-zoom/body jethro-mode-map))beacon
Beacon makes sure you don’t lose track of your cursor when jumping around a buffer.
(use-package beacon
:diminish beacon-mode
:init
(add-hook 'after-init-hook 'beacon-mode)
:config
(setq beacon-push-mark 10))Show Matching parenthesis
Always show matching parenthesis.
(show-paren-mode 1)
(setq show-paren-delay 0)golden-ratio
Give the working window more screen estate.
(use-package golden-ratio
:diminish golden-ratio-mode
:init
(add-hook 'after-init-hook 'golden-ratio-mode))volatile-highlights
Highlights recently copied/pasted text.
(use-package volatile-highlights
:diminish volatile-highlights-mode
:init
(add-hook 'after-init-hook 'volatile-highlights-mode))diff-hl
(use-package diff-hl
:bind (:map jethro-mode-map
("C-c h v" . jethro/hydra-diff-hl/body))
:init
(defconst jethro/diff-hl-mode-hooks '(emacs-lisp-mode-hook
conf-space-mode-hook ;.tmux.conf
markdown-mode-hook
css-mode-hook
web-mode-hook
sh-mode-hook
python-mode-hook
yaml-mode-hook ;tmuxp yaml configs
c-mode-hook)
"List of hooks of major modes in which diff-hl-mode should be enabled.")
(dolist (hook jethro/diff-hl-mode-hooks)
(add-hook hook #'diff-hl-mode))
(defhydra jethro/hydra-diff-hl (:color red)
"diff-hl"
("=" diff-hl-diff-goto-hunk "goto hunk")
("<RET>" diff-hl-diff-goto-hunk "goto hunk")
("u" diff-hl-revert-hunk "revert hunk")
("[" diff-hl-previous-hunk "prev hunk")
("p" diff-hl-previous-hunk "prev hunk")
("]" diff-hl-next-hunk "next hunk")
("n" diff-hl-next-hunk "next hunk")
("q" nil "cancel"))
(add-hook 'dired-mode-hook #'diff-hl-dired-mode))
*** Simple Custom mode-line
Copied from https://github.com/bastibe/.emacs.d/blob/master/init.el
(setq-default mode-line-format
'(((:eval (let* ((buffer-name (concat
(propertize (buffer-name) 'face '(:weight bold))
":" (propertize (format-mode-line "%l,%c") 'face '(:weight light))))
(left (concat (format-mode-line mode-line-front-space)
"(" (if (buffer-modified-p) "⋯" "✓") ")"
" "
(format "%-30s" buffer-name)
" "
(if vc-mode (concat "" vc-mode " (" (symbol-name (vc-state (buffer-file-name))) ")") "")
" "
(format-mode-line mode-line-misc-info)))
(right (concat "("
(propertize (format-mode-line mode-name) 'face '(:weight bold))
(format-mode-line minor-mode-alist)
")"
(format-mode-line mode-line-end-spaces)))
(padding (make-string (- (window-width) 4 (length left) (length right)) ? )))
(format "%s %s %s" left padding right))))))Moving Around
Keychord
(use-package key-chord
:config
(key-chord-mode 1))Layouts
Eyebrowse
(use-package eyebrowse
:bind (:map jethro-mode-map
("M-0" . eyebrowse-switch-to-window-config-0)
("M-1" . eyebrowse-switch-to-window-config-1)
("M-2" . eyebrowse-switch-to-window-config-2)
("M-3" . eyebrowse-switch-to-window-config-3)
("M-4" . eyebrowse-switch-to-window-config-4)
("M-5" . eyebrowse-switch-to-window-config-5)
("M-6" . eyebrowse-switch-to-window-config-6)
("M-7" . eyebrowse-switch-to-window-config-7)
("M-8" . eyebrowse-switch-to-window-config-8)
("M-9" . eyebrowse-switch-to-window-config-9))
:init
(add-hook 'after-init-hook 'eyebrowse-mode))Persp-mode
(use-package persp-mode
:bind
(:map jethro-mode-map
("C-x b" . persp-switch-to-buffer)
("C-x k" . persp-kill-buffer))
:init
(setq persp-keymap-prefix (kbd "C-c p"))
(setq persp-lighter
'(:eval
(format
(propertize
" [p] %.10s"
'face (let ((persp (get-current-persp)))
(if persp
(if (persp-contain-buffer-p (current-buffer) persp)
'persp-face-lighter-default
'persp-face-lighter-buffer-not-in-persp)
'persp-face-lighter-nil-persp)))
(file-name-nondirectory (directory-file-name (safe-persp-name (get-current-persp)))))))
(persp-mode 1))Eyebrowse and Persp-Mode Integration
(use-package dash)
(require 'dash)
(defun jethro//get-persp-workspace (&optional persp frame)
"Get the correct workspace parameters for perspective.
PERSP is the perspective, and defaults to the current perspective.
FRAME is the frame where the parameters are expected to be used, and
defaults to the current frame."
(let ((param-names (if (display-graphic-p frame)
'(gui-eyebrowse-window-configs
gui-eyebrowse-current-slot
gui-eyebrowse-last-slot)
'(term-eyebrowse-window-configs
term-eyebrowse-current-slot
term-eyebrowse-last-slot))))
(--map (persp-parameter it persp) param-names)))
(defun jethro//set-persp-workspace (workspace-params &optional persp frame)
"Set workspace parameters for perspective.
WORKSPACE-PARAMS should be a list containing 3 elements in this order:
- window-configs, as returned by (eyebrowse--get 'window-configs)
- current-slot, as returned by (eyebrowse--get 'current-slot)
- last-slot, as returned by (eyebrowse--get 'last-slot)
PERSP is the perspective, and defaults to the current perspective.
FRAME is the frame where the parameters came from, and defaults to the
current frame.
Each perspective has two sets of workspace parameters: one set for
graphical frames, and one set for terminal frames."
(let ((param-names (if (display-graphic-p frame)
'(gui-eyebrowse-window-configs
gui-eyebrowse-current-slot
gui-eyebrowse-last-slot)
'(term-eyebrowse-window-configs
term-eyebrowse-current-slot
term-eyebrowse-last-slot))))
(--zip-with (set-persp-parameter it other persp)
param-names workspace-params)))
(defun jethro/load-eyebrowse-for-perspective (type &optional frame)
"Load an eyebrowse workspace according to a perspective's parameters.
FRAME's perspective is the perspective that is considered, defaulting to
the current frame's perspective.
If the perspective doesn't have a workspace, create one."
(when (eq type 'frame)
(let* ((workspace-params (jethro//get-persp-workspace (get-frame-persp frame) frame))
(window-configs (nth 0 workspace-params))
(current-slot (nth 1 workspace-params))
(last-slot (nth 2 workspace-params)))
(if window-configs
(progn
(eyebrowse--set 'window-configs window-configs frame)
(eyebrowse--set 'current-slot current-slot frame)
(eyebrowse--set 'last-slot last-slot frame)
(eyebrowse--load-window-config current-slot))
(eyebrowse--set 'window-configs nil frame)
(eyebrowse-init frame)
(jethro/save-eyebrowse-for-perspective frame)))))
(defun jethro/load-eyebrowse-after-loading-layout (_state-file _phash persp-names)
"Bridge between `persp-after-load-state-functions' and
`jethro/load-eyebrowse-for-perspective'.
_PHASH is the hash were the loaded perspectives were placed, and
PERSP-NAMES are the names of these perspectives."
(let ((cur-persp (get-current-persp)))
;; load eyebrowse for current perspective only if it was one of the loaded
;; perspectives
(when (member (or (and cur-persp (persp-name cur-persp))
persp-nil-name)
persp-names)
(jethro/load-eyebrowse-for-perspective 'frame))))
(defun jethro/update-eyebrowse-for-perspective (&rest _args)
"Update and save current frame's eyebrowse workspace to its perspective."
(let* ((current-slot (eyebrowse--get 'current-slot))
(current-tag (nth 2 (assoc current-slot (eyebrowse--get 'window-configs)))))
(eyebrowse--update-window-config-element
(eyebrowse--current-window-config current-slot current-tag)))
(jethro/save-eyebrowse-for-perspective))
(defun jethro/save-eyebrowse-for-perspective (&optional frame)
"Save FRAME's eyebrowse workspace to FRAME's perspective.
FRAME defaults to the current frame."
(jethro//set-persp-workspace (list (eyebrowse--get 'window-configs frame)
(eyebrowse--get 'current-slot frame)
(eyebrowse--get 'last-slot frame))
(get-frame-persp frame)
frame))
(add-hook 'persp-before-switch-functions
#'jethro/update-eyebrowse-for-perspective)
(add-hook 'eyebrowse-post-window-switch-hook
#'jethro/save-eyebrowse-for-perspective)
(add-hook 'persp-activated-functions
#'jethro/load-eyebrowse-for-perspective)
(add-hook 'persp-before-save-state-to-file-functions #'jethro/update-eyebrowse-for-perspective)
(add-hook 'persp-after-load-state-functions #'jethro/load-eyebrowse-after-loading-layout)ibuffer
(with-eval-after-load "ibuffer"
(require 'ibuf-ext)
(define-ibuffer-filter persp
"Toggle current view to buffers of current perspective."
(:description "persp-mode"
:reader (persp-prompt (safe-persp-name (get-frame-persp)) t))
(find buf (safe-persp-buffers (persp-get-by-name qualifier))))
(defun persp-update-or-add-ibuffer-group ()
(let ((perspslist (list (safe-persp-name (get-frame-persp))
(cons 'persp (safe-persp-name (get-frame-persp))))))
(setq ibuffer-saved-filter-groups
(delete* "persp-mode" ibuffer-saved-filter-groups
:test 'string= :key 'car))
(add-to-list 'ibuffer-saved-filter-groups (list "persp-mode" perspslist))))
(add-hook 'ibuffer-mode-hook
#'(lambda ()
(persp-update-or-add-ibuffer-group)
(ibuffer-switch-to-saved-filter-groups "persp-mode"))))guru-mode
(use-package guru-mode
:diminish guru-mode
:init
(add-hook 'after-init-hook 'guru-global-mode))Crux
(use-package crux
:bind (:map jethro-mode-map
("C-c o" . crux-open-with)
("C-c n" . crux-cleanup-buffer-or-region)
("C-c D" . crux-delete-file-and-buffer)
("C-a" . crux-move-beginning-of-line)
("M-o" . crux-smart-open-line)
("C-c r" . crux-rename-file-and-buffer)
("M-d" . crux-duplicate-current-line-or-region)
("M-D" . crux-duplicate-and-comment-current-line-or-region)
("s-o" . crux-smart-open-line-above)))
Anzu
(use-package anzu
:diminish anzu-mode
:init
(add-hook 'after-init-hook 'global-anzu-mode)
:config
(define-key isearch-mode-map [remap isearch-query-replace] #'anzu-isearch-query-replace)
(define-key isearch-mode-map [remap isearch-query-replace-regexp] #'anzu-isearch-query-replace-regexp))avy
Use avy to move between visible text.
(use-package avy
:bind
(:map jethro-mode-map
("C-'" . avy-goto-char)
("C-," . avy-goto-char-2))
:config
(setq avy-keys '(?h ?t ?n ?s ?m ?w ?v ?z)))dumb-jump
Use it to jump to function definitions. Requires no external depedencies.
(use-package dumb-jump
:bind (("M-g o" . dumb-jump-go-other-window)
("M-g j" . dumb-jump-go)
("M-g i" . dumb-jump-go-prompt)
("M-g x" . dumb-jump-go-prefer-external)
("M-g z" . dumb-jump-go-prefer-external-other-window))
:config (setq dumb-jump-selector 'ivy))Window switching
(use-package windmove
:config
;; use command key on Mac
(windmove-default-keybindings 'super)
;; wrap around at edges
(setq windmove-wrap-around t))Dired
Requiring dired
(require 'dired)Dired for Mac OSX
(let ((gls "/usr/local/bin/gls"))
(if (file-exists-p gls)
(setq insert-directory-program gls)))trash files instead of deleting them
(setq delete-by-moving-to-trash t)find-dired
(require 'find-dired)
(setq find-ls-option '("-print0 | xargs -0 ls -ld" . "-ld"))Hide details
Hide details and only show file and folder names.
(defun jethro/dired-mode-setup-hook ()
"hook for dired-mode"
(dired-hide-details-mode 1))
(add-hook 'dired-mode-hook 'jethro/dired-mode-setup-hook)Peep Dired
(use-package peep-dired
:bind (:map peep-dired-mode-map
("SPC" . nil)
("<backspace>" . nil))
(setq peep-dired-cleanup-eagerly t))Sort directories first
(setq dired-listing-switches "-aBhl --group-directories-first")Recursive Copying and Deleting
(setq dired-recursive-copies (quote always))
(setq dired-recursive-deletes (quote top))dired-jump from file
(require 'dired-x)allow editing of permissions
(use-package wdired
:config
(setq wdired-allow-to-change-permissions t))dired-k
(use-package dired-k
:config
(define-key dired-mode-map (kbd "K") 'dired-k)
(setq dired-k-style 'git))dired-narrow
(use-package dired-narrow
:bind (:map dired-mode-map
("N" . dired-narrow-fuzzy)))dired-ranger
(use-package dired-ranger
:bind (:map dired-mode-map
("C" . dired-ranger-copy)
("P" . dired-ranger-paste)
("M" . dired-ranger-move)))dired-subtree
The dired-subtree package (part of the magnificent dired hacks) allows you to expand subdirectories in place, like a tree structure.
(use-package dired-subtree
:config
(bind-keys :map dired-mode-map
("i" . dired-subtree-insert)
(";" . dired-subtree-remove)))all-the-icons-dired
(use-package all-the-icons-dired
:init
(add-hook 'dired-mode-hook 'all-the-icons-dired-mode))hydra window movements
(defhydra jethro/window-movement ()
("h" windmove-left)
("s" windmove-right)
("t" windmove-down)
("n" windmove-up)
("y" other-window "other")
("f" find-file "file")
("F" find-file-other-window "other file")
("v" (progn (split-window-right) (windmove-right)))
("o" delete-other-windows :color blue)
("d" delete-window "delete")
("q" nil))
(bind-key "M-'" 'jethro/window-movement/body jethro-mode-map)ibuffer
(use-package ibuffer
:bind (:map jethro-mode-map
([remap list-buffers] . ibuffer))
:config
(setq ibuffer-expert t))shackle
(use-package shackle
:diminish shackle-mode
:if (not (bound-and-true-p disable-pkg-shackle))
:config
(shackle-mode 1)
(setq shackle-rules
'((compilation-mode :select nil)
("*undo-tree*" :size 0.25 :align right)
("*eshell*" :select t :size 0.3 :align t)
("*Shell Command Output*" :select nil)
("\\*Async Shell.*\\*" :regexp t :ignore t)
(occur-mode :select nil :align t)
("*Help*" :select t :inhibit-window-quit t :other t)
("*Completions*" :size 0.3 :align t)
("*Messages*" :select nil :inhibit-window-quit t :other t)
("\\*[Wo]*Man.*\\*" :regexp t :select t :inhibit-window-quit t :other t)
("*Calendar*" :select t :size 0.3 :align below)
("*info*" :select t :inhibit-window-quit t :same t)
(magit-status-mode :select t :inhibit-window-quit t :same t)
(magit-log-mode :select t :inhibit-window-quit t :same t))))go to matching parentheses
(defun goto-match-paren (arg)
"Go to the matching parenthesis if on parenthesis, otherwise insert %.
vi style of % jumping to matching brace."
(interactive "p")
(cond ((looking-at "\\s\(") (forward-list 1) (backward-char 1))
((looking-at "\\s\)") (forward-char 1) (backward-list 1))
(t (self-insert-command (or arg 1)))))
(bind-key "C-%" 'goto-match-paren jethro-mode-map)occur
(bind-key "C-c C-o" 'occur jethro-mode-map)Editing Text
easy-kill
(use-package easy-kill
:config
(global-set-key [remap kill-ring-save] 'easy-kill))visual-regexp
(use-package visual-regexp
:bind (:map jethro-mode-map
("C-M-%" . vr/query-replace)
("C-c m" . vr/mc-mark)))Align Regexp
(defun jethro/align-repeat (start end regexp &optional justify-right after)
"Repeat alignment with respect to the given regular expression.
If JUSTIFY-RIGHT is non nil justify to the right instead of the
left. If AFTER is non-nil, add whitespace to the left instead of
the right."
(interactive "r\nsAlign regexp: ")
(let* ((ws-regexp (if (string-empty-p regexp)
"\\(\\s-+\\)"
"\\(\\s-*\\)"))
(complete-regexp (if after
(concat regexp ws-regexp)
(concat ws-regexp regexp)))
(group (if justify-right -1 1)))
(message "%S" complete-regexp)
(align-regexp start end complete-regexp group 1 t)))
;; Modified answer from http://emacs.stackexchange.com/questions/47/align-vertical-columns-of-numbers-on-the-decimal-point
(defun jethro/align-repeat-decimal (start end)
"Align a table of numbers on decimal points and dollar signs (both optional)"
(interactive "r")
(require 'align)
(align-region start end nil
'((nil (regexp . "\\([\t ]*\\)\\$?\\([\t ]+[0-9]+\\)\\.?")
(repeat . t)
(group 1 2)
(spacing 1 1)
(justify nil t)))
nil))
(defmacro jethro/create-align-repeat-x (name regexp &optional justify-right default-after)
(let ((new-func (intern (concat "jethro/align-repeat-" name))))
`(defun ,new-func (start end switch)
(interactive "r\nP")
(let ((after (not (eq (if switch t nil) (if ,default-after t nil)))))
(jethro/align-repeat start end ,regexp ,justify-right after)))))
(jethro/create-align-repeat-x "comma" "," nil t)
(jethro/create-align-repeat-x "semicolon" ";" nil t)
(jethro/create-align-repeat-x "colon" ":" nil t)
(jethro/create-align-repeat-x "equal" "=")
(jethro/create-align-repeat-x "math-oper" "[+\\-*/]")
(jethro/create-align-repeat-x "ampersand" "&")
(jethro/create-align-repeat-x "bar" "|")
(jethro/create-align-repeat-x "left-paren" "(")
(jethro/create-align-repeat-x "right-paren" ")" t)
(jethro/create-align-repeat-x "backslash" "\\\\")
(defvar align-regexp-map nil "keymap for `align-regexp'")
(setq align-regexp-map (make-sparse-keymap))
(define-key align-regexp-map (kbd "&") 'jethro/align-repeat-ampersand)
(define-key align-regexp-map (kbd "(") 'jethro/align-repeat-left-paren)
(define-key align-regexp-map (kbd ")") 'jethro/align-repeat-right-paren)
(define-key align-regexp-map (kbd ",") 'jethro/align-repeat-comma)
(define-key align-regexp-map (kbd ".") 'jethro/align-repeat-decimal)
(define-key align-regexp-map (kbd ":") 'jethro/align-repeat-colon)
(define-key align-regexp-map (kbd ";") 'jethro/align-repeat-semicolon)
(define-key align-regexp-map (kbd "=") 'jethro/align-repeat-equal)
(define-key align-regexp-map (kbd "\\") 'jethro/align-repeat-backslash)
(define-key align-regexp-map (kbd "a") 'align)
(define-key align-regexp-map (kbd "c") 'align-current)
(define-key align-regexp-map (kbd "m") 'jethro/align-repeat-math-oper)
(define-key align-regexp-map (kbd "r") 'jethro/align-repeat)
(define-key align-regexp-map (kbd "|") 'jethro/align-repeat-bar)
(bind-key "C-x a" 'align-regexp-map jethro-mode-map)aggressive-indent
Keep your text indented at all times. Remember to turn this off for indentation-dependent languages like Python and Haml.
(use-package aggressive-indent
:diminish aggressive-indent-mode
:config
(add-hook 'after-init-hook 'global-aggressive-indent-mode)
(setq aggressive-indent-excluded-modes
'(bibtex-mode
cider-repl-mode
coffee-mode
comint-mode
conf-mode
Custom-mode
diff-mode
doc-view-mode
dos-mode
erc-mode
jabber-chat-mode
haml-mode
intero-mode
haskell-mode
interative-haskell-mode
haskell-interactive-mode
image-mode
makefile-mode
makefile-gmake-mode
minibuffer-inactive-mode
netcmd-mode
python-mode
sass-mode
slim-mode
special-mode
shell-mode
snippet-mode
eshell-mode
tabulated-list-mode
term-mode
TeX-output-mode
text-mode
yaml-mode)))multiple-cursors
A port of Sublime Text’s multiple-cursors functionality.
(use-package multiple-cursors
:bind (:map jethro-mode-map
("C-M-c" . mc/edit-lines)
("C->" . mc/mark-next-like-this)
("C-<" . mc/mark-previous-like-this)
("C-c C-<" . mc/mark-all-like-this)))expand-region
Use this often, and in combination with multiple-cursors.
(use-package expand-region
:bind (:map jethro-mode-map
("C-=" . er/expand-region)))smartparens
(use-package smartparens
:bind (("C-c h S" . jethro/smartparens/body)
(:map smartparens-mode-map
("C-M-f" . sp-forward-sexp)
("C-M-b" . sp-backward-sexp)
("C-M-u" . sp-backward-up-sexp)
("C-M-d" . sp-down-sexp)
("C-M-p" . sp-backward-down-sexp)
("C-M-n" . sp-up-sexp)
("M-s" . sp-splice-sexp)
("C-M-<up>" . sp-splice-sexp-killing-backward)
("C-M-<down>" . sp-splice-sexp-killing-forward)
("C-M-r" . sp-splice-sexp-killing-around)
("C-)" . sp-forward-slurp-sexp)
("C-<right>" . sp-forward-slurp-sexp)
("C-}" . sp-forward-barf-sexp)
("C-<left>" . sp-forward-barf-sexp)
("C-(" . sp-backward-slurp-sexp)
("C-M-<left>" . sp-backward-slurp-sexp)
("C-{" . sp-backward-barf-sexp)
("C-M-<right>" . sp-backward-barf-sexp)
("M-S" . sp-split-sexp)))
:init
(add-hook 'after-init-hook 'smartparens-global-strict-mode)
:config
(require 'smartparens-config)
(defhydra jethro/smartparens (:hint nil)
"
Sexps (quit with _q_)
^Nav^ ^Barf/Slurp^ ^Depth^
^---^------------^----------^-----------------^-----^-----------------
_f_: forward _<left>_: slurp forward _R_: splice
_b_: backward _<right>_: barf forward _r_: raise
_u_: backward ↑ _C-<left>_: slurp backward _<up>_: raise backward
_d_: forward ↓ _C-<right>_: barf backward _<down>_: raise forward
_p_: backward ↓
_n_: forward ↑
^Kill^ ^Misc^ ^Wrap^
^----^-----------^----^-----------------------^----^------------------
_w_: copy _j_: join _(_: wrap with ( )
_k_: kill _s_: split _{_: wrap with { }
^^ _t_: transpose _'_: wrap with ' '
^^ _c_: convolute _\"_: wrap with \" \"
^^ _i_: indent defun"
("q" nil)
;; Wrapping
("(" (lambda (a) (interactive "P") (sp-wrap-with-pair "(")))
("{" (lambda (a) (interactive "P") (sp-wrap-with-pair "{")))
("'" (lambda (a) (interactive "P") (sp-wrap-with-pair "'")))
("\"" (lambda (a) (interactive "P") (sp-wrap-with-pair "\"")))
;; Navigation
("f" sp-forward-sexp)
("b" sp-backward-sexp)
("u" sp-backward-up-sexp)
("d" sp-down-sexp)
("p" sp-backward-down-sexp)
("n" sp-up-sexp)
;; Kill/copy
("w" sp-copy-sexp)
("k" sp-kill-sexp)
;; Misc
("t" sp-transpose-sexp)
("j" sp-join-sexp)
("s" sp-split-sexp)
("c" sp-convolute-sexp)
("i" sp-indent-defun)
;; Depth changing
("R" sp-splice-sexp)
("r" sp-splice-sexp-killing-around)
("<up>" sp-splice-sexp-killing-backward)
("<down>" sp-splice-sexp-killing-forward)
;; Barfing/slurping
("<right>" sp-forward-slurp-sexp)
("<left>" sp-forward-barf-sexp)
("C-<left>" sp-backward-barf-sexp)
("C-<right>" sp-backward-slurp-sexp))
;; Org-mode config
(sp-with-modes 'org-mode
(sp-local-pair "'" nil :unless '(sp-point-after-word-p))
(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))
(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 "«" "»"))
(defun sp--org-skip-asterisk (ms mb me)
(or (and (= (line-beginning-position) mb)
(eq 32 (char-after (1+ mb))))
(and (= (1+ (line-beginning-position)) me)
(eq 32 (char-after me))))))zap-up-to-char
(autoload 'zap-up-to-char "misc"
"Kill up to, but not including ARGth occurrence of CHAR.
\(fn arg char)"
'interactive)
(bind-key "M-z" 'zap-up-to-char jethro-mode-map)move-text
I disabled this, because it interferes with some org-mode keybindings, and I realise I don’t use this as much as I’d like to.
(use-package move-text
:bind (:map jethro-mode-map
("M-<up>" . move-text-up)
("M-<down>" . move-text-down)))ws-butler
Only lines touched get trimmed. If the white space at end of buffer is changed, then blank lines at the end of buffer are truncated respecting require-final-newline. Trimming only happens when saving.
(use-package ws-butler
:diminish 'ws-butler-mode
:config
(add-hook 'prog-mode-hook 'ws-butler-mode))Linting with Flycheck
(use-package flycheck
:bind (:map jethro-mode-map
("C-c h f" . jethro/hydra-flycheck/body))
:init
(add-hook 'prog-mode-hook 'flycheck-mode)
:config
(defun jethro/adjust-flycheck-automatic-syntax-eagerness ()
"Adjust how often we check for errors based on if there are any.
This lets us fix any errors as quickly as possible, but in a
clean buffer we're an order of magnitude laxer about checking."
(setq flycheck-idle-change-delay
(if flycheck-current-errors 0.3 3.0)))
;; Each buffer gets its own idle-change-delay because of the
;; buffer-sensitive adjustment above.
(make-variable-buffer-local 'flycheck-idle-change-delay)
;; Remove newline checks, since they would trigger an immediate check
;; when we want the idle-change-delay to be in effect while editing.
(setq-default flycheck-check-syntax-automatically '(save
idle-change
mode-enabled))
(add-hook 'flycheck-after-syntax-check-hook
'jethro/adjust-flycheck-automatic-syntax-eagerness)
(defun flycheck-handle-idle-change ()
"Handle an expired idle time since the last change.
This is an overwritten version of the original
flycheck-handle-idle-change, which removes the forced deferred.
Timers should only trigger inbetween commands in a single
threaded system and the forced deferred makes errors never show
up before you execute another command."
(flycheck-clear-idle-change-timer)
(flycheck-buffer-automatically 'idle-change))
;; Temporary workaround: Direnv needs to load PATH before flycheck looks
;; for linters
(setq flycheck-executable-find
(lambda (cmd)
(direnv-update-environment default-directory)
(executable-find cmd)))
(defhydra jethro/hydra-flycheck
(:pre (progn (setq hydra-lv t) (flycheck-list-errors))
:post (progn (setq hydra-lv nil) (quit-windows-on "*Flycheck errors*"))
:hint nil)
"Errors"
("f" flycheck-error-list-set-filter "Filter")
("n" flycheck-next-error "Next")
("p" flycheck-previous-error "Previous")
("<" flycheck-first-error "First")
(">" (progn (goto-char (point-max)) (flycheck-previous-error)) "Last")
("q" nil))
(use-package flycheck-pos-tip
:init
(add-hook 'flycheck-mode-hook 'flycheck-pos-tip-mode)))Templating with Yasnippet
(use-package yasnippet
:diminish yas-global-mode yas-minor-mode
:init (add-hook 'after-init-hook 'yas-global-mode)
:config (setq yas-snippet-dirs '("~/.emacs.d/snippets/snippets/")))Autocompletions with Company
(use-package company
:diminish company-mode
:bind (:map company-active-map
("M-n" . nil)
("M-p" . nil)
("C-n" . company-select-next)
("C-p" . company-select-previous))
:init
(add-hook 'after-init-hook 'global-company-mode)
:config
(setq company-dabbrev-ignore-case nil
company-dabbrev-code-ignore-case nil
company-dabbrev-downcase nil
company-idle-delay 0
company-minimum-prefix-length 2
company-require-match nil
company-begin-commands '(self-insert-command)
company-transformers '(company-sort-by-occurrence))
(use-package company-quickhelp
:bind (:map company-active-map
("M-h" . company-quickhelp-manual-begin))
:config (company-quickhelp-mode 1))
(defun company-mode/backend-with-yas (backend)
(if (and (listp backend) (member 'company-yasnippet backend))
backend
(append (if (consp backend) backend (list backend))
'(:with company-yasnippet))))
(setq company-backends (mapcar #'company-mode/backend-with-yas company-backends)))Spellcheck with Flyspell
(use-package flyspell
:ensure f
:diminish flyspell-mode
:init
(setenv "DICTIONARY" "en_GB")
:config
(add-hook 'text-mode-hook 'flyspell-mode))Auto-fill-mode
(add-hook 'text-mode-hook 'auto-fill-mode)
(diminish 'auto-fill-mode)Hippie Expand
(bind-key "M-/" 'hippie-expand)
(setq hippie-expand-try-functions-list
'(yas-hippie-try-expand
try-expand-all-abbrevs
try-complete-file-name-partially
try-complete-file-name
try-expand-dabbrev
try-expand-dabbrev-from-kill
try-expand-dabbrev-all-buffers
try-expand-list
try-expand-line
try-complete-lisp-symbol-partially
try-complete-lisp-symbol))Conveniences
Fill and unfill paragraphs
Stolen from http://endlessparentheses.com/fill-and-unfill-paragraphs-with-a-single-key.html.
(defun endless/fill-or-unfill ()
"Like `fill-paragraph', but unfill if used twice."
(interactive)
(let ((fill-column
(if (eq last-command 'endless/fill-or-unfill)
(progn (setq this-command nil)
(point-max))
fill-column)))
(call-interactively #'fill-paragraph)))
(global-set-key [remap fill-paragraph]
#'endless/fill-or-unfill)Keyboard hydra
(defhydra jethro/hydra-draw-box (:color pink)
"Draw box with IBM single line box characters (ESC to Quit)."
("ESC" nil :color blue) ;; Esc to exit.
("'" (lambda () (interactive) (insert "┌")) "top left ┌")
("," (lambda () (interactive) (insert "┬")) "top ┬")
("." (lambda () (interactive) (insert "┐")) "top right ┐")
("a" (lambda () (interactive) (insert "├")) "left ├")
("o" (lambda () (interactive) (insert "┼")) "center ┼")
("e" (lambda () (interactive) (insert "┤")) "right ┤")
(";" (lambda () (interactive) (insert "└")) "bottom left └")
("q" (lambda () (interactive) (insert "┴")) "bottom ┴")
("j" (lambda () (interactive) (insert "┘")) "bottom right ┘")
("k" (lambda () (interactive) (insert "─")) "horizontal ─")
("x" (lambda () (interactive) (insert "│")) "vertical │"))
(bind-key "C-c h d" 'jethro/hydra-draw-box/body jethro-mode-map)Environment
Direnv
(use-package direnv
:init
(add-hook 'after-init-hook 'direnv-mode)
(setq direnv-always-show-summary t))Languages
Common Lisp
(use-package slime
:config
(setq inferior-lisp-program "sbcl")
(setq slime-contribs '(slime-fancy))
(use-package slime-company
:config
(slime-setup '(slime-company))))Emacs Lisp
(bind-key "C-c C-k" 'eval-buffer emacs-lisp-mode-map)Elixir
elixir-mode
(use-package elixir-mode)Alchemist
(use-package alchemist)Docker
(use-package docker
:commands docker-mode)
(use-package dockerfile-mode
:mode "Dockerfile\\'")Nix
(use-package nix-mode
:config
(add-hook 'nix-mode-hook (lambda ()
(aggressive-indent-mode -1))))Haskell
(use-package haskell-mode
:mode ("\\.hs\\'" . haskell-mode)
:init
(add-hook 'haskell-mode-hook
(lambda ()
(setq compile-command "stack build --fast --test --bench --no-run-tests --no-run-benchmarks"))))Intero
(use-package intero
:init
(add-hook 'haskell-mode-hook 'intero-mode))Go
(use-package go-mode
:mode ("\\.go\\'" . go-mode)
:config
(add-hook 'go-mode-hook 'compilation-auto-quit-window)
(add-hook 'go-mode-hook (lambda ()
(set (make-local-variable 'company-backends) '(company-go))
(company-mode)))
(add-hook 'go-mode-hook (lambda ()
(add-hook 'before-save-hook 'gofmt-before-save)
(local-set-key (kbd "M-.") 'godef-jump)))
(add-hook 'go-mode-hook
(lambda ()
(unless (file-exists-p "Makefile")
(set (make-local-variable 'compile-command)
(let ((file (file-name-nondirectory buffer-file-name)))
(format "go build %s"
file))))))
(use-package go-dlv
:config (require 'go-dlv))
(use-package golint
:config
(add-to-list 'load-path (concat (getenv "GOPATH") "/src/github.com/golang/lint/misc/emacs"))
(require 'golint))
(use-package gorepl-mode
:config (add-hook 'go-mode-hook #'gorepl-mode))
(use-package company-go
:config (add-hook 'go-mode-hook (lambda ()
(set (make-local-variable 'company-backends) '(company-go))
(company-mode)))))C
(defun jethro/compile-c ()
(unless (file-exists-p "Makefile")
(set (make-local-variable 'compile-command)
(let ((file (file-name-nondirectory buffer-file-name)))
(format "cc -Wall %s -o %s --std=c99"
file
(file-name-sans-extension file))))))
(add-hook 'c-mode-hook jethro/compile-c)C++
C++ compile function
(add-hook 'c++-mode-hook
(lambda ()
(unless (file-exists-p "Makefile")
(set (make-local-variable 'compile-command)
(let ((file (file-name-nondirectory buffer-file-name)))
(format "g++ -Wall -s -pedantic-errors %s -o %s --std=c++14"
file
(file-name-sans-extension file)))))))Fish
(use-package fish-mode
:mode ("\\.fish\\'" . fish-mode))Rust
(use-package rust-mode
:mode ("\\.rs\\'" . rust-mode))Python
Python Path
(eval-after-load "python-mode"
(lambda ()
(setq python-remove-cwd-from-path t)))Sphinx Docs
(use-package sphinx-doc
:init
(add-hook 'python-mode-hook 'sphinx-doc-mode))Anaconda
(use-package anaconda-mode
:init
(add-hook 'python-mode-hook 'anaconda-mode)
(add-hook 'python-mode-hook 'anaconda-eldoc-mode))Company
(use-package company-anaconda
:config
(eval-after-load "company"
'(add-to-list 'company-backends '(company-anaconda))))isort
(use-package py-isort
:commands
(py-isort-buffer py-isort-region))yapfify
(use-package yapfify)pytest
(use-package pytest
:bind (:map python-mode-map
("C-c a" . pytest-all)
("C-c m" . pytest-module)
("C-c ." . pytest-one)
("C-c d" . pytest-directory)
("C-c p a" . pytest-pdb-all)
("C-c p m" . pytest-pdb-module)
("C-c p ." . pytest-pdb-one)))realgud
(use-package realgud)Highlight Indent Guides
(use-package highlight-indent-guides
:init
(add-hook 'python-mode-hook 'highlight-indent-guides-mode)
:config
(setq highlight-indent-guides-method 'character))Isend-mode
(use-package isend-mode
:bind
(:map isend-mode-map
("C-M-e" . isend-send-defun))
:init
(add-hook 'isend-mode-hook 'isend-default-python-setup))HTML
Web-mode
(use-package web-mode
:mode (("\\.html\\'" . web-mode)
("\\.html\\.erb\\'" . web-mode)
("\\.mustache\\'" . web-mode)
("\\.jinja\\'" . web-mode)
("\\.njk\\'" . web-mode)
("\\.php\\'" . web-mode))
:config
(setq web-mode-enable-css-colorization t)
(setq-default css-indent-offset 2
web-mode-markup-indent-offset 2
web-mode-css-indent-offset 2
web-mode-code-indent-offset 2
web-mode-attr-indent-offset 2))Emmet-mode
(use-package emmet-mode
:diminish emmet-mode
:config
(add-hook 'web-mode-hook 'emmet-mode)
(add-hook 'vue-mode-hook 'emmet-mode))CSS
Rainbow-mode
(use-package rainbow-mode
:diminish rainbow-mode
:config
(add-hook 'css-mode-hook 'rainbow-mode)
(add-hook 'scss-mode-hook 'rainbow-mode))SCSS-mode
(use-package scss-mode
:mode "\\.scss\\'"
:config (progn
(setq scss-compile-at-save nil)))Javascript
JS2-mode
Here I also added tern-mode. This requires the tern executable:
npm install -g tern(use-package js2-mode
:mode ("\\.js\\'" . js2-mode)
:config
(setq-default flycheck-disabled-checkers
(append flycheck-disabled-checkers
'(javascript-jshint)))
(setq js-switch-indent-offset 2)
(use-package tern
:diminish tern-mode
:config
(add-hook 'js2-mode-hook 'tern-mode)
(use-package company-tern
:config
(add-to-list 'company-backends 'company-tern))))Indium
(use-package indium)Flycheck
(require 'flycheck)
(flycheck-add-mode 'javascript-eslint 'js2-mode)
(flycheck-add-mode 'javascript-eslint 'web-mode)Skewer
(use-package skewer-mode
:bind (:map skewer-mode-map
("C-c C-k" . skewer-load-buffer))
:config
(add-hook 'js2-mode-hook 'skewer-mode))js-comint
(use-package js-comint
:config
(add-hook 'js2-mode-hook
(lambda ()
(local-set-key (kbd "C-x C-e") 'js-send-last-sexp)
(local-set-key (kbd "C-M-x") 'js-send-last-sexp-and-go)
(local-set-key (kbd "C-c b") 'js-send-buffer)
(local-set-key (kbd "C-c C-b") 'js-send-buffer-and-go)
(local-set-key (kbd "C-c l") 'js-load-file-and-go))))js-doc
(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 "jethrokuan95@gmail.com"
js-doc-author (format "Jethro Kuan <%s>" js-doc-mail-address)
js-doc-url "http://www.jethrokuan.com/"
js-doc-license "MIT"))JS2-refactor
(use-package js2-refactor
:config
(add-hook 'js2-mode-hook #'js2-refactor-mode)
(js2r-add-keybindings-with-prefix "C-c C-j"))Vue-mode
Additional support for Vue.js projects.
(use-package vue-mode
:mode "\\.vue\\'")React-mode
(defun jethro/setup-rjsx-mode ()
(setq-local emmet-expand-jsx-className? t)
(setq-local web-mode-enable-auto-quoting nil))
(use-package rjsx-mode
:init
(add-to-list 'auto-mode-alist '("\\.jsx\\'" . rjsx-mode))
(add-to-list 'auto-mode-alist '("\\.react.js\\'" . rjsx-mode))
(add-to-list 'auto-mode-alist '("\\index.android.js\\'" . rjsx-mode))
(add-to-list 'auto-mode-alist '("\\index.ios.js\\'" . rjsx-mode))
(add-to-list 'magic-mode-alist '("/\\*\\* @jsx React\\.DOM \\*/" . rjsx-mode))
(add-to-list 'magic-mode-alist '("^import React" . rjsx-mode))
(add-hook 'rjsx-mode-hook 'jethro/setup-rjsx-mode)
(add-hook 'rjsx-mode-hook 'tern-mode)
(add-hook 'rjsx-mode-hook 'emmet-mode)
:config
(with-eval-after-load 'flycheck
(dolist (checker '(javascript-eslint javascript-standard))
(flycheck-add-mode checker 'rjsx-mode)))
(defun jethro/line-align-closing-bracket ()
"Workaround sgml-mode and align closing bracket with opening bracket"
(save-excursion
(beginning-of-line)
(when (looking-at-p "^ +\/?> *$")
(delete-char sgml-basic-offset))))
(advice-add #'js-jsx-indent-line
:after
#'jethro/line-align-closing-bracket))Typescript
typescript-mode
(use-package typescript-mode)Tide
(defun setup-tide-mode ()
(interactive)
(tide-setup)
(flycheck-mode +1)
(eldoc-mode +1)
(tide-hl-identifier-mode +1)
(company-mode +1))
(use-package tide
:mode "\\.ts\\'"
:init
(add-hook 'before-save-hook 'tide-format-before-save)
(add-hook 'typescript-mode-hook #'setup-tide-mode)
:config
(setq company-tooltip-align-annotations t))JSON
(use-package json-mode
:mode "\\.json\\'"
:config (add-hook 'json-mode-hook (lambda ()
(make-local-variable 'js-indent-level)
(setq js-indent-level 2))))Markdown
(use-package markdown-mode
:mode ("\\.md\\'" . markdown-mode)
:commands (markdown-mode gfm-mode)
:init
(setq markdown-fontify-code-blocks-natively t)
:config
(setq markdown-command "multimarkdown --snippet --smart --notes"
markdown-enable-wiki-links t
markdown-indent-on-enter 'indent-and-new-item
markdown-asymmetric-header t
markdown-live-preview-delete-export 'delete-on-destroy))
(use-package org-table
:after 'markdown-mode
:init
(add-hook 'markdown-mode-hook 'orgtbl-mode))Clojure
Clojure-mode
(use-package clojure-mode
:mode (("\\.clj\\'" . clojure-mode)
("\\.boot\\'" . clojure-mode)
("\\.edn\\'" . clojure-mode)
("\\.cljs\\'" . clojurescript-mode)
("\\.cljs\\.hl\\'" . clojurescript-mode))
:init
(add-hook 'clojure-mode-hook #'eldoc-mode)
(add-hook 'clojure-mode-hook #'subword-mode)
(add-hook 'clojure-mode-hook #'cider-mode)
(add-hook 'clojure-mode-hook #'clj-refactor-mode))Cider
(use-package cider
:init
(add-hook 'cider-mode-hook #'clj-refactor-mode)
(add-hook 'cider-repl-mode-hook #'company-mode)
(add-hook 'cider-mode-hook #'company-mode)
:diminish subword-mode
:config
(setq nrepl-log-messages t
cider-repl-display-in-current-window t
cider-repl-use-clojure-font-lock t
cider-prompt-save-file-on-load 'always-save
cider-font-lock-dynamically '(macro core function var)
nrepl-hide-special-buffers t
cider-show-error-buffer nil
cider-overlays-use-font-lock t
cider-repl-result-prefix ";; => ")
(setq cider-cljs-lein-repl "(do (use 'figwheel-sidecar.repl-api) (start-figwheel!) (cljs-repl))")
(cider-repl-toggle-pretty-printing))clj-refactor
(use-package clj-refactor
:defines cljr-add-keybindings-with-prefix
:diminish clj-refactor-mode
:config (cljr-add-keybindings-with-prefix "C-c C-j"))Squiggly-clojure
(use-package flycheck-clojure
:config
(flycheck-clojure-setup))Latex
AucTeX
(use-package auctex
:defer t
:config
(setq TeX-auto-save t
TeX-parse-self t
TeX-syntactic-comment t
;; Synctex support
TeX-source-correlate-start-server nil
;; Don't insert line-break at inline math
LaTeX-fill-break-at-separators nil)
(setq TeX-view-program-list '(("Evince" "evince --page-index=%(outpage) %o")
("qpdfview" "qpdfview %o#%(outpage)")))
(setq TeX-view-program-selection '((output-pdf "qpdfview")
(output-pdf "Evince")))
(when latex-enable-auto-fill
(add-hook 'LaTeX-mode-hook 'latex/auto-fill-mode))
(when latex-enable-folding
(add-hook 'LaTeX-mode-hook 'TeX-fold-mode))
(add-hook 'LaTeX-mode-hook 'LaTeX-math-mode)
(add-hook 'LaTeX-mode-hook 'TeX-source-correlate-mode)
(add-hook 'LaTeX-mode-hook 'TeX-PDF-mode))Autocomplete support
(use-package company-auctex
:defer t)Yaml
(use-package yaml-mode
:mode ("\\.yaml\\'" . yaml-mode))PDFs
We use pdf-tools for PDF viewing, which has first class support for highlighting and annotations.
(use-package pdf-tools
:defer 10
:config
(pdf-tools-install))Org-Mode
Setup
Basics
We use org-plus-contrib, which contains several contrib plugins that
may come in handy later, including org-drill and some org-babel
language support.
To install org-plus-contrib, one has to add the package archive to
Emacs. This is typically where you also add MELPA.
(when (>= emacs-major-version 24)
(require 'package)
(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/") t)
(add-to-list 'package-archives '("org" . "http://orgmode.org/elpa/") t)
(package-initialize))(use-package org-plus-contrib
:bind
(:map jethro-mode-map
("C-c l" . org-store-link)
("C-c a" . org-agenda)
("C-c b" . org-iswitchb)
("C-c c" . org-capture))
:bind
(:map org-mode-map
("M-n" . outline-next-visible-heading)
("M-p" . outline-previous-visible-heading)))Saner Defaults
(setq org-return-follows-link t)Org Bullets
(use-package org-bullets
:init
(add-hook 'org-mode-hook 'org-bullets-mode)
:config
(setq org-bullets-bullet-list
'("◉" "◎" "⚫" "○" "►" "◇")))Org Diary file
(setq org-agenda-diary-file "~/.org/diary.org")Variable Pitch Mode
(add-hook 'org-mode-hook
'(lambda ()
(variable-pitch-mode 1) ;; All fonts with variable pitch.
(mapc
(lambda (face) ;; Other fonts with fixed-pitch.
(set-face-attribute face nil :inherit 'fixed-pitch))
(list 'org-code
'org-link
'org-block
'org-table
'org-block-begin-line
'org-block-end-line
'org-meta-line
'org-document-info-keyword))))Easy Templates
I added an emacs-lisp easy template for my literate Emacs configuration.
(add-to-list 'org-structure-template-alist '("el" "#+BEGIN_SRC emacs-lisp :tangle yes?\n\n#+END_SRC"))Org Download
This extension facilitates moving images from point A to point B.
(use-package org-download
:config
(setq org-download-image-dir "./images"))Org Mode for GTD
This document aims to extensively document my implementation of Getting Things Done, a methodology by David Allen. This will always be a work-in-progress, and is fully representative of the GTD setup I am currently using.
This document is written primarily for my own reference. However, it is also written with readers who are looking for inspiration when implementing GTD in org-mode.
Reasoning
There is no shortage of existing GTD implementations, in org-mode. Perhaps the best reference document out there is by Bernt Hansen, published here. However, there are some slight deviations from the GTD that David Allen proposes, and some conveniences he takes making the GTD system he implements weaker, that can perhaps be solved by writing some Elisp. This is a major adaptation of his setup, but with additional customizations that make it closer to the ideal system that David Allen speaks about.
Organizing Your Life Into Org-mode Files
Bernt Hansen uses separate files as logical groups, such as a separation between work and life. This may suit your purpose, but this makes it a lot harder to write general Elisp code for. Once a new logical group appears, the code that generates the weekly review would have to change as well, for example.
Instead, I use David Allen’s physical categories as different files, and use org-mode tags to separate the different context. That is, I have the files:
| file (.org) | Purpose |
|---|---|
| inbox | Includes everything on your mind: tasks, ideas etc. |
| someday | Includes things that will be done later on (with no specific deadline), to be reviewed often |
| reference | I don’t actually have this file; I use deft-mode as my braindump |
| waiting | This contains a list of names of people as level one headings, and things I’m waiting for them to complete as subheadings |
| next | This contains one-off tasks that don’t belong to projects. |
| projects | This contains the list of projects, and their corresponding todo items |
(setq org-agenda-files '("~/.org/gtd/inbox.org"
"~/.org/gtd/timetable.org"
"~/.org/gtd/projects.org"
"~/.org/gtd/tickler.org"))Stage 1: Collecting
Collecting needs to be convenient. This is achieved easily be using
org-capture. The capture template is kept simple, to minimize
friction in capturing new items as they pop up.
(setq org-capture-templates
`(("i" "inbox" entry (file "~/.org/gtd/inbox.org")
"* TODO %?
Captured %<%Y-%m-%d %H:%M>")
("w" "Web site" entry (file "~/.org/deft/websites.org")
"* %c\n" :immediate-finish t)
("p" "paper" entry (file "~/.org/gtd/inbox.org")
"* TODO Read: %:title\n%a" :immediate-finish t)))Stage 2: Processing
During predetermined times of each day, the inbox is to be processed,
each item in inbox sorted into their respective folders.
org-agenda provides a brilliant interface for viewing and processing
the inbox. At the end of the “processing” stage, inbox.org should be
empty, unless the processing is done on the whim. This will be
facilitated with an iOS or android app later on.
The process is clearly outlined in GTD, but key to the GTD implementation here are a few factors:
- Which file: Is this to be done someday when there’s time, or is this a project (old or new), or is this a simple action?
- Adding of context: Is this school-related, or work-related? Do I have to be at a specific location to perform this task?
At the end of the process, the item in inbox would have placed in
either a non-actionable file, or an actionable file (projects, or
next) with a physical actionable. To encourage this, we have a list
of verbs.
David Allen recommends processing inbox items top-down or bottom-up, one item at a time. However, I like to have an overview of my inbox, so I can estimate the number of items left to process.
This process is therefore contigent on several factors:
- There aren’t too many items in the inbox at the same time. This can prove to be too distracting. Fortunately, I’ve yet to experience this.
- Processing of inbox is more regular. Keeping inbox zero at all times should be a goal, but not a priority.
Org Agenda Inbox View
This view is where I see all my inbox items: it is a simple list of
captured items in inbox.org.
(require 'org-agenda)
(setq jethro/org-agenda-inbox-view
`("i" "Inbox" todo ""
((org-agenda-files '("~/.org/gtd/inbox.org")))))Org TODO Keywords
| keyword | meaning |
|---|---|
| TODO | An item that has yet to be processed, or cannot be attempted at this moment. |
| NEXT | An action that can be completed at this very moment, in the correct context |
| DONE | An item that is completed, and ready to be archived |
| WAITING | An item that awaits input from an external party |
| HOLD | An item that is delayed due to circumstance |
| CANCELLED | An item that was once considered, but no longer to be attempted |
WAITING, HOLD, and CANCELLED are all keywords that require
supplementary information. For example, who am I waiting for? Or why
is this item on hold? As such, it is convenient to trigger a note when
an item transitions to these states. Note that the triggers only
happen with “slow” state transitions, i.e. C-c C-t.
(setq org-todo-keywords
'((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d)")
(sequence "WAITING(w@/!)" "HOLD(h@/!)" "|" "CANCELLED(c@/!)")))
(setq org-log-done 'time)
(setq org-log-into-drawer t)
(setq org-log-state-notes-insert-after-drawers nil)The Process
Step 1: Clarifying
Tags
(setq org-tag-alist (quote (("@errand" . ?e)
("@office" . ?o)
("@home" . ?h)
("@school" . ?s)
(:newline)
("WAITING" . ?w)
("HOLD" . ?H)
("CANCELLED" . ?c))))
(setq org-fast-tag-selection-single-key nil)
Step 2: Organizing
This step involves refiling the item in the appropriate location. We
set org-refile-allow-creating-parent-nodes to =’confirm=, because this
allows us to create new projects if there are no matches.
When capturing new projects, it helps to pen down a few things about the project:
- Project Purpose/Principles
- Outcome Vision
This is currently done using org-add-note, but when my elisp-fu gets
stronger, I’d create a dedicated buffer with a template each time a
project is created.
;; https://github.com/syl20bnr/spacemacs/issues/3094
(setq org-refile-use-outline-path 'file
org-outline-path-complete-in-steps nil)
(setq org-refile-allow-creating-parent-nodes 'confirm)
(setq org-refile-targets '(("someday.org" :maxlevel . 1)
("projects.org" :maxlevel . 1)))(defun jethro/org-rename-item ()
(interactive)
(save-excursion
(when (org-at-heading-p)
(let* ((hl-text (nth 4 (org-heading-components)))
(new-header (read-string "New Text: " nil nil hl-text)))
(unless (or (null hl-text)
(org-string-match-p "^[ \t]*:[^:]+:$" hl-text))
(beginning-of-line)
(search-forward hl-text (point-at-eol))
(replace-string
hl-text
new-header
nil (- (point) (length hl-text)) (point)))))))
(defun jethro/org-agenda-process-inbox-item (&optional goto rfloc no-update)
(interactive "P")
(org-with-wide-buffer
(org-agenda-set-tags)
(org-agenda-refile nil nil t)
;; (org-mark-ring-push)
;; (org-refile-goto-last-stored)
;; (jethro/org-rename-item)
;; (org-mark-ring-goto)
(org-agenda-redo)))
(defun jethro/org-inbox-capture ()
"Capture a task in agenda mode."
(interactive)
(org-capture nil "i"))
(define-key org-agenda-mode-map "i" 'org-agenda-clock-in)
(define-key org-agenda-mode-map "r" 'jethro/org-agenda-process-inbox-item)
(define-key org-agenda-mode-map "c" 'jethro/org-inbox-capture)add advice
(defvar jethro/new-project-template
"
*Project Purpose/Principles*:
*Project Outcome*:
"
"Project template, inserted when a new project is created")
(defvar jethro/is-new-project nil
"Boolean indicating whether it's during the creation of a new project")
(defun jethro/refile-new-child-advice (orig-fun parent-target child)
(let ((res (funcall orig-fun parent-target child)))
(save-excursion
(find-file (nth 1 parent-target))
(goto-char (org-find-exact-headline-in-buffer child))
(org-add-note)
)
res))
(advice-add 'org-refile-new-child :around #'jethro/refile-new-child-advice)Clocking in
(defun jethro/set-todo-state-next ()
"Visit each parent task and change NEXT states to TODO"
(org-todo "NEXT"))
(add-hook 'org-clock-in-hook 'jethro/set-todo-state-next 'append)Stage 3: Reviewing
Custom agenda Commands
(setq org-agenda-block-separator nil)
(setq jethro/org-agenda-todo-view
`(" " "Agenda"
((agenda ""
((org-agenda-span 'day)
(org-deadline-warning-days 365)))
(todo "TODO"
((org-agenda-overriding-header "To Refile")
(org-agenda-files '("~/.org/gtd/inbox.org"))))
(todo "NEXT"
((org-agenda-overriding-header "In Progress")
(org-agenda-files '("~/.org/gtd/someday.org" "~/.org/gtd/projects.org"))
(org-agenda-skip-function '(org-agenda-skip-entry-if 'deadline 'scheduled))))
(todo "TODO"
((org-agenda-overriding-header "Todo")
(org-agenda-files '("~/.org/gtd/someday.org" "~/.org/gtd/projects.org"))
(org-agenda-skip-function '(org-agenda-skip-entry-if 'deadline 'scheduled))))
nil)))
(setq org-agenda-custom-commands
`(,jethro/org-agenda-inbox-view
,jethro/org-agenda-todo-view))
(defun jethro/org-agenda-skip-all-siblings-but-first ()
"Skip all but the first non-done entry."
(let (should-skip-entry)
(unless (or (org-current-is-todo)
(not (org-get-scheduled-time (point))))
(setq should-skip-entry t))
(save-excursion
(while (and (not should-skip-entry) (org-goto-sibling t))
(when (org-current-is-todo)
(setq should-skip-entry t))))
(when should-skip-entry
(or (outline-next-heading)
(goto-char (point-max))))))
(defun org-current-is-todo ()
(string= "TODO" (org-get-todo-state)))
(defun jethro/switch-to-agenda ()
(interactive)
(org-agenda nil " ")
(delete-other-windows))
(bind-key "<f10>" 'jethro/switch-to-agenda jethro-mode-map)Column View
(setq org-columns-default-format "%40ITEM(Task) %Effort(EE){:} %CLOCKSUM(Time Spent) %SCHEDULED(Scheduled) %DEADLINE(Deadline)")Stage 4: Doing
Org-pomodoro
(use-package org-pomodoro
:bind
(:map org-agenda-mode-map
(("I" . org-pomodoro)))
:config
(setq org-pomodoro-format "%s"))Org Mode for Note taking
Deft
(use-package deft
:bind
(:map jethro-mode-map
("C-c n" . deft))
:config
(setq deft-default-extension "org")
(setq deft-directory "~/.org/deft/")
(setq deft-use-filename-as-title t))Exporting Deft Notes
(defun jethro/org-export-deft-file (file)
(interactive)
(org-html-export-to-html t t))Org Mode for Blogging
Blog Mode
(define-derived-mode blog-mode org-mode "blog")
(add-to-list 'auto-mode-alist '("\\.blog\\'" . blog-mode))
(bind-key "C-c C-c" 'jethro/org-hugo-export blog-mode-map)
(bind-key "C-c TAB" 'jethro/insert-blog-props blog-mode-map)
Helpers
(defun jethro/org-kwds ()
"parse the buffer and return a cons list of (property . value)
from lines like:
#+PROPERTY: value"
(org-element-map (org-element-parse-buffer 'element) 'keyword
(lambda (keyword) (cons (org-element-property :key keyword)
(org-element-property :value keyword)))))
(defun jethro/org-kwd (KEYWORD)
"get the value of a KEYWORD in the form of #+KEYWORD: value"
(cdr (assoc KEYWORD (jethro/org-kwds))))
(defun now-is ()
(concat (format-time-string "%Y-%m-%dT%T")
((lambda (x) (concat (substring x 0 3) ":" (substring x 3 5)))
(format-time-string "%z"))))
(defun jethro/promote-everything ()
"Promote all subtrees in buffer"
(interactive)
(save-excursion
(save-match-data
(goto-char (point-min))
(while (search-forward-regexp "^\\*+" nil t)
(delete-backward-char 1)))))Exporting
;; http://whyarethingsthewaytheyare.com/setting-up-the-blog/#workflow
(defun jethro/org-hugo-export ()
"Export current subheading to the hugo blog."
(interactive)
;; Save cursor position
(save-excursion
;; Go to top level heading for subtree (you can change the number 10
;; if you /really/ need more than 10 sublevels...)
;; (unless (eq (org-current-level) 1)
;; (outline-up-heading 10))
(let* ((hl (org-element-at-point))
(title (org-element-property :title hl))
(slug (org-element-property :SLUG hl))
(filename (concat (jethro/org-kwd "HUGO_CONTENT_ROOT")
(format "%s.org" slug)))
(date (org-element-property :DATE hl))
(tags
(format "%s"
(mapconcat 'identity (org-get-tags) " "))))
;; Make the export
(org-copy-subtree)
(with-temp-buffer (generate-new-buffer filename)
(goto-char (point-min))
(org-yank)
(goto-char (point-min))
(condition-case nil
(while (org-promote-subtree))
(error nil))
(goto-char (point-min))
(let ((end (search-forward ":END:")))
(delete-region (point-min) end))
(jethro/promote-everything)
(insert "#+TITLE: " title)
(insert "\n#+DATE: " date)
(insert "\n#+SLUG: " slug)
(insert "\n#+TAGS: " tags)
(write-file filename)))))Inserting post properties
(defun jethro/get-post-title (title)
"Get post title from TITLE"
(replace-regexp-in-string " " "-" (replace-regexp-in-string "[^a-zA-Z0-9 ]" ""
(downcase title))))
(defun jethro/insert-blog-props ()
(interactive)
(let* ((title (cdr (assoc "ITEM" (org-entry-properties))))
(slug (jethro/get-post-title title))
(date (now-is))
(str (format ":PROPERTIES:
:SLUG: %s
:DATE: %s
:END:" slug date)))
(insert str)))Exporting PDFs
I use export to LaTeX through ox-latex, using xelatex for a nicer export template.
(setq org-latex-pdf-process
'("pdflatex -shell-escape -interaction nonstopmode %f"
"pdflatex -shell-escape -interaction nonstopmode %f"))
(require 'ox-latex)
(setq org-latex-default-table-environment "tabular")
(setq org-latex-tables-booktabs t)
(setq org-latex-listings 'minted)
(setq org-format-latex-options (plist-put org-format-latex-options :scale 2.0))
(setq org-latex-classes
'(("article"
"\\documentclass{article}
\\usepackage[margin=1in]{geometry}
\\usepackage{amsmath,amsthm,amssymb}
\\newcommand{\N}{\mathbb{N}}
\\newcommand{\Z}{\mathbb{Z}}
\\usepackage{hyperref}
\\usepackage{minted}
\\usepackage{tabularx}
\\usepackage{parskip}
\\linespread{1.1}
\\renewcommand\\headrulewidth{0.4pt}
\\renewcommand\\footrulewidth{0.4pt}
\\setlength\\columnsep{10pt}
\\setlength{\\columnseprule}{1pt}"
("\\section{%s}" . "\\section*{%s}")
("\\subsection{%s}" . "\\subsection*{%s}")
("\\subsubsection{%s}" . "\\subsubsection*{%s}")
("\\paragraph{%s}" . "\\paragraph*{%s}")
("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
("book"
"\\documentclass[10pt]{memoir}
\\usepackage{charter}
\\usepackage[T1]{fontenc}
\\usepackage{booktabs}
\\usepackage{amsmath}
\\usepackage{minted}
\\usemintedstyle{borland}
\\usepackage{color}
\\usepackage{epigraph}
\\usepackage{enumitem}
\\setlist{nosep}
\\setlength\\epigraphwidth{13cm}
\\setlength\\epigraphrule{0pt}
\\usepackage{fontspec}
\\usepackage{graphicx}
\\usepackage{hyperref}
\\hypersetup {colorlinks = true, allcolors = red}
\\title{}
[NO-DEFAULT-PACKAGES]
[NO-PACKAGES]"
("\\chapter{%s}" . "\\chapter*{%s}")
("\\section{%s}" . "\\section*{%s}")
("\\subsection{%s}" . "\\subsection*{%s}")
("\\subsubsection{%s}" . "\\subsubsection*{%s}")
("\\paragraph{%s}" . "\\paragraph*{%s}")
("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
("latex-notes"
"\\documentclass[8pt]{article}
\\usepackage[margin={0.2in,0.2in}, a4paper,landscape]{geometry}
\\usepackage{hyperref}
\\usepackage{amsmath}
\\usepackage{multicol}
\\usepackage{booktabs}
\\usepackage{enumitem}
\\usepackage[compact]{titlesec}
\\titlespacing{\\section}{0pt}{*2}{*0}
\\titlespacing{\\subsection}{0pt}{*2}{*0}
\\titlespacing{\\subsubsection}{0pt}{*2}{*0}
\\titleformat*{\\section}{\\large\\bfseries}
\\titleformat*{\\subsection}{\\normalsize\\bfseries}
\\titleformat*{\\subsubsection}{\\normalsize\\bfseries}
\\setlist[itemize]{leftmargin=*}
\\setlist[enumerate]{leftmargin=*}
\\setlength\\columnsep{5pt}
\\setlength{\\columnseprule}{1pt}
\\setlength{\\parindent}{0cm}
\\setlist{nosep}
\\usepackage{minted}
\\usemintedstyle{bw}
\\usemintedstyle[java]{bw}
\\setminted[]{frame=none,fontsize=\\footnotesize,linenos=false}
"
("\\section{%s}" . "\\section*{%s}")
("\\subsection{%s}" . "\\subsection*{%s}")
("\\subsubsection{%s}" . "\\subsubsection*{%s}")
("\\paragraph{%s}" . "\\paragraph*{%s}")
("\\subparagraph{%s}" . "\\subparagraph*{%s}"))))
(defun jethro/org-multicol-to-latex (async subtreep visible-only body-only)
(let ((contents (buffer-string))
(buffer-name (file-name-sans-extension buffer-file-name)))
(with-temp-buffer
(insert "#+LATEX_CLASS: latex-notes\n")
(insert contents)
(goto-char (point-min))
(org-next-visible-heading 1)
(insert "#+BEGIN_EXPORT latex\n\\begin{multicols*}{4}\n#+END_EXPORT\n")
(goto-char (point-max))
(insert "#+BEGIN_EXPORT latex\n\\end{multicols*}\n#+END_EXPORT")
(org-export-to-file 'latex (format "%s.tex" buffer-name)
async subtreep visible-only body-only nil))))
(defun jethro/org-multicol-to-pdf (async subtreep visible-only body-only)
(let ((contents (buffer-string))
(buffer-name (file-name-sans-extension buffer-file-name)))
(with-temp-buffer
(insert "#+LATEX_CLASS: latex-notes\n")
(insert contents)
(goto-char (point-min))
(org-next-visible-heading 1)
(insert "#+BEGIN_EXPORT latex\n\\begin{multicols*}{4}\n#+END_EXPORT\n")
(goto-char (point-max))
(insert "#+BEGIN_EXPORT latex\n\\end{multicols*}\n#+END_EXPORT")
(org-export-to-file 'latex (format "%s.tex" buffer-name)
async subtreep visible-only body-only nil
(lambda (file) (org-latex-compile file))))))
(org-export-define-derived-backend 'latex-notes 'latex
:menu-entry
'(?L "Export to LaTeX notes"
((?l "Export to LaTeX" jethro/org-multicol-to-latex)
(?p "Export to PDF" jethro/org-multicol-to-pdf))))Org Mode for Reading papers
(use-package org-ref
:after org
:config
(setq org-ref-notes-directory "~/.org/papers/"
org-ref-bibliography-notes "~/.org/papers/index.org"
org-ref-default-bibliography '("~/.org/papers/index.bib")
org-ref-pdf-directory "~/.org/papers/lib/")
(setq ivy-bibtex-bibliography "~/.org/papers/index.bib" ;; where your references are stored
ivy-bibtex-library-path "~/.org/papers/lib/" ;; where your pdfs etc are stored
ivy-bibtex-notes-path "~/.org/papers/index.org" ;; where your notes are stored
bibtex-completion-bibliography "~/.org/papers/index.bib" ;; writing completion
bibtex-completion-notes-path "~/.org/papers/index.org"
bibtex-completion-library-path "~/.org/papers/lib/")
(setq bibtex-completion-pdf-symbol "⌘")
(setq bibtex-completion-notes-symbol "✎")
(setq bibtex-autokey-year-length 4
bibtex-autokey-name-year-separator "-"
bibtex-autokey-year-title-separator "-"
bibtex-autokey-titleword-separator "-"
bibtex-autokey-titlewords 2
bibtex-autokey-titlewords-stretch 1
bibtex-autokey-titleword-length 5)
(require 'org-ref-bibtex)
(key-chord-define-global "jj" 'org-ref-bibtex-hydra/body)
(require 'org-ref-url-utils)
(require 'org-ref-arxiv))Project Management
Version Control
vc
(use-package vc
:bind (:map jethro-mode-map
("C-x v =" . jethro/vc-diff)
("C-x v H" . vc-region-history)) ; New command in emacs 25.x
:config
(progn
(defun jethro/vc-diff (no-whitespace)
"Call `vc-diff' as usual if buffer is not modified.
If the buffer is modified (yet to be saved), call `diff-buffer-with-file'.
If NO-WHITESPACE is non-nil, ignore all white space when doing diff."
(interactive "P")
(let* ((no-ws-switch '("-w"))
(vc-git-diff-switches (if no-whitespace
no-ws-switch
vc-git-diff-switches))
(vc-diff-switches (if no-whitespace
no-ws-switch
vc-diff-switches))
(diff-switches (if no-whitespace
no-ws-switch
diff-switches))
;; Set `current-prefix-arg' to nil so that the HISTORIC arg
;; of `vc-diff' stays nil.
current-prefix-arg)
(if (buffer-modified-p)
(diff-buffer-with-file (current-buffer))
(call-interactively #'vc-diff))))))Smerge-mode
Useful when handling git merge conflicts.
(use-package smerge-mode
:bind (:map jethro-mode-map
("C-c h s" . jethro/hydra-smerge/body))
:init
(progn
(defun jethro/enable-smerge-maybe ()
"Auto-enable `smerge-mode' when merge conflict is detected."
(save-excursion
(goto-char (point-min))
(when (re-search-forward "^<<<<<<< " nil :noerror)
(smerge-mode 1))))
(add-hook 'find-file-hook #'jethro/enable-smerge-maybe :append))
:config
(defalias 'smerge-keep-upper 'smerge-keep-mine)
(defalias 'smerge-keep-lower 'smerge-keep-other)
(defalias 'smerge-diff-base-upper 'smerge-diff-base-mine)
(defalias 'smerge-diff-upper-lower 'smerge-diff-mine-other)
(defalias 'smerge-diff-base-lower 'smerge-diff-base-other)
(defhydra jethro/hydra-smerge (:color pink
:hint nil
:pre (smerge-mode 1)
;; Disable `smerge-mode' when quitting hydra if
;; no merge conflicts remain.
:post (smerge-auto-leave))
"
^Move^ ^Keep^ ^Diff^ ^Other^
^^-----------^^-------------------^^---------------------^^-------
_n_ext _b_ase _<_: upper/base _C_ombine
_p_rev _u_pper _=_: upper/lower _r_esolve
^^ _l_ower _>_: base/lower _k_ill current
^^ _a_ll _R_efine
^^ _RET_: current _E_diff
"
("n" smerge-next)
("p" smerge-prev)
("b" smerge-keep-base)
("u" smerge-keep-upper)
("l" smerge-keep-lower)
("a" smerge-keep-all)
("RET" smerge-keep-current)
("\C-m" smerge-keep-current)
("<" smerge-diff-base-upper)
("=" smerge-diff-upper-lower)
(">" smerge-diff-base-lower)
("R" smerge-refine)
("E" smerge-ediff)
("C" smerge-combine-with-next)
("r" smerge-resolve)
("k" smerge-kill-current)
("q" nil "cancel" :color blue)))Magit
(use-package magit
:bind (:map jethro-mode-map
("s-g" . magit-status)
("C-c g" . magit-status)
("s-G" . magit-blame)
("C-c G" . magit-blame))
:init
(add-hook 'magit-mode-hook 'hl-line-mode)
:config
(setq magit-auto-revert-mode nil))Projectile
(use-package projectile
:demand t
:init
(setq projectile-keymap-prefix (kbd "C-x p"))
(add-hook 'after-init-hook 'projectile-global-mode)
:config
(require 'projectile)
(use-package counsel-projectile
:bind (:map jethro-mode-map
("s-f" . counsel-projectile-find-file)
("s-b" . counsel-projectile-switch-to-buffer)
("C-c s" . jethro/counsel-projectile-rg))
:config
(defun jethro/counsel-projectile-rg (&optional options)
"Ivy version of `projectile-rg'."
(interactive)
(if (projectile-project-p)
(let* ((options
(if current-prefix-arg
(read-string "options: ")
options))
(ignored
(unless (eq (projectile-project-vcs) 'git)
;; rg supports git ignore files
(append
(cl-union (projectile-ignored-files-rel) grep-find-ignored-files)
(cl-union (projectile-ignored-directories-rel) grep-find-ignored-directories))))
(options
(concat options " "
(mapconcat (lambda (i)
(concat "--ignore-file " (shell-quote-argument i)))
ignored
" "))))
(counsel-rg (ivy-thing-at-point)
(projectile-project-root)
options
(projectile-prepend-project-name "rg")))
(user-error "You're not in a project")))
(counsel-projectile-on))
(setq projectile-use-git-grep t)
(setq projectile-create-missing-test-files t)
(setq projectile-completion-system 'ivy)
(setq projectile-switch-project-action
#'projectile-commander)
(def-projectile-commander-method ?S
"Run a search in the project"
(counsel-projectile-rg))
(def-projectile-commander-method ?s
"Open a *eshell* buffer for the project."
(projectile-run-eshell))
(def-projectile-commander-method ?d
"Open project root in dired."
(projectile-dired))
(def-projectile-commander-method ?g
"Show magit status."
(magit-status))
(def-projectile-commander-method ?j
"Jack-in."
(let* ((opts (projectile-current-project-files))
(file (ivy-read
"Find file: "
opts)))
(find-file (expand-file-name
file (projectile-project-root)))
(run-hooks 'projectile-find-file-hook)
(cider-jack-in))))ivy switch persp
(defun jethro/ivy-persp-switch-project (arg)
(interactive "P")
(ivy-read "Switch to Project Perspective: "
(if (projectile-project-p)
(cons (abbreviate-file-name (projectile-project-root))
(projectile-relevant-known-projects))
projectile-known-projects)
:action (lambda (project)
(let ((persp-reset-windows-on-nil-window-conf t))
(persp-switch project)
(let ((projectile-completion-system 'ivy))
(projectile-switch-project-by-name project))))))
(bind-key "C-x p p" 'jethro/ivy-persp-switch-project jethro-mode-map)Magithub (Disabled)
(use-package magithub
:after magit
:config (magithub-feature-autoinject t))Miscellaneous
SOS
Search Stack Overflow
(use-package sos
:commands (sos))which-key
(use-package which-key
:diminish which-key-mode
:init
(add-hook 'after-init-hook 'which-key-mode))Olivetti
(use-package olivetti
:bind (:map jethro-mode-map
("C-c M o" . olivetti-mode)))bury-successful-compilation
Closes compile buffer if there are no errors.
(use-package bury-successful-compilation
:init
(add-hook 'after-init-hook 'bury-successful-compilation))Load Custom File
Custom file should take precedence.
(load custom-file)