diff options
-rw-r--r-- | init.el | 1378 | ||||
-rw-r--r-- | lisp/acdw-modeline.el | 92 | ||||
-rw-r--r-- | lisp/acdw-re.el | 66 | ||||
-rw-r--r-- | lisp/acdw.el | 129 |
4 files changed, 863 insertions, 802 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 | ||
405 | positions 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 | ||
414 | non-nil optional argument DELIMITED, only replace matches | ||
415 | surrounded 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)) |
670 | call `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 | ||
997 | call `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))) | ||
diff --git a/lisp/acdw-modeline.el b/lisp/acdw-modeline.el index c7b904f..79249bb 100644 --- a/lisp/acdw-modeline.el +++ b/lisp/acdw-modeline.el | |||
@@ -22,64 +22,60 @@ | |||
22 | (require 'simple-modeline) | 22 | (require 'simple-modeline) |
23 | (require 'minions) | 23 | (require 'minions) |
24 | 24 | ||
25 | ;; modified from `simple-modeline' | 25 | (defun acdw-modeline/buffer-name () ; gonsie |
26 | (defun acdw-modeline/modified () | 26 | (propertize " %b " |
27 | "Displays a color-coded buffer modification/read-only | 27 | 'face |
28 | (if (buffer-modified-p) | ||
29 | 'font-lock-warning-face | ||
30 | 'font-lock-type-face) | ||
31 | 'help-echo (buffer-file-name))) | ||
32 | |||
33 | (defun acdw-modeline/god-mode-indicator () | ||
34 | (when (bound-and-true-p god-local-mode) | ||
35 | " God")) | ||
36 | |||
37 | (defun acdw-modeline/modified () ; modified from `simple-modeline' | ||
38 | "Displays a color-coded buffer modification/read-only | ||
28 | indicator in the mode-line." | 39 | indicator in the mode-line." |
29 | (if (not (string-match-p "\\*.*\\*" (buffer-name))) | 40 | (if (not (string-match-p "\\*.*\\*" (buffer-name))) |
30 | (let* ((read-only (and buffer-read-only (buffer-file-name))) | 41 | (let* ((read-only (and buffer-read-only (buffer-file-name))) |
31 | (modified (buffer-modified-p))) | 42 | (modified (buffer-modified-p))) |
32 | (propertize | 43 | (propertize |
33 | (if read-only " =" (if modified " +" " -")) | 44 | (if read-only " =" (if modified " +" " -")) |
34 | 'help-echo (format | 45 | 'help-echo (format |
35 | (concat "Buffer is %s and %smodified\n" | 46 | (concat "Buffer is %s and %smodified\n" |
36 | "mouse-1: Toggle read-only status.") | 47 | "mouse-1: Toggle read-only status.") |
37 | (if read-only "read-only" "writable") | 48 | (if read-only "read-only" "writable") |
38 | (if modified "" "not ")) | 49 | (if modified "" "not ")) |
39 | 'local-map (purecopy (simple-modeline-make-mouse-map | 50 | 'local-map (purecopy (simple-modeline-make-mouse-map |
40 | 'mouse-1 | 51 | 'mouse-1 |
41 | (lambda (event) | 52 | (lambda (event) |
42 | (interactive "e") | 53 | (interactive "e") |
43 | (with-selected-window | 54 | (with-selected-window |
44 | (posn-window (event-start event)) | 55 | (posn-window (event-start event)) |
45 | (read-only-mode 'toggle))))) | 56 | (read-only-mode 'toggle))))) |
46 | 'mouse-face 'mode-line-highlight)))) | 57 | 'mouse-face 'mode-line-highlight)))) |
47 | 58 | ||
48 | ;; all me, baby | 59 | (defun acdw-modeline/minions () ; by me |
49 | (defun acdw-modeline/minions () | 60 | "Display a button for `minions-minor-modes-menu'." |
50 | "Display a button for `minions-minor-modes-menu'." | 61 | (concat |
51 | (concat | 62 | " " |
52 | " " | 63 | (propertize |
53 | (propertize | 64 | "&" |
54 | "&" | 65 | 'help-echo (format |
55 | 'help-echo (format | 66 | "Minor modes menu\nmouse-1: show menu.") |
56 | "Minor modes menu\nmouse-1: show menu.") | 67 | 'local-map (purecopy (simple-modeline-make-mouse-map |
57 | 'local-map (purecopy (simple-modeline-make-mouse-map | 68 | 'mouse-1 |
58 | 'mouse-1 | 69 | (lambda (event) |
59 | (lambda (event) | 70 | (interactive "e") |
60 | (interactive "e") | 71 | (with-selected-window (posn-window |
61 | (with-selected-window (posn-window | 72 | (event-start event)) |
62 | (event-start event)) | 73 | (minions-minor-modes-menu))))) |
63 | (minions-minor-modes-menu))))) | 74 | 'mouse-face 'mode-line-highlight))) |
64 | 'mouse-face 'mode-line-highlight))) | ||
65 | 75 | ||
66 | ;; from https://www.gonsie.com/blorg/modeline.html, from Doom | ||
67 | (defun acdw-modeline/vc-branch () | 76 | (defun acdw-modeline/vc-branch () |
77 | ;; from https://www.gonsie.com/blorg/modeline.html, from Doom | ||
68 | (if-let ((backend (vc-backend buffer-file-name))) | 78 | (if-let ((backend (vc-backend buffer-file-name))) |
69 | (substring vc-mode (+ (if (eq backend 'Hg) 2 3) 2)))) | 79 | (substring vc-mode (+ (if (eq backend 'Hg) 2 3) 2)))) |
70 | |||
71 | ;; from gonsie | ||
72 | (defun acdw-modeline/buffer-name () | ||
73 | (propertize " %b " | ||
74 | 'face | ||
75 | (if (buffer-modified-p) | ||
76 | 'font-lock-warning-face | ||
77 | 'font-lock-type-face) | ||
78 | 'help-echo (buffer-file-name))) | ||
79 | |||
80 | ;; god-mode indicator | ||
81 | (defun acdw-modeline/god-mode-indicator () | ||
82 | (when (bound-and-true-p god-local-mode) | ||
83 | " God")) | ||
84 | 80 | ||
85 | (provide 'acdw-modeline) | 81 | (provide 'acdw-modeline) |
diff --git a/lisp/acdw-re.el b/lisp/acdw-re.el new file mode 100644 index 0000000..ea1c42a --- /dev/null +++ b/lisp/acdw-re.el | |||
@@ -0,0 +1,66 @@ | |||
1 | ;;; acdw-re.el -*- lexical-binding: t; coding: utf-8-unix -*- | ||
2 | ;; Author: Case Duckworth <acdw@acdw.net> | ||
3 | ;; Created: 2021-04-29 | ||
4 | ;; Keywords: configuration | ||
5 | ;; URL: https://tildegit.org/acdw/emacs | ||
6 | |||
7 | ;; This file is NOT part of GNU Emacs. | ||
8 | |||
9 | ;;; License: | ||
10 | ;; Everyone is permitted to do whatever with this software, without | ||
11 | ;; limitation. This software comes without any warranty whatsoever, | ||
12 | ;; but with two pieces of advice: | ||
13 | ;; - Don't hurt yourself. | ||
14 | ;; - Make good choices. | ||
15 | |||
16 | ;;; Commentary: | ||
17 | ;; Pulled mostly from karthinks: | ||
18 | ;; https://karthinks.com/software/bridging-islands-in-emacs-1/ | ||
19 | |||
20 | ;;; Code: | ||
21 | |||
22 | (defvar acdw/re-builder-positions nil | ||
23 | "Store point and region bounds before calling re-builder") | ||
24 | |||
25 | (defun acdw/re-builder-save-state (&rest _) | ||
26 | "Save into `acdw/re-builder-positions' the point and region | ||
27 | positions before calling `re-builder'." | ||
28 | (setq acdw/re-builder-positions | ||
29 | (cons (point) | ||
30 | (when (region-active-p) | ||
31 | (list (region-beginning) | ||
32 | (region-end)))))) | ||
33 | |||
34 | (defun reb-replace-regexp (&optional delimited) | ||
35 | "Run `query-replace-regexp' with the contents of re-builder. With | ||
36 | non-nil optional argument DELIMITED, only replace matches | ||
37 | surrounded by word boundaries." | ||
38 | (interactive "P") | ||
39 | (reb-update-regexp) | ||
40 | (let* ((re (reb-target-binding reb-regexp)) | ||
41 | (replacement (query-replace-read-to | ||
42 | re | ||
43 | (concat "Query replace" | ||
44 | (if current-prefix-arg | ||
45 | (if (eq current-prefix-arg '-) | ||
46 | " backward" | ||
47 | " word") | ||
48 | "") | ||
49 | " regexp" | ||
50 | (if (with-selected-window reb-target-window | ||
51 | (region-active-p)) " in region" "")) | ||
52 | t)) | ||
53 | (pnt (car acdw/re-builder-positions)) | ||
54 | (beg (cadr acdw/re-builder-positions)) | ||
55 | (end (caddr acdw/re-builder-positions))) | ||
56 | (with-selected-window reb-target-window | ||
57 | ;; replace with (goto-char (match-beginning 0)) if you want to control | ||
58 | ;; where in the buffer the replacement starts with re-builder | ||
59 | (goto-char pnt) | ||
60 | (setq acdw/re-builder-positions nil) | ||
61 | (reb-quit) | ||
62 | (query-replace-regexp re replacement delimited beg end)))) | ||
63 | |||
64 | (provide 'acdw-re) | ||
65 | |||
66 | ;;; acdw-re.el ends here | ||
diff --git a/lisp/acdw.el b/lisp/acdw.el index 54b139c..1093a8d 100644 --- a/lisp/acdw.el +++ b/lisp/acdw.el | |||
@@ -19,7 +19,7 @@ | |||
19 | 19 | ||
20 | ;;; Code: | 20 | ;;; Code: |
21 | 21 | ||
22 | ;; Utility constants | 22 | ;;; Variables |
23 | 23 | ||
24 | (defconst acdw/system (pcase system-type | 24 | (defconst acdw/system (pcase system-type |
25 | ('gnu/linux :home) | 25 | ('gnu/linux :home) |
@@ -28,19 +28,15 @@ | |||
28 | "Which computer system is currently being used.") | 28 | "Which computer system is currently being used.") |
29 | 29 | ||
30 | 30 | ||
31 | ;; Utility functions | 31 | ;;; Utility functions |
32 | 32 | ||
33 | (defmacro when-unfocused (name &rest forms) | 33 | (defun expand-file-name-exists-p (&rest expand-file-name-args) |
34 | "Define a function NAME, executing FORMS, that fires when Emacs | 34 | "Call `expand-file-name' on EXPAND-FILE-NAME-ARGS, returning |
35 | is unfocused." | 35 | its name if it exists, or NIL otherwise." |
36 | (declare (indent 1)) | 36 | (let ((file (apply #'expand-file-name expand-file-name-args))) |
37 | (let ((func-name (intern (concat "when-unfocused-" (symbol-name name))))) | 37 | (if (file-exists-p file) |
38 | `(progn | 38 | file |
39 | (defun ,func-name () "Defined by `when-unfocused'." | 39 | nil))) |
40 | (when (seq-every-p #'null | ||
41 | (mapcar #'frame-focus-state (frame-list))) | ||
42 | ,@forms)) | ||
43 | (add-function :after after-focus-change-function #',func-name)))) | ||
44 | 40 | ||
45 | (defmacro hook-defun (name hooks &rest forms) | 41 | (defmacro hook-defun (name hooks &rest forms) |
46 | "Define a function NAME that executes FORMS, and add it to | 42 | "Define a function NAME that executes FORMS, and add it to |
@@ -88,13 +84,17 @@ With a prefix argument, run git pull on the repo first." | |||
88 | (when (file-exists-p file) | 84 | (when (file-exists-p file) |
89 | (load-file file)))))) | 85 | (load-file file)))))) |
90 | 86 | ||
91 | (defun expand-file-name-exists-p (&rest expand-file-name-args) | 87 | (defmacro when-unfocused (name &rest forms) |
92 | "Call `expand-file-name' on EXPAND-FILE-NAME-ARGS, returning | 88 | "Define a function NAME, executing FORMS, that fires when Emacs |
93 | its name if it exists, or NIL otherwise." | 89 | is unfocused." |
94 | (let ((file (apply #'expand-file-name expand-file-name-args))) | 90 | (declare (indent 1)) |
95 | (if (file-exists-p file) | 91 | (let ((func-name (intern (concat "when-unfocused-" (symbol-name name))))) |
96 | file | 92 | `(progn |
97 | nil))) | 93 | (defun ,func-name () "Defined by `when-unfocused'." |
94 | (when (seq-every-p #'null | ||
95 | (mapcar #'frame-focus-state (frame-list))) | ||
96 | ,@forms)) | ||
97 | (add-function :after after-focus-change-function #',func-name)))) | ||
98 | 98 | ||
99 | (defmacro with-message (message &rest body) | 99 | (defmacro with-message (message &rest body) |
100 | "Execute BODY, messaging 'MESSAGE...' before and 'MESSAGE... Done.' after." | 100 | "Execute BODY, messaging 'MESSAGE...' before and 'MESSAGE... Done.' after." |
@@ -105,6 +105,9 @@ With a prefix argument, run git pull on the repo first." | |||
105 | ,@body) | 105 | ,@body) |
106 | (message "%s... Done." ,message))) | 106 | (message "%s... Done." ,message))) |
107 | 107 | ||
108 | |||
109 | ;;; Specialized functions | ||
110 | |||
108 | (defun acdw/dir (&optional file make-directory) | 111 | (defun acdw/dir (&optional file make-directory) |
109 | "Place Emacs files in one place. | 112 | "Place Emacs files in one place. |
110 | 113 | ||
@@ -122,37 +125,6 @@ if MAKE-DIRECTORY is non-nil." | |||
122 | file-name) | 125 | file-name) |
123 | dir))) | 126 | dir))) |
124 | 127 | ||
125 | (defun acdw/gc-enable () | ||
126 | "Enable the Garbage collector." | ||
127 | (setq gc-cons-threshold (* 800 1024 1024) | ||
128 | gc-cons-percentage 0.1)) | ||
129 | |||
130 | (defun acdw/gc-disable () | ||
131 | "Functionally disable the Garbage collector." | ||
132 | (setq gc-cons-threshold most-positive-fixnum | ||
133 | gc-cons-percentage 0.8)) | ||
134 | |||
135 | (defun acdw/sunrise-sunset (sunrise-command sunset-command) | ||
136 | "Run commands at sunrise and sunset." | ||
137 | (let* ((times-regex (rx (* nonl) | ||
138 | (: (any ?s ?S) "unrise") " " | ||
139 | (group (repeat 1 2 digit) ":" | ||
140 | (repeat 1 2 digit) | ||
141 | (: (any ?a ?A ?p ?P) (any ?m ?M))) | ||
142 | (* nonl) | ||
143 | (: (any ?s ?S) "unset") " " | ||
144 | (group (repeat 1 2 digit) ":" | ||
145 | (repeat 1 2 digit) | ||
146 | (: (any ?a ?A ?p ?P) (any ?m ?M))) | ||
147 | (* nonl))) | ||
148 | (ss (sunrise-sunset)) | ||
149 | (_m (string-match times-regex ss)) | ||
150 | (sunrise-time (match-string 1 ss)) | ||
151 | (sunset-time (match-string 2 ss))) | ||
152 | (run-at-time sunrise-time (* 60 60 24) sunrise-command) | ||
153 | (run-at-time sunset-time (* 60 60 24) sunset-command) | ||
154 | (run-at-time "12:00am" (* 60 60 24) sunset-command))) | ||
155 | |||
156 | (defun acdw/find-emacs-dotfiles () | 128 | (defun acdw/find-emacs-dotfiles () |
157 | "Finds lisp files in `user-emacs-directory' and passes them to | 129 | "Finds lisp files in `user-emacs-directory' and passes them to |
158 | `completing-read'." | 130 | `completing-read'." |
@@ -161,6 +133,30 @@ if MAKE-DIRECTORY is non-nil." | |||
161 | (directory-files-recursively | 133 | (directory-files-recursively |
162 | user-emacs-directory "\.el$")))) | 134 | user-emacs-directory "\.el$")))) |
163 | 135 | ||
136 | (defun acdw/find-emacs-source () | ||
137 | "Find where Emacs keeps its source tree." | ||
138 | (pcase acdw/system | ||
139 | (:work (expand-file-name | ||
140 | (concat "~/src/emacs-" emacs-version "/src"))) | ||
141 | (:home (expand-file-name "~/src/pkg/emacs/src/emacs-git/src")) | ||
142 | (:other nil))) | ||
143 | |||
144 | (defun acdw/gc-disable () | ||
145 | "Functionally disable the Garbage collector." | ||
146 | (setq gc-cons-threshold most-positive-fixnum | ||
147 | gc-cons-percentage 0.8)) | ||
148 | |||
149 | (defun acdw/gc-enable () | ||
150 | "Enable the Garbage collector." | ||
151 | (setq gc-cons-threshold (* 800 1024 1024) | ||
152 | gc-cons-percentage 0.1)) | ||
153 | |||
154 | (defun acdw/insert-iso-date (with-time) | ||
155 | "Insert the ISO-8601-formatted date, with optional time." | ||
156 | (interactive "P") | ||
157 | (let ((format (if with-time "%FT%T%z" "%F"))) | ||
158 | (insert (format-time-string format (current-time))))) | ||
159 | |||
164 | (defun acdw/kill-a-buffer (&optional prefix) | 160 | (defun acdw/kill-a-buffer (&optional prefix) |
165 | "Kill a buffer based on the following rules: | 161 | "Kill a buffer based on the following rules: |
166 | 162 | ||
@@ -179,14 +175,30 @@ Prompt only if there are unsaved changes." | |||
179 | (16 (mapc 'kill-buffer (delq (current-buffer) (buffer-list))) | 175 | (16 (mapc 'kill-buffer (delq (current-buffer) (buffer-list))) |
180 | (delete-other-windows)))) | 176 | (delete-other-windows)))) |
181 | 177 | ||
182 | (defun acdw/insert-iso-date (with-time) | 178 | (defun acdw/sunrise-sunset (sunrise-command sunset-command) |
183 | "Insert the ISO-8601-formatted date, with optional time." | 179 | "Run commands at sunrise and sunset." |
184 | (interactive "P") | 180 | (let* ((times-regex (rx (* nonl) |
185 | (let ((format (if with-time "%FT%T%z" "%F"))) | 181 | (: (any ?s ?S) "unrise") " " |
186 | (insert (format-time-string format (current-time))))) | 182 | (group (repeat 1 2 digit) ":" |
183 | (repeat 1 2 digit) | ||
184 | (: (any ?a ?A ?p ?P) (any ?m ?M))) | ||
185 | (* nonl) | ||
186 | (: (any ?s ?S) "unset") " " | ||
187 | (group (repeat 1 2 digit) ":" | ||
188 | (repeat 1 2 digit) | ||
189 | (: (any ?a ?A ?p ?P) (any ?m ?M))) | ||
190 | (* nonl))) | ||
191 | (ss (sunrise-sunset)) | ||
192 | (_m (string-match times-regex ss)) | ||
193 | (sunrise-time (match-string 1 ss)) | ||
194 | (sunset-time (match-string 2 ss))) | ||
195 | (run-at-time sunrise-time (* 60 60 24) sunrise-command) | ||
196 | (run-at-time sunset-time (* 60 60 24) sunset-command) | ||
197 | (run-at-time "12:00am" (* 60 60 24) sunset-command))) | ||
187 | 198 | ||
188 | 199 | ||
189 | ;; Make `C-z' more useful | 200 | ;;; Keymaps |
201 | |||
190 | (defvar acdw/leader | 202 | (defvar acdw/leader |
191 | (let ((map (make-sparse-keymap)) | 203 | (let ((map (make-sparse-keymap)) |
192 | (c-z (global-key-binding "\C-z"))) | 204 | (c-z (global-key-binding "\C-z"))) |
@@ -195,7 +207,8 @@ Prompt only if there are unsaved changes." | |||
195 | map)) | 207 | map)) |
196 | 208 | ||
197 | 209 | ||
198 | ;; `acdw/reading-mode' | 210 | ;;; Minor modes |
211 | |||
199 | (define-minor-mode acdw/reading-mode | 212 | (define-minor-mode acdw/reading-mode |
200 | "A mode for reading." | 213 | "A mode for reading." |
201 | :init-value nil | 214 | :init-value nil |