dotfiles/emacs/.emacs.d/init.org

6832 lines
233 KiB
Org Mode
Raw Normal View History

-*- eval: (add-hook 'after-save-hook 'org-babel-tangle 0 t) -*-
2019-11-27 19:43:06 +00:00
#+PROPERTY: header-args :results silent
#+PROPERTY: header-args:emacs-lisp :lexical t :tangle ~/.emacs.d/config/base.el
2019-01-28 02:46:29 +00:00
2020-01-31 02:55:33 +00:00
This is a literate init file holding my Emacs configuration. It is
2020-08-24 13:28:42 +00:00
initially loaded by a [[file:init.el][bootstrap file]] that lives at ~/.emacs.d/init.el.
2019-01-28 02:46:29 +00:00
* Prelude
2019-04-05 14:05:40 +00:00
Enables lexical binding for everything in init.el:
#+BEGIN_SRC emacs-lisp
;;; -*- lexical-binding: t; -*-
#+END_SRC
2020-01-14 00:07:01 +00:00
** Garbage collection
Some GC tweaks [[https://github.com/hlissner/doom-emacs/blob/develop/docs/faq.org#how-does-doom-start-up-so-quickly]["borrowed" from Doom emacs]].
Turn off GC during init and restore it afterwards:
#+BEGIN_SRC emacs-lisp
(setq gc-cons-threshold most-positive-fixnum
gc-cons-percentage 0.6)
(add-hook 'emacs-startup-hook
(lambda ()
2020-05-22 22:10:12 +00:00
(setq gc-cons-threshold 100000000
2020-01-14 00:07:01 +00:00
gc-cons-percentage 0.1)))
#+END_SRC
Also suppress GC for 1 second after the minibuffer is active to avoid stuttering autocompletion and other GC hangups:
#+BEGIN_SRC emacs-lisp
(defun defer-garbage-collection ()
(setq gc-cons-threshold most-positive-fixnum))
(defun restore-garbage-collection ()
(run-at-time
2020-05-22 22:10:12 +00:00
1 nil (lambda () (setq gc-cons-threshold 100000000))))
2020-01-14 00:07:01 +00:00
(add-hook 'minibuffer-setup-hook #'defer-garbage-collection)
(add-hook 'minibuffer-exit-hook #'restore-garbage-collection)
#+END_SRC
** Unset file-handler-alist during initialization
Another optimization from [[https://github.com/hlissner/doom-emacs/blob/develop/docs/faq.org#how-does-doom-start-up-so-quickly][Doom Emacs]].
#+BEGIN_SRC emacs-lisp
(defvar file-name-handler-alist-backup file-name-handler-alist)
(setq file-name-handler-alist nil)
(add-hook 'emacs-startup-hook
(lambda ()
(setq file-name-handler-alist file-name-handler-alist-backup)))
#+END_SRC
** Variables
#+BEGIN_SRC emacs-lisp
2020-02-23 14:40:15 +00:00
(setq vc-follow-symlinks t
2020-04-27 20:35:31 +00:00
frame-resize-pixelwise t
tab-always-indent 'complete
2020-05-22 22:10:12 +00:00
enable-recursive-minibuffers t
2020-07-21 13:39:47 +00:00
read-process-output-max (* 1024 1024)
bookmark-save-flag 1)
2020-04-27 20:35:31 +00:00
(setq-default indent-tabs-mode nil)
#+END_SRC
2019-02-07 21:53:16 +00:00
* Default directory
#+BEGIN_SRC emacs-lisp
(cd "~")
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-01-28 02:46:29 +00:00
* Packages
Load [[https://github.com/raxod502/straight.el][straight.el]] to manage package installation:
#+BEGIN_SRC emacs-lisp
(defvar bootstrap-version)
(unless (boundp 'bootstrapping-init)
(let ((bootstrap-file
(expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
(bootstrap-version 5))
(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)))
#+END_SRC
2019-01-28 02:46:29 +00:00
2019-12-27 16:29:59 +00:00
`use-package` is a macro that simplifies installing and loading packages.
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2019-12-27 15:38:55 +00:00
(straight-use-package 'use-package)
(setq straight-use-package-by-default t)
2019-01-28 02:46:29 +00:00
#+END_SRC
** Utility functions
#+BEGIN_SRC emacs-lisp
(defun find-library-readme (library)
(interactive (list (read-library-name)))
(let* ((dir (file-name-directory (file-truename (find-library-name library))))
(doc (car (directory-files dir t "\\(readme\\|README\\)\\..*"))))
(if (not doc)
(error "No README found")
(find-file doc)
(when (eq major-mode 'markdown-mode)
(markdown-view-mode)))))
#+END_SRC
2019-01-28 02:46:29 +00:00
* Benchmarking
`benchmark-init` does what it says on the box. This sets it up to benchmark my init time and then disable benchmarking after init completes.
#+BEGIN_SRC emacs-lisp
(use-package benchmark-init
:config
(add-hook 'after-init-hook 'benchmark-init/deactivate))
#+END_SRC
* General
Better keybinding.
#+BEGIN_SRC emacs-lisp
(use-package general
:init
(setq general-override-states '(insert
emacs
hybrid
normal
visual
motion
operator
replace)))
2019-01-28 02:46:29 +00:00
#+END_SRC
* Which-key
`which-key` makes keybindings discoverable.
#+BEGIN_SRC emacs-lisp
(use-package which-key
:config
(which-key-mode))
#+END_SRC
This function defines a prefix group for `which-key` so that it doesn't display `prefix`.
#+BEGIN_SRC emacs-lisp
(defun jdormit/define-prefix (binding name)
(which-key-add-key-based-replacements
(concat leader " " binding)
name)
(which-key-add-key-based-replacements
(concat "," " " binding)
2019-01-28 02:46:29 +00:00
name))
#+END_SRC
* Evil Mode
Because I like modal editing and dislike RSI.
#+BEGIN_SRC emacs-lisp
(use-package evil
2019-11-18 22:32:58 +00:00
:init
(setq evil-want-keybinding nil)
2019-01-28 02:46:29 +00:00
:config
(evil-mode 1))
#+END_SRC
Make undo not undo paragraphs at a time:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(setq evil-want-fine-undo t)
#+END_SRC
Add a convenience function for making buffer-local ex-commands:
#+BEGIN_SRC emacs-lisp
(defun evil-ex-define-local-cmd (cmd function)
(set (make-local-variable 'evil-ex-commands) (copy-tree evil-ex-commands))
(evil-ex-define-cmd cmd function))
#+END_SRC
2019-11-18 22:32:58 +00:00
** evil-collection
A collection of evil bindings for various modes
#+BEGIN_SRC emacs-lisp
(use-package evil-collection
2019-11-19 15:43:50 +00:00
:after (evil)
2020-08-23 03:33:53 +00:00
:hook ((after-init . evil-collection-init))
2019-11-19 15:43:50 +00:00
:config
2020-08-23 03:33:53 +00:00
(setq evil-collection-company-use-tng nil))
#+END_SRC
2019-11-18 22:32:58 +00:00
** leader key
Use the spacebar as a leader key in evil-mode's normal state and in various other modes:
#+BEGIN_SRC emacs-lisp
(defconst leader "SPC")
(general-define-key
:keymaps 'override
:states '(normal visual motion)
"SPC" nil)
(general-create-definer leader-def-key
:keymaps 'override
:states '(normal visual motion)
:prefix leader
:prefix-map 'leader-map)
#+END_SRC
2019-11-20 14:52:22 +00:00
#+BEGIN_SRC emacs-lisp
(jdormit/define-prefix "?" "help")
(leader-def-key "?" help-map)
2019-11-18 22:32:58 +00:00
#+END_SRC
2019-12-12 19:33:07 +00:00
** evil-snipe
#+BEGIN_SRC emacs-lisp
(use-package evil-snipe
:after (evil)
:config
(evil-snipe-mode 1)
(evil-snipe-override-mode 1)
2020-06-03 19:08:47 +00:00
(add-to-list 'evil-snipe-disabled-modes 'dired-mode)
2020-06-14 16:25:35 +00:00
(add-to-list 'evil-snipe-disabled-modes 'structlog-mode)
2019-12-12 19:33:07 +00:00
(with-eval-after-load 'magit
2019-12-13 20:55:00 +00:00
(add-hook 'magit-mode-hook 'turn-off-evil-snipe-override-mode))
(with-eval-after-load 'prodigy
(add-hook 'prodigy-mode-hook 'turn-off-evil-snipe-mode))
(with-eval-after-load 'pass
(add-hook 'pass-mode-hook 'turn-off-evil-snipe-mode)))
2019-12-12 19:33:07 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
2020-01-02 15:55:34 +00:00
** evil-commentary
Adds Evil commands to comment out lines of code:
#+BEGIN_SRC emacs-lisp
(use-package evil-commentary
:after (evil)
2020-02-07 15:29:34 +00:00
:hook ((prog-mode . evil-commentary-mode)))
2020-01-02 15:55:34 +00:00
#+END_SRC
2019-11-04 22:08:52 +00:00
** Additional keybindings
#+BEGIN_SRC emacs-lisp
(general-def 'normal "zM" #'hs-hide-level :keymaps 'override)
(general-def 'normal "z=" #'text-scale-increase :keymaps 'override)
(general-def 'normal "z-" #'text-scale-decrease :keymaps 'override)
(general-def 'normal "z0" #'text-scale-adjust :keymaps 'override)
(general-def 'normal view-mode-map "0" nil :keymaps 'override)
(general-def 'normal prodigy-view-mode-map "0" nil :keymaps 'override)
(general-def 'normal messages-buffer-mode-map "SPC" leader-map :keymaps 'override)
2019-11-04 22:08:52 +00:00
#+END_SRC
2020-01-07 15:52:05 +00:00
* Hydra
[[https://github.com/abo-abo/hydra][Hydra]]s are convenient keybinding menus.
#+BEGIN_SRC emacs-lisp
2020-01-14 15:23:55 +00:00
(use-package hydra
:defer t)
2020-01-07 15:52:05 +00:00
#+END_SRC
2020-08-23 03:33:53 +00:00
* Syncthing
I put lots of stuff in Syncthing, but the actual folder location
differs on my different computers. This function resolves to the
Syncthing directory:
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(defcustom syncthing-path (expand-file-name "~/Sync")
"The absolute path to the Syncthing directory"
:type 'directory)
2020-08-23 03:33:53 +00:00
(defun syncthing-directory (&optional path)
(f-join syncthing-path (s-chop-prefix (f-path-separator) (or path ""))))
#+END_SRC
* Emacs Lisp
** Packages
Some helpful ELisp packages:
- [[https://github.com/kiwanami/emacs-deferred][deferred]] is an async API library
- [[https://github.com/magnars/s.el][s.el]] is a string manipulation library
- [[https://github.com/magnars/dash.el][dash.el]] is a list manipulation library
- [[https://github.com/rejeep/f.el][f.el]] is a file manipulation library
- [[https://github.com/tkf/emacs-request][request]] is an HTTP library
#+BEGIN_SRC emacs-lisp
(use-package deferred
:commands (deferred:next
deferred:nextc
deferred:error
deferred:cancel
deferred:watch
deferred:wait
deferred:$
deferred:loop
deferred:parallel
deferred:earlier
deferred:call
deferred:apply
deferred:process
deferred:process-buffer
deferred:wait-idle
deferred:url-retrieve
deferred:url-get
deferred:url-post
deferred:new
deferred:succeed
deferred:fail
deferred:callback
deferred:callback-post
deferred:errorback
deferred:errorback-post
deferred:try
deferred:timeout
deferred:process))
(use-package s
:defer t
2020-08-23 03:33:53 +00:00
:init
(add-hook 'emacs-startup-hook (lambda () (require 's)))
:config
(defun camel-case (&optional arg)
(interactive "P")
(let* ((bounds (bounds-of-thing-at-point 'symbol))
(word (buffer-substring (car bounds) (cdr bounds)))
(camel (if arg (s-upper-camel-case word)
(s-lower-camel-case word))))
(delete-region (car bounds) (cdr bounds))
(insert camel))))
(use-package dash
:defer t
:init
(add-hook 'emacs-startup-hook (lambda () (require 'dash))))
(use-package dash-functional
:defer t
:init
(add-hook 'emacs-startup-hook (lambda () (require 'dash-functional))))
(use-package f
:defer t
:init
(add-hook 'emacs-startup-hook (lambda () (require 'f)))
(autoload 'f-join "f"))
(use-package request
:commands (request request-deferred))
2020-08-23 03:33:53 +00:00
#+END_SRC
** Editing Elisp
2019-11-18 16:01:04 +00:00
#+BEGIN_SRC emacs-lisp
(general-def '(normal motion) emacs-lisp-mode-map "C-c C-c" #'eval-defun :keymaps 'override)
(general-def '(normal motion insert) lisp-interaction-mode-map "C-c C-c" #'eval-print-last-sexp :keymaps 'override)
(add-hook 'ielm-mode-hook 'smartparens-strict-mode)
2019-11-18 16:01:04 +00:00
#+END_SRC
** Load path
For machine or user specific libraries:
2019-12-13 16:55:40 +00:00
#+BEGIN_SRC emacs-lisp
(add-to-list 'load-path (expand-file-name "~/site-lisp"))
#+END_SRC
2020-08-23 03:33:53 +00:00
And for global ones:
#+BEGIN_SRC emacs-lisp
(add-to-list 'load-path "/usr/local/share/emacs/site-lisp")
2019-12-13 16:55:40 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
** Utilities
Reading a file as a string:
#+BEGIN_SRC emacs-lisp
(defun read-file (path)
"Returns the contents of the file as a string"
(with-temp-buffer
(insert-file-contents path)
(buffer-string)))
#+END_SRC
2020-01-02 03:03:12 +00:00
Opening a file as sudo:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(defun sudo-find-file (file-name)
"Like find file, but opens the file as root."
(interactive "F")
(let ((tramp-file-name (concat "/sudo::" (expand-file-name file-name))))
(find-file tramp-file-name)))
2019-01-28 02:46:29 +00:00
#+END_SRC
Recursive =assoc= for nested alists:
2019-11-22 23:19:46 +00:00
#+BEGIN_SRC emacs-lisp
(defun assoc-recursive (alist &rest keys)
"Recursively find KEYs in ALIST."
(while keys
(setq alist (cdr (assoc (pop keys) alist))))
alist)
2019-11-22 23:19:46 +00:00
#+END_SRC
2019-12-26 14:58:38 +00:00
Format a millis timestamp into human-readable form:
2019-06-03 14:48:42 +00:00
#+BEGIN_SRC emacs-lisp
(defun format-epoch-millis (millis)
(interactive "nTimestamp: ")
(message (format-time-string "%F %r" (/ millis 1000))))
2019-06-03 14:48:42 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
The same but for seconds:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(defun format-epoch-seconds (seconds)
(interactive "nTimestamp: ")
(message (format-time-string "%F %r" seconds)))
2019-01-28 02:46:29 +00:00
#+END_SRC
Checking if a buffer contains a string:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(defun buffer-contains-substring (string)
(save-excursion
(save-match-data
(goto-char (point-min))
(search-forward string nil t))))
2019-01-28 02:46:29 +00:00
#+END_SRC
Pretty-print JSON:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(defun pprint-json (raw-json)
(with-temp-buffer
(insert raw-json)
(json-pretty-print (point-min) (point-max))
(buffer-substring (point-min) (point-max))))
2019-01-28 02:46:29 +00:00
#+END_SRC
Load environment variables into Emacs from a shell script:
#+BEGIN_SRC emacs-lisp
(cl-defun extract-vars-from-env-file (file &key dir)
"Extracts an alist of variable name to value from
a bash script that exports environment variables."
(let ((file (expand-file-name file))
(var-re "\\(.+?\\)=\\(.+\\)$")
(env '()))
(with-temp-buffer
(cd (expand-file-name (or dir (file-name-directory file))))
(insert (shell-command-to-string (concat "source "
(shell-quote-argument file)
" > /dev/null && env")))
(goto-char (point-min))
(save-match-data
(while (re-search-forward var-re nil t)
(push (cons (match-string 1) (match-string 2)) env))))
env))
(defun source-env-file (file)
(interactive "fFile: \n")
(let ((env (extract-vars-from-env-file file)))
(dolist (binding env)
(setenv (car binding) (cdr binding)))))
(cl-defmacro with-env-from-file (file &rest body)
(declare (indent 1))
(let ((env-var (make-symbol "the-env"))
(path-var (make-symbol "the-path")))
`(let* ((,env-var (extract-vars-from-env-file ,file))
(,path-var (assoc "PATH" ,env-var))
(exec-path
(if ,path-var
(append (split-string (cdr ,path-var) ":") exec-path)
exec-path))
(process-environment
(append
(mapcar
(lambda (elt) (format "%s=%s" (car elt) (cdr elt)))
,env-var)
process-environment)))
,@body)))
(cl-defun call-with-env-from-file (file callback &key dir)
(let* ((env (extract-vars-from-env-file file :dir dir))
(path (assoc "PATH" env))
(exec-path
(if path
(append (split-string (cdr path) ":") exec-path)
exec-path))
(process-environment
(append (mapcar (lambda (elt) (format "%s=%s" (car elt) (cdr elt))) env)
process-environment)))
(funcall callback)))
(defmacro with-env (env &rest body)
(declare (indent 1))
`(let* ((process-environment
(append
(mapcar
(lambda (elt) (format "%s=%s" (car elt) (cdr elt)))
,env)
process-environment)))
,@body))
2019-01-28 02:46:29 +00:00
#+END_SRC
Convenience macro to run some code in a particular default-directory:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(defmacro with-default-directory (dir &rest body)
(declare (indent 1))
`(let ((default-directory ,dir))
,@body))
2019-01-28 02:46:29 +00:00
#+END_SRC
#+BEGIN_SRC emacs-lisp
(defun random-alnum (&optional n)
(let* ((n-chars (or n 1))
(alnum "abcdefghijklmnopqrstuvwxyz0123456789")
(result ""))
(dotimes (_ n-chars result)
(let ((i (% (abs (random)) (length alnum))))
(setq result
(concat result (substring alnum i (1+ i))))))))
2019-01-28 02:46:29 +00:00
#+END_SRC
A handy function to colorize a buffer with ANSI escape characters in it:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(defun ansi-color (&optional begin end)
(interactive)
(let ((begin (or begin (point-min)))
(end (or end (point-max)))
(inhibit-read-only t))
(ansi-color-apply-on-region begin end)))
2019-01-28 02:46:29 +00:00
#+END_SRC
** Persisting variables between session
The idea behind this is pretty simple - variables get persisted in ~/.emacs.d/<persisted-vars-file> as a plist of (variable-name variable-value).
2019-07-19 14:39:48 +00:00
#+BEGIN_SRC emacs-lisp
(defvar persisted-vars-file "~/.emacs.d/persisted-vars")
2019-07-19 14:39:48 +00:00
#+END_SRC
This function retrieves the plist of persisted variables or nil if it doesn't exist:
2019-08-26 14:11:44 +00:00
#+BEGIN_SRC emacs-lisp
(defun get-persisted-plist ()
(let ((file (expand-file-name persisted-vars-file)))
(when (file-exists-p file)
(let ((vars-plist-str (read-file file)))
(unless (string= vars-plist-str "")
(car (read-from-string vars-plist-str)))))))
2019-08-26 14:11:44 +00:00
#+END_SRC
This function retrieves a persisted variable:
#+BEGIN_SRC emacs-lisp
(defun get-persisted-var (var-name)
"Retrieves the value of persisted variable `var-name`, or nil if not found"
(let ((vars-plist (get-persisted-plist)))
(when vars-plist
(plist-get vars-plist var-name))))
#+END_SRC
And this function persists a variable:
#+BEGIN_SRC emacs-lisp
(defun persist-variable (var-name value)
(let ((file (expand-file-name persisted-vars-file))
(vars-plist (get-persisted-plist)))
(if vars-plist
(progn
(plist-put vars-plist var-name value)
(write-region
(prin1-to-string vars-plist) nil file))
(let ((vars-plist `(,var-name ,value)))
(write-region
(prin1-to-string vars-plist) nil file)))))
#+END_SRC
** Process handling
Some utilities for calling out to other processes.
2019-12-18 16:26:50 +00:00
#+BEGIN_SRC emacs-lisp
(defun make-process-sentinel (success err)
"Makes a process sentinel that calls `success` on success and `err` on error"
(lambda (proc event)
(cond ((string-match-p "finished" event) (funcall success))
(t (funcall err)))))
(defun make-success-err-msg-sentinel (buf success-msg err-msg &optional kill-on-err)
(make-process-sentinel
(lambda ()
(message success-msg)
(kill-buffer buf))
(lambda ()
(message err-msg)
(when kill-on-err
(kill-buffer buf)))))
2019-12-18 16:26:50 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
A function to call a process passing some string as stdin and returning the process output:
2020-02-11 21:05:22 +00:00
#+BEGIN_SRC emacs-lisp
(defun make-process-fn (program &rest args)
"Returns a function that, when called, call `program` with arguments `args`,
passing the function argument as stdin"
(lambda (&optional input)
(with-temp-buffer
(if input
(progn
(insert input)
(apply #'call-process-region (point-min) (point-max) program t t nil args))
(apply #'call-process program nil t nil args))
(buffer-substring-no-properties (point-min) (point-max)))))
2020-02-11 21:05:22 +00:00
#+END_SRC
The same function but for commands that need to run in a shell:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(defun make-shell-fn (program &rest args)
"Returns a function that, when called, calls `program` in a shell
with arguments `args`, passing the function argument as stdin"
(lambda (&optional input)
(let ((cmd (combine-and-quote-strings `(,program ,@args))))
(with-temp-buffer
(if input
(progn
(insert input)
(call-shell-region (point-min) (point-max) cmd t t))
(call-process-shell-command cmd nil t))
(buffer-substring-no-properties (point-min) (point-max))))))
#+END_SRC
2019-04-23 13:02:42 +00:00
Running a shell command as sudo:
#+BEGIN_SRC emacs-lisp
(defun sudo-shell-command (command)
(with-temp-buffer
(cd "/sudo::/")
(shell-command command)))
#+END_SRC
2020-08-23 03:33:53 +00:00
** Buffer switch hooks
I want to be able to run code whenever I switch to a buffer running certain modes. The code to run is stored in a alist mapping mode names to lists of code to run (stored as a raw data structure to be eval'ed):
#+BEGIN_SRC emacs-lisp
(defvar buffer-mode-hooks '())
#+END_SRC
2020-08-23 03:33:53 +00:00
To add a new hook, push the code to run onto the correct list:
#+BEGIN_SRC emacs-lisp
(defun add-buffer-mode-hook (mode fn)
(if-let ((existing-entry (assoc mode buffer-mode-hooks)))
(push fn (cdr existing-entry))
(let ((new-entry `(,mode . (,fn))))
(push new-entry buffer-mode-hooks))))
#+END_SRC
2020-08-23 03:33:53 +00:00
Whenever the buffer changes, look up the major-mode to see if there is any code to run:
#+BEGIN_SRC emacs-lisp
(defun run-buffer-mode-hooks ()
(when-let ((entry (assoc major-mode buffer-mode-hooks)))
(dolist (fn (cdr entry))
(funcall fn))))
2020-08-23 03:33:53 +00:00
(add-hook 'buffer-list-update-hook #'run-buffer-mode-hooks)
#+END_SRC
2020-08-23 03:33:53 +00:00
** Aliases
#+BEGIN_SRC emacs-lisp
(defalias 'doc 'describe-symbol)
#+END_SRC
2020-08-23 03:33:53 +00:00
** Miscellaneous
#+BEGIN_SRC emacs-lisp
(setq warning-suppress-types
'((undo discard-info)))
#+END_SRC
2020-08-23 03:33:53 +00:00
* Org Mode
Notes, agenda, calendar, blogging, journaling, etc.
Loaded early to [[https://github.com/raxod502/straight.el#the-wrong-version-of-my-package-was-loaded][avoid a version clash]].
2019-04-23 14:25:46 +00:00
First, a function to get my notes directory:
#+BEGIN_SRC emacs-lisp
(defun org-directory (&optional path)
"Returns the directory for my org notes, appends PATH if given"
2020-10-10 14:04:17 +00:00
(f-join (expand-file-name "~/org")
(s-chop-prefix (f-path-separator) (or path ""))))
#+END_SRC
2020-08-23 03:33:53 +00:00
#+BEGIN_SRC emacs-lisp
(use-package org
:straight org-plus-contrib
:commands (org-element-map
org-agenda
org-capture)
:mode (("\\.org\\'" . org-mode))
:init
(jdormit/define-prefix "o" "org")
(leader-def-key "oa" 'org-agenda)
(leader-def-key "oc" 'org-capture)
:config
(defun agenda-files (&optional file)
(let ((agenda-dir (org-directory)))
(if file
(concat (file-name-as-directory agenda-dir) file)
agenda-dir)))
(setq org-src-fontify-natively t
org-ellipsis " ▼"
org-directory (org-directory)
org-link-elisp-confirm-function 'y-or-n-p
org-startup-with-inline-images t
org-return-follows-link t
org-log-done 'time
org-file-apps '(("log" . emacs)
(auto-mode . emacs)
(directory . emacs)
("\\.pdf\\'" . emacs)
("\\.mm\\'" . default)
("\\.x?html?\\'" . default))
org-agenda-files (list (agenda-files))
org-capture-templates `(("L" "Lola task" entry
(file+headline ,(agenda-files "todo.org") "Lola")
"* TODO %i%?")
("p" "Personal task" entry
(file+headline ,(agenda-files "todo.org") "Personal")
"* TODO %i%?")
("n" "Note" entry
(file ,(agenda-files "notes.org"))
"* %^{Description}\n%i%?")
("l" "Log" entry
(file ,(agenda-files "log.org"))
"* %<%Y-%m-%d %H:%M:%S>%?"))
org-refile-use-outline-path 'file
org-refile-targets `((org-agenda-files :level . 0)
(,(agenda-files "notes.org") :level . 1)
(,(agenda-files "todo.org") :level . 1))
org-todo-keywords '((sequence
"TODO(t)"
"IN PROGRESS(i)"
"BLOCKED(b)"
"|"
"DONE(d)"
"CANCELLED(c)"))
org-agenda-todo-ignore-scheduled 'future
org-agenda-tags-todo-honor-ignore-options t
org-agenda-span 'day
org-agenda-custom-commands
'(("L" "Lola" ((tags-todo "@lola")))
("t" "TODOs"
((agenda)
(alltodo)))))
(defun setup-org-mode ()
(require 'org-attach)
(org-display-inline-images nil t)
(org-redisplay-inline-images)
(auto-fill-mode))
(add-hook 'org-mode-hook #'setup-org-mode)
2020-08-23 03:33:53 +00:00
:general
(normal org-mode-map
"T" #'org-insert-todo-heading
"K" #'org-move-subtree-up
"J" #'org-move-subtree-down
"<return>" #'org-return
"TAB" #'org-cycle
"SPC" leader-map
"gn" #'org-next-link
"gp" #'org-previous-link)
(org-mode-map "C-c e" #'org-preview-latex-fragment)
2020-11-04 16:28:07 +00:00
(org-mode-map "C-c C-l" #'org-insert-link)
("C-c l" #'org-store-link))
#+END_SRC
** Evil-Org
Set up evil keybindings for Org mode:
2019-07-08 20:44:54 +00:00
#+BEGIN_SRC emacs-lisp
(use-package evil-org
:after (evil org)
:hook (org-mode . evil-org-mode)
:config
(add-hook 'evil-org-mode-hook
(lambda ()
(evil-org-set-key-theme
'(textobjects
insert
navigation
additional
shift
todo))
(general-def 'insert org-mode-map [backspace] 'org-delete-backward-char)))
(require 'evil-org-agenda)
(evil-org-agenda-set-keys)
(general-def 'motion org-agenda-mode-map "SPC" leader-map)
(general-def 'motion org-agenda-mode-map "gs" 'org-save-all-org-buffers)
(general-def '(normal motion) evil-org-mode-map
"C-S-j" nil
"C-S-k" nil
"C-S-h" nil
"C-S-l" nil))
2019-07-08 20:44:54 +00:00
#+END_SRC
** Org-mode hydra
A helpful agenda-mode hydra:
2019-07-08 20:44:54 +00:00
#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'org-agenda
(defhydra hydra-org-agenda (:pre (setq which-key-inhibit t)
:post (setq which-key-inhibit nil)
:hint none)
"
Org agenda (_q_uit)
^Clock^ ^Visit entry^ ^Date^ ^Other^
^-----^---- ^-----------^------------ ^----^----------- ^-----^---------
_ci_ in _SPC_ in other window _ds_ schedule _gr_ reload
_co_ out _TAB_ & go to location _dd_ set deadline _._ go to today
_cq_ cancel _RET_ & del other windows _dt_ timestamp _gd_ go to date
_cj_ jump _o_ link _+_ do later ^^
^^ ^^ _-_ do earlier ^^
^^ ^^ ^^ ^^
^View^ ^Filter^ ^Headline^ ^Toggle mode^
^----^-------- ^------^--------------- ^--------^------- ^-----------^----
_vd_ day _ft_ by tag _ht_ set status _tf_ follow
_vw_ week _fr_ refine by tag _hk_ kill _tl_ log
_vt_ fortnight _fc_ by category _hr_ refile _ta_ archive trees
_vm_ month _fh_ by top headline _hA_ archive _tA_ archive files
_vy_ year _fx_ by regexp _h:_ set tags _tr_ clock report
_vn_ next span _fd_ delete all filters _hp_ set priority _td_ diaries
_vp_ prev span ^^ ^^ ^^
_vr_ reset ^^ ^^ ^^
^^ ^^ ^^ ^^
"
;; Entry
("hA" org-agenda-archive-default)
("hk" org-agenda-kill)
("hp" org-agenda-priority)
("hr" org-agenda-refile)
("h:" org-agenda-set-tags)
("ht" org-agenda-todo)
;; Visit entry
("o" link-hint-open-link :exit t)
("<tab>" org-agenda-goto :exit t)
("TAB" org-agenda-goto :exit t)
("SPC" org-agenda-show-and-scroll-up)
("RET" org-agenda-switch-to :exit t)
;; Date
("dt" org-agenda-date-prompt)
("dd" org-agenda-deadline)
("+" org-agenda-do-date-later)
("-" org-agenda-do-date-earlier)
("ds" org-agenda-schedule)
;; View
("vd" org-agenda-day-view)
("vw" org-agenda-week-view)
("vt" org-agenda-fortnight-view)
("vm" org-agenda-month-view)
("vy" org-agenda-year-view)
("vn" org-agenda-later)
("vp" org-agenda-earlier)
("vr" org-agenda-reset-view)
;; Toggle mode
("ta" org-agenda-archives-mode)
("tA" (org-agenda-archives-mode 'files))
("tr" org-agenda-clockreport-mode)
("tf" org-agenda-follow-mode)
("tl" org-agenda-log-mode)
("td" org-agenda-toggle-diary)
;; Filter
("fc" org-agenda-filter-by-category)
("fx" org-agenda-filter-by-regexp)
("ft" org-agenda-filter-by-tag)
("fr" org-agenda-filter-by-tag-refine)
("fh" org-agenda-filter-by-top-headline)
("fd" org-agenda-filter-remove-all)
;; Clock
("cq" org-agenda-clock-cancel)
("cj" org-agenda-clock-goto :exit t)
("ci" org-agenda-clock-in :exit t)
("co" org-agenda-clock-out)
;; Other
("q" nil :exit t)
("gd" org-agenda-goto-date)
("." org-agenda-goto-today)
("gr" org-agenda-redo))
(general-def '(normal visual motion insert emacs) org-agenda-mode-map "." 'hydra-org-agenda/body))
2020-08-23 03:33:53 +00:00
#+END_SRC
** Exporting
*** HTML
Export to HTML:
2020-08-23 03:33:53 +00:00
#+BEGIN_SRC emacs-lisp
(use-package htmlize
:commands htmlize-buffer)
2019-07-08 20:44:54 +00:00
#+END_SRC
Don't put section numbers in front of headers:
2019-07-08 20:44:54 +00:00
#+BEGIN_SRC emacs-lisp
(setq org-export-with-section-numbers nil)
2019-07-08 20:44:54 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
Disable the preamble and postamble:
2019-12-26 14:58:48 +00:00
#+BEGIN_SRC emacs-lisp
(setq org-html-preamble nil
org-html-postamble nil)
2019-12-26 14:58:48 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
Redefine org-html-src-block to wrap code blocks in <pre><code> and language class for use by highlight.js:
2019-11-13 14:48:26 +00:00
#+BEGIN_SRC emacs-lisp
(defun org-html-src-block (src-block _contents info)
"Transcode a SRC-BLOCK element from Org to HTML.
CONTENTS holds the contents of the item. INFO is a plist holding
contextual information."
(if (org-export-read-attribute :attr_html src-block :textarea)
(org-html--textarea-block src-block)
(let* ((lang (org-element-property :language src-block))
(code (org-html-format-code src-block info))
(label (let ((lbl (and (org-element-property :name src-block)
(org-export-get-reference src-block info))))
(if lbl (format " id=\"%s\"" lbl) "")))
(klipsify (and (plist-get info :html-klipsify-src)
(member lang '("javascript" "js"
"ruby" "scheme" "clojure" "php" "html")))))
(if (not lang) (format "<pre><code class=\"example\"%s>\n%s</code></pre>" label code)
(format "<div class=\"org-src-container\">\n%s%s\n</div>"
;; Build caption.
(let ((caption (org-export-get-caption src-block)))
(if (not caption) ""
(let ((listing-number
(format
"<span class=\"listing-number\">%s </span>"
(format
(org-html--translate "Listing %d:" info)
(org-export-get-ordinal
src-block info nil #'org-html--has-caption-p)))))
(format "<label class=\"org-src-name\">%s%s</label>"
listing-number
(org-trim (org-export-data caption info))))))
;; Contents.
(if klipsify
(format "<pre><code class=\"src src-%s\"%s%s>%s</code></pre>"
lang
label
(if (string= lang "html")
" data-editor-type=\"html\""
"")
code)
(format "<pre><code class=\"src %s src-%s\"%s>%s</code></pre>"
lang lang label code)))))))
2019-11-13 14:48:26 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
*** Github-flavored markdown
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(use-package ox-gfm
:after (org)
:hook (org-mode . (lambda () (require 'ox-gfm))))
2019-01-28 02:46:29 +00:00
#+END_SRC
*** Jira
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(use-package ox-jira
:after (org)
2020-11-02 18:20:01 +00:00
:straight (:host github :repo "stig/ox-jira.el" :branch "trunk")
:hook (org-mode . (lambda () (require 'ox-jira))))
2019-01-28 02:46:29 +00:00
#+END_SRC
** org-babel
Literate programming!
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(add-hook 'org-mode-hook
(lambda ()
(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
(python . t)
(shell . t)
(clojure . t)
(lisp . t)
(scheme . t)
(java . t)
(js . t)
(dot . t)
(ditaa . t)
(ledger . t)
(sql . t)
(jq . t)
(restclient . t)
(plantuml . t)))))
2019-01-28 02:46:29 +00:00
#+END_SRC
Get rid of the confirmation prompt:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(setq org-confirm-babel-evaluate nil)
2019-01-28 02:46:29 +00:00
#+END_SRC
Display inline images after executing a source block:
2020-08-23 03:33:53 +00:00
#+BEGIN_SRC emacs-lisp
(add-hook 'org-babel-after-execute-hook
(lambda ()
(org-display-inline-images nil t)
(org-redisplay-inline-images)))
2020-08-23 03:33:53 +00:00
#+END_SRC
2019-01-28 02:46:29 +00:00
Enable async source block execution. Note: this execute the contents of the source block in a separate Emacs process, so blocks that rely on anything defined in init.org or the current Emacs process won't work as expected.
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(use-package ob-async
:straight (ob-async :host github :repo "astahlman/ob-async")
:defer t
:hook (org-mode . (lambda () (require 'ob-async))))
2019-01-28 02:46:29 +00:00
#+END_SRC
Filter out the "u" from unicode results in org tabels returned from Python source blocks:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'ob-python
(defun org-babel-python-table-or-string (results)
"Convert RESULTS into an appropriate elisp value.
If the results look like a list or tuple, then convert them into an
Emacs-lisp table, otherwise return the results as a string."
(let ((res (org-babel-script-escape results)))
(if (listp res)
(mapcar (lambda (el)
(cond
((eq el 'None) org-babel-python-None-to)
((listp el) (-filter (lambda (m) (not (eq m 'u))) el))
(t el)))
res)
res))))
2019-01-28 02:46:29 +00:00
#+END_SRC
** Images
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(setq org-image-actual-width nil)
2019-01-28 02:46:29 +00:00
#+END_SRC
** org-scratch
It's very useful to open a new org buffer for a quick org-babel exploration.
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(defun org-scratch (&optional make-new)
"Switches to an org-mode buffer not associated with any file.
If called with a prefix argument, always creates a new buffer;
otherwise, switches to the existing *org-scratch* buffer if it exists."
(interactive "P")
(let ((org-scratch-buffer-name
(generate-new-buffer-name
"*org-scratch*"
(unless make-new "*org-scratch*"))))
(switch-to-buffer org-scratch-buffer-name)
(org-mode)))
2019-01-28 02:46:29 +00:00
(leader-def-key "os" #'org-scratch)
2019-01-28 02:46:29 +00:00
#+END_SRC
** org-gcal
Integrate Google calendar with org-mode:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(defun get-calendar-file (name)
(org-directory name))
(defun org-gcal-sync-advice (oldfn &rest args)
(deferred:nextc (apply oldfn args)
(lambda (_)
(dolist (entry org-gcal-fetch-file-alist)
(let ((file (cdr entry)))
(with-current-buffer (find-file-noselect file)
(save-buffer)))))))
(use-package org-gcal
:commands (org-gcal-sync
org-gcal-fetch
org-gcal-post-at-point
org-gcal-delete-at-point
org-gcal-request-token)
:config
(advice-add 'org-gcal-sync :around #'org-gcal-sync-advice)
(setq org-gcal-client-id (password-store-get "lola-org-gcal-client-id")
org-gcal-client-secret (password-store-get "lola-org-gcal-client-secret")
org-gcal-fetch-file-alist `(("jeremydormitzer@lola.com" . ,(get-calendar-file "lola-gcal.org"))
("jeremy.dormitzer@gmail.com" . ,(get-calendar-file "personal-gcal.org"))
("lut2o2moohg6qkdsto1qfq7th4@group.calendar.google.com" . ,(get-calendar-file "j-n-gcal.org")))
org-gcal-notify-p nil))
(defun org-agenda-redo-and-fetch-gcal (&optional all)
(interactive "P")
(let ((cb (if all #'org-agenda-redo-all #'org-agenda-redo)))
(deferred:nextc (org-gcal-fetch) cb)))
(with-eval-after-load 'org-agenda
(general-def '(normal motion) org-agenda-mode-map "gR" #'org-agenda-redo-and-fetch-gcal))
2019-01-28 02:46:29 +00:00
#+END_SRC
2019-10-23 20:27:19 +00:00
** Utilities
A function to get the TITLE property of the current org buffer:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(defun org-get-title (&optional contents)
(let ((raw (or contents
(buffer-substring (point-min) (point-max)))))
(with-temp-buffer
(insert raw)
(car
(org-element-map
(org-element-parse-buffer)
'keyword
(lambda (el)
(when (string-match-p "TITLE"
(org-element-property :key el))
(org-element-property :value el))))))))
2019-01-28 02:46:29 +00:00
#+END_SRC
** org-present
An ultra-minimalist presentation mode for Org:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(use-package org-present
:commands (org-present)
2020-08-23 03:33:53 +00:00
:config
(defun org-present-on ()
(org-present-big)
(org-display-inline-images)
(org-present-hide-cursor)
(display-line-numbers-mode -1)
(org-present-read-only))
(defun org-present-off ()
(org-present-small)
(org-present-show-cursor)
(unless org-startup-with-inline-images
(org-remove-inline-images))
(display-line-numbers-mode)
(org-present-read-write))
(add-hook 'org-present-mode-hook #'org-present-on)
(add-hook 'org-present-mode-quit-hook #'org-present-off)
;; Redefine org-present to call org-present-narrow after running hooks
(defun org-present ()
"init."
(interactive)
(setq org-present-mode t)
(org-present-add-overlays)
(run-hooks 'org-present-mode-hook)
(org-present-narrow)
(org-present-run-after-navigate-functions))
:init
(general-def '(normal visual motion emacs) org-present-mode-keymap
"<left>" #'org-present-prev
"<right>" #'org-present-next
"C-k" #'org-present-prev
"C-j" #'org-present-next
"q" #'org-present-quit))
2019-01-28 02:46:29 +00:00
#+END_SRC
** org-cliplink
Intelligently inserts an org-mode link from the clipboard.
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(use-package org-cliplink
:after (org)
:commands (org-cliplink
org-cliplink-clipboard-content)
:general
2020-11-04 16:28:07 +00:00
(org-mode-map "C-c C-S-L" #'org-cliplink))
2019-01-28 02:46:29 +00:00
#+END_SRC
** org-board
[[https://github.com/scallywag/org-board][Org-board]] is a bookmarking/archiving system built on Org mode:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(use-package org-board
:after (org)
:config
2020-01-02 03:03:12 +00:00
;; Org-capture setup
2020-08-23 03:33:53 +00:00
(defvar org-capture-bookmark-last-url nil)
(defvar org-capture-bookmark-last-title nil)
2019-01-28 02:46:29 +00:00
(defun org-capture-bookmark-get-url ()
(let* ((clip (org-cliplink-clipboard-content))
(parsed (url-generic-parse-url clip)))
(if (url-type parsed)
clip
(read-string "Bookmark URL: "))))
2020-08-23 03:33:53 +00:00
(defun org-capture-bookmark-get-title (url)
(or (org-cliplink-retrieve-title-synchronously url)
(read-string "Bookmark title: ")))
2020-08-23 03:33:53 +00:00
(defun bookmark-file (title)
(org-directory (format "%s.org" (org-roam--get-new-id title))))
2020-08-23 03:33:53 +00:00
(defun org-capture-bookmark-file ()
(let* ((url (org-capture-bookmark-get-url))
(title (org-capture-bookmark-get-title url)))
(setq org-capture-bookmark-last-url url)
(setq org-capture-bookmark-last-title title)
(bookmark-file title)))
2020-08-23 03:33:53 +00:00
(defun org-capture-bookmark-link ()
(format "[[%s][%s]]"
org-capture-bookmark-last-url
org-capture-bookmark-last-title))
2019-01-28 02:46:29 +00:00
(defun org-capture-bookmark-title ()
org-capture-bookmark-last-title)
2019-01-28 02:46:29 +00:00
(defun save-bookmark (url)
(save-excursion
(goto-char (point-min))
(when (search-forward "* Bookmark" nil t)
(org-board-new url)
(wallabag-add-entry url
(cl-function
(lambda (&key data &allow-other-keys)
(let ((entry-url (format "%s/view/%s"
wallabag-base-url
(alist-get 'id data))))
(message "Added bookmark to Wallabag: %s" entry-url))))))))
2019-01-28 02:46:29 +00:00
(defun org-capture-bookmark-after-finalize ()
"Runs `org-board-new' on the captured entry.
Also saves to Wallabag."
(let ((success (not org-note-abort))
(key (plist-get org-capture-plist :key))
(desc (plist-get org-capture-plist :description)))
(when (and success
(equal key "b")
(equal desc "Bookmark")
org-capture-bookmark-last-url)
(save-bookmark org-capture-bookmark-last-url)
(setq org-capture-bookmark-last-url nil)
(setq org-capture-bookmark-last-title nil))))
2019-01-28 02:46:29 +00:00
(add-hook 'org-capture-prepare-finalize-hook
#'org-capture-bookmark-after-finalize)
2019-01-28 02:46:29 +00:00
(add-to-list 'org-capture-templates
'("b" "Bookmark" plain
(file org-capture-bookmark-file)
"#+TITLE: %(org-capture-bookmark-title)\n\n- tags :: [[file:deft/bookmarks.org][Bookmarks]]\n- source :: %(org-capture-bookmark-link)\n%?\n* Bookmark"))
;; Org-protocol setup
(defun make-org-protocol-bookmark (url title)
(with-temp-buffer
(let ((filename (bookmark-file title)))
(save-excursion
(insert (concat (format "#+TITLE: %s\n\n" title)
"- tags :: [[file:deft/bookmarks.org][Bookmarks]]\n"
(format "- source :: [[%s][%s]]\n\n" url title)
"* Bookmark"))
(write-file filename)
(save-bookmark url)
(save-buffer)))))
(defun bookmark-via-org-protocol (url)
(org-cliplink-retrieve-title (url-unhex-string url) #'make-org-protocol-bookmark))
(with-eval-after-load 'org-protocol
(add-to-list 'org-protocol-protocol-alist
'("Bookmark"
:protocol "bookmark"
:function bookmark-via-org-protocol
:kill-client t)))
(add-to-list 'org-board-wget-switches "--recursive")
(add-to-list 'org-board-wget-switches "--level=1")
(add-to-list 'org-board-wget-switches "--span-hosts")
;; Use w3m instead of eww to open org-board archived links
(advice-add 'org-board-open-with :around
(lambda (oldfn filename-string arg &rest args)
(cond
((not (file-exists-p filename-string)) 1)
((and filename-string
(or (and arg (eq org-board-default-browser 'system))
(and (not arg) (eq org-board-default-browser 'eww))))
(let ((filename (concat "file://"
(s-chop-prefix "file://"
filename-string))))
(w3m filename t)
0))
(:else (apply oldfn filename-string arg args)))))
:general
(org-mode-map "C-c b" org-board-keymap))
2019-01-28 02:46:29 +00:00
#+END_SRC
** org-rifle
Quickly find stuff in Org files:
#+BEGIN_SRC emacs-lisp
(use-package helm-org-rifle
:commands (helm-org-rifle
helm-org-rifle-agenda-files
helm-org-rifle-current-buffer
helm-org-rifle-directories
helm-org-rifle-files
helm-org-rifle-org-directory
helm-org-rifle-occur
helm-org-rifle-occur-agenda-files
helm-org-rifle-occur-current-buffer
helm-org-rifle-occur-directories
helm-org-rifle-occur-files
helm-org-rifle-occur-org-directory)
:init
(defvar helm-org-rifle-commands-map (make-sparse-keymap))
(general-def helm-org-rifle-commands-map
"r" #'helm-org-rifle
"a" #'helm-org-rifle-agenda-files
"b" #'helm-org-rifle-current-buffer
"d" #'helm-org-rifle-directories
"f" #'helm-org-rifle-files
"o" #'helm-org-rifle-org-directory
"R" #'helm-org-rifle-occur
"A" #'helm-org-rifle-occur-agenda-files
"B" #'helm-org-rifle-occur-current-buffer
"D" #'helm-org-rifle-occur-directories
"F" #'helm-org-rifle-occur-files
"O" #'helm-org-rifle-occur-org-directory)
(leader-def-key "or" helm-org-rifle-commands-map)
(jdormit/define-prefix "or" "org-rifle")
:config
(defun around-helm-org-rifle (oldfn &rest args)
(if (and (boundp 'centaur-tabs-mode) centaur-tabs-mode)
(progn
(centaur-tabs-mode -1)
(apply oldfn args)
(centaur-tabs-mode 1))
(apply oldfn args)))
(advice-add 'helm-org-rifle :around #'around-helm-org-rifle))
#+END_SRC
2020-01-02 03:03:12 +00:00
** Org Noter
[[https://github.com/weirdNox/org-noter][Org Noter]] lets me take org-mode notes on PDFs, epubs, and DocView files:
2020-05-22 22:10:37 +00:00
#+BEGIN_SRC emacs-lisp
(use-package org-noter
:commands org-noter
:general
(normal org-noter-doc-mode-map
"i" #'org-noter-insert-note
"q" #'org-noter-kill-session
"C-M-n" #'org-noter-sync-next-note
"C-M-p" #'org-noter-sync-prev-note
"M-." #'org-noter-sync-current-page-or-chapter
"M-i" #'org-noter-insert-precise-note
"M-n" #'org-noter-sync-next-page-or-chapter
"M-p" #'org-noter-sync-prev-page-or-chapter
"C-M-." #'org-noter-sync-current-note))
2020-05-22 22:10:37 +00:00
#+END_SRC
** Org Roam
[[https://org-roam.readthedocs.io/en/develop/][Org-roam]] is another backlink package for org-mode:
2020-07-21 13:40:33 +00:00
#+BEGIN_SRC emacs-lisp
(use-package org-roam
:straight (:host github :repo "jethrokuan/org-roam")
:commands
(org-roam
org-roam-today
org-roam-find-file
org-roam-insert
org-roam-show-graph
org-roam--get-new-id)
:custom
(org-roam-directory (org-directory))
:init
(defvar org-roam-map (make-sparse-keymap))
(jdormit/define-prefix "on" "org-roam")
(which-key-add-key-based-replacements "C-c n" "org-roam")
(which-key-add-major-mode-key-based-replacements
'org-mode "gn" "org-roam")
(with-eval-after-load 'org
(org-roam-mode))
:config
(add-hook 'org-roam-backlinks-mode-hook #'olivetti-mode)
:general
(leader-map "fo" #'org-roam-find-file
"of" #'org-roam-find-file
"on" org-roam-map)
(org-roam-map "l" #'org-roam
"t" #'org-roam-today
"f" #'org-roam-find-file
"i" #'org-roam-insert
"g" #'org-roam-show-graph)
(normal org-mode-map
"gr" org-roam-map)
(normal org-roam-backlinks-mode-map
"<return>" #'org-open-at-point
"q" #'quit-window)
("C-c n" org-roam-map))
2020-07-21 13:40:33 +00:00
#+END_SRC
** org-journal
[[https://github.com/bastibe/org-journal][org-journal]] is a package that provides some convenience functions
around keeping a daily Org-mode journal. I also set it up so it plays
nice with Org-roam:
2020-01-07 16:14:34 +00:00
#+BEGIN_SRC emacs-lisp
(use-package org-journal
:commands (org-journal-today
org-journal-new-entry)
:init
(defun org-journal-file-header-func (time)
(format "#+TITLE: %s"
(format-time-string "%Y-%m-%d" time)))
(defun org-journal-today ()
(interactive)
(org-journal-new-entry t))
(defun org-journal-capture-func ()
(org-journal-new-entry t)
(goto-char (point-min))
;; Account for the #+TITLE
(forward-line))
(jdormit/define-prefix "oj" "org-journal")
(leader-def-key "ojn" #'org-journal-new-entry)
(leader-def-key "ojt" #'org-journal-today)
:config
(add-to-list 'org-capture-templates
'("j" "Journal entry" entry (function org-journal-capture-func)
"* %(format-time-string org-journal-time-format)\n%?"))
:custom
(org-journal-file-type 'daily)
(org-journal-dir (org-directory))
(org-journal-file-format "%Y-%m-%d.org")
(org-journal-file-header 'org-journal-file-header-func)
(org-journal-carryover-items "")
:general
(org-roam-map "t" 'org-journal-today))
2020-01-07 16:14:34 +00:00
#+END_SRC
** org-super-agenda
2020-01-02 04:04:19 +00:00
#+BEGIN_SRC emacs-lisp
(use-package org-super-agenda
:after (org)
:init
(setq org-super-agenda-groups
'((:name "In progress"
:todo "IN PROGRESS")
(:name "Lola"
:tag "@lola")
(:name "unifyDB"
:tag "@unifydb")
(:name "Personal"
:tag "@personal")))
(org-super-agenda-mode)
:config
(setq org-super-agenda-header-map (make-sparse-keymap)))
2020-01-02 04:04:19 +00:00
#+END_SRC
** Org-Jira
Jira in Emacs:
2020-05-06 04:00:46 +00:00
#+BEGIN_SRC emacs-lisp
(use-package org-jira
:after (org)
:init
(setq jiralib-url "https://lola.atlassian.net"
org-jira-working-dir (org-directory "jira")
org-jira-jira-status-to-org-keyword-alist '(("To Do" . "TODO")
("Blocked" . "BLOCKED")
("In Progress" . "IN PROGRESS")
("Code Review" . "IN PROGRESS")
("Awaiting Release" . "IN PROGRESS")
("Done" . "DONE")
("Won't Fix" . "CANCELLED")))
(add-to-list 'org-agenda-files org-jira-working-dir)
(add-to-list 'org-super-agenda-groups
`(:name "Jira" :file-path ,org-jira-working-dir)
t))
2020-05-06 04:00:46 +00:00
#+END_SRC
* Doom themes
2020-01-07 17:45:23 +00:00
#+BEGIN_SRC emacs-lisp
(use-package doom-themes)
2020-01-07 17:45:23 +00:00
(use-package doom-modeline)
(doom-modeline-mode 1)
#+END_SRC
2020-01-02 03:03:12 +00:00
* Ewal theme
#+BEGIN_SRC emacs-lisp
(use-package doom-ewal-themes
:straight (doom-ewal-themes
:host github
:repo "jdormit/doom-ewal-themes"
:files ("themes" :defaults)))
#+END_SRC
* Customization File
I don't want anything to write to my init.el, so save customizations in a separate file:
#+BEGIN_SRC emacs-lisp
(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(load custom-file t)
#+END_SRC
* Path
`exec-path-from-shell` uses Bash to set MANPATH, PATH, and exec-path from those defined in the user's shell config. This won't work on Windows.
2020-01-07 17:45:23 +00:00
#+BEGIN_SRC emacs-lisp
(use-package exec-path-from-shell
:if (memq window-system '(mac ns x))
:config
(setq exec-path-from-shell-variables '("PATH" "MANPATH" "LEDGER_FILE" "LOLA_HOME"
"MODELS_HOME" "LOLA_TRAVEL_SERVICE_HOME" "WORKON_HOME"
"PIPENV_VERBOSITY" "PIPENV_DONT_LOAD_ENV"
2021-01-15 20:57:56 +00:00
"PIPENV_MAX_DEPTH" "PYENV_ROOT" "KOPS_STATE_STORE"
"PLAID_CLIENT_ID" "PLAID_SECRET" "PLAID_ENVIRONMENT")
exec-path-from-shell-check-startup-files nil)
(exec-path-from-shell-initialize))
2020-08-23 03:33:53 +00:00
#+END_SRC
2020-01-07 17:45:23 +00:00
* Autocompletion
There seems to be [[https://github.com/company-mode/company-mode/issues/68][some contention]] about whether autocomplete or company are better autocomplete packages. I'm going with company for now because the maintainer seems nicer...
2020-08-23 03:33:53 +00:00
#+BEGIN_SRC emacs-lisp
(use-package company
:commands (company-complete
company-mode)
:hook ((prog-mode . company-mode))
:config
(setq company-idle-delay 0.3))
(general-def "C-M-i" #'company-complete)
(general-def "M-<tab>" #'company-complete)
2020-01-07 17:45:23 +00:00
#+END_SRC
* Multiple cursors
2019-11-07 21:28:35 +00:00
#+BEGIN_SRC emacs-lisp
(use-package evil-mc
:defer 0
:config
(global-evil-mc-mode)
(general-def '(normal visual)
"gs" evil-mc-cursors-map
"M-n" #'evil-mc-make-and-goto-next-cursor
"M-p" #'evil-mc-make-and-goto-prev-cursor
"C-n" #'evil-mc-make-and-goto-next-match
"C-t" #'evil-mc-skip-and-goto-next-match
"C-p" #'evil-mc-make-and-goto-prev-match))
2019-11-07 21:28:35 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
2020-06-25 18:01:21 +00:00
#+BEGIN_SRC emacs-lisp
(use-package evil-multiedit
:defer 0
:config
(evil-multiedit-default-keybinds))
2020-06-25 18:01:21 +00:00
#+END_SRC
* Transient
A framework for creating Magit-style popups:
2019-01-29 14:51:56 +00:00
#+BEGIN_SRC emacs-lisp
(use-package transient
:commands (define-transient-command))
2019-01-29 14:51:56 +00:00
#+END_SRC
* Magit
Magit is objectively the best Git interface.
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(use-package magit
:commands (magit-status
magit-blame
magit-find-file
magit-name-local-branch))
2019-01-28 02:46:29 +00:00
#+END_SRC
#+BEGIN_SRC emacs-lisp
(jdormit/define-prefix "g" "git")
2021-01-15 22:25:20 +00:00
(leader-def-key
"gs" #'magit-status
"gg" #'magit-file-dispatch
"gd" #'magit-dispatch
"gf" #'magit-find-file)
2019-01-28 02:46:29 +00:00
#+END_SRC
** Forge
[[https://github.com/magit/forge][Forge]] is an extension for Magit that lets it interact with code forges (e.g. GitHub).
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(use-package forge
:after (magit)
:config
(add-to-list 'forge-alist '("git.jeremydormitzer.com"
"git.jeremydormitzer.com/api/v1"
"git.jeremydormitzer.com"
forge-gitea-repository))
(with-eval-after-load 'evil-magit
(general-def '(normal motion) magit-mode-map
"yu" #'forge-copy-url-at-point-as-kill))
:general
((normal motion visual) forge-topic-list-mode-map
"y" #'forge-copy-url-at-point-as-kill
"q" #'quit-window)
:custom
(forge-owned-accounts '((jdormit . (remote-name "jdormit")))))
2019-01-28 02:46:29 +00:00
#+END_SRC
** evil-magit
Evil keybindings for magit!
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(use-package evil-magit
:after (evil magit)
:config
(with-eval-after-load 'magit
(require 'evil-magit))
:general
('normal magit-mode-map "SPC" leader-map))
2019-01-28 02:46:29 +00:00
#+END_SRC
** Transient
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(setq transient-default-level 7)
2019-01-28 02:46:29 +00:00
#+END_SRC
* git-link
Open files in Git forges (GitHub, GitLab, etc.):
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(use-package git-link
:commands (git-link)
:init
(defun git-link-copy ()
(interactive)
(let ((git-link-open-in-browser nil))
(call-interactively 'git-link)))
(leader-def-key "gl" #'git-link)
(leader-def-key "gy" #'git-link-copy)
:custom
(git-link-open-in-browser t)
(git-link-remote-alist '(("git.sr.ht" git-link-sourcehut)
("github" git-link-github)
("bitbucket" git-link-bitbucket)
("gitorious" git-link-gitorious)
("gitlab" git-link-gitlab)
("visualstudio\\|azure" git-link-azure)
("git.jeremydormitzer.com" git-link-bitbucket))))
2019-01-28 02:46:29 +00:00
#+END_SRC
* with-editor
A utility from the author of Magit to run shell commands using the current Emacs instance as $EDITOR.
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(shell-command-with-editor-mode)
(add-hook 'shell-mode-hook #'with-editor-export-editor)
(add-hook 'term-exec-hook #'with-editor-export-editor)
(add-hook 'eshell-mode-hook #'with-editor-export-editor)
2019-01-28 02:46:29 +00:00
#+END_SRC
* Password Store
Interfacing with Pass, the "standard Unix password manager". This should also be loaded before `exec-path-from-shell`.
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(defun password-store-synchronize ()
(interactive)
(with-editor-async-shell-command "pass git pull && pass git push"))
(use-package password-store
:defer t
:if (executable-find "pass")
:config
(setq password-store-password-length 20)
:init
(leader-def-key "P" 'password-store-copy)
(epa-file-enable))
(use-package pass
:if (executable-find "pass")
:commands pass
:general
2020-12-28 16:46:42 +00:00
(normal pass-mode-map "S" #'password-store-synchronize))
(leader-def-key "ap" #'pass)
(setq auth-sources '("~/.authinfo" password-store))
2019-01-28 02:46:29 +00:00
#+END_SRC
2020-08-23 03:33:53 +00:00
* Init File
A function to reload my init file. It reloads the major mode after the init file is loaded to rebind keymappings.
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(defun reload-init-file ()
(interactive)
(load-file "~/.emacs.d/init.el")
2020-08-23 03:33:53 +00:00
(funcall major-mode))
#+END_SRC
2020-01-07 15:52:05 +00:00
2020-08-23 03:33:53 +00:00
And another one to edit it:
#+BEGIN_SRC emacs-lisp
(defun find-init-file ()
(interactive)
(find-file "~/.emacs.d/init.org"))
#+END_SRC
2020-01-07 15:52:05 +00:00
2020-08-23 03:33:53 +00:00
* Keybindings
These are general keybindings; those specific to certain packages are in the sections for those packages.
2020-01-07 15:52:05 +00:00
2019-01-28 02:46:29 +00:00
2020-08-23 03:33:53 +00:00
In some modes I want vanilla Emacs bindings:
#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'evil
(add-to-list 'evil-emacs-state-modes 'benchmark-init/tabulated-mode)
(add-to-list 'evil-emacs-state-modes 'benchmark-init/tree-mode)
(add-to-list 'evil-emacs-state-modes 'cider-stacktrace-mode)
(add-to-list 'evil-emacs-state-modes 'geiser-debug-mode)
(add-to-list 'evil-emacs-state-modes 'undo-tree-visualizer-mode)
(add-to-list 'evil-emacs-state-modes 'makey-key-mode)
(add-to-list 'evil-emacs-state-modes 'term-mode))
#+END_SRC
2019-01-28 02:46:29 +00:00
2020-08-23 03:33:53 +00:00
** Visual line navigation
#+BEGIN_SRC emacs-lisp
(general-def 'motion "j" #'evil-next-visual-line)
(general-def 'motion "k" #'evil-previous-visual-line)
(general-def 'motion "j" #'evil-next-visual-line)
(general-def 'motion "k" #'evil-previous-visual-line)
2019-01-28 02:46:29 +00:00
#+END_SRC
2020-08-23 03:33:53 +00:00
** M-x
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(leader-def-key "SPC" 'execute-extended-command)
#+END_SRC
2020-08-23 03:33:53 +00:00
** Eval-ing
#+BEGIN_SRC emacs-lisp
(leader-def-key ":" #'eval-expression)
#+END_SRC
2020-08-23 03:33:53 +00:00
** Init file commands
#+BEGIN_SRC emacs-lisp
(jdormit/define-prefix "." "dotfile")
(leader-def-key ".r" 'reload-init-file)
(leader-def-key ".f" 'find-init-file)
#+END_SRC
2020-08-23 03:33:53 +00:00
** Commands about files
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(jdormit/define-prefix "f" "files")
(leader-def-key "ff" 'find-file)
(leader-def-key "fd" 'dired)
(leader-def-key "fs" 'sudo-find-file)
(leader-def-key "ft" 'auto-revert-tail-mode)
(leader-def-key "fp" 'find-file-at-point)
(general-def '(normal motion visual) "gf" 'find-file-at-point)
2019-01-28 02:46:29 +00:00
#+END_SRC
2020-08-23 03:33:53 +00:00
** Window commands
2019-08-13 13:50:47 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(jdormit/define-prefix "w" "window")
(leader-def-key "w/" 'split-window-right)
(leader-def-key "w-" 'split-window-below)
(leader-def-key "wm" 'delete-other-windows)
(leader-def-key "wd" 'delete-window)
(leader-def-key "ws" 'window-swap-states)
2019-08-13 13:50:47 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
2020-08-23 03:33:53 +00:00
** Buffer commands
A function to switch to previous buffer from [[http://emacsredux.com/blog/2013/04/28/switch-to-previous-buffer/][this blog post]]:
#+BEGIN_SRC emacs-lisp
(defun switch-to-previous-buffer ()
"Switch to previously open buffer.
Repeated invocations toggle between the two most recently open buffers."
(interactive)
(switch-to-buffer (other-buffer (current-buffer) 1)))
(leader-def-key "TAB" 'switch-to-previous-buffer)
#+END_SRC
2019-01-28 02:46:29 +00:00
2020-08-23 03:33:53 +00:00
A function to kill all buffers except the current one from [[https://www.emacswiki.org/emacs/KillingBuffers#toc2][EmacsWiki]]:
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(defun kill-other-buffers ()
"Kill all other buffers."
(interactive)
(mapc 'kill-buffer (delq (current-buffer) (buffer-list))))
#+END_SRC
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(jdormit/define-prefix "b" "buffer")
(leader-def-key "bb" #'switch-to-buffer)
(leader-def-key "bn" #'next-buffer)
(leader-def-key "bp" #'previous-buffer)
(leader-def-key "bd" #'kill-buffer)
(leader-def-key "bm" #'kill-other-buffers)
(leader-def-key "br" #'rename-buffer)
2019-01-28 02:46:29 +00:00
#+END_SRC
2020-08-23 03:33:53 +00:00
** Frame commands
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(jdormit/define-prefix "F" "frame")
(leader-def-key "Fn" #'make-frame-command)
(leader-def-key "Fo" #'other-frame)
(leader-def-key "Fm" #'delete-other-frames)
(leader-def-key "Fd" #'delete-frame)
2019-01-28 02:46:29 +00:00
#+END_SRC
2020-08-23 03:33:53 +00:00
** Running shell commands
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(leader-def-key "!" 'shell-command)
(leader-def-key "|" 'shell-command-on-region)
2019-01-28 02:46:29 +00:00
#+END_SRC
2020-08-23 03:33:53 +00:00
** Toggles
Like in Spacemacs, put all toggle commands behind a prefix:
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(jdormit/define-prefix "t" "toggle")
#+END_SRC
2020-08-23 03:33:53 +00:00
Toggles about line truncation:
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(leader-def-key "tt" 'toggle-truncate-lines)
(leader-def-key "tT" 'visual-line-mode)
(leader-def-key "tf" 'auto-fill-mode)
#+END_SRC
2020-08-23 03:33:53 +00:00
Toggle lisp debugging:
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(leader-def-key "td" 'toggle-debug-on-error)
#+END_SRC
2020-08-23 03:33:53 +00:00
** Shells/REPLs
Emacs has a shell for every mood!
2020-07-25 20:41:26 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(jdormit/define-prefix "s" "shells/REPLs")
(leader-def-key "ss" 'shell)
(leader-def-key "si" 'ielm)
(leader-def-key "se" 'eshell)
(leader-def-key "sa" 'ansi-term)
2020-07-25 20:41:26 +00:00
#+END_SRC
2020-08-23 03:33:53 +00:00
** Applications
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(jdormit/define-prefix "a" "applications")
2019-01-28 02:46:29 +00:00
#+END_SRC
2020-08-23 03:33:53 +00:00
** Help Buffers
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(general-def 'motion help-mode-map "TAB" #'forward-button)
2019-01-28 02:46:29 +00:00
#+END_SRC
2020-08-23 03:33:53 +00:00
** Code commands
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(jdormit/define-prefix "c" "code")
(leader-def-key "cd" #'xref-find-definitions)
(leader-def-key "cr" #'xref-find-references)
(leader-def-key "ca" #'xref-find-apropos)
(general-def 'normal "M-." #'xref-find-definitions)
2019-01-28 02:46:29 +00:00
#+END_SRC
2019-10-23 20:27:19 +00:00
2020-08-23 03:33:53 +00:00
** Process list
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(general-def '(normal motion visual) process-menu-mode-map
"d" #'process-menu-delete-process)
2019-01-28 02:46:29 +00:00
#+END_SRC
2020-08-23 03:33:53 +00:00
** Registers
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(jdormit/define-prefix "r" "registers")
(leader-def-key "r" ctl-x-r-map)
2019-01-28 02:46:29 +00:00
#+END_SRC
2020-08-23 03:33:53 +00:00
* Evil-numbers
Handy functions to increment/decrement numbers at point:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(use-package evil-numbers
:straight (:host github :repo "janpath/evil-numbers")
:general
((normal visual motion) "g+" 'evil-numbers/inc-at-pt)
((normal visual motion) "g-" 'evil-numbers/dec-at-pt)
((normal visual motion) "g C-+" 'evil-numbers/inc-at-pt-incremental)
((normal visual motion) "g C--" 'evil-numbers/dec-at-pt-incremental))
2019-01-28 02:46:29 +00:00
#+END_SRC
2020-08-23 03:33:53 +00:00
* Winner
Winner is a minor mode that keeps an undo/redo history of the window configuration:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(winner-mode 1)
(leader-def-key "wn" #'winner-redo)
(leader-def-key "wp" #'winner-undo)
2019-01-28 02:46:29 +00:00
#+END_SRC
2020-08-23 03:33:53 +00:00
* Buffer move
A handy package to shift buffers between open windows:
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(use-package buffer-move
:general
("C-S-j" #'buf-move-down)
("C-S-k" #'buf-move-up)
("C-S-h" #'buf-move-left)
("C-S-l" #'buf-move-right))
#+END_SRC
2020-08-23 03:33:53 +00:00
* Info
2020-01-07 17:45:23 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(use-package info
:init
(defhydra hydra-info (:color blue
:hint nil)
2020-01-07 17:45:23 +00:00
"
2020-08-23 03:33:53 +00:00
Info-mode:
^^_]_ forward (next logical node) ^^_l_ast (←) _u_p (↑) _f_ollow reference _T_OC
^^_[_ backward (prev logical node) ^^_r_eturn (→) _m_enu (↓) (C-u for new window) _i_ndex _d_irectory
^^_n_ext (same level only) ^^_H_istory _g_oto (C-u for new window) _,_ next index item _c_opy node name
^^_p_rev (same level only) _<_/_t_op _b_eginning of buffer virtual _I_ndex _C_lone buffer
regex _s_earch (_S_ case sensitive) ^^_>_ final _e_nd of buffer ^^ _a_propos
_1_ .. _9_ Pick first .. ninth item in the node's menu.
2020-01-07 17:45:23 +00:00
"
2020-08-23 03:33:53 +00:00
("]" Info-forward-node)
("[" Info-backward-node)
("n" Info-next)
("p" Info-prev)
("s" Info-search)
("S" Info-search-case-sensitively)
2020-01-07 17:45:23 +00:00
2020-08-23 03:33:53 +00:00
("l" Info-history-back)
("r" Info-history-forward)
("H" Info-history)
("t" Info-top-node)
("<" Info-top-node)
(">" Info-final-node)
("u" Info-up)
("^" Info-up)
("m" Info-menu)
("g" Info-goto-node)
("b" beginning-of-buffer)
("e" end-of-buffer)
("f" Info-follow-reference)
("i" Info-index)
("," Info-index-next)
("I" Info-virtual-index)
("T" Info-toc)
("d" Info-directory)
("c" Info-copy-current-node-name)
("C" clone-buffer)
("a" info-apropos)
("1" Info-nth-menu-item)
("2" Info-nth-menu-item)
("3" Info-nth-menu-item)
("4" Info-nth-menu-item)
("5" Info-nth-menu-item)
("6" Info-nth-menu-item)
("7" Info-nth-menu-item)
("8" Info-nth-menu-item)
("9" Info-nth-menu-item)
2019-01-28 02:46:29 +00:00
2020-08-23 03:33:53 +00:00
("?" Info-summary "Info summary")
("h" Info-help "Info help")
("q" Info-exit "Info exit")
("C-g" nil "cancel" :color blue))
:general
(Info-mode-map "C-/" 'hydra-info/body))
2019-01-28 02:46:29 +00:00
#+END_SRC
2020-08-23 03:33:53 +00:00
* xref
After I select an xref reference, I want the xref buffer closed:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(defun xref-goto-xref-and-quit ()
(interactive)
(xref-goto-xref t))
(general-def 'normal xref--xref-buffer-mode-map "RET"
#'xref-goto-xref-and-quit :keymaps 'override)
2019-01-28 02:46:29 +00:00
#+END_SRC
2020-08-23 03:33:53 +00:00
Don't prompt for an identifier for these xref commands:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(setq xref-prompt-for-identifier
'(not xref-find-definitions
xref-find-definitions-other-window
xref-find-definitions-other-frame
xref-find-references))
2019-01-28 02:46:29 +00:00
#+END_SRC
2020-08-23 03:33:53 +00:00
Some keybindings:
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(general-def "M-r" #'xref-find-references)
#+END_SRC
2020-08-23 03:33:53 +00:00
* IBuffer
2020-02-25 14:48:27 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(use-package ibuffer
:straight (:type built-in)
2020-02-25 14:48:27 +00:00
:init
2020-08-23 03:33:53 +00:00
(defhydra hydra-ibuffer-main (:color pink :hint nil)
"
^Navigation^ | ^Mark^ | ^Actions^ | ^View^
-^----------^-+-^----^--------+-^-------^--------+-^----^-------
_k_: ʌ | _m_: mark | _D_: delete | _g_: refresh
_RET_: visit | _u_: unmark | _S_: save | _s_: sort
_j_: v | _*_: specific | _a_: all actions | _/_: filter
-^----------^-+-^----^--------+-^-------^--------+-^----^-------
"
("j" ibuffer-forward-line)
("RET" ibuffer-visit-buffer :color blue)
("k" ibuffer-backward-line)
2020-02-25 14:48:27 +00:00
2020-08-23 03:33:53 +00:00
("m" ibuffer-mark-forward)
("u" ibuffer-unmark-forward)
("*" hydra-ibuffer-mark/body :color blue)
("D" ibuffer-do-delete)
("S" ibuffer-do-save)
("a" hydra-ibuffer-action/body :color blue)
("g" ibuffer-update)
("s" hydra-ibuffer-sort/body :color blue)
("/" hydra-ibuffer-filter/body :color blue)
("o" ibuffer-visit-buffer-other-window "other window" :color blue)
("q" quit-window "quit ibuffer" :color blue)
("." nil "toggle hydra" :color blue))
(defhydra hydra-ibuffer-mark (:color teal
:columns 5
:after-exit (hydra-ibuffer-main/body))
"Mark"
("*" ibuffer-unmark-all "unmark all")
("M" ibuffer-mark-by-mode "mode")
("m" ibuffer-mark-modified-buffers "modified")
("u" ibuffer-mark-unsaved-buffers "unsaved")
("s" ibuffer-mark-special-buffers "special")
("r" ibuffer-mark-read-only-buffers "read-only")
("/" ibuffer-mark-dired-buffers "dired")
("e" ibuffer-mark-dissociated-buffers "dissociated")
("h" ibuffer-mark-help-buffers "help")
("z" ibuffer-mark-compressed-file-buffers "compressed")
("b" hydra-ibuffer-main/body "back" :color blue))
(defhydra hydra-ibuffer-action (:color teal
:columns 4
:after-exit
(if (eq major-mode 'ibuffer-mode)
(hydra-ibuffer-main/body)))
"Action"
("A" ibuffer-do-view "view")
("E" ibuffer-do-eval "eval")
("F" ibuffer-do-shell-command-file "shell-command-file")
("I" ibuffer-do-query-replace-regexp "query-replace-regexp")
("H" ibuffer-do-view-other-frame "view-other-frame")
("N" ibuffer-do-shell-command-pipe-replace "shell-cmd-pipe-replace")
("M" ibuffer-do-toggle-modified "toggle-modified")
("O" ibuffer-do-occur "occur")
("P" ibuffer-do-print "print")
("Q" ibuffer-do-query-replace "query-replace")
("R" ibuffer-do-rename-uniquely "rename-uniquely")
("T" ibuffer-do-toggle-read-only "toggle-read-only")
("U" ibuffer-do-replace-regexp "replace-regexp")
("V" ibuffer-do-revert "revert")
("W" ibuffer-do-view-and-eval "view-and-eval")
("X" ibuffer-do-shell-command-pipe "shell-command-pipe")
("b" nil "back"))
(defhydra hydra-ibuffer-sort (:color amaranth :columns 3)
"Sort"
("i" ibuffer-invert-sorting "invert")
("a" ibuffer-do-sort-by-alphabetic "alphabetic")
("v" ibuffer-do-sort-by-recency "recently used")
("s" ibuffer-do-sort-by-size "size")
("f" ibuffer-do-sort-by-filename/process "filename")
("m" ibuffer-do-sort-by-major-mode "mode")
("b" hydra-ibuffer-main/body "back" :color blue))
(defhydra hydra-ibuffer-filter (:color amaranth :columns 4)
"Filter"
("m" ibuffer-filter-by-used-mode "mode")
("M" ibuffer-filter-by-derived-mode "derived mode")
("n" ibuffer-filter-by-name "name")
("c" ibuffer-filter-by-content "content")
("e" ibuffer-filter-by-predicate "predicate")
("f" ibuffer-filter-by-filename "filename")
(">" ibuffer-filter-by-size-gt "size")
("<" ibuffer-filter-by-size-lt "size")
("/" ibuffer-filter-disable "disable")
("b" hydra-ibuffer-main/body "back" :color blue))
:general
((normal motion visual insert emacs) ibuffer-mode-map "." 'hydra-ibuffer-main/body))
2019-01-28 02:46:29 +00:00
#+END_SRC
2020-08-23 03:33:53 +00:00
* Speedbar
Speedbar is cool but having it open in a separate frame is annoying. This makes it open in a side window in the same frame:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(use-package sr-speedbar
:commands (sr-speedbar-toggle
sr-speedbar-open
sr-speedbar-select-window
sr-speedbar-exist-p)
:general
(speedbar-mode-map "q" #'sr-speedbar-close))
(defun switch-to-speedbar ()
(interactive)
(unless (sr-speedbar-exist-p)
(sr-speedbar-open))
(sr-speedbar-select-window))
(leader-def-key "S" #'switch-to-speedbar)
2019-01-28 02:46:29 +00:00
#+END_SRC
2020-08-23 03:33:53 +00:00
* Trailing whitespace
[[https://github.com/lewang/ws-butler][ws-butler]] deletes trailing whitespace on lines that you touch, but
leaves other lines alone:
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(use-package ws-butler
:straight (ws-butler :host github :repo "lewang/ws-butler")
:hook ((prog-mode . ws-butler-mode)))
#+END_SRC
2020-08-23 03:33:53 +00:00
* Whitespace Visualation
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(setq whitespace-line-column 80
whitespace-style '(face lines-tail))
(leader-def-key "tw" #'whitespace-mode)
#+END_SRC
2020-08-23 03:33:53 +00:00
* Line Numbers
Toggle line numbers:
2019-02-07 21:53:52 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(setq display-line-numbers-type 'visual)
(leader-def-key "tn" 'display-line-numbers-mode)
2019-02-07 21:53:52 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
2020-08-23 03:33:53 +00:00
Toggle line numbering mode (normal or relative):
2019-11-07 17:05:44 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(defun toggle-line-number-mode ()
(interactive)
(when display-line-numbers
(if (eq display-line-numbers 'visual)
(progn
(setq display-line-numbers t)
(setq display-line-numbers-type t))
(progn
(setq display-line-numbers 'visual)
(setq display-line-numbers-type 'visual)))))
2019-11-07 17:05:44 +00:00
2020-08-23 03:33:53 +00:00
(leader-def-key "tr" #'toggle-line-number-mode)
2019-11-07 17:05:44 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
2020-08-23 03:33:53 +00:00
Display line numbers by default in code and org-mode buffers:
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(add-hook 'prog-mode-hook #'display-line-numbers-mode)
(add-hook 'org-mode-hook #'display-line-numbers-mode)
#+END_SRC
2020-06-22 14:45:27 +00:00
2020-08-23 03:33:53 +00:00
* Amx
A better M-x.
#+BEGIN_SRC emacs-lisp
(use-package amx
:config
2020-08-23 03:33:53 +00:00
(amx-mode))
#+END_SRC
2020-01-02 03:03:12 +00:00
2020-08-23 03:33:53 +00:00
* Olivetti Mode
Olivetti is a minor mode for a nice writing environment.
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(use-package olivetti
:config
(setq-default olivetti-body-width 100)
(setq olivetti-body-width 100)
:commands olivetti-mode)
(leader-def-key "to" 'olivetti-mode)
#+END_SRC
2020-08-23 03:33:53 +00:00
* Winum
This package includes functions to switch windows by number.
2020-05-05 20:54:22 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(defun winum-assign-0-to-dired-sidebar ()
(when (equal major-mode 'dired-sidebar-mode) 10))
(use-package winum
2020-05-05 20:54:22 +00:00
:config
2020-08-23 03:33:53 +00:00
(winum-mode)
(add-to-list 'winum-assign-functions #'winum-assign-0-to-dired-sidebar)
(leader-def-key "0" 'winum-select-window-0-or-10)
(leader-def-key "1" 'winum-select-window-1)
(leader-def-key "2" 'winum-select-window-2)
(leader-def-key "3" 'winum-select-window-3)
(leader-def-key "4" 'winum-select-window-4)
(leader-def-key "5" 'winum-select-window-5)
(leader-def-key "6" 'winum-select-window-6)
(leader-def-key "7" 'winum-select-window-7)
(leader-def-key "8" 'winum-select-window-8)
(leader-def-key "9" 'winum-select-window-9))
2020-05-05 20:54:22 +00:00
#+END_SRC
2020-08-23 03:33:53 +00:00
I don't want which-key display "lambda" for the descriptions of these, so set a custom display function. This is [[https://github.com/syl20bnr/spacemacs/blob/master/layers/+distributions/spacemacs-bootstrap/packages.el#L312][stolen from Spacemacs]].
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(push '(("\\(.*\\) 0" . "select-window-0") . ("\\1 0..9" . "window 0..9"))
which-key-replacement-alist)
(push '((nil . "select-window-[1-9]") . t) which-key-replacement-alist)
#+END_SRC
2020-08-23 03:33:53 +00:00
* Backups and Autosaves
Store backups and autosaves in a centralized place. This should really be the default...
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(make-directory (expand-file-name "~/.emacs.d/autosaves") t)
(setq auto-save-file-name-transforms '((".*" "~/.emacs.d/autosaves" t)))
(setq backup-directory-alist '(("." . "~/.emacs.d/backups")))
#+END_SRC
2020-08-23 03:33:53 +00:00
* Smartparens/Parinfer
Smartparens enables structured editing of s-expressions and other pairs:
2020-02-04 16:14:04 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(use-package smartparens
:hook ((prog-mode . smartparens-strict-mode)
(eshell-mode . smartparens-strict-mode)
(geiser-repl-mode . smartparens-strict-mode)
(inferior-python-mode . smartparens-strict-mode)
(after-init . smartparens-global-mode))
:init
2020-08-23 03:33:53 +00:00
(defhydra hydra-smartparens (:hint nil)
"
Moving^^^^ Slurp & Barf^^ Wrapping^^ Sexp juggling^^^^ Destructive
------------------------------------------------------------------------------------------------------------------------
[_a_] beginning [_n_] down [_h_] bw slurp [_R_] rewrap [_S_] split [_t_] transpose [_c_] change inner [_w_] copy
[_e_] end [_N_] bw down [_H_] bw barf [_u_] unwrap [_s_] splice [_A_] absorb [_C_] change outer
[_f_] forward [_p_] up [_l_] slurp [_U_] bw unwrap [_r_] raise [_E_] emit [_k_] kill [_g_] quit
[_b_] backward [_P_] bw up [_L_] barf [_(__{__[_] wrap (){}[] [_j_] join [_o_] convolute [_K_] bw kill [_q_] quit"
2020-08-23 03:33:53 +00:00
;; Moving
("a" sp-beginning-of-sexp)
("e" sp-end-of-sexp)
("f" sp-forward-sexp)
("b" sp-backward-sexp)
("n" sp-down-sexp)
("N" sp-backward-down-sexp)
("p" sp-up-sexp)
("P" sp-backward-up-sexp)
2020-04-17 17:25:46 +00:00
2020-08-23 03:33:53 +00:00
;; Slurping & barfing
("h" sp-backward-slurp-sexp)
("H" sp-backward-barf-sexp)
("l" sp-forward-slurp-sexp)
("L" sp-forward-barf-sexp)
2020-04-17 17:25:46 +00:00
2020-08-23 03:33:53 +00:00
;; Wrapping
("R" sp-rewrap-sexp)
("u" sp-unwrap-sexp)
("U" sp-backward-unwrap-sexp)
("(" sp-wrap-round)
("{" sp-wrap-curly)
("[" sp-wrap-square)
2020-04-17 17:25:46 +00:00
2020-08-23 03:33:53 +00:00
;; Sexp juggling
("S" sp-split-sexp)
("s" sp-splice-sexp)
("r" sp-raise-sexp)
("j" sp-join-sexp)
("t" sp-transpose-sexp)
("A" sp-absorb-sexp)
("E" sp-emit-sexp)
("o" sp-convolute-sexp)
2020-04-17 17:25:46 +00:00
2020-08-23 03:33:53 +00:00
;; Destructive editing
("c" sp-change-inner :exit t)
("C" sp-change-enclosing :exit t)
("k" sp-kill-sexp)
("K" sp-backward-kill-sexp)
("w" sp-copy-sexp)
2020-04-17 17:25:46 +00:00
2020-08-23 03:33:53 +00:00
("q" nil)
("g" nil))
:config
2020-08-23 03:33:53 +00:00
(require 'smartparens-config)
(show-smartparens-global-mode t)
(defun sp-wrap-double-quotes (&optional arg)
(interactive "P")
(sp-wrap-with-pair "\""))
(defun sp-wrap-single-quotes (&optional arg)
(interactive "P")
(sp-wrap-with-pair "\'"))
(setq sp-ignore-modes-list
(delete 'minibuffer-inactive-mode sp-ignore-modes-list))
(sp-local-pair 'minibuffer-inactive-mode "\'" nil :actions nil)
2020-02-04 16:14:04 +00:00
:general
2020-08-23 03:33:53 +00:00
(prog-mode-map "C-c p" 'hydra-smartparens/body)
(normal prog-mode-map "g p" 'hydra-smartparens/body)
(normal "g[" 'sp-wrap-square)
(normal "g(" 'sp-wrap-round)
(normal "g{" 'sp-wrap-curly)
(normal "g\"" 'sp-wrap-double-quotes)
(normal "g\'" 'sp-wrap-single-quotes))
(use-package evil-smartparens
:after (evil smartparens)
:hook ((smartparens-enabled . evil-smartparens-mode)))
2020-08-23 03:33:53 +00:00
(jdormit/define-prefix "l" "lisp")
(jdormit/define-prefix "lw" "wrap")
(leader-def-key "lwr" 'sp-wrap-round)
(leader-def-key "lws" 'sp-wrap-square)
(leader-def-key "lwc" 'sp-wrap-curly)
(leader-def-key "ls" 'sp-forward-slurp-sexp)
(leader-def-key "lb" 'sp-forward-barf-sexp)
2020-02-04 16:14:04 +00:00
#+END_SRC
2020-08-23 03:33:53 +00:00
Enable ES6 arrow functions in web-mode ("borrowed" from [[https://github.com/Fuco1/smartparens/issues/823#issuecomment-403019519][this GitHub comment]]):
2020-02-04 16:14:04 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(with-eval-after-load 'smartparens
(defun sp-after-equals-p (_id action _context)
(when (memq action '(insert navigate))
(sp--looking-back-p "=>" 2)))
2020-08-23 03:33:53 +00:00
(defun sp-after-equals-skip-p (ms mb _me)
2020-08-23 03:33:53 +00:00
(when (eq ms ">")
(save-excursion
(goto-char mb)
(sp--looking-back-p "=" 1))))
2020-08-23 03:33:53 +00:00
(sp-local-pair '(web-mode) "<" nil
:unless '(:add sp-after-equals-p)
:skip-match 'sp-after-equals-skip-p))
2020-02-04 16:14:04 +00:00
#+END_SRC
2020-08-23 03:33:53 +00:00
Parinfer infers parens from indentation and vice-versa. Currently
disabled since it turned out to be more annoying than good...
2020-02-16 15:35:51 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(use-package parinfer
:disabled
2020-02-16 15:35:51 +00:00
:init
2020-08-23 03:33:53 +00:00
(leader-def-key "lt" 'parinfer-toggle-mode)
(setq parinfer-extensions '(defaults
pretty-parens
evil
smart-tab
smart-yank))
:hook ((clojure-mode . parinfer-mode)
(emacs-lisp-mode . parinfer-mode)
(common-lisp-mode . parinfer-mode)
(scheme-mode . parinfer-mode)
(lisp-mode . parinfer-mode)))
2020-02-16 15:35:51 +00:00
#+END_SRC
2020-08-23 03:33:53 +00:00
* jq
The JSON multitool.
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(use-package jq-mode
:commands (jq-mode jq-interactively))
2020-02-19 14:34:21 +00:00
#+END_SRC
2020-01-07 17:45:23 +00:00
2020-08-23 03:33:53 +00:00
* link-hint
A very helpful package that provides jump-to-link functionality:
2020-04-16 21:28:22 +00:00
#+BEGIN_SRC emacs-lisp
2020-08-23 03:33:53 +00:00
(use-package link-hint
:commands (link-hint-open-link
link-hint-copy-link)
2020-04-16 21:28:22 +00:00
:init
2020-08-23 03:33:53 +00:00
(jdormit/define-prefix "ol" "link-hint")
(leader-def-key "oll" #'link-hint-open-link)
(leader-def-key "olc" #'link-hint-copy-link))
2020-04-16 21:28:22 +00:00
#+END_SRC
2020-02-19 14:34:21 +00:00
* Projectile
#+BEGIN_SRC emacs-lisp
(use-package projectile
2021-01-15 20:58:40 +00:00
:hook ((after-init . projectile-mode))
2020-08-23 03:33:53 +00:00
:commands (projectile-mode
projectile-find-file
projectile-grep
projectile-switch-project
projectile-project-root)
:config
(jdormit/define-prefix "p" "projectile")
2020-05-18 14:34:31 +00:00
(leader-def-key "p" projectile-command-map))
2020-04-16 22:12:23 +00:00
(defmacro with-projectile-root (&rest body)
`(with-temp-buffer
(when (projectile-project-root)
2020-08-23 03:33:53 +00:00
(cd (projectile-project-root)))
2020-04-16 22:12:23 +00:00
,@body))
2020-02-19 14:34:21 +00:00
#+END_SRC
2019-01-28 02:46:29 +00:00
2021-01-15 20:58:40 +00:00
* Perspective
A package that groups buffers/windows into workspaces per project:
#+BEGIN_SRC emacs-lisp
(use-package perspective
:commands (persp-mode)
:hook ((after-init . persp-mode))
:init
(jdormit/define-prefix "v" "perspective")
:config
(leader-def-key "v" perspective-map)
(defun switch-to-previous-buffer ()
"Switch to previously open buffer.
Repeated invocations toggle between the two most recently open buffers."
(interactive)
(persp-switch-to-buffer (other-buffer (current-buffer) 1)))
:custom
(persp-sort 'access)
:general
([remap kill-buffer] #'persp-kill-buffer*)
([remap switch-to-buffer] #'persp-switch-to-buffer*)
([remap counsel-switch-buffer] #'persp-counsel-switch-buffer)
([remap ivy-switch-buffer] #'persp-ivy-switch-buffer))
(use-package persp-projectile
:after (perspective projectile)
:demand t
:config
(projectile-persp-bridge counsel-projectile)
(projectile-persp-bridge counsel-projectile-switch-project)
:general
([remap projectile-switch-project] #'projectile-persp-switch-project))
#+END_SRC
2019-01-28 02:46:29 +00:00
* Mode line
* UI
Get rid of the janky buttons:
#+BEGIN_SRC emacs-lisp
2019-01-28 02:46:29 +00:00
(tool-bar-mode -1)
#+END_SRC
2019-08-25 18:27:30 +00:00
And the menu bar:
#+BEGIN_SRC emacs-lisp
2019-08-25 18:27:30 +00:00
(menu-bar-mode -1)
#+END_SRC
2019-01-28 02:46:29 +00:00
And the ugly scroll bars:
#+BEGIN_SRC emacs-lisp
2019-01-28 02:46:29 +00:00
(set-scroll-bar-mode nil)
#+END_SRC
Use =variable-pitch-mode= in text modes:
#+BEGIN_SRC emacs-lisp
(add-hook 'text-mode-hook (lambda () (variable-pitch-mode)))
(add-hook 'w3m-mode-hook (lambda () (variable-pitch-mode)))
#+END_SRC
Always use =buffer-face-mode= in code and text buffers:
#+BEGIN_SRC emacs-lisp
(add-hook 'prog-mode-hook #'buffer-face-mode)
(add-hook 'text-mode-hook #'buffer-face-mode)
#+END_SRC
Display the column number in programming modes:
#+BEGIN_SRC emacs-lisp
(add-hook 'prog-mode-hook #'column-number-mode)
#+END_SRC
2020-02-15 20:39:45 +00:00
Disable the bell:
#+BEGIN_SRC emacs-lisp
(setq ring-bell-function 'ignore)
#+END_SRC
Render stuff differently based on whether or not we are graphical:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(defun graphical-setup ()
(when (display-graphic-p (selected-frame))
(message "Running graphically")))
(defun non-graphical-setup ()
(when (not (display-graphic-p (selected-frame)))
(message "Running in terminal")
(menu-bar-mode -1)))
(defun do-graphical-non-graphical-setup ()
(graphical-setup)
(non-graphical-setup))
(add-hook 'window-setup-hook #'do-graphical-non-graphical-setup)
#+END_SRC
Try to make the background normal colored in the terminal:
#+BEGIN_SRC emacs-lisp
(defvar no-background-in-tty-faces '(default line-number magit-section-highlight))
(defun on-frame-open (frame)
(unless (display-graphic-p frame)
(menu-bar-mode -1)
(dolist (face no-background-in-tty-faces)
(set-face-background face "unspecified" frame))))
(mapc #'on-frame-open (frame-list))
(add-hook 'after-make-frame-functions #'on-frame-open)
(defun on-after-init ()
(unless (display-graphic-p (selected-frame))
(dolist (face no-background-in-tty-faces)
(set-face-background face "unspecified" (selected-frame)))))
(add-hook 'window-setup-hook #'on-after-init)
2019-01-28 02:46:29 +00:00
#+END_SRC
UI-related keybindings:
#+BEGIN_SRC emacs-lisp
(jdormit/define-prefix "u" "UI")
(leader-def-key "ut" #'customize-themes)
(leader-def-key "uf" #'customize-face)
(leader-def-key "uc" #'display-time-mode)
(leader-def-key "ub" #'display-battery-mode)
2019-01-28 02:46:29 +00:00
#+END_SRC
2021-01-15 20:58:40 +00:00
* Window handling
Following [[https://github.com/nex3/perspective-el#some-musings-on-emacs-window-layouts][some excellent advice]] from the author of perspective.el
about making Emacs' window handling saner:
#+BEGIN_SRC emacs-lisp
(setq display-buffer-alist
'((".*" (display-buffer-reuse-window display-buffer-same-window))))
(setq display-buffer-reuse-frames t) ; reuse windows in other frames
(setq even-window-sizes nil) ; display-buffer: avoid resizing
#+END_SRC
2020-02-14 20:14:56 +00:00
* Centaur tabs
[[https://github.com/ema2159/centaur-tabs][Centaur tabs]] is a package that gives Emacs buffer tabs similar to those in Atom or VS Code:
#+BEGIN_SRC emacs-lisp
(use-package centaur-tabs
:commands (centaur-tabs-mode
2020-05-29 18:35:12 +00:00
centaur-tabs-local-mode
centaur-tabs-mode-on-p)
2020-02-14 20:14:56 +00:00
:init
(setq centaur-tabs-set-icons t
2020-05-29 18:35:12 +00:00
centaur-tabs-gray-out-icons 'buffer
centaur-tabs-height 30
centaur-tabs-set-bar 'under
x-underline-at-descent-line t
centaur-tabs-set-modified-marker t
centaur-tabs-show-navigation-buttons t
centaur-tabs-down-tab-text " ☰ "
centaur-tabs-backward-tab-text " ◀ "
centaur-tabs-forward-tab-text " ▶ "
centaur-tabs-close-button "✕"
centaur-tabs-modified-marker "⬤"
centaur-tabs-cycle-scope 'tabs
centaur-tabs-label-fixed-length 20)
2020-02-14 20:14:56 +00:00
(leader-def-key "uT" #'centaur-tabs-mode)
(centaur-tabs-mode)
:config
(centaur-tabs-group-by-projectile-project)
;; Custom buffer groups
(defun centaur-tabs-projectile-buffer-groups ()
"Return the list of group names BUFFER belongs to."
(if centaur-tabs-projectile-buffer-group-calc
(symbol-value 'centaur-tabs-projectile-buffer-group-calc)
(set (make-local-variable 'centaur-tabs-projectile-buffer-group-calc)
(cond
((or (get-buffer-process (current-buffer))
(memq major-mode '(comint-mode compilation-mode))) '("Term"))
((string-equal "*" (substring (buffer-name) 0 1)) '("Misc"))
((condition-case _err
(projectile-project-root)
(error nil)) (list (projectile-project-name)))
((memq major-mode '(emacs-lisp-mode python-mode emacs-lisp-mode c-mode
c++-mode javascript-mode js-mode
js2-mode makefile-mode
lua-mode vala-mode)) '("Coding"))
((memq major-mode '(nxhtml-mode html-mode
mhtml-mode css-mode)) '("HTML"))
((memq major-mode '(org-journal-mode)) '("Journal"))
((memq major-mode '(org-mode calendar-mode diary-mode)) '("Org"))
((memq major-mode '(dired-mode)) '("Dir"))
(t '("Other"))))
(symbol-value 'centaur-tabs-projectile-buffer-group-calc)))
;; Don't show tabs for certain types of buffers
(advice-add 'centaur-tabs-hide-tab :around
(lambda (oldfn buf &rest args)
(if (with-current-buffer buf
2020-05-29 18:35:12 +00:00
(or (eq major-mode 'vuiet-mode)
(eq major-mode 'dired-sidebar-mode)))
t
(apply oldfn buf args))))
;; Only show tabs in buffers visiting files
(advice-add 'centaur-tabs-line :around
(lambda (oldfn &rest args)
(if (buffer-file-name)
(apply oldfn args)
(setq header-line-format nil))))
;; Enable prefix argument for tab switching keybindings
(advice-add 'centaur-tabs-forward :around
(lambda (oldfn &rest args)
(if (numberp current-prefix-arg)
(dotimes (_ current-prefix-arg)
(apply oldfn args))
(apply oldfn args))))
(advice-add 'centaur-tabs-backward :around
(lambda (oldfn &rest args)
(if (numberp current-prefix-arg)
(dotimes (_ current-prefix-arg)
(apply oldfn args))
(apply oldfn args))))
;; Use Org-mode titles for tab names when possible
(advice-add 'centaur-tabs-buffer-tab-label :around
(lambda (oldfn tab &rest args)
(if-let ((title (or (and
(fboundp 'org-roam-db--get-titles)
(car
(org-roam-db--get-titles
(buffer-file-name (car tab)))))
(org-get-title
(with-current-buffer (car tab)
(buffer-substring (point-min)
(min (point-max) 200)))))))
(if (> centaur-tabs-label-fixed-length 0)
(centaur-tabs-truncate-string centaur-tabs-label-fixed-length
(format " %s" title))
(format " %s" title))
(apply oldfn tab args))))
2020-03-23 14:09:55 +00:00
;; Add a cache to speed up icon rendering for huge groups
(defvar centaur-tabs-icon-cache (make-hash-table :test 'equal)
"A cache holding icons generated for centaur-tabs mode tabs.")
(advice-add 'centaur-tabs-icon :around
(lambda (oldfn tab face selected &rest args)
(let ((key (list tab face selected)))
(or (gethash key centaur-tabs-icon-cache)
(puthash key
(apply oldfn tab face selected args)
centaur-tabs-icon-cache)))))
2020-03-23 14:09:55 +00:00
2020-02-14 20:14:56 +00:00
:general
((normal motion visual) "g t" #'centaur-tabs-forward)
((normal motion visual) "g T" #'centaur-tabs-backward)
2020-02-29 13:16:56 +00:00
(leader-map "pt" #'centaur-tabs-counsel-switch-group)
2020-02-14 20:14:56 +00:00
:hook
(git-commit-mode . (lambda ()
(when (centaur-tabs-mode-on-p)
(centaur-tabs-local-mode)))))
2020-02-14 20:14:56 +00:00
#+END_SRC
2019-01-28 02:46:29 +00:00
* Frame parameters
Functions to change the frame size:
#+BEGIN_SRC emacs-lisp
(defun jdormit/set-frame-size (width height)
(interactive "nWidth: \nnHeight: ")
(if (display-graphic-p)
2019-02-07 21:54:16 +00:00
(set-frame-size (selected-frame) width height)
2019-01-28 02:46:29 +00:00
(message "Not running graphically")))
(defun jdormit/set-frame-width (width)
(interactive "nWidth: ")
(jdormit/set-frame-size width (frame-height)))
(defun jdormit/set-frame-height (height)
(interactive "nHeight: ")
(jdormit/set-frame-size (frame-width) height))
#+END_SRC
Keybindings:
#+BEGIN_SRC emacs-lisp
(leader-def-key "Fw" 'jdormit/set-frame-width)
(leader-def-key "Fh" 'jdormit/set-frame-height)
(leader-def-key "Fs" 'jdormit/set-frame-size)
#+END_SRC
2020-01-22 14:59:43 +00:00
* Transpose Frame
A handy utility that reverse the current frame window split (vertical to horizontal or vice-versa):
#+BEGIN_SRC emacs-lisp
(use-package transpose-frame
:defer t
:init
(leader-def-key "wt" 'transpose-frame))
#+END_SRC
2019-01-28 02:46:29 +00:00
* EShell
Easy keybinding to open EShell:
#+BEGIN_SRC emacs-lisp
(defun open-eshell (&optional arg)
(interactive "P")
(if (and (fboundp 'projectile-project-root)
(projectile-project-root))
(projectile-run-eshell arg)
(eshell arg)))
(leader-def-key "'" 'open-eshell)
2019-01-28 02:46:29 +00:00
#+END_SRC
Make EShell's tab completion work like Bash's:
#+BEGIN_SRC emacs-lisp
(setq eshell-cmpl-cycle-completions nil)
#+END_SRC
2020-06-02 13:40:29 +00:00
Add additional useful modules:
#+BEGIN_SRC emacs-lisp
2020-07-07 20:33:11 +00:00
(with-eval-after-load 'esh-module
(add-to-list 'eshell-modules-list 'eshell-tramp))
2020-07-07 20:33:11 +00:00
(with-eval-after-load 'eshell
(require 'esh-module))
2020-06-02 13:40:29 +00:00
#+END_SRC
Prefer Lisp commands to external programs:
#+BEGIN_SRC emacs-lisp
(setq eshell-prefer-lisp-functions t
eshell-prefer-lisp-variables t)
#+END_SRC
Enable password caching:
#+BEGIN_SRC emacs-lisp
(setq password-cache t
password-cache-expiry 120)
#+END_SRC
2019-01-28 02:46:29 +00:00
Destroy shell buffers created by eshell when the process dies::
#+BEGIN_SRC emacs-lisp
(setq eshell-destroy-buffer-when-process-dies t)
#+END_SRC
Visual programs:
#+BEGIN_SRC emacs-lisp
(defun eshell-setup ()
(add-to-list 'eshell-visual-commands "crawl")
(add-to-list 'eshell-visual-commands "ssh")
2020-12-28 16:46:42 +00:00
(add-to-list 'eshell-visual-commands "watch")
(add-to-list 'eshell-visual-subcommands '("kubectl" "exec"))
2020-12-28 16:46:42 +00:00
(add-to-list 'eshell-visual-subcommands '("k" "exec"))
(add-to-list 'eshell-visual-subcommands '("docker" "build"))
(add-to-list 'eshell-visual-subcommands '("docker" "push")))
2019-01-28 02:46:29 +00:00
(add-hook 'eshell-mode-hook #'eshell-setup)
#+END_SRC
And a function to run any program visually:
#+BEGIN_SRC emacs-lisp
(defun eshell/v (&rest args)
(apply #'eshell-exec-visual args))
#+END_SRC
2019-02-06 15:50:55 +00:00
Load .dir-locals.el when switching directories:
#+BEGIN_SRC emacs-lisp
2019-02-07 21:54:35 +00:00
(add-hook 'eshell-mode-hook #'hack-dir-local-variables-non-file-buffer)
(add-hook 'eshell-after-prompt-hook #'hack-dir-local-variables-non-file-buffer)
2019-02-06 15:50:55 +00:00
#+END_SRC
2019-11-13 17:04:31 +00:00
A function to properly clear the eshell:
#+BEGIN_SRC emacs-lisp
(defun clear-eshell (&optional prefix)
(interactive)
(let ((input (eshell-get-old-input)))
(eshell/clear-scrollback)
(eshell-emit-prompt)
(insert input)))
2020-02-12 19:24:14 +00:00
(add-hook 'eshell-mode-hook
(lambda () (general-def eshell-mode-map "C-c C-o" #'clear-eshell)))
2019-11-13 17:04:31 +00:00
#+END_SRC
2021-01-15 20:58:54 +00:00
Compilation-shell-minor-mode:
#+BEGIN_SRC emacs-lisp
(add-hook 'eshell-mode-hook #'compilation-shell-minor-mode)
#+END_SRC
2020-01-15 21:29:15 +00:00
** Prompt
#+BEGIN_SRC emacs-lisp
(defun jdormit-eshell-prompt ()
(let ((branch (magit-name-local-branch "HEAD")))
(format "%s%s"
(if branch (format "(%s) " branch) "")
(concat (abbreviate-file-name (eshell/pwd))
2021-01-15 20:59:17 +00:00
" "
(propertize
2021-01-15 20:59:17 +00:00
(if (= (user-uid) 0) "#" "λ")
'face `(:foreground "#859900"))
" "))))
2020-01-15 21:29:15 +00:00
(setq jdormit-eshell-prompt-regex "^[^#λ\n]* [#λ] ")
2020-01-15 21:29:15 +00:00
(setq eshell-prompt-function 'jdormit-eshell-prompt)
(setq eshell-prompt-regexp jdormit-eshell-prompt-regex)
#+END_SRC
2020-01-02 15:55:29 +00:00
* Flycheck
Syntax checking etc.:
#+BEGIN_SRC emacs-lisp
(use-package flycheck
2020-01-07 17:45:23 +00:00
:init
(defhydra hydra-flycheck
(:pre (flycheck-list-errors)
:post (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))
2020-01-02 15:55:29 +00:00
:config
(setq-default flycheck-disabled-checkers '(emacs-lisp emacs-lisp-checkdoc))
2020-01-07 17:45:23 +00:00
(global-flycheck-mode)
:general
((normal motion visual) flycheck-mode-map "ze" 'hydra-flycheck/body))
2020-01-02 15:55:29 +00:00
#+END_SRC
2020-01-31 16:19:04 +00:00
* Tabs
#+BEGIN_SRC emacs-lisp
(defun disable-tab-insert ()
(interactive)
(setq indent-tabs-mode nil))
#+END_SRC
2020-06-14 16:26:15 +00:00
* aggressive-indent-mode
Like [[help:electric-indent-mode][electric-indent-mode]] but reindents after every change:
#+BEGIN_SRC emacs-lisp
(use-package aggressive-indent
:hook ((clojure-mode . aggressive-indent-mode)
2020-08-20 22:06:35 +00:00
(emacs-lisp-mode . aggressive-indent-mode)
(lisp-mode . aggressive-indent-mode)
(scheme-mode . aggressive-indent-mode)))
2020-06-14 16:26:15 +00:00
#+END_SRC
2020-07-29 17:48:00 +00:00
* Indentation guides
#+BEGIN_SRC emacs-lisp
2020-08-20 22:06:35 +00:00
(use-package highlight-indent-guides
:commands highlight-indent-guides-mode
2020-11-18 15:29:43 +00:00
:hook ((python-mode . highlight-indent-guides-mode)
(yaml-mode . highlight-indent-guides-mode))
2020-08-20 22:06:35 +00:00
:init
(leader-def-key "th" #'highlight-indent-guides-mode)
:custom
(highlight-indent-guides-method 'character)
(highlight-indent-guides-auto-character-face-perc 7)
(highlight-indent-guides-responsive 'stack)
(highlight-indent-guides-auto-stack-character-face-perc 10))
2020-07-29 17:48:00 +00:00
#+END_SRC
2019-06-06 18:46:33 +00:00
* JSON
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(use-package json-mode
2020-12-28 16:46:42 +00:00
:mode (("\\.json\\'" . json-mode))
:config
(setq js-indent-level 2))
2019-06-07 13:46:05 +00:00
(use-package json-navigator
:commands (json-navigator-navigator
json-navigator-navigate-after-point
json-navigator-navigate-region))
(use-package tree-mode
:general
(normal tree-mode-map
"D" #'tree-mode-delete-tree
"k" #'tree-mode-previous-node
"j" #'tree-mode-next-node
"l" #'tree-mode-next-sib
"h" #'tree-mode-previous-sib
"u" #'tree-mode-goto-parent
"r" #'tree-mode-goto-root
"gr" #'tree-mode-reflesh
"E" #'tree-mode-expand-level
"e" #'tree-mode-toggle-expand
"s" #'tree-mode-sort-by-tag
"/" #'tree-mode-keep-match
"!" #'tree-mode-collapse-other-except)
:hook
((json-navigator-mode . tree-minor-mode)))
2019-12-16 19:00:28 +00:00
(defun json-pprint ()
2019-12-16 19:00:28 +00:00
(interactive)
(let ((begin (if (region-active-p) (region-beginning) (point-min)))
(end (if (region-active-p) (region-end) (point-max))))
(if (executable-find "jq")
(shell-command-on-region begin end "jq ." nil t)
(json-pretty-print begin end))))
2019-12-16 19:00:28 +00:00
(general-def json-mode-map "C-M-\\" 'json-pprint)
2019-01-28 02:46:29 +00:00
#+END_SRC
2019-06-06 18:46:33 +00:00
* JavaScript
2019-02-15 19:13:34 +00:00
Some formatting stuff:
#+BEGIN_SRC emacs-lisp
(setq js-indent-level 4)
#+END_SRC
2019-02-15 19:25:01 +00:00
#+BEGIN_SRC emacs-lisp
(use-package web-mode
:mode (("\\.html\\'" . web-mode)
2020-12-13 13:34:18 +00:00
("\\.js\\'" . web-mode)
("\\.jsx\\'" . web-mode)
("\\.mako\\'" . web-mode)
("\\.jinja2\\'" . web-mode)
("\\.hbs\\'" . web-mode))
2019-02-15 19:25:01 +00:00
:config
2019-12-01 16:45:22 +00:00
(setq web-mode-engines-alist
2020-12-13 13:34:18 +00:00
'(("django" . "\\.jinja2\\'")))
2019-02-15 19:25:01 +00:00
(add-hook 'web-mode-hook
2020-12-13 13:34:18 +00:00
(lambda ()
(when (equal web-mode-content-type "javascript")
(web-mode-set-content-type "jsx"))
(when (or (equal web-mode-content-type "javascript")
(equal web-mode-content-type "jsx"))
(lsp-deferred))))
(add-hook 'web-mode-hook #'disable-tab-insert)
:custom
(web-mode-enable-auto-pairing nil))
2019-02-15 19:25:01 +00:00
#+END_SRC
2019-02-14 14:41:31 +00:00
Use nvm to manage node versions:
#+BEGIN_SRC emacs-lisp
(use-package nvm
2020-11-02 18:20:01 +00:00
:straight (nvm :host github :repo "rejeep/nvm.el")
2019-02-14 14:41:31 +00:00
:commands (nvm-use nvm-use-for nvm-use-for-buffer))
#+END_SRC
2019-11-27 15:12:56 +00:00
A command to format JS via prettier:
#+BEGIN_SRC emacs-lisp
(defun prettier ()
(interactive)
(let ((start (if (use-region-p) (region-beginning) (point-min)))
2019-12-18 16:27:11 +00:00
(end (if (use-region-p) (region-end) (point-max)))
(parser (cond
((eq major-mode 'graphql-mode) "graphql")
(t "babel"))))
(shell-command-on-region start end (concat "prettier --parser " parser) nil t)))
2019-11-27 15:12:56 +00:00
#+END_SRC
** NVM
Manage node version via NVM within Emacs:
#+BEGIN_SRC emacs-lisp
(use-package nvm
:commands (nvm-use
nvm-use-for
nvm-use-for-buffer
nvm--installed-versions)
:init
(defun nvm (version)
(interactive (list
(completing-read "Node version: "
(mapcar #'car
(nvm--installed-versions)))))
(nvm-use version)))
#+END_SRC
2020-03-18 13:09:20 +00:00
* Java
#+BEGIN_SRC emacs-lisp
(use-package lsp-java
:hook ((java-mode . lsp-deferred)))
2020-03-18 13:09:20 +00:00
#+END_SRC
* Kotlin
#+BEGIN_SRC emacs-lisp
(use-package kotlin-mode
:mode (("\\.kt\\'" . kotlin-mode))
:config
(with-eval-after-load 'lsp
(when (executable-find "kotlin-language-server")
(add-hook 'kotlin-mode-hook #'lsp-deferred))))
2020-03-18 13:09:20 +00:00
#+END_SRC
2020-02-22 03:32:12 +00:00
* Groovy
Used for Jenkins configuration scripts and probably other things.
#+BEGIN_SRC emacs-lisp
(use-package groovy-mode
:commands (groovy-mode)
:mode (("\\.groovy\\'" . groovy-mode))
:init
(add-to-list 'interpreter-mode-alist
'("groovy" . groovy-mode))
:custom
(groovy-indent-offset 2))
#+END_SRC
2019-12-19 21:59:55 +00:00
* Typescript
#+BEGIN_SRC emacs-lisp
(use-package typescript-mode
:mode ("\\.ts\\'")
:config
(with-eval-after-load 'lsp
(add-hook 'typescript-mode-hook 'lsp-deferred)))
2019-12-19 21:59:55 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-02-16 18:01:50 +00:00
* LSP Mode
Emacs support for the Language Server Protocol
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2019-11-14 21:03:13 +00:00
(use-package lsp-mode
2020-01-07 22:04:34 +00:00
:defer t
2020-01-07 17:45:23 +00:00
:init
(defhydra hydra-lsp (:exit t :hint nil)
"
Buffer^^ Server^^ Symbol
-------------------------------------------------------------------------------------
[_f_] format [_M-r_] restart [_d_] declaration [_i_] implementation [_o_] documentation
[_m_] imenu [_S_] shutdown [_D_] definition [_t_] type [_r_] rename
[_x_] execute action [_M-s_] describe session [_R_] references [_s_] signature"
("d" lsp-find-declaration)
("D" lsp-ui-peek-find-definitions)
("R" lsp-ui-peek-find-references)
("i" lsp-ui-peek-find-implementation)
("t" lsp-find-type-definition)
("s" lsp-signature-help)
("o" lsp-describe-thing-at-point)
("r" lsp-rename)
("f" lsp-format-buffer)
("m" lsp-ui-imenu)
("x" lsp-execute-code-action)
("M-s" lsp-describe-session)
("M-r" lsp-restart-workspace)
("S" lsp-shutdown-workspace))
:general
(lsp-mode-map "C-c h" 'hydra-lsp/body)
((normal visual motion) lsp-mode-map "K" #'lsp-describe-thing-at-point)
2020-05-22 22:10:12 +00:00
:hook
((lsp-mode . (lambda ()
(let ((lsp-keymap-prefix "gl"))
(lsp-enable-which-key-integration)))))
2020-01-07 17:17:20 +00:00
:config
(setq lsp-prefer-flymake nil)
2020-05-26 03:10:49 +00:00
(general-def '(normal visual motion) "gl" lsp-command-map)
2020-05-22 22:10:12 +00:00
:commands lsp-mode lsp lsp-deferred
:custom
(lsp-enable-snippet nil)
(lsp-eldoc-render-all nil))
2019-01-28 02:46:29 +00:00
(use-package lsp-ui
2019-11-14 21:03:13 +00:00
:after (lsp-mode)
2020-02-29 13:17:14 +00:00
:custom
(lsp-ui-sideline-enable t)
(lsp-ui-sideline-show-symbol t)
(lsp-ui-sideline-show-hover t)
(lsp-ui-sideline-show-code-actions t)
(lsp-ui-sideline-update-mode 'point)
(lsp-ui-doc-alignment 'window)
(lsp-ui-doc-header t)
(lsp-ui-doc-position 'top)
(lsp-ui-doc-background '((t (:inherit region))))
(lsp-ui-doc-header '((t (:inherit lsp-face-highlight-write))))
2020-03-02 15:28:14 +00:00
(lsp-ui-sideline-current-symbol '((t (:inherit font-lock-constant-face
2020-05-22 22:10:12 +00:00
:weight ultra-bold)))))
2020-01-02 22:06:35 +00:00
2019-02-25 16:10:42 +00:00
(with-eval-after-load 'lsp-clients
(defun lsp-typescript-javascript-tsx-jsx-activate-p (filename major-mode)
"Checks if the javascript-typescript language server should be enabled
2020-01-02 22:06:35 +00:00
based on FILE-NAME and MAJOR-MODE"
2019-02-25 16:10:42 +00:00
(or (member major-mode '(typescript-mode typescript-tsx-mode js-mode js2-mode rjsx-mode))
2020-05-18 14:33:28 +00:00
(and (eq major-mode 'web-mode)
(or (string-suffix-p ".tsx" filename t)
(string-suffix-p ".jsx" filename t)
(string-suffix-p ".js" filename t))))))
2019-02-16 18:01:50 +00:00
#+END_SRC
2019-01-28 02:46:29 +00:00
* Python
2020-04-28 16:52:41 +00:00
** General
2019-02-16 18:01:50 +00:00
#+BEGIN_SRC emacs-lisp
2019-11-08 14:53:27 +00:00
(leader-def-key "sp" #'run-python)
2020-01-31 16:19:04 +00:00
(add-hook 'python-mode-hook #'disable-tab-insert)
2019-02-16 18:01:50 +00:00
#+END_SRC
2020-04-28 16:52:41 +00:00
ISort is a Python utility to sort imports:
#+BEGIN_SRC emacs-lisp
(use-package py-isort
:commands (py-isort-buffer py-isort-region)
:config
(setq py-isort-options '("-m=3"))
:general
(python-mode-map "C-c C-i" #'py-isort-buffer))
#+END_SRC
2020-04-28 16:52:41 +00:00
Run black on the current buffer:
#+BEGIN_SRC emacs-lisp
(general-def 'normal python-mode-map "C-M-\\" #'format-all-buffer)
2019-08-30 15:48:20 +00:00
#+END_SRC
2020-04-28 16:52:41 +00:00
[[https://github.com/naiquevin/sphinx-doc.el][sphinx-doc.el]] automatically generates doc strings for Python functions (and updates existing ones!):
2019-11-07 17:06:36 +00:00
#+BEGIN_SRC emacs-lisp
2020-04-28 16:52:41 +00:00
(use-package sphinx-doc
:hook ((python-mode . sphinx-doc-mode)))
2019-11-07 17:06:36 +00:00
#+END_SRC
2020-04-28 16:52:41 +00:00
** Dev environment/IDE stuff
2019-11-07 17:06:36 +00:00
Support pyvenv within Emacs:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2019-02-25 16:10:42 +00:00
(use-package pyvenv
:defer 0
2020-04-28 16:52:41 +00:00
:commands (pyvenv-mode
pyvenv-tracking-mode
2020-04-28 16:52:41 +00:00
pyvenv-workon
pyvenv-activate
pyvenv-track-virtualenv)
:config
2020-06-05 17:36:27 +00:00
(pyvenv-mode)
(pyvenv-tracking-mode))
2019-02-25 16:10:42 +00:00
2019-02-05 22:37:11 +00:00
(defun eshell/workon (name)
(pyvenv-workon name))
(defun eshell/activate (dir)
(pyvenv-activate dir))
(defun eshell/deactivate ()
(pyvenv-deactivate))
2019-10-23 20:27:19 +00:00
#+END_SRC
2019-01-28 02:46:29 +00:00
2020-04-28 16:52:41 +00:00
And support pyenv (NOT pyvenv) to change Python versions:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2020-04-28 16:52:41 +00:00
(use-package pyenv-mode
:defer t)
2019-01-28 02:46:29 +00:00
#+END_SRC
2020-04-28 16:52:41 +00:00
Use the LSP python client:
#+BEGIN_SRC emacs-lisp
2020-05-29 18:35:29 +00:00
(use-package lsp-python-ms
:init
(setq lsp-python-ms-auto-install-server t)
(defun python-lsp ()
(require 'lsp-python-ms)
(lsp-deferred))
:hook
(python-mode . python-lsp)
:general
(python-mode-map "C-c C-d" #'lsp-describe-thing-at-point))
2020-04-28 16:52:41 +00:00
(general-def 'normal python-mode-map "C-c C-d" #'lsp-describe-thing-at-point)
#+END_SRC
2020-05-29 18:35:29 +00:00
Override the flycheck python-mypy checker to run in the right
directory:
#+BEGIN_SRC emacs-lisp
(flycheck-define-checker python-mypy
"Mypy syntax and type checker. Requires mypy>=0.580.
See URL `http://mypy-lang.org/'."
:command ("mypy"
"--show-column-numbers"
(config-file "--config-file" flycheck-python-mypy-config)
(option "--cache-dir" flycheck-python-mypy-cache-dir)
source-original)
:error-patterns
((error line-start (file-name) ":" line (optional ":" column)
": error:" (message) line-end)
(warning line-start (file-name) ":" line (optional ":" column)
": warning:" (message) line-end)
(info line-start (file-name) ":" line (optional ":" column)
": note:" (message) line-end))
:modes python-mode
;; Ensure the file is saved, to work around
;; https://github.com/python/mypy/issues/4746.
:predicate flycheck-buffer-saved-p
:working-directory (lambda (checker)
(projectile-compilation-dir)))
#+END_SRC
2020-02-13 16:39:33 +00:00
** Autoflake
[[https://pypi.org/project/autoflake/][Autoflake]] is a tool that removes unused imports and variables from Python code:
#+BEGIN_SRC emacs-lisp
(defvar autoflake-args '()
"Arguments to pass to the autoflake command.
See URL `https://pypi.org/project/autoflake' for options.")
(defun autoflake (arg)
(interactive "P")
(let ((autoflake-cmd (or (executable-find "autoflake")
(error "Autoflake executable not found")))
(file (cond
((or arg (not buffer-file-name))
(read-file-name "Run autoflake on file: "))
(buffer-file-name buffer-file-name)
(:else (error "Invalid file for autoflake")))))
(apply #'call-process autoflake-cmd nil nil nil
(append autoflake-args (list "--in-place" file)))))
#+END_SRC
2020-02-14 16:44:19 +00:00
2020-02-14 17:38:18 +00:00
** Testing
2020-02-14 16:44:19 +00:00
[[https://github.com/wbolster/emacs-python-pytest][python-pytest.el]] integrates Pytest with Emacs:
#+BEGIN_SRC emacs-lisp
(use-package python-pytest
:commands (python-pytest-popup
2020-04-01 02:39:09 +00:00
python-pytest--current-defun)
2020-02-14 16:44:19 +00:00
:general
2020-04-01 02:39:09 +00:00
(python-mode-map "C-c t p" #'python-pytest-popup)
((normal motion visual) python-pytest-mode-map "g r" #'python-pytest-repeat)
((normal motion visual) python-pytest-mode-map "q" #'quit-window))
2020-02-14 16:44:19 +00:00
#+END_SRC
2020-02-13 16:39:33 +00:00
2020-02-14 17:38:18 +00:00
And borrowing some functions from python-pytest, we can get some nosetests support as well:
#+BEGIN_SRC emacs-lisp
(defvar nosetests-args ""
"Additional args to pass to nosetests")
(defun run-nose (args &optional debug)
"Runs nosetests with `args'
If `debug' is non-nil, run in a GUD PDB session."
(let* ((nosetests-cmd (executable-find "nosetests"))
(cmdline (format "%s %s" nosetests-cmd args)))
2020-02-14 17:38:18 +00:00
(when (not nosetests-cmd)
(user-error "Nosetests command not found"))
(if debug
2020-05-01 15:54:30 +00:00
(realgud:pdb cmdline)
(compile cmdline))))
(defun run-nose-reading-args (arg nose-args &optional debug)
"Runs nosetests with default args or prompts for args with prefix
2020-02-14 17:38:18 +00:00
If `debug' is non-nil, run in a GUD PDB session."
2020-02-14 17:38:18 +00:00
(let ((args (if arg
(read-string "Nosetests arguments: "
nil nil nosetests-args)
nosetests-args)))
(run-nose (format "%s %s" args nose-args) debug)))
2020-02-14 17:38:18 +00:00
(defun run-nose-in-project (arg nose-args &optional debug)
"Runs nosetests from the project root
If `debug' is non-nil, run in a GUD PDB session."
2020-02-14 17:38:18 +00:00
(let ((dir (or (projectile-project-root)
default-directory)))
(with-temp-buffer
(cd dir)
(run-nose-reading-args arg nose-args debug))))
2020-02-14 17:38:18 +00:00
(defun nosetests-all (arg)
"Runs nosetests on all project tests, prompting for the tests directory"
(interactive "P")
(let ((test-dir (read-file-name "Test directory: "
(or (projectile-project-root)
default-directory)
nil t "tests" #'directory-name-p)))
(run-nose-in-project arg (directory-file-name test-dir))))
(defun nosetests-debug-all (arg)
"Runs nosetests in a GUD session on all project tests"
(interactive "P")
(let ((test-dir (read-file-name "Test directory: "
(or (projectile-project-root)
default-directory)
nil t "tests" #'directory-name-p)))
(run-nose-in-project arg (directory-file-name test-dir) t)))
2020-02-14 17:38:18 +00:00
(defun nosetests-module (arg module)
"Runs nosetests in the module of the current file"
(interactive (list current-prefix-arg
(read-file-name "Run nosetests on module: "
nil nil t
(file-name-directory
(buffer-file-name))
#'directory-name-p)))
(run-nose-in-project arg (directory-file-name module)))
(defun nosetests-debug-module (arg module)
"Runs nosetests in a GUD session in the module of the current file"
(interactive (list current-prefix-arg
(read-file-name "Run nosetests on module: "
nil nil t
(file-name-directory
(buffer-file-name))
#'directory-name-p)))
(run-nose-in-project arg (directory-file-name module) t))
2020-02-14 17:38:18 +00:00
(defun nosetests-file (arg file)
"Runs nosetests on the current file"
(interactive (list current-prefix-arg
(read-file-name "Run nosetests on file: "
nil nil t (buffer-file-name))))
(run-nose-in-project arg file))
(defun nosetests-debug-file (arg file)
"Runs nosetests in a GUD session on the current file"
(interactive (list current-prefix-arg
(read-file-name "Run nosetests on file: "
nil nil t (buffer-file-name))))
(run-nose-in-project arg file t))
2020-02-14 17:38:18 +00:00
(defun nosetests-def (arg def)
"Runs nosetests on the enclosing function at point"
(interactive (list current-prefix-arg
(python-pytest--current-defun)))
(run-nose-in-project arg (format "%s:%s"
(buffer-file-name)
def)))
(defun nosetests-debug-def (arg def)
"Runs nosetests in a GUD session on the enclosing function at point"
(interactive (list current-prefix-arg
(python-pytest--current-defun)))
(run-nose-in-project arg
(format "%s:%s"
(buffer-file-name)
def)
t))
2020-02-14 17:38:18 +00:00
(defvar nosetests-map (make-sparse-keymap)
"Keymap for nosetests")
(defvar nosetests-debug-map (make-sparse-keymap)
"Keymap for debugging nosetests")
2020-02-14 17:38:18 +00:00
(general-def python-mode-map "C-c t n" nosetests-map)
(general-def nosetests-map "a" #'nosetests-all)
(general-def nosetests-map "m" #'nosetests-module)
(general-def nosetests-map "f" #'nosetests-file)
(general-def nosetests-map "d" #'nosetests-def)
(general-def nosetests-map "b" nosetests-debug-map)
(general-def nosetests-debug-map "a" #'nosetests-debug-all)
(general-def nosetests-debug-map "m" #'nosetests-debug-module)
(general-def nosetests-debug-map "f" #'nosetests-debug-file)
(general-def nosetests-debug-map "d" #'nosetests-debug-def)
(which-key-add-major-mode-key-based-replacements
'python-mode "C-c t n" "nosetests")
(which-key-add-major-mode-key-based-replacements
'python-mode "C-c t n b" "debug")
2020-02-14 17:38:18 +00:00
#+END_SRC
2019-08-12 15:26:14 +00:00
* Hy
Python but Lispy!
#+BEGIN_SRC emacs-lisp
(defun run-hy ()
(interactive)
(run-lisp (expand-file-name "~/.virtualenvs/hy/bin/hy")))
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-01-28 02:46:29 +00:00
* Go
Basic support:
#+BEGIN_SRC emacs-lisp
(use-package go-mode
:mode (("\\.go\\'" . go-mode)))
#+END_SRC
2019-02-16 18:01:50 +00:00
LSP support - requires [[https://github.com/sourcegraph/go-langserver][go-langserver]].
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(add-hook 'go-mode-hook #'lsp-deferred)
2019-01-28 02:46:29 +00:00
#+END_SRC
* Clojure
Start with clojure-mode:
#+BEGIN_SRC emacs-lisp
(use-package clojure-mode
:mode (("\\.clj\\'" . clojure-mode)
("\\.cljs\\'" . clojurescript-mode)
("\\.cljc\\'" . clojurec-mode)
("\\.edn\\'" . clojure-mode))
2019-01-28 02:46:29 +00:00
:config
(define-clojure-indent
(defroutes 'defun)
(GET 2)
(POST 2)
(PUT 2)
(DELETE 2)
(HEAD 2)
(ANY 2)
(OPTIONS 2)
(PATCH 2)
(rfn 2)
(let-routes 1)
(context 2)
(:= 3)
(:+ 3)
(match 1)
(for-all 2)
(checking 2)
(let-flow 1)))
2019-01-28 02:46:29 +00:00
#+END_SRC
2020-01-04 13:40:03 +00:00
Add flycheck support:
#+BEGIN_SRC emacs-lisp
(use-package flycheck-clj-kondo
:after (clojure-mode)
:if (executable-find "clj-kondo")
:ensure t)
2020-01-04 13:40:03 +00:00
#+END_SRC
2019-01-28 02:46:29 +00:00
Sprinkle in some CIDER:
#+BEGIN_SRC emacs-lisp
(use-package cider
:commands (cider-mode cider-jack-in cider-jack-in-clojurescript)
2020-01-04 13:40:03 +00:00
:config
(setq cider-known-endpoints
'(("local" "localhost" "4005"))
cider-prompt-for-symbol nil)
2020-01-04 13:40:03 +00:00
(general-def cider-mode-map "C-c t" cider-test-commands-map)
2020-01-08 02:38:01 +00:00
(add-hook 'cider-repl-mode-hook 'smartparens-strict-mode)
2019-01-28 02:46:29 +00:00
:hook ((clojure-mode . cider-mode)
(clojurescript-mode . cider-mode)
(clojurec-mode . cider-mode))
2019-01-28 02:46:29 +00:00
:general
(cider-stacktrace-mode-map "SPC" leader-map)
('normal cider-mode-map "M-." #'cider-find-var)
(cider-repl-mode-map "C-c C-l" #'cider-repl-clear-buffer))
2019-01-28 02:46:29 +00:00
(defun jdormit/cider-setup ()
(local-set-key (kbd "C-c M-b") 'cider-debug-defun-at-point))
(add-hook 'cider-mode-hook 'jdormit/cider-setup)
#+END_SRC
2020-02-14 02:04:25 +00:00
Add some handy hydras:
#+BEGIN_SRC emacs-lisp
(use-package cider-hydra
:after (cider)
:hook ((cider-mode . cider-hydra-mode)))
#+END_SRC
2020-01-07 15:39:04 +00:00
Clj-refactor adds magical refactoring abilities:
#+BEGIN_SRC emacs-lisp
(use-package clj-refactor
:init
(defun clj-refactor-setup ()
(interactive)
(clj-refactor-mode 1)
(cljr-add-keybindings-with-prefix "C-c r"))
:config
(setq cljr-auto-sort-ns t)
:hook ((clojure-mode . clj-refactor-setup)))
#+END_SRC
Add support for running Org-mode Clojure source blocks with [[https://github.com/borkdude/babashka][Babashka]]:
#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'ob-clojure
(defcustom org-babel-clojure-backend nil
"Backend used to evaluate Clojure code blocks."
:group 'org-babel
:type '(choice
(const :tag "inf-clojure" inf-clojure)
(const :tag "cider" cider)
(const :tag "slime" slime)
(const :tag "bb" bb)
(const :tag "Not configured yet" nil)))
(defun elisp->clj (in)
(cond
((listp in) (concat "[" (s-join " " (mapcar #'elisp->clj in)) "]"))
(t (format "%s" in))))
(defun ob-clojure-eval-with-bb (expanded params)
"Evaluate EXPANDED code block with PARAMS using babashka."
(unless (executable-find "bb")
(user-error "Babashka not installed"))
(let* ((stdin (let ((stdin (cdr (assq :stdin params))))
(when stdin
(elisp->clj
(org-babel-ref-resolve stdin)))))
(input (cdr (assq :input params)))
(file (make-temp-file "ob-clojure-bb" nil nil expanded))
(command (concat (when stdin (format "echo %s | " (shell-quote-argument stdin)))
(format "bb %s -f %s"
(cond
((equal input "edn") "")
((equal input "text") "-i")
(t ""))
(shell-quote-argument file))))
(result (shell-command-to-string command)))
(s-trim result)))
(defun org-babel-execute:clojure (body params)
"Execute a block of Clojure code with Babel."
(let* ((org-babel-clojure-backend (or (cdr (assq :backend params))
org-babel-clojure-backend))
(org-babel-clojure-backend (when org-babel-clojure-backend
(intern org-babel-clojure-backend))))
(unless org-babel-clojure-backend
(user-error "You need to customize org-babel-clojure-backend"))
(let* ((expanded (org-babel-expand-body:clojure body params))
(result-params (cdr (assq :result-params params)))
result)
(setq result
(cond
((eq org-babel-clojure-backend 'inf-clojure)
(ob-clojure-eval-with-inf-clojure expanded params))
((eq org-babel-clojure-backend 'cider)
(ob-clojure-eval-with-cider expanded params))
((eq org-babel-clojure-backend 'slime)
(ob-clojure-eval-with-slime expanded params))
((eq org-babel-clojure-backend 'bb)
(ob-clojure-eval-with-bb expanded params))))
(org-babel-result-cond result-params
result
(condition-case nil (org-babel-script-escape result)
(error result))))))
(customize-set-variable 'org-babel-clojure-backend 'bb))
(add-hook 'org-mode-hook (lambda () (require 'ob-clojure)))
#+END_SRC
2019-01-28 02:46:29 +00:00
* Scheme
Tell emacs about file extensions which should activate scheme-mode:
#+BEGIN_SRC emacs-lisp
(add-to-list 'auto-mode-alist '("\\.guile\\'" . scheme-mode))
(add-to-list 'auto-mode-alist '("\\.rkt\\'" . scheme-mode))
#+END_SRC
[[http://www.nongnu.org/geiser/geiser_1.html][Geiser]] is a Scheme IDE for Emacs that supports a bunch of common Scheme implementations.
#+BEGIN_SRC emacs-lisp
(use-package geiser
2019-11-14 21:03:13 +00:00
:commands (run-geiser)
:config
(setq geiser-active-implementations
(cl-reduce (lambda (acc val)
(if (executable-find (symbol-name val))
(cons val acc)
acc))
'(guile racket chicken chez mit chibi gambit)
:initial-value nil))
2019-01-28 02:46:29 +00:00
:general
2020-08-20 22:06:35 +00:00
(geiser-mode-map 'normal
"M-." 'geiser-edit-symbol-at-point
"M-," 'geiser-pop-symbol-stack)
2019-01-28 02:46:29 +00:00
(geiser-debug-mode-map "SPC" leader-map))
#+END_SRC
And a handy shortcut to hop into a Geiser REPL:
#+BEGIN_SRC emacs-lisp
(leader-def-key "sg" 'run-geiser)
#+END_SRC
* Common Lisp
[[https://common-lisp.net/project/slime/][SLIME]] is a set of modes and utilities for writing Common Lisp in Emacs. [[https://www.quicklisp.org/beta/][Quicklisp]] is the de-facto Common Lisp package manager. It comes with some Emacs bindings.
#+BEGIN_SRC emacs-lisp
2019-11-14 21:03:13 +00:00
(use-package slime-company
:after (slime))
2019-01-28 02:46:29 +00:00
(use-package slime
:commands (slime)
2019-01-28 02:46:29 +00:00
:config
(setq inferior-lisp-program
2020-08-20 22:06:35 +00:00
(executable-find "sbcl")
slime-contribs '(slime-repl
slime-fancy
slime-company))
2019-01-28 02:46:29 +00:00
(when (file-exists-p
2020-08-20 22:06:35 +00:00
(expand-file-name "~/quicklisp/slime-helper.el"))
(load (expand-file-name "~/quicklisp/slime-helper.el")))
(add-hook 'slime-repl-mode-hook 'smartparens-strict-mode)
2020-08-20 22:06:35 +00:00
(add-to-list 'browse-url-browser-function
'("lispworks.com/documentation/HyperSpec" . w3m-browse-url))
:general
((normal motion visual insert emacs) slime-mode-map "M-." #'slime-edit-definition))
2019-01-28 02:46:29 +00:00
(add-to-list 'auto-mode-alist '("\\.cl\\'" . lisp-mode))
#+END_SRC
Keyboard shortcut to start a SLIME REPL:
#+BEGIN_SRC emacs-lisp
(leader-def-key "sc" 'slime)
#+END_SRC
* Haskell
#+BEGIN_SRC emacs-lisp
(defun jdormit/haskell-setup ()
(local-set-key (kbd "C-c M-j") 'interactive-haskell-mode))
(use-package haskell-mode
:mode (("\\.hs\\'" . haskell-mode)))
(add-hook 'haskell-mode-hook 'jdormit/haskell-setup)
#+END_SRC
* PHP
#+BEGIN_SRC emacs-lisp
(use-package php-mode
:mode "\\.php\\'")
2019-11-14 21:03:13 +00:00
(use-package mmm-mode
:after php-mode)
2019-01-28 02:46:29 +00:00
#+END_SRC
Geben is an interface to XDebug allowing debugging PHP in Emacs:
#+BEGIN_SRC emacs-lisp
(use-package geben
:commands (geben))
#+END_SRC
Some keybindings to start and end Geben:
#+BEGIN_SRC emacs-lisp
(general-def php-mode-map "C-c C-d" #'geben)
(general-def php-mode-map "C-c C-q" #'geben-end)
#+END_SRC
2019-02-16 18:01:50 +00:00
LSP for PHP requires [[https://github.com/felixfbecker/php-language-server][php-language-server]] to be installed in ~/.composer:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(add-hook 'php-mode-hook #'lsp-deferred)
2019-01-28 02:46:29 +00:00
#+END_SRC
* YAML
#+BEGIN_SRC emacs-lisp
(use-package yaml-mode
:mode ("//.yml//'"))
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-01-28 02:46:29 +00:00
* Pharen
[[https://pharen.org][Pharen]] is a Lisp that compiles to PHP. It looks a lot like Clojure.
#+BEGIN_SRC emacs-lisp
(add-to-list 'auto-mode-alist '("\\.phn\\'" . clojure-mode))
#+END_SRC
2019-02-16 18:01:50 +00:00
* Bash
Use LSP if [[https://github.com/mads-hartmann/bash-language-server][bash-language-server]] is installed.
#+BEGIN_SRC emacs-lisp
(when (executable-find "bash-language-server")
(add-hook 'sh-mode-hook #'lsp-deferred))
2019-02-16 18:01:50 +00:00
#+END_SRC
2019-04-05 23:23:01 +00:00
* Ruby
#+BEGIN_SRC emacs-lisp
(add-hook 'ruby-mode-hook #'lsp-deferred)
2019-04-05 23:23:01 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-11-04 15:03:26 +00:00
* Rust
#+BEGIN_SRC emacs-lisp
(use-package rust-mode
:mode "\\.rs\\'"
:general
2019-11-07 17:07:13 +00:00
(rust-mode-map "C-c <tab>" #'rust-format-buffer)
:config
(add-hook 'rust-mode-hook #'lsp-deferred))
2019-11-04 15:03:26 +00:00
(use-package cargo
:after (rust-mode)
:config
(add-hook 'rust-mode-hook #'cargo-minor-mode))
#+END_SRC
2020-01-02 03:03:12 +00:00
2020-12-13 13:39:47 +00:00
* Elixir
#+BEGIN_SRC emacs-lisp
(use-package elixir-mode
:defer t)
(use-package alchemist
:hook ((elixir-mode . alchemist-mode)
(alchemist-iex-mode . company-mode)))
#+END_SRC
2019-11-04 22:08:52 +00:00
* XML
Set up hideshow for nXML mode:
#+BEGIN_SRC emacs-lisp
(add-hook 'nxml-mode-hook #'hs-minor-mode)
(add-to-list 'hs-special-modes-alist
'(nxml-mode
"<!--\\|<[^/>]*[^/]>" ;; regexp for start block
"-->\\|</[^/>]*[^/]>" ;; regexp for end block
"<!--"
nxml-forward-element
nil))
#+END_SRC
2019-12-02 22:44:30 +00:00
A function to format XML using tidy or xmllint if available, falling back to sgml-pretty-print:
#+BEGIN_SRC emacs-lisp
(defun xml-pretty-print ()
(interactive)
(let ((start (if (region-active-p) (region-beginning) (point-min)))
(end (if (region-active-p) (region-end) (point-max))))
(cond
((executable-find "tidy")
(shell-command-on-region start end "tidy -wrap 88 -q -i -xml" nil t))
((executable-find "xmllint")
(shell-command-on-region start end "xmllint --format -" nil t))
(t (sgml-pretty-print start end)))))
(general-def nxml-mode-map "C-M-\\" #'xml-pretty-print)
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-01-28 02:46:29 +00:00
* CSVs
#+BEGIN_SRC emacs-lisp
(use-package csv-mode
:mode "\\.csv\\'")
#+END_SRC
* Markdown
#+BEGIN_SRC emacs-lisp
(use-package markdown-mode
:commands (markdown-mode gfm-mode)
:mode (("README\\.md\\'" . gfm-mode)
("\\.md\\'" . markdown-mode)
("\\.markdown\\'" . markdown-mode))
:init (setq markdown-command "pandoc"))
#+END_SRC
2019-12-29 14:46:46 +00:00
Edit-indirect allows markdown-mode to edit source blocks in separate buffers:
#+BEGIN_SRC emacs-lisp
2020-01-14 15:23:55 +00:00
(use-package edit-indirect
:defer t)
2019-12-29 14:46:46 +00:00
#+END_SRC
2020-12-13 13:39:55 +00:00
* Terraform
#+BEGIN_SRC emacs-lisp
(use-package terraform-mode
:mode "\\.tf\\'")
#+END_SRC
2019-12-29 14:46:46 +00:00
2019-01-28 02:46:29 +00:00
* IELM
Enable lexical scoping in IELM:
#+BEGIN_SRC emacs-lisp
(add-hook 'ielm-mode-hook
#'(lambda ()
(interactive)
(setq lexical-binding t)))
#+END_SRC
* Ledger Mode
This mode requires that [[https://github.com/ledger/ledger][ledger]] be installed on the system.
#+BEGIN_SRC emacs-lisp
(use-package ledger-mode
:mode "\\.ledger\\'"
:config
(add-to-list 'evil-emacs-state-modes 'ledger-report-mode)
(general-def ledger-report-mode-map "SPC" leader-map)
(add-hook 'ledger-mode-hook
(lambda ()
(variable-pitch-mode 0)))
(add-hook 'ledger-report-mode-hook
(lambda ()
(variable-pitch-mode 0))))
#+END_SRC
2019-02-28 01:58:11 +00:00
** Importing
#+BEGIN_SRC emacs-lisp
(defvar ledger-file (expand-file-name "~/journal.ledger"))
(defvar bank-alist
'(("DCU Checking" . ((acct . "Assets:Checking")
(fid . "9999")))
("DCU Savings" . ((acct . "Assets:Savings")
(fid . "9999")))
("DCU Visa" . ((acct . "Liabilities:DCU Visa")
(fid . "9999")))
("Chase Visa" . ((acct . "Liabilities:Chase Visa")))))
(defun ledger-import-ofx (bank file)
(interactive
(list
2019-11-27 15:12:29 +00:00
(completing-read "Bank: "
2019-02-28 01:58:11 +00:00
(mapcar #'car bank-alist))
2019-11-27 15:12:29 +00:00
(read-file-name "OFX file: ")))
2019-02-28 01:58:11 +00:00
(if-let ((ledger-autosync (executable-find "ledger-autosync")))
(let* ((bank-def (alist-get bank bank-alist))
(acct (alist-get 'acct bank-def))
(fid (alist-get 'fid bank-def))
(cmd (concat
ledger-autosync
(if fid (concat " --fid " fid) "")
" --account '" acct "'"
" '" file "'"))
(output (shell-command-to-string cmd)))
(find-file ledger-file)
(goto-char (point-max))
(insert "\n")
(insert output)
(ledger-sort-region (point-min) (point-max))
(ledger-post-align-postings (point-min) (point-max)))
(error "Unable to find ledger-autosync")))
#+END_SRC
2019-01-28 02:46:29 +00:00
* PDFs
#+BEGIN_SRC emacs-lisp
(use-package pdf-tools
:mode ("\\.pdf\\'" . pdf-view-mode)
2020-01-07 17:45:23 +00:00
:init
(defhydra hydra-pdftools (:color blue :hint nil)
"
╭───────────┐
Move History Scale/Fit Annotations Search/Link Do │ PDF Tools │
2020-01-07 17:45:23 +00:00
╭──────────────────────────────────────────────────────────────────┴───────────╯
^^_g_^^ _B_ ^↧^ _+_ ^ ^ [_al_] list [_s_] search [_u_] revert buffer
^^^↑^^^ ^↑^ _H_ ^↑^ ↦ _W_ ↤ [_am_] markup [_o_] outline [_i_] info
^^_p_^^ ^ ^ ^↥^ _0_ ^ ^ [_at_] text [_F_] link [_d_] dark mode
^^^↑^^^ ^↓^ ╭─^─^─┐ ^↓^ ╭─^ ^─┐ [_ad_] delete [_f_] search link
2020-01-07 17:45:23 +00:00
_h_ ←pag_e__l_ _N__P__-_ _b_ [_aa_] dired
^^^↓^^^ ^ ^ ╰─^─^─╯ ^ ^ ╰─^ ^─╯ [_y_] yank
^^_n_^^ ^ ^ _r_eset slice box
^^^↓^^^
^^_G_^^
2020-01-07 17:45:23 +00:00
--------------------------------------------------------------------------------
"
2020-01-07 17:45:23 +00:00
("\\" hydra-master/body "back")
("<ESC>" nil "quit")
("al" pdf-annot-list-annotations)
("ad" pdf-annot-delete)
("aa" pdf-annot-attachment-dired)
("am" pdf-annot-add-markup-annotation)
("at" pdf-annot-add-text-annotation)
("y" pdf-view-kill-ring-save)
("+" pdf-view-enlarge :color red)
("-" pdf-view-shrink :color red)
("0" pdf-view-scale-reset)
("H" pdf-view-fit-height-to-window)
("W" pdf-view-fit-width-to-window)
("P" pdf-view-fit-page-to-window)
("n" pdf-view-next-page-command :color red)
("p" pdf-view-previous-page-command :color red)
("d" pdf-view-dark-minor-mode)
("b" pdf-view-set-slice-from-bounding-box)
("r" pdf-view-reset-slice)
("g" pdf-view-first-page)
("G" pdf-view-last-page)
("e" pdf-view-goto-page)
("o" pdf-outline)
("s" pdf-occur)
("i" pdf-misc-display-metadata)
("u" pdf-view-revert-buffer)
("F" pdf-links-action-perform)
2020-01-07 17:45:23 +00:00
("f" pdf-links-isearch-link)
("B" pdf-history-backward :color red)
("N" pdf-history-forward :color red)
("l" image-forward-hscroll :color red)
("h" image-backward-hscroll :color red))
2019-01-28 02:46:29 +00:00
:config
(pdf-tools-install)
:general
2020-01-07 17:45:23 +00:00
(pdf-view-mode-map "SPC" leader-map)
2020-10-13 16:09:10 +00:00
(normal pdf-view-mode-map "." #'hydra-pdftools/body)
(normal pdf-view-mode-map "F" #'pdf-links-action-perform)
(normal pdf-view-mode-map "C-o" #'pdf-history-backward))
2019-01-28 02:46:29 +00:00
#+END_SRC
* EPubs
#+BEGIN_SRC emacs-lisp
(defun jdormit/nov-config ()
(olivetti-mode)
(nov-render-document))
(use-package nov
:mode ("\\.epub\\'" . nov-mode)
:config
(setq nov-text-width 80)
(add-hook 'nov-mode-hook 'jdormit/nov-config)
:general
('normal nov-mode-map "r" 'nov-render-document)
('normal nov-mode-map "=" 'nov-view-source)
('normal nov-mode-map "+" 'nov-view-content-source)
('normal nov-mode-map "m" 'nov-display-metadata)
('normal nov-mode-map "n" 'nov-next-document)
('normal nov-mode-map "]" 'nov-next-document)
('normal nov-mode-map "p" 'nov-previous-document)
('normal nov-mode-map "[" 'nov-previous-document)
('normal nov-mode-map "t" 'nov-goto-toc)
('normal nov-mode-map "RET" 'nov-browse-url)
('normal nov-mode-map "<follow-link>" 'mouse-face)
('normal nov-mode-map "<mouse-2>" 'nov-browse-url)
('normal nov-mode-map "TAB" 'shr-next-link)
('normal nov-mode-map "M-TAB" 'shr-previous-link)
('normal nov-mode-map "<backtab>" 'shr-previous-link)
('normal nov-mode-map "SPC" 'nov-scroll-up)
('normal nov-mode-map "S-SPC" 'nov-scroll-down)
('normal nov-mode-map "DEL" 'nov-scroll-down)
('normal nov-mode-map "<home>" 'beginning-of-buffer)
('normal nov-mode-map "<end>" 'end-of-buffer)
('normal nov-mode-map "SPC" leader-map))
#+END_SRC
2019-10-23 20:27:19 +00:00
2019-01-28 02:46:29 +00:00
** Org mode links
First, keep a reference to filename of the .epub before nov.el blows it away:
#+BEGIN_SRC emacs-lisp
(defvar nov-epub-file)
(add-hook 'nov-mode-hook
#'(lambda ()
(message "epub file: " buffer-file-name)
(setq nov-epub-file buffer-file-name)))
#+END_SRC
That reference lets us construct a link back to the .epub:
#+BEGIN_SRC emacs-lisp
(defun org-epub-store-link ()
(when (eq major-mode 'nov-mode)
(let ((epub-name (alist-get 'title nov-metadata)))
(org-store-link-props
:type "file"
:link (concat "file://" nov-epub-file)))))
(add-hook 'org-store-link-functions 'org-epub-store-link)
#+END_SRC
* Dashboard
Instead of the *GNU Emacs* buffer on startup, display a cool dashboard:
#+BEGIN_SRC emacs-lisp
(use-package dashboard
2019-10-30 13:34:13 +00:00
:general
(dashboard-mode-map "SPC" leader-map)
2019-01-28 02:46:29 +00:00
:config
2019-11-14 21:03:13 +00:00
(with-eval-after-load 'evil
(add-to-list 'evil-emacs-state-modes 'dashboard-mode))
2019-01-28 02:46:29 +00:00
(setq dashboard-items '((recents . 5)
2019-10-30 13:34:13 +00:00
(projects . 5))
dashboard-startup-banner 'official
dashboard-set-heading-icons t
dashboard-set-file-icons t
2020-01-07 22:04:34 +00:00
dashboard-set-navigator t))
(dashboard-setup-startup-hook)
2019-01-28 02:46:29 +00:00
#+END_SRC
And hide some files from the recentf list:
2020-01-03 15:15:49 +00:00
#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'recentf
(setq recentf-exclude '(".*gcal.org"
#'backup-file-name-p)))
2020-01-03 15:15:49 +00:00
#+END_SRC
2020-04-20 14:34:10 +00:00
* Email
I use [[https://www.djcbsoftware.nl/code/mu/mu4e.html][mu/mu4e]] as my email client.
2019-01-28 02:46:29 +00:00
2020-04-20 14:34:10 +00:00
First, add it to the load path:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2020-05-18 01:39:54 +00:00
(defvar mu4e-load-path)
(setq mu4e-load-path
(cond
((file-exists-p "/usr/local/share/emacs/site-lisp/mu/mu4e")
"/usr/local/share/emacs/site-lisp/mu/mu4e")
((file-exists-p "/usr/share/emacs/site-lisp/mu4e")
"/usr/share/emacs/site-lisp/mu4e")))
2020-04-20 14:34:10 +00:00
(add-to-list 'load-path mu4e-load-path)
2019-01-28 02:46:29 +00:00
#+END_SRC
2020-04-20 14:34:10 +00:00
Then set up autoloads on the entry functions:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(autoload 'mu4e (concat mu4e-load-path "/mu4e.el") nil t)
(autoload 'mu4e-update-index (concat mu4e-load-path "/mu4e.el") nil t)
2019-01-28 02:46:29 +00:00
#+END_SRC
2020-04-20 14:34:10 +00:00
Then configure it:
#+begin_src emacs-lisp
(with-eval-after-load 'mu4e
(setq
;; General
mu4e-maildir (expand-file-name "~/.mail")
mu4e-completing-read-function 'completing-read
mu4e-attachment-dir (expand-file-name "~/Downloads")
mu4e-change-filenames-when-moving t
2020-04-22 13:51:06 +00:00
user-mail-address "jeremy.dormitzer@gmail.com"
2020-04-21 21:01:28 +00:00
mu4e-view-show-images t
mu4e-headers-skip-duplicates t
mail-user-agent 'mu4e-user-agent
;; Custom actions
mu4e-view-actions '(("capture message" . mu4e-action-capture-message)
2020-04-27 20:36:49 +00:00
("view as pdf" . mu4e-action-view-as-pdf)
("show this thread" . mu4e-action-show-thread)
("View in browser" . mu4e-action-view-in-browser))
2020-04-22 20:50:47 +00:00
;; Bookmarked searches
mu4e-bookmarks '((:name "Inbox"
2020-04-27 20:36:49 +00:00
:query (concat "maildir:/jeremy-dormitzer-gmail-com/Inbox"
" OR maildir:/jeremydormitzer-lola-com/Inbox")
:key ?i)
(:name "Unread messages"
:query "flag:unread AND NOT flag:trashed"
:key ?u)
(:name "Today's messages"
:query "date:today..now"
:key ?t)
(:name "Last 7 days"
:query "date:7d..now"
:key ?p))
2020-04-20 14:34:10 +00:00
;; Getting mail
mu4e-get-mail-command "mbsync -a"
;; Sending mail
2020-06-17 17:08:24 +00:00
send-mail-function #'message-send-mail-with-sendmail
message-send-mail-function #'message-send-mail-with-sendmail
2020-04-20 14:34:10 +00:00
sendmail-program (executable-find "msmtp")
;; Let Gmail handle putting sent messages in the sent folder
mu4e-sent-messages-behavior 'delete
2020-04-21 21:01:28 +00:00
;; Move to trash folder instead of adding trash flag for Gmail mailboxes
mu4e-move-to-trash-patterns '("jeremy-dormitzer-gmail-com" "jeremydormitzer-lola-com")
;; HTML email rendering
shr-use-colors nil
2020-04-20 14:34:10 +00:00
;; Make sure mu4e knows about my different accounts
mu4e-context-policy 'ask
mu4e-compose-context-policy 'ask
mu4e-contexts
2020-04-21 21:01:28 +00:00
`(,(make-mu4e-context
2020-06-14 16:27:33 +00:00
:name "Lola Gmail"
:match-func (lambda (msg)
(when msg
(or
(mu4e-message-contact-field-matches msg
:to
"lola.com")
(string-match-p
"jeremydormitzer-lola-com"
(mu4e-message-field msg :path)))))
:vars '((user-mail-address . "jeremydormitzer@lola.com")
(mu4e-compose-signature . "Best regards, \nJeremy Dormitzer \nLola.com")
(message-signature-insert-empty-line . t)
(mu4e-sent-folder . "/jeremydormitzer-lola-com/Sent")
(mu4e-drafts-folder . "/jeremydormitzer-lola-com/Drafts")
(mu4e-refile-folder . "/jeremydormitzer-lola-com/Archive")
(mu4e-trash-folder . "/jeremydormitzer-lola-com/Trash")
(message-sendmail-extra-arguments
. ("-a" "jeremydormitzer-lola-com"))))
,(make-mu4e-context
2020-04-27 20:36:49 +00:00
:name "Personal Gmail"
:match-func (lambda (msg)
(when msg
(string-match-p
"jeremy-dormitzer-gmail-com"
(mu4e-message-field msg :path))))
:vars '((user-mail-address . "jeremy.dormitzer@gmail.com")
(mu4e-compose-signature . nil)
2020-04-27 20:36:49 +00:00
(mu4e-sent-folder . "/jeremy-dormitzer-gmail-com/Sent")
(mu4e-drafts-folder . "/jeremy-dormitzer-gmail-com/Drafts")
(mu4e-refile-folder . "/jeremy-dormitzer-gmail-com/Archive")
(mu4e-trash-folder . "/jeremy-dormitzer-gmail-com/Trash")
(message-sendmail-extra-arguments
2020-06-14 16:27:33 +00:00
. ("-a" "jeremy-dormitzer-gmail-com"))))))
2020-04-21 21:01:28 +00:00
;; Custom mark function to mark messages matching the current message
(defun mu4e-mark-matching-pred (msg from)
(mu4e-message-contact-field-matches msg :from from))
(defun mu4e-mark-matching-input ()
(let* ((msg (mu4e-message-at-point t)))
2020-04-27 20:36:49 +00:00
(if (not msg)
(error "No message at point")
(cdr (mu4e-message-field msg :from)))))
2020-04-21 21:01:28 +00:00
(setq mu4e-headers-custom-markers
2020-04-27 20:36:49 +00:00
'(("Older than"
(lambda
(msg date)
(time-less-p
(mu4e-msg-field msg :date)
date))
(lambda nil
(mu4e-get-time-date "Match messages before: ")))
("Newer than"
(lambda
(msg date)
(time-less-p date
(mu4e-msg-field msg :date)))
(lambda nil
(mu4e-get-time-date "Match messages after: ")))
("Bigger than"
(lambda
(msg bytes)
(>
(mu4e-msg-field msg :size)
(* 1024 bytes)))
(lambda nil
(read-number "Match messages bigger than (Kbytes): ")))
("Matching current message from: field"
(lambda (msg from)
(mu4e-message-contact-field-matches msg :from from))
(lambda ()
(let* ((msg (mu4e-message-at-point t)))
(if (not msg)
(error "No message at point")
(cdar (mu4e-message-field msg :from))))))))
(add-hook 'mu4e-compose-pre-hook
(lambda ()
(set
(make-local-variable '*should-delete-trailing-whitespace*)
nil))))
2020-04-20 14:34:10 +00:00
#+end_src
2019-01-28 02:46:29 +00:00
Support sending attachments from Dired:
#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'dired
(require 'gnus-dired)
;; make the `gnus-dired-mail-buffers' function also work on
;; message-mode derived modes, such as mu4e-compose-mode
(defun gnus-dired-mail-buffers ()
"Return a list of active message buffers."
(let (buffers)
(save-current-buffer
(dolist (buffer (buffer-list t))
(set-buffer buffer)
(when (and (derived-mode-p 'message-mode)
(null message-sent-message-via))
(push (buffer-name buffer) buffers))))
(nreverse buffers)))
(setq gnus-dired-mail-mode 'mu4e-user-agent)
(add-hook 'dired-mode-hook 'turn-on-gnus-dired-mode))
#+END_SRC
Support sending rich-text emails via Markdown:
#+BEGIN_SRC emacs-lisp
(defvar *message-md-pandoc-html-template*
"<html>
<head>
<meta charset=\"utf-8\" />
</head>
<body>
$body$
</body>
</html>"
"The default template used when converting markdown to HTML via pandoc.")
(defun gfm->html (gfm &optional template)
"Converts GitHub-flavored markdown to HTML via pandoc.
By default, the template `*message-md-pandoc-html-template*' is used,
but this can be overridden with the TEMPLATE argument."
(unless (executable-find "pandoc")
(error "Pandoc not found, unable to convert"))
(let ((template-file (make-temp-file "gfm->html-template"
nil ".html"
(or template *message-md-pandoc-html-template*))))
(with-temp-buffer
(insert gfm)
(unless
(= 0
(call-process-region (point-min) (point-max) "pandoc" t t nil
"--template" template-file "--quiet"
"-f" "gfm" "-t" "html"))
(error "Markdown to HTML conversion failed: %s"
(buffer-substring (point-min) (point-max))))
(buffer-substring (point-min) (point-max)))))
(defun mml-node->str (node)
"Converts a parsed MML node back to an MML string."
(let ((node-name (car node))
(node-alist (cdr node)))
(format "<#%s%s>\n%s"
node-name
(cl-reduce (lambda (acc pair)
(if (equal (car pair) 'contents)
acc
(concat acc (format " %s=%S"
(car pair)
(cdr pair)))))
node-alist
:initial-value "")
(cdr (assoc 'contents node-alist))
node-name)))
(defun multipart-message-as-string (plain html inline attachments)
"Given MML nodes PLAIN, and HTML and MML node lists INLINE and
ATTACHMENTS, constructs a multipart MML email and returns it as a
string."
(let* ((alternative (concat "<#multipart type=alternative>\n"
(mml-node->str plain) "\n"
(mml-node->str html) "\n"
"<#/multipart>"))
(related (when inline
(concat "<#multipart type=related>\n"
alternative "\n"
(cl-reduce
(lambda (acc node)
(concat acc (mml-node->str node) "\n"))
inline
:initial-value "")
"<#/multipart>")))
(mixed (when attachments
(concat "<#multipart type=mixed>\n"
(or related alternative) "\n"
(cl-reduce
(lambda (acc node)
(concat acc (mml-node->str node) "\n"))
attachments
:initial-value "")
"<#/multipart>"))))
(or mixed related alternative)))
(defun assoc-mml-node (key node)
(cdr (assoc key (cdr node))))
(defun multipart-html-message (raw)
"Creates a multipart HTML email with a text part and an html part."
(with-temp-buffer
(insert raw)
(let* ((parsed (mml-parse))
(plain (cl-reduce
(lambda (acc node)
(if (not (equal (assoc-mml-node 'type node) "text/plain"))
acc
`(part (type . "text/plain")
(contents . ,(concat (cdaddr acc)
(cdaddr node))))))
parsed
:initial-value '(part (type . "text/plain")
(contents . ""))))
(html `(part (type . "text/html")
(contents . ,(gfm->html (cdaddr plain)))))
(inline (nreverse
(cl-reduce
(lambda (acc node)
(if (not (equal (assoc-mml-node 'disposition node)
"inline"))
acc
(cons node acc)))
parsed
:initial-value nil)))
(attachments (nreverse
(cl-reduce
(lambda (acc node)
(if (not (equal (assoc-mml-node 'disposition node)
"attachment"))
acc
(cons node acc)))
parsed
:initial-value nil))))
(multipart-message-as-string plain
html
inline
attachments))))
(defun convert-message-to-markdown ()
2020-04-23 21:12:24 +00:00
"Convert the message in the current buffer to a multipart HTML email.
The HTML is rendered by treating the message content as Markdown."
(interactive)
(let* ((begin
(save-excursion
(goto-char (point-min))
(search-forward mail-header-separator)))
(end (point-max))
(raw-body (buffer-substring begin end)))
(undo-boundary)
(delete-region begin end)
(save-excursion
(goto-char begin)
(newline)
(insert (multipart-html-message raw-body)))))
(defun message-md-send (&optional arg)
"Convert the current buffer and send it.
If given prefix arg ARG, skips markdown conversion."
(interactive "P")
(unless arg
(convert-message-to-markdown))
(message-send))
(defun message-md-send-and-exit (&optional arg)
"Convert the current buffer and send it, then exit from mail buffer.
If given prefix arg ARG, skips markdown conversion."
(interactive "P")
(unless arg
(convert-message-to-markdown))
(message-send-and-exit))
(with-eval-after-load 'message
2020-04-23 21:12:24 +00:00
(define-key message-mode-map (kbd "C-c C-s") #'message-md-send)
(define-key message-mode-map (kbd "C-c C-c") #'message-md-send-and-exit))
;; Handle replies to HTML emails as well
(defun html->gfm (html)
(unless (executable-find "pandoc")
(error "Pandoc not found, unable to convert"))
(with-temp-buffer
(insert html)
(unless
(= 0
(call-process-region (point-min) (point-max) "pandoc" t t nil
"--quiet"
"-f" "html-native_divs-native_spans"
"-t" "gfm"))
(error "HTML to mardkwon conversion failed: %s"
(buffer-substring (point-min) (point-max))))
(buffer-substring (point-min) (point-max))))
(with-eval-after-load 'mu4e
(defvar message-md-rich-text-reply t)
(defvar message-md--inhibit-rich-text-reply nil)
(defun html-to-md-command (msg)
"Returns the text of MSG as Markdown."
(if (mu4e-msg-field msg :body-html)
(html->gfm (mu4e-msg-field msg :body-html))
(mu4e-msg-field msg :body-txt)))
(defun mu4e-draft-cite-original-advice (oldfn &rest args)
(let ((res (if (and message-md-rich-text-reply
(not message-md--inhibit-rich-text-reply))
(let ((mu4e-view-prefer-html t)
(mu4e-html2text-command #'html-to-md-command))
(apply oldfn args))
(apply oldfn args))))
(setq message-md--inhibit-rich-text-reply nil)
res))
(defun mu4e-compose-reply-advice (oldfn &rest args)
(when current-prefix-arg
(setq message-md--inhibit-rich-text-reply t))
(apply oldfn args))
(advice-add 'mu4e~draft-cite-original :around #'mu4e-draft-cite-original-advice)
(advice-add 'mu4e-compose-reply :around #'mu4e-compose-reply-advice))
;; Add an "X-Attachment-Id" header to MIME stuff as well as a Content-Id:
(with-eval-after-load 'mml
(defun mml-insert-mime-headers (cont type charset encoding flowed)
(let (parameters id disposition description)
(setq parameters
(mml-parameter-string
cont mml-content-type-parameters))
(when (or charset
parameters
flowed
(not (equal type mml-generate-default-type))
mml-insert-mime-headers-always)
(when (consp charset)
(error
"Can't encode a part with several charsets"))
(insert "Content-Type: " type)
(when charset
(mml-insert-parameter
(mail-header-encode-parameter "charset" (symbol-name charset))))
(when flowed
(mml-insert-parameter "format=flowed"))
(when parameters
(mml-insert-parameter-string
cont mml-content-type-parameters))
(insert "\n"))
(when (setq id (cdr (assq 'id cont)))
(insert "Content-ID: " id "\n"))
(when (setq x-attachment-id (cdr (assq 'x-attachment-id cont)))
(insert "X-Attachment-Id: " x-attachment-id "\n"))
(setq parameters
(mml-parameter-string
cont mml-content-disposition-parameters))
(when (or (setq disposition (cdr (assq 'disposition cont)))
parameters)
(insert "Content-Disposition: "
(or disposition
(mml-content-disposition type (cdr (assq 'filename cont)))))
(when parameters
(mml-insert-parameter-string
cont mml-content-disposition-parameters))
(insert "\n"))
(unless (eq encoding '7bit)
(insert (format "Content-Transfer-Encoding: %s\n" encoding)))
(when (setq description (cdr (assq 'description cont)))
(insert "Content-Description: ")
(setq description (prog1
(point)
(insert description "\n")))
(mail-encode-encoded-word-region description (point))))))
(defun message-md-insert-inline-image (image type description)
(interactive
(let* ((file (mml-minibuffer-read-file "Insert image: "))
(type (if current-prefix-arg
(or (mm-default-file-encoding file)
"application/octet-stream")
(mml-minibuffer-read-type file)))
(description (if current-prefix-arg
nil
(mml-minibuffer-read-description))))
(list file type description)))
(let ((id (format "%s%s" (s-snake-case description) (random))))
(insert (format "![%s](cid:%s)" description id))
(save-excursion
(goto-char (point-max))
(newline)
(insert
(format (concat "<#part id=\"<%s>\" x-attachment-id=%S "
"type=%S filename=%S disposition=inline description=%s>")
id id type image description)))))
;; The default message signature begins with "-- \n". To makes this
;; markdown compatible, we need to add an extra space at the end: "-- \n"
(defun message-md-add-space-to-sig-separator (&rest args)
(save-excursion
(when (search-backward "--" nil t)
(forward-char 2)
(insert " "))))
(advice-add 'message-insert-signature :after
#'message-md-add-space-to-sig-separator)
#+END_SRC
Global keybindings:
#+begin_src emacs-lisp
(leader-def-key "am" #'mu4e)
#+end_src
2020-04-21 21:01:28 +00:00
Keybindings within mu4e:
#+begin_src emacs-lisp
(with-eval-after-load 'mu4e
(general-def '(normal motion insert emacs) mu4e-headers-mode-map "t" #'mu4e-headers-mark-thread)
(general-def '(normal motion insert emacs) mu4e-view-mode-map "t" #'mu4e-view-mark-thread))
#+end_src
Mu4e uses shr to render HTML emails. Unfortunately the shr function
that sets faces in the rendered document has a bug: it appends the shr
faces to the existing face-list, rather than prepending the shr
face. This means that e.g. links don't actually get rendered correctly
if there is some non-link face already on the text. The fix:
#+BEGIN_SRC emacs-lisp
2020-04-22 14:07:21 +00:00
(with-eval-after-load 'shr
(defun shr-add-font (start end type)
(save-excursion
(goto-char start)
(while (< (point) end)
(when (bolp)
(skip-chars-forward " "))
;; Remove the APPEND argument to add-face-text-property
;; so the face ends up at the head of the face list
(add-face-text-property (point) (min (line-end-position) end) type)
(if (< (line-end-position) end)
(forward-line 1)
(goto-char end))))))
#+END_SRC
2019-01-28 02:46:29 +00:00
* w3m
Browsing the web from Emacs. Relies on having [[http://w3m.sourceforge.net/][w3m]] installed.
#+BEGIN_SRC emacs-lisp
(use-package w3m
:commands (w3m
w3m-browse-url
w3m-search-new-session)
2020-03-05 00:41:01 +00:00
:init
2019-01-28 02:46:29 +00:00
(setq w3m-home-page "https://start.duckduckgo.com"
w3m-search-default-engine "duckduckgo"
w3m-cookie-reject-domains '("www.wsj.com"
"www.bbc.com"
"www.nytimes.com"
"www.washingtonpost.com")
2020-07-21 13:44:05 +00:00
w3m-use-tab t
w3m-use-tab-line t
2020-05-11 16:41:55 +00:00
browse-url-browser-function '(("nytimes.com" . w3m-browse-url)
("wsj.com" . w3m-browse-url)
("." . browse-url-default-browser)))
2020-02-07 15:29:34 +00:00
:general
('normal w3m-mode-map "SPC" leader-map)
('(normal visual motion) w3m-mode-map "C-f" #'w3m-scroll-up-or-next-url)
('(normal visual motion) w3m-mode-map "C-b" #'w3m-scroll-down-or-previous-url)
2020-02-26 18:11:09 +00:00
('normal w3m-mode-map "J" #'w3m-previous-buffer)
('normal w3m-mode-map "K" #'w3m-next-buffer)
2020-02-07 15:29:34 +00:00
('normal w3m-mode-map "gs" #'w3m-search)
('normal w3m-mode-map "gS" #'w3m-search-new-session)
('normal w3m-mode-map "gl" #'link-hint-open-link)
('normal w3m-mode-map "gc" #'link-hint-copy-link)
('normal w3m-mode-map "zc" #'w3m-print-current-url))
2019-01-28 02:46:29 +00:00
(jdormit/define-prefix "aw" "w3m")
(leader-def-key "aww" 'w3m)
(leader-def-key "aws" 'w3m-search-new-session)
(leader-def-key "awb" 'w3m-browse-url)
#+END_SRC
I mostly want `browse-url-at-point` to open stuff in Firefox, but in some cases I want it within Emacs:
#+BEGIN_SRC emacs-lisp
2020-06-19 14:36:02 +00:00
(setq browse-url-generic-program
(cond
((executable-find "open") "open")
((executable-find "xdg-open") "xdg-open")))
2020-06-19 14:36:02 +00:00
2019-01-28 02:46:29 +00:00
(defun browse-url-at-point-w3m ()
"Opens the URL at point in w3m"
(interactive)
(let ((browse-url-browser-function 'w3m-browse-url))
(if (eq major-mode 'org-mode)
(org-open-at-point)
2020-06-19 14:36:02 +00:00
(browse-url-at-point))))
2019-01-28 02:46:29 +00:00
(leader-def-key "awB" 'browse-url-at-point-w3m)
#+END_SRC
I want to be able to set a custom "Referer" header by setting the variable `jdormit/w3m-referers`. I also want to be able to set websites that cookies will never get sent to:
#+BEGIN_SRC emacs-lisp
(defvar jdormit/w3m-referers nil)
(defvar jdormit/w3m-no-cookie-sites nil)
(defun get-referer (url referer-list)
"Retrieve the referer specified by url in referer-list"
(when (not (eq nil referer-list))
(let ((first (car referer-list))
(rest (cdr referer-list)))
(if (string-match-p (car first) url)
(cdr first)
(get-referer url rest)))))
(defun should-not-set-cookie-p (url no-cookie-sites)
"Non-nil if cookies should not be sent to url"
(when (not (eq nil no-cookie-sites))
(if (string-match-p (car no-cookie-sites) url)
t
(should-not-set-cookie-p url (cdr no-cookie-sites)))))
(advice-add 'w3m-header-arguments :around
(lambda (w3m-header-arguments &rest r)
(cl-destructuring-bind
(method url temp-file body referer content-type) r
(let ((w3m-use-cookies
(if (should-not-set-cookie-p url jdormit/w3m-no-cookie-sites)
2019-10-23 20:27:19 +00:00
nil
2019-01-28 02:46:29 +00:00
w3m-use-cookies)))
2019-10-23 20:27:19 +00:00
(if-let ((referer (get-referer url jdormit/w3m-referers)))
2019-01-28 02:46:29 +00:00
(funcall w3m-header-arguments
method
url
temp-file
body
referer
content-type)
(apply w3m-header-arguments r))))))
#+END_SRC
And here are the websites where I want custom referers and/or no cookies:
#+BEGIN_SRC emacs-lisp
(setq jdormit/w3m-referers '(("www.wsj.com" . "https://google.com")
("www.nytimes.com" . "https://google.com")
("www.newyorker.com" . "https://google.com")
("www.economist.com" . "https://google.com")))
(setq jdormit/w3m-no-cookie-sites '("www.wsj.com"
"www.nytimes.com"
"www.economist.com"
"www.newyorker.com"))
#+END_SRC
Render =<code>= and =<pre>= blocks in a different face. Which face can be set by setting or customizing the variable =w3m-code-block-face=. It defaults to ='fixed-pitch=:
#+BEGIN_SRC emacs-lisp
(setq w3m-code-open-delimiter "{{{W3M_CODE_BLOCK_BEGIN}}}")
(setq w3m-code-close-delimiter "{{{W3M_CODE_BLOCK_END}}}")
(defun w3m-filter-code-blocks (url)
(w3m-tag-code-block-filter "code")
(w3m-tag-code-block-filter "pre"))
(defun w3m-tag-code-block-filter (tag)
(goto-char (point-min))
(let ((open-tag-re (format "<%s[ \t\r\f\n]*[^>]*>" tag))
(close-tag-re (format "</%s[ \t\r\f\n]*>" tag)))
(while (re-search-forward open-tag-re nil t)
(let ((start (match-beginning 0)))
(when (re-search-forward close-tag-re nil t)
(goto-char start)
(insert w3m-code-open-delimiter)
(goto-char (+ (string-width w3m-code-open-delimiter) (match-end 0)))
(insert w3m-code-close-delimiter))))))
(defcustom w3m-code-block-face 'fixed-pitch
"Face for <code> and <pre> blocks in w3m")
(defun w3m-fontify-code-block ()
(goto-char (point-min))
(while (search-forward w3m-code-open-delimiter nil t)
(let ((begin-block-start (match-beginning 0))
(begin-block-end (match-end 0))
(code-start (match-end 0)))
(when (search-forward w3m-code-close-delimiter nil t)
(w3m-add-face-property code-start (match-beginning 0) w3m-code-block-face)
(delete-region (match-beginning 0) (match-end 0)))
(delete-region begin-block-start begin-block-end)
(goto-char (point-min)))))
;(add-hook 'w3m-fontify-before-hook 'w3m-fontify-code-block)
#+END_SRC
Add a w3m filter to handle the code block delimiters:
#+BEGIN_SRC emacs-lisp
;; (with-eval-after-load 'w3m-filter
;; (add-to-list 'w3m-filter-configuration
;; '(t "Render code blocks with a different face" ".*" w3m-filter-code-blocks)))
#+END_SRC
* Wakatime
[[https://wakatime.com/emacs][Wakatime]] is a tool that tracks how much time you spend coding and various metrics about your development activity.
It needs a helper script to work properly.
#+BEGIN_SRC emacs-lisp
2019-02-07 00:14:56 +00:00
(use-package wakatime-mode
2020-04-20 13:28:23 +00:00
:if (executable-find "wakatime")
2019-02-07 00:14:56 +00:00
:init
(setq wakatime-api-key (password-store-get "wakatime-api-key")
2020-04-20 13:28:23 +00:00
wakatime-cli-path (executable-find "wakatime"))
2019-02-07 00:14:56 +00:00
:config
(global-wakatime-mode)
;; global-wakatime-mode breaks recovering autosaves for some reason
(advice-add 'recover-this-file :around
(lambda (oldfn &rest args)
(let ((wakatime-was-enabled global-wakatime-mode))
(when wakatime-was-enabled
(global-wakatime-mode -1))
(apply oldfn args)
(when wakatime-was-enabled
(global-wakatime-mode))))))
2019-01-28 02:46:29 +00:00
#+END_SRC
* Elfeed
Elfeed is a feed reader for Emacs.
#+BEGIN_SRC emacs-lisp
(defun elfeed-setup-hook ()
(set (make-local-variable 'browse-url-browser-function)
2020-05-05 20:55:35 +00:00
'w3m-browse-url)
2019-01-28 02:46:29 +00:00
(set (make-local-variable 'jdormit/w3m-referer) "https://www.google.com")
2021-01-15 21:55:36 +00:00
(smartparens-mode -1)
(evil-smartparens-mode -1))
2019-01-28 02:46:29 +00:00
(use-package elfeed
:commands elfeed
:config
2021-01-15 21:55:36 +00:00
(add-hook 'elfeed-search-mode-hook 'elfeed-setup-hook)
2020-06-14 16:27:53 +00:00
(setq-default elfeed-search-filter "@6-months-ago +unread")
2019-01-28 02:46:29 +00:00
(add-to-list 'evil-normal-state-modes 'elfeed-search-mode)
(add-to-list 'evil-normal-state-modes 'elfeed-show-mode)
(setq elfeed-feeds
2020-06-14 16:27:53 +00:00
'(;; ("http://www.wsj.com/xml/rss/3_7085.xml" news)
;; ("https://www.wsj.com/xml/rss/3_7014.xml" news)
;; ("http://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml" news)
;; ("https://www.newyorker.com/feed/everything" news)
;; ("https://www.economist.com/sections/business-finance/rss.xml" news)
;; ("https://www.economist.com/sections/economics/rss.xml" news)
("https://taibbi.substack.com/feed" news)
;; NYTimes - The Morning newsletter
("https://www.kill-the-newsletter.com/feeds/lb5od7xsvr3nwmg9qp2a.xml" news)
2020-07-21 13:44:12 +00:00
;; Boston Globe - Today's Headlines
("https://www.kill-the-newsletter.com/feeds/nhmomrn756ihlqorrg5w.xml" news)
("http://syndication.boston.com/news/local/walker?mode=rss_10" news)
("https://metaredux.com/feed.xml" clojure blog)
("https://lambdaisland.com/feeds/blog.atom" clojure blog)
2020-06-14 16:27:53 +00:00
("https://emacsredux.com/atom.xml" emacs)
("https://sachachua.com/blog/category/emacs-news/feed" emacs)
2020-07-21 13:44:12 +00:00
("https://mxb.dev/feed.xml" web blog)
2020-06-17 17:08:43 +00:00
"https://feed.tedium.co/"
"https://joy.recurse.com/feed.atom"
;; Changelog Weekly
"https://www.kill-the-newsletter.com/feeds/cfiasax3ct12r7b9svjq.xml"
2020-07-21 13:44:12 +00:00
("https://wiki.xxiivv.com/links/rss.xml" blog)
("https://jeremydormitzer.com/blog/feed.xml" my-website)
("https://fossegr.im/feed.xml" emacs blog)
2020-07-25 20:42:28 +00:00
("https://nullprogram.com/feed/" blog)
;; The Economist - This Week
("https://www.kill-the-newsletter.com/feeds/x3cwlql9hhc5pn39ci31.xml" news)
;; The Economist - Today
("https://www.kill-the-newsletter.com/feeds/rl6dr7kiotn6kn2ry6aa.xml" news)
;; AllSides Weekly Newsletter
2020-10-10 14:04:32 +00:00
("https://kill-the-newsletter.com/feeds/31flkb42eg3v31yu7ecm.xml" news)
2020-10-18 20:19:33 +00:00
("https://joshwcomeau.com/rss.xml" web blog)
("https://victoria.dev/index.xml" blog))
2021-01-15 21:55:36 +00:00
shr-use-colors nil)
:general
('normal elfeed-search-mode-map "q" 'elfeed-search-quit-window)
('normal elfeed-search-mode-map "C-r" 'elfeed-search-update--force)
('normal elfeed-search-mode-map "R" 'elfeed-search-fetch)
('normal elfeed-search-mode-map "RET" 'elfeed-search-show-entry)
('normal elfeed-search-mode-map "s" 'elfeed-search-live-filter)
('normal elfeed-search-mode-map "S" 'elfeed-search-set-filter)
('normal elfeed-search-mode-map "B" 'elfeed-search-browse-url)
('normal elfeed-search-mode-map "y" 'elfeed-search-yank)
('normal elfeed-search-mode-map "u" 'elfeed-search-tag-all-unread)
('normal elfeed-search-mode-map "r" 'elfeed-search-untag-all-unread)
('normal elfeed-search-mode-map "n" 'next-line)
('normal elfeed-search-mode-map "p" 'previous-line)
('normal elfeed-search-mode-map "+" 'elfeed-search-tag-all)
('normal elfeed-search-mode-map "-" 'elfeed-search-untag-all)
('normal elfeed-show-mode-map "d" 'elfeed-show-save-enclosure)
('normal elfeed-show-mode-map "q" 'elfeed-kill-buffer)
('normal elfeed-show-mode-map "r" 'elfeed-show-refresh)
('normal elfeed-show-mode-map "n" 'elfeed-show-next)
('normal elfeed-show-mode-map "p" 'elfeed-show-prev)
('normal elfeed-show-mode-map "s" 'elfeed-show-new-live-search)
('normal elfeed-show-mode-map "B" 'elfeed-show-visit)
('normal elfeed-show-mode-map "y" 'elfeed-show-yank)
('normal elfeed-show-mode-map "u" (elfeed-expose #'elfeed-show-tag 'unread))
('normal elfeed-show-mode-map "+" 'elfeed-show-tag)
('normal elfeed-show-mode-map "-" 'elfeed-show-untag)
('normal elfeed-show-mode-map "\t" 'shr-next-link)
('normal elfeed-show-mode-map [tab] 'shr-next-link)
('normal elfeed-show-mode-map "\e\t" 'shr-previous-link)
('normal elfeed-show-mode-map [backtab] 'shr-previous-link)
('normal elfeed-show-mode-map [mouse-2] 'shr-browse-url)
('normal elfeed-show-mode-map "A" 'elfeed-show-add-enclosure-to-playlist)
('normal elfeed-show-mode-map "P" 'elfeed-show-play-enclosure)
('normal elfeed-show-mode-map "M-<return>" (lambda () (interactive) (shr-browse-url t))))
2019-01-28 02:46:29 +00:00
#+END_SRC
Keybinding for opening Elfeed:
#+BEGIN_SRC emacs-lisp
(leader-def-key "al" 'elfeed)
#+END_SRC
* Undo Tree
#+BEGIN_SRC emacs-lisp
(use-package undo-tree
:init
(global-undo-tree-mode)
(leader-def-key "bu" 'undo-tree-visualize)
:general
('normal "u" #'undo-tree-undo)
('normal "C-r" #'undo-tree-redo)
(undo-tree-visualizer-mode-map "SPC" leader-map))
#+END_SRC
* Emojify
Because emojis make everything better.
#+BEGIN_SRC emacs-lisp
2019-11-14 21:03:13 +00:00
(use-package emojify
:commands (emojify-mode
emojify-apropos-emoji
2020-03-19 22:31:57 +00:00
emojify-insert-emoji)
:hook
((emacs-startup . emojify-mode))
:init
(leader-def-key "te" 'emojify-mode))
2019-01-28 02:46:29 +00:00
#+END_SRC
* Calc
#+BEGIN_SRC emacs-lisp
(leader-def-key "ac" 'calc)
#+END_SRC
* Deadgrep
A nice Emacs UI over [[https://github.com/BurntSushi/ripgrep#installation][ripgrep]].
#+BEGIN_SRC emacs-lisp
(use-package deadgrep
:commands deadgrep
:general
(deadgrep-mode-map "SPC" leader-map))
2019-01-28 02:46:29 +00:00
(leader-def-key "fg" 'deadgrep)
#+END_SRC
* RCIRC
IRC in Emacs, just in case anyone actually still uses it...
Channels:
#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'rcirc
2019-02-04 13:24:29 +00:00
(setq rcirc-server-alist
`(("znc.jeremydormitzer.com"
:port 3000
:nick "jdormit"
:user-name "jdormit"
:password ,(password-store-get "znc.jeremydormitzer.com")))))
2019-01-28 02:46:29 +00:00
#+END_SRC
Key bindings:
#+BEGIN_SRC emacs-lisp
(leader-def-key "ai" 'irc)
#+END_SRC
Use evil keybindings by default:
#+BEGIN_SRC emacs-lisp
2019-10-23 20:27:19 +00:00
(add-to-list 'evil-normal-state-modes 'rcirc-mode)
2019-01-28 02:46:29 +00:00
#+END_SRC
* dumb-jump
[[https://github.com/jacktasia/dumb-jump][Dumb-jump]] uses ripgrep and some algorithms based on the major-mode to implement jump-to-definition.
#+BEGIN_SRC emacs-lisp
(use-package dumb-jump
2019-11-14 21:03:13 +00:00
:commands (dumb-jump-go dumb-jump-prompt)
2019-01-28 02:46:29 +00:00
:config (dumb-jump-mode))
(leader-def-key "cj" 'dumb-jump-go)
(leader-def-key "cp" 'dumb-jump-go-prompt)
#+END_SRC
* Dictionary
This package looks up word definitions online.
#+BEGIN_SRC emacs-lisp
(use-package define-word
2020-07-21 13:40:33 +00:00
:commands (define-word define-word-at-point)
:general
(normal "gl" #'define-word-at-point)
(normal "gL" #'define-word))
2019-01-28 02:46:29 +00:00
#+END_SRC
* Gnus
An ancient newsreader.
#+BEGIN_SRC emacs-lisp
(leader-def-key "ag" 'gnus)
(general-def gnus-mode-map "SPC" leader-map)
#+END_SRC
It can read email:
#+BEGIN_SRC emacs-lisp
(setq gnus-select-method '(nnnil "")
gnus-secondary-select-methods
'((nnmaildir "jeremy@dormitzer.net"
2019-10-23 20:27:19 +00:00
(directory "~/.mail/jeremy-dormitzer-net"))
2019-01-28 02:46:29 +00:00
(nnmaildir "jeremy@getpterotype.com"
(directory "~/.mail/jeremy-getpterotype-com"))
(nnmaildir "jeremy.dormitzer@gmail.com"
2019-10-23 20:27:19 +00:00
(directory "~/.mail/jeremy-dormitzer-gmail-com"))
2019-01-28 02:46:29 +00:00
(nnmaildir "jdormitzer@hubspot.com"
(directory "~/.mail/jdormitzer-hubspot-com")))
2019-10-23 20:27:19 +00:00
mm-text-html-renderer 'gnus-w3m)
2019-01-28 02:46:29 +00:00
#+END_SRC
Or Gnus can read RSS feeds directly:
#+BEGIN_SRC emacs-lisp
;; (add-to-list 'gnus-secondary-select-methods
;; '(nnrss "http://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml"))
#+END_SRC
* Dired
** Dired-X
#+BEGIN_SRC emacs-lisp
(defun dired-x-setup ()
(require 'dired-x)
(dired-omit-mode 1))
(add-hook 'dired-mode-hook #'dired-x-setup)
#+END_SRC
** Utility functions
#+BEGIN_SRC emacs-lisp
(defun dired-counsel-find-file (&optional initial-input)
(interactive)
(let ((default-directory (dired-current-directory)))
(counsel-find-file initial-input)))
(defun dired-create-empty-file-in-current-dir (file)
(interactive (list (let ((default-directory (dired-current-directory)))
(read-file-name "Create empty file: "))))
(dired-create-empty-file file))
(defun dired-do-rename-in-current-dir (&optional arg)
(interactive "P")
(let ((default-directory (dired-current-directory)))
(dired-do-rename arg)))
#+END_SRC
** Keybindings
#+BEGIN_SRC emacs-lisp
(general-def 'normal dired-mode-map
"f" #'dired-create-empty-file-in-current-dir
[remap find-file] 'dired-counsel-find-file
[remap counsel-find-file] 'dired-counsel-find-file
[remap dired-do-rename] 'dired-do-rename-in-current-dir)
#+END_SRC
2020-06-03 20:51:55 +00:00
** Dired subtree
#+BEGIN_SRC emacs-lisp
(use-package dired-subtree
:init
(defun dired-counsel-find-file (&optional initial-input)
(interactive)
(let ((default-directory (dired-current-directory)))
(counsel-find-file initial-input)))
2020-06-03 20:51:55 +00:00
:general
(normal dired-mode-map "TAB" 'dired-subtree-toggle)
:config
(advice-add 'dired-subtree-toggle :after (lambda () (revert-buffer))))
#+END_SRC
2020-06-14 16:28:04 +00:00
** Dired narrow
#+BEGIN_SRC emacs-lisp
(use-package dired-narrow
:general
(normal dired-mode-map "/" #'dired-narrow))
#+END_SRC
2020-05-28 16:19:20 +00:00
** Dired sidebar
#+BEGIN_SRC emacs-lisp
2020-05-28 16:19:20 +00:00
(use-package dired-sidebar
:commands (dired-sidebar-toggle-sidebar)
:init
(leader-def-key "d" #'dired-sidebar-toggle-sidebar)
(add-hook 'dired-sidebar-mode-hook
(lambda ()
(unless (file-remote-p default-directory)
(auto-revert-mode))))
2020-06-03 20:10:39 +00:00
:config
(with-eval-after-load 'all-the-icons-dired
;; Display chevrons next to directories
(defun all-the-icons-icon-for-dir-with-chevron (dir &optional chevron padding initial-padding &rest arg-overrides)
"Format an icon for DIR with CHEVRON similar to tree based directories.
If PADDING is provided, it will prepend and separate the chevron
and directory with PADDING.
Produces different symbols by inspecting DIR to distinguish
symlinks and git repositories which do not depend on the
directory contents"
(let ((icon (apply 'all-the-icons-icon-for-dir dir arg-overrides))
(chevron (if chevron (all-the-icons-octicon (format "chevron-%s" chevron) :height 0.8 :v-adjust -0.1) ""))
(padding (or padding "\t")))
(format "%s%s%s%s%s" initial-padding chevron padding icon padding)))
(defun all-the-icons-dired-sidebar--refresh ()
2020-06-03 20:10:39 +00:00
"Display the icons of files in a dired buffer."
(all-the-icons-dired--remove-all-overlays)
(save-excursion
(goto-char (point-min))
(while (not (eobp))
(when (dired-move-to-filename nil)
(let ((file (dired-get-filename 'relative 'noerror)))
(when file
(let ((icon (if (file-directory-p file)
(all-the-icons-icon-for-dir-with-chevron file
(if (dired-subtree--is-expanded-p)
"down"
"right")
"\t"
""
:face 'all-the-icons-dired-dir-face
:v-adjust all-the-icons-dired-v-adjust)
(all-the-icons-icon-for-file file :v-adjust all-the-icons-dired-v-adjust))))
(if (member file '("." ".."))
(all-the-icons-dired--add-overlay (point) " \t")
(all-the-icons-dired--add-overlay
(point)
(concat (when (not (file-directory-p file))
" \t")
icon
"\t")))))))
(forward-line 1))))
(advice-add 'all-the-icons-dired--refresh :around
(lambda (oldfn &rest args)
(if (eq major-mode 'dired-sidebar-mode)
(apply #'all-the-icons-dired-sidebar--refresh args)
(apply oldfn args)))))
2020-06-03 20:10:39 +00:00
(add-to-list 'dired-sidebar-special-refresh-commands 'dired-kill-subdir)
2020-05-28 16:19:20 +00:00
:custom
(dired-sidebar-should-follow-file t)
(dired-sidebar-pop-to-sidebar-on-toggle-open nil)
(dired-sidebar-no-delete-other-windows t))
2020-05-28 16:19:20 +00:00
#+END_SRC
** All-the-icons
#+BEGIN_SRC emacs-lisp
(use-package all-the-icons)
(use-package all-the-icons-dired
2020-08-23 03:33:53 +00:00
:defer t
2020-06-03 20:10:39 +00:00
:hook ((dired-mode . (lambda ()
(unless (eq major-mode 'dired-sidebar-mode)
(all-the-icons-dired-mode))))))
#+END_SRC
2020-05-28 16:19:20 +00:00
** Variables
#+BEGIN_SRC emacs-lisp
(setq dired-dwim-target t)
#+END_SRC
** Dired hydra
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2020-01-07 17:45:23 +00:00
(use-package dired
:straight (:type built-in)
:init
(defhydra hydra-dired (:hint nil :color pink)
"
_+_ mkdir _v_iew _m_ark _(_ details _i_nsert-subdir wdired
_C_opy _O_ view other _U_nmark all _)_ omit-mode _$_ hide-subdir C-x C-q : edit
_D_elete _o_pen other _u_nmark _l_ redisplay _w_ kill-subdir C-c C-c : commit
_R_ename _M_ chmod _t_oggle _g_ revert buf _e_ ediff C-c ESC : abort
_Y_ rel symlink _G_ chgrp _E_xtension mark _s_ort _=_ pdiff
_S_ymlink ^ ^ _F_ind marked _._ toggle hydra \\ flyspell
_r_sync ^ ^ ^ ^ ^ ^ _?_ summary
_z_ compress-file _A_ find regexp
_Z_ compress _Q_ repl regexp
T - tag prefix
"
("\\" dired-do-ispell)
("(" dired-hide-details-mode)
(")" dired-omit-mode)
("+" dired-create-directory)
("=" diredp-ediff) ;; smart diff
("?" dired-summary)
("$" diredp-hide-subdir-nomove)
("A" dired-do-find-regexp)
("C" dired-do-copy) ;; Copy all marked files
("D" dired-do-delete)
("E" dired-mark-extension)
("e" dired-ediff-files)
("F" dired-do-find-marked-files)
("G" dired-do-chgrp)
("g" revert-buffer) ;; read all directories again (refresh)
("i" dired-maybe-insert-subdir)
2020-01-07 22:04:34 +00:00
("l" dired-do-redisplay) ;; relist the marked or single directory
2020-01-07 17:45:23 +00:00
("M" dired-do-chmod)
("m" dired-mark)
("O" dired-display-file)
("o" dired-find-file-other-window)
("Q" dired-do-find-regexp-and-replace)
("R" dired-do-rename)
("r" dired-do-rsynch)
("S" dired-do-symlink)
("s" dired-sort-toggle-or-edit)
("t" dired-toggle-marks)
("U" dired-unmark-all-marks)
("u" dired-unmark)
("v" dired-view-file) ;; q to exit, s to search, = gets line #
("w" dired-kill-subdir)
("Y" dired-do-relsymlink)
("z" diredp-compress-this-file)
("Z" dired-do-compress)
("q" nil)
("." nil :color blue))
:general
((normal visual motion insert emacs) dired-mode-map "." 'hydra-dired/body)
(dired-mode-map "SPC" leader-map))
2019-01-28 02:46:29 +00:00
#+END_SRC
* Crontab
Magit ships with a cool utility called =with-editor= that lets you run a shell command using the current Emacs instance as $EDITOR. This means we can define a command to edit the crontab with the current Emacs instance:
#+BEGIN_SRC emacs-lisp
(defun edit-crontab ()
2019-01-28 02:46:29 +00:00
(interactive)
(with-editor-async-shell-command "crontab -e"))
#+END_SRC
* Emacs Server
In case I need an =emacsclient= for some reason.
#+BEGIN_SRC emacs-lisp
(add-hook 'after-init-hook #'server-start)
#+END_SRC
* YASnippet
YASnippet is Yet Another Snippet template system.
#+BEGIN_SRC emacs-lisp
(use-package yasnippet
2020-08-23 03:33:53 +00:00
:hook ((text-mode . yas-minor-mode)
(prog-mode . yas-minor-mode)
(after-init . yas-reload-all))
2019-01-28 02:46:29 +00:00
:config
(unless (file-exists-p (expand-file-name "~/.emacs.d/snippets"))
(mkdir (expand-file-name "~/.emacs.d/snippets") t))
(setq yas-snippet-dirs
`(,(syncthing-directory "yasnippet")
2020-08-23 03:33:53 +00:00
,(expand-file-name "~/.emacs.d/snippets"))))
2019-01-28 02:46:29 +00:00
(use-package yasnippet-snippets
:after (yasnippet)
:config (yasnippet-snippets-initialize))
#+END_SRC
* mpc
An Emacs interface to MPD, the Music Player Daemon
#+BEGIN_SRC emacs-lisp
(leader-def-key "ad" #'mpc)
2019-11-14 21:03:13 +00:00
(with-eval-after-load 'evil
(add-to-list 'evil-emacs-state-modes 'mpc-mode))
2019-01-28 02:46:29 +00:00
(general-def mpc-mode-map "SPC" leader-map)
(general-def mpc-mode-map "a" #'mpc-playlist-add)
#+END_SRC
* wgrep
#+BEGIN_SRC emacs-lisp
(use-package wgrep
:defer t)
#+END_SRC
2019-11-27 15:12:29 +00:00
* Ivy
An alternative minibuffer completion framework:
#+BEGIN_SRC emacs-lisp
(use-package counsel
2019-11-27 18:19:38 +00:00
:defer 0
2019-11-27 15:12:29 +00:00
:config
(ivy-mode 1)
(counsel-mode 1)
2019-11-27 15:12:29 +00:00
(setq ivy-height 20
ivy-wrap t
ivy-use-virtual-buffers nil
ivy-count-format "%d/%d ")
2019-11-27 15:12:29 +00:00
(with-eval-after-load 'projectile
2019-11-27 17:42:07 +00:00
(setq projectile-completion-system 'ivy))
(leader-def-key "SPC" #'counsel-M-x)
(jdormit/define-prefix "i" "ivy")
2019-11-29 15:06:40 +00:00
(jdormit/define-prefix "iU" "ui")
(leader-def-key "ir" #'ivy-resume)
(leader-def-key "ip" #'swiper-thing-at-point)
(leader-def-key "iP" #'counsel-yank-pop)
2019-11-29 15:06:40 +00:00
(leader-def-key "iu" #'counsel-unicode-char)
(leader-def-key "iUt" #'counsel-load-theme)
2019-11-27 17:42:07 +00:00
(leader-def-key "is" #'swiper)
2020-02-06 17:46:35 +00:00
(leader-def-key "ia" #'swiper-all)
2019-11-27 17:42:07 +00:00
(leader-def-key "ff" #'counsel-find-file)
(leader-def-key "oc" #'counsel-org-capture)
2021-01-15 20:58:40 +00:00
(leader-def-key "bb" #'ivy-switch-buffer)
2020-02-26 15:06:23 +00:00
2019-12-12 20:23:19 +00:00
(if (executable-find "rg")
(leader-def-key "ig" #'counsel-rg)
2019-12-12 20:23:19 +00:00
(leader-def-key "ig" #'counsel-grep))
2020-02-26 15:06:00 +00:00
(defvar counsel-set-frame-font-history nil)
2020-02-22 03:32:50 +00:00
(defun counsel-set-frame-font (font)
2020-02-26 15:06:00 +00:00
(interactive (list (ivy-read "Font: " (delete-dups (font-family-list))
:require-match t
:history 'counsel-set-frame-font-history
:caller 'counsel-set-frame-font)))
2020-02-22 03:32:50 +00:00
(set-frame-font font))
(leader-def-key "iUf" #'counsel-set-frame-font)
2020-02-26 15:06:23 +00:00
(defun counsel-ibuffer-kill-buffer (x)
(kill-buffer (cdr x)))
(ivy-set-actions
'counsel-ibuffer
'(("k" counsel-ibuffer-kill-buffer "kill buffer")))
2020-02-26 15:06:23 +00:00
2019-12-26 14:38:21 +00:00
;; Function to open files without Ivy to avoid lag in really huge directories
(defun find-file-default (filename &optional wildcards)
(interactive
(let ((completing-read-function 'completing-read-default))
(find-file-read-args "Find file: "
(confirm-nonexistent-file-or-buffer))))
2019-12-26 14:38:21 +00:00
(funcall-interactively 'find-file filename wildcards))
2020-02-26 15:06:23 +00:00
2019-11-27 17:42:07 +00:00
:general
("C-c C-r" #'ivy-resume)
2019-11-27 17:42:07 +00:00
("M-x" #'counsel-M-x)
2019-11-29 15:06:40 +00:00
("C-x C-f" #'counsel-find-file)
2019-12-02 22:44:51 +00:00
("C-M-u" #'counsel-unicode-char)
2020-01-10 16:06:36 +00:00
("C-c P" #'counsel-yank-pop)
2019-12-12 20:30:39 +00:00
("C-s" #'swiper-isearch)
(help-map "f" #'counsel-describe-function)
(help-map "v" #'counsel-describe-variable)
((normal motion visual) "g/" #'swiper-thing-at-point))
2019-11-27 15:12:29 +00:00
(use-package ivy-hydra
:after counsel)
2019-11-27 17:42:07 +00:00
(use-package counsel-projectile
:after (counsel projectile)
:commands (counsel-projectile
counsel-projectile-switch-project
counsel-projectile-find-file
counsel-projectile-grep)
2019-11-27 17:42:07 +00:00
:init
(counsel-projectile-mode)
(leader-def-key "pp" #'counsel-projectile-switch-project)
(leader-def-key "pf" #'counsel-projectile)
2019-11-27 17:42:07 +00:00
(if (executable-find "rg")
(leader-def-key "pg" #'counsel-projectile-rg)
2019-11-27 17:42:07 +00:00
(leader-def-key "pg" #'counsel-projectile-grep)))
2019-12-06 17:27:39 +00:00
(use-package lsp-ivy
:after (ivy lsp)
:commands (lsp-ivy-workspace-symbol
lsp-ivy-global-workspace-symbol))
2019-12-06 17:27:39 +00:00
2021-01-15 20:59:26 +00:00
(use-package ivy-rich
:hook (ivy-mode . ivy-rich-mode))
(use-package all-the-icons-ivy-rich
:after (ivy-rich)
:demand t
:config
(all-the-icons-ivy-rich-mode 1))
2019-12-06 17:27:39 +00:00
(leader-def-key "cs" #'lsp-ivy-workspace-symbol)
2019-11-27 15:12:29 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
2020-06-05 21:31:31 +00:00
#+BEGIN_SRC emacs-lisp
(use-package ivy-xref
:ensure t
:straight (ivy-xref :fork (:host github :repo "jdormit/ivy-xref"))
:init
;; xref initialization is different in Emacs 27 - there are two different
;; variables which can be set rather than just one
(when (>= emacs-major-version 27)
(setq xref-show-definitions-function #'ivy-xref-show-defs))
;; Necessary in Emacs <27. In Emacs 27 it will affect all xref-based
;; commands other than xref-find-definitions (e.g. project-find-regexp)
;; as well
(setq xref-show-xrefs-function #'ivy-xref-show-xrefs))
#+END_SRC
2019-01-28 02:46:29 +00:00
* graphviz
#+BEGIN_SRC emacs-lisp
(use-package graphviz-dot-mode
:mode (("\\.dot\\'" . graphviz-dot))
:init
2020-01-07 22:04:34 +00:00
(with-eval-after-load 'org
(add-to-list 'org-src-lang-modes '("dot" . graphviz-dot))))
2019-01-28 02:46:29 +00:00
#+END_SRC
** Functions
A function that converts a lisp form into Graphviz format, e.g.:
#+BEGIN_EXAMPLE
'(a ((label . "Node A"))
(b ((label . "Node B"))
(d ((label . "Node D"))))
(c ((label . "Node C"))
(e ((label . "Node E")))))
#+END_EXAMPLE
becomes:
#+BEGIN_EXAMPLE
digraph {
a[label="Node A"];
b[label="Node B"];
c[label="Node C"];
d[label="Node D"];
e[label="Node E"];
a -> {b, c};
b -> d;
c -> e;
}
#+END_EXAMPLE
#+BEGIN_SRC emacs-lisp
(defun graphviz-make-node-string (id attrs)
"Makes a Graphviz Dot string representing a node with attributes"
(if attrs
(format
"%s[%s];"
id
(mapconcat 'identity
(mapcar
(lambda (attr)
(format "%s=\"%s\"" (car attr) (cdr attr)))
attrs)
", "))
(format "%s;" id)))
(defun graphviz-make-edge-string (id children)
"Makes a Graphviz Dot string representing the edges between id and children"
(when children
(format "%s -> {%s}"
id
(mapconcat
'identity
(mapcar
(lambda (child)
(format "%s" (car child)))
children)
2019-10-23 20:27:19 +00:00
"; "))))
2019-01-28 02:46:29 +00:00
(defun graphviz-parse-graph (graph)
2019-10-23 20:27:19 +00:00
"Parses a graph into nodes and edges represented in the dot language.
2019-01-28 02:46:29 +00:00
Returns an alist ((nodes (<node strings>)) (edges (<edge strings>)))"
(when graph
(let* ((id (car graph))
(attrs (cadr graph))
(children (cddr graph))
(child-graphs (mapcar #'graphviz-parse-graph children)))
`((nodes ,(cons (graphviz-make-node-string id attrs)
(apply #'append
(mapcar (lambda (child)
(cadr (assoc 'nodes child)))
child-graphs))))
(edges ,(let ((edge-string (graphviz-make-edge-string id children))
(child-edges (apply #'append
(mapcar (lambda (child)
(cadr (assoc 'edges child)))
child-graphs))))
(if edge-string
(cons edge-string child-edges)
child-edges)))))))
(defun graphviz-compile-graph (graph)
"Transpiles a graph defined as a lisp form to the Graphviz Dot language"
(let* ((graph (graphviz-parse-graph graph))
(nodes (cadr (assoc 'nodes graph)))
(edges (cadr (assoc 'edges graph))))
(message "%s" graph)
(format "digraph {\n %s\n %s\n}"
(mapconcat 'identity nodes "\n ")
(mapconcat 'identity edges "\n "))))
#+END_SRC
* HideShow
[[help:hs-minor-mode][hs-minor-mode]] enables comment and code-folding. It's useful almost everywhere, so just enable it:
#+BEGIN_SRC emacs-lisp
(add-hook 'prog-mode-hook (lambda () (hs-minor-mode 1)))
#+END_SRC
* Slack
#+BEGIN_SRC emacs-lisp
(use-package slack
:commands (slack-start)
:init
2019-11-21 17:47:52 +00:00
(setq slack-buffer-emojify t)
2019-01-28 02:46:29 +00:00
(setq slack-prefer-current-team t)
:config
(slack-register-team
2019-11-21 17:47:52 +00:00
:name "lolatravel"
2019-01-28 02:46:29 +00:00
:default t
2019-11-21 17:47:52 +00:00
;; :client-id "2400384563.846406584294"
;; :client-secret "31d7bb557aebc773ef26fb53b0f3caf5"
:token "xoxs-2400384563-531653313251-753670261511-dd9fd44b4755430caf78045af40361d9a2bf46d8a5392f09ea1b6f3c6db0b545"
;; :token "xoxp-2400384563-531653313251-844227895840-6bd114a9d3d8642116f0a1d50675fa43"
:subscribed-channels '(1-1-2020
backend
booking-team-backend
critical-bugs
dev
devops
frontend
search-and-book
smash
southwest
work
not-work)
2019-01-28 02:46:29 +00:00
:full-and-display-names t)
:general
('normal slack-info-mode-map ",u" #'slack-room-update-messages)
('normal slack-mode-map
",c" 'slack-buffer-kill
",ra" 'slack-message-add-reaction
",rr" 'slack-message-remove-reaction
",rs" 'slack-message-show-reaction-users
",pl" 'slack-room-pins-list
",pa" 'slack-message-pins-add
",pr" 'slack-message-pins-remove
",mm" 'slack-message-write-another-buffer
",me" 'slack-message-edit
",md" 'slack-message-delete
",u" 'slack-room-update-messages
",2" 'slack-message-embed-mention
",3" 'slack-message-embed-channel
"\C-n" 'slack-buffer-goto-next-message
2019-10-23 20:27:19 +00:00
"\C-p" 'slack-buffer-goto-prev-message)
2019-01-28 02:46:29 +00:00
('normal slack-edit-message-mode-map
",k" #'slack-message-cancel-edit
",s" #'slack-message-send-from-buffer
",2" #'slack-message-embed-mention
",3" #'slack-message-embed-channel))
#+END_SRC
#+BEGIN_SRC emacs-lisp
2019-11-21 17:47:52 +00:00
(use-package alert
:commands (alert)
:init
(setq alert-default-style (if (eq system-type 'darwin)
'osx-notifier
'libnotify)))
2019-01-28 02:46:29 +00:00
#+END_SRC
* Matrix
#+BEGIN_SRC emacs-lisp
(use-package matrix-client
2019-11-14 21:03:13 +00:00
:commands matrix-client-connect
2020-11-02 18:20:01 +00:00
:straight (matrix-client :host github :repo "alphapapa/matrix-client.el"
:files (:defaults "logo.png" "matrix-client-standalone.el.sh")))
2019-01-28 02:46:29 +00:00
#+END_SRC
* EMMS
2019-01-29 02:29:49 +00:00
The Emacs Multi-Media System. For libtag to work, libtag must be installed on the system via the system package manager. Then Emms should be installed from source via:
#+BEGIN_SRC shell :tangle no
git clone git://git.sv.gnu.org/emms.git
cd emms
make emms-print-metadata
make
make install
#+END_SRC
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(add-to-list 'load-path "/usr/local/share/emacs/site-lisp/emms")
2019-11-14 21:03:13 +00:00
(autoload 'emms-smart-browse "emms")
(autoload 'emms-start "emms")
(autoload 'emms-stop "emms")
(autoload 'emms-pause "emms")
(autoload 'emms-next "emms")
(autoload 'emms-previous "emms")
(autoload 'emms "emms")
(autoload 'emms-play-directory-tree "emms")
2020-01-07 23:50:22 +00:00
(autoload 'emms-all "emms-setup")
2019-11-14 21:03:13 +00:00
(with-eval-after-load 'emms
2019-01-28 02:46:29 +00:00
(emms-all)
(emms-default-players)
(require 'emms-info-libtag)
(setq emms-info-functions
'(emms-info-libtag)
emms-source-file-default-directory
(syncthing-directory "/music"))
2019-01-28 02:46:29 +00:00
(jdormit/define-prefix "ae" "emms")
2019-11-14 21:03:13 +00:00
(with-eval-after-load 'evil
(add-to-list 'evil-emacs-state-modes 'emms-browser-mode))
(general-def emms-browser-mode-map "," leader-map)
(general-def emms-playlist-mode-map "SPC" leader-map)
(general-def emms-playlist-mode-map "," leader-map)
2019-01-28 02:46:29 +00:00
(leader-def-key "aeb" 'emms-smart-browse)
(leader-def-key "aes" 'emms-start)
(leader-def-key "aeS" 'emms-stop)
(leader-def-key "aeP" 'emms-pause)
(leader-def-key "aen" 'emms-next)
(leader-def-key "aep" 'emms-previous)
(leader-def-key "aee" 'emms)
2019-02-05 13:44:24 +00:00
(leader-def-key "aed" 'emms-play-directory-tree)
(when (eq system-type 'darwin)
(define-emms-simple-player afplay '(file)
(regexp-opt '(".mp3" ".m4a" ".aac" ".m4p"))
"afplay")
2019-02-05 13:44:24 +00:00
(setq emms-player-list `(,emms-player-afplay))))
2019-01-28 02:46:29 +00:00
#+END_SRC
2019-02-05 11:12:38 +00:00
* Direnv
[[https://direnv.net/][Direnv]] automatically runs bash scripts when you enter certain directories. This sets it up to work with Emacs:
#+BEGIN_SRC emacs-lisp
(defun update-cider-env ()
(direnv-update-directory-environment nrepl-project-dir))
2019-02-05 11:12:38 +00:00
(use-package direnv
2019-02-07 00:20:03 +00:00
:if (executable-find "direnv")
:config
(direnv-mode)
2019-02-07 21:54:35 +00:00
(add-hook 'eshell-mode-hook #'direnv-update-directory-environment)
(add-hook 'eshell-directory-change-hook
(lambda ()
(unless (file-remote-p default-directory)
(direnv-update-directory-environment)))))
2019-02-05 11:12:38 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-02-06 03:17:51 +00:00
* SQL
Emacs has excellent built-in SQL support.
#+BEGIN_SRC emacs-lisp
2019-06-03 14:48:42 +00:00
(leader-def-key "sP" #'sql-postgres)
2019-02-06 03:17:51 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-02-09 01:13:04 +00:00
* GraphQL
2020-01-09 17:24:09 +00:00
GraphQL mode for editing GraphQL queries:
2019-02-09 01:13:04 +00:00
#+BEGIN_SRC emacs-lisp
(use-package graphql-mode
2019-11-14 21:03:13 +00:00
:mode "\\.gql\\'"
2020-01-09 17:24:09 +00:00
:commands (graphql-mode)
2019-02-09 01:13:04 +00:00
:config
(defvar graphql-env-alist '()
"An alist defining available GraphQL servers
The key is any symbol and the value is a cons pair of
(graphql-url . graphql-auth-token)")
(setq graphql-variables-file "~/variables.graphql")
(defun graphql-set-env (env)
(interactive
(list
(intern
2019-11-27 15:12:29 +00:00
(completing-read
2019-02-09 01:13:04 +00:00
"GraphQL env: "
(mapcar #'car graphql-env-alist)))))
(let* ((gql-params (alist-get env graphql-env-alist))
(url (car gql-params))
(auth-token (cdr gql-params)))
(setq graphql-url url
graphql-extra-headers `(("Authorization" . ,auth-token)))))
(defun graphql (env)
"Opens a graphql-mode buffer in the specified environment"
(interactive
(list
(intern
2019-11-27 15:12:29 +00:00
(completing-read
2019-02-09 01:13:04 +00:00
"GraphQL env: "
(mapcar #'car graphql-env-alist)))))
(graphql-set-env env)
(let ((graphql-buf (get-buffer-create "*graphql-query*")))
(set-buffer graphql-buf)
(when (string= "" (buffer-substring (point-min) (point-max)))
(insert "{\n \n}")
(goto-char 5))
(graphql-mode)
(switch-to-buffer graphql-buf)))
(defun find-graphql-variables-file ()
(interactive)
(find-file graphql-variables-file))
(jdormit/define-prefix "q" "graphql")
(leader-def-key "qe" #'graphql-set-env)
(leader-def-key "qf" #'find-graphql-variables-file)
(leader-def-key "aq" #'graphql)
(general-def graphql-mode-map "C-c C-e" #'graphql-set-env)
(general-def graphql-mode-map "C-c C-v" #'find-graphql-variables-file))
#+END_SRC
GraphQL environments:
#+BEGIN_SRC emacs-lisp
(setq graphql-env-alist
`((dev . ("https://api-dev.lola.co/api/graphql" .
,(password-store-get "lola-graphql-dev-token")))
(local . ("http://localhost:7200/api/graphql" .
,(password-store-get "lola-graphql-local-token")))))
#+END_SRC
2020-01-02 03:03:12 +00:00
2020-01-09 17:24:09 +00:00
And ob-graphql for evaluating GraphQL source blocks in org-mode:
#+BEGIN_SRC emacs-lisp
2020-01-14 15:23:55 +00:00
(use-package ob-graphql
:defer t)
2020-01-09 17:24:09 +00:00
#+END_SRC
* Docker
Syntax highlighting for Dockerfiles:
#+BEGIN_SRC emacs-lisp
(use-package dockerfile-mode
:mode ("\\Dockerfile\\'"))
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-04-05 14:05:40 +00:00
* Kubernetes
#+BEGIN_SRC emacs-lisp
2019-04-05 14:05:40 +00:00
(use-package kubernetes
:ensure t
:commands (kubernetes-overview)
:init (leader-def-key "ak" #'kubernetes-overview)
:config
(add-to-list 'evil-emacs-state-modes 'kubernetes-overview-mode)
(general-def kubernetes-overview-mode-map "SPC" leader-map))
2019-04-05 14:05:40 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
* AWS
2020-12-13 13:40:06 +00:00
** Switching profiles
Switch AWS profiles:
#+BEGIN_SRC emacs-lisp
(defvar aws-profiles '("default")
"AWS profile names")
(defvar aws-current-profile nil
"Currently active AWS profile")
2020-12-28 16:46:42 +00:00
(defun aws-local-profile ()
(make-local-variable 'aws-current-profile))
(add-hook 'eshell-mode-hook #'aws-local-profile)
(add-hook 'vterm-mode-hook #'aws-local-profile)
(add-hook 'term-mode-hook #'aws-local-profile)
2020-12-13 13:40:06 +00:00
(setq aws-current-profile (getenv "AWS_PROFILE"))
2020-12-28 16:46:42 +00:00
(add-to-list 'aws-profiles "personal")
(add-to-list 'aws-profiles "lola-cde")
2020-12-13 13:40:06 +00:00
(defun aws-switch-profile (profile)
(interactive (list (completing-read "Profile: " aws-profiles)))
(setenv "AWS_PROFILE" profile)
(setq aws-current-profile profile))
#+END_SRC
** AWS-MFA
The aws-mfa command:
#+BEGIN_SRC emacs-lisp
(defun aws-mfa (mfa-token)
(interactive (list
(let ((prompt (if aws-current-profile
(format "MFA code for %s: " aws-current-profile)
"MFA code: ")))
(read-from-minibuffer prompt))))
(let ((proc (start-process "aws-mfa"
"*aws-mfa*"
"aws-mfa"
"--force")))
(set-process-sentinel
proc
(make-success-err-msg-sentinel "*aws-mfa*"
"AWS MFA succeeded"
"AWS MFA failed, check *aws-mfa* buffer for details"))
(process-send-string proc (concat mfa-token "\n"))))
(with-eval-after-load 'kubernetes
(general-def kubernetes-overview-mode-map "m" #'aws-mfa))
#+END_SRC
** S3
#+BEGIN_SRC emacs-lisp
2020-04-17 18:16:53 +00:00
(use-package s3ed
:commands (s3ed-mode
2020-05-18 14:34:14 +00:00
s3ed-find-file
s3ed-save-file)
2020-04-17 18:16:53 +00:00
:init
(jdormit/define-prefix "fS" "s3")
(leader-def-key "fSf" #'s3ed-find-file)
(leader-def-key "fSs" #'s3ed-save-file)
:config
2020-04-22 20:48:52 +00:00
(defun browse-blob-in-emacs (url bufname)
(let* ((token (password-store-get "blob-logs-token"))
2020-05-18 14:34:14 +00:00
(buf (generate-new-buffer bufname))
(blob (shell-command-to-string (concat "curl "
"-s "
"--cookie 'TOKEN=" token "' "
url)))
(status
(with-current-buffer buf
(insert blob)
(json-mode)
(format-all-buffer))))
(if (not (equal status "Formatting error"))
(switch-to-buffer-other-window buf)
(with-current-buffer buf
(set-buffer-modified-p nil)
(kill-buffer))
(switch-to-buffer-other-window "*format-all-errors*"))))
2020-04-22 20:48:52 +00:00
(defun s3ed-open-blob-log (arg)
"Opens the blob log at point via the cloudfront proxy.
If given prefix arg ARG, opens in browser, otherwise opens in Emacs."
(interactive "P")
(let ((fname-at-point (dired-file-name-at-point)))
2020-05-18 14:34:14 +00:00
(if (string-match
"\\(bloblogs.ops.lola.co[m]*\\)\/blobs\/\\(\\(production\\|dev\\|local\\|smoke\\|staging\\)\/[0-9]+/.*\\)"
fname-at-point)
(let* ((proxy-url (format "https://%s" (match-string 0 fname-at-point))))
(if arg
(browse-url proxy-url)
(browse-blob-in-emacs proxy-url fname-at-point)))
(error "Not in a blob logs directory"))))
:general
((normal) s3ed-mode-map "B" #'s3ed-open-blob-log))
#+END_SRC
2020-01-02 03:03:12 +00:00
* Prodigy
[[https://github.com/rejeep/prodigy.el][Prodigy]] gives Emacs a nice way to run services (web servers, etc.).
#+BEGIN_SRC emacs-lisp
(use-package prodigy
:commands (prodigy)
:general
('normal 'prodigy-mode-map "SPC" leader-map)
('normal 'prodigy-view-mode-map "SPC" leader-map)
:config
2020-01-21 15:44:23 +00:00
(setq prodigy-completion-system 'default)
(add-hook 'prodigy-view-mode-hook (lambda () (toggle-truncate-lines 1))))
2019-11-20 13:53:16 +00:00
(leader-def-key "aP" #'prodigy)
#+END_SRC
Add the ability to associate a file with a service instead of a buffer:
#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'prodigy
(defun prodigy-service-file (service)
"Return SERVICE file.
If SERVICE file exists, use that. If not, find the first SERVICE
tag that has a file and return that."
(let ((file (prodigy-service-or-first-tag-with service :file)))
(if (functionp file)
(prodigy-callback-with-plist file service)
file)))
(defun prodigy-display-process-file-or-buffer ()
(interactive)
(when-let (service (prodigy-service-at-pos))
(if-let (file (prodigy-service-file service))
(progn
(find-file-literally file)
(prodigy-view-mode)
(auto-revert-tail-mode)
(general-define-key
:states 'normal
:keymaps 'local
"Q" #'kill-this-buffer))
(prodigy-switch-to-process-buffer service))))
(general-def 'normal prodigy-mode-map "`" #'prodigy-display-process-file-or-buffer))
#+END_SRC
And add the ability to inhibit all service output processing:
#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'prodigy
(defun prodigy-start-service (service &optional callback)
"Start process associated with SERVICE unless already started.
When CALLBACK function is specified, that is called when the
process has been started.
When the process is started, a timer starts and checks every
second for `prodigy-start-tryouts' times if the process is live.
If the process is not live after `prodigy-start-tryouts' seconds,
the process is put in failed status."
(declare (indent 1))
(unless (prodigy-service-started-p service)
(let* ((default-directory
(-if-let (cwd (prodigy-service-cwd service))
(f-full cwd)
default-directory))
(name (plist-get service :name))
(sudo (plist-get service :sudo))
(command (prodigy-service-command service))
(args (prodigy-service-args service))
(exec-path (append (prodigy-service-path service) exec-path))
(env (--map (s-join "=" it) (prodigy-service-env service)))
(process-environment (append env process-environment))
(process nil)
(create-process
(lambda ()
(unless process
(setq process (apply (if sudo 'prodigy-start-sudo-process 'start-process)
(append (list name nil command) args)))))))
(-when-let (init (prodigy-service-init service))
(funcall init))
(-when-let (init-async (prodigy-service-init-async service))
(let (callbacked)
(funcall
init-async
(lambda ()
(setq callbacked t)
(funcall create-process)))
(with-timeout
(prodigy-init-async-timeout
(error "Did not callback async callback within %s seconds"
prodigy-init-async-timeout))
(while (not callbacked) (accept-process-output nil 0.005)))))
(funcall create-process)
(let ((tryout 0))
(prodigy-every 1
(lambda (next)
(setq tryout (1+ tryout))
(if (process-live-p process)
(when callback (funcall callback))
(if (= tryout prodigy-start-tryouts)
(prodigy-set-status service 'failed)
(funcall next))))))
(plist-put service :process process)
(when (not (plist-get service :inhibit-process-filter))
(set-process-filter
process
(lambda (_ output)
(run-hook-with-args 'prodigy-process-on-output-hook service output))))
(set-process-query-on-exit-flag process nil)))))
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-04-05 14:05:40 +00:00
* Lola
2019-10-23 20:27:19 +00:00
Some functions to make my day job easier.
2019-04-05 14:05:40 +00:00
** Services (Prodigy)
#+BEGIN_SRC emacs-lisp
(defun call-with-venv (venv callback)
(let ((venv-dir (cond
((file-exists-p venv) venv)
((file-exists-p
(substitute-in-file-name
(format "$WORKON_HOME/%s" venv)))
(substitute-in-file-name
(format "$WORKON_HOME/%s" venv)))
2020-05-29 18:35:29 +00:00
(t (error "virtual environment %s does not exist" venv)))))
(call-with-env-from-file (format "%s/bin/activate" venv-dir) callback)))
2019-12-19 22:00:03 +00:00
(defun kill-log-buffers ()
(interactive)
(kill-matching-buffers "\\.log$" nil t)
(message "Killed log buffers"))
(cl-defun python-service-setup (venv &optional env-file &key env-dir)
(lambda (done)
(call-with-venv
venv
(if env-file
(lambda ()
(call-with-env-from-file env-file done :dir env-dir))
done))))
2020-02-14 15:21:17 +00:00
(defun call-with-lola-env (callback)
(let ((process-environment
(cons (format "LOLA_ENV=%s"
(completing-read
"Environment: "
'("local" "development" "staging")))
process-environment)))
2020-02-14 15:21:17 +00:00
(funcall callback)))
2019-11-19 13:46:50 +00:00
(prodigy-define-tag
:name 'lola)
(prodigy-define-service
:name "lola-server (gunicorn)"
:tags '(lola backend)
:command "bash"
:args (lambda ()
(list
"-c"
2020-06-02 13:41:04 +00:00
"gunicorn -c server/web/gunicorn.conf.py \
-b 127.0.0.1:7200 bin.start_web:init_and_create_flask_app\\(\\) \
2020-06-02 13:41:04 +00:00
>> ~/lola/logs/lola-server.log 2>&1"))
:file "~/lola/logs/lola-server.log"
:inhibit-process-filter t
:cwd "~/lola/lola-server"
:stop-signal 'int
:truncate-output t
:init-async (python-service-setup "~/.pyenv/versions/lola-server"
"~/lola/lola-server/.env"))
(prodigy-define-service
:name "lola-server celery worker"
:tags '(lola backend)
:command "python"
:args '("bin/start_celery_worker.py" "-P" "gevent" "-n" "lola-server")
:cwd "~/lola/lola-server"
:stop-signal 'int
:truncate-output t
:init-async (python-service-setup "~/.pyenv/versions/lola-server"
"~/lola/lola-server/.env"))
(prodigy-define-service
:name "travel-service"
:tags '(lola backend)
:command "bash"
:args (lambda ()
(list
2020-06-02 13:41:04 +00:00
"-c" "python bin/start_web.py >> ~/lola/logs/travel-svc.log 2>&1"))
:cwd "~/lola/lola-travel-service"
2020-06-02 13:41:04 +00:00
:file "~/lola/logs/travel-svc.log"
:inhibit-process-filter t
:stop-signal 'int
:truncate-output t
:init-async (python-service-setup "~/.pyenv/versions/travel-service"
"~/lola/lola-travel-service/.env"))
(prodigy-define-service
:name "travel-service celery worker"
:tags '(lola backend)
:command "bash"
:args (lambda ()
(list
"-c"
(concat "python "
"bin/start_celery_workers.py "
"-n " "travel-service "
"-Q "
"default,io_pool,cpu_pool,priority_io_pool,priority_cpu_pool "
2020-06-02 13:41:04 +00:00
">> ~/lola/logs/travel-svc-celery.log 2>&1")))
:file "~/lola/logs/travel-svc-celery.log"
:inhibit-process-filter t
:cwd "~/lola/lola-travel-service"
:stop-signal 'int
:truncate-output t
:init-async (python-service-setup "~/.pyenv/versions/travel-service"
"~/lola/lola-travel-service/.env"))
(prodigy-define-service
:name "secrets"
:tags '(lola backend)
:command "python"
:args '("bin/cmdline.py" "www")
:cwd "~/lola/secrets"
:truncate-output t
:stop-signal 'int
:init-async (python-service-setup "~/.pyenv/versions/secrets"
"~/lola/secrets/.env"))
(prodigy-define-service
:name "lola-desktop"
:tags '(lola frontend)
:command "npm"
:args '("start")
:cwd "~/lola/lola-desktop"
:port 3001
:env '(("PORT" "3001"))
:stop-signal 'int
:init-async #'call-with-lola-env)
(prodigy-define-service
:name "wallet"
:tags '(lola frontend)
:command "npm"
:args '("start")
:cwd "~/lola/wallet"
:stop-signal 'int
:env '(("PORT" "3000"))
:init-async #'call-with-lola-env)
2020-02-11 15:58:18 +00:00
(prodigy-define-service
:name "agent-console"
:command "npm"
:args '("start")
:cwd "~/lola/agent-console"
:stop-signal 'int
:env '(("PORT" "3002"))
:init-async (lambda (done)
(call-with-lola-env
(lambda ()
(nvm-use "v10.15.1" done)))))
2020-02-11 15:58:18 +00:00
(prodigy-define-service
:name "luigid"
:command "luigid"
:cwd "~/lola/data-pipeline"
:port 8082
:stop-signal 'int
:init-async (python-service-setup "data-pipeline"
"~/lola/data-pipeline/.env"))
(prodigy-define-service
:name "prometheus"
:command "prometheus"
:args '("--config.file=prometheus.yml")
:port 9090
:stop-signal 'int
:cwd "~/prometheus")
(prodigy-define-service
:name "priceline-service"
:tags '(lola backend)
2020-05-29 18:35:29 +00:00
:command "~/lola/python_services/priceline/bin/start.sh"
:args '("web")
2020-05-29 18:35:29 +00:00
:cwd "~/lola/python_services"
:stop-signal 'int
2020-05-29 18:35:29 +00:00
:init-async (python-service-setup "~/lola/python_services/.venv"
"~/lola/python_services/priceline/.env"
:env-dir "~/lola/python_services"))
2019-12-06 17:27:46 +00:00
2020-06-02 18:59:08 +00:00
(prodigy-define-service
:name "priceline-cars-service"
:tags '(lola backend)
:command "bash"
:args '("-c"
"priceline_cars/bin/start.sh web >> ~/lola/logs/priceline-cars.log 2>&1")
:cwd "~/lola/python_services"
:inhibit-process-filter t
:file "~/lola/logs/priceline-cars.log"
:stop-signal 'int
:init-async (python-service-setup "~/lola/python_services/.venv"
"~/lola/python_services/priceline_cars/.env"
:env-dir "~/lola/python_services"))
2020-02-14 15:28:00 +00:00
(prodigy-define-service
:name "threev-service"
:tags '(lola backend)
2020-05-29 18:35:29 +00:00
:command "~/lola/python_services/threev/bin/start.sh"
2020-02-14 15:28:00 +00:00
:args '("web")
2020-05-29 18:35:29 +00:00
:cwd "~/lola/python_services"
2020-02-14 15:28:00 +00:00
:stop-signal 'int
2020-05-29 18:35:29 +00:00
:init-async (python-service-setup "~/lola/python_services/.venv"
"~/lola/python_services/threev/.env"
:env-dir "~/lola/python_services"))
2020-02-14 15:28:00 +00:00
(prodigy-define-service
:name "amd-flight-service"
:tags '(lola backend)
:command "~/lola/python_services/amd_flight/bin/start.sh"
:args '("web")
:cwd "~/lola/python_services"
:stop-signal 'int
:init-async (python-service-setup "~/lola/python_services/.venv"
"~/lola/python_services/amd_flight/.env"
:env-dir "~/lola/python_services"))
2020-03-30 14:10:55 +00:00
(prodigy-define-service
:name "ean-hotels-service"
:tags '(lola backend)
2020-06-02 13:41:04 +00:00
:command "bash"
:args '("-c"
"ean_hotels/bin/start.sh web >> ~/lola/logs/ean-hotels.log 2>&1")
2020-05-29 18:35:29 +00:00
:cwd "~/lola/python_services"
2020-06-02 13:41:04 +00:00
:inhibit-process-filter t
:file "~/lola/logs/ean-hotels.log"
2020-05-22 22:11:41 +00:00
:stop-signal 'kill
2020-05-29 18:35:29 +00:00
:init-async (python-service-setup "~/lola/python_services/.venv"
"~/lola/python_services/ean_hotels/.env"
:env-dir "~/lola/python_services"))
2020-03-30 14:10:55 +00:00
2020-06-17 17:08:35 +00:00
(prodigy-define-service
:name "smp-hotels-service"
:command "bash"
:args '("-c"
"smp_hotels/bin/start.sh web >> ~/lola/logs/smp-hotels.log 2>&1")
:cwd "~/lola/python_services"
:inhibit-process-filter t
:file "~/lola/logs/smp-hotels.log"
:stop-signal 'kill
:init-async (python-service-setup "~/lola/python_services/.venv"
"~/lola/python_services/smp_hotels/.env"
:env-dir "~/lola/python_services"))
(prodigy-define-service
:name "email-template-service"
:tags '(lola backend)
:command "npm"
:args '("start")
:cwd "~/lola/email-template-service"
2020-03-05 00:40:10 +00:00
:env '(("PORT" "7300"))
:stop-signal 'int
:init-async (lambda (done)
(nvm-use "10.15.1" done)))
2019-12-06 17:27:46 +00:00
(prodigy-define-service
:name "mabl-link-agent"
:command "link-agent"
:args (lambda ()
(list "-a" (password-store-get "mabl-link-agent")
"-n" "jdormit-macbook")))
(prodigy-define-service
:name "xray-daemon"
:command "xray_mac"
:args '("-o" "-n" "us-east-1"))
(prodigy-define-service
:name "spend-service"
:tags '(lola backend)
:command "~/lola/python_services/spend/bin/start.sh"
:args '("web")
:cwd "~/lola/python_services"
:stop-signal 'int
:init-async (python-service-setup "~/lola/python_services/.venv"
"~/lola/python_services/spend/.env"
:env-dir "~/lola/python_services"))
#+END_SRC
2020-01-02 03:03:12 +00:00
** Services (eShell)
2019-04-05 14:05:40 +00:00
#+BEGIN_SRC emacs-lisp :lexical yes
(defun run-service-in-eshell (name dir cmd &optional setup)
(if-let ((buf (get-buffer name)))
(progn (when (eq major-mode 'eshell-mode)
(eshell-interrupt-process)
(while eshell-process-list))
(kill-buffer buf)))
(let ((buf (get-buffer-create name)))
(switch-to-buffer buf)
2019-04-05 14:05:40 +00:00
(cd dir)
(eshell-mode)
2019-04-05 14:05:40 +00:00
(when setup (funcall setup))
(insert cmd)
(eshell-send-input)))
2019-09-24 16:09:00 +00:00
(defun release-manager ()
(interactive)
(run-service-in-eshell "*release-manager*"
2019-12-02 22:45:02 +00:00
"~/lola/release-manager"
"pipenv run python release-manager"))
2019-09-24 16:09:00 +00:00
(jdormit/define-prefix "L" "lola")
2019-09-24 16:09:00 +00:00
(leader-def-key "Lr" #'release-manager)
#+END_SRC
2019-04-05 14:05:40 +00:00
** Python stuff
Run pip install in the current project:
#+BEGIN_SRC emacs-lisp
(defun pip-install (reqs)
(interactive
(list
(read-file-name "Requirements file: "
(or (projectile-project-root)
default-directory)
nil t "requirements"
(lambda (name)
(string-match-p "requirements.*"
name)))))
(with-temp-buffer
(cd (or (projectile-project-root)
default-directory))
(compile (format "pip install -r %s" reqs))))
#+END_SRC
2019-04-23 14:26:31 +00:00
** 1Password
#+BEGIN_SRC emacs-lisp
(defvar op-token nil
"The 1Password session token")
(defun 1pass-signin ()
(interactive)
2019-04-24 15:38:35 +00:00
(cl-letf* ((signin-address "team-lolatravel.1password.com")
(signin-email "jeremydormitzer@lola.com")
(secret-key (password-store-get "1pass-lola-secret-key"))
((symbol-function 'op-signin)
(make-shell-fn "pass" "team-lolatravel.1password.com" "|" "op" "signin" signin-address signin-email secret-key "--output=raw"))
(token (op-signin)))
(if (string-match-p "ERROR" token)
(error "Unable to sign in to 1Password: %s" token)
(setf op-token token)
(message (format "Signed in to 1Password with session token %s" op-token))
op-token)))
(defun op-fn (&rest args)
(lambda (&optional input)
(cl-letf* (((symbol-function 'op-function)
(apply #'make-shell-fn "op" `(,@args ,(format "--session=%s" op-token))))
(output (op-function input)))
(if (or (string-match-p "Authentication required" output)
(string-match-p "You are not currently signed in" output))
(cl-letf* ((new-token (1pass-signin))
((symbol-function 'op-function)
(apply #'make-shell-fn "op" `(,@args ,(format "--session=%s" new-token)))))
(op-function input))
output))))
(defun op-list-items ()
2019-04-24 15:38:35 +00:00
(cl-flet ((op-list-items-fn (op-fn "list" "items")))
(mapcar #'cdr
(mapcar (apply-partially #'assoc 'title)
(mapcar (apply-partially #'assoc 'overview)
(json-read-from-string (op-list-items-fn)))))))
(defun op-get-item (item)
2019-04-24 15:38:35 +00:00
(cl-flet ((op-get-item (op-fn "get" "item" item)))
(json-read-from-string (op-get-item))))
2019-06-03 14:48:42 +00:00
(defun op-get-item-field (item-json field-designation)
(let* ((fields (assoc-recursive item-json 'details 'fields))
2019-04-24 15:38:35 +00:00
(pw-field (car (seq-filter
(lambda (field)
2019-06-03 14:48:42 +00:00
(string= field-designation (cdr (assoc 'designation field))))
2019-04-24 15:38:35 +00:00
fields))))
(when pw-field (cdr (assoc 'value pw-field)))))
(defun op-copy-password (item)
(interactive
(list
2019-11-27 15:12:29 +00:00
(completing-read "1Password item: " (op-list-items))))
2019-06-03 14:48:42 +00:00
(if-let ((password (op-get-item-field (op-get-item item) "password")))
(with-temp-buffer
(insert password)
(copy-region-as-kill (point-min) (point-max))
(message "Copied password for \"%s\" to kill ring." item))
2019-06-03 14:48:42 +00:00
;; TODO if no password found, prompt for alternate field in record to return
2019-04-24 15:38:35 +00:00
(error "No password found in 1Password for \"%s\"." item)))
(leader-def-key "ao" #'op-copy-password)
2019-04-23 14:26:31 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
** Resetting DNSResponder
#+BEGIN_SRC emacs-lisp
(defun reset-dnsresponsder ()
(interactive)
(sudo-shell-command "killall -HUP mDNSResponder"))
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-07-19 14:40:37 +00:00
** Devpi
#+BEGIN_SRC emacs-lisp
(defvar devpi-indices nil
"Login profiles for devpi. An alist
where the keys are index names and
the values are alists with the keys
devpi-index-url, devpi-index-username,
and devpi-index-password-fn.")
(setq devpi-indices
`((pip .
((devpi-index-url . "https://pip.aws.lolatravel.com/pip/dev")
(devpi-index-username . "pip")
(devpi-index-password-fn . ,(lambda ()
(op-get-item-field
(op-get-item "PIP Devpi Login")
"password")))))
(jdormit .
((devpi-index-url . "https://pip.aws.lolatravel.com/jdormit/dev")
(devpi-index-username . "jdormit")
(devpi-index-password-fn . ,(lambda ()
(password-store-get "devpi-jdormit")))))))
(defun devpi-use-index (index-name)
(interactive
(list
(intern
2019-11-27 15:12:29 +00:00
(completing-read
2019-07-19 14:40:37 +00:00
"Devpi index to use: "
(mapcar #'car devpi-indices)))))
(cl-letf* ((index-alist (alist-get index-name devpi-indices))
(index-url (alist-get 'devpi-index-url index-alist))
(index-username (alist-get 'devpi-index-username index-alist))
((symbol-function 'index-password-fn) (alist-get 'devpi-index-password-fn index-alist))
(index-password (index-password-fn))
((symbol-function 'devpi-use) (make-process-fn "devpi" "use" index-url))
((symbol-function 'devpi-login)
(make-process-fn "devpi" "login" index-username "--password" index-password))
(login-result (devpi-login))
(use-result (devpi-use)))
(save-match-data
(if (and (string-match "credentials valid" login-result)
(string-match "current devpi index" use-result))
(message "Switched to Devpi index %s" index-name)
(with-current-buffer (get-buffer-create "*devpi*")
(erase-buffer)
(goto-char (point-min))
(insert login-result)
(insert use-result))
(message "Failed to switch to Devpi index %s - check the *devpi* buffer for details" index-name)))))
(defun devpi-upload ()
(interactive)
(cl-letf (((symbol-function 'devpi-upload) (make-process-fn "devpi" "upload")))
2019-08-26 14:11:44 +00:00
(message (devpi-upload))))
2019-07-19 14:40:37 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
2020-03-19 19:44:21 +00:00
** Release notes
#+BEGIN_SRC emacs-lisp
(defvar release-notes-repo (expand-file-name
"~/lola/release-manager/release_notes")
"The directory containing the release notes repository.")
(defun lola-release-notes (staged &optional repos)
2020-03-19 19:44:21 +00:00
"Prints release notes for the latest release.
If called with a prefix argument, prints release notes
for the upcoming release instead."
(interactive "P")
(let* ((all-repos '("lola-server"
"lola-travel-service"
"lola-desktop"))
(repos
(or
repos
(if (y-or-n-p "Query all repos?")
all-repos
(completing-read-multiple "Repos: "
all-repos)))))
2020-03-19 19:44:21 +00:00
(with-env-from-file (expand-file-name
(concat release-notes-repo "/.env"))
(async-shell-command
(format
(concat release-notes-repo "/venv/bin/python "
release-notes-repo "/release_notes/query_release_notes.py "
(when staged "--staged ")
" %s")
(combine-and-quote-strings repos))
"*release-notes*"))))
#+END_SRC
2019-05-04 16:36:32 +00:00
* StumpWM
A handy keybinding to connect to the StumpWM SBCL process via SLIME:
#+BEGIN_SRC emacs-lisp
(defun connect-stumpwm ()
(interactive)
(slime-connect "127.0.0.1" 4004))
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-05-04 16:36:32 +00:00
* Emacs Network Client
Emacs frontend for networkmanager.
#+BEGIN_SRC emacs-lisp
(use-package enwc
:config
(setq enwc-default-backend 'nm)
(add-to-list 'evil-emacs-state-modes 'enwc-mode)
(general-def enwc-mode-map "SPC" leader-map)
2019-08-25 19:00:56 +00:00
:init
2019-05-04 16:36:32 +00:00
(leader-def-key "an" #'enwc)
:commands (enwc))
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-06-03 14:48:42 +00:00
* Deft
A fuzzy-finder for notes.
#+BEGIN_SRC emacs-lisp
(use-package deft
2019-11-14 21:03:13 +00:00
:commands (deft)
2019-06-03 14:48:42 +00:00
:init
(setq deft-extensions '("org" "txt" "md" "markdown" "text")
2020-08-23 03:33:53 +00:00
deft-recursive t
deft-directory (org-directory))
;; Still lots of notes in the old Deft directory
2020-08-23 03:33:53 +00:00
(with-eval-after-load 'org
(add-to-list 'org-agenda-files (org-directory "deft")))
2020-02-16 15:35:13 +00:00
(leader-def-key "D" #'deft)
(leader-def-key "od" #'deft)
2019-06-03 14:48:42 +00:00
:config
2019-11-14 21:03:13 +00:00
(setq deft-use-filter-string-for-filename t
2020-08-23 03:33:53 +00:00
deft-file-naming-rules '((noslash . "-")
(nospace . "-")
(case-fn . downcase))
deft-auto-save-interval 0)
2019-11-14 21:03:13 +00:00
(add-to-list 'evil-emacs-state-modes 'deft-mode))
2019-06-03 14:48:42 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
Tell Deft to use the TITLE property for entry titles, if it exists:
#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'deft
(advice-add
'deft-parse-title :around
(lambda (old-fn file contents &rest args)
(let ((title (org-get-title contents)))
(if title
title
(apply old-fn file contents args))))))
#+END_SRC
2019-06-03 14:48:42 +00:00
* Pollen
#+BEGIN_SRC emacs-lisp
2019-11-14 21:03:13 +00:00
(use-package pollen-mode
:commands (pollen-server-start pollen-server-stop)
:mode ("\\.p\\'" "\\.pp\\'" "\\.pm\\'" "\\.ptree\\'"))
2019-06-03 14:48:42 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-09-15 15:12:38 +00:00
* Ngrok
#+BEGIN_SRC emacs-lisp
(defun ngrok (port &optional subdomain)
(interactive "nPort: \nsSubdomain: ")
(let ((buf (get-buffer-create
(concat "*ngrok-"
(number-to-string port)
"*")))
(cmd (if (and subdomain (not (string-empty-p subdomain)))
(concat "ngrok http "
"--subdomain=" subdomain " "
(number-to-string port))
(concat "ngrok http " (number-to-string port)))))
(async-shell-command cmd buf)))
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-09-15 15:12:46 +00:00
* Make
#+BEGIN_SRC emacs-lisp
(defun make ()
(interactive)
(let ((project-root (projectile-project-root)))
(if project-root
(with-temp-buffer
(cd project-root)
(async-shell-command "make"))
(error "Not in a project"))))
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-10-09 13:52:58 +00:00
* Redis
#+BEGIN_SRC emacs-lisp
(defun redis-cli (&optional host port)
(interactive (list (read-string "host (default localhost): " nil nil "localhost")
(read-number "port: " 6379)))
(let ((cli-path (executable-find "redis-cli"))
(host (or host "localhost"))
(port (or port 6379)))
(if cli-path
(progn
(make-comint-in-buffer
"redis-cli"
nil
"redis-cli"
nil
"-h" host
"-p" (number-to-string port)
"--no-raw")
(switch-to-buffer "*redis-cli*"))
(error "Can't find redis-cli"))))
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-11-07 22:01:35 +00:00
* Restclient
Explore APIs from within Emacs!
#+BEGIN_SRC emacs-lisp
2019-11-14 21:03:13 +00:00
(use-package restclient
:commands (restclient-mode))
2019-11-07 22:01:35 +00:00
(use-package company-restclient
:after (restclient company)
:config
(add-to-list 'company-backends 'company-restclient))
(use-package ob-restclient
2020-01-07 22:04:34 +00:00
:defer t
:hook (org-mode . (lambda () (require 'ob-restclient))))
2019-11-07 22:01:35 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-11-07 22:01:14 +00:00
* IMenu
Get a nice IMenu sidebar:
#+BEGIN_SRC emacs-lisp
(use-package imenu-list
:config
2019-11-07 22:47:11 +00:00
(setq imenu-list-focus-after-activation t)
2019-11-07 22:01:14 +00:00
:general
(imenu-list-major-mode-map "SPC" leader-map)
(imenu-list-major-mode-map "." #'imenu-list-display-entry))
(defun open-imenu-list ()
(interactive)
(if (and (fboundp 'lsp-ui-imenu)
(boundp 'lsp-mode)
lsp-mode)
(lsp-ui-imenu)
(imenu-list-smart-toggle)))
(leader-def-key "\\" #'open-imenu-list)
2019-11-13 03:46:05 +00:00
(leader-def-key "m" #'imenu)
2019-11-07 22:01:14 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-11-20 15:48:54 +00:00
* calfw-org
A fancy calendar view:
#+BEGIN_SRC emacs-lisp
(use-package calfw
:commands (cfw:open-calendar-buffer))
2019-11-20 15:48:54 +00:00
(use-package calfw-org
2019-11-21 15:44:03 +00:00
:config (require 'calfw)
2019-11-20 15:48:54 +00:00
:commands (cfw:open-org-calendar))
(leader-def-key "oC" #'cfw:open-org-calendar)
#+END_SRC
2020-01-02 03:03:12 +00:00
* hackernews
#+BEGIN_SRC emacs-lisp
(use-package hackernews
:commands (hackernews)
:config
(setq hackernews-internal-browser-function 'w3m-browse-url)
:general
('normal hackernews-mode-map "SPC" leader-map))
(leader-def-key "ah" 'hackernews)
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-11-27 18:19:55 +00:00
* counsel-spotify
Spotify in Emacs!
#+BEGIN_SRC emacs-lisp
(use-package counsel-spotify
:commands (counsel-spotify-search-track
counsel-spotify-search-album
counsel-spotify-search-artist
counsel-spotify-seach-tracks-by-album
counsel-spotify-search-tracks-by-artist
counsel-spotify-play
counsel-spotify-toggle-play-pause
counsel-spotify-next
counsel-spotify-previous)
:config
(setq counsel-spotify-client-id "825add4224704126adf3912b847c86df"
counsel-spotify-client-secret "f71fb236c06b4af886658667056ef7bd"))
(jdormit/define-prefix "iS" "counsel-spotify")
(leader-def-key "iSt" #'counsel-spotify-search-track)
(leader-def-key "iSa" #'counsel-spotify-search-album)
(leader-def-key "iSA" #'counsel-spotify-search-artist)
(leader-def-key "iS SPC" #'counsel-spotify-toggle-play-pause)
(leader-def-key "iSn" #'counsel-spotify-next)
(leader-def-key "iSp" #'counsel-spotify-previous)
#+END_SRC
2019-12-12 19:33:34 +00:00
* Flyspell
2019-12-12 19:33:34 +00:00
#+BEGIN_SRC emacs-lisp
(when (executable-find "hunspell")
(setq ispell-program-name "hunspell"
ispell-really-hunspell t))
(add-hook 'text-mode-hook #'flyspell-mode)
2019-12-12 19:33:34 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-12-26 16:59:45 +00:00
* VTerm
A better terminal emulator for Emacs. Replaces ansi-term, not EShell.
#+BEGIN_SRC emacs-lisp
(defun eshell-exec-in-vterm (&rest args)
(let* ((program (car args))
2020-12-28 16:46:42 +00:00
(buf (generate-new-buffer
(concat "*" (file-name-nondirectory program) "*"))))
(with-current-buffer buf
2020-12-28 16:46:42 +00:00
(vterm-mode)
(vterm-send-string (concat (s-join " " args) "\n")))
(switch-to-buffer buf)))
2020-12-28 16:46:42 +00:00
(defun vterm-send-interactively (key)
(interactive "cSend key: ")
(vterm-send (format "%c" key)))
2019-12-26 16:59:45 +00:00
(use-package vterm
:if module-file-suffix
:init
(with-eval-after-load 'em-term
(defun eshell-exec-visual (&rest args)
2020-12-28 16:46:42 +00:00
(apply #'eshell-exec-in-vterm args)))
2021-01-15 20:58:54 +00:00
:config
(add-hook 'vterm-mode-hook #'compilation-shell-minor-mode)
2020-12-28 16:46:42 +00:00
:commands (vterm vterm-other-window vterm-mode)
:general
(vterm-mode-map "M-\\" #'vterm-send-interactively))
2019-12-26 16:59:45 +00:00
(defun run-vterm (&optional new-buffer)
2019-12-26 16:59:45 +00:00
(interactive "P")
(let ((buffer-name (when (not new-buffer) "vterm")))
(if (and buffer-name (get-buffer buffer-name))
2020-12-28 16:46:42 +00:00
(switch-to-buffer buffer-name)
(vterm buffer-name))))
2019-12-26 16:59:45 +00:00
(defun open-vterm (&optional arg)
(interactive "P")
(if (and (fboundp 'projectile-project-root)
2020-12-28 16:46:42 +00:00
(projectile-project-root))
(projectile-run-vterm arg)
(run-vterm arg)))
2019-12-26 16:59:45 +00:00
(leader-def-key "sv" 'open-vterm)
#+END_SRC
* Hide mode line
2020-05-28 16:19:20 +00:00
Does what it says on the box.
#+BEGIN_SRC emacs-lisp
(use-package hide-mode-line
2020-05-28 16:19:20 +00:00
:hook ((dired-sidebar-mode imenu-list-major-mode) . hide-mode-line-mode))
#+END_SRC
2020-01-03 15:15:57 +00:00
2020-01-09 17:23:59 +00:00
* Dash
[[https://kapeli.com/dash][Dash]] is a code browser app for MacOS. [[https://github.com/stanaka/dash-at-point][dash-at-point]] is an Emacs interface to it.
#+BEGIN_SRC emacs-lisp
(use-package dash-at-point
:if (file-exists-p "/Applications/Dash.app")
:general
((normal visual motion insert emacs) "C-c d" 'dash-at-point)
((normal visual motion insert emacs) "C-c e" 'dash-at-point-with-docset))
#+END_SRC
2020-01-09 17:50:04 +00:00
* Alfred
Add a function to power the [[https://orgmode.org/worg/org-contrib/alfred-org-capture.html][alfred-org-capture]] Alfred workflow:
#+BEGIN_SRC emacs-lisp
(defun make-orgcapture-frame ()
"Create a new frame and run org-capture."
(interactive)
(make-frame '((name . "Org Capture") (width . 100) (height . 24)
(top . 400) (left . 300)))
(select-frame-by-name "Org Capture")
(add-hook 'org-capture-after-finalize-hook #'delete-frame t t)
(counsel-org-capture))
2020-01-09 17:50:04 +00:00
#+END_SRC
2020-01-10 16:06:17 +00:00
* gist.el
[[https://github.com/defunkt/gist.el][Integrate with GitHub gists]] from Emacs!
#+BEGIN_SRC emacs-lisp
(use-package gist
2020-02-07 21:46:40 +00:00
:defer t
:config
(add-hook 'gist-mode-hook
(lambda ()
(evil-ex-define-local-cmd "w[rite]" 'gist-mode-save-buffer))))
2020-01-10 16:06:17 +00:00
#+END_SRC
2020-01-11 02:52:34 +00:00
* ffap
Add the ability to jump to a particular line number in find-file-at-point. "Borrowed" from the [[https://www.emacswiki.org/emacs/FindFileAtPoint#toc6][EmacsWiki]].
#+BEGIN_SRC emacs-lisp
(defvar ffap-file-at-point-line-number nil
"Variable to hold line number from the last `ffap-file-at-point' call.")
(defadvice ffap-file-at-point (after ffap-store-line-number activate)
"Search `ffap-string-at-point' for a line number pattern and
save it in `ffap-file-at-point-line-number' variable."
(let* ((string (ffap-string-at-point)) ;; string/name definition copied from `ffap-string-at-point'
(name
(or (condition-case nil
(and (not (string-match "//" string)) ; foo.com://bar
(substitute-in-file-name string))
(error nil))
string))
(line-number-string
(and (string-match ":[0-9]+" name)
(substring name (1+ (match-beginning 0)) (match-end 0))))
(line-number
(and line-number-string
(string-to-number line-number-string))))
(if (and line-number (> line-number 0))
(setq ffap-file-at-point-line-number line-number)
(setq ffap-file-at-point-line-number nil))))
(defadvice find-file-at-point (after ffap-goto-line-number activate)
"If `ffap-file-at-point-line-number' is non-nil goto this line."
(when ffap-file-at-point-line-number
(goto-line ffap-file-at-point-line-number)
(setq ffap-file-at-point-line-number nil)))
#+END_SRC
2020-01-28 15:05:51 +00:00
* Format-all-the-code
A package that bundles together common code beautifying tools for many languages (the actual formatting tools still need to be installed separately):
#+BEGIN_SRC emacs-lisp
(use-package format-all
:commands (format-all-buffer)
2020-07-21 13:45:12 +00:00
:config
(defun format-all--resolve-system (choices)
"Get first choice matching `format-all--system-type' from CHOICES."
(cl-dolist (choice choices)
(cond ((atom choice)
(cl-return choice))
((eql format-all--system-type (car choice))
(cl-return (cadr choice))))))
(when (executable-find "zprint")
(define-format-all-formatter zprint
(:executable "zprint")
(:install "https://github.com/kkinnear/zprint/releases")
(:languages "Clojure")
(:format (format-all--buffer-easy executable))))
2020-01-28 15:05:51 +00:00
:init
(leader-def-key "cf" 'format-all-buffer))
2020-01-28 15:05:51 +00:00
#+END_SRC
2020-01-29 16:28:58 +00:00
* Compiling
Enable ANSI colors in compile buffers:
#+BEGIN_SRC emacs-lisp
(autoload 'ansi-color-apply-on-region "ansi-color")
(defun colorize-compilation-buffer ()
(ansi-color-apply-on-region compilation-filter-start (point-max)))
(add-hook 'compilation-filter-hook #'colorize-compilation-buffer)
#+END_SRC
Set up some keybindings for Comint-mode compilation buffers:
2020-02-14 17:38:18 +00:00
#+BEGIN_SRC emacs-lisp
(general-def (normal visual motion) compilation-shell-minor-mode-map "q" #'quit-window)
(general-def (normal visual motion) compilation-shell-minor-mode-map "gr" #'recompile)
2020-02-14 17:38:18 +00:00
#+END_SRC
2021-01-15 20:58:54 +00:00
Recognize text of the form <filename>:<line number> as a compilation
error:
#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'compile
(add-to-list 'compilation-error-regexp-alist '("\\(.+\\):\\([[:digit:]]+\\)$" 1 2)))
#+END_SRC
* Wallabag
2020-02-07 19:52:13 +00:00
[[https://github.com/jdormit/emacs-wallabag-client][My Wallabag client]] is still a WIP, but it is useful enough to pull in right now:
#+BEGIN_SRC emacs-lisp
(use-package wallabag
2020-02-07 21:46:40 +00:00
:defer t
:straight (:host github :repo "jdormit/emacs-wallabag-client")
:custom
(wallabag-base-url "https://wallabag.jeremydormitzer.com")
(wallabag-client-id (password-store-get "wallabag-client-id"))
(wallabag-client-secret (password-store-get "wallabag-client-secret"))
(wallabag-username "jdormit")
(wallabag-password (password-store-get "wallabag.jeremydormitzer.com")))
#+END_SRC
2020-02-23 14:40:15 +00:00
* Clipmon
Syncs up the X Windows clipboard with the Emacs kill ring. Only
necessary on Linux.
#+BEGIN_SRC emacs-lisp
(use-package clipmon
:if (eq window-system 'x)
:hook (after-init . clipmon-mode-start))
#+END_SRC
2020-02-25 18:29:24 +00:00
* GUD
Emacs ships with a built-in debugger interface that I primarily use
for PDB. The default keybindings are RSI-inducing, so here's a hydra
to use instead:
#+BEGIN_SRC emacs-lisp
(defhydra hydra-gud (:hint nil)
"
_n_: step over _b_: set breakpoint _e_: execute statement _l_: refresh buffer
_s_: step into _d_: remove breakpoint _p_: print statement _q_: quit hyra
_>_: up stack frame _f_: finish function _w_: watch expression
_<_: down stack frame _c_: continue
"
(">" gud-up)
("<" gud-down)
("b" gud-break)
("d" gud-remove)
("e" gud-statement)
("f" gud-finish)
("l" gud-refresh)
("n" gud-next)
("p" gud-print)
("c" gud-cont :color blue)
("s" gud-step)
("w" gud-watch)
("q" nil))
(leader-def-key "cg" #'hydra-gud/body)
#+END_SRC
2020-03-05 00:42:16 +00:00
2020-04-16 22:12:23 +00:00
* realgud
Like [[info:emacs#Debuggers][GUD]], but better!
#+BEGIN_SRC emacs-lisp
(use-package realgud
2020-08-23 03:33:53 +00:00
:commands (realgud:pdb)
2020-04-16 22:12:23 +00:00
:init
(defun projectile-pdb ()
(interactive)
(with-projectile-root
2020-05-22 22:12:06 +00:00
(funcall-interactively #'realgud:pdb))))
2020-04-16 22:12:23 +00:00
#+END_SRC
2020-03-05 00:42:16 +00:00
* EDiff
#+BEGIN_SRC emacs-lisp
(setq ediff-window-setup-function #'ediff-setup-windows-plain)
#+END_SRC
2020-03-06 22:17:07 +00:00
* Apache Drill
#+BEGIN_SRC emacs-lisp
(use-package sql-drill
:straight (:host github :repo "jdormit/sql-drill.el")
:commands (sql-drill)
:init
(when (file-exists-p "~/drill/apache-drill-1.17.0/bin/drill-embedded")
(setq sql-drill-program
(expand-file-name "~/drill/apache-drill-1.17.0/bin/drill-embedded"))))
#+END_SRC
2020-03-23 18:47:27 +00:00
* Helpful
A much-improved help buffer:
#+BEGIN_SRC emacs-lisp
(use-package helpful
:defer t
:init
(setq counsel-describe-function-function #'helpful-callable
counsel-describe-variable-function #'helpful-variable)
(add-hook 'emacs-lisp-mode-hook
(lambda ()
(set (make-local-variable 'evil-lookup-func)
#'helpful-at-point)))
:general
(help-map "k" #'helpful-key))
#+END_SRC
2020-04-01 02:39:27 +00:00
2020-05-06 04:01:33 +00:00
* Inform
Provides links to the help documentation for ELisp symbols from Info
buffers:
#+BEGIN_SRC emacs-lisp
(use-package inform
:straight (:host github :repo "dieter-wilhelm/inform")
:defer 0
:config
;; If Helpful is installed, link to Helpful buffers instead of vanilla help
(with-eval-after-load 'helpful
(define-button-type 'inform-function
:supertype 'inform-xref
'inform-function 'helpful-function
'inform-echo (purecopy "mouse-2, RET: describe this function"))
(define-button-type 'inform-variable
:supertype 'inform-xref
'inform-function 'helpful-variable
'inform-echo (purecopy "mouse-2, RET: describe this variable"))
(define-button-type 'inform-symbol
:supertype 'inform-xref
'inform-function #'helpful-symbol
'inform-echo (purecopy "mouse-2, RET: describe this symbol"))))
2020-05-06 04:01:33 +00:00
#+END_SRC
2020-04-01 02:39:27 +00:00
* Vuiet
A music browser and player:
#+BEGIN_SRC emacs-lisp
(use-package versuri
:straight (versuri :host github :repo "mihaiolteanu/versuri")
:defer t)
(use-package vuiet
:straight (vuiet :host github :repo "mihaiolteanu/vuiet")
2020-05-08 21:45:53 +00:00
:commands (vuiet-track-artist
vuiet-track-name
vuiet-track-duration
vuiet--playing-track)
2020-04-01 02:39:27 +00:00
:defer t
:config
(setq vuiet-update-mode-line-automatically t
vuiet-update-mode-line-interval 1)
2020-04-04 11:33:21 +00:00
(defun vuiet-mode-setup ()
(set (make-local-variable 'org-link-elisp-confirm-function)
nil))
2020-04-04 11:33:21 +00:00
(add-hook 'vuiet-mode-hook #'vuiet-mode-setup)
2020-04-01 02:39:27 +00:00
:general
2020-04-04 11:33:21 +00:00
((normal motion visual) vuiet-mode-map "q" #'quit-window)
((normal motion visual) vuiet-mode-map "C-m" #'org-open-at-point))
2020-04-01 02:39:27 +00:00
#+END_SRC
2020-04-01 18:15:49 +00:00
And a handy hydra for it:
#+BEGIN_SRC emacs-lisp
2020-04-01 19:34:14 +00:00
(defun now-playing (length)
(let* ((str (s-truncate length
2020-05-08 21:45:53 +00:00
(if-let ((track (vuiet--playing-track)))
(format "%s - %s"
(vuiet-track-artist track)
(vuiet-track-name track))
"----")))
(remainder (- length (length str))))
2020-04-01 19:34:14 +00:00
(if (> remainder 0)
2020-05-08 21:45:53 +00:00
(let* ((left-pad (/ remainder 2))
(right-pad (- remainder left-pad)))
(s-prepend (s-repeat left-pad " ")
(s-append (s-repeat right-pad " ") str)))
str)))
2020-04-01 19:34:14 +00:00
(defun track-pos ()
2020-05-08 21:45:53 +00:00
(if-let ((track (vuiet--playing-track)))
(format "[%s/%s]"
(format-time-string "%M:%S" (condition-case nil
(mpv-get-playback-position)
(error 0)))
(s-pad-left 5 "0" (vuiet-track-duration track)))
2020-04-02 22:15:14 +00:00
"[00:00/00:00]"))
2020-04-01 19:34:14 +00:00
(defvar hydra-vuiet-body-timer)
2020-04-02 22:15:14 +00:00
(defun refresh-hydra-vuiet ()
(interactive)
(hydra-show-hint hydra-vuiet/hint 'refresh-hydra-vuiet))
2020-04-01 19:34:14 +00:00
(defhydra hydra-vuiet (:hint nil
2020-05-08 21:45:53 +00:00
:color blue
:body-pre (setq hydra-vuiet-body-timer
(run-at-time 1 1 #'refresh-hydra-vuiet))
:post (cancel-function-timers 'refresh-hydra-vuiet))
2020-04-01 18:15:49 +00:00
"
2020-05-08 21:45:53 +00:00
--- ^^ ^^ ^^ ---
2020-04-02 22:15:14 +00:00
+------------------/ / ^^ ^^ ^^ +------------------/ /
| - ------- / / ^^ ^^ ^^ | - ------- / /
|(-) .d########b. //)|+------------^^----------------^^------------^^-----+|(-) .d########b. //)| Play [_t_]ag(s)
| .d############// ||+-----------^^----------------^^------------^^----+|| .d############// | Play [_a_]rtist
| .d######''####//b. |||%s(now-playing 43)^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^||| .d######''####//b. | Play a[_l_]bum
| 9######( )#-//##P ||+-----------^^--+-------------^^+-----------^^----+|| 9######( )#-//##P |
| 'b######++#/-/##d' || ^^ |%s(track-pos)^^| ^^ || 'b######++#/-/##d' | [_L_]ove current track
| '9############P' || ^^ +-------------^^+ ^^ || '9############P' | [_U_]nlove current track
2020-04-02 22:15:14 +00:00
| -'9a#######aP' || +---------^^+---------------^^--+---------^^--+ || -'9a#######aP' |
| |-| `'''''' || | _s_top | _p_lay/pause | _n_ext | || |-| `'''''' | [_I_]nfo menu
| ---..----------- || +---------^^+---------------^^--+---------^^--+ || ---..----------- | [_S_]imilar menu
2020-04-02 22:15:14 +00:00
| |---||-----------| |+------------^^----------------^^------------^^-----+| |---||-----------| |
| | ^^ ^^ ^^ | | [_q_]uit
+--------------------+ ^^ ^^ ^^ +--------------------+
2020-04-01 18:15:49 +00:00
"
("s" vuiet-stop)
("p" vuiet-play-pause :color red)
("n" vuiet-next :color red)
2020-04-02 22:15:14 +00:00
("t" vuiet-play-tag-similar)
("a" vuiet-play-artist)
("l" vuiet-play-album)
("L" vuiet-love-track)
("U" vuiet-unlove-track)
("I" hydra-vuiet-info/body)
("S" hydra-vuiet-similar/body)
("q" nil))
(general-def "C-c v" #'hydra-vuiet/body)
(defvar hydra-vuiet-info-body-timer)
(defun refresh-hydra-vuiet-info ()
(interactive)
(hydra-show-hint hydra-vuiet-info/hint 'refresh-hydra-vuiet-info))
(defhydra hydra-vuiet-info (:hint nil
2020-05-08 21:45:53 +00:00
:color blue
:body-pre (setq hydra-vuiet-info-body-timer
(run-at-time 1 1 #'refresh-hydra-vuiet-info))
:post (cancel-function-timers 'refresh-hydra-vuiet-info))
"
2020-05-08 21:45:53 +00:00
--- ^^ ^^ ^^ ---
+------------------/ / ^^ ^^ ^^ +------------------/ /
| - ------- / / ^^ ^^ ^^ | - ------- / /
|(-) .d########b. //)|+------------^^----------------^^------------^^-----+|(-) .d########b. //)| Info for [_t_]ag
| .d############// ||+-----------^^----------------^^------------^^----+|| .d############// | Info for [_a_]rtist
| .d######''####//b. |||%s(now-playing 43)^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^||| .d######''####//b. | Info for a[_l_]bum
| 9######( )#-//##P ||+-----------^^--+-------------^^+-----------^^----+|| 9######( )#-//##P | Info for [_c_]urrent artist
2020-04-04 10:41:45 +00:00
| 'b######++#/-/##d' || ^^ |%s(track-pos)^^| ^^ || 'b######++#/-/##d' | L[_y_]rics for current track
| '9############P' || ^^ +-------------^^+ ^^ || '9############P' | [_L_]oved tracks
| -'9a#######aP' || +---------^^+---------------^^--+---------^^--+ || -'9a#######aP' |
| |-| `'''''' || | _s_top | _p_lay/pause | _n_ext | || |-| `'''''' | [_P_]layer menu
| ---..----------- || +---------^^+---------------^^--+---------^^--+ || ---..----------- | [_S_]imilar menu
| |---||-----------| |+------------^^----------------^^------------^^-----+| |---||-----------| |
| | ^^ ^^ ^^ | | [_q_]uit
+--------------------+ ^^ ^^ ^^ +--------------------+
"
("s" vuiet-stop)
("p" vuiet-play-pause :color red)
("n" vuiet-next :color red)
("t" vuiet-tag-info)
("a" vuiet-artist-info-search)
("l" vuiet-album-info-search)
("c" vuiet-playing-artist-info)
2020-04-04 10:41:45 +00:00
("y" vuiet-playing-track-lyrics)
("L" vuiet-loved-tracks-info)
("P" hydra-vuiet/body)
("S" hydra-vuiet-similar/body)
("q" nil))
(defvar hydra-vuiet-similar-body-timer)
(defun refresh-hydra-vuiet-similar ()
(interactive)
(hydra-show-hint hydra-vuiet-similar/hint 'refresh-hydra-vuiet-similar))
(defhydra hydra-vuiet-similar (:hint nil
2020-05-08 21:45:53 +00:00
:color blue
:body-pre (setq hydra-vuiet-similar-body-timer
(run-at-time 1 1 #'refresh-hydra-vuiet-similar))
:post (cancel-function-timers 'refresh-hydra-vuiet-similar))
"
2020-05-08 21:45:53 +00:00
--- ^^ ^^ ^^ ---
+------------------/ / ^^ ^^ ^^ +------------------/ /
| - ------- / / ^^ ^^ ^^ | - ------- / /
|(-) .d########b. //)|+------------^^----------------^^------------^^-----+|(-) .d########b. //)| Play [_t_]ag similar
| .d############// ||+-----------^^----------------^^------------^^----+|| .d############// | Play [_a_]rtist similar
| .d######''####//b. |||%s(now-playing 43)^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^||| .d######''####//b. | Play [_l_]oved tracks similar
| 9######( )#-//##P ||+-----------^^--+-------------^^+-----------^^----+|| 9######( )#-//##P |
| 'b######++#/-/##d' || ^^ |%s(track-pos)^^| ^^ || 'b######++#/-/##d' | Play [_c_]urrent artist similar
| '9############P' || ^^ +-------------^^+ ^^ || '9############P' | Play current [_T_]ag similar
| -'9a#######aP' || +---------^^+---------------^^--+---------^^--+ || -'9a#######aP' |
| |-| `'''''' || | _s_top | _p_lay/pause | _n_ext | || |-| `'''''' | [_P_]layer menu
| ---..----------- || +---------^^+---------------^^--+---------^^--+ || ---..----------- | [_I_]nfo menu
| |---||-----------| |+------------^^----------------^^------------^^-----+| |---||-----------| |
| | ^^ ^^ ^^ | | [_q_]uit
+--------------------+ ^^ ^^ ^^ +--------------------+
"
("s" vuiet-stop)
("p" vuiet-play-pause :color red)
("n" vuiet-next :color red)
("t" vuiet-play-tag-similar)
("a" vuiet-play-artist-similar)
("l" vuiet-play-loved-tracks-similar)
("c" vuiet-play-playing-artist-similar)
("T" vuiet-play-playing-tag-similar)
("P" hydra-vuiet/body)
("I" hydra-vuiet-info/body)
("q" nil))
2020-04-01 18:15:49 +00:00
#+END_SRC
2020-05-18 16:31:51 +00:00
* Twitter
Socially networking from Emacs:
#+BEGIN_SRC emacs-lisp
(use-package twittering-mode
:commands (twit)
:init
(leader-def-key "at" #'twit)
:config
(setq twittering-use-master-password t)
(defun twittering-browse-uri (uri)
(interactive (list (or (get-text-property (point) 'uri)
(if (get-text-property (point) 'field)
(let* ((id (get-text-property (point) 'id))
(status (twittering-find-status id)))
(twittering-get-status-url-from-alist status))
nil))))
(when uri (browse-url uri)))
:general
(twittering-mode-map "SPC" leader-map)
(twittering-mode-map "W" #'twittering-browse-uri))
2020-05-18 16:31:51 +00:00
#+END_SRC
2020-06-14 16:25:35 +00:00
* Structlog
#+BEGIN_SRC emacs-lisp
(use-package structlog-mode
:straight (structlog-mode :repo "https://git.jeremydormitzer.com/jdormit/structlog-el.git")
2020-08-23 03:33:53 +00:00
:commands (structlog)
2020-06-14 16:25:35 +00:00
:config
(setq structlog-db-username "jdormit"
structlog-db-database "fluentd"))
#+END_SRC
2020-07-25 20:41:55 +00:00
* PlantUML
#+BEGIN_SRC emacs-lisp
(use-package plantuml-mode
2020-08-23 03:33:53 +00:00
:commands (plantuml-mode)
2020-07-25 20:41:55 +00:00
:config
(setq plantuml-default-exec-mode 'jar
plantuml-jar-path "~/bin/plantuml.jar"
org-plantuml-jar-path "~/bin/plantuml.jar"))
#+END_SRC
2020-08-23 03:33:53 +00:00
* Typo mode
An incredibly useful mode for editing typographic text - automatically
makes quotes curly and dashes long, etc.
#+BEGIN_SRC emacs-lisp
(use-package typo
:commands (typo-mode typo-global-mode))
2020-08-23 03:33:53 +00:00
#+END_SRC
2020-12-30 19:23:13 +00:00
* Open Policy Agent
#+BEGIN_SRC emacs-lisp
(use-package rego-mode
:mode (("\\.rego\\'" . rego-mode)))
#+END_SRC