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
- Intro
- Initial Setup
- Preface
- Core
- Help
- Files/Buffers
- Editing
- Navigation
- Appearance
- Dev
- Tools
- Enhancements
- Fun
- Web services
- EXWM
- Finalize
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))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
aandg
- second option for
- 1.4x Line spacing
| Regular | Input Mono Light |
| Italic | Input Mono Light Italic |
| Bold | Input Mono Medium |
| Bold Italic | Input 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: