diff options
Diffstat (limited to 'config.org')
-rw-r--r-- | config.org | 984 |
1 files changed, 780 insertions, 204 deletions
diff --git a/config.org b/config.org index b27fb35..ea8c78c 100644 --- a/config.org +++ b/config.org | |||
@@ -5,7 +5,15 @@ | |||
5 | 5 | ||
6 | #+begin_src emacs-lisp :noweb-ref settings | 6 | #+begin_src emacs-lisp :noweb-ref settings |
7 | (setq user-full-name "Case Duckworth" | 7 | (setq user-full-name "Case Duckworth" |
8 | user-mail-address "acdw@acdw.net") | 8 | user-mail-address "acdw@acdw.net") |
9 | #+end_src | ||
10 | |||
11 | ** Where I am | ||
12 | |||
13 | #+begin_src emacs-lisp :noweb-ref settings | ||
14 | (setq calendar-location-name "Baton Rouge, LA" | ||
15 | calendar-latitude 30.4 | ||
16 | calendar-longitude -91.1) | ||
9 | #+end_src | 17 | #+end_src |
10 | 18 | ||
11 | * Look and feel | 19 | * Look and feel |
@@ -14,7 +22,7 @@ | |||
14 | 22 | ||
15 | *** Initial frame setup | 23 | *** Initial frame setup |
16 | :PROPERTIES: | 24 | :PROPERTIES: |
17 | :header-args: :noweb-ref initial-frame-setup | 25 | :header-args: :noweb-ref early-init-frame |
18 | :END: | 26 | :END: |
19 | 27 | ||
20 | I tangle this section to =early-init.el=, since that's evaluated | 28 | I tangle this section to =early-init.el=, since that's evaluated |
@@ -25,7 +33,7 @@ of unstyled content" thing. | |||
25 | 33 | ||
26 | #+begin_src emacs-lisp | 34 | #+begin_src emacs-lisp |
27 | (add-to-list 'default-frame-alist | 35 | (add-to-list 'default-frame-alist |
28 | '(tool-bar-lines . 0)) | 36 | '(tool-bar-lines . 0)) |
29 | 37 | ||
30 | (tool-bar-mode -1) | 38 | (tool-bar-mode -1) |
31 | #+end_src | 39 | #+end_src |
@@ -34,7 +42,7 @@ of unstyled content" thing. | |||
34 | 42 | ||
35 | #+begin_src emacs-lisp | 43 | #+begin_src emacs-lisp |
36 | (add-to-list 'default-frame-alist | 44 | (add-to-list 'default-frame-alist |
37 | '(menu-bar-lines . 0)) | 45 | '(menu-bar-lines . 0)) |
38 | 46 | ||
39 | (menu-bar-mode -1) | 47 | (menu-bar-mode -1) |
40 | #+end_src | 48 | #+end_src |
@@ -43,19 +51,29 @@ of unstyled content" thing. | |||
43 | 51 | ||
44 | #+begin_src emacs-lisp | 52 | #+begin_src emacs-lisp |
45 | (add-to-list 'default-frame-alist | 53 | (add-to-list 'default-frame-alist |
46 | '(vertical-scroll-bars . nil) | 54 | '(vertical-scroll-bars . nil) |
47 | '(horizontal-scroll-bars . nil)) | 55 | '(horizontal-scroll-bars . nil)) |
48 | 56 | ||
49 | (scroll-bar-mode -1) | 57 | (scroll-bar-mode -1) |
50 | (horizontal-scroll-bar-mode -1) | 58 | (horizontal-scroll-bar-mode -1) |
51 | #+end_src | 59 | #+end_src |
52 | 60 | ||
61 | **** Resizing | ||
62 | |||
63 | I don't want the frame to resize when I change fonts and stuff, and I | ||
64 | want it to resize by pixels -- we /are/ using a GUI, after all. | ||
65 | |||
66 | #+begin_src emacs-lisp | ||
67 | (setq-default frame-inhibit-implied-resize t | ||
68 | frame-resize-pixelwise t) | ||
69 | #+end_src | ||
70 | |||
53 | *** Frame titles | 71 | *** Frame titles |
54 | 72 | ||
55 | #+begin_src emacs-lisp :noweb-ref settings | 73 | #+begin_src emacs-lisp :noweb-ref settings |
56 | (setq-default frame-title-format | 74 | (setq-default frame-title-format |
57 | (concat invocation-name "@" (system-name) | 75 | (concat invocation-name "@" (system-name) |
58 | ": %b %+%+ %f")) | 76 | ": %b %+%+ %f")) |
59 | #+end_src | 77 | #+end_src |
60 | 78 | ||
61 | *** Fringes | 79 | *** Fringes |
@@ -131,7 +149,7 @@ to switch to the other buffer if there's only one window in the frame. | |||
131 | "Switch to other window, or the previous buffer." | 149 | "Switch to other window, or the previous buffer." |
132 | (interactive) | 150 | (interactive) |
133 | (if (eq (count-windows) 1) | 151 | (if (eq (count-windows) 1) |
134 | (switch-to-buffer nil) | 152 | (switch-to-buffer nil) |
135 | (other-window 1))) | 153 | (other-window 1))) |
136 | #+end_src | 154 | #+end_src |
137 | 155 | ||
@@ -164,12 +182,12 @@ also want it to show a cute little message to myself. | |||
164 | 182 | ||
165 | #+begin_src emacs-lisp :noweb-ref settings | 183 | #+begin_src emacs-lisp :noweb-ref settings |
166 | (setq-default inhibit-startup-screen t ; Don't show that splash screen thing. | 184 | (setq-default inhibit-startup-screen t ; Don't show that splash screen thing. |
167 | initial-buffer-choice t ; Start on *scratch* | 185 | initial-buffer-choice t ; Start on *scratch* |
168 | initial-scratch-message | 186 | initial-scratch-message |
169 | (concat ";; Howdy, " | 187 | (concat ";; Howdy, " |
170 | (nth 0 (split-string user-full-name)) "!\n" | 188 | (nth 0 (split-string user-full-name)) "!\n" |
171 | ";; Welcome to Emacs." | 189 | ";; Welcome to Emacs." |
172 | "\n\n")) | 190 | "\n\n")) |
173 | #+end_src | 191 | #+end_src |
174 | 192 | ||
175 | *** Immortal =*scratch*= buffer | 193 | *** Immortal =*scratch*= buffer |
@@ -181,7 +199,7 @@ function to the =kill-buffer-query-functions= hook that will return | |||
181 | #+begin_src emacs-lisp :noweb-ref functions | 199 | #+begin_src emacs-lisp :noweb-ref functions |
182 | (defun immortal-scratch () | 200 | (defun immortal-scratch () |
183 | (if (not (eq (current-buffer) (get-buffer "*scratch*"))) | 201 | (if (not (eq (current-buffer) (get-buffer "*scratch*"))) |
184 | t | 202 | t |
185 | (bury-buffer) | 203 | (bury-buffer) |
186 | nil)) | 204 | nil)) |
187 | #+end_src | 205 | #+end_src |
@@ -203,12 +221,12 @@ function to the =kill-buffer-query-functions= hook that will return | |||
203 | (interactive "P") | 221 | (interactive "P") |
204 | (pcase (or (car prefix) 0) | 222 | (pcase (or (car prefix) 0) |
205 | (0 (kill-current-buffer) | 223 | (0 (kill-current-buffer) |
206 | (unless (one-window-p) (delete-window))) | 224 | (unless (one-window-p) (delete-window))) |
207 | (4 (other-window 1) | 225 | (4 (other-window 1) |
208 | (kill-current-buffer) | 226 | (kill-current-buffer) |
209 | (unless (one-window-p) (delete-window))) | 227 | (unless (one-window-p) (delete-window))) |
210 | (16 (mapc #'kill-buffer (delq (current-buffer) (buffer-list))) | 228 | (16 (mapc #'kill-buffer (delq (current-buffer) (buffer-list))) |
211 | (delete-other-windows)))) | 229 | (delete-other-windows)))) |
212 | #+end_src | 230 | #+end_src |
213 | 231 | ||
214 | #+begin_src emacs-lisp :noweb-ref bindings | 232 | #+begin_src emacs-lisp :noweb-ref bindings |
@@ -223,7 +241,7 @@ I like a vertical bar, but only in the selected window. | |||
223 | 241 | ||
224 | #+begin_src emacs-lisp :noweb-ref settings | 242 | #+begin_src emacs-lisp :noweb-ref settings |
225 | (setq-default cursor-type 'bar | 243 | (setq-default cursor-type 'bar |
226 | cursor-in-non-selected-windows nil) | 244 | cursor-in-non-selected-windows nil) |
227 | #+end_src | 245 | #+end_src |
228 | 246 | ||
229 | *** Don't blink the cursor | 247 | *** Don't blink the cursor |
@@ -238,7 +256,7 @@ I like a vertical bar, but only in the selected window. | |||
238 | 256 | ||
239 | #+begin_src emacs-lisp :noweb-ref settings | 257 | #+begin_src emacs-lisp :noweb-ref settings |
240 | (setq-default tab-bar-tab-name-function | 258 | (setq-default tab-bar-tab-name-function |
241 | #'tab-bar-tab-name-current-with-count) | 259 | #'tab-bar-tab-name-current-with-count) |
242 | #+end_src | 260 | #+end_src |
243 | 261 | ||
244 | *** When to show the tab bar | 262 | *** When to show the tab bar |
@@ -255,13 +273,13 @@ Only when there's more than one tab. | |||
255 | 273 | ||
256 | #+begin_src emacs-lisp :noweb-ref functions | 274 | #+begin_src emacs-lisp :noweb-ref functions |
257 | (defun set-face-from-alternatives (face frame &rest fontspecs) | 275 | (defun set-face-from-alternatives (face frame &rest fontspecs) |
258 | "Set FACE on FRAME from first available spec from FONTSPECS. | 276 | "Set FACE on FRAME from first available spec from FONTSPECS. |
259 | FACE and FRAME work the same as with `set-face-attribute.'" | 277 | FACE and FRAME work the same as with `set-face-attribute.'" |
260 | (catch :return | 278 | (catch :return |
261 | (dolist (spec fontspecs) | 279 | (dolist (spec fontspecs) |
262 | (when-let ((found (find-font (apply #'font-spec spec)))) | 280 | (when-let ((found (find-font (apply #'font-spec spec)))) |
263 | (set-face-attribute face frame :font found) | 281 | (set-face-attribute face frame :font found) |
264 | (throw :return found))))) | 282 | (throw :return found))))) |
265 | #+end_src | 283 | #+end_src |
266 | 284 | ||
267 | *** Setup fonts on first window focus | 285 | *** Setup fonts on first window focus |
@@ -276,24 +294,28 @@ At the end of this function, it removes itself from | |||
276 | removes itself from that hook." | 294 | removes itself from that hook." |
277 | (interactive) | 295 | (interactive) |
278 | (set-face-from-alternatives 'default nil | 296 | (set-face-from-alternatives 'default nil |
279 | '(:family "Input Mono" | 297 | '(:family "Iosevka Acdw" |
280 | :slant normal | 298 | :slant normal |
281 | :weight normal | 299 | :weight normal |
282 | :height 110) | 300 | :height 105) |
283 | '(:family "Consolas" | 301 | '(:family "Iosevka Extended" |
284 | :slant normal | 302 | :slant normal |
285 | :weight normal | 303 | :weight normal |
286 | :height 100)) | 304 | :height 105) |
305 | '(:family "Consolas" | ||
306 | :slant normal | ||
307 | :weight normal | ||
308 | :height 100)) | ||
287 | ;; `fixed-pitch' inherits from `default' | 309 | ;; `fixed-pitch' inherits from `default' |
288 | (set-face-attribute 'fixed-pitch nil :inherit 'default) | 310 | (set-face-attribute 'fixed-pitch nil :inherit 'default) |
289 | ;; variable-pitch is different | 311 | ;; variable-pitch is different |
290 | (set-face-from-alternatives 'variable-pitch nil | 312 | (set-face-from-alternatives 'variable-pitch nil |
291 | '(:family "Input Sans" | 313 | '(:family "DejaVu Sans" |
292 | :slant normal | 314 | :slant normal |
293 | :weight normal) | 315 | :weight normal) |
294 | '(:family "Georgia" | 316 | '(:family "Georgia" |
295 | :slant normal | 317 | :slant normal |
296 | :weight normal)) | 318 | :weight normal)) |
297 | ;; remove self from hook | 319 | ;; remove self from hook |
298 | (remove-function after-focus-change-function #'acdw/setup-fonts)) | 320 | (remove-function after-focus-change-function #'acdw/setup-fonts)) |
299 | #+end_src | 321 | #+end_src |
@@ -304,7 +326,7 @@ using the graphical Emacs. | |||
304 | #+begin_src emacs-lisp :noweb-ref hooks | 326 | #+begin_src emacs-lisp :noweb-ref hooks |
305 | (when (display-graphic-p) | 327 | (when (display-graphic-p) |
306 | (add-function :before after-focus-change-function | 328 | (add-function :before after-focus-change-function |
307 | #'acdw/setup-fonts)) | 329 | #'acdw/setup-fonts)) |
308 | #+end_src | 330 | #+end_src |
309 | 331 | ||
310 | *** Underlines | 332 | *** Underlines |
@@ -317,6 +339,53 @@ underline below all the text. | |||
317 | (setq-default x-underline-at-descent-line t) | 339 | (setq-default x-underline-at-descent-line t) |
318 | #+end_src | 340 | #+end_src |
319 | 341 | ||
342 | ** Theming | ||
343 | |||
344 | *** Modus themes | ||
345 | |||
346 | #+begin_src emacs-lisp :noweb-ref packages | ||
347 | (straight-use-package 'modus-themes) | ||
348 | #+end_src | ||
349 | |||
350 | #+begin_src emacs-lisp :noweb-ref settings | ||
351 | (setq-default modus-themes-slanted-constructs t | ||
352 | modus-themes-bold-constructs t | ||
353 | modus-themes-region 'bg-only | ||
354 | modus-themes-org-blocks 'grayscale | ||
355 | modus-themes-headings '((1 . line) | ||
356 | (t . t)) | ||
357 | modus-themes-scale-headings nil) | ||
358 | #+end_src | ||
359 | |||
360 | *** Change themes based on time of day | ||
361 | |||
362 | #+begin_src emacs-lisp :noweb-ref functions | ||
363 | (defun acdw/run-with-sun (sunrise-command sunset-command) | ||
364 | "Run commands at sunrise and sunset." | ||
365 | (let* ((times-regex (rx (* nonl) | ||
366 | (: (any ?s ?S) "unrise") " " | ||
367 | (group (repeat 1 2 digit) ":" | ||
368 | (repeat 1 2 digit) | ||
369 | (: (any ?a ?A ?p ?P) (any ?m ?M))) | ||
370 | (* nonl) | ||
371 | (: (any ?s ?S) "unset") " " | ||
372 | (group (repeat 1 2 digit) ":" | ||
373 | (repeat 1 2 digit) | ||
374 | (: (any ?a ?A ?p ?P) (any ?m ?M))) | ||
375 | (* nonl))) | ||
376 | (ss (sunrise-sunset)) | ||
377 | (_m (string-match times-regex ss)) | ||
378 | (sunrise-time (match-string 1 ss)) | ||
379 | (sunset-time (match-string 2 ss))) | ||
380 | (run-at-time sunrise-time (* 60 60 24) sunrise-command) | ||
381 | (run-at-time sunset-time (* 60 60 24) sunset-command))) | ||
382 | #+end_src | ||
383 | |||
384 | #+begin_src emacs-lisp :noweb-ref hooks | ||
385 | (acdw/run-with-sun #'modus-themes-load-operandi | ||
386 | #'modus-themes-load-vivendi) | ||
387 | #+end_src | ||
388 | |||
320 | * Interactivity | 389 | * Interactivity |
321 | 390 | ||
322 | ** Dialogs and alerts | 391 | ** Dialogs and alerts |
@@ -346,7 +415,7 @@ systems. | |||
346 | 415 | ||
347 | #+begin_src emacs-lisp :noweb-ref settings | 416 | #+begin_src emacs-lisp :noweb-ref settings |
348 | (setq-default visible-bell nil | 417 | (setq-default visible-bell nil |
349 | ring-bell-function #'flash-mode-line) | 418 | ring-bell-function #'flash-mode-line) |
350 | #+end_src | 419 | #+end_src |
351 | 420 | ||
352 | **** Flash the mode-line | 421 | **** Flash the mode-line |
@@ -363,9 +432,9 @@ systems. | |||
363 | 432 | ||
364 | #+begin_src emacs-lisp :noweb-ref settings | 433 | #+begin_src emacs-lisp :noweb-ref settings |
365 | (setq-default minibuffer-prompt-properties | 434 | (setq-default minibuffer-prompt-properties |
366 | '(read-only t | 435 | '(read-only t |
367 | cursor-intangible t | 436 | cursor-intangible t |
368 | face minibuffer-prompt)) | 437 | face minibuffer-prompt)) |
369 | #+end_src | 438 | #+end_src |
370 | 439 | ||
371 | *** Enable a recursive minibuffer | 440 | *** Enable a recursive minibuffer |
@@ -401,8 +470,8 @@ to /hide/ those contents. | |||
401 | 470 | ||
402 | #+begin_src emacs-lisp :noweb-ref | 471 | #+begin_src emacs-lisp :noweb-ref |
403 | (setq-default completion-ignore-case t | 472 | (setq-default completion-ignore-case t |
404 | read-buffer-completion-ignore-case t | 473 | read-buffer-completion-ignore-case t |
405 | read-file-name-completion-ignore-case t) | 474 | read-file-name-completion-ignore-case t) |
406 | #+end_src | 475 | #+end_src |
407 | 476 | ||
408 | * Persistence | 477 | * Persistence |
@@ -419,11 +488,11 @@ I keep all of it. | |||
419 | 488 | ||
420 | #+begin_src emacs-lisp :noweb-ref modes | 489 | #+begin_src emacs-lisp :noweb-ref modes |
421 | (setq-default savehist-additional-variables | 490 | (setq-default savehist-additional-variables |
422 | '(kill-ring | 491 | '(kill-ring |
423 | search-ring | 492 | search-ring |
424 | regexp-search-ring) | 493 | regexp-search-ring) |
425 | history-length t ; Don't truncate | 494 | history-length t ; Don't truncate |
426 | history-delete-duplicates t) | 495 | history-delete-duplicates t) |
427 | #+end_src | 496 | #+end_src |
428 | 497 | ||
429 | #+begin_src emacs-lisp :noweb-ref modes | 498 | #+begin_src emacs-lisp :noweb-ref modes |
@@ -461,13 +530,21 @@ with that. | |||
461 | 530 | ||
462 | #+begin_src emacs-lisp :noweb-ref settings | 531 | #+begin_src emacs-lisp :noweb-ref settings |
463 | (setq-default recentf-max-menu-items 100 | 532 | (setq-default recentf-max-menu-items 100 |
464 | recentf-max-saved-items nil) | 533 | recentf-max-saved-items nil) |
465 | #+end_src | 534 | #+end_src |
466 | 535 | ||
467 | #+begin_src emacs-lisp :noweb-ref modes | 536 | #+begin_src emacs-lisp :noweb-ref modes |
468 | (recentf-mode +1) | 537 | (recentf-mode +1) |
469 | #+end_src | 538 | #+end_src |
470 | 539 | ||
540 | I also want to ignore the =no-littering-var-directory= and | ||
541 | =no-littering-etc-directory=, since those aren't useful. | ||
542 | |||
543 | #+begin_src emacs-lisp :noweb-ref no-littering | ||
544 | (add-to-list 'recentf-exclude no-littering-var-directory) | ||
545 | (add-to-list 'recentf-exclude no-littering-etc-directory) | ||
546 | #+end_src | ||
547 | |||
471 | *** Save the recentf list periodically | 548 | *** Save the recentf list periodically |
472 | 549 | ||
473 | #+begin_src emacs-lisp :noweb-ref functions | 550 | #+begin_src emacs-lisp :noweb-ref functions |
@@ -477,14 +554,14 @@ with that. | |||
477 | "When we last saved the `recentf-save-list'.") | 554 | "When we last saved the `recentf-save-list'.") |
478 | 555 | ||
479 | (when (> (time-convert (time-since recentf--last-save) 'integer) | 556 | (when (> (time-convert (time-since recentf--last-save) 'integer) |
480 | (* 60 5)) | 557 | (* 60 5)) |
481 | (setq-default recentf--last-save (time-convert nil 'integer)) | 558 | (setq-default recentf--last-save (time-convert nil 'integer)) |
482 | (when-unfocused #'recentf-save-list))) | 559 | (when-unfocused #'recentf-save-list))) |
483 | #+end_src | 560 | #+end_src |
484 | 561 | ||
485 | #+begin_src emacs-lisp :noweb-ref hooks | 562 | #+begin_src emacs-lisp :noweb-ref hooks |
486 | (add-function :after after-focus-change-function | 563 | (add-function :after after-focus-change-function |
487 | #'maybe-save-recentf) | 564 | #'maybe-save-recentf) |
488 | #+end_src | 565 | #+end_src |
489 | 566 | ||
490 | * Responsiveness | 567 | * Responsiveness |
@@ -500,6 +577,7 @@ the user is looking at something else. | |||
500 | #+begin_src emacs-lisp :noweb-ref functions | 577 | #+begin_src emacs-lisp :noweb-ref functions |
501 | (defun when-unfocused (func &rest args) | 578 | (defun when-unfocused (func &rest args) |
502 | "Run FUNC, with ARGS, iff all frames are out of focus." | 579 | "Run FUNC, with ARGS, iff all frames are out of focus." |
580 | (require 'seq) | ||
503 | (when (seq-every-p #'null (mapcar #'frame-focus-state (frame-list))) | 581 | (when (seq-every-p #'null (mapcar #'frame-focus-state (frame-list))) |
504 | (apply func args))) | 582 | (apply func args))) |
505 | #+end_src | 583 | #+end_src |
@@ -519,7 +597,7 @@ It's 2020. Let's encode files like it is. | |||
519 | (set-keyboard-coding-system 'utf-8) | 597 | (set-keyboard-coding-system 'utf-8) |
520 | 598 | ||
521 | (setq-default buffer-file-coding-system 'utf-8 | 599 | (setq-default buffer-file-coding-system 'utf-8 |
522 | x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)) | 600 | x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)) |
523 | #+end_src | 601 | #+end_src |
524 | 602 | ||
525 | *** UNIX-style line endings | 603 | *** UNIX-style line endings |
@@ -531,7 +609,7 @@ This function is from the [[https://www.emacswiki.org/emacs/EndOfLineTips][Emacs | |||
531 | "Convert line endings to UNIX, dammit." | 609 | "Convert line endings to UNIX, dammit." |
532 | (let ((coding-str (symbol-name buffer-file-coding-system))) | 610 | (let ((coding-str (symbol-name buffer-file-coding-system))) |
533 | (when (string-match "-\\(?:dos\\|mac\\)$" coding-str) | 611 | (when (string-match "-\\(?:dos\\|mac\\)$" coding-str) |
534 | (set-buffer-file-coding-system 'unix)))) | 612 | (set-buffer-file-coding-system 'unix)))) |
535 | #+end_src | 613 | #+end_src |
536 | 614 | ||
537 | I add it to both =file-find-hook= /and/ =before-save-hook= because I'm | 615 | I add it to both =file-find-hook= /and/ =before-save-hook= because I'm |
@@ -546,16 +624,559 @@ Notepad can handle UNIX line endings, so I don't want to hear it. | |||
546 | 624 | ||
547 | ** Keep =~/.emacs.d= clean :package: | 625 | ** Keep =~/.emacs.d= clean :package: |
548 | 626 | ||
627 | #+begin_src emacs-lisp :noweb-ref packages :noweb yes | ||
628 | (straight-use-package 'no-littering) | ||
629 | (require 'no-littering) | ||
630 | (with-eval-after-load 'no-littering | ||
631 | <<no-littering>> | ||
632 | ) ; end of no-littering | ||
633 | #+end_src | ||
634 | |||
635 | ** Backups | ||
636 | |||
637 | #+begin_src emacs-lisp :noweb-ref settings | ||
638 | (setq-default backup-by-copying t | ||
639 | ;; Don't delete old versions | ||
640 | delete-old-versions -1 | ||
641 | ;; Make numeric backups unconditionally | ||
642 | version-control t | ||
643 | ;; Also backup files covered by version control | ||
644 | vc-make-backup-files t) | ||
645 | #+end_src | ||
646 | |||
647 | #+begin_src emacs-lisp :noweb-ref no-littering | ||
648 | (let ((dir (no-littering-expand-var-file-name "backup"))) | ||
649 | (make-directory dir :parents) | ||
650 | (setq-default backup-directory-alist | ||
651 | `((".*" . ,dir)))) | ||
652 | #+end_src | ||
653 | |||
654 | ** Autosaves :package: | ||
655 | |||
656 | I don't use the =auto-save= system, preferring instead to use | ||
657 | Bozhidar Batsov's [[https://github.com/bbatsov/super-save][super-save]] package. | ||
658 | |||
659 | #+begin_src emacs-lisp :noweb-ref settings | ||
660 | (setq-default auto-save-default nil) | ||
661 | |||
662 | (setq-default super-save-remote-files nil | ||
663 | super-save-exclude '(".gpg") | ||
664 | super-save-auto-save-when-idle t) | ||
665 | #+end_src | ||
666 | |||
667 | #+begin_src emacs-lisp :noweb-ref packages | ||
668 | (straight-use-package 'super-save) | ||
669 | #+end_src | ||
670 | |||
671 | #+begin_src emacs-lisp :noweb-ref modes | ||
672 | (super-save-mode +1) | ||
673 | #+end_src | ||
674 | |||
675 | ** Auto-revert files | ||
676 | |||
677 | I like to keep the buffers Emacs has in-memory in sync with the actual | ||
678 | contents of the files the represent on-disk. Thus, we have | ||
679 | =auto-revert-mode=. | ||
680 | |||
681 | #+begin_src emacs-lisp :noweb-ref settings | ||
682 | (setq-default auto-revert-verbose nil) | ||
683 | #+end_src | ||
684 | |||
685 | #+begin_src emacs-lisp :noweb-ref modes | ||
686 | (global-auto-revert-mode +1) | ||
687 | #+end_src | ||
688 | |||
689 | |||
690 | * Editing | ||
691 | |||
692 | ** Lines | ||
693 | |||
694 | *** Auto-fill vs. Visual-line | ||
695 | |||
696 | I've mostly been using visual-line mode, and it's been pretty good. | ||
697 | There are some times, however, when lines are just ... really long, | ||
698 | and they wrap weird or whatever. Not to mention, in Org mode, | ||
699 | =visual-line-mode= screws up the bindings for line movement. So | ||
700 | here's what I'm going to do. | ||
701 | |||
702 | 1. Enable =visual-line-mode= with =text-mode=, but /not/ with | ||
703 | =org-mode=. | ||
704 | |||
705 | #+begin_src emacs-lisp :noweb-ref hooks | ||
706 | (defun hook--visual-line-mode () | ||
707 | (unless (eq major-mode 'org-mode) | ||
708 | (visual-line-mode +1))) | ||
709 | |||
710 | (add-hook 'text-mode-hook #'hook--visual-line-mode) | ||
711 | #+end_src | ||
712 | |||
713 | 2. Enable =auto-fill-mode= with =org-mode=. | ||
714 | |||
715 | #+begin_src emacs-lisp :noweb-ref hooks | ||
716 | (add-hook 'org-mode-hook #'auto-fill-mode) | ||
717 | #+end_src | ||
718 | |||
719 | 3. /Just/ in case ... let's "fix" =visual-line-mode= if we're in =org-mode=. | ||
720 | |||
721 | #+begin_src emacs-lisp :noweb-ref hooks | ||
722 | (defun hook--visual-line-fix-org-keys () | ||
723 | (when (derived-mode-p 'org-mode) | ||
724 | (local-set-key (kbd "C-a") #'org-beginning-of-line) | ||
725 | (local-set-key (kbd "C-e") #'org-end-of-line) | ||
726 | (local-set-key (kbd "C-k") #'org-kill-line))) | ||
727 | |||
728 | (add-hook 'visual-line-mode-hook #'hook--visual-line-fix-org-keys) | ||
729 | |||
730 | #+end_src | ||
731 | |||
732 | I think that'll work -- I only care about line aesthetics with text. | ||
733 | Programming modes should be /allowed/ to have long lines, regardless | ||
734 | of how /terrible/ it is to have them. | ||
735 | |||
736 | *** Stay snappy with long-lined files | ||
737 | |||
738 | #+begin_src emacs-lisp :noweb-ref modes | ||
739 | (when (fboundp 'global-so-long-mode) | ||
740 | (global-so-long-mode +1)) | ||
741 | #+end_src | ||
742 | |||
743 | ** Whitespace | ||
744 | |||
745 | *** Whitespace style | ||
746 | |||
747 | The =whitespace-style= defines what kinds of whitespace to clean up on | ||
748 | =whitespace-cleanup=, as well as what to highlight (if that option is | ||
749 | enabled). | ||
750 | |||
751 | #+begin_src emacs-lisp :noweb-ref settings | ||
752 | (setq-default whitespace-style '(empty ; remove blank lines at buffer edges | ||
753 | indentation ; clean up indentation | ||
754 | ;; fix mixed spaces and tabs | ||
755 | space-before-tab | ||
756 | space-after-tab)) | ||
757 | #+end_src | ||
758 | |||
759 | *** Clean up whitespace on save | ||
760 | |||
761 | #+begin_src emacs-lisp :noweb-ref hooks | ||
762 | (add-hook 'before-save-hook #'whitespace-cleanup) | ||
763 | #+end_src | ||
764 | |||
765 | *** Don't use TABs | ||
766 | |||
767 | I was team TAB for a while, but I find them easier to avoid in Emacs. | ||
768 | It manages my whitespace for me, anyway. | ||
769 | |||
770 | #+begin_src emacs-lisp :noweb-ref settings | ||
771 | (setq-default indent-tabs-mode nil) | ||
772 | #+end_src | ||
773 | |||
774 | ** Killing & Yanking | ||
775 | |||
776 | *** Replace the selection when typing | ||
777 | |||
778 | #+begin_src emacs-lisp :noweb-ref modes | ||
779 | (delete-selection-mode +1) | ||
780 | #+end_src | ||
781 | |||
782 | *** Work better with the system clipboard | ||
783 | |||
784 | #+begin_src emacs-lisp :noweb-ref settings | ||
785 | (setq-default | ||
786 | ;; Save existing clipboard text to the kill ring before replacing it. | ||
787 | save-interprogram-paste-before-kill t | ||
788 | ;; Update the X selection when rotating the kill ring. | ||
789 | yank-pop-change-selection t | ||
790 | ;; Enable clipboards | ||
791 | x-select-enable-clipboard t | ||
792 | x-select-enable-primary t | ||
793 | ;; Copy a region when it's selected with the mouse | ||
794 | mouse-drag-copy-region t) | ||
795 | #+end_src | ||
796 | |||
797 | *** Don't append the same thing twice to the kill ring | ||
798 | |||
799 | #+begin_src emacs-lisp :noweb-ref settings | ||
800 | (setq-default kill-do-not-save-duplicates t) | ||
801 | #+end_src | ||
802 | |||
803 | ** Overwrite mode | ||
804 | |||
805 | *** Change the cursor | ||
806 | |||
807 | #+begin_src emacs-lisp :noweb-ref hooks | ||
808 | (defun hook--overwrite-mode-change-cursor () | ||
809 | (setq cursor-type (if overwrite-mode t 'bar))) | ||
810 | |||
811 | (add-hook 'overwrite-mode-hook #'hook--overwrite-mode-change-cursor) | ||
812 | #+end_src | ||
813 | |||
814 | ** The Mark | ||
815 | |||
816 | *** Repeat popping the mark without repeating the prefix argument | ||
817 | |||
818 | #+begin_src emacs-lisp :noweb-ref settings | ||
819 | (setq-default set-mark-repeat-command-pop t) | ||
820 | #+end_src | ||
821 | |||
822 | * Writing | ||
823 | |||
824 | ** Word count :package: | ||
825 | |||
826 | #+begin_src emacs-lisp :noweb-ref packages | ||
827 | (straight-use-package 'wc-mode) | ||
828 | #+end_src | ||
829 | |||
830 | #+begin_src emacs-lisp :noweb-ref hooks | ||
831 | (add-hook 'text-mode-hook #'wc-mode) | ||
832 | #+end_src | ||
833 | |||
834 | * Programming | ||
835 | |||
836 | ** Comments | ||
837 | |||
838 | *** Auto fill comments in programming modes | ||
839 | |||
840 | Okay, so I lied in the [[*Auto-fill vs. Visual-line][Auto-fill vs. Visual-line]] section. I /do/ want | ||
841 | to auto-fill in programming modes, but /only/ the comments. | ||
842 | |||
843 | #+begin_src emacs-lisp :noweb-ref hooks | ||
844 | (defun hook--comment-auto-fill () | ||
845 | (setq-local comment-auto-fill-only-comments t) | ||
846 | (auto-fill-mode +1)) | ||
847 | |||
848 | (add-hook 'prog-mode-hook #'hook--comment-auto-fill) | ||
849 | #+end_src | ||
850 | |||
851 | ** Parentheses | ||
852 | |||
853 | *** Show parentheses | ||
854 | |||
855 | #+begin_src emacs-lisp :noweb-ref modes | ||
856 | (show-paren-mode +1) | ||
857 | #+end_src | ||
858 | |||
859 | #+begin_src emacs-lisp :noweb-ref settings | ||
860 | (setq-default show-paren-delay 0 | ||
861 | ;; Show the matching paren if visible, else the whole expression | ||
862 | show-paren-style 'mixed) | ||
863 | #+end_src | ||
864 | |||
865 | ** Executable scripts | ||
866 | |||
867 | This poorly-named function will make a file executable if it looks | ||
868 | like a script (looking at the function definition, it looks like it | ||
869 | checks for a shebang). | ||
870 | |||
871 | #+begin_src emacs-lisp :noweb-ref hooks | ||
872 | (add-hook 'after-save-hook | ||
873 | #'executable-make-buffer-file-executable-if-script-p) | ||
874 | #+end_src | ||
875 | |||
876 | ** Language-specific | ||
877 | |||
878 | *** Emacs Lisp | ||
879 | |||
880 | **** Don't limit the length of evaluated expressions | ||
881 | |||
882 | #+begin_src emacs-lisp :noweb-ref settings | ||
883 | (setq-default eval-expression-print-length nil | ||
884 | eval-expression-print-level nil) | ||
885 | #+end_src | ||
886 | |||
887 | **** Indent Elisp like Common Lisp | ||
888 | |||
889 | #+begin_src emacs-lisp :noweb-ref requires | ||
890 | (require 'cl-lib) | ||
891 | #+end_src | ||
892 | |||
893 | #+begin_src emacs-lisp :noweb-ref settings | ||
894 | (setq-default lisp-indent-function #'common-lisp-indent-function) | ||
895 | #+end_src | ||
896 | |||
897 | |||
898 | * Applications | ||
899 | |||
900 | Emacs is well-known for its ability to subsume one's entire computing | ||
901 | life. There are a few /killer apps/ that make Emacs really shine. | ||
902 | Here, I configure them and a few others. | ||
903 | |||
904 | My rubric for what makes a package an application, versus just a | ||
905 | package, is mostly based on the way I feel about it. Don't expect to | ||
906 | agree with all of my choices. | ||
907 | |||
908 | ** Web browsing | ||
909 | |||
910 | *** Browse-url | ||
911 | |||
912 | I like using Firefox. | ||
913 | |||
914 | #+begin_src emacs-lisp :noweb-ref settings | ||
915 | (setq-default browse-url-browser-function 'browse-url-firefox | ||
916 | browse-url-new-window-flag t | ||
917 | browse-url-firefox-new-window-is-tab t) | ||
918 | #+end_src | ||
919 | |||
920 | At work, I need to tell Emacs where Firefox is. | ||
921 | |||
922 | #+begin_src emacs-lisp :noweb-ref windows-specific | ||
923 | (add-to-list 'exec-path "C:/Program Files/Mozilla Firefox") | ||
924 | #+end_src | ||
925 | |||
926 | ** Dired | ||
927 | |||
928 | #+begin_src emacs-lisp :noweb-ref hooks | ||
929 | (defun hook--dired-mode () | ||
930 | (hl-line-mode +1) | ||
931 | (dired-hide-details-mode +1)) | ||
932 | |||
933 | (add-hook 'dired-mode-hook #'hook--dired-mode) | ||
934 | #+end_src | ||
935 | |||
936 | A note on =dired-listing-switches=: when I'm able to figure out how to | ||
937 | move up a directory with a keybinding, I'll change =-a= to =-A=. | ||
938 | |||
939 | #+begin_src emacs-lisp :noweb-ref settings | ||
940 | (setq-default dired-recursive-copies 'always | ||
941 | dired-recursive-deletes 'always | ||
942 | delete-by-moving-to-trash t | ||
943 | dired-listing-switches "-alh") | ||
944 | #+end_src | ||
945 | |||
946 | *** Expand subtrees :package: | ||
947 | |||
948 | Instead of opening each folder in its own buffer, =dired-subtree= | ||
949 | enables me to open them in the same buffer, fancily indented. | ||
950 | |||
951 | #+begin_src emacs-lisp :noweb-ref packages | ||
952 | (straight-use-package 'dired-subtree) | ||
953 | #+end_src | ||
954 | |||
955 | #+begin_src emacs-lisp :noweb-ref bindings | ||
956 | (with-eval-after-load 'dired | ||
957 | (define-key dired-mode-map "i" #'dired-subtree-toggle)) | ||
958 | #+end_src | ||
959 | |||
960 | *** Collapse singleton directories | ||
961 | |||
962 | If a directory only has one item in it, =dired-collapse= shows what | ||
963 | that one item is. | ||
964 | |||
965 | #+begin_src emacs-lisp :noweb-ref packages | ||
966 | (straight-use-package 'dired-subtree) | ||
967 | #+end_src | ||
968 | |||
969 | #+begin_src emacs-lisp :noweb-ref hooks | ||
970 | (add-hook 'dired-mode-hook #'dired-collapse-mode) | ||
971 | #+end_src | ||
972 | |||
973 | ** Org mode | ||
974 | |||
975 | #+begin_src emacs-lisp :noweb-ref packages | ||
976 | (straight-use-package 'org) | ||
977 | |||
978 | (with-eval-after-load 'org | ||
979 | (require 'org-tempo) | ||
980 | (require 'ox-md)) | ||
981 | #+end_src | ||
982 | |||
983 | #+begin_src emacs-lisp :noweb-ref settings | ||
984 | (setq-default | ||
985 | ;; Where to look for Org files | ||
986 | org-directory "~/org" ; this is the default | ||
987 | ;; Fontify stuff | ||
988 | org-hide-emphasis-markers t | ||
989 | org-fontify-whole-heading-line t | ||
990 | org-fontify-done-headline t | ||
991 | org-fontify-quote-and-verse-blocks t | ||
992 | org-src-fontify-natively t | ||
993 | org-ellipsis "…" | ||
994 | org-pretty-entities t | ||
995 | ;; Source blocks | ||
996 | org-src-tab-acts-natively t | ||
997 | org-src-window-setup 'current-window ; could change this | ||
998 | org-confirm-babel-evaluate nil | ||
999 | ;; Behavior | ||
1000 | org-adapt-indentation nil ; don't indent things | ||
1001 | org-catch-invisible-edits 'smart ; let's try this | ||
1002 | org-special-ctrl-a/e t | ||
1003 | org-special-ctrl-k t | ||
1004 | ;; Exporting | ||
1005 | org-export-headline-levels 8) | ||
1006 | #+end_src | ||
1007 | |||
1008 | *** Org templates | ||
1009 | |||
1010 | #+begin_src emacs-lisp :noweb-ref settings | ||
1011 | (with-eval-after-load 'org-tempo | ||
1012 | (dolist (cell '(("el" . "src emacs-lisp") | ||
1013 | ("cr" . "src emacs-lisp :noweb-ref requires") | ||
1014 | ("cf" . "src emacs-lisp :noweb-ref functions") | ||
1015 | ("cs" . "src emacs-lisp :noweb-ref settings") | ||
1016 | ("cm" . "src emacs-lisp :noweb-ref modes") | ||
1017 | ("cl" . "src emacs-lisp :noweb-ref linux-specific") | ||
1018 | ("cw" . "src emacs-lisp :noweb-ref windows-specific") | ||
1019 | ("cp" . "src emacs-lisp :noweb-ref packages") | ||
1020 | ("ch" . "src emacs-lisp :noweb-ref hooks") | ||
1021 | ("cb" . "src emacs-lisp :noweb-ref bindings") | ||
1022 | ("cnl" . "src emacs-lisp :noweb-ref no-littering"))) | ||
1023 | (add-to-list 'org-structure-template-alist cell))) | ||
1024 | #+end_src | ||
1025 | |||
1026 | *** Org Return: DWIM :unpackaged: | ||
1027 | |||
1028 | #+begin_src emacs-lisp :noweb-ref functions | ||
1029 | (defun unpackaged/org-element-descendant-of (type element) | ||
1030 | "Return non-nil if ELEMENT is a descendant of TYPE. | ||
1031 | TYPE should be an element type, like `item' or `paragraph'. | ||
1032 | ELEMENT should be a list like that returned by `org-element-context'." | ||
1033 | ;; MAYBE: Use `org-element-lineage'. | ||
1034 | (when-let* ((parent (org-element-property :parent element))) | ||
1035 | (or (eq type (car parent)) | ||
1036 | (unpackaged/org-element-descendant-of type parent)))) | ||
1037 | |||
1038 | (defun unpackaged/org-return-dwim (&optional default) | ||
1039 | "A helpful replacement for `org-return'. With prefix, call `org-return'. | ||
1040 | |||
1041 | On headings, move point to position after entry content. In | ||
1042 | lists, insert a new item or end the list, with checkbox if | ||
1043 | appropriate. In tables, insert a new row or end the table." | ||
1044 | ;; Inspired by John Kitchin: http://kitchingroup.cheme.cmu.edu/blog/2017/04/09/A-better-return-in-org-mode/ | ||
1045 | (interactive "P") | ||
1046 | (if default | ||
1047 | (org-return) | ||
1048 | (cond | ||
1049 | ;; Act depending on context around point. | ||
1050 | |||
1051 | ;; NOTE: I prefer RET to not follow links, but by uncommenting this block, links will be | ||
1052 | ;; followed. | ||
1053 | |||
1054 | ;; ((eq 'link (car (org-element-context))) | ||
1055 | ;; ;; Link: Open it. | ||
1056 | ;; (org-open-at-point-global)) | ||
1057 | |||
1058 | ((org-at-heading-p) | ||
1059 | ;; Heading: Move to position after entry content. | ||
1060 | ;; NOTE: This is probably the most interesting feature of this function. | ||
1061 | (let ((heading-start (org-entry-beginning-position))) | ||
1062 | (goto-char (org-entry-end-position)) | ||
1063 | (cond ((and (org-at-heading-p) | ||
1064 | (= heading-start (org-entry-beginning-position))) | ||
1065 | ;; Entry ends on its heading; add newline after | ||
1066 | (end-of-line) | ||
1067 | (insert "\n\n")) | ||
1068 | (t | ||
1069 | ;; Entry ends after its heading; back up | ||
1070 | (forward-line -1) | ||
1071 | (end-of-line) | ||
1072 | (when (org-at-heading-p) | ||
1073 | ;; At the same heading | ||
1074 | (forward-line) | ||
1075 | (insert "\n") | ||
1076 | (forward-line -1)) | ||
1077 | ;; FIXME: looking-back is supposed to be called with more arguments. | ||
1078 | (while (not (looking-back (rx (repeat 3 (seq (optional blank) "\n"))) nil)) | ||
1079 | (insert "\n")) | ||
1080 | (forward-line -1))))) | ||
1081 | |||
1082 | ((org-at-item-checkbox-p) | ||
1083 | ;; Checkbox: Insert new item with checkbox. | ||
1084 | (org-insert-todo-heading nil)) | ||
1085 | |||
1086 | ((org-in-item-p) | ||
1087 | ;; Plain list. Yes, this gets a little complicated... | ||
1088 | (let ((context (org-element-context))) | ||
1089 | (if (or (eq 'plain-list (car context)) ; First item in list | ||
1090 | (and (eq 'item (car context)) | ||
1091 | (not (eq (org-element-property :contents-begin context) | ||
1092 | (org-element-property :contents-end context)))) | ||
1093 | (unpackaged/org-element-descendant-of 'item context)) ; Element in list item, e.g. a link | ||
1094 | ;; Non-empty item: Add new item. | ||
1095 | (org-insert-item) | ||
1096 | ;; Empty item: Close the list. | ||
1097 | ;; TODO: Do this with org functions rather than operating on the text. Can't seem to find the right function. | ||
1098 | (delete-region (line-beginning-position) (line-end-position)) | ||
1099 | (insert "\n")))) | ||
1100 | |||
1101 | ((when (fboundp 'org-inlinetask-in-task-p) | ||
1102 | (org-inlinetask-in-task-p)) | ||
1103 | ;; Inline task: Don't insert a new heading. | ||
1104 | (org-return)) | ||
1105 | |||
1106 | ((org-at-table-p) | ||
1107 | (cond ((save-excursion | ||
1108 | (beginning-of-line) | ||
1109 | ;; See `org-table-next-field'. | ||
1110 | (cl-loop with end = (line-end-position) | ||
1111 | for cell = (org-element-table-cell-parser) | ||
1112 | always (equal (org-element-property :contents-begin cell) | ||
1113 | (org-element-property :contents-end cell)) | ||
1114 | while (re-search-forward "|" end t))) | ||
1115 | ;; Empty row: end the table. | ||
1116 | (delete-region (line-beginning-position) (line-end-position)) | ||
1117 | (org-return)) | ||
1118 | (t | ||
1119 | ;; Non-empty row: call `org-return'. | ||
1120 | (org-return)))) | ||
1121 | (t | ||
1122 | ;; All other cases: call `org-return'. | ||
1123 | (org-return))))) | ||
1124 | #+end_src | ||
1125 | |||
1126 | #+begin_src emacs-lisp :noweb-ref bindings | ||
1127 | (with-eval-after-load 'org | ||
1128 | (define-key org-mode-map (kbd "RET") #'unpackaged/org-return-dwim)) | ||
1129 | #+end_src | ||
1130 | |||
1131 | |||
549 | * Package management :package: | 1132 | * Package management :package: |
550 | :PROPERTIES: | 1133 | :PROPERTIES: |
551 | :header-args: :noweb-ref package-bootstrap | 1134 | :header-args: :noweb-ref early-init-package |
552 | :END: | 1135 | :END: |
553 | 1136 | ||
554 | Emacs is the /extensible/ editor, and that means I want to use | 1137 | Emacs is the /extensible/ editor, and that means I want to use |
555 | third-party packages. Of course, first I have to /manage/ those | 1138 | third-party packages. Of course, first I have to /manage/ those |
556 | packages. I use the excellent =straight.el=. | 1139 | packages. I use the excellent =straight.el=. |
557 | 1140 | ||
558 | ** TODO Update the PATH | 1141 | ** Update the PATH |
1142 | |||
1143 | PATH handling on Emacs is a little complicated. There's the regular | ||
1144 | environment variable =$PATH=, which we all know and love, and then | ||
1145 | Emacs has its own special =exec-path= on /top/ of that. From my | ||
1146 | research, it looks like Emacs uses =exec-path= for itself, and =$PATH= | ||
1147 | for any shells or other processes it spawns. They don't /have/ to be | ||
1148 | the same, but luckily for us, Emacs sets =exec-path= from =$PATH= on | ||
1149 | initialization, so when I add stuff to =exec-path= to, say, run git, I | ||
1150 | can just change =$PATH= right back to the expanded =exec-path= without | ||
1151 | any data loss. Here's what all that looks like. | ||
1152 | |||
1153 | #+begin_src emacs-lisp | ||
1154 | (let ((win-app-dir "~/Applications")) | ||
1155 | (dolist (path (list | ||
1156 | ;; Windows | ||
1157 | (expand-file-name "Git/bin" win-app-dir) | ||
1158 | (expand-file-name "Git/usr/bin" win-app-dir) | ||
1159 | (expand-file-name "Git/mingw64/bin" win-app-dir) | ||
1160 | ;; Linux | ||
1161 | (expand-file-name "bin" | ||
1162 | user-emacs-directory) | ||
1163 | (expand-file-name "~/bin") | ||
1164 | (expand-file-name "~/.local/bin") | ||
1165 | (expand-file-name "~/Scripts") | ||
1166 | )) | ||
1167 | (when (file-exists-p path) | ||
1168 | (add-to-list 'exec-path path :append)))) | ||
1169 | |||
1170 | ;; Set $PATH | ||
1171 | (setenv "PATH" (mapconcat #'identity exec-path path-separator)) | ||
1172 | #+end_src | ||
1173 | |||
1174 | *** References | ||
1175 | |||
1176 | - [[https://emacs.stackexchange.com/questions/550/exec-path-and-path][exec-path and $PATH (StackExchange)]] | ||
1177 | - [[https://utoi.tistory.com/entry/Difference-Between-Emacss-%E2%80%9Cgetenv-PATH%E2%80%9D-and-%E2%80%9Cexec-path%E2%80%9D][Difference between Emacs's "(getenv PATH)" and "exec-path" (U&I)]] | ||
1178 | - [[https://emacs.stackexchange.com/questions/27326/gui-emacs-sets-the-exec-path-only-from-windows-environment-variable-but-not-from][GUI Emacs sets the exec-path only from Windows environment variable | ||
1179 | but not from .emacs file (StackExchange)]] | ||
559 | 1180 | ||
560 | ** Disable =package.el= | 1181 | ** Disable =package.el= |
561 | 1182 | ||
@@ -573,19 +1194,19 @@ function so I can call it in another wrapper. | |||
573 | "Bootstrap straight.el." | 1194 | "Bootstrap straight.el." |
574 | (defvar bootstrap-version) | 1195 | (defvar bootstrap-version) |
575 | (let ((bootstrap-file | 1196 | (let ((bootstrap-file |
576 | (expand-file-name | 1197 | (expand-file-name |
577 | "straight/repos/straight.el/bootstrap.el" | 1198 | "straight/repos/straight.el/bootstrap.el" |
578 | user-emacs-directory)) | 1199 | user-emacs-directory)) |
579 | (bootstrap-version 5)) | 1200 | (bootstrap-version 5)) |
580 | (unless (file-exists-p bootstrap-file) | 1201 | (unless (file-exists-p bootstrap-file) |
581 | (with-current-buffer | 1202 | (with-current-buffer |
582 | (url-retrieve-synchronously | 1203 | (url-retrieve-synchronously |
583 | (concat | 1204 | (concat |
584 | "https://raw.githubusercontent.com/" | 1205 | "https://raw.githubusercontent.com/" |
585 | "raxod502/straight.el/develop/install.el") | 1206 | "raxod502/straight.el/develop/install.el") |
586 | 'silent 'inhibit-cookies) | 1207 | 'silent 'inhibit-cookies) |
587 | (goto-char (point-max)) | 1208 | (goto-char (point-max)) |
588 | (eval-print-last-sexp))) | 1209 | (eval-print-last-sexp))) |
589 | (load bootstrap-file nil 'nomessage))) | 1210 | (load bootstrap-file nil 'nomessage))) |
590 | #+end_src | 1211 | #+end_src |
591 | 1212 | ||
@@ -594,17 +1215,18 @@ directly. If it errors (it tends to on Windows), I'll directly clone | |||
594 | the repo using git, /then/ run the bootstrap code. | 1215 | the repo using git, /then/ run the bootstrap code. |
595 | 1216 | ||
596 | #+begin_src emacs-lisp | 1217 | #+begin_src emacs-lisp |
597 | (unless (ignore-errors (acdw/bootstrap-straight)) | 1218 | (when (executable-find "git") |
598 | (let ((msg "Straight.el didn't bootstrap correctly. Cloning directly")) | 1219 | (unless (ignore-errors (acdw/bootstrap-straight)) |
599 | (message "%s..." msg) | 1220 | (let ((msg "Straight.el didn't bootstrap correctly. Cloning directly")) |
600 | (call-process "git" nil | 1221 | (message "%s..." msg) |
601 | (get-buffer-create "*bootstrap-straight-messages*") nil | 1222 | (call-process "git" nil |
602 | "clone" | 1223 | (get-buffer-create "*bootstrap-straight-messages*") nil |
603 | "https://github.com/raxod502/straight.el" | 1224 | "clone" |
604 | (expand-file-name "straight/repos/straight.el" | 1225 | "https://github.com/raxod502/straight.el" |
605 | user-emacs-directory)) | 1226 | (expand-file-name "straight/repos/straight.el" |
606 | (message "%s...Done." msg) | 1227 | user-emacs-directory)) |
607 | (acdw/bootstrap-straight))) | 1228 | (message "%s...Done." msg) |
1229 | (acdw/bootstrap-straight)))) | ||
608 | #+end_src | 1230 | #+end_src |
609 | 1231 | ||
610 | * System-specific | 1232 | * System-specific |
@@ -615,9 +1237,13 @@ settings and written some ancillary scripts. | |||
615 | 1237 | ||
616 | ** Determine where I am | 1238 | ** Determine where I am |
617 | :PROPERTIES: | 1239 | :PROPERTIES: |
618 | :header-args: :noweb-ref functions | 1240 | :header-args: :noweb-ref when-at |
619 | :END: | 1241 | :END: |
620 | 1242 | ||
1243 | This macro needs to go into =init.el=, /before/ loading =config.el= -- | ||
1244 | because I've used the =when-at= form in the =:tangle= directive for | ||
1245 | the scripts in this section. | ||
1246 | |||
621 | #+begin_src emacs-lisp | 1247 | #+begin_src emacs-lisp |
622 | (defmacro when-at (conditions &rest commands) | 1248 | (defmacro when-at (conditions &rest commands) |
623 | "Run COMMANDS, or let the user know, when at a specific place. | 1249 | "Run COMMANDS, or let the user know, when at a specific place. |
@@ -629,20 +1255,20 @@ settings and written some ancillary scripts. | |||
629 | If COMMANDS is empty or nil, simply return the result of CONDITIONS." | 1255 | If COMMANDS is empty or nil, simply return the result of CONDITIONS." |
630 | (declare (indent 1)) | 1256 | (declare (indent 1)) |
631 | (let ((at-work '(memq system-type '(ms-dos windows-nt))) | 1257 | (let ((at-work '(memq system-type '(ms-dos windows-nt))) |
632 | (at-home '(memq system-type '(gnu gnu/linux gnu/kfreebsd)))) | 1258 | (at-home '(memq system-type '(gnu gnu/linux gnu/kfreebsd)))) |
633 | (pcase conditions | 1259 | (pcase conditions |
634 | (:work (if commands `(when ,at-work ,@commands) at-work)) | 1260 | (:work (if commands `(when ,at-work ,@commands) at-work)) |
635 | (:home (if commands `(when ,at-home ,@commands) at-home)) | 1261 | (:home (if commands `(when ,at-home ,@commands) at-home)) |
636 | ((guard (eq (car conditions) :work)) | 1262 | ((guard (eq (car conditions) :work)) |
637 | (if commands | 1263 | (if commands |
638 | `(when (and ,at-work ,@(cdr conditions)) | 1264 | `(when (and ,at-work ,@(cdr conditions)) |
639 | ,@commands) | 1265 | ,@commands) |
640 | `(and ,at-work ,@(cdr conditions)))) | 1266 | `(and ,at-work ,@(cdr conditions)))) |
641 | ((guard (eq (car conditions) :home)) | 1267 | ((guard (eq (car conditions) :home)) |
642 | (if commands | 1268 | (if commands |
643 | `(when (and ,at-home ,@(cdr conditions)) | 1269 | `(when (and ,at-home ,@(cdr conditions)) |
644 | ,@commands) | 1270 | ,@commands) |
645 | `(and ,at-work ,@(cdr conditions))))))) | 1271 | `(and ,at-work ,@(cdr conditions))))))) |
646 | #+end_src | 1272 | #+end_src |
647 | 1273 | ||
648 | ** Linux (home) | 1274 | ** Linux (home) |
@@ -663,7 +1289,7 @@ Here's a wrapper script that'll start =emacs --daemon= if there isn't | |||
663 | one, and then launch =emacsclient= with the arguments. Install it to | 1289 | one, and then launch =emacsclient= with the arguments. Install it to |
664 | your =$PATH= somewhere. | 1290 | your =$PATH= somewhere. |
665 | 1291 | ||
666 | #+begin_src sh :shebang "#!/bin/sh" :tangle (when-at :home "~/bin/em") | 1292 | #+begin_src sh :shebang "#!/bin/sh" :tangle (if (eq system-type 'gnu/linux) "~/bin/em" "") |
667 | if ! emacsclient -nc "$@"; then | 1293 | if ! emacsclient -nc "$@"; then |
668 | emacs --daemon | 1294 | emacs --daemon |
669 | emacsclient -nc "$@" | 1295 | emacsclient -nc "$@" |
@@ -678,7 +1304,7 @@ your =$PATH= somewhere. | |||
678 | I haven't really tested this yet, but it should allow me to open other | 1304 | I haven't really tested this yet, but it should allow me to open other |
679 | files and things in Emacs. From [[https://www.taingram.org/blog/emacs-client.html][taingram]]. | 1305 | files and things in Emacs. From [[https://www.taingram.org/blog/emacs-client.html][taingram]]. |
680 | 1306 | ||
681 | #+begin_src conf-desktop :tangle (when-at :home "~/.local/share/applications/emacsclient.desktop") | 1307 | #+begin_src conf-desktop :tangle (if (eq system-type 'gnu/linux) "~/.local/share/applications/emacsclient.desktop" "") |
682 | [Desktop Entry] | 1308 | [Desktop Entry] |
683 | Name=Emacs Client | 1309 | Name=Emacs Client |
684 | GenericName=Text Editor | 1310 | GenericName=Text Editor |
@@ -713,10 +1339,10 @@ section come from [[https://github.com/termitereform/JunkPile/blob/master/emacs- | |||
713 | 1339 | ||
714 | **** Common variables | 1340 | **** Common variables |
715 | 1341 | ||
716 | #+NAME: w32-bat-common | 1342 | #+begin_src bat :noweb-ref w32-bat-common |
717 | #+begin_src bat | ||
718 | set HOME=%~dp0..\.. | 1343 | set HOME=%~dp0..\.. |
719 | set EMACS=%HOME%\Applications\Emacs\bin\runemacs.exe | 1344 | set EMACS=%HOME%\Applications\Emacs\bin\runemacs.exe |
1345 | chdir %HOME% | ||
720 | #+end_src | 1346 | #+end_src |
721 | 1347 | ||
722 | **** Emacs Daemon | 1348 | **** Emacs Daemon |
@@ -725,7 +1351,7 @@ Either run this once at startup, or put a shortcut of it in the | |||
725 | Startup folder: | 1351 | Startup folder: |
726 | =%USERPROFILE%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup=. | 1352 | =%USERPROFILE%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup=. |
727 | 1353 | ||
728 | #+begin_src bat :tangle (when-at :work "~/Applications/cmd/Emacs Daemon.cmd") | 1354 | #+begin_src bat :tangle (if (eq system-type 'windows-nt) "~/Applications/cmd/Emacs Daemon.cmd" "") |
729 | <<w32-bat-common>> | 1355 | <<w32-bat-common>> |
730 | %EMACS% --daemon | 1356 | %EMACS% --daemon |
731 | #+end_src | 1357 | #+end_src |
@@ -737,7 +1363,7 @@ run =runemacs.exe=. | |||
737 | 1363 | ||
738 | *This is the main shortcut for running Emacs.* | 1364 | *This is the main shortcut for running Emacs.* |
739 | 1365 | ||
740 | #+begin_src bat :tangle (when-at :work "~/Applications/cmd/Emacs.cmd") | 1366 | #+begin_src bat :tangle (if (eq system-type 'windows-nt) "~/Applications/cmd/Emacs.cmd" "") |
741 | <<w32-bat-common>> | 1367 | <<w32-bat-common>> |
742 | set EMACSC=%HOME%\Applications\Emacs\bin\emacsclientw.exe | 1368 | set EMACSC=%HOME%\Applications\Emacs\bin\emacsclientw.exe |
743 | "%EMACSC%" -n -c -a "%EMACS%" %* | 1369 | "%EMACSC%" -n -c -a "%EMACS%" %* |
@@ -747,7 +1373,7 @@ set EMACSC=%HOME%\Applications\Emacs\bin\emacsclientw.exe | |||
747 | 1373 | ||
748 | This runs Emacs with the factory settings. | 1374 | This runs Emacs with the factory settings. |
749 | 1375 | ||
750 | #+begin_src bat :tangle (when-at :work "~/Applications/cmd/Emacs Safe Start.cmd") | 1376 | #+begin_src bat :tangle (if (eq system-type 'windows-nt) "~/Applications/cmd/Emacs Safe Start.cmd" "") |
751 | <<w32-bat-common>> | 1377 | <<w32-bat-common>> |
752 | "%EMACS%" -Q %* | 1378 | "%EMACS%" -Q %* |
753 | #+end_src | 1379 | #+end_src |
@@ -756,7 +1382,7 @@ This runs Emacs with the factory settings. | |||
756 | 1382 | ||
757 | This runs Emacs with the =--debug-init= option enabled. | 1383 | This runs Emacs with the =--debug-init= option enabled. |
758 | 1384 | ||
759 | #+begin_src bat :tangle (when-at :work "~/Applications/cmd/Emacs Debug.cmd") | 1385 | #+begin_src bat :tangle (if (eq system-type 'windows-nt) "~/Applications/cmd/Emacs Debug.cmd" "") |
760 | <<w32-bat-common>> | 1386 | <<w32-bat-common>> |
761 | "%EMACS%" --debug-init %* | 1387 | "%EMACS%" --debug-init %* |
762 | #+end_src | 1388 | #+end_src |
@@ -794,20 +1420,25 @@ my config here /logically/, while keeping the generated file organized | |||
794 | ;;; REQUIRES | 1420 | ;;; REQUIRES |
795 | <<requires>> | 1421 | <<requires>> |
796 | ;;; PACKAGES | 1422 | ;;; PACKAGES |
797 | <<packages>> | 1423 | ;; straight.el depends on git, which /should be/ find-able by the PATH |
1424 | ;; manipulation in early-init.el. Just in case, though, we'll check | ||
1425 | ;; that we can find git. | ||
1426 | (when (executable-find "git") | ||
1427 | <<packages>> | ||
1428 | ) | ||
798 | ;;; FUNCTIONS | 1429 | ;;; FUNCTIONS |
799 | <<functions>> | 1430 | <<functions>> |
800 | ;;; SETTINGS | 1431 | ;;; SETTINGS |
801 | <<settings>> | 1432 | <<settings>> |
802 | ;;; MODES | ||
803 | <<modes>> | ||
804 | ;;; SYSTEM-DEPENDENT SETTINGS | 1433 | ;;; SYSTEM-DEPENDENT SETTINGS |
805 | (when-at :home | 1434 | (when-at :home |
806 | <<linux-specific>> | 1435 | <<linux-specific>> |
807 | ) | 1436 | ) ; end when-at :home |
808 | (when-at :work | 1437 | (when-at :work |
809 | <<windows-specific>> | 1438 | <<windows-specific>> |
810 | ) | 1439 | ) ; end when-at :work |
1440 | ;;; MODES | ||
1441 | <<modes>> | ||
811 | ;;; HOOKS | 1442 | ;;; HOOKS |
812 | <<hooks>> | 1443 | <<hooks>> |
813 | ;;; BINDINGS | 1444 | ;;; BINDINGS |
@@ -836,6 +1467,14 @@ The classic Emacs initiation file. | |||
836 | (setq-default load-prefer-newer t) | 1467 | (setq-default load-prefer-newer t) |
837 | #+end_src | 1468 | #+end_src |
838 | 1469 | ||
1470 | **** =when-at= | ||
1471 | |||
1472 | See [[*Determine where I am][the definition above]] for rationale as to why this is here. | ||
1473 | |||
1474 | #+begin_src emacs-lisp | ||
1475 | <<when-at>> | ||
1476 | #+end_src | ||
1477 | |||
839 | **** Load the config | 1478 | **** Load the config |
840 | 1479 | ||
841 | I keep most of my config in =config.el=, which is tangled directly | 1480 | I keep most of my config in =config.el=, which is tangled directly |
@@ -844,28 +1483,28 @@ directly from Org if it's newer. | |||
844 | 1483 | ||
845 | #+begin_src emacs-lisp | 1484 | #+begin_src emacs-lisp |
846 | (let* (;; Speed up init | 1485 | (let* (;; Speed up init |
847 | (gc-cons-threshold most-positive-fixnum) | 1486 | (gc-cons-threshold most-positive-fixnum) |
848 | (file-name-handler-alist nil) | 1487 | (file-name-handler-alist nil) |
849 | ;; Config file names | 1488 | ;; Config file names |
850 | (config (expand-file-name "config" | 1489 | (config (expand-file-name "config" |
851 | user-emacs-directory)) | 1490 | user-emacs-directory)) |
852 | (config.el (concat config ".el")) | 1491 | (config.el (concat config ".el")) |
853 | (config.org (concat config ".org")) | 1492 | (config.org (concat config ".org")) |
854 | (straight-org-dir (expand-file-name "straight/build/org" | 1493 | (straight-org-dir (expand-file-name "straight/build/org" |
855 | user-emacs-directory))) | 1494 | user-emacs-directory))) |
856 | ;; Unless config.org is /newer/ than config.el, *or* the config | 1495 | ;; Unless config.org is /newer/ than config.el, *or* the config |
857 | ;; is able to be loaded without errors, load the config from | 1496 | ;; is able to be loaded without errors, load the config from |
858 | ;; config.org. | 1497 | ;; config.org. |
859 | (unless (or (file-newer-than-file-p config.org config.el) | 1498 | (unless (or (file-newer-than-file-p config.org config.el) |
860 | (load config 'no-error)) | 1499 | (load config 'no-error)) |
861 | ;; A plain require here just loads the older `org' | 1500 | ;; A plain require here just loads the older `org' |
862 | ;; in Emacs' install dir. We need to add the newer | 1501 | ;; in Emacs' install dir. We need to add the newer |
863 | ;; one to the `load-path', hopefully that's all. | 1502 | ;; one to the `load-path', hopefully that's all. |
864 | (when (file-exists-p straight-org-dir) | 1503 | (when (file-exists-p straight-org-dir) |
865 | (add-to-list 'load-path straight-org-dir)) | 1504 | (add-to-list 'load-path straight-org-dir)) |
866 | ;; Load config.org | 1505 | ;; Load config.org |
867 | (require 'org) | 1506 | (require 'org) |
868 | (org-babel-load-file config.org))) | 1507 | (org-babel-load-file config.org))) |
869 | #+end_src | 1508 | #+end_src |
870 | 1509 | ||
871 | *** early-init.el | 1510 | *** early-init.el |
@@ -877,46 +1516,13 @@ Beginning with 27.1, Emacs also loads an =early-init.el= file, before | |||
877 | the package manager or the UI code. The Info says we should put as | 1516 | the package manager or the UI code. The Info says we should put as |
878 | little as possible in this file, so I only have what I need. | 1517 | little as possible in this file, so I only have what I need. |
879 | 1518 | ||
880 | **** Don't byte-compile this file | ||
881 | |||
882 | #+begin_src emacs-lisp | 1519 | #+begin_src emacs-lisp |
883 | ;; early-init.el -*- no-byte-compile: t; -*- | 1520 | ;; early-init.el -*- no-byte-compile: t; -*- |
884 | <<disclaimer>> | 1521 | <<disclaimer>> |
885 | #+end_src | 1522 | ;; BOOTSTRAP PACKAGE MANAGEMENT |
886 | 1523 | <<early-init-package>> | |
887 | **** Package management | 1524 | ;; SETUP FRAME |
888 | 1525 | <<early-init-frame>> | |
889 | Since =early-init.el= loads before the built-in package manager | ||
890 | initializes, I disable it and bootstrap my package manager of choice, | ||
891 | =straight.el=. | ||
892 | |||
893 | #+begin_src emacs-lisp | ||
894 | <<package-bootstrap>> | ||
895 | #+end_src | ||
896 | |||
897 | **** Don't resize the frame when loading fonts | ||
898 | |||
899 | #+begin_src emacs-lisp | ||
900 | (setq-default frame-inhibit-implied-resize t) | ||
901 | #+end_src | ||
902 | |||
903 | **** Resize frame by pixels | ||
904 | |||
905 | #+begin_src emacs-lisp | ||
906 | (setq-default frame-resize-pixelwise t) | ||
907 | #+end_src | ||
908 | |||
909 | **** Shoe-horned from elsewhere in =config.org= | ||
910 | |||
911 | A fundamental tension of literal programming is logical versus | ||
912 | programmatic ordering. I understand that's a problem it's meant to | ||
913 | solve but hey, maybe I'm not quite there yet. I feel that having this | ||
914 | weird shoe-horning of other bits of my config here, in a backwater | ||
915 | heading in an appendix, isn't quite the future I wanted. But it's | ||
916 | what I have for now. | ||
917 | |||
918 | #+begin_src emacs-lisp | ||
919 | <<initial-frame-setup>> | ||
920 | #+end_src | 1526 | #+end_src |
921 | 1527 | ||
922 | ** License | 1528 | ** License |
@@ -924,7 +1530,7 @@ what I have for now. | |||
924 | :header-args: :tangle LICENSE | 1530 | :header-args: :tangle LICENSE |
925 | :END: | 1531 | :END: |
926 | 1532 | ||
927 | Copyright © 2020 Case Duckworth <acdw@acdw.net> | 1533 | Copyright © 2020 Case Duckworth <acdw@acdw.net> |
928 | 1534 | ||
929 | This work is free. You can redistribute it and/or modify it under the | 1535 | This work is free. You can redistribute it and/or modify it under the |
930 | terms of the Do What the Fuck You Want To Public License, Version 2, | 1536 | terms of the Do What the Fuck You Want To Public License, Version 2, |
@@ -954,33 +1560,3 @@ It's highly likely that the WTFPL is completely incompatible with the | |||
954 | GPL, for what should be fairly obvious reasons. To that, I say: | 1560 | GPL, for what should be fairly obvious reasons. To that, I say: |
955 | 1561 | ||
956 | *SUE ME, RMS!* | 1562 | *SUE ME, RMS!* |
957 | |||
958 | ** Make it easier to edit =config.org= :noexport: | ||
959 | |||
960 | Because I use a lot of =:noweb-ref= directives in this file, editing | ||
961 | it can be annoying -- I need some nice templates. Run this code block | ||
962 | with =C-c C-c=. | ||
963 | |||
964 | #+begin_src emacs-lisp :results output silent | ||
965 | (require 'org-tempo) | ||
966 | |||
967 | (dolist (cell '(("el" . "src emacs-lisp") | ||
968 | ("cr" . "src emacs-lisp :noweb-ref requires") | ||
969 | ("cf" . "src emacs-lisp :noweb-ref functions") | ||
970 | ("cs" . "src emacs-lisp :noweb-ref settings") | ||
971 | ("cm" . "src emacs-lisp :noweb-ref modes") | ||
972 | ("cl" . "src emacs-lisp :noweb-ref linux-specific") | ||
973 | ("cw" . "src emacs-lisp :noweb-ref windows-specific") | ||
974 | ("cp" . "src emacs-lisp :noweb-ref packages") | ||
975 | ("ch" . "src emacs-lisp :noweb-ref hooks") | ||
976 | ("cb" . "src emacs-lisp :noweb-ref bindings"))) | ||
977 | (add-to-list 'org-structure-template-alist cell)) | ||
978 | #+end_src | ||
979 | |||
980 | ** Local variables :noexport: | ||
981 | |||
982 | # Local variables: | ||
983 | # org-adapt-indentation: nil | ||
984 | # lisp-indent-function: 'common-lisp-indent-function | ||
985 | # eval: (auto-fill-mode +1) | ||
986 | # End: | ||