Switch branches/tags
Nothing to show
Find file History
Latest commit 2c4fda1 Nov 1, 2017 @dieggsy dieggsy Dired tweaks.
 - Turns out the "t" binding is useful after all, heh.
 - Refine d/dired-quit.
 - Disable dired-subtree.

README.org

Emacs Init File!

Intro

This is the bulk of my emacs configuration, loaded by init.el. It’s pretty cool that I can write it in org mode, because easy organization and rearrangement are sweet. Check out @aaronbieber, @hrs, and spacemacs - a lot of the inspiration (and erm, code) for this is taken from them.

Note: The usual way to do this is to call (org-babel-load-file "config.org"), and I did that for a while, but for whatever reason I decided I wanted config.el (and its compiled verison) to be produced as I changed the file itself, hence the dotfile handling functions and file local variables. Also, the optional COMPILE argument of org-babel-load-file wasn’t working for me, so there’s that.

Contents

Initial Setup

Lexical binding

In accordance with the emacs-lisp-style-guide.

;;; -*- lexical-binding: t -*-

package-initialize

package.el is annoying like that.

;;(package-initialize)

Startup time

Next we define a variable for timing startup, and change gc-cons-threshhold temporarily (only during startup). This saves me like .3 s of startup time.

(defconst d/emacs-start-time (current-time))
(setq gc-cons-threshold 64000000)
(add-hook 'after-init-hook (lambda ()
                             (setq gc-cons-threshold 800000)))

Server

Start server if not already running. You can do this with emacs --daemon or even automate it with brew services start emacs on macOS, but I usually just run Emacs on login anyway, so this suffices.

This makes startup time irrelevant. Start emacs once, connect with emacsclient every other time. See Using Emacs as a Server.

(require 'server)
(unless (server-running-p)
  (server-start))

Load Path

Add lisp directory and subdirectories to load-path and custom-theme-load-path.

This is where I put lisp that isn’t necessarily central to my config or needs to be in a standalone file such as a some auth settings, lisp practice, really bad self-made themes, org-export-async-init-file, etc.

(eval-and-compile
  (add-to-list 'load-path (expand-file-name "lisp" user-emacs-directory))
  (let ((default-directory  "~/.emacs.d/lisp/"))
    (normal-top-level-add-subdirs-to-load-path))

  (add-to-list 'custom-theme-load-path "~/.emacs.d/lisp/"))

Libraries

I’m sure this will be required by some package somewhere along the way, but I use this a fair bit so let’s explicitly require it.

(require 'cl-lib)
(require 'subr-x)

Init utilities

(defmacro d/time (name &rest body)
  (declare (indent defun))
  `(let ((s-time (current-time)))
     (prog1
         (progn
           ,@body)
       (message "`%s' execution took %.5f seconds."
                ,name
                (float-time (time-subtract (current-time) s-time))))))

(defmacro d/setup-hook (hooks &rest body)
  "Create a setup function for HOOKS and add it to relevant hook(s)."
  (declare (indent defun))
  (let ((hooks (if (listp hooks) hooks (list hooks))))
    (let ((setup-func (intern (concat "d/setup-" (symbol-name (car hooks))))))
      `(progn
         (defun ,setup-func ()
           ,@body)
         ,@(cl-loop for hook in hooks collect
                    (let ((hook-name (intern (concat (symbol-name hook)
                                                     "-hook"))))
                      `(add-hook ',hook-name #',setup-func)))))))

(defmacro d/with-eval-after-load (feature &rest body)
  (declare (indent defun))
  `(with-eval-after-load ,feature
     (condition-case-unless-debug err
         (progn
           ,@body)
       (error
        (display-warning
         'init
         (format "%s eval-after-load: %s "
                 (symbol-name ,feature)
                 (error-message-string err))
         :error)))))

Package management

straight

Next-generation, purely functional package manager for the Emacs hacker.

(defvar straight-recipe-overrides '((nil (straight :host github
                                                 :repo "raxod502/straight.el"
                                                 :files ("straight.el")
                                                 :branch "develop"))))
(let ((bootstrap-file (concat user-emacs-directory "straight/bootstrap.el"))
      (bootstrap-version 2))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
        (url-retrieve-synchronously
         "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))
(straight-register-package
  '(seq :repo "https://git.savannah.gnu.org/git/emacs/elpa.git" :files ("packages/seq/*.el")))

use-package

A use-package declaration for simplifying your .emacs

An excellent utility for managing packages and package configuration in a neat and organized way, with advanced support for deferring, pre/post-loading configuration, time reporting, and more.

Using use-package, you can use the same init file across computers without keeping track of what’s installed or not and it will ensure that any missing packages are installed. It’s pretty neat.

(straight-use-package 'use-package)

I also set some variables - in particular, tell me the load time when a package takes more than .001 seconds, always tell me about package loading, and always defer and ensure packages are installed unless otherwise stated.

(setq use-package-minimum-reported-time .001
      use-package-verbose t
      use-package-always-defer t
      use-package-always-ensure t)

(eval-when-compile
  (require 'use-package))

Preface

Packages

no-littering

Help keeping ~/.emacs.d clean

Usually, a bunch of crap is kept in your .emacs.d folder by both built-in emacs features and external packages. This package sets up a convention to store everything in either .emacs.d/var or .emacs.d/etc.

(use-package no-littering
  :demand t
  :config
  (savehist-mode 1)
  (add-to-list 'savehist-additional-variables 'kill-ring)
  (save-place-mode 1)
  (setq auto-save-file-name-transforms
        `((".*" ,(no-littering-expand-var-file-name "auto-save/") t))))

exec-path-from-shell

Make Emacs use the $PATH set up by the user’s shell

If you’ve ever had issues where emacs doesn’t find your executables, this package should fix them nicely.

(use-package exec-path-from-shell
  :defer 5
  :config
  (setq exec-path-from-shell-check-startup-files nil)
  (exec-path-from-shell-initialize))

prettify-utils

(use-package prettify-utils
  :recipe (:host github
           :repo "Ilazki/prettify-utils.el"))

hydra

make Emacs bindings that stick around

(use-package hydra
  :config)

Core

Defaults

Custom

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

Column

Show the column number in the modeline, because I’m not a savage.

(setq column-number-mode t)

Disabled Commands

(setq disabled-command-function nil)

Kill-ring

Save stuff you’ve copied in other applications to the emacs kill-ring.

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

Line Numbers

(setq display-line-numbers-type 'relative
      display-line-numbers-width-start t
      display-line-numbers-grow-only t)
(add-hook 'display-line-numbers-mode-hook (lambda ()
                                            (setq display-line-numbers-width
                                                  (length (number-to-string
                                                           (count-lines (point-min) (point-max)))))))

Messages

Allow more messages in *Messages* buffer so you can look at what happened waaay back if you need to.

(setq message-log-max 10000)

Minibuffer

Allow editing in the minibuffer… with the minibuffer. Also resize minibuffer windows to fit text.

(setq enable-recursive-minibuffers t
      resize-mini-windows t)

Prompts

Having to type “yes” can be annoying.

(defalias 'yes-or-no-p #'y-or-n-p)

Scratch

(setq initial-scratch-message ""
      initial-major-mode 'emacs-lisp-mode)

Scrolling

Scroll one line at a time, and only scroll the current line when moving past right boundary.

(setq scroll-step 1
      scroll-conservatively 10000
      auto-hscroll-mode 'current-line)

Smoother mouse scrolling, which is now irrelevant to me since I’ve disabled the mouse in emacs.

(setq mouse-wheel-scroll-amount '(2 ((shift) . 1) ((control) . nil))
      mouse-wheel-progressive-speed nil)

Tab

Use tab for completion and cycling candidates (relevant for the minibuffer?).

(setq tab-stop-list (number-sequence 4 200 4)
      completion-cycle-threshold t
      tab-always-indent 'complete)

Time Display

(with-eval-after-load 'time
  (setq  display-time-24hr-format t
         display-time-default-load-average nil
         display-time-format "%Y-%d-%m %H:%M "
         display-time-load-average nil))

Tramp

Use ssh by default and remember passwords for tramp. Also make it quieter except for warnings and errors.

(setq tramp-default-method "ssh"
      tramp-verbose 2
      password-cache t
      password-cache-expiry 86400)

EPA

(setq epa-pinentry-mode 'loopback)

Functions

Dotfiles

(d/with-eval-after-load 'org
  (defvar d/show-async-tangle-results nil)

  (defvar d/async-babel-tangle-decrypt nil)

  (defun d/async-babel-tangle (&optional decrypt)
    "Tangle org file asynchronously."
    (interactive)
    (let ((init-tangle-start-time (current-time))
          (file (buffer-file-name))
          (async-quiet-switch "-q"))
      (async-start
       `(lambda ()
          (require 'org)
          (when ,d/async-babel-tangle-decrypt
            (require 'org-crypt)
            (org-crypt-use-before-save-magic)
            (add-hook 'org-babel-pre-tangle-hook 'org-decrypt-entries)
            (remove-hook 'org-babel-pre-tangle-hook 'save-buffer))
          (org-babel-tangle-file ,file))
       (unless d/show-async-tangle-results
         `(lambda (result)
            (if result
                (message "SUCCESS: init.org successfully tangled. (%.3fs)"
                         (float-time (time-subtract (current-time)
                                                    ',init-tangle-start-time)))
              (message "ERROR: init.org tangle failed."))))))))

Other

(defun d/toggle-rlines ()
  "Toggle relative line numbers."
  (interactive)
  (if (eq display-line-numbers 'relative)
      (setq display-line-numbers t)
    (setq display-line-numbers 'relative)))

(defmacro d/fbound-and-true? (name &optional args)
  `(and (fboundp #',name)
        (apply #',name ,args)))

Packages

These are packages that I consider absolutely essential to my emacs workflow, or that enhance emacs at a deeper level than any regular mode.

general

More convenient key definitions in emacs

That undersells it. The most convenient key definitions in emacs.

(use-package general
  :demand t
  :recipe (:host github
           :repo "noctuid/general.el"
           :branch "buttercup")
  :config
  (general-evil-setup t)

  (dolist (func '(imap emap iemap nmap vmap nvmap omap mmap rmap otomap itomap tomap))
    (put (intern (concat "general-" (symbol-name func))) 'lisp-indent-function 'defun))

  (general-create-definer
   d/mode-leader-keys
   :states '(emacs normal visual motion insert)
   :non-normal-prefix "C-,"
   :prefix ",")

  (general-create-definer
   d/leader-keys
   :states '(emacs normal visual motion insert)
   :non-normal-prefix "C-SPC"
   :prefix "SPC"))

evil

The extensible vi layer for Emacs.

I really like Vim bindings. I originally learned Emacs bindings but there was something really appealing about the simplicity and power of modal editing. So I went for it. Now I’ll never go back.

(use-package evil
  :demand t
  :general
  (nmap
   "-" 'negative-argument
   "\\" 'evil-window-next
   ;; Basically C-[ for a Dvorak keyboard (_ is for terminal).
   "C-_" 'keyboard-quit
   "C-/"  'keyboard-quit
   [escape]  'keyboard-quit)
  (:states '(insert replace visual)
   "C-_" 'evil-normal-state
   "C-/" 'evil-normal-state)
  (vmap [escape] 'keyboard-quit)
  :init
  (setq evil-want-C-u-scroll t
        evil-want-fine-undo t
        evil-search-module 'evil-search
        evil-lookup-func (lambda () (man (thing-at-point 'word))))
  :config
  (setq evil-insert-state-cursor '(bar . 1)
        evil-emacs-state-cursor '(bar . 1)
        evil-ex-search-vim-style-regexp t
        evil-normal-state-tag  " N "
        evil-insert-state-tag  " I "
        evil-motion-state-tag  " M "
        evil-visual-state-tag  " V "
        evil-emacs-state-tag   " E "
        evil-replace-state-tag " R "
        evil-operator-state-tag " O ")

  (evil-ex-define-cmd "dtw" #'delete-trailing-whitespace)

  (evil-mode 1))

ivy

Ivy - a generic completion frontend for Emacs, Swiper - isearch with an overview, and more. Oh, man!

A really nice search/completion system for emacs.

ivy

(use-package ivy
  :general
  (:keymaps 'ivy-minibuffer-map
   [escape] 'keyboard-escape-quit
   "C-/" 'keyboard-escape-quit
   [S-return] 'ivy-dispatching-done-hydra
   [C-return] 'ivy-immediate-done
   "C-j" 'ivy-next-line
   "C-k" 'ivy-previous-line
   [S-up] 'ivy-previous-history-element
   [S-down] 'ivy-next-history-element)
  (d/leader-keys
   "-" 'ivy-resume
   "bb" 'ivy-switch-buffer
   "bB" 'ivy-switch-buffer-other-window)
  :config
  (ivy-mode 1)

  (setq ivy-re-builders-alist '((swiper . ivy--regex-plus)
                                (t . ivy--regex-ignore-order)))
  (setq ivy-format-function 'ivy-format-function-line
        ivy-use-virtual-buffers t ; Show recent files
        ivy-count-format ""
        ivy-extra-directories nil; '("../") ; ignore current folder and parent dir
        recentf-max-saved-items 50
        ivy-use-selectable-prompt t
        ivy-display-functions-alist nil
        ivy-switch-buffer-faces-alist '((dired-mode . ivy-subdir)
                                        (wdired-mode . ivy-subdir)
                                        (ranger-mode . ivy-subdir))))

(use-package ivy-hydra
  :after ivy)

swiper

(use-package swiper
  :general
  (d/leader-keys
   "sm" 'swiper-multi
   "sS" 'swiper-all)
  :config (setq swiper-goto-start-of-match t))

counsel

(use-package counsel
  :general
  ("M-x" 'counsel-M-x
   "C-x C-f" 'counsel-find-file)
  (d/leader-keys
   "SPC" 'counsel-M-x
   "aa"  'counsel-linux-app
   "ff"  'counsel-find-file
   "fF"  'find-file-other-window
   "fj"  'counsel-file-jump
   "fl"  'counsel-locate
   "hdF" 'counsel-describe-face
   "hdb" 'counsel-descbinds
   "hdf" 'counsel-describe-function
   "hdv" 'counsel-describe-variable
   "iu"  'counsel-unicode-char
   "sr"  'counsel-rg
   "ss"  'counsel-grep-or-swiper
   "y"   'counsel-yank-pop)
  :commands counsel-describe-face
  :config
  (when (eq system-type 'darwin)
    (setq counsel-locate-cmd 'counsel-locate-cmd-mdfind))

  (setq conusel-org-goto-display-style 'path
        counsel-org-goto-separator ": "
        counsel-org-goto-face-style 'org
        counsel-org-goto-display-todo t
        counsel-grep-base-command "rg -i -M 120 --no-heading --line-number --color never %s %s"
        counsel-yank-pop-separator "\n─────────────────────────\n"
        counsel-find-file-ignore-regexp (rx (or (group string-start (char ".#"))
                                                (group (char "~#") string-end)
                                                (group ".elc" string-end)
                                                (group ".pyc" string-end))))
  (counsel-mode 1)
  (defalias 'locate #'counsel-locate)

  (define-advice counsel-yank-pop-action (:override (s) paste-after)
    "Paste text after point, which is consistent with evil-paste-after.

Source: https://git.io/vQKmf"
    (save-excursion
      (undo-boundary)
      (unless (eq (point) (point-max))
        (forward-char))
      (with-ivy-window
        (delete-region ivy-completion-beg
                       ivy-completion-end)
        (insert (substring-no-properties s))
        (setq ivy-completion-end (point))))
    (forward-char (length s)))

  ;; (define-advice counsel--yank-pop-format-function (:override (cand-pairs) arrow-format)
  ;;     "Use the arrow format for counsel-yank-pop for consistency with
  ;; ivy-format-function-arrow.

  ;; Source: https://git.io/vQK0v"
  ;;     (ivy--format-function-generic
  ;;      (lambda (str)
  ;;        (let ((temp-list
  ;;               (split-string (counsel--yank-pop-truncate str) "\n" t)))
  ;;          (mapconcat 'identity
  ;;                     (append (list (concat "> " (car temp-list)))
  ;;                             (mapcar (lambda (s) (concat "  " s))
  ;;                                     (cdr temp-list)))
  ;;                     "\n")))
  ;;      (lambda (str)
  ;;        (mapconcat (lambda (s) (concat "  " s))
  ;;                   (split-string (counsel--yank-pop-truncate str) "\n" t)
  ;;                   "\n"))
  ;;      cand-pairs
  ;;      counsel-yank-pop-separator))
  )

org-mode

Org mode is for keeping notes, maintaining TODO lists, planning projects, and authoring documents with a fast and effective plain-text system.

But really, it’s life.

Note: Getting emacs to run the latest version of org can be weird. Least I didn’t find a satisfactory solution for a while. Check out this blog post for some advice on that (still relevant now-2016). In particular, make sure you have something like:
(package-initialize)
(setq package-enable-at-startup nil)

at the beginning of your init.el/emacs.d, or as in my case something like:

;; (package-initialize)
(require 'package)
(setq package-enable-at-startup nil)
;; ---------------------------
;; my load-path settings here
;; ---------------------------
(package-initialize)

I actually don’t think I tried the former option, but the latter simply worked so I went with it.

Package

(use-package org
  :recipe (:host github
           :repo "emacsmirror/org"
           :files ("lisp/*.el" "contrib/lisp/*.el"))
  :general
  (nmap org-mode-map
    "<" 'org-metaleft
    ">" 'org-metaright
    "gh" 'outline-up-heading
    "gl" 'outline-next-visible-heading
    "gj" 'org-forward-heading-same-level
    "gk" 'org-backward-heading-same-level
    "gt" 'org-todo
    "ga" 'org-archive-subtree
    "M-l" 'org-metaright
    "M-h" 'org-metaleft
    "M-k" 'org-metaup
    "M-j" 'org-metadown
    "M-L" 'org-shiftmetaright
    "M-H" 'org-shiftmetaleft
    "M-K" 'org-shiftmetaup
    "M-J" 'org-shiftmetadown)
  (d/leader-keys
   "C"   'org-capture
   "bo"  'org-iswitchb
   "ao"  '(:ignore t :wk "org")
   "ao#" 'org-agenda-list-stuck-projects
   "ao/" 'org-occur-in-agenda-files
   "aoO" 'org-clock-out
   "aoa" 'org-agenda-list
   "aoe" 'org-store-agenda-views
   "aol" 'org-store-link
   "aom" 'org-store-tags-view
   "aoo" 'org-agenda
   "aos" 'org-search-view
   "aot" 'org-todo-list )
  :init
  (setq org-list-allow-alphabetical t)
  :config
  (require 'ox-extra)
  (ox-extras-activate '(ignore-headlines))
  (d/with-eval-after-load 'org-crypt
    (org-crypt-use-before-save-magic))
  (require 'org-mobile))

Defaults

Files
(d/with-eval-after-load 'org
  (setq org-agenda-text-search-extra-files '(agenda-archives)
        org-agenda-files '("~/Dropbox/org/todo.org" "~/Dropbox/org/gcal.org")
        org-default-notes-file "~/Dropbox/org/todo.org"
        d/notes-file "~Dropbox/org/notes.org"
        org-directory "~/Dropbox/org"
        org-archive-location "~/Dropbox/org/archive.org::"
        org-mobile-inbox-for-pull "~/Dropbox/org/mobile.org"
        org-export-async-init-file
        (locate-user-emacs-file "lisp/org-async-init.el")))
Todo/agenda
(d/with-eval-after-load 'org
  (setq org-enforce-todo-dependencies t
        org-enforce-todo-checkbox-dependencies t
        org-log-done 'time
        org-log-redeadline 'time
        org-log-reschedule 'time
        org-agenda-skip-scheduled-if-done t
        org-agenda-skip-deadline-if-done t
        org-agenda-hide-tags-regexp ".*"
        org-agenda-span 'week)

  (setq org-agenda-deadline-faces
        '((1.0 . org-warning)
          (0.5 . org-upcoming-deadline)
          (0.0 . '(:foreground "#A89984"))))

  (setq org-todo-keywords
        '((sequence "TODO(t)" "IN-PROGRESS(p)" "WAITING(w)" "|"
                    "DONE(d)" "CANCELED(c)")
          (sequence "READ(r)" "|"
                    "DONE(h)")))

  (setq org-capture-templates
        '(("t" "Todo")
          ("ts" "Todo: School")
          ("te" "Todo: Emacs" entry
           (file+olp org-default-notes-file "Emacs")
           "* TODO %?")
          ("n" "Note")
          ("g" "Google calendar" entry
           (file "~/Dropbox/org/gcal.org") "* %?\n\n%^T"))))
Behavior/appearance
(d/with-eval-after-load 'org
  (setq org-startup-indented t
        org-catch-invisible-edits 'error
        org-insert-heading-respect-content t
        org-src-window-setup 'current-window
        org-list-demote-modify-bullet '(("-" . "*")
                                        ("*" . "+"))
        org-export-in-background t
        org-confirm-babel-evaluate nil
        org-src-tab-acts-natively t
        org-M-RET-may-split-line nil
        org-list-use-circular-motion t
        org-log-into-drawer t
        org-imenu-depth 5
        org-goto-interface 'outline-path-completion
        org-outline-path-complete-in-steps nil
        org-link-search-must-match-exact-headline nil
        org-confirm-elisp-link-function 'y-or-n-p
        org-tags-exclude-from-inheritance '("crypt")
        org-crypt-key "diegoamundo@protonmail.com"
        org-confirm-elisp-link-not-regexp (rx "("
                                              (or "org-wiki-search"
                                                  "describe-function"
                                                  "describe-variable")
                                              (minimal-match (0+ nonl))
                                              ")"))
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((python . t)
     (emacs-lisp . t)
     (calc . t)
     ;; (ipython . t)
     (shell . t)
     (lisp . t)
     (C . t)
     (scheme . t)))

  ;; appearance
  (setq org-src-fontify-natively t
        org-src-preserve-indentation t
        org-fontify-quote-and-verse-blocks t
        org-hide-emphasis-markers t
        org-startup-with-inline-images t
        org-ellipsis ""
        org-highlight-latex-and-related '(latex)
        org-pretty-entities t
        org-image-actual-width 500)

  (mapc (lambda (arg) (setcdr arg (list (downcase (cadr arg)))))
        org-structure-template-alist)

  (add-to-list 'org-structure-template-alist
               (list "sel" (concat "#+begin_src emacs-lisp\n"
                                   "?\n"
                                   "#+end_src")))
  (add-to-list 'org-structure-template-alist
               (list "sp" (concat "#+begin_src python"
                                  "?\n"
                                  "#+end_src")))

  ;; latex
  (setq org-latex-listings t)
  (add-to-list 'org-latex-packages-alist '("" "listings"))
  (add-to-list 'org-latex-packages-alist '("" "color"))
  (add-to-list 'org-latex-packages-alist '("" "tabularx")))

Variables

(defvar d/org-prettify-alist
  (prettify-utils-generate
   ("TODO" "❯❯❯")
   ("READ" "❙❙❙")
   ("IN-PROGRESS" "○○○")
   ("WAITING" "■■■")
   ("CANCELED" "✗✗✗")
   ("DONE" "✓✓✓")))

Functions

(d/with-eval-after-load 'org
  (defmacro d/create-block-wrap (&rest blocktypes)
    `(progn
       ,@(cl-loop
          for type in blocktypes collect
          (let ((newfunc (intern
                          (concat "d/org-wrap-with-block-"
                                  (replace-regexp-in-string " " "-" type)))))
            `(defun ,newfunc ()
               (interactive)
               (backward-paragraph)
               (insert ,(format "\n#+begin_%s" type))
               (forward-paragraph)
               (insert ,(format "#+end_%s\n" (car (split-string type))))
               (backward-paragraph))))))

  (d/create-block-wrap
   "src"
   "src python"
   "src emacs-lisp"
   "export latex")

  (defmacro d/org-emphasize (&rest args)
    "Make functions for setting the emphasis in org mode"
    `(progn
       ,@(cl-loop for (name char) on args
                  by #'cddr collect
                  (let ((fname (intern (concat "d/org-" name))))
                    `(defun ,fname ()
                       (interactive)
                       (org-emphasize ,char))))))

  (d/org-emphasize
   "bold" ?*
   "italic" ?/
   "code" ?~
   "underline" ?_
   "verbatim" ?=
   "strike-through" ?+
   "clear" ? )

  (defun d/org-agenda-toggle-date (current-line)
    "Toggle `SCHEDULED' and `DEADLINE' tag in the capture buffer.

Source: https://git.io/vQK0I"
    (interactive "P")
    (save-excursion
      (let ((search-limit (if current-line
                              (line-end-position)
                            (point-max))))

        (if current-line (beginning-of-line)
          (goto-char (point-min)))
        (if (search-forward "DEADLINE:" search-limit t)
            (replace-match "SCHEDULED:")
          (and (search-forward "SCHEDULED:" search-limit t)
               (replace-match "DEADLINE:"))))))

  (defun d/org-insert-list-leader-or-self (char)
    "If on column 0, insert space-padded CHAR; otherwise insert CHAR.

This has the effect of automatically creating a properly indented list
leader; like hyphen, asterisk, or plus sign; without having to use
list-specific key maps.

Source: https://git.io/vQK0s"
    (if (= (current-column) 0)
        (insert (concat " " char " "))
      (insert char)))

  (defun d/org-swap-tags (tags)
    "Replace any tags on the current headline with TAGS.

The assumption is that TAGS will be a string conforming to Org Mode's
tag format specifications, or nil to remove all tags.

Source: https://git.io/vQKEE"
    (let ((old-tags (org-get-tags-string))
          (tags (if tags
                    (concat " " tags)
                  "")))
      (save-excursion
        (beginning-of-line)
        (re-search-forward
         (concat "[ \t]*" (regexp-quote old-tags) "[ \t]*$")
         (line-end-position) t)
        (replace-match tags)
        (org-set-tags t))))

  (defun d/org-set-tags (tag)
    "Add TAG if it is not in the list of tags, remove it otherwise.

TAG is chosen interactively from the global tags completion table.

Source: https://git.io/vQKEa"
    (interactive
     (list (let ((org-last-tags-completion-table
                  (if (derived-mode-p 'org-mode)
                      (org-uniquify
                       (delq nil (append (org-get-buffer-tags)
                                         (org-global-tags-completion-table))))
                    (org-global-tags-completion-table))))
             (completing-read
              "Tag: " 'org-tags-completion-function nil nil nil
              'org-tags-history))))
    (let* ((cur-list (org-get-tags))
           (new-tags (mapconcat 'identity
                                (if (member tag cur-list)
                                    (delete tag cur-list)
                                  (append cur-list (list tag)))
                                ":"))
           (new (if (> (length new-tags) 1) (concat " :" new-tags ":")
                  nil)))
      (d/org-swap-tags new)))

  (defun d/org-choose-bullet-type ()
    "Change the bullet type for org lists with a prompt."
    (interactive)
    (let ((char (read-char-choice
                 "Bullet type? (-|*|+|1|2|a|b|A|B): "
                 '(?* ?- ?+ ?1 ?2 ?a ?b ?A ?B))))
      (pcase char
        (?1 (org-cycle-list-bullet 3))
        (?2 (org-cycle-list-bullet 4))
        (?a (org-cycle-list-bullet 5))
        (?b (org-cycle-list-bullet 7))
        (?A (org-cycle-list-bullet 6))
        (?B (org-cycle-list-bullet 8))
        (_ (org-cycle-list-bullet (char-to-string char))))))

  (defun d/org-at-openable-item? ()
    (when (eq major-mode 'org-mode)
      (let* ((context (org-element-lineage
                       (org-element-context)
                       '(clock footnote-definition footnote-reference headline
                               inlinetask link timestamp)
                       t))
             (type (org-element-type context)))
        (memq type '(footnote-definition
                     footnote-reference
                     headline inlinetask
                     link
                     timestamp)))))

  (defun d/org-hugo-export ()
    "Export current subheading to markdown using pandoc."
    (interactive)
    ;; Save cursor position
    (save-excursion
      ;; Go to top level heading for subtree
      (unless (eq (org-current-level) 1)
        (org-up-heading-all 10))
      ;; Set export format, pandoc options, post properties
      (let* ((org-pandoc-format 'markdown)
             (org-pandoc-options-for-markdown '((standalone . t)
                                                (atx-headers . t)
                                                (columns . 79)))
             (hl (org-element-at-point))
             (filename (org-element-property :EXPORT_FILE_NAME hl))
             (title (format "\"%s\"" (org-element-property :title hl)))
             (slug (format "\"%s\"" (org-element-property :SLUG hl)))
             (date (format "\"%s\"" (org-element-property :DATE hl)))
             (tags (org-get-tags-at))
             (categories
              (format "[\"%s\"]" (mapconcat 'identity tags "\",\""))))
        (if (string= (org-get-todo-state) "DRAFT")
            (message "Draft not exported")
          (progn
            ;; Make the export
            (org-export-to-file
                'pandoc
                (org-export-output-file-name
                 (concat (make-temp-name ".tmp") ".org") t)
              nil t nil nil nil
              (lambda (f)
                (org-pandoc-run-to-buffer-or-file f 'markdown t nil)))
            ;; Use advice-add to add advice to existing process sentinel
            ;; to modify file /after/ the export process has finished.
            (advice-add
             #'org-pandoc-sentinel
             :after
             `(lambda (process event)
                (with-temp-file ,filename
                  (insert-file-contents ,filename)
                  (goto-char (point-min))
                  ;; Remove default header
                  (re-search-forward "---\\(.\\|\n\\)+?---\n\n")
                  (replace-match "")
                  (goto-char (point-min))
                  ;; Insert new properties
                  (insert
                   (format
                    "---\ntitle: %s\nslug: %s\ndate: %s\ncategories: %s\n---\n\n"
                    ,title ,slug ,date ,categories))
                  ;; Demote headings and tweak code blocks
                  (dolist (reps '(("^#" . "##")
                                  ("``` {\\.\\(.+?\\)}" . "```\\1")))
                    (goto-char (point-min))
                    (while (re-search-forward (car reps) nil t)
                      (replace-match (cdr reps))))))
             '((name . "hugo-advice")))
            ;; We don't want our advice to stick around afterwards
            (advice-remove #'org-pandoc-sentinel 'hugo-advice)
            (when (string= (org-get-todo-state) "")
              (org-todo))))))))

Keyboard Macros

Turn this into an elisp function
(d/with-eval-after-load 'org
  (fset 'd/org-wrap-with-quote
        [?\{ ?i return ?# ?+ ?b ?e ?g ?i ?n ?_ ?q ?u ?o ?t ?e ?\C-/ ?\} ?i return
             up ?# ?+ ?e ?n ?d ?_ ?q ?u ?o ?t ?e ?\C-/ ?\{ ?j ?i ?  ?  ?\M-q
             ?\M-q ?\M-q ?\C-/]))

Bindings

(d/mode-leader-keys
 :keymaps 'org-mode-map
 "$"  'org-archive-subtree
 "'"  'org-edit-special
 "."  'org-time-stamp
 "/"  'org-sparse-tree
 ":"  'd/org-set-tags
 "-"  'org-decrypt-entry
 "A"  'org-archive-subtree
 "N"  'widen
 "P"  'org-set-property
 "R"  'org-refile
 "^"  'org-sort
 "a"  'org-agenda
 "c"  'org-capture
 "d"  'org-deadline
 "g"  'counsel-org-goto
 "G"  'counsel-org-goto-all
 "l"  'd/org-choose-bullet-type
 "n"  'org-narrow-to-subtree
 "s"  'org-schedule

 "i"  '(:ignore t :wk "insert")
 "ic" 'org-table-insert-column
 "ir" 'org-table-insert-row
 "il" 'org-insert-link
 "if" 'org-footnote-new
 "id" 'org-insert-drawer

 "e"  '(:ignore t :wk "eval/export")
 "ed" 'org-export-dispatch
 "eh" 'd/org-hugo-export
 "es" 'd/eval-surrounding-sexp
 "er" 'eval-region
 "eb" 'd/eval-buffer
 "ef" 'd/eval-defun

 "b"  'org-babel-tangle

 "x"  '(:ignore t :wk "text")
 "xb" 'd/org-bold
 "xi" 'd/org-italic
 "xc" 'd/org-code
 "xu" 'd/org-underline
 "xv" 'd/org-verbatim
 "xs" 'd/org-strike-through
 "xr" 'd/org-clear
 "xq" 'd/org-wrap-with-quote
 "xx" 'org-cut-special
 "xp" 'org-paste-special

 ;; tables
 "t"   '(:ignore t :wk "table")
 "ta"  'org-table-align
 "tb"  'org-table-blank-field
 "tc"  'org-table-convert
 "tdc" 'org-table-delete-column
 "tdr" 'org-table-kill-row
 "te"  'org-table-eval-formula
 "tE"  'org-table-export
 "th"  'org-table-previous-field
 "tH"  'org-table-move-column-left
 "tic" 'org-table-insert-column
 "tih" 'org-table-insert-hline
 "tiH" 'org-table-hline-and-move
 "tir" 'org-table-insert-row
 "tI"  'org-table-import
 "tj"  'org-table-next-row
 "tJ"  'org-table-move-row-down
 "tK"  'org-table-move-row-up
 "tl"  'org-table-next-field
 "tL"  'org-table-move-column-right
 "tn"  'org-table-create
 "tN"  'org-table-create-with-table.el
 "tr"  'org-table-recalculate
 "ts"  'org-table-sort-lines
 "ttf" 'org-table-toggle-formula-debugger
 "tto" 'org-table-toggle-coordinate-overlays
 "tw"  'org-table-wrap-region)

(d/with-eval-after-load 'org
  (d/mode-leader-keys
   :keymaps 'org-src-mode
   :definer 'minor-mode
   "'" 'org-edit-src-exit)

  (d/leader-keys
   :keymaps 'org-src-mode
   :definer 'minor-mode
   "fs" 'org-edit-src-save))

Setup

Agenda
(general-def org-agenda-mode-map
  "j" 'org-agenda-next-line
  "k" 'org-agenda-previous-line
  "n" 'org-agenda-next-date-line
  "p" 'org-agenda-previous-date-line
  "c" 'org-agenda-capture
  "R" 'org-revert-all-org-buffers
  "RET" 'org-agenda-switch-to)

(d/with-eval-after-load 'org-agenda
  (setq org-habit-graph-column 50))

(d/setup-hook org-agenda-mode
  (setq-local prettify-symbols-alist d/org-prettify-alist)
  (prettify-symbols-mode))
Capture
(imap org-capture-mode-mop
  "C-d" 'd/org-agenda-toggle-date)
(nmap org-capture-mode-map
  "C-d" 'd/org-agenda-toggle-date)
Org
(d/with-eval-after-load 'org
  (dolist (char '("+" "-"))
    (define-key org-mode-map (kbd char)
      `(lambda ()
         (interactive)
         (d/org-insert-list-leader-or-self ,char))))
  (setq org-bullets-bullet-list '("")))

(d/setup-hook org-mode
  (setq-local prettify-symbols-alist d/org-prettify-alist)
  (prettify-symbols-mode)
  ;; (org-bullets-mode 1)
  (goto-address-mode))

Bindings

Leader

(d/leader-keys
 "qf" 'delete-frame
 "qq" 'save-buffers-kill-emacs

 "t"   '(:def d/toggle/body :wk "toggle")

 "&"   'async-shell-command
 ":"   'eval-expression
 "r"   'repeat
 "u"   'universal-argument)

macOS fullscreen

A convenient full-screen binding I’m used to from iTerm.

(when (eq system-type 'darwin)
  (global-set-key (kbd "<s-return>") #'toggle-frame-fullscreen))

Macros

(general-define-key
 "<f11>" 'kmacro-start-macro-or-insert-counter
 "<f12>" 'kmacro-end-or-call-macro)

Minibuffer

I like to use C-/ as Evil/Vim’s C-[ since I use a Dvorak keyboard, so I like to also use these keys to quit out of the minibuffer.

(general-define-key
 :keymaps '(minibuffer-local-map
            minibuffer-local-ns-map
            minibuffer-local-completion-map
            minibuffer-local-must-match-map
            minibuffer-local-isearch-map)
 [?\C-/]  'minibuffer-keyboard-quit
 [?\C-_]  'minibuffer-keyboard-quit
 [escape] 'minibuffer-keyboard-quit)

Ret

(mmap "RET"
      (general-predicate-dispatch nil
        (thing-at-point 'url) 'goto-address-at-point
        (d/fbound-and-true? d/org-at-openable-item?) 'org-open-at-point
        (d/fbound-and-true? org-at-item-checkbox-p) 'org-toggle-checkbox
        (d/fbound-and-true? org-in-src-block-p) 'org-babel-execute-src-block))

universal argument

(general-define-key
 :keymaps 'universal-argument-map
 "SPC u" 'universal-argument-more)

Help

External Packages

define-word

Display the definition of word at point in Emacs

(use-package define-word
  :commands d/define-word
  :general
  (d/leader-keys "sw" 'd/define-word)
  :config
  (defun d/define-word (&optional word)
    (interactive)
    (if word
        (define-word word)
      (let ((word (read-string
                   (concat "Define word ["
                           (if (region-active-p)
                               (buffer-substring (region-beginning) (region-end))
                             (thing-at-point 'word)) "]: ")
                   nil nil
                   (thing-at-point 'word))))
        (define-word word)))))

devdocs

Emacs package allowing you to easily search the DevDocs documentation

(use-package devdocs
  :general
  (d/leader-keys "hdd"  'devdocs-search))

emacs-google-this

A set of emacs functions and bindings to google under point.

(use-package google-this
  :commands ddg-this-search
  :general
  (d/leader-keys
   "sd" 'ddg-this-search
   "sg" 'google-this-search)
  :config
  (defun ddg-this-parse-and-search-string (text prefix &optional search-url)
    "Convert illegal characters in TEXT to their %XX versions, and then duckduckgo.
PREFIX determines quoting.

Don't call this function directly, it could change depending on
version. Use `ddg-this-string' instead."
    (let* (;; Create the url
           (query-string (google-this--maybe-wrap-in-quotes text prefix))
           ;; Perform the actual search.
           (browse-result (funcall google-this-browse-url-function
                                   (format (or search-url "https://duckduckgo.com/?q=%s")
                                           (url-hexify-string query-string)))))
      ;; Maybe suspend emacs.
      (when google-this-suspend-after-search (suspend-frame))
      ;; Return what browse-url returned (very usefull for tests).
      browse-result))

  (defun ddg-this-pick-term (prefix)
    "Decide what \"this\" and return it.
PREFIX determines quoting."
    (let* ((term (if (region-active-p)
                     (buffer-substring (region-beginning) (region-end))
                   (or (thing-at-point 'symbol)
                       (thing-at-point 'word)
                       (buffer-substring (line-beginning-position)
                                         (line-end-position)))))
           (term (read-string (concat "DuckDuckGo [" term "]: ") nil nil term)))
      term))

  (defun ddg-this-search (prefix &optional search-string)
    "Write and do a DuckDuckGo search.
Interactively PREFIX determines quoting.
Non-interactively SEARCH-STRING is the string to search."
    (interactive "P")
    (let* ((term (ddg-this-pick-term prefix)))
      (if (stringp term)
          (ddg-this-parse-and-search-string term prefix search-string)
        (message "[google-this-string] Empty query.")))))

google-translate

Emacs interface to Google Translate

(use-package google-translate)

info+

Extensions to info.el.

(use-package info+)

sx

Stack Exchange for Emacs

(use-package sx)

tldr

(use-package tldr
  :general
  (d/leader-keys "ht" 'tldr)
  (nmap tldr-mode-map
    "q" 'quit-window))

Builtin

Man

(use-package man
  :general
  (d/leader-keys
   "hm" 'man)
  :config
  (setq Man-notify-method 'aggressive))

Info

(use-package Info
  :ensure nil
  :config
  (nmap Info-mode-map
    "i" Info-mode-map))

Bindings

Leader bindings

(d/leader-keys
 "hc"  '(:ignore t :wk "customize")
 "hca" 'customize-apropos
 "hcf" 'customize-face-other-window
 "hcg" 'customize-group-other-window
 "hcm" 'customize-mode
 "hcv" 'customize-variable-other-window
 "hdV" 'apropos-value
 "hdc" 'describe-char
 "hdk" 'describe-key
 "hdm" 'describe-mode
 "hdp" 'describe-package
 "hds" 'describe-symbol
 "hdt" 'describe-theme
 "hn"  'view-emacs-news
 "hs"  'system-name
 "hv"  'version)

Setup

(d/setup-hook help-mode
  (goto-address-mode)
  (rainbow-mode))

Files/Buffers

Defaults

How to uniquify buffer names.

(setq uniquify-buffer-name-style 'forward)

If a frame is already open, use it to open files.

(setq ns-pop-up-frames nil)

Follow symlinks to files under version control because why would I not.

(setq vc-follow-symlinks t)

Select the help window when opening it (I like this so I can quickly q out).

(setq help-window-select t)

Clean up whitespace before saving files.

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

Backup

(setq version-control t
      delete-old-versions t)

Executable

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

External Packages

osx-trash

Make Emacs’ delete-by-moving-to-trash do what you expect it to do on OS X.

(use-package osx-trash
  :defer 5
  :if (eq system-type 'darwin)
  :config
  (osx-trash-setup)
  (setq delete-by-moving-to-trash t))

dired-du

(use-package dired-du
  :recipe (:host github :repo "emacsmirror/dired-du")
  :config
  (setq dired-du-size-format t))

ranger

Bringing the goodness of ranger to dired!

(use-package ranger
  :commands d/deer
  :disabled
  :general
  (d/leader-keys
   "ad" 'd/deer
   "ar" 'ranger)
  :init
  (require 'bookmark)
  :config
  (ranger-override-dired-mode t)
  (setq ranger-show-literal nil
        ranger-show-hidden nil
        ranger-cleanup-eagerly t
        ranger-parent-depth 0
        ranger-modify-header nil)

  (define-advice ranger-travel (:override nil with-ivy)
    "Use ivy for ranger-travel instead."
    (interactive)
    (cond
     ((featurep 'ivy)
      (counsel-find-file default-directory))
     (t (call-interactively #'ido-find-file))))

  (define-advice ranger-toggle-mark (:after nil next-file)
    (interactive)
    (ranger-next-file 1))

  (defun d/deer (arg)
    (interactive "P")
    (if arg
        (deer)
      (deer-jump-other-window))))

projectile

Project Interaction Library for Emacs

(use-package projectile
  :general
  (d/leader-keys
   "p"  '(:ignore t :wk "project")
   "pg" 'projectile-vc
   "pk" 'projectile-kill-buffers
   "po" 'projectile-multi-occur
   "pr" 'projectil-recentf)
  :config
  (setq projectile-globally-ignored-files '("TAGS" ".DS_Store")
        projectile-ignored-projects '("/usr/local")
        projectile-completion-system 'ivy)
  (projectile-mode))

counsel-projectile

(use-package counsel-projectile
  :commands d/project-find-file
  :general
  (d/leader-keys
   "pb" 'counsel-projectile-switch-to-buffer
   "pd" 'counsel-projectile-find-dir
   "pf" 'counsel-projectile-find-file
   "pp" 'counsel-projectile
   "ps" 'counsel-projectile-switch-project)
  :config
  (defun d/project-find-file ()
    (interactive)
    (condition-case nil
        (counsel-git)
      (error (counsel-projectile-find-file)))))

headlong

(use-package headlong
  :general
  (d/leader-keys "fB" 'headlong-bookmark-jump-other
                     "fb" 'headlong-bookmark-jump)
  :config
  (bookmark-maybe-load-default-file))

Built-in

Dired

(use-package dired
  :ensure nil
  :commands dired-here
  :general
  (d/leader-keys
   "ad" 'dired-here)
  :init
  (d/setup-hook dired-mode
    (hl-line-mode 1)
    (dired-collapse-mode)
    (dired-omit-mode)
    (diredfl-mode)
    (setq-local prettify-symbols-alist d/dired-prettify-alist)
    (prettify-symbols-mode))
  (d/setup-hook dired-after-readin
    (dired-git-status))
  (defvar d/dired-prettify-alist
    '(("->" . "")))
  :config
  (nmap dired-mode-map
    "q" 'd/dired-quit
    "h" 'dired-up-directory
    "l" 'dired-open-file
    "j" 'dired-next-line
    "k" 'dired-previous-line
    "r" 'dired-do-rename
    "R" 'dired-do-redisplay
    "n" 'evil-ex-search-next
    "N" 'evil-ex-search-previous)

  (setq dired-listing-switches "-lGXhA --group-directories-first"
        dired-dwim-target t
        dired-omit-files (rx string-start "." (1+ nonl) string-end)
        dired-clean-confirm-killing-deleted-buffers nil)

  (with-eval-after-load 'dired-async
    (dired-async-mode 1))

  (d/leader-keys
   :keymaps 'wdired-mode-map
   "fs" 'wdired-finish-edit)

  (defun d/dired-quit ()
    (interactive)
    (while (eq major-mode 'dired-mode)
      (quit-window))
    (when (and d/dired-close-window
               (eq major-mode 'dired-mode))
      (delete-window))
    (setq d/dired-close-window nil))

  (defun dired-here (&optional arg)
    (interactive "P")
    (if arg
        (dired default-directory)
      (dired-other-window default-directory)))

  (defvar d/dired-close-window nil)

  (define-advice dired-other-window
      (:before (dirname &optional switches) other-window-exists)
    (if (= (length (window-list)) 1)
        (setq d/dired-close-window t)
      (setq d/dired-close-window nil))))

Ibuffer

(use-package ibuffer
  :init
  (d/setup-hook ibuffer-mode
    (ibuffer-switch-to-saved-filter-groups "Default")))

Functions

File/Buffer Manipulation

(defun d/copy-file ()
  "Copy file to another location.

Source: https://git.io/vQKES"
  (interactive)
  (call-interactively #'write-file))

(defun d/safe-erase-buffer ()
  "Prompt before erasing buffer.
Source: https://git.io/vQKEd"
  (interactive)
  (if (y-or-n-p (format "Erase content of buffer %s ? " (current-buffer)))
      (progn
        (erase-buffer)
        (message "Buffer erased."))
    (message "erase-buffer cancelled")))

(defun d/download-file ()
  "Download a file from url to specified path."
  (interactive)
  (let* ((file-url (read-from-minibuffer "URL: "))
         (file-name
          (read-from-minibuffer "File : "
                                (concat default-directory
                                        (file-name-nondirectory file-url)))))
    (url-copy-file file-url file-name)))

Switching

(defun d/switch-to-scratch ()
  "Switch to scratch buffer."
  (interactive)
  (switch-to-buffer "*scratch*"))

(defun d/switch-to-star ()
  "Switch to '*' buffers."
  (interactive)
  (let ((ivy-initial-inputs-alist '((ivy-switch-buffer . "^*"))))
    (ivy-switch-buffer)))

(defun d/switch-to-customize ()
  "Switch to \"Customize\" buffers."
  (interactive)
  (let ((ivy-initial-inputs-alist '((ivy-switch-buffer . "^*customize "))))
    (ivy-switch-buffer)))

(defun d/switch-to-messages ()
  "Switch to *Messages* buffer."
  (interactive)
  (switch-to-buffer "*Messages*"))

Narrowing

(defun d/narrow-and-set-normal ()
  "Narrow to the region and, if in a visual mode, set normal mode.

Source: https://git.io/vQKEx"
  (interactive)
  (narrow-to-region (region-beginning) (region-end))
  (if (string= evil-state "visual")
      (progn (evil-normal-state nil)
             (evil-goto-first-line))))

(defun d/narrow-to-region-or-subtree ()
  "Narrow to a region, if set, otherwise to an Org subtree, if present.

Source: https://git.io/vQKuf"
  (interactive)
  (if (and mark-active
           (not (= (region-beginning) (region-end))))
      (d/narrow-and-set-normal)
    (if (derived-mode-p 'org-mode)
        (org-narrow-to-subtree))))

(defun d/narrow-dwim ()
  "Narrow to a thing or widen based on context.
Attempts to follow the Do What I Mean philosophy.

Source: https://git.io/vQKuU"
  (interactive)
  (if (buffer-narrowed-p)
      (widen)
    (d/narrow-to-region-or-subtree)))

Bindings

(d/leader-keys
 "b*" 'd/switch-to-star
 "bC" 'd/switch-to-customize
 "bK" 'kill-buffer
 "bM" 'd/switch-to-messages
 "br" 'revert-buffer
 "bR" 'rename-buffer
 "bS" 'd/switch-to-scratch
 "bc" 'clone-indirect-buffer-other-window
 "be" 'd/safe-erase-buffer
 "bi" 'ibuffer
 "bk" 'kill-this-buffer
 "bm" 'kill-matching-buffers
 "bq" 'kill-buffer-and-window
 "bv" 'view-mode

 "fc" 'd/copy-file
 "fs" 'save-buffer

 "nf" 'narrow-to-defun
 "nn" 'd/narrow-dwim
 "np" 'narrow-to-page
 "nr" 'narrow-to-region)

Editing

Defaults

Text-mode is nicer than fundamental-mode, or so I hear.

(setq-default major-mode 'text-mode)

Fill column default, and use auto-fill for text-mode (and derived modes, such as org-mode, markdown, etc.).

(setq-default fill-column 79)
(add-hook 'text-mode-hook 'auto-fill-mode)

TeX input is really useful for inputing special characters. Setting it as default makes it quickly available with C-\, or toggle-input-method.

This way, when you need to input a greek letter or an em-dash or something, type C-\, use latex input, and see the automagic replacement happen in all its glory.

(setq default-input-method "TeX")

Who uses double spaces between sentences?

(setq sentence-end-double-space nil)

Dear god I hate tabs. Also, four spaces is a good indentation default.

(setq-default indent-tabs-mode nil
              tab-width 4)

Tools

Packages

auto-yasnippet

(use-package auto-yasnippet)

company-mode

Modular in-buffer completion framework for Emacs

Supposedly better than autocomplete.

(use-package company
  :defer 5
  :general
  (:keymaps 'company-active-map
   [tab] (general-predicate-dispatch nil
           (not (eq major-mode 'eshell-mode)) 'company-complete-common-or-cycle)
   "<right>" (general-predicate-dispatch nil
               (eq major-mode 'eshell-mode) 'company-complete-common))
  :init
  (setq company-idle-delay 0.3
        company-minimum-prefix-length 1
        company-selection-wrap-around t
        company-dabbrev-char-regexp "\\sw\\|\\s_\\|[-_]")
  :config
  (defun company-mode/backend-with-yas (backend)
    "Source: https://git.io/vQKE6"
    (if (and (listp backend) (member 'company-yasnippet backend))
        backend
      (append (if (consp backend) backend (list backend))
              '(:with company-yasnippet))))
  (setq company-backends (mapcar #'company-mode/backend-with-yas
                                 company-backends))
  (global-company-mode t))

evil-mc

(use-package evil-mc
  :general
  (nmap
   "gr"  '(:ignore t :wk "mc")
   "grm" 'evil-mc-make-all-cursors
   "gru" 'evil-mc-undo-all-cursors
   "grs" 'evil-mc-pause-cursors
   "grr" 'evil-mc-resume-cursors
   "grf" 'evil-mc-make-and-goto-first-cursor
   "grl" 'evil-mc-make-and-goto-last-cursor
   "grh" 'evil-mc-make-cursor-here
   "grj" 'evil-mc-make-cursor-move-next-line
   "grk" 'evil-mc-make-cursor-move-prev-line
   ;; "M-n" 'evil-mc-skip-and-goto-next-cursor
   "grN" 'evil-mc-make-and-goto-next-cursor
   ;; "M-p" 'evil-mc-skip-and-goto-prev-cursor
   "grP" 'evil-mc-make-and-goto-prev-cursor
   ;; "C-n" 'evil-mc-skip-and-goto-next-match
   "grn" 'evil-mc-make-and-goto-next-match
   ;; "C-p" 'evil-mc-skip-and-goto-prev-match
   "grp" 'evil-mc-make-and-goto-prev-match)
  :init
  (setq evil-mc-key-map nil)
  :config
  (global-evil-mc-mode)
  (push 'evil-smartparens-mode evil-mc-incompatible-minor-modes)
  (push 'fci-mode evil-mc-incompatible-minor-modes))

evil-multiedit

Multiple cursors for evil-mode, based on iedit

(use-package evil-multiedit
  :config
  (evil-ex-define-cmd "ie[dit]" 'evil-multiedit-ex-match))

flyspell

Flyspell spell-checking and ivy integration with d12frosted/flyspell-correct

(use-package flyspell
  :config
  (defun d/flyspell-correct-next ()
    (interactive "p")
    (flyspell-goto-next-error)
    (flyspell-auto-correct-word))

  (defun d/flyspell-add-to-dictionary ()
    "Add word at point to flyspell dictionary at `/Users/d/.ispell_english'.

Source: http://tinyurl.com/k8g9sex"
    (interactive)
    (let ((current-location (point))
          (word (flyspell-get-word)))
      (when (consp word)
        (flyspell-do-correct 'save
                             nil
                             (car word)
                             current-location
                             (cl-caddr word)
                             (cl-caddr word)
                             current-location)))))

(use-package flyspell-correct-ivy
  :after flyspell)

fontawesome

(use-package fontawesome)

hungry-delete

(use-package hungry-delete
  :defer 5
  :config
  (global-hungry-delete-mode))

smartparens

Minor mode for Emacs that deals with parens pairs and tries to be smart about it.

(use-package smartparens
  :defer 5
  :config
  (require 'smartparens-config)
  (smartparens-global-mode)
  (show-smartparens-global-mode)
  (add-hook 'eval-expression-minibuffer-setup-hook #'smartparens-strict-mode)
  (add-hook 'eval-expression-minibuffer-setup-hook #'show-smartparens-mode)
  (let ((modes '(text-mode
                 org-mode
                 markdown-mode
                 minibuffer-inactive-mode
                 html-mode)))
    (sp-local-pair modes "'" nil :actions nil)
    (sp-local-pair modes "`" nil :actions nil))

  (defmacro d/sp-wrap-with (&rest args)
    "Make function(s) for wrapping with character using `sp-wrap-with-pair'"
    `(progn
       ,@(cl-loop for (char-name char) on args
                  by #'cddr collect
                  (let ((fname (intern (concat "d/sp-wrap-with-" char-name))))
                    `(defun ,fname (&optional arg)
                       (interactive "P")
                       (sp-wrap-with-pair ,char))))))

  (d/sp-wrap-with
   "paren" "("
   "curly" "{"
   "double-quote" "\""
   "single-quote" "'"))

(use-package evil-smartparens
  :after smartparens
  :config
  (add-hook 'smartparens-enabled-hook #'evil-smartparens-mode))

undo-tree

Kind of makes undo’s like git. Or Vim, apparently, if you’re into that.

(use-package undo-tree
  :general
  (d/leader-keys "au" 'undo-tree-visualize)
  :config
  (setq undo-tree-visualizer-timestamps t))

unfill

(use-package unfill
  :general
  (d/leader-keys "xq" 'unfill-toggle)
  ([remap fill-paragraph] 'unfill-toggle))

yasnippet

A template system for Emacs

Freakin yasnippet. It’s the best.

(use-package yasnippet
  :defer 5
  :general (d/leader-keys "iy" 'yas-insert-snippet)
  :config
  (yas-global-mode 1))
haskell-snippets
(use-package haskell-snippets)

Functions

Move text

(defun d/transpose-chars (arg)
  "Move character at point forward one character.
With prefix arg ARG, effect is to take character at point
and drag it forward past ARG other characters (backward if ARG negative)."
  (interactive "P")
  (forward-char)
  (if arg
      (transpose-chars arg)
    (transpose-chars 1))
  (backward-char))

(defun d/backward-transpose-chars (arg)
  "Move character at point backward one character.
With prefix arg ARG, effect is to take character at point
and drag it backward past ARG other characters (backward if ARG negative)."
  (interactive "P")
  (forward-char)
  (if arg
      (transpose-chars (- arg))
    (transpose-chars -1))
  (backward-char))

(defun d/backward-transpose-words (arg)
  "Interchange words around point, leaving point at end of them.
With prefix arg ARG, effect is to take word before or around point
and drag it forward past ARG other words (backward if ARG negative).
If ARG is zero, the words around or after point and around or after mark
are interchanged."
  (interactive "P")
  (if arg
      (transpose-words (- arg))
    (transpose-words -1)))

(defun d/move-line-or-region (arg)
  "Move line or region down one one line.
With prefix arg ARG, effect is to take line at point and
drag it down past ARG other lines (up if ARG negative)."
  (interactive "P")
  (if (or (not arg) (>= arg 0))
      (let ((reg-or-lin (if (region-active-p) "'>" "."))
            (reactivate-region (if (region-active-p) "gv=gv" ""))
            (num (if arg arg 1)))
        (execute-kbd-macro
         (concat ":m" reg-or-lin "+" (number-to-string num) (kbd "RET") reactivate-region)))
    (d/backward-move-line-or-region (- arg))))

(defun d/backward-move-line-or-region (arg)
  "Move line or region up one one line.
With prefix arg ARG, effect is to take line at point and
drag it up past ARG other lines (down if ARG negative)."
  (interactive "P")
  (if (or (not arg) (>= arg 0))
      (let ((reg-or-lin (if (region-active-p) "'<" "."))
            (reactivate-region (if (region-active-p) "gv=gv" ""))
            (num (if arg (+ arg 1) 2)))
        (execute-kbd-macro
         (concat ":m" reg-or-lin "-" (number-to-string num) (kbd "RET") reactivate-region)))
    (d/move-line-or-region (- arg))))

Align

(defun d/align-repeat (start end regexp &optional justify-right after)
  "Repeat alignment with respect to the given regular expression.

If JUSTIFY-RIGHT is non nil justify to the right instead of the
left. If AFTER is non-nil, add whitespace to the left instead of
the right.

Source: https://git.io/vQKul"
  (interactive "r\nsAlign regexp: ")
  (let* ((ws-regexp (if (string-empty-p regexp)
                        "\\(\\s-+\\)"
                      "\\(\\s-*\\)"))
         (complete-regexp (if after
                              (concat regexp ws-regexp)
                            (concat ws-regexp regexp)))
         (group (if justify-right -1 1)))
    (message "%S" complete-regexp)
    (align-regexp start end complete-regexp group 1 t)))

(defmacro d/create-align-repeat-x (&rest args)
  "Create an alignment function given name and alignment regexp.

Source: https://git.io/vQKu4"
  `(progn
     ,@(cl-loop
        for (name regexp justify-right default-after) on args
        by #'cddddr collect
        (let ((new-func (intern (concat "d/align-repeat-" name))))
          `(defun ,new-func (start end switch)
             (interactive "r\nP")
             (let ((after (not (eq (if switch t nil) (if ,default-after t nil)))))
               (d/align-repeat start end ,regexp ,justify-right after)))))))

(d/create-align-repeat-x
 "comma" "," nil t
 "semicolon" ";" nil t
 "colon" ":" nil t
 "equal" "=" nil nil
 "math-oper" "[+\\-*/]" nil nil
 "ampersand" "&" nil nil
 "bar" "|" nil nil
 "left-paren" "(" nil nil
 "right-paren" ")" t nil
 "backslash" "\\\\" nil nil
 "single-quote" "'" nil nil)

(defun d/align-repeat-decimal (start end)
  "Align a table of numbers on decimal points and dollar signs (both optional).

Source: https://git.io/vQKu2"
  (interactive "r")
  (require 'align)
  (align-region start end nil
                '((nil (regexp . "\\([\t ]*\\)\\$?\\([\t ]+[0-9]+\\)\\.?")
                       (repeat . t)
                       (group 1 2)
                       (spacing 1 1)
                       (justify nil t)))
                nil))

Justify

(defmacro d/create-justify-x (&rest types)
  "Create justification function(s), one per given TYPE."
  `(progn
     ,@(cl-loop
        for type in types collect
        (let ((func-name (intern (concat "d/justify-" type)))
              (current-type type))
          `(defun ,func-name ()
             (interactive)
             (if (region-active-p)
                 (set-justification (region-beginning)
                                    (region-end)
                                    (intern ,current-type))
               (set-justification (line-beginning-position)
                                  (line-end-position)
                                  (intern ,current-type))))))))

(d/create-justify-x
 "left"
 "right"
 "full"
 "center"
 "none")

Paragraph

(defun d/paragraphize ()
  "Remove newlines from region."
  (interactive)
  (if (region-active-p)
      (flush-lines "^$" (region-beginning) (region-end))
    (message "No region active.")))

Url

(defun d/shorten-url-at-point ()
  "Shorten the url at point using the github url shortener or the TinyURL api.

Source: http://tinyurl.com/l8z7vph"
  (interactive)
  (if (thing-at-point 'url)
      (let* ((long-url (thing-at-point 'url))
             (short-url
              (cond ((save-match-data
                       (string-match "https://\\(github.com\\|gist.github.com\\)" long-url))
                     (let ((info (shell-command-to-string
                                  (format "curl -i \"https://git.io\" -F \"url=%s\""
                                          long-url))))
                       (save-match-data
                         (and (string-match "Location: \\(.*?\\)
" info)
                              (match-string 1 info)))))
                    (t
                     (shell-command-to-string
                      (format "curl -s \"http://tinyurl.com/api-create.php?url=%s\""
                              (url-hexify-string long-url))))))
             (bounds (bounds-of-thing-at-point 'url)))
        (kill-region (car bounds) (cdr bounds))
        (insert short-url))
    (error "No url at point.")))

(defun d/expand-url-at-point ()
  (interactive)
  (if (thing-at-point 'url)
      (let* ((short-url (thing-at-point 'url))
             (long-url (shell-command-to-string (format "curl -Ls -o /dev/null -w '%%{url_effective}' \"%s\""
                                                        short-url)))
             (bounds (bounds-of-thing-at-point 'url)))
        (kill-region (car bounds) (cdr bounds))
        (insert long-url))
    (error "No url at point.")))

Hydras

Multiedit

(d/with-eval-after-load 'hydra
  (defhydra d/multiedit (:hint nil)
    "
multiedit:
_r_estore  _t_oggle/restrict  match _a_ll
_n_ext     match and _N_ext
_p_rev     match and _P_rev
"
    ("a"   evil-multiedit-match-all)
    ("n"   evil-multiedit-next)
    ("p"   evil-multiedit-prev)
    ("r"   evil-multiedit-restore)
    ("t"   evil-multiedit-toggle-or-restrict-region)
    ("N"   evil-multiedit-match-and-next)
    ("P"   evil-multiedit-match-and-prev)
    ("q"   evil-multiedit-abort :exit t)))

Transpose

(d/with-eval-after-load 'hydra
  (defhydra d/transpose ()
    "transpose"
    ("c" d/transpose-chars "char")
    ("C" d/backward-transpose-chars "backward char")
    ("j" d/move-line-or-region "line/region")
    ("k" d/backward-move-line-or-region "backward line/region")
    ("w" transpose-words "word")
    ("W" d/backward-transpose-words "backward word")
    ("s" transpose-sexps "sexp" :exit t)))

Justify

(d/with-eval-after-load 'hydra
  (defhydra d/justify (:exit t)
    "justify"
    ("r" d/justify-right "right")
    ("l" d/justify-left "left")
    ("c" d/justify-center "center")
    ("f" d/justify-full "full")
    ("n" d/justify-none "none")))

Smartparens

(d/with-eval-after-load 'hydra
  (defhydra d/smartparens (:hint nil)
    "
smartparens:
_r_ewrap  _s_lurp             _(_
_u_nwrap  _S_lurp (back)   _[_ wrap _{_
        _b_arf             _'_  _\"_
        _B_arf (back)
"
    ("r"  sp-rewrap-sexp)
    ("u"  sp-unwrap-sexp)
    ("b"  sp-forward-barf-sexp)
    ("B"  sp-backward-barf-sexp)
    ("s"  sp-forward-slurp-sexp)
    ("S"  sp-backward-slurp-sexp)
    ("("  d/sp-wrap-with-paren)
    ("["  d/sp-wrap-with-bracket)
    ("{"  d/sp-wrap-with-curly)
    ("\"" d/sp-wrap-with-quote2)
    ("'"  d/sp-wrap-with-quote)))

Evil-numbers

(d/with-eval-after-load 'hydra
  (defhydra d/numbers ()
    "evil-numbers"
    ("="  evil-numbers/inc-at-pt "inc")
    ("-" evil-numbers/dec-at-pt "dec")))

Spelling

(d/with-eval-after-load 'hydra
  (defhydra d/flyspell (:pre (require 'flyspell))
    "flyspell"
    ("b" flyspell-buffer "buffer")
    ("j" flyspell-goto-next-error "next")
    ("a" d/flyspell-add-to-dictionary "add to dict")
    ("n" flyspell-correct-next-word-generic "correct next generic")
    ("p" flyspell-correct-previous-word-generic "correct prev generic")
    ("N" d/flyspell-correct-next "correct next")
    ("P" flyspell-auto-correct-previous-word "correct pref")))

Modes

cmake-mode

(use-package cmake-mode)

conf-mode

(d/setup-hook conf-mode
  (d/setup-prog-mode))
(add-to-list 'auto-mode-alist '("\\.service\\'" . conf-mode))

csv-mode

Major mode for editing comma/char separated values

Eh, wanted to try a simpler way of editing csv files. (Excel and Numbers both kinda suck at this, LibreOffice was slightly better.) Haven’t used this much.

(use-package csv-mode
  :mode "\\.csv\\'"
  :config
  (add-hook 'csv-mode-hook #'csv-align-fields))

json-mode

Major mode for editing JSON files with emacs

(use-package json-mode
  :mode "\\.json\\'")

(d/setup-hook json-mode
  (highlight-numbers-mode -1))

markdown-mode

Syntax highlighting for markdown files.

(use-package markdown-mode
  :mode "\\.md\\'"
  :config)

text-mode

(d/mode-leader-keys
 :keymap 'text-mode-map
 "f" '(:def d/flyspell/body :wk "flyspell"))

yaml-mode

The emacs major mode for editing files in the YAML data serialization format.

(use-package yaml-mode
  :mode "\\.yml\\'")

Bindings

Make indent-rigidly more vimmy.

(general-def indent-rigidly-map
  "h" 'indent-rigidly-left
  "l" 'indent-rigidly-right
  "H" 'indent-rigidly-left-to-tab-stop
  "L" 'indent-rigidly-right-to-tab-stop)

Leader keys

(d/leader-keys
 "n-"  'd/numbers/evil-numbers/dec-at-pt
 "n="  'd/numbers/evil-numbers/inc-at-pt

 "x"   '(:ignore t :wk "text")
 "xa"  '(:ignore t :wk "align")
 "xa&" 'd/align-repeat-ampersand
 "xa'" 'd/align-repeat-single-quote
 "xa(" 'd/align-repeat-left-paren
 "xa)" 'd/align-repeat-right-paren
 "xa," 'd/align-repeat-comma
 "xa." 'd/align-repeat-decimal
 "xa:" 'd/align-repeat-colon
 "xa;" 'd/align-repeat-semicolon
 "xa=" 'd/align-repeat-equal
 "xaa" 'align
 "xac" 'align-current
 "xam" 'd/align-repeat-math-oper
 "xar" 'align-regexp
 "xar" 'd/align-repeat
 "xa|" 'd/align-repeat-bar

 "xib" 'indent-buffer
 "xii" 'indent-rigidly
 "xir" 'indent-region
 "xj"  '(:def d/justify/body :wk "justify")
 "xls" 'sort-lines
 "xt"  '(:def d/transpose/body :wk "transpose")
 "xc" 'count-words

 "xm"  '(:def d/multiedit/body :wk "multiedit")

 "xp"  '(:def d/smartparens/body :wk "smartparens")

 "xs"  'd/shorten-url-at-point
 "xe"  'd/expand-url-at-point

 "im"  'insert-kbd-macro)

Navigation

Functions

(defun d/toggle-window-split ()
  "Switch between vertical and horizontal window split.

Source: http://tinyurl.com/k7s96fa"
  (interactive)
  (if (= (count-windows) 2)
      (let* ((this-win-buffer (window-buffer))
             (next-win-buffer (window-buffer (next-window)))
             (this-win-edges (window-edges (selected-window)))
             (next-win-edges (window-edges (next-window)))
             (this-win-2nd (not (and (<= (car this-win-edges)
                                         (car next-win-edges))
                                     (<= (cadr this-win-edges)
                                         (cadr next-win-edges)))))
             (splitter
              (if (= (car this-win-edges)
                     (car (window-edges (next-window))))
                  #'split-window-horizontally
                #'split-window-vertically)))
        (delete-other-windows)
        (let ((first-win (selected-window)))
          (funcall splitter)
          (if this-win-2nd (other-window 1))
          (set-window-buffer (selected-window) this-win-buffer)
          (set-window-buffer (next-window) next-win-buffer)
          (select-window first-win)
          (if this-win-2nd (other-window 1))))))

(defun d/split-vert-focus ()
  "Split window vertically and move focus to other window."
  (interactive)
  (split-window-right)
  (other-window 1))

(defun d/split-horz-focus ()
  "Split window horizontally and move focus to other window."
  (interactive)
  (split-window-below)
  (other-window 1))

(defun d/move-splitter-left (arg)
  "Move window splitter left.

Source: https://git.io/vQKuS"
  (interactive "p")
  (if (let ((windmove-wrap-around))
        (windmove-find-other-window 'right))
      (shrink-window-horizontally arg)
    (enlarge-window-horizontally arg)))

(defun d/move-splitter-right (arg)
  "Move window splitter right.

Source: https://git.io/vQKu7"
  (interactive "p")
  (if (let ((windmove-wrap-around))
        (windmove-find-other-window 'right))
      (enlarge-window-horizontally arg)
    (shrink-window-horizontally arg)))

(defun d/move-splitter-up (arg)
  "Move window splitter up.

Source: https://git.io/vQKu5"
  (interactive "p")
  (if (let ((windmove-wrap-around))
        (windmove-find-other-window 'up))
      (enlarge-window arg)
    (shrink-window arg)))

(defun d/move-splitter-down (arg)
  "Move window splitter down.

Source: https://git.io/vQKuF"
  (interactive "p")
  (if (let ((windmove-wrap-around))
        (windmove-find-other-window 'up))
      (shrink-window arg)
    (enlarge-window arg)))

Packages

ace-window

Quickly switch windows in Emacs

(use-package ace-window
  :general
  (d/leader-keys
   "\\" 'ace-window
   "wD" 'ace-delete-window
   "wS" 'ace-swap-window
   "wa" 'ace-window)
  :config
  (setq aw-keys (string-to-list "aoeuidhtns")))

avy

Jump to things in Emacs tree-style

(use-package avy
  :general
  (d/leader-keys
   "jc" 'avy-goto-char-2
   "jl" 'avy-goto-line
   "jw" 'avy-goto-word-1)
  :config
  (setq avy-keys (string-to-list "aoeuidhtns")))

eyebrowse

(use-package eyebrowse
  :commands (eyebrowse-switch-to-window-config
             eyebrowse-next-window-config
             eyebrowse-prev-window-config
             eyebrowse-rename-window-config
             eyebrowse-close-window-connfig
             eyebrowse-close-window-config
             eyebrowse-last-window-config
             eyebrowse-switch-to-window-config-0
             eyebrowse-switch-to-window-config-1
             eyebrowse-switch-to-window-config-2
             eyebrowse-switch-to-window-config-3
             eyebrowse-switch-to-window-config-4
             eyebrowse-switch-to-window-config-5
             eyebrowse-switch-to-window-config-6
             eyebrowse-switch-to-window-config-7
             eyebrowse-switch-to-window-config-8
             eyebrowse-switch-to-window-config-9)
  :general
  ("<f10>" 'eyebrowse-switch-to-window-config-0
   "<f1>" 'eyebrowse-switch-to-window-config-1
   "<f2>" 'eyebrowse-switch-to-window-config-2
   "<f3>" 'eyebrowse-switch-to-window-config-3
   "<f4>" 'eyebrowse-switch-to-window-config-4
   "<f5>" 'eyebrowse-switch-to-window-config-5
   "<f6>" 'eyebrowse-switch-to-window-config-6
   "<f7>" 'eyebrowse-switch-to-window-config-7
   "<f8>" 'eyebrowse-switch-to-window-config-8
   "<f9>" 'eyebrowse-switch-to-window-config-9)
  (d/leader-keys
   "e"  '(:ignore t :wk "eyebrowse")
   "es" 'eyebrowse-switch-to-window-config
   "el" 'eyebrowse-next-window-config
   "eh" 'eyebrowse-prev-window-config
   "er" 'eyebrowse-rename-window-config
   "ec" 'eyebrowse-close-window-config
   "e'" 'eyebrowse-last-window-config
   "e0" 'eyebrowse-switch-to-window-config-0
   "e1" 'eyebrowse-switch-to-window-config-1
   "e2" 'eyebrowse-switch-to-window-config-2
   "e3" 'eyebrowse-switch-to-window-config-3
   "e4" 'eyebrowse-switch-to-window-config-4
   "e5" 'eyebrowse-switch-to-window-config-5
   "e6" 'eyebrowse-switch-to-window-config-6
   "e7" 'eyebrowse-switch-to-window-config-7
   "e8" 'eyebrowse-switch-to-window-config-8
   "e9" 'eyebrowse-switch-to-window-config-9)
  :config
  (setq eyebrowse-wrap-around t
        eyebrowse-new-workspace t
        eyebrowse-switch-back-and-forth t)

  (eyebrowse-mode))

zoom

(use-package zoom
  :config
  (setq zoom-size '(0.618 . 0.618)
        zoom-ignored-buffer-names '(" *which-key*"
                                    "*Calculator*"
                                    "*Calc Trail*")))

dumb-jump

(use-package dumb-jump
  :general
  (d/leader-keys
   "jE" 'dumb-jump-go-prefer-external-other-window
   "jG" 'dumb-jump-go-other-window
   "jb" 'dumb-jump-back
   "je" 'dumb-jump-go-prefer-external
   "jg" 'dumb-jump-go)
  :config
  (setq dumb-jump-selector 'ivy
        dumb-jump-prefer-searcher 'rg))

imenu-anywhere

ido/ivy/helm imenu tag selection across buffers with the same mode/project etc

imenu on steroids.

(use-package imenu-anywhere)

zoom-window

(use-package zoom-window
  :general
  (d/leader-keys
   "wz" 'zoom-window-zoom)
  :config
  (setq zoom-window-mode-line-color "#1D2021"))

Hydras

(d/with-eval-after-load 'hydra
  (defhydra d/splitter ()
    "splitter"
    ("h" d/move-splitter-left "")
    ("j" d/move-splitter-down "")
    ("k" d/move-splitter-up "")
    ("l" d/move-splitter-right "" )
    ("=" balance-windows "balance")))

Bindings

(d/leader-keys
 "jI" 'imenu-anywhere
 "jf" 'find-function-other-window
 "ji" 'imenu
 "jv" 'find-variable-other-window
 "jj" 'find-library-other-window

 "wd" 'delete-window
 "wf" 'make-frame
 "wh" 'd/split-horz-focus
 "wo" 'delete-other-windows
 "ws" 'd/splitter/body
 "wt" 'd/toggle-window-split
 "wv" 'd/split-vert-focus)

Appearance

Defaults

Startup

Get right to your files or the scratch buffer. No B.S.

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

Time display

(d/with-eval-after-load 'time
  (setq  display-time-24hr-format t
         display-time-default-load-average nil
         display-time-format "│ %Y-%d-%m %H:%M │"
         display-time-load-average nil))

Gui elements

Turn off gui elements that I never use. Gui emacs is great, but I still prefer text-based interaction thank you very much.

(setq custom-raised-buttons nil
      use-dialog-box nil)

Buffer display

I don’t want line-wrapping madness, just tell me there’s more to see and I’ll have a look. Additionally, show whitespace.

(setq-default truncate-lines t)
(setq whitespace-style '(face trailing tabs))
(global-whitespace-mode)
(setq whitespace-global-modes '(not erc-mode ses-mode))

Font

Choose a font in order of preference, when available.

(when window-system
  (cond ((x-list-fonts "Iosevka Term")
         (add-to-list 'default-frame-alist '(font . "Iosevka Term-9.5:weight=book"))
         (set-face-attribute 'default t :font "Iosevka Term-9.5:weight=book"))
        ((x-list-fonts "Input")
         (add-to-list 'default-frame-alist '(font . "Input-10"))
         (set-face-attribute 'default t :font "Input-10"))
        ((x-list-fonts "Office Code Pro D")
         (add-to-list 'default-frame-alist '(font . "Office Code Pro D-11"))
         (set-face-attribute 'default t :font "Office Code Pro D-11"))
        ((x-list-fonts "Consolas")
         (add-to-list 'default-frame-alist '(font . "Consolas-11"))
         (set-face-attribute 'default t :font "Consolas-11"))
        ((x-list-fonts "Menlo")
         (add-to-list 'default-frame-alist '(font . "Menlo-11"))
         (set-face-attribute 'default t :font "Menlo-11"))))

And use Font Awesome for the unicode glyphs it supports.

(when (and window-system (x-list-fonts "FontAwesome"))
  (set-fontset-font t 'unicode "FontAwesome" nil 'prepend))
Current: Now I use Iosevka, which is really great since it has all of the wonderful customization abilities of Input, but is fully open source (and has web variants!), sees active development, and the maintainer is incredibly responsive. It is quite narrow, but give it a chance - I wasn’t able to go back after a few weeks.

2016-ish: I’m using the wonderful Input font. If, like me, you’re coming from something more like SauceCodePro, Consolas, or even just Menlo, this font may at first seem a little “blocky,” but it’s so wonderfully customizable that you should really give it a try. My current settings are:

  • Download a custom four-style family
  • Source Code Pro style
    • second option for a and g
  • 1.4x Line spacing
RegularInput Mono Light
ItalicInput Mono Light Italic
BoldInput Mono Medium
Bold ItalicInput Mono Medium Italic

Note: Check out this post for advice on fixing the Consolas font on macOS. This helps with the modeline text vertical alignments and makes it consistent with other fonts. Post last updated in 2011, last successfully tested in 09/2016 (by myself).

Minibuffer

Use a bar cursor in the minibuffer.

(add-hook 'minibuffer-setup-hook
          (lambda () (setq-local cursor-type '(bar . 1))))

Margin

(setq-default left-margin-width 1)
(setq-default right-margin-width 1)

Packages

focus

Dim the font color of text in surrounding paragraphs

(use-package focus)

rainbow-mode

Colorize color names in buffers

(use-package rainbow-mode
  :config
  (setq rainbow-x-colors-major-mode-list '(c-mode c++-mode java-mode)))

darktooth-theme

An Emacs 24 theme remixed from gruvbox

(my fork)

(use-package darktooth-theme
  :demand t
  :recipe (:host github
           :repo "dieggsy/emacs-theme-darktooth"
           :upstream (:host github
                      :repo "emacsfodder/emacs-theme-darktooth")))

column-enforce-mode

Highlight text that extends beyond a certain column.

(use-package column-enforce-mode
  :init
  (setq-default column-enforce-column 79))

color-identifiers

Emacs minor mode to highlight each source code identifier uniquely based on its name

(use-package color-identifiers-mode)

highlight-numbers

Highlight numbers in source code

Neat-o

(use-package highlight-numbers)

highlight-parentheses

Emacs: highlight surrounding parentheses

(use-package highlight-parentheses)

powerline

(use-package powerline)

rainbow-delimiters

Better parentheses coloring

(use-package rainbow-delimiters)

xterm-color

(use-package xterm-color)

Hydras

(d/with-eval-after-load 'hydra
  (defhydra d/zoom ()
    "zoom"
    ("=" text-scale-increase "in")
    ("-" text-scale-decrease "out")
    ("0" (text-scale-adjust 0) "reset")))

Bindings

(d/leader-keys
 "z" '(:def d/zoom/body :wk "zoom"))

Mode line

Helpers

(defun d/flycheck-lighter (state)
  "Return flycheck information for the given error type STATE.

Source: https://git.io/vQKzv"
  (let* ((counts (flycheck-count-errors flycheck-current-errors))
         (errorp (flycheck-has-current-errors-p state))
         (err (or (cdr (assq state counts)) "?"))
         (running (eq 'running flycheck-last-status-change)))
    (if (or errorp running) (format "%s" err))))

(defmacro d/with-window-status (&rest body)
  (declare (indent defun))
  `(let ((window-active? (powerline-selected-window-active)))
     (cl-flet ((propertize-active
                (&rest arguments)
                (let ((str (car arguments)))
                  (cond  ((and window-active? (stringp str))
                          (apply #'propertize arguments) )
                         ((stringp str)
                          (car arguments))))))
       ,@body)))

(defmacro d/with-evil-tag-color (&rest body)
  (declare (indent defun))
  `(let ((evil-tag-color
          (pcase (substring-no-properties
                  evil-mode-line-tag)
            (" N " "#B8BB26")
            (" I " "#66999D")
            (" M " "#D3869B")
            (" V " "#FE8019")
            (" E " "#FABD2F" )
            (" R " "#FE8019")
            (" O " "#B8BB26"))))
     ,@body))

(defun d/make-xpm (color height width)
  "Create an XPM bitmap.

Source: https://git.io/vQKzL"
  (propertize
   " " 'display
   (let ((data (make-list height (make-list width 1)))
         (color (or color "None")))
     (create-image
      (concat
       (format "/* XPM */\nstatic char * percent[] = {\n\"%i %i 2 1\",\n\". c %s\",\n\"  c %s\","
               (length (car data))
               (length data)
               color
               color)
       (apply #'concat
              (cl-loop with idx = 0
                       with len = (length data)
                       for dl in data
                       do (cl-incf idx)
                       collect
                       (concat "\""
                               (cl-loop for d in dl
                                        if (= d 0) collect (string-to-char " ")
                                        else collect (string-to-char "."))
                               (if (eq idx len) "\"};" "\",\n")))))
      'xpm t :ascent 'center))))

(defun d/eyebrowse-relevant? ()
  (and (featurep 'eyebrowse)
       (< 1 (length (eyebrowse--get 'window-configs)))))

(defun d/in-macro? ()
  (or defining-kbd-macro executing-kbd-macro))

(defun d/in-evil-substitution? ()
  (or (assq 'evil-ex-substitute evil-ex-active-highlights-alist)
      (assq 'evil-ex-global-match evil-ex-active-highlights-alist)
      (assq 'evil-ex-buffer-match evil-ex-active-highlights-alist)))

(defun d/evil-substitute-num-matches ()
  "Return the number of matches for the current evil substitution.

Source: https://git.io/vQKzq"
  (let ((range (if evil-ex-range
                   (cons (car evil-ex-range) (cadr evil-ex-range))
                 (cons (line-beginning-position) (line-end-position))))
        (pattern (car-safe (evil-delimited-arguments evil-ex-argument 2))))
    (if pattern
        (format "%s matches" (how-many pattern (car range) (cdr range)))
      " ... ")))

Mode-line

(defvar d/mode-line
  '((:eval
     (d/with-evil-tag-color
       (d/with-window-status
         (cond ((and window-active?
                     (not (or (featurep 'exwm)
                              (d/eyebrowse-relevant?)
                              (bound-and-true-p anzu--state)
                              (d/in-macro?)
                              (d/in-evil-substitution?))))
                (d/make-xpm evil-tag-color 66 8))
               ((not window-active?)
                (d/make-xpm "#1D2021" 66 8))))))
    (:eval
     (d/with-window-status
       (d/with-evil-tag-color
         (when window-active?
           (propertize-active
            (cond ((d/in-evil-substitution?)
                   (d/evil-substitute-num-matches))
                  ((d/in-macro?)
                   (if (bound-and-true-p evil-this-macro)
                       (char-to-string evil-this-macro)
                     "Macro"))
                  ((bound-and-true-p anzu--state)
                   (anzu--update-mode-line))
                  ((featurep 'exwm)
                   (number-to-string (1+ exwm-workspace-current-index)))
                  ((d/eyebrowse-relevant?)
                   (let* ((num (eyebrowse--get 'current-slot))
                          (tag (nth 2 (assoc num (eyebrowse--get 'window-configs)))))
                     (if (and tag (< 0 (length tag)))
                         tag
                       (int-to-string num)))))
            'face
            `(:foreground
              "#3E3D31"
              :weight bold
              :background ,evil-tag-color
              :box (:color ,evil-tag-color :line-width 19)))))))
    ;; File modified
    " %* "
    ;; Buffer name & recursive editing
    "%[" mode-line-buffer-identification "%] "
    ;; Remote
    (:eval (d/with-window-status
             (let ((host (file-remote-p default-directory 'host)))
               (propertize-active
                (cond ((and host
                            default-directory
                            (string= host (system-name)))
                       (concat "@"
                               (file-remote-p default-directory 'user)
                               " "))
                      ((and host default-directory)
                       (concat "@" host " ")))
                'face
                '(:foreground "#D3869B")))))
    ;; Line/column number
    (:eval (d/with-window-status
             (unless (eq major-mode 'exwm-mode)
               (propertize-active "%4l:%2c  "
                                  'face '(:foreground "#A89984")))))
    ;; Major mode
    (:eval
     (d/with-window-status
       (propertize-active
        (concat (format-mode-line mode-name) "  ")
        'face '(:foreground "#83A598" :weight bold))))
    ;; Version control
    (:eval
     (d/with-window-status
       (when vc-mode
         (propertize-active
          (concat (replace-regexp-in-string "^ Git." "" vc-mode) "  ")
          'face '(:foreground "#FE8019" )))))
    ;; Flycheck
    (:eval
     (d/with-window-status
       (when (and (bound-and-true-p flycheck-mode)
                  (or flycheck-current-errors
                      (eq 'running flycheck-last-status-change)))
         (concat
          (cl-loop for state in '((error . "#FB4933")
                                  (warning . "#FABD2F")
                                  (info . "#83A598"))
                   as lighter = (d/flycheck-lighter (car state))
                   when lighter
                   concat (propertize-active
                           lighter
                           'face `(:foreground ,(cdr state))))
          " "))))
    ;; Input method
    (:eval
     (d/with-window-status
       (when (or current-input-method
                 (and (bound-and-true-p evil-mode)
                      (bound-and-true-p evil-input-method)))
         (cond
          (current-input-method
           (propertize-active
            (concat current-input-method-title " ")
            'face
            'bold))
          ((and (featurep 'evil) (bound-and-true-p evil-input-method))
           (concat (nth 3 (assoc default-input-method input-method-alist))
                   " "))))))
    erc-modified-channels-object))

(setq-default mode-line-format d/mode-line)
(with-current-buffer "*Messages*"
  (setq-local mode-line-format d/mode-line))

EXWM mode-line

(d/with-eval-after-load 'exwm
  (display-time-mode)

  (defvar d/ssid-keymap
    (let ((map (make-sparse-keymap)))
      (define-key map [mode-line mouse-1]
        (lambda ()
          (interactive)
          (call-process-shell-command "networkmanager_dmenu")))
      map))

  (defvar d/kb-layout-keymap
    (let ((map (make-sparse-keymap)))
      (define-key map [mode-line mouse-1]
        (lambda ()
          (interactive)
          (pcase d/kb-layout
            ("DV"
             (setq d/kb-layout "QW")
             (call-process-shell-command "setxkbmap us")
             (force-mode-line-update 'all))
            ("QW"
             (setq d/kb-layout "DV")
             (call-process-shell-command "setxkbmap dvorak")
             (force-mode-line-update 'all)))))
      map))

  (defvar d/mode-line-exwm
    '("%e"
      (:eval
       (let* ((lhs d/mode-line)
              (l-width (length (format-mode-line lhs)))
              (rhs `((:eval
                      (d/with-window-status
                        (concat
                         (when d/mute
                           (propertize-active "M  " 'face '(:foreground "#FB4933")))
                         (propertize-active (concat d/kb-layout "  ")
                                            'face '(:foreground "#D3869B")
                                            'local-map d/kb-layout-keymap)
                         (propertize-active (concat d/ssid "  ")
                                            'face '(:foreground "#B8bb26")
                                            'local-map d/ssid-keymap)

                         (propertize-active (concat d/battery "  ")
                                            'face '(:foreground "#83a598"))

                         (propertize-active (concat display-time-string "  ")
                                            'face '(:foreground "#A89984")))))))
              (r-width (length (format-mode-line rhs)))
              (t-width (+ l-width r-width))
              (pad (if (> (window-width) (+ l-width r-width))
                       (make-string
                        (- (window-width)
                           (+ l-width r-width)
                           (if eldoc-mode-line-string
                               (+ (length eldoc-mode-line-string) 2)
                             0)
                           (if (eq major-mode 'exwm-mode)
                               -1
                             0))
                        ?\ )
                     "")))
         (format-mode-line
          (append lhs `(,pad) rhs))))))
  (setq-default mode-line-format d/mode-line-exwm)
  (with-current-buffer "*Messages*"
    (setq-local mode-line-format d/mode-line-exwm)))

Dev

Prog

Packages

aggressive-indent

Emacs minor mode that keeps your code always indented. More reliable than electric-indent-mode.

(use-package aggressive-indent)

flycheck

On the fly syntax checking for GNU Emacs

(use-package flycheck
  :config
  (setq flycheck-completing-read-function 'ivy-completing-read)
  (define-fringe-bitmap 'flycheck-fringe-bitmap-double-arrow
    (vector #b00000000
            #b10000000
            #b11000000
            #b11100000
            #b11110000
            #b11111000
            #b11111100
            #b11111110
            #b11111111
            #b11111110
            #b11111100
            #b11111000
            #b11110000
            #b11100000
            #b11000000
            #b10000000
            #b00000000)))

Hydras

(d/with-eval-after-load 'hydra
  (defhydra d/flycheck
    (:pre (progn
            (flycheck-mode 1)
            (setq hydra-lv t)
            (flycheck-list-errors))
     :post (progn
             (setq hydra-lv nil)
             (quit-windows-on "*Flycheck errors*"))
     :hint nil)
    "Errors"
    ("f"  flycheck-error-list-set-filter                            "Filter")
    ("j"  flycheck-next-error                                       "Next")
    ("k"  flycheck-previous-error                                   "Previous")
    ("gg" flycheck-first-error                                      "First")
    ("G"  (progn (goto-char (point-max)) (flycheck-previous-error)) "Last")
    ("q"  nil)))

Bindings

(d/mode-leader-keys
 :keymaps 'prog-mode-map
 "f" '(:def d/flycheck/body :wk "flycheck"))

Setup

(d/setup-hook prog-mode
  (subword-mode)
  (smartparens-strict-mode)
  (highlight-parentheses-mode 1)
  (rainbow-delimiters-mode)
  (goto-address-prog-mode)
  (display-line-numbers-mode)
  (column-enforce-mode)
  (highlight-numbers-mode)
  (auto-fill-mode 1)
  (setq-local comment-auto-fill-only-comments t)
  (setq-local calc-embedded-open-mode (concat comment-start " "))
  (setq-local calc-embedded-close-mode (concat comment-end "\n"))
  (hl-line-mode 1))

Lang

Python

Packages

anaconda-mode

Code navigation, documentation lookup and completion for Python.

(use-package anaconda-mode)
company-anaconda

Anaconda backend for company-mode.

(use-package company-anaconda
  :after company)
EIN

Jupyter and IPython 2.x/3.x notebook client in Emacs

(use-package ein)
pyenv-mode

Integrate pyenv with python-mode.

(use-package pyenv-mode
  :config
  (pyenv-mode))
pyenv-mode-auto
(use-package pyenv-mode-auto
  :after pyenv-mode)
sphinx-doc

Generate Sphinx friendly docstrings for Python functions in Emacs

Pretty neat, though not entirely complete, IMO.

(use-package sphinx-doc)
yapfify

Yapf for Emacs

(use-package yapfify)

Variables

(defvar d/python-prettify-alist
  '(("<=" . "")
    (">=" . "")
    ("!=" . "")
    ("is" . "")
    ("=" . "")
    ("==" . "")
    ("not" . "¬")
    ("is not" . "")
    ("sum" . "")
    ("sqrt" . "")
    ("pi" . "π")
    ("lambda" . "λ")
    ("and" . "")
    ("or" . "")
    ("self" . "")
    ("in" . "")
    ("def" . "ƒ")
    ("not in" . "")
    ("return" . "")
    ("yield" . "")
    ("None" . "")
    ("*" . "·")
    ("inf" . "")
    ("all" . "")
    ("^" . "")
    ("**" . "^")))

Functions

(defun d/ipython-shell ()
  "Open an ipython shell using multi-term, respecting virtualenv."
  (interactive)
  (let ((sane-term-shell-command "ipython"))
    (sane-term-create)))

Setup

(d/setup-hook python-mode
  (set (make-local-variable 'comment-inline-offset) 2)
  (flycheck-mode)
  (anaconda-mode)
  (anaconda-eldoc-mode)
  (require 'company)
  (add-to-list 'company-backends
               (company-mode/backend-with-yas 'company-anaconda))
  (setq-local prettify-symbols-alist
              d/python-prettify-alist))

Lisps

Packages

slime

The Superior Lisp Interaction Mode for Emacs

(use-package slime
  :general
  (imap slime-repl-mode-map
    [up] 'slime-repl-previous-input
    [down] 'slime-repl-next-input)
  :config
  (setq inferior-lisp-program "/usr/local/bin/sbcl")
  (setq slime-contribs '(slime-fancy slime-company)))

(use-package slime-company
  :after slime)
request

Easy HTTP request for Emacs Lisp

(use-package request)

(use-package request-deferred)
s

The long lost Emacs string manipulation library.

(use-package s)
dash

A modern list library for Emacs

(use-package dash)
f
(use-package f)
flycheck-package
(use-package flycheck-package
  :after flycheck
  :config
  (flycheck-package-setup))
suggest
(use-package suggest)

Variables

(defvar d/lisp-prettify-alist
  (prettify-utils-generate
   ("<=" "")
   (">=" "")
   ("/=" "")
   ("eq" "")
   ("equal" "")
   ("sqrt" "")
   ("float-pi" "π")
   ("lambda" "λ")
   ("and" "")
   ("or" "")
   ("defun" "ƒ")
   ("defmacro" "ƒₘ")
   ("not" "¬")
   ("nil" "")))

Functions

(defun d/eval-surrounding-sexp (levels)
  "Eval sexp around point, specifying depth with LEVELS.

Source: http://tinyurl.com/le6wxuo"
  (interactive "p")
  (save-excursion
    (sp-end-of-sexp (abs levels))
    (eval-last-sexp nil)))

(defun d/pp-eval-surrounding-sexp (levels)
  "Replace the preceding sexp with its value.

Source: http://tinyurl.com/mh5ev6x"
  (interactive "p")
  (let (pp-escape-newlines)
    (save-excursion
      (sp-end-of-sexp (abs levels))
      (pp-eval-last-sexp nil))))

(defun d/pp-macroexpand-surrounding-sexp (levels)
  (interactive "p")
  (let (pp-escape-newlines)
    (save-excursion
      (sp-end-of-sexp (abs levels))
      (pp-macroexpand-last-sexp nil))))

(defun d/eval-buffer ()
  (interactive)
  (if (eq major-mode 'org-mode)
      (org-babel-execute-src-block)
    (eval-buffer)))

(defun d/eval-defun ()
  (interactive)
  (if  (eq major-mode 'org-mode)
      (progn
        (when (org-in-src-block-p)
          (org-edit-special)
          (eval-defun nil)
          (org-edit-src-exit)))
    (eval-defun)))

(defmacro d/let (&rest clause)
  (declare (indent defun))
  (pcase clause
    ((and `(,name ,vars . ,body) (guard (symbolp name)))
     (let ((args (cl-loop for i in vars if (listp i) collect (car i) else collect i))
           (init-vals (cl-loop for i in vars if (listp i) append (cdr i) else collect nil)))
       `(cl-labels ((,name ,args ,@body))
          (,name ,@init-vals))))
    (_ `(let ,@clause))))

(defun d/lisp-indent-function (indent-point state)
  "This function is the normal value of the variable `lisp-indent-function'.
The function `calculate-lisp-indent' calls this to determine
if the arguments of a Lisp function call should be indented specially.
INDENT-POINT is the position at which the line being indented begins.
Point is located at the point to indent under (for default indentation);
STATE is the `parse-partial-sexp' state for that position.
If the current line is in a call to a Lisp function that has a non-nil
property `lisp-indent-function' (or the deprecated `lisp-indent-hook'),
it specifies how to indent.  The property value can be:
* `defun', meaning indent `defun'-style
  \(this is also the case if there is no property and the function
  has a name that begins with \"def\", and three or more arguments);
* an integer N, meaning indent the first N arguments specially
  (like ordinary function arguments), and then indent any further
  arguments like a body;
* a function to call that returns the indentation (or nil).
  `lisp-indent-function' calls this function with the same two arguments
  that it itself received.
This function returns either the indentation to use, or nil if the
Lisp function does not specify a special indentation.

Source: https://git.io/vQKz8"
  (let ((normal-indent (current-column))
        (orig-point (point)))
    (goto-char (1+ (elt state 1)))
    (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t)
    (cond
     ;; car of form doesn't seem to be a symbol, or is a keyword
     ((and (elt state 2)
           (or (not (looking-at "\\sw\\|\\s_"))
               (looking-at ":")))
      (if (not (> (save-excursion (forward-line 1) (point))
                  calculate-lisp-indent-last-sexp))
          (progn (goto-char calculate-lisp-indent-last-sexp)
                 (beginning-of-line)
                 (parse-partial-sexp (point)
                                     calculate-lisp-indent-last-sexp 0 t)))
      ;; Indent under the list or under the first sexp on the same
      ;; line as calculate-lisp-indent-last-sexp.  Note that first
      ;; thing on that line has to be complete sexp since we are
      ;; inside the innermost containing sexp.
      (backward-prefix-chars)
      (current-column))
     ((and (save-excursion
             (goto-char indent-point)
             (skip-syntax-forward " ")
             (not (looking-at ":")))
           (save-excursion
             (goto-char orig-point)
             (looking-at ":")))
      (save-excursion
        (goto-char (+ 2 (elt state 1)))
        (current-column)))
     (t
      (let ((function (buffer-substring (point)
                                        (progn (forward-sexp 1) (point))))
            method)
        (setq method (or (function-get (intern-soft function)
                                       'lisp-indent-function)
                         (get (intern-soft function) 'lisp-indent-hook)))
        (cond ((or (eq method 'defun)
                   (and (null method)
                        (> (length function) 3)
                        (string-match "\\`def" function)))
               (lisp-indent-defform state indent-point))
              ((integerp method)
               (lisp-indent-specform method state
                                     indent-point normal-indent))
              (method
               (funcall method indent-point state))))))))

Bindings

(d/mode-leader-keys
 :keymaps 'emacs-lisp-mode-map
 "eb" 'd/eval-buffer
 "ef" 'eval-defun
 "er" 'eval-region
 "eR" 'd/eval-and-replace
 "es" 'd/eval-surrounding-sexp
 "el" 'eval-last-sexp
 "pm" 'd/pp-macroexpand-surrounding-sexp
 "ps" 'd/pp-eval-surrounding-sexp)

Setup

(d/setup-hook (lisp-mode emacs-lisp-mode)
  (hs-minor-mode)
  (aggressive-indent-mode)
  (push '(? . ("`" . "'")) evil-surround-pairs-alist)
  (setq-local lisp-indent-function #'d/lisp-indent-function)
  (setq-local prettify-symbols-alist d/lisp-prettify-alist))

C

Setup

(d/setup-hook c-mode
  (aggressive-indent-mode))

Haskell

Packages

haskell-mode
(use-package haskell-mode
  :mode "\\.hs\\'"
  :config
  (setq haskell-indentation-layout-offset 4
        haskell-indentation-left-offset 4
        haskell-indentation-ifte-offset 4))

Variables

(defvar d/haskell-prettify-alist
  '(("=>" . "")
    ("forall" . "")
    ("->" . "")
    ("<-" . "")
    ("not" . "¬")
    ("lambda" . "λ")))

Setup

(d/setup-hook haskell-mode
  (setq-local prettify-symbols-alist d/haskell-prettify-alist))

Scheme

Packages

geiser
(use-package geiser
  :config
  (setq geiser-active-implementations '(chicken)
        geiser-debug-jump-to-debug-p nil
        geiser-debug-show-debug-p nil))

Variables

(defvar d/scheme-prettify-alist
  (prettify-utils-generate
   ("<=" "")
   (">=" "")
   ("/=" "")
   ("eqv?" "")
   ("equal?" "")
   ("sqrt" "")
   ("float-pi" "π")
   ("lambda" "λ")
   ("and" "")
   ("or" "")
   ("define" "ƒ")
   ("define-syntax" "ƒₛ")
   ("not" "¬")
   ("->" "")))

Functions

(defun d/geiser-eval-surrounding-sexp (levels)
  "Eval sexp around point, specifying depth with LEVELS.

Source: http://tinyurl.com/le6wxuo"
  (interactive "p")
  (save-excursion
    (sp-end-of-sexp (abs levels))
    (geiser-eval-last-sexp nil)))

Bindings

(d/mode-leader-keys
 :keymaps 'scheme-mode-map
 "eb" 'geiser-eval-buffer
 "ef" 'geiser-eval-definition
 "er" 'geiser-eval-region
 "el" 'geiser-eval-last-sexp
 "es" 'd/geiser-eval-surrounding-sexp)

Setup

(d/setup-hook scheme-mode
  (hs-minor-mode)
  (aggressive-indent-mode)
  (push '(? . ("`" . "'")) evil-surround-pairs-alist)
  (setq-local prettify-symbols-alist d/scheme-prettify-alist))

(font-lock-add-keywords 'scheme-mode '(("\\<\\sw+:\\>" . 'font-lock-builtin-face)) t)
(font-lock-add-keywords 'geiser-repl-mode '(("\\<\\sw+:\\>" . 'font-lock-builtin-face)) t)

nix-mode

(use-package nix-mode
  :commands (d/nix-update-fetchgit d/nix-update-my-packages)
  :config
  (require 'hilit-chg)

  (defmacro d/nix-update-helper (search get-url)
    `(save-excursion
       (beginning-of-buffer)
       (d/let lop ((bound
                    (re-search-forward
                     (rx "=" (opt " ") ,search (opt " ") "{"
                         (group (minimal-match (0+ anything))) "}")
                     nil
                     'noerror)))
         (if (not bound)
             '()
           (cons
            (progn
              (goto-char (match-beginning 1))
              (list
               (save-excursion
                 ,get-url)
               (save-excursion
                 (and
                  (re-search-forward "\"?rev\"?[ ]*?=[ ]*?\"\\(.*\\)\""
                                     bound)
                  (list (match-beginning 1)
                        (match-end 1))))
               (save-excursion
                 (and
                  (re-search-forward "\"?sha256\"?[ ]*?=[ ]*?\"\\(.*\\)\""
                                     bound)
                  (list (match-beginning 1)
                        (match-end 1))))))
            (lop (re-search-forward
                  (rx "=" (opt " ") ,search (opt " ") "{"
                      (group (minimal-match (0+ anything))) "}")
                  nil
                  'noerror)))))))

  (cl-defun d/nix-update-fetchgit (&optional (file (buffer-file-name)))
    (interactive)
    (let ((visited-p (get-file-buffer file))
          (file-buffer (find-file-noselect file)))
      (with-current-buffer file-buffer
        (let* ((git-urls
                (d/nix-update-helper
                 "fetchgit"
                 (and
                  (re-search-forward "\"?url\"?[ ]*?=[ ]*?\"\\(.*\\)\"" bound)
                  (match-string-no-properties 1))))
               (github-urls
                (d/nix-update-helper
                 "fetchFromGitHub"
                 (concat
                  "https://github.com/"
                  (and
                   (save-excursion
                     (re-search-forward "\"?owner\"?[ ]*?=[ ]*?\"\\(.*\\)\"" bound)
                     (match-string-no-properties 1)))
                  "/"
                  (and
                   (save-excursion
                     (re-search-forward "\"?repo\"?[ ]*?=[ ]*?\"\\(.*\\)\"" bound)
                     (match-string-no-properties 1))))))
               (all-urls (append git-urls github-urls)))
          (dolist (repo all-urls)
            (let ((url (car repo))
                  (rev-bounds (cadr repo))
                  (sha-bounds (caddr repo)))
              (make-process
               :name (format "nix-prefetch-git: %s" url)
               :command `("nix-prefetch-git" ,url "--fetch-submodules")
               :stderr (get-buffer-create "*nix-prefetch-stderr*")
               :filter
               `(lambda (process output)
                  (let* ((json-vals (json-read-from-string output))
                         (rev (alist-get 'rev json-vals))
                         (sha (alist-get 'sha256 json-vals)))
                    (let ((visited-p (get-file-buffer ,file))
                          (file-buffer (find-file-noselect ,file)))
                      (with-current-buffer file-buffer
                        (unless highlight-changes-mode
                          (highlight-changes-mode))
                        (save-excursion
                          (unless (string=
                                   (apply #'buffer-substring-no-properties
                                          ',rev-bounds)
                                   rev)
                            (apply #'delete-region ',rev-bounds)
                            (goto-char (car ',rev-bounds))
                            (insert rev))
                          (unless (string=
                                   (apply #'buffer-substring-no-properties
                                          ',sha-bounds)
                                   sha)
                            (apply #'delete-region ',sha-bounds)
                            (goto-char (car ',sha-bounds))
                            (insert sha)))
                        (save-buffer))
                      (unless visited-p
                        (kill-buffer file-buffer)))))))))
        (unless visited-p
          (kill-buffer file-buffer)))))

  (defun d/nix-update-my-packages ()
    (interactive)
    (let ((nix-files
           (split-string
            (shell-command-to-string
             "grep -lrP 'fetchgit|fetchFromGitHub' ~/dotfiles/nix-local")
            "\n"
            'omit-nulls)))
      (dolist (file nix-files)
        (d/nix-update-fetchgit file)))))

vimrc-mode

Enables syntax highlighting for .vimrc/_vimrc files

(use-package vimrc-mode)

web-mode

(use-package web-mode
  :mode ("\\.html?\\'" "\\.xml\\'" "\\.launch\\'")
  :config
  (setq web-mode-markup-indent-offset 2))

VCS

Functions

(defun d/magit-blame-toggle ()
  "Toggle magit-blame-mode on and off interactively.

Source: https://git.io/vQKub"
  (interactive)
  (if (bound-and-true-p magit-blame-mode)
      (magit-blame-quit)
    (call-interactively 'magit-blame)))

(defun gitlab-snippet (&optional public)
  (interactive "P")
  (require 'request)
  (let ((title (read-from-minibuffer "Snippet title: "))
        (file-name (buffer-name))
        (content (if (region-active-p)
                     (buffer-substring (region-beginning) (region-end))
                   (buffer-string)))
        (visibility (if public "public" "private"))
        (token (password-store-get "tokens/gitlab/master")))
    (request
     "https://gitlab.com/api/v4/snippets"
     :type "POST"
     :data `(("title" . ,title)
             ("file_name" . ,file-name)
             ("content" . ,content)
             ("visibility" . ,visibility))
     :headers `(("PRIVATE-TOKEN" . ,token)))))

Packages

magit

It’s Magit! A Git Porcelain inside Emacs.

Like git, for emacs. But cooler.

(use-package magit
  :general
  (d/leader-keys
   "g"  '(:ignore t :wk "magit")
   "gB" 'd/magit-blame-toggle
   "gC" 'magit-clone
   "gL" 'magit-log-buffer-file
   "ga" 'magit-submodule-add
   "gb" 'magit-branch
   "gc" 'magit-checkout
   "gf" 'magit-find-file
   "gl" 'magit-log-all
   "gs" 'magit-status)
  :config
  (setq magit-diff-refine-hunk t
        auto-revert-check-vc-info t
        git-commit-summary-max-length 50
        git-commit-major-mode 'org-mode)
  (d/setup-hook git-commit-mode
    (evil-insert-state)
    (setq-local fill-column 72)
    (setq-local org-hide-emphasis-markers nil)
    (setq-local org-pretty-entities nil)))

git-gutter-fringe

Fringe version of git-gutter.el

(use-package git-gutter-fringe
  :demand t
  :commands fringe-helper-define
  :config
  (global-git-gutter-mode)
  (add-hook 'focus-in-hook #'git-gutter:update-all-windows)
  (fringe-helper-define 'git-gutter-fr:modified nil
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX")
  (fringe-helper-define 'git-gutter-fr:added nil
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX")
  (fringe-helper-define 'git-gutter-fr:deleted nil
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"
    "....XXXX"))

gist

(use-package gist
  :config
  (let ((gh-vals (cdar gh-profile-alist)))
    (setf gh-vals (plist-put gh-vals :username "dieggsy")
          gh-vals (plist-put gh-vals :token (password-store-get "tokens/github/gist")))))

Tools

Calc

(use-package calc
  :ensure nil
  :general
  (d/leader-keys
   "ac" 'calc-dispatch)
  (emap calc-mode-map
    "x" (lambda () (interactive) (counsel-M-x "^calc-")))
  :config
  (setq calc-multiplication-has-precedence nil)
  (setq calc-symbolic-mode t))

Eshell

Built-in

(use-package eshell
  :ensure nil
  :init
  (d/setup-hook eshell-mode
    (require 'company)
    (imap :keymaps 'eshell-mode-map
      [remap eshell-pcomplete] 'completion-at-point
      "C-p" '(general-predicate-dispatch nil
               (eshell-point-within-input-p) 'eshell/ivy-ps)
      "C-r" 'eshell/ivy-history
      "C-c C-l" 'eshell-clear-keystroke)
    (nmap :keymaps 'eshell-mode-map
      "G" 'end-of-buffer)
    (fish-completion-eshell-toggle)
    (setq-local company-frontends '(company-preview-frontend))
  (d/setup-hook eshell-directory-change
    (if (file-remote-p default-directory)
        (setq-local company-idle-delay nil)
      (setq-local company-idle-delay 0.3)))
    (setq-local company-backends '(company-eshell-autosuggest)))
  :config
  (setq eshell-buffer-maximum-lines 20000
        eshell-history-size 1000
        eshell-prefer-lisp-functions t
        eshell-scroll-to-bottom-on-input t
        eshell-hist-ignoredups t))

External Packages

egp

(use-package egp
  :ensure nil
  :after eshell
  :config
  (setq eshell-highlight-prompt nil
        eshell-prompt-function 'egp-theme))

eshell-z

cd to frequent directory in eshell, an Emacs port of https://github.com/rupa/z

(use-package eshell-z
  :after eshell)

emacs-fish-completion

(use-package emacs-fish-completion
  :after eshell
  :recipe (:host github
           :repo "ambrevar/emacs-fish-completion")
  :config
  (fish-completion-eshell-global-toggle))

pcmpl-git

(use-package pcmpl-git
  :after eshell)

pcomplete-extension

(use-package pcomplete-extension
  :after eshell)

Functions

(d/with-eval-after-load 'eshell
  (defun d/eshell-run (str args)
    (eshell-do-eval
     (eshell-parse-command
      (string-join (append `(,str) args) " "))
     t))

  (cl-defmacro d/eshell-defun (name keys &rest pats)
    (declare (indent defun))
    (let* ((path (plist-get keys :path))
           (opts (plist-get keys :opts))
           (command-name (or path
                             (concat "/usr/bin/" (symbol-name name)))))
      `(defun ,(intern (concat "eshell/" (symbol-name name))) (&rest args)
         (pcase args
           ,@(cl-loop for pattern in pats
                      if (listp (car pattern))
                      collect pattern
                      else collect `('(,(car pattern))
                                     ,(cadr pattern)))
           (_ (d/eshell-run ,(concat command-name " "  opts)
                            args))))))

  (defun eshell/clear ()
    "Custom `eshell' clear function to clear to top."
    (interactive)
    (let ((inhibit-read-only t))
      (erase-buffer)
      (delete-all-overlays)))

  (defun eshell-clear-keystroke ()
    "Allow for keystrokes to invoke eshell/clear."
    (interactive)
    (eshell/clear)
    (eshell-emit-prompt))

  (defun eshell/cpg (&optional x)
    (let* ((ghq-dirs
            (split-string (shell-command-to-string "ghq list")
                          "\n"
                          t))
           (current (string-remove-prefix "/home/diego/.ghq/" (eshell/pwd)))
           (current-pos (cl-position current ghq-dirs :test 'string=)))
      (cond ((string= x "next")
             (eshell/cd (concat "~/.ghq/" (nth (1+ current-pos) ghq-dirs))))
            ((string= x "prev")
             (eshell/cd (concat "~/.ghq/" (nth (1- current-pos) ghq-dirs))))
            (t
             (ivy-read "[cpg] "
                       ghq-dirs
                       :action (lambda (x)
                                 (eshell/cd (concat "~/.ghq/" x))))))))

  (defun eshell/cpwd ()
    (kill-new (eshell/pwd)))

  (defun eshell/csi ()
    (call-interactively #'geiser))

  (defun eshell/sbcl ()
    (call-interactively #'slime))

  (d/eshell-defun git
    (:path "~/.nix-profile/bin/hub"
     :opts "-c color.ui=always")
    ("status" (magit-status-internal (eshell/pwd)))
    ("diff" (magit-status-internal (eshell/pwd)))
    ("log" (call-interactively #'magit-log-current))
    (`("log" . ,ll) (magit-log ll)))

  (d/eshell-defun pass (:path "~/.nix-profile/bin/pass")
    ("rm" (call-interactively #'password-store-remove))
    ("generate" (call-interactively #'password-store-generate))
    ("insert" (call-interactively #'password-store-insert))
    ("mv" (call-interactively #'password-store-rename))
    ("edit" (call-interactively #'password-store-edit))
    ("cp" (call-interactively #'password-store-copy))
    ("-c" (call-interactively #'password-store-copy))
    (`("edit" . ,ll) (password-store-edit ll)))

  (defun eshell/rg (&rest args)
    (eshell-grep "rg" (append '("--no-heading" "-M" "120") args) t))

  (defun eshell/ivy-ps ()
    (interactive)
    (let ((ps  (split-string
                (shell-command-to-string
                 "ps axco user,pid,%cpu,%mem,start,time,command")
                "\n"
                t)))
      (ivy-read "[ps] "
                ps
                :action (lambda (x)
                          (insert (cadr (split-string x " " t)))))))

  (defun eshell/ivy-history ()
    (interactive)
    (let ((history
           (cl-remove-duplicates
            (mapcar (lambda (str)
                      (string-trim (substring-no-properties str)))
                    (cl-remove-if #'not (ring-elements eshell-history-ring)))
            :from-end t
            :test #'string=))
          (input (let* ((beg (save-excursion (eshell-bol)))
                        (end (save-excursion (end-of-line) (point))))
                   (buffer-substring-no-properties beg end))))
      (ivy-read "[history] "
                history
                :action (lambda (x)
                          (end-of-line)
                          (eshell-kill-input)
                          (insert x))
                :initial-input input))))

History autosuggest

(d/with-eval-after-load 'eshell
  (defun company-eshell-autosuggest-candidates (prefix)
    (let* ((history
            (cl-remove-duplicates
             (mapcar (lambda (str)
                       (string-trim (substring-no-properties str)))
                     (cl-remove-if #'not (ring-elements eshell-history-ring)))
             :from-end t
             :test #'string=))
           (most-similar (cl-find-if
                          (lambda (str)
                            (string-prefix-p prefix str))
                          history)))
      (when most-similar
        `(,most-similar))))

  (defun company-eshell-autosuggest--prefix ()
    (let ((prefix
           (string-trim-left
            (buffer-substring-no-properties
             (save-excursion
               (eshell-bol))
             (save-excursion (end-of-line) (point))))))
      (if (not (string-empty-p prefix))
          prefix
        'stop)))

  (defun company-eshell-autosuggest (command &optional arg &rest ignored)
    (interactive (list 'interactive))
    (cl-case command
      (interactive (company-begin-backend 'company-eshell))
      (prefix (and (eq major-mode 'eshell-mode)
                   (company-eshell-autosuggest--prefix)))
      (candidates (company-eshell-autosuggest-candidates arg)))))

Term

(use-package term
  :ensure nil
  :config
  (load-file (locate-user-emacs-file "lisp/term-256color-override.el")))

pdf-tools

Emacs support library for PDF files.

;; (use-package pdf-tools
;;   ;; :defer-install t
;;   :defer 10
;;   :general
;;   (nmap pdf-view-mode-map
;;     "s" 'pdf-view-fit-width-to-window
;;     "a" 'pdf-view-fit-height-to-window
;;     "/" 'isearch-forward
;;     "J" 'pdf-view-next-page
;;     "K" 'pdf-view-previous-page
;;     "j" 'pdf-view-next-line-or-next-page
;;     "k" 'pdf-view-previou-line-or-previous-page
;;     "-" 'pdf-view-shrink
;;     "+" 'pdf-view-enlarg)
;;   :config
;;   (pdf-tools-install))

nov.el

(use-package nov
  :mode "\\.epub\\'")

turing-machine

(use-package turing-machine)

sane-term

Cycle through terms in emacs

I wanted a slightly better terminal in emacs. This seems to do the trick.

(use-package sane-term
  :after term
  :general (d/leader-keys "at" 'sane-term-create))

Enhancements

Dired

Packages

dired-avfs

(use-package dired-avfs
  :after dired)

dired-git

(use-package dired-git
  :ensure nil
  :after dired)

dired-open

(use-package dired-open
  :after dired)

dired-subtree

(use-package dired-subtree
  :after dired
  :disabled
  :general
  (nmap dired-mode-map
    "TAB" 'dired-subtree-toggle))

dired-collpase

(use-package dired-collapse
  :after dired)

diredfl

(use-package diredfl
  :after dired
  :init
  (setq diredfl-ignore-compressed-flag nil))

dired-rainbow

(use-package dired-rainbow
  :after dired
  :config
  (dired-rainbow-define-chmod executable-unix "#B8BB26" "-[rw-]+x.*"))

Emacs

Packages

centered-cursor-mode

Cursor stays vertically centered.

I use this for reading, mostly.

(use-package centered-cursor-mode)

crux

A Collection of Ridiculously Useful eXtensions for Emacs

(use-package crux
  :defer 5
  :general
  (d/leader-keys
   "TAB" 'crux-switch-to-previous-buffer
   "fd" 'crux-delete-file-and-buffer
   "fr" 'crux-rename-file-and-buffer
   "bs" 'crux-sudo-edit)
  :commands (crux-with-region-or-line
             crux-with-region-or-buffer
             crux-switch-to-previous-buffer
             crux-rename-file-and-buffer)
  :config
  (crux-with-region-or-line eval-region)
  (crux-with-region-or-buffer indent-region)
  (crux-with-region-or-buffer untabify)
  (crux-with-region-or-buffer tabify)
  (crux-with-region-or-buffer fill-region))

disable-mouse

Disable the mouse in Emacs

(use-package disable-mouse
  :demand  t
  :if (not (string= (getenv "XDG_CURRENT_DESKTOP") "exwm"))
  :config
  (global-disable-mouse-mode)
  (dolist (key '([mouse-1]
                 [mouse-2]
                 [mouse-4]
                 [mouse-5]
                 [mouse-6]
                 [mouse-7]
                 [down-mouse-1]
                 [drag-mouse-1]
                 [wheel-right]
                 [double-wheel-right]
                 [triple-wheel-right]
                 [wheel-left]
                 [double-wheel-left]
                 [triple-wheel-left]
                 [wheel-down]
                 [double-wheel-down]
                 [triple-wheel-down]
                 [wheel-up]
                 [double-wheel-up]
                 [triple-wheel-up]))
    (define-key evil-motion-state-map key #'ignore)))

elmacro

emacs-which-key

Emacs package that displays available keybindings in popup

(use-package which-key
  :defer 10
  :general
  (d/leader-keys
   "hk" 'which-key-show-top-level)
  :config
  (which-key-mode)

  (defmacro d/declare-prefix (&rest body)
    (declare (indent defun))
    `(which-key-add-key-based-replacements
       ,@(cl-loop
          for (prefix name) on body
          by #'cddr
          while name
          append `(,(concat "SPC " prefix) ,name
                   ,(concat "C-SPC " prefix) ,name))))

  (defmacro d/declare-mode-prefix (modes &rest body)
    (declare (indent defun))
    (let ((modes (if (listp modes) modes (list modes))))
      `(progn
         ,@(cl-loop
            for mode in modes collect
            `(which-key-add-major-mode-key-based-replacements ',mode
               ,@(cl-loop
                  for (prefix name) on body
                  by #'cddr
                  while name
                  append `(,(concat ", " prefix) ,name
                           ,(concat "C-, " prefix) ,name)))))))

  (defmacro d/which-key-remove-prefix (&rest body)
    (declare (indent defun))
    `(progn
       ,@(cl-loop for regexp in body collect
                  `(push '((nil . ,(concat regexp "\\(.+\\)")) . (nil . "\\1"))
                         which-key-replacement-alist))))


  (d/declare-prefix
    "a"   "applications"
    "b"   "buffer"
    "f"   "file"
    "h"   "help"
    "hd"  "describe"
    "i"   "insert"
    "j"   "jump"
    "n"   "narrow/numbers"
    "q"   "quit"
    "s"   "search"
    "w"   "window"
    "xi"  "indent"
    "xl"  "lines"
    "SPC" "root")

  (d/declare-mode-prefix emacs-lisp-mode
    "e" "eval"
    "p" "pp")

  (setq which-key-sort-order 'which-key-key-order-alpha)
  (setq which-key-sort-uppercase-first nil)

  (d/which-key-remove-prefix
    "avy-"
    "counsel-"
    "counsel-projectile-"
    "crux-"
    "customize-"
    "d/"
    "evil-mc-"
    "evilnc-"
    "eyebrowse-"
    "eyebrowse-switch-to-"
    "ivy-"
    "magit-"
    "projectile-"))

Package to display keyboard macros or latest interactive commands as emacs lisp.

(use-package elmacro
  :disabled t
  :defer 10
  :config
  (elmacro-mode))

esup

ESUP - Emacs Start Up Profiler

(use-package esup
  :config
  (setq esup-insignificant-time 0.001))

flx

Fuzzy matching for Emacs … a la Sublime Text.

(use-package flx)

password-store

(use-package password-store
  :config
  (setq password-store-password-length 20))

persistent-scratch

Preserve the scratch buffer across Emacs sessions

(use-package persistent-scratch
  :defer 10
  :config
  (persistent-scratch-setup-default))

restart-emacs

A simple emacs package to restart emacs from within emacs.

SUPER nifty.

(use-package restart-emacs
  :general (d/leader-keys "qr" 'restart-emacs)
  :init
  (evil-ex-define-cmd "qr[estart]" 'restart-emacs))

smex

A smart M-x enhancement for Emacs.

Sorts ivy by most recently used, I think.

(use-package smex)

Hydras

toggle

(d/with-eval-after-load 'hydra
  (defhydra d/toggle
    (:pre (progn
            (defvar flycheck-mode nil)
            (defvar focus-mode nil)
            (defvar d/show-async-tangle-results nil))
     :color pink
     :timeout 1
     :hint nil)
    "
_a_ abbrev:              %-3s`abbrev-mode   _d_ debug-on-error:      %-3s`debug-on-error   _P_ prettify-symbols:       %-3s`prettify-symbols-mode
_i_ aggressive-indent:   %-3s`aggressive-indent-mode   _l_ rlines:              %-3s`display-line-numbers-mode   _F_ focus:                  %-3s`focus-mode
_c_ column-indicator:    %-3s`column-enforce-mode   _p_ smartparens:         %-3s`smartparens-mode   _z_ async-tangle-results:   %-3s`d/show-async-tangle-results
_f_ flycheck:            %-3s`flycheck-mode   _e_ evil-smartparens:    %-3s`evil-smartparens-mode
_s_ flyspell:            %-3s`flyspell-mode   _w_ global-whitespace:   %-3s`global-whitespace-mode
"
    ("a" abbrev-mode nil)
    ("i" aggressive-indent-mode nil)
    ("d" toggle-debug-on-error nil)
    ("c" column-enforce-mode nil)
    ("l" display-line-numbers-mode nil)
    ("p" smartparens-mode nil)
    ("P" prettify-symbols-mode nil)
    ("e" evil-smartparens-mode nil)
    ("f" flycheck-mode nil)
    ("F" focus-mode nil)
    ("s" flyspell-mode nil)
    ("w" global-whitespace-mode nil)
    ("z" (if d/show-async-tangle-results
             (setq d/show-async-tangle-results nil)
           (setq d/show-async-tangle-results t)) nil)
    ("q" nil)))

Evil

Packages

evil-anzu

(use-package evil-anzu
  :after evil
  :config
  (setq anzu-cons-mode-line-p nil)
  (defun d/anzu-update-mode-line (here total)
    (when anzu--state
      (let ((status (cl-case anzu--state
                      (search (format "%s/%d%s"
                                      (anzu--format-here-position here total)
                                      total (if anzu--overflow-p "+" "")))
                      (replace-query (format "(%d replace)" total))
                      (replace (format "(%d/%d)" here total)))))
        status)))
  (setq anzu-mode-line-update-function #'d/anzu-update-mode-line))

evil-indent-plus

Better indent textobjects for evil

(use-package evil-indent-plus
  :after evil
  :config
  (evil-indent-plus-default-bindings))

evil-magit

Black magic or evil keys for magit

(use-package evil-magit
  :after magit
  :config)

evil-matchit

(use-package evil-matchit
  :after evil
  :config (global-evil-matchit-mode 1))

evil-nerd-commenter

Comment/uncomment lines efficiently. Like Nerd Commenter in Vim

(use-package evil-nerd-commenter
  :commands (evilnc-comment-operator
             d/comment-or-uncomment-lines-inverse)
  :general
  (nmap
   "gc" 'evilnc-comment-operator
   "gy" 'evilnc-copy-and-comment-lines)
  (d/leader-keys
   ";"  'evilnc-comment-operator
   "c"  '(:ignore t :wk "comment")
   "ci" 'd/comment-or-uncomment-lines-inverse
   "cl" 'evilnc-comment-or-uncomment-lines
   "cp" 'evilnc-comment-or-uncomment-paragraphs
   "ct" 'evilnc-comment-or-uncomment-to-the-line
   "cy" 'evilnc-copy-and-comment-lines)
  :config
  (defun d/comment-or-uncomment-lines-inverse (&optional arg)
    "Source: https://git.io/vQKza"
    (interactive "p")
    (let ((evilnc-invert-comment-line-by-line t))
      (evilnc-comment-or-uncomment-lines arg))))

evil-numbers

Increment and decrement numbers in Emacs

(use-package evil-numbers
  :recipe (:host github
           :repo "dieggsy/evil-numbers"
           :upstream (:host github
                      :repo "cofi/evil-numbers")))

evil-surround

(use-package evil-surround
  :after evil
  :config
  (global-evil-surround-mode 1))

evil-embrace

(use-package evil-embrace
  :after evil-surround
  :config
  (add-hook 'org-mode-hook 'embrace-org-mode-hook)
  (evil-embrace-enable-evil-surround-integration)
  (d/with-eval-after-load 'exwm
    (setq evil-embrace-show-help-p nil)))

Bindings

(nmap custom-mode-map
  "q" #'Custom-buffer-done)

(evil-add-hjkl-bindings occur-mode-map 'emacs
  (kbd "/")       'evil-search-forward
  (kbd "n")       'evil-search-next
  (kbd "N")       'evil-search-previous
  (kbd "C-d")     'evil-scroll-down
  (kbd "C-u")     'evil-scroll-up
  (kbd "C-w C-w") 'other-window)

(evil-add-hjkl-bindings completion-list-mode-map 'emacs
  (kbd "/") 'evil-ex-search-forward)

(add-hook 'edebug-mode-hook #'evil-normalize-keymaps)

(evil-add-hjkl-bindings proced-mode-map 'normal
  "n" 'evil-ex-search-next
  "K" 'proced-send-signal)

Org

ob-ipython

org-babel integration with Jupyter for evaluation of (Python by default) code blocks

(use-package ob-ipython
  :after org)

ox-pandoc

Another org-mode exporter via pandoc.

Translates Org-mode file to various other formats via Pandoc. Pretty neat.

(use-package ox-pandoc
  :after ox
  :if (executable-find "pandoc")
  :config
  ;; default options for all output formats
  (setq org-pandoc-options '((standalone . t)
                             (latex-engine . xelatex)
                             (mathjax . t)
                             (parse-raw . t)))
  ;; cancel above settings only for 'docx' format
  (setq org-pandoc-options-for-docx '((standalone . nil))))

ox-twbs

Export org-mode docs as HTML compatible with Twitter Bootstrap.

(use-package ox-twbs
  :after ox)

toc-org

toc-org is an Emacs utility to have an up-to-date table of contents in the org files without exporting (useful primarily for readme files on GitHub)

(use-package toc-org
  :after org
  :config
  (add-hook 'org-mode-hook #'toc-org-enable))

org-gcal

Org sync with Google Calendar

(use-package org-gcal
  :commands org-gcal-sync
  :config
  (setq org-gcal-client-id (password-store-get "api/org-gcal-id")
        org-gcal-client-secret (password-store-get "api/org-gcal-secret")
        org-gcal-file-alist '(("diegoamundo@gmail.com" . "~/Dropbox/org/gcal.org"))))

ox-gfm

(use-package ox-gfm
  :after ox)

Elfeed

elfeed-org

Configure the Elfeed RSS reader with an Orgmode file

(use-package elfeed-org
  :after elfeed
  :config
  (elfeed-org))

elfeed-goodies

Various goodies for Elfeed

(use-package elfeed-goodies
  :after elfeed
  :config
  (elfeed-goodies/setup)
  (setq elfeed-goodies/entry-pane-position 'bottom))

Fun

Packages

drawille

Drawille library implementation in elisp.

Draws stuff in ascii.

(use-package drawille)

emms

(use-package emms
  :commands emms-smart-browse
  :general
  (d/leader-keys
   "mc" 'emms-pause
   "mb" 'emms-next
   "mz" 'emms-previous
   "ml" 'emms-seek-forward
   "mh" 'emms-seek-backward)
  :config
  (d/setup-hook emms-browser-mode
    (hl-line-mode 1))
  (require 'emms-setup)
  (emms-all)
  (setq emms-source-file-default-directory "~/Music/"
        emms-player-mpd-music-directory "~/Music/"
        emms-seek-seconds 5)
  (require 'emms-player-mpd)
  (add-to-list 'emms-player-list 'emms-player-mpd)
  (emms-player-mpd-connect)
  ;; (nmap :keymaps emms-browser-mode-map
  ;;   "SPC" 'emms-browser-toggle-subitems)
  ;; (defhydra h/emms (:hint nil/)
  ;;     "
  ;;      ⏯
  ;;     _p_
  ;; ")
  )

fireplace

A cozy fireplace for emacs.

For the cold winters.

(use-package fireplace
  :config
  :general
  (nmap fireplace-mode-map
    "q" 'fireplace-off
    "Q" 'fireplace-off
    "-" 'fireplace-down
    "=" 'fireplace-up
    "*" 'fireplace-toggle-smoke))

hacker-typer

A customizable implementation of http://hackertyper.com in emacs.

(use-package hacker-typer
  :config
  (setq hacker-typer-show-hackerman t)
  (setq hacker-typer-remove-comments t))

highlight-tail

Draw a colourful “tail” while you write

(pure awesome)

(use-package highlight-tail)

selectric-mode

Make your Emacs sound like a proper typewriter.

Clackity-clack.

(use-package selectric-mode)

speed-type

Practice touch/speed typing in emacs.

(use-package speed-type
  :general (d/leader-keys "as" 'speed-type-text)
  :config
  (setq speed-type--gb-url-format
        "http://www.gutenberg.org/cache/epub/%d/pg%d.txt"))

spray

A speed reading mode for Emacs.

(use-package spray)

xkcd

Read xkcd from Emacs.

(use-package xkcd
  :general
  (d/leader-keys "ax" 'xkcd)
  (nmap xkcd-mode-map
    "j" 'xkcd-next
    "h" 'xkcd-prev
    "k" 'xkcd-prev
    "l" 'xkcd-next
    "t" 'xkcd-alt-text
    "q" 'xkcd-kill-buffer
    "c" 'xkcd-copy-link
    "g" 'xkcd-get
    "r" 'xkcd-rand
    "o" 'xkcd-open-browser
    "e" 'xkcd-open-explanation-browser
    "G" 'xkcd-get-latest)
  :config)

Functions

(eval-when-compile
  (defvar zone-programs))

(defun d/zone-choose ()
  "Choose a PGM to run for `zone'.

Source: http://tinyurl.com/lo96nwc"
  (interactive)
  (require 'zone nil t)
  (let* ((pgm (completing-read
               "Program: "
               (mapcar #'symbol-name zone-programs)))
         (zone-programs (list (intern pgm))))
    (redisplay)
    (zone)))

Bindings

(d/leader-keys
 "ag"  '(:ignore t :wk "games")
 "agd" 'dunnet
 "agg" 'gomoku
 "agt" 'tetris)

Web services

ERC

Defaults

(d/with-eval-after-load 'erc
  (setq erc-prompt "\n            ❱❱"
        erc-fill-prefix  "          \t";"              "
        erc-notice-prefix   "          \t*** "
        erc-format-nick-function 'd/erc-format-nick
        erc-nick "dieggsy"
        erc-prompt-for-password nil
        erc-hide-timestamps t
        erc-input-line-position -1
        erc-autojoin-timing 'ident
        erc-header-line-format nil
        erc-fill-column 79
        erc-hide-list '("353")
        erc-lurker-threshold-time (* 6 60 60)
        erc-lurker-hide-list '("JOIN" "PART" "QUIT" "NICK")
        erc-kill-buffer-on-part t
        erc-kill-queries-on-quit t
        erc-kill-server-buffer-on-quit t
        erc-rename-buffers t
        erc-join-buffer 'bury
        erc-track-use-faces nil
        erc-track-priority-faces-only 'all
        erc-track-exclude-server-buffer t
        erc-format-query-as-channel-p t
        erc-track-faces-priority-list '(erc-error-face
                                        erc-current-nick-face
                                        erc-keyword-face
                                        erc-nick-msg-face
                                        erc-direct-msg-face
                                        erc-dangerous-host-face)
        erc-autojoin-channels-alist '(("freenode.net"
                                       "#emacs"
                                       "#gaygeeks"
                                       "##linux"
                                       "#zsh"
                                       "#python"
                                       "##programming"
                                       "#i3"
                                       "#lisp"
                                       "#chicken"
                                       "#nixos"))
        erc-log-channels-directory (no-littering-expand-var-file-name "erc/logs"))

  (erc-define-catalog-entry 'english 'ACTION "         *\t%n %a"))

Builtin

(use-package erc
  :ensure nil
  :general
  (d/mode-leader-keys
   :keymaps 'erc-mode-map
   "g" (lambda () (interactive) (let ((ivy-use-virtual-buffers nil)) (erc-switch-to-buffer))))
  (d/leader-keys
   "ai" 'd/erc)
  (imap erc-mode-map
    [up] 'erc-previous-command
    [down] 'erc-next-command)
  :config
  (add-hook 'window-configuration-change-hook #'d/erc-to-bottom)
  (add-hook 'erc-insert-post-hook #'erc-save-buffer-in-logs))

Packages

erc-hl-nicks

(use-package erc-hl-nicks
  :recipe (:host github
           :repo "dieggsy/erc-hl-nicks"
           :upstream (:host github
                      :repo "leathekd/erc-hl-nicks"))
  :after erc
  :config
  (setq erc-hl-nicks-skip-nicks '("diegs" "dieggsy")
        erc-lurker-ignore-chars erc-hl-nicks-ignore-chars)
  (push "erc-current-nick-face" erc-hl-nicks-skip-faces))

Variables

(d/with-eval-after-load 'erc
  (defvar d/erc-last-speaker (make-hash-table :test 'equal))
  (setq erc-lurker-state (make-hash-table :test 'equal)))

Functions

(d/with-eval-after-load 'erc
  (defmacro d/erc-nick-format-function-body (cond nick &optional else)
    `(if ,cond
         (let* ((nick ,nick)
                (trimmed (erc-hl-nicks-trim-irc-nick nick))
                (server (erc-canonicalize-server-name
                         erc-server-announced-name)))
           (unless (gethash server d/erc-last-speaker)
             (puthash server (make-hash-table :test 'equal) d/erc-last-speaker))
           (let* ((channel (buffer-name))
                  (mode (erc-get-user-mode-prefix nick))
                  (nick-was-last (string=
                                  (gethash channel
                                           (gethash server d/erc-last-speaker))
                                  nick))
                  (long-char
                   (if (and (> (length nick) 10)
                            (not nick-was-last))
                       (erc-propertize "" 'face '(:foreground "#D3869B" ))
                     "")))

             (when (>= (cl-incf erc-lurker-cleanup-count)
                       erc-lurker-cleanup-interval)
               (setq erc-lurker-cleanup-count 0)
               (erc-lurker-cleanup))
             (unless (gethash server erc-lurker-state)
               (puthash server (make-hash-table :test 'equal) erc-lurker-state))
             (puthash trimmed (current-time)
                      (gethash server erc-lurker-state))
             (puthash channel nick (gethash server d/erc-last-speaker))
             (format "%10.10s%s	"
                     (cond (nick-was-last
                            (erc-propertize
                             ""
                             'face
                             (gethash trimmed erc-hl-nicks-face-table)))
                           ((not (member trimmed erc-hl-nicks-skip-nicks))
                            (concat
                             (erc-propertize mode 'face 'erc-nick-prefix-face)
                             (erc-propertize nick 'face (erc-hl-nicks-make-face trimmed))))
                           (t
                            (concat
                             (erc-propertize mode 'face 'erc-nick-prefix-face)
                             nick)))
                     long-char)))
       ,else))

  (defun d/erc-format-nick (&optional user channel-data)
    "Truncate nick when too long, substitute when repeated speaker, and
update lurker status."
    (d/erc-nick-format-function-body
     user
     (erc-server-user-nickname user)))

  (define-advice erc-format-my-nick (:override nil truncate-substitute-lurk)
    "Truncate nick when too long, substitute when repeated speaker, and
update lurker status."
    (d/erc-nick-format-function-body
     erc-show-my-nick
     (erc-current-nick)
     (let ((prefix "> "))
       (erc-propertize prefix 'font-lock-face 'erc-default-face))))

  (define-advice erc-format-privmessage
      (:override (nick msg privp msgp) no-brackets)
    "Remove the annoying angle brackets."
    (let* ((mark-s (if msgp (if privp "*" "") "          \t-"))
           (mark-e (if msgp (if privp "*" "") "- "))
           (str    (format "%s%s%s%s" mark-s nick mark-e msg))
           (nick-face (if privp 'erc-nick-msg-face 'erc-nick-default-face))
           (msg-face (if privp 'erc-direct-msg-face 'erc-default-face)))
      ;; add text properties to text before the nick, nick and after nick
      (erc-put-text-property 0 (length mark-s) 'font-lock-face msg-face str)
      (erc-put-text-property (length mark-s) (+ (length mark-s) (length nick))
                             'font-lock-face nick-face str)
      (erc-put-text-property (+ (length mark-s) (length nick)) (length str)
                             'font-lock-face msg-face str)
      str))

  (define-advice erc-track-find-face (:around (fn faces) promote-query)
    "Promote query buffers as if everything contains current nick.

Source: http://tinyurl.com/y8tj8vxx"
    (if (erc-query-buffer-p)
        (setq ad-return-value (intern "erc-current-nick-face"))
      (funcall fn faces)))

  (define-advice erc-track-modified-channels (:around (fn) promote-query)
    "Promote query buffers as if everything contains current nick when
only tracking priority faces.

Source: http://tinyurl.com/y8tj8vxx"
    (when (erc-query-buffer-p) (setq erc-track-priority-faces-only nil))
    (funcall fn)
    (when (erc-query-buffer-p) (setq erc-track-priority-faces-only 'all)))

  (define-advice erc-notifications-notify (:override (nick msg) channel-title)
    "Use channel as notification title and remove erc-fill-prefix."
    (let ((server (erc-canonicalize-server-name
                   erc-server-announced-name))
          (channel (buffer-name))
          (msg (string-join
                (split-string
                 (replace-regexp-in-string erc-fill-prefix "" msg)
                 nil
                 t
                 " ")
                " ")))
      (dbus-ignore-errors
        (setq erc-notifications-last-notification
              (notifications-notify
               :bus erc-notifications-bus
               :title (xml-escape-string (format "%s@%s" channel server))
               :body (xml-escape-string (format "%s: %s" nick msg))
               :replaces-id erc-notifications-last-notification
               :app-icon erc-notifications-icon)))))

  (defun d/erc-to-bottom ()
    (and (eq major-mode 'erc-mode)
         (erc-scroll-to-bottom))))

Setup

(d/setup-hook erc-mode
  (erc-hl-nicks-mode 1)
  (smartparens-mode -1)
  (setq tab-width 5)
  (erc-stamp-mode -1)
  (erc-notifications-mode 1)
  (erc-log-mode 1)
  (evil-smartparens-mode -1)
  (show-smartparens-mode -1)
  (erc-move-to-prompt-mode 1))

Packages

elfeed

An Emacs web feeds client

Configure the Elfeed RSS reader with an Orgmode file

(use-package elfeed
  :general
  (d/leader-keys "ae" 'elfeed)
  (nmap elfeed-search-mode-map
    "RET" 'elfeed-search-show-entry
    "+" 'elfeed-search-tag-all
    "-" 'elfeed-search-untag-all
    "G" 'elfeed-search-fetch
    "S" 'elfeed-search-set-filter
    "b" 'elfeed-search-browse-url
    "g" 'elfeed-search-update--force
    "q" 'quit-window
    "r" 'elfeed-search-untag-all-unread
    "s" 'elfeed-search-live-filter
    "u" 'elfeed-search-tag-all-unread
    "y" 'elfeed-search-yank
    "U" 'elfeed-update)
  (nmap elfeed-show-mode-map
    "+" 'elfeed-show-tag
    "-" 'elfeed-show-untag
    "P" 'elfeed-show-play-enclosure
    "b" 'elfeed-show-visit
    "d" 'elfeed-show-save-enclosure
    "g" 'elfeed-show-refresh
    "l" 'elfeed-goodies/split-show-next
    "h" 'elfeed-goodies/split-show-prev
    "q" 'elfeed-kill-buffer
    "s" 'elfeed-show-new-live-search
    "y" 'elfeed-show-yank)
  :config
  (add-hook 'elfeed-search-mode-hook (lambda () (evil-smartparens-mode -1))))

sunshine

An Emacs package for displaying the forecast from OpenWeatherMap.

(use-package sunshine
  :general
  (d/leader-keys
   "aW" 'sunshine-quick-forecast
   "aw" 'sunshine-forecast)
  :config
  (setq sunshine-location "02139,USA"
        sunshine-appid (password-store-get "api/openweathermap")))

EXWM

Startup

(use-package exwm
  :demand t
  :if (string= (getenv "XDG_CURRENT_DESKTOP") "exwm")
  :config
  (add-hook 'exwm-update-class-hook
            (lambda ()
              (exwm-workspace-rename-buffer exwm-class-name)))
  (exwm-input-set-key (kbd "s-r") #'exwm-reset)
  (exwm-input-set-key (kbd "s-w") #'exwm-workspace-switch)
  (dotimes (i 10)
    (exwm-input-set-key (kbd (format "s-%d" (if (= 9 i) 0 (1+ i))))
                        `(lambda ()
                           (interactive)
                           (exwm-workspace-switch-create ,i))))
  (setq exwm-workspace-index-map (lambda (i) (number-to-string (1+ i))))
  (exwm-enable)

  (setenv "INSIDE_EMACS" "1")

  (setq window-divider-default-right-width 1)
  (setq window-divider-default-bottom-width 1)

  (window-divider-mode)

  (setq windmove-wrap-around t)

  ;; (pinentry-start)


  (add-to-list 'evil-insert-state-modes 'exwm-mode)

  (add-hook 'evil-normal-state-entry-hook (lambda ()
                                            (setq-local exwm-input-line-mode-passthrough t)))
  (add-hook 'evil-insert-state-entry-hook (lambda ()
                                            (setq-local exwm-input-line-mode-passthrough nil)))

  (add-hook 'exwm-floating-setup-hook (lambda () (setq-local mode-line-format nil))))

Functions

(d/with-eval-after-load 'exwm
  (defmacro d/exwm-define-key (&rest bindings)
    (declare (indent defun))
    (let ((keys (cl-loop for (key func) on bindings
                         by #'cddr
                         collect (elt (kbd key) 0))))

      `(progn
         (setq exwm-input-prefix-keys ',keys)
         (general-define-key ,@bindings))))

  (cl-defmacro d/exec (cmd &key pre post)
    `(lambda () (interactive) ,pre (call-process-shell-command ,cmd) ,post))

  (cl-defmacro d/exwm-mode-line-process (name &key filter-body cmd repeat fmt)
    (declare (indent defun))
    (let ((shell-cmd (format "while sleep %d; do %s; done" repeat cmd))
          (filter-name (intern (concat name "-filter")))
          (var-name (intern name)))
      `(progn
         (defvar ,var-name (string-trim (shell-command-to-string ,cmd)))
         (start-process-shell-command ,name nil ,shell-cmd)
         (defun ,filter-name (process output)
           filter-body
           (setq ,var-name ,(if fmt `(funcall ,fmt (string-trim output))
                              `(string-trim output)))
           (force-mode-line-update 'all))
         (set-process-filter (get-process ,name) #',filter-name))))

  (defun d/swap-window-right ()
    (interactive)
    (let* ((buff (current-buffer))
           (win (windmove-find-other-window 'right))
           (o-buff (window-buffer win)))
      (set-window-buffer win buff)
      (set-window-buffer (selected-window) o-buff)
      (windmove-right)))

  (defun d/swap-window-left ()
    (interactive)
    (let* ((buff (current-buffer))
           (win (windmove-find-other-window 'left))
           (o-buff (window-buffer win)))
      (set-window-buffer win buff)
      (set-window-buffer (selected-window) o-buff)
      (windmove-left)))

  (defun d/swap-window-down ()
    (interactive)
    (let* ((buff (current-buffer))
           (win (windmove-find-other-window 'down))
           (o-buff (window-buffer win)))
      (set-window-buffer win buff)
      (set-window-buffer (selected-window) o-buff)
      (windmove-down)))

  (defun d/swap-window-up ()
    (interactive)
    (let* ((buff (current-buffer))
           (win (windmove-find-other-window 'up))
           (o-buff (window-buffer win)))
      (set-window-buffer win buff)
      (set-window-buffer (selected-window) o-buff)
      (windmove-up))))

Processes

(d/with-eval-after-load 'exwm

  (setq d/mute
        (string= (string-trim
                  (shell-command-to-string "amixer -D pulse get Master \
| grep \"Left: Playback\" \
| awk '{print $6}' \
| tr -d \"[-]\""))
                 "off"))

  (setq d/kb-layout (string-trim
                     (shell-command-to-string
                      "layout=$(setxkbmap -query | grep layout | cut -d' ' -f6)

if [ \"$layout\" = \"us\" ]; then
    echo 'QW'
elif [ \"$layout\" = \"dvorak\" ]; then
     echo 'DV'
fi")))

  (d/exwm-mode-line-process "d/ssid"
    :cmd "iwgetid -r || echo 'None'"
    :repeat 5)

  (d/exwm-mode-line-process "d/battery"
    :cmd "bat=$(acpi | cut -d \" \" -f4 | tr -d \"%,\")
Adapt=$(acpi -a | cut -d \" \" -f3)

touch /tmp/battery-status
if [ \"$bat\" -lt 11 ] && [ \"$(</tmp/battery-status)\" != \"critically-low\" ] && [ \"$Adapt\" != \"on-line\" ]; then
    espeak -vf4 \"Battery critically low, consider charging.\" &
    notify-send \"Battery critically low, consider charging.\" &
    echo \"critically-low\" > /tmp/battery-status
else
    echo \"fine\" > /tmp/battery-status
fi

echo -e \" $bat \""
    :repeat 30))

Bindings

(d/with-eval-after-load 'exwm
  (d/exwm-define-key
    "s-u" 'exwm-layout-toggle-fullscreen

    "s-&" (lambda (command)
            (interactive (list (read-shell-command "$ ")))
            (start-process-shell-command command nil command))

    "s-s" 'evil-window-right
    "s-h" 'evil-window-left
    "s-t" 'evil-window-down
    "s-n" 'evil-window-up

    "s-<right>" 'evil-window-right
    "s-<left>" 'evil-window-left
    "s-<down>" 'evil-window-down
    "s-<up>" 'evil-window-up

    "s-S" 'd/swap-window-right
    "s-H" 'd/swap-window-left
    "s-T" 'd/swap-window-down
    "s-N" 'd/swap-window-up

    "s-S-<right>" 'd/swap-window-right
    "s-S-<left>" 'd/swap-window-left
    "s-S-<down>" 'd/swap-window-down
    "s-S-<up>" 'd/swap-window-up

    "s-p" 'password-store-copy)

  (add-to-list 'exwm-input-prefix-keys ?\C-/))

Finalize

(when window-system
  (let ((elapsed (float-time (time-subtract (current-time)
                                            d/emacs-start-time))))
    (message "Loading %s...done (%.3fs)" load-file-name elapsed))

  (add-hook 'after-init-hook
            `(lambda ()
               (let ((elapsed (float-time (time-subtract (current-time)
                                                         d/emacs-start-time))))
                 (message "Loading %s...done (%.3fs) [after-init]"
                          ,load-file-name elapsed)))
            t))
# Local Variables:
# after-save-hook: (git-gutter d/async-babel-tangle)
# End: