/.emacs.d

Personal .emacs.d directory.

Primary LanguageEmacs Lisp

Jon-Michael Deldin’s Emacs Configuration

I started using Emacs in the fall of 2010 after a few years of Vim. I use GNU Emacs 25.1+ on Ubuntu for Org-Mode, Ruby, LaTeX, sh, and a host of miscellaneous other languages.

(load "server")
(unless (server-running-p) (server-start))

Installation

You can clone directly as $HOME/.emacs.d:

git clone git://github.com/jmdeldin/.emacs.d.git ~/.emacs.d/

Notes

Improvements

add org-mode/orgstruct to magit

evaluate company-mode

Keys

KeyDescription
C-q TABInsert literal tab
M-yTraverse kill-ring (after C-y)
C-u C-SPCJump to last mark
M-n(In cua-mode) Increment digit
C-M \Indent according to major mode
C-c C-sStart IRB
C-c C-rSend region to IRB (C-c C-s first)
C-x v =Diff against HEAD
C-u C-x v =Diff against other branch
C-x v uDiscard changes
C-x v ~Checkout another rev
C-x v lView commit log
C-Shift BSKill whole line
C-c ’Edit code block (org-babel)
C-x r mBookmark the current loc in a file
C-x r bJump to bookmark
C-x r lList bookmarks
C-x C-nNarrow region
C-x C-wWiden region
C-x r wSave window configuration
C-x r jRestore configuration
M-&Execute shell command asynchronously
C-x r tRemoves the content in a rectangle (mark first)
M-0 .. M-9Prefix argument
M– M-cCapitalize last word (M-dash for back)
M-eForward sentence
M-aBackward sentence
C-M-fMove forward by a balanced expr (e.g., “,’, [])
C-M-bMove backward by a balanced expr
M-mback-to-indentation (^ in Vim)
M-rmove the point w/o scrolling
M-g M-ggoto-line
M-o M-sCenter text
C-x r NNumber lines

Searching

KeyDescription
M-pprevious search term
M-nnext search term
M-ctoggle case-sensitivity
C-s C-wsearch with the word at point (* inv Vim)

Ido-mode

These keys work inside the Ido minibuffer (C-x C-f).

KeyDescription
C-dOpen a dired buffer in the current directory
C-aToggles showing ignored files
C-cToggles case-sensitivity
C-s & C-rMove to the next/prev match
C-SPCRestrict to an expr (C-x C-b .org C-SPC for just org)

Functions and modes

FunctionDescription
follow-modeBrowse splits like they’re one window
ielm-modeElisp REPL
hexl-modeHex viewer
bury-bufferSend buffer to end of list
highlight-changes-modeShow newly changed text in red
info-aproposfull-text search of info

org-mode

CommandDecription
C-c C-nnext heading
C-c C-pprevious heading
C-c C-fnext heading (same level)
C-c C-bprevious heading (same level)
C-c C-uback to a higher heading
C-c /Sparse tree
M-S-RETInsert new item with checkbox
M-S-UP/DOWNMove items including subitems up/down
C-c -Cycle list level through different bullets

Dired

CommandDescription
% uuppercase filename
% llowercase filename
% Rregex rename

Initialization

Load custom helper functions

;; dependency issue - these defuns should not bother with install crap
  (load-file (concat user-emacs-directory "site-lisp/defuns.el"))

Set custom-file

On clean installs, some packages will try to append custom vars to init.el, breaking installation. The custom-file must be set before trying to install packages.

(setq custom-file (concat user-emacs-directory "local/emacs-custom.el"))
(load custom-file 'noerror)

Set up repositories

(setq package-archives '(("gnu" . "https://elpa.gnu.org/packages/")
                         ("melpa-stable" . "https://stable.melpa.org/packages/")
                         ("melpa-unstable" . "https://melpa.org/packages/")))

Define required packages

We can declare the origin of our packages with the package-pinned-packages variable:

(setq package-pinned-packages '((use-package . "melpa-unstable")
                                (diminish . "melpa-unstable")))

(setq use-package-verbose t)

Install packages

(setq jm/packages (mapcar 'car package-pinned-packages))

(jm/install-all-packages jm/packages)
(setq use-package-verbose t)

Load use-package

(eval-when-compile
  (require 'use-package))
(require 'diminish)
(require 'bind-key)

Utility packages

(use-package tiny
  :pin melpa-unstable
  :ensure t
  :defer t)

(use-package s
  :pin melpa-stable
  :ensure t
  :defer t)

Server

Only load the server if it isn’t running:

(load "server")
(unless (server-running-p)
  (server-start))

Garbage

This section is dedicated to all of the turd files Emacs leaves all over your machine.

Lockfiles

Disable lockfiles – there’s only one user on this machine. This prevents guard from re-running specs everytime the file is edited (but not saved).

(setq create-lockfiles nil)

Backups

Place backups in ~/.emacs.d/local/backups:

(setq backup-by-copying t)
(setq backup-directory-alist
      (list (cons "." (jm/local-path "backups"))))
(setq delete-old-versions t)
(setq kept-new-versions 6)
(setq kept-old-versions 2)
(setq version-control t)

Auto-saves

Keep auto-save files together. Test with M-x do-auto-save.

(let ((new-dir (jm/local-path "auto-saves/")))
  (setq auto-save-list-file-prefix new-dir)
  (setq auto-save-file-name-transforms
      `((".*" ,new-dir t))))

Bookmarks

Use ~/.emacs.d/local/.emacs.bmk for bookmarks:

(setq bookmark-default-file (jm/local-path "bookmarks"))

Communication

ERC

Prevent auto-joining #erc

(setq erc-autojoin-channels-alist '())

Spell-check ERC:

(with-eval-after-load 'erc
  (erc-spelling-mode 1))

Ignore noise:

(setq erc-network-hide-list '("JOIN" "PART" "QUIT" "NICK" "MODE"))
(setq erc-track-exclude-server-buffer t)

Highlight nick:

(setq erc-current-nick-highlight-type 'nick)

mu4e

;; install sudo apt install mu4e gnutls-bin
(use-package mu4e
  :bind ("<f8>" . mu4e)
  :init
  (defun jm/email (user domain)
    "Builds an email address to dodge some spam bot harvesting
    for public config."
    (combine-and-quote-strings (list user domain) "@"))

  (defun jm/match-mu4e-context (email msg)
    (when msg
      (mu4e-message-contact-field-matches msg :to email)))
  :config
  (add-hook 'message-send-hook
            (lambda ()
              (unless (yes-or-no-p "Are you sure you want to send this?")
                (signal 'quit nil))))

  ;; when viewing a message, hit `a V' to view message in browser
  (add-to-list 'mu4e-view-actions
               '("ViewInBrowser" . mu4e-action-view-in-browser) t)

  (setq mu4e-get-mail-command "offlineimap")
  (setq send-mail-function 'smtpmail-send-it)
  (setq message-send-mail-function 'smtpmail-send-it)

  ;; enable inline images:
  (setq mu4e-view-show-images t)
  (when (fboundp 'imagemagick-register-types)
    (imagemagick-register-types))

  (setq mu4e-compose-dont-reply-to-self t)
  (setq message-kill-buffer-on-exit t)

  (setq mu4e-context-policy 'ask-if-none)
  (setq mu4e-compose-context-policy 'ask-if-none)

  ;; the backquote is needed to evaluate the alist values
  (setq mu4e-contexts `(
                        ,(make-mu4e-context
                           :name "HighSeas"
                           :match-func (lambda (msg)
                                         (jm/match-mu4e-context (jm/email "jmdeldin" "highseas.com") msg))
                           :vars `( (user-mail-address . ,(jm/email "jmdeldin" "highseas.com"))
                                    (smtpmail-smtp-user . ,(jm/email "jmdeldin" "highseas.com"))
                                    (smtpmail-smtp-server . "smtp.gmail.com")
                                    (smtpmail-smtp-service . 587)
                                    (mu4e-sent-messages-behavior . delete) ; gmail copies emails for us
                                    (smtpmail-stream-type . starttls)
                                    (mu4e-drafts-folder . "/HighSeas/[Gmail].Drafts")
                                    (mu4e-sent-folder . "/HighSeas/[Gmail].Sent Mail")
                                    (mu4e-trash-folder . "/HighSeas/[Gmail].Trash")
                                    ))
                        ,(make-mu4e-context
                           :name "Personal"
                           :match-func (lambda (msg)
                                         (when msg
                                           (string-prefix-p "/Personal" (mu4e-message-field msg :maildir))))
                           :vars `( (user-mail-address . ,(jm/email "jm" "deldin.us"))
                                    (smtpmail-smtp-user . ,(jm/email "jm" "deldin.us"))
                                    (smtpmail-smtp-server . "smtp.fastmail.com")
                                    (smtpmail-smtp-service . 587)
                                    (smtpmail-stream-type . starttls)
                                    (mu4e-drafts-folder . "/Personal/Drafts")
                                    (mu4e-sent-folder . "/Personal/Sent")
                                    (mu4e-trash-folder . "/Personal/Trash")
                                    )))))

News

Elfeed is a really nice RSS reader for Emacs that’s easy to use and fast.

(use-package elfeed
  :pin melpa-unstable
  :ensure t
  :preface
  (defun jm/elfeed ()
    "Download feed updates before using elfeed."
    (interactive)
    (elfeed-update)
    (elfeed))
  :bind ("<f9>" . jm/elfeed)
  :config
  (setq elfeed-db-directory (concat user-emacs-directory "local/elfeed")))

Feeds

(setq elfeed-feeds
      '(
        ("http://nullprogram.com/feed/" emacs)
        ("http://planet.emacsen.org/atom.xml" emacs)
        ("http://sachachua.com/blog/feed/" emacs)
        ("https://www.masteringemacs.org/feed" emacs)
        ("http://matthewkirk.com/feed/" friends)
        ("http://www.jmdeldin.com/atom.xml" personal)
        ("https://jvns.ca/atom.xml" programming)
        ("https://www.reddit.com/r/everett.xml" news)))

Text Editing

Default to 72 column width for plain text

(add-hook 'text-mode-hook
          '(lambda ()
             (set-fill-column 72)))

Match parens and quotes

(electric-pair-mode t)

Enable on-the-fly reindentation

(electric-indent-mode t)

Insert a newline around special characters

(electric-layout-mode t)

Use single spaces between sentences for fill-paragraph (M-q)

(setq sentence-end-double-space nil)

Use Unicode everywhere, as per Mastering Emacs’ post:

(set-language-environment "UTF-8")
(prefer-coding-system 'utf-8)
(set-default-coding-systems 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(setq-default buffer-file-coding-system 'utf-8)

;; clipboard as UTF-8
(setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING))

Changing a region’s case is useful

(put 'upcase-region 'disabled nil)
(put 'downcase-region 'disabled nil)

Remember last edit position

(require 'saveplace)
(setq-default save-place t)
(setq save-place-file (jm/local-path "places"))

Snippets

(use-package yasnippet
  :pin melpa-stable
  :ensure t
  :config
  (yas-global-mode t))

(use-package yasnippet-snippets
  :pin melpa-stable
  :ensure t)

Auto-Complete Mode

(use-package auto-complete-config
  :ensure auto-complete
  :pin melpa-stable
  :defer 2
  :diminish auto-complete-mode
  :config
  (ac-config-default)
  (ac-flyspell-workaround)
  (setq ac-ignore-case nil)
  (setq ac-comphist-file (jm/local-path "ac-comphist.dat")))

Spelling

Use aspell instead of ispell, use list for faster region checking, and use a faster suggestion mode.

(setq ispell-program-name "aspell")
(setq ispell-list-command "list")
(setq ispell-extra-args '("--sug-mode=ultra"))

Turn it on for some modes:

(mapcar (lambda (mode)
          (add-hook mode (lambda () (flyspell-mode t))))
        '(org-mode-hook markdown-mode mu4e-compose-mode-hook text-mode))
(add-hook 'git-commit-mode-hook 'turn-on-flyspell)

Whitespace

Wrap lines at column 78

(setq-default fill-column 78)

Highlight right-margin when whitespace-mode is on

(setq whitespace-line-column fill-column)

Highlight empty lines

(setq-default indicate-empty-lines t)

Hard-wrap lines all the time

(add-hook 'text-mode-hook 'turn-on-auto-fill)

Use spaces, not tabs (C-q C-i to insert a hard-tab)

(setq-default indent-tabs-mode nil)

2-space tabs

(setq-default tab-width 2)

Insert tabs when appropriate

(setq indent-line-function 'insert-tab)

Insert a newline at the EOF

(setq-default require-final-newline t)

Delete trailing whitespace on save

(add-hook 'before-save-hook 'delete-trailing-whitespace)

Highlight Stuff

(use-package highlight-indent-guides
  :ensure t
  :pin melpa-unstable
  :config
  (setq highlight-indent-guides-method 'fill)
  (mapcar (lambda (hook) (add-hook hook 'highlight-indent-guides-mode))
          '(prog-mode-hook yaml-mode-hook)))

UI

Hide the {menu,tool,scroll}bars

(if window-system
    (progn
      (scroll-bar-mode -1)
      (tool-bar-mode -1)))
(menu-bar-mode -1)

Hide the startup messages

(setq inhibit-startup-message t)
(setq inhibit-startup-echo-area-message t)

“y or n” instead of “yes or no”

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

Show line & column number in the mode line

(column-number-mode t)

Show file size

(size-indication-mode t)

Highlight parens

(show-paren-mode t)
(setq show-paren-delay 0.0)

Highlight current line

(global-hl-line-mode 1)

Use ibuffer instead of list-buffers

(defalias 'list-buffers 'ibuffer)

No bells

(setq ring-bell-function 'ignore)

Window Management

Restore window configuration with C-c LEFT

(winner-mode)

Enable windmove – default binding is shift

(windmove-default-keybindings)
(setq windmove-wrap-around t)

Make windmove work in org-mode:

(add-hook 'org-shiftup-final-hook 'windmove-up)
(add-hook 'org-shiftleft-final-hook 'windmove-left)
(add-hook 'org-shiftdown-final-hook 'windmove-down)
(add-hook 'org-shiftright-final-hook 'windmove-right)

Minibuffer

IDO

Interactively-do-things is the greatest Emacs extension.

(setq ido-enable-flex-matching t)
(setq ido-everywhere t)
(setq ido-show-dot-for-dired t)
(setq ido-save-directory-list-file (jm/local-path "ido.last"))
(setq ido-use-virtual-buffers t)
(ido-mode 1)

Uniquify

Use part of the directory to distinguish between identically-named files:

(use-package uniquify
  :config
  (setq uniquify-buffer-name-style 'forward))

Minibuffer History

Save minibuffer history:

(savehist-mode 1)
(setq savehist-additional-variables '(kill-ring search-ring regexp-search-ring))
(setq savehist-file (jm/local-path "savehist"))

Recent Files

Enable recent files:

(use-package recentf
  :defer 1
  :config
  (setq recentf-save-file (jm/local-path "recentf"))
  (setq recentf-max-saved-items 1000)
  (recentf-mode 1))

SMEX

M-x – ido-like completion for functions:

(use-package smex
  :pin melpa-stable
  :ensure t
  :bind ("M-x" . smex)
  :config
  (setq smex-save-file (jm/local-path "smex-items")))

Mouse

Enable mouse support in a terminal (from StackOverflow):

(unless window-system
  (require 'mouse)
  (xterm-mouse-mode t)
  (global-set-key [mouse-4] '(lambda ()
                               (interactive)
                               (scroll-down 1)))
  (global-set-key [mouse-5] '(lambda ()
                               (interactive)
                               (scroll-up 1)))
  (defun track-mouse (e))
  (setq mouse-sel-mode t))

Keybindings

Evil

Arguably the best Vim ever, but sometimes, I still want Emacs keys.

  (use-package undo-tree
    :ensure t
    :init
    (global-undo-tree-mode))

  (use-package evil
    :pin melpa-unstable
    :ensure t
    :demand t
    :diminish undo-tree-mode
    :bind (
           :map evil-insert-state-map
           ("C-a" . beginning-of-line)
           ("C-e" . end-of-line)
           ("C-d" . delete-forward-char)
           ("C-k" . kill-line)
           ("C-p" . evil-previous-line)
           ("C-p" . evil-previous-line)
           ("C-n" . evil-next-line)
           ("C-z" . suspend-emacs)
           :map evil-normal-state-map
           ("C-n" . evil-next-line)
           ("C-p" . evil-previous-line)
           ("C-z" . suspend-emacs))
    :config
    ;; disable evil in some buffers:
    (mapc (lambda (mode) (evil-set-initial-state mode 'emacs))
          '(elfeed-show-mode elfeed-search-mode Info-mode-hook ledger-report-mode))
    (evil-set-undo-system 'undo-tree)

    (evil-mode t))

  ;; Jump between tags with %
  (use-package evil-matchit
    :pin melpa-stable
    :ensure t
    :defer 1
    :config (global-evil-matchit-mode t))

  ;; increment/decrement numbers like Vim (just not with C-a/C-x)
  (use-package evil-numbers
    :pin melpa-stable
    :ensure t
    :bind (("C-c +" . evil-numbers/inc-at-pt)
           ("C-c -" . evil-numbers/dec-at-pt)))

  (use-package evil-surround
    :pin melpa-stable
    :ensure t
    :defer 1
    :config
    (global-evil-surround-mode 1)
)

Editing

M-/ – use a more powerful expansion

(global-set-key (kbd "M-/") 'hippie-expand)

C-c C-r – Revert buffer

(global-set-key (kbd "C-c C-r") 'revert-buffer)

Swap C-j and RET

(global-set-key (kbd "RET") 'reindent-then-newline-and-indent)
(global-set-key (kbd "C-j") 'newline)

C-c C-d – Remove trailing whitespace

(global-set-key (kbd "C-c C-d") 'delete-trailing-whitespace)

C-w – delete the previous word (like most shells)

(global-set-key (kbd "C-w") 'backward-kill-word)

C-x C-k – kill region (since we just unbound it with C-w)

(global-set-key (kbd "C-x C-k") 'kill-region)

C-x C-j – join line

(global-set-key (kbd "C-x C-j") 'join-line)

C-c w – toggle whitespace mode

(global-set-key (kbd "C-c w") 'whitespace-mode)

better commenting (replaces the original comment-dwim)

(global-set-key (kbd "M-;") 'comment-or-uncomment-region)

C-x m – recompile

(global-set-key (kbd "C-x m") 'recompile)

C-x xjm/shell

(global-set-key (kbd "C-x x") 'jm/shell)

M-#jm/reload-init

(global-set-key (kbd "M-#") 'jm/reload-init)

F12 – calculate and insert result in buffer

(global-set-key (kbd "<f12>") (lambda ()
                                (interactive)
                                (quick-calc t)))

Windows

M-s/M-S – switch windows

(use-package ace-window
  :bind (("M-s" . ace-window))
  :ensure t
  :pin melpa-stable)
(global-set-key (kbd "M-S") 'append-window)

Mac

Make the Cmd and Opt keys work for M-x

(when system-type "darwin"
  (setq-default mac-command-modifier 'super)
  (setq-default mac-option-modifier 'meta))

Clutter

VC mode does not need to show more than the branch. The following code from Malabrba does just that:

(setcdr (assq 'vc-mode mode-line-format)
        '((:eval (replace-regexp-in-string "^ Git" " " vc-mode))))

Themes

For dumb terminals:

(if (and (not window-system) (string= (getenv "TERM") "dumb"))
    (progn
      (set-face-background 'hl-line "#666")
      (set-face-foreground 'hl-line "#fff")))

For GUIs:

(defvar jm/font-size
  130
  "Default font size. #pixels height * 10.")

(defun old-man ()
  "Increase the font size of all buffers."
  (interactive)
  (set-face-attribute 'default nil :height (+ 100 jm/font-size)))

(defun set-default-font-size (size)
  "Scale things back down. Optionally invoke with C-u SIZE to set
the global size. Remember that the font size is pixels*10, e.g.,
150 == 15 px."
  (interactive "P")
  (set-face-attribute 'default nil :height (if size size jm/font-size)))

(if window-system
    (let ((jm/font "Source Code Pro-13"))
      (set-face-attribute 'default nil :font jm/font)
      (set-frame-font jm/font)))

Languages

C

The only way to program.

(setq c-default-style "k&r")

Use four spaces for tabs.

(setq-default c-basic-offset 4)

Many-windows mode makes Emacs into a more traditional IDE for GDB. See C-h f gdb for details. NOTE: This doesn’t work on OS 10.8 (non-stop mode isn’t supported).

(setq gdb-many-windows t)

Clojure

(use-package cider
  :pin melpa-stable
  :ensure t
  :mode ("\\.clj\\'" . clojure-mode))

CSS

Turn on rainbow-mode for colored hex values

(use-package rainbow-mode
  :pin gnu
  :ensure t
  :defer t
  :init
  (add-hook 'prog-mode-hook 'rainbow-mode))

Associate less and scss:

(associate-file-type '(".less" ".scss" ".sass") 'css-mode)

Prevent SCSS from compiling at save time:

(setq scss-compile-at-save nil)

Two spaces:

(setq css-indent-offset 2)

Graphviz

(use-package graphviz-dot-mode
  :pin melpa-stable
  :ensure t
  :mode ("\\.gv\\'" "\\.dot\\'"))

Go

(use-package go-mode
  :pin melpa-stable
  :ensure t
  :mode "\\.go\\'")

JavaScript

2 space indent:

(setq js-indent-level 2)
(setq js2-basic-offset 2)
(setq js2-bounce-indent-p t)

Show errors immediately:

(setq flycheck-display-errors-delay 0)

JSON

(use-package json-mode
  :pin melpa-stable
  :ensure t
  :mode (".babelrc" "\\.json\\'"))

React

(use-package rjsx-mode
  :pin melpa-unstable
  :ensure t
  :mode (("\\.jsx" . rjsx-mode))
  :bind (:map rjsx-mode-map ("<" . nil)))

LaTeX

Produce PDFs instead of DVIs

(setq TeX-PDF-mode t)

Lisp

(define-key lisp-mode-shared-map (kbd "C-c e") 'eval-buffer)

; probably not needed with 25+ 4
(add-hook 'emacs-lisp-mode-hook 'turn-on-eldoc-mode)

Markdown

(use-package markdown-mode
  :pin melpa-stable
  :ensure t
  :mode ("\\.md\\'" "\\.markdown\\'"))

Nginx

(use-package nginx-mode
  :pin melpa-stable
  :ensure t
  :mode "nginx.conf")

ERB

Support editing mixed mode files, like ERB templates.

(use-package mmm-auto
  :ensure mmm-mode
  :pin melpa-stable
  :mode ((".rhtml" . html-erb-mode)
         (".html.erb" . html-erb-mode)
         (".erb" . html-erb-mode))
  :config
  (setq mmm-global-mode 'auto)
  (setq mmm-submode-decoration-level 2)
  (setq mmm-parse-when-idle t))

Org-Mode

(use-package org
  :pin gnu
  :ensure t
  :mode ("\\.org" . org-mode)
  :bind (
         ("C-c l" . org-store-link)
         ("C-c a" . org-agenda)
         ("C-c c" . org-capture)
         ("C-c b" . org-iswitchb)
         ("C-c C-x h" . org-html-export-to-html)
         ;; for terminals -- TAB does not work
         ("C-x t" . org-cycle))
  :config
  (org-babel-do-load-languages
   'org-babel-load-languages (mapcar (lambda (l) (cons l t))
                                     '(C calc emacs-lisp gnuplot latex ledger perl python ruby screen shell))))

;; for org-html export
(use-package htmlize
  :pin melpa-stable
  :ensure t
  :defer t)

Include the org-habit module for the agenda:

(setq org-modules (quote (org-habit)))

Use Org-Mode for the *scratch* buffer:

(setq initial-major-mode 'org-mode)
(setq initial-scratch-message nil)

Paths

This configuration assumes org files live in the ~/org directory. You can customize it by setting these variables in ../local/local.el:

(setq org-directory "~/org")
(setq org-default-notes-file "~/org/capture.org")
(setq org-journal-file "~/org/journal.org.gpg")
(setq org-work-journal-file "~/org/work.org")
(setq org-log-file "~/org/log.org")
(setq org-notes-file "~/org/notes.org")
(setq org-archive-location "archive/%s_archive::")
(setq org-agenda-files (filter (lambda (fn)
                                 (not (string-match (rx "#") fn)))
                               (file-expand-wildcards org-directory)))

Capture Templates

Hit C-c c to trigger these:

(defun jm/find-org-headline (&optional heading)
  "Prompts for and finds a heading.

Adapted from URL `http://emacs.stackexchange.com/a/5931'."
  (let* ((target (save-excursion
                   (org-refile-get-location heading nil t t)))
         (file (nth 1 target))
         (pos (nth 3 target)))
    (with-current-buffer (find-file-noselect org-notes-file)
      (goto-char pos)
      (org-end-of-subtree)
      (org-return))))

(setq org-capture-templates
      '(("t" "TODO" entry (file+headline org-default-notes-file "Tasks")
         "* TODO %^{Task} %^g \n%U \n%?")
        ("w" "Work Journal" entry (file+datetree org-work-journal-file)
         "* %^{Title}\n%U \n%?\n")
        ("j" "Journal" entry (file+datetree org-journal-file)
         "* %^{Title}\n%U \n%?\n")
        ("n" "Notes" entry (file+function org-notes-file jm/find-org-headline)
         "* %^{Title}\n%U \n%?"
         :empty-lines 1)
        ("d" "Appointment" entry (file+headline org-default-notes-file "Tasks")
          "* TODO %^{Description} %^g\n%?\nSCHEDULED: %t\n%i\n%a")
        ("l" "Log" entry (file+datetree+prompt org-log-file)
         "* %^{Task} %^g\n%?" :clock-in t :clock-resume t)
        ))

Skeleton

(define-skeleton orgmode-skeleton
  "Inserts orgmode defaults into the current buffer."
  "Title: "
  "#+TITLE:       " str | (file-name-nondirectory buffer-file-name) \n
  "#+DESCRIPTION: " (skeleton-read "Description: ") \n
  "#+STARTUP:     align hidestars indent lognotedone" \n
  \n _)

Babel

Highlight src blocks

(setq org-src-fontify-natively t)
(defun jm/org-confirm-babel-evaluate (lang body)
  "Don't ask to confirm evaluating a ledger block."
  (not (string= lang "ledger")))
(setq org-confirm-babel-evaluate 'jm/org-confirm-babel-evaluate)

Diary/Agenda

Hide some holidays:

(setq holiday-bahai-holidays nil)
(setq holiday-hebrew-holidays nil)
(setq holiday-islamic-holidays nil)

Show the agenda from the current day:

(setq org-agenda-start-on-weekday nil)

Show all habits

(setq org-habit-show-habits-only-for-today nil)

Set the location for showing the sunrise/sunset in the agenda:

(setq calendar-latitude 47.9790)
(setq calendar-longitude -122.2021)
(setq calendar-location-name "Everett, WA")

Exporting

Remove “Valid XHTML” link

(setq org-export-html-validation-link nil)

Minted latex export

(setq org-export-latex-minted-options
      '(("fontsize" "\\scriptsize")))

Perl

Use the more modern cperl-mode

(defalias 'perl-mode 'cperl-mode)

Use cperl-mode for .t tests

(associate-file-type '(".t") 'cperl-mode)

Use four-space indents

(setq cperl-indent-level 4)

Indent only four-spaces in broken-up calls like

someCall(
    $var,
    $var2
)
(setq cperl-indent-parens-as-block t)
(setq cperl-close-paren-offset -4)

Fix indentation for lines not starting statements (e.g., hash members)

(setq cperl-continued-statement-offset 0)

PlantUML

(use-package plantuml-mode
  :pin melpa-unstable
  :ensure t
  :mode ("\\.plantuml" . plantuml-mode)
  :config
  (setq plantuml-default-exec-mode 'jar)
  (setq plantuml-jar-path "/usr/share/plantuml/plantuml.jar")
  )

Ruby

; default for Ruby 2+ is UTF-8, so no need for this anymore
(setq ruby-insert-encoding-magic-comment nil)

(use-package rspec-mode
  :pin melpa-stable
  :ensure t
  :bind (:map ruby-mode-map ("C-c , x" . rspec-verify-single))
  :config
  (setq rspec-use-rake-when-possible nil)
  (setq rspec-use-spring-when-possible t))
(use-package ruby-mode
  :mode
  ("\\.rb\\'" "\\.rake\\'" "Capfile" "Gemfile" "Guardfile" "Rakefile" "\\.ru\\'")
  :preface
  (defun jm/run-ruby-buffer ()
    "Run the current Ruby script and switch focus back to the script."
    (interactive)
    (ruby-compilation-this-buffer)
    (other-window -1))
  :bind (:map ruby-mode-map ("C-c C-c" . jm/run-ruby-buffer))
  :interpreter "ruby"
  :config
  (rspec-mode t))

(use-package ruby-compilation
  :pin melpa-stable
  :ensure t
  :defer t)

(use-package inf-ruby
  :pin melpa-stable
  :defer t
  :ensure t
  :init
  ;; for binding.pry to work
  (add-hook 'after-init-hook 'inf-ruby-switch-setup))

Scheme

Variables

(setq scheme-program-name "scheme")

Helper functions

(defun scheme-run-buffer ()
  "Runs the current buffer through scheme and switches focus back to the script."
  (interactive)
  (scheme-send-region (point-min) (point-max)))

Hooks

(add-hook 'scheme-mode-hook
          (lambda ()
            (local-set-key (kbd "C-c C-c") 'scheme-run-buffer)
            (local-set-key (kbd "C-j") 'scheme-send-last-sexp)))

YAML

(use-package yaml-mode
  :pin melpa-stable
  :ensure t
  :mode ("\\.yml\\'" "\\.yaml\\'")
  :bind (:map yaml-mode-map ("C-m" . newline-and-indent)))

Tools

Browsers

There are far too many ways to configure which browser to use in emacs. xdg-open should be the default but alas, we need this:

(setq shr-external-browser 'browse-url-xdg-open)
(setq browse-url-browser-function 'browse-url-generic
      browse-url-generic-program "sensible-browser")

Clock

Customize M-x display-time-world:

(setq display-time-world-list
      '(("America/Los_Angeles" "Seattle")
        ("America/Denver" "Cascade")
        ; coworkers:
        ("America/Chicago" "Ann Arbor")
        ("America/New_York" "New York")
        ("America/Sao_Paulo" "Brazil")
        ("America/Bogota" "Columbia")
        ("Asia/Karachi" "Karachi")
        ("Asia/Manila" "Manila")
        ("UTC" "UTC")))

Dired

Make it so hitting C on a filename in a dired window defaults to the other dired window:

(setq dired-dwim-target t)

Jump

(use-package avy
  :init
  (bind-key "C-:" 'avy-goto-char-2))

Editorconfig

(use-package editorconfig
  :pin melpa-stable
  :ensure t
  :config (editorconfig-mode t))

Ledger

(use-package ledger-mode
  :pin melpa-unstable
  :ensure t
  :defer t
  :config
  (define-skeleton monthly-interest-skeleton
    "Inserts an interest accrued entry into ledger."
    "" (insert (replace-regexp-in-string "-" "/" (org-read-date))) " * Monthly Interest Paid" \n
    "    Assets:"(skeleton-read "Account: ") "    " (skeleton-read "Amount: ") \n
    "Income:Interest:CapitalOne" \n
    \n _)

  (defun ledger-toggle-cleared ()
    "Toggle --cleared flag on an open ledger report."
    (interactive)
    (setq ledger-report-cmd
          (if (string-match-p "--cleared" ledger-report-cmd)
              (replace-regexp-in-string "--cleared" "" ledger-report-cmd)
            (replace-regexp-in-string "$" " --cleared" ledger-report-cmd)))
    (ledger-report-redo))

  (define-key ledger-report-mode-map (kbd "C-c C-c") 'ledger-toggle-cleared)

  (defun jm/ledger-format ()
    "Clean up a ledger buffer automatically."
    (interactive)
    (ledger-sort-buffer)
    (ledger-post-align-postings (point-min) (point-max)))

  ;; for consistency with ledger-mode:
  (define-key ledger-report-mode-map (kbd "C-c C-o C-r") 'ledger-report-select-report)

  (setq ledger-reports
        '(
          ("bal"                "%(binary) -f %(ledger-file) bal")
          ("biz"                "%(binary) -f %(ledger-file) bal Biz")
          ("biz-income"         "%(binary) -f %(ledger-file) equity Revenue Biz:Receivable")
          ("cash"               "%(binary) -f %(ledger-file) bal Assets:Checking Assets:Savings --cleared")
          ("cat"                "%(binary) -f %(ledger-file) reg Assets:Reimbursements:Catherine --dc --cleared")
          ("cc-rewards"         "%(binary) -f %(ledger-file) bal Income:Rebate --cleared")
          ("cc-spending"        "%(binary) -f %(ledger-file) bal Liabilities:Credit -l 'amount < 0'")
          ("checking-cap1"      "%(binary) -f %(ledger-file) reg Assets:Checking:CapitalOne --display 'd>=[last month]' --cleared")
          ("credit-cap1"        "%(binary) -f %(ledger-file) reg Liabilities:Credit:CapitalOne --display 'd>=[last month]' --cleared")
          ("donations"          "%(binary) -f %(ledger-file) bal Expenses:Donations Liabilities:Donations")
          ("exp-nonessential"   "%(binary) -f %(ledger-file) bal Expenses:Entertainment Expenses:Sports Expenses:Travel Expenses:Woodworking")
          ("mortgage"           "%(binary) -f %(ledger-file) bal Virtual:Home Assets:Home")
          ("net"                "%(binary) -f %(ledger-file) bal Assets Liabilities --cleared")
          ("payee"              "%(binary) -f %(ledger-file) reg @%(payee)")
          ("reg"                "%(binary) -f %(ledger-file) reg")
          ("savings-goals"      "%(binary) -f %(ledger-file) bal Virtual:Goals Assets:Savings:Buffer Assets:Savings:Emergency ")
          ("savings-emergency"  "%(binary) -f %(ledger-file) reg Assets:Savings:Emergency --display 'd>=[last month]' --cleared")
          ("savings-buffer"     "%(binary) -f %(ledger-file) reg Assets:Savings:Buffer --display 'd>=[last month]' --cleared")
          ("savings-taxes"      "%(binary) -f %(ledger-file) reg Assets:Savings:Taxes --display 'd>=[last month]' --cleared")
          ("taxes"              "%(binary) -f %(ledger-file) bal --cleared Liabilities:Taxes Savings:Taxes")
          ("uncleared"          "%(binary) -f %(ledger-file) reg --uncleared")
          ("year-spending"      "%(binary) -f %(ledger-file) --monthly --empty --collapse reg Expenses")
          )))

Man

Open man pages in a different window

(setq Man-notify-method 'friendly)

I tend to keep man pages pretty narrow

(setenv "MANWIDTH" "72")

Magit

(use-package magit
  :pin melpa-stable
  :ensure t
  :bind (("C-x g" . magit-status)
         ("C-x G" . magit-blame-addition)))

MOTD

(defun motd ()
  "Inspirational quotes and such."
  (interactive)
  (let ((quotes '(("Victor Frankl" . "Between stimulus and response there is a space. In that space is our power to choose our response. In our response lies our growth and our freedom.")
                  ("Steve Jobs" . "If today were the last day of my life, would I want to do what I am about to do today? And whenever the answer has been ``no'' for too many days in a row, I know I need to change something."))))
    (let ((record (nth (random (length quotes)) quotes)))
      (message "> %s\n> —%s" (cdr record) (car record)))))

Run the above every few hours:

(setq my-timer (run-with-idle-timer 14400 t 'motd))

Projectile

Find files in a project:

(use-package projectile
  :pin melpa-stable
  :ensure t
  :bind ("C-x p" . projectile-find-file)
  :bind-keymap ("C-x P" . projectile-command-map)
  :config
  (projectile-mode t)
  (setq projectile-enable-caching t)
  (setq projectile-cache-file (jm/local-path "projectile-cache")))
  (setq projectile-known-projects-file (jm/local-path "projectile-bookmarks.eld"))

Shell

Set $PAGER to cat to avoid WARNING: terminal is not fully functional messages.

(setenv "PAGER" "cat")

Ensure eshell garbage stays in ~/.emacs.d/local:

(setq eshell-directory-name (concat user-emacs-directory "local/eshell"))

Silver Searcher

(use-package ag
  :ensure t
  :pin melpa-stable
  :bind ("C-x a" . ag-project-regexp)
  :config
  (setq ag-highlight-search t))

Local configuration

Load local config to override any of the above settings

(load (jm/local-path "local") 'noerror)