Skip to content

Latest commit

 

History

History
2547 lines (2319 loc) · 93.3 KB

michael.org

File metadata and controls

2547 lines (2319 loc) · 93.3 KB

Tasshin Fogleman’s Emacs configuration

Introduction

Quotations

On Emacs

Emacs outshines all other editing software in approximately the same way that the noonday sun does the stars. It is not just bigger and brighter; it simply makes everything else vanish.

– Neal Stephenson, ”In the Beginning was the Command Line

On 2 Apr 1992 in the journal Nature the discovery was reported that a giant ancient fungus had been living under the forests of Michigan for at least 1,500 years, the oldest and largest living thing on Earth. Software could get to be like that, I suppose, and Emacs, incorporating, like the fungal thallus, all the the filamentous strands of Emacs Lisp that now already thinly web the Earth, is surely a front runner. But do not be distracted by such lives. Even the life of Emacs, like the life of that fungus, is an ephemerality; to grok life one must transcend not only thermospace but cyberspace.

Will Mengarini

On Literate Programming

Let us change our traditional attitude to the construction of programs: Instead of imagining that our main task is to instruct a computer what to do, let us concentrate rather on explaining to human beings what we want a computer to do.

The practitioner of literate programming can be regarded as an essayist, whose main concern is with exposition and excellence of style. Such an author, with thesaurus in hand, chooses the names of variables carefully and explains what each variable means. He or she strives for a program that is comprehensible because its concepts have been introduced in an order that is best for human understanding, using a mixture of formal and informal methods that reinforce each other.

– Donald Knuth

In my experience, software created with literate programming has turned out to be significantly better than software developed in more traditional ways…

Jon Bentley probably hit the nail on the head when he once was asked why literate programming hasn’t taken the whole world by storm. He observed that a small percentage of the world’s population is good at programming, and a small percentage is good at writing; apparently I am asking everybody to be in both subsets.

Yet to me, literate programming is certainly the most important thing that came out of the TeX project. Not only has it enabled me to write and maintain programs faster and more reliably than ever before, and been one of my greatest sources of joy since the 1980s—it has actually been indispensable at times.

Donald Knuth

Settings

Personal information

(setq user-full-name "Tasshin Fogleman"
      user-mail-address "[email protected]")

Enable Package Support

I set up packages, Melpa, and use-package bright and early so that I can make use of use-package’s bind-key macro.

If I recall correctly, when I first copy this set-up into a new machine, I still have to require package, add MELPA, initialize package, and grab use-package, solarized-theme. This could be improved, but it’s not so bad.

Use-Package

Use-package is a handful of things: you can make sure a package is downloaded, efficiently configure it (e.g. after load, or as needed), bind keys in a concise way, and more.

(require 'package)
(setq package-archives
      '(("gnu" . "https://elpa.gnu.org/packages/")
        ("melpa" . "https://melpa.org/packages/")))
(setq load-prefer-newer t)
(unless (package-installed-p 'use-package)
  (package-refresh-contents)
  (package-install 'use-package))
(require 'use-package)
(setq use-package-always-ensure t)

Async

Async enables asynchronous processing in Emacs, as well as some basic implementations of asynchronous capabilities (e.g. for dired and packages and so on.

(use-package async
  :init
  (autoload 'dired-async-mode "dired-async.el" nil t)
  (dired-async-mode 1)
  (async-bytecomp-package-mode 1)
  (autoload 'dired-async-mode "dired-async.el" nil t)
  (async-bytecomp-package-mode 1)
  (dired-async-mode 1)
  (require 'smtpmail-async)
  (setq send-mail-function 'async-smtpmail-send-it))

Detection

Detect Operating System

These functions identify what operating system is hosting Emacs, which can be handy for system specific configuration. I have run Emacs on Windows, OS X, Linux, and BSD. I currently use Linux most of the time, and OS X some of the time, so my configuration is geared towards those. Still, I may want to run my configuration on Windows or BSD in the future so I include those functions.

(defun is-mac-p
    ()
  (eq system-type 'darwin))

(defun is-linux-p
    ()
  (eq system-type 'gnu/linux))

(defun is-windows-p
    ()
  (or
   (eq system-type 'ms-dos)
   (eq system-type 'windows-nt)
   (eq system-type 'cygwin)))

(defun is-bsd-p
    ()
  (eq system-type 'gnu/kfreebsd))

Detect Internet Connection

(defun internet-up-p (&optional host)
  (= 0 (call-process "ping" nil nil nil "-c" "1" "-W" "1"
                     (if host host "www.google.com"))))

Graphical Features

Theme

Load spacemacs-light automatically on startup.

(use-package spacemacs-theme
  :load-path "themes"
  :defer t
  :init
  (load-theme 'spacemacs-light t))

Font

(set-frame-font "Source Code Pro" nil t)
(set-face-attribute 'default nil :height 150)

Prettify Symbols

(global-prettify-symbols-mode +1)

Cursor

Adaptive cursor width shows width of character, e.g. TAB. Via Pragmatic Emacs.

(setq x-stretch-cursor t)

Turn off unnecessary graphical features

Tool Bar

(if (fboundp 'menu-bar-mode) (menu-bar-mode -1))
(if (fboundp 'tool-bar-mode) (tool-bar-mode -1))
(if (fboundp 'scroll-bar-mode) (scroll-bar-mode -1))

Startup Messages

(setq inhibit-startup-message t
      initial-scratch-message ""
      inhibit-startup-echo-area-message t)

Mode Line

Sebastian Wiesner inspired me to slim down my mode line. I have a very simple mode line that has the name of the file on the left and the date and time on the right. I’ve found I don’t really need the mode information very often - I usually know which modes are active; if I do need the mode information, I can access that with C-h m, describe-mode. Moreover, that means I don’t need to diminish most packages.

I commented out any variables that I eliminated from the mode-line, so that I can add them in later if I deem them useful.

(setq-default mode-line-format
              '("%e" ; print error message about full memory.
                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
                "   "
                ;; mode-line-position
                ;; (vc-mode vc-mode)
                ;; "  "
                ;; mode-line-modes
                "   "
                ;; mode-line-misc-info
                ;; battery-mode-line-string
                mode-line-end-spaces))

(setq display-time-format "%a, %b %e %R"
      battery-mode-line-format "%p%%"  ; Default: "[%b%p%%]"
      global-mode-string   (remove 'display-time-string global-mode-string)
      mode-line-end-spaces (list (propertize " "
                                             'display '(space :align-to (- right 17)))
                                 'display-time-string))
(display-time-mode 1)
(display-time-update)

Security

TLS

(setq tls-checktrust t
      gnutls-verify-error t)

Encryption

This will force Emacs to use its own internal password prompt instead of an external pin entry program.

(setenv "GPG_AGENT_INFO" nil)

Backups

I find Emacs default behavior of saving files relative to the current directory annoying, as it puts backup files everywhere. Instead, this saves backups in one directory, a backup folder within my Emacs directory.

(setq backup-directory-alist
      `(("." . ,(expand-file-name
                 (concat user-emacs-directory "backups")))))

Prompts

Yes or No

Make yes or no prompts be y or n prompts.

(fset 'yes-or-no-p 'y-or-n-p)

Buffer / File Warnings

Remove the warning if a buffer or file does not exist, so you can create them. (Source.)

(setq confirm-nonexistent-file-or-buffer nil)

(defun create-non-existent-directory ()
  "Check whether a given file's parent directories exist; if they do not, offer to create them."
  (let ((parent-directory (file-name-directory buffer-file-name)))
    (when (and (not (file-exists-p parent-directory))
               (y-or-n-p (format "Directory `%s' does not exist! Create it?" parent-directory)))
      (make-directory parent-directory t))))

(add-to-list 'find-file-not-found-functions #'create-non-existent-directory)

Other

Macros

Think about macros! Play with macros!

(setq kmacro-ring-max 30)

Ediff

The default Ediff behavior is confusing and not desirable. This fixes it.

(setq ediff-window-setup-function 'ediff-setup-windows-plain
      ediff-split-window-function 'split-window-horizontally)

Enable functionality

Some features and settings are disabled by default; this is sane behavior for new users, but it is expected that we will disable them eventually.

(put 'narrow-to-region 'disabled nil)
(put 'narrow-to-page 'disabled nil)
(put 'upcase-region 'disabled nil)
(put 'downcase-region 'disabled nil)
(put 'erase-buffer 'disabled nil)
(put 'set-goal-column 'disabled nil)

Casing

The following advice makes the upcase/downcase/capitalize-word functions more usable. Thanks, Oleh!

(defadvice upcase-word (before upcase-word-advice activate)
  (unless (looking-back "\\b" nil)
    (backward-word)))

(defadvice downcase-word (before downcase-word-advice activate)
  (unless (looking-back "\\b" nil)
    (backward-word)))

(defadvice capitalize-word (before capitalize-word-advice activate)
  (unless (looking-back "\\b" nil)
    (backward-word)))

Also, unbind downcase region, which has plagued my documents for eons.

(unbind-key "C-x C-l")

Working with the Mark

From Artur’s article, ”Faster Pop to Mark Command.”

(defadvice pop-to-mark-command (around ensure-new-position activate)
  (let ((p (point)))
    (dotimes (i 10)
      (when (= p (point)) ad-do-it))))

(setq set-mark-command-repeat-pop t)

Encoding

(prefer-coding-system 'utf-8)
(setq coding-system-for-read 'utf-8)
(setq coding-system-for-write 'utf-8)

Bell

(setq ring-bell-function 'ignore)

Key Bindings

Although keybindings are also located elsewhere, this section will aim to provide bindings that are not specific to any mode, package, or function.

Text Expansion

(bind-key "M-/" #'hippie-expand)

Lines

Enable line indenting automatically. If needed, you can disable on a mode-by-mode basis.

(bind-keys ("RET" . newline-and-indent)
           ("C-j" . newline-and-indent))

Make C-n insert new lines if the point is at the end of the buffer.

(setq next-line-add-newlines t)

Movement

These allow you to move lines and characters with an automatic prefix argument of 5, which accelerates movements. Via What the .emacs.d?.

(defun super-next-line ()
  (interactive)
  (ignore-errors (next-line 5)))

(defun super-previous-line ()
  (interactive)
  (ignore-errors (previous-line 5)))

(defun super-backward-char ()
  (interactive)
  (ignore-errors (backward-char 5)))

(defun super-forward-char ()
  (interactive)
  (ignore-errors (forward-char 5)))

(bind-keys ("C-S-n" . super-next-line)
           ("C-S-p" . super-previous-line)
           ("C-S-b" . super-backward-char)
           ("C-S-f" . super-forward-char))

Meta Binds

I make use of meta-binds to get additional, easy bindings. Prefix arguments can be invoked with control key or C-u.

(bind-keys ("M-1" . delete-other-windows)
           ("M-O" . mode-line-other-buffer))

Copying and Killing

This advises kill-region (C-w) so that, if no region is selected, it kills or copies the current line.

(defadvice kill-region (before slick-cut activate compile)
  "When called interactively with no active region, kill a single line instead."
  (interactive
   (if mark-active (list (region-beginning) (region-end))
     (list (line-beginning-position)
           (line-beginning-position 2)))))

backward-kill-line

This binding comes from Emacs Redux. Note that we don’t need a new function, just an anonymous function.

(bind-key "C-<backspace>" (lambda ()
                            (interactive)
                            (kill-line 0)
                            (indent-according-to-mode)))

Sentence and Paragraph Commands

By default, sentence-end-double-space is set to t. That convention may be programatically convenient, but that’s not how I write. I want to be able to write normal sentences, but still be able to fill normally. Let to the rescue!

(defadvice forward-sentence (around real-forward)
  "Consider a sentence to have one space at the end."
  (let ((sentence-end-double-space nil))
    ad-do-it))

(defadvice backward-sentence (around real-backward)
  "Consider a sentence to have one space at the end."
  (let ((sentence-end-double-space nil))
    ad-do-it))

(defadvice kill-sentence (around real-kill)
  "Consider a sentence to have one space at the end."
  (let ((sentence-end-double-space nil))
    ad-do-it))

(ad-activate 'forward-sentence)
(ad-activate 'backward-sentence)
(ad-activate 'kill-sentence)

A slightly less tricky matter is the default binding of backward- and forward-paragraph, which are at the inconvenient M-{ and M-}. This makes a bit more sense, no?

(bind-keys ("M-A" . backward-paragraph)
           ("M-E" . forward-paragraph)
           ("M-K" . kill-paragraph))

Cycle Spacing

(bind-key "C-x SPC" 'cycle-spacing)

Special Key Maps

Augmented by a post on Irreal. Some keys on the toggle map are elsewhere in this config.

Narrowing and Widening

Before this function, I was alternating between C-x n s (org-narrow-to-subtree) and C-x n w (widen) in Org files. I originally implemented this to toggle between those two cases as well as the region. Artur Malabarba and Sacha Chua have made successive improvements: a prefix argument to narrow no matter what, and increasing features for Org.

(defun narrow-or-widen-dwim (p)
  "If the buffer is narrowed, it widens. Otherwise, it narrows
intelligently.  Intelligently means: region, org-src-block,
org-subtree, or defun, whichever applies first.  Narrowing to
org-src-block actually calls `org-edit-src-code'.

With prefix P, don't widen, just narrow even if buffer is already
narrowed."
  (interactive "P")
  (declare (interactive-only))
  (cond ((and (buffer-narrowed-p) (not p)) (widen))
        ((and (boundp 'org-src-mode) org-src-mode (not p))
         (org-edit-src-exit))
        ((region-active-p)
         (narrow-to-region (region-beginning) (region-end)))
        ((derived-mode-p 'org-mode)
         (cond ((ignore-errors (org-edit-src-code))
                (delete-other-windows))
               ((org-at-block-p)
                (org-narrow-to-block))
               (t (org-narrow-to-subtree))))
        ((derived-mode-p 'prog-mode) (narrow-to-defun))
        (t (error "Please select a region to narrow to"))))

(eval-after-load 'org-src
  '(bind-key "C-x C-s" 'org-edit-src-exit org-src-mode-map))

Cold Folding

For code folding, which is similar to narrowing and widening but different.

(use-package hideshow
  :hook ((prog-mode . hs-minor-mode)))

(defun toggle-fold ()
  (interactive)
  (save-excursion
    (end-of-line)
    (hs-toggle-hiding)))

Toggle Read Only

A lot of modes let you change from read-only to writeable, or backwards: files, Dired, and also wgrep-enabled modes. I use ag, the silver searcher, instead of grep or ack. Anyways, this function decides which mode I am in and acts accordingly. That way, I need to remember just one key bind, C-x t r.

(defun read-write-toggle ()
  "Toggles read-only in any relevant mode: ag-mode, Dired, or
just any file at all."
  (interactive)
  (if (equal major-mode 'ag-mode)
      ;; wgrep-ag can support ag-mode
      (wgrep-change-to-wgrep-mode)
    ;; dired-toggle-read-only has its own conditional:
    ;; if the mode is Dired, it will make the directory writable
    ;; if it is not, it will just toggle read only, as desired
    (dired-toggle-read-only)))

Keymap

(bind-keys :prefix-map toggle-map
           :prefix "C-x t"
           ("d" . toggle-debug-on-error)
           ("f" . toggle-fold)
           ("l" . display-line-numbers-mode)
           ("n" . narrow-or-widen-dwim)
           ("o" . org-mode)
           ("r" . read-write-toggle)
           ("t" . text-mode)
           ("w" . whitespace-mode))

Scratch

(defun scratch ()
    (interactive)
    (switch-to-buffer-other-window (get-buffer-create "*scratch*")))

The default scratch buffer in Emacs uses lisp-interaction-mode, which is great, but it’s useful to have one that uses Org-Mode.

(defun make-org-scratch ()
  (interactive)
  (find-file "~/org/scratch.org"))

Keymap

(bind-keys :prefix-map launcher-map
           :prefix "C-x l"
           ("A" . ansi-term) ;; save "a" for open-agenda
           ("c" . calc)
           ("C" . calendar)
           ("d" . ediff-buffers)
           ("e" . eshell)
           ("E" . eww)
           ("h" . man)
           ("l" . package-list-packages)
           ("P" . proced)
           ("s" . scratch)
           ("S" . make-org-scratch))

(when (is-linux-p)
  (bind-keys :map launcher-map
             ("." . counsel-linux-app)))

(when (is-mac-p)
  (use-package counsel-osx-app
    :bind (:map launcher-map
                ("." . counsel-osx-app))))

(use-package counsel-osx-app)

Macro Map

(bind-keys :prefix-map macro-map
           :prefix "C-c m"
           ("a" . kmacro-add-counter)
           ("b" . kmacro-bind-to-key)
           ("e" . kmacro-edit-macro)
           ("i" . kmacro-insert-counter)
           ("I" . insert-kbd-macro)
           ("K" . kmacro-end-or-call-macro-repeat)
           ("n" . kmacro-cycle-ring-next)
           ("N" . kmacro-name-last-macro)
           ("p" . kmacro-cycle-ring-previous)
           ("r" . apply-macro-to-region-lines)
           ("c" . kmacro-set-counter)
           ("s" . kmacro-start-macro)
           ("t" . kmacro-end-or-call-macro))

System

All of my packages for interacting with my computer.

OS

OS X

Key Bindings

(when (is-mac-p)
  (setq mac-command-modifier 'meta
        mac-option-modifier 'super
        mac-control-modifier 'control
        ns-function-modifier 'hyper))

Face Attributes

(when (is-mac-p)
  (set-face-attribute 'default nil :height 165))

Shells

Over time, I’ve come to prefer Eshell over shell mode or Ansi-term. The main features I tend to use are setting aliases, executing e-Lisp, and writing command output to a buffer.

Eshell

(use-package eshell
  :bind (("<f1>" . eshell))
  :hook ((eshell-mode . with-editor-export-editor)
         (eshell-mode . setup-company-eshell-autosuggest))
  :init
  (setq eshell-banner-message "")

  (defun new-eshell ()
    (interactive)
    (eshell 'true))

  (use-package esh-autosuggest
    :hook (eshell-mode . esh-autosuggest-mode)))

Shell Mode

(use-package shell
  :bind (:map shell-mode-map
              ("<s-up>" . comint-previous-input)
              ("<s-down>" . comint-next-input))
  :init
  (dirtrack-mode)
  (setq explicit-shell-file-name (cond ((is-linux-p) "/bin/bash")
                                       ((is-mac-p) "/usr/bin/bash")))
  ;; (when (is-mac-p)
  ;;   (use-package exec-path-from-shell
  ;;     :init
  ;;     (exec-path-from-shell-initialize)))
  )

(add-hook 'after-save-hook
          'executable-make-buffer-file-executable-if-script-p)

Directories and Files (Dired)

For me, Dired is one of Emacs’ (less-heralded) killer apps, along with Org-Mode and Magit.

(use-package dired
  :ensure f
  :bind (("<f2>" . dired)
         ("C-x C-d" . dired)
         :map dired-mode-map
         ("C-x o" . ace-window)
         ("<return>" . dired-find-alternate-file)
         ("'" . wdired-change-to-wdired-mode)
         ("s-/" . dired-filter-mode))
  :init
  (when (is-mac-p)
    (setq insert-directory-program "/opt/homebrew/opt/coreutils/libexec/gnubin/ls"))
  :config
  (bind-key "^" (lambda () (interactive) (find-alternate-file "..")) dired-mode-map)
  (put 'dired-find-alternate-file 'disabled nil)
  ;; (add-hook 'dired-mode-hook #'dired-omit-mode)
  (setq dired-dwim-target t
        dired-recursive-deletes 'always
        dired-recursive-copies 'always
        dired-isearch-filenames t
        dired-listing-switches "-alh"
        ;; dired-omit-files-p t
        ;; dired-omit-files "\\|^.DS_STORE$\\|^.projectile$"
        )
  ;; (use-package dired+
  ;;   :init
  ;;   (setq diredp-hide-details-initially-flag t))
  ;; also automatically calls dired-x, enabling dired-jump, C-x C-j
  (use-package dired-details
    :disabled t
    :init
    (dired-details-install))
  (use-package dired-filter)
  (use-package dired-subtree
    :init
    (unbind-key "M-O" dired-mode-map) ;; to support mode-line-other-buffer in Dired
    (bind-keys :map dired-mode-map
               :prefix "C-,"
               :prefix-map dired-subtree-map
               :prefix-docstring "Dired subtree map."
               ("C-i" . dired-subtree-insert)
               ("i" . dired-subtree-insert)
               ("C-/" . dired-subtree-apply-filter)
               (";" . dired-subtree-remove)
               ("C-k" . dired-subtree-remove)
               ("C-n" . dired-subtree-next-sibling)
               ("C-p" . dired-subtree-previous-sibling)
               ("C-u" . dired-subtree-up)
               ("C-d" . dired-subtree-down)
               ("C-a" . dired-subtree-beginning)
               ("C-e" . dired-subtree-end)
               ("m" . dired-subtree-mark-subtree)
               ("u" . dired-subtree-unmark-subtree)
               ("C-o C-f" . dired-subtree-only-this-file)
               ("C-o C-d" . dired-subtree-only-this-directory)))
  (use-package dired-quick-sort
    :init
    (dired-quick-sort-setup))
  (use-package dired-collapse
    :hook dired-mode))

Some of these suggestions are adapted from Xah Lee’s article on Dired. dired-find-alternate-file, which is bound to a, is disabled by default. <return> was previously dired-advertised-find-file, and ^ was previously dired-up-directory. Relatedly, I re-bind ‘q’ to my kill-this-buffer function below.

Dired-details lets me show or hide the details with ) and (, respectively. If, for some reason, it becomes hard to remember this, dired-details+ makes the parentheses interchangeable.

Searching (rg)

(use-package deadgrep
  :bind (("C-c d" . deadgrep)
         ("C-c D" . counsel-rg)
         (:map deadgrep-mode-map
               ("q" . kill-this-buffer))))

Emacs

These are helper packages that make Emacs even more awesome.

Hydra

(use-package hydra
  :config
  (setq hydra-lv nil))

Zooming

(defhydra hydra-zoom ()
  "zoom"
  ("+" text-scale-increase "in")
  ("=" text-scale-increase "in")
  ("-" text-scale-decrease "out")
  ("_" text-scale-decrease "out")
  ("0" (text-scale-adjust 0) "reset")
  ("q" nil "quit" :color blue))

(bind-keys ("C-x C-0" . hydra-zoom/body)
           ("C-x C-=" . hydra-zoom/body)
           ("C-x C--" . hydra-zoom/body)
           ("C-x C-+" . hydra-zoom/body))

Window Management

Adapted from Sacha’s config and a reddit comment.

(defun vsplit-last-buffer ()
  (interactive)
  (split-window-vertically)
  (other-window 1 nil)
  (switch-to-next-buffer))

(defun hsplit-last-buffer ()
  (interactive)
  (split-window-horizontally)
  (other-window 1 nil)
  (switch-to-next-buffer))

(bind-key "C-x 2" 'vsplit-last-buffer)
(bind-key "C-x 3" 'hsplit-last-buffer)

Zoom Mode

This replaces Golden Ratio mode.

(use-package zoom
  :init
  (setq zoom-mode t
        zoom-size '(0.618 . 0.618)))

Completion (Ivy, Counsel, and Swiper; IDO and Smex)

I prefer Ivy, Counsel, and Swiper, but I keep IDO around for Smex (in particular smex-major-mode-commands).

Counsel

(use-package counsel
    :bind (("C-x C-f" . counsel-find-file)
           ("C-x C-m" . counsel-M-x)
           ("C-x C-f" . counsel-find-file)
           ("C-h f" . counsel-describe-function)
           ("C-h v" . counsel-describe-variable)
           ("M-i" . counsel-imenu)
           ("M-I" . counsel-imenu)
           ("C-c i" . counsel-unicode-char)
           :map read-expression-map
           ("C-r" . counsel-expression-history)))

Recent Files

Recent files keeps track of which files you’re using; I access it by rebinding find-file-read-only with counsel-recentf.

(use-package recentf
  :bind ("C-x C-r" . counsel-recentf)
  :init
  (recentf-mode t)
  (setq recentf-max-saved-items 100))

Ivy and Swiper

(use-package swiper
  :bind (("C-s" . swiper)
         ("C-r" . swiper)
         ("C-c C-r" . ivy-resume)
         :map ivy-minibuffer-map
         ("C-SPC" . ivy-restrict-to-matches))
  :init
  (ivy-mode 1)
  :config
  (setq ivy-count-format "(%d/%d) "
        ivy-display-style 'fancy
        ivy-height 4
        ivy-use-virtual-buffers t
        ivy-initial-inputs-alist () ;; http://irreal.org/blog/?p=6512
        enable-recursive-minibuffers t))

(use-package all-the-icons)

(use-package ivy-rich
  :after ivy
  :config
  ;; All the icon support to ivy-rich
  (defun ivy-rich-switch-buffer-icon (candidate)
    (with-current-buffer
        (get-buffer candidate)
      (all-the-icons-icon-for-mode major-mode)))

  (setq ivy-rich--display-transformers-list
        '(ivy-switch-buffer
          (:columns
           ((ivy-rich-switch-buffer-icon (:width 2))
            (ivy-rich-candidate (:width 30))
            (ivy-rich-switch-buffer-size (:width 7))
            (ivy-rich-switch-buffer-indicators (:width 4 :face error :align right))
            (ivy-rich-switch-buffer-major-mode (:width 12 :face warning))
            (ivy-rich-switch-buffer-project (:width 15 :face success))
            (ivy-rich-switch-buffer-path (:width (lambda (x) (ivy-rich-switch-buffer-shorten-path x (ivy-rich-minibuffer-width 0.3))))))
           :predicate
           (lambda (cand) (get-buffer cand)))))

  ;; Add custom icons for various modes that can break ivy-rich
  (add-to-list 'all-the-icons-mode-icon-alist '(dashboard-mode all-the-icons-fileicon "elisp" :height 1.0 :v-adjust -0.2 :face all-the-icons-dsilver))
  (add-to-list 'all-the-icons-mode-icon-alist '(ess-mode all-the-icons-fileicon "R" :face all-the-icons-lblue))

  (ivy-rich-mode 1))

IDO

(setq ido-enable-flex-matching t
      ido-everywhere t
      ido-use-faces t ;; disable ido faces to see flx highlights.
      ido-create-new-buffer 'always
      ;; suppress  "reference to free variable problems"
      ido-cur-item nil
      ido-context-switch-command nil
      ido-cur-list nil
      ido-default-item nil)

(use-package ido-vertical-mode
  :init
  (ido-vertical-mode)
  (setq ido-vertical-define-keys 'C-n-and-C-p-only))

(use-package flx-ido
  :init
  (setq flx-ido-threshold 1000)
  (flx-ido-mode 1))

Smex

Smex (Smart M-X) implements IDO functionality for the M-X window.

(use-package smex
  :bind (("C-x M-m" . smex-major-mode-commands)
         ("M-x" . smex-major-mode-commands)
         ("C-c C-c M-x" . execute-extended-command))
  :init
  (smex-initialize))

Company Mode

Configurations via Using digits to select company-mode candidates.

(use-package company
  :bind (("C-." . company-complete)
         :map company-active-map
         ("C-n" . company-select-next)
         ("C-p" . company-select-previous)
         ("C-d" . company-show-doc-buffer)
         ("<tab>" . company-complete))
  :init
  (global-company-mode 1)
  :config
  (setq company-show-numbers t
        company-tooltip-align-annotations t)

  (let ((map company-active-map))
    (mapc
     (lambda (x)
       (define-key map (format "%d" x) 'ora-company-number))
     (number-sequence 0 9))
    (define-key map " " (lambda ()
                          (interactive)
                          (company-abort)
                          (self-insert-command 1)))
    (define-key map (kbd "<return>") nil))

  (defun ora-company-number ()
    "Forward to `company-complete-number'.

Unless the number is potentially part of the candidate.
In that case, insert the number."
    (interactive)
    (let* ((k (this-command-keys))
           (re (concat "^" company-prefix k)))
      (if (cl-find-if (lambda (s) (string-match re s))
                      company-candidates)
          (self-insert-command 1)
        (company-complete-number (string-to-number k))))))

Jumping (Avy, Ace-Window and Friends)

Jump to Characters and Words

(use-package avy
  :bind ("M-SPC" . avy-goto-char)
  :config
  (setq avy-background t
        avy-keys '(?a ?o ?e ?u ?i ?d ?h ?t ?n ?s)))

Jump to Windows

(use-package ace-window
  :bind (("C-x o" . ace-window)
         ("M-2" . ace-window))
  :init
  (setq aw-background nil
        aw-keys '(?a ?o ?e ?u ?i ?d ?h ?t ?n ?s)))

Zapping

Generic Zapping

(autoload 'zap-up-to-char "misc"
  "Kill up to, but not including ARGth occurrence of CHAR.")
(bind-key "M-Z" 'zap-up-to-char)

Jump to Zap

(use-package avy-zap)

Jump to Links

(use-package ace-link
    :init
    (ace-link-setup-default))

Jumping Keymap

(bind-keys :prefix-map avy-map
           :prefix "C-c j"
           ("c" . avy-goto-char)
           ("l" . avy-goto-line)
           ("w" . avy-goto-word-or-subword-1)
           ("W" . ace-window)
           ("z" . avy-zap-to-char)
           ("Z" . avy-zap-up-to-char))

Selection (Expand Region)

Configured like Magnars in Emacs Rocks, Episode 09.

(use-package expand-region
  :bind (("C-@" . er/expand-region)
         ("C-=" . er/expand-region)
         ("M-3" . er/expand-region)))

(pending-delete-mode t)

Deletion

Kill Ring

The word “kill” might be antiquated, idiosyncratic jargon, but it’s great that Emacs keeps track of what’s been killed. The package “Browse Kill Ring” is crucial to making that functionality visible and usable.

(use-package browse-kill-ring
  :bind ("C-x C-y" . browse-kill-ring)
  :config
  (setq browse-kill-ring-quit-action 'kill-and-delete-window))

(setq save-interprogram-paste-before-kill t)

easy-kill

(use-package easy-kill
  :bind ("M-w" . easy-kill))

Hungry Delete Mode

I learned about this package via Endless Parentheses. This is the first Emacs package that I contributed to!

(use-package hungry-delete
  :init
  (global-hungry-delete-mode))

Files

Automatic Saving

This snippet automatically saves buffers in an intelligent way. It was originally mentioned in a post by Bozhidar Batsov; the version below is adapted from his Prelude distribution and his post on focus hooks in Emacs 24.4.

(defun auto-save-command ()
  (let* ((basic (and buffer-file-name
                     (buffer-modified-p (current-buffer))
                     (file-writable-p buffer-file-name)
                     (not org-src-mode)))
         (proj (and (projectile-project-p)
                    basic)))
    (if proj
        (projectile-save-project-buffers)
      (when basic
        (save-buffer)))))

(defmacro advise-commands (advice-name commands class &rest body)
  "Apply advice named ADVICE-NAME to multiple COMMANDS.
The body of the advice is in BODY."
  `(progn
     ,@(mapcar (lambda (command)
                 `(defadvice ,command (,class ,(intern (concat (symbol-name command) "-" advice-name)) activate)
                    ,@body))
               commands)))

(advise-commands "auto-save"
                 (ido-switch-buffer ace-window magit-status windmove-up windmove-down windmove-left windmove-right mode-line-other-buffer)
                 before
                 (auto-save-command))

(add-hook 'mouse-leave-buffer-hook 'auto-save-command)
(add-hook 'focus-out-hook 'auto-save-command)

(bind-key "C-x C-s" 'save-buffer)

This tweak saves autosave files in one location, rather than in the same directory as the file that is being edited.

(defvar backup-dir (expand-file-name "~/.emacs.d/emacs_backup/"))
(defvar autosave-dir (expand-file-name "~/.emacs.d/autosave/"))
(setq backup-directory-alist (list (cons ".*" backup-dir))
      auto-save-list-file-prefix autosave-dir
      auto-save-file-name-transforms `((".*" ,autosave-dir t))
      tramp-backup-directory-alist backup-directory-alist
      tramp-auto-save-directory autosave-dir)

Auto Revert Mode

Revert buffers automatically when underlying files are changed externally.

(global-auto-revert-mode t)
(setq global-auto-revert-non-file-buffers t
      auto-revert-verbose nil)

Save Place

I learned about save place from Magnars; if you close a buffer, it remembers where you were in the file, so that when you re-open that file the buffer goes straight to that place. The configuration of this mode is very simple as of Emacs 25.1.

(setq save-place-file (expand-file-name ".places" user-emacs-directory))

(save-place-mode)

Regular Expressions

Regexes are great. Not everyone knows them, and most user interfaces don’t expose them, but I think most people who use computers could use them. Luckily, Emacs is great about this. It’s easier to use them if you have good tools for noticing if your regular expressions match input.

Build Regexes

(use-package re-builder
  :bind (("C-c R" . re-builder))
  :config
  (setq reb-re-syntax 'string))

Replace Strings with Regexes

(use-package visual-regexp
    :bind (("M-5" . vr/replace)
           ("M-%" . vr/query-replace)))

Multiple Occurrences

Moving Between Multiple Occurences (Highlight-Symbol)

I used to use Smart Scan (see Mickey’s explanation and the readme) but have since replaced Smart Scan with highlight-symbol and related functions from Wilfred’s config.

(use-package highlight-symbol
  :bind (("M-p" . highlight-symbol-prev)
         ("M-n" . highlight-symbol-next)
         ("M-'" . highlight-symbol-query-replace))
  :init
  (defun highlight-symbol-first ()
    "Jump to the first location of symbol at point."
    (interactive)
    (push-mark)
    (eval
     `(progn
        (goto-char (point-min))
        (search-forward-regexp
         (rx symbol-start ,(thing-at-point 'symbol) symbol-end)
         nil t)
        (beginning-of-thing 'symbol))))

  (defun highlight-symbol-last ()
    "Jump to the last location of symbol at point."
    (interactive)
    (push-mark)
    (eval
     `(progn
        (goto-char (point-max))
        (search-backward-regexp
         (rx symbol-start ,(thing-at-point 'symbol) symbol-end)
         nil t))))

  (bind-keys ("M-P" . highlight-symbol-first)
             ("M-N" . highlight-symbol-last)))

Editing Multiple Occurences (Iedit)

Iedit is probably the best tool I’ve found for editing multiple occurences of the same symbol or string.

(use-package iedit
  :bind ("C-;" . iedit-mode))

Edit List

Edit List lets you edit a list variable as if it were a buffer.

(use-package edit-list)

Discoverability

which-key

(use-package which-key
  :init
  (which-key-mode))

discover-my-major

(use-package discover-my-major
  :bind ("C-h C-m" . discover-my-major))

Interaction Log

Interaction Log is like view-lossage (C-h l) or kmacro-edit-macro but it is live-updating and not tied to macros. It’s useful for when you type an (awesome? terrible?) Emacs command and want to figure out which function you used so you can use it again or destroy it forever. For a long time I was plagued by accidentally hitting downcase-region and didn’t know what the function was - this would have been so useful!

(use-package interaction-log)

(interaction-log-mode +1)

(defun open-interaction-log ()
  (interactive)
  (display-buffer ilog-buffer-name))

(bind-key "C-h C-l" 'open-interaction-log)

Goto-chg

Adapted from the article Move Through Edit Points. This works like the mark, except it cycles through edit points. It takes you through your undo history without actually undoing anything.

“C-u 0 C-c ,” will give a description of changes made.

(use-package goto-chg
  :bind (("C-c ," . goto-last-change)
         ("C-c ." . goto-last-change-reverse)))

Selected

(use-package selected
  :commands selected-minor-mode
  :init
  (setq selected-org-mode-map (make-sparse-keymap))
  (selected-global-mode 1)
  :bind (:map selected-keymap
              ("e" . er/expand-region)
              ("i" . indent-region)
              ("l" . downcase-region)
              ("m" . apply-macro-to-region-lines)
              ("q" . selected-off)
              ("r" . reverse-region)
              ("s" . sort-lines)
              ("u" . upcase-region)
              ("w" . count-words-region)
              ("y" . yank)
              :map selected-org-mode-map
              ("t" . org-table-convert-region)))

Beginend

(use-package beginend
  :init
  (beginend-global-mode))

Actionable URL’s

Actionable URLs in Emacs buffers via Álvaro Ramírez.

(use-package goto-addr
  :hook ((compilation-mode . goto-address-mode)
         (prog-mode . goto-address-prog-mode)
         (eshell-mode . goto-address-mode)
         (shell-mode . goto-address-mode))
  :bind (:map goto-address-highlight-keymap
              ("C-c C-o" . goto-address-at-point))
  :commands (goto-address-prog-mode
             goto-address-mode))

Emojis

(use-package emojify
  :init (global-emojify-mode))

Browsing

External Browsers

(setq browse-url-browser-function (cond ((is-mac-p) 'browse-url-default-macosx-browser)
                                        ((is-linux-p) 'browse-url-default-browser)))

(bind-key "C-c B" 'browse-url-at-point)

EWW

This package brings one neat feature of Conkeror to eww.

(use-package eww-lnum
  :after eww
  :bind (:map eww-mode-map
              ("f" . eww-lnum-follow)
              ("F" . eww-lnum-universal)))

Programming and Development

Here are language-specific (largely Lisps) or development-related packages.

Programming Languages

Clojure

Clojure Mode

(use-package clojure-mode
  :mode (("\\.boot$"  . clojure-mode)
         ("\\.clj$"   . clojure-mode)
         ("\\.cljs$"  . clojurescript-mode)
         ("\\.edn$"   . clojure-mode))
  :config
  (use-package align-cljlet
    :bind (:map clojure-mode-map
                ("C-! a a" . align-cljlet)
                :map clojurescript-mode-map
                ("C-! a a" . align-cljlet)
                :map clojurec-mode-map
                ("C-! a a" . align-cljlet))))

Clojure Refactor

(use-package clj-refactor
  :disabled
  :init
  (defun my-clj-refactor-mode-hook ()
    (clj-refactor-mode 1)
    (yas-minor-mode 1))
  (add-hook 'clojure-mode-hook #'my-clj-refactor-mode-hook)
  (setq cljr-clojure-test-declaration "[clojure.test :refer :all]"
        cljr-cljs-clojure-test-declaration "[cljs.test :refer-macros [deftest is use-fixtures]]")
  :config
  (cljr-add-keybindings-with-prefix "<menu>")

  (add-to-list 'cljr-magic-require-namespaces
               '("s"  . "clojure.spec.alpha"))

  (add-to-list 'cljr-magic-require-namespaces
               '("S"  . "com.rpl.specter"))

  (advice-add 'cljr-add-require-to-ns :after
              (lambda (&rest _)
                (yas-next-field)
                (yas-next-field))))

Configurations from Endless Parentheses (clj-refactor: Unleash your Clojure wizard and A small improvement to clj-refactor).

CIDER

The function “cider-interactive-eval” comes from A CIDER Excursion.

(use-package cider
  :bind (:map cider-repl-mode-map
              ("M-r" . cider-refresh)
              ("M-R" . cider-use-repl-tools))
  :config
  (setq nrepl-hide-special-buffers t
        nrepl-popup-stacktraces-in-repl t
        nrepl-history-file "~/.emacs.d/nrepl-history"
        cider-mode-line " CIDER"
        cider-repl-display-in-current-window t
        cider-auto-select-error-buffer nil
        cider-repl-pop-to-buffer-on-connect nil
        cider-show-error-buffer nil
        cider-repl-use-pretty-printing t
        cider-cljs-lein-repl "(do (use 'figwheel-sidecar.repl-api) (start-figwheel!) (cljs-repl))")

  (defun cider-use-repl-tools ()
    (interactive)
    (cider-interactive-eval
     "(use 'clojure.repl)"))

  (fset 'cider-eval-last-sexp-and-comment
        "\C-u\C-x\C-e\C-a\260 ;; \C-e")

  (bind-key "C-j" 'cider-eval-last-sexp-and-comment clojure-mode-map)

  ;; this snippet comes from schmir https://github.com/schmir/.emacs.d/blob/master/lisp/setup-clojure.el
  (defadvice cider-load-buffer (after switch-namespace activate compile)
    "switch to namespace"
    (cider-repl-set-ns (cider-current-ns))
    (cider-switch-to-repl-buffer))

  ;; fix cond indenting
  (put 'cond 'clojure-backtracking-indent '(2 4 2 4 2 4 2 4 2 4 2 4 2 4 2 4 2 4 2 4 2 4 2 4 2 4 2 4 2 4)))

Emacs Lisp

Elisp-Slime-Nav

(use-package elisp-slime-nav
  :init
  (dolist (hook '(emacs-lisp-mode-hook ielm-mode-hook))
    (add-hook hook 'elisp-slime-nav-mode)))

Eldoc

When in emacs-lisp-mode, display the argument list for the current function. I liked this functionality in SLIME; glad Emacs has it too. Thanks for the tip and code, Sacha.

(autoload 'turn-on-eldoc-mode "eldoc" nil t)
(add-hook 'emacs-lisp-mode-hook 'eldoc-mode)
(add-hook 'lisp-interaction-mode-hook 'eldoc-mode)
(add-hook 'ielm-mode-hook 'eldoc-mode)
(add-hook 'cider-mode-hook 'eldoc-mode)

HTML

;  (bind-key "C-c C-l" 'html-href-anchor html-mode-map)

Development Tools

Version Control (Git)

This code from Magnars opens magit-status in one frame, and then restores the old window configuration when you quit.

Magit

(use-package magit
  :bind (("C-x g" . magit-status)
         ("C-c g" . magit-status)
         :map magit-status-mode-map
         ("TAB" . magit-section-toggle)
         ("<C-tab>" . magit-section-cycle)
         :map magit-branch-section-map
         ("RET" . magit-checkout))
  :config
  (add-hook 'after-save-hook 'magit-after-save-refresh-status)
  (setq magit-use-overlays nil
        magit-section-visibility-indicator nil
        magit-completing-read-function 'ivy-completing-read
        magit-push-always-verify nil
        magit-repository-directories '("~/src/"))
  (use-package git-timemachine
    :bind (("C-x v t" . git-timemachine)))
  (use-package git-link
    :bind (("C-x v L" . git-link))
    :init
    (setq git-link-open-in-browser t))
  (use-package pcmpl-git)
  (defun visit-pull-request-url ()
    "Visit the current branch's PR on Github."
    (interactive)
    (browse-url
     (format "https://github.com/%s/pull/new/%s"
             (replace-regexp-in-string
              "\\`.+github\\.com:\\(.+\\)\\.git\\'" "\\1"
              (magit-get "remote"
                         (magit-get-remote)
                         "url"))
             (cdr (magit-get-remote-branch)))))

  (bind-key "v" 'visit-pull-request-url magit-mode-map)

  ;; Do Not Show Recent Commits in status window
  ;; https://github.com/magit/magit/issues/3230#issuecomment-339900039
  (magit-add-section-hook 'magit-status-sections-hook
                          'magit-insert-unpushed-to-upstream
                          'magit-insert-unpushed-to-upstream-or-recent
                          'replace))

Git Auto Commit Mode

(use-package git-auto-commit-mode
  :delight)

Project Management (Projectile)

Projectile configuration adapted from Improving Projectile with extra commands on Endless Parentheses.

(use-package projectile
  :bind ("C-c p" . projectile-switch-project)
  :init
  (projectile-global-mode)
  (use-package ibuffer-projectile
    :bind (("C-x C-b" . ibuffer)
           :map ibuffer-mode-map
           ("c" . clean-buffer-list)
           ("n" . ibuffer-forward-filter-group)
           ("p" . ibuffer-backward-filter-group))
    :init
    (add-hook 'ibuffer-hook
              (lambda ()
                (ibuffer-projectile-set-filter-groups)
                (unless (eq ibuffer-sorting-mode 'alphabetic)
                  (ibuffer-do-sort-by-alphabetic)))))
  :config
  (setq projectile-enable-caching t
        projectile-create-missing-test-files t
        projectile-completion-system 'ivy
        projectile-use-git-grep t
        projectile-switch-project-action #'projectile-commander
        ;; I'm redefining a lot of bindings, so unset pre-defined methods
        ;; and define everyting here.
        projectile-commander-methods nil)


  (def-projectile-commander-method ?? "Commander help buffer."
    (ignore-errors (kill-buffer projectile-commander-help-buffer))
    (with-current-buffer (get-buffer-create projectile-commander-help-buffer)
      (insert "Projectile Commander Methods:\n\n")
      (dolist (met projectile-commander-methods)
        (insert (format "%c:\t%s\n" (car met) (cadr met))))
      (goto-char (point-min))
      (help-mode)
      (display-buffer (current-buffer) t))
    (projectile-commander))
  (def-projectile-commander-method ?a
    "Run ag on project."
    (counsel-projectile-ag))
  (def-projectile-commander-method ?b
    "Open an IBuffer window showing all buffers in the current project."
    (counsel-projectile-switch-to-buffer))
  (def-projectile-commander-method ?B
    "Display a project buffer in other window."
    (projectile-display-buffer))
  (def-projectile-commander-method ?c
    "Run `compile' in the project."
    (projectile-compile-project nil))
  (def-projectile-commander-method ?d
    "Open project root in dired."
    (projectile-dired))
  (def-projectile-commander-method ?D
    "Find a project directory in other window."
    (projectile-find-dir-other-window))
  (def-projectile-commander-method ?e
    "Open an eshell buffer for the project."
    ;; This requires a snapshot version of Projectile.
    (projectile-run-eshell))
  (def-projectile-commander-method ?f
    "Find a project directory in other window."
    (projectile-find-file))
  (def-projectile-commander-method ?F
    "Find project file in other window."
    (projectile-find-file-other-window))
  (def-projectile-commander-method ?g
    "Open project root in vc-dir or magit."
    (projectile-vc))
  (def-projectile-commander-method ?G
    "Run grep on project."
    (projectile-grep))
  (def-projectile-commander-method ?i
    "Open an IBuffer window showing all buffers in the current project."
    (projectile-ibuffer))
  (def-projectile-commander-method ?j
    "Jack in to CLJ or CLJS depending on context."
    (let* ((opts (projectile-current-project-files))
           (file (ido-completing-read
                  "Find file: "
                  opts
                  nil nil nil nil
                  (car (cl-member-if
                        (lambda (f)
                          (string-match "core\\.clj\\'" f))
                        opts)))))
      (find-file (expand-file-name
                  file (projectile-project-root)))
      (run-hooks 'projectile-find-file-hook)
      (if (derived-mode-p 'clojurescript-mode)
          (cider-jack-in-clojurescript)
        (cider-jack-in))))
  (def-projectile-commander-method ?r
    "Find recently visited file in project."
    (projectile-recentf))
  (def-projectile-commander-method ?s
    "Switch project."
    (counsel-projectile-switch-project))
  (def-projectile-commander-method ?t
    "Find test file in project."
    (projectile-find-test-file))
  (def-projectile-commander-method ?\C-?
    "Go back to project selection."
    (projectile-switch-project)))

Style Checking (Checkdoc with Flycheck)

Flycheck presents a handsome and usable interface for checkdoc, amongst other things.

(use-package flycheck
  :init
  (use-package flycheck-clojure)
  (global-flycheck-mode)
  (setq flycheck-indication-mode 'right-fringe))

REST Exploring (Restclient)

See Magnars’ tutorial on Emacs Rocks.

(use-package restclient)

Developing in Emacs

Whitespace

(add-hook 'before-save-hook 'whitespace-cleanup)

Managing Parentheses (Smartparens)

(use-package smartparens
  :bind
  (("C-M-f" . sp-forward-sexp)
   ("C-M-b" . sp-backward-sexp)
   ("C-M-d" . sp-down-sexp)
   ("C-M-a" . sp-backward-down-sexp)
   ("C-S-a" . sp-beginning-of-sexp)
   ("C-S-d" . sp-end-of-sexp)
   ("C-M-e" . sp-up-sexp)
   ("C-M-u" . sp-backward-up-sexp)
   ("C-M-t" . sp-transpose-sexp)
   ("C-M-n" . sp-next-sexp)
   ("C-M-p" . sp-previous-sexp)
   ("C-M-k" . sp-kill-sexp)
   ("C-M-w" . sp-copy-sexp)
   ("M-<delete>" . sp-unwrap-sexp)
   ("M-S-<backspace>" . sp-backward-unwrap-sexp)
   ("C-<right>" . sp-forward-slurp-sexp)
   ("C-<left>" . sp-forward-barf-sexp)
   ("C-M-<left>" . sp-backward-slurp-sexp)
   ("C-M-<right>" . sp-backward-barf-sexp)
   ("M-D" . sp-splice-sexp)
   ("C-M-<delete>" . sp-splice-sexp-killing-forward)
   ("C-M-<backspace>" . sp-splice-sexp-killing-backward)
   ("C-M-S-<backspace>" . sp-splice-sexp-killing-around)
   ("C-]" . sp-select-next-thing-exchange)
   ("C-<left_bracket>" . sp-select-previous-thing)
   ("C-M-]" . sp-select-next-thing)
   ("M-F" . sp-forward-symbol)
   ("M-B" . sp-backward-symbol)
   ("H-t" . sp-prefix-tag-object)
   ("H-p" . sp-prefix-pair-object)
   ("H-s c" . sp-convolute-sexp)
   ("H-s a" . sp-absorb-sexp)
   ("H-s e" . sp-emit-sexp)
   ("H-s p" . sp-add-to-previous-sexp)
   ("H-s n" . sp-add-to-next-sexp)
   ("H-s j" . sp-join-sexp)
   ("H-s s" . sp-split-sexp)
   ("M-9" . sp-backward-sexp)
   ("M-0" . sp-forward-sexp))
  :init
  (smartparens-global-mode t)
  (show-smartparens-global-mode t)
  (use-package smartparens-config
    :ensure f)
  (bind-key "s" 'smartparens-mode toggle-map)
  (when (is-mac-p)
    (bind-keys ("<s-right>" . sp-forward-slurp-sexp)
               ("<s-left>" . sp-forward-barf-sexp)))
  (sp-with-modes '(markdown-mode gfm-mode)
    (sp-local-pair "*" "*"))
  (sp-with-modes '(org-mode)
    (sp-local-pair "=" "=")
    (sp-local-pair "*" "*")
    (sp-local-pair "/" "/")
    (sp-local-pair "_" "_")
    (sp-local-pair "+" "+")
    (sp-local-pair "<" ">")
    (sp-local-pair "[" "]"))
  (use-package rainbow-delimiters
    :hook (prog-mode . rainbow-delimiters-mode)))

Commenting (comment-dwim-2)

Additions via A comment-or-uncomment-sexp command in Emacs · Endless Parentheses. Would love a solution integrated into comment-dwim-2.

(use-package comment-dwim-2
  :bind
  (("M-;" . comment-dwim-2)
   ("C-M-;" . comment-or-uncomment-sexp))
  :init
  (defun uncomment-sexp (&optional n)
    "Uncomment a sexp around point."
    (interactive "P")
    (let* ((initial-point (point-marker))
           (inhibit-field-text-motion t)
           (p)
           (end (save-excursion
                  (when (elt (syntax-ppss) 4)
                    (re-search-backward comment-start-skip
                                        (line-beginning-position)
                                        t))
                  (setq p (point-marker))
                  (comment-forward (point-max))
                  (point-marker)))
           (beg (save-excursion
                  (forward-line 0)
                  (while (and (not (bobp))
                              (= end (save-excursion
                                       (comment-forward (point-max))
                                       (point))))
                    (forward-line -1))
                  (goto-char (line-end-position))
                  (re-search-backward comment-start-skip
                                      (line-beginning-position)
                                      t)
                  (ignore-errors
                    (while (looking-at-p comment-start-skip)
                      (forward-char -1)))
                  (point-marker))))
      (unless (= beg end)
        (uncomment-region beg end)
        (goto-char p)
        ;; Indentify the "top-level" sexp inside the comment.
        (while (and (ignore-errors (backward-up-list) t)
                    (>= (point) beg))
          (skip-chars-backward (rx (syntax expression-prefix)))
          (setq p (point-marker)))
        ;; Re-comment everything before it.
        (ignore-errors
          (comment-region beg p))
        ;; And everything after it.
        (goto-char p)
        (forward-sexp (or n 1))
        (skip-chars-forward "\r\n[:blank:]")
        (if (< (point) end)
            (ignore-errors
              (comment-region (point) end))
          ;; If this is a closing delimiter, pull it up.
          (goto-char end)
          (skip-chars-forward "\r\n[:blank:]")
          (when (eq 5 (car (syntax-after (point))))
            (delete-indentation))))
      ;; Without a prefix, it's more useful to leave point where
      ;; it was.
      (unless n
        (goto-char initial-point))))

  (defun comment-sexp--raw ()
    "Comment the sexp at point or ahead of point."
    (pcase (or (bounds-of-thing-at-point 'sexp)
               (save-excursion
                 (skip-chars-forward "\r\n[:blank:]")
                 (bounds-of-thing-at-point 'sexp)))
      (`(,l . ,r)
       (goto-char r)
       (skip-chars-forward "\r\n[:blank:]")
       (save-excursion
         (comment-region l r))
       (skip-chars-forward "\r\n[:blank:]"))))

  (defun comment-or-uncomment-sexp (&optional n)
    "Comment the sexp at point and move past it.
If already inside (or before) a comment, uncomment instead.
With a prefix argument N, (un)comment that many sexps."
    (interactive "P")
    (if (or (elt (syntax-ppss) 4)
            (< (save-excursion
                 (skip-chars-forward "\r\n[:blank:]")
                 (point))
               (save-excursion
                 (comment-forward 1)
                 (point))))
        (uncomment-sexp n)
      (dotimes (_ (or n 1))
        (comment-sexp--raw)))))

Indenting (aggressive-indent)

(use-package aggressive-indent
  :init
  (global-aggressive-indent-mode 1)
  (add-to-list 'aggressive-indent-excluded-modes 'html-mode)
  (unbind-key "C-c C-q" aggressive-indent-mode-map))

Words and Numbers

“GNU Office Suite Pro Edition,” coming to a cubicle near you!

Writing (Org Mode)

Quotations

Org-mode does outlining, note-taking, hyperlinks, spreadsheets, TODO lists, project planning, GTD, HTML and LaTeX authoring, all with plain text files in Emacs.

Carsten Dominik

If I hated everything about Emacs, I would still use it for org-mode.

Avdi on Twitter

…for all intents and purposes, Org-mode is Taskpaper!

Carsten Dominik

Configuration

I use the stock package of org-mode as the default major mode.

My settings for capture were some of my first Elisp :) I did need, and still need, the help of the Org-Mode manual, of course.

The theming for Org-Mode is derived from Ricing up Org Mode with help from folks on Reddit. Be sure to download the fonts here.

(use-package org
  :bind (("C-c l" . org-store-link)
         ("C-c c" . org-capture)
         ("C-c a" . org-agenda)
         ("C-c b" . org-iswitchb)
         ("C-c M-k" . org-cut-subtree)
         ("<down>" . org-insert-todo-heading)
         :map org-mode-map
         ("C-c >" . org-time-stamp-inactive))
  :custom-face
  (variable-pitch ((t (:family "ETBembo"))))
  (org-document-title ((t (:foreground "#171717" :weight bold :height 1.5))))
  (org-done ((t (:background "#E8E8E8" :foreground "#0E0E0E" :strike-through t :weight bold))))
  (org-headline-done ((t (:foreground "#171717" :strike-through t))))
  (org-level-1 ((t (:foreground "#090909" :weight bold :height 1.3))))
  (org-level-2 ((t (:foreground "#090909" :weight normal :height 1.2))))
  (org-level-3 ((t (:foreground "#090909" :weight normal :height 1.1))))
  :init
  (setq default-major-mode 'org-mode
        org-directory "~/org/"
        org-log-done t
        org-startup-indented t
        org-startup-truncated nil
        org-startup-with-inline-images t
        org-completion-use-ido t
        org-default-notes-file (concat org-directory "notes.org")
        org-image-actual-width '(300)
        org-goto-max-level 10
        org-imenu-depth 5
        org-goto-interface 'outline-path-completion
        org-outline-path-complete-in-steps nil
        org-src-fontify-natively t
        org-lowest-priority ?C
        org-default-priority ?B
        org-expiry-inactive-timestamps t
        org-show-notification-handler 'message
        org-special-ctrl-a/e t
        org-special-ctrl-k t
        org-yank-adjusted-subtrees t
        org-file-apps
        '((auto-mode . emacs)
          ("\\.mm\\'" . default)
          ("\\.x?html?\\'" . "firefox %s")
          ("\\.pdf\\'" . "open %s"))
        org-todo-keywords
        '((sequence "TODO(t)" "STARTED(s)" "WAITING(w)" "SOMEDAY(.)" "MAYBE(m)" "|" "DONE(x!)" "CANCELLED(c)"))
        ;; Theming
        org-ellipsis "" ;; folding symbol
        org-pretty-entities t
        org-hide-emphasis-markers t ;; show actually italicized text instead of /italicized text/
        org-agenda-block-separator ""
        org-fontify-whole-heading-line t
        org-fontify-done-headline t
        org-fontify-quote-and-verse-blocks t
        org-image-actual-width 600)

  (add-to-list 'org-global-properties
               '("Effort_ALL". "0:05 0:15 0:30 1:00 2:00 3:00 4:00"))

  (add-hook 'org-mode-hook
            '(lambda ()
               (setq line-spacing 0.2) ;; Add more line padding for readability
               (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-verbatim
                      'org-block-begin-line
                      'org-block-end-line
                      'org-meta-line
                      'org-document-info-keyword))))

  (custom-theme-set-faces
   'spacemacs-light
   `(org-block-begin-line ((t (:background "#fbf8ef"))))
   `(org-block-end-line ((t (:background "#fbf8ef"))))))

Packages

org-cliplink

org-cliplink lets you insert a link from your clipboard with a title that is fetched from the page’s metadata.

(use-package org-cliplink
  :bind ("C-x p i" . org-cliplink))

org-bullets

(use-package org-bullets
  :init
  (add-hook 'org-mode-hook (lambda () (org-bullets-mode 1))))

org-autosort

I wanted to be able to add a sort property to files or subtrees and get automatic autosorting. A user of r/orgmode delivered a solution.

(defun yant/org-entry-has-subentries ()
  "Any entry with subheadings."
  (let ((subtree-end (save-excursion (org-end-of-subtree t))))
    (save-excursion
      (org-back-to-heading)
      (forward-line 1)
      (when (< (point) subtree-end)
        (re-search-forward "^\*+ " subtree-end t)))))

(defun yant/org-entry-sort-by-property nil
  "Apply property sort on current entry. The sorting is done using property with the name from value of :SORT: property.
      For example, :SORT: DEADLINE will apply org-sort-entries by DEADLINE property on current entry."
  (let ((property (org-entry-get (point) "SORT" 'INHERIT)))
    (when (and (not (seq-empty-p property))
               (yant/org-entry-has-subentries))
      (funcall #'org-sort-entries nil ?r nil nil property))))

(defun yant/org-buffer-sort-by-property (&optional MATCH)
  "Sort all subtrees in buffer by the property, which is the value of their :SORT: property.
        Only subtrees, matching MATCH are selected"
  (org-map-entries #'yant/org-entry-sort-by-property MATCH 'file))

(add-hook 'org-mode-hook #'yant/org-buffer-sort-by-property)

yankpad

(use-package yasnippet)

(use-package yankpad
  :init
  (setq yankpad-file "~/org/templates/yankpad.org")
  (bind-keys :prefix-map yank-map
             :prefix "C-c y"
             ("c" . yankpad-set-category)
             ("e" . yankpad-edit)
             ("i" . yankpad-insert)
             ("m" . yankpad-map)
             ("r" . yankpad-reload)
             ("x" . yankpad-expand)))

Functionality

Agendas

Configuration
(setq org-agenda-inhibit-startup nil
      org-agenda-show-future-repeats nil
      org-agenda-start-on-weekday nil
      org-agenda-skip-deadline-if-done t
      org-agenda-skip-scheduled-if-done t)

(unbind-key "C-c [")
(unbind-key "C-c ]")
Org-Super-Agenda
(use-package org-super-agenda
  :init
  (org-super-agenda-mode)

  (defun my-org-super-agenda ()
    (interactive)
    (let ((org-super-agenda-groups
           '((:name "Schedule"
                    :time-grid t)
             (:name "MAPLE" ;; monastery work
                    :tag "maple")
             ;; After the last group, the agenda will display items that didn't
             ;; match any of these groups, with the default order position of 99
             ;; To prevent this, add this code:
             ;; (:discard (:anything t))
             )))
      (org-agenda nil "a")))

  (defun my-org-super-agenda-today ()
    (interactive)
    (progn
      (my-org-super-agenda)
      (org-agenda-day-view)))

  (defun my-personal-agenda ()
    (interactive)
    (let ((org-super-agenda-groups
           '(;; After the last group, the agenda will display items that didn't
             ;; match any of these groups, with the default order position of 99
             ;; To prevent this, add this code:
             (:discard (:tag ("maple"))))))
      (org-agenda nil "a")
      (org-agenda-day-view)))

  (defun my-monastery-agenda ()
    (interactive)
    (let ((org-super-agenda-groups
           '((:name "MAPLE" ;; monastery work
                    :tag "maple")
             ;; After the last group, the agenda will display items that didn't
             ;; match any of these groups, with the default order position of 99
             ;; To prevent this, add this code:
             (:discard (:anything t)))))
      (org-agenda nil "a")
      (org-agenda-day-view)))

  (bind-keys ("C-c `" . my-org-super-agenda-today)
             ("C-c 1" . my-personal-agenda)
             ("C-c 2" . my-monastery-agenda)
             ("C-c 0" . my-org-super-agenda))

  :config
  ;; Enable folding
  (use-package origami
    :bind (:map org-super-agenda-header-map
                ("TAB" . origami-toggle-node))
    :hook ((org-agenda-mode . origami-mode))))
Agenda Files
(setq org-agenda-files (quote ("~/org/waiting.org"
                               "~/org/calendar/gcal.org"
                               "~/org/calendar/maple.org"
                               "~/org/somedaymaybe.org")))
Open Org Agenda

This function opens the agenda in full screen.

(defun open-agenda ()
  "Opens the org-agenda."
  (interactive)
  (let ((agenda "*Org Agenda*"))
    (if (equal (get-buffer agenda) nil)
        (org-agenda-list)
      (unless (equal (buffer-name (current-buffer)) agenda)
        (switch-to-buffer agenda))
      (org-agenda-redo t)
      (beginning-of-buffer))))

(bind-key "<f5>" 'open-agenda)
(bind-key "a" 'open-agenda launcher-map)
Close Other Windows

Agendas should be full screen!

(add-hook 'org-agenda-finalize-hook (lambda () (delete-other-windows)))
Custom Agendas

Here are some hand-made agenda files.

(defun org-buffer-todo ()
  (interactive)
  "Creates a todo-list for the current buffer. Equivalent to the sequence: org-agenda, < (restrict to current buffer), t (todo-list)."
  (progn
    (org-agenda-set-restriction-lock 'file)
    (org-todo-list)))

(defun org-buffer-agenda ()
  (interactive)
  "Creates an agenda for the current buffer. Equivalent to the sequence: org-agenda, < (restrict to current buffer), a (agenda-list)."
  (progn
    (org-agenda-set-restriction-lock 'file)
    (org-agenda-list)))

(defun org-buffer-day-agenda ()
  (interactive)
  "Creates an agenda for the current buffer. Equivalent to the sequence: org-agenda, < (restrict to current buffer), a (agenda-list), d (org-agenda-day-view)."
  (progn
    (org-agenda-set-restriction-lock 'file)
    (org-agenda-list)
    (org-agenda-day-view))) ;; Maybe I should try writing a Emacs Lisp macro for this kind of thing!

(bind-key "y" 'org-agenda-todo-yesterday org-agenda-mode-map)
Timeline

The org-timeline functionality was recently removed. This code, adapted from a comment on Reddit, adds similar functionality back.

(add-to-list 'org-agenda-custom-commands
             '("L" "Timeline"
               ((agenda
                 ""
                 ((org-agenda-span 7)
                  (org-agenda-prefix-format '((agenda . " %1c %?-12t% s"))))))))
Unscheduled Tasks
(add-to-list 'org-agenda-custom-commands
             '("u" "Unscheduled TODOs"
               ((todo ""
                      ((org-agenda-overriding-header "\nUnscheduled TODO")
                       (org-agenda-skip-function '(org-agenda-skip-entry-if 'timestamp 'todo '("DONE" "CANCELLED" "MAYBE" "WAITING" "SOMEDAY"))))))) t)

Thanks Sacha for this custom code!

(defun my-org-agenda-recent-open-loops ()
  (interactive)
  (let ((org-agenda-start-with-log-mode t)
        (org-agenda-use-time-grid nil)
        (org-agenda-files '("~/org/calendar/gcal.org" "~/org/calendar/maple.org")))
    (fetch-calendar)
    (org-agenda-list nil (org-read-date nil nil "-2d") 4)
    (beginend-org-agenda-mode-goto-beginning)))

(defun my-org-agenda-longer-open-loops ()
  (interactive)
  (let ((org-agenda-start-with-log-mode t)
        (org-agenda-use-time-grid nil)
        (org-agenda-files '("~/org/calendar/gcal.org" "~/org/calendar/maple.org")))
    (fetch-calendar)
    (org-agenda-list 'file (org-read-date nil nil "-14d") 28)
    (beginend-org-agenda-mode-goto-beginning)))
Delegated and Waiting Tasks
(add-to-list 'org-agenda-custom-commands
             '("w" "WAITING" todo "WAITING" ((org-agenda-overriding-header "Delegated and/or Waiting"))) t)
Auto Advance
(defun org-agenda-set-tags-auto-advance ()
  (interactive)
  (while t
    (call-interactively #'org-agenda-set-tags)
    (org-agenda-next-line)))

(bind-key "`" 'org-agenda-set-tags-auto-advance org-agenda-mode-map)

Capture Templates

The first Emacs Lisp that I ever wrote was to configure my capture templates!

(setq org-capture-templates
      '(("t" "Task" entry (file "~/org/inbox.org")
         "* TODO %?\n")
        ("p" "Project" entry (file+headline "~/org/todo.org" "Projects")
         (file "~/org/templates/newprojecttemplate.org"))
        ("s" "Someday" entry (file+headline "~/org/somedaymaybe.org" "Someday / Maybe")
         "* SOMEDAY %?\n")
        ("m" "Maybe" entry (file+headline "~/org/somedaymaybe.org" "Someday / Maybe")
         "* MAYBE %?\n")
        ("l" "Log" entry (file+olp+datetree "~/org/log.org" "Log")
         (file "~/org/templates/logtemplate.org"))))

Clocking

I really like Org-mode’s clocking functionality. I mostly use it for keeping time of billable tasks.

Configuration
(setq org-log-done 'time
      org-clock-idle-time nil
      org-clock-continuously nil
      org-clock-persist t
      org-clock-in-switch-to-state "STARTED"
      org-clock-in-resume nil
      org-clock-report-include-clocking-task t
      org-clock-out-remove-zero-time-clocks t
      ;; Too many clock entries clutter up a heading
      org-log-into-drawer t
      org-clock-into-drawer 1)
Remove Empty Logbook Drawers

Remove empty LOGBOOK drawers on clock out, from Michael Englehorn’s Emacs Configuration. This Stack Overflow post shows the fix to the bug in the original function (remove the “LOGBOOK” specification).

(defun bh/remove-empty-drawer-on-clock-out ()
  (interactive)
  (save-excursion
    (beginning-of-line 0)
    (org-remove-empty-drawer-at (point))))

(add-hook 'org-clock-out-hook 'bh/remove-empty-drawer-on-clock-out 'append)
Key Bindings

However, there are a lot of commands for clocking; this is a perfect instance for a Hydra.

(defhydra hydra-org-clock (:color blue :hint nil)
  "
Clock   In/out^     ^Edit^   ^Summary     (_?_)
-----------------------------------------
        _i_n         _e_dit   _g_oto entry
        _c_ontinue   _q_uit   _d_isplay
        _o_ut        ^ ^      _r_eport
      "
  ("i" org-clock-in)
  ("o" org-clock-out)
  ("c" org-clock-in-last)
  ("e" org-clock-modify-effort-estimate)
  ("q" org-clock-cancel)
  ("g" org-clock-goto)
  ("d" org-clock-display)
  ("r" org-clock-report)
  ("?" (org-info "Clocking commands")))

(defhydra hydra-org-agenda-clock (:color blue :hint nil)
  "
Clock   In/out^
-----------------------------------------
        _i_n
        _g_oto entry
        _o_ut
        _q_uit
      "
  ("i" org-agenda-clock-in)
  ("o" org-agenda-clock-out)
  ("q" org-agenda-clock-cancel)
  ("g" org-agenda-clock-goto))

(bind-keys ("C-c w" . hydra-org-clock/body)
           :map org-agenda-mode-map
           ("C-c w" . hydra-org-agenda-clock/body))

Drag and Drop

(use-package org-download)

Exporting

I often want to export Org-Mode documents into other formats. This section contains some defaults and packages that have made that workflow easier for me.

Export Defaults

Org’s export defaults to inserting a TOC and using section numbers, but usually I don’t want or need those things. (It can make delieverables more attractive, though, so I make sure to put a hint in here to remind me about how to change that on a file-by-file basis.)

(setq org-export-with-toc nil
      org-export-with-section-numbers nil)

To override, add to the #+OPTIONS: org header for a particular file:

  • To add in a table of contents: toc:4 (to a particular depth) or toc:t (to all depths)
  • To show the section numbers, set num:t
Use Bootstrap
(use-package ox-twbs)
Preview Org Exported HTML

I don’t much like Markdown, but many Markdown programs (ando other text formats and editors) have a feature where you can preview your formatting. This package adds this functionality for org-mode, using eww, the plain text browser built into Emacs.

Once in the eww buffer, hit & (eww-browse-with-external-browser) to view the buffer in your external browser of choice.

(use-package org-preview-html
  :commands org-preview-html/preview
  :after org)
Formatted copy commands for org-mode

ox-clip lets you select a part of an Org-Mode buffer, run ox-clip-formatted-copy. I needed to install xclip on Arch Linux to get this to work; it looks like you don’t need to install packages to get this to run on Windows or OS X. You also need to bind ox-clip; I chose C-c x.

(use-package htmlize
  :after org)
(use-package ox-clip
  :after org
  :config
  (defun ox-clip-dwim ()
    "If the region is active, call ox-clip as normal. Otherwise, call ox-clip on whole buffer (or visible / narrowed section, if applicable)."
    (interactive)
    (if (region-active-p)
        (ox-clip-formatted-copy (region-beginning) (region-end))
      ;; if buffer is narrowed, this will work on visible; if not, it will capture whole buffer
      (ox-clip-formatted-copy (point-min) (point-max))))
  (bind-keys ("C-c x" . ox-clip-dwim)
             :map selected-org-mode-map
             ("x" . ox-clip-dwim)))

ID’s

By using unique ID’s for links in Org-mode, links will work even if you move them across files.

(setq org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id
      org-clone-delete-id t)

Movement (org-teleport)

This function comes from John Kitchin.

(defun org-teleport (&optional arg)
  "Teleport the current heading to after a headline selected with avy.
  With a prefix ARG move the headline to before the selected
  headline. With a numeric prefix, set the headline level. If ARG
  is positive, move after, and if negative, move before."
  (interactive "P")
  ;; Kill current headline
  (org-mark-subtree)
  (kill-region (region-beginning) (region-end))
  ;; Jump to a visible headline
  (avy-with avy-goto-line (avy--generic-jump "^\\*+" nil avy-style))
  (cond
   ;; Move before  and change headline level
   ((and (numberp arg) (> 0 arg))
    (save-excursion
      (yank))
    ;; arg is what we want, second is what we have
    ;; if n is positive, we need to demote (increase level)
    (let ((n (- (abs arg) (car (org-heading-components)))))
      (cl-loop for i from 1 to (abs n)
               do
               (if (> 0 n)
                   (org-promote-subtree)
                 (org-demote-subtree)))))
   ;; Move after and change level
   ((and (numberp arg) (< 0 arg))
    (org-mark-subtree)
    (goto-char (region-end))
    (when (eobp) (insert "\n"))
    (save-excursion
      (yank))
    ;; n is what we want and second is what we have
    ;; if n is positive, we need to demote
    (let ((n (- (abs arg) (car (org-heading-components)))))
      (cl-loop for i from 1 to (abs n)
               do
               (if (> 0 n) (org-promote-subtree)
                 (org-demote-subtree)))))

   ;; move to before selection
   ((equal arg '(4))
    (save-excursion
      (yank)))
   ;; move to after selection
   (t
    (org-mark-subtree)
    (goto-char (region-end))
    (when (eobp) (insert "\n"))
    (save-excursion
      (yank))))
  (outline-hide-leaves))

(add-to-list 'org-speed-commands
             (cons "q" (lambda ()
                         (avy-with avy-goto-line
                           (avy--generic-jump "^\\*+" nil avy-style)))))

(add-to-list 'org-speed-commands (cons "T" 'org-teleport))

(bind-key "T" 'org-teleport selected-org-mode-map)

Refiling

(setq org-refile-targets '((("~/org/todo.org" "~/org/somedaymaybe.org") :maxlevel . 3))
      ;; org-refile-use-cache t
      org-refile-use-outline-path t)

Exclude completed tasks from refile targets, from Michael Englehorn’s Emacs Configuration.

(defun bh/verify-refile-target ()
  "Exclude todo keywords with a done state from refile targets"
  (not (member (nth 2 (org-heading-components)) org-done-keywords)))

(setq org-refile-target-verify-function 'bh/verify-refile-target)

Speed Commands

(setq org-use-speed-commands t
      org-speed-commands
      '(("N" org-narrow-to-subtree)
        ("$" org-archive-subtree)
        ("A" org-archive-subtree)
        ("W" widen)
        ("d" org-down-element)
        ("k" org-cut-subtree)
        ("m" org-mark-subtree)
        ("s" org-sort)
        ("x" smex-major-mode-commands)
        ("X" org-todo-done)
        ("R" org-done-and-archive)
        ("y" org-todo-yesterday)))

Speed commands are really useful, but I often want to make use of them when I’m not at the beginning of a header. This command brings you back to the beginning of an item’s header, so that you can do speed commands.

(defun org-go-speed ()
  "Goes to the beginning of an element's header, so that you can execute speed commands."
  (interactive)
  (when (equal major-mode 'org-mode)
    (if (org-at-heading-p)
        (beginning-of-line)
      (outline-previous-heading))))

(bind-key "C-c s" 'org-go-speed)

Tagging

(setq org-tag-alist '(
                      ;; Depth
                      ("@immersive" . ?i) ;; "Deep"
                      ("@process" . ?p) ;; "Shallow"
                      ;; Context
                      ("@work" . ?w)
                      ("@home" . ?h)
                      ("@errand" . ?e)
                      ;; Time
                      ("15min" . ?<)
                      ("30min" . ?=)
                      ("1h" . ?>)
                      ;; Energy
                      ("Challenge" . ?1)
                      ("Average" . ?2)
                      ("Easy" . ?3)
                      ))

Other

Shell in Org Source Blocks

(org-babel-do-load-languages 'org-babel-load-languages
                             '((shell . t)))

Sentence-End

(setq sentence-end-double-space nil)

Easy Bind To Open Todos

(defun open-todo-file ()
  (interactive)
  (find-file "~/org/todo.org"))

(bind-key "C-c t" 'open-todo-file)

Prettify Symbols

(add-hook 'org-mode-hook
              (lambda ()
                (push '("TODO"  . ?▲) prettify-symbols-alist)
                (push '("DONE"  . ?✓) prettify-symbols-alist)
                (push '("CANCELLED"  . ?✘) prettify-symbols-alist)
                (push '("QUESTION"  . ??) prettify-symbols-alist)))

Visual Line Mode

(add-hook 'text-mode-hook 'turn-on-visual-line-mode)

Repeated Words

From an appendix in Intro to Emacs Lisp.

(defun the-the ()
  "Search forward for for a duplicated word."
  (interactive)
  (message "Searching for for duplicated words ...")
  (push-mark)
  ;; This regexp is not perfect
  ;; but is fairly good over all:
  (if (re-search-forward
       "\\b\\([^@ \n\t]+\\)[ \n\t]+\\1\\b" nil 'move)
      (message "Found duplicated word.")
    (message "End of buffer")))

;; Bind 'the-the' to  C-c \
(bind-key "C-c \\" 'the-the)

Markdown

(use-package markdown-mode)

Obsidian

(defun obsidian-search-vault ()
  "Searches my Obsidian vault."
  (interactive)
  (counsel-find-file "/Users/tasshin/Google Drive/tasshin/pages/"))

(defun obsidian-search-projects ()
  "Searches my Obsidian vault."
  (interactive)
  (counsel-find-file "/Users/tasshin/Google Drive/tasshin/1_Projects/"))

(defun obsidian-search-journals ()
  "Searches my Obsidian vault."
  (interactive)
  (counsel-find-file "/Users/tasshin/Google Drive/tasshin/journals/"))

(defun obsidian-find-today ()
  "Searches my Obsidian vault."
  (interactive)
  (find-file (concat "/Users/tasshin/Google Drive/tasshin/journals/" (format-time-string "%Y-%m-%d") ".md")))

(bind-keys :prefix-map obsidian-map
           :prefix "C-c o"
           ("v" . obsidian-search-vault)
           ("p" . obsidian-search-projects)
           ("j" . obsidian-search-journals)
           ("t" . obsidian-find-today))

Calculator (Calc)

(use-package calc
  :config
  (setq calc-display-trail ()))

Numbers at Point

(use-package shift-number
  :bind (("M-+" . shift-number-up)
         ("M-_" . shift-number-down)))

Spell Checking (Flyspell)

(use-package flyspell
  :bind (("C-`" . ispell-word)
         ("C-~" . ispell-buffer))
  :init
  (dolist (hook '(text-mode-hook org-mode-hook))
    (add-hook hook (lambda () (flyspell-mode 1))))
  :config
  (setq ispell-program-name "aspell"
        ispell-list-command "--list"))

Document Conversions (Pandoc)

Hit C-c / to run pandoc-main-hydra/body, the main entry point for pandoc-mode.

(use-package pandoc-mode
  :init
  (add-hook 'pandoc-mode-hook 'pandoc-load-default-settings)
  :config
  (when (is-mac-p)
    (add-to-list 'exec-path "/usr/local/texlive/2016basic/bin/universal-darwin")))

CSV

(use-package csv-mode
  :mode ("\\.csv$" . csv-mode))

Functions

Emacs Configuration File

This function and the corresponding keybinding allows me to rapidly access my configuration. They are adapted from Bozhidar Batsov’s post on Emacs Redux.

I use mwf-init-file rather than user-init-file, because I edit the config file in a Git repo.

(defun find-init-file ()
  "Edit my init file in another window."
  (interactive)
  (let ((mwf-init-file "~/src/.emacs.d/michael.org"))
    (find-file mwf-init-file)))

(bind-key "C-c I" 'find-init-file)

Relatedly, I often want to reload my init-file. This will actually use the system-wide user-init-file variable.

(defun reload-init-file ()
  "Reload my init file."
  (interactive)
  (load-file user-init-file))

(bind-key "C-c M-l" 'reload-init-file)

Buffer Management

Kill This Buffer

(defun kill-this-buffer ()
  (interactive)
  (kill-buffer (current-buffer)))

(bind-key "C-x C-k" 'kill-this-buffer)

By default, pressing ‘q’ in Dired, the packages menu, or Elfeed runs quit-window, which quits the window and buries its buffer. I’d prefer the buffer to close.

(bind-keys :map dired-mode-map
           ("q" . kill-this-buffer))

(bind-keys :map package-menu-mode-map
           ("q" . kill-this-buffer))

Kill All Other Buffers

(defun kill-other-buffers ()
   "Kill all other buffers."
   (interactive)
   (mapc 'kill-buffer (delq (current-buffer) (buffer-list))))

Minibuffer

This code comes from EmacsWiki.

(defun switch-to-minibuffer ()
  "Switch to minibuffer window."
  (interactive)
  (if (active-minibuffer-window)
      (select-window (active-minibuffer-window))
    (error "Minibuffer is not active")))

(bind-key "M-m" 'switch-to-minibuffer)

Edit as Root

This tip comes from an emacs-fu blog post.

(defun find-file-as-root ()
  "Like `ido-find-file, but automatically edit the file with
root-privileges (using tramp/sudo), if the file is not writable by
user."
  (interactive)
  (let ((file (ido-read-file-name "Edit as root: ")))
    (unless (file-writable-p file)
      (setq file (concat "/sudo:root@localhost:" file)))
    (find-file file)))

(bind-key "C-x F" 'find-file-as-root)

Unfill Paragraph

This function borrowed from Sacha.

(defun unfill-paragraph (&optional region)
  "Takes a multi-line paragraph and makes it into a single line of text."
  (interactive (progn
                 (barf-if-buffer-read-only)
                 (list t)))
  (let ((fill-column (point-max)))
    (fill-paragraph nil region)))

(bind-key "M-Q" 'unfill-paragraph)

Move Lines

Via Harry Schwartz.

(defun move-line-up ()
  (interactive)
  (transpose-lines 1)
  (forward-line -2))

(defun move-line-down ()
  (interactive)
  (forward-line 1)
  (transpose-lines 1)
  (forward-line -1))

(bind-keys ("M-<up>" . move-line-up)
           ("M-<down>" . move-line-down))

Flush Empty Lines

(defun flush-empty-lines ()
  (interactive)
  (flush-lines "^$"))

Processing imported notes

(defun add-newlines-between-paragraphs ()
  (interactive)
  (save-excursion
    (beginning-of-buffer)
    (while (< (point) (point-max))
      (move-end-of-line nil)
      (newline)
      (next-line))))

(defun clean-instapaper-evernote-notes ()
  "Cleans notes exported from Instapaper into Evernote. Copies processed buffer to clipboard."
  (interactive)
  (beginning-of-buffer)
  (replace-regexp "^\“" "")
  (beginning-of-buffer)
  (replace-regexp "\”$" "")
  (beginning-of-buffer)
  (add-newlines-between-paragraphs)
  (copy-whole-buffer))

(defun clean-liner-evernote-notes ()
  "Cleans notes exported from Liner into Evernote. Copies processed buffer to clipboard."
  (interactive)
  (beginning-of-buffer)
  (replace-regexp "Source \:" "Source:")
  (beginning-of-buffer)
  (replace-regexp "^- " "")
  (copy-whole-buffer))

(defun remove-roam-brackets ()
  "Removes square brackets from notes exported from Roam into Emacs."
  (interactive)
  (beginning-of-buffer)
  (replace-regexp "\\[" "")
  (beginning-of-buffer)
  (replace-regexp "\\]" "")
  (copy-whole-buffer))

Single Blank Lines

(fset 'single-blanks
      (kmacro-lambda-form [?\C-x ?h ?\C-x ?\C-m ?r ?e ?p ?l ?a ?c ?e ?- ?r ?e ?g ?e ?x ?p return ?^ ?\C-q ?\C-j ?\C-q ?\C-j ?+ return ?\C-q ?\C-j return] 0 "%d"))