Beckwith Tangled Emacs Initialization

Table of Contents

This document tangles (in literate programming style) the necessary commands to initialize Emacs to my liking and the documentation for my choices.

To clone, go to the github repository. For a pretty view, head over to the generated page.

1 Installation

My init.el file is quite simple and is generated by the following block. Essentially, I just have to install this package (bnb-emacs) in the ~/.emacs.d/ directory and run the following code block (C-c C-c) to bootstrap the system.

;;; init.el --- bnbeckwith config -*- eval: (read-only-mode 1) -*-
(require 'package)
(setq package-enable-at-startup nil)
(package-initialize)

(require 'ob-tangle)
(org-babel-load-file "~/.emacs.d/bnb-emacs/Readme.org")

From there on, the bootstrapping is simple. Emacs finds ~/.emacs.d/init.el and runs the code. The first step is to initialize the packages I have installed via ELPA and others.

Next, I load ob-tangle (part of org-mode). Then org-babel-load-file extracts the emacs-lisp code blocks in this document and loads the resulting Readme.org.

As I add packages or lines to this document, my initialization is already in place and ready to go.

If you are reading this online, the html version of this file is generated by using `bnb/export-readme` explained in Styled HTML Export.

2 Notes

This section has specific notes that are relevant to my emacs setup in general and this document in particular.

2.1 Emacs Build

My current flavor of Emacs comes from: https://github.com/d12frosted/homebrew-emacs-plus

2.2 Pending sections

There are some features that I like to take on a trial run. These are marked with the PENDING tag to help me remember and evaluate.

2.3 Performance

By utilizing elements of use-package, I can keep an eye on troublesome packages during startup. Together, these turn on reporting and set the minimum time to consider when building the report.

(setq use-package-verbose t
      use-package-compute-statistics t
      use-package-minimum-reported-time 0)

The generated messages will be found in the *Messsages* buffer.

3 Personal Information

The full name is used for email messages.

(setq user-full-name "Benjamin Beckwith")

4 Local customizations (custom.el)

I typically use the customize interface to generate any local settings such as proxies, paths, fonts, etc. that may vary from machine to machine. This keeps the setup the same and allows for only some details to differ.

I like to set the custom file explicitly. Mine resides in the ~/.emacs.d/ directory. This code block loads it if it exists.

(setq custom-file "~/.emacs.d/custom.el")
(if (file-exists-p custom-file)
    (load-file custom-file))

(with-eval-after-load "bind-key"
  (bind-key "<f7>"
            (lambda ()
              (interactive
               (find-file custom-file)))))

If the file doesn't exist, Emacs will still use the file if any changes are made through the custom interface.

Sometimes I'll get bad settings or cruft in that file. I now have a key, <F7>, for easy navigation to wherever the custom-file points.

5 Small Settings

The next sections encompass some small settings to make Emacs mine (and maybe yours). There is a loose order of dependency, but even then it isn't strict.

5.1 Package archives

I like to pull from the popular list of package archives.

(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(add-to-list 'package-archives '("org" . "https://orgmode.org/elpa/") t)

I install the packages with these repositories. This may take some time the first time it runs.

Org-mode has their own repositories that keep me on the bleeding edge. They is why that repository is listed separately above.

Once the repositories are defined, it's time to bootstrap the package system and use-package in general since I use it everywhere.

(unless (package-installed-p 'use-package)
  (package-refresh-contents)
  (package-install 'use-package))
(setq use-package-verbose t
      use-package-always-ensure nil)
(require 'use-package)

5.1.1 From Source

Quelpa grabs and builds packages from source (e.g. Github). I have found a few things not in any package archive yet that I'd like to have automatically installed.

Along with the quelpa package, quelpa-use-package adds a handler to use-package making it fit in nicely with the rest of my configuration.

(use-package quelpa)
(use-package quelpa-use-package :ensure t)

;; Handle the `use-package-always-ensure' setting
(quelpa-use-package-activate-advice)

5.1.2 Load the latest files

Always pick the latest version of the library to load.

(setq load-prefer-newer t)

5.1.3 Paradox

The paradox interface offers a few enhancements over the included package package. It adds github stars in the package listing, performs autoremoval of packages, and installs packages in parallel.

(use-package paradox
  :ensure t
  :delight " ፨"
  :commands (paradox-list-packages))

5.2 Binding Keys

For binding keys, I use the bind-key package. Not only does it easily bind keys, but it does so with some nice features.

(use-package bind-key
  :bind ("C-h B" . describe-personal-keybindings))

By using bind-key, you can specify the keystrokes that invoke a command. This is regular behavior that you can already achieve in Emacs and will result in a global binding.

If you also want to override any possible minor-mode bindings of the same keys, you can use bind-key* instead.

There is also an unbind-key to, of course, remove any binding.

The real kicker is that it will keep track of these bindings and let you see a summary of your customizations with

M-x describe-personal-keybindings

This is bound to C-h B above.

5.3 Path

Sometimes Emacs' idea of path differs from the shell. The package exec-path-from-shell seeks to bring those in line with each other.

(use-package exec-path-from-shell
  :ensure t
  :config
  (exec-path-from-shell-initialize))

5.4 Backups

Sensible backup settings from https://www.emacswiki.org/emacs/BackupDirectory

(setq backup-by-copying t
create-lockfiles nil
backup-directory-alist '((".*" . "~/.saves"))
;; auto-save-file-name-transforms `((".*" "~/.saves" t))
delete-old-versions t
kept-new-versions 6
kept-old-versions 2
version-control t)

Here's a quick rundown of the settings:

backup-by-copying
Use copying to create backups when t
create-lockfiles
Don't use lockfiles if nil
backup-directory-alist
List of regexp/location pairs of where to backup files
auto-save-file-name-transforms
Transform file names before autosave
delete-old-versions
Delete excess backups silently if t
kept-new-versions
Number of newest versions to keep
kept-old-versions
Number of oldest versions to keep
version-control
When t, make numeric backup versions always

5.5 Super keys

I like to be able to use the command (or super or hyper) keys for shortcuts. I need to take care to not interfere with the built-in shortcuts or my bindings will not work.

(setq mac-function-modifier 'hyper
      mac-pass-command-to-system nil
      mac-right-option-modifier 'super
      mac-right-command-modifier 'super
      mac-right-control-modifier 'hyper
      mac-command-modifier 'meta
      mac-control-modifier 'ctrl
      mac-option-modifier 'none)

Note that the right option and command keys will pass through to the system. This is especially cool for the option key on a mac that lets insert special characters directly. (E.g. á or ∑ or ®)

Inspiration for the keys comes from wisdom and wonder.

5.6 Hydra

Sometimes it is useful to go into a command mode that lets you quickly do a few different actions. Hydra does that and more.

By defining specific hydras, you can group together commands with documentation. Think of it as a mini-control-panel. I include it here and use it elsewhere when grouping commands. (See 5.8.1 for an example)

(use-package hydra
  :ensure t)

5.7 Delight

The mode line can get pretty busy showing all of the package names. delight helps tone it down by removing some packages from showing up, or changing their name to something shorter.

(use-package delight :ensure t)

5.8 Personal Keymaps

5.8.1 Toggle Map

This toggle map shows the current toggleable settings with shortcut keys for enabling. The amaranth color makes this buffer stay around until I press q.

(defmacro toggle-setting-string (setting)
  `(if (and (boundp ',setting) ,setting) '[x] '[_]))

(bind-key
 "C-x t"
 (defhydra hydra-toggle (:color amaranth)
   "
    _c_ column-number : %(toggle-setting-string column-number-mode)  _b_ orgtbl-mode    : %(toggle-setting-string orgtbl-mode)  _x_/_X_ trans          : %(identity bnb/transparency)
    _e_ debug-on-error: %(toggle-setting-string debug-on-error)  _s_ orgstruct-mode : %(toggle-setting-string orgstruct-mode)  _m_   hide mode-line : %(toggle-setting-string bnb/hide-mode-line-mode)
    _u_ debug-on-quit : %(toggle-setting-string debug-on-quit)  _h_ diff-hl-mode   : %(toggle-setting-string diff-hl-mode)
    _f_ auto-fill     : %(toggle-setting-string auto-fill-function)  _B_ battery-mode   : %(toggle-setting-string display-battery-mode)
    _t_ truncate-lines: %(toggle-setting-string truncate-lines)  _l_ highlight-line : %(toggle-setting-string hl-line-mode)
    _r_ read-only     : %(toggle-setting-string buffer-read-only)  _n_ line-numbers   : %(toggle-setting-string linum-mode)
    _w_ whitespace    : %(toggle-setting-string whitespace-mode)  _N_ relative lines : %(if (eq linum-format 'linum-relative) '[x] '[_])
    "
   ("c" column-number-mode nil)
   ("e" toggle-debug-on-error nil)
   ("u" toggle-debug-on-quit nil)
   ("f" auto-fill-mode nil)
   ("t" toggle-truncate-lines nil)
   ("r" dired-toggle-read-only nil)
   ("w" whitespace-mode nil)
   ("b" orgtbl-mode nil)
   ("s" orgstruct-mode nil)
   ("x" bnb/transparency-next nil)
   ("B" display-battery-mode nil)
   ("X" bnb/transparency-previous nil)
   ("h" diff-hl-mode nil)
   ("l" hl-line-mode nil)
   ("n" linum-mode nil)
   ("N" linum-relative-toggle nil)
   ("m" bnb/hide-mode-line-mode nil)
   ("q" nil)))

5.8.2 Elisp Maps

Here are some nice-to-have features when in elisp-mode.

(bind-key
 "C-c e"
 (defhydra hydra-elisp-cmds (:color blue)
   ("b" eval-buffer "eval buffer")
   ("e" toggle-debug-on-error "debug-on-error")
   ("f" emacs-lisp-byte-compile-and-load "byte-compile-and-load")
   ("r" eval-region "eval-region")
   ("q" nil))
 emacs-lisp-mode-map)

There is one block for execuing items and another for looking up specific elisp help.

(bind-key
 "C-h e"
 (defhydra hydra-elisp-help (:color blue)
   ("e" view-echo-area-messages "view-echod-area-messages")
   ("f" find-function "find-function")
   ("k" find-function-on-key "find-function-on-key")
   ("l" find-library "find-library")
   ("v" find-variable "find-variable")
   ("V" apropos-value "apropos-value")
   ("i" info-display-manual "info-display-manual")
   ("q" nil))
 emacs-lisp-mode-map)

5.8.3 Small bindings

This sections contains smaller bindings (or overrides) that I use to customize functionality.

  1. Whitespace

    Handle the emptiness without staring too long into the void.

    1. Whitespace Mode

      A little setup for whitespace-mode to diminish the mode and cleanup on save.

      (use-package whitespace
        :ensure nil
        :config
        (setq whitespace-line-column nil)
        (add-hook 'before-save-hook 'whitespace-cleanup)
        :delight whitespace-mode)
      
    2. Deletion

      By default, M-\ performs delete-horizontal-space and will consume all of the whitespace present.

      I'd like it to be smart and leave one or no spaces if possible. The fixup-whitespace function will do that.

      (bind-key "M-k" 'fixup-whitespace)
      
  2. Scroll window up/down

    In addition to moving the cursor, it is also interesting to scroll the screen (without moving the cursor with respect to the frame).

    (defun bnb/scroll-up-1 ()
      "Scroll up by one line."
      (interactive)
      (cua-scroll-up 1))
    
    (defun bnb/scroll-down-1 ()
      "Scroll down by one line."
      (interactive)
      (cua-scroll-down 1))
    
    (bind-keys
     ("M-n" . bnb/scroll-up-1)
     ("M-p" . bnb/scroll-down-1))
    
  3. Align Regexp

    When selecting a region, a quick trip to align-regexp can align all of that nasty text.

    (bind-key "C-c TAB" 'align-regexp)
    

5.8.4 Kill current buffer

Another great tip from Pragmatic Emacs, use kill-this-buffer to kill the current buffer instead of asking which one. I'm not overriding the C-x k default, but added a C-x C-k alternative.

(defun bnb/kill-this-buffer ()
  "Kill the current buffer"
  (interactive)
  (kill-buffer (current-buffer)))

(bind-keys
 ("C-x C-k" . bnb/kill-this-buffer))

5.9 Minibuffer History

Let's get rid of duplicates in the minibuffer history.

(setq history-delete-duplicates t)

This saves the minibuffer histories to preserve across emacs sessions.

(setq savehist-additional-variables '(search-ring regexp-search-ring)
      savehist-file "~/.emacs.d/savehist")
(savehist-mode t)

5.10 Abbrev

The following block is courtesy of Endless Parentheses.

(bind-key "C-x C-i" 'bnb/ispell-word-then-abbrev)

(defun bnb/ispell-word-then-abbrev (p)
  "Call `ispell-word'. Then create an abbrev for the correction
    made. With prefix P, create local abbrev. Otherwise, it will be
    global."
  (interactive "P")
  (let ((bef (downcase (or (thing-at-point 'word) ""))) aft)
    (call-interactively 'ispell-word)
    (setq aft (downcase (or (thing-at-point 'word) "")))
    (unless (string= aft bef)
      (message "\"%s\" now expands to \"%s\" %sally"
               bef aft (if p "loc" "glob"))
      (define-abbrev
        (if p global-abbrev-table local-abbrev-table)
        bef aft))))

(use-package abbrev
  :delight " ⚆"
  :config
  (setq save-abbrevs t)
  (setq-default abbrev-mode t))

5.11 Hippie Expand

Try to expand the text before point in an intelligent way. Repeat the keypress to cycle through options.

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

5.12 Emacs Bookmarks

http://emacswiki.org/emacs/BookMarks

Keystroke Action
C-x r m Set a bookmark
C-x r b Jump to a bookmark
C-x r l List your bookmarks
M-x bookmark-delete Delete bookmark by name

I will auto-save my bookmarks.

(setq bookmark-save-flag t)

5.13 Writegood Mode

This mode installs through the ELPA system.

(use-package writegood-mode
  :ensure t
  :bind
  ("C-c g"     . writegood-mode)
  ("C-c C-g g" . writegood-grade-level)
  ("C-c C-g e" . writegood-reading-ease))

5.14 Spell Checking

This site has an interesting suggestion on how to use aspell for CamelCase spell checking.

  (cond
   ((executable-find "aspell")
    (setq ispell-program-name (executable-find "aspell")
    ispell-extra-args '("--sug-mode=ultra" "--lang=en_US")))
   (t (setq ispell-program-name nil)
(message "No aspell found!")))

  (bind-key "H-$" 'ispell-word)

5.15 Proselint

  (with-eval-after-load "flycheck-mode"
    (flycheck-define-checker proselint
"A linter for prose"
:command ("proselint" source-inplace)
:error-patterns
((warning line-start (file-name) ":" line ":" column ": "
    (id (one-or-more (not (any " "))))
    (message (one-or-more not-newline)
       (zero-or-more "\n" (any " ") (one-or-more not-newline)))
    line-end))
:modes (text-mode markdown-mode gfm-mode org-mode))
    (add-to-list 'flycheck-checkers 'proselint))

5.16 Development

For any lisp development, the following is nice to have.

(show-paren-mode t)

While developing, documentation is nice to have handy and automatic.

(add-hook 'cperl-mode-hook 'turn-on-eldoc-mode)
(add-hook 'eshell-mode-hook 'turn-on-eldoc-mode)

5.17 Read-only helpers

For read-only files, look at them in view-mode which will enable vi-style navigation.

(use-package view
  :delight " 👁"
  :init (setq view-read-only t)
  :bind (:map view-mode-map
              ("n" . next-line    )
              ("p" . previous-line)
              ("j" . next-line    )
              ("k" . previous-line)
              ("l" . forward-char)
              ("h" . bnb/view/h)
              ("q" . bnb/view/q))
  :config
  (defun bnb/view/h ()
    "Setup a function to go backwards a character"
    (interactive)
    (forward-char -1))
  (defun bnb/view/q ()
    "Setup a function to quit `view-mode`"
    (interactive)
    (view-mode -1)))

5.18 Default File encoding

I like to have the files be utf-8 by default. Do let me know if I shouldn't do this, will you?

Set utf-8 for all coding systems except for the clipboard on windows. That one gets utf-16le to be compatible.

(prefer-coding-system       'utf-8)
(set-default-coding-systems 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(set-language-environment 'utf-8)
(setq buffer-file-coding-system 'utf-8
      x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING))
;; MS Windows clipboard is UTF-16LE
(when (eq system-type 'windows-nt)
  (set-clipboard-coding-system 'utf-16le-dos))

5.19 Vimrc generic mode

Good to have to help with my pentadactyl configuration as it is in a vim-style of configuration.

  (define-generic-mode 'vimrc-generic-mode
    '()
    '()
    '(("^[\t ]*:?\\(!\\|ab\\|map\\|unmap\\)[^\r\n\"]*\"[^\r\n\"]*\\(\"[^\r\n\"]*\"[^\r\n\"]*\\)*$"
 (0 font-lock-warning-face))
("\\(^\\|[\t ]\\)\\(\".*\\)$"
 (2 font-lock-comment-face))
("\"\\([^\n\r\"\\]\\|\\.\\)*\""
 (0 font-lock-string-face)))
    '("/vimrc\\'" "\\.vim\\(rc\\)?\\'")
    '((lambda ()
  (modify-syntax-entry ?\" ".")))
    "Generic mode for Vim configuration files.")

5.20 Ediff single frame

I really dislike the multi-frame mode of ediff. It is confusing to use and really messes up my dwm usage. By explicitly setting the following setting, it forces ediff to use only one frame.

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

Now the control window will be a small window instead of a separate frame.

5.21 Git

5.21.1 Magit

Magit is a git interface for Emacs.

Here I set a global key for magit-status. Think 'G' looks like 6.

(use-package magit
  :ensure t
  :bind ("<f6>" . magit-status)
  :config
  (setq magit-last-seen-setup-instructions "1.4.0"))
  1. Release 1.4.0

    This magit release warns about auto-revert of buffers. This is turned on by default and I will keep that setting. To turn off the magit warning, I set magit-last-seen-setup-instructions to 1.4.0 as shown above.

  2. Forge

    Forge is the replacement for magithub. It uses `ghub` to work with git forges such as github and gitlab.

    (use-package forge
      :ensure t
      :commands (forge-pull))
    
  3. Magit Todos

    Within the 5.21.1 interface, show any todos listed in the files under version control.

    (use-package magit-todos
      :ensure t
      :after magit
      :hook (magit-mode-hook . magit-todos-mode))
    

5.21.2 Git timemachine   PENDING

Loop through the history of a file with some simple keystrokes (or wheeling up and down)

(use-package git-timemachine
  :disabled
  :ensure t
  :bind ("C-x C-g" . git-timemachine-toggle)
  :config
  (bind-keys
   :map git-timemachine-mode-map
   ("<M-up>" . git-timemachine-show-previous-revision)
   ("<M-down>" . git-timemachine-show-next-revision)
   ("<S-wheel-up>" . git-timemachine-show-previous-revision)
   ("<S-wheel-down>" . git-timemachine-show-next-revision)))

5.21.3 Smerge

Somewhere along the line, smerge was added. To facilitate editing merge conflicts, this hydra helps me do the work.

(add-hook
 'smerge-mode-hook
 (lambda ()
   (bind-key
    "C-c ^ h"
    (defhydra hydra-smerge (:color amaranth)
      ("a" smerge-keep-all "Keep all")
      ("b" smerge-keep-base "Keep base")
      ("m" smerge-keep-mine "Keep mine")
      ("o" smerge-keep-other "Keep other")
      ("n" smerge-next "Next conflict")
      ("p" smerge-previous "Previous conflict")
      ("r" smerge-resolve "Keep mine")
      ("q" nil "quit"))
    smerge-mode-map)))

5.22 Open/Edit This file

When I hit <F5>, open this file for editing. That way, any time I have something I need to remember for my emacs setting, it is just a button-push away.

(bind-key "<f5>"
          (lambda ()
            (interactive)
            (find-file "~/.emacs.d/bnb-emacs/Readme.org")))

5.23 Sounds

I dislike the bell ringing when I hit C-g. To silence the bell, just set the ring-bell-function to nil.

(setq visual-bell nil
      ring-bell-function `(lambda () nil))

5.24 Midnight Mode

This mode looks at midnight and kills any inactive buffers. By default, inactive means is any buffer untouched for three days.

(use-package midnight
  :ensure t
  :defer 10)

5.25 Which Key

This helpful little package makes it easy to remember emacs prefixed commands. Start typing a prefix such as C-x after a brief delay, the options for any following commands are shown.

I am using a setup that tries the right side of emacs first, and punts to a bottom window if there is not enough room.

(use-package which-key
  :ensure t
  :delight which-key-mode
  :init
  (which-key-mode)
  (which-key-setup-side-window-right-bottom)
  (setq which-key-max-description-length 60))

5.26 Ace Utilities

5.26.1 Ace Flyspell

Turn on ace-flyspell when flyspell is enabled.

(use-package ace-flyspell
  :ensure t
  :commands (ace-flyspell-setup)
  :bind
  ("H-s" . hydra-fly/body)
  :init
  (add-hook 'flyspell-mode-hook 'ace-flyspell-setup)
  (defhydra hydra-fly (:color pink)
    ("n" flyspell-goto-next-error "Next error")
    ("c" ispell-word "Correct word")
    ("j" ace-flyspell-jump-word "Jump word")
    ("." ace-flyspell-dwim "dwim")
    ("q" nil "Quit")))

5.26.2 Ace Isearch   PENDING

Supercharge isearch to vary its behavior depending on the input. The C-' key let's me jump to the isearch match easily with the ace-jump methods.

(use-package ace-isearch
  :ensure t
  :bind (:map isearch-mode-map
              ("C-'" . ace-isearch-jump-during-isearch))
  :delight ace-isearch-mode
  :config
  (global-ace-isearch-mode t)
  (setq ace-isearch-input-length 8))

5.26.3 Ace Link

In modes with links, use o to jump to links. Map M-o to do the same in org-mode.

    (use-package ace-link
:ensure t
:bind (:map org-mode-map
      ("M-o" . ace-link-org))
:config (ace-link-setup-default))

5.26.4 Ace Window

Instead of C-x o traversal, ace-window mode provides numbers for quick window access

Set the keys to something other than the default numbers. Note that this also limits the number of windows that can be used, but given my usage, I doubt it goes up to 'j' often.

Also, I modify the face attribute to make the window numbers large.

After reading the wiki, I supercharged the interface for ace-window.

(use-package ace-window
  :ensure t
  :bind
  ("H-a"    . ace-window)
  ("<f9> a" . ace-window)
  :config
  (setq aw-keys '(?j ?k ?l ?\; ?n ?m)
        aw-leading-char-style 'path
        aw-dispatch-always t
        aw-dispatch-alist
        '((?x aw-delete-window "Ace - Delete Window")
          (?c aw-swap-window   "Ace - Swap window")
          (?n aw-flip-window   "Ace - Flip window")
          (?v aw-split-window-vert "Ace - Split Vert Window")
          (?h aw-split-window-horz "Ace - Split Horz Window")
          (?m delete-other-windows "Ace - Maximize Window")
          (?b balance-windows)))

  (defhydra hydra-window-size (:color amaranth)
    "Window size"
    ("h" shrink-window-horizontally "shrink horizontal")
    ("j" shrink-window "shrink vertical")
    ("k" enlarge-window "enlarge vertical")
    ("l" enlarge-window-horizontally "enlarge horizontal")
    ("q" nil "quit"))
  (add-to-list 'aw-dispatch-alist '(?w hydra-window-size/body) t)

  (defhydra hydra-window-frame (:color red)
    "Frame"
    ("f" make-frame "new frame")
    ("x" delete-frame "delete frame")
    ("q" nil "quit"))
  (add-to-list 'aw-dispatch-alist '(?\; hydra-window-frame/body) t)

  (defhydra hydra-window-scroll (:color amaranth)
    "Scroll other window"
    ("n" scroll-other-window "scroll")
    ("p" scroll-other-window-down "scroll down")
    ("q" nil "quit"))
  (add-to-list 'aw-dispatch-alist '(?o hydra-window-scroll/body) t)

  (set-face-attribute 'aw-leading-char-face nil :height 2.0))

5.26.5 Avy Goto

It is time to make some shortcuts for jumping to see if they make sense for me. The interesting feature that I can use is that these highlight text an all shown buffers.

(use-package avy
  :ensure t
  :bind
  ("H-SPC" . avy-goto-char-timer)
  ("H-w"   . avy-goto-word-1)
  ("H-c"   . avy-goto-char-2)
  ("H-l"   . avy-goto-line)
  ("H-d"   . avy-goto-word-0)
  ("<f9> SPC" . avy-goto-char-timer)
  ("C-c g" . avy-goto-word-1)
  ("M-g l" . avy-goto-line)
  ("M-g c" . avy-goto-char-2)
  ("M-g w" . avy-goto-word-0))

The commands begin with the normal prefix of M-g for the goto commands and use l,c and w for lines, characters and words respectively.

The char version I use here is the two-element version. For single character jumping, I have the Ace Isearch mode below that will facilitate quick jumping.

5.26.6 Avy Zap

Zap to char using avy.

(use-package avy-zap
  :ensure t
  :bind ("M-z" . avy-zap-to-char-dwim)
  ("M-Z" . avy-zap-up-to-char-dwim))

5.27 Edit Server

The edit server talks to Chrome and uses emacs to edit any text areas. I start this server here.

(use-package edit-server
  :ensure t
  :defer 10
  :init
  (edit-server-start))

Chrome needs to have the proper extension installed there too for the installation to be complete.

5.28 Regexp-Builder

Emacs regular expressions are not the easiest to use out of the box. Emacs now has regexp-builder to assist you in building the correct regexp as you type.

However, to complicate matters, there are five different syntaxes of regular expression that the builder can use. The string syntax is what I tend to use most in searching and replacing, so I will make that my default.

(setq reb-re-syntax 'string)
Key Binding Meaning
C-c TAB Switch syntax
C-c C-e Sub-expression mode (show matching groups)
C-c C-s/r Search forward/backward
C-c C-w Copy regexp to kill ring
C-c C-q Quit the builder

Be sure to consult the syntax of regular expressions to learn more about the weird backslashing.

5.29 IBuffer

Use ibuffer instead of list-buffers for buffer management. The most visible difference being the coloring that ibuffer uses.

I also squash any empty groups from being displayed and add hooks to automatically set the filter groups and update contents.

(bind-key "C-x C-b" 'ibuffer)

(setq ibuffer-show-empty-filter-groups nil)

(add-hook 'ibuffer-mode-hook
          '(lambda ()
             (ibuffer-auto-mode 1)
             (ibuffer-switch-to-saved-filter-groups "Standard")))

5.29.1 Groups

The buffer list splits into arbitrary groups for easier management. Below I create an "Org" group for org-mode buffers.

(setq ibuffer-saved-filter-groups
      '(("Standard"
         ("Emacs" (or (filename . ".*bnb-emacs.*")
                      (mode . emacs-lisp-mode)))
         ("Org" (mode . org-mode))
         ("Magit" (name . "\*magit"))
         ("Mail" (or (mode . message-mode)
                     (mode . mail-mode)))
         ("HTML" (mode . html-mode))
         ("Help" (or (name . "\*Help\*")
                     (name . "\*Apropos\*")
                     (name . "\*info\*"))))))
  1. VC Grouping

    The ibuffer-vc package provides groups according to version control sets. Here I setup a small keybinding to get to the filtered vc groups. The keys / R will go back to the standard view.

    (use-package ibuffer-vc :ensure t
      :bind
      (:map ibuffer-mode-map
            ("/ v" . ibuffer-vc-set-filter-groups-by-vc-root)))
    

5.30 Multiple Cursors

This interface is a mix of an example on the hydra wiki and my own additions.

I think that the key thing is remembering to not have this affect all cursors when prompted. Otherwise, it seems, the cursors are duplicated in strange ways.

(use-package multiple-cursors
  :ensure t
  :bind
  ("H-m"   . hydra-mc/body)
  ("C-x m" . hydra-mc/body)
  ("s-<mouse-1>" . mc/add-cursor-on-click)
  ("C-x M" . compose-mail)
  :config
  (defhydra hydra-mc (:hint nil)
    "
  ^Up^            ^Down^        ^Miscellaneous^
   -----------------------------------------------------------
   [_p_]   Next    [_n_]   Next    [_l_] Edit lines  [_x_] Arrows
   [_P_]   Skip    [_N_]   Skip    [_a_] Mark all    [_g_] Regexp
   [_M-p_] Unmark  [_M-n_] Unmark  [_q_] Quit"
    ("l"   mc/edit-lines :exit t)
    ("a"   mc/mark-all-like-this-dwim :exit t)
    ("n"   mc/mark-next-like-this)
    ("N"   mc/skip-to-next-like-this)
    ("M-n" mc/unmark-next-like-this)
    ("p"   mc/mark-previous-like-this)
    ("P"   mc/skip-to-previous-like-this)
    ("M-p" mc/unmark-previous-like-this)
    ("g"   mc/mark-all-in-region-regexp :exit t)
    ("r"   mc/mark-sgml-tag-pair :exit t)
    ("x"   mc/mark-more-like-this-extended)
    ("q"   nil))
  (add-hydra-mc-funcs))

(defun add-hydra-mc-funcs ()
  "Add my hydra-mc funcs to the proper whitelist"
  (let* ((hydra-mc-funcs
          (cl-remove-if-not
           'functionp
           (apply #'append hydra-mc/heads)))
         (mc-funcs-to-ignore (cl-intersection
                              hydra-mc-funcs
                              mc--default-cmds-to-run-once))
         (funcs-to-whitelist
          (cl-mapcar
           (lambda (x) (intern (concat "hydra-mc/" (symbol-name x))))
           mc-funcs-to-ignore)))
    (let (value)
      (dolist (element funcs-to-whitelist nil)
        (add-to-list 'mc/cmds-to-run-once element)))))

5.31 Expand Region

Nice way to expand selections to semantic regions. Read more on https://github.com/magnars/expand-region.el.

(use-package expand-region
  :ensure t
  :bind ("C-=" . er/expand-region))

5.32 Hooks

In general, hooks may be best with other configuration items, but if they are just pieces on their own, they belong here.

5.32.1 Ensure proper lisping

(add-hook 'after-save-hook  'check-parens nil t)

5.32.2 Auto Reverting in modes

For view-only buffers rendering content, it is useful to have them auto-revert in case of changes.

(add-hook 'doc-view-mode-hook 'auto-revert-mode)
(add-hook 'image-mode 'auto-revert-mode)

5.33 Recentf

I enable emacs remembering recently open files.

(recentf-mode t)

5.34 Executable Scripts on save

Taken from: http://mbork.pl/2015-01-10_A_few_random_Emacs_tips

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

5.35 Scroll Screen Position

This is one of those cool finds for a problem I mostly knew that I had. I often hit C-v by accident and lose my place. With the following setting, M-v completely undoes the scroll leaving the cursor back in the original position.

(setq scroll-preserve-screen-position 'always)

Thanks to http://irreal.org/blog/?p=3963 for the tip.

5.36 Unique Buffer Names

When editing files with the same name, but different location, a unique identifier (based on path) is preferred over a number.

(use-package uniquify
  :defer 10
  :config
  (setq uniquify-buffer-name-style 'post-forward
        uniquify-separator ":"))

5.37 Focus Mode

Dim everything except for the thing-at-point. Improves focus when reading code and text.

(use-package focus
  :ensure t
  :bind
  ("C-c f" . focus-mode)
  ("C-c F" . focus-read-only-mode))

5.38 Relative line numbers

(use-package linum-relative
  :defer 10
  :ensure t)

5.39 ImageMagick

Register file types if we can.

(when (fboundp 'imagemagick-register-types)
  (imagemagick-register-types))

5.40 PDF Tools

This replaces the built-in DocView for PDF files. Find out the details on the repo.

(defcustom bnb/homebrew-prefix ""
  "Prefix to use for an alternative path to homebrew items"
  :type 'string
  :group 'bnb)

(setenv "PKG_CONFIG_PATH"
        (let ((hbp bnb/homebrew-prefix))
          (setq pdf-info-epdfinfo-program (concat hbp "/usr/local/bin/epdfinfo"))
          (mapconcat 'identity
                     (list (concat hbp "/usr/local/Cellar/libffi/3.2.1/lib/pkgconfig")
                           (concat hbp "/usr/local/Cellar/zlib/1.2.8/lib/pkgconfig")
                           (concat hbp "/usr/local/lib/pkgconfig")
                           "/opt/X11/lib/pkgconfig")
                     ":")))

(use-package pdf-tools
  :defer t
  :ensure t
  :init (add-hook 'pdf-view-mode-hook
                  (lambda ()
                    (nlinum-mode 0)))
  :config
  (custom-set-variables '(pdf-tools-handle-upgrades nil))
  (setq-default pdf-view-display-size 'fit-page)
  (pdf-loader-install))

5.41 Helpful

(use-package helpful
  :ensure t
  :bind
  ("C-h f" . helpful-function)
  ("C-h x" . helpful-command)
  ("C-h z" . helpful-macro))

5.42 All the icons

https://github.com/domtronn/all-the-icons.el

(use-package all-the-icons
  :ensure t
  :defer 10)

5.42.1 All the icons Dired

Dired can also make use of pretty icons. Because of this integration, sunrise-commander also gets some nice icons.

(use-package all-the-icons-dired
  :ensure t
  :commands (all-the-icons-dired-mode)
  :config (add-hook 'dired-mode-hook 'all-the-icons-dired-mode))

5.43 Discover my mode

If you ever wanted to know what keybindings were active in the current buffer for the current mode, then it is now just a keypress away.

(use-package discover-my-major
  :ensure t
  :bind (("C-h C-m" . discover-my-major)
   ("C-h C-d" . discover-my-mode)))

5.44 Easy Kill

While looking for a way to store the filename in the clipboard, I ran across easy-kill. Not only will it grab the filename, but provides ways to grab all sorts of fun things.

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

5.45 Undo Tree

It's time to give undo-tree another try. The following hydra makes repeated undo/redo easier.

  (use-package undo-tree
    :delight "¬"
    :ensure t
    :config
    (global-undo-tree-mode)
    (defhydra hydra-undo-tree (:color yellow :hint nil)
"
    _p_: undo _n_: redo _s_: save _l_: load  "
("p" undo-tree-undo)
("n" undo-tree-redo)
("s" undo-tree-save-history)
("l" undo-tree-load-history)
("u" undo-tree-visualize "visualize" :color blue)
("q" nil "quit" :color blue))
    (global-set-key (kbd "H-,") 'hydra-undo-tree/body))

5.46 Adaptive Fill   PENDING

Try to keep any prefixed elements of the first line for paragraph filling.

(use-package filladapt
  :delight " ▦"
  :ensure t
  :commands filladapt-mode
  :init (setq-default filladapt-mode t)
  :hook ((text-mode . filladapt-mode)
         (org-mode . turn-off-filladapt-mode)
         (prog-mode . turn-off-filladapt-mode)))

6 Style

The following sections describe items that affect the visual elements of Emacs.

6.1 Frame Changes

These following items make Emacs really beautiful on every platform. I remove the menu bar, tool bar and the scroll bar for starters. Then, I setup the fringe area with some items

(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))

6.2 Window Changes

In the fringe area, I like to have markers to show me where the buffer begins/ends on the right. On the left, I have emacs show little dashes where empty lines exist.

In the title bar, I have it print the buffer name, full file name and size.

(setq-default indicate-buffer-boundaries 'right)
(setq-default indicate-empty-lines t)
(setq-default frame-title-format '("%b %f %I"))

6.3 Faces

6.3.1 Default Fonts

I love Source Code Pro, but I'm giving Fira Code a try for some extra ligature fun.

https://github.com/tonsky/FiraCode/wiki/Setting-up-Emacs

(use-package ring
  :bind ("H-f" . bnb/font-next)
  ("H-F" . bnb/font-prev)
  :config
  (setq bnb/fontlist '("Fira Code-13" "Source Code Pro-13")
        bnb/font-ring
        (ring-convert-sequence-to-ring bnb/fontlist)
        bnb/font
        (ring-ref bnb/font-ring 0))
  (defun bnb/font-apply (font)
    "Change the default font to FONT."
    (set-frame-font (setq bnb/font font))
    (message "Set default font to %s" bnb/font))
  (defun bnb/font-next ()
    "Cycle the default font to the next in the ring."
    (interactive)
    (bnb/font-apply (ring-next bnb/font-ring bnb/font)))
  (defun bnb/font-prev ()
    "Cycle the default font to the previous in the ring."
    (interactive)
    (bnb/font-apply (ring-prev bnb/font-ring bnb/font)))
  (set-frame-font bnb/font))

(defconst fira-code-font-lock-keywords-alist
  (mapcar (lambda (regex-char-pair)
            `(,(car regex-char-pair)
              (0 (prog1 ()
                   (compose-region (match-beginning 1)
                                   (match-end 1)
                                   ;; The first argument to concat is a string containing a literal tab
                                   ,(concat " " (list (decode-char 'ucs (cadr regex-char-pair)))))))))
          '(("\\(www\\)"                   #Xe100)
            ("[^/]\\(\\*\\*\\)[^/]"        #Xe101)
            ("\\(\\*\\*\\*\\)"             #Xe102)
            ("\\(\\*\\*/\\)"               #Xe103)
            ("\\(\\*>\\)"                  #Xe104)
            ("[^*]\\(\\*/\\)"              #Xe105)
            ("\\(\\\\\\\\\\)"              #Xe106)
            ("\\(\\\\\\\\\\\\\\)"          #Xe107)
            ("\\({-\\)"                    #Xe108)
            ("\\(\\[\\]\\)"                #Xe109)
            ("\\(::\\)"                    #Xe10a)
            ("\\(:::\\)"                   #Xe10b)
            ("[^=]\\(:=\\)"                #Xe10c)
            ("\\(!!\\)"                    #Xe10d)
            ("\\(!=\\)"                    #Xe10e)
            ("\\(!==\\)"                   #Xe10f)
            ("\\(-}\\)"                    #Xe110)
            ("\\(--\\)"                    #Xe111)
            ("\\(---\\)"                   #Xe112)
            ("\\(-->\\)"                   #Xe113)
            ("[^-]\\(->\\)"                #Xe114)
            ("\\(->>\\)"                   #Xe115)
            ("\\(-<\\)"                    #Xe116)
            ("\\(-<<\\)"                   #Xe117)
            ("\\(-~\\)"                    #Xe118)
            ("\\(#{\\)"                    #Xe119)
            ("\\(#\\[\\)"                  #Xe11a)
            ("\\(##\\)"                    #Xe11b)
            ("\\(###\\)"                   #Xe11c)
            ("\\(####\\)"                  #Xe11d)
            ("\\(#(\\)"                    #Xe11e)
            ("\\(#\\?\\)"                  #Xe11f)
            ("\\(#_\\)"                    #Xe120)
            ("\\(#_(\\)"                   #Xe121)
            ("\\(\\.-\\)"                  #Xe122)
            ("\\(\\.=\\)"                  #Xe123)
            ("\\(\\.\\.\\)"                #Xe124)
            ("\\(\\.\\.<\\)"               #Xe125)
            ("\\(\\.\\.\\.\\)"             #Xe126)
            ("\\(\\?=\\)"                  #Xe127)
            ("\\(\\?\\?\\)"                #Xe128)
            ("\\(;;\\)"                    #Xe129)
            ("\\(/\\*\\)"                  #Xe12a)
            ("\\(/\\*\\*\\)"               #Xe12b)
            ("\\(/=\\)"                    #Xe12c)
            ("\\(/==\\)"                   #Xe12d)
            ("\\(/>\\)"                    #Xe12e)
            ("\\(//\\)"                    #Xe12f)
            ("\\(///\\)"                   #Xe130)
            ("\\(&&\\)"                    #Xe131)
            ("\\(||\\)"                    #Xe132)
            ("\\(||=\\)"                   #Xe133)
            ("[^|]\\(|=\\)"                #Xe134)
            ("\\(|>\\)"                    #Xe135)
            ("\\(\\^=\\)"                  #Xe136)
            ("\\(\\$>\\)"                  #Xe137)
            ("\\(\\+\\+\\)"                #Xe138)
            ("\\(\\+\\+\\+\\)"             #Xe139)
            ("\\(\\+>\\)"                  #Xe13a)
            ("\\(=:=\\)"                   #Xe13b)
            ("[^!/]\\(==\\)[^>]"           #Xe13c)
            ("\\(===\\)"                   #Xe13d)
            ("\\(==>\\)"                   #Xe13e)
            ("[^=]\\(=>\\)"                #Xe13f)
            ("\\(=>>\\)"                   #Xe140)
            ("\\(<=\\)"                    #Xe141)
            ("\\(=<<\\)"                   #Xe142)
            ("\\(=/=\\)"                   #Xe143)
            ("\\(>-\\)"                    #Xe144)
            ("\\(>=\\)"                    #Xe145)
            ("\\(>=>\\)"                   #Xe146)
            ("[^-=]\\(>>\\)"               #Xe147)
            ("\\(>>-\\)"                   #Xe148)
            ("\\(>>=\\)"                   #Xe149)
            ("\\(>>>\\)"                   #Xe14a)
            ("\\(<\\*\\)"                  #Xe14b)
            ("\\(<\\*>\\)"                 #Xe14c)
            ("\\(<|\\)"                    #Xe14d)
            ("\\(<|>\\)"                   #Xe14e)
            ("\\(<\\$\\)"                  #Xe14f)
            ("\\(<\\$>\\)"                 #Xe150)
            ("\\(<!--\\)"                  #Xe151)
            ("\\(<-\\)"                    #Xe152)
            ("\\(<--\\)"                   #Xe153)
            ("\\(<->\\)"                   #Xe154)
            ("\\(<\\+\\)"                  #Xe155)
            ("\\(<\\+>\\)"                 #Xe156)
            ("\\(<=\\)"                    #Xe157)
            ("\\(<==\\)"                   #Xe158)
            ("\\(<=>\\)"                   #Xe159)
            ("\\(<=<\\)"                   #Xe15a)
            ("\\(<>\\)"                    #Xe15b)
            ("[^-=]\\(<<\\)"               #Xe15c)
            ("\\(<<-\\)"                   #Xe15d)
            ("\\(<<=\\)"                   #Xe15e)
            ("\\(<<<\\)"                   #Xe15f)
            ("\\(<~\\)"                    #Xe160)
            ("\\(<~~\\)"                   #Xe161)
            ("\\(</\\)"                    #Xe162)
            ("\\(</>\\)"                   #Xe163)
            ("\\(~@\\)"                    #Xe164)
            ("\\(~-\\)"                    #Xe165)
            ("\\(~=\\)"                    #Xe166)
            ("\\(~>\\)"                    #Xe167)
            ("[^<]\\(~~\\)"                #Xe168)
            ("\\(~~>\\)"                   #Xe169)
            ("\\(%%\\)"                    #Xe16a)
            ;; ("\\(x\\)"                   #Xe16b) This ended up being hard to do properly so i'm leaving it out.
            ("[^:=]\\(:\\)[^:=]"           #Xe16c)
            ("[^\\+<>]\\(\\+\\)[^\\+<>]"   #Xe16d)
            ("[^\\*/<>]\\(\\*\\)[^\\*/<>]" #Xe16f))))

(defun add-fira-code-symbol-keywords ()
  (font-lock-add-keywords nil fira-code-font-lock-keywords-alist))

;;(add-fira-code-symbol-keywords)
  1. Unicode

    Support unicode fonts

    (use-package unicode-fonts
      :ensure t
      :defer 10
      :init
      (unicode-fonts-setup))
    

6.3.2 Trying out fonts on Windows

(defun bnb/windows-set-font ()
  "Use Windows font selection to set the default font."
  (interactive)
  (set-face-attribute 'default nil :font (w32-select-font)))

6.3.3 Dynamic Font sizes

Changing font sizes in presentations is crucial to have at hand. I use the following keybindings. C-- overrides the negative argument function, but that one is also accessible by M--.

(defun bnb/change-frame-font-size (fn)
  "Change the frame font size according to function FN."
  (let* ((font-name (frame-parameter nil 'font))
         (decomposed-font-name (x-decompose-font-name font-name))
         (font-size (string-to-number (aref decomposed-font-name 5))))
    (aset decomposed-font-name 5 (int-to-string (funcall fn font-size)))
    (set-frame-font (x-compose-font-name decomposed-font-name))))

(defun bnb/frame-text-scale-increase ()
  "Increase the frame font size by 1."
  (interactive)
  (bnb/change-frame-font-size '1+))

(defun bnb/frame-text-scale-decrease ()
  "Decrease the frame font size by 1."
  (interactive)
  (bnb/change-frame-font-size '1-))

(bind-keys
 ("C-+" . text-scale-increase)
 ("C--" . text-scale-decrease)
 ("s--" . bnb/frame-text-scale-decrease)
 ("s-+" . bnb/frame-text-scale-increase)
 ("s-=" . bnb/frame-text-scale-increase))

6.3.4 Mode Line Style

I dislike the box around the mode-line making it look like a button. I disable (set to nil) this face attribute, box, to get a flat feel. Be sure to do it to all mode-line faces that have this attribute.

(set-face-attribute 'mode-line nil :box nil)
(set-face-attribute 'mode-line-inactive nil :box nil)
(set-face-attribute 'mode-line-highlight nil :box nil)

6.3.5 Missing Glyphs

If I ever use a font with a missing glyph, this will let Emacs check the Symbola font for the missing data.

Download Symbola if you do not have it.

(set-fontset-font "fontset-default" nil
                  (font-spec :size 20 :name "Symbola"))

6.3.6 Cursor width

Make the cursor the full width of the character at point.

(setq x-stretch-cursor t)

6.4 Themes

I used to have something else from Greg Hendershot, but now I created my own theme override and layer it corectly.

(defun bnb/disable-all-themes ()
  "Disable all enabled themes."
  (interactive)
  (mapc #'disable-theme custom-enabled-themes))

On creating themes: https://www.gnu.org/software/emacs/manual/html_node/emacs/Creating-Custom-Themes.html#Creating-Custom-Themes

6.4.1 Extra Themes

I like to have a few options for themes easily available. This set respresents my favorite go-to combinations.

(use-package minimal-theme :ensure t :defer t)
(use-package gruvbox-theme :ensure t :defer t)
(use-package material-theme :ensure t :defer t)
(use-package tango-plus-theme :ensure t :defer t)
(use-package color-theme-sanityinc-tomorrow :ensure t :defer t)
;; dichromacy
;; adwaita

6.5 Sky Color Clock

This is a nice addition to any modeline. In a little block, it shows the date, time, moon phase

(use-package sky-color-clock
  :defer 10
  :quelpa
  (sky-color-clock :fetcher github :repo "zk-phi/sky-color-clock")
  :config
  (sky-color-clock-initialize 40)
  (setq sky-color-clock-enable-emoji-icon t)
  (sky-color-clock-initialize-openweathermap-client
   bnb/openweathermap-api-key
   bnb/openweathermap-city-id))

6.6 Doom Modeline

(use-package doom-modeline
  :ensure t
  :init (doom-modeline-mode t)
  :config
  (setq
   doom-modeline-height 0))

6.7 ⁂ Mode

Let's try ⁂-mode for an interesting mini-buffer line.

(defun bnb/smaller-sky-color ()
  (let* ((scc (sky-color-clock))
         (len (length scc)))
    (add-face-text-property 0 len '(:height 80) t scc)
    (add-face-text-property 0 len '(:justification right) t scc)
    scc))

(use-package asterism-mode
  :quelpa
  (asterism-mode :fetcher gitlab :repo "lunar.studio/asterism-mode")
  :config
  (setq ⁂-format '((:eval (bnb/smaller-sky-color)))))

6.8 Font lock profiling

Allow for profiling of font-locking.

(use-package font-lock-profiler
  :commands (font-lock-profiler-start
             font-lock-profiler-buffer
             font-lock-profiler-region))

6.9 Pretty Symbols

Emacs can be so pretty sometimes.

6.9.1 Pretty mode

(use-package pretty-mode
  :disabled
  :config
  (global-pretty-mode t)
  (pretty-activate-groups
   '(:sub-and-superscripts :greek :arithmetic-nary)))

6.9.2 Prettify symbols mode

Some reading to consider:

(use-package prog-mode ; Contains pretty-symbols-mode
  :config
  (setq prettify-symbols-unprettify-at-point 'right-edge)
  (global-prettify-symbols-mode t)
  (add-hook
   'python-mode-hook
   (lambda ()
     (mapc (lambda (pair) (push pair prettify-symbols-alist))
           '(;; Syntax
             ("def" .      ?ℱ)
             ("not" .      ?❗)
             ("in" .       ?∈)
             ("not in" .   ?∉)
             ("return" .   ?⟼)
             ("yield" .    ?⟻)
             ("for" .      ?∀)
             ;; Base Types
             ("int" .      ?ℤ)
             ("float" .    ?ℝ)
             ("str" .      ?𝕊)
             ("True" .     ?𝕋)
             ("False" .    ?𝔽)
             ;; Mypy
             ("Dict" .     ?𝔇)
             ("List" .     ?ℒ)
             ("Tuple" .    ?⨂)
             ("Set" .      ?Ω)
             ("Iterable" . ?𝔊)
             ("Any" .      ?❔)
             ("Union" .    ?∪))))))

6.10 Color

6.10.1 Rainbow Mode

In order to see the colors in the buffer this mode higlights color words and definitions with their values.

(use-package rainbow-mode
  :commands (rainbow-mode)
  :ensure t)

6.10.2 KureColor

Color helper KureColor allows easy modifications of hue, saturation and brightness.

(use-package kurecolor
  :bind (("H-k" . kurecolor-increase-hue-by-step)
         ("H-j" . kurecolor-decrease-hue-by-step)
         ("s-k" . kurecolor-increase-saturation-by-step)
         ("s-j" . kurecolor-decrease-saturation-by-step)
         ("s-l" . kurecolor-increase-brightness-by-step)
         ("s-h" . kurecolor-decrease-brightness-by-step))
  :ensure t)

7 BNB Helpers

This is a collection of code specific to how I use emacs. Some are from different websites or other Emacs users.

7.1 Exit behavior

Instead of exiting emacs, I prefer to leave it running and only minimize (iconize) it. Especially since I use server with emacs, it is advantageous to not kill my session by accident (muscle-memory). This idea is from Emacs-Fu.

To accomplish this, I advise the kill-emacs function. But first, I need to have some pieces in place to perform some functionality if a killing operation triggers (keystrokes, button presses, etc).

I create a variable, bnb/kill-emacs-hooks, for functions that need to run before emacs is killed.

(defvar bnb/kill-emacs-hooks)
(add-hook 'bnb/kill-emacs-hooks
          (lambda () (if (functionp 'server-edit)(server-edit))))

In the hook above, I call the server-edit function to act as if I am closing emacs for a file opened via the server API. This has the effect of providing an illusion of opening the editor on a specific file and then closing it with normal keypresses.

Next, I provide a flag and a function to set the flag if emacs is to really exit. We always need an escape hatch.

(defvar bnb/really-kill-emacs nil)
(defun bnb/kill-emacs ()
  "Actually kill emacs."
  (interactive)
  (setq bnb/really-kill-emacs t)
  (kill-emacs))

Now that mechanism is in place, it is time to augment kill-emacs with some advice around the function.

(defadvice kill-emacs (around bnb/pardon-emacs activate)
  "Only kill emacs if a prefix is set"
  (run-hooks 'bnb/kill-emacs-hooks)
  (if bnb/really-kill-emacs
      ad-do-it
    (iconify-frame)))

Now, when any event triggers a call to kill-emacs, a small set of activities happen. First, the bnb/kill-emacs-hooks execute and perform their jobs. Next, I check the flag for really exiting and then call the proper kill-emacs if true. Otherwise, the flag is false and emacs is simply iconified.

7.2 Workweeks

This is vestigal content from my Intel days and this generates their idea of a work week number.

(defun bnb/workweek ()
  "Return the current workweek number."
  (interactive)
  (let* ((now (current-time))
         (weeks (string-to-number
                 (format-time-string "%W" now)))
         (days (time-to-day-in-year now))
         (daynum (string-to-number
                  (format-time-string "%w" now)))
         (left (% days 7)))
    (if (and (= 0 daynum) (= left 0))
        weeks
      (+ 1 weeks))))

(defun bnb/workweek-string ()
  "Convert the current workweek into a string.

The string is of the format WW##."
  (interactive)
  (concat "WW"
          (number-to-string
           (bnb/workweek))))

(require 'calendar)
(defun bnb/workweek-from-gregorian (&optional date)
  "Calculate the workweek from the Gregorian calendar."
  (let* ((date (or date (calendar-current-date)))
         (year (calendar-extract-year date))
         (fst (calendar-day-of-week (list 1 1 year)))
         (x   (if (>= fst 4)1 0)))
    (+ x
       (car
        (calendar-iso-from-absolute
         (calendar-absolute-from-gregorian date))))))

(setq calendar-week-start-day 1
      calendar-intermonth-text
      '(propertize
        (format "%2d"
                (bnb/workweek-from-gregorian (list month day year)))
        'font-lock-face 'font-lock-function-name-face))

7.3 Better window splitting functions

http://www.reddit.com/r/emacs/comments/25v0eo/you_emacs_tips_and_tricks/chldury

These settings split the window and load a previous buffer (instead of the same buffer in both). This has a better chance of being what I want when splitting strings.

(defun bnb/vplit-last-buffer ()
  "When splitting the frame, load the last visited buffer."
  (interactive)
  (split-window-vertically)
  (other-window 1 nil)
  (switch-to-next-buffer))

(defun bnb/hsplit-last-buffer ()
  "When splitting the frame, load the last visited buffer."
  (interactive)
  (split-window-horizontally)
  (other-window 1 nil)
  (switch-to-next-buffer))

(bind-keys
 ("C-x 2" . bnb/vplit-last-buffer)
 ("C-x 3" . bnb/hsplit-last-buffer))

7.4 Weekly Time Reporting

This is a function to create an entry like a datetree, but using years and workweeks instead.

(defun bnb/find-year-create (year)
  "Find or create a [YEAR] in an Org journal."
  (let ((re "^\\**[ \t]+\\([12][0-9]\\{3\\}\\)")
        match)
    (org-narrow-to-subtree)
    (goto-char (point-min))
    (while (and (setq match (re-search-forward re nil t))
                (goto-char (match-beginning 1))
                (< (string-to-number (match-string 1)) year)))
    (cond
     ((not match)
      (goto-char (point-max))
      (or (bolp) (newline))
      (insert (format  "** %s\n" year)))
     ((= (string-to-number (match-string 1)) year)
      (goto-char (point-at-bol)))
     (t
      (beginning-of-line 1)
      (insert (format  "** %s\n" year))))))

(defun bnb/find-ww-create (ww)
  "Find or create a [WW] (workweek) in an Org journal."
  (let ((re "^\\**[ \t]+\\WW\\([0-9]\\{2\\}\\)")
        match)
    (org-narrow-to-subtree)
    (goto-char (point-min))
    (while (and (setq match (re-search-forward re nil t))
                (goto-char (match-beginning 1))
                (< (string-to-number (match-string 1)) ww)))
    (cond
     ((not match)
      (goto-char (point-max))
      (or (bolp) (newline))
      (insert (format "*** WW%02d\n" ww)))
     ((= (string-to-number (match-string 1)) ww)
      (goto-char (point-at-bol)))
     (t
      (beginning-of-line 1)
      (insert (format "*** WW%02d\n" ww))))))

(defun bnb/insert-weekly-time-sheet ()
  "Generated and insert a weekly time sheet generated from the default Org Agenda."
  (with-temp-buffer
    (insert
     (concat  "#+BEGIN: clocktable :maxlevel 3 :scope agenda-with-archives :block lastweek :fileskip0 t :properties (\"Score\") :indent nil \n"
              "#+TBLFM: $6='(org-clock-time% @2$4 $3..$5);%.1f::@2$2=vsum(@3$2..@>$2)\n"
              "#+END:\n\n"))
    (goto-char (point-min))
    (org-update-dblock)
    (buffer-substring (point-min) (point-max))))

(defun bnb/insert-weekly-clocking ()
  "Insert the weekly clocking clocking data."
  (let ((year (number-to-string (nth 2 (calendar-gregorian-from-absolute (org-today)))))
        (ww (bnb/workweek)))
    (goto-char (point-min))
    (goto-char (cdr (org-id-find "clocking")))
    (bnb/find-year-create (string-to-number year))
    (bnb/find-ww-create ww)))

7.5 Weekly Score Goal in Org-Agenda

I use a scoring system to keep track of my overall progress. This involves scoring my tasks and attributing my idea of impact of a particular done item.

To use these numbers, I do a weekly review on Monday and compare the numbers to past years/weeks/etc. To keep pushing forward, this little bit of code will insert a running status at the top of my agenda.

If I am on track for the given day (at or above the scaled goal), all is green. Otherwise, I get a warning type formatting above 80% and error type formatting under.

;; Define my goal to hit
(defvar bnb/weekly-score-goal 42)

;; Add up all the scores from DONE items in the agenda files
(defun bnb/agenda-score-goal ()
  "Add up scores from done items.

     In the agenda, this will show the number of done items and the
     target goal from `bnb/weekly-score-goal`."
  (let* ((score ;; Add up all scores from DONE items
          (apply '+
                 (org-map-entries
                  (lambda () (string-to-number (or (org-entry-get (point) "Score") "0")))
                  "/DONE" 'agenda)))
         (scaled-goal (* bnb/weekly-score-goal
                         (/ (string-to-number (format-time-string "%w"))
                            5.0)))
         (face (cond ((>= score scaled-goal) 'success)
                     ((>= score (* .8 scaled-goal)) 'warning)
                     (t 'error)))
         (goal-label (format "✧ Score Goal (%d): " scaled-goal))
         (goal-metric (format "%d/%d\n" score bnb/weekly-score-goal))
         (header-size (+ (string-width goal-label)
                         (string-width goal-metric)))
         (goal-separator (concat (make-string header-size ?┄) "\n")))
    (insert
     (concat
      (propertize goal-label 'face 'org-agenda-structure)
      (propertize goal-metric 'face face)
      (propertize goal-separator 'face 'org-agenda-structure)))))

;; This hook runs first in the agenda (and before it is set to read-only)
(add-hook 'org-agenda-mode-hook 'bnb/agenda-score-goal)

7.6 Auto-display agenda

John Weigley shows a way to display the agenda after some period of inactivity.

(defun bnb/jump-to-org-agenda ()
  "Create and jump to the bnb org agenda."
  (interactive)
  (let ((buf (get-buffer "*Org Agenda*"))
        wind)
    (if buf
        (if (setq wind (get-buffer-window buf))
            (select-window wind)
          (if (called-interactively-p)
              (progn
                (select-window (display-buffer buf t t))
                (org-fit-window-to-buffer))
            (with-selected-window (display-buffer buf)
              (org-fit-window-to-buffer))))
      (bnb/org-agenda-with-tip nil))))

(defun bnb/idle-agenda (&optional arg)
  "Set or cancel idle agenda timer based on [ARG]."
  (interactive "P")
  (setq bnb/iagenda
        (if arg
            (cancel-timer bnb/iagenda)
          (run-with-idle-timer 3600 t 'bnb/jump-to-org-agenda))))

7.7 Transparency

Using the ring package, these commands will cycles through transparency settings.

(use-package ring
  :commands (bnb/transparency-apply bnb/transparency-next bnb/transparency-previous
                                    bnb/transparency-cycle bnb/transparency-add)
  :config
  (setq bnb/transparency-ring
        (ring-convert-sequence-to-ring (list '(100 100) '(100 50) '(100 10) '(95 50) '(90 50) '(85 50)))
        bnb/transparency
        (ring-ref bnb/transparency-ring 0))

  (defun bnb/transparency-apply (trans)
    "Apply the TRANS alpha value to the frame."
    (set-frame-parameter (selected-frame) 'alpha (setq bnb/transparency trans)))

  (defun bnb/transparency-next ()
    "Apply the next transparency value in the ring `bnb/transparency-ring`."
    (interactive)
    (bnb/transparency-apply (ring-next bnb/transparency-ring bnb/transparency)))

  (defun bnb/transparency-previous ()
    "Apply the previous transparency value in the ring `bnb/transparency-ring`."
    (interactive)
    (bnb/transparency-apply (ring-previous bnb/transparency-ring bnb/transparency)))

  (defun bnb/transparency-cycle ()
    "Cycle to the next transparency setting."
    (interactive)
    (bnb/transparency-next))

  (defun bnb/transparency-add (active inactive)
    "Add ACTIVE and INACTIVE transparency values to the ring."
    (interactive "nActive Transparency:\nnInactive Transparency:")
    (ring-insert+extend bnb/transparency-ring (list active inactive) t)
    (bnb/transparency-apply (list active inactive))))

7.8 Styled HTML Export

This is how I get the one-page html output for Github Pages. There are two main parts to setting up and executing the export.

First, I use a SETUPFILE from https://github.com/fniessen/org-html-themes. Specifically, I use the readtheorg style.

Second, I setup the emacs theme correctly for nice code output. Syntax highlighting in the export will pull from the current theme. I don't want this. Instead, I want to specify which theme to use for every export.

The code below stores away the current list of enabled themes before disabling them all. Then, it enables my preferred export theme (sanityinc-tomorrow-day) before performing the export. Finally, it disables the last theme and renables all of the ones on the list.

(defun bnb/export-readme ()
  "Export the tangled org setting as html.

`sanityinc-tomorrow-day` is used to style the code exports."
  (interactive)
  (let ((themes custom-enabled-themes))
    (mapc 'disable-theme themes)
    (load-theme 'sanityinc-tomorrow-day)
    (org-html-export-to-html)
    (disable-theme 'sanityinc-tomorrow-day)
    (mapcar 'load-theme (reverse themes))))

7.9 Auto-indent when pasting

Automatically indent pasted blocks of text.

(dolist (command '(yank yank-pop))
  (eval `(defadvice ,command (after indent-region activate)
           (and (not current-prefix-arg)
                (let ((mark-even-if-inactive transient-mark-mode))
                  (indent-region (region-beginning) (region-end) nil))))))

7.10 Hide mode line

This is a fun one I picked from a now defunct website. This block of code hides the mode-line for the current buffer (window).

;; Setup buffer-local behavior
(defvar-local bnb/hide-mode-line-mode nil)
;; Setup minor mode
(define-minor-mode bnb/hide-mode-line-mode
  "Minor mode to hide mode-line in current buffer"
  :init-value nil
  :global nil
  :variable bnb/hide-mode-line-mode
  :group 'editing-basics
  (if bnb/hide-mode-line-mode
      (setq bnb/hide-mode-line-mode/saved-format mode-line-format
            mode-line-format nil)
    (setq mode-line-format bnb/hide-mode-line-mode/saved-format
          bnb/hide-mode-line-mode/saved-format nil))
  (force-mode-line-update)
  (redraw-display)
  (when (and (called-interactively-p 'interactive)
             bnb/hide-mode-line-mode)
    (run-with-idle-timer
     0 nil 'message
     (concat "Goodbye mode line!"
             "Use M-x bnb/hide-mode-line-mode to make the mode-line appear"))))

;; Bind global key
(bind-key "H-0" 'bnb/hide-mode-line-mode)

7.11 Org-column resizing

In order to resize the face when `org-column` mode is on, some advice is in order. The face used has a set :height that is not overridden by custom face settings.

To have a custom height, this advice prepends the list with an anonymous face with a height of 0.8. This setting happens first, so it wins.

(defun bnb/org-overlay-font-override (orig-fn beg end &optional txt face)
  (let ((bnbface (cons '(:height 0.8) face)))
      (funcall orig-fn beg end txt bnbface)))

(advice-add 'org-columns--new-overlay :around #'bnb/org-overlay-font-override)

;(advice-remove 'org-columns--new-overlay #'bnb/org-overlay-font-override)

8 Company

Company Mode is an in-buffer completion system. To get started, I need to load the mode and bind some keys.

(use-package company
  :ensure t
  :delight company-mode
  :init
  (add-hook 'after-init-hook 'global-company-mode)
  :bind
  ("<C-tab>" . company-complete-common-or-cycle)
  :config
  (bind-key "C-<tab>" 'company-complete org-mode-map)
  (bind-keys :map company-active-map
             ("s-<tab>" . company-complete-common-or-cycle)
             ("C-n"     . company-select-next)
             ("C-p"     . company-select-previous))
  ;; Turn off the auto downcasing of things
  (setq company-dabbrev-downcase nil
        company-show-numbers t
        company-tooltip-align-annotations t
        company-selection-wrap-around t
        company-tooltip-flip-when-above t
        company-dabbrev-code-everywhere t
        company-dabbrev-code-ignore-case t))

9 File Management

9.1 Dired

These are part of the dired-hacks repository.

(use-package dired-hacks-utils
  :defer
  :ensure t)

(use-package dired-filter
  :defer
  :ensure t)

(use-package dired-rainbow
  :defer
  :ensure t)

(use-package dired-narrow
  :defer
  :ensure t)

(use-package dired-collapse
  :defer
  :ensure t)

Also, there is a nice faculty to run an external command on a given file with !.

9.2 Neotree

Neotree is a file sidebar for navigation.

(use-package neotree
  :ensure t
  :commands (neotree)
  :bind ("H-t" . neotree-toggle)
  :config
  (setq neo-theme (if (display-graphic-p) 'icons 'arrow)))

9.3 OSX Dictionary

Search Dictionary.app from the comfort of an Emacs buffer.

(use-package osx-dictionary
  :ensure t
  :bind
  ("C-c d" . osx-dictionary-search-word-at-point)
  ("C-c i" . osx-dictionary-search-input))

9.4 OSX Reveal

Show files in finder.

(use-package reveal-in-osx-finder
  :ensure t
  :bind ("C-c z" . reveal-in-osx-finder))

10 Smart Tabs

SmartTabs try to do the right thing regarding tabs/spaces in indentation/alignment. It installs through the package interface. Look for smart-tabs-mode.

By default, I'm enabling it in all modes that I can.

Since we are dealing with tabs here, I also take the time to set the default width to 4. Because of the way this mode works, any change in the default width will result in code that still aligns.

(use-package smart-tabs-mode
  :defer 10
  :ensure t
  :init
  (setq-default indent-tabs-mode nil)
  (smart-tabs-insinuate 'c 'cperl 'c++)
  (setq-default tab-width 2))

10.1 Notes

To re-tab the whole file, use C-x h C-M-\.

11 CUA mode

CUA has a primary feature of enabling cut, copy, paste and undo shortcuts compatible with most applications (C-x, C-c, C-v). However, it also has interesting rectangle features and that is why I enable it. I also happen to turn off those other bindings and prefer the emacs defaults.

(cua-mode t)
(setq cua-enable-cua-keys nil)

11.1 Bindings

There are two main binding types: 11.1.1 and 11.1.2.

11.1.1 CUA Rectangles

These take place with an active rectangle. To start/cancel a rectangle use C-RET.

Keys Function
M-<arrow> Move rectangle overlay
C-SPACE Activate region bounded by rectangle
M-a Align all words at the left edge
M-b Fill rectangle with blanks (tabs and spaces)
M-c Closes the rectangle by removing left edge blanks
M-f Fills the rectangle with a single character (prompt)
M-i Increases number found on each line of rectangle
M-k Kills the rectangle as normal multi-line kill
M-l Downcases the rectangle
M-m Copies the rectangle for normal multi-line paste
M-n Fills each line with increasing numbers (prompt)
M-o Opens the rect by moving hilighted text right and filling with blanks
M-p Toggles virtual straight rectangle edges
M-P Inserts tabs and spaces to make real straight edges
M-q Performs text filling on the rectangle
M-q Performs text filling on the rectangle
M-r Replaces REGEXP (prompt) by STRING (prompt) in rectangle
M-R Reverse the lines in the rectangle
M-s Fills each line of the rectangle with the same STRING (prompt)
M-t Performs text fill of the rectangle with TEXT (prompt)
M-u Upcases the rectangle
M-<Vertical Bar> Runs shell command on rectangle
M-' Restricts rectangle to lines with CHAR (prompt) at left column
M-/ Restricts rectangle to lines matching REGEXP (prompt)
C-? Shows a brief list of the above commands.
M-C-<UP/DOWN> Scrolls the lines INSIDE the rectangle up/down

11.1.2 CUA Global Mark

The global mark feature enables a target the receives any typed/copied/killed text from any buffer (even the current one).

Key function
<ch> All characters (including newlines) you type are inserted at the global mark!
C-x If you cut a region or rectangle, it is automatically inserted at the global mark, and the global mark is advanced.
C-c If you copy a region or rectangle, it is immediately inserted at the global mark, and the global mark is advanced.
C-v Copies a single character to the global mark.
C-d Moves (i.e. deletes and inserts) a single character to the global mark.
backspace deletes the character before the global mark
delete deletes the character after the global mark.
S-C-space Jumps to and cancels the global mark.
C-u S-C-space Cancels the global mark (stays in current buffer).
TAB Indents the current line or rectangle to the column of the global mark.

12 Paredit

I added paredit-mode to several of the lisp modes that follow.

Paredit Cheatsheet

Animated Paredit

(use-package paredit
  :ensure t
  :delight " 🍐"
  :commands (paredit-mode))

13 Paxedit

Maybe even more power for lisp coding? Paxedit repo

(use-package paxedit
  :ensure t
  :delight " ꁀ"
  :commands (paxedit-mode)
  :bind
  ("M-<right>" . paxedit-transpose-forward)
  ("M-<left>"  . paxedit-transpose-backward)
  ("M-<up>"    . paxedit-backward-up)
  ("M-<down>"  . paxedit-backward-end)
  ("M-b"       . paxedit-previous-symbol)
  ("M-f"       . paxedit-next-symbol)
  ("C-%"       . paxedit-copy)
  ("C-&"       . paxedit-kill)
  ("C-*"       . paxedit-delete)
  ("C-^"       . paxedit-sexp-raise)
  ("M-u"       . paxedit-symbol-change-case)
  ("C-@"       . paxedit-symbol-copy)
  ("C-#"       . paxedit-symbol-kill))

14 Projectile

Handling project buffers and files may be easier with projectile. Currently under evaluation.

From the Hydra Wiki, the additional hydras launch the projectile functions.

(use-package projectile
  :ensure t
  :bind
  ("C-c p" . projectile-command-map)
  ("C-x w" . hydra-projectile-other-window/body)
  ("C-c C-p" . hydra-projectile/body)
  :config
  (use-package counsel-projectile
    :after (projectile)
    :ensure t
    :bind
    (:map projectile-command-map
          ("s s" . counsel-projectile-rg)
          ("p" . counsel-projectile-switch-project)))
  (when (eq system-type 'windows-nt)
    (setq projectile-indexing-method 'native))
  (setq projectile-enable-caching t
        projectile-require-project-root t
        projectile-mode-line '(:eval (format " 🛠[%s]" (projectile-project-name)))
        projectile-completion-system 'default)
  (add-to-list 'projectile-globally-ignored-directories "node_modules")
  (projectile-mode)
  (defhydra hydra-projectile-other-window (:color teal)
    "projectile-other-window"
    ("f"  projectile-find-file-other-window        "file")
    ("g"  projectile-find-file-dwim-other-window   "file dwim")
    ("d"  projectile-find-dir-other-window         "dir")
    ("b"  projectile-switch-to-buffer-other-window "buffer")
    ("q"  nil                                      "cancel" :color blue))
  (defhydra hydra-projectile (:color teal :hint nil)
    "
 PROJECTILE: %(projectile-project-root)

 Find File            Search/Tags          Buffers                Cache
  ------------------------------------------------------------------------------------------
  _C-f_: file            _r_: ag                _i_: Ibuffer           _c_: cache clear
   _ff_: file dwim       _g_: update gtags      _b_: switch to buffer  _x_: remove known project
   _fd_: file curr dir   _o_: multi-occur     _C-k_: Kill all buffers  _X_: cleanup non-existing
    _r_: recent file                                               ^^^^_z_: cache current
    _d_: dir

  "
    ("r"   counsel-projectile-rg)
    ("b"   projectile-switch-to-buffer)
    ("c"   projectile-invalidate-cache)
    ("d"   projectile-find-dir)
    ("C-f" projectile-find-file)
    ("ff"  projectile-find-file-dwim)
    ("fd"  projectile-find-file-in-directory)
    ("g"   ggtags-update-tags)
    ("C-g" ggtags-update-tags)
    ("i"   projectile-ibuffer)
    ("K"   projectile-kill-buffers)
    ("C-k" projectile-kill-buffers)
    ("m"   projectile-multi-occur)
    ("o"   projectile-multi-occur)
    ("C-p" projectile-switch-project "switch project")
    ("p"   projectile-switch-project)
    ("s"   projectile-switch-project)
    ("r"   projectile-recentf)
    ("x"   projectile-remove-known-project)
    ("X"   projectile-cleanup-known-projects)
    ("z"   projectile-cache-current-file)
    ("`"   hydra-projectile-other-window/body "other window")
    ("q"   nil "cancel" :color blue)))

15 Programming Languages

15.1 Utilities

15.1.1 Flycheck

    (use-package flycheck
:ensure t
:defer 10
              ;:init (global-flycheck-mode)
:config
(bind-key "H-!"
    (defhydra hydra-toggle (:color amaranth)
      "
  _c_ Check buffer      _x_ Explain error
  _n_ Next error        _h_ Show error
  _p_ Previous error
  _l_ Show all errors   _s_ Select syntax checker
  _C_ Clear errors      _?_ Describe syntax checker
  "
      ("c" flycheck-buffer)
      ("n" flycheck-next-error)
      ("p" flycheck-previous-error)
      ("l" flycheck-list-errors)
      ("C" flycheck-clear-errors)
      ("x" flycheck-explain-error-at-point)
      ("h" flycheck-display-error-at-point)
      ("s" flycheck-select-checker)
      ("?" flycheck-describe-checker)
      ("q" nil))))

15.1.2 Prog fill   PENDING

(use-package prog-fill
  :ensure t
  :commands (prog-fill)
  :config
  (setq
   prog-fill-floating-close-paren-p nil
   prog-fill-break-method-immediate-p t))

15.1.3 Language Server Protocol

Key Function
C-M-i completion at point
M-. Goto definition
M-? Symbol references
M-x Rename
(use-package lsp-mode
  :defer
  :ensure t)

15.2 C++

Oh my! I finally have a C++ addition to make!

15.2.1 Compilation Buffers

I forget where I snarfed this from, but it does a great job fixing the ANSI escape sequences in compilation buffers.

(require 'ansi-color)
(defun colorize-compilation-buffer ()
  (toggle-read-only)
  (ansi-color-apply-on-region compilation-filter-start (point))
  (toggle-read-only))
(add-hook 'compilation-filter-hook 'colorize-compilation-buffer)

15.3 Elisp

When modified emacs-lisp, it is most helpful to use paredit and eldoc.

(add-hook 'emacs-lisp-mode-hook 'paredit-mode)
(add-hook 'emacs-lisp-mode-hook 'paxedit-mode)
(add-hook 'emacs-lisp-mode-hook 'turn-on-eldoc-mode)

15.4 Javascript

Let's make some javascript settings!

(setq js-indent-level 2)

Additional setup from https://emacs.cafe/emacs/javascript/setup/2017/04/23/emacs-setup-javascript.html

15.4.1 JS2

(use-package js2-mode
  :ensure t
  :mode "\\.js\\'"
  :init
  (add-hook 'js2-mode-hook #'js2-imenu-extras-mode)
  :config
  (setq js2-basic-offset 2))
(use-package js2-refactor
  :ensure t
  :after js2-mode
  :functions (js2r-add-keybindings-with-prefix)
  :bind (:map js2-mode-map
              ("C-k" . js2r-kill))
  :config
  (js2r-add-keybindings-with-prefix "C-c C-r"))
(use-package xref-js2
  :ensure t
  :after js2-mode
  :config
  (add-hook 'js2-mode-hook
            (lambda ()
              (add-hook 'xref-backend-functions #'xref-js2-xref-backend nil t))))
  1. React Style JSX
    (use-package rjsx-mode
      :mode "(components|src\\/pages)\\/.*\\.js\\'")
    

15.5 Rust

These initial settings are copied from the spacemacs rust layer.

(use-package cargo
  :ensure t
  :mode "\\.rs"
  :init
  (bind-keys :prefix-map cargo-mode-map
             :prefix "C-c c"
             ("C" . cargo-process-repeat)
             ("." . cargo-process-repeat)
             ("X" . cargo-run-example)
             ("c" . cargo-process-build)
             ("d" . cargo-process-doc)
             ("e" . cargo-process-bench)
             ("R" . cargo-process-current-test)
             ("f" . cargo-process-fmt)
             ("i" . cargo-process-init)
             ("n" . cargo-process-new)
             ("o" . cargo-process-current-file-tests)
             ("s" . cargo-process-search)
             ("u" . cargo-process-update)
             ("x" . cargo-process-run)
             ("t" . cargo-process-test)
             ("R" . cargo-process-test-regexp)))

(use-package racer
  :ensure t
  :mode "\\.rs"
  :init
  (setq racer-rust-src-path "/Users/bnbeckwith/.rustup/toolchains/stable-x86_64-apple-darwin"))

(use-package flycheck-rust
  :ensure t
  :mode "\\.rs"
  :init
  (add-hook 'flycheck-mode-hook #'flycheck-rust-setup))

(use-package ggtags
  :defer)

(use-package html-gtags
  :defer)

(use-package racer-mode
  :ensure racer
  :defer
  :init
  (add-hook 'racer-mode-hook #'eldoc-mode)
  (add-hook 'racer-mode-hook #'company-mode))

(use-package company-racer
  :ensure t
  :defer
  :init
  (with-eval-after-load 'company
    (add-to-list 'company-backends 'company-racer)))

(use-package rustic
  :ensure t
  :commands (cargo-minor-mode)
  :mode ("\\.rs" . rustic-mode)
  :config
  (bind-keys :map rustic-mode-map
             ("C-c TAB" . rustic-format-buffer)
             ("TAB" . company-indent-or-complete-common))
  :init
  (setq company-tooltip-align-annotations t
        rustic-format-on-save nil)
  (add-hook 'rustic-mode-hook #'cargo-minor-mode)
  (add-hook 'rustic-mode-hook #'racer-mode)
  (add-hook 'rustic-mode-hook #'flycheck-mode)
  (add-hook 'rustic-mode-hook #'electric-pair-mode))

(use-package toml-mode
  :quelpa (toml-mode :fetcher github :repo "dryman/toml-mode.el")
  :mode "\\.toml\\'")

15.6 Sass

(use-package sass-mode
  :defer
  :ensure t)

15.7 Web Mode

For all of the webish-stuff, this mode works well. Let's enable it on the right things.

(use-package web-mode
  :defer
  :ensure t
  :bind ("H-b" . browse-url-of-file)
  :config
  (add-to-list 'auto-mode-alist '("\\.[agj]sp\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.erb\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.hbs\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.mustache\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.djhtml\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.svelte\\'" . web-mode))
  (setq web-mode-engines-alist
        '(("handlebars" . "\\.hbs\\'")))
  (defun bnb/web-mode-hook ()
    "Setup indentation when loading `web-mode`."
    (setq web-mode-markup-indent-offset 2
          web-mode-css-indent-offset 2
          web-mode-code-indent-offset 2))
  (add-hook 'web-mode-hook 'bnb/web-mode-hook))

15.8 HTML

15.8.1 Impatient mode

In order to play nicely with HTML+, it needs to be added to the filters of impatient mode.

(use-package impatient-mode
  :ensure t
  :mode "\\.html\\'"
  :config
  (add-to-list 'imp-default-user-filters '(mhtml-mode . nil)))

15.8.2 Emmet

Emmet mode allows for terse description of nested elements. There is great documentation on the approach at emmet.io.

(use-package emmet-mode
  :ensure t
  :commands (emmet-mode)
  :config
  (add-hook 'web-mode-hook 'emmet-mode)
  (add-hook 'sgml-mode-hook 'emmet-mode)
  (add-hook 'css-mode-hook 'emmet-mode))

15.9 Plantuml

Not a programming language, but certainly a language.

(use-package plantuml-mode
  :defer
  :ensure t)

15.10 Pollen

(use-package pollen-mode
  :ensure t
  :defer t
  :mode (("\\.pp$" . pollen-mode)))

(use-package company-pollen
  :defer t
  :ensure t)

16 Selectrum

(defun bnb/buffer-backward-kill-dwim (&optional arg)
  (interactive "p")
  (cond ((looking-back "/") (backward-kill-sexp))
        (t (delete-backward-char 1))))

(use-package selectrum
  :ensure t
  :bind (:map selectrum-minibuffer-map
              (("DEL" . #'bnb/buffer-backward-kill-dwim)))
  :config (selectrum-mode t)
  (setq
   selectrum-count-style 'current/matches
   selectrum-show-indices nil
   file-name-shadow-properties '(invisible t)))

(use-package selectrum-prescient
  :ensure t
  :after selectrum
  :config
  (setq prescient-persist-mode t)
  (selectrum-prescient-mode t))

16.1 Consult

https://github.com/minad/consult

(use-package consult
  :ensure t
  :bind (("C-c o" . consult-outline)
         ("C-x b" . consult-buffer)
         ("C-x r x" . consult-register)
         ("C-x r b" . consult-bookmark)
         ("M-g o" . consult-ouline)
         ("M-y" . consult-yank-pop))
  :init
  (fset 'multi-occur #'consult-multi-occur))

16.2 Marginalia

https://github.com/minad/marginalia

(use-package marginalia
  :ensure t
  :init
  (marginalia-mode)
  (setq marginalia-annotators
        '(marginalia-annotators-heavy marginalia-annotators-light)))

17 Shells

There are two useful shells in emacs: eshell and ansi-term.

17.1 Ansi Term

Some of the following settings were stolen from https://ogbe.net/emacsconfig.html.

These settings close ansi-term when I exit the shell. They also default to just launching zsh instead of asking me (preferred). Then it closes by setting up a hook to ensure some nice functionality in the terminal mode window.

(defadvice term-sentinel (around bnb/advise-term-sentinel (proc msg))
  (if (memq (process-status proc) '(signal exit))
      (let ((buffer (process-buffer proc)))
        ad-do-it
        (kill-buffer buffer))
    ad-do-it))
(ad-activate 'term-sentinel)

(defadvice ansi-term (before force-zsh)
  (interactive (list "/bin/zsh")))
(ad-activate 'ansi-term)

(defun bnb/term-mode-hook ()
  "Setup `term-mode`."
  (goto-address-mode)
  (setq-local term-buffer-maximum-size 10000))

(add-hook 'term-mode-hook 'bnb/term-mode-hook)

(defalias 'zsh 'ansi-term)

(setq ansi-term-color-vector
      [term term-color-black term-color-red term-color-green term-color-yellow term-color-blue term-color-magenta term-color-cyan term-color-white]
      ansi-color-faces-vector
      [default bold shadow italic underline bold bold-italic bold])

17.2 Eshell

Built-in Eshell can provide a shell that works the same on windows or GNU/Linux. One of the really cool features is that you can define commands to use (like aliases) within the shell and have them directly integrate with emacs.

17.2.1 Eshell Settings

Turn off any $PAGER settings inherited in the environment. Because this is running in Emacs, there is no need for a pager.

(setenv "PAGER" "cat")

17.2.2 Eshell Commands

Fast fingers are used to typing emacs at a prompt to open a file. This gives the same behavior in eshell.

(defun eshell/emacs (&rest args)
  "Open a file in emacs the natural way"
  (if (null args)
      ;; If emacs is called by itself, then just go to emacs directly
      (bury-buffer)
    ;; If opening multiple files with a directory name, e.g.
    ;; > emacs bar/bar.txt foo.txt
    ;; then the names must be expanded to complete file paths.
    ;; Otherwise, find-file will look in the current directory which
    ;; would fail for 'foo.txt' in the example above.
    (mapc #'find-file (mapcar #'expand-file-name (eshell-flatten-list (reverse args))))))

One can also keep the shell active and open files in the other window.

(defun eshell/emo (&rest args)
  (mapc
   (lambda (f)
     (save-selected-window
       (find-file-other-window f)))
   (mapcar #'expand-file-name (eshell-flatten-list (reverse args)))))

I also setup some command aliases. Here, I create a long listing, ll, alias for ls, a llc varient with colored output, and an emacs shortcut.

(with-eval-after-load "em-alias"
  '(progn
     (eshell/alias "em" "emacs")
     (eshell/alias "ll" "ls -Aloh")
     (eshell/alias "llc" "*ls -AlohG --color=always")))

On a windows box, setup grep to be a cygwin version.

(when (eq system-type 'windows-nt)
  (with-eval-after-load "eshell"
    (defun eshell/grep (&rest args)
      (eshell-grep "c:/cygwin/bin/grep.exe" args t))))

For Magit, there are some niceties to add.

(defun eshell/gst (&rest args)
  (magit-status-internal (pop args) nil)
  (eshell/echo))

(defun eshell/gd (&rest args)
  (magit-diff-unstaged)
  (eshell/echo))

(defun eshell/gds (&rest args)
  (magit-diff-staged)
  (eshell/echo))

17.2.3 Plan 9 Smart Shells

See the complete guide to mastering Eshell for more on this. Basically, the cursor stays on the command for editing if necessary.

(require 'eshell)
(require 'em-smart)

(setq eshell-where-to-jump 'begin)
(setq eshell-review-quick-commands nil)
(setq eshell-smart-space-goes-to-end t)
(add-hook  'eshell-mode-hook 'eshell-smart-initialize)

18 AucTeX

AUCTeX Manual

Superb handling of TeX documents.

(use-package tex-site
  :defer 10
  :ensure auctex
  :config
  (add-hook 'LaTeX-mode-hook 'flyspell-mode)
  (add-hook 'LaTeX-mode-hook 'LaTeX-math-mode)
  (add-hook 'LaTeX-mode-hook 'auto-fill-mode)
  (add-hook 'LaTeX-mode-hook 'orgtbl-mode)
  (setq TeX-auto-untabify t
        TeX-auto-save t
        TeX-save-query nil
        TeX-parse-self t
        TeX-output-view-style
        (if (eq system-type 'windows-nt)
            (quote
             (("^pdf$" "." "SumatraPDF.exe -reuse-instance %o")
              ("^html?$" "." "start %o")))
          (quote
           (("^pdf$" "." "evince -f %o")
            ("^html?$" "." "start %o"))))
        TeX-command-extra-options "-shell-escape"
        TeX-PDF-mode 1
        TeX-engine 'xetex)
  (setq-default TeX-master nil)
  (add-to-list 'org-latex-packages-alist
               '("" "tikz" t))
  (add-to-list 'org-latex-packages-alist
               '("" "minted" t))
  (setq org-latex-create-formula-image-program 'imagemagick)
  (eval-after-load "preview"
    '(add-to-list 'preview-default-preamble "\\PreviewEnvironment{tikzpicture}" t))
  (add-hook 'doc-view-mode-hook 'auto-revert-mode))

18.1 RefTeX

RefTeX Manual

RefTeX provides navigation, easy references, easy citations and integrates well into AUCTeX.

(add-hook 'LaTeX-mode-hook 'turn-on-reftex)
Keystroke Function
C-c = Show TOC and jump to sections
C-c ( Insert a label
C-c ) Reference a label
C-c [ Insert a citation (from BibTex db)
C-c < Index entry
C-c > View index
C-c & View crossref

18.2 TeX Settings

Here are some nice features to have enabled. Parse-self and auto-save will parse the file on load and save respectively. Untabify will remove tabs (real ones) before saving.

I also have a default of TeX-master set to nil. I used to have it set to "master" as recommended in the documentation, but I had bad results for LaTeX files generated on the fly.


For viewing the output, I can specify the command to use on the files generated in the process. However, the programs differ on GNU/Linux and Windows, so I have differing settings below.


18.3 XeTeX settings

To get more beautiful fonts, I use the XeTeX processor. I also use this in PDF mode.


18.4 DocView

Have docview automatically revert the buffer.

19 Markdown

Everything can't be as nice as org-mode. Oh well.

(use-package markdown-mode
  :ensure t
  :commands (markdown-mode gfm-mode)
  :mode (("README\\.md\\'" . gfm-mode)
         ("\\.md\\'" . markdown-mode)
         ("\\.markdown\\'" . markdown-mode))
  :init (setq markdown-command "multimarkdown"))

20 Orgmode

The one feature I cannot do without. Let's set up some basics.

20.1 Orgmode Initialization

Here are the initial settings that will invoke org-mode or need to be set when it is loaded.

(use-package org-mode
  :ensure org-plus-contrib
  :delight (org-mode "🦄" :major)
  :mode "\\.org\\(.gpg|_archive\\)?$"
  :bind
  ("C-c t"  . orgtbl-mode)
  ("C-c l"  . org-store-link)
  ("C-c r"  . org-capture)
  ("C-c b"  . org-iswitchb)
  ("<f12>"  . org-agenda)
  ("H-z"    . org-agenda)
  ("H-g"    . org-mac-grab-link)
  ("<apps>" . org-agenda)
  ("<f9> v" . visible-mode)
  ("<f9> g" . org-clock-goto)
  ("<f9> i" . org-clock-in)
  ("<f9> o" . org-clock-out)
  :config
  (bind-key "M-i" 'org-toggle-inline-images org-mode-map)
  (add-hook 'org-babel-after-execute-hook
            (lambda () (org-display-inline-images t t))))

20.1.1 Additional modules

There are now some Orgmode features that are not included by default. In this section, I enable them.

  1. Info Links

    Loading the ol-info library provides the support for info node links.

    (use-package ol-info
      :after org-mode)
    

20.2 Auto mode

I add gpg and _archive to the list of known org files. These two alternative extensions are for either encrypted org files (.org.gpg) or for archives (.org_archive).

This mode is set above with use-package.

20.3 Hooks

There are three hooks to consider. These are initialized in 20.1. First, I add in a keystroke to toggle the inline images.

The next hook just saves the org files opened before exiting emacs – just in case.

(add-hook 'bnb/kill-emacs-hooks 'org-save-all-org-buffers 'append)

The final hook shows images automatically. When I execute babel to get graphs from my work logs, I hate having to toggle the inline images on/off again. Here is a hook from Rick Frankel to save the day.

20.4 Keys

20.4.1 Global

Some org-mode features are so useful that I need to have them be available globally.

  • orgtbl-mode Use orgtbl in other modes
  • org-store-link Store a link (context-aware) to the current location
  • org-agenda Launch the agenda
  • org-capture Capture a task/note
  • org-iswitchb Switch org buffers
  • visible-mode Show the file as-is (no special org handling)
  • org-clock-in/org-clock-out Clock in/out of current subtree

20.4.2 Speed

Using org-mode efficiently for task management is best done with speed keys. This are in effect when the cursor is on the first * of a headline. And they come with an easy cheat-sheet by typing ?. I enable this feature and add some of my own commands.

(setq org-use-speed-commands t
      org-speed-commands-user
      (quote (("0" . delete-window)
              ("1" . delete-other-windows)
              ("2" . split-window-vertically)
              ("3" . split-window-horizontally)
              ("h" . hide-other)
              ("s" . org-save-all-org-buffers)
              ("z" . org-add-note)
              ("N" . org-narrow-to-subtree)
              ("W" . widen)
              ("m" . org-mark-subtree)
              ("C" . org-global-cycle))))

20.5 Org TODO Configuration

This is the meat of what Org can do. Keeping track of todo items with due dates, tags, etc. is really powerful. And I get to customize it to suit my needs and my workflow.

20.5.1 Keywords

The keywords that org uses in the headlines exist as sequences describing the state changes. The sequences describe how you can cycle through the different states. However, I don't cycle through states and just select them. I find that is better for the large list of possibilities here.

The characters in () also allow fast access to these states described here.

(setq org-todo-keywords
      (quote ((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d!/!)")
              (sequence "WAITING(w@/!)" "SOMEDAY(s!)" "|" "CANCELED(c@/!)")
              (sequence "CANCELED(c@/!)"))))

I used to set org-todo-keyword-faces, but now I rely on theme settings.

20.5.2 Tags

Org uses tags on headlines for organization. I don't currently use them much. I organize mainly by file with a file tag specified via in-buffer settings (#+FILETAGS).

However, a global tag list provides a selection list for the tagging interface. I use 'project' as my tag to easily differentiate simple tasks from more complex ones.

(setq org-tag-alist '(("PROJECT" . ?p)))

20.5.3 Mechanics

The todo interface allows easy selection of states and triggers on certain states to store notes.

Instead of cycling through states (and possibly triggering log entries), I prefer fast entry to jump right to the correct state. I also turn off the S-cursor transitions as state changes to avoid the logging prompts.

(setq org-use-fast-todo-selection t)
(setq org-treat-S-cursor-todo-selection-as-state-change nil)

Upon changing the state of todo items, I can automatically add/remove tags with the following list. It's a bit lispy, but describes what happens upon entry in the specified state. The state named as a string has tuples of tags and flags. 't' indicates to set the flag, empty means to remove it.

(setq org-todo-state-tags-triggers
      (quote (("CANCELED"
               ("CANCELED" . t))
              ("WAITING"
               ("WAITING" . t))
              ("SOMEDAY"
               ("SOMEDAY" . t))
              (done
               ("WAITING"))
              ("TODO"
               ("WAITING")
               ("CANCELED"))
              ("NEXT"
               ("WAITING"))
              ("DONE"
               ("WAITING")
               ("CANCELED")))))

Along with tags and states are priorities. I do not use task priorities myself so I turn them off.

(setq org-enable-priority-commands nil)
  1. Logging

    Org allows logging of states. I turn this on to prompt myself for reasons behind specific state changes. There is also a setting to set a different drawer for clocking and logs.

    (setq org-log-done (quote note)
          org-log-redeadline (quote time)
          org-log-reschedule (quote time)
          org-log-into-drawer t
          org-drawers '("PROPERTIES" "LOGBOOK" "CLOCK"))
    
  2. Sub-tasks

    Naturally, some tasks are projects composed of smaller sub-tasks. Org allows for this as well. I like to enforce the dependencies of regular todo items and plain checkbox lists. In this way, the overall item cannot change to done without the completion of the sub-tasks.

    (setq org-enforce-todo-checkbox-dependencies t
          org-enforce-todo-dependencies t)
    

    Because of the previous enforcement of state, I can also automatically infer when a parent state is complete. The following code marks the parent complete once the sub-tasks are all done.

    (defun org-summary-todo (n-done n-not-done)
      "Switch entry to DONE when all sub-entries are done, to TODO otherwise."
      (let (org-log-done org-log-states)
        (org-todo (if (= n-not-done 0) "DONE" "TODO"))))
    (add-hook 'org-after-todo-statistics-hook 'org-summary-todo)
    

20.6 Capture

Capturing is crucial to a task system and in this vein, org is no slouch. The capture templates define what get captures, where it goes, and what the user needs to type.

(setq org-capture-templates
      '(("t" "Todo" entry
         (file "~/Documents/Org/Refile.org")
         "* TODO %?\n  %U\n%^{Score}p" :clock-in t :clock-resume t)
        ("r" "todo (Remember location)" entry
         (file "~/Documents/Org/Refile.org")
         "* TODO %?\n  %U\n  %a" :clock-in t :clock-resume t)
        ("n" "Note" entry
         (file "~/Documents/Org/Refile.org")
         "* %?                                                                            :NOTE:\n  %U\n  %a\n  :CLOCK:\n  :END:")
        ("c" "Capture current TODO mix in table" table-line (file+headline "~/Documents/Org/WeeklyReports.org" "Burndown")
         "%(bnb/org-count-tasks-by-status)")
        ("s" "Capture Weekly Score in table" table-line (file+headline "~/Documents/Org/WeeklyReports.org" "Scores")
         "%(bnb/add-weekly-score-table-entry)")
        ("e" "Capture Weekly time in table" table-line (file+headline "~/Documents/Org/WeeklyReports.org" "Minutes")
         "%(bnb/org-time-logged-table-entry)")
        ("u" "Url" entry (file "~/Documents/Org/Refile.org")
         "* TODO %?\n  %U\n\n  %(org-mac-chrome-get-frontmost-url)")
        ("m" "Mail" entry (file "~/Documents/Org/Refile.org")
         "* TODO %?\n  %U\n\n  %(org-mac-message-get-links \"s\")")))

There are five main capture templates here. The first two store a todo item in my Refile.org file. The only difference is automatic (contextual) link storage in the second case.

The next item simply stores a note. The next for "Weekly Report" is a work in progress. I think that I'll have to either settle for a proper datetree or write a custom function.

The final item is not for direct use, but through the org-protocol interface and org-outlook usage. This lets me add a link to an Outlook message on windows. I can then get an email at work, mark it to store in emacs and quickly get back to the message later.

20.6.1 Capture-template helpers for data tables

These helpers provide functionality used in the capture templates above.

Modified from Sacha Chua, this code get the current mix of tasks in the agenda files. I use this as part of my weekly review for task amount and mix at a glance.

(defun bnb/org-count-tasks-by-status ()
  "Create a table entry for the tracking of task mix."
  (interactive)
  (let ((counts (make-hash-table :test 'equal))
        (today (format-time-string "%Y-%m-%d" (current-time)))
        values output)
    (org-map-entries
     (lambda ()
       (let ((status (elt (org-heading-components) 2)))
         (when status
           (puthash status (1+ (or (gethash status counts) 0)) counts))))
     "-HOME"
     'agenda)
    (setq values (mapcar (lambda (x)
                           (or (gethash x counts) 0))
                         '("DONE" "TODO" "WAITING" "CANCELLED" "SOMEDAY")))
    (setq output
          (concat "| " today " | "
                  (mapconcat 'number-to-string values " | ")
                  " | "
                  (number-to-string (apply '+ values))
                  " | "
                  (number-to-string
                   (round (/ (* 100.0 (car values)) (apply '+ values))))
                  "% |"))
    (if (called-interactively-p 'any)
        (insert output)
      output)))

I also have a helper function to get the score of done tasks closed within the last week. I store this in a table line with year and workweek number.

(defun bnb/add-weekly-score-table-entry ()
  "Track my weekly scores in a table."
  (let ((score (apply
                '+
                (org-map-entries
                 (lambda ()
                   (string-to-number (or (org-entry-get (point) "Score") "0")))
                 "/DONE"
                 'agenda)))
        (year (format-time-string "%Y" (current-time)))
        (ww (number-to-string (bnb/workweek))))
    (format "| %s | %s | %s |" year ww score)))

How about the hours logged last week? Let's give that a go.

(defun bnb/org-time-logged-table-entry (&optional additional-weeks)
  "Insert table of minutes per category.
  Optionally provide ADDITIONAL-WEEKS to get more history"
  (interactive "P")
  (unless additional-weeks (setq additional-weeks 0))
  (let* ((minh (make-hash-table :test 'equal))
         (now (decode-time))
         (start (encode-time 0 0 0 (- (nth 3 now) (nth 6 now) (* 7 (or additional-weeks 1))) (nth 4 now) (nth 5 now)))
         (today (format-time-string "%Y-%m-%d" (current-time))))
    ;; Collect minutes clocked per category
    (org-map-entries
     (lambda ()
       (let ((category (org-entry-get-with-inheritance "CATEGORY" t))
             (minutes (org-clock-sum-current-item start)))
         (puthash category (+ minutes
                              (or (gethash category minh) 0)) minh)))
     "LEVEL=1"
     'agenda)
    ;; Print out table lines
    (let ((rows nil))
      (maphash
       (lambda (k v)
         (when (> v 0)
           (setq rows
                 (cons (format "| %s | %s | %d |" today k v) rows))))
       minh)
      (if (called-interactively-p 'any)
          (insert (mapconcat 'identity rows "\n"))
        (mapconcat 'identity rows "\n")))))

20.7 Refile

Refiling notes is also spectacular with Org. That is what makes it possible for me to simply put every captured item into Refile.org and worry about organization later.

For my setup, I use separate files that hold a singular Tasks headline. Because of that, I turn on caching first.

For the refile targets, I will allow up to 2 levels of search for filing in any of the agenda files. For refiling within the current file, I set the max to five levels. Anything deeper than six levels will exhaust the depth of my thought.

Finally, I set the filenames to be first for refiling.

(setq org-refile-use-cache t
      org-refile-targets '((org-agenda-files :maxlevel . 2)
                           (nil :maxlevel . 5))
      org-refile-use-outline-path 'file)

20.8 Agenda

Once I have captured and refiled my tasks, I need to remember to do them and see what is on the agenda. The ways to view the tasks at hand are nicely programmable.

Some basic settings control small tidbits in the agenda. I turn on tags in the agenda line, show the logged items for the day, and only show a time grid if a scheduled tasks exists.

(setq org-agenda-show-inherited-tags t
      org-agenda-log-mode-items '(clock)
      org-agenda-clockreport-parameter-plist '(:link nil :maxlevel 2 :fileskip0 t)
      ;;org-agenda-block-separator ?┄
      org-agenda-block-separator nil
      org-agenda-dim-blocked-tasks nil
      org-agenda-inhibit-startup t
      org-agenda-breadcrumbs-separator " ❱ ")

20.8.1 Category Icons   PENDING

Org-mode can show category icons in some agenda views. The underlying setting is just an alist of categories and the icons to use.

(setq org-agenda-category-icon-alist
      `(("[rR]efile" ,(list (all-the-icons-faicon "inbox")) nil nil :ascent center)
        (".*" ,(list (all-the-icons-faicon "file")) nil nil :ascent center)))

20.8.2 Views

The key to knowing what work there is the agenda views. These provide a landscape to list, filter or manipulate tasks. org-agenda-custom-commands defines which views are available by default.

First, I define a little helper function (from Sacha Chua) to display a note with agenda.

(defun bnb/org-agenda-with-tip (arg)
  "Show agenda for ARG days."
  (org-agenda-list arg)
  (let ((inhibit-read-only t)
        (pos (point)))
    (goto-char (point-max))
    (goto-char pos)))

WIP from: https://github.com/fniessen/emacs-leuven/blob/master/org-custom-agenda-views.el

;; Reset everything to nil
(setq org-agenda-custom-commands nil)
(add-to-list 'org-agenda-custom-commands
             '("o" "My Agenda"
               ((todo "TODO" (
                              (org-agenda-overriding-header "\n⚡ Do Today\n┄┄┄┄┄┄┄┄┄┄")
                              (org-agenda-remove-tags t)
                              (org-agenda-prefix-format " %-2i %-15b")
                              (org-agenda-todo-keyword-format "")
                              ))
                (agenda "" (
                            (org-agenda-start-day "+0d")
                            (org-agenda-span 5)
                            (org-agenda-overriding-header "⚡ Schedule\n┄┄┄┄┄┄┄┄┄┄")
                            (org-agenda-repeating-timestamp-show-all nil)
                            (org-agenda-remove-tags t)
                            (org-agenda-prefix-format   "  %-3i  %-15b %t%s")
                            (org-agenda-todo-keyword-format " ☐ ")
                            (org-agenda-current-time-string "⮜┈┈┈┈┈┈┈ now")
                            (org-agenda-scheduled-leaders '("" ""))
                            (org-agenda-time-grid (quote ((daily today remove-match)
                                                          (0900 1200 1500 1800 2100)
                                                          "      " "┈┈┈┈┈┈┈┈┈┈┈┈┈")))
                            ))
                )))
(add-to-list 'org-agenda-custom-commands
             '("b" "Agenda" bnb/org-agenda-with-tip))
(add-to-list 'org-agenda-custom-commands
             '("c" . "COLLECT...") t)
(add-to-list 'org-agenda-custom-commands
             '("cb" "CollectBox"
               ((alltodo ""))))
(add-to-list 'org-agenda-custom-commands
             '("f" . "FOCUS...") t)
(add-to-list 'org-agenda-custom-commands
             `("f." "Today"
               ((agenda ""
                        ((org-agenda-entry-types '(:timestamp :sexp))
                         (org-agenda-overriding-header
                          (concat "CALENDAR Today"
                                  (format-time-string "%a %d" (current-time))))
                         (org-agenda-span 'day)))
                (tags-todo "LEVEL=1+REFILE"
                           ((org-agenda-overriding-header "COLLECTBOX (Unscheduled)")))
                (tags-todo "DEADLINE=\"<+0d>\""
                           ((org-agenda-overriding-header "DUE TODAY")
                            (org-agenda-skip-function
                             '(org-agenda-skip-entry-if 'notedeadline))
                            (org-agenda-sorting-strategy '(priority-down))))
                (tags-todo "DEADLINE<\"<+0d>\""
                           ((org-agenda-overriding-header "OVERDUE")
                            (org-agenda-skip-function
                             '(org-agenda-skip-entry-if 'notedeadline))
                            (org-agenda-sorting-strategy '(priority-down))))
                (agenda ""
                        ((org-agenda-entry-types '(:scheduled))
                         (org-agenda-overriding-header "SCHEDULED")
                         (org-agenda-skip-function
                          '(org-agenda-skip-entry-if 'todo 'done))
                         (org-agenda-sorting-strategy
                          '(priority-down time-down))
                         (org-agenda-span 'day)
                         (org-agenda-start-on-weekday nil)
                         (org-agenda-time-grid nil)))
                (todo "DONE"
                      ((org-agenda-overriding-header "COMPLETED"))))
               ((org-agenda-format-date "")
                (org-agenda-start-with-clockreport-mode nil))) t)
(add-to-list 'org-agenda-custom-commands
             '("fh" "Hotlist"
               ((tags-todo "DEADLINE<\"<+0d>\""
                           ((org-agenda-overriding-header "OVERDUE")))
                (tags-todo "DEADLINE>=\"<+0d>\"+DEADLINE<=\"<+1w>\""
                           ((org-agenda-overriding-header "DUE IN NEXT 7 DAYS")))
                (tags-todo "DEADLINE=\"\"+FLAGGED|DEADLINE>\"<+1w>\"+FLAGGED"
                           ((org-agenda-overriding-header "FLAGGED"))))
               ((org-agenda-todo-ignore-scheduled 'future)))  t)
(add-to-list 'org-agenda-custom-commands
             '("r" . "REVIEW...") t)

(add-to-list 'org-agenda-custom-commands
             '("ra" . "All Tasks...") t)
(add-to-list 'org-agenda-custom-commands
             '("rad" "All Tasks (grouped by Due Date)"
               ((tags-todo "DEADLINE<\"<+0d>\""
                           ((org-agenda-overriding-header "OVERDUE")
                            (org-agenda-skip-function
                             '(org-agenda-skip-entry-if 'notdeadline))))
                (tags-todo "DEADLINE=\"<+0d>\""
                           ((org-agenda-overriding-header "DUE TODAY")
                            (org-agenda-skip-function
                             '(org-agenda-skip-entry-if 'notdeadline))))
                (tags-todo "DEADLINE=\"<+1d>\""
                           ((org-agenda-overriding-header "DUE TOMORROW")
                            (org-agenda-skip-function
                             '(org-agenda-skip-entry-if 'notdeadline))))
                (tags-todo "DEADLINE>\"<+1d>\"+DEADLINE<=\"<+7d>\""
                           ((org-agenda-overriding-header "DUE WITHIN A WEEK")
                            (org-agenda-skip-function
                             '(org-agenda-skip-entry-if 'notdeadline))))
                (tags-todo "DEADLINE>\"<+7d>\"+DEADLINE<=\"<+28d>\""
                           ((org-agenda-overriding-header "DUE WITHIN A MONTH")
                            (org-agenda-skip-function
                             '(org-agenda-skip-entry-if 'notdeadline))))
                (tags-todo "DEADLINE>\"<+28d>\""
                           ((org-agenda-overriding-header "DUE LATER")
                            (org-agenda-skip-function
                             '(org-agenda-skip-entry-if 'notdeadline))))
                (tags-todo "TODO={WAIT}"
                           ((org-agenda-overriding-header "WAITING FOR")
                            (org-agenda-skip-function
                             '(org-agenda-skip-entry-if 'deadline))))
                (todo ""
                      ((org-agenda-overriding-header "WAITING FOR")
                       (org-agenda-skip-function
                        '(org-agenda-skip-entry-if 'deadline)))))
               ((org-agenda-sorting-strategy '(priority-down))
                (org-agenda-write-buffer-name "All Tasks (grouped by Due Date)"))
               "~/Documents/Org/all-tasks-by-due-date.pdf") t)
(add-to-list 'org-agenda-custom-commands
             '("ra1" "All Tasks with a due date"
               ((alltodo ""))
               ((org-agenda-overriding-header "All Tasks (sorted by Due Date)")
                (org-agenda-skip-function
                 '(org-agenda-skip-entry-if 'notdeadline))
                (org-agenda-sorting-strategy '(deadline-up)))) t)
(add-to-list 'org-agenda-custom-commands
             '("rag" "Grouped Tasks")
             ())
(add-to-list 'org-agenda-custom-commands
             '("rt" . "Timesheet...") t)
;; Show what happened today.
(add-to-list 'org-agenda-custom-commands
             '("rtd" "Daily Timesheet"
               ((agenda ""))
               ((org-agenda-log-mode-items '(clock closed))
                (org-agenda-overriding-header "DAILY TIMESHEET")
                (org-agenda-show-log 'clockcheck)
                (org-agenda-span 'day)
                (org-agenda-start-with-clockreport-mode t)
                (org-agenda-time-grid nil))) t)

;; Show what happened this week.
(add-to-list 'org-agenda-custom-commands
             '("rtw" "Weekly Timesheet"
               ((agenda ""))
               (
                ;; (org-agenda-format-date "")
                (org-agenda-overriding-header "WEEKLY TIMESHEET")
                (org-agenda-skip-function '(org-agenda-skip-entry-if 'timestamp))
                (org-agenda-span 'week)
                (org-agenda-start-on-weekday 1)
                (org-agenda-start-with-clockreport-mode t)
                (org-agenda-time-grid nil))) t)
(add-to-list 'org-agenda-custom-commands
             '("rc" . "Calendar...") t)

(add-to-list 'org-agenda-custom-commands
             '("rc7" "Events and appointments for 7 days"
               ((agenda ""))
               ((org-agenda-entry-types '(:timestamp :sexp))
                ;; (org-agenda-overriding-header "Calendar for 7 days")
                (org-agenda-span 'week)
                (org-agenda-format-date "\n%a %d")
                ;; (org-agenda-date-weekend ... new face ...)
                (org-agenda-time-grid nil))) t)
(add-to-list 'org-agenda-custom-commands
             '("rw" "Weekly review"
               ((tags "CATEGORY={@REFILE}&LEVEL<=2"
                      ((org-agenda-overriding-header "NEW TASKS")))
                (agenda ""
                        ((org-agenda-clockreport-mode t)
                         (org-agenda-format-date
                          (concat "\n"
                                  "%Y-%m-%d" " %a "
                                  (make-string (window-width) ?_)))
                         (org-agenda-overriding-header "PAST WEEK")
                         (org-agenda-prefix-format " %?-11t %i %-12:c% s")
                         (org-agenda-show-log 'clockcheck)
                         (org-agenda-span 7)
                         (org-agenda-start-day "-1w")
                         (org-deadline-warning-days 0)))
                (agenda ""
                        ((org-agenda-overriding-header "NEXT MONTH")
                         (org-agenda-span 'month)
                         (org-agenda-start-day "+0d")
                         (org-deadline-warning-days 0)))
                (todo "PROJECT"
                      ((org-agenda-overriding-header "PROJECT LIST")))
                (todo "DONE|PROJECTDONE"
                      ((org-agenda-overriding-header
                        "Candidates to be archived"))))))
(use-package org-super-agenda
  :defer
  :ensure t
  :quelpa (org-super-agenda :fetcher github :repo "alphapapa/org-super-agenda")
  :config
  (org-super-agenda-mode t)
  (add-to-list 'org-agenda-custom-commands
               '("rag" "Grouped Tasks"
                 ((todo "" ((org-super-agenda-groups
                             '((:name "All Tasks" :auto-category t))))))))
  (add-to-list 'org-agenda-custom-commands
               '("f1" "Score 1 Tasks"
                 ((tags "+Score=1" ((org-super-agenda-groups
                                     '((:name "Score 1 Tasks" :auto-category t))))))))
  (add-to-list 'org-agenda-custom-commands
               '("f2" "Score 2 Tasks"
                 ((tags "+Score=2" ((org-super-agenda-groups
                                     '((:name "Score 1 Tasks" :auto-category t))))))))
  (add-to-list 'org-agenda-custom-commands
               '("f3" "Score 3 Tasks"
                 ((tags "+Score=3" ((org-super-agenda-groups
                                     '((:name "Score 1 Tasks" :auto-category t))))))))
  (add-to-list 'org-agenda-custom-commands
               '("f5" "Score 5 Tasks"
                 ((tags "+Score=5" ((org-super-agenda-groups
                                     '((:name "Score 1 Tasks" :auto-category t))))))))
  (add-to-list 'org-agenda-custom-commands
               '("f8" "Score 8 Tasks"
                 ((tags "+Score=8" ((org-super-agenda-groups
                                     '((:name "Score 1 Tasks" :auto-category t)))))))))

Phew! That is a lot of lisp! It is easiest to describe each view by the key that triggers it.

  • a Agenda with tip (keystroke tip)
  • w Tasks waiting on something
  • r Refile New notes and tasks
  • n Next Any task with the NEXT tag
  • A Tasks ready for Archive
  • u Upcoming tasks Scheduled or due in the next week.
  • U Unscheduled tasks
  • P Printable agenda Formats tasks at the top with upcoming items below.
  • S Scoreless tasks Use this to get the scoreless tasks and edit in column mode
  • h Habits
  • # Stuck projects
  • z Agenda with Personal Files
  • c Select default clocking task

20.9 Export

Here are some global export settings make sense for HTML and \LaTeX.

20.9.1 HTML

For HTML, I just want to inline the links to images.

(setq org-export-html-inline-images t)

I also used to suppress the postamble with org-export-html-postamble.

(setq org-html-postamble nil)

I'll use the fancy HTML5 export by default.

(setq org-html-doctype "html5"
      org-html-html5-fancy t)

I like to have striped tables in email, but this is terribly difficult due to cruddy CSS support. Luckily, org-html-table-row-tags saves the day and assigns the right classes to the table rows. Now styling can be done in CSS-reduced instances.

(setq org-html-table-row-tags
      (cons '(cond (top-row-p "<tr class=\"tr-top\">")
                   (bottom-row-p "<tr class=\"tr-bottom\">")
                   (t (if (= (mod row-number 2) 1)
                          "<tr class=\"tr-odd\">"
                        "<tr class=\"tr-even\">")))
            "</tr>"))

20.9.2 LaTeX

For \LaTeX, I want to convert fragments to images, and use minted for any source blocks. I also want to have xelatex as the backend.

(setq org-export-latex-listings 'minted
      org-export-latex-custom-lang-environments
      '((emacs-lisp "common-lispcode"))
      org-export-latex-minted-options '()
      org-highlight-latex-and-related '(latex script entities)
      org-latex-to-pdf-process
      '("xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"
        "xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"
        "xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"))

Also, I want to enable some of the other contributed exporters. To do this, simply require the files necessary that are not on by default.

I add exporters for github-flavored markdown and tufte html.

(use-package ox-gfm :defer 10)
(use-package ox-tufte :defer 10)

20.9.3 EPUB

E-readers may be a thing that sticks around.

(use-package ox-epub :after org-mode)

20.10 Clocking

I have found clocking to be useful in understanding where my time goes. And Org makes this easy, fast and painless to do. So very nice.

The clock has some general settings around persistence (resuming clocks), history length and resuming a task after clocking in twice (interrupted task).

(org-clock-persistence-insinuate)
(setq org-clock-history-length 28
      org-clock-in-resume t)

Behavior of the clock can change to accommodate other needs. I like having clocks log into a specific drawer. Also, it is nice to remove zero-time clocks and clock out automatically when an item completes.

(setq org-clock-into-drawer "CLOCK"
      org-clock-out-remove-zero-time-clocks t
      org-clock-out-when-done t)

Two settings help resolve most clock issues that I have seen. Persisting the clock across sessions helps prevent loss of time by accident. Auto-resolution of open clocks help prompt how to handle the situation where a dangling clock exists.

(setq org-clock-persist 'history
      org-clock-auto-clock-resolution 'when-no-clock-is-running)

Two final settings regarding clocking setup how I change and view the clocks. I want any clock reports to include the currently clocked task as well. And for clock editing, I change to 15 minute increments.

(setq org-clock-report-include-clocking-task t
      org-time-stamp-rounding-minutes '(1 15))

20.11 Modules

Org-modules allow for specific functionality within org-mode.

(setq org-modules
      '(org-bbdb
        org-bibtex
        org-crypt
        org-gnus
        org-id
        org-info
        org-jsinfo
        org-habit
        org-inlinetask
        org-irc
        org-plot
        org-protocol
        org-bookmark
        org-calc))

20.11.1 Habit

Some tasks repeat, but you still want to log when you have done it. I use this to help me always do my weekly or yearly reviews. By including it in org-modules, habits get activated.

My one setting blow sets a width for the graph in Agenda View.

(setq org-habit-graph-column 50)

20.12 Babel

(org-babel-do-load-languages
 'org-babel-load-languages
 '((calc       . t)
   (C          . t)
   (ditaa      . t)
   (dot        . t)
   (emacs-lisp . t)
   (gnuplot    . t)
   (latex      . t)
   (maxima     . t)
   (perl       . t)
   (plantuml   . t)
   (python     . t)
   (ruby       . t)
   (shell      . t)
   (sqlite     . t)
   (sql        . t)
   (R          . t)))

20.12.1 Extra Babel Packages

(use-package ob-http :after org-mode)

(use-package ob-restclient
  :after org-mode
  :ensure t
  :quelpa
  (ob-restclient :fetcher github :repo "alf/ob-restclient.el")
  :config
  (org-babel-do-load-languages
   'org-babel-load-languages
   (append org-babel-load-languages '((restclient . t)))))

20.13 Miscellaneous Settings

20.13.1 Columns

The default columns are as follows.

(setq org-columns-default-format
      "%80ITEM(Task) %5Score{+} %10Effort(Effort){:} %10CLOCKSUM")

20.13.2 Automatically save org files

I like to save early and often. In earlier versions of orgmode, I sometimes had the capture buffer/timer crash on me. So, now I save at the top of every hour to be sure.

(run-at-time "00:59" 3600 'org-save-all-org-buffers)

20.13.3 File Applications

This list lets org know how to handle the links of given file types. Most things open inside emacs, but the others set to default rely on the OS to supply a program.

(setq org-file-apps
      '((auto-mode . emacs)
        ("\\.x?html?\\'" . default)
        ("\\.pdf\\'" . default)
        ("\\.mm\\'" . default)))

20.13.4 Goto Interface

By using C-c C-j, you can jump easily around a large orgfile such as this one. Naturally, the interface you use to do so is customizable.

I explicitly set it to the default because I sometimes go back and forth with the default and outline-path-completion setting.

(setq org-goto-interface 'outline-path)

20.13.5 Special Control Keys

Orgmode has a different idea of some of the default emacs commands to make it easier to work with the structures involved.

For C-a or C-e within a headline, it will only try to navigate the headline text the first time. Additional keypresses will move to the true beginning/ending of lines.

C-k also can behave specially in headlines depending on its location. When point is at the beginning, it will kill the headline and the folded subtree below. In the middle of a headline, it kills the headline text up to the tags. After the headline text, it kills the tags.

(setq org-special-ctrl-a/e t
      org-special-ctrl-k t)

20.13.6 Auto-revert mode

If the org files are under DVCS like git, then the edits may happen while open in emacs.

This is a global setting, but most useful for the org files that exists elsewhere.

(use-package autorevert
  :diminish " "
  :custom
  (global-auto-revert-mode t))

20.13.7 IDO Integration

IDO integrates well into orgmode. Anytime completion is necessary, I like to use the IDO mechanics.

The outline-path-completion may conflict with IDO, so then it is best to have it not use IDO in this case.

(setq org-completion-use-ido t
      org-outline-path-complete-in-steps nil)

20.13.8 Display settings

There are a collection of settings that define how the headlines, subtrees, and notes render.

For the headline stars, there are two settings of note. I am explicit that I do not want only odd levels. I also like to hide the leading stars.

(setq org-odd-levels-only nil
      org-hide-leading-stars nil)

Cycling the headline states can produce different views of the files. I like this to be as compact as possible, so I try to squash the lines between the collapsed trees. There is also a flag to open a file collapsed. This I like too – I get a compact view of the file and can jump to a relevant section with C-c C-j.

(setq org-cycle-separator-lines 0
      org-startup-folded 'content)

When using SRC-blocks, org can provide highlighting native to the SRC type. Note that this may slow down some files.

(setq org-src-fontify-natively t)

20.13.9 Insertion

I define when org should leave a blank line before an item. In my case it is headings and plain list items.

(setq org-blank-before-new-entry '((heading)
                                   (plain-list-item)))

Also, when inserting a new heading, do so after the current subtree.

(setq org-insert-heading-respect-content t)

20.13.10 Properties

(setq org-global-properties
      '(("STYLE_ALL"  . "habit")
        ("Effort_ALL" . "0:10 0:30 1:00 2:00 3:00 4:00")
        ("Score_ALL"  . "1 2 3 5 8")))

20.13.11 Teleport

From the Kitchin Group, I can have a nice teleport function for org sections. Using a speed command on the current headline kills it and then presents avy-style markers to select the insertion point.

By default the teleported headline will be inserted after the target. With a prefix argument, it will preceed the target.

(defun bnb/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-user
             (cons "k" (lambda ()
                         (org-mark-subtree)
                         (kill-region
                          (region-beginning)
                          (region-end)))))

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

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

20.13.12 Plantuml

Setup the path for orgmode to find the jar needed.

(setq org-plantuml-jar-path "/usr/local/Cellar/plantuml/1.2017.18/libexec/plantuml.jar")

20.14 Pretty Org-mode

This collection of settings enhances the visual appeal when working in org-mode.

First, some initial built-in settings to make.

(setq org-hide-leading-stars t
      org-hide-emphasis-markers t
      org-fontify-done-headline t
      org-pretty-entities t)

Here's some fun

(add-hook
 'org-mode-hook
 (lambda ()
   (mapc (lambda (pair) (push pair prettify-symbols-alist))
         '(("#+BEGIN_SRC" . "⌈")
           ("#+END_SRC" . "⌊")
           ("#+begin_src" . "⌈")
           ("#+end_src" . "⌊")
           (">=" . "≥")
           ("=>" . "⇨")))))

20.14.1 Org Bullets

(use-package org-bullets
  :ensure t
  :custom
  (org-bullets-bullet-list '("◉" "◊" "○" "⧫" "✸" "⬨" "⬟" "⬧" "⬢" "⬫" "⌑" "⬪" "▱"))
  (org-ellipsis "˯") ;; Options: ˯⇂↯⤵🠻🢗
  :hook (org-mode . org-bullets-mode))

20.15 Org Repo Todo   PENDING

Make it easy to setup a TODO.org from within a repo. I use this to capture thoughts while coding

(use-package org-repo-todo
  :ensure t
  :bind ("s-;" . ort/capture-todo)
  ("s-'" . ort/capture-checkitem)
  ("s-`" . ort/goto-todos))

20.16 Org projectile   PENDING

Make it easy to capture tasks into the right projects and setup a project todo.

(use-package org-projectile
  :bind ("C-c n p" . org-projectile:project-todo-completing-read)
  :ensure t
  :config
  (org-projectile:per-repo)
  (add-to-list 'org-capture-templates
               (org-projectile:project-todo-entry))
  (setq org-agenda-files
        (append org-agenda-files (org-projectile:todo-files))
        org-projectile:per-repo-filename "Tasks.org"))

20.17 Org Ref

(use-package org-ref
  :defer 10
  :config
  (setq org-ref-notes-directory "~/Documents/Personal/Org/Biblio/"
        org-ref-bibliography-notes "~/Documents/Personal/Org/Biblio/index.org"
        org-ref-default-bibliography '("~/Documents/Personal/Org/Biblio/index.bib")
        org-ref-pdf-directory "~/Documents/Personal/Org/Biblio/lib"))

20.18 Org Noter

Taking notes in a PDF is a useful trick. org-noter lets me do just that.

(use-package org-noter
  :ensure t
  :bind ("H-n" . org-noter))

20.19 Org Roam

The best of all worlds? Org-mode and a Zettelkasten system? Yes, Org-roam sets up a knowledge capture and organization system built on the principles of a Zettelkasten system.

(use-package org-roam
  :ensure t
  :delight " 𝕫"
  :hook
  (after-init . org-roam-mode)
  :custom
  (org-roam-directory "~/Documents/zettel")
  :bind (:map org-roam-mode-map
              (("C-c n l" . org-roam)
               ("C-c n t" . org-roam-dailies-find-today)
               ("C-c n w" . org-roam-dailies-find-tomorrow)
               ("C-c n d" . org-roam-date)
               ("C-c n f" . org-roam-find-file)
               ("C-c n g" . org-roam-show-graph))
              :map org-mode-map
              (("C-c n i" . org-roam-insert))
              (("C-c n I" . org-roam-insert-immediate))))

20.19.1 Company Org Roam

(use-package company-org-roam
  :ensure t
  :quelpa (company-org-roam :fetcher github :repo "org-roam/company-org-roam")
  :config (push 'company-org-roam company-backends))

20.20 Org QL

(use-package org-ql
  :ensure t
  :quelpa (org-ql :fetcher github :repo "alphapapa/org-ql")
  :commands (org-ql-search)
  :config (progn
            (require 'org-habit))
  :ensure t)

21 Calc

Working in computer land, I add these additional units to calc.

(use-package calc
  :commands (calc)
  :init
  (setq math-additional-units
        '((GiB "1024 * MiB" "Giga Byte")
          (MiB "1024 * KiB" "Mega Byte")
          (KiB "1024 * B"   "Kilo Byte")
          (B   nil          "Byte")
          (Gib "1024 * Mib" "Giga bit")
          (Mib "1024 * Kib" "Mega bit")
          (Kib "1024 * b"   "Kilo bit")
          (b   nil          "bit")
          )))

22 Server

Using Emacs as a server is a great way to keep the power responsive.

(when (and (or (eq system-type 'windows-nt) (eq system-type 'darwin))
           (not (and (boundp 'server-clients) server-clients))
           (not (daemonp)))
  (server-start))

I need to look into the TCP connections to see how that work across machines. Perhaps it can be useful in a multi-machine work environment.

23 Local customizations (custom.el)

I typically use the customize interface to generate any local settings such as proxies, paths, fonts, etc. that may vary from machine to machine.

(setq custom-file "~/.emacs.d/custom.el")
(load-file custom-file)

24 Local customizations (user-login-name)

I also intend to have a generic call to an installed local file that may need to behave differently from custom.el. This loads last so that it can modify any existing setting made here to work on the specific system in question.

In the code below, I add ~/.emacs.d/ to the load path and have a protected call to load-library. If the file exists, it gets loaded, otherwise the error normally returned if the file is non-existent gets ignored.

(condition-case err
    (progn
      (load-file (format "~/.emacs.d/%s.el"  user-login-name))
      (message "Loaded local settings file %s.el" user-login-name))
  (file-error
   (message "Skipping %s library as it does not exist." user-login-name))
  nil)

Author: Benjamin Beckwith

Created: 2021-04-10 Sat 14:43

Validate