summary refs log tree commit diff stats
path: root/init.el
diff options
context:
space:
mode:
Diffstat (limited to 'init.el')
-rw-r--r--init.el1623
1 files changed, 467 insertions, 1156 deletions
diff --git a/init.el b/init.el index 707125e..4648042 100644 --- a/init.el +++ b/init.el
@@ -1,1163 +1,474 @@
1;;; init.el --- An Emacs of one's own -*- lexical-binding: t -*- 1;;; Emacs init.el -*- lexical-binding: t; -*-
2;; by Case Duckworth <acdw@acdw.net>
3;; Bankruptcy 10: "Annoyance"
2 4
3;; Author: Case Duckworth <acdw@acdw.net>, with inspo from many others 5;;; Commentary:
4;; Homepage: https://git.acdw.net/emacs
5;; Config-Requires: ((emacs "29.0"))
6;; Bankruptcy: 9.4
7 6
8;; This configuration is Free Software. Everyone is permitted to do whatever 7;; This is my Emacs configuration. There are many like it but this
9;; they want with it, without limitation. This software comes without any 8;; one is mine.
10;; warranty whatsoever, but with two pieces of advice:
11;; 9;;
12;; - Don't hurt others. 10;; For the tenth time!
13;; - Make good choices. 11
14 12;;; Packages
15;;; Code: 13
16 14(require 'package)
17(load (locate-user-emacs-file "basics")) ; super basic stuff 15(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
18 16(package-initialize)
19 17
20;;; Built-ins 18(dolist (pkg `(consult
21 19 marginalia
22(use-package emacs ; Misc. config 20 visual-fill-column
23 :config 21 adaptive-wrap
24 (setq recenter-positions '(top middle bottom) 22 geiser
25 initial-major-mode 'lisp-interaction-mode 23 ,(when (executable-find "csi") 'geiser-chicken)
26 initial-scratch-message ";; Emacs!\n\n" 24 avy
27 ;; (format "%s\n\n" 25 zzz-to-char
28 ;; (mapconcat (lambda (s) (format ";; %s" s)) 26 hungry-delete
29 ;; (process-lines "fortune" "-s") 27 undohist
30 ;; "\n")) 28 jinx))
31 eval-expression-print-level nil 29 (when (and pkg (not (package-installed-p pkg)))
32 eval-expression-print-length nil 30 (unless (ignore-errors (package-install pkg))
33 x-select-enable-clipboard-manager nil) 31 (package-refresh-contents)
34 ;; TODO: move this ... elsewhere 32 (package-install pkg))))
35 (setq mode-line-format 33
36 '("%e" 34(load (locate-user-emacs-file "definitions"))
37 mode-line-front-space 35(load (locate-user-emacs-file "packages"))
38 ;; (:propertize ("" mode-line-mule-info 36(load (locate-user-emacs-file "private"))
39 ;; mode-line-client 37
40 ;; mode-line-modified 38(setopt custom-file (locate-user-emacs-file "custom.el"))
41 ;; mode-line-remote) 39(load custom-file :noerror)
42 ;; display (min-width (5.0))) 40
43 ("" mode-line-mule-info 41;;; General keybinding changes
42
43(keymap-global-set "M-o" #'other-window-or-switch-buffer)
44
45(keymap-global-set "M-SPC" #'cycle-spacing@)
46
47(keymap-global-set "M-u" #'universal-argument)
48(keymap-set universal-argument-map "M-u" #'universal-argument-more)
49
50;;; Theme
51
52(if (daemonp)
53 (add-hook 'server-after-make-frame-hook #'first-frame@set-fonts)
54 (run-with-idle-timer 1 nil #'first-frame@set-fonts))
55
56(tool-bar-mode -1)
57
58(load-theme 'modus-vivendi :no-confirm :no-enable)
59(load-theme 'modus-operandi :no-confirm)
60
61(add-hook 'text-mode-hook #'visual-line-mode)
62
63;;; Mode line
64
65(defvar mode-line-position
66 '(""
67 (:eval (if line-number-mode "%3l" ""))
68 (:eval (if column-number-mode
69 (if column-number-indicator-zero-based
70 "/%2c"
71 "/%2C")
72 ""))
73 " (" (-3 "%p") ") "))
74
75(setopt mode-line-format
76 '(("%e" mode-line-front-space
44 mode-line-client 77 mode-line-client
45 mode-line-modified 78 mode-line-modified
46 mode-line-remote) 79 mode-line-remote " "
47 mode-line-frame-identification 80 mode-line-buffer-identification
48 mode-line-buffer-identification 81 (vc-mode vc-mode)
49 " " 82 " "
50 mode-line-position 83 (mode-line-position mode-line-position)
51 (vc-mode vc-mode) 84 mode-line-modes
52 " " 85 mode-line-misc-info
53 minions-mode-line-modes 86 mode-line-end-spaces)))
54 mode-line-misc-info 87
55 mode-line-end-spaces)) 88;; Remove modes from mode-line
56 (keymap-global-unset "C-\\") 89(dolist (minor-mode '(frowny-mode
57 (keymap-global-unset "<f2>") 90 whitespace-mode
58 (setf (alist-get "\\*Compile-Log\\*" display-buffer-alist nil nil #'equal) 91 hungry-delete-mode))
59 '(display-buffer-no-window)) 92 (setf (alist-get minor-mode minor-mode-alist) (list ""))
60 ;; (add-hook 'after-init-hook 93 (add-hook (intern (format "%s-hook" minor-mode))
61 ;; (defun global-mode-string@setup () 94 (lambda ()
62 ;; (defvar jabber-activity-mode-string) 95 (setf (alist-get minor-mode minor-mode-alist) (list "")))))
63 ;; (defvar org-mode-line-string) 96
64 ;; (defvar display-time-mode) 97;;; Completion & minibuffer
65 ;; (defvar display-time-string) 98
66 ;; (setf global-mode-string 99(setopt icomplete-in-buffer t
67 ;; '((t jabber-activity-mode-string) 100 icomplete-tidy-shadowed-file-names t)
68 ;; org-mode-line-string 101(fido-vertical-mode)
69 ;; (display-time-mode display-time-string))))) 102
70 (keymap-global-set "C-c t" 103(setopt completion-auto-help (not icomplete-mode)
71 (define-keymap 104 completion-auto-select 'second-tab
72 :prefix 'toggle-map 105 completions-header-format nil
73 "e" #'toggle-debug-on-error 106 completions-max-height 12
74 "q" #'toggle-debug-on-quit 107 completions-format 'one-column
75 "c" #'column-number-mode 108 completion-styles '(basic partial-completion flex)
76 "l" #'line-number-mode 109 completion-ignore-case t
77 "L" #'display-line-numbers-mode))) 110 read-buffer-completion-ignore-case t
78 111 read-file-name-completion-ignore-case t
79(use-package faces 112 completions-detailed t
80 :config 113 enable-recursive-minibuffers t
81 (add-hook 'server-after-make-frame-hook 114 file-name-shadow-properties '(invisible t intangible t)
82 (defun first-frame@set-fonts () 115 minibuffer-eldef-shorten-default t
83 (remove-hook 'server-after-make-frame-hook 116 minibuffer-prompt-properties '( read-only t
84 #'first-frame@set-fonts) 117 cursor-intangible t
85 (face-spec-set 'default 118 face minibuffer-prompt)
86 `((t :family ,(find-font 119 window-resize-pixelwise t
87 "Recursive Mono Casual Static" 120 frame-resize-pixelwise t)
88 "Comic Code" 121
89 "DejaVu Sans Mono") 122(add-hook 'completion-list-mode-hook #'truncate-lines-mode)
90 :height 110))) 123(add-hook 'minibuffer-setup-hook #'truncate-lines-mode)
91 (face-spec-set 'fixed-pitch 124
92 `((t :family ,(find-font 125;; Up/down when completing in the minibuffer
93 "Recursive Mono Linear Static" 126(define-key minibuffer-local-map (kbd "C-p") #'minibuffer-previous-completion)
94 "Comic Code" 127(define-key minibuffer-local-map (kbd "C-n") #'minibuffer-next-completion)
95 "DejaVu Sans Mono") 128
96 :height 1.0))) 129;; Up/down when competing in a normal buffer
97 (face-spec-set 'variable-pitch 130(define-key completion-in-region-mode-map (kbd "C-p") #'minibuffer-previous-completion)
98 `((t :family ,(find-font 131(define-key completion-in-region-mode-map (kbd "C-n") #'minibuffer-next-completion)
99 "Recursive Sans Casual Static" 132
100 "Atkinson Hyperlegible" 133(setopt completions-sort #'renz/sort-multi-category)
101 "DejaVu Serif") 134
102 :height 1.0))) 135(setopt tab-always-indent 'complete)
103 (face-spec-set 'font-lock-comment-face 136
104 `((t :slant italic 137(file-name-shadow-mode)
105 :inherit variable-pitch))) 138(minibuffer-electric-default-mode)
106 ;; Emojis 139
107 (cl-loop with ffl = (font-family-list) 140(scroll-bar-mode -1)
108 for font in '("Noto Emoji" "Noto Color Emoji" 141(menu-bar-mode -1)
109 "Segoe UI Emoji" "Apple Color Emoji" 142
110 "FreeSans" "FreeMono" "FreeSerif" 143(add-hook 'prog-mode-hook #'indent-tabs-mode-maybe)
111 "Unifont" "Symbola") 144
112 if (member font ffl) 145(setopt electric-pair-skip-whitespace 'chomp)
113 do (set-fontset-font t 'symbol font)) 146(electric-pair-mode)
114 ;; International scripts 147
115 (cl-loop with ffl = (font-family-list) 148(setopt sh-basic-offset tab-width)
116 for (charset . font) 149
117 in '((latin . "Noto Sans") 150(keymap-set emacs-lisp-mode-map "C-c C-c" #'eval-defun)
118 (han . "Noto Sans CJK SC Regular") 151(keymap-set emacs-lisp-mode-map "C-c C-k" #'eval-buffer)
119 (kana . "Noto Sans CJK JP Regular") 152(keymap-set lisp-interaction-mode-map "C-c C-c" #'eval-defun)
120 (hangul . "Noto Sans CJK KR Regular") 153(keymap-set lisp-interaction-mode-map "C-c C-k" #'eval-buffer)
121 (cjk-misc . "Noto Sans CJK KR Regular") 154
122 (khmer . "Noto Sans Khmer") 155(advice-add 'indent-region :around #'call-with-region-or-buffer)
123 (lao . "Noto Sans Lao") 156(advice-add 'tabify :around #'call-with-region-or-buffer)
124 (burmese . "Noto Sans Myanmar") 157(advice-add 'untabify :around #'call-with-region-or-buffer)
125 (thai . "Noto Sans Thai") 158
126 (ethiopic . "Noto Sans Ethiopic") 159(with-eval-after-load 'scheme
127 (hebrew . "Noto Sans Hebrew") 160 (keymap-unset scheme-mode-map "M-o" t)
128 (arabic . "Noto Sans Arabic") 161 ;; Comparse "keywords" --- CHICKEN (http://wiki.call-cc.org/eggref/5/comparse)
129 (gujarati . "Noto Sans Gujarati") 162 (put 'sequence* 'scheme-indent-function 1)
130 (devanagari . "Noto Sans Devanagari") 163 (put 'satisfies 'scheme-indent-function 1)
131 (kannada . "Noto Sans Kannada") 164 (add-hook 'scheme-mode-hook #'geiser-mode))
132 (malayalam . "Noto Sans Malayalam") 165(with-eval-after-load 'geiser-mode
133 (oriya . "Noto Sans Oriya") 166 (keymap-set geiser-mode-map "C-c C-k" #'geiser-eval-buffer-and-go)
134 (sinhala . "Noto Sans Sinhala") 167 (keymap-unset geiser-mode-map "C-." t))
135 (tamil . "Noto Sans Tamil") 168
136 (telugu . "Noto Sans Telugu") 169(setopt visual-fill-column-center-text t
137 (tibetan . "Noto Sans Tibetan")) 170 visual-fill-column-width (+ fill-column 2))
138 if (member font ffl) 171(advice-add 'text-scale-adjust :after #'visual-fill-column-adjust)
139 do (set-fontset-font t charset font)))) 172(add-hook 'visual-line-mode-hook #'visual-fill-column-mode)
140 (unless (daemonp) 173(add-hook 'visual-line-mode-hook #'adaptive-wrap-prefix-mode)
141 (run-with-idle-timer 1 nil #'first-frame@set-fonts))) 174
142 175(setopt major-mode
143(use-package text-mode 176 (lambda () ; guess major mode from buffer name
144 :config 177 (unless buffer-file-name
145 (add-hook 'text-mode-hook #'abbrev-mode)) 178 (let ((buffer-file-name (buffer-name)))
146 179 (set-auto-mode)))))
147(use-package prog-mode 180
148 :config 181;; Dialogs
149 ;;; TABS 182(unless (boundp 'use-short-answers)
150 (setq tab-width 8 183 (fset 'yes-or-no-p 'y-or-n-p))
151 sh-indentation tab-width 184
152 ) 185(setopt read-answer-short t
153 ;;; Hooks 186 use-dialog-box nil
154 (add-hook 'prog-mode-hook #'auto-fill-mode) 187 use-file-dialog nil
155 (add-hook 'prog-mode-hook 188 use-short-answers t)
156 (defun prog@indent-tabs-maybe () 189
157 (indent-tabs-mode 190(require 'savehist)
158 (if (derived-mode-p 'emacs-lisp-mode 191(setopt history-length 1024
159 'lisp-mode 192 history-delete-duplicates t
160 'scheme-mode 193 ;; savehist-file (etc/ "savehist.el")
161 'python-mode 194 savehist-save-minibuffer-history t
162 'haskell-mode) 195 savehist-autosave-interval 30)
163 -1 1)))) 196(savehist-mode)
164 (global-prettify-symbols-mode)) 197
165 198;; Killing and yanking
166(use-package auth-source 199(setopt kill-do-not-save-duplicates t
167 :config 200 kill-read-only-ok t
168 (setq auth-sources '(default "secrets:passwords")) 201 ;; XXX: This setting causes an error message the first time it's
169 (add-hook 'auth-info-hook #'truncate-lines-local-mode)) 202 ;; called: "Selection owner couldn't convert: TIMESTAMP". I have
170 203 ;; absolutely no idea why I get this error, but it's generated in
171(use-package fringe 204 ;; `x_get_foreign_selection'. I also can't inhibit the message or
172 :config 205 ;; do anything else with it, so for now, I'll just live with the
173 (fringe-mode '(nil . 0))) 206 ;; message.
174 207 save-interprogram-paste-before-kill t
175(use-package ispell 208 yank-pop-change-selection t)
176 :config 209(delete-selection-mode)
177 (setq ispell-program-name (choose-executable "aspell" "ispell")) 210
178 ;; (add-hook 'before-save-hook 211;; Notifying the user
179 ;; #'+ispell-move-buffer-words-to-dir-locals-hook) 212(setopt echo-keystrokes 0.01
180 (put 'ispell-buffer-session-localwords 'safe-local-variable 213 ring-bell-function #'ignore)
181 '+ispell-safe-local-p)) 214
182 215;; Point and mark
183(use-package dired 216(setopt set-mark-command-repeat-pop t)
184 :bind (("C-x C-j" . dired-jump) 217
185 ([remap list-directory] . dired) 218;; The system
186 :map dired-mode-map 219(setopt read-process-output-max (* 10 1024 1024))
187 ("C-j" . dired-up-directory) 220
188 ("<backspace>" . dired-up-directory)) 221;; Startup
189 :config 222(setopt inhibit-startup-screen t
190 (require 'dired-x) 223 initial-buffer-choice t
191 (setq dired-recursive-copies 'always 224 initial-scratch-message nil)
192 dired-recursive-deletes 'always 225
193 dired-create-destination-dirs 'always 226(define-advice startup-echo-area-message (:override ())
194 dired-do-revert-buffer t 227 (if (get-buffer "*Warnings*")
195 dired-hide-details-hide-symlink-targets nil 228 ";_;"
196 dired-isearch-filenames 'dwim 229 "^_^"))
197 delete-by-moving-to-trash t 230
198 dired-auto-revert-buffer t 231;; Text editing
199 dired-listing-switches "-AlFhv --group-directories-first" 232(setopt fill-column 80
200 ls-lisp-dirs-first t 233 sentence-end-double-space nil
201 dired-ls-F-marks-symlinks t 234 tab-width 8
202 dired-clean-confirm-killing-deleted-buffers nil 235 tab-always-indent 'complete)
203 dired-no-confirm '(byte-compile 236(global-so-long-mode)
204 load chgrp chmod chown 237
205 copy move hardlink symlink 238(setopt show-paren-delay 0.01
206 shell touch) 239 show-paren-style 'parenthesis
207 dired-dwim-target t) 240 show-paren-when-point-in-periphery t
208 (add-hook 'dired-mode-hook #'dired-hide-details-mode) 241 show-paren-when-point-inside-paren t)
209 (add-hook 'dired-mode-hook #'hl-line-mode) 242(show-paren-mode)
210 (add-hook 'dired-mode-hook #'truncate-lines-local-mode)) 243
211 244
212(use-package dictionary 245;; Encodings
213 :custom 246(set-language-environment "UTF-8")
214 (dictionary-server (if (or (executable-find "dictd") 247(setopt buffer-file-coding-system 'utf-8-unix
215 (file-exists-p "/usr/sbin/dictd")) ; debian 248 coding-system-for-read 'utf-8-unix
216 "localhost" 249 coding-system-for-write 'utf-8-unix
217 "dict.org")) 250 default-process-coding-system '(utf-8-unix . utf-8-unix)
218 :bind 251 locale-coding-system 'utf-8-unix)
219 (("C-c w d" . dictionary-search)) 252(set-charset-priority 'unicode)
220 :config 253(prefer-coding-system 'utf-8-unix)
221 (setf (alist-get "\\*Dictionary\\*" display-buffer-alist nil nil #'equal) 254(set-default-coding-systems 'utf-8-unix)
222 '(display-buffer-in-side-window 255(set-terminal-coding-system 'utf-8-unix)
223 (window-width . 80) 256(set-keyboard-coding-system 'utf-8-unix)
224 (side . right)))) 257(pcase system-type
225 258 ((or 'ms-dos 'windows-nt)
226(use-package calendar 259 (set-clipboard-coding-system 'utf-16-le)
227 :custom 260 (set-selection-coding-system 'utf-16-le))
228 (diary-file (private/ "diary"))) 261 (_
229 262 (set-selection-coding-system 'utf-8)
230(use-package mouse 263 (set-clipboard-coding-system 'utf-8)))
231 :config 264
232 (setq context-menu-functions '(context-menu-undo 265
233 context-menu-region 266;; Files
234 context-menu-middle-separator 267(setopt auto-revert-verbose nil
235 context-menu-local 268 global-auto-revert-non-file-buffers t
236 context-menu-minor)) 269 create-lockfiles nil
237 (context-menu-mode)) 270 find-file-visit-truename t
238 271 mode-require-final-newline t
239(use-package password-cache 272 view-read-only t
240 :config 273 save-silently t)
241 (setq password-cache t 274(global-auto-revert-mode)
242 password-cache-expiry 3600)) 275
243 276(setopt auto-save-default nil
244(use-package time 277 auto-save-interval 1
245 :config 278 auto-save-no-message t
246 (setq display-time-format " %H:%M" 279 auto-save-timeout 1
247 display-time-interval 60 280 auto-save-visited-interval 1
248 display-time-use-mail-icon t 281 remote-file-name-inhibit-auto-save-visited t)
249 display-time-mail-function 282(add-to-list 'auto-save-file-name-transforms
250 (defun +notmuch-new-mail-p () 283 `(".*" ,(locate-user-emacs-file "auto-save/") t))
251 (plist-get (cl-find "inbox+unread" 284(auto-save-visited-mode)
252 (ignore-errors 285
253 (notmuch-hello-query-counts notmuch-saved-searches)) 286(setopt backup-by-copying t
254 :key (lambda (l) (plist-get l :name)) 287 version-control t
255 :test #'equal) 288 kept-new-versions 8
256 :count)) 289 kept-old-versions 8
257 display-time-default-load-average nil) 290 delete-old-versions t)
258 (with-eval-after-load 'notmuch 291(setq-default backup-directory-alist
259 (add-hook 'notmuch-after-tag-hook #'display-time-update)) 292 `(("^/dev/shm" . nil)
260 ;; (display-time-mode) 293 ("^/tmp" . nil)
261 ) 294 (,(getenv "XDG_RUNTIME_DIR") . nil)
262 295 ("." . ,(locate-user-emacs-file "backup"))))
263(use-package tab-bar 296
264 :config 297(require 'recentf)
265 (setq tab-bar-show t 298(setopt
266 tab-bar-close-button-show t) 299 recentf-max-menu-items 500
267 (setopt tab-bar-format 300 recentf-max-saved-items nil ; Save the whole list
268 `(tab-bar-format-history 301 recentf-auto-cleanup 'mode
269 tab-bar-format-tabs 302 recentf-case-fold-search t)
270 tab-bar-separator 303;; (add-to-list 'recentf-exclude etc/)
271 tab-bar-format-add-tab 304(add-to-list 'recentf-exclude "-autoloads.el\\'")
272 tab-bar-format-align-right 305(add-hook 'buffer-list-update-hook #'recentf-track-opened-file)
273 ,(defun tab-bar-extra-info () 306(add-hook 'after-save-hook #'recentf-save-list)
274 `((global menu-item 307(recentf-mode)
275 ,(format-mode-line 308
276 '((jabber-activity-mode jabber-activity-mode-string) 309(require 'saveplace)
277 (:eval (when (and (fboundp 'org-clocking-p) 310(setopt
278 (org-clocking-p)) 311 save-place-forget-unreadable-files (eq system-type
279 (format " %s" 312 'gnu/linux))
280 (truncate-string-to-width 313(save-place-mode)
281 org-mode-line-string 314
282 16 315(require 'uniquify)
283 nil 316(setq uniquify-after-kill-buffer-p t
284 nil 317 uniquify-buffer-name-style 'forward
285 (truncate-string-ellipsis))))) 318 uniquify-ignore-buffers-re "^\\*"
286 (:eval (tmr-mode-line)) 319 uniquify-separator path-separator)
287 (display-time-mode 320
288 (:eval (format " %s" (string-trim display-time-string)))) 321(setq-local vc-follow-symlinks t
289 ("" " "))) 322 vc-make-backup-files t)
290 ignore)))) 323
291 mode-line-misc-info (cl-delete-if (lambda (x) 324;; Whitespace
292 (eq (car x) 'global-mode-string)) 325(require 'whitespace)
293 mode-line-misc-info)) 326(setopt whitespace-style
294 (if (daemonp) 327 '(face trailing tabs tab-mark))
295 (add-hook 'server-after-make-frame-hook 328(global-whitespace-mode)
296 (defun after-frame@tab-bar () 329(add-hook 'before-save-hook #'delete-trailing-whitespace-except-current-line)
297 (tab-bar-mode) 330
298 (remove-hook 'server-after-make-frame-hook 331;; Native compilation
299 #'after-frame@tab-bar))) 332(setopt native-comp-async-report-warnings-errors 'silent
300 (run-with-idle-timer 2 nil #'tab-bar-mode))) 333 native-comp-deferred-compilation t
301 334 native-compile-target-directory
302(use-package info 335 (locate-user-emacs-file "eln"))
303 :preface 336(when (boundp 'native-comp-eln-load-path)
304 (defun Info-copy-current-node-name-0 () 337 (add-to-list 'native-comp-eln-load-path native-compile-target-directory))
305 "Call `Info-copy-current-node-name' with a 0 prefix arg." 338(when (fboundp 'startup-redirect-eln-cache)
306 (interactive) 339 (startup-redirect-eln-cache native-compile-target-directory))
307 (Info-copy-current-node-name 0)) 340
308 :bind (:map Info-mode-map 341(global-goto-address-mode)
309 ("w" . Info-copy-current-node-name-0) 342
310 ("c" . Info-copy-current-node-name))) 343;; Winner
311 344(winner-mode)
312(use-package make-mode 345
313 :defer t 346;;; Hooks
314 :config 347(add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p)
315 (add-hook 'makefile-mode-hook 348(add-hook 'find-file-not-found-functions #'create-missing-directories)
316 (defun make-mode@setup () 349(add-hook 'find-file-hook #'vc-remote-off)
317 (remove-hook 'write-file-functions 350(add-hook 'dired-mode-hook #'hl-line-mode)
318 #'makefile-warn-suspicious-lines t) 351(add-hook 'org-agenda-mode-hook #'hl-line-mode)
319 (remove-hook 'write-file-functions 352
320 #'makefile-warn-continuations t)))) 353;;; Tab bar
321 354
322(use-package eglot 355(defun tab-bar-end-space ()
323 :preface 356 `((end menu-item " " ignore)))
324 (defun +eglot-eldoc () 357
325 ;; https://www.masteringemacs.org/article/seamlessly-merge-multiple-documentation-sources-eldoc 358(setopt tab-bar-show t)
326 (setq eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly)) 359(add-to-list 'tab-bar-format 'tab-bar-format-align-right :append)
327 :hook 360(add-to-list 'tab-bar-format 'tab-bar-format-global :append)
328 ((bash-ts-mode . eglot-ensure) 361(add-to-list 'tab-bar-format 'tab-bar-end-space :append)
329 (scheme-mode . eglot-ensure)) 362(tab-bar-mode)
330 :config 363
331 (add-to-list 'eglot-server-programs 364;;; Org mode
332 '(scheme-mode . ("chicken-lsp-server"))) 365
333 (add-hook 'eglot-managed-mode #'+eglot-eldoc)) 366(keymap-global-set "C-c a" #'org-agenda)
334 367(setopt org-clock-clocked-in-display 'frame-title
335(use-package eldoc 368 org-clock-frame-title-format
336 :config 369 '("%b" " - " (t org-mode-line-string)))
337 (setq eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly) 370
338 (setf (alist-get "^\\*eldoc for" display-buffer-alist nil nil #'equal) 371;;; Spelling
339 '(display-buffer-at-bottom 372
340 (window-height . 4))) 373(defun list-of-strings-p (x)
341 (eldoc-add-command-completions "paredit-")) 374 "Is X a list of strings?"
342 375 (and x
343(use-package pulse 376 (listp x)
344 :config 377 (cl-every #'stringp x)))
345 (setq pulse-flag nil 378
346 pulse-delay 1 379(put 'ispell-local-words 'safe-local-variable
347 pulse-iterations 1)) 380 'list-of-strings-p)
348 381
349(use-package flyspell 382(add-hook 'text-mode-hook #'jinx-mode)
350 :hook (org-mode-hook)) 383(with-eval-after-load 'jinx
351 384 (keymap-set jinx-mode-map "M-$" #'jinx-correct)
352;; (use-package display-fill-column-indicator 385 (keymap-set jinx-mode-map "C-M-$" #'jinx-languages))
353;; :hook (prog-mode-hook)) 386
354 387;;; Copy rich text to the keyboard
355(use-package package 388
356 :config 389;; Thanks to Oleh Krehel:
357 (defun package-update-async-in-progress (&rest _) 390;; https://emacs.stackexchange.com/questions/54292/copy-results-of-org-export-directly-to-clipboard
358 (message "Package async update in progress.")) 391;; So. Emacs can't do this itself because it doesn't support sending clipboard
359 392;; or selection contents as text/html. We have to use xclip instead.
360 (defun package-update-all-async () 393;; (defun org-to-html-to-clipboard (&rest org-export-args)
361 "Update packages asyncronously." 394;; "Export current org buffer to HTML, then copy it to the clipboard.
362 (interactive) 395;; ORG-EXPORT-ARGS are passed to `org-export-to-file'."
363 (let ((message "Package update (async)...") 396;; (let ((f (make-temp-file "org-html-export")))
364 (disable-fns '(package-update 397;; (apply #'org-export-to-file 'html f org-export-args)
365 package-update-all 398;; (start-process "xclip" " *xclip*"
366 package-update-all-async))) 399;; "xclip" "-verbose" "-i" f
367 (dolist (fn disable-fns) 400;; "-t" "text/html" "-selection" "clipboard")
368 (advice-add fn :override #'package-update-async-in-progress)) 401;; (message "HTML pasted to clipboard.")))
369 (message "%s" message) 402
370 (unwind-protect 403;; Wayland version.. TODO: make it work for both
371 (async-start 404(defun org-to-html-to-clipboard (&rest org-export-args)
372 `(lambda () 405 "Export current org buffer to HTML, then copy it to the clipboard.
373 (package-initialize) 406ORG-EXPORT-ARGS are passed to `org-export-to-file'."
374 (package-update-all)) 407 (let ((buf (generate-new-buffer "*org-html-clipboard*" t)))
375 `(lambda (result) 408 (apply #'org-export-to-buffer 'html buf org-export-args)
376 (message "%s %s" ,message result))) 409 (with-current-buffer buf
377 (dolist (fn ',disable-fns) 410 (call-process-region (point-min) (point-max)
378 (advice-remove fn 'package-update-async-in-progress)))))) 411 "wl-copy" nil nil nil
379 412 "-t" "text/html")
380(use-package ielm 413 (kill-buffer-and-window))
381 ;; https://www.n16f.net/blog/making-ielm-more-comfortable/ 414 (message "HTML copied to clipboard.")))
382 :preface 415
383 (defun +ielm-init-history () 416(defun org-subtree-to-html-to-clipboard ()
384 (let ((path (etc/ "ielm/history" t))) 417 "Export current subtree to HTML."
385 (setq-local comint-input-ring-file-name path)) 418 (interactive)
386 (setq-local comint-input-ring-size 10000) 419 (org-to-html-to-clipboard nil :subtree))
387 (setq-local comint-input-ignoredups t) 420
388 (ignore-errors (comint-read-input-ring))) 421(undohist-initialize)
389 (defun +ielm-write-history (&rest _args) 422
390 (with-file-modes #o600 423(require 'hungry-delete)
391 (comint-write-input-ring))) 424(setopt hungry-delete-chars-to-skip " \t"
392 (defun +ielm (&optional buf-name)
393 "Interactively evaluate Emacs Lisp expressions.
394Switches to the buffer named BUF-NAME if provided (`*ielm*' by default),
395or creates it if it does not exist.
396See `inferior-emacs-lisp-mode' for details."
397 (interactive)
398 (let (old-point
399 (buf-name (or buf-name "*ielm*")))
400 (unless (comint-check-proc buf-name)
401 (with-current-buffer (get-buffer-create buf-name)
402 (unless (zerop (buffer-size)) (setq old-point (point)))
403 (inferior-emacs-lisp-mode)))
404 (pop-to-buffer buf-name)
405 (when old-point (push-mark old-point))))
406 :bind (:map emacs-lisp-mode-map
407 ("C-c C-z" . +ielm))
408 :config
409 (add-hook 'ielm-mode-hook #'eldoc-mode)
410 (add-hook 'ielm-mode-hook #'+ielm-init-history)
411 (advice-add 'ielm-send-input :after #'+ielm-write-history))
412
413(use-package elec-pair
414 :config
415 (setopt electric-pair-skip-whitespace 'chomp)
416 (electric-pair-mode))
417
418(use-package bookmark
419 :config
420 (setopt bookmark-save-flag 1))
421
422(use-package sh-script
423 :config
424 (sh-electric-here-document-mode -1))
425
426(use-package cc-mode
427 :config
428 (setopt c-basic-offset 8))
429
430
431;;; Applications
432
433(use-package acdw-mail
434 :load-path "lisp/"
435 :demand t
436 :bind (("C-c n" . +notmuch-goto)))
437
438(use-package acdw-org
439 :load-path "lisp/"
440 :config
441 (global-set-key [f8] #'org-clock-out))
442
443(use-package acdw-shell
444 :load-path "lisp/")
445
446(use-package acdw-web
447 :load-path "lisp/")
448
449(use-package acdw-chat
450 :load-path "lisp/")
451
452(use-package _work
453 :load-path "~/sync/emacs/private/")
454
455
456;;; Locally-developed packages
457
458(use-package +scratch
459 :load-path "lisp/"
460 :config
461 (setq +scratch-save-dir (sync/ "emacs/scratch.d/" t))
462 (add-hook 'kill-buffer-query-functions #'+scratch@immortal)
463 (add-hook 'kill-emacs-hook #'+scratch-save-on-exit)
464 (with-current-buffer (get-scratch-buffer-create)
465 (local-set-key (kbd "C-x C-s") #'+scratch-save))
466 ;; Save *scratch* every hour
467 (run-at-time t (* 60 60) #'+scratch-save "%FT%H%z")
468 ;; Clean old *scratch* saves every day
469 (run-at-time t (* 60 60 24) #'+scratch-clean))
470
471(use-package pulse-location
472 :load-path "~/src/pulse-location.el/"
473 :config
474 (pulse-location-mode))
475
476(use-package emacs ; `modus-themes' isn't a package ...
477 :config
478 (setopt modus-themes-mixed-fonts t)
479 (add-hook 'modus-themes-after-load-theme-hook
480 (defun +reset-faces ()
481 (dolist (face '(font-lock-regexp-face
482 font-lock-variable-name-face
483 font-lock-preprocessor-face
484 font-lock-remove-face
485 font-lock-delimiter-face
486 font-lock-label-face
487 font-lock-operator-face
488 font-lock-property-face
489 font-lock-builtin-face
490 font-lock-number-face
491 font-lock-set-face
492 font-lock-warning-face
493 font-lock-punctuation-face
494 font-lock-constant-face
495 font-lock-type-face
496 font-lock-function-name-face
497 font-lock-reference-face
498 font-lock-negation-char-face
499 font-lock-misc-punctuation-face
500 font-lock-escape-face
501 font-lock-bracket-face))
502 (face-spec-set face '((t :foreground unspecified
503 :background unspecified))))
504 (face-spec-set 'font-lock-keyword-face
505 '((t :foreground unspecified
506 :background unspecified
507 :weight bold)))
508 (face-spec-set 'font-lock-doc-face
509 '((t :slant italic)))))
510 (add-hook 'after-init-hook
511 (defun modus@load ()
512 (+reset-faces)
513 (pcase (string-trim (shell-command-to-string "darkman get"))
514 ("light" (load-theme 'modus-operandi t))
515 ("dark" (load-theme 'modus-vivendi t))))))
516
517(use-package electric-cursor
518 :load-path "~/src/electric-cursor.el/"
519 :config
520 (setq electric-cursor-alist '((overwrite-mode . box)
521 (t . bar)))
522 (electric-cursor-mode))
523
524(use-package mode-line-bell
525 :load-path "~/src/mode-line-bell.el/"
526 :config
527 (setq mode-line-bell-flash-time 0.25)
528 (mode-line-bell-mode))
529
530(use-package titlecase
531 :load-path "~/src/titlecase.el/"
532 :preface
533 (defun +titlecase-sentence-style-dwim (&optional arg)
534 "Titlecase a sentence.
535With prefix ARG, toggle the value of
536`titlecase-downcase-sentences' before sentence-casing."
537 (interactive "P")
538 (let ((titlecase-downcase-sentences (if arg (not titlecase-downcase-sentences)
539 titlecase-downcase-sentences)))
540 (titlecase-dwim 'sentence)))
541 (defun +titlecase-org-headings ()
542 (interactive)
543 (require 'org)
544 (save-excursion
545 (goto-char (point-min))
546 ;; See also `org-map-tree'. I'm not using that function because I want to
547 ;; skip the first headline. A better solution would be to patch
548 ;; `titlecase-line' to ignore org-mode metadata (TODO cookies, tags, etc).
549 (let ((level (funcall outline-level))
550 (org-special-ctrl-a/e t))
551 (while (and (progn (outline-next-heading)
552 (> (funcall outline-level) level))
553 (not (eobp)))
554 (titlecase-region (progn (org-beginning-of-line) (point))
555 (progn (org-end-of-line) (point)))))))
556 :config
557 (with-eval-after-load 'scule
558 (keymap-set scule-map "M-t" #'titlecase-dwim)))
559
560(use-package scule
561 :load-path "~/src/scule.el/"
562 :bind-keymap ("M-c" . scule-map)
563 :init
564 ;; Use M-u for prefix keys
565 (keymap-global-set "M-u" #'universal-argument)
566 (keymap-set universal-argument-map "M-u" #'universal-argument-more))
567
568(use-package filldent
569 :load-path "~/src/filldent.el/"
570 :bind ("M-q" . filldent-dwim)
571 :config
572 (setq filldent-fill-modes '(web-mode)))
573
574(use-package frowny
575 :load-path "~/src/frowny.el/"
576 :config
577 (global-frowny-mode))
578
579(use-package keepassxc-shim
580 :load-path "~/src/keepassxc-shim.el/"
581 :config
582 (keepassxc-shim-activate))
583
584(use-package hippie-completing-read
585 :load-path "~/src/hippie-completing-read.el/"
586 :bind (("M-/" . hippie-completing-read)))
587
588
589;;; External packages
590
591(use-package async
592 :ensure t
593 :config
594 ;; https://github.com/jwiegley/emacs-async/issues/64
595 ;; (setq message-send-mail-function #'async-smtpmail-send-it)
596 (dired-async-mode)
597 (async-bytecomp-package-mode))
598
599(use-package trashed
600 :ensure t)
601
602(use-package form-feed
603 :ensure t
604 :hook (prog-mode-hook))
605
606;; (use-package clean-kill-ring
607;; :vc (:url "https://github.com/NicholasBHubbard/clean-kill-ring.el")
608;; :config
609;; (setq clean-kill-ring-prevent-duplicates t)
610;; (clean-kill-ring-mode))
611
612(use-package minions
613 :ensure t
614 :config (minions-mode))
615
616(use-package visual-fill-column
617 :preface
618 (defcustom visual-fill-column-widen-amount 4
619 "Amount to widen `fill-column' by in `visual-fill-column-mode'."
620 :type 'natnum
621 :group 'visual-fill-column)
622 (defun visual-fill-column--widen/narrow-handle-arg (cols)
623 (cond
624 ((null cols) visual-fill-column-widen-amount)
625 ((listp cols) (* visual-fill-column-widen-amount
626 (1+ (/ (car cols) 4))))
627 ((eq '- cols) (- visual-fill-column-widen-amount))
628 (:else cols)))
629 (defun visual-fill-column-widen (&optional cols)
630 "Widen `fill-column' by COLS, and re-display.
631If COLS is missing or nil, widen by
632`visual-fill-column-widen-amount'. When called with a plain
633\\[universal-argument], multiply that amount by 1 + the amount of
634\\[universal-argument]s. If called with a numerical prefix
635argument, widen by that number of columns."
636 (interactive "P")
637 (let ((cols (visual-fill-column--widen/narrow-handle-arg cols)))
638 (cl-incf fill-column cols)
639 (visual-fill-column-adjust)
640 (message "Fill-column: %s" fill-column)))
641 (defun visual-fill-column-narrow (&optional cols)
642 "Narrow `fill-column' by COLS, then redisplay.
643The prefix argument is as in `visual-fill-column-widen' but negated."
644 (interactive "P")
645 (let ((cols (visual-fill-column--widen/narrow-handle-arg cols)))
646 (cl-decf fill-column cols)
647 (visual-fill-column-adjust)
648 (message "Fill-column: %s" fill-column)))
649 :ensure t
650 :config
651 (setopt visual-fill-column-center-text t
652 visual-fill-column-extra-text-width '(3 . 3)
653 visual-fill-column-width (+ fill-column 4))
654 (keymap-set toggle-map "v" #'visual-fill-column-mode)
655 (keymap-set visual-fill-column-mode-map "C-x C->" #'visual-fill-column-widen)
656 (keymap-set visual-fill-column-mode-map "C-x C-<" #'visual-fill-column-narrow)
657 (add-hook 'visual-fill-column-mode-hook #'visual-line-mode)
658 (add-hook 'eww-mode-hook #'visual-fill-column-mode)
659 (advice-add 'text-scale-adjust :after #'visual-fill-column-adjust))
660
661(use-package mlscroll
662 :ensure t :defer 1
663 :after modus-themes
664 :preface
665 (define-advice load-theme (:after (&rest _) mlscroll)
666 (mlscroll-mode -1)
667 (when (seq-intersection '(modus-vivendi modus-operandi)
668 custom-enabled-themes)
669 (modus-themes-with-colors
670 (setq mlscroll-in-color fg-dim
671 mlscroll-out-color bg-inactive)))
672 (run-with-idle-timer 1 nil #'mlscroll-mode))
673 :config
674 (load-theme@mlscroll))
675
676(use-package cape
677 :ensure t
678 :config
679 (add-hook 'completion-at-point-functions #'cape-file 90)
680 (add-hook 'completion-at-point-functions #'cape-dabbrev 91)
681 (advice-add 'emacs-completion-at-point
682 :around #'cape-wrap-nonexclusive))
683
684(use-package wgrep
685 :ensure t
686 :config
687 (setq wgrep-enable-key (kbd "C-x C-q"))
688 :bind (:map grep-mode-map
689 ("C-x C-q" . wgrep-change-to-wgrep-mode)))
690
691(use-package avy
692 :ensure t
693 :bind (("M-j" . avy-goto-char-timer)
694 :map isearch-mode-map
695 ("M-j" . avy-isearch))
696 :config
697 (setq avy-background t
698 avy-keys (string-to-list "asdfghjklqwertyuiopzxcvbnm")))
699
700(use-package zzz-to-char
701 :ensure t
702 :bind (("M-z" . zzz-to-char)))
703
704(use-package anzu
705 :ensure t
706 :bind (("M-%" . anzu-query-replace-regexp)
707 ("C-M-%" . anzu-query-replace)))
708
709(use-package isearch-mb
710 :ensure t
711 :config
712 (setq isearch-lazy-count t
713 isearch-regexp-lax-whitespace t
714 search-whitespace-regexp "\\W+"
715 search-default-mode t ; Search regexp by default
716 isearch-wrap-pause 'no)
717 (define-advice isearch-cancel (:before (&rest _) add-search-to-history)
718 "Add search string to history when canceling."
719 (unless (equal "" isearch-string)
720 (isearch-update-ring isearch-string isearch-regexp)))
721 (define-advice perform-replace (:around (orig &rest r) no-anykey-exit)
722 "Don't exit replace for any key that's not in `query-replace-map'."
723 (save-window-excursion
724 (cl-letf* ((lookup-key-orig (symbol-function 'lookup-key))
725 ((symbol-function 'lookup-key)
726 (lambda (map key &optional accept-default)
727 (or (apply lookup-key-orig map key accept-default)
728 (when (eq map query-replace-map) 'help)))))
729 (apply orig r))))
730 ;; Consult
731 (autoload 'consult-line "consult" nil t)
732 (autoload 'consult-isearch-history "consult" nil t)
733 (add-to-list 'isearch-mb--after-exit #'consult-line)
734 (add-to-list 'isearch-mb--with-buffer #'consult-isearch-history)
735 (keymap-set isearch-mb-minibuffer-map "M-s l" #'consult-line)
736 (keymap-set isearch-mb-minibuffer-map "M-r" #'consult-isearch-history)
737 ;; Anzu
738 (autoload 'anzu-isearch-query-replace "anzu" nil t)
739 (autoload 'anzu-isearch-query-replace-regexp "anzu" nil t)
740 (add-to-list 'isearch-mb--after-exit #'anzu-isearch-query-replace)
741 (add-to-list 'isearch-mb--after-exit #'anzu-isearch-query-replace-regexp)
742 (keymap-set isearch-mb-minibuffer-map
743 "M-%" #'anzu-isearch-query-replace-regexp)
744 (keymap-set isearch-mb-minibuffer-map
745 "C-M-%" #'anzu-isearch-query-replace)
746 (isearch-mb-mode))
747
748;; (use-package paredit
749;; :ensure t
750;; :hook ( emacs-lisp-mode-hook ielm-mode-hook
751;; eval-expression-minibuffer-setup-hook
752;; lisp-interaction-mode-hook
753;; lisp-mode-hook scheme-mode-hook
754;; fennel-mode-hook fennel-repl-mode-hook
755;; geiser-mode-hook geiser-repl-mode-hook)
756;; :config
757;; (keymap-set paredit-mode-map "C-j"
758;; (defun +paredit-newline ()
759;; (interactive)
760;; (call-interactively
761;; (if (derived-mode-p 'lisp-interaction-mode)
762;; #'eval-print-last-sexp #'paredit-newline))))
763;; (keymap-unset paredit-mode-map "RET" t)
764;; (keymap-unset paredit-mode-map "M-s" t)
765;; (keymap-unset paredit-mode-map "M-r" t)
766;; (add-to-list 'paredit-space-for-delimiter-predicates
767;; (defun paredit@dont-space-@ (endp delimiter)
768;; "Don't add a space after @ in `paredit-mode'."
769;; (let ((point (point)))
770;; (or endp
771;; (seq-every-p
772;; (lambda (prefix)
773;; (and (> point (length prefix))
774;; (let ((start (- point (length prefix)))
775;; (end point))
776;; (not (string= (buffer-substring start end)
777;; prefix)))))
778;; ;; Add strings to this list to inhibit adding a space
779;; ;; after them.
780;; '(",@")))))))
781
782(use-package hungry-delete
783 :ensure t
784 :config
785 (setq hungry-delete-chars-to-skip " \t"
786 hungry-delete-skip-regexp (format "[%s]" hungry-delete-chars-to-skip) 425 hungry-delete-skip-regexp (format "[%s]" hungry-delete-chars-to-skip)
787 hungry-delete-join-reluctantly nil) 426 hungry-delete-join-reluctantly nil)
788 (add-to-list 'hungry-delete-except-modes 'eshell-mode) 427(add-to-list 'hungry-delete-except-modes 'eshell-mode)
789 (add-to-list 'hungry-delete-except-modes 'nim-mode) 428(add-to-list 'hungry-delete-except-modes 'nim-mode)
790 (add-to-list 'hungry-delete-except-modes 'python-mode) 429(add-to-list 'hungry-delete-except-modes 'python-mode)
791 ;; Keys 430(global-hungry-delete-mode)
792 (with-eval-after-load 'paredit 431
793 (define-key paredit-mode-map [remap paredit-backward-delete] 432(setopt avy-background t
794 (defun paredit/hungry-delete-backward (arg) 433 avy-keys (string-to-list "asdfghjklqwertyuiopzxcvbnm"))
795 (interactive "*p") 434(keymap-global-set "M-j" #'avy-goto-char-timer)
796 (if (looking-back hungry-delete-skip-regexp) 435(keymap-set isearch-mode-map "M-j" #'avy-isearch)
797 (hungry-delete-backward (or arg 1)) 436(keymap-global-set "M-z" #'zzz-to-char)
798 (paredit-backward-delete arg)))) 437
799 (define-key paredit-mode-map [remap paredit-forward-delete] 438(marginalia-mode)
800 (defun paredit/hungry-delete-forward (arg) 439
801 (interactive "*p") 440(keymap-global-set "C-x b" #'consult-buffer)
802 (if (looking-at hungry-delete-skip-regexp) 441(keymap-global-set "C-x 4 b" #'consult-buffer-other-window)
803 (hungry-delete-forward (or arg 1)) 442(keymap-global-set "C-x 5 b" #'consult-buffer-other-frame)
804 (paredit-forward-delete arg))))) 443(keymap-global-set "C-x r b" #'consult-bookmark)
805 ;; Mode 444(keymap-global-set "M-y" #'consult-yank-pop)
806 (global-hungry-delete-mode)) 445(keymap-global-set "M-g g" #'consult-goto-line)
807 446(keymap-global-set "M-g M-g" #'consult-goto-line)
808(use-package macrostep 447(keymap-global-set "M-g o" #'consult-outline)
809 :ensure t 448(keymap-global-set "M-g m" #'consult-mark)
810 :after elisp-mode 449(keymap-global-set "M-g i" #'consult-imenu)
811 :bind ( :map emacs-lisp-mode-map 450(keymap-global-set "M-s d" #'consult-find)
812 ("C-c e" . macrostep-expand) 451(keymap-global-set "M-s D" #'consult-locate)
813 :map lisp-interaction-mode-map 452(keymap-global-set "M-s g" #'consult-grep)
814 ("C-c e" . macrostep-expand))) 453(keymap-global-set "M-s G" #'consult-git-grep)
815 454(keymap-global-set "M-s r" #'consult-ripgrep)
816(use-package package-lint 455(keymap-global-set "M-s l" #'consult-line)
817 :ensure t) 456(keymap-global-set "M-s k" #'consult-keep-lines)
818 457(keymap-global-set "M-s u" #'consult-focus-lines)
819(use-package sly 458
820 :ensure t 459(keymap-global-set "M-s e" #'consult-isearch-history)
821 :when inferior-lisp-program 460(keymap-set isearch-mode-map "M-e" #'consult-isearch-history)
822 :preface 461(keymap-set isearch-mode-map "M-s e" #'consult-isearch-history)
823 (setq inferior-lisp-program (choose-executable "sbcl")) 462(keymap-set isearch-mode-map "M-s l" #'consult-line)
824 (defun +sly-start-or-mrepl () 463
825 (interactive) 464(keymap-set minibuffer-local-map "M-n" #'consult-history)
826 (if (ignore-errors (sly-connection)) 465(keymap-set minibuffer-local-map "M-p" #'consult-history)
827 (sly-mrepl (lambda (buf) 466
828 (display-buffer-pop-up-window buf nil))) 467(setopt completion-in-region-function #'consult-completion-in-region
829 (call-interactively #'sly))) 468 xref-show-xrefs-function #'consult-xref
830 :config 469 xref-show-definitions-function #'consult-xref)
831 (autoload 'sly-mrepl "sly-mrepl" nil t) 470
832 (keymap-set sly-mode-map "C-c C-z" #'+sly-start-or-mrepl) 471(setopt initial-scratch-message ";;; Emacs!\n\n")
833 (setq sly-net-coding-system 'utf-8-unix) 472
834 (sly-symbol-completion-mode -1)) 473(keymap-global-set "C-x C-b" #'ibuffer)
835 474(add-hook 'ibuffer-hook #'hl-line-mode)
836(use-package pdf-tools
837 :ensure t
838 :mode ("\\.[pP][dD][fF]\\'" . pdf-view-mode)
839 :magic ("%PDF" . pdf-view-mode)
840 :config
841 (pdf-tools-install))
842
843(use-package keychain-environment
844 :ensure t
845 :when (executable-find "keychain")
846 :hook (after-init-hook . keychain-refresh-environment))
847
848(use-package web-mode
849 :ensure t
850 :mode ("\\.phtml\\'"
851 "\\.tpl\\.php\\'"
852 "\\.[agj]sp\\'"
853 "\\.as[cp]x\\'"
854 "\\.erb\\'"
855 "\\.mustache\\'"
856 "\\.djhtml\\'"
857 "\\.html?\\'")
858 :config
859 (add-hook 'web-mode-hook
860 (defun web-mode@setup ()
861 (indent-tabs-mode -1))))
862
863(use-package nginx-mode
864 :ensure t
865 :mode "/nginx/sites-\\(?:available\\|enabled\\)/")
866
867(use-package markdown-mode
868 :ensure t
869 :mode "\\.\\(?:md\\|markdown\\|mkd\\|mdown\\|mkdn\\|mdwn\\)\\'"
870 :config
871 (setq markdown-command (choose-executable
872 '("pandoc" "--from=markdown" "--to=html5")
873 "markdown"))
874 (add-hook 'markdown-mode-hook #'visual-fill-column-mode))
875
876(use-package pandoc-mode
877 :ensure t
878 :hook ((markdown-mode-hook . pandoc-mode)
879 (pandoc-mode-hook . pandoc-load-default-settings)))
880
881(use-package edit-indirect
882 :ensure
883 :bind (("C-c '" . edit-indirect-region)))
884
885(use-package transpose-frame
886 :ensure t
887 :bind (("C-x 5 t" . transpose-frame)
888 ("C-x 5 h" . flop-frame) ; horizontal
889 ("C-x 5 v" . flip-frame) ; vertical
890 ))
891
892(use-package magit
893 :pin melpa-stable
894 :ensure t
895 :bind ("C-x g" . magit))
896
897(use-package git-modes
898 :ensure t)
899
900(use-package eradio
901 :ensure t
902 :preface
903 (defun eradio-toggle|play (&optional arg)
904 "Run `eradio-toggle', or `eradio-play' with prefix ARG."
905 (interactive "P")
906 (if arg (eradio-play) (eradio-toggle)))
907 :bind (("C-c r p" . eradio-toggle|play)
908 ("C-c r s" . eradio-stop))
909 :config
910 (setq eradio-player '("mpv" "--no-video" "--no-terminal")
911 eradio-channels
912 ;; (name . url)
913 '(("Nightwave Plaza" . "http://radio.plaza.one/ogg")
914 ("Radio Paradise - Main Mix" .
915 "http://stream.radioparadise.com/rp_192m.ogg")
916 ("Radio Paradise - Mellow Mix" .
917 "http://stream.radioparadise.com/mellow-96m.ogg")
918 ("Radio Paradise - Rock Mix" .
919 "http://stream.radioparadise.com/rock-96m.ogg")
920 ("Radio Paradise - Global Mix" .
921 "http://stream.radioparadise.com/global-96m.ogg")
922 ("KLSU" . "http://130.39.238.143:8010/stream.mp3"))
923 ;; At some point I should actually ... write this in to this file or
924 ;; something. But until I decide to quit using radish altogether, this
925 ;; what I got.
926 ;; (with-current-buffer (find-file-noselect "~/etc/radish/stations")
927 ;; (let (chans)
928 ;; (dolist (line (string-split (buffer-substring-no-properties
929 ;; (point-min) (point-max))
930 ;; "\n")
931 ;; chans)
932 ;; (unless (string-match-p "^#" line)
933 ;; (let* ((ll (string-split line "\t"))
934 ;; (url (cl-first ll))
935 ;; (name (cl-second ll))
936 ;; (tags (cl-third ll)))
937 ;; (when (and name
938 ;; (string-match-p "[^ \n\t]*://[^ \n\t]*" url))
939 ;; (push (cons (format "%s - %s" name tags) url)
940 ;; chans)))))))
941 ))
942
943(use-package wiki-abbrev
944 :after org ; Don't need abbrevs til I load org.
945 :load-path "~/src/wiki-abbrev.el/"
946 :config
947 (setq wiki-abbrev-file (etc/ "wiki-abbrevs"))
948 (wiki-abbrev-insinuate))
949
950(use-package flyspell-correct
951 :ensure t
952 :preface
953 (defun +flyspell-correct-buffer (&optional prefix)
954 "Run `flyspell-correct-wrapper' on all misspelled words in the buffer.
955With PREFIX, prompt to change the current dictionary."
956 (interactive "P")
957 (flyspell-buffer)
958 (when prefix
959 (let ((current-prefix-arg nil))
960 (call-interactively #'ispell-change-dictionary)))
961 (flyspell-correct-move (point-min) :forward :rapid))
962 :after flyspell
963 :bind (("<f7>" . +flyspell-correct-buffer)
964 (:map flyspell-mode-map
965 ("C-;" . flyspell-correct-wrapper)))
966 :config
967 (setq flyspell-correct--cr-key ";")
968 (keymap-unset flyspell-mode-map "C-," t)
969 (keymap-unset flyspell-mode-map "C-." t))
970
971(use-package dired-subtree
972 :ensure t
973 :after dired
974 :bind (:map dired-mode-map
975 (("TAB" . dired-subtree-cycle)
976 ("i" . dired-subtree-toggle))))
977
978(use-package dired-hide-dotfiles
979 ;; I could maybe use a more general package for this ... see
980 ;; https://emacs.grym.io/#orgbbda609
981 :ensure t
982 :bind (:map dired-mode-map
983 ("." . dired-hide-dotfiles-mode)))
984
985(use-package dired-git-info
986 :ensure t
987 :bind (:map dired-mode-map
988 (")" . dired-git-info-mode))
989 :config
990 (setq dgi-auto-hide-details-p nil))
991
992(use-package expand-region ; needed for embrace anyway
993 :ensure t
994 :bind (("C-=" . er/expand-region)))
995
996(use-package embrace
997 :ensure t
998 :preface
999 (defmacro org-insert-or-embrace (char)
1000 "Define a function to insert CHAR, or `embrace' the region with it."
1001 (let* ((fn-name (intern (format "org-insert-or-embrace-%s" char)))
1002 (char (cond ((characterp char) char)
1003 ((stringp char) (string-to-char char))
1004 (t (user-error "Bad format for char: %S" char)))))
1005 `(defun ,fn-name (n)
1006 ,(format "Insert N %ss, or surround the region with them."
1007 (char-to-string char))
1008 (interactive "p")
1009 (if (region-active-p)
1010 (dotimes (_ n)
1011 (embrace--add-internal (region-beginning) (region-end) ,char)
1012 (forward-char 1))
1013 (self-insert-command n ,char)))))
1014 (with-eval-after-load 'org
1015 (require 'embrace)
1016 (keymap-set org-mode-map "*" (org-insert-or-embrace "*"))
1017 (keymap-set org-mode-map "/" (org-insert-or-embrace "/"))
1018 (keymap-set org-mode-map "_" (org-insert-or-embrace "_"))
1019 (keymap-set org-mode-map "=" (org-insert-or-embrace "="))
1020 (keymap-set org-mode-map "~" (org-insert-or-embrace "~"))
1021 (keymap-set org-mode-map "+" (org-insert-or-embrace "+")))
1022 :bind (("C-'" . embrace-commander))
1023 :hook ((org-mode-hook . embrace-org-mode-hook)
1024 (ruby-mode-hook . embrace-ruby-mode-hook)
1025 (emacs-lisp-mode-hook . embrace-emacs-lisp-mode-hook)
1026 (latex-mode-hook . embrace-LaTeX-mode-hook)))
1027
1028(use-package apheleia
1029 :ensure t
1030 :config
1031 (setq apheleia-hide-log-buffers t)
1032 (setf (alist-get 'shfmt apheleia-formatters)
1033 '("shfmt" "--case-indent"))
1034 (global-set-key (kbd "M-C-\\")
1035 (defun +apheleia-format|indent-buffer ()
1036 (interactive)
1037 (if-let ((formatters (apheleia--get-formatters)))
1038 (apheleia-format-buffer
1039 formatters
1040 (lambda ()
1041 (with-demoted-errors "Apheleia: %s"
1042 (when buffer-file-name
1043 (let ((apheleia--format-after-save-in-progress t))
1044 (apheleia--save-buffer-silently)))
1045 (run-hooks 'apheleia-post-format-hook))))
1046 (indent-region (point-min) (point-max))
1047 (when buffer-file-name
1048 (save-buffer))))))
1049
1050(use-package php-mode
1051 :ensure t)
1052
1053(use-package rec-mode
1054 :ensure t)
1055
1056
1057(use-package geiser
1058 :ensure t
1059 :config
1060 (when (executable-find "guile")
1061 (use-package geiser-guile :ensure t))
1062 (when (executable-find "chicken")
1063 (use-package geiser-chicken :ensure t))
1064 (when (or (prog1 (executable-find "chez")
1065 (setopt geiser-chez-binary
1066 (executable-find "chez")))
1067 (executable-find "petite")
1068 (executable-find "scheme"))
1069 (use-package geiser-chez :ensure t))
1070 (when (executable-find "gambit")
1071 (use-package geiser-gambit :ensure t))
1072 (when (executable-find "chibi-scheme")
1073 (use-package geiser-chibi :ensure t))
1074 (use-package macrostep-geiser
1075 :ensure t
1076 :config
1077 (eval-after-load 'geiser-mode
1078 '(add-hook 'geiser-mode-hook #'macrostep-geiser-setup))
1079 (eval-after-load 'geiser-repl
1080 '(add-hook 'geiser-repl-mode-hook #'macrostep-geiser-setup)))
1081 (with-eval-after-load 'geiser-mode
1082 (keymap-set geiser-mode-map "C-c C-k" #'geiser-eval-buffer-and-go)
1083 (keymap-unset geiser-mode-map "C-." t)
1084 (keymap-unset scheme-mode-map "M-o" t)))
1085
1086(use-package detached
1087 :when (executable-find "dtach")
1088 :ensure t
1089 :init
1090 (add-hook 'after-init-hook #'detached-init)
1091 :bind (([remap async-shell-command] . detached-shell-command)
1092 ([remap compile] . detached-compile)
1093 ([remap recompile] . detached-compile-recompile))
1094 :config
1095 (setf detached-terminal-data-command system-type)
1096 (with-eval-after-load 'consult
1097 (global-set-key [remap detached-open-session] #'detached-consult-session)))
1098
1099(use-package lin
1100 :ensure t
1101 :config
1102 (setq lin-face 'lin-cyan
1103 lin-mode-hooks
1104 '(dired-mode-hook
1105 ;; bongo-mode-hook
1106 ;; elfeed-search-mode-hook
1107 git-rebase-mode-hook
1108 grep-mode-hook
1109 ibuffer-mode-hook
1110 ilist-mode-hook
1111 ;; ledger-report-mode-hook
1112 log-view-mode-hook
1113 magit-log-mode-hook
1114 ;; mu4e-headers-mode-hook
1115 notmuch-search-mode-hook
1116 notmuch-tree-mode-hook
1117 occur-mode-hook
1118 org-agenda-mode-hook
1119 pdf-outline-buffer-mode-hook
1120 proced-mode-hook
1121 tabulated-list-mode-hook))
1122 (lin-global-mode))
1123
1124(use-package gcmh
1125 :ensure t
1126 :config
1127 (setq gcmh-idle-delay 'auto
1128 gcmh-verbose nil)
1129 (gcmh-mode))
1130
1131(use-package tmr
1132 :ensure t
1133 :preface
1134 (defun tmr-mode-line ()
1135 (if (seq-find (lambda (tmr)
1136 (not (tmr--timer-finishedp tmr)))
1137 tmr--timers)
1138 (propertize "⏲" 'face 'font-lock-warning-face)
1139 ""))
1140 ;; (add-to-list 'global-mode-string
1141 ;; '("" (:eval (tmr-mode-line)))
1142 ;; 'append)
1143 )
1144
1145(use-package dumb-jump
1146 :ensure t
1147 :hook ((xref-backend-functions . dumb-jump-xref-activate)))
1148
1149(use-package le-thesaurus
1150 :ensure t
1151 :bind (("C-c w s" . le-thesaurus-get-synonyms)
1152 ("C-c w a" . le-thesaurus-get-antonyms)))
1153
1154(use-package devdocs
1155 :ensure t
1156 ;; not sure what to bind anything to yet ... so M-x it is
1157 )
1158
1159(use-package comment-dwim-2
1160 :ensure t
1161 :bind (("M-;" . comment-dwim-2)
1162 :map org-mode-map
1163 ("M-;" . org-comment-dwim-2)))