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.el1378
1 files changed, 682 insertions, 696 deletions
diff --git a/init.el b/init.el index fcbfd09..8f7363e 100644 --- a/init.el +++ b/init.el
@@ -15,100 +15,235 @@
15;; - Make good choices. 15;; - Make good choices.
16 16
17;;; Code: 17;;; Code:
18
19 18
20;;; Necessary packages 19;;; Setup
21 20
22;;; `setup' 21;;;; `setup'
23(progn 22(straight-use-package '(setup :host nil :repo "https://git.sr.ht/~zge/setup"))
24 (straight-use-package '(setup :host nil 23(require 'setup)
25 :repo "https://git.sr.ht/~zge/setup")) 24
26 (require 'setup)) 25(setup setup
27 26 (setup-define :straight
28(setup-define :straight 27 (lambda (recipe)
29 (lambda (recipe) 28 `(or (ignore-errors (straight-use-package ',recipe))
30 `(or (ignore-errors (straight-use-package ',recipe)) 29 (progn
31 (progn 30 (message "Straight error: %S" ',recipe)
32 (message "Straight error: %S" ',recipe) 31 (throw 'setup-exit nil))))
33 (throw 'setup-exit nil)))) 32 :documentation "Install RECIPE with `straight-use-package'."
34 :documentation "Install RECIPE with `straight-use-package'." 33 :repeatable t
35 :repeatable t 34 :shorthand (lambda (sexp)
36 :shorthand (lambda (sexp) 35 (let ((recipe (cadr sexp)))
37 (let ((recipe (cadr sexp))) 36 (if (consp recipe)
38 (if (consp recipe) 37 (car recipe)
39 (car recipe) 38 recipe))))
40 recipe)))) 39
41 40 (setup-define :leader
42(setup-define :leader 41 (lambda (key command)
43 (lambda (key command) 42 `(progn
44 `(progn 43 (autoload #',command (symbol-name setup-name))
45 (autoload #',command (symbol-name setup-name)) 44 (define-key acdw/leader
46 (define-key acdw/leader 45 ,(if (stringp key) (kbd key) key)
47 ,(if (stringp key) (kbd key) key) 46 #',command)))
48 #',command))) 47 :documentation "Bind KEY to COMMAND in `acdw/leader' (C-z) map."
49 :documentation "Bind KEY to COMMAND in `acdw/leader' (C-z) map." 48 :repeatable t))
50 :repeatable t) 49
51 50;;;; `no-littering'
52;;; `no-littering'
53(setup (:straight no-littering) 51(setup (:straight no-littering)
54 (:option no-littering-etc-directory (acdw/dir) 52 (:option no-littering-etc-directory (acdw/dir)
55 no-littering-var-directory (acdw/dir)) 53 no-littering-var-directory (acdw/dir))
56 (require 'no-littering)) 54 (require 'no-littering))
57 55
58;;; My packages 56;;;; My packages
59(when-let ((default-directory 57(when-let ((default-directory
60 (expand-file-name-exists-p "pkg/" user-emacs-directory))) 58 (expand-file-name-exists-p "pkg/" user-emacs-directory)))
61 (normal-top-level-add-subdirs-to-load-path)) 59 (normal-top-level-add-subdirs-to-load-path))
62 60
63 61
64;;; Good defaults 62;;;; Utility functions and variables
63;; see also: `acdw' and friends. Functions here aren't big enough, or they're
64;; too tightly bound to stuff here, to be placed in `acdw'.
65
66;; Regular modes: `text-mode' and `prog-mode'
67(defun acdw/fringe-setup ()
68 (setq-local indicate-empty-lines t
69 indicate-buffer-boundaries '((top . right)
70 (bottom . right))))
65 71
66(defmacro setc (&rest args) 72;; Flash the mode line
67 "Customize user options using ARGS like `setq'." 73(defun flash-mode-line ()
68 (declare (debug setq)) 74 "Flash the modeline as a bell."
69 `(setup (:option ,@args))) 75 (when (eq acdw/system :home)
76 (beep))
77 (invert-face 'mode-line)
78 (run-with-timer 0.1 nil #'invert-face 'mode-line))
70 79
71(setup emacs 80(defvar lispy-modes '(emacs-lisp-mode
72 ;; Me 81 eval-expression-minibuffer
82 ielm-mode
83 lisp-mode
84 lisp-interaction-mode
85 scheme-mode
86 slime-repl-mode)
87 "List of modes that are lisp-like enough to hook packages into.")
88
89
90;;; Basics
91;; NOTE that some of the names in `setup' forms are arbitrary.
92
93(setup acdw
73 (:option user-full-name "Case Duckworth" 94 (:option user-full-name "Case Duckworth"
74 user-mail-address "acdw@acdw.net" 95 user-mail-address "acdw@acdw.net"
75 calendar-location-name "Baton Rouge, LA" 96 calendar-location-name "Baton Rouge, LA"
76 calendar-latitude 30.4 97 calendar-latitude 30.4
77 calendar-longitude -91.1) 98 calendar-longitude -91.1))
99
100(setup autorevert
101 (global-auto-revert-mode +1))
102
103(setup browse-url
104 (setq-default browse-url-browser-function 'eww-browse-url
105 browse-url-secondary-browser-function
106 (if (executable-find "firefox")
107 'browse-url-firefox
108 'browse-url-default-browser)
109 browse-url-new-window-flag t
110 browse-url-firefox-new-window-is-tab t)
111 (when (eq acdw/system :work)
112 (add-to-list 'exec-path "C:/Program Files/Mozilla Firefox")))
113
114(setup buffers
115 (:global "C-x C-b" ibuffer
116 "C-x k" acdw/kill-a-buffer))
117
118(setup completion
119 (:option completion-ignore-case t
120 read-buffer-completion-ignore-case t
121 icomplete-delay-completions-threshold 0
122 icomplete-max-delay-chars 0
123 icomplete-compute-delay 0
124 icomplete-show-matches-on-no-input t
125 icomplete-with-buffer-completion-tables t
126 icomplete-in-buffer t
127 completion-styles '(partial-completion substring flex)
128 completion-category-defaults nil
129 completion-category-overrides
130 '((file (styles . (partial-completion)))))
131
132 (:global "M-/" hippie-expand)
78 133
79 ;; Lines 134 (icomplete-mode +1))
80 (:option fill-column 79
81 word-wrap t
82 truncate-lines nil)
83 135
84 (global-display-fill-column-indicator-mode +1) 136(setup cursor
85 (global-so-long-mode +1) 137 (:option cursor-type 'bar
138 cursor-in-non-selected-windows 'hollow
139 blink-cursor-blinks 1)
140 (blink-cursor-mode +1))
86 141
87 (add-hook 'visual-line-mode-hook 142(setup cus-edit
88 (defun acdw/disable-fill-column-indicator () 143 (:option custom-file (acdw/dir "custom.el")
89 (display-fill-column-indicator-mode 144 custom-magic-show nil
90 (if visual-line-mode -1 +1)))) 145 custom-magic-show-button t
146 custom-unlispify-tag-names nil
147 custom-variable-default-form 'edit))
91 148
92 ;; Whitespace 149(setup debugger
93 (:option whitespace-style 150 (:hook visual-line-mode)
94 '(empty indentation space-before-tab space-after-tab) 151 (:leader "d" toggle-debug-on-error))
95 indent-tabs-mode nil 152
96 tab-width 4 153(setup dired
97 backward-delete-char-untabify-method 'hungry) 154 (setq-default dired-recursive-copies 'always
155 dired-recursive-deletes 'always
156 delete-by-moving-to-trash t
157 dired-listing-switches "-Al"
158 ls-lisp-dirs-first t
159 dired-ls-F-marks-symlinks t
160 dired-no-confirm '(byte-compile
161 chgrp chmod chown copy
162 hardlink load move
163 shell touch symlink)
164 dired-dwim-target t)
165
166 (:also-load dired-x)
167
168 (:hook dired-hide-details-mode
169 hl-line-mode)
98 170
99 (add-hook 'before-save-hook #'whitespace-cleanup) 171 (:global "C-x C-j" dired-jump)
100 172
101 ;; Killing and yanking 173 (pcase acdw/system
102 (:option save-interprogram-paste-before-kill t 174 (:work (:straight w32-browser)
103 yank-pop-change-selection t 175 (autoload 'dired-w32-browser "w32-browser")
104 x-select-enable-clipboard t 176 (:bind "RET" dired-w32-browser))
105 x-select-enable-primary t 177 (:home (:straight dired-open)
106 mouse-drag-copy-region t 178 (require 'dired-open)
107 kill-do-not-save-duplicates t) 179 (:bind "RET" dired-find-alternate-file)
180 (:option (prepend dired-open-functions) #'dired-open-xdg)))
181
182 (:when-loaded
183 (:straight dired-subtree)
184 (:bind "i" dired-subtree-toggle
185 "TAB" dired-subtree-cycle)
186
187 (:straight dired-collapse)
188 (:hook dired-collapse-mode)
189
190 (:straight dired-git-info)
191 (:bind ")" dired-git-info-mode)
192
193 (:straight trashed)
194 (:option trashed-action-confirmer #'y-or-n-p)))
195
196(setup ediff
197 (:option ediff-window-setup-function 'ediff-setup-windows-plain
198 ediff-split-window-function 'split-window-horizontally))
199
200(setup eldoc
201 (:option eldoc-idle-delay 0.1
202 eldoc-echo-area-use-multiline-p nil))
203
204(setup elisp-mode
205 (:option eval-expression-print-length nil
206 eval-expression-print-level nil
207 lisp-indent-function #'lisp-indent-function)
208
209 (defun acdw/enforce-lexical-binding ()
210 (setq lexical-binding t))
211
212 (add-hook 'emacs-lisp-mode-hook #'acdw/enforce-lexical-binding)
213
214 (defun acdw/eval-region-or-buffer ()
215 (interactive)
216 (if (region-active-p)
217 (let ((begin (region-beginning))
218 (end (region-end)))
219 (with-message (format "Evaluating %S -> %S" begin end)
220 (eval-region begin end)))
221 (with-message "Evaluating buffer"
222 (eval-buffer))))
223
224 ;; Emulate slime's eval binds
225 (:with-map emacs-lisp-mode-map
226 (:bind "C-c C-c" eval-defun
227 "C-c C-k" acdw/eval-region-or-buffer
228 "C-c C-z" ielm))
229
230 (add-hook 'emacs-lisp-mode-hook 'turn-on-eldoc-mode)
231 (add-hook 'ielm-mode-hook 'turn-on-eldoc-mode)
232
233 (setup (:straight macrostep)
234 (define-key emacs-lisp-mode-map (kbd "C-c e") #'macrostep-expand))
235
236 (setup (:straight eros)
237 (:hook-into emacs-lisp-mode))
108 238
109 (delete-selection-mode +1) 239 ;; Add advice to pulse evaluated regions
240 (defun acdw/elisp-eval-region-advice (fn start end &rest args)
241 (pulse-momentary-highlight-region start end)
242 (apply fn start end args))
110 243
111 ;; Encoding 244 (advice-add 'eval-region :around #'acdw/elisp-eval-region-advice))
245
246(setup encoding
112 (:option locale-coding-system 'utf-8-unix 247 (:option locale-coding-system 'utf-8-unix
113 coding-system-for-read 'utf-8-unix 248 coding-system-for-read 'utf-8-unix
114 coding-system-for-write 'utf-8-unix 249 coding-system-for-write 'utf-8-unix
@@ -131,51 +266,7 @@
131 (set-selection-coding-system 'utf-16-le)) 266 (set-selection-coding-system 'utf-16-le))
132 (_ (set-selection-coding-system 'utf-8) 267 (_ (set-selection-coding-system 'utf-8)
133 (set-clipboard-coding-system 'utf-8))) 268 (set-clipboard-coding-system 'utf-8)))
134
135 ;; Cursor
136 (:option cursor-type 'bar
137 cursor-in-non-selected-windows 'hollow
138 blink-cursor-blinks 1)
139 (blink-cursor-mode +1)
140
141 ;; Scrolling
142 (:option auto-window-vscroll nil
143 fast-but-imprecise-scrolling t
144 scroll-margin 0
145 scroll-conservatively 101
146 scroll-preserve-screen-position 1)
147
148 ;; Minibuffer
149 (:option minibuffer-prompt-properties
150 '(read-only t cursor-intangible t face minibuffer-prompt)
151 enable-recursive-minibuffers t
152 file-name-shadow-properties '(invisible t intangible t)
153 read-answer-short t)
154
155 (add-hook 'minibuffer-setup-hook #'acdw/gc-disable)
156 (add-hook 'minibuffer-exit-hook #'acdw/gc-enable)
157
158 (minibuffer-depth-indicate-mode +1)
159 (file-name-shadow-mode +1)
160 (minibuffer-electric-default-mode +1)
161 (fset 'yes-or-no-p #'y-or-n-p)
162 269
163 ;; Completion
164 (:option completion-ignore-case t
165 read-buffer-completion-ignore-case t
166 icomplete-delay-completions-threshold 0
167 icomplete-max-delay-chars 0
168 icomplete-compute-delay 0
169 icomplete-show-matches-on-no-input t
170 icomplete-with-buffer-completion-tables t
171 icomplete-in-buffer t
172 completion-styles '(partial-completion substring flex)
173 completion-category-defaults nil
174 completion-category-overrides
175 '((file (styles . (partial-completion)))))
176 ;; (icomplete-mode +1)
177
178 ;; Emoji
179 (let ((ffl (font-family-list))) 270 (let ((ffl (font-family-list)))
180 (dolist (emoji-font '("Segoe UI Emoji" 271 (dolist (emoji-font '("Segoe UI Emoji"
181 "Noto Color Emoji" 272 "Noto Color Emoji"
@@ -183,82 +274,109 @@
183 "Symbola")) 274 "Symbola"))
184 (when (member emoji-font ffl) 275 (when (member emoji-font ffl)
185 (set-fontset-font t 'symbol 276 (set-fontset-font t 'symbol
186 (font-spec :family emoji-font) nil 'append)))) 277 (font-spec :family emoji-font) nil 'append)))))
278
279(setup eshell
280 (:option eshell-directory-name (acdw/dir "eshell/" t)
281 eshell-aliases-file (acdw/dir "eshell/aliases" t))
282
283 (defun eshell-quit-or-delete-char (arg)
284 "Delete the character to the right, or quit eshell on an empty line."
285 (interactive "p")
286 (if (and (eolp) (looking-back eshell-prompt-regexp))
287 (eshell-life-is-too-much)
288 (delete-forward-char arg)))
289
290 (global-set-key (kbd "<f12>") #'eshell)
187 291
188 ;; Etc. 292 (hook-defun eshell-setup 'eshell-mode-hook
189 (:option inhibit-startup-screen t 293 (define-key eshell-mode-map (kbd "<f12>") #'bury-buffer)
190 initial-buffer-choice t 294 (define-key eshell-mode-map (kbd "C-d") #'eshell-quit-or-delete-char)
191 initial-scratch-message (concat ";; Howdy, " 295 (when (boundp 'simple-modeline--mode-line)
192 (nth 0 (split-string 296 (setq mode-line-format '(:eval simple-modeline--mode-line)))))
193 user-full-name)) 297
194 "! " 298(setup eww
195 "Welcome to GNU Emacs.\n\n") 299 (:hook acdw/reading-mode))
196 initial-major-mode 'emacs-lisp-mode 300
197 disabled-command-function nil 301(setup files
198 load-prefer-newer t 302 (:option backup-directory-alist `((".*" . ,(acdw/dir "backup/" t)))
199 comp-async-report-warnings-errors nil 303 tramp-backup-directory-alist backup-directory-alist
200 frame-title-format '((:eval (if-let ((bn buffer-file-name)) 304 auto-save-file-name-transforms `((".*" ,(acdw/dir "auto-save/" t) t))
305 auto-save-list-file-prefix (acdw/dir "auto-save-list/.saves-" t)
306 backup-by-copying t
307 delete-old-versions t
308 version-control t
309 vc-make-backup-files t)
310
311 (:global "C-c i" acdw/find-emacs-dotfiles)
312
313 (auto-save-visited-mode +1)
314
315 (when-unfocused save-some-buffers
316 (save-some-buffers t)))
317
318(setup flyspell
319 (setq-default ispell-program-name "hunspell"
320 ispell-dictionary "en_US"
321 ispell-personal-dictionary "~/.hunspell_personal")
322 (:needs ispell-program-name) ; don't proceed if not installed
323
324 (unless (file-exists-p ispell-personal-dictionary)
325 (write-region "" nil ispell-personal-dictionary nil 0))
326
327 (defun flyspell-start ()
328 "Start `flyspell-mode' or `flyspell-prog-mode', depending on current mode."
329 (interactive)
330 (cond ((derived-mode-p 'text-mode)
331 (flyspell-mode))
332 ((derived-mode-p 'prog-mode)
333 (flyspell-prog-mode))
334 (t (message "Non-text or -prog mode. Run `flyspell-mode'."))))
335 (:leader "s" flyspell-start)
336
337 (:when-loaded
338 (setup (:straight flyspell-correct)
339 (define-key flyspell-mode-map (kbd "C-;") #'flyspell-correct-wrapper))))
340
341(setup frames
342 (:option frame-title-format '((:eval (if-let ((bn buffer-file-name))
201 (abbreviate-file-name bn) 343 (abbreviate-file-name bn)
202 "%b")) 344 "%b"))
203 " %+%* GNU Emacs" 345 " %+%* GNU Emacs"
204 (:eval (when (frame-parameter 346 (:eval (when (frame-parameter
205 nil 'client) 347 nil 'client)
206 " Client"))) 348 " Client")))
207 tab-bar-show 1 349 window-resize-pixelwise t)
208 use-dialog-box nil 350 (when-unfocused garbage-collect
209 use-file-dialog nil 351 (garbage-collect)))
210 echo-keystrokes 0.01
211 recenter-positions '(top middle bottom)
212 attempt-stack-overflow-recovery nil
213 attempt-orderly-shutdown-on-fatal-signal nil
214 window-resize-pixelwise t
215 find-function-C-source-directory
216 (pcase acdw/system
217 (:work (expand-file-name (concat "~/src/emacs-"
218 emacs-version
219 "/src")))
220 (:home (expand-file-name
221 "~/src/pkg/emacs/src/emacs-git/src"))
222 (:other nil))
223 w32-allow-system-shell t
224 w32-pass-lwindow-to-system nil
225 w32-lwindow-modifier 'super
226 w32-pass-rwindow-to-system nil
227 w32-rwindow-modifier 'super
228 w32-pass-apps-to-system nil
229 w32-apps-modifier 'hyper
230 visible-bell nil
231 ring-bell-function #'flash-mode-line
232 view-read-only t)
233 352
234 (defun flash-mode-line () 353(setup gnus
235 "Flash the modeline as a bell." 354 (:option gnus-home-directory (expand-file-name "gnus" user-emacs-directory)
236 (when (eq acdw/system :home) 355 gnus-directory (expand-file-name "gnus/News" user-emacs-directory)
237 (beep)) 356 gnus-init-file (expand-file-name "gnus.el" user-emacs-directory))
238 (invert-face 'mode-line) 357 (when (not (file-exists-p gnus-directory))
239 (run-with-timer 0.1 nil #'invert-face 'mode-line)) 358 (make-directory gnus-directory :parents))
359 (:leader "m" gnus))
240 360
241 (when-unfocused garbage-collect 361(setup imenu
242 (garbage-collect)) 362 (:option imenu-auto-rescan t))
243 363
244 (tooltip-mode -1) 364(setup isearch
245 (winner-mode +1) 365 (:option search-default-mode t))
246 366
247 (add-hook 'view-mode-hook (defun acdw/read-view-mode () 367(setup lines
248 (acdw/reading-mode (if view-mode +1 -1)))) 368 (:option fill-column 79
369 word-wrap t
370 truncate-lines nil)
249 371
250 ;;; Bindings 372 (global-display-fill-column-indicator-mode +1)
251 (:global "M-SPC" cycle-spacing 373 (global-so-long-mode +1)
252 "M-/" hippie-expand
253 "M-=" count-words
254 "C-x C-b" ibuffer
255 "C-c i" acdw/find-emacs-dotfiles
256 "C-x k" acdw/kill-a-buffer)
257 374
258 (:leader "C-c" save-buffers-kill-emacs 375 (add-hook 'visual-line-mode-hook
259 "t" acdw/insert-iso-date) 376 (defun acdw/disable-fill-column-indicator ()
377 (display-fill-column-indicator-mode
378 (if visual-line-mode -1 +1))))
260 379
261 ;;; Advice
262 ;; `acdw/kill-line-and-join-advice' cribs from `crux-kill-and-join-forward'. 380 ;; `acdw/kill-line-and-join-advice' cribs from `crux-kill-and-join-forward'.
263 ;; I can't simply advise `kill-line' with an override from crux because crux 381 ;; I can't simply advise `kill-line' with an override from crux because crux
264 ;; itself calls `kill-line', leading to a infinite nesting situation. 382 ;; itself calls `kill-line', leading to a infinite nesting situation.
@@ -269,15 +387,20 @@
269 (delete-indentation 1) 387 (delete-indentation 1)
270 (apply orig args))))) 388 (apply orig args)))))
271 389
272;; Regular modes (`text-mode', `prog-mode', etc.) 390(setup minibuffer
273(defun acdw/setup-regular-modes () 391 (:option minibuffer-prompt-properties
274 (setq-local indicate-empty-lines t 392 '(read-only t cursor-intangible t face minibuffer-prompt)
275 indicate-buffer-boundaries '((top . right) 393 enable-recursive-minibuffers t
276 (bottom . right)))) 394 file-name-shadow-properties '(invisible t intangible t)
395 read-answer-short t)
277 396
278(setup text 397 (add-hook 'minibuffer-setup-hook #'acdw/gc-disable)
279 (:hook turn-on-auto-fill 398 (add-hook 'minibuffer-exit-hook #'acdw/gc-enable)
280 acdw/setup-regular-modes)) 399
400 (minibuffer-depth-indicate-mode +1)
401 (file-name-shadow-mode +1)
402 (minibuffer-electric-default-mode +1)
403 (fset 'yes-or-no-p #'y-or-n-p))
281 404
282(setup prog 405(setup prog
283 (:option smie-indent-basic tab-width) 406 (:option smie-indent-basic tab-width)
@@ -293,46 +416,23 @@
293 416
294 (:hook show-paren-mode 417 (:hook show-paren-mode
295 electric-pair-local-mode 418 electric-pair-local-mode
296 acdw/setup-regular-modes) 419 acdw/fringe-setup)
297 420
298 (add-hook 'after-save-hook 421 (add-hook 'after-save-hook
299 #'executable-make-buffer-file-executable-if-script-p)) 422 #'executable-make-buffer-file-executable-if-script-p))
300 423
301(setup cus-edit 424(setup re-builder
302 (:option custom-file (acdw/dir "custom.el") 425 (require 'acdw-re)
303 custom-magic-show nil 426 (advice-add 're-builder :before #'acdw/re-builder-save-state)
304 custom-magic-show-button t
305 custom-unlispify-tag-names nil
306 custom-variable-default-form 'edit))
307
308(setup uniquify
309 (:option uniquify-buffer-name-style 'forward
310 uniquify-separator path-separator
311 uniquify-after-kill-buffer-p t
312 uniquify-ignore-buffers-re "^\\*"))
313
314(setup files
315 (:option backup-directory-alist `((".*" . ,(acdw/dir "backup/" t)))
316 tramp-backup-directory-alist backup-directory-alist
317 auto-save-file-name-transforms `((".*" ,(acdw/dir "auto-save/" t) t))
318 auto-save-list-file-prefix (acdw/dir "auto-save-list/.saves-" t)
319 backup-by-copying t
320 delete-old-versions t
321 version-control t
322 vc-make-backup-files t)
323
324 (auto-save-visited-mode +1)
325
326 (when-unfocused save-some-buffers
327 (save-some-buffers t)))
328 427
329(setup autorevert 428 (:global "C-M-%" re-builder)
330 (global-auto-revert-mode +1))
331 429
332(setup saveplace 430 (dolist (map '(reb-mode-map reb-lisp-mode-map))
333 (:option save-place-file (acdw/dir "places.el") 431 (let ((setup-map map))
334 save-place-forget-unreadable-files (eq acdw/system :home)) 432 (:bind "RET" reb-replace-regexp
335 (save-place-mode +1)) 433 "M-n" reb-next-match
434 "M-p" reb-prev-match
435 "M-q" reb-quit))))
336 436
337(setup (:require recentf) 437(setup (:require recentf)
338 (:option recentf-save-file (acdw/dir "recentf.el") 438 (:option recentf-save-file (acdw/dir "recentf.el")
@@ -343,178 +443,78 @@
343 (recentf-mode +1)) 443 (recentf-mode +1))
344 444
345(setup (:require savehist) 445(setup (:require savehist)
346 (:option (append savehist-additional-variables) 'kill-ring 446 (:option history-length t
347 (append savehist-additional-variables) 'search-ring
348 (append savehist-additional-variables) 'regexp-search-ring
349 history-length t
350 history-delete-duplicates t 447 history-delete-duplicates t
351 savehist-autosave-interval 6 448 savehist-autosave-interval 6
352 savehist-file (acdw/dir "savehist.el")) 449 savehist-file (acdw/dir "savehist.el"))
353 (savehist-mode +1))
354
355(setup imenu
356 (:option imenu-auto-rescan t))
357
358(setup isearch
359 (:option search-default-mode t))
360
361(setup debugger
362 (:hook visual-line-mode)
363 (:leader "d" toggle-debug-on-error))
364
365(setup eldoc
366 (:option eldoc-idle-delay 0.1
367 eldoc-echo-area-use-multiline-p nil))
368 450
369(setup flyspell 451 (dolist (var '(kill-ring
370 (setq-default ispell-program-name "hunspell" 452 search-ring
371 ispell-dictionary "en_US" 453 regexp-search-ring))
372 ispell-personal-dictionary "~/.hunspell_personal") 454 (:option (append savehist-additional-variables) var))
373 (:needs ispell-program-name) ; don't proceed if not installed
374 455
375 (unless (file-exists-p ispell-personal-dictionary) 456 (savehist-mode +1))
376 (write-region "" nil ispell-personal-dictionary nil 0))
377 457
378 (defun flyspell-start () 458(setup saveplace
379 "Start `flyspell-mode' or `flyspell-prog-mode', depending on current mode." 459 (:option save-place-file (acdw/dir "places.el")
380 (interactive) 460 save-place-forget-unreadable-files (eq acdw/system :home))
381 (cond ((derived-mode-p 'text-mode) 461
382 (flyspell-mode)) 462 (save-place-mode +1))
383 ((derived-mode-p 'prog-mode)
384 (flyspell-prog-mode))
385 (t (message "Non-text or -prog mode. Run `flyspell-mode'."))))
386 (:leader "s" flyspell-start))
387 463
388(setup scratch 464(setup scratch
465 (:option inhibit-startup-screen t
466 initial-buffer-choice t
467 initial-scratch-message (concat ";; Howdy, "
468 (nth 0 (split-string
469 user-full-name))
470 "! "
471 "Welcome to GNU Emacs.\n\n")
472 initial-major-mode 'emacs-lisp-mode)
473
389 (hook-defun immortal-scratch kill-buffer-query-functions 474 (hook-defun immortal-scratch kill-buffer-query-functions
390 (if (eq (current-buffer) (get-buffer "*scratch*")) 475 (if (eq (current-buffer) (get-buffer "*scratch*"))
391 (progn (bury-buffer) 476 (progn (bury-buffer)
392 nil) 477 nil)
393 t))) 478 t)))
394 479
395(setup re-builder 480(setup scrolling
396 ;; https://karthinks.com/software/bridging-islands-in-emacs-1/ 481 (:option auto-window-vscroll nil
397 482 fast-but-imprecise-scrolling t
398 (defvar acdw/re-builder-positions nil 483 scroll-margin 0
399 "Store point and region bounds before calling re-builder") 484 scroll-conservatively 101
400 485 scroll-preserve-screen-position 1))
401 (advice-add 're-builder
402 :before
403 (defun acdw/re-builder-save-state (&rest _)
404 "Save into `acdw/re-builder-positions' the point and region
405positions before calling `re-builder'."
406 (setq acdw/re-builder-positions
407 (cons (point)
408 (when (region-active-p)
409 (list (region-beginning)
410 (region-end)))))))
411
412 (defun reb-replace-regexp (&optional delimited)
413 "Run `query-replace-regexp' with the contents of re-builder. With
414non-nil optional argument DELIMITED, only replace matches
415surrounded by word boundaries."
416 (interactive "P")
417 (reb-update-regexp)
418 (let* ((re (reb-target-binding reb-regexp))
419 (replacement (query-replace-read-to
420 re
421 (concat "Query replace"
422 (if current-prefix-arg
423 (if (eq current-prefix-arg '-) " backward" " word")
424 "")
425 " regexp"
426 (if (with-selected-window reb-target-window
427 (region-active-p)) " in region" ""))
428 t))
429 (pnt (car acdw/re-builder-positions))
430 (beg (cadr acdw/re-builder-positions))
431 (end (caddr acdw/re-builder-positions)))
432 (with-selected-window reb-target-window
433 (goto-char pnt) ; replace with (goto-char (match-beginning 0)) if you want
434 ; to control where in the buffer the replacement starts
435 ; with re-builder
436 (setq acdw/re-builder-positions nil)
437 (reb-quit)
438 (query-replace-regexp re replacement delimited beg end))))
439
440 (:global "C-M-%" re-builder)
441
442 (dolist (map '(reb-mode-map reb-lisp-mode-map))
443 (let ((setup-map map))
444 (:bind "RET" reb-replace-regexp
445 "M-n" reb-next-match
446 "M-p" reb-prev-match
447 "M-q" reb-quit))))
448
449
450;;; Applications
451 486
452(setup (:straight (org :host nil 487(setup selection
453 :repo "https://code.orgmode.org/bzg/org-mode.git")) 488 (:option save-interprogram-paste-before-kill t
454 (require 'acdw-org) ; so I don't clutter up init.el 489 yank-pop-change-selection t
455 (:option org-adapt-indentation nil 490 x-select-enable-clipboard t
456 org-catch-invisible-edits 'smart 491 x-select-enable-primary t
457 org-confirm-babel-evaluate nil 492 mouse-drag-copy-region t
458 org-export-coding-system 'utf-8-unix 493 kill-do-not-save-duplicates t)
459 org-export-headline-levels 8
460 org-export-with-section-numbers nil
461 org-export-with-smart-quotes t
462 org-export-with-sub-superscripts t
463 org-export-with-toc nil
464 org-fontify-done-headline t
465 org-fontify-quote-and-verse-blocks t
466 org-fontify-whole-heading-line t
467 org-hide-emphasis-markers t
468 org-html-coding-system 'utf-8-unix
469 org-imenu-depth 3
470 org-pretty-entities t
471 org-special-ctrl-a/e t
472 org-special-ctrl-k t
473 org-src-fontify-natively t
474 org-src-tab-acts-natively t
475 org-src-window-setup 'current-window
476 org-startup-truncated nil
477 org-tags-column (- 0 fill-column -3)
478 org-directory "~/org")
479 (:bind "RET" acdw-org/return-dwim
480 "<S-return>" acdw-org/org-table-copy-down)
481 (add-hook 'before-save-hook #'acdw-org/fix-blank-lines-in-buffer)
482 (advice-add 'org-delete-backward-char
483 :override #'acdw-org/delete-backward-char))
484 494
485(setup eshell 495 (delete-selection-mode +1))
486 (:option eshell-directory-name (acdw/dir "eshell/" t)
487 eshell-aliases-file (acdw/dir "eshell/aliases" t))
488 496
489 (defun eshell-quit-or-delete-char (arg) 497(setup (:require server)
490 "Delete the character to the right, or quit eshell on an empty line." 498 (unless (server-running-p)
491 (interactive "p") 499 (server-start)))
492 (if (and (eolp) (looking-back eshell-prompt-regexp))
493 (eshell-life-is-too-much)
494 (delete-forward-char arg)))
495 500
496 (global-set-key (kbd "<f12>") #'eshell) 501(setup sh-mode
502 (:option sh-basic-offset tab-width
503 sh-indent-after-case 0
504 sh-indent-for-case-alt '+
505 sh-indent-for-case-label 0)
497 506
498 (hook-defun eshell-setup 'eshell-mode-hook 507 (:local-set indent-tabs-mode t)
499 (define-key eshell-mode-map (kbd "<f12>") #'bury-buffer) 508
500 (define-key eshell-mode-map (kbd "C-d") #'eshell-quit-or-delete-char) 509 (when (executable-find "shfmt")
501 (when (boundp 'simple-modeline--mode-line) 510 (with-eval-after-load 'apheleia
502 (setq mode-line-format '(:eval simple-modeline--mode-line))))) 511 (:option (append apheleia-formatters) '(shfmt . ("shfmt"))
503 512 (append apheleia-mode-alist) '(sh-mode . shfmt))))
504(setup ediff
505 (:option ediff-window-setup-function 'ediff-setup-windows-plain
506 ediff-split-window-function 'split-window-horizontally))
507 513
508(setup browse-url 514 (when (executable-find "shellcheck")
509 (setq-default browse-url-browser-function 'eww-browse-url 515 (:straight flymake-shellcheck)
510 browse-url-secondary-browser-function 516 (:hook flymake-mode
511 (if (executable-find "firefox") 517 flymake-shellcheck-load)))
512 'browse-url-firefox
513 'browse-url-default-browser)
514 browse-url-new-window-flag t
515 browse-url-firefox-new-window-is-tab t)
516 (when (eq acdw/system :work)
517 (add-to-list 'exec-path "C:/Program Files/Mozilla Firefox")))
518 518
519(setup shr 519(setup shr
520 (:option shr-width fill-column 520 (:option shr-width fill-column
@@ -523,137 +523,94 @@ surrounded by word boundaries."
523 shr-image-animate t 523 shr-image-animate t
524 shr-discard-aria-hidden t)) 524 shr-discard-aria-hidden t))
525 525
526(setup eww 526(setup text
527 (:hook acdw/reading-mode)) 527 (:hook turn-on-auto-fill
528 528 acdw/fringe-setup))
529(setup (:straight (elpher :host nil
530 :repo "git://thelambdalab.xyz/elpher.git"))
531 (:option elpher-ipv4-always t
532 elpher-certificate-directory (acdw/dir "elpher/")
533 elpher-gemini-max-fill-width fill-column)
534 (:leader "e" elpher-bookmarks)
535 (:bind "n" elpher-next-link
536 "p" elpher-prev-link
537 "o" elpher-follow-current-link
538 "G" elpher-go-current)
539 (:hook acdw/reading-mode)
540 (autoload 'elpher-bookmarks "elpher" nil t)
541 (autoload 'elpher-go "elpher" nil t)
542 ;; Make `eww' gemini/gopher aware. From Emacswiki.
543 (advice-add 'eww-browse-url :around
544 (defun elpher:eww-browse-url (original url &optional new-window)
545 "Handle gemini and gopher links."
546 (cond ((string-match-p "\\`\\(gemini\\|gopher\\)://" url)
547 (require 'elpher)
548 (elpher-go url))
549 (t (funcall original url new-window))))))
550
551(setup (:straight (gemini-mode
552 :host nil
553 :repo "https://git.carcosa.net/jmcbray/gemini.el.git"))
554 (:file-match "\\.\\(gemini\\|gmi\\)\\'"))
555
556(setup (:straight (gemini-write
557 :host nil
558 :repo "https://alexschroeder.ch/cgit/gemini-write"
559 :fork
560 (:repo "https://tildegit.org/acdw/gemini-write"
561 :branch "main")))
562 (with-eval-after-load 'elpher
563 (require 'gemini-write)))
564
565(setup dired
566 (setq-default dired-recursive-copies 'always
567 dired-recursive-deletes 'always
568 delete-by-moving-to-trash t
569 dired-listing-switches "-Al"
570 ls-lisp-dirs-first t
571 dired-ls-F-marks-symlinks t
572 dired-no-confirm '(byte-compile
573 chgrp chmod chown copy
574 hardlink load move
575 shell touch symlink)
576 dired-dwim-target t)
577 (:also-load dired-x)
578 (:hook dired-hide-details-mode
579 hl-line-mode)
580 (:global "C-x C-j" dired-jump)
581
582 (pcase acdw/system
583 (:work (:straight w32-browser)
584 (autoload 'dired-w32-browser "w32-browser")
585 (:bind "RET" dired-w32-browser))
586 (:home (:straight dired-open)
587 (require 'dired-open)
588 (:bind "RET" dired-find-alternate-file)
589 (:option (prepend dired-open-functions) #'dired-open-xdg)))
590
591 (:straight dired-subtree)
592 (:bind "i" dired-subtree-toggle
593 "TAB" dired-subtree-cycle)
594 529
595 (:straight dired-collapse) 530(setup uniquify
596 (:hook dired-collapse-mode) 531 (:option uniquify-buffer-name-style 'forward
532 uniquify-separator path-separator
533 uniquify-after-kill-buffer-p t
534 uniquify-ignore-buffers-re "^\\*"))
597 535
598 (:straight dired-git-info) 536(setup view
599 (:bind ")" dired-git-info-mode) 537 (:option view-read-only t)
538
539 (defun acdw/read-view-mode ()
540 (acdw/reading-mode (if view-mode +1 -1)))
541
542 (:hook acdw/read-view-mode))
600 543
601 (:straight trashed) 544(setup whitespace
602 (:option trashed-action-confirmer #'y-or-n-p)) 545 (:option whitespace-style
546 '(empty indentation space-before-tab space-after-tab)
547 indent-tabs-mode nil
548 tab-width 4
549 backward-delete-char-untabify-method 'hungry)
603 550
604(setup (:straight magit) 551 (:global "M-SPC" cycle-spacing)
605 (:leader "g" magit-status) 552
553 (add-hook 'before-save-hook #'whitespace-cleanup))
606 554
607 (defun magit-display-buffer-same-window (buffer) 555(setup windows
608 "Display BUFFER in the selected window like God intended." 556 (:option use-dialog-box nil
609 (display-buffer buffer '(display-buffer-same-window))) 557 use-file-dialog nil
558 tab-bar-show 1
559 visible-bell nil
560 ring-bell-function #'flash-mode-line
561 recenter-positions '(top middle bottom))
610 562
611 (:option magit-display-buffer-function #'magit-display-buffer-same-window 563 (tooltip-mode -1)
612 magit-popup-display-buffer-action '((display-buffer-same-window)) 564 (winner-mode +1))
613 magit-refresh-status-buffer nil))
614 565
615(setup (:straight nov) 566(setup w32
616 (:option nov-text-width fill-column) 567 (:option w32-allow-system-shell t
617 (:file-match "\\.epub\\'")) 568 w32-pass-lwindow-to-system nil
569 w32-lwindow-modifier 'super
570 w32-pass-rwindow-to-system nil
571 w32-rwindow-modifier 'super
572 w32-pass-apps-to-system nil
573 w32-apps-modifier 'hyper))
618 574
619(setup gnus 575;; "Et cetera" settings
620 (:option gnus-home-directory (expand-file-name "gnus" user-emacs-directory) 576(setup emacs
621 gnus-directory (expand-file-name "gnus/News" user-emacs-directory) 577 (:option disabled-command-function nil
622 gnus-init-file (expand-file-name "gnus.el" user-emacs-directory)) 578 load-prefer-newer t
623 (when (not (file-exists-p gnus-directory)) 579 comp-async-report-warnings-errors nil
624 (make-directory gnus-directory :parents)) 580 echo-keystrokes 0.01
625 (:leader "m" gnus)) 581 attempt-stack-overflow-recovery nil
582 attempt-orderly-shutdown-on-fatal-signal nil
583 find-function-C-source-directory (acdw/find-emacs-source))
626 584
627(when (eq acdw/system :home) 585 (:global "M-=" count-words)
628 ;; Apparently the original repo is unmaintained, thus this fork.
629 (setup (:straight (pdf-tools
630 :host github
631 :repo "vedang/pdf-tools"))
632 (pdf-loader-install))
633 586
634 (setup (:straight vterm))) 587 (:leader "C-c" save-buffers-kill-emacs
588 "t" acdw/insert-iso-date))
635 589
636 590
637;; Extra packages 591;;; Packages
638 592
639(setup (:straight (beginend 593(setup (:straight (0x0 :host nil
640 :fork (:repo "duckwork/beginend"))) 594 :repo "https://git.sr.ht/~zge/nullpointer-emacs"))
641 (beginend-global-mode +1)) 595 (:option 0x0-default-host 'ttm))
642 596
643(setup (:straight mwim) 597(setup (:straight (apheleia :host github
644 (:global "C-a" mwim-beginning 598 :repo "raxod502/apheleia"))
645 "C-e" mwim-end)) 599
600 (apheleia-global-mode +1)
646 601
647(setup (:straight expand-region) 602 ;; Use a dumb formatter on modes that `apheleia' doesn't work for.
648 (:global "C-=" er/expand-region)) 603 (hook-defun dumb-auto-format before-save-hook
604 (setq stupid-modes '(makefile-mode))
605 ;; If there's no apheleia formatter for the mode, just indent the buffer.
606 (unless (or (apply #'derived-mode-p stupid-modes)
607 (and (fboundp 'apheleia--get-formatter-command)
608 (apheleia--get-formatter-command)))
609 (indent-region (point-min) (point-max)))))
649 610
650(setup (:straight crux) 611(setup (:straight async)
651 (:global "M-`" crux-other-window-or-switch-buffer 612 (autoload 'dired-async-mode "dired-async.el" nil t)
652 "C-o" crux-smart-open-line 613 (dired-async-mode +1))
653 "M-o" crux-smart-open-line-above
654 "C-M-\\" crux-cleanup-buffer-or-region
655 "C-x 4 t" crux-transpose-windows)
656 (crux-reopen-as-root-mode +1))
657 614
658(setup (:straight avy) 615(setup (:straight avy)
659 (:global "C-:" avy-goto-char 616 (:global "C-:" avy-goto-char
@@ -661,50 +618,13 @@ surrounded by word boundaries."
661 "M-g f" avy-goto-line 618 "M-g f" avy-goto-line
662 "M-g w" avy-goto-word-1 619 "M-g w" avy-goto-word-1
663 "C-c C-j" avy-resume) 620 "C-c C-j" avy-resume)
621
664 (eval-after-load "isearch" 622 (eval-after-load "isearch"
665 '(define-key isearch-mode-map (kbd "C-'") #'avy-isearch))) 623 '(define-key isearch-mode-map (kbd "C-'") #'avy-isearch)))
666 624
667(setup (:straight zzz-to-char) 625(setup (:straight (beginend
668 (defun acdw/zzz-up-to-char (prefix) 626 :fork (:repo "duckwork/beginend")))
669 "Call `zzz-up-to-char', unless issued a PREFIX, in which case 627 (beginend-global-mode +1))
670call `zzz-to-char'."
671 (interactive "P")
672 (if prefix
673 (call-interactively #'zzz-to-char)
674 (call-interactively #'zzz-up-to-char)))
675
676 (:global "M-z" acdw/zzz-up-to-char))
677
678(setup (:straight async)
679 (autoload 'dired-async-mode "dired-async.el" nil t)
680 (dired-async-mode +1))
681
682(setup (:straight undo-fu)
683 (:global "C-/" undo-fu-only-undo
684 "C-?" undo-fu-only-redo))
685
686(setup (:straight undo-fu-session)
687 (:option undo-fu-session-incompatible-files '("/COMMIT_EDITMSG\\'"
688 "/git-rebase-todo\\'")
689 undo-fu-session-directory (acdw/dir "undo/" t)
690 undo-fu-session-compression (eq acdw/system :home))
691
692 (global-undo-fu-session-mode +1))
693
694(setup (:straight (vertico
695 :host github
696 :repo "minad/vertico"))
697 (setq resize-mini-windows 'grow-only)
698 (if (boundp 'comp-deferred-compilation-deny-list)
699 (add-to-list 'comp-deferred-compilation-deny-list "vertico"))
700 (icomplete-mode -1)
701 (vertico-mode +1))
702
703(setup (:straight (orderless
704 :host github
705 :repo "oantolin/orderless"))
706 (require 'orderless)
707 (:option (prepend completion-styles) 'orderless))
708 628
709(setup (:straight (consult 629(setup (:straight (consult
710 :host github 630 :host github
@@ -788,6 +708,124 @@ if ripgrep is installed, otherwise `consult-grep'."
788 completion-cycle-threshold 3 708 completion-cycle-threshold 3
789 tab-always-indent 'complete)) 709 tab-always-indent 'complete))
790 710
711(setup (:straight crux)
712
713 (:global "M-`" crux-other-window-or-switch-buffer
714 "C-o" crux-smart-open-line
715 "M-o" crux-smart-open-line-above
716 "C-M-\\" crux-cleanup-buffer-or-region
717 "C-x 4 t" crux-transpose-windows)
718
719 (crux-reopen-as-root-mode +1))
720
721(setup (:straight (electric-cursor
722 :host github
723 :repo "duckwork/electric-cursor"))
724 (electric-cursor-mode +1))
725
726(setup (:straight (elpher :host nil
727 :repo "git://thelambdalab.xyz/elpher.git"))
728 (:option elpher-ipv4-always t
729 elpher-certificate-directory (acdw/dir "elpher/")
730 elpher-gemini-max-fill-width fill-column)
731
732 (:leader "e" elpher-bookmarks)
733
734 (:bind "n" elpher-next-link
735 "p" elpher-prev-link
736 "o" elpher-follow-current-link
737 "G" elpher-go-current)
738
739 (:hook acdw/reading-mode)
740
741 (autoload 'elpher-bookmarks "elpher" nil t)
742 (autoload 'elpher-go "elpher" nil t)
743
744 ;; Make `eww' gemini/gopher aware. From Emacswiki.
745 (advice-add 'eww-browse-url :around
746 (defun elpher:eww-browse-url (original url &optional new-window)
747 "Handle gemini and gopher links."
748 (cond ((string-match-p "\\`\\(gemini\\|gopher\\)://" url)
749 (require 'elpher)
750 (elpher-go url))
751 (t (funcall original url new-window)))))
752
753 (:when-loaded
754 (setup (:straight (gemini-write
755 :host nil
756 :repo "https://alexschroeder.ch/cgit/gemini-write"
757 :fork
758 (:repo "https://tildegit.org/acdw/gemini-write"
759 :branch "main")))
760 (require 'gemini-write))))
761
762(setup (:straight expand-region)
763 (:global "C-=" er/expand-region))
764
765(setup (:straight fennel-mode)
766 (:needs "fennel")
767 (autoload 'fennel-repl "fennel-mode" nil t)
768 (:file-match "\\.fnl\\'"))
769
770(setup (:straight form-feed)
771 (global-form-feed-mode +1))
772
773(setup (:straight geiser))
774
775(setup (:straight slime)
776 (defvar acdw/lisp-bin (or (executable-find "sbcl")
777 (executable-find "clisp")))
778 (:needs acdw/lisp-bin)
779
780 (:also-load slime-autoloads)
781
782 (:option inferior-lisp-program acdw/lisp-bin)
783
784 (when-let ((slime-helper (or (expand-file-name-exists-p
785 "~/quicklisp/slime-helper.el")
786 (expand-file-name-exists-p
787 "~/var/quicklisp/slime-helper.el"))))
788 (load slime-helper))
789
790 (with-eval-after-load 'company
791 (setup (:straight slime-company)
792 (:option slime-company-completion 'fuzzy
793 slime-company-after-completion nil)
794 (slime-setup '(slime-fancy slime-company)))))
795
796(setup (:straight (gemini-mode
797 :host nil
798 :repo "https://git.carcosa.net/jmcbray/gemini.el.git"))
799 (:file-match "\\.\\(gemini\\|gmi\\)\\'"))
800
801(setup gforth
802 (when (locate-library "gforth")
803 (autoload 'forth-mode "gforth")
804 (add-to-list 'auto-mode-alist '("\\.fs\\'" . forth-mode))
805 (autoload 'forth-block-mode "gforth")
806 (add-to-list 'auto-mode-alist '("\\.fb\\'" . forth-block-mode))))
807
808(setup (:straight helpful)
809 (:global "<help> f" helpful-callable
810 "<help> v" helpful-variable
811 "<help> k" helpful-key
812 "<help> o" helpful-symbol
813 "C-c C-d" helpful-at-point))
814
815(setup (:straight lua-mode)
816 (:file-match "\\.lua\\'"))
817
818(setup (:straight magit)
819 (:leader "g" magit-status)
820
821 (defun magit-display-buffer-same-window (buffer)
822 "Display BUFFER in the selected window like God intended."
823 (display-buffer buffer '(display-buffer-same-window)))
824
825 (:option magit-display-buffer-function #'magit-display-buffer-same-window
826 magit-popup-display-buffer-action '((display-buffer-same-window))
827 magit-refresh-status-buffer nil))
828
791(setup (:straight marginalia) 829(setup (:straight marginalia)
792 (:option marginalia-annotators '(marginalia-annotators-heavy 830 (:option marginalia-annotators '(marginalia-annotators-heavy
793 marginalia-annotators-light)) 831 marginalia-annotators-light))
@@ -803,107 +841,76 @@ if ripgrep is installed, otherwise `consult-grep'."
803 modus-themes-headings '((1 . section) 841 modus-themes-headings '((1 . section)
804 (t . no-color)) 842 (t . no-color))
805 modus-themes-mode-line nil) 843 modus-themes-mode-line nil)
844
806 (acdw/sunrise-sunset #'modus-themes-load-operandi 845 (acdw/sunrise-sunset #'modus-themes-load-operandi
807 #'modus-themes-load-vivendi)) 846 #'modus-themes-load-vivendi))
808 847
809(setup (:straight simple-modeline) 848(setup (:straight mwim)
810 (setup (:straight minions)) 849 (:global "C-a" mwim-beginning
811 (:option simple-modeline-segments 850 "C-e" mwim-end))
812 '((acdw-modeline/modified 851
813 acdw-modeline/buffer-name 852(setup (:straight nov)
814 acdw-modeline/vc-branch 853 (:option nov-text-width fill-column)
815 simple-modeline-segment-position 854 (:file-match "\\.epub\\'"))
816 simple-modeline-segment-word-count)
817 (simple-modeline-segment-misc-info
818 simple-modeline-segment-process
819 acdw-modeline/god-mode-indicator
820 acdw-modeline/minions
821 simple-modeline-segment-major-mode)))
822 (require 'acdw-modeline)
823 (simple-modeline-mode +1))
824 855
825(setup (:straight olivetti) 856(setup (:straight olivetti)
826 (:option olivetti-body-width (+ fill-column 4) 857 (:option olivetti-body-width (+ fill-column 4)
827 olivetti-minimum-body-width fill-column) 858 olivetti-minimum-body-width fill-column)
859
828 (add-hook 'olivetti-mode-hook 860 (add-hook 'olivetti-mode-hook
829 (defun acdw/olivetti-mode-hook () 861 (defun acdw/olivetti-mode-hook ()
830 (if olivetti-mode 862 (if olivetti-mode
831 (setq-local indicate-empty-lines nil 863 (setq-local indicate-empty-lines nil
832 indicate-buffer-boundaries nil) 864 indicate-buffer-boundaries nil)
833 (acdw/setup-regular-modes))))) 865 (acdw/fringe-setup)))))
834
835(setup (:straight form-feed)
836 (global-form-feed-mode +1))
837
838(setup (:straight which-key)
839 (:option which-key-show-early-on-C-h t
840 which-key-idle-delay 10000
841 which-key-idle-secondary-delay 0.05)
842 (which-key-setup-side-window-bottom)
843 (which-key-mode +1))
844
845(setup (:straight helpful)
846 (:global "<help> f" helpful-callable
847 "<help> v" helpful-variable
848 "<help> k" helpful-key
849 "<help> o" helpful-symbol
850 "C-c C-d" helpful-at-point))
851 866
852(setup (:straight (0x0 :host nil 867(setup (:straight (orderless
853 :repo "https://git.sr.ht/~zge/nullpointer-emacs"))
854 (:option 0x0-default-host 'ttm))
855
856(with-eval-after-load 'flyspell
857 (setup (:straight flyspell-correct)
858 (define-key flyspell-mode-map (kbd "C-;") #'flyspell-correct-wrapper)))
859
860(setup (:straight (electric-cursor
861 :host github 868 :host github
862 :repo "duckwork/electric-cursor")) 869 :repo "oantolin/orderless"))
863 (electric-cursor-mode +1)) 870 (require 'orderless)
864 871 (:option (prepend completion-styles) 'orderless))
865(setup (:straight restart-emacs)
866 (defun upgrade-packages-and-restart ()
867 "Upgrade all packages and restart Emacs."
868 (interactive)
869 (straight-pull-all)
870 (restart-emacs)))
871
872
873;;; Programming
874
875;;; General tools
876(setup (:straight (apheleia :host github
877 :repo "raxod502/apheleia"))
878 (apheleia-global-mode +1)
879
880 ;; Use a dumb formatter on modes that `apheleia' doesn't work for.
881 (hook-defun dumb-auto-format before-save-hook
882 (setq stupid-modes '(makefile-mode))
883 ;; If there's no apheleia formatter for the mode, just indent the buffer.
884 (unless (or (apply #'derived-mode-p stupid-modes)
885 (and (fboundp 'apheleia--get-formatter-command)
886 (apheleia--get-formatter-command)))
887 (indent-region (point-min) (point-max)))))
888
889;;; Lisps
890(defvar lispy-modes '(emacs-lisp-mode
891 eval-expression-minibuffer
892 ielm-mode
893 lisp-mode
894 lisp-interaction-mode
895 scheme-mode
896 slime-repl-mode)
897 "List of modes that are lisp-like enough to hook packages into.")
898 872
899(setup (:straight paren-face) 873(setup (:straight (org :host nil
900 (dolist (mode lispy-modes) 874 :repo "https://code.orgmode.org/bzg/org-mode.git"))
901 (add-hook (intern (concat (symbol-name mode) "-hook")) #'paren-face-mode))) 875 (require 'acdw-org) ; so I don't clutter up init.el
876 (:option org-adapt-indentation nil
877 org-catch-invisible-edits 'smart
878 org-confirm-babel-evaluate nil
879 org-export-coding-system 'utf-8-unix
880 org-export-headline-levels 8
881 org-export-with-section-numbers nil
882 org-export-with-smart-quotes t
883 org-export-with-sub-superscripts t
884 org-export-with-toc nil
885 org-fontify-done-headline t
886 org-fontify-quote-and-verse-blocks t
887 org-fontify-whole-heading-line t
888 org-hide-emphasis-markers t
889 org-html-coding-system 'utf-8-unix
890 org-imenu-depth 3
891 org-pretty-entities t
892 org-special-ctrl-a/e t
893 org-special-ctrl-k t
894 org-src-fontify-natively t
895 org-src-tab-acts-natively t
896 org-src-window-setup 'current-window
897 org-startup-truncated nil
898 org-tags-column (- 0 fill-column -3)
899 org-directory "~/org")
900
901 (:bind "RET" acdw-org/return-dwim
902 "<S-return>" acdw-org/org-table-copy-down)
903
904 (add-hook 'before-save-hook #'acdw-org/fix-blank-lines-in-buffer)
905
906 (advice-add 'org-delete-backward-char
907 :override #'acdw-org/delete-backward-char))
902 908
903(setup (:straight paredit) 909(setup (:straight paredit)
904 910
905 ;; I don't use paredit-splice-sexp much, and it stomps on isearch. 911 ;; I don't use paredit-splice-sexp much, and it stomps on isearch.
906 (:unbind "M-s") 912 (:unbind "M-s")
913
907 (defun setup-paredit-mode () 914 (defun setup-paredit-mode ()
908 "Correct weirdnesses and set up paredit mode." 915 "Correct weirdnesses and set up paredit mode."
909 (paredit-mode +1) 916 (paredit-mode +1)
@@ -916,92 +923,54 @@ if ripgrep is installed, otherwise `consult-grep'."
916 (require 'eldoc) 923 (require 'eldoc)
917 (eldoc-add-command 'paredit-backward-delete 'paredit-close-round)) 924 (eldoc-add-command 'paredit-backward-delete 'paredit-close-round))
918 925
919(setup elisp-mode 926(setup (:straight paren-face)
920 (:option eval-expression-print-length nil 927 (dolist (mode lispy-modes)
921 eval-expression-print-level nil 928 (add-hook (intern (concat (symbol-name mode) "-hook")) #'paren-face-mode)))
922 lisp-indent-function #'lisp-indent-function)
923
924 (defun acdw/enforce-lexical-binding ()
925 (setq lexical-binding t))
926
927 (add-hook 'emacs-lisp-mode-hook #'acdw/enforce-lexical-binding)
928 929
929 (defun acdw/eval-region-or-buffer () 930(setup (:straight restart-emacs)
931 (defun upgrade-packages-and-restart ()
932 "Upgrade all packages and restart Emacs."
930 (interactive) 933 (interactive)
931 (if (region-active-p) 934 (straight-pull-all)
932 (let ((begin (region-beginning)) 935 (restart-emacs)))
933 (end (region-end)))
934 (with-message (format "Evaluating %S -> %S" begin end)
935 (eval-region begin end)))
936 (with-message "Evaluating buffer"
937 (eval-buffer))))
938
939 ;; Emulate slime's eval binds
940 (:with-map emacs-lisp-mode-map
941 (:bind "C-c C-c" eval-defun
942 "C-c C-k" acdw/eval-region-or-buffer
943 "C-c C-z" ielm))
944
945 (add-hook 'emacs-lisp-mode-hook 'turn-on-eldoc-mode)
946 (add-hook 'ielm-mode-hook 'turn-on-eldoc-mode)
947
948 (setup (:straight macrostep)
949 (define-key emacs-lisp-mode-map (kbd "C-c e") #'macrostep-expand))
950
951 (setup (:straight eros)
952 (:hook-into emacs-lisp-mode))
953
954 ;; Add advice to pulse evaluated regions
955 (defun acdw/elisp-eval-region-advice (fn start end &rest args)
956 (pulse-momentary-highlight-region start end)
957 (apply fn start end args))
958
959 (advice-add 'eval-region :around #'acdw/elisp-eval-region-advice))
960
961(setup (:straight geiser))
962 936
963(when-let (lisp-bin (or (executable-find "sbcl") 937(setup (:straight simple-modeline)
964 (executable-find "clisp"))) 938 (setup (:straight minions))
965 (setup (:straight slime) 939 (:option simple-modeline-segments
966 (require 'slime-autoloads) 940 '((acdw-modeline/modified
967 (:option inferior-lisp-program lisp-bin) 941 acdw-modeline/buffer-name
968 942 acdw-modeline/vc-branch
969 (when-let ((slime-helper (or (expand-file-name-exists-p 943 simple-modeline-segment-position
970 "~/quicklisp/slime-helper.el") 944 simple-modeline-segment-word-count)
971 (expand-file-name-exists-p 945 (simple-modeline-segment-misc-info
972 "~/var/quicklisp/slime-helper.el")))) 946 simple-modeline-segment-process
973 (load slime-helper)) 947 acdw-modeline/god-mode-indicator
974 948 acdw-modeline/minions
975 (with-eval-after-load 'company 949 simple-modeline-segment-major-mode)))
976 (setup (:straight slime-company) 950
977 (:option slime-company-completion 'fuzzy 951 (require 'acdw-modeline)
978 slime-company-after-completion nil) 952 (simple-modeline-mode +1))
979 (slime-setup '(slime-fancy slime-company))))))
980
981(when (executable-find "fennel")
982 (setup (:straight fennel-mode)
983 (autoload 'fennel-repl "fennel-mode" nil t)
984 (:file-match "\\.fnl\\'")))
985 953
986(setup (:straight lua-mode) 954(setup (:straight undo-fu)
987 (:file-match "\\.lua\\'")) 955 (:global "C-/" undo-fu-only-undo
956 "C-?" undo-fu-only-redo))
988 957
989(setup sh-mode 958(setup (:straight undo-fu-session)
990 (:option sh-basic-offset tab-width 959 (:option undo-fu-session-incompatible-files '("/COMMIT_EDITMSG\\'"
991 sh-indent-after-case 0 960 "/git-rebase-todo\\'")
992 sh-indent-for-case-alt '+ 961 undo-fu-session-directory (acdw/dir "undo/" t)
993 sh-indent-for-case-label 0) 962 undo-fu-session-compression (eq acdw/system :home))
994 (:local-set indent-tabs-mode t)
995 963
996 (when (executable-find "shfmt") 964 (global-undo-fu-session-mode +1))
997 (with-eval-after-load 'apheleia
998 (:option (append apheleia-formatters) '(shfmt . ("shfmt"))
999 (append apheleia-mode-alist) '(sh-mode . shfmt))))
1000 965
1001 (when (executable-find "shellcheck") 966(setup (:straight (vertico
1002 (straight-use-package 'flymake-shellcheck) 967 :host github
1003 (:hook flymake-mode 968 :repo "minad/vertico"))
1004 flymake-shellcheck-load))) 969 (setq resize-mini-windows 'grow-only)
970 (if (boundp 'comp-deferred-compilation-deny-list)
971 (add-to-list 'comp-deferred-compilation-deny-list "vertico"))
972 (icomplete-mode -1)
973 (vertico-mode +1))
1005 974
1006(setup (:straight web-mode) 975(setup (:straight web-mode)
1007 (:option css-level-offset 2 976 (:option css-level-offset 2
@@ -1015,18 +984,35 @@ if ripgrep is installed, otherwise `consult-grep'."
1015 "\\.erb\\'" 984 "\\.erb\\'"
1016 "\\.mustache\\'")) 985 "\\.mustache\\'"))
1017 986
1018(when (locate-library "gforth") 987(setup (:straight which-key)
1019 (autoload 'forth-mode "gforth") 988 (:option which-key-show-early-on-C-h t
1020 (add-to-list 'auto-mode-alist '("\\.fs\\'" . forth-mode)) 989 which-key-idle-delay 10000
1021 (autoload 'forth-block-mode "gforth") 990 which-key-idle-secondary-delay 0.05)
1022 (add-to-list 'auto-mode-alist '("\\.fb\\'" . forth-block-mode))) 991 (which-key-setup-side-window-bottom)
992 (which-key-mode +1))
1023 993
1024(when (eq acdw/system :home) 994(setup (:straight zzz-to-char)
1025 (setup (:straight pkgbuild-mode))) 995 (defun acdw/zzz-up-to-char (prefix)
996 "Call `zzz-up-to-char', unless issued a PREFIX, in which case
997call `zzz-to-char'."
998 (interactive "P")
999 (if prefix
1000 (call-interactively #'zzz-to-char)
1001 (call-interactively #'zzz-up-to-char)))
1002
1003 (:global "M-z" acdw/zzz-up-to-char))
1026 1004
1027 1005
1028;;; Server 1006;;; System-dependent
1007
1008;;;; Home
1009(when (eq acdw/system :home)
1029 1010
1030(require 'server) 1011 (setup (:straight pkgbuild-mode))
1031(unless (server-running-p) 1012
1032 (server-start)) 1013 (setup (:straight (pdf-tools
1014 :host github
1015 :repo "vedang/pdf-tools"))
1016 (pdf-loader-install))
1017
1018 (setup (:straight vterm)))