2020-01-07 20:07:05 +00:00
-*- eval: (add-hook 'after-save-hook 'org-babel-tangle 0 t) -* -
2019-11-27 19:43:06 +00:00
#+PROPERTY : header-args :results silent
2020-01-07 20:07:05 +00:00
#+PROPERTY : header-args:emacs-lisp :lexical t :tangle ~/.emacs.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
initially loaded by a [[file:.emacs.d/init.el::;;; -*- lexical-binding: t; -*- ][bootstrap file ]] that lives at ~/.emacs.d/init.el;
after the initial bootstrapping it writes itself to ~/.emacs.el. Since
~/.emacs.el takes priority over ~ /.emacs.d/init.el, after the initial
bootstrapping process the tangled ~/.emacs.el file will get loaded
without needing to load the bootstrap file first.
2019-01-28 02:46:29 +00:00
2019-04-23 15:30:19 +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
2019-04-23 15:30:19 +00:00
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
2020-01-07 20:07:05 +00:00
#+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
2020-05-20 17:10:52 +00:00
tab-always-indent 'complete
2020-05-22 22:10:12 +00:00
enable-recursive-minibuffers t
read-process-output-max (* 1024 1024))
2020-04-27 20:35:31 +00:00
(setq-default indent-tabs-mode nil)
2020-01-07 20:07:05 +00:00
#+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
2020-01-07 20:07:05 +00:00
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
2020-06-18 17:04:35 +00:00
** 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
2020-01-07 20:07:05 +00:00
* Load org mode
2020-02-13 19:21:04 +00:00
Load org-mode early to [[https://github.com/raxod502/straight.el#the-wrong-version-of-my-package-was-loaded ][avoid a version clash ]].
2019-12-27 16:29:48 +00:00
#+BEGIN_SRC emacs-lisp
2020-01-07 20:07:05 +00:00
(use-package org
2020-01-07 22:04:34 +00:00
:straight org-plus-contrib
2020-02-13 19:21:04 +00:00
:commands (org-element-map)
2020-01-07 22:04:34 +00:00
:mode (("\\.org\\'" . org-mode)))
2020-02-13 23:02:38 +00:00
;; Annoying that this is necessary...
(require 'org)
(require 'org-refile)
2020-04-17 17:25:46 +00:00
(require 'org-protocol)
2019-12-27 16:29:48 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
2020-01-03 00:54:41 +00:00
* Doom themes
#+BEGIN_SRC emacs-lisp
(use-package doom-themes)
2020-01-07 20:07:05 +00:00
(use-package doom-modeline)
(doom-modeline-mode 1)
2020-01-03 00:54:41 +00:00
#+END_SRC
2020-03-27 13:03:17 +00:00
* Ewal theme
#+BEGIN_SRC emacs-lisp
(use-package doom-ewal-themes
:straight (doom-ewal-themes
:host github
:repo "jdormit/doom-ewal-themes"
2020-04-12 15:24:56 +00:00
:files ("themes" :defaults)))
2020-03-27 13:03:17 +00:00
#+END_SRC
2019-12-27 16:29:48 +00:00
* 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
2020-01-02 03:03:12 +00:00
2019-01-28 02:46:29 +00:00
* 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.
#+BEGIN_SRC emacs-lisp
(use-package exec-path-from-shell
:if (memq window-system '(mac ns x))
:config
2019-02-07 00:13:45 +00:00
(setq exec-path-from-shell-variables '("PATH" "MANPATH" "LEDGER_FILE" "LOLA_HOME"
2020-04-28 18:38:50 +00:00
"MODELS_HOME" "LOLA_TRAVEL_SERVICE_HOME" "WORKON_HOME"
2020-05-05 20:53:06 +00:00
"PIPENV_VERBOSITY" "PIPENV_DONT_LOAD_ENV"
2020-05-29 15:41:40 +00:00
"PIPENV_MAX_DEPTH" "PYENV_ROOT")
2020-04-27 21:34:31 +00:00
exec-path-from-shell-check-startup-files nil)
2019-01-28 02:46:29 +00:00
(exec-path-from-shell-initialize))
#+END_SRC
* General
Better keybinding.
#+BEGIN_SRC emacs-lisp
2019-11-21 14:47:45 +00:00
(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
2019-12-27 15:38:22 +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...
#+BEGIN_SRC emacs-lisp
(use-package company
:config
(setq company-idle-delay 0.3)
(add-hook 'after-init-hook #'global-company-mode))
(general-def "C-M-i" #'company-complete)
(general-def "M-<tab >" #'company-complete)
#+END_SRC
2019-01-28 02:46:29 +00:00
* 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)
2019-01-28 14:52:04 +00:00
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-11-21 14:47:45 +00:00
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(setq evil-want-fine-undo t)
#+END_SRC
2020-01-13 19:10:13 +00:00
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)
:config
2019-11-29 14:10:36 +00:00
(setq evil-collection-company-use-tng nil)
2019-11-19 15:43:50 +00:00
(evil-collection-init))
2019-11-21 14:47:45 +00:00
#+END_SRC
2019-11-18 22:32:58 +00:00
2019-11-21 14:47:45 +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
2019-11-21 14:47:45 +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
2019-12-18 18:59:04 +00:00
(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
2019-11-22 23:19:08 +00:00
(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)
2019-11-27 15:11:25 +00:00
(general-def 'normal prodigy-view-mode-map "0" nil :keymaps 'override)
2019-11-22 23:19:08 +00:00
(general-def 'normal messages-buffer-mode-map "SPC" leader-map :keymaps 'override)
2019-11-04 22:08:52 +00:00
#+END_SRC
2020-05-05 20:53:34 +00:00
* Multiple cursors
#+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))
#+END_SRC
#+BEGIN_SRC emacs-lisp
(use-package evil-multiedit
:defer 0
:config
(evil-multiedit-default-keybinds))
#+END_SRC
2020-01-02 22:06:35 +00:00
* Transient
A framework for creating Magit-style popups:
#+BEGIN_SRC emacs-lisp
2020-01-14 15:23:55 +00:00
(use-package transient
:commands (define-transient-command))
2020-01-02 22:06:35 +00:00
#+END_SRC
2020-01-03 00:54:41 +00:00
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
2019-08-12 15:24:17 +00:00
* Magit
Magit is objectively the best Git interface.
#+BEGIN_SRC emacs-lisp
(use-package magit
2020-05-07 15:14:26 +00:00
:commands (magit-status
magit-blame
magit-find-file
2020-05-27 01:45:38 +00:00
magit-name-local-branch))
2019-08-12 15:24:17 +00:00
#+END_SRC
#+BEGIN_SRC emacs-lisp
(jdormit/define-prefix "g" "git")
(leader-def-key "gs" #'magit-status)
2019-12-24 14:35:00 +00:00
(leader-def-key "gg" #'magit-file-dispatch)
2019-08-12 15:24:17 +00:00
(leader-def-key "gf" #'magit-find-file)
#+END_SRC
Use ido-mode for completion within Magit:
#+BEGIN_SRC emacs-lisp
2019-11-27 15:12:29 +00:00
;; (setq magit-completing-read-function 'magit-ido-completing-read)
2019-08-12 15:24:17 +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).
#+BEGIN_SRC emacs-lisp
2019-12-27 15:38:55 +00:00
(use-package forge
2019-11-14 21:03:13 +00:00
:after (magit)
2019-08-12 15:24:17 +00:00
:config
2020-05-07 15:14:26 +00:00
(add-to-list 'forge-alist '("git.jeremydormitzer.com"
"git.jeremydormitzer.com/api/v1"
"git.jeremydormitzer.com"
forge-gitea-repository))
2020-05-28 15:06:07 +00:00
(with-eval-after-load 'evil-magit
(general-def '(normal motion) magit-mode-map
"yu" #'forge-copy-url-at-point-as-kill))
2019-11-08 15:34:55 +00:00
:general
2020-05-07 15:14:26 +00:00
((normal motion visual) forge-topic-list-mode-map
"y" #'forge-copy-url-at-point-as-kill
2020-06-05 21:31:19 +00:00
"q" #'quit-window)
:custom
(forge-owned-accounts '((jdormit . (remote-name "jdormit")))))
2019-08-12 15:24:17 +00:00
#+END_SRC
2019-11-18 16:01:04 +00:00
** evil-magit
Evil keybindings for magit!
#+BEGIN_SRC emacs-lisp
(use-package evil-magit
2020-05-28 14:24:53 +00:00
:after (evil magit)
2020-05-27 01:45:38 +00:00
:config
(with-eval-after-load 'magit
(require 'evil-magit))
2019-11-21 14:47:45 +00:00
:general
('normal magit-mode-map "SPC" leader-map))
2019-11-18 16:01:04 +00:00
#+END_SRC
2019-12-13 16:55:40 +00:00
** Transient
#+BEGIN_SRC emacs-lisp
(setq transient-default-level 7)
#+END_SRC
2020-01-02 03:03:12 +00:00
2020-05-07 15:14:51 +00:00
* git-link
Open files in Git forges (GitHub, GitLab, etc.):
#+BEGIN_SRC emacs-lisp
(use-package 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))))
#+END_SRC
2019-08-12 15:24:17 +00:00
* with-editor
A utility from the author of Magit to run shell commands using the current Emacs instance as $EDITOR.
#+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)
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-01-28 02:46:29 +00:00
* Password Store
Interfacing with Pass, the "standard Unix password manager". This should also be loaded before `exec-path-from-shell`.
#+BEGIN_SRC emacs-lisp
2019-02-07 14:34:57 +00:00
(defun password-store-synchronize ()
(interactive)
2019-02-14 14:41:31 +00:00
(with-editor-async-shell-command "pass git pull && pass git push"))
2019-02-07 14:34:57 +00:00
2019-01-28 02:46:29 +00:00
(use-package password-store
2020-01-14 22:15:39 +00:00
:defer t
2019-02-07 14:34:57 +00:00
:if (executable-find "pass")
2019-01-28 02:46:29 +00:00
:config
2019-02-07 14:34:57 +00:00
(setq password-store-password-length 20)
2020-01-14 22:15:21 +00:00
:init
(leader-def-key "P" 'password-store-copy)
(epa-file-enable))
2019-01-28 02:46:29 +00:00
(use-package pass
2019-02-07 14:34:57 +00:00
:if (executable-find "pass")
2019-01-28 02:46:29 +00:00
:commands pass
:general
2019-12-18 18:59:04 +00:00
('(normal motion visual) pass-mode-map "S" #'password-store-synchronize))
2019-02-09 01:11:28 +00:00
(leader-def-key "ap" #'pass)
2020-05-08 21:45:00 +00:00
(setq auth-sources '("~/.authinfo" password-store))
2019-01-28 02:46:29 +00:00
#+END_SRC
* Emacs Lisp
2019-11-22 23:19:46 +00:00
** Packages
2019-12-26 14:58:38 +00:00
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
2019-11-22 23:19:46 +00:00
#+BEGIN_SRC emacs-lisp
(use-package deferred
:commands (deferred:next
2020-05-07 15:15:09 +00:00
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))
2020-01-14 15:23:55 +00:00
(use-package s
:defer t
:init
2020-05-07 15:15:09 +00:00
(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))))
2020-01-14 15:23:55 +00:00
(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))))
2020-01-07 22:04:34 +00:00
(use-package request
:commands (request request-deferred))
2019-11-22 23:19:46 +00:00
#+END_SRC
2019-12-26 14:58:38 +00:00
2019-06-03 14:48:42 +00:00
** Editing Elisp
#+BEGIN_SRC emacs-lisp
2020-01-02 03:21:52 +00:00
(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)
2020-01-08 16:06:53 +00:00
(add-hook 'ielm-mode-hook 'smartparens-strict-mode)
2019-06-03 14:48:42 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-01-28 02:46:29 +00:00
** Load path
For machine or user specific libraries:
#+BEGIN_SRC emacs-lisp
(add-to-list 'load-path (expand-file-name "~/site-lisp"))
#+END_SRC
And for global ones:
#+BEGIN_SRC emacs-lisp
(add-to-list 'load-path "/usr/local/share/emacs/site-lisp")
#+END_SRC
** 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
Opening a file as sudo:
#+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)))
#+END_SRC
Recursive =assoc= for nested alists:
#+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)
#+END_SRC
Format a millis timestamp into human-readable form:
#+BEGIN_SRC emacs-lisp
(defun format-epoch-millis (millis)
(interactive "nTimestamp: ")
2019-04-05 14:05:40 +00:00
(message (format-time-string "%F %r" (/ millis 1000))))
2019-01-28 02:46:29 +00:00
#+END_SRC
The same but for seconds:
#+BEGIN_SRC emacs-lisp
(defun format-epoch-seconds (seconds)
(interactive "nTimestamp: ")
2019-04-05 14:05:40 +00:00
(message (format-time-string "%F %r" seconds)))
2019-01-28 02:46:29 +00:00
#+END_SRC
2019-07-19 14:39:48 +00:00
Checking if a buffer contains a string:
#+BEGIN_SRC emacs-lisp
(defun buffer-contains-substring (string)
(save-excursion
(save-match-data
(goto-char (point-min))
(search-forward string nil t))))
#+END_SRC
2019-08-26 14:11:44 +00:00
Pretty-print JSON:
#+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))))
#+END_SRC
2019-11-04 15:02:58 +00:00
Load environment variables into Emacs from a shell script:
#+BEGIN_SRC emacs-lisp
2020-03-19 19:44:01 +00:00
(cl-defun extract-vars-from-env-file (file &key dir)
2019-11-11 22:01:43 +00:00
"Extracts an alist of variable name to value from
a bash script that exports environment variables."
2020-04-16 15:53:52 +00:00
(let ((file (expand-file-name file))
(var-re "\\(.+?\\)=\\(.+ \\)$")
2019-11-11 22:01:43 +00:00
(env '()))
2019-11-04 15:02:58 +00:00
(with-temp-buffer
2020-03-19 19:44:01 +00:00
(cd (expand-file-name (or dir (file-name-directory file))))
2020-04-16 15:53:52 +00:00
(insert (shell-command-to-string (concat "source "
(shell-quote-argument file)
" > /dev/null && env")))
2019-11-11 22:01:43 +00:00
(goto-char (point-min))
2019-11-04 15:02:58 +00:00
(save-match-data
(while (re-search-forward var-re nil t)
2019-11-11 22:01:43 +00:00
(push (cons (match-string 1) (match-string 2)) env))))
env))
(defun source-env-file (file)
(interactive "fFile: \n")
2019-11-20 13:54:06 +00:00
(let ((env (extract-vars-from-env-file file)))
2019-11-11 22:01:43 +00:00
(dolist (binding env)
(setenv (car binding) (cdr binding)))))
2020-03-19 19:44:01 +00:00
(cl-defmacro with-env-from-file (file &rest body)
2019-11-11 22:01:43 +00:00
(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)))
2020-03-19 19:44:01 +00:00
(cl-defun call-with-env-from-file (file callback &key dir)
(let* ((env (extract-vars-from-env-file file :dir dir))
2019-11-18 22:33:26 +00:00
(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)))
2019-11-11 22:01:43 +00:00
(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))
#+END_SRC
Convenience macro to run some code in a particular default-directory:
#+BEGIN_SRC emacs-lisp
(defmacro with-default-directory (dir &rest body)
(declare (indent 1))
`(let ((default-directory ,dir))
,@body))
2019-11-04 15:02:58 +00:00
#+END_SRC
2019-12-18 16:26:50 +00:00
#+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))))))))
#+END_SRC
2020-01-02 03:03:12 +00:00
2020-02-11 21:05:22 +00:00
A handy function to colorize a buffer with ANSI escape characters in it:
#+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)))
#+END_SRC
2019-01-28 02:46:29 +00:00
** 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).
#+BEGIN_SRC emacs-lisp
(defvar persisted-vars-file "~/.emacs.d/persisted-vars")
#+END_SRC
This function retrieves the plist of persisted variables or nil if it doesn't exist:
#+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)))))))
#+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
2019-04-23 13:02:42 +00:00
** Process handling
Some utilities for calling out to other processes.
#+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)))))
#+END_SRC
2019-04-23 14:25:46 +00:00
A function to call a process passing some string as stdin and returning the process output:
#+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)
2019-04-24 15:38:17 +00:00
(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)))))
#+END_SRC
The same function but for commands that need to run in a shell:
#+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))))
2019-04-23 14:25:46 +00:00
(with-temp-buffer
2019-04-24 15:38:17 +00:00
(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))))))
2019-04-23 14:25:46 +00:00
#+END_SRC
2019-05-13 12:38:04 +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
2019-07-08 20:44:54 +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
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
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))))
(add-hook 'buffer-list-update-hook #'run-buffer-mode-hooks)
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-12-26 14:58:48 +00:00
** Aliases
#+BEGIN_SRC emacs-lisp
(defalias 'doc 'describe-symbol)
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-11-13 14:48:26 +00:00
** Miscellaneous
#+BEGIN_SRC emacs-lisp
(setq warning-suppress-types
'((undo discard-info)))
#+END_SRC
2020-01-02 03:03:12 +00:00
2020-06-19 21:16:09 +00:00
* Syncthing
2019-01-28 02:46:29 +00:00
I put lots of stuff in Dropbox, but the actual folder location differs on my different computers. This function resolves to the Dropbox directory:
#+BEGIN_SRC emacs-lisp
2020-06-19 21:16:09 +00:00
(defcustom syncthing-path (expand-file-name "~/Sync")
"The absolute path to the Syncthing directory"
:type 'directory)
2019-10-23 20:27:19 +00:00
2020-06-19 21:16:09 +00:00
(defun syncthing-directory (&optional path)
(f-join syncthing-path (s-chop-prefix (f-path-separator) (or path ""))))
2019-01-28 02:46:29 +00:00
#+END_SRC
Load up libraries from Dropbox, if there are any:
#+BEGIN_SRC emacs-lisp
2020-06-19 21:16:09 +00:00
(add-to-list 'load-path (syncthing-directory "site-lisp"))
2019-01-28 02:46:29 +00:00
#+END_SRC
* Init File
A function to reload my init file. It reloads the major mode after the init file is loaded to rebind keymappings.
#+BEGIN_SRC emacs-lisp
(defun reload-init-file ()
(interactive)
2020-01-07 20:07:05 +00:00
(load-file "~/.emacs.el")
2019-01-28 02:46:29 +00:00
(funcall major-mode))
#+END_SRC
And another one to edit it:
#+BEGIN_SRC emacs-lisp
(defun find-init-file ()
(interactive)
(find-file "~/init.org"))
#+END_SRC
* Keybindings
These are general keybindings; those specific to certain packages are in the sections for those packages.
In some modes I want vanilla Emacs bindings:
#+BEGIN_SRC emacs-lisp
2019-11-14 21:03:13 +00:00
(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))
2019-01-28 02:46:29 +00:00
#+END_SRC
** 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)
#+END_SRC
** M-x
#+BEGIN_SRC emacs-lisp
(leader-def-key "SPC" 'execute-extended-command)
#+END_SRC
** Eval-ing
#+BEGIN_SRC emacs-lisp
2019-09-15 15:11:59 +00:00
(leader-def-key ":" #'eval-expression)
2019-01-28 02:46:29 +00:00
#+END_SRC
** 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
** Commands about files
#+BEGIN_SRC emacs-lisp
(jdormit/define-prefix "f" "files")
(leader-def-key "ff" 'find-file)
2020-05-18 14:33:03 +00:00
(leader-def-key "fd" 'dired)
2019-01-28 02:46:29 +00:00
(leader-def-key "fs" 'sudo-find-file)
(leader-def-key "ft" 'auto-revert-tail-mode)
2020-05-22 22:10:25 +00:00
(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
2019-10-23 20:27:19 +00:00
2019-01-28 02:46:29 +00:00
** Window commands
#+BEGIN_SRC emacs-lisp
(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)
2020-02-29 14:24:24 +00:00
(leader-def-key "ws" 'window-swap-states)
2019-01-28 02:46:29 +00:00
#+END_SRC
** 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
A function to kill all buffers except the current one from [[https://www.emacswiki.org/emacs/KillingBuffers#toc2 ][EmacsWiki ]]:
#+BEGIN_SRC emacs-lisp
(defun kill-other-buffers ()
"Kill all other buffers."
(interactive)
(mapc 'kill-buffer (delq (current-buffer) (buffer-list))))
#+END_SRC
#+BEGIN_SRC emacs-lisp
(jdormit/define-prefix "b" "buffer")
2019-03-02 17:45:46 +00:00
(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-01-02 03:03:12 +00:00
2019-01-28 02:46:29 +00:00
** Frame commands
#+BEGIN_SRC emacs-lisp
(jdormit/define-prefix "F" "frame")
2019-03-02 17:45:46 +00:00
(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
** Running shell commands
#+BEGIN_SRC emacs-lisp
(leader-def-key "!" 'shell-command)
(leader-def-key "|" 'shell-command-on-region)
#+END_SRC
** Toggles
Like in Spacemacs, put all toggle commands behind a prefix:
#+BEGIN_SRC emacs-lisp
(jdormit/define-prefix "t" "toggle")
#+END_SRC
Toggles about line truncation:
#+BEGIN_SRC emacs-lisp
(leader-def-key "tt" 'toggle-truncate-lines)
(leader-def-key "tT" 'visual-line-mode)
2020-02-22 03:28:06 +00:00
(leader-def-key "tf" 'auto-fill-mode)
2019-01-28 02:46:29 +00:00
#+END_SRC
Toggle lisp debugging:
#+BEGIN_SRC emacs-lisp
(leader-def-key "td" 'toggle-debug-on-error)
#+END_SRC
** Shells/REPLs
Emacs has a shell for every mood!
#+BEGIN_SRC emacs-lisp
(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)
#+END_SRC
** Applications
#+BEGIN_SRC emacs-lisp
(jdormit/define-prefix "a" "applications")
#+END_SRC
2019-01-30 15:21:42 +00:00
** Help Buffers
#+BEGIN_SRC emacs-lisp
(general-def 'motion help-mode-map "TAB" #'forward-button)
#+END_SRC
2019-11-05 15:03:40 +00:00
** Code commands
#+BEGIN_SRC emacs-lisp
(jdormit/define-prefix "c" "code")
2019-11-07 17:05:06 +00:00
(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-11-05 15:03:40 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
2020-05-22 22:10:37 +00:00
** Process list
#+BEGIN_SRC emacs-lisp
(general-def '(normal motion visual) process-menu-mode-map
"d" #'process-menu-delete-process)
#+END_SRC
2020-01-07 16:14:34 +00:00
* Evil-numbers
Handy functions to increment/decrement numbers at point:
#+BEGIN_SRC emacs-lisp
(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))
#+END_SRC
2020-01-02 04:04:19 +00:00
* Winner
Winner is a minor mode that keeps an undo/redo history of the window configuration:
#+BEGIN_SRC emacs-lisp
(winner-mode 1)
(leader-def-key "wn" #'winner-redo)
(leader-def-key "wp" #'winner-undo)
#+END_SRC
2020-05-06 04:00:46 +00:00
* Buffer move
A handy package to shift buffers between open windows:
#+BEGIN_SRC emacs-lisp
(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-01-07 17:45:23 +00:00
* Info
#+BEGIN_SRC emacs-lisp
(use-package info
:init
(defhydra hydra-info (:color blue
:hint nil)
"
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.
"
("]" Info-forward-node)
("[" Info-backward-node)
("n" Info-next)
("p" Info-prev)
("s" Info-search)
("S" Info-search-case-sensitively)
("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)
("?" 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))
#+END_SRC
2019-11-11 22:01:43 +00:00
* xref
After I select an xref reference, I want the xref buffer closed:
#+BEGIN_SRC emacs-lisp
(defun xref-goto-xref-and-quit ()
(interactive)
(xref-goto-xref t))
2020-02-26 15:05:22 +00:00
(general-def 'normal xref--xref-buffer-mode-map "RET"
#'xref-goto-xref-and-quit :keymaps 'override)
2019-11-11 22:01:43 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
2020-02-26 15:05:22 +00:00
Don't prompt for an identifier for these xref commands:
#+BEGIN_SRC emacs-lisp
(setq xref-prompt-for-identifier
'(not xref-find-definitions
xref-find-definitions-other-window
xref-find-definitions-other-frame
xref-find-references))
#+END_SRC
Some keybindings:
#+BEGIN_SRC emacs-lisp
2020-02-26 20:20:55 +00:00
(general-def "M-r" #'xref-find-references)
2020-02-26 15:05:22 +00:00
#+END_SRC
2020-01-07 17:45:23 +00:00
* IBuffer
#+BEGIN_SRC emacs-lisp
(use-package ibuffer
:straight (:type built-in)
:init
(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)
("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))
#+END_SRC
2019-11-07 21:28:35 +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:
#+BEGIN_SRC emacs-lisp
(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))
2019-11-07 22:01:14 +00:00
(leader-def-key "S" #'switch-to-speedbar)
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
* 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
(use-package ws-butler
:straight (ws-butler :host github :repo "lewang/ws-butler")
:hook ((prod-mode . ws-butler-mode)))
#+END_SRC
2019-01-29 14:51:56 +00:00
* Whitespace Visualation
#+BEGIN_SRC emacs-lisp
(setq whitespace-line-column 80
whitespace-style '(face lines-tail))
(leader-def-key "tw" #'whitespace-mode)
#+END_SRC
2019-01-28 02:46:29 +00:00
* Line Numbers
Toggle line numbers:
#+BEGIN_SRC emacs-lisp
(setq display-line-numbers-type 'visual)
(leader-def-key "tn" 'display-line-numbers-mode)
#+END_SRC
Toggle line numbering mode (normal or relative):
#+BEGIN_SRC emacs-lisp
(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)))))
(leader-def-key "tr" #'toggle-line-number-mode)
#+END_SRC
Display line numbers by default in code and org-mode buffers:
#+BEGIN_SRC emacs-lisp
(add-hook 'prog-mode-hook #'display-line-numbers-mode)
(add-hook 'org-mode-hook #'display-line-numbers-mode)
#+END_SRC
* Amx
A better M-x.
#+BEGIN_SRC emacs-lisp
(use-package amx
:config
(amx-mode))
#+END_SRC
* Olivetti Mode
Olivetti is a minor mode for a nice writing environment.
#+BEGIN_SRC emacs-lisp
(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
* Winum
This package includes functions to switch windows by number.
#+BEGIN_SRC emacs-lisp
2020-05-28 16:19:20 +00:00
(defun winum-assign-0-to-dired-sidebar ()
(when (equal major-mode 'dired-sidebar-mode) 10))
2019-10-29 14:46:24 +00:00
2019-01-28 02:46:29 +00:00
(use-package winum
:config
(winum-mode)
2020-05-28 16:19:20 +00:00
(add-to-list 'winum-assign-functions #'winum-assign-0-to-dired-sidebar)
2019-01-28 02:46:29 +00:00
(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))
#+END_SRC
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
(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
* Backups and Autosaves
Store backups and autosaves in a centralized place. This should really be the default...
#+BEGIN_SRC emacs-lisp
(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-01-07 14:45:37 +00:00
* Smartparens/Parinfer
Smartparens enables structured editing of s-expressions and other pairs:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2020-01-07 14:45:37 +00:00
(use-package smartparens
2020-06-03 19:09:09 +00:00
:hook ((prog-mode . smartparens-strict-mode)
2020-06-04 19:11:15 +00:00
(eshell-mode . smartparens-strict-mode)
(inferior-python-mode . smartparens-strict-mode))
2020-01-07 15:52:05 +00:00
:init
(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"
;; 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)
;; Slurping & barfing
("h" sp-backward-slurp-sexp)
("H" sp-backward-barf-sexp)
("l" sp-forward-slurp-sexp)
("L" sp-forward-barf-sexp)
;; Wrapping
("R" sp-rewrap-sexp)
("u" sp-unwrap-sexp)
("U" sp-backward-unwrap-sexp)
("(" sp-wrap-round)
("{" sp-wrap-curly)
("[" sp-wrap-square)
;; 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)
;; 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)
("q" nil)
("g" nil))
2020-01-07 16:52:33 +00:00
:config
(require 'smartparens-config)
2020-06-03 19:09:09 +00:00
(show-smartparens-global-mode t)
2020-01-07 15:52:05 +00:00
:general
(prog-mode-map "C-c p" 'hydra-smartparens/body)
2020-06-03 19:09:09 +00:00
(normal prog-mode-map "g p" 'hydra-smartparens/body)
(normal "g[" 'sp-wrap-square)
(normal "g(" 'sp-wrap-round)
(normal "g{" 'sp-wrap-curly))
2019-01-28 02:46:29 +00:00
2020-01-07 14:45:37 +00:00
(use-package evil-smartparens
:after (evil smartparens)
:hook ((smartparens-enabled . evil-smartparens-mode)))
2019-01-28 02:46:29 +00:00
(jdormit/define-prefix "l" "lisp")
(jdormit/define-prefix "lw" "wrap")
2020-01-07 14:45:37 +00:00
(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)
2019-01-28 02:46:29 +00:00
#+END_SRC
2020-02-05 20:46:08 +00:00
Enable ES6 arrow functions in web-mode ("borrowed" from [[https://github.com/Fuco1/smartparens/issues/823#issuecomment-403019519 ][this GitHub comment ]]):
#+BEGIN_SRC emacs-lisp
2020-02-06 17:46:35 +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-02-05 20:46:08 +00:00
2020-02-06 17:46:35 +00:00
(defun sp-after-equals-skip (ms mb _me)
(when (eq ms ">")
(save-excursion
(goto-char mb)
(sp--looking-back-p "=" 1))))
2020-02-05 20:46:08 +00:00
2020-02-06 17:46:35 +00:00
(sp-local-pair '(web-mode) "<" nil
:unless '(:add sp-after-equals-p)
:skip-match 'sp-after-equals-skip-p))
2020-02-05 20:46:08 +00:00
#+END_SRC
2020-02-23 14:40:29 +00:00
Parinfer infers parens from indentation and vice-versa. Currently
disabled since it turned out to be more annoying than good...
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(use-package parinfer
2020-02-23 14:40:29 +00:00
:disabled
2019-01-28 02:46:29 +00:00
:init
(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)))
#+END_SRC
2019-08-13 13:50:47 +00:00
* jq
The JSON multitool.
#+BEGIN_SRC emacs-lisp
2019-11-14 21:03:13 +00:00
(use-package jq-mode
:commands (jq-mode jq-interactively))
2019-08-13 13:50:47 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-01-28 02:46:29 +00:00
* Org Mode
Notes, agenda, calendar, blogging, journaling, etc.
2020-06-19 21:02:58 +00:00
A function to get my org directory:
#+BEGIN_SRC emacs-lisp
(defun org-directory (&optional path)
"Returns the directory for my org notes, appends PATH if given"
2020-06-19 21:16:09 +00:00
(f-join (syncthing-directory "org") (s-chop-prefix (f-path-separator) (or path ""))))
2020-06-19 21:02:58 +00:00
#+END_SRC
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
(jdormit/define-prefix "o" "org")
(leader-def-key "oa" 'org-agenda)
(leader-def-key "oc" 'org-capture)
2020-01-07 14:45:46 +00:00
(setq org-src-fontify-natively t
2020-05-05 20:54:22 +00:00
org-ellipsis " ▼"
2020-06-19 21:02:58 +00:00
org-directory (org-directory)
2020-05-05 20:54:22 +00:00
org-link-elisp-confirm-function 'y-or-n-p
org-startup-with-inline-images t)
2020-04-05 14:52:29 +00:00
(add-hook 'org-mode-hook #'auto-fill-mode)
2019-01-28 02:46:29 +00:00
#+END_SRC
Use RET to follow links:
#+BEGIN_SRC emacs-lisp
(setq org-return-follows-link t)
#+END_SRC
Always show inline images:
#+BEGIN_SRC emacs-lisp
(add-hook 'org-mode-hook
(lambda ()
(org-display-inline-images nil t)
(org-redisplay-inline-images)))
#+END_SRC
2020-02-07 21:46:20 +00:00
Include a timestamp when TODO entries are closed:
#+BEGIN_SRC emacs-lisp
(setq org-log-done 'time)
#+END_SRC
2020-02-22 03:28:23 +00:00
Tell Emacs how to open file links of various types:
2020-02-04 16:13:42 +00:00
#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'org
2020-02-22 03:28:23 +00:00
(add-to-list 'org-file-apps '(directory . emacs))
(add-to-list 'org-file-apps '("log" . emacs)))
2020-02-04 16:13:42 +00:00
#+END_SRC
2020-02-04 16:21:56 +00:00
A function to add ids to every heading in an Org file:
#+BEGIN_SRC emacs-lisp
(defun org-get-create-ids-all ()
(interactive)
(save-excursion
(goto-char (point-max))
(while (outline-previous-heading)
(org-id-get-create))))
#+END_SRC
2019-01-28 02:46:29 +00:00
** Agenda files
#+BEGIN_SRC emacs-lisp
2019-02-05 22:36:50 +00:00
(defun agenda-files (&optional file)
2020-06-19 21:02:58 +00:00
(let ((agenda-dir (org-directory)))
2019-02-05 22:36:50 +00:00
(if file
(concat (file-name-as-directory agenda-dir) file)
agenda-dir)))
(setq org-agenda-files `(,(agenda-files)))
2019-01-28 02:46:29 +00:00
#+END_SRC
** Capture templates
#+BEGIN_SRC emacs-lisp
(setq org-capture-templates
2019-10-29 14:46:42 +00:00
`(("L" "Lola task" entry
2019-10-09 13:52:11 +00:00
(file+headline ,(agenda-files "todo.org") "Lola")
2019-01-28 02:46:29 +00:00
"* TODO %i%?")
2019-10-09 13:52:11 +00:00
("p" "Personal task" entry
(file+headline ,(agenda-files "todo.org") "Personal")
2019-01-28 02:46:29 +00:00
"* TODO %i%?")
("n" "Note" entry
2019-02-05 22:36:50 +00:00
(file ,(agenda-files "notes.org"))
2019-01-28 02:46:29 +00:00
"* %^{Description}\n%i%?")
2019-08-26 14:19:54 +00:00
("l" "Log" entry
(file ,(agenda-files "log.org"))
2020-02-19 14:33:25 +00:00
"* %<%Y-%m-%d %H:%M:%S >%?")))
2019-01-28 02:46:29 +00:00
#+END_SRC
** Refile targets
#+BEGIN_SRC emacs-lisp
(setq org-refile-use-outline-path 'file
2019-02-07 00:14:03 +00:00
org-refile-targets `((org-agenda-files :level . 0)
2019-02-08 14:05:26 +00:00
(,(agenda-files "notes.org") :level . 1)
(,(agenda-files "todo.org") :level . 1)))
2019-01-28 02:46:29 +00:00
#+END_SRC
2019-10-23 20:27:19 +00:00
2019-01-28 02:46:29 +00:00
** Todo keywords
#+BEGIN_SRC emacs-lisp
2020-06-14 16:25:51 +00:00
(setq org-todo-keywords
'((sequence "TODO(t)" "IN PROGRESS(i)" "BLOCKED(b)" "|" "DONE(d)" "CANCELLED(c)")))
2019-01-28 02:46:29 +00:00
#+END_SRC
** Agenda views
#+BEGIN_SRC emacs-lisp
2020-04-16 21:28:22 +00:00
(setq org-agenda-todo-ignore-scheduled 'future
org-agenda-tags-todo-honor-ignore-options t
org-agenda-span 'day
org-agenda-custom-commands
2019-09-24 16:08:49 +00:00
'(("L" "Lola" ((tags-todo "@lola")))
2020-04-16 21:28:22 +00:00
("t" "TODOs"
2019-01-28 02:46:29 +00:00
((agenda)
2020-04-16 21:28:22 +00:00
(alltodo)))))
2019-01-28 02:46:29 +00:00
#+END_SRC
** Keybindings
#+BEGIN_SRC emacs-lisp
(general-def 'normal org-mode-map "T" #'org-insert-todo-heading)
(general-def 'normal org-mode-map "K" #'org-move-subtree-up)
(general-def 'normal org-mode-map "J" #'org-move-subtree-down)
(general-def 'normal org-mode-map "<return >" #'org-return)
2019-01-29 22:54:34 +00:00
(general-def 'normal org-mode-map "TAB" #'org-cycle)
2019-11-19 15:39:34 +00:00
(general-def 'normal org-mode-map "SPC" leader-map)
2020-02-17 04:10:30 +00:00
(general-def '(normal motion visual) org-mode-map "gn" #'org-next-link)
(general-def '(normal motion visual) org-mode-map "gp" #'org-previous-link)
2019-01-28 02:46:29 +00:00
(general-def org-mode-map "C-c e" #'org-preview-latex-fragment)
(general-def "C-c l" #'org-store-link)
#+END_SRC
2019-11-19 15:39:34 +00:00
Set up evil keybindings:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2019-11-19 15:39:34 +00:00
(use-package evil-org
:after (evil org)
2020-01-07 22:04:34 +00:00
:hook (org-mode . evil-org-mode)
2019-11-19 15:39:34 +00:00
:config
(add-hook 'evil-org-mode-hook
2020-05-06 04:00:46 +00:00
(lambda ()
(evil-org-set-key-theme
'(textobjects
insert
navigation
additional
shift
todo))
(general-def 'insert org-mode-map [backspace] 'org-delete-backward-char)))
2019-11-19 15:39:34 +00:00
(require 'evil-org-agenda)
2019-11-20 13:53:28 +00:00
(evil-org-agenda-set-keys)
2019-11-21 14:47:45 +00:00
(general-def 'motion org-agenda-mode-map "SPC" leader-map)
2020-05-06 04:00:46 +00:00
(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-01-28 02:46:29 +00:00
#+END_SRC
2019-02-09 01:12:27 +00:00
And a global keybinding to open an org file:
#+BEGIN_SRC emacs-lisp
(defun find-org-file (file)
(interactive
2019-11-27 15:12:29 +00:00
(list (completing-read
2019-02-09 01:12:27 +00:00
"Find org file: "
(directory-files (agenda-files) t))))
(find-file file))
(leader-def-key "fo" #'find-org-file)
#+END_SRC
2020-01-07 17:45:23 +00:00
Finally, add a helpful agenda-mode hydra:
#+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))
#+END_SRC
2019-01-28 02:46:29 +00:00
** Exporting
*** HTML
Export to HTML:
#+BEGIN_SRC emacs-lisp
(use-package htmlize
:commands htmlize-buffer)
#+END_SRC
Don't put section numbers in front of headers:
#+BEGIN_SRC emacs-lisp
(setq org-export-with-section-numbers nil)
#+END_SRC
Disable the preamble and postamble:
#+BEGIN_SRC emacs-lisp
(setq org-html-preamble nil
org-html-postamble nil)
#+END_SRC
Redefine org-html-src-block to wrap code blocks in <pre ><code > and language class for use by highlight.js:
#+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)))))))
#+END_SRC
2020-01-09 22:49:49 +00:00
*** Github-flavored markdown
#+BEGIN_SRC emacs-lisp
(use-package ox-gfm
:defer t
:init
(with-eval-after-load 'org
(require 'ox-gfm)))
#+END_SRC
2020-02-25 14:48:27 +00:00
*** Jira
#+BEGIN_SRC emacs-lisp
(use-package ox-jira
:defer t
:straight (:host github :repo "stig/ox-jira.el")
:init
(with-eval-after-load 'org
(require 'ox-jira)))
#+END_SRC
2019-01-28 02:46:29 +00:00
** org-babel
Literate programming!
#+BEGIN_SRC emacs-lisp
2019-11-14 21:03:13 +00:00
(add-hook 'org-mode-hook
2019-01-28 02:46:29 +00:00
(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)
2019-02-28 01:58:11 +00:00
(ditaa . t)
2019-04-23 11:16:02 +00:00
(ledger . t)
2019-08-13 13:50:47 +00:00
(sql . t)
2019-11-07 22:01:35 +00:00
(jq . t)
(restclient . t)))))
2019-01-28 02:46:29 +00:00
#+END_SRC
Get rid of the confirmation prompt:
#+BEGIN_SRC emacs-lisp
(setq org-confirm-babel-evaluate nil)
#+END_SRC
Display inline images after executing a source block:
#+BEGIN_SRC emacs-lisp
(add-hook 'org-babel-after-execute-hook
(lambda ()
(org-display-inline-images nil t)
2019-10-23 20:27:19 +00:00
(org-redisplay-inline-images)))
2019-01-28 02:46:29 +00:00
#+END_SRC
2019-10-29 14:47:01 +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.
#+BEGIN_SRC emacs-lisp
(use-package ob-async
2020-01-07 22:04:34 +00:00
:straight (ob-async :host github :repo "astahlman/ob-async")
:defer t
:hook (org-mode . (lambda () (require 'ob-async))))
2019-10-29 14:47:01 +00:00
#+END_SRC
2020-01-29 21:04:36 +00:00
Filter out the "u" from unicode results in org tabels returned from Python source blocks:
#+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))))
#+END_SRC
2019-02-07 21:53:52 +00:00
** Images
#+BEGIN_SRC emacs-lisp
2019-12-13 22:11:06 +00:00
(setq org-image-actual-width nil)
2019-02-07 21:53:52 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-11-07 17:05:44 +00:00
** org-scratch
It's very useful to open a new org buffer for a quick org-babel exploration.
#+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)))
(leader-def-key "os" #'org-scratch)
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-11-20 14:52:34 +00:00
** org-gcal
Integrate Google calendar with org-mode:
#+BEGIN_SRC emacs-lisp
(defun get-calendar-file (name)
2020-06-19 21:02:58 +00:00
(org-directory name))
2019-11-20 14:52:34 +00:00
2020-06-22 14:45:27 +00:00
(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)))))))
2019-11-20 14:52:34 +00:00
(use-package org-gcal
2020-06-22 14:45:27 +00:00
:hook ((emacs-startup . org-gcal-fetch))
2019-11-20 14:52:34 +00:00
:commands (org-gcal-sync
2020-06-19 21:02:58 +00:00
org-gcal-fetch
org-gcal-post-at-point
org-gcal-delete-at-point
2020-06-22 14:45:27 +00:00
org-gcal-request-token)
2019-11-20 14:52:34 +00:00
:config
2020-06-22 14:45:27 +00:00
(advice-add 'org-gcal-sync :around #'org-gcal-sync-advice)
2019-11-20 14:52:34 +00:00
(setq org-gcal-client-id (password-store-get "lola-org-gcal-client-id")
2020-06-19 21:02:58 +00:00
org-gcal-client-secret (password-store-get "lola-org-gcal-client-secret")
2020-06-22 14:45:27 +00:00
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")))
2020-06-19 21:02:58 +00:00
org-gcal-notify-p nil))
2019-11-22 23:20:07 +00:00
(defun org-agenda-redo-and-fetch-gcal (&optional all)
(interactive "P")
(let ((cb (if all #'org-agenda-redo-all #'org-agenda-redo)))
2020-06-22 14:45:27 +00:00
(deferred:nextc (org-gcal-fetch) cb)))
2019-11-22 23:20:07 +00:00
2020-03-10 14:43:18 +00:00
(with-eval-after-load 'org-agenda
(general-def '(normal motion) org-agenda-mode-map "gR" #'org-agenda-redo-and-fetch-gcal))
2019-11-20 14:52:34 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
2020-02-13 19:21:04 +00:00
** Utilities
A function to get the TITLE property of the current org buffer:
#+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))))))))
#+END_SRC
2020-05-05 20:54:22 +00:00
* org-present
An ultra-minimalist presentation mode for Org:
#+BEGIN_SRC emacs-lisp
(use-package org-present
:commands (org-present)
: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))
#+END_SRC
2020-02-03 22:31:11 +00:00
* link-hint
A very helpful package that provides jump-to-link functionality:
#+BEGIN_SRC emacs-lisp
(use-package link-hint
:commands (link-hint-open-link
link-hint-copy-link)
:init
(jdormit/define-prefix "ol" "link-hint")
(leader-def-key "oll" #'link-hint-open-link)
2020-04-17 16:44:41 +00:00
(leader-def-key "olc" #'link-hint-copy-link))
2020-02-03 22:31:11 +00:00
#+END_SRC
* org-cliplink
Intelligently inserts an org-mode link from the clipboard.
#+BEGIN_SRC emacs-lisp
(use-package org-cliplink
2020-02-03 23:22:21 +00:00
:commands (org-cliplink
org-cliplink-clipboard-content)
2020-02-03 22:31:11 +00:00
:general
(org-mode-map "C-c C-S-l" #'org-cliplink))
#+END_SRC
2020-02-04 16:14:04 +00:00
* org-board
[[https://github.com/scallywag/org-board ][Org-board ]] is a bookmarking/archiving system built on Org mode:
#+BEGIN_SRC emacs-lisp
(use-package org-board
:defer t
2020-02-19 14:33:25 +00:00
:after org
:init
2020-04-17 17:25:46 +00:00
;; Org-capture setup
2020-02-19 14:33:25 +00:00
(defvar org-capture-bookmark-last-url nil)
2020-02-25 14:48:04 +00:00
(defvar org-capture-bookmark-last-title nil)
(defun org-capture-bookmark-get-url ()
2020-02-19 14:33:25 +00:00
(let* ((clip (org-cliplink-clipboard-content))
2020-06-19 21:02:58 +00:00
(parsed (url-generic-parse-url clip)))
(if (url-type parsed)
clip
(read-string "Bookmark URL: "))))
2020-02-25 14:48:04 +00:00
(defun org-capture-bookmark-get-title (url)
(or (org-cliplink-retrieve-title-synchronously url)
2020-06-19 21:02:58 +00:00
(read-string "Bookmark title: ")))
2020-02-25 14:48:04 +00:00
2020-04-17 17:25:46 +00:00
(defun bookmark-file (title)
2020-06-19 21:02:58 +00:00
(org-directory (format "%s.org" (org-roam--get-new-id title))))
2020-04-17 17:25:46 +00:00
2020-02-25 14:48:04 +00:00
(defun org-capture-bookmark-file ()
(let* ((url (org-capture-bookmark-get-url))
2020-06-19 21:02:58 +00:00
(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-02-25 14:48:04 +00:00
(defun org-capture-bookmark-link ()
(format "[[%s ][%s ]]"
2020-06-19 21:02:58 +00:00
org-capture-bookmark-last-url
org-capture-bookmark-last-title))
2020-02-25 14:48:04 +00:00
(defun org-capture-bookmark-title ()
org-capture-bookmark-last-title)
2020-04-17 17:25:46 +00:00
(defun save-bookmark (url)
(save-excursion
2020-06-19 21:02:58 +00:00
(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))))))))
2020-04-17 17:25:46 +00:00
2020-02-19 14:33:25 +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))
2020-06-19 21:02:58 +00:00
(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))))
2020-02-25 14:48:04 +00:00
2020-02-19 14:33:25 +00:00
(add-hook 'org-capture-prepare-finalize-hook
2020-06-19 21:02:58 +00:00
#'org-capture-bookmark-after-finalize)
2020-02-25 14:48:04 +00:00
2020-02-19 14:33:25 +00:00
(add-to-list 'org-capture-templates
2020-06-19 21:02:58 +00:00
'("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"))
2020-04-17 17:25:46 +00:00
;; Org-protocol setup
(defun make-org-protocol-bookmark (url title)
(with-temp-buffer
2020-06-19 21:02:58 +00:00
(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)))))
2020-04-17 17:25:46 +00:00
(defun bookmark-via-org-protocol (url)
(org-cliplink-retrieve-title (url-unhex-string url) #'make-org-protocol-bookmark))
(add-to-list 'org-protocol-protocol-alist
2020-06-19 21:02:58 +00:00
'("Bookmark"
:protocol "bookmark"
:function bookmark-via-org-protocol
:kill-client t))
2020-04-17 17:25:46 +00:00
2020-02-19 14:33:25 +00:00
:config
2020-03-05 00:41:29 +00:00
(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")
2020-02-19 14:33:25 +00:00
;; Use w3m instead of eww to open org-board archived links
(advice-add 'org-board-open-with :around
2020-06-19 21:02:58 +00:00
(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)))))
2020-02-04 16:14:04 +00:00
:general
(org-mode-map "C-c b" org-board-keymap))
#+END_SRC
* org-rifle
Quickly find stuff in Org files:
#+BEGIN_SRC emacs-lisp
(use-package helm-org-rifle
:defer t
: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)
2020-02-17 04:10:30 +00:00
(leader-def-key "or" helm-org-rifle-commands-map)
(jdormit/define-prefix "or" "org-rifle"))
2020-02-04 16:14:04 +00:00
#+END_SRC
2020-02-11 21:04:44 +00:00
* Org Noter
[[https://github.com/weirdNox/org-noter ][Org Noter ]] lets me take org-mode notes on PDFs, epubs, and DocView files:
#+BEGIN_SRC emacs-lisp
(use-package org-noter
:commands org-noter
:general
((normal visual motion) org-noter-doc-mode-map "i" #'org-noter-insert-note)
((normal visual motion) org-noter-doc-mode-map "q" #'org-noter-kill-session)
((normal visual motion) org-noter-doc-mode-map "C-M-n" #'org-noter-sync-next-note)
((normal visual motion) org-noter-doc-mode-map "C-M-p" #'org-noter-sync-prev-note)
((normal visual motion) org-noter-doc-mode-map "M-." #'org-noter-sync-current-page-or-chapter)
((normal visual motion) org-noter-doc-mode-map "M-i" #'org-noter-insert-precise-note)
((normal visual motion) org-noter-doc-mode-map "M-n" #'org-noter-sync-next-page-or-chapter)
((normal visual motion) org-noter-doc-mode-map "M-p" #'org-noter-sync-prev-page-or-chapter)
((normal visual motion) org-noter-doc-mode-map "C-M-." #'org-noter-sync-current-note))
#+END_SRC
2020-02-16 15:35:51 +00:00
* Org Roam
[[https://org-roam.readthedocs.io/en/develop/ ][Org-roam ]] is another backlink package for org-mode:
#+BEGIN_SRC emacs-lisp
(use-package org-roam
:after org
2020-05-21 17:56:51 +00:00
:straight (:host github :repo "jethrokuan/org-roam")
2020-02-16 15:35:51 +00:00
:commands
(org-roam
org-roam-today
org-roam-find-file
org-roam-insert
org-roam-show-graph
org-roam--get-new-id)
:hook
2020-05-22 22:11:02 +00:00
(after-init . org-roam-mode)
2020-02-16 15:35:51 +00:00
:custom
2020-06-19 21:02:58 +00:00
(org-roam-directory (org-directory))
2020-02-16 15:35:51 +00:00
:init
(leader-def-key "fo" #'org-roam-find-file)
(leader-def-key "of" #'org-roam-find-file)
(defvar org-roam-map (make-sparse-keymap))
2020-02-17 04:10:30 +00:00
(leader-def-key "on" org-roam-map)
(jdormit/define-prefix "on" "org-roam")
2020-02-16 15:35:51 +00:00
(which-key-add-key-based-replacements "C-c n" "org-roam")
(which-key-add-major-mode-key-based-replacements
'org-mode "gn" "org-roam")
2020-02-17 20:21:17 +00:00
:config
2020-02-19 14:32:07 +00:00
(add-hook 'org-roam-backlinks-mode-hook #'olivetti-mode)
2020-02-16 15:35:51 +00:00
:general
(org-roam-map "l" #'org-roam)
(org-roam-map "t" #'org-roam-today)
(org-roam-map "f" #'org-roam-find-file)
(org-roam-map "i" #'org-roam-insert)
(org-roam-map "g" #'org-roam-show-graph)
2020-02-17 04:10:30 +00:00
((normal motion visual) org-mode-map "gr" org-roam-map)
2020-05-22 22:11:02 +00:00
((normal motion visual) org-roam-backlinks-mode-map "<return >" #'org-open-at-point)
((normal motion visual emacs) org-roam-backlinks-mode-map "q" #'quit-window)
2020-02-16 15:35:51 +00:00
("C-c n" org-roam-map))
#+END_SRC
2020-02-19 14:34:21 +00:00
* 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:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2020-02-19 14:34:21 +00:00
(use-package org-journal
:defer t
2020-01-07 17:45:23 +00:00
:init
2020-02-19 14:34:21 +00:00
(defun org-journal-file-header-func ()
(let ()
(format "#+TITLE: %s"
(format-time-string "%Y-%m-%d"))))
(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))
(add-to-list 'org-capture-templates
'("j" "Journal entry" entry (function org-journal-capture-func)
"* %(format-time-string org-journal-time-format)\n%?"))
(jdormit/define-prefix "oj" "org-journal")
(leader-def-key "ojn" #'org-journal-new-entry)
(leader-def-key "ojt" #'org-journal-today)
:custom
(org-journal-file-type 'daily)
2020-06-19 21:02:58 +00:00
(org-journal-dir (org-directory))
2020-02-19 14:34:21 +00:00
(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))
#+END_SRC
2020-01-07 17:45:23 +00:00
2020-04-16 21:28:22 +00:00
* org-super-agenda
#+BEGIN_SRC emacs-lisp
(use-package org-super-agenda
:init
(setq org-super-agenda-groups
2020-06-14 16:25:51 +00:00
'((:name "In progress"
:todo "IN PROGRESS")
(:name "Lola"
:tag "@lola")
(:name "unifyDB"
:tag "@unifydb")
(:name "Personal"
:tag "@personal")))
2020-04-16 21:28:22 +00:00
(org-super-agenda-mode)
:config
(setq org-super-agenda-header-map (make-sparse-keymap)))
#+END_SRC
2020-02-19 14:34:21 +00:00
* Projectile
#+BEGIN_SRC emacs-lisp
2020-03-09 20:30:48 +00:00
(use-package projectile
:commands (projectile-find-file
projectile-grep
projectile-switch-project
projectile-project-root)
:init
(defhydra hydra-projectile (:color teal :hint nil) "
2020-02-19 14:34:21 +00:00
PROJECTILE: %(projectile-project-root)
2020-01-07 17:45:23 +00:00
2020-02-19 14:34:21 +00:00
Find File Search/Tags Buffers Cache
------------------------------------------------------------------------------------------
_s-f_ : file _a_ : ag _i_ : Ibuffer _c_ : cache clear _ff_ : file dwim
_g_ : update gtags _b_ : switch to buffer _x_ : remove known project
_fd_ : file curr dir _o_ : multi-occur _s-k_ : Kill all buffers _X_ :
cleanup non-existing _r_ : recent file ^^^^_z_ : cache current _d_ :
dir
2020-03-09 20:30:48 +00:00
"
("a" projectile-ag)
("b" projectile-switch-to-buffer)
("c" projectile-invalidate-cache)
("d" projectile-find-dir)
("s-f" projectile-find-file)
("ff" projectile-find-file-dwim)
("fd" projectile-find-file-in-directory)
("g" ggtags-update-tags)
("s-g" ggtags-update-tags)
("i" projectile-ibuffer)
("K" projectile-kill-buffers)
("s-k" projectile-kill-buffers)
("m" projectile-multi-occur)
("o" projectile-multi-occur)
("s-p" projectile-switch-project "switch project")
("p" projectile-switch-project)
("s" projectile-switch-project)
("r" projectile-recentf)
("x" projectile-remove-known-project)
("X" projectile-cleanup-known-projects)
("z" projectile-cache-current-file)
("`" hydra-projectile-other-window/body "other window")
("q" nil "cancel" :color blue))
:config
(projectile-mode)
(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)
(cd (projectile-project-root)))
,@body))
2020-02-19 14:34:21 +00:00
#+END_SRC
2019-01-28 02:46:29 +00:00
* Mode line
* UI
Get rid of the janky buttons:
2020-02-12 19:24:00 +00:00
#+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:
2020-02-12 19:24:00 +00:00
#+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:
2020-02-12 19:24:00 +00:00
#+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
2019-01-29 22:54:22 +00:00
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
2019-08-19 17:36:09 +00:00
Render stuff differently based on whether or not we are graphical:
2019-01-28 02:46:29 +00:00
#+BEGIN_SRC emacs-lisp
2019-08-19 17:36:09 +00:00
(defun graphical-setup ()
(when (display-graphic-p (selected-frame))
2019-12-27 16:29:48 +00:00
(message "Running graphically")))
2019-08-19 17:36:09 +00:00
(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
2019-08-19 18:19:11 +00:00
(defvar no-background-in-tty-faces '(default line-number magit-section-highlight))
2019-08-19 17:36:09 +00:00
(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")
2019-01-30 15:21:58 +00:00
(leader-def-key "ut" #'customize-themes)
(leader-def-key "uf" #'customize-face)
2019-08-25 19:00:43 +00:00
(leader-def-key "uc" #'display-time-mode)
(leader-def-key "ub" #'display-battery-mode)
2019-01-28 02:46:29 +00:00
#+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
2020-02-15 12:38:45 +00:00
: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)
2020-03-19 20:56:33 +00:00
2020-03-23 18:48:19 +00:00
;; 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
2020-05-21 17:57:30 +00:00
(symbol-value 'centaur-tabs-projectile-buffer-group-calc)
2020-03-23 18:48:19 +00:00
(set (make-local-variable 'centaur-tabs-projectile-buffer-group-calc)
2020-05-21 17:57:30 +00:00
(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"))))
2020-03-23 18:48:19 +00:00
(symbol-value 'centaur-tabs-projectile-buffer-group-calc)))
2020-04-04 10:34:02 +00:00
;; Don't show tabs for certain types of buffers
(advice-add 'centaur-tabs-hide-tab :around
2020-05-21 17:57:30 +00:00
(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)))
2020-05-21 17:57:30 +00:00
t
(apply oldfn buf args))))
2020-04-04 10:34:02 +00:00
2020-02-15 18:31:33 +00:00
;; Only show tabs in buffers visiting files
(advice-add 'centaur-tabs-line :around
2020-05-21 17:57:30 +00:00
(lambda (oldfn &rest args)
(if (buffer-file-name)
(apply oldfn args)
(setq header-line-format nil))))
2020-03-19 20:56:33 +00:00
2020-02-26 22:33:28 +00:00
;; Enable prefix argument for tab switching keybindings
2020-02-26 20:21:16 +00:00
(advice-add 'centaur-tabs-forward :around
2020-05-21 17:57:30 +00:00
(lambda (oldfn &rest args)
(if (numberp current-prefix-arg)
(dotimes (_ current-prefix-arg)
(apply oldfn args))
(apply oldfn args))))
2020-02-26 20:21:16 +00:00
(advice-add 'centaur-tabs-backward :around
2020-05-21 17:57:30 +00:00
(lambda (oldfn &rest args)
(if (numberp current-prefix-arg)
(dotimes (_ current-prefix-arg)
(apply oldfn args))
(apply oldfn args))))
2020-03-19 20:56:33 +00:00
2020-02-26 22:33:28 +00:00
;; Use Org-mode titles for tab names when possible
(advice-add 'centaur-tabs-buffer-tab-label :around
2020-05-21 17:57:30 +00:00
(lambda (oldfn tab &rest args)
(if-let ((title (or (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
2020-05-21 17:57:30 +00:00
(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
2020-02-15 18:31:33 +00:00
(git-commit-mode . (lambda ()
2020-05-21 17:57:30 +00:00
(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
2020-02-11 22:13:57 +00:00
(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
2020-07-07 16:04:39 +00:00
(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"))
(add-hook 'eshell-mode-hook #'eshell-setup)
#+END_SRC
2019-08-12 15:25:47 +00:00
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)
2020-04-28 20:04:29 +00:00
(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
2019-01-28 02:46:29 +00:00
Some aliases:
#+BEGIN_SRC emacs-lisp
2020-02-07 15:29:48 +00:00
(setq eshell-aliases
2019-10-09 13:52:50 +00:00
'(("k" . "kubectl $*")
2020-02-07 15:29:48 +00:00
("kctx" . "kubectx $*")
("root" . "cd (projectile-project-root)")))
2019-10-09 13:52:50 +00:00
(add-hook
'eshell-mode-hook
(lambda ()
(dolist (alias eshell-aliases)
(eshell/alias (car alias) (cdr alias)))))
2019-01-28 02:46:29 +00:00
#+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))
(if (= (user-uid) 0) " # " " $ ")))))
(setq jdormit-eshell-prompt-regex "^[^#$\n]* [#$] ")
(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)
(emacs-lisp-mode . aggressive-indent-mode)))
#+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
:mode (("\\.json\\'" . json-mode)))
2019-06-07 13:46:05 +00:00
(use-package json-navigator
:commands (json-navigator-navigator
2020-06-14 16:26:31 +00:00
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
2019-12-17 14:25:30 +00:00
(defun json-pprint ()
2019-12-16 19:00:28 +00:00
(interactive)
(let ((begin (if (region-active-p) (region-beginning) (point-min)))
2020-06-14 16:26:31 +00:00
(end (if (region-active-p) (region-end) (point-max))))
2019-12-17 14:25:30 +00:00
(if (executable-find "jq")
2020-06-14 16:26:31 +00:00
(shell-command-on-region begin end "jq ." nil t)
(json-pretty-print begin end))))
2019-12-16 19:00:28 +00:00
2019-12-17 14:25:30 +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)
("\\.js\\'" . web-mode)
2019-11-04 15:03:12 +00:00
("\\.jsx\\'" . web-mode)
2019-12-01 16:45:22 +00:00
("\\.mako\\'" . web-mode)
2019-12-27 21:22:50 +00:00
("\\.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
'(("django" . "\\.jinja2\\'")))
2019-02-15 19:25:01 +00:00
(add-hook 'web-mode-hook
2019-02-18 13:44:21 +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"))
2020-04-28 13:27:32 +00:00
(lsp-deferred))))
2020-01-31 16:19:04 +00:00
(add-hook 'web-mode-hook #'disable-tab-insert))
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
2019-12-27 15:38:55 +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
2020-02-11 15:58:00 +00:00
** 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
2020-04-28 13:27:32 +00:00
: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")
2020-04-28 13:27:32 +00:00
(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
2020-04-28 13:27:32 +00:00
(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)
2020-02-17 20:21:33 +00:00
((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-10 21:06:36 +00:00
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
2020-05-18 14:33:43 +00:00
:defer 0
2020-04-28 16:52:41 +00:00
:commands (pyvenv-mode
2020-05-18 14:33:43 +00:00
pyvenv-tracking-mode
2020-04-28 16:52:41 +00:00
pyvenv-workon
pyvenv-activate
2020-05-18 14:33:43 +00:00
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:
2020-02-04 16:42:41 +00:00
#+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)
2020-02-04 16:42:41 +00:00
#+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
2020-02-19 14:31:01 +00:00
: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)
2020-04-06 15:58:29 +00:00
((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")
2020-02-25 18:58:14 +00:00
(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"))
2020-02-25 18:58:14 +00:00
(if debug
2020-05-01 15:54:30 +00:00
(realgud:pdb cmdline)
2020-02-25 18:58:14 +00:00
(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
2020-02-25 18:58:14 +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)))
2020-02-25 18:58:14 +00:00
(run-nose (format "%s %s" args nose-args) debug)))
2020-02-14 17:38:18 +00:00
2020-02-25 18:58:14 +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)
2020-02-25 18:58:14 +00:00
(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))))
2020-02-25 18:58:14 +00:00
(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)))
2020-02-25 18:58:14 +00:00
(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))
2020-02-25 18:58:14 +00:00
(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)))
2020-02-25 18:58:14 +00:00
(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")
2020-02-25 18:58:14 +00:00
(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)
2020-02-25 18:58:14 +00:00
(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)
2020-02-14 17:47:13 +00:00
2020-02-16 15:35:29 +00:00
(which-key-add-major-mode-key-based-replacements
'python-mode "C-c t n" "nosetests")
2020-02-25 18:58:14 +00:00
(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
2020-04-28 13:27:32 +00:00
(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)
2020-06-14 16:26:45 +00:00
("\\.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)
2020-06-14 16:26:45 +00:00
(:+ 3)
(match 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
2020-06-02 13:40:46 +00:00
(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)
(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))
:general
2019-11-07 17:06:51 +00:00
(cider-stacktrace-mode-map "SPC" leader-map)
('normal cider-mode-map "M-." #'cider-find-var))
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
2020-04-28 21:37:12 +00:00
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."
(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)
2019-01-28 02:46:29 +00:00
:general
(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
2019-11-24 04:53:23 +00:00
:commands (slime)
2019-01-28 02:46:29 +00:00
:config
(setq inferior-lisp-program
2019-11-14 21:03:13 +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
2019-11-14 21:03:13 +00:00
(expand-file-name "~/quicklisp/slime-helper.el"))
2020-01-20 04:41:00 +00:00
(load (expand-file-name "~/quicklisp/slime-helper.el")))
2020-04-05 14:56:51 +00:00
(add-hook 'slime-repl-mode-hook 'smartparens-strict-mode)
: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
An Eshell alias to start PHP using XDebug:
#+BEGIN_SRC emacs-lisp
(add-hook 'eshell-mode-hook
(lambda ()
(eshell/alias "php-debug"
"php -d xdebug.remote_enable=on -d xdebug.remote_host=127.0.0.1 -d xdebug.remote_port=9000 -d xdebug.remote_handler=dbgp -d xdebug.idekey=geben -d xdebug.remote_autostart=On $*")))
#+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
2020-04-28 13:27:32 +00:00
(add-hook 'php-mode-hook #'lsp-deferred)
2019-01-28 02:46:29 +00:00
#+END_SRC
2019-04-23 11:18:57 +00:00
* 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")
2020-04-28 13:27:32 +00:00
(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
2020-04-28 13:27:32 +00:00
(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
2020-04-28 13:27:32 +00:00
(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
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
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)
"
2020-05-08 21:45:25 +00:00
╭───────────┐
Move History Scale/Fit Annotations Search/Link Do │ PDF Tools │
2020-01-07 17:45:23 +00:00
╭──────────────────────────────────────────────────────────────────┴───────────╯
2020-05-08 21:45:25 +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
2020-05-08 21:45:25 +00:00
^^^↓^^^ ^ ^ ╰─^─^─╯ ^ ^ ╰─^ ^─╯ [_y_ ] yank
^^_n_ ^^ ^ ^ _r_eset slice box
^^^↓^^^
^^_G_ ^^
2020-01-07 17:45:23 +00:00
--------------------------------------------------------------------------------
2020-05-08 21:45:25 +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)
2020-05-08 21:45:25 +00:00
("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-05-08 21:45:25 +00:00
((normal motion visual) pdf-view-mode-map "." #'hydra-pdftools/body)
((normal motion visual) pdf-view-mode-map "F" #'pdf-links-action-perform))
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
2020-01-03 15:15:49 +00:00
For some reason Emacs is starting up with the dashboard and the *scratch* buffer open in a split configuration. Not sure why, but let's put a stop to that...
#+BEGIN_SRC emacs-lisp
(add-hook
'after-init-hook
(lambda ()
(switch-to-buffer "*dashboard* ")
(delete-other-windows)))
#+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
2020-04-20 20:20:21 +00:00
(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")
2020-04-20 20:56:10 +00:00
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")
2020-06-25 18:01:29 +00:00
(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")
2020-05-09 20:20:35 +00:00
(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
2020-05-05 20:55:00 +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
2020-04-22 13:51:45 +00:00
Support sending rich-text emails via Markdown:
#+BEGIN_SRC emacs-lisp
2020-05-09 20:21:09 +00:00
(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"
2020-05-11 02:31:00 +00:00
(or template *message-md-pandoc-html-template* ))))
2020-05-09 20:21:09 +00:00
(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)))))
2020-05-11 02:31:00 +00:00
(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))))
2020-04-22 13:51:45 +00:00
(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."
2020-04-22 13:51:45 +00:00
(interactive)
(let* ((begin
2020-05-05 20:55:00 +00:00
(save-excursion
(goto-char (point-min))
(search-forward mail-header-separator)))
(end (point-max))
2020-05-11 02:31:00 +00:00
(raw-body (buffer-substring begin end)))
2020-04-22 13:51:45 +00:00
(undo-boundary)
(delete-region begin end)
(save-excursion
2020-05-05 20:55:00 +00:00
(goto-char begin)
(newline)
2020-05-11 02:31:00 +00:00
(insert (multipart-html-message raw-body)))))
2020-04-22 13:51:45 +00:00
(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))
2020-04-22 20:50:34 +00:00
(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))
2020-05-05 20:55:14 +00:00
;; Handle replies to HTML emails as well
2020-05-09 20:21:09 +00:00
(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
2020-05-18 15:52:32 +00:00
"--quiet"
"-f" "html-native_divs-native_spans"
"-t" "gfm"))
2020-05-09 20:21:09 +00:00
(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))
2020-05-11 16:41:15 +00:00
;; 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))))))
2020-05-11 16:59:40 +00:00
(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)))))
2020-06-05 17:59:35 +00:00
;; 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)
2020-04-22 13:51:45 +00:00
#+END_SRC
2020-04-20 20:20:21 +00:00
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
2020-04-21 21:06:10 +00:00
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))))))
2020-04-21 21:06:10 +00:00
#+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
2020-05-07 15:15:27 +00:00
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"
2020-05-07 15:15:27 +00:00
w3m-search-default-engine "duckduckgo"
w3m-cookie-reject-domains '("www.wsj.com"
"www.bbc.com"
"www.nytimes.com"
"www.washingtonpost.com")
w3m-use-tab-line nil
2020-05-11 16:41:55 +00:00
browse-url-browser-function '(("nytimes.com" . w3m-browse-url)
("wsj.com" . w3m-browse-url)
2020-05-07 15:15:27 +00:00
("." . browse-url-default-browser)))
2020-02-07 15:29:34 +00:00
:general
('normal w3m-mode-map "SPC" leader-map)
2020-05-20 12:58:13 +00:00
('(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")
2020-07-07 16:04:39 +00:00
((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
2020-04-23 13:47:29 +00:00
(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")
(general-def 'normal elfeed-search-mode-map "q" 'elfeed-search-quit-window)
(general-def 'normal elfeed-search-mode-map "C-r" 'elfeed-search-update--force)
(general-def 'normal elfeed-search-mode-map "R" 'elfeed-search-fetch)
(general-def 'normal elfeed-search-mode-map "RET" 'elfeed-search-show-entry)
(general-def 'normal elfeed-search-mode-map "s" 'elfeed-search-live-filter)
(general-def 'normal elfeed-search-mode-map "S" 'elfeed-search-set-filter)
(general-def 'normal elfeed-search-mode-map "B" 'elfeed-search-browse-url)
(general-def 'normal elfeed-search-mode-map "y" 'elfeed-search-yank)
(general-def 'normal elfeed-search-mode-map "u" 'elfeed-search-tag-all-unread)
(general-def 'normal elfeed-search-mode-map "r" 'elfeed-search-untag-all-unread)
(general-def 'normal elfeed-search-mode-map "n" 'next-line)
(general-def 'normal elfeed-search-mode-map "p" 'previous-line)
(general-def 'normal elfeed-search-mode-map "+" 'elfeed-search-tag-all)
(general-def 'normal elfeed-search-mode-map "-" 'elfeed-search-untag-all)
(general-def 'normal elfeed-show-mode-map "d" 'elfeed-show-save-enclosure)
(general-def 'normal elfeed-show-mode-map "q" 'elfeed-kill-buffer)
(general-def 'normal elfeed-show-mode-map "r" 'elfeed-show-refresh)
(general-def 'normal elfeed-show-mode-map "n" 'elfeed-show-next)
(general-def 'normal elfeed-show-mode-map "p" 'elfeed-show-prev)
(general-def 'normal elfeed-show-mode-map "s" 'elfeed-show-new-live-search)
2020-06-19 14:35:43 +00:00
(general-def 'normal elfeed-show-mode-map "B" 'elfeed-show-visit)
2019-01-28 02:46:29 +00:00
(general-def 'normal elfeed-show-mode-map "y" 'elfeed-show-yank)
(general-def 'normal elfeed-show-mode-map "u" (elfeed-expose #'elfeed-show-tag 'unread))
(general-def 'normal elfeed-show-mode-map "+" 'elfeed-show-tag)
(general-def 'normal elfeed-show-mode-map "-" 'elfeed-show-untag)
(general-def 'normal elfeed-show-mode-map "SPC" 'scroll-up-command)
(general-def 'normal elfeed-show-mode-map "DEL" 'scroll-down-command)
(general-def 'normal elfeed-show-mode-map "\t" 'shr-next-link)
(general-def 'normal elfeed-show-mode-map [tab] 'shr-next-link)
(general-def 'normal elfeed-show-mode-map "\e\t" 'shr-previous-link)
(general-def 'normal elfeed-show-mode-map [backtab] 'shr-previous-link)
(general-def 'normal elfeed-show-mode-map [mouse-2] 'shr-browse-url)
(general-def 'normal elfeed-show-mode-map "A" 'elfeed-show-add-enclosure-to-playlist)
(general-def 'normal elfeed-show-mode-map "P" 'elfeed-show-play-enclosure))
(use-package elfeed
:commands elfeed
:config
(add-hook 'elfeed-show-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)
2020-06-17 18:58:11 +00:00
;; NYTimes - The Morning newsletter
("https://www.kill-the-newsletter.com/feeds/lb5od7xsvr3nwmg9qp2a.xml" news)
2020-05-05 20:55:35 +00:00
("https://metaredux.com/feed.xml" clojure)
2020-06-18 17:04:24 +00:00
("https://lambdaisland.com/feeds/blog.atom" clojure)
2020-06-14 16:27:53 +00:00
("https://emacsredux.com/atom.xml" emacs)
("https://sachachua.com/blog/category/emacs-news/feed" emacs)
2020-06-19 14:35:43 +00:00
("https://mxb.dev/feed.xml" web)
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-06-19 21:03:22 +00:00
"https://wiki.xxiivv.com/links/rss.xml"
2020-06-14 16:27:53 +00:00
("https://jeremydormitzer.com/blog/feed.xml" my-website))))
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
2019-11-11 22:01:43 +00:00
: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
:commands (define-word define-word-at-point))
(jdormit/define-prefix "r" "research")
(leader-def-key "rd" 'define-word-at-point)
(leader-def-key "rD" 'define-word)
#+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
2020-06-04 19:11:02 +00:00
** Keybindings
#+BEGIN_SRC emacs-lisp
2020-06-05 17:36:00 +00:00
(general-def 'normal dired-mode-map "f" #'dired-create-empty-file)
2020-06-04 19:11:02 +00:00
#+END_SRC
2020-06-03 20:51:55 +00:00
** Dired subtree
#+BEGIN_SRC emacs-lisp
(use-package dired-subtree
: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
2020-05-06 04:01:00 +00:00
#+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)))
2020-06-03 20:51:40 +00:00
(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")))))))
2020-06-03 20:51:40 +00:00
(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)
2020-05-29 20:18:51 +00:00
(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-06-03 20:10:39 +00:00
:hook ((dired-mode . (lambda ()
(unless (eq major-mode 'dired-sidebar-mode)
(all-the-icons-dired-mode))))))
2020-05-06 04:01:00 +00:00
#+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
2019-02-07 00:15:05 +00:00
(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
:config
(unless (file-exists-p (expand-file-name "~/.emacs.d/snippets"))
(mkdir (expand-file-name "~/.emacs.d/snippets") t))
(setq yas-snippet-dirs
2020-06-19 21:16:09 +00:00
`(,(syncthing-directory "yasnippet")
,(expand-file-name "~/.emacs.d/snippets")))
2019-01-28 02:46:29 +00:00
(yas-global-mode))
(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
2020-05-01 15:53:58 +00:00
* 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)
2020-05-01 15:53:58 +00:00
(counsel-mode 1)
2019-11-27 15:12:29 +00:00
(setq ivy-height 20
2020-05-01 15:53:58 +00:00
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")
2020-05-01 15:53:58 +00:00
(leader-def-key "ir" #'ivy-resume)
2020-05-20 17:10:52 +00:00
(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)
2019-12-12 20:12:08 +00:00
(leader-def-key "bb" #'counsel-ibuffer)
2020-02-26 15:06:23 +00:00
2019-12-12 20:23:19 +00:00
(if (executable-find "rg")
2020-05-01 15:53:58 +00:00
(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))
2020-05-01 15:53:58 +00:00
: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)))
2019-12-13 21:03:49 +00:00
(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))
2020-05-01 15:53:58 +00:00
(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
2020-05-01 15:53:58 +00:00
("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)
2020-05-20 17:10:52 +00:00
(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
2020-05-01 15:53:58 +00:00
counsel-projectile-switch-project
counsel-projectile-find-file
counsel-projectile-grep)
2019-11-27 17:42:07 +00:00
:init
2020-05-01 15:53:58 +00:00
(counsel-projectile-mode)
2019-11-29 15:28:18 +00:00
(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")
2020-05-01 15:53:58 +00:00
(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
2020-05-01 15:53:58 +00:00
lsp-ivy-global-workspace-symbol))
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
2019-12-27 15:38:55 +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
2020-06-19 21:16:09 +00:00
'(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))
2019-01-28 14:52:04 +00:00
(general-def emms-browser-mode-map "," leader-map)
2019-01-29 14:51:41 +00:00
(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)
2020-06-19 21:16:09 +00:00
(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
2019-07-19 14:39:59 +00:00
(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")
2019-10-29 14:47:41 +00:00
:config
(direnv-mode)
2019-02-07 21:54:35 +00:00
(add-hook 'eshell-mode-hook #'direnv-update-directory-environment)
2019-10-29 14:47:41 +00:00
(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
2019-04-23 11:18:57 +00:00
* 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
2019-02-26 22:44:45 +00:00
#+BEGIN_SRC emacs-lisp
2019-04-05 14:05:40 +00:00
(use-package kubernetes
:ensure t
:commands (kubernetes-overview)
2019-04-23 11:19:29 +00:00
: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
2019-04-23 11:20:32 +00:00
* AWS
** 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)
2020-04-17 22:09:05 +00:00
(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")
2020-04-17 22:09:05 +00:00
(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"))))
2020-04-17 22:09:05 +00:00
:general
((normal) s3ed-mode-map "B" #'s3ed-open-blob-log))
2019-04-23 11:20:32 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-11-18 22:33:26 +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)
2019-11-21 14:47:45 +00:00
:general
('normal 'prodigy-mode-map "SPC" leader-map)
('normal 'prodigy-view-mode-map "SPC" leader-map)
2019-11-18 22:33:26 +00:00
:config
2020-01-21 15:44:23 +00:00
(setq prodigy-completion-system 'default)
2019-11-18 22:33:26 +00:00
(add-hook 'prodigy-view-mode-hook (lambda () (toggle-truncate-lines 1))))
2019-11-20 13:53:16 +00:00
(leader-def-key "aP" #'prodigy)
2019-11-18 22:33:26 +00:00
#+END_SRC
2019-11-19 06:48:12 +00:00
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)
2019-11-19 07:05:39 +00:00
(auto-revert-tail-mode)
(general-define-key
:states 'normal
:keymaps 'local
"Q" #'kill-this-buffer))
2019-11-19 06:48:12 +00:00
(prodigy-switch-to-process-buffer service))))
(general-def 'normal prodigy-mode-map "`" #'prodigy-display-process-file-or-buffer))
#+END_SRC
2019-11-19 15:40:05 +00:00
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
2019-11-18 22:33:26 +00:00
** Services (Prodigy)
#+BEGIN_SRC emacs-lisp
(defun call-with-venv (venv callback)
2020-05-20 13:51:18 +00:00
(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)))))
2020-05-20 13:51:18 +00:00
(call-with-env-from-file (format "%s/bin/activate" venv-dir) callback)))
2019-11-18 22:33:26 +00:00
2019-12-19 22:00:03 +00:00
(defun kill-log-buffers ()
(interactive)
(kill-matching-buffers "\\.log$" nil t)
(message "Killed log buffers"))
2020-03-19 19:44:01 +00:00
(cl-defun python-service-setup (venv &optional env-file &key env-dir)
2019-11-18 22:33:26 +00:00
(lambda (done)
(call-with-venv
venv
(if env-file
2020-05-20 13:51:18 +00:00
(lambda ()
(call-with-env-from-file env-file done :dir env-dir))
done))))
2019-11-18 22:33:26 +00:00
2020-02-14 15:21:17 +00:00
(defun call-with-lola-env (callback)
(let ((process-environment
2020-05-20 13:51:18 +00:00
(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)
2019-11-18 22:33:26 +00:00
(prodigy-define-service
:name "lola-server (gunicorn)"
2019-12-13 21:04:09 +00:00
:tags '(lola backend)
2019-11-19 06:48:12 +00:00
:command "bash"
:args (lambda ()
2020-05-20 13:51:18 +00:00
(list
"-c"
2020-06-02 13:41:04 +00:00
"gunicorn -c server/web/gunicorn.conf.py \
2019-11-19 06:48:12 +00:00
-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"
2019-11-19 15:40:05 +00:00
:inhibit-process-filter t
2019-11-18 22:33:26 +00:00
:cwd "~/lola/lola-server"
:stop-signal 'int
:truncate-output t
:init-async (python-service-setup "lola-server"
2020-05-20 13:51:18 +00:00
"~/lola/lola-server/ .env"))
2019-11-18 22:33:26 +00:00
(prodigy-define-service
:name "lola-server celery worker"
2019-12-13 21:04:09 +00:00
:tags '(lola backend)
2019-11-18 22:33:26 +00:00
:command "python"
2020-02-12 19:24:38 +00:00
:args '("bin/start_celery_worker.py" "-P" "gevent" "-n" "lola-server")
2019-11-18 22:33:26 +00:00
:cwd "~/lola/lola-server"
:stop-signal 'int
:truncate-output t
:init-async (python-service-setup "lola-server"
2020-05-20 13:51:18 +00:00
"~/lola/lola-server/ .env"))
2019-11-18 22:33:26 +00:00
(prodigy-define-service
:name "travel-service"
2019-12-13 21:04:09 +00:00
:tags '(lola backend)
2019-11-19 06:48:12 +00:00
:command "bash"
:args (lambda ()
2020-05-20 13:51:18 +00:00
(list
2020-06-02 13:41:04 +00:00
"-c" "python bin/start_web.py >> ~/lola/logs/travel-svc.log 2>&1"))
2019-11-18 22:33:26 +00:00
:cwd "~/lola/lola-travel-service"
2020-06-02 13:41:04 +00:00
:file "~/lola/logs/travel-svc.log"
2019-11-19 15:40:05 +00:00
:inhibit-process-filter t
2019-11-18 22:33:26 +00:00
:stop-signal 'int
:truncate-output t
:init-async (python-service-setup "travel-service"
2020-05-20 13:51:18 +00:00
"~/lola/lola-travel-service/ .env"))
2019-11-18 22:33:26 +00:00
(prodigy-define-service
:name "travel-service celery worker"
2019-12-13 21:04:09 +00:00
:tags '(lola backend)
2019-11-22 23:20:25 +00:00
:command "bash"
:args (lambda ()
2020-05-20 13:51:18 +00:00
(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"
2019-11-22 23:20:25 +00:00
:inhibit-process-filter t
2019-11-18 22:33:26 +00:00
:cwd "~/lola/lola-travel-service"
:stop-signal 'int
:truncate-output t
:init-async (python-service-setup "travel-service"
2020-05-20 13:51:18 +00:00
"~/lola/lola-travel-service/ .env"))
2019-11-18 22:33:26 +00:00
(prodigy-define-service
:name "secrets"
2019-12-13 21:04:09 +00:00
:tags '(lola backend)
2019-11-18 22:33:26 +00:00
:command "python"
:args '("bin/cmdline.py" "www")
:cwd "~/lola/secrets"
:truncate-output t
:stop-signal 'int
:init-async (python-service-setup "secrets"
2020-05-20 13:51:18 +00:00
"~/lola/secrets/ .env"))
2019-11-18 22:33:26 +00:00
(prodigy-define-service
:name "lola-desktop"
2019-12-13 21:04:09 +00:00
:tags '(lola frontend)
2019-11-18 22:33:26 +00:00
:command "npm"
:args '("start")
:cwd "~/lola/lola-desktop"
:port 3001
2019-11-21 14:47:45 +00:00
:env '(("PORT" "3001"))
2019-11-18 22:33:26 +00:00
:stop-signal 'int
:init-async #'call-with-lola-env)
(prodigy-define-service
:name "wallet"
2019-12-13 21:04:09 +00:00
:tags '(lola frontend)
2019-11-18 22:33:26 +00:00
:command "npm"
:args '("start")
:cwd "~/lola/wallet"
:stop-signal 'int
2019-11-21 14:47:45 +00:00
:env '(("PORT" "3000"))
2019-11-18 22:33:26 +00:00
:init-async #'call-with-lola-env)
2020-02-11 15:58:18 +00:00
(prodigy-define-service
:name "agent-console"
:tags '(lola frontend)
:command "npm"
:args '("start")
:cwd "~/lola/agent-console"
:stop-signal 'int
:env '(("PORT" "3002"))
:init-async (lambda (done)
2020-05-20 13:51:18 +00:00
(call-with-lola-env
(lambda ()
(nvm-use "v10.15.1" done)))))
2020-02-11 15:58:18 +00:00
2019-11-18 22:33:26 +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"
2020-05-20 13:51:18 +00:00
"~/lola/data-pipeline/ .env"))
2019-11-18 22:33:26 +00:00
(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"
2019-12-13 21:04:09 +00:00
:tags '(lola backend)
2020-05-29 18:35:29 +00:00
:command "~/lola/python_services/priceline/bin/start.sh"
2019-11-18 22:33:26 +00:00
:args '("web")
2020-05-29 18:35:29 +00:00
:cwd "~/lola/python_services"
2019-11-18 22:33:26 +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/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
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"
:tags '(lola backend)
: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"))
2020-01-08 21:11:18 +00:00
(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"))
2020-02-14 15:59:19 +00:00
:stop-signal 'int
:init-async (lambda (done)
2020-05-20 13:51:18 +00:00
(nvm-use "10.15.1" done)))
2020-01-08 21:11:18 +00:00
2019-12-06 17:27:46 +00:00
(prodigy-define-service
:name "mabl-link-agent"
:command "link-agent"
:args (lambda ()
2020-05-20 13:51:18 +00:00
(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"))
2019-11-18 22:33:26 +00:00
#+END_SRC
2020-01-02 03:03:12 +00:00
2019-11-18 22:33:26 +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)))
2019-02-26 22:44:45 +00:00
(switch-to-buffer buf)
2019-04-05 14:05:40 +00:00
(cd dir)
2019-02-26 22:44:45 +00:00
(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
2019-02-26 22:44:45 +00:00
(jdormit/define-prefix "L" "lola")
2019-09-24 16:09:00 +00:00
(leader-def-key "Lr" #'release-manager)
2019-02-26 22:44:45 +00:00
#+END_SRC
2019-04-05 14:05:40 +00:00
2020-02-14 15:21:53 +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 11:21:34 +00:00
** AWS-MFA
2019-04-05 14:05:40 +00:00
The aws-mfa command:
#+BEGIN_SRC emacs-lisp
(defun aws-mfa (mfa-token)
(interactive "MMFA code: ")
2019-04-23 13:02:42 +00:00
(let ((proc (start-process "aws-mfa"
"*aws-mfa* "
2020-04-21 18:02:19 +00:00
"aws-mfa"
2019-04-23 13:02:42 +00:00
"--force")))
(set-process-sentinel
proc
(make-success-err-msg-sentinel "*aws-mfa* "
"AWS MFA succeeded"
"AWS MFA failed, check *aws-mfa* buffer for details"))
2019-04-05 14:05:40 +00:00
(process-send-string proc (concat mfa-token "\n"))))
(with-eval-after-load 'kubernetes
2019-04-23 11:21:34 +00:00
(general-def kubernetes-overview-mode-map "m" #'aws-mfa))
2019-04-05 14:05:40 +00:00
#+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))))
2019-04-23 15:30:19 +00:00
(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)))))))
2019-04-23 15:30:19 +00:00
(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-04-23 15:30:19 +00:00
2019-06-03 14:48:42 +00:00
(defun op-get-item-field (item-json field-designation)
2019-04-23 15:30:19 +00:00
(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))))
2019-04-23 15:30:19 +00:00
(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")))
2019-04-23 15:30:19 +00:00
(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)))
2019-04-23 15:30:19 +00:00
(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
2019-05-13 12:38:04 +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.")
2020-04-17 16:44:53 +00:00
(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
2020-04-17 16:44:53 +00:00
(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
2020-02-16 15:43:09 +00:00
(setq deft-extensions '("org" "txt" "md" "markdown" "text")
deft-recursive t
2020-06-19 21:02:58 +00:00
deft-directory (org-directory))
2020-02-16 15:43:09 +00:00
;; Still lots of notes in the old Deft directory
2020-06-19 21:02:58 +00:00
(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
2019-06-03 14:48:42 +00:00
deft-file-naming-rules '((noslash . "-")
(nospace . "-")
2019-11-10 05:47:20 +00:00
(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
2020-02-13 19:21:04 +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
:mode ("\\.p\\'" "\\.pp\\'" "\\.pm\\'"))
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))
2020-02-12 19:25:06 +00:00
(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
2019-11-21 14:47:45 +00:00
: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
2019-11-21 14:47:45 +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
2020-06-14 16:28:15 +00:00
* Flyspell
2019-12-12 19:33:34 +00:00
#+BEGIN_SRC emacs-lisp
(when (executable-find "hunspell")
(setq ispell-program-name "hunspell"
2020-06-14 16:28:15 +00:00
ispell-really-hunspell t))
(add-hook 'text-mode-hook #'flyspell-mode)
(add-hook 'prog-mode-hook #'flyspell-prog-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
2019-12-27 17:07:05 +00:00
(defun eshell-exec-in-vterm (&rest args)
(let* ((program (car args))
(buf (generate-new-buffer
(concat "*" (file-name-nondirectory program) "* "))))
(with-current-buffer buf
(vterm-mode)
(vterm-send-string (concat (s-join " " args) "\n")))
(switch-to-buffer buf)))
2019-12-26 16:59:45 +00:00
(use-package vterm
:if module-file-suffix
2019-12-27 17:07:05 +00:00
:init
(with-eval-after-load 'em-term
(defun eshell-exec-visual (&rest args)
(apply #'eshell-exec-in-vterm args)))
:commands (vterm vterm-other-window vterm-mode))
2019-12-26 16:59:45 +00:00
2020-02-11 22:13:57 +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))
(switch-to-buffer buffer-name)
(vterm buffer-name))))
2020-02-11 22:13:57 +00:00
(defun open-vterm (&optional arg)
(interactive "P")
(if (and (fboundp 'projectile-project-root)
(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
2020-01-03 14:58:59 +00:00
* Hide mode line
2020-05-28 16:19:20 +00:00
Does what it says on the box.
2020-01-03 14:58:59 +00:00
#+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))
2020-01-03 14:58:59 +00:00
#+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")
2020-02-13 19:21:45 +00:00
(add-hook 'org-capture-after-finalize-hook #'delete-frame t t)
2020-01-11 02:52:50 +00:00
(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
2020-01-13 19:10:13 +00:00
(use-package gist
2020-02-07 21:46:40 +00:00
:defer t
2020-01-13 19:10:13 +00:00
: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)
:init
2020-02-25 18:29:09 +00:00
(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
2020-02-07 19:35:04 +00:00
2020-02-19 14:30:43 +00:00
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)
2020-02-19 14:30:43 +00:00
(general-def (normal visual motion) compilation-shell-minor-mode-map "gr" #'recompile)
2020-02-14 17:38:18 +00:00
#+END_SRC
2020-02-07 19:35:04 +00:00
* 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:
2020-02-07 19:35:04 +00:00
#+BEGIN_SRC emacs-lisp
(use-package wallabag
2020-02-07 21:46:40 +00:00
:defer t
2020-02-07 19:35:04 +00:00
: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
: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
2020-05-06 14:13:41 +00:00
;; 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
2020-05-19 16:15:41 +00:00
vuiet-track-name
vuiet-track-duration
vuiet--playing-track)
2020-04-01 02:39:27 +00:00
:defer t
:config
2020-05-19 16:15:41 +00:00
(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)
2020-05-19 16:15:41 +00:00
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)))
2020-05-19 16:15:41 +00:00
(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 |
2020-04-12 15:25:11 +00:00
| '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' |
2020-04-04 10:18:03 +00:00
| |-| `'''''' || | _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
"
2020-04-04 10:18:03 +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)
2020-04-12 15:25:11 +00:00
("L" vuiet-love-track)
("U" vuiet-unlove-track)
2020-04-04 10:18:03 +00:00
("I" hydra-vuiet-info/body)
("S" hydra-vuiet-similar/body)
("q" nil))
2020-04-01 18:29:22 +00:00
(general-def "C-c v" #'hydra-vuiet/body)
2020-04-04 10:18:03 +00:00
(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-04-04 10:18:03 +00:00
"
2020-05-08 21:45:53 +00:00
--- ^^ ^^ ^^ ---
2020-04-04 10:18:03 +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
2020-04-04 10:18:03 +00:00
| '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)
2020-04-04 10:18:03 +00:00
("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-04-04 10:18:03 +00:00
"
2020-05-08 21:45:53 +00:00
--- ^^ ^^ ^^ ---
2020-04-04 10:18:03 +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-08 21:46:04 +00:00
* Jira
Jira in Emacs:
#+BEGIN_SRC emacs-lisp
(use-package org-jira
:init
(setq jiralib-url "https://lola.atlassian.net"
2020-06-19 21:02:58 +00:00
org-jira-working-dir (org-directory "jira")
2020-06-14 16:25:51 +00:00
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")))
2020-05-08 21:46:04 +00:00
(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))
#+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
2020-05-18 21:53:06 +00:00
(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")
:config
(setq structlog-db-username "jdormit"
structlog-db-database "fluentd"))
#+END_SRC