summary refs log tree commit diff stats
path: root/README.md
diff options
context:
space:
mode:
Diffstat (limited to 'README.md')
-rw-r--r--README.md1248
1 files changed, 745 insertions, 503 deletions
diff --git a/README.md b/README.md index d6b2457..44219ec 100644 --- a/README.md +++ b/README.md
@@ -1,13 +1,17 @@
1Why the hell not, let’s do this again. 1# Basics
2 2
3 3
4# Basics 4## Disclaimer
5
6 ;; config.el -*- lexical-binding: t -*-
7 ;; This file is automatically tangled from config.org.
8 ;; Hand edits will be overwritten!
5 9
6 10
7## About me 11## About me
8 12
9 (setq user-full-name "Case Duckworth" 13 (setq user-full-name "Case Duckworth"
10 user-mail-address "acdw@acdw.net") 14 user-mail-address "acdw@acdw.net")
11 15
12 16
13## Correct `exec-path` 17## Correct `exec-path`
@@ -16,25 +20,25 @@ Straight depends on Git, so I need to tell Emacs where different paths are.
16 20
17 (let ((win-downloads "c:/Users/aduckworth/Downloads")) 21 (let ((win-downloads "c:/Users/aduckworth/Downloads"))
18 (dolist (path (list 22 (dolist (path (list
19 ;; Linux 23 ;; Linux
20 (expand-file-name "bin" 24 (expand-file-name "bin"
21 user-emacs-directory) 25 user-emacs-directory)
22 (expand-file-name "~/bin") 26 (expand-file-name "~/bin")
23 (expand-file-name "~/.local/bin") 27 (expand-file-name "~/.local/bin")
24 (expand-file-name "~/Scripts") 28 (expand-file-name "~/Scripts")
25 ;; Windows 29 ;; Windows
26 (expand-file-name "emacs/bin" 30 (expand-file-name "emacs/bin"
27 win-downloads) 31 win-downloads)
28 (expand-file-name "m/usr/bin" 32 (expand-file-name "m/usr/bin"
29 win-downloads) 33 win-downloads)
30 (expand-file-name "m/mingw64/bin" 34 (expand-file-name "m/mingw64/bin"
31 win-downloads) 35 win-downloads)
32 (expand-file-name "PortableGit/bin" 36 (expand-file-name "PortableGit/bin"
33 win-downloads) 37 win-downloads)
34 (expand-file-name "PortableGit/usr/bin" 38 (expand-file-name "PortableGit/usr/bin"
35 win-downloads))) 39 win-downloads)))
36 (when (file-exists-p path) 40 (when (file-exists-p path)
37 (add-to-list 'exec-path path)))) 41 (add-to-list 'exec-path path))))
38 42
39 43
40## Package management 44## Package management
@@ -49,21 +53,21 @@ bootstrap code from straight's repo in a function.
49 "Bootstrap straight.el." 53 "Bootstrap straight.el."
50 (defvar bootstrap-version) 54 (defvar bootstrap-version)
51 (let ((bootstrap-file 55 (let ((bootstrap-file
52 (expand-file-name 56 (expand-file-name
53 "straight/repos/straight.el/bootstrap.el" 57 "straight/repos/straight.el/bootstrap.el"
54 user-emacs-directory)) 58 user-emacs-directory))
55 (bootstrap-version 5)) 59 (bootstrap-version 5))
56 (unless (file-exists-p bootstrap-file) 60 (unless (file-exists-p bootstrap-file)
57 (with-current-buffer 61 (with-current-buffer
58 (url-retrieve-synchronously 62 (url-retrieve-synchronously
59 (concat 63 (concat
60 "https://raw.githubusercontent.com/" 64 "https://raw.githubusercontent.com/"
61 "raxod502/straight.el/" 65 "raxod502/straight.el/"
62 "develop/install.el") 66 "develop/install.el")
63 'silent 'inhibit-cookies) 67 'silent 'inhibit-cookies)
64 (goto-char (point-max)) 68 (goto-char (point-max))
65 (eval-print-last-sexp))) 69 (eval-print-last-sexp)))
66 (load bootstrap-file nil 'nomessage))) 70 (load bootstrap-file nil 'nomessage)))
67 71
68Now, I'll *try* running it regular-style, ignoring the errors. If it 72Now, I'll *try* running it regular-style, ignoring the errors. If it
69doesn't work, I'll call git directly and clone the repo myself. 73doesn't work, I'll call git directly and clone the repo myself.
@@ -71,15 +75,15 @@ doesn't work, I'll call git directly and clone the repo myself.
71 (unless (ignore-errors (acdw/bootstrap-straight)) 75 (unless (ignore-errors (acdw/bootstrap-straight))
72 (message "%s" "Straight.el didn't bootstrap correctly. Cloning directly...") 76 (message "%s" "Straight.el didn't bootstrap correctly. Cloning directly...")
73 (call-process "git" nil 77 (call-process "git" nil
74 (get-buffer-create "*bootstrap-straight-messages*") nil 78 (get-buffer-create "*bootstrap-straight-messages*") nil
75 "clone" 79 "clone"
76 "https://github.com/raxod502/straight.el" 80 "https://github.com/raxod502/straight.el"
77 (expand-file-name "straight/repos/straight.el" 81 (expand-file-name "straight/repos/straight.el"
78 user-emacs-directory)) 82 user-emacs-directory))
79 (acdw/bootstrap-straight)) 83 (acdw/bootstrap-straight))
80 84
81 85
82## Customize macros 86## Ease-of-configuring functions
83 87
84 88
85### Emulate use-package’s `:custom` 89### Emulate use-package’s `:custom`
@@ -87,9 +91,9 @@ doesn't work, I'll call git directly and clone the repo myself.
87 (defmacro cuss (var val &optional docstring) 91 (defmacro cuss (var val &optional docstring)
88 "Basically, `:custom' from `use-package', but without `use-package'." 92 "Basically, `:custom' from `use-package', but without `use-package'."
89 (declare (doc-string 3) 93 (declare (doc-string 3)
90 (indent 2)) 94 (indent 2))
91 `(funcall (or (get ',var 'custom-set) #'set-default) 95 `(funcall (or (get ',var 'custom-set) #'set-default)
92 ',var ,val)) 96 ',var ,val))
93 97
94 98
95### Emulate use-package’s `:custom-face`, but better 99### Emulate use-package’s `:custom-face`, but better
@@ -109,6 +113,16 @@ doesn't work, I'll call git directly and clone the repo myself.
109 (add-hook 'after-init-hook #'acdw/set-custom-faces)) 113 (add-hook 'after-init-hook #'acdw/set-custom-faces))
110 114
111 115
116### Determine whether any Emacs frame is focused or not
117
118This comes in handy when I want to garbage collect, say, or save recent files.
119
120 (defun acdw/when-unfocused (func &rest args)
121 "Run FUNC with ARGS only if all frames are out of focus."
122 (if (seq-every-p #'null (mapcar #'frame-focus-state (frame-list)))
123 (apply func args)))
124
125
112## Clean `.emacs.d` 126## Clean `.emacs.d`
113 127
114 (straight-use-package 'no-littering) 128 (straight-use-package 'no-littering)
@@ -140,25 +154,13 @@ doesn't work, I'll call git directly and clone the repo myself.
140 154
1411. Tool bars and menu bars 1551. Tool bars and menu bars
142 156
143 (cuss default-frame-alist 157 (menu-bar-mode -1)
144 '((tool-bar-lines . 0) 158 (tool-bar-mode -1)
145 (menu-bar-lines .0))
146 "Setup the default frame alist.")
147
148 (menu-bar-mode -1)
149 (tool-bar-mode -1)
150 159
1512. Scroll bars 1602. Scroll bars
152 161
153 (add-to-list 'default-frame-alist 162 (scroll-bar-mode -1)
154 '(vertical-scroll-bars . nil)) 163 (horizontal-scroll-bar-mode -1)
155
156 (scroll-bar-mode -1)
157
158 (add-to-list 'default-frame-alist
159 '(horizontal-scroll-bars . nil))
160
161 (horizontal-scroll-bar-mode -1)
162 164
163 165
164### Dialogs 166### Dialogs
@@ -168,94 +170,130 @@ doesn't work, I'll call git directly and clone the repo myself.
168 170
1691. Yes or no questions 1711. Yes or no questions
170 172
171 (fset 'yes-or-no-p #'y-or-n-p) 173 (fset 'yes-or-no-p #'y-or-n-p)
172 174
1732. The Bell 1752. The Bell
174 176
175 (defun acdw/ring-bell-function () 177 from [EmacsWiki](https://www.emacswiki.org/emacs/AlarmBell#h5o-3).
176 "Ring the bell." 178
177 (let ((orig-face (face-foreground 'mode-line))) 179 (setq visible-bell nil
178 (set-face-foreground 'mode-line "#F2804F") 180 ring-bell-function 'flash-mode-line)
179 (run-with-idle-timer
180 0.1 nil
181 (lambda (fg)
182 (set-face-foreground 'mode-line fg))
183 orig-face)))
184 181
185 (cuss ring-bell-function #'acdw/ring-bell-function) 182 (defun flash-mode-line ()
183 (invert-face 'mode-line)
184 (run-with-timer 0.1 nil #'invert-face 'mode-line))
186 185
187 186
188### Frames 187### Frames
189 188
1901. Fringes 1891. Fringes
191 190
192 (cuss indicate-empty-lines t 191 (cuss indicate-empty-lines t
193 "Show an indicator on the left fringe of empty lines past the 192 "Show an indicator on the left fringe of empty lines past the
194 end of the buffer.") 193 end of the buffer.")
195 (cuss indicate-buffer-boundaries 'right 194 (cuss indicate-buffer-boundaries 'right
196 "Indicate the beginning and end of the buffer and whether it 195 "Indicate the beginning and end of the buffer and whether it
197 scrolls off-window in the right fringe.") 196 scrolls off-window in the right fringe.")
198 197
1992. Minibuffer 1982. Minibuffer
200 199
201 (cuss minibuffer-prompt-properties 200 (cuss minibuffer-prompt-properties
202 '(read-only t cursor-intangible t face minibuffer-prompt) 201 '(read-only t cursor-intangible t face minibuffer-prompt)
203 "Keep the cursor away from the minibuffer prompt.") 202 "Keep the cursor away from the minibuffer prompt.")
204 203
2053. Tabs 2043. Tabs
206 205
207 (cuss tab-bar-tab-name-function 206 (cuss tab-bar-tab-name-function
208 #'tab-bar-tab-name-current-with-count 207 #'tab-bar-tab-name-current-with-count
209 "Show the tab name as the name of the current buffer, plus a 208 "Show the tab name as the name of the current buffer, plus a
210 count of the windows in the tab.") 209 count of the windows in the tab.")
211 210
212 (cuss tab-bar-show 1 211 (cuss tab-bar-show 1
213 "Show the tab bar, when there's more than one tab.") 212 "Show the tab bar, when there's more than one tab.")
214 213
215 214
216### Windows 215### Windows
217 216
2181. Winner mode 2171. Winner mode
219 218
220 (when (fboundp 'winner-mode) 219 (when (fboundp 'winner-mode)
221 (winner-mode +1)) 220 (winner-mode +1))
222 221
2232. Switch windows 2222. Switch windows
224 223
225 (global-set-key (kbd "M-o") #'other-window) 224 (global-set-key (kbd "M-o") #'other-window)
226 225
227 226
228### Buffers 227### Buffers
229 228
2301. Uniquify buffers 2291. Uniquify buffers
231 230
232 (require 'uniquify) 231 (require 'uniquify)
233 (cuss uniquify-buffer-name-style 'forward 232 (cuss uniquify-buffer-name-style 'forward
234 "Uniquify buffers' names by going up the path trees until they 233 "Uniquify buffers' names by going up the path trees until they
235 become unique.") 234 become unique.")
236 235
2372. Startup buffers 2362. Startup buffers
238 237
239 (cuss inhibit-startup-screen t 238 (cuss inhibit-startup-screen t
240 "Don't show Emacs' startup buffer.") 239 "Don't show Emacs' startup buffer.")
241 240
242 (cuss initial-buffer-choice t 241 (cuss initial-buffer-choice t
243 "Start with *scratch*.") 242 "Start with *scratch*.")
244 243
245 (cuss initial-scratch-message "" 244 (cuss initial-scratch-message ""
246 "Empty *scratch* buffer.") 245 "Empty *scratch* buffer.")
246
2473. Kill the current buffer
248
249 (defun acdw/kill-a-buffer (&optional prefix)
250 "Kill a buffer based on the following rules:
251
252 C-x k ⇒ Kill current buffer & window
253 C-u C-x k ⇒ Kill OTHER window and its buffer
254 C-u C-u C-x C-k ⇒ Kill all other buffers and windows
255
256 Prompt only if there are unsaved changes."
257 (interactive "P")
258 (pcase (or (car prefix) 0)
259 ;; C-x k ⇒ Kill current buffer & window
260 (0 (kill-current-buffer)
261 (unless (one-window-p) (delete-window)))
262 ;; C-u C-x k ⇒ Kill OTHER window and its buffer
263 (4 (other-window 1)
264 (kill-current-buffer)
265 (unless (one-window-p) (delete-window)))
266 ;; C-u C-u C-x C-k ⇒ Kill all other buffers and windows
267 (16 (mapc 'kill-buffer (delq (current-buffer) (buffer-list)))
268 (delete-other-windows))))
269
270 (define-key ctl-x-map "k" #'acdw/kill-a-buffer)
271
272 1. Remap `C-x M-k` to bring up the buffer-killing menu
273
274 (define-key ctl-x-map (kbd "M-k") #'kill-buffer)
275
2764. Immortal `*scratch*` buffer
277
278 (defun immortal-scratch ()
279 (if (eq (current-buffer) (get-buffer "*scratch*"))
280 (progn (bury-buffer)
281 nil)
282 t))
283
284 (add-hook 'kill-buffer-query-functions 'immortal-scratch)
247 285
248 286
249### Modeline 287### Modeline
250 288
2511. Smart mode line 2891. Smart mode line
252 290
253 (straight-use-package 'smart-mode-line) 291 (straight-use-package 'smart-mode-line)
254 292
255 (cuss sml/no-confirm-load-theme t 293 (cuss sml/no-confirm-load-theme t
256 "Pass the NO-CONFIRM flag to `load-theme'.") 294 "Pass the NO-CONFIRM flag to `load-theme'.")
257 295
258 (sml/setup) 296 (sml/setup)
259 297
2602. Rich minority 2982. Rich minority
261 299
@@ -263,112 +301,129 @@ doesn't work, I'll call git directly and clone the repo myself.
263 instead of `diminish` or another package. I do have to write this 301 instead of `diminish` or another package. I do have to write this
264 helper function, though, to add things to the whitelist. 302 helper function, though, to add things to the whitelist.
265 303
266 (defun rm/whitelist-add (regexp) 304 (defun rm/whitelist-add (regexp)
267 "Add a REGEXP to the whitelist for `rich-minority'." 305 "Add a REGEXP to the whitelist for `rich-minority'."
268 (if (listp 'rm--whitelist-regexps) 306 (if (listp 'rm--whitelist-regexps)
269 (add-to-list 'rm--whitelist-regexps regexp) 307 (add-to-list 'rm--whitelist-regexps regexp)
270 (setq rm--whitelist-regexps `(,regexp))) 308 (setq rm--whitelist-regexps `(,regexp)))
271 (setq rm-whitelist 309 (setq rm-whitelist
272 (mapconcat 'identity rm--whitelist-regexps "\\|"))) 310 (mapconcat 'identity rm--whitelist-regexps "\\|")))
273 311
274 (straight-use-package 'rich-minority) 312 (straight-use-package 'rich-minority)
275 313
276 (rm/whitelist-add "^$") 314 (rm/whitelist-add "^$")
277 315
278 316
279### Theme 317### Theme
280 318
2811. Modus Themes 3191. Modus Themes
282 320
283 (straight-use-package 'modus-themes) 321 (straight-use-package 'modus-themes)
322
323 (cuss modus-themes-slanted-constructs t
324 "Use more slanted constructs.")
325 (cuss modus-themes-bold-constructs t
326 "Use more bold constructs.")
284 327
285 (cuss modus-themes-slanted-constructs t 328 (cuss modus-themes-region 'bg-only
286 "Use more slanted constructs.") 329 "Only highlight the background of the selected region.")
287 (cuss modus-themes-bold-constructs t
288 "Use more bold constructs.")
289 330
290 (cuss modus-themes-region 'bg-only 331 (cuss modus-themes-org-blocks 'grayscale
291 "Only highlight the background of the selected region.") 332 "Show org-blocks with a grayscale background.")
333 (cuss modus-themes-headings
334 '((1 . line)
335 (t . t))
336 "Highlight top headings with `line' style, and others by default.")
292 337
293 (cuss modus-themes-org-blocks 'grayscale 338 (cuss modus-themes-scale-headings t
294 "Show org-blocks with a grayscale background.") 339 "Scale headings by the ratios below.")
295 (cuss modus-themes-headings 340 (cuss modus-themes-scale-1 1.1)
296 '((1 . line) 341 (cuss modus-themes-scale-2 1.15)
297 (t . t)) 342 (cuss modus-themes-scale-3 1.21)
298 "Highlight top headings with `line' style, and others by default.") 343 (cuss modus-themes-scale-4 1.27)
344 (cuss modus-themes-scale-5 1.33)
299 345
300 (cuss modus-themes-scale-headings t 346 (load-theme 'modus-operandi t)
301 "Scale headings by the ratios below.") 347
302 (cuss modus-themes-scale-1 1.1) 3482. Change themes based on time of day
303 (cuss modus-themes-scale-2 1.15) 349
304 (cuss modus-themes-scale-3 1.21) 350 (cuss calendar-latitude 30.4515)
305 (cuss modus-themes-scale-4 1.27) 351 (cuss calendar-longitude -91.1871)
306 (cuss modus-themes-scale-5 1.33) 352
353 ;; sunrise
354 (run-at-time (nth 1 (split-string (sunrise-sunset)))
355 (* 60 60 24)
356 (lambda ()
357 (modus-themes-load-operandi)))
307 358
308 (load-theme 'modus-operandi t) 359 ;; sunset
360 (run-at-time (nth 4 (split-string (sunrise-sunset)))
361 (* 60 60 24)
362 (lambda ()
363 (modus-themes-load-vivendi)))
309 364
310 365
311### Fonts 366### Fonts
312 367
3131. Define fonts 3681. Define fonts
314 369
315 (defun set-face-from-alternatives (face fonts) 370 (defun set-face-from-alternatives (face fonts)
316 (catch :return 371 (catch :return
317 (dolist (font fonts) 372 (dolist (font fonts)
318 (when (find-font (font-spec :family (car font))) 373 (when (find-font (font-spec :family (car font)))
319 (apply #'set-face-attribute `(,face 374 (apply #'set-face-attribute `(,face
320 nil 375 nil
321 :family (car font) 376 :family (car font)
322 ,@(cdr font))) 377 ,@(cdr font)))
323 (throw :return font))))) 378 (throw :return font)))))
324 379
325 (defun acdw/setup-fonts () 380 (defun acdw/setup-fonts ()
326 "Setup fonts. This has to happen after the frame is setup for 381 "Setup fonts. This has to happen after the frame is setup for
327 the first time, so it should be added to `window-setup-hook'. It 382 the first time, so it should be added to `window-setup-hook'. It
328 removes itself from that hook." 383 removes itself from that hook."
329 (interactive) 384 (interactive)
330 (when (display-graphic-p) 385 (when (display-graphic-p)
331 (set-face-from-alternatives 'default 386 (set-face-from-alternatives 'default
332 '(("Input Mono" 387 '(("Input Mono"
333 :height 105) 388 :height 105)
334 ("Go Mono" 389 ("Go Mono"
335 :height 100) 390 :height 100)
336 ("Consolas" 391 ("Consolas"
337 :height 100))) 392 :height 100)))
338 393
339 (set-face-from-alternatives 'fixed-pitch 394 (set-face-from-alternatives 'fixed-pitch
340 '(("Input Mono") 395 '(("Input Mono")
341 ("Go Mono") 396 ("Go Mono")
342 ("Consolas"))) 397 ("Consolas")))
343 398
344 (set-face-from-alternatives 'variable-pitch 399 (set-face-from-alternatives 'variable-pitch
345 '(("Input Serif") 400 '(("Input Serif")
346 ("Georgia"))) 401 ("Georgia")))
347 402
348 (remove-function after-focus-change-function #'acdw/setup-fonts))) 403 (remove-function after-focus-change-function #'acdw/setup-fonts)))
349 404
350 (add-function :before after-focus-change-function #'acdw/setup-fonts) 405 (add-function :before after-focus-change-function #'acdw/setup-fonts)
351 406
3522. Custom faces 4072. Custom faces
353 408
354 (cussface '(font-lock-comment-face 409 (cussface '(font-lock-comment-face
355 ((t (:inherit (custom-comment italic variable-pitch)))))) 410 ((t (:inherit (custom-comment italic variable-pitch))))))
356 411
3573. Line spacing 4123. Line spacing
358 413
359 (cuss line-spacing 0.1 414 (cuss line-spacing 0.1
360 "Add 10% extra space below each line.") 415 "Add 10% extra space below each line.")
361 416
3624. Underlines 4174. Underlines
363 418
364 (cuss x-underline-at-descent-line t 419 (cuss x-underline-at-descent-line t
365 "Draw the underline at the same place as the descent line.") 420 "Draw the underline at the same place as the descent line.")
366 421
3675. Unicode Fonts 4225. Unicode Fonts
368 423
369 (straight-use-package 'unicode-fonts) 424 (straight-use-package 'unicode-fonts)
370 (require 'unicode-fonts) 425 (require 'unicode-fonts)
371 (unicode-fonts-setup) 426 (unicode-fonts-setup)
372 427
373 428
374## Interactivity 429## Interactivity
@@ -378,81 +433,81 @@ doesn't work, I'll call git directly and clone the repo myself.
378 433
3791. Shadow file names in `completing-read`. 4341. Shadow file names in `completing-read`.
380 435
381 (cuss file-name-shadow-properties '(invisible t)) 436 (cuss file-name-shadow-properties '(invisible t))
382 437
383 (file-name-shadow-mode +1) 438 (file-name-shadow-mode +1)
384 439
3852. Ignore case in `completing-read` 4402. Ignore case in `completing-read`
386 441
387 (cuss completion-ignore-case t) 442 (cuss completion-ignore-case t)
388 (cuss read-buffer-completion-ignore-case t) 443 (cuss read-buffer-completion-ignore-case t)
389 (cuss read-file-name-completion-ignore-case t) 444 (cuss read-file-name-completion-ignore-case t)
390 445
3913. Minibuffer recursivity 4463. Minibuffer recursivity
392 447
393 (cuss enable-recursive-minibuffers t) 448 (cuss enable-recursive-minibuffers t)
394 (minibuffer-depth-indicate-mode +1) 449 (minibuffer-depth-indicate-mode +1)
395 450
3964. Selectrum 4514. Selectrum
397 452
398 (straight-use-package 'selectrum) 453 (straight-use-package 'selectrum)
399 (require 'selectrum) 454 (require 'selectrum)
400 (selectrum-mode +1) 455 (selectrum-mode +1)
401 456
4025. Prescient 4575. Prescient
403 458
404 (straight-use-package 'prescient) 459 (straight-use-package 'prescient)
405 (require 'prescient) 460 (require 'prescient)
406 (prescient-persist-mode +1) 461 (prescient-persist-mode +1)
407 462
408 (straight-use-package 'selectrum-prescient) 463 (straight-use-package 'selectrum-prescient)
409 (require 'selectrum-prescient) 464 (require 'selectrum-prescient)
410 (selectrum-prescient-mode +1) 465 (selectrum-prescient-mode +1)
411 466
4126. Consult 4676. Consult
413 468
414 (straight-use-package '(consult 469 (straight-use-package '(consult
415 :host github 470 :host github
416 :repo "minad/consult")) 471 :repo "minad/consult"))
417 (require 'consult) 472 (require 'consult)
418 473
419 (straight-use-package '(consult-selectrum 474 (straight-use-package '(consult-selectrum
420 :host github 475 :host github
421 :repo "minad/consult")) 476 :repo "minad/consult"))
422 (require 'consult-selectrum) 477 (require 'consult-selectrum)
423 478
424 (with-eval-after-load 'consult 479 (with-eval-after-load 'consult
425 (define-key ctl-x-map "b" #'consult-buffer) 480 (define-key ctl-x-map "b" #'consult-buffer)
426 (define-key ctl-x-map (kbd "C-r") #'consult-buffer) 481 (define-key ctl-x-map (kbd "C-r") #'consult-buffer)
427 (define-key ctl-x-map "4b" #'consult-buffer-other-window) 482 (define-key ctl-x-map "4b" #'consult-buffer-other-window)
428 (define-key ctl-x-map "5b" #'consult-buffer-other-frame) 483 (define-key ctl-x-map "5b" #'consult-buffer-other-frame)
429 484
430 (define-key goto-map "o" #'consult-outline) 485 (define-key goto-map "o" #'consult-outline)
431 (define-key goto-map "g" #'consult-line) 486 (define-key goto-map "g" #'consult-line)
432 (define-key goto-map (kbd "M-g") #'consult-line) 487 (define-key goto-map (kbd "M-g") #'consult-line)
433 (define-key goto-map "l" #'consult-line) 488 (define-key goto-map "l" #'consult-line)
434 (define-key goto-map "m" #'consult-mark) 489 (define-key goto-map "m" #'consult-mark)
435 (define-key goto-map "i" #'consult-imenu) 490 (define-key goto-map "i" #'consult-imenu)
436 (define-key goto-map "e" #'consult-error) 491 (define-key goto-map "e" #'consult-error)
437 492
438 (global-set-key (kbd "M-y") #'consult-yank-pop) 493 (global-set-key (kbd "M-y") #'consult-yank-pop)
439 494
440 (define-key help-map "a" #'consult-apropos) 495 (define-key help-map "a" #'consult-apropos)
441 496
442 (fset 'multi-occur #'consult-multi-occur)) 497 (fset 'multi-occur #'consult-multi-occur))
443 498
4447. Marginalia 4997. Marginalia
445 500
446 (straight-use-package '(marginalia 501 (straight-use-package '(marginalia
447 :host github 502 :host github
448 :repo "minad/marginalia" 503 :repo "minad/marginalia"
449 :branch "main")) 504 :branch "main"))
450 505
451 (cuss marginalia-annotators 506 (cuss marginalia-annotators
452 '(marginalia-annotators-heavy 507 '(marginalia-annotators-heavy
453 marginalia-annotators-light)) 508 marginalia-annotators-light))
454 509
455 (marginalia-mode +1) 510 (marginalia-mode +1)
456 511
457 512
458### Completion 513### Completion
@@ -460,6 +515,20 @@ doesn't work, I'll call git directly and clone the repo myself.
460 (global-set-key (kbd "M-/") #'hippie-expand) 515 (global-set-key (kbd "M-/") #'hippie-expand)
461 516
462 517
518### Garbage collection
519
520 (straight-use-package 'gcmh)
521 (gcmh-mode +1)
522
523 (defun dotfiles--gc-on-last-frame-out-of-focus ()
524 "GC if all frames are inactive."
525 (if (seq-every-p #'null (mapcar #'frame-focus-state (frame-list)))
526 (garbage-collect)))
527
528 (add-function :after after-focus-change-function
529 #'dotfiles--gc-on-last-frame-out-of-focus)
530
531
463## Keyboard 532## Keyboard
464 533
465 534
@@ -472,15 +541,21 @@ doesn't work, I'll call git directly and clone the repo myself.
472 541
473 (defvar acdw/map 542 (defvar acdw/map
474 (let ((map (make-sparse-keymap)) 543 (let ((map (make-sparse-keymap))
475 (c-z (global-key-binding "\C-z"))) 544 (c-z (global-key-binding "\C-z")))
476 (global-unset-key "\C-z") 545 (global-unset-key "\C-z")
477 (define-key global-map "\C-z" map) 546 (define-key global-map "\C-z" map)
478 (define-key map "\C-z" c-z) 547 (define-key map "\C-z" c-z)
479 map)) 548 map))
480 549
481 (run-hooks 'acdw/map-defined-hook) 550 (run-hooks 'acdw/map-defined-hook)
482 551
483 552
553### Show keybindings
554
555 (straight-use-package 'which-key)
556 (which-key-mode +1)
557
558
484## Mouse 559## Mouse
485 560
486 561
@@ -494,7 +569,7 @@ from [u/TheFrenchPoulp](https://www.reddit.com/r/emacs/comments/km9by4/weekly_ti
494 "Like `mwheel-scroll' but preserve screen position. 569 "Like `mwheel-scroll' but preserve screen position.
495 See `scroll-preserve-screen-position'." 570 See `scroll-preserve-screen-position'."
496 (let ((scroll-preserve-screen-position :always)) 571 (let ((scroll-preserve-screen-position :always))
497 (apply original arguments))) 572 (apply original arguments)))
498 573
499 574
500## Persistence 575## Persistence
@@ -505,9 +580,9 @@ from [u/TheFrenchPoulp](https://www.reddit.com/r/emacs/comments/km9by4/weekly_ti
505 (require 'savehist) 580 (require 'savehist)
506 581
507 (cuss savehist-additional-variables 582 (cuss savehist-additional-variables
508 '(kill-ring 583 '(kill-ring
509 search-ring 584 search-ring
510 regexp-search-ring) 585 regexp-search-ring)
511 "Other variables to save alongside the minibuffer history.") 586 "Other variables to save alongside the minibuffer history.")
512 587
513 (cuss history-length t 588 (cuss history-length t
@@ -544,8 +619,20 @@ from [u/TheFrenchPoulp](https://www.reddit.com/r/emacs/comments/km9by4/weekly_ti
544 619
545 (recentf-mode +1) 620 (recentf-mode +1)
546 621
547 ;; save the recentf-list every 5 minutes 622 ;; save recentf list when focusing away
548 (run-at-time nil (* 5 60) 'recentf-save-list) 623 (defun acdw/maybe-save-recentf ()
624 "Save `recentf-file' when out of focus, but only if we haven't
625 in five minutes."
626 (defvar recentf-last-save (time-convert nil 'integer)
627 "How long it's been since we last saved the recentf list.")
628
629 (when (> (time-convert (time-since recentf-last-save) 'integer)
630 (* 60 5))
631 (setq recentf-last-save (time-convert nil 'integer))
632 (acdw/when-unfocused #'recentf-save-list)))
633
634 (add-function :after after-focus-change-function
635 #'acdw/maybe-save-recentf)
549 636
550 637
551## Undo 638## Undo
@@ -560,14 +647,14 @@ from [u/TheFrenchPoulp](https://www.reddit.com/r/emacs/comments/km9by4/weekly_ti
560 (require 'undo-fu-session) 647 (require 'undo-fu-session)
561 648
562 (cuss undo-fu-session-incompatible-files 649 (cuss undo-fu-session-incompatible-files
563 '("/COMMIT_EDITMSG\\'" 650 '("/COMMIT_EDITMSG\\'"
564 "/git-rebase-todo\\'") 651 "/git-rebase-todo\\'")
565 "A list of files that are incompatible with the concept of undo sessions.") 652 "A list of files that are incompatible with the concept of undo sessions.")
566 653
567 (with-eval-after-load 'no-littering 654 (with-eval-after-load 'no-littering
568 (let ((dir (no-littering-expand-var-file-name "undos"))) 655 (let ((dir (no-littering-expand-var-file-name "undos")))
569 (make-directory dir 'parents) 656 (make-directory dir 'parents)
570 (cuss undo-fu-session-directory dir))) 657 (cuss undo-fu-session-directory dir)))
571 658
572 (global-undo-fu-session-mode +1) 659 (global-undo-fu-session-mode +1)
573 660
@@ -579,30 +666,30 @@ from [u/TheFrenchPoulp](https://www.reddit.com/r/emacs/comments/km9by4/weekly_ti
579 666
5801. UTF-8 6671. UTF-8
581 668
582 (set-language-environment "UTF-8") 669 (set-language-environment "UTF-8")
583 (set-terminal-coding-system 'utf-8) 670 (set-terminal-coding-system 'utf-8)
584 (cuss locale-coding-system 'utf-8) 671 (cuss locale-coding-system 'utf-8)
585 (set-default-coding-systems 'utf-8) 672 (set-default-coding-systems 'utf-8)
586 (set-selection-coding-system 'utf-8) 673 (set-selection-coding-system 'utf-8)
587 (prefer-coding-system 'utf-8) 674 (prefer-coding-system 'utf-8)
588 675
5892. Convert all files to UNIX-style line endings 6762. Convert all files to UNIX-style line endings
590 677
591 from [Emacs Wiki](https://www.emacswiki.org/emacs/EndOfLineTips). 678 from [Emacs Wiki](https://www.emacswiki.org/emacs/EndOfLineTips).
592 679
593 (defun ewiki/no-junk-please-were-unixish () 680 (defun ewiki/no-junk-please-were-unixish ()
594 "Convert line endings to UNIX, dammit." 681 "Convert line endings to UNIX, dammit."
595 (let ((coding-str (symbol-name buffer-file-coding-system))) 682 (let ((coding-str (symbol-name buffer-file-coding-system)))
596 (when (string-match "-\\(?:dos\\|mac\\)$" coding-str) 683 (when (string-match "-\\(?:dos\\|mac\\)$" coding-str)
597 (set-buffer-file-coding-system 'unix)))) 684 (set-buffer-file-coding-system 'unix))))
598 685
599 I add it to the `find-file-hook` *and* `before-save-hook` because I 686 I add it to the `find-file-hook` *and* `before-save-hook` because I
600 don't want to ever work with anything other than UNIX line endings 687 don't want to ever work with anything other than UNIX line endings
601 ever again. I just don't care. Even Microsoft Notepad can handle 688 ever again. I just don't care. Even Microsoft Notepad can handle
602 UNIX line endings, so I don't want to hear it. 689 UNIX line endings, so I don't want to hear it.
603 690
604 (add-hook 'find-file-hook #'ewiki/no-junk-please-were-unixish) 691 (add-hook 'find-file-hook #'ewiki/no-junk-please-were-unixish)
605 (add-hook 'before-save-hook #'ewiki/no-junk-please-were-unixish) 692 (add-hook 'before-save-hook #'ewiki/no-junk-please-were-unixish)
606 693
607 694
608### Backups 695### Backups
@@ -614,18 +701,18 @@ from [u/TheFrenchPoulp](https://www.reddit.com/r/emacs/comments/km9by4/weekly_ti
614 701
615 (with-eval-after-load 'no-littering 702 (with-eval-after-load 'no-littering
616 (let ((dir (no-littering-expand-var-file-name "backup"))) 703 (let ((dir (no-littering-expand-var-file-name "backup")))
617 (make-directory dir 'parents) 704 (make-directory dir 'parents)
618 (cuss backup-directory-alist 705 (cuss backup-directory-alist
619 `((".*" . ,dir))))) 706 `((".*" . ,dir)))))
620 707
621 708
622### Auto-saves 709### Auto-saves
623 710
624 (with-eval-after-load 'no-littering 711 (with-eval-after-load 'no-littering
625 (let ((dir (no-littering-expand-var-file-name "autosaves"))) 712 (let ((dir (no-littering-expand-var-file-name "autosaves")))
626 (make-directory dir 'parents) 713 (make-directory dir 'parents)
627 (cuss auto-save-file-name-transforms 714 (cuss auto-save-file-name-transforms
628 `((".*" ,dir t)))) 715 `((".*" ,dir t))))
629 716
630 (auto-save-visited-mode +1)) 717 (auto-save-visited-mode +1))
631 718
@@ -663,41 +750,165 @@ from [u/TheFrenchPoulp](https://www.reddit.com/r/emacs/comments/km9by4/weekly_ti
663 750
6641. Replace selection when typing 7511. Replace selection when typing
665 752
666 (delete-selection-mode +1) 753 (delete-selection-mode +1)
667 754
6682. Work better with the system clipboard 7552. Work better with the system clipboard
669 756
670 (cuss save-interprogram-paste-before-kill t 757 (cuss save-interprogram-paste-before-kill t
671 "Save existing clipboard text into the kill ring before 758 "Save existing clipboard text into the kill ring before
672 replacing it.") 759 replacing it.")
673 760
674 (cuss yank-pop-change-selection t 761 (cuss yank-pop-change-selection t
675 "Update the X selection when rotating the kill ring.") 762 "Update the X selection when rotating the kill ring.")
676 763
677 764
678### Searching & Replacing 765### Searching & Replacing
679 766
6801. Replace with Anzu 7671. Replace with Anzu
681 768
682 (straight-use-package 'anzu) 769 (straight-use-package 'anzu)
683 (require 'anzu) 770 (require 'anzu)
684 771
685 ;; show search count in the modeline 772 ;; show search count in the modeline
686 (global-anzu-mode +1) 773 (global-anzu-mode +1)
687 774
688 (cuss anzu-replace-to-string-separator " → " 775 (cuss anzu-replace-to-string-separator " → "
689 "What to separate the search from the replacement.") 776 "What to separate the search from the replacement.")
690 777
691 (global-set-key [remap query-replace] #'anzu-query-replace) 778 (global-set-key [remap query-replace] #'anzu-query-replace)
692 (global-set-key [remap query-replace-regexp] #'anzu-query-replace-regexp) 779 (global-set-key [remap query-replace-regexp] #'anzu-query-replace-regexp)
693 780
694 (define-key isearch-mode-map [remap isearch-query-replace] #'anzu-isearch-query-replace) 781 (define-key isearch-mode-map [remap isearch-query-replace] #'anzu-isearch-query-replace)
695 (define-key isearch-mode-map [remap isearch-query-replace-regexp] #'anzu-isearch-query-replace-regexp) 782 (define-key isearch-mode-map [remap isearch-query-replace-regexp] #'anzu-isearch-query-replace-regexp)
783
784
785### Overwrite mode
786
787 (defun acdw/overwrite-mode-change-cursor ()
788 (setq cursor-type (if overwrite-mode t 'bar)))
789
790 (add-hook 'overwrite-mode-hook #'acdw/overwrite-mode-change-cursor)
791
792 (rm/whitelist-add "Ovwrt")
793
794
795### The Mark
796
797 (cuss set-mark-repeat-command-pop t
798 "Repeat `set-mark-command' with a prefix argument, without
799 repeatedly entering the prefix argument.")
800
801
802### Whitespace
803
804 (cuss whitespace-style
805 '(empty ;; remove blank lines at the beginning and end of buffers
806 indentation ;; clean up indentation
807 space-before-tab ;; fix mixed spaces and tabs
808 space-after-tab))
809
810 (add-hook 'before-save-hook #'whitespace-cleanup)
696 811
697 812
698# Programming 813# Programming
699 814
700 815
816## Prettify symbols
817
818
819### A bit from Rasmus
820
821from [Rasmus Roulund](https://pank.eu/blog/pretty-babel-src-blocks.html#coderef-symbol), via [Musa Al-hassy](https://alhassy.github.io/emacs.d/index.html#Making-Block-Delimiters-Less-Intrusive).
822
823 (defvar-local rasmus/org-at-src-begin -1
824 "Variable that holds whether last position was a ")
825
826 (defvar rasmus/ob-header-symbol ?☰
827 "Symbol used for babel headers")
828
829 (defun rasmus/org-prettify-src--update ()
830 (let ((case-fold-search t)
831 (re "^[ \t]*#\\+begin_src[ \t]+[^ \f\t\n\r\v]+[ \t]*")
832 found)
833 (save-excursion
834 (goto-char (point-min))
835 (while (re-search-forward re nil t)
836 (goto-char (match-end 0))
837 (let ((args (org-trim
838 (buffer-substring-no-properties (point)
839 (line-end-position)))))
840 (when (org-string-nw-p args)
841 (let ((new-cell (cons args rasmus/ob-header-symbol)))
842 (cl-pushnew new-cell prettify-symbols-alist :test #'equal)
843 (cl-pushnew new-cell found :test #'equal)))))
844 (setq prettify-symbols-alist
845 (cl-set-difference prettify-symbols-alist
846 (cl-set-difference
847 (cl-remove-if-not
848 (lambda (elm)
849 (eq (cdr elm) rasmus/ob-header-symbol))
850 prettify-symbols-alist)
851 found :test #'equal)))
852 ;; Clean up old font-lock-keywords.
853 (font-lock-remove-keywords nil prettify-symbols--keywords)
854 (setq prettify-symbols--keywords (prettify-symbols--make-keywords))
855 (font-lock-add-keywords nil prettify-symbols--keywords)
856 (while (re-search-forward re nil t)
857 (font-lock-flush (line-beginning-position) (line-end-position))))))
858
859 (defun rasmus/org-prettify-src ()
860 "Hide src options via `prettify-symbols-mode'.
861
862 `prettify-symbols-mode' is used because it has uncollpasing. It's
863 may not be efficient."
864 (let* ((case-fold-search t)
865 (at-src-block (save-excursion
866 (beginning-of-line)
867 (looking-at "^[ \t]*#\\+begin_src[ \t]+[^ \f\t\n\r\v]+[ \t]*"))))
868 ;; Test if we moved out of a block.
869 (when (or (and rasmus/org-at-src-begin
870 (not at-src-block))
871 ;; File was just opened.
872 (eq rasmus/org-at-src-begin -1))
873 (rasmus/org-prettify-src--update))
874 ;; Remove composition if at line; doesn't work properly.
875 ;; (when at-src-block
876 ;; (with-silent-modifications
877 ;; (remove-text-properties (match-end 0)
878 ;; (1+ (line-end-position))
879 ;; '(composition))))
880 (setq rasmus/org-at-src-begin at-src-block)))
881
882 (defun rasmus/org-prettify-symbols ()
883 (mapc (apply-partially 'add-to-list 'prettify-symbols-alist)
884 (cl-reduce 'append
885 (mapcar (lambda (x) (list x (cons (upcase (car x)) (cdr x))))
886 `(("#+begin_src" . ?✎) ;; ➤ 🖝 ➟ ➤ ✎
887 ("#+end_src" . ?⏹) ;; □
888 ("#+header:" . ,rasmus/ob-header-symbol)
889 ("#+begin_quote" . ?»)
890 ("#+end_quote" . ?«)))))
891 (turn-on-prettify-symbols-mode)
892 (add-hook 'post-command-hook 'rasmus/org-prettify-src t t))
893
894
895### Regular prettify-symbols-mode
896
897 (cuss prettify-symbols-unprettify-at-point 'right-edge
898 "Unprettify a symbol when inside it or next to it.")
899
900 (defun acdw/org-mode-prettify ()
901 "Prettify `org-mode'."
902 (append '(("[ ]" . ?□) ("[X]" . ?☑) ("[-]" . ?◐)
903 ("#begin_src" . ?🖬) ("#BEGIN_SRC" . ?🖬)
904 ("#end_src" . ?■) ("#END_SRC" . ?■))
905 prettify-symbols-alist))
906
907 (add-hook 'org-mode-hook #'acdw/org-mode-prettify)
908
909 (global-prettify-symbols-mode +1)
910
911
701## Parentheses 912## Parentheses
702 913
703 914
@@ -747,46 +958,22 @@ from [u/TheFrenchPoulp](https://www.reddit.com/r/emacs/comments/km9by4/weekly_ti
747 958
748This has to be done *before* loading the package. It's included in `visual-fill-column`, too, but for some reason isn't loaded there. 959This has to be done *before* loading the package. It's included in `visual-fill-column`, too, but for some reason isn't loaded there.
749 960
750 (global-set-key [right-margin mouse-1] (global-key-binding [mouse-1])) ; #'mouse-set-point 961 (dolist (margin '(right-margin left-margin))
751 (global-set-key [right-margin mouse-2] (global-key-binding [mouse-2])) ; #'mouse-yank-primary 962 (dolist (button '(mouse-1 mouse-2 mouse-3))
752 (global-set-key [right-margin mouse-3] (global-key-binding [mouse-3])) ; #'mouse-save-then-kill 963 (global-set-key (vector margin button)
753 (global-set-key [right-margin drag-mouse-1] #'ignore) 964 (global-key-binding (vector button)))))
754 (global-set-key [right-margin drag-mouse-2] #'ignore)
755 (global-set-key [right-margin drag-mouse-3] #'ignore)
756 (global-set-key [right-margin double-mouse-1] #'ignore)
757 (global-set-key [right-margin double-mouse-2] #'ignore)
758 (global-set-key [right-margin double-mouse-3] #'ignore)
759 (global-set-key [right-margin triple-mouse-1] #'ignore)
760 (global-set-key [right-margin triple-mouse-2] #'ignore)
761 (global-set-key [right-margin triple-mouse-3] #'ignore)
762 (global-set-key [left-margin mouse-1] (global-key-binding [mouse-1])) ; #'mouse-set-point
763 (global-set-key [left-margin mouse-2] (global-key-binding [mouse-2])) ; #'mouse-yank-primary
764 (global-set-key [left-margin mouse-3] (global-key-binding [mouse-3])) ; #'mouse-save-then-kill
765 (global-set-key [left-margin drag-mouse-1] #'ignore)
766 (global-set-key [left-margin drag-mouse-2] #'ignore)
767 (global-set-key [left-margin drag-mouse-3] #'ignore)
768 (global-set-key [left-margin double-mouse-1] #'ignore)
769 (global-set-key [left-margin double-mouse-2] #'ignore)
770 (global-set-key [left-margin double-mouse-3] #'ignore)
771 (global-set-key [left-margin triple-mouse-1] #'ignore)
772 (global-set-key [left-margin triple-mouse-2] #'ignore)
773 (global-set-key [left-margin triple-mouse-3] #'ignore)
774 965
775 (mouse-wheel-mode +1) 966 (mouse-wheel-mode +1)
776 967
777 (when (bound-and-true-p mouse-wheel-mode) 968 (when (bound-and-true-p mouse-wheel-mode)
778 (global-set-key [right-margin mouse-wheel-down-event] #'mwheel-scroll) 969 (dolist (margin '(right-margin left-margin))
779 (global-set-key [right-margin mouse-wheel-up-event] #'mwheel-scroll) 970 (dolist (event '(mouse-wheel-down-event
780 (global-set-key [right-margin wheel-down] #'mwheel-scroll) 971 mouse-wheel-up-event
781 (global-set-key [right-margin wheel-up] #'mwheel-scroll) 972 wheel-down
782 (global-set-key [left-margin mouse-wheel-down-event] #'mwheel-scroll) 973 wheel-up
783 (global-set-key [left-margin mouse-wheel-up-event] #'mwheel-scroll) 974 mouse-4
784 (global-set-key [left-margin wheel-down] #'mwheel-scroll) 975 mouse-5))
785 (global-set-key [left-margin wheel-up] #'mwheel-scroll) 976 (global-set-key (vector margin event) #'mwheel-scroll))))
786 (global-set-key [right-margin mouse-4] #'mwheel-scroll)
787 (global-set-key [right-margin mouse-5] #'mwheel-scroll)
788 (global-set-key [left-margin mouse-4] #'mwheel-scroll)
789 (global-set-key [left-margin mouse-5] #'mwheel-scroll))
790 977
791 978
792### Load the package 979### Load the package
@@ -800,7 +987,7 @@ This has to be done *before* loading the package. It's included in `visual-fill
800 "Width of fill-column, and thus, visual-fill-column.") 987 "Width of fill-column, and thus, visual-fill-column.")
801 988
802 (advice-add 'text-scale-adjust 989 (advice-add 'text-scale-adjust
803 :after #'visual-fill-column-adjust) 990 :after #'visual-fill-column-adjust)
804 991
805 (global-visual-fill-column-mode +1) 992 (global-visual-fill-column-mode +1)
806 993
@@ -818,11 +1005,43 @@ This has to be done *before* loading the package. It's included in `visual-fill
818 (straight-use-package 'typo) 1005 (straight-use-package 'typo)
819 1006
820 (add-hook 'text-mode-hook #'typo-mode) 1007 (add-hook 'text-mode-hook #'typo-mode)
1008
1009 ;; Disable `typo-mode' when inside an Org source block
1010 (with-eval-after-load 'typo
1011 (add-to-list 'typo-disable-electricity-functions
1012 #'org-in-src-block-p))
1013
1014
1015## Word count
1016
1017 (straight-use-package 'wc-mode)
1018
1019 (add-hook 'text-mode-hook #'wc-mode)
1020
1021 (rm/whitelist-add "WC")
821 1022
822 1023
823# Applications 1024# Applications
824 1025
825 1026
1027## Dired
1028
1029
1030### Expand subtrees
1031
1032 (straight-use-package 'dired-subtree)
1033
1034 (with-eval-after-load 'dired
1035 (define-key dired-mode-map "i" #'dired-subtree-toggle))
1036
1037
1038### Collapse singleton directories
1039
1040 (straight-use-package 'dired-collapse)
1041
1042 (add-hook 'dired-mode-hook #'dired-collapse-mode)
1043
1044
826## Org mode 1045## Org mode
827 1046
828I’ve put org mode under Applications, as opposed to Writing, because it’s more generally-applicable than that. 1047I’ve put org mode under Applications, as opposed to Writing, because it’s more generally-applicable than that.
@@ -830,7 +1049,8 @@ I’ve put org mode under Applications, as opposed to Writing, because it’s m
830 1049
831### Basics 1050### Basics
832 1051
833 (straight-use-package 'org) 1052 (straight-use-package '(org
1053 :repo "https://code.orgmode.org/bzg/org-mode.git"))
834 1054
835 (with-eval-after-load 'org 1055 (with-eval-after-load 'org
836 (require 'org-tempo) 1056 (require 'org-tempo)
@@ -849,194 +1069,205 @@ I’ve put org mode under Applications, as opposed to Writing, because it’s m
849 (cuss org-confirm-babel-evaluate nil) 1069 (cuss org-confirm-babel-evaluate nil)
850 (cuss org-directory "~/Org") 1070 (cuss org-directory "~/Org")
851 (cuss org-ellipsis "…") 1071 (cuss org-ellipsis "…")
1072 (cuss org-catch-invisible-edits 'show)
852 1073
8531. Tags 10741. Tags
854 1075
855 (cuss org-tags-column 0 1076 (cuss org-tags-column 0
856 "Show tags directly after the headline. 1077 "Show tags directly after the headline.
857 This is the best-looking option with variable-pitch fonts.") 1078 This is the best-looking option with variable-pitch fonts.")
858 1079
859 (cussface 1080 (cussface
860 '(org-tag 1081 '(org-tag
861 ((t 1082 ((t
862 (:height 0.8 :weight normal :slant italic :foreground "grey40" :inherit 1083 (:height 0.8 :weight normal :slant italic :foreground "grey40" :inherit
863 (variable-pitch)))))) 1084 (variable-pitch))))))
864 1085
865 1086
866### General 1087### General
867 1088
8681. [Org Return: DWIM](https://github.com/alphapapa/unpackaged.el#org-return-dwim) 10891. [Org Return: DWIM](https://github.com/alphapapa/unpackaged.el#org-return-dwim)
869 1090
870 (defun unpackaged/org-element-descendant-of (type element) 1091 (defun unpackaged/org-element-descendant-of (type element)
871 "Return non-nil if ELEMENT is a descendant of TYPE. 1092 "Return non-nil if ELEMENT is a descendant of TYPE.
872 TYPE should be an element type, like `item' or `paragraph'. 1093 TYPE should be an element type, like `item' or `paragraph'.
873 ELEMENT should be a list like that returned by `org-element-context'." 1094 ELEMENT should be a list like that returned by `org-element-context'."
874 ;; MAYBE: Use `org-element-lineage'. 1095 ;; MAYBE: Use `org-element-lineage'.
875 (when-let* ((parent (org-element-property :parent element))) 1096 (when-let* ((parent (org-element-property :parent element)))
876 (or (eq type (car parent)) 1097 (or (eq type (car parent))
877 (unpackaged/org-element-descendant-of type parent)))) 1098 (unpackaged/org-element-descendant-of type parent))))
878 1099
879 ;;;###autoload 1100 ;;;###autoload
880 (defun unpackaged/org-return-dwim (&optional default) 1101 (defun unpackaged/org-return-dwim (&optional default)
881 "A helpful replacement for `org-return'. With prefix, call `org-return'. 1102 "A helpful replacement for `org-return'. With prefix, call `org-return'.
882 1103
883 On headings, move point to position after entry content. In 1104 On headings, move point to position after entry content. In
884 lists, insert a new item or end the list, with checkbox if 1105 lists, insert a new item or end the list, with checkbox if
885 appropriate. In tables, insert a new row or end the table." 1106 appropriate. In tables, insert a new row or end the table."
886 ;; Inspired by John Kitchin: http://kitchingroup.cheme.cmu.edu/blog/2017/04/09/A-better-return-in-org-mode/ 1107 ;; Inspired by John Kitchin: http://kitchingroup.cheme.cmu.edu/blog/2017/04/09/A-better-return-in-org-mode/
887 (interactive "P") 1108 (interactive "P")
888 (if default 1109 (if default
889 (org-return) 1110 (org-return)
890 (cond 1111 (cond
891 ;; Act depending on context around point. 1112 ;; Act depending on context around point.
892 1113
893 ;; NOTE: I prefer RET to not follow links, but by uncommenting this block, links will be 1114 ;; NOTE: I prefer RET to not follow links, but by uncommenting this block, links will be
894 ;; followed. 1115 ;; followed.
895 1116
896 ;; ((eq 'link (car (org-element-context))) 1117 ;; ((eq 'link (car (org-element-context)))
897 ;; ;; Link: Open it. 1118 ;; ;; Link: Open it.
898 ;; (org-open-at-point-global)) 1119 ;; (org-open-at-point-global))
899 1120
900 ((org-at-heading-p) 1121 ((org-at-heading-p)
901 ;; Heading: Move to position after entry content. 1122 ;; Heading: Move to position after entry content.
902 ;; NOTE: This is probably the most interesting feature of this function. 1123 ;; NOTE: This is probably the most interesting feature of this function.
903 (let ((heading-start (org-entry-beginning-position))) 1124 (let ((heading-start (org-entry-beginning-position)))
904 (goto-char (org-entry-end-position)) 1125 (goto-char (org-entry-end-position))
905 (cond ((and (org-at-heading-p) 1126 (cond ((and (org-at-heading-p)
906 (= heading-start (org-entry-beginning-position))) 1127 (= heading-start (org-entry-beginning-position)))
907 ;; Entry ends on its heading; add newline after 1128 ;; Entry ends on its heading; add newline after
908 (end-of-line) 1129 (end-of-line)
909 (insert "\n\n")) 1130 (insert "\n\n"))
910 (t 1131 (t
911 ;; Entry ends after its heading; back up 1132 ;; Entry ends after its heading; back up
912 (forward-line -1) 1133 (forward-line -1)
913 (end-of-line) 1134 (end-of-line)
914 (when (org-at-heading-p) 1135 (when (org-at-heading-p)
915 ;; At the same heading 1136 ;; At the same heading
916 (forward-line) 1137 (forward-line)
917 (insert "\n") 1138 (insert "\n")
918 (forward-line -1)) 1139 (forward-line -1))
919 ;; FIXME: looking-back is supposed to be called with more arguments. 1140 ;; FIXME: looking-back is supposed to be called with more arguments.
920 (while (not (looking-back (rx (repeat 3 (seq (optional blank) "\n"))) nil)) 1141 (while (not (looking-back (rx (repeat 3 (seq (optional blank) "\n"))) nil))
921 (insert "\n")) 1142 (insert "\n"))
922 (forward-line -1))))) 1143 (forward-line -1)))))
923 1144
924 ((org-at-item-checkbox-p) 1145 ((org-at-item-checkbox-p)
925 ;; Checkbox: Insert new item with checkbox. 1146 ;; Checkbox: Insert new item with checkbox.
926 (org-insert-todo-heading nil)) 1147 (org-insert-todo-heading nil))
927 1148
928 ((org-in-item-p) 1149 ((org-in-item-p)
929 ;; Plain list. Yes, this gets a little complicated... 1150 ;; Plain list. Yes, this gets a little complicated...
930 (let ((context (org-element-context))) 1151 (let ((context (org-element-context)))
931 (if (or (eq 'plain-list (car context)) ; First item in list 1152 (if (or (eq 'plain-list (car context)) ; First item in list
932 (and (eq 'item (car context)) 1153 (and (eq 'item (car context))
933 (not (eq (org-element-property :contents-begin context) 1154 (not (eq (org-element-property :contents-begin context)
934 (org-element-property :contents-end context)))) 1155 (org-element-property :contents-end context))))
935 (unpackaged/org-element-descendant-of 'item context)) ; Element in list item, e.g. a link 1156 (unpackaged/org-element-descendant-of 'item context)) ; Element in list item, e.g. a link
936 ;; Non-empty item: Add new item. 1157 ;; Non-empty item: Add new item.
937 (org-insert-item) 1158 (org-insert-item)
938 ;; Empty item: Close the list. 1159 ;; Empty item: Close the list.
939 ;; TODO: Do this with org functions rather than operating on the text. Can't seem to find the right function. 1160 ;; TODO: Do this with org functions rather than operating on the text. Can't seem to find the right function.
940 (delete-region (line-beginning-position) (line-end-position)) 1161 (delete-region (line-beginning-position) (line-end-position))
941 (insert "\n")))) 1162 (insert "\n"))))
942 1163
943 ((when (fboundp 'org-inlinetask-in-task-p) 1164 ((when (fboundp 'org-inlinetask-in-task-p)
944 (org-inlinetask-in-task-p)) 1165 (org-inlinetask-in-task-p))
945 ;; Inline task: Don't insert a new heading. 1166 ;; Inline task: Don't insert a new heading.
946 (org-return)) 1167 (org-return))
947 1168
948 ((org-at-table-p) 1169 ((org-at-table-p)
949 (cond ((save-excursion 1170 (cond ((save-excursion
950 (beginning-of-line) 1171 (beginning-of-line)
951 ;; See `org-table-next-field'. 1172 ;; See `org-table-next-field'.
952 (cl-loop with end = (line-end-position) 1173 (cl-loop with end = (line-end-position)
953 for cell = (org-element-table-cell-parser) 1174 for cell = (org-element-table-cell-parser)
954 always (equal (org-element-property :contents-begin cell) 1175 always (equal (org-element-property :contents-begin cell)
955 (org-element-property :contents-end cell)) 1176 (org-element-property :contents-end cell))
956 while (re-search-forward "|" end t))) 1177 while (re-search-forward "|" end t)))
957 ;; Empty row: end the table. 1178 ;; Empty row: end the table.
958 (delete-region (line-beginning-position) (line-end-position)) 1179 (delete-region (line-beginning-position) (line-end-position))
959 (org-return)) 1180 (org-return))
960 (t 1181 (t
961 ;; Non-empty row: call `org-return'. 1182 ;; Non-empty row: call `org-return'.
962 (org-return)))) 1183 (org-return))))
963 (t 1184 (t
964 ;; All other cases: call `org-return'. 1185 ;; All other cases: call `org-return'.
965 (org-return))))) 1186 (org-return)))))
966 1187
967 (with-eval-after-load 'org 1188 (with-eval-after-load 'org
968 (define-key org-mode-map (kbd "RET") #'unpackaged/org-return-dwim)) 1189 (define-key org-mode-map (kbd "RET") #'unpackaged/org-return-dwim))
969 1190
9702. Insert blank lines around headers 11912. Insert blank lines around headers
971 1192
972 from [unpackaged.el](https://github.com/alphapapa/unpackaged.el#ensure-blank-lines-between-headings-and-before-contents). 1193 from [unpackaged.el](https://github.com/alphapapa/unpackaged.el#ensure-blank-lines-between-headings-and-before-contents).
973 1194
974 ;;;###autoload 1195 ;;;###autoload
975 (defun unpackaged/org-fix-blank-lines (&optional prefix) 1196 (defun unpackaged/org-fix-blank-lines (&optional prefix)
976 "Ensure that blank lines exist between headings and between headings and their contents. 1197 "Ensure that blank lines exist between headings and between headings and their contents.
977 With prefix, operate on whole buffer. Ensures that blank lines 1198 With prefix, operate on whole buffer. Ensures that blank lines
978 exist after each headings's drawers." 1199 exist after each headings's drawers."
979 (interactive "P") 1200 (interactive "P")
980 (org-map-entries (lambda () 1201 (org-map-entries (lambda ()
981 (org-with-wide-buffer 1202 (org-with-wide-buffer
982 ;; `org-map-entries' narrows the buffer, which prevents us 1203 ;; `org-map-entries' narrows the buffer, which prevents us
983 ;; from seeing newlines before the current heading, so we 1204 ;; from seeing newlines before the current heading, so we
984 ;; do this part widened. 1205 ;; do this part widened.
985 (while (not (looking-back "\n\n" nil)) 1206 (while (not (looking-back "\n\n" nil))
986 ;; Insert blank lines before heading. 1207 ;; Insert blank lines before heading.
987 (insert "\n"))) 1208 (insert "\n")))
988 (let ((end (org-entry-end-position))) 1209 (let ((end (org-entry-end-position)))
989 ;; Insert blank lines before entry content 1210 ;; Insert blank lines before entry content
990 (forward-line) 1211 (forward-line)
991 (while (and (org-at-planning-p) 1212 (while (and (org-at-planning-p)
992 (< (point) (point-max))) 1213 (< (point) (point-max)))
993 ;; Skip planning lines 1214 ;; Skip planning lines
994 (forward-line)) 1215 (forward-line))
995 (while (re-search-forward org-drawer-regexp end t) 1216 (while (re-search-forward org-drawer-regexp end t)
996 ;; Skip drawers. You might think that `org-at-drawer-p' 1217 ;; Skip drawers. You might think that `org-at-drawer-p'
997 ;; would suffice, but for some reason it doesn't work 1218 ;; would suffice, but for some reason it doesn't work
998 ;; correctly when operating on hidden text. This 1219 ;; correctly when operating on hidden text. This
999 ;; works, taken from `org-agenda-get-some-entry-text'. 1220 ;; works, taken from `org-agenda-get-some-entry-text'.
1000 (re-search-forward "^[ \t]*:END:.*\n?" end t) 1221 (re-search-forward "^[ \t]*:END:.*\n?" end t)
1001 (goto-char (match-end 0))) 1222 (goto-char (match-end 0)))
1002 (unless (or (= (point) (point-max)) 1223 (unless (or (= (point) (point-max))
1003 (org-at-heading-p) 1224 (org-at-heading-p)
1004 (looking-at-p "\n")) 1225 (looking-at-p "\n"))
1005 (insert "\n")))) 1226 (insert "\n"))))
1006 t (if prefix 1227 t (if prefix
1007 nil 1228 nil
1008 'tree))) 1229 'tree)))
1009 1230
1010 1. Add a before-save-hook 1231 1. Add a before-save-hook
1011 1232
1012 (defun cribbed/org-mode-fix-blank-lines () 1233 (defun cribbed/org-mode-fix-blank-lines ()
1013 (when (eq major-mode 'org-mode) 1234 (when (eq major-mode 'org-mode)
1014 (let ((current-prefix-arg 4)) ; Emulate C-u 1235 (let ((current-prefix-arg 4)) ; Emulate C-u
1015 (call-interactively 'unpackaged/org-fix-blank-lines)))) 1236 (call-interactively 'unpackaged/org-fix-blank-lines))))
1016 1237
1017 (add-hook 'before-save-hook #'cribbed/org-mode-fix-blank-lines) 1238 (add-hook 'before-save-hook #'cribbed/org-mode-fix-blank-lines)
1018 1239
1019 1240
1020### Org Agenda 1241### Org Agenda
1021 1242
1022 (cuss org-agenda-files 1243 (cuss org-agenda-files
1023 (let ((list)) 1244 (let ((list))
1024 (dolist (file '(;; add more files to this list 1245 (dolist (file '(;; add more files to this list
1025 "home.org" 1246 "home.org"
1026 "work.org") 1247 "work.org")
1027 list) 1248 list)
1028 (push (expand-file-name file org-directory) list)))) 1249 (push (expand-file-name file org-directory) list))))
1029 1250
1030 (define-key acdw/map (kbd "C-a") #'org-agenda) 1251 (define-key acdw/map (kbd "C-a") #'org-agenda)
1031 1252
1032 (cuss org-todo-keywords 1253 (cuss org-todo-keywords
1033 '((sequence "RECUR(r)" "TODO(t)" "|" "DONE(d)") 1254 '((sequence "RECUR(r)" "TODO(t)" "|" "DONE(d)")
1034 (sequence "|" "CANCELLED(c)"))) 1255 (sequence "|" "CANCELLED(c)")))
1035 1256
1036 1257
1037### TODO Capture 1258### TODO Capture
1038 1259
1039 1260
1261### Include Org links in source code
1262
1263 (straight-use-package '(org-link-minor-mode
1264 :host github
1265 :repo "seanohalpin/org-link-minor-mode"))
1266
1267 ;; enable in elisp buffers
1268 (add-hook 'emacs-lisp-mode-hook #'org-link-minor-mode)
1269
1270
1040## Git 1271## Git
1041 1272
1042 (straight-use-package 'magit) 1273 (straight-use-package 'magit)
@@ -1047,8 +1278,8 @@ I’ve put org mode under Applications, as opposed to Writing, because it’s m
1047## Beancount mode 1278## Beancount mode
1048 1279
1049 (straight-use-package '(beancount-mode 1280 (straight-use-package '(beancount-mode
1050 :host github 1281 :host github
1051 :repo "beancount/beancount-mode")) 1282 :repo "beancount/beancount-mode"))
1052 (require 'beancount) 1283 (require 'beancount)
1053 1284
1054 (add-to-list 'auto-mode-alist '("\\.beancount\\'" . beancount-mode)) 1285 (add-to-list 'auto-mode-alist '("\\.beancount\\'" . beancount-mode))
@@ -1072,24 +1303,33 @@ I’ve put org mode under Applications, as opposed to Writing, because it’s m
1072 1303
1073### init.el 1304### init.el
1074 1305
1075I realized I didn’t need `early-init.el`, since it really only set `load-prefer-newer`. So I’ve set that here, and wrapped the actual loading of config in a `let*` form that speeds up init, and loads the newer of either `config.org` or `config.el`.
1076
1077 ;; init.el -*- lexical-binding: t -*- 1306 ;; init.el -*- lexical-binding: t -*-
1078 1307
1079 (setq load-prefer-newer t) 1308 (setq load-prefer-newer t)
1080 1309
1081 (let* (;; Speed up init 1310 (let* (;; Speed up init
1082 (gc-cons-threshold most-positive-fixnum) 1311 (gc-cons-threshold most-positive-fixnum)
1083 (file-name-handler-alist nil) 1312 (file-name-handler-alist nil)
1084 ;; Config file names 1313 ;; Config file names
1085 (conf (expand-file-name "config" 1314 (conf (expand-file-name "config"
1086 user-emacs-directory)) 1315 user-emacs-directory))
1087 (conf-el (concat conf ".el")) 1316 (conf-el (concat conf ".el"))
1088 (conf-org (concat conf ".org"))) 1317 (conf-org (concat conf ".org")))
1089 (unless (and (file-newer-than-file-p conf-el conf-org) 1318 (unless (and (file-newer-than-file-p conf-el conf-org)
1090 (load conf 'no-error)) 1319 (load conf 'no-error))
1091 (require 'org) 1320 (require 'org)
1092 (org-babel-load-file conf-org))) 1321 (org-babel-load-file conf-org)))
1322
1323
1324### early-init.el
1325
1326 ;; early-init.el -*- no-byte-compile: t; -*-
1327
1328 ;; I use `straight.el' instead of `package.el'.
1329 (setq package-enable-at-startup nil)
1330
1331 ;; Don't resize the frame when loading fonts
1332 (setq frame-inhibit-implied-resize t)
1093 1333
1094 1334
1095## Ease tangling and loading of Emacs' init 1335## Ease tangling and loading of Emacs' init
@@ -1099,29 +1339,29 @@ I realized I didn’t need `early-init.el`, since it really only set `load-prefe
1099 Then, load the byte-compilations unless passed with a prefix argument." 1339 Then, load the byte-compilations unless passed with a prefix argument."
1100 (interactive "P") 1340 (interactive "P")
1101 (let ((config (expand-file-name "config.org" user-emacs-directory))) 1341 (let ((config (expand-file-name "config.org" user-emacs-directory)))
1102 (save-mark-and-excursion 1342 (save-mark-and-excursion
1103 (with-current-buffer (find-file config) 1343 (with-current-buffer (find-file config)
1104 (let ((prog-mode-hook nil)) 1344 (let ((prog-mode-hook nil))
1105 ;; generate the readme 1345 ;; generate the readme
1106 (when (file-newer-than-file-p config (expand-file-name 1346 (when (file-newer-than-file-p config (expand-file-name
1107 "README.md" 1347 "README.md"
1108 user-emacs-directory)) 1348 user-emacs-directory))
1109 (message "%s" "Exporting README.md...") 1349 (message "%s" "Exporting README.md...")
1110 (require 'ox-md) 1350 (require 'ox-md)
1111 (with-demoted-errors "Problem exporting README.md: %S" 1351 (with-demoted-errors "Problem exporting README.md: %S"
1112 (org-md-export-to-markdown))) 1352 (org-md-export-to-markdown)))
1113 ;; tangle config.org 1353 ;; tangle config.org
1114 (when (file-newer-than-file-p config (expand-file-name 1354 (when (file-newer-than-file-p config (expand-file-name
1115 "config.el" 1355 "config.el"
1116 user-emacs-directory)) 1356 user-emacs-directory))
1117 (message "%s" "Tangling config.org...") 1357 (message "%s" "Tangling config.org...")
1118 (require 'org) 1358 (require 'org)
1119 (let ((inits (org-babel-tangle))) 1359 (let ((inits (org-babel-tangle)))
1120 ;; byte-compile resulting files 1360 ;; byte-compile resulting files
1121 (message "%s" "Byte-compiling...") 1361 (message "%s" "Byte-compiling...")
1122 (dolist (f inits) 1362 (dolist (f inits)
1123 (when (string-match "\\.el\\'" f) 1363 (when (string-match "\\.el\\'" f)
1124 (byte-compile-file f (not disable-load))))))))))) 1364 (byte-compile-file f (not disable-load)))))))))))
1125 1365
1126 1366
1127## Ancillary scripts 1367## Ancillary scripts
@@ -1129,32 +1369,35 @@ I realized I didn’t need `early-init.el`, since it really only set `load-prefe
1129 1369
1130### emacsdc 1370### emacsdc
1131 1371
1372:header-args: :tangle bin/emacsdc :tangle-mode (identity #o755)
1373
1132Here's a wrapper script that'll start `emacs --daemon` if there isn't 1374Here's a wrapper script that'll start `emacs --daemon` if there isn't
1133one, and then launch `emacsclient` with the arguments. I'd recommend 1375one, and then launch `emacsclient` with the arguments. I'd recommend
1134installing with either `ln -s bin/emacsdc $HOME/.local/bin/`, or 1376installing with either `ln -s bin/emacsdc $HOME/.local/bin/`, or
1135adding `$HOME/.local/bin` to your `$PATH`. 1377adding `$HOME/.local/bin` to your `$PATH`.
1136 1378
1137 if ! emacsclient -nc "$@" 2>/dev/null; then 1379 if ! emacsclient -nc "$@" 2>/dev/null; then
1138 emacs --daemon 1380 emacs --daemon
1139 emacsclient -nc "$@" 1381 emacsclient -nc "$@"
1140 fi 1382 fi
1141 1383
1142 1384
1143### Emacs.cmd 1385### Emacs.cmd
1144 1386
1145Here's a wrapper script that'll run Emacs on Windows, with a custom 1387Here's a wrapper script that'll run Emacs on Windows, with a custom `$HOME`. I have
1146`$HOME`. I have mine setup like this: Emacs is downloaded from [the 1388mine setup like this: Emacs is downloaded from [the GNU mirror](https://mirrors.tripadvisor.com/gnu/emacs/windows/emacs-27/emacs-27.1-x86_64.zip) and unzipped to
1147GNU mirror](https://mirrors.tripadvisor.com/gnu/emacs/windows/emacs-27/emacs-27.1-x86_64.zip) and unzipped to `~/Downloads/emacs/`. `Emacs.cmd` sets 1389`~/Downloads/emacs/`. For some reason, Emacs by default sets `$HOME` to `%APPDATA%`,
1148`$HOME` to `~/Downloads/emacshome/`, which is where `.emacs.d` is, and 1390which doesn’t make a lot of sense to me. I change it to `%USERPROFILE%`, and then run
1149whatever else I might want to throw in there. 1391Emacs with the supplied arguments.
1150 1392
1151 set HOME=%~dp0..\..\emacshome 1393 set HOME=%USERPROFILE%
1394 set EMACS="%USERPROFILE%\Downloads\emacs\bin\runemacs.exe"
1152 1395
1153 REM Run "Quick Mode" 1396 REM Run "Quick Mode"
1154 REM "%~dp0runemacs.exe" -Q %* 1397 REM "%EMACS%" -Q %*
1155 1398
1156 REM Regular 1399 REM Regular
1157 "%~dp0runemacs.exe" %* 1400 "%EMACS%" %*
1158 1401
1159 1402
1160## License 1403## License
@@ -1188,4 +1431,3 @@ It's highly likely that the WTFPL is completely incompatible with the
1188GPL, for what should be fairly obvious reasons. To that, I say: 1431GPL, for what should be fairly obvious reasons. To that, I say:
1189 1432
1190**SUE ME, RMS!** 1433**SUE ME, RMS!**
1191