diff options
Diffstat (limited to 'init.el')
-rw-r--r-- | init.el | 2336 |
1 files changed, 949 insertions, 1387 deletions
diff --git a/init.el b/init.el index ffe7f81..3fcf374 100644 --- a/init.el +++ b/init.el | |||
@@ -1,1228 +1,977 @@ | |||
1 | ;;; emacs init --- an init for emacs -*- lexical-binding: t; -*- | 1 | ;;; init.el --- a config of one's own -*- lexical-binding: t; -*- |
2 | ;; by C. Duckworth <acdw@acdw.net> | 2 | ;; by C. Duckworth <acdw@acdw.net> |
3 | ;; URL: https://git.acdw.net/emacs | 3 | ;; Bankruptcy: 9.3 |
4 | ;; Bankruptcy: 9 | 4 | |
5 | ;; | 5 | ;;; Code: |
6 | ;; Everyone is permitted to do whatever they like with this software | 6 | |
7 | ;; without limitation. This software comes without any warranty | 7 | ;;; Remove when done bankrupting |
8 | ;; whatsoever, but with two pieces of advice: | 8 | |
9 | ;; - Be kind to yourself. | 9 | (defkeys t "C-x C-c" #'restart-emacs) |
10 | ;; - Make good choices. | 10 | |
11 | 11 | ;;; My extras | |
12 | (yoke +emacs | 12 | |
13 | (require* '+emacs '+window '+lisp) | 13 | (push (expand-file-name (locate-user-emacs-file "lisp")) load-path) |
14 | ;; Settings | 14 | (require 'acdw) ; Omnibus fun stuff |
15 | (setc truncate-string-ellipsis "…" | 15 | |
16 | ring-bell-function #'ignore | 16 | ;;; Basic defaults |
17 | read-file-name-completion-ignore-case t | 17 | |
18 | comment-auto-fill-only-comments t | 18 | (use-package no-littering |
19 | password-cache t | 19 | :ensure t |
20 | password-cache-expiry (* 60 60) | 20 | :demand t |
21 | switch-to-buffer-in-dedicated-window 'pop | 21 | :custom |
22 | switch-to-buffer-obey-display-actions t | 22 | (no-littering-etc-directory etc/) |
23 | initial-buffer-choice (defun +initial-buffer-choose () | 23 | (no-littering-var-directory var/) |
24 | (cond | 24 | (custom-file (etc/ "custom.el")) |
25 | ((equal (get-buffer "*Messages*") | 25 | (auto-save-file-name-transforms `(("." ,(var/ "auto-save/") t))) |
26 | (other-buffer)) | 26 | (auto-save-list-file-prefix (var/ "auto-save/.saves-" t)) |
27 | (get-buffer "*scratch*")) | 27 | (backup-directory-alist `(("." . ,(var/ "backup/" t))))) |
28 | (:else (other-buffer))))) | 28 | |
29 | ;; "Safe" variables | 29 | (use-package custom-allowed |
30 | (dolist (var+pred | 30 | :load-path "~/src/emacs/custom-allowed/" |
31 | '((browse-url-browser-function | 31 | :custom |
32 | ;; All types defined by custom are safe. | 32 | (custom-file (private/ "custom.el")) |
33 | . (lambda (f) | 33 | :config |
34 | ;; Whooooo boy | 34 | (dolist (var '(safe-local-variable-values |
35 | (memq f (mapcar (lambda (i) | 35 | warning-suppress-types |
36 | (plist-get (cdr i) :value)) | 36 | ispell-buffer-session-localwords |
37 | (seq-filter | 37 | calendar-latitude |
38 | (lambda (i) | 38 | calendar-longitude |
39 | (eq (car i) 'function-item)) | 39 | user-full-name |
40 | (cdr (get 'browse-url-browser-function | 40 | user-mail-address)) |
41 | 'custom-type))))))))) | 41 | (add-to-list 'custom-allowed-variables var)) |
42 | (put (car var+pred) 'safe-local-variable (cdr var+pred))) | 42 | :hook |
43 | ;; Keys | 43 | (after-init-hook . custom-allowed-load-custom-file)) |
44 | (defkeys t | 44 | |
45 | "C-x C-k" #'kill-current-buffer | 45 | (use-package modus-themes |
46 | "C-/" #'undo-only | 46 | :load-path "~/usr/share/emacs/30.0.50/etc/themes/" |
47 | "C-?" #'undo-redo | 47 | :custom |
48 | "C-x C-c" (defun delete-frame-or-quit (arg) | 48 | (modus-themes-bold-constructs t) |
49 | (interactive "P") | 49 | (modus-themes-italic-constructs t) |
50 | (cond (arg (delete-frame nil :force)) | 50 | (modus-themes-variable-pitch-ui nil)) |
51 | ((= 1 (length (frame-list))) | 51 | |
52 | (and (yes-or-no-p "Kill emacs? ") | 52 | (use-package dawn |
53 | (save-buffers-kill-emacs t))) | 53 | :load-path "~/src/emacs/dawn/" |
54 | (:else (delete-frame)))) | 54 | :after modus-themes |
55 | "C-x r q" (defun really-quit-emacs (arg) | 55 | :config |
56 | (interactive "P") | 56 | (load-theme 'modus-operandi :noconfirm :noenable) |
57 | (cond (arg (save-buffers-kill-emacs t)) | 57 | (load-theme 'modus-vivendi :noconfirm :noenable) |
58 | (:else (save-buffers-kill-terminal t)))) | 58 | (defhook custom-allowed-after-load-hook |
59 | "M-SPC" #'+cycle-spacing | 59 | (dawn-schedule-themes 'modus-operandi |
60 | ;; "M-/" #'hippie-expand ; `hippie-completing-read' | 60 | 'modus-vivendi) |
61 | "M-=" #'count-words | 61 | (set-face-attribute 'default nil |
62 | "C-x C-b" #'ibuffer | 62 | :family "IBM Plex Mono" |
63 | "C-x 4 n" #'clone-buffer | 63 | :height 100) |
64 | "S-<down-mouse-1>" #'mouse-set-mark | 64 | (set-face-attribute 'variable-pitch nil |
65 | "C-x 0" #'+delete-window-or-bury-buffer | 65 | :family "Georgia" |
66 | ;; "M-j" nil ; `avy' | 66 | :height 1.2))) |
67 | "<Scroll_Lock>" nil | 67 | |
68 | "C-z" nil | 68 | (use-package midnight |
69 | "M-o" #'other-window|switch-buffer | 69 | :config |
70 | "C-M-;" #'+lisp-comment-or-uncomment-sexp | 70 | (add-hook 'midnight-mode-hook #'recentf-cleanup) |
71 | "C-x 5 z" #'suspend-frame | 71 | (midnight-mode)) |
72 | "C-x f" #'find-file | 72 | |
73 | "C-c t" (defmap toggle-map | 73 | (use-package sophomore |
74 | "A map for toggling various settings." | 74 | :load-path "~/src/emacs/sophomore/" |
75 | "d" (defmap toggle-debug-map | 75 | :config |
76 | "Easily toggle debug flavors." | 76 | (sophomore-enable-all) |
77 | "e" #'toggle-debug-on-error | 77 | (sophomore-disable 'view-hello-file |
78 | "q" #'toggle-debug-on-quit) | 78 | 'describe-gnu-project |
79 | "w" #'toggle-word-wrap | 79 | 'suspend-frame) |
80 | "t" #'toggle-truncate-lines | 80 | (sophomore-mode)) |
81 | "c" #'column-number-mode | 81 | |
82 | "l" #'line-number-mode | 82 | ;;; Completions |
83 | "v" (defmap toggle-view-map | 83 | |
84 | "Easily toggle UI elements' views." | 84 | (use-package vertico |
85 | "c" #'display-fill-column-indicator-mode | 85 | :ensure t |
86 | "l" #'display-line-numbers-mode | 86 | :custom |
87 | "m" #'menu-bar-mode | 87 | (resize-mini-windows 'grow-only) |
88 | "t" #'tool-bar-mode | 88 | (vertico-cycle t) |
89 | "s" #'scroll-bar-mode))) | 89 | :init |
90 | (defkeys text-mode-map | 90 | (use-package vertico-directory |
91 | "C-M-k" #'kill-paragraph | 91 | :after vertico |
92 | "C-o" (defun open-paragraph (&optional arg) | 92 | :config |
93 | "Open a paragraph after paragraph at point. | 93 | (add-hook 'rfn-eshadow-update-overlay-hook #'vertico-directory-tidy)) |
94 | A paragraph is defined as continguous non-empty lines of text | 94 | (vertico-mode)) |
95 | surrounded by empty lines, so opening a paragraph means to make | 95 | |
96 | three blank lines, then place the point on the second one. | 96 | (use-package marginalia |
97 | 97 | :ensure t | |
98 | Called with prefix ARG, open a paragraph before point." | 98 | :demand t |
99 | ;; TODO: Take an integer as ARG, allowing for skipping paragraphs up and down. | 99 | :config |
100 | (interactive "*P") | 100 | (add-hook 'minibuffer-setup-hook #'truncate-lines-local-mode) |
101 | ;; Go to next blank line. This /isn't/ `end-of-paragraph-text' because | 101 | (marginalia-mode)) |
102 | ;; that's weird with org, and I'm guessing other modes too. | ||
103 | (unless (looking-at "^$") (forward-line (if arg -1 +1))) | ||
104 | (while (and (not (looking-at "^$")) | ||
105 | (= 0 (forward-line (if arg -1 +1))))) | ||
106 | (newline) | ||
107 | (when arg (newline) (forward-line -2)) | ||
108 | (delete-blank-lines) | ||
109 | (newline 2) | ||
110 | (previous-line))) | ||
111 | ;; Hooks | ||
112 | (add-hook 'after-save-hook | ||
113 | #'executable-make-buffer-file-executable-if-script-p) | ||
114 | (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode) | ||
115 | (add-hook 'find-file-not-found-functions #'+auto-create-missing-dirs) | ||
116 | (add-hook 'text-mode-hook #'abbrev-mode) | ||
117 | (add-hook 'find-file-hook #'+vc-off-when-remote) | ||
118 | (add-hook 'prog-mode-hook #'auto-fill-mode) | ||
119 | ;; Advice | ||
120 | (add-function :after after-focus-change-function | ||
121 | #'+save-some-buffers-debounce) | ||
122 | (define-advice keyboard-escape-quit (:around (fn &rest r) keep-window-open) | ||
123 | "Don't close quits on `keyboard-escape-quit'." | ||
124 | (let ((buffer-quit-function #'ignore)) | ||
125 | (apply fn r))) | ||
126 | ;; Faces | ||
127 | (set-face-attribute 'default nil :family "Comic Code" :height 100) | ||
128 | (set-face-attribute 'bold nil :family "Comic Code" :weight 'bold) | ||
129 | (set-face-attribute 'variable-pitch nil :family "Comic Code") | ||
130 | ;; Modes | ||
131 | (winner-mode)) | ||
132 | |||
133 | (yoke custom ; This is `cus-edit' but meh | ||
134 | (require '+custom) | ||
135 | (setf custom-file (private/ "custom.el")) | ||
136 | (add-to-list* '+custom-allowed-variables | ||
137 | 'safe-local-variable-values | ||
138 | 'warning-suppress-types | ||
139 | 'ispell-buffer-session-localwords) | ||
140 | (eval-after init | ||
141 | (+custom-load-some-customizations :noerror))) | ||
142 | |||
143 | ;; (yoke modus-themes | ||
144 | ;; (setc modus-themes-bold-constructs t | ||
145 | ;; modus-themes-italic-constructs t | ||
146 | ;; modus-themes-headings '((1 monochrome bold italic) | ||
147 | ;; (2 monochrome bold) | ||
148 | ;; (3 monochrom italic) | ||
149 | ;; (t monochrome))) | ||
150 | ;; (defhook modus-themes-after-load-theme-hook | ||
151 | ;; :name modus-monochrome | ||
152 | ;; (modus-themes-with-colors | ||
153 | ;; (cl-loop for x being the symbols | ||
154 | ;; if (string-match-p "\\`font-lock-.*-face\\'" | ||
155 | ;; (symbol-name x)) | ||
156 | ;; do | ||
157 | ;; (custom-set-faces | ||
158 | ;; `(,x ((,class :foreground | ||
159 | ;; ,(cond | ||
160 | ;; ((memq x '(font-lock-string-face | ||
161 | ;; font-lock-doc-face | ||
162 | ;; font-lock-doc-markup-face)) | ||
163 | ;; fg-special-warm) | ||
164 | ;; ((memq x '(font-lock-warning-face)) | ||
165 | ;; fg-lang-warning) | ||
166 | ;; ((memq x '(font-lock-comment-face)) | ||
167 | ;; fg-alt) | ||
168 | ;; (:else 'unspecified)) | ||
169 | ;; :background unspecified | ||
170 | ;; :weight | ||
171 | ;; ,(cond | ||
172 | ;; ((memq x '(font-lock-keyword-face)) | ||
173 | ;; 'bold) | ||
174 | ;; (:else 'normal)) | ||
175 | ;; :slant | ||
176 | ;; ,(cond | ||
177 | ;; ((memq x '(font-lock-doc-face | ||
178 | ;; font-lock-comment-face)) | ||
179 | ;; 'italic) | ||
180 | ;; (:else 'normal)) | ||
181 | ;; :underline | ||
182 | ;; ,(cond | ||
183 | ;; ((memq x '(font-lock-warning-face)) | ||
184 | ;; t) | ||
185 | ;; (:else nil))))))))) | ||
186 | ;; (when (or (custom-theme-enabled-p 'modus-operandi) | ||
187 | ;; (custom-theme-enabled-p 'modus-vivendi)) | ||
188 | ;; (modus-monochrome)) | ||
189 | ;; (cond ((require 'dawn nil :noerrer) | ||
190 | ;; (defhook +custom-after-load-hook | ||
191 | ;; :name dawn@custom | ||
192 | ;; (load-theme 'modus-operandi :noconfirm :noenable) | ||
193 | ;; (load-theme 'modus-vivendi :noconfirm :noenable) | ||
194 | ;; (dawn-schedule #'modus-themes-load-operandi | ||
195 | ;; #'modus-themes-load-vivendi))) | ||
196 | ;; (:else (modus-themes-load-operandi)))) | ||
197 | |||
198 | (yoke time | ||
199 | (setc display-time-mail-function | ||
200 | (defun +notmuch-new-mail-p () | ||
201 | (plist-get (cl-find "inbox+unread" | ||
202 | (ignore-errors | ||
203 | (notmuch-hello-query-counts notmuch-saved-searches)) | ||
204 | :key (lambda (l) (plist-get l :name)) | ||
205 | :test #'equal) | ||
206 | :count)) | ||
207 | display-time-use-mail-icon nil | ||
208 | display-time-mail-string (format "⋅ Mail (%s)" (+notmuch-new-mail-p)) | ||
209 | read-mail-command #'+notmuch-goto | ||
210 | display-time-format " %a %-e, %H:%M" | ||
211 | ;; `display-time-format' makes these unnecessary, but I'll keep em | ||
212 | display-time-24hr-format t | ||
213 | display-time-day-and-date t | ||
214 | display-time-default-load-average nil) | ||
215 | (define-advice display-time-update (:after (&rest _) update-mail-count) | ||
216 | (setq display-time-mail-string (format "⋅ Mail (%s)" (+notmuch-new-mail-p)))) | ||
217 | (display-time-mode)) | ||
218 | |||
219 | (yoke pita | ||
220 | (require 'pita) | ||
221 | (advice-add 'indent-region :before #'with-region-or-buffer)) | ||
222 | |||
223 | (yoke (undo-fu-session "https://codeberg.org/ideasman42/emacs-undo-fu-session") | ||
224 | (setc undo-fu-session-incompatible-files '("/COMMIT_EDITMSG\\'" | ||
225 | "/git-rebase-todo\\'") | ||
226 | undo-fu-session-directory (.etc "undo/" t) | ||
227 | undo-fu-session-compression (cond | ||
228 | ((executable-find "gzip") 'gz) | ||
229 | ((executable-find "bzip2") 'bz2) | ||
230 | ((executable-find "xz") 'xz) | ||
231 | (t nil))) | ||
232 | (global-undo-fu-session-mode)) | ||
233 | |||
234 | (yoke whitespace | ||
235 | (setc whitespace-line-column nil | ||
236 | whitespace-style '( face trailing | ||
237 | tabs tab-mark | ||
238 | indentation | ||
239 | space-after-tab space-before-tab)) | ||
240 | (defhook (text-mode-hook prog-mode-hook read-only-mode-hook) | ||
241 | :name +whitespace-mode-for-writable-buffers | ||
242 | :doc "Turn on `whitespace-mode' if the buffer is writable, off otherwise." | ||
243 | (whitespace-mode (if buffer-read-only -1 t))) | ||
244 | (defhook before-save-hook #'whitespace-cleanup) | ||
245 | (define-advice whitespace-cleanup (:around (fn &rest r) preserve-point) | ||
246 | (let ((col (current-column))) | ||
247 | (apply fn r) | ||
248 | (move-to-column col t) | ||
249 | (set-buffer-modified-p nil)))) | ||
250 | |||
251 | (yoke elisp-mode | ||
252 | (setc eval-expression-print-length nil ; remove ellipses from `eval-expression' | ||
253 | eval-expression-print-level nil) | ||
254 | (defkeys (emacs-lisp-mode-map lisp-interaction-mode-map) | ||
255 | "C-c C-c" #'eval-defun | ||
256 | "C-c C-k" (defun +elisp-eval-region-or-buffer () | ||
257 | (interactive) | ||
258 | (cond | ||
259 | ((region-active-p) | ||
260 | (eval-region (region-beginning) (region-end)) | ||
261 | (message "Region evaluated.")) | ||
262 | (t | ||
263 | (eval-buffer) | ||
264 | (message "Buffer %s evaluated." (buffer-name))))) | ||
265 | "C-c C-z" #'ielm) | ||
266 | (define-advice eval-region (:around (fn beg end &rest args) pulse) | ||
267 | (apply fn beg end args) | ||
268 | (pulse-momentary-highlight-region beg end))) | ||
269 | |||
270 | (yoke isearch | ||
271 | (defkeys t | ||
272 | "C-s" #'isearch-forward-regexp | ||
273 | "C-r" #'isearch-backward-regexp | ||
274 | "C-M-s" #'isearch-forward | ||
275 | "C-M-r" #'isearch-backward)) | ||
276 | 102 | ||
277 | (yoke ispell | 103 | (use-package orderless |
278 | (require* '+ispell 'ispell) | 104 | :ensure t |
279 | (add-hook 'before-save-hook | 105 | :custom |
280 | #'+ispell-move-buffer-words-to-dir-locals-hook) | 106 | (completion-styles '(orderless basic)) |
281 | (setc ispell-program-name (or (executable-find "ispell") | 107 | (completion-category-overrides |
282 | (executable-find "aspell"))) | 108 | '((file (styles basic partial-completion))))) |
283 | (put 'ispell-buffer-session-localwords | 109 | |
284 | 'safe-local-variable #'+ispell-safe-local-p)) | 110 | (use-package consult |
285 | 111 | :ensure t | |
286 | 112 | :custom | |
287 | (yoke mouse | 113 | (register-preview-delay 0.01) |
288 | ;; Brand new for Emacs 28: see https://ruzkuku.com/texts/emacs-mouse.html | 114 | (register-preview-function #'consult-register-format) |
289 | ;; Actually, look at this as well: https://www.emacswiki.org/emacs/Mouse3 | 115 | (xref-show-xrefs-function #'consult-xref) |
290 | (when (fboundp 'context-menu-mode) | 116 | (tab-always-indent 'complete) |
291 | (setc context-menu-functions '(context-menu-ffap | 117 | (completion-in-region-function #'consult-completion-in-region) |
292 | context-menu-region | 118 | (consult-narrow-key "<") |
293 | context-menu-undo | 119 | (consult--regexp-compiler #'consult--default-regexp-compiler) |
294 | ;; context-menu-dictionary | 120 | :config |
295 | )) | ||
296 | (context-menu-mode +1)) | ||
297 | (dolist (click '(;; Fix scrolling in the margin | ||
298 | wheel-down double-wheel-down triple-wheel-down | ||
299 | wheel-up double-wheel-up triple-wheel-up)) | ||
300 | (global-set-key (vector 'right-margin click) 'mwheel-scroll) | ||
301 | (global-set-key (vector 'left-margin click) 'mwheel-scroll))) | ||
302 | |||
303 | (yoke dired | ||
304 | (require 'dired-x) | ||
305 | (setc dired-recursive-copies 'always | ||
306 | dired-recursive-deletes 'always | ||
307 | dired-create-destination-dirs 'always | ||
308 | dired-do-revert-buffer t | ||
309 | dired-hide-details-hide-symlink-targets nil | ||
310 | dired-isearch-filenames 'dwim | ||
311 | delete-by-moving-to-trash t | ||
312 | dired-auto-revert-buffer t | ||
313 | dired-listing-switches "-AlF" | ||
314 | ls-lisp-dirs-first t | ||
315 | dired-ls-F-marks-symlinks t | ||
316 | dired-clean-confirm-killing-deleted-buffers nil | ||
317 | dired-no-confirm '(byte-compile | ||
318 | load chgrp chmod chown | ||
319 | copy move hardlink symlink | ||
320 | shell touch) | ||
321 | dired-dwim-target t) | ||
322 | (setq-local-hook dired-mode-hook | ||
323 | truncate-lines t) | ||
324 | (defkeys t | ||
325 | "C-x C-j" #'dired-jump | ||
326 | [remap list-directory] #'dired) | ||
327 | (defkeys ((dired-mode-map :after dired)) | ||
328 | "<backspace>" #'dired-up-directory | ||
329 | "C-j" #'dired-up-directory) | ||
330 | (defhook dired-mode-hook | ||
331 | #'dired-hide-details-mode | ||
332 | #'hl-line-mode)) | ||
333 | |||
334 | (yoke (dired-hacks "https://github.com/Fuco1/dired-hacks") | ||
335 | (defkeys dired-mode-map | ||
336 | "TAB" #'dired-subtree-sycle | ||
337 | "i" #'dired-subtree-toggle) | ||
338 | (defhook 'dired-mode-hook | ||
339 | #'dired-collapse-mode)) | ||
340 | |||
341 | (yoke auth-source | ||
342 | (setc auth-sources `(default "secrets:passwords")) | ||
343 | (setq-local-hook authinfo-mode-hook | ||
344 | truncate-lines t)) | ||
345 | |||
346 | (yoke (consult "https://github.com/minad/consult") | ||
347 | (require 'consult) | ||
348 | (setf register-preview-delay 0 | ||
349 | register-preview-function #'consult-register-format | ||
350 | xref-show-xrefs-function #'consult-xref | ||
351 | tab-always-indent 'complete | ||
352 | completion-in-region-function #'consult-completion-in-region | ||
353 | consult-narrow-key "<" | ||
354 | consult--regexp-compiler #'consult--default-regexp-compiler) | ||
355 | (advice-add #'register-preview :override #'consult-register-window) | 121 | (advice-add #'register-preview :override #'consult-register-window) |
356 | (define-key* (current-global-map) | 122 | (define-advice completing-read-multiple (:filter-args (args) indicator) |
357 | ;; Etc | 123 | (cons (format "[CRM%s] %s" |
358 | "M-S-x" #'consult-mode-command | 124 | (replace-regexp-in-string |
359 | ;; C-c bindings (mode-specific-map) | 125 | "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" "" |
360 | "C-c h" #'consult-history | 126 | crm-separator) |
361 | "C-c b" #'consult-bookmark | 127 | (car args)) |
362 | "C-c k" #'consult-kmacro | 128 | (cdr args))) |
363 | ;; C-x bindings (ctl-x-map) | 129 | :bind |
364 | "C-x M-:" #'consult-complex-command | 130 | (([remap switch-to-buffer] . consult-buffer) |
365 | "C-x b" #'consult-buffer | 131 | ([remap switch-to-buffer-other-window] . consult-buffer-other-window) |
366 | "C-x 4 b" #'consult-buffer-other-window | 132 | ([remap switch-to-buffer-other-frame] . consult-buffer-other-frame) |
367 | "C-x 5 b" #'consult-buffer-other-frame | 133 | ([remap yank-pop] . consult-yank-pop) |
368 | ;; Custom M-# bindings for fast register access | 134 | ("M-g g" . consult-goto-line) |
369 | "M-#" #'consult-register-load | 135 | ("M-g M-g" . consult-goto-line) |
370 | "M-'" #'consult-register-store | 136 | ("M-g i" . consult-imenu) |
371 | "C-M-#" #'consult-register | 137 | ("M-g M-i" . consult-imenu) |
372 | ;; Other custom bindings | 138 | ("M-s l" . consult-line) |
373 | "M-y" #'consult-yank-pop | 139 | ("M-s f" . consult-find) |
374 | ;;("<f1> a" . consult-apropos) | 140 | ("M-s M-f" . consult-find) |
375 | ;; M-g bindings (goto-map) | 141 | ("M-s g" . consult-grep) |
376 | "M-g e" #'consult-compile-error | 142 | ("M-s M-g" . consult-grep))) |
377 | "M-g f" #'consult-flymake ; or consult-flycheck | 143 | |
378 | "M-g g" #'consult-goto-line | 144 | ;;; General enhancements |
379 | "M-g M-g" #'consult-goto-line | 145 | |
380 | "M-g o" #'consult-outline ; or consult-org-heading | 146 | (use-package embark |
381 | "M-g m" #'consult-mark | 147 | :ensure t |
382 | "M-g k" #'consult-global-mark | 148 | :custom |
383 | "M-g i" #'consult-imenu | 149 | (prefix-help-command #'embark-prefix-help-command) |
384 | "M-g M-i" #'consult-imenu | 150 | :bind |
385 | "M-g I" #'consult-imenu-multi | 151 | (("C-." . embark-act) |
386 | ;; M-s bindings (search-map) | 152 | ("M-." . embark-dwim) |
387 | "M-s f" #'consult-find | 153 | ("C-h b" . embark-bindings)) |
388 | "M-s F" #'consult-locate | 154 | :custom |
389 | "M-s g" #'consult-grep | 155 | (add-to-list 'display-buffer-alist |
390 | "M-s G" #'consult-git-grep | 156 | '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" |
391 | "M-s r" #'consult-ripgrep | 157 | nil |
392 | "M-s l" #'consult-line | 158 | (window-parameters (mode-line-format . none))))) |
393 | "M-s L" #'consult-line-multi | 159 | |
394 | "M-s m" #'consult-multi-occur | 160 | (use-package embark-consult |
395 | "M-s k" #'consult-keep-lines | 161 | :ensure t |
396 | "M-s u" #'consult-focus-lines | 162 | :after (embark consult) |
397 | ;; Isearch integration | 163 | :hook (embark-collect-mode-hook . consult-preview-at-point-mode)) |
398 | "M-s e" #'consult-isearch-history) | 164 | |
399 | (eval-after isearch-mode | 165 | (use-package undo-fu-session |
400 | (define-key* isearch-mode-map | 166 | :ensure t |
401 | "M-e" #'consult-isearch-history | 167 | :custom |
402 | "M-s e" #'consult-isearch-history | 168 | (undo-fu-session-directory (etc/ "undo/" t)) |
403 | "M-s l" #'consult-line | 169 | (undo-fu-session-incompatible-files |
404 | "M-s L" #'consult-line-multi)) | 170 | '("/COMMIT_EDITMSG\\'" |
405 | (eval-after org | 171 | "/git-rebase-todo\\'")) |
406 | (define-key org-mode-map (kbd "M-g o") #'consult-org-heading)) | 172 | (undo-fu-session-compression |
407 | (eval-after consult-imenu | 173 | (cl-loop for (exe . sym) in '(("gzip" . gz) |
408 | (setf (alist-get ?y (plist-get (alist-get 'emacs-lisp-mode | 174 | ("bzip2" . bz2) |
409 | consult-imenu-config) | 175 | ("xz" . xz)) |
410 | :types)) | 176 | if (executable-find exe) |
411 | '("Yoke")))) | 177 | return sym)) |
412 | 178 | :config | |
413 | (yoke (orderless "https://github.com/oantolin/orderless") | 179 | (global-undo-fu-session-mode)) |
414 | (require 'orderless) | ||
415 | (setf completion-styles '(substring orderless basic) | ||
416 | completion-category-defaults nil | ||
417 | completion-category-overrides | ||
418 | '((file (styles basic partial-completion))) | ||
419 | orderless-component-separator #'orderless-escapable-split-on-space)) | ||
420 | |||
421 | (yoke (vertico "https://github.com/minad/vertico") | ||
422 | (require 'vertico) | ||
423 | (setf resize-mini-windows 'grow-only | ||
424 | vertico-count-format nil | ||
425 | vertico-cycle t) | ||
426 | (vertico-mode) | ||
427 | (add-to-list 'load-path (expand-file-name "vertico/extensions" yoke-dir)) | ||
428 | (require 'vertico-directory) | ||
429 | (add-hook 'rfn-eshadow-update-overlay-hook #'vertico-directory-tidy)) | ||
430 | |||
431 | (yoke (embark "https://github.com/oantolin/embark") | ||
432 | (require 'embark) | ||
433 | (setf prefix-help-command #'embark-prefix-help-command | ||
434 | embar-keymap-prompter-key ";") | ||
435 | (defkeys (t minibuffer-local-map) | ||
436 | "C-." #'embark-act | ||
437 | "M-." #'embark-dwim | ||
438 | "<f1> B" #'embark-bindings) | ||
439 | (define-key* embark-file-map | ||
440 | "l" #'vlf) | ||
441 | (eval-after (embark consult) | ||
442 | (require 'embark-consult) | ||
443 | (add-hook 'embark-collect-mode-hook #'consult-preview-at-point-mode))) | ||
444 | |||
445 | (yoke (marginalia "https://github.com/minad/marginalia/") | ||
446 | (marginalia-mode)) | ||
447 | 180 | ||
448 | (yoke (wgrep "https://github.com/mhayashi1120/Emacs-wgrep") | 181 | (use-package ws-butler |
449 | (require 'wgrep) | 182 | :ensure t |
450 | (define-key* grep-mode-map | 183 | :custom |
451 | "C-x C-q" #'wgrep-change-to-wgrep-mode)) | 184 | (ws-butler-trim-predicate (lambda (begin end) |
185 | (not (eq 'font-lock-string-face | ||
186 | (get-text-property end 'face))))) | ||
187 | :config | ||
188 | (ws-butler-global-mode)) | ||
189 | |||
190 | (use-package minions | ||
191 | :ensure t | ||
192 | :config | ||
193 | (minions-mode)) | ||
452 | 194 | ||
453 | (yoke (slime "https://github.com/slime/slime") | 195 | (use-package mode-line-bell |
454 | :when (executable-find "sbcl") | 196 | :vc ( :url "https://github.com/duckwork/mode-line-bell" |
455 | (setc inferior-lisp-program (executable-find "sbcl")) | 197 | :rev "remap-face") |
456 | (defhook lisp-mode-hook | 198 | :custom |
457 | :name slime-mode-setup | 199 | (mode-line-bell-flash-time 0.25) |
458 | (load (expand-file-name "~/quicklisp/slime-helper.el") :noerror) | 200 | :config |
459 | (slime-mode)) | 201 | (mode-line-bell-mode)) |
460 | (eval-after slime | 202 | |
461 | (setc slime-completion-at-point-functions | 203 | (use-package electric-cursor |
462 | (delq 'slime-c-p-c-completion-at-point | 204 | :load-path "~/src/emacs/electric-cursor/" |
463 | slime-completion-at-point-functions)))) | 205 | :custom |
464 | 206 | (electric-cursor-alist '((overwrite-mode . box) | |
465 | (yoke (puni "https://github.com/amaikinono/puni") | 207 | (t . bar))) |
466 | (electric-pair-mode) | 208 | (electric-cursor-mode)) |
467 | (defkeys puni-mode-map | ||
468 | "C-)" #'puni-slurp-forward | ||
469 | "C-(" #'puni-slurp-backward | ||
470 | "C-}" #'puni-barf-forward | ||
471 | "C-{" #'puni-barf-backward | ||
472 | "M-(" (defun +puni-open-then-slurp-forward (&optional n) | ||
473 | (interactive "p") | ||
474 | (insert "()") | ||
475 | (backward-char) | ||
476 | (ignore-errors (puni-slurp-forward n)))) | ||
477 | (defhook (prog-mode-hook | ||
478 | lisp-interaction-mode-hook emacs-lisp-mode-hook | ||
479 | lisp-mode-hook scheme-mode-hook | ||
480 | ielm-mode-hook eval-expression-minibuffer-setup-hook) | ||
481 | #'puni-mode)) | ||
482 | |||
483 | (yoke (hungry-delete "https://github.com/nflath/hungry-delete") | ||
484 | (setc hungry-delete-chars-to-skip " \t" | ||
485 | hungry-delete-join-reluctantly nil) | ||
486 | (eval-after hungry-delete | ||
487 | (add-to-list* 'hungry-delete-except-modes | ||
488 | #'eshell-mode | ||
489 | #'nim-mode | ||
490 | #'python-mode)) | ||
491 | (defun +hungry-delete-or (hd-fn fn arg) | ||
492 | (funcall (if (looking-back (format "[%s]" hungry-delete-chars-to-skip) arg) | ||
493 | hd-fn | ||
494 | fn) | ||
495 | arg)) | ||
496 | (defkeys puni-mode-map | ||
497 | [remap puni-backward-delete-char] | ||
498 | (defun +puni|hungry-delete-backward (arg) | ||
499 | (interactive "p") | ||
500 | (+hungry-delete-or #'hungry-delete-backward | ||
501 | #'puni-backward-delete-char | ||
502 | arg)) | ||
503 | [remap puni-forward-delete-char] | ||
504 | (defun +puni|hungry-delete-forward (arg) | ||
505 | (interactive "p") | ||
506 | (+hungry-delete-or #'hungry-delete-forward | ||
507 | #'puni-forward-delete-char | ||
508 | arg))) | ||
509 | (global-hungry-delete-mode)) | ||
510 | 209 | ||
511 | (yoke (cape "https://github.com/minad/cape") | 210 | (use-package visual-fill-column |
512 | ;; Insinuate in a lot of modes | 211 | :ensure t |
513 | (defvar +capes '(cape-file cape-dabbrev)) | 212 | :custom |
514 | (defun +cape-insinuate (hook capf &optional capes) | 213 | (visual-fill-column-center-text t) |
515 | "Insinuate CAPES into a HOOK along with CAPF function. | 214 | :hook ((visual-fill-column-mode-hook . visual-line-mode) |
516 | CAPES defaults to `+capes'. CAPF will be made un-exclusive." | 215 | (eww-mode-hook . visual-fill-column-mode)) |
517 | (setq-local-hook hook | 216 | :config |
518 | completion-at-point-functions | 217 | (advice-add 'text-scale-adjust :after #'visual-fill-column-adjust)) |
519 | (apply #'list (cape-capf-properties capf :exclusive 'no) | ||
520 | (or capes +capes)))) | ||
521 | (+cape-insinuate 'emacs-lisp-mode-hook #'elisp-completion-at-point)) | ||
522 | |||
523 | (yoke (minions "https://github.com/tarsius/minions") | ||
524 | (minions-mode)) | ||
525 | 218 | ||
526 | (yoke (magit "https://github.com/magit/magit" | 219 | (use-package cape |
527 | :load "lisp") | 220 | :ensure t |
528 | :depends ((transient "https://github.com/magit/transient" | 221 | :demand t |
529 | :load "lisp") | 222 | :init |
530 | (dash "https://github.com/magnars/dash.el") | 223 | (dolist (fn '(cape-file cape-dabbrev)) |
531 | (with-editor "https://github.com/magit/with-editor" | 224 | (add-hook 'completion-at-point-functions fn 90)) |
532 | :load "lisp")) | 225 | :config |
533 | (autoload #'transient--with-suspended-override "transient") | 226 | (require '+cape) |
534 | (autoload #'magit "magit" nil :interactive) | 227 | (advice-add 'emacs-completion-at-point :around #'cape-wrap-nonexclusive) |
535 | (defkeys t | 228 | (+cape-insinuate text-mode-hook ( cape-dict cape-ispell |
536 | "C-x g" #'magit)) | 229 | cape-file cape-dabbrev))) |
230 | |||
231 | (use-package wgrep | ||
232 | :ensure t | ||
233 | :custom | ||
234 | (wgrep-enable-key (kbd "C-x C-q")) | ||
235 | :bind (:map grep-mode-map | ||
236 | ("C-x C-q" . wgrep-change-to-wgrep-mode))) | ||
237 | |||
238 | (progn | ||
239 | (use-package-statistics-gather :use-package 'mlscroll nil) | ||
240 | (use-package-ensure-elpa 'mlscroll | ||
241 | '(t) | ||
242 | 'nil) | ||
243 | (use-package-statistics-gather :preface 'mlscroll nil) | ||
244 | (eval-and-compile | ||
245 | (defhook modus-themes-after-load-theme-hook :name modus-themes-load@mlscroll | ||
246 | (mlscroll-mode -1) | ||
247 | (when | ||
248 | (or | ||
249 | (memq 'modus-vivendi custom-enabled-themes) | ||
250 | (memq 'modus-operandi custom-enabled-themes)) | ||
251 | (modus-themes-with-colors | ||
252 | (setf mlscroll-in-color fg-dim mlscroll-out-color bg-inactive))) | ||
253 | (eval-after 1 | ||
254 | (mlscroll-mode 1)))) | ||
255 | (defvar use-package--warning99 | ||
256 | (function | ||
257 | (lambda | ||
258 | (keyword err) | ||
259 | (let | ||
260 | ((msg | ||
261 | (format "%s/%s: %s" 'mlscroll keyword | ||
262 | (error-message-string err)))) | ||
263 | (display-warning 'use-package msg :error))))) | ||
264 | (condition-case-unless-debug err | ||
265 | (progn | ||
266 | (use-package-statistics-gather :init 'mlscroll nil) | ||
267 | (condition-case-unless-debug err | ||
268 | (when | ||
269 | (daemonp) | ||
270 | (add-hook 'server-after-make-frame | ||
271 | (function modus-themes-load@ml-scroll))) | ||
272 | (error | ||
273 | (funcall use-package--warning99 :init err))) | ||
274 | (run-with-idle-timer 1 nil | ||
275 | (function require) | ||
276 | 'mlscroll nil t) | ||
277 | (eval-after-load 'mlscroll | ||
278 | '(progn | ||
279 | (use-package-statistics-gather :config 'mlscroll nil) | ||
280 | (let | ||
281 | ((now | ||
282 | (current-time))) | ||
283 | (message "%s..." "Configuring package mlscroll") | ||
284 | (prog1 | ||
285 | (condition-case-unless-debug err | ||
286 | (progn | ||
287 | (modus-themes-load@mlscroll) | ||
288 | t) | ||
289 | (error | ||
290 | (funcall use-package--warning99 :config err))) | ||
291 | (let | ||
292 | ((elapsed | ||
293 | (float-time | ||
294 | (time-subtract | ||
295 | (current-time) | ||
296 | now)))) | ||
297 | (if | ||
298 | (> elapsed 0.1) | ||
299 | (message "%s...done (%.3fs)" "Configuring package mlscroll" elapsed) | ||
300 | (message "%s...done" "Configuring package mlscroll"))))) | ||
301 | (use-package-statistics-gather :config 'mlscroll t))) | ||
302 | (use-package-statistics-gather :init 'mlscroll t)) | ||
303 | (error | ||
304 | (funcall use-package--warning99 :catch err))) | ||
305 | (use-package-statistics-gather :preface 'mlscroll t) | ||
306 | (use-package-statistics-gather :use-package 'mlscroll t)) | ||
307 | |||
308 | (use-package avy | ||
309 | :ensure t | ||
310 | :demand t | ||
311 | :custom | ||
312 | (avy-background t) | ||
313 | (avy-keys (string-to-list "asdfghjklqwertyuiopzxcvbnm")) | ||
314 | :bind (("M-j" . avy-goto-char-timer) | ||
315 | :map isearch-mode-map | ||
316 | ("M-j" . avy-isearch))) | ||
317 | |||
318 | (use-package zzz-to-char | ||
319 | :ensure t | ||
320 | :bind (("M-z" . zzz-to-char))) | ||
321 | |||
322 | ;;; Searching | ||
323 | |||
324 | (use-package isearch-mb | ||
325 | :ensure t | ||
326 | :custom | ||
327 | (isearch-lazy-count t) | ||
328 | (isearch-regexp-lax-whitespace t) | ||
329 | ;; Space matches whitespace, newlines, punctuation | ||
330 | (search-whitespace-regexp "\\W+") | ||
331 | (search-default-mode t "Search using regexp by default.") | ||
332 | ;; Space matches any sequence of characters in a line | ||
333 | ;; search-whitespace-regexp ".*?" | ||
334 | (isearch-wrap-pause 'no) | ||
335 | :config | ||
336 | (require '+isearch) | ||
337 | (advice-add 'isearch-cancel :before | ||
338 | #'+isearch-cancel@add-search-to-history) | ||
339 | (advice-add 'perform-replace :around | ||
340 | #'+perform-replace-dont-exit-on-anykey) | ||
341 | (eval-after (isearch-mb consult) | ||
342 | (add-to-list 'isearch-mb--after-exit #'consult-line) | ||
343 | (add-to-list 'isearch-mb--with-buffer #'consult-isearch-history) | ||
344 | (defkeys isearch-mb-minibuffer-map | ||
345 | "M-s l" #'consult-line | ||
346 | "M-r" #'consult-isearch-history)) | ||
347 | (eval-after (isearch-mb anzu) | ||
348 | (add-to-list 'isearch-mb--after-exit #'anzu-isearch-query-replace) | ||
349 | (defkeys isearch-mb-minibuffer-map | ||
350 | "M-%" #'anzu-isearch-query-replace-regexp | ||
351 | "C-M-%" #'anzu-isearch-query-replace)) | ||
352 | (isearch-mb-mode)) | ||
537 | 353 | ||
538 | (yoke (git-modes "https://github.com/magit/git-modes") | 354 | (use-package anzu |
539 | (require 'git-modes)) | 355 | :ensure t |
356 | :bind | ||
357 | (("M-%" . anzu-query-replace-regexp) | ||
358 | ("C-M-%" . anzu-query-replace))) | ||
540 | 359 | ||
541 | (yoke (visual-fill-column "https://codeberg.org/joostkremers/visual-fill-column") | 360 | (use-package frowny |
542 | (setc visual-fill-column-center-text t) | 361 | :load-path "~/src/emacs/frowny/" |
543 | (add-hook 'visual-fill-column-mode-hook #'visual-line-mode) | 362 | :config |
544 | (advice-add 'text-scale-adjust :after #'visual-fill-column-adjust)) | 363 | (global-frowny-mode)) |
545 | 364 | ||
546 | (yoke (org "https://git.savannah.gnu.org/git/emacs/org-mode.git" | 365 | (use-package transpose-frame |
547 | :load "lisp") | 366 | :ensure t |
548 | :depends ((org-contrib "https://git.sr.ht/~bzg/org-contrib" | 367 | :bind |
549 | :load "lisp")) | 368 | (("C-x 5 t" . transpose-frame))) |
550 | ;; DON'T load system org | 369 | |
551 | (setc load-path | 370 | ;;; Text editing |
552 | (cl-remove-if (lambda (path) (string-match-p "lisp/org\\'" path)) | 371 | |
553 | load-path)) | 372 | (use-package org |
554 | (setc org-adapt-indentation nil | 373 | :defer t |
555 | org-auto-align-tags t | 374 | :after derived |
556 | org-archive-mark-done t | 375 | :init |
557 | org-fold-catch-invisible-edits 'show-and-error | 376 | (require 'init-org) |
558 | org-clock-clocked-in-display 'mode-line | 377 | (require '_work)) |
559 | org-clock-frame-title-format (cons | 378 | |
560 | '(t org-mode-line-string) | 379 | (use-package ispell |
561 | (cons " --- " frame-title-format)) | 380 | :custom |
562 | org-clock-string-limit 7 ; just the clock bit | 381 | (ispell-program-name (choose-executable "ispell" "aspell")) |
563 | ;; org-clock-string-limit 25 ; gives enough information | 382 | :config |
564 | org-clock-persist nil | 383 | (require '+ispell) |
565 | org-confirm-babel-evaluate nil | 384 | (add-hook 'before-save-hook |
566 | org-cycle-separator-lines 0 | 385 | #'+ispell-move-buffer-words-to-dir-locals-hook) |
567 | org-directory (sync/ "org/" t) | 386 | (put 'ispell-buffer-session-localwords 'safe-local-variable |
568 | org-ellipsis (or truncate-string-ellipsis "…") | 387 | #'+ispell-safe-local-p)) |
569 | org-fontify-done-headline t | 388 | |
570 | org-fontify-quote-and-verse-blocks t | 389 | (use-package flyspell |
571 | org-fontify-whole-heading-line t | 390 | :hook org-mode-hook) |
572 | org-hide-emphasis-markers t | 391 | |
573 | org-html-coding-system 'utf-8-unix | 392 | (use-package flyspell-correct |
574 | org-image-actual-width (list (* (window-font-width) | 393 | :ensure t |
575 | (- fill-column 8))) | 394 | :after flyspell |
576 | org-imenu-depth 3 | 395 | :custom |
577 | org-indent-indentation-per-level 0 | 396 | (flyspell-correct--cr-key ";") |
578 | org-indent-mode-turns-on-hiding-stars nil | 397 | :bind |
579 | org-insert-heading-respect-content t | 398 | (:map flyspell-mode-map |
580 | org-list-demote-modify-bullet '(("-" . "+") | 399 | (("C-;" . flyspell-correct-wrapper) |
581 | ("+" . "-")) | 400 | ("<f7>" . +flyspell-correct-buffer))) |
582 | org-log-done 'time | 401 | :config |
583 | org-log-into-drawer t | 402 | (require '+flyspell-correct) |
584 | org-num-skip-commented t | 403 | (defkeys flyspell-mode-map |
585 | org-num-skip-unnumbered t | 404 | "C-," nil |
586 | org-num-skip-footnotes t | 405 | "C-." nil)) |
587 | org-outline-path-complete-in-steps nil | 406 | |
588 | org-pretty-entities t | 407 | (use-package text-mode |
589 | org-pretty-entities-include-sub-superscripts nil | 408 | :config |
590 | org-refile-targets '((nil . (:maxlevel . 2)) | 409 | (defhook text-mode-hook |
591 | (org-agenda-files . (:maxlevel . 1))) | 410 | #'abbrev-mode)) |
592 | org-refile-use-outline-path 'file | 411 | |
593 | org-special-ctrl-a/e t | 412 | (use-package filldent |
594 | org-special-ctrl-k t | 413 | :load-path "~/src/emacs/filldent/" |
595 | org-src-fontify-natively t | 414 | :bind |
596 | org-src-tab-acts-natively t | 415 | ("M-q" . filldent-dwim)) |
597 | org-src-window-setup 'current-window | 416 | |
598 | org-startup-truncated nil | 417 | (use-package scule |
599 | org-startup-with-inline-images t | 418 | :load-path "~/src/emacs/scule/" |
600 | org-tags-column -77 ;; (- (- fill-column 1 (length org-ellipsis))) | 419 | :config |
601 | org-todo-keywords | ||
602 | '((sequence "TODO(t)" "WAIT(w@/!)" "ONGOING(o@)" | ||
603 | "|" "DONE(d!)" "ASSIGNED(a@/!)") | ||
604 | (sequence "|" "CANCELED(k@)") | ||
605 | (sequence "MEETING(m)")) | ||
606 | org-use-speed-commands t | ||
607 | org-emphasis-alist '(("*" org-bold) | ||
608 | ("/" org-italic) | ||
609 | ("_" org-underline) | ||
610 | ("=" org-verbatim) | ||
611 | ("~" org-code) | ||
612 | ("+" org-strikethrough))) | ||
613 | (defhook org-mode-hook | ||
614 | #'variable-pitch-mode | ||
615 | #'visual-fill-column-mode | ||
616 | #'turn-off-auto-fill | ||
617 | #'org-indent-mode | ||
618 | #'prettify-symbols-mode | ||
619 | #'abbrev-mode | ||
620 | (defhook ((before-save-hook nil :local)) | ||
621 | :name before-save@org-mode | ||
622 | (+org-hide-drawers-except-point) | ||
623 | (org-align-tags 'all))) | ||
624 | (eval-after org | ||
625 | (require '+org) | ||
626 | (org-clock-persistence-insinuate) | ||
627 | (+org-agenda-inhibit-hooks-mode) | ||
628 | (defkeys org-mode-map | ||
629 | "C-M-k" #'kill-paragraph | ||
630 | "C-M-t" #'transpose-paragraphs | ||
631 | "RET" #'+org-return-dwim | ||
632 | "S-<return>" #'+org-table-copy-down|+org-return | ||
633 | "C-c C-o" #'+org-open-at-point-dwim)) | ||
634 | (eval-after ol ; org-link | ||
635 | (defmacro define-org-link-type (type args &rest body) | ||
636 | "Define an org link TYPE with ARGS that does something. | ||
637 | If BODY is blank, message the user about the link." | ||
638 | (declare (indent 2) (doc-string 3) (debug (sexp sexp def-body))) | ||
639 | (let ((fn (intern (format "org-%s-open" type))) | ||
640 | (body (or body `((message ,(format "%s: %%S" type) ,(car args))))) | ||
641 | (type-string (format "%s" type))) | ||
642 | `(prog1 | ||
643 | (defun ,fn ,args | ||
644 | ,@body) | ||
645 | (org-link-set-parameters ,type-string :follow #',fn)))) | ||
646 | (define-org-link-type sms (number _)) | ||
647 | (define-org-link-type tel (number _)))) | ||
648 | |||
649 | (yoke org-word-count ; in lisp/ | ||
650 | (eval-after org | ||
651 | (require 'org-word-count) | ||
652 | (add-hook 'org-mode-hook #'org-word-count-mode))) | ||
653 | |||
654 | (yoke org-agenda | ||
655 | (setq org-agenda-skip-deadline-if-done t | ||
656 | org-agenda-skip-scheduled-if-done t | ||
657 | org-agenda-span 10 | ||
658 | org-agenda-block-separator ?─ | ||
659 | org-agenda-time-grid | ||
660 | '((daily today require-timed) | ||
661 | (800 1000 1200 1400 1600 1800 2000) | ||
662 | " ┄┄┄┄┄ " "┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄") | ||
663 | org-agenda-current-time-string | ||
664 | "← now ─────────────────────────────────────────────────" | ||
665 | org-agenda-include-diary nil ; I use the org-diary features | ||
666 | org-agenda-todo-ignore-deadlines 'near | ||
667 | org-agenda-todo-ignore-scheduled 'future | ||
668 | org-agenda-include-deadlines t | ||
669 | org-deadline-warning-days 0 | ||
670 | org-agenda-show-future-repeats 'next | ||
671 | org-agenda-window-setup 'current-window | ||
672 | org-agenda-file-skip-regexp "sync-conflict") | ||
673 | (defcustom org-agenda-file-skip-regexp nil | ||
674 | "Files matching this regexp are removed from `org-agenda-files'." | ||
675 | :group 'org-agenda) | ||
676 | (define-advice org-agenda-files (:filter-return (files) skip-regexp) | ||
677 | (when org-agenda-file-skip-regexp | ||
678 | (setq files (seq-remove (lambda (file) | ||
679 | (string-match-p org-agenda-file-skip-regexp | ||
680 | file)) | ||
681 | files))) | ||
682 | files) | ||
683 | (setq-local-hook org-agenda-mode-hook | ||
684 | truncate-lines t) | ||
685 | (add-hook 'org-agenda-mode-hook #'hl-line-mode) | ||
686 | (add-hook 'org-agenda-after-show-hook #'org-narrow-to-subtree) | ||
687 | (defkeys t | 420 | (defkeys t |
688 | "C-c c" #'org-capture | 421 | "M-c" (defmap scule-map |
689 | "C-c a" #'org-agenda) | 422 | "Keymap for twiddling scules." |
690 | (eval-after org-capture | 423 | "M-u" #'scule-upcase |
691 | (require '+org-capture))) | 424 | "M-l" #'scule-downcase |
692 | 425 | "M-c" #'scule-capitalize))) | |
693 | (yoke ox ; org-export | 426 | |
694 | (eval-after org (require 'ox)) | 427 | (use-package titlecase |
695 | (eval-after ox | 428 | :load-path "~/src/emacs/titlecase.el/" |
696 | (require* '+ox '(ox-md nil t)) | 429 | :after org |
697 | (+org-export-pre-hooks-insinuate)) | 430 | :bind (:map scule-map |
698 | (setq org-export-coding-system 'utf-8-unix | 431 | ("M-t" . titlecase-dwim)) |
699 | org-export-headline-levels 8 | 432 | :config |
700 | org-export-with-drawers nil | 433 | (require '+titlecase)) |
701 | org-export-with-section-numbers nil | 434 | |
702 | org-export-with-smart-quotes t | 435 | ;;; Programming |
703 | org-export-with-sub-superscripts t | 436 | |
704 | org-export-with-toc nil)) | 437 | (use-package prog-mode |
705 | 438 | :config | |
706 | (yoke (electric-cursor "https://codeberg.org/acdw/electric-cursor.el") | 439 | (defhook prog-mode-hook |
707 | (setq electric-cursor-alist '((overwrite-mode . hbar) | 440 | #'auto-fill-mode |
708 | (t . bar))) | 441 | ;; Use `indent-tabs-mode' unless one of the following modes |
709 | (electric-cursor-mode)) | 442 | (indent-tabs-mode (if (derived-mode-p 'emacs-lisp-mode |
443 | 'python-mode | ||
444 | 'haskell-mode) | ||
445 | -1 1)))) | ||
446 | |||
447 | (use-package paredit | ||
448 | :ensure t | ||
449 | :preface | ||
450 | (defun +paredit-newline () | ||
451 | (interactive) | ||
452 | (call-interactively | ||
453 | (if (derived-mode-p 'lisp-interaction-mode) | ||
454 | #'eval-print-last-sexp | ||
455 | #'paredit-newline))) | ||
456 | :hook ((emacs-lisp-mode-hook | ||
457 | eval-expression-minibuffer-setup-hook | ||
458 | ielm-mode-hook lisp-interaction-mode-hook | ||
459 | lisp-mode-hook scheme-mode-hook | ||
460 | fennel-repl-mode-hook fennel-mode-hook | ||
461 | geiser-mode-hook geiser-repl-mode-hook | ||
462 | ielm-mode-hook) | ||
463 | . enable-paredit-mode) | ||
464 | :bind | ||
465 | (("C-j" . +paredit-newline)) | ||
466 | :config | ||
467 | (defkeys paredit-mode-map | ||
468 | "RET" nil | ||
469 | "M-s" nil) | ||
470 | (require '+paredit) | ||
471 | (add-to-list 'paredit-space-for-delimiter-predicates | ||
472 | #'+paredit-space-for-delimiter) | ||
473 | (eval-after (paredit eldoc) | ||
474 | (eldoc-add-command #'paredit-backward-delete #'paredit-close-round))) | ||
475 | |||
476 | (use-package hungry-delete | ||
477 | :ensure t | ||
478 | :custom | ||
479 | (hungry-delete-chars-to-skip " \t") | ||
480 | (hungry-delete-join-reluctantly nil) | ||
481 | :config | ||
482 | (dolist (m '(eshell-mode | ||
483 | nim-mode | ||
484 | python-mode)) | ||
485 | (add-to-list 'hungry-delete-except-modes m)) | ||
486 | (require '+hungry-delete) | ||
487 | (eval-after paredit | ||
488 | (defkeys paredit-mode-map | ||
489 | [remap paredit-backward-delete] | ||
490 | (+hungry-delete-define-alternative | ||
491 | paredit-backward-delete hungry-delete-backward) | ||
492 | [remap paredit-forward-delete] | ||
493 | (+hungry-delete-define-alternative | ||
494 | paredit-forward-delete hungry-delete-forward))) | ||
495 | (global-hungry-delete-mode)) | ||
710 | 496 | ||
711 | (yoke _work | 497 | (use-package ediff |
712 | :depends ((bbdb "https://git.savannah.nongnu.org/git/bbdb.git" | 498 | :custom |
713 | :load "lisp") | 499 | (ediff-keep-variants nil) |
714 | (bbdb-vcard "https://github.com/tohojo/bbdb-vcard/")) | 500 | (ediff-split-window-function #'split-window-horizontally) |
715 | (setf bbdb-complete-mail-allow-cycling t | 501 | (ediff-window-setup-function #'ediff-setup-windows-plain)) |
716 | bbdb-file (private/ "bbdb")) | ||
717 | (defhook +custom-after-load-hook | ||
718 | :name _work@after-custom | ||
719 | (require* 'private '_work) | ||
720 | (require* 'bbdb 'bbdb-message) | ||
721 | (bbdb-initialize 'gnus 'message))) | ||
722 | |||
723 | (yoke (org-taskwise "https://codeberg.org/acdw/org-taskwise.el")) | ||
724 | |||
725 | (yoke scule | ||
726 | (require 'scule) | ||
727 | (defvar scule-map (let ((map (make-sparse-keymap))) | ||
728 | (define-key map (kbd "M-u") #'scule-upcase) | ||
729 | (define-key map (kbd "M-l") #'scule-downcase) | ||
730 | (define-key map (kbd "M-c") #'scule-capitalize) | ||
731 | map) | ||
732 | "Keymap for scule twiddling.") | ||
733 | (define-key* (current-global-map) | ||
734 | "M-c" scule-map | ||
735 | "M-u" #'universal-argument) | ||
736 | (define-key universal-argument-map (kbd "M-u") #'universal-argument-more)) | ||
737 | |||
738 | (yoke (titlecase "https://codeberg.org/acdw/titlecase.el") | ||
739 | (eval-after titlecase | ||
740 | (add-to-list* 'titlecase-skip-words-regexps | ||
741 | (rx word-boundary | ||
742 | (+ (any upper digit)) | ||
743 | word-boundary))) | ||
744 | (eval-after scule | ||
745 | (define-key* scule-map | ||
746 | "M-t" #'titlecase-dwim))) | ||
747 | |||
748 | (yoke (flyspell-correct "https://github.com/duckwork/flyspell-correct") | ||
749 | (eval-after flyspell | ||
750 | (require* 'flyspell-correct | ||
751 | `(+flyspell-correct ,(locate-user-emacs-file "lisp/+flyspell-correct"))) | ||
752 | (define-key* flyspell-mode-map | ||
753 | "C-;" #'flyspell-correct-wrapper | ||
754 | "<f7>" #'+flyspell-correct-buffer | ||
755 | "C-," nil | ||
756 | "C-." nil)) | ||
757 | (add-hook 'org-mode-hook #'flyspell-mode) | ||
758 | (setq flyspell-correct--cr-key ";")) | ||
759 | |||
760 | (yoke (helpful "https://github.com/Wilfred/helpful") | ||
761 | :depends ((dash "https://github.com/magnars/dash.el") | ||
762 | (f "https://github.com/rejeep/f.el") | ||
763 | (s "https://github.com/magnars/s.el") | ||
764 | (elisp-refs "https://github.com/Wilfred/elisp-refs")) | ||
765 | (defkeys t | ||
766 | "<f1> f" #'helpful-callable | ||
767 | "<f1> v" #'helpful-variable | ||
768 | "<f1> k" #'helpful-key | ||
769 | "<f1> ." #'helpful-at-point | ||
770 | "<f1> o" #'helpful-symbol) | ||
771 | (unless (featurep 'info-look) | ||
772 | (run-with-idle-timer 5 nil (lambda () | ||
773 | (require 'info-look) | ||
774 | (let ((inhibit-message t)) | ||
775 | (info-lookup-setup-mode 'symbol | ||
776 | 'emacs-lisp-mode))))) | ||
777 | (defhook window-configuration-change-hook | ||
778 | :name side-window-setup | ||
779 | (setf fit-window-to-buffer-horizontally t | ||
780 | (alist-get (rx (or "*helpful" "*Help" "*info")) | ||
781 | display-buffer-alist nil nil #'string=) | ||
782 | `(display-buffer-in-side-window | ||
783 | ,@(if (< (frame-text-width) (frame-text-height)) | ||
784 | '((side . bottom) (window-height . 24)) | ||
785 | '((side . right) (window-width . fit-window-to-buffer))))))) | ||
786 | |||
787 | (yoke (hippie-completing-read | ||
788 | "https://codeberg.org/acdw/hippie-completing-read.el") | ||
789 | (define-key* (current-global-map) | ||
790 | "M-/" #'hippie-completing-read)) | ||
791 | |||
792 | (yoke dictionary ; Comes with Emacs 29! | ||
793 | (defkeys (t (org-mode-map :after org)) | ||
794 | "C-c d" #'dictionary-search) | ||
795 | (defkeys ((embark-identifier-map :after embark)) | ||
796 | "@" #'dictionary-search) | ||
797 | (setc dictionary-server (if (or (executable-find "dictd") | ||
798 | (file-exists-p "/usr/sbin/dictd")) ; oh debian | ||
799 | "localhost" | ||
800 | "dict.org")) | ||
801 | (setf (alist-get "^\\*Dictionary\\*" display-buffer-alist nil nil #'string=) | ||
802 | '((display-buffer-in-side-window) | ||
803 | (side . bottom) | ||
804 | (window-height . 20)))) | ||
805 | |||
806 | (yoke (anzu "https://github.com/emacsorphanage/anzu") | ||
807 | (require 'anzu) | ||
808 | (global-anzu-mode) | ||
809 | (defkeys t | ||
810 | [remap query-replace] #'anzu-query-replace-regexp | ||
811 | [remap query-replace-regexp] #'anzu-query-replace) | ||
812 | (defkeys (isearch-mode-map (isearch-mb-minibuffer-map :after isearch-mb)) | ||
813 | [remap isearch-query-replace] #'anzu-isearch-query-replace-regexp | ||
814 | [remap isearch-query-replace-regexp] #'anzu-isearch-query-replace) | ||
815 | (defun anzu-qr@window (fn &rest r) | ||
816 | "ADVICE to query-replace from the beginning of the window." | ||
817 | (let ((scroll-margin 0)) | ||
818 | (cond ((region-active-p) | ||
819 | (apply fn r)) | ||
820 | (:else (save-excursion | ||
821 | (goto-char (window-start)) | ||
822 | (apply fn r)))))) | ||
823 | (advice-add 'anzu-query-replace-regexp :around #'anzu-qr@window) | ||
824 | (advice-add 'anzu-query-replace :around #'anzu-qr@window)) | ||
825 | |||
826 | (yoke tempo | ||
827 | (require '+tempo)) | ||
828 | |||
829 | (yoke (0x0 "https://gitlab.com/willvaughn/emacs-0x0") | ||
830 | (setf 0x0-default-server 'ttm) | ||
831 | (define-advice 0x0-shorten-uri (:around (fn server uri) use-0x0) | ||
832 | (interactive (list (cdr (assq 'envs 0x0-servers)) | ||
833 | (read-string "URI: "))) | ||
834 | (funcall fn server uri)) | ||
835 | (eval-after embark | ||
836 | (define-key* embark-region-map | ||
837 | "U" #'0x0-dwim))) | ||
838 | |||
839 | (yoke (filldent "https://codeberg.org/acdw/filldent.el") | ||
840 | (define-advice canonically-space-region (:around (orig &rest r) double-space) | ||
841 | (let ((sentence-end-double-space t)) | ||
842 | (apply orig r))) | ||
843 | (defkeys t | ||
844 | "M-q" #'filldent-unfill-toggle)) | ||
845 | |||
846 | (yoke (avy "https://github.com/abo-abo/avy") | ||
847 | (require 'avy) | ||
848 | (setf avy-background t | ||
849 | (alist-get ?. avy-dispatch-alist) | ||
850 | (defun avy-action-embark (pt) | ||
851 | (unwind-protect | ||
852 | (save-excursion | ||
853 | (goto-char pt) | ||
854 | (embark-act)) | ||
855 | (select-window | ||
856 | (cdr (ring-ref avy-ring 0)))) | ||
857 | t)) | ||
858 | (define-key* (current-global-map) | ||
859 | "M-j" #'avy-goto-char-timer) | ||
860 | (define-key* isearch-mode-map | ||
861 | "M-j" #'avy-isearch)) | ||
862 | |||
863 | (yoke (frowny "https://codeberg.org/acdw/frowny.el") | ||
864 | (setf frowny-eyes (rx (any ":=") (opt "'") (? "-"))) | ||
865 | (global-frowny-mode)) | ||
866 | 502 | ||
867 | (yoke (isearch-mb "https://github.com/astoff/isearch-mb") | 503 | ;;; Lisps |
868 | (eval-after (consult anzu) | ||
869 | (require 'isearch-mb) | ||
870 | (dolist (spec '((isearch-mb--with-buffer | ||
871 | ("M-e" . consult-isearch) | ||
872 | ("C-o" . loccur-isearch)) | ||
873 | (isearch-mb--after-exit | ||
874 | ("M-%" . anzu-isearch-query-replace) | ||
875 | ("M-s l" . consult-line)))) | ||
876 | (let ((isearch-mb-list (car spec)) | ||
877 | (isearch-mb-binds (cdr spec))) | ||
878 | (dolist (cell isearch-mb-binds) | ||
879 | (let ((key (car cell)) | ||
880 | (command (cdr cell))) | ||
881 | (when (fboundp command) | ||
882 | (add-to-list isearch-mb-list command) | ||
883 | (define-key isearch-mb-minibuffer-map (kbd key) command))))))) | ||
884 | (isearch-mb-mode)) | ||
885 | 504 | ||
886 | (yoke (keepassxc-shim "https://codeberg.org/acdw/keepassxc-shim.el") | 505 | (use-package elisp-mode |
506 | :custom | ||
507 | (eval-expression-print-length nil) | ||
508 | (eval-expression-print-level nil) | ||
509 | :preface | ||
510 | (defun elisp-eval-region|eval-buffer () | ||
511 | (interactive) | ||
512 | (if (region-active-p) | ||
513 | (let ((start (region-beginning)) | ||
514 | (end (region-end))) | ||
515 | (eval-region start end) | ||
516 | (message "Region from %s to %s evaluated." | ||
517 | start end)) | ||
518 | (eval-buffer) | ||
519 | (message "Buffer `%s' evaluated." (buffer-name)))) | ||
520 | (defkeys (emacs-lisp-mode-map lisp-interaction-mode-map) | ||
521 | "C-c C-c" #'eval-defun | ||
522 | "C-c C-k" #'elisp-eval-region|eval-buffer | ||
523 | "C-c C-z" #'ielm) | ||
524 | :config | ||
525 | (define-advice eval-region (:around (orig start end &rest args) pulse) | ||
526 | (apply orig start end args) | ||
527 | (pulse-momentary-highlight-region start end)) | ||
528 | (defhook emacs-lisp-mode-hook | ||
529 | (setq-local page-delimiter "^\\(\\|;;;+\\).*"))) | ||
530 | |||
531 | (use-package macrostep | ||
532 | :ensure t | ||
533 | :after elisp-mode | ||
534 | :bind ( :map emacs-lisp-mode-map | ||
535 | ("C-c e" . macrostep-expand) | ||
536 | :map lisp-interaction-mode-map | ||
537 | ("C-c e" . macrostep-expand))) | ||
538 | |||
539 | (use-package eros | ||
540 | :ensure t | ||
541 | :after elisp-mode | ||
542 | :custom (eros-eval-result-prefix "; ") | ||
543 | :hook emacs-lisp-mode-hook) | ||
544 | |||
545 | (use-package package-lint | ||
546 | :ensure t) | ||
547 | |||
548 | (use-package sly | ||
549 | :ensure t | ||
550 | :when (executable-find "sbcl") | ||
551 | :custom | ||
552 | (inferior-lisp-program (executable-find "sbcl")) | ||
553 | (sly-net-coding-system 'utf-8-unix) | ||
554 | :bind (:map sly-mode-map | ||
555 | (("C-c C-z" . sly-mrepl))) | ||
556 | :config | ||
557 | (sly-symbol-completion-mode -1)) | ||
558 | |||
559 | (use-package edebug | ||
560 | :preface | ||
561 | (defun turn-off-eldoc-mode () | ||
562 | (eldoc-mode -1)) | ||
563 | :config | ||
564 | (add-hook 'edebug-mode-hook #'turn-off-eldoc-mode)) | ||
565 | |||
566 | ;;; Web languages | ||
567 | |||
568 | (use-package web-mode | ||
569 | :ensure t | ||
570 | :mode ("\\.phtml\\'" | ||
571 | "\\.tpl\\.php\\'" | ||
572 | "\\.[agj]sp\\'" | ||
573 | "\\.as[cp]x\\'" | ||
574 | "\\.erb\\'" | ||
575 | "\\.mustache\\'" | ||
576 | "\\.djhtml\\'" | ||
577 | "\\.html?\\'")) | ||
578 | |||
579 | ;;; Applications | ||
580 | |||
581 | (use-package dired | ||
582 | :init (require 'dired-x) | ||
583 | :custom | ||
584 | (dired-recursive-copies 'always) | ||
585 | (dired-recursive-deletes 'always) | ||
586 | (dired-create-destination-dirs 'always) | ||
587 | (dired-do-revert-buffer t) | ||
588 | (dired-hide-details-hide-symlink-targets nil) | ||
589 | (dired-isearch-filenames 'dwim) | ||
590 | (delete-by-moving-to-trash t) | ||
591 | (dired-auto-revert-buffer t) | ||
592 | (dired-listing-switches "-AlF") | ||
593 | (ls-lisp-dirs-first t) | ||
594 | (dired-ls-F-marks-symlinks t) | ||
595 | (dired-clean-confirm-killing-deleted-buffers nil) | ||
596 | (dired-no-confirm '(byte-compile | ||
597 | load chgrp chmod chown | ||
598 | copy move hardlink symlink | ||
599 | shell touch)) | ||
600 | (dired-dwim-target t) | ||
601 | :bind | ||
602 | (("C-x C-j" . dired-jump) | ||
603 | ([remap list-directory] . dired) | ||
604 | :map dired-mode-map | ||
605 | ("C-j" . dired-up-directory) | ||
606 | ("<backspace>" . dired-up-directory)) | ||
607 | :init | ||
608 | (defhook dired-mode-hook | ||
609 | #'dired-hide-details-mode | ||
610 | #'hl-line-mode | ||
611 | #'truncate-lines-local-mode)) | ||
612 | |||
613 | (use-package dired-subtree | ||
614 | :ensure t | ||
615 | :after dired | ||
616 | :bind (:map dired-mode-map | ||
617 | (("TAB" . dired-subtree-cycle) | ||
618 | ("i" . dired-subtree-toggle)))) | ||
619 | |||
620 | (use-package dired-collapse | ||
621 | :ensure t | ||
622 | :hook dired-mode-hook) | ||
623 | |||
624 | (use-package dired-hide-dotfiles | ||
625 | :ensure t | ||
626 | :bind (:map dired-mode-map | ||
627 | ("." . dired-hide-dotfiles-mode))) | ||
628 | |||
629 | (use-package magit | ||
630 | :ensure t | ||
631 | :bind | ||
632 | ("C-x g" . magit)) | ||
633 | |||
634 | (use-package auth-source | ||
635 | :custom | ||
636 | (auth-sources '(default "secrets:passwords")) | ||
637 | :config | ||
638 | (defhook authinfo-mode-hook | ||
639 | #'truncate-lines-local-mode)) | ||
640 | |||
641 | (use-package keychain-environment | ||
642 | :ensure t | ||
643 | :when (executable-find "keychain") | ||
644 | :hook (after-init-hook . keychain-refresh-environment)) | ||
645 | |||
646 | (use-package keepassxc-shim | ||
647 | :load-path "~/src/emacs/keepassxc-shim/" | ||
648 | :config | ||
887 | (keepassxc-shim-activate)) | 649 | (keepassxc-shim-activate)) |
888 | 650 | ||
889 | (yoke (keychain-environment "https://github.com/tarsius/keychain-environment") | 651 | (use-package eat |
890 | :when (executable-find "keychain") | 652 | :ensure t |
891 | (keychain-refresh-environment)) | 653 | :commands eat-eshell-mode |
892 | 654 | :hook (eshell-load-hook . eat-eshell-mode)) | |
893 | (yoke (exec-path-from-shell "https://github.com/purcell/exec-path-from-shell") | 655 | |
894 | :when (eq system-type 'gnu/linux) | 656 | (use-package dictionary |
895 | (require 'exec-path-from-shell) | 657 | :custom |
896 | (dolist (var '("SSH_AUTH_SOCK" | 658 | (dictionary-server (if (or (executable-find "dictd") |
897 | "SSH_AGENT_PID" | 659 | (file-exists-p "/usr/sbin/dictd")) ; debian |
898 | "GPG_AGENT_INFO" | 660 | "localhost" |
899 | "LANG" | 661 | "dict.org")) |
900 | "LC_CTYPE" | 662 | :bind |
901 | "XDG_CONFIG_HOME" | 663 | (("C-c d" . dictionary-search))) |
902 | "XDG_CONFIG_DIRS" | 664 | |
903 | "XDG_DATA_HOME" | 665 | (use-package pdf-tools |
904 | "XDG_DATA_DIRS" | 666 | :ensure t |
905 | "XDG_CACHE_HOME")) | 667 | :mode ("\\.[pP][dD][fF]\\'" . pdf-view-mode) |
906 | (add-to-list 'exec-path-from-shell-variables var)) | 668 | :magic ("%PDF" . pdf-view-mode) |
907 | (exec-path-from-shell-initialize)) | 669 | :config |
908 | 670 | (pdf-tools-install)) | |
909 | (yoke (sophomore "https://codeberg.org/acdw/sophomore.el") | ||
910 | (sophomore-enable-all) | ||
911 | (sophomore-disable #'view-hello-file | ||
912 | #'describe-gnu-project) | ||
913 | (sophomore-disable-with 'confirm #'save-buffers-kill-terminal)) | ||
914 | |||
915 | (yoke (macrostep "https://github.com/joddie/macrostep") | ||
916 | (eval-after elisp-mode (require 'macrostep)) | ||
917 | (define-key* '(emacs-lisp-mode-map | ||
918 | lisp-interaction-mode-map) | ||
919 | "C-c e" #'macrostep-expand)) | ||
920 | |||
921 | (yoke (expand-region "https://github.com/magnars/expand-region.el") | ||
922 | (define-advice er/clear-history (:after (&rest _) refold-org) | ||
923 | (when (derived-mode-p 'org-mode) | ||
924 | (+org-hide-drawers-except-point) | ||
925 | (org-link-descriptive-ensure) | ||
926 | (font-lock-update))) | ||
927 | (defkeys t | ||
928 | "C-=" #'er/expand-region)) | ||
929 | 671 | ||
930 | (yoke (embrace "https://github.com/cute-jumper/embrace.el") | 672 | |
931 | :depends ((expand-region "https://github.com/magnars/expand-region.el")) | 673 | |
932 | (defkeys t | 674 | (use-package bbdb |
933 | "C-\"" #'embrace-commander) | 675 | :ensure t |
934 | (dolist (fnhook '((org-mode-hook embrace-org-mode-hook) | 676 | :custom |
935 | (ruby-mode-hook embrace-ruby-mode-hook) | 677 | (bbdb-complete-mail-allow-cycling t) |
936 | (emacs-lisp-mode-hook embrace-emacs-lisp-mode-hook) | 678 | (bbdb-file (private/ "bbdb")) |
937 | (latex-mode-hook embrace-LaTeX-mode-hook))) | 679 | :init |
938 | (apply #'add-hook fnhook)) | 680 | (defhook custom-allowed-after-load-hook |
939 | (eval-after org | 681 | :name bbdb@after-custom |
940 | (defkeys org-mode-map | 682 | (require 'bbdb) |
941 | "C-\"" #'embrace-commander) | 683 | (require 'bbdb-message) |
942 | (defmacro org-insert-or-embrace (char) | 684 | (bbdb-initialize 'message))) |
943 | "Define a function to insert CHAR, or `embrace' the region with it." | 685 | |
944 | (let* ((fn-name (intern (format "org-insert-or-embrace-%s" char))) | 686 | (use-package bbdb-vcard |
945 | (char (cond ((characterp char) char) | 687 | :ensure t) |
946 | ((stringp char) (string-to-char char)) | 688 | |
947 | (t (user-error "Bad format for char: %S" char))))) | 689 | (use-package notmuch |
948 | `(defun ,fn-name (n) | 690 | :load-path "~/usr/share/emacs/site-lisp/" |
949 | ,(format "Insert N %ss, or surround the region with them." | 691 | :preface |
950 | (char-to-string char)) | 692 | (defdir notmuch/ (sync/ "emacs/notmuch")) |
951 | (interactive "p") | 693 | :init |
952 | (if (region-active-p) | 694 | (use-package +notmuch |
953 | (dotimes (_ n) | 695 | :after notmuch |
954 | (embrace--add-internal (region-beginning) (region-end) ,char) | 696 | :load-path "lisp/" |
955 | (forward-char 1)) | 697 | :bind (("C-c n" . +notmuch-goto) |
956 | (self-insert-command n ,char))))) | 698 | :map notmuch-search-mode-map |
957 | (define-key* org-mode-map | 699 | ("!" . +notmuch-search-mark-spam) |
958 | "*" (org-insert-or-embrace "*") | 700 | :map notmuch-tree-mode-map |
959 | "/" (org-insert-or-embrace "/") | 701 | ("!" . +notmuch-search-mark-spam-then-next) |
960 | "_" (org-insert-or-embrace "_") | 702 | ("M-<" . +notmuch-tree-beginning) |
961 | "=" (org-insert-or-embrace "=") | 703 | ("M->" . +notmuch-tree-end)) |
962 | "~" (org-insert-or-embrace "~") | 704 | :hook (message-send-hook . +send-mail-dispatch) |
963 | "+" (org-insert-or-embrace "+")))) | 705 | :config |
964 | 706 | ;; Saved searches | |
965 | (yoke (notmuch "~/usr/share/emacs/site-lisp") | 707 | (+notmuch-make-saved-search "inbox+unread" "m" 'tree "tag:inbox" "tag:unread" "NOT tag:Spam") |
966 | (eval-after bbdb | 708 | (+notmuch-make-saved-search "inbox" "i" 'tree "tag:inbox" "NOT tag:Spam") |
967 | (require* 'notmuch '+notmuch '+message)) | 709 | (+notmuch-make-saved-search "lists+unread" "l" 'tree "tag:/List/" "tag:unread") |
968 | (+define-dir notmuch/ (sync/ "emacs/notmuch") | 710 | (+notmuch-make-saved-search "lists" "L" 'tree "tag:/List/") |
969 | "Notmuch configuration and data.") | 711 | (+notmuch-make-saved-search "unread" "u" 'tree "tag:unread" "NOT tag:Spam") |
970 | (setf notmuch-init-file (notmuch/ "notmuch-init.el" t) | 712 | (+notmuch-make-saved-search "flagged" "f" 'tree "tag:flagged") |
971 | notmuch-address-save-filename (notmuch/ "addresses" t) | 713 | (+notmuch-make-saved-search "sent" "t" 'tree "tag:sent") |
972 | notmuch-address-use-company (featurep 'company) | 714 | (+notmuch-make-saved-search "drafts" "d" 'tree "tag:draft") |
973 | notmuch-search-oldest-first nil | 715 | (+notmuch-make-saved-search "all mail" "a" 'tree "*")) |
974 | notmuch-archive-tags '("-inbox" "-unread") | 716 | (use-package message |
975 | notmuch-draft-tags '("+draft" "-inbox" "-unread") | 717 | :after notmuch |
976 | mail-user-agent 'notmuch-user-agent | 718 | :hook (message-setup-hook . message-signature-setup) |
977 | bbdb-mail-user-agent 'notmuch-user-agent | 719 | :config (require '+message)) |
978 | message-mail-user-agent t) | 720 | (defhook (notmuch-show-mode-hook notmuch-message-mode-hook) |
979 | (define-key* (current-global-map) | ||
980 | "C-c m" #'notmuch-mua-new-mail | ||
981 | "C-c n" #'+notmuch-goto) | ||
982 | ;; Reading mail | ||
983 | (setf notmuch-show-indent-content nil) | ||
984 | (add-hook* '(notmuch-show-mode-hook | ||
985 | notmuch-message-mode-hook) | ||
986 | #'visual-fill-column-mode) | 721 | #'visual-fill-column-mode) |
987 | (eval-after notmuch | 722 | :custom |
988 | (define-key* notmuch-search-mode-map | 723 | (notmuch-init-file (notmuch/ "notmuch-init.el" t)) |
989 | "RET" #'notmuch-search-show-thread | 724 | (notmuch-address-save-filename (notmuch/ "addresses" t)) |
990 | "M-RET" #'notmuch-tree-from-search-thread | 725 | (notmuch-address-use-company (featurep 'company)) |
991 | "!" #'+notmuch-search-mark-spam) | 726 | (notmuch-search-oldest-first nil) |
992 | (define-key* notmuch-tree-mode-map | 727 | (notmuch-archive-tags '("-inbox" "-unread")) |
993 | "!" #'+notmuch-search-mark-spam-then-next | 728 | (notmuch-draft-tags '("+draft" "-inbox" "-unread")) |
994 | "M-<" (notmuch-tree--define-do-in-message-window | 729 | (mail-user-agent 'notmuch-user-agent) |
995 | notmuch-tree-beginning-of-message beginning-of-buffer) | 730 | (bbdb-mail-user-agent 'notmuch-user-agent) |
996 | "M->" (notmuch-tree--define-do-in-message-window | 731 | (message-mail-user-agent t) |
997 | notmuch-tree-end-of-message end-of-buffer))) | 732 | (notmuch-show-indent-content nil) |
998 | ;; Writing mail | 733 | (message-kill-buffer-on-exit t) |
999 | (setf message-kill-buffer-on-exit t | 734 | (message-auto-save-directory nil) |
1000 | message-auto-save-directory nil) | 735 | (send-mail-function #'sendmail-send-it) |
1001 | ;; Sending mail | 736 | (mail-specify-envelope-from t) |
1002 | (setf send-mail-function #'sendmail-send-it | 737 | (message-sendmail-envelope-from 'header) |
1003 | mail-specify-envelope-from t | 738 | (message-envelope-from 'header) |
1004 | message-sendmail-envelope-from 'header | 739 | (notmuch-saved-searches nil) |
1005 | message-envelope-from 'header) | 740 | :bind |
1006 | ;; Extras | 741 | (("C-c m" . notmuch-mua-new-mail) |
1007 | (define-advice mm-save-part-to-file (:before (_handle file) create-directory) | 742 | :map notmuch-search-mode-map |
1008 | (let ((directory (file-name-directory file))) | 743 | ("RET" . notmuch-search-show-thread) |
1009 | (when (yes-or-no-p (format "Directory %s doesn't exist. Create?" directory)) | 744 | ("M-RET" . notmuch-tree-from-search-thread)) |
1010 | (make-directory directory :parents)))) | 745 | :config |
1011 | (eval-after notmuch | 746 | (load notmuch-init-file :noerror) |
1012 | (require '+notmuch) | 747 | (advice-add 'notmuch-tag :filter-args #'+notmuch-correct-tags) |
1013 | (load notmuch-init-file :noerror) | 748 | ;; Mailing lists |
1014 | (add-hook 'message-setup-hook #'+message-signature-setup) | 749 | (add-to-list 'notmuch-message-headers "List-Post" :append) |
1015 | (add-hook 'message-send-hook #'+send-mail-dispatch) | 750 | (define-advice notmuch-mua-new-reply (:around (orig &rest r) list-aware) |
1016 | (advice-add 'notmuch-tag :filter-args #'+notmuch-correct-tags) | 751 | "Make `notmuch-mua-new-reply' list-aware." |
1017 | (advice-add 'notmuch-bury-or-kill-this-buffer :after | 752 | (let ((ml (notmuch-show-get-header :List-Post))) |
1018 | (defun +display-time@notmuch (&rest _) | 753 | (apply orig r) |
1019 | ;; (display-time-event-handler) | 754 | (when ml |
1020 | (setq display-time-mail-string | 755 | (with-buffer-modified-unmodified |
1021 | (replace-regexp-in-string "(.*)" | 756 | (message-remove-header "To") |
1022 | (format "(%s)" (+notmuch-new-mail-p)) | 757 | (message-add-header |
1023 | display-time-mail-string)) | 758 | (format "To: %s" |
1024 | (display-time-update) | 759 | (replace-regexp-in-string "<mailto:\\(.*\\)>" "\\1" ml))) |
1025 | (force-mode-line-update))) | 760 | (message-goto-body)))))) |
1026 | (add-to-list 'notmuch-message-headers "List-Post" :append) | 761 | |
1027 | (define-advice notmuch-mua-new-reply (:around (orig &rest r) list-aware) | 762 | ;;; The INTERNET |
1028 | "Make `notmuch-mua-new-reply' list-aware." | 763 | |
1029 | (let ((ml (notmuch-show-get-header :List-Post))) | 764 | (use-package link-hint |
1030 | (apply orig r) | 765 | :ensure t |
1031 | (when ml | 766 | :demand t |
1032 | (with-buffer-modified-unmodified | 767 | :preface |
1033 | (message-remove-header "To") | 768 | (global-set-key (kbd "M-l") nil) |
1034 | (message-add-header | 769 | :custom |
1035 | (format "To: %s" | 770 | (link-hint-avy-style 'at-full) |
1036 | (replace-regexp-in-string "<mailto:\\(.*\\)>" "\\1" ml))) | 771 | (link-hint-avy-all-windows t) |
1037 | (message-goto-body))))) | 772 | :config |
1038 | (setf notmuch-saved-searches (list | 773 | (require '+link-hint) |
1039 | (list :name "inbox+unread" | 774 | (+link-hint-open-secondary-setup) |
1040 | :query (+notmuch-query-concat | 775 | (+link-hint-open-chrome-setup) |
1041 | "tag:inbox" | 776 | :bind |
1042 | "tag:unread" | 777 | (("M-l M-l" . +link-hint-open-link) |
1043 | "NOT tag:Spam") | 778 | ("M-l l" . +link-hint-open-link) |
1044 | :key "m" | 779 | ("M-l M-o" . +link-hint-open-secondary) |
1045 | :search-type 'tree) | 780 | ("M-l o" . +link-hint-open-secondary) |
1046 | (list :name "inbox" | 781 | ("M-l M-m" . +link-hint-open-multiple-links) |
1047 | :query (+notmuch-query-concat | 782 | ("M-l m" . +link-hint-open-multiple-links) |
1048 | "tag:inbox" | 783 | ("M-l M-w" . link-hint-copy-link) |
1049 | "NOT tag:Spam") | 784 | ("M-l w" . link-hint-copy-link) |
1050 | :key "i" | 785 | ("M-l M-c" . +link-hint-open-chrome) |
1051 | :search-type 'tree) | 786 | ("M-l c" . +link-hint-open-chrome))) |
1052 | (list :name "lists+unread" | 787 | |
1053 | :query (+notmuch-query-concat | 788 | (use-package browse-url |
1054 | "tag:/List/" | 789 | :demand t |
1055 | "tag:unread") | 790 | :preface |
1056 | :key "l" | 791 | (require '+browse-url) |
1057 | :search-type 'tree) | 792 | :config |
1058 | (list :name "lists" | 793 | (eval-after chd |
1059 | :query "tag:/List/" | 794 | (add-to-list 'browse-url-handlers (cons chd/url-regexps |
1060 | :key "L" | 795 | #'browse-url-chrome)) |
1061 | :search-type 'tree) | 796 | (add-to-list '+browse-url-other-safe-browser-functions |
1062 | (list :name "unread" | 797 | #'chd/browse-url)) |
1063 | :query (+notmuch-query-concat | 798 | (put 'browse-url-browser-function 'safe-local-variable |
1064 | "tag:unread" | 799 | #'+browse-url-browser-function-safe-p) |
1065 | "NOT tag:Spam") | 800 | :custom |
1066 | :key "u" | 801 | (browse-url-browser-function #'eww-browse-url) |
1067 | :search-type 'tree) | 802 | (browse-url-chromium-program (choose-executable "chromium")) |
1068 | (list :name "flagged" | 803 | (browse-url-chrome-program (choose-executable "chrome" "google-chrome-stable")) |
1069 | :query "tag:flagged" | 804 | (browse-url-firefox-program (choose-executable "firefox" "firefox-esr")) |
1070 | :key "f" | 805 | (browse-url-firefox-new-window-is-tab t) |
1071 | :search-type 'tree) | 806 | (browse-url-firefox-arguments '("--new-tab")) |
1072 | (list :name "sent" | 807 | (browse-url-generic-program (or browse-url-firefox-program |
1073 | :query "tag:sent" | 808 | browse-url |
1074 | :key "t" | 809 | browse-url-chrome-program)) |
1075 | :search-type 'tree) | 810 | (browse-url-generic-args |
1076 | (list :name "drafts" | 811 | (cl-loop for prog in '(firefox chromium chrome) |
1077 | :query "tag:draft" | 812 | for progfn = (intern (format "browse-url-%s-program" |
1078 | :key "d" | 813 | prog)) |
1079 | :search-type 'tree) | 814 | for progargs = (intern (format "browse-url-%s-arguments" |
1080 | (list :name "all mail" | 815 | prog)) |
1081 | :query "*" | 816 | if (equal browse-url-generic-program |
1082 | :key "a" | 817 | (symbol-value progfn)) |
1083 | :search-type 'tree))))) | 818 | return (symbol-value progargs) |
1084 | 819 | finally return nil)) | |
1085 | (yoke (cider "https://github.com/clojure-emacs/cider") | 820 | (browse-url-handlers |
1086 | :depends ((clojure-mode "http://github.com/clojure-emacs/clojure-mode") | 821 | `(;; Videos |
1087 | (parseedn "https://github.com/clojure-emacs/parseedn/") | 822 | (,(+browse-url-matches "youtube\\.com" "youtu\\.be" |
1088 | (parseclj "https://github.com/clojure-emacs/parseclj/") ; parseedn | 823 | "invidious" "yewtu\\.be" |
1089 | (queue "https://elpa.gnu.org/packages/queue-0.2.el" :type 'http) | 824 | (rx "." (or "mp4" "gif" "mov" "MOV" "webm") |
1090 | (spinner "https://github.com/Malabarba/spinner.el") | 825 | eos)) |
1091 | (sesman "https://github.com/vspinu/sesman")) | 826 | . +browse-url-with-mpv) |
1092 | :when (executable-find "clojure")) | 827 | ;; Music |
1093 | 828 | (,(+browse-url-matches "soundcloud\\.com" "bandcamp\\.com" | |
1094 | (yoke (web-mode "https://github.com/fxbois/web-mode") | 829 | (rx "." (or "ogg" "mp3" "opus" "m4a") eos)) |
1095 | (setf (alist-get (rx "." (or "htm" "html" "phtml" "tpl.php" | 830 | . +browse-url-with-mpv) |
1096 | "asp" "gsp" "jsp" "ascx" "aspx" | 831 | ;; Images |
1097 | "erb" "mustache" "djhtml") | 832 | (,(+browse-url-matches "pbs\\.twimg\\.com" |
1098 | eos) | 833 | (rx "." (or "jpeg" "jpg" "png" "bmp" "webp") |
1099 | auto-mode-alist nil nil #'string=) | 834 | eos)) |
1100 | 'web-mode)) | 835 | . +browse-url-with-mpv-image) |
1101 | 836 | ;; Blobs (binary files) | |
1102 | (yoke (chicken-geiser "https://gitlab.com/emacs-geiser/chicken") | 837 | (,(+browse-url-matches (rx "." (or ".tar.gz" ".pdf") eos)) |
1103 | :depends ((geiser "https://gitlab.com/emacs-geiser/geiser" | 838 | . +browse-url-download) |
1104 | :load "elisp")) | 839 | ;; External URLs --- these are URLs that don't open in EWW very well, |
1105 | :when (executable-find "csi") | 840 | ;; so I want to open them in the external browser. |
1106 | :pre ((autoload 'geiser-activate-implementation "geiser-impl")) | 841 | (+browse-url-external-url-p . ,(if (featurep 'xwidget-internal) |
1107 | (autoload 'geiser "geiser" nil :interactive) | 842 | #'xwidget-webkit-browse-url |
1108 | (add-hook 'scheme-mode-hook 'geiser-mode)) | 843 | browse-url-secondary-browser-function)))) |
1109 | 844 | (+browse-url-external-domains '("github.com" "gitlab.com" "codeberg.org" | |
1110 | (yoke (zoom-frm "https://github.com/emacsmirror/zoom-frm") | 845 | "tildegit.org" "git.tilde.town" |
1111 | :depends ((frame-cmds "https://github.com/emacsmirror/frame-cmds") | 846 | "google.com" "imgur.com" "twitch.tv" |
1112 | (frame-fns "https://github.com/emacsmirror/frame-fns")) | 847 | "pixelfed" "instagram.com" |
1113 | (define-key* (current-global-map) | 848 | "bibliogram.art" "reddit.com" |
1114 | "M-+" #'zoom-frm-in | 849 | "teddit.net" "libreddit.de" |
1115 | "M-_" #'zoom-frm-out)) | 850 | "streamable.com" "spotify.com" |
1116 | 851 | "hetzner.cloud" "melpa.org" | |
1117 | (yoke (jabber "https://codeberg.org/acdw/emacs-jabber") | 852 | "twitter.com" ("^t\\.co$") |
1118 | :depends ((srv "https://github.com/legoscia/srv.el") | 853 | "nitter.snopyta.org" "nitter.net"))) |
1119 | (fsm "https://elpa.gnu.org/packages/fsm-0.2.1.el" :type 'http)) | 854 | |
1120 | (setf jabber-account-list '(("acdw@hmm.st")) | 855 | (use-package browse-url-transform |
1121 | jabber-auto-reconnect t | 856 | :after browse-url |
1122 | jabber-chat-buffer-format "xmpp:%n" | 857 | :load-path "~/src/emacs/browse-url-transform/" |
1123 | jabber-browse-buffer-format "xmpp-browse:%n" | 858 | :custom |
1124 | jabber-groupchat-buffer-format "xmpp-muc:%n" | 859 | (browse-url-transform-alist |
1125 | jabber-muc-private-buffer-format "xmpp-muc-private:%n" | 860 | `(;; Privacy-respecting alternatives |
1126 | jabber-groupchat-prompt-format "%>10n │ " | 861 | ("twitter\\.com" . "nitter.snopyta.org") |
1127 | jabber-chat-local-prompt-format "%>10n │ " | 862 | ("\\(?:\\(?:old\\.\\)?reddit\\.com\\)" . "libreddit.de") |
1128 | jabber-chat-system-prompt-format " * * * * * *" | 863 | ("medium\\.com" . "scribe.rip") |
1129 | jabber-chat-foreign-prompt-format "%>10n │ " | 864 | (".*substack\\.com.*" . ,substack-proxy) |
1130 | jabber-muc-private-foreign-prompt-format "%g/%n " | 865 | ;; Text-mode of non-text-mode sites |
1131 | jabber-last-read-marker "----------------------------------------" | 866 | ("www\\.npr\\.org" . "text.npr.org") |
1132 | jabber-muc-header-line-format '("" jabber-muc-topic) | 867 | ;; Ask for raw versions of paste sites |
1133 | jabber-muc-decorate-presence-patterns | 868 | ("^.*dpaste\\.com.*$" . "\\&.txt") |
1134 | '(("\\( enters the room ([^)]+)\\| has left the chatroom\\)$") | 869 | ("bpa\\.st/\\(.*\\)" . "bpa.st/raw/\\1") |
1135 | ("." . jabber-muc-presence-dim)) | 870 | ("\\(paste\\.debian\\.net\\)/\\(.*\\)" . "\\1/plain/\\2") |
1136 | jabber-activity-make-strings | 871 | ("\\(pastebin\\.com\\)/\\\(.*\\)" . "\\1/raw/\\2") |
1137 | #'jabber-activity-make-strings-shorten | 872 | ("\\(paste\\.centos\\.org/view\\)/\\(.*\\)" . "\\1/raw/\\2"))) |
1138 | ;; (defun +jabber-activity-make-strings (jids) | 873 | :config |
1139 | ;; (mapcar (lambda (jid) | 874 | (browse-url-transform-mode)) |
1140 | ;; (cons jid | 875 | |
1141 | ;; (let ((s (jabber-activity-make-string-default jid))) | 876 | (use-package eww |
1142 | ;; (cond | 877 | :custom |
1143 | ;; ((string-match-p "%" s) | 878 | (eww-use-browse-url ".") |
1144 | ;; (replace-regexp-in-string "%.*" "" s)) | 879 | :config |
1145 | ;; (:else s))))) | 880 | (defhook eww-mode-hook |
1146 | ;; jids)) | 881 | (defhook ((visual-fill-column-mode nil :local)) |
1147 | jabber-rare-time-format " - - - - - - %H:%M %F") | 882 | :name visual-fill-column@eww-mode-refresh |
883 | (eww-reload t))) | ||
884 | (defkeys eww-mode-map | ||
885 | "&" (+browse-url-switch-external-browser eww-mode | ||
886 | (plist-get eww-data :url)))) | ||
887 | |||
888 | (use-package xwidget | ||
889 | :when (featurep 'xwidget-internal) | ||
890 | :preface | ||
891 | (setenv "WEBKIT_FORCE_SANDBOX" "0") ; This is probably a bad idea | ||
892 | :custom | ||
893 | (xwidget-webkit-cookie-file (cache/ "xwidget-cookies")) | ||
894 | (xwidget-webkit-download-dir (expand-file-name "~/var/download")) | ||
895 | :config | ||
896 | (defkeys xwidget-webkit-mode-map | ||
897 | "&" (+browse-url-switch-external-browser xwidget-webkit-mode | ||
898 | (xwidget-webkit-uri (xwidget-webkit-current-session))))) | ||
899 | |||
900 | (use-package xwwp | ||
901 | :load-path "~/src/emacs/xwwp/" | ||
902 | :when (featurep 'xwidget-internal) | ||
903 | :custom | ||
904 | (xwwp-search-prefix "https://duckduckgo.com/?q=") | ||
905 | :bind (:map xwidget-webkit-mode-map | ||
906 | ("f" . xwwp-follow-link))) | ||
907 | |||
908 | (use-package jabber | ||
909 | :load-path "~/src/emacs/emacs-jabber" | ||
910 | :custom | ||
911 | (jabber-account-list '(("acdw@hmm.st"))) | ||
912 | (jabber-auto-reconnect t) | ||
913 | (jabber-chat-buffer-format "xmpp:%n") | ||
914 | (jabber-browse-buffer-format "xmpp-browse:%n") | ||
915 | (jabber-groupchat-buffer-format "xmpp-muc:%n") | ||
916 | (jabber-muc-private-buffer-format "xmpp-muc-private:%n") | ||
917 | (jabber-groupchat-prompt-format "%>10n │ ") | ||
918 | (jabber-chat-local-prompt-format "%>10n │ ") | ||
919 | (jabber-chat-system-prompt-format " * * * * * *") | ||
920 | (jabber-chat-foreign-prompt-format "%>10n │ ") | ||
921 | (jabber-muc-private-foreign-prompt-format "%g/%n ") | ||
922 | (jabber-last-read-marker "----------------------------------------") | ||
923 | (jabber-muc-header-line-format '("" jabber-muc-topic)) | ||
924 | (jabber-muc-decorate-presence-patterns | ||
925 | '(("\\( enters the room ([^)]+)\\| has left the chatroom\\)$") | ||
926 | ("." . jabber-muc-presence-dim))) | ||
927 | (jabber-activity-make-strings | ||
928 | #'jabber-activity-make-strings-shorten) | ||
929 | (jabber-rare-time-format " - - - - - - %H:%M %F") | ||
930 | :custom-face | ||
931 | (jabber-chat-prompt-local ((t :inherit font-lock-keyword-face | ||
932 | :foreground unspecified))) | ||
933 | (jabber-activity-face ((t :inherit jabber-chat-prompt-foreign | ||
934 | :foreground unspecified | ||
935 | :weight normal))) | ||
936 | (jabber-activity-personal-face ((t :inherit font-lock-warning-face | ||
937 | :foreground unspecified | ||
938 | :weight bold))) | ||
939 | (jabber-chat-prompt-foreign ((t :inherit font-lock-constant-face | ||
940 | :foreground unspecified))) | ||
941 | (jabber-chat-prompt-system ((t :inherit font-lock-doc-face | ||
942 | :foreground unspecified))) | ||
943 | (jabber-rare-time-face ((t :inherit font-lock-comment-face | ||
944 | :foreground unspecified | ||
945 | :underline nil))) | ||
946 | :init | ||
1148 | (defhook (jabber-chat-mode-hook | 947 | (defhook (jabber-chat-mode-hook |
1149 | jabber-browse-mode-hook | 948 | jabber-browse-mode-hook |
1150 | jabber-roster-mode-hook | 949 | jabber-roster-mode-hook |
1151 | jabber-console-mode-hook) | 950 | jabber-console-mode-hook) |
1152 | :name jabber-ui-setup | 951 | :name jabber-ui-setup |
952 | (visual-fill-column-mode) | ||
1153 | (electric-pair-local-mode -1) | 953 | (electric-pair-local-mode -1) |
1154 | (auto-fill-mode -1) | 954 | (auto-fill-mode -1) |
1155 | #'visual-fill-column-mode) | 955 | (setq-local wrap-prefix (format "%13s" " "))) |
1156 | (setq-local-hook jabber-chat-mode-hook | 956 | :bind-keymap ("C-c j" . jabber-global-keymap) |
1157 | wrap-prefix (format "%13s" " ")) | 957 | :bind (("C-c C-SPC" . jabber-activity-switch-to)) |
1158 | (defun +jabber-fix-keybinds-dammit () | 958 | :config |
1159 | "Jabber autoloads keybinds which is really annoying." | 959 | (global-set-key (kbd "C-x C-j") #'dired-jump) ; Extremely annoying fix |
1160 | (define-key* (current-global-map) | 960 | (require 'jabber-httpupload nil :noerror) |
1161 | "C-x C-j" #'dired-jump | 961 | (add-hook 'jabber-post-connect-hooks #'jabber-enable-carbons) |
1162 | "C-c j" jabber-global-keymap | 962 | (remove-hook 'jabber-alert-muc-hooks 'jabber-muc-echo) |
1163 | "C-c C-SPC" #'jabber-activity-switch-to)) | 963 | (remove-hook 'jabber-alert-presence-hooks 'jabber-presence-echo) |
1164 | (eval-after init (+jabber-fix-keybinds-dammit)) | 964 | (add-hook 'jabber-alert-muc-hooks |
1165 | (eval-after jabber | 965 | (defun jabber@highlight-acdw (&optional _nick _group buf _text _title) |
1166 | (require 'jabber-httpupload nil :noerror) | 966 | (when buf |
1167 | (add-hook 'jabber-post-connect-hooks #'jabber-enable-carbons) | 967 | (with-current-buffer buf |
1168 | (remove-hook 'jabber-alert-muc-hooks 'jabber-muc-echo) | 968 | (let ((regexp (rx word-boundary |
1169 | (remove-hook 'jabber-alert-presence-hooks 'jabber-presence-echo) | 969 | "acdw" ; maybe get from the config? |
1170 | (add-hook 'jabber-alert-muc-hooks | 970 | word-boundary))) |
1171 | (defun jabber@highlight-acdw (&optional _nick _group buf _text _title) | 971 | (hi-lock-unface-buffer regexp) |
1172 | (when buf | 972 | (highlight-regexp regexp 'hi-blue)))))) |
1173 | (with-current-buffer buf | 973 | (add-hook 'window-configuration-change-hook #'jabber-chat-update-focus) |
1174 | (let ((regexp (rx word-boundary | 974 | (eval-after consult |
1175 | "acdw" ; maybe get from the config? | ||
1176 | word-boundary))) | ||
1177 | (hi-lock-unface-buffer regexp) | ||
1178 | (highlight-regexp regexp 'hi-blue)))))) | ||
1179 | (add-hook 'window-configuration-change-hook #'jabber-chat-update-focus) | ||
1180 | (+jabber-fix-keybinds-dammit) | ||
1181 | (defkeys jabber-chat-mode-map | ||
1182 | "C-l" (defun +jabber-recenter-last-read () | ||
1183 | (interactive) | ||
1184 | (cond | ||
1185 | ((eq last-command '+jabber-recenter-last-read) | ||
1186 | (setq this-command #'recenter) | ||
1187 | (recenter -1)) | ||
1188 | (:else | ||
1189 | (save-excursion | ||
1190 | (condition-case e | ||
1191 | (re-search-backward jabber-last-read-marker) | ||
1192 | (search-failed nil) | ||
1193 | (:success | ||
1194 | (recenter 3))))))))) | ||
1195 | (defun jabber-chat-kill-buffers () | ||
1196 | "Kill all `jabber-chat-mode' buffers." | ||
1197 | (interactive) | ||
1198 | (mapc-buffers (lambda () (message "%S" (buffer-name))) '(jabber-chat-mode))) | ||
1199 | (defun jabber-chat@after-modus-themes-load () | ||
1200 | (modus-themes-with-colors | ||
1201 | (custom-set-faces | ||
1202 | `(jabber-chat-prompt-foreign ((t :foreground unspecified | ||
1203 | :inherit modus-themes-bold)) | ||
1204 | :now) | ||
1205 | `(jabber-chat-prompt-local ((t :foreground unspecified | ||
1206 | :inherit modus-themes-bold)) | ||
1207 | :now) | ||
1208 | `(jabber-chat-prompt-system ((t :foreground unspecified | ||
1209 | :inherit modus-themes-bold)) | ||
1210 | :now) | ||
1211 | `(jabber-activity-face ((t :slant italic))) | ||
1212 | `(jabber-activity-personal-face ((t :slant italic :weight bold))) | ||
1213 | `(jabber-rare-time-face ((t :inherit font-lock-comment-face))))) | ||
1214 | (setq jabber-muc-nick-value | ||
1215 | (pcase (frame--current-backround-mode (selected-frame)) | ||
1216 | ('light 0.5) | ||
1217 | ('dark 1.0)))) | ||
1218 | (eval-after modus-themes | ||
1219 | (add-hook 'modus-themes-after-load-theme-hook | ||
1220 | #'jabber-chat@after-modus-themes-load)) | ||
1221 | (when (or (custom-theme-enabled-p 'modus-operandi) | ||
1222 | (custom-theme-enabled-p 'modus-vivendi)) | ||
1223 | (jabber-chat@after-modus-themes-load)) | ||
1224 | (eval-after (consult jabber) | ||
1225 | ;; Jabber.el chat buffers source for `consult-buffer' | ||
1226 | (defvar jabber-chat-buffer-source | 975 | (defvar jabber-chat-buffer-source |
1227 | `( :name "Jabber" | 976 | `( :name "Jabber" |
1228 | :hidden nil | 977 | :hidden nil |
@@ -1238,190 +987,3 @@ CAPES defaults to `+capes'. CAPF will be made un-exclusive." | |||
1238 | (add-to-list 'consult-buffer-sources 'jabber-chat-buffer-source :append) | 987 | (add-to-list 'consult-buffer-sources 'jabber-chat-buffer-source :append) |
1239 | ;; Also hide xmpp buffers from regular buffer list | 988 | ;; Also hide xmpp buffers from regular buffer list |
1240 | (add-to-list 'consult-buffer-filter "\\`xmpp" nil #'string-equal))) | 989 | (add-to-list 'consult-buffer-filter "\\`xmpp" nil #'string-equal))) |
1241 | |||
1242 | (yoke (link-hint "https://github.com/noctuid/link-hint.el/") | ||
1243 | :depends ((avy "https://github.com/abo-abo/avy")) | ||
1244 | (require '+link-hint) | ||
1245 | (+link-hint-open-secondary-setup) | ||
1246 | (+link-hint-open-chrome-setup) | ||
1247 | (setf link-hint-avy-style 'at-full | ||
1248 | link-hint-avy-all-windows t) | ||
1249 | (global-set-key (kbd "M-l") +link-hint-map) | ||
1250 | (define-key* +link-hint-map | ||
1251 | "M-l" #'+link-hint-open-link "l" #'+link-hint-open-link | ||
1252 | "M-o" #'+link-hint-open-secondary "o" #'+link-hint-open-secondary | ||
1253 | "M-m" #'+link-hint-open-multiple-links "m" #'+link-hint-open-multiple-links | ||
1254 | "M-w" #'link-hint-copy-link "w" #'link-hint-copy-link | ||
1255 | "M-c" #'+link-hint-open-chrome "c" #'+link-hint-open-chrome)) | ||
1256 | |||
1257 | (yoke (elpher "git://thelambdalab.xyz/elpher.git") | ||
1258 | (eval-after elpher | ||
1259 | (define-key* elpher-mode-map | ||
1260 | "l" #'elpher-back))) | ||
1261 | |||
1262 | (yoke (epithet "https://github.com/oantolin/epithet") | ||
1263 | (defhook (Info-selection-hook | ||
1264 | help-mode-hook | ||
1265 | occur-mode-hook | ||
1266 | shell-mode-hook) | ||
1267 | #'epithet-rename-buffer) | ||
1268 | (cond ((boundp 'eww-auto-rename-buffer) | ||
1269 | (setc eww-auto-rename-buffer 'title)) | ||
1270 | (:else (defhook eww-after-render-hook #'epithet-rename-buffer)))) | ||
1271 | |||
1272 | (yoke browse-url | ||
1273 | (require '+browse-url) | ||
1274 | (setf browse-url-browser-function #'eww-browse-url | ||
1275 | browse-url-chrome-program (seq-some #'executable-find | ||
1276 | '("chromium" "chrome" "google-chrome-stable")) | ||
1277 | browse-url-firefox-program (seq-some #'executable-find | ||
1278 | '("firefox" "firefox-esr")) | ||
1279 | browse-url-generic-program (or browse-url-firefox-program | ||
1280 | browse-url-chrome-program) | ||
1281 | browse-url-firefox-new-window-is-tab t | ||
1282 | browse-url-firefox-arguments "-new-tab" | ||
1283 | browse-url-handlers `((video-url-p . +browse-url-with-mpv) | ||
1284 | (music-url-p . +browse-url-with-mpv) | ||
1285 | (image-url-p . +browse-image-with-mpv) | ||
1286 | (blobp . +browse-url-download) | ||
1287 | (external-url-p . ,browse-url-secondary-browser-function) | ||
1288 | ;; HERE FOR REFERENCE --- OPEN MASTO URLS SOME WAY | ||
1289 | (,(defun mastodon-url-p (url) | ||
1290 | "Try to determine whether URL is a mastodon URL." | ||
1291 | (string-match-p "/@[^/]+\\(/\\|/[[:digit:]]+\\)?$" url)) | ||
1292 | . ,browse-url-secondary-browser-function))) | ||
1293 | (+browse-url-make-external-viewer-handler "mpv" '("--cache-pause-wait=30" | ||
1294 | "--cache-pause-initial=yes") | ||
1295 | "Video URL: " | ||
1296 | :fallback browse-url-secondary-browser-function) | ||
1297 | (+browse-url-make-external-viewer-handler "mpv" '("--image-display-duration=inf") | ||
1298 | "Image URL: " | ||
1299 | :name +browse-image-with-mpv) | ||
1300 | (defun video-url-p (url) "Is URL a video?" | ||
1301 | (string-match-p (rx (or "youtube.com" "youtu.be" "invidious" "yewtu.be" | ||
1302 | (seq "." (or "mp4" "gif" "mov" "MOV" "webm") eos))) | ||
1303 | url)) | ||
1304 | (defun music-url-p (url) "Is URL music?" | ||
1305 | (string-match-p (rx "soundcloud.com" "bandcamp.com" | ||
1306 | (seq "." (or "ogg" "mp3" "opus" "m4a" "flac") eos)) | ||
1307 | url)) | ||
1308 | (defun image-url-p (url) "Is URL an image?" | ||
1309 | (string-match-p (rx | ||
1310 | (or (: "." (or "jpeg" "jpg" "png" "bmp" "webp") eos) | ||
1311 | "pbs.twimg.com")) | ||
1312 | url)) | ||
1313 | (defun external-url-p (url) "Should URL open in an external browser?" | ||
1314 | (string-match-p (rx (or "github.com" "gitlab.com" "codeberg.org" | ||
1315 | "tildegit.org" "git.tilde.town" "google.com" | ||
1316 | "imgur.com" "twitch.tv" "pixelfed" "instagram.com" | ||
1317 | "bibliogram.art" "reddit.com" "teddit.net" | ||
1318 | ;; "twitter.com" "nitter" "t.co" | ||
1319 | "streamable.com" "spotify.com" | ||
1320 | "hetzner.cloud" "melpa.org")) | ||
1321 | url)) | ||
1322 | (defun blobp (url) "Is URL some other blob that can't open in Emacs?" | ||
1323 | (string-match-p (rx (or (: (or ".tar.gz" ".pdf") | ||
1324 | eos))) | ||
1325 | url)) | ||
1326 | (eval-after chd | ||
1327 | (add-to-list 'browse-url-handlers (cons chd/url-regexps #'chd/browse-url))) | ||
1328 | (require 'browse-url-transform) | ||
1329 | (setf browse-url-transform-alist `(;; Privacy-respecting alternatives | ||
1330 | ("twitter\\.com" . "nitter.snopyta.org") | ||
1331 | ("\\(?:\\(?:old\\.\\)?reddit\\.com\\)" | ||
1332 | . "libreddit.de") | ||
1333 | ("medium\\.com" . "scribe.rip") | ||
1334 | ;; Text-mode of non-text-mode sites | ||
1335 | ("www\\.npr\\.org" . "text.npr.org") | ||
1336 | ;; Ask for raw versions of paste sites | ||
1337 | ("^.*dpaste\\.com.*$" . "\\&.txt") | ||
1338 | ("bpa\\.st/\\(.*\\)" . "bpa.st/raw/\\1") | ||
1339 | ("\\(paste\\.debian\\.net\\)/\\(.*\\)" | ||
1340 | . "\\1/plain/\\2") | ||
1341 | ("\\(pastebin\\.com\\)/\\\(.*\\)" | ||
1342 | . "\\1/raw/\\2") | ||
1343 | ("gist\\.github\\.com/\\(.*\\)" | ||
1344 | . "gist.githubusercontent.com/\\1/raw/"))) | ||
1345 | (browse-url-transform-mode)) | ||
1346 | |||
1347 | (yoke eww | ||
1348 | (setc eww-use-browse-url ".") | ||
1349 | (eval-after eww | ||
1350 | (defhook eww-mode-hook | ||
1351 | #'visual-fill-column-mode | ||
1352 | (defhook ((visual-fill-column-mode-hook nil :local)) | ||
1353 | :name eww-mode-refresh@visual-fill-column | ||
1354 | (eww-reload t))) | ||
1355 | (defkeys eww-mode-map | ||
1356 | "&" | ||
1357 | (defun +eww-browse-with-external-browser (&optional url) | ||
1358 | "Browse URL with an external browser and close eww." | ||
1359 | (interactive nil eww-mode) | ||
1360 | (condition-case e | ||
1361 | ;; This is wrapped in a `condition-case' so that the eww window | ||
1362 | ;; won't close if there's an error calling the browser. | ||
1363 | (funcall browse-url-secondary-browser-function | ||
1364 | (or url (plist-get eww-data :url))) | ||
1365 | (:success | ||
1366 | (when (null url) ; interactive | ||
1367 | (quit-window))) | ||
1368 | (t (signal (car e) (cdr e))))))) | ||
1369 | (eval-after (eww link-hint) | ||
1370 | (defkeys eww-mode-map | ||
1371 | "f" #'+link-hint-open-link))) | ||
1372 | |||
1373 | (yoke tab-bar | ||
1374 | (setf tab-bar-show t | ||
1375 | global-mode-string | ||
1376 | '((jabber-activity-mode | ||
1377 | (:eval | ||
1378 | (let ((str (or (bound-and-true-p jabber-activity-mode-string) | ||
1379 | ""))) | ||
1380 | (concat (truncate-string-to-width str 20 nil nil t) | ||
1381 | (if (< 0 (length str)) " ⋅" ""))))) | ||
1382 | display-time-string | ||
1383 | "|")) | ||
1384 | (eval-after jabber | ||
1385 | (defhook jabber-activity-mode-hook | ||
1386 | (setf global-mode-string | ||
1387 | '((jabber-activity-mode | ||
1388 | (:eval | ||
1389 | (let ((str (or (bound-and-true-p jabber-activity-mode-string) | ||
1390 | ""))) | ||
1391 | (concat (truncate-string-to-width str 20 nil nil t) | ||
1392 | (if (< 0 (length str)) " ⋅" ""))))) | ||
1393 | display-time-string | ||
1394 | "|")))) | ||
1395 | (add-to-list 'tab-bar-format 'tab-bar-format-align-right :append) | ||
1396 | (add-to-list 'tab-bar-format 'tab-bar-format-global :append) | ||
1397 | (tab-bar-mode)) | ||
1398 | |||
1399 | (yoke (pdf-tools "https://github.com/vedang/pdf-tools" | ||
1400 | :load "lisp") | ||
1401 | :depends ((tablist "https://github.com/politza/tablist/")) | ||
1402 | :when (executable-find "epdfinfo") ; installed from Debian repos | ||
1403 | (pdf-tools-install)) | ||
1404 | |||
1405 | (yoke which-function | ||
1406 | (setf (alist-get 'which-function-mode mode-line-misc-info) | ||
1407 | '((which-func-mode ; Only display if buffer supports it | ||
1408 | (:eval (when (which-function) | ||
1409 | (list "" which-func-format " ")))))) | ||
1410 | (which-function-mode)) | ||
1411 | |||
1412 | (yoke (zzz-to-char "https://github.com/mrkkrp/zzz-to-char") | ||
1413 | :depends ((avy "https://github.com/abo-abo/avy")) | ||
1414 | (setf zzz-to-char-reach 120) | ||
1415 | (defkeys t | ||
1416 | [remap zap-to-char] | ||
1417 | (defun +zzz-to-char (&optional prefix) | ||
1418 | "Run `zzz-up-to-char', or `zzz-to-char' with PREFIX." | ||
1419 | (interactive "P") | ||
1420 | (call-interactively (cond (prefix #'zzz-to-char) | ||
1421 | (:else #'zzz-up-to-char)))))) | ||
1422 | |||
1423 | (yoke sh-mode | ||
1424 | (defhook sh-mode-hook | ||
1425 | :name turn-off-sh-electric-here-document-mode | ||
1426 | (sh-electric-here-document-mode -1))) | ||
1427 | |||