diff options
-rw-r--r-- | config.org | 386 | ||||
-rw-r--r-- | early-init.el | 8 | ||||
-rw-r--r-- | init.el | 4 |
3 files changed, 346 insertions, 52 deletions
diff --git a/config.org b/config.org index 2a9c97b..ac18133 100644 --- a/config.org +++ b/config.org | |||
@@ -1,6 +1,6 @@ | |||
1 | #+TITLE: Emacs configuration, literate-style | 1 | #+TITLE: Emacs configuration, literate-style |
2 | #+AUTHOR: Case Duckworth | 2 | #+AUTHOR: Case Duckworth |
3 | #+PROPERTY: header-args :tangle yes :tangle-mode (identity #o444) :comments both :mkdirp yes | 3 | #+PROPERTY: header-args :tangle yes :tangle-mode (identity #o444) :comments both |
4 | 4 | ||
5 | * Settings | 5 | * Settings |
6 | 6 | ||
@@ -56,11 +56,10 @@ These should not require non-built-in packages. | |||
56 | (add-function :before after-focus-change-function #'acdw/set-custom-faces) | 56 | (add-function :before after-focus-change-function #'acdw/set-custom-faces) |
57 | 57 | ||
58 | (defmacro cussface (face spec &optional _docstring) | 58 | (defmacro cussface (face spec &optional _docstring) |
59 | "Add the form (FACE SPEC) to `acdw--custom-faces', and add a | 59 | "Add the form (FACE SPEC) to `acdw--custom-faces'." |
60 | hook to run `acdw/set-custom-faces' after init." | ||
61 | (declare (doc-string 3) | 60 | (declare (doc-string 3) |
62 | (indent 2)) | 61 | (indent defun)) |
63 | `(add-to-list acdw--custom-faces '(,face ,spec))) | 62 | `(add-to-list 'acdw--custom-faces '(,face ,spec))) |
64 | #+end_src | 63 | #+end_src |
65 | 64 | ||
66 | *** Only do something when Emacs is unfocused | 65 | *** Only do something when Emacs is unfocused |
@@ -110,43 +109,6 @@ My name and email address. | |||
110 | (blink-cursor-mode -1) | 109 | (blink-cursor-mode -1) |
111 | #+end_src | 110 | #+end_src |
112 | 111 | ||
113 | *** Dialogs and alerts | ||
114 | |||
115 | **** Don't use a dialog box | ||
116 | |||
117 | #+begin_src emacs-lisp | ||
118 | (cuss use-dialog-box nil) | ||
119 | #+end_src | ||
120 | |||
121 | **** Yes or no questions | ||
122 | |||
123 | #+begin_src emacs-lisp | ||
124 | (fset 'yes-or-no-p #'y-or-n-p) | ||
125 | #+end_src | ||
126 | |||
127 | **** The Bell | ||
128 | |||
129 | #+begin_src emacs-lisp | ||
130 | ;; Don't flash the whole screen on bell | ||
131 | (cuss visible-bell nil) | ||
132 | |||
133 | ;; Instead, flash the mode line | ||
134 | (cuss ring-bell-function #'flash-mode-line) | ||
135 | |||
136 | (defun flash-mode-line () | ||
137 | (invert-face 'mode-line) | ||
138 | (run-with-timer 0.2 nil #'invert-face 'mode-line)) | ||
139 | #+end_src | ||
140 | |||
141 | t*** Minibuffer | ||
142 | |||
143 | **** Keep the cursor away from the minibuffer prompt | ||
144 | |||
145 | #+begin_src emacs-lisp | ||
146 | (cuss minibuffer-prompt-properties | ||
147 | '(read-only t cursor-intangible t face minibuffer-prompt)) | ||
148 | #+end_src | ||
149 | |||
150 | *** Tabs | 112 | *** Tabs |
151 | 113 | ||
152 | **** Tab names should be current buffer + a count of windows | 114 | **** Tab names should be current buffer + a count of windows |
@@ -237,20 +199,352 @@ and have made a custom fringe bitmap. | |||
237 | 199 | ||
238 | #+begin_src emacs-lisp | 200 | #+begin_src emacs-lisp |
239 | (cuss visual-line-fringe-indicators '(left-curly-arrow nil)) | 201 | (cuss visual-line-fringe-indicators '(left-curly-arrow nil)) |
202 | #+end_src | ||
240 | 203 | ||
241 | ;; And make the `left-curly-arrow' indicator less distracting. | 204 | ***** Customize fringe bitmaps |
242 | 205 | ||
206 | ****** Curly arrows (continuation lines) | ||
207 | |||
208 | #+begin_src emacs-lisp | ||
243 | (define-fringe-bitmap 'left-curly-arrow | 209 | (define-fringe-bitmap 'left-curly-arrow |
244 | [#b11000000 | 210 | [#b11000000 |
245 | #b01100000 | 211 | #b01100000 |
246 | #b00110000 | 212 | #b00110000 |
247 | #b00011000]) | 213 | #b00011000]) |
214 | |||
215 | (define-fringe-bitmap 'right-curly-arrow | ||
216 | [#b00011000 | ||
217 | #b00110000 | ||
218 | #b01100000 | ||
219 | #b11000000]) | ||
220 | #+end_src | ||
221 | |||
222 | ****** Arrows (truncation lines) | ||
223 | |||
224 | #+begin_src emacs-lisp | ||
225 | (define-fringe-bitmap 'left-arrow | ||
226 | [#b00000000 | ||
227 | #b01010100 | ||
228 | #b01010100 | ||
229 | #b00000000]) | ||
230 | |||
231 | (define-fringe-bitmap 'right-arrow | ||
232 | [#b00000000 | ||
233 | #b00101010 | ||
234 | #b00101010 | ||
235 | #b00000000]) | ||
248 | #+end_src | 236 | #+end_src |
249 | 237 | ||
250 | *** Windows | 238 | *** Windows |
251 | 239 | ||
240 | **** Winner mode | ||
241 | |||
242 | I don't really /use/ winner-mode as of yet, but it seems like a really | ||
243 | good thing to have. It lets you move between window configurations | ||
244 | with =C-c <-/->=. | ||
245 | |||
246 | #+begin_src emacs-lisp | ||
247 | (when (fboundp 'winner-mode) | ||
248 | (winner-mode +1)) | ||
249 | #+end_src | ||
250 | |||
251 | **** Switch windows, or buffers if there's only one | ||
252 | |||
253 | from [[https://www.reddit.com/r/emacs/comments/kz347f/what_parts_of_your_config_do_you_like_best/gjlnp2c/][u/astoff1]]. | ||
254 | |||
255 | #+begin_src emacs-lisp | ||
256 | (defun other-window-or-buffer () | ||
257 | "Switch to the other window, or previous buffer." | ||
258 | (interactive) | ||
259 | (if (eq (count-windows) 1) | ||
260 | (switch-to-buffer nil) | ||
261 | (other-window 1))) | ||
262 | #+end_src | ||
263 | |||
252 | *** Buffers | 264 | *** Buffers |
253 | 265 | ||
266 | **** Startup buffers | ||
267 | |||
268 | I don't want to see Emacs's splash screen, and I want the =*scratch*= | ||
269 | buffer to have a little message. | ||
270 | |||
271 | #+begin_src emacs-lisp | ||
272 | (cuss inhibit-startup-screen t | ||
273 | "Don't show the startup buffer.") | ||
274 | |||
275 | (cuss initial-buffer-choice t | ||
276 | "Start with *scratch*.") | ||
277 | |||
278 | (cuss initial-scratch-message | ||
279 | (concat ";; Hello, " (nth 0 (split-string user-full-name)) "!\n" | ||
280 | ";; Happy hacking ...")) | ||
281 | #+end_src | ||
282 | |||
283 | **** Immortal =*scratch*= buffer | ||
284 | |||
285 | I don't want to accidentally kill the =*scratch*= buffer. | ||
286 | |||
287 | #+begin_src emacs-lisp | ||
288 | (defun immortal-scratch () | ||
289 | (if (eq (current-buffer) (get-buffer "*scratch*")) | ||
290 | (progn (bury-buffer) | ||
291 | nil) | ||
292 | t)) | ||
293 | |||
294 | (add-hook 'kill-buffer-query-functions #'immortal-scratch) | ||
295 | #+end_src | ||
296 | |||
297 | **** Uniquify buffers | ||
298 | |||
299 | I like the =forward= style, which uniquifies buffers by including path | ||
300 | elements up the tree until the names are unique. | ||
301 | |||
302 | #+begin_src emacs-lisp | ||
303 | (require 'uniquify) | ||
304 | (cuss uniquify-buffer-name-style 'forward) | ||
305 | #+end_src | ||
306 | |||
307 | **** Kill buffers more smarter-ly | ||
308 | |||
309 | #+begin_src emacs-lisp | ||
310 | (defun kill-a-buffer (&optional prefix) | ||
311 | "Kill buffers and windows sanely. | ||
312 | |||
313 | `kill-a-buffer' works based on the prefix argument as follows: | ||
314 | |||
315 | - 0 => kill the CURRENT buffer and window | ||
316 | - 4 (C-u) => kill the OTHER window and its buffer | ||
317 | - 16 (C-u C-u) => kill ALL OTHER buffers and windows | ||
318 | |||
319 | Prompt iff there are unsaved changes." | ||
320 | (interactive "P") | ||
321 | (pcase (or (car prefix) 0) | ||
322 | (0 (kill-current-buffer) | ||
323 | (unless (one-window-p) (delete-window))) | ||
324 | (4 (other-window 1) | ||
325 | (kill-current-buffer) | ||
326 | (unless (one-window-p) (delete-window))) | ||
327 | (16 (mapc #'kill-buffer (delq (current-buffer) (buffer-list))) | ||
328 | (delete-other-windows)))) | ||
329 | #+end_src | ||
330 | |||
331 | *** Fonts | ||
332 | |||
333 | **** Function: =set-face-from-alternatives= | ||
334 | |||
335 | To be honest, this might be better off using =cussface=, but that's | ||
336 | another story for another day. | ||
337 | |||
338 | #+begin_src emacs-lisp | ||
339 | (defun set-face-from-alternatives (face frame &rest fontspecs) | ||
340 | "Set FACE on FRAME from first available font from FONTSPECS. | ||
341 | FACE and FRAME work the same as with `set-face-attribute'." | ||
342 | (catch :return | ||
343 | (dolist (spec fontspecs) | ||
344 | (when-let ((found (find-font (apply #'font-spec spec)))) | ||
345 | (set-face-attribute face frame :font found) | ||
346 | (throw :return found))))) | ||
347 | #+end_src | ||
348 | |||
349 | **** Add a hook to setup fonts after the first window focus change | ||
350 | |||
351 | Of course, I only need to setup the fonts on a graphical session. | ||
352 | |||
353 | #+begin_src emacs-lisp | ||
354 | (when (display-graphic-p) | ||
355 | (add-function :before after-focus-change-function #'acdw/setup-fonts)) | ||
356 | #+end_src | ||
357 | |||
358 | **** Setup my fonts | ||
359 | |||
360 | Notice that this function removes itself from | ||
361 | =after-focus-change-function=, since ideally you'll only need to set | ||
362 | the fonts once. | ||
363 | |||
364 | #+begin_src emacs-lisp | ||
365 | (defun acdw/setup-fonts () | ||
366 | "Setup fonts. | ||
367 | |||
368 | This has to happen after the frame is setup for the first time, | ||
369 | so it should be added to `after-focus-change-function'. It | ||
370 | removes itself." | ||
371 | (set-face-from-alternatives 'default nil | ||
372 | '(:family "Input Mono" | ||
373 | :slant normal | ||
374 | :weight normal | ||
375 | :height 110) | ||
376 | '(:family "Consolas" | ||
377 | :slant normal | ||
378 | :weight normal | ||
379 | :height 100)) | ||
380 | ;; `fixed-pitch' should just inherit from `default' | ||
381 | (set-face-attribute 'fixed-pitch nil :inherit 'default) | ||
382 | |||
383 | (set-face-from-alternatives 'variable-pitch nil | ||
384 | '(:family "Input Sans" | ||
385 | :slant normal | ||
386 | :weight normal) | ||
387 | '(:family "Georgia" | ||
388 | :slant normal | ||
389 | :weight normal)) | ||
390 | |||
391 | (remove-function after-focus-change-function #'acdw/setup-fonts)) | ||
392 | #+end_src | ||
393 | |||
394 | **** Underlines | ||
395 | |||
396 | #+begin_src emacs-lisp | ||
397 | (cuss x-underline-at-descent-line t) | ||
398 | #+end_src | ||
399 | |||
400 | ** Interactivity | ||
401 | *** Dialogs and alerts | ||
402 | |||
403 | **** Don't use a dialog box | ||
404 | |||
405 | #+begin_src emacs-lisp | ||
406 | (cuss use-dialog-box nil) | ||
407 | #+end_src | ||
408 | |||
409 | **** Yes or no questions | ||
410 | |||
411 | #+begin_src emacs-lisp | ||
412 | (fset 'yes-or-no-p #'y-or-n-p) | ||
413 | #+end_src | ||
414 | |||
415 | **** The Bell | ||
416 | |||
417 | #+begin_src emacs-lisp | ||
418 | ;; Don't flash the whole screen on bell | ||
419 | (cuss visible-bell nil) | ||
420 | |||
421 | ;; Instead, flash the mode line | ||
422 | (cuss ring-bell-function #'flash-mode-line) | ||
423 | |||
424 | (defun flash-mode-line () | ||
425 | (invert-face 'mode-line) | ||
426 | (run-with-timer 0.2 nil #'invert-face 'mode-line)) | ||
427 | #+end_src | ||
428 | |||
429 | t*** Minibuffer | ||
430 | |||
431 | *** Minibuffer | ||
432 | |||
433 | **** Keep the cursor away from the minibuffer prompt | ||
434 | |||
435 | #+begin_src emacs-lisp | ||
436 | (cuss minibuffer-prompt-properties | ||
437 | '(read-only t cursor-intangible t face minibuffer-prompt)) | ||
438 | #+end_src | ||
439 | |||
440 | **** Enable recursive minibuffer | ||
441 | |||
442 | #+begin_src emacs-lisp | ||
443 | (cuss enable-recursive-minibuffers t) | ||
444 | #+end_src | ||
445 | |||
446 | **** Show how deep the minibuffer goes in the modeline | ||
447 | |||
448 | #+begin_src emacs-lisp | ||
449 | (minibuffer-depth-indicate-mode +1) | ||
450 | #+end_src | ||
451 | |||
452 | *** Completing-read | ||
453 | |||
454 | **** Shadow file names | ||
455 | |||
456 | When typing =~= or =/= in the file-selection dialog, Emacs "pretends" | ||
457 | that you've typed them at the beginning of the line. By default, | ||
458 | however, it only /fades out/ the previous contents of the line. I | ||
459 | want to /hide/ those contents. | ||
460 | |||
461 | #+begin_src emacs-lisp | ||
462 | (cuss file-name-shadow-properties '(invisible t)) | ||
463 | |||
464 | (file-name-shadow-mode +1) | ||
465 | #+end_src | ||
466 | |||
467 | **** Ignore case | ||
468 | |||
469 | #+begin_src emacs-lisp | ||
470 | (cuss completion-ignore-case t) | ||
471 | (cuss read-buffer-completion-ignore-case t) | ||
472 | (cuss read-file-name-completion-ignore-case t) | ||
473 | #+end_src | ||
474 | |||
475 | ** Persistence | ||
476 | |||
477 | *** Minibuffer history | ||
478 | |||
479 | The =savehist= package saves the minibuffer history between sessions. | ||
480 | It can also save some other variables alongside the minibuffer | ||
481 | history. Since storage is cheap, I'm also going to keep all my | ||
482 | history. | ||
483 | |||
484 | #+begin_src emacs-lisp | ||
485 | (require 'savehist) | ||
486 | |||
487 | (cuss savehist-additional-variables | ||
488 | '(kill-ring | ||
489 | search-ring | ||
490 | regexp-search-ring)) | ||
491 | |||
492 | ;; Don't truncate history | ||
493 | (cuss history-length t) | ||
494 | |||
495 | ;; Delete history duplicates | ||
496 | (cuss history-delete-duplicates t) | ||
497 | |||
498 | (savehist-mode +1) | ||
499 | #+end_src | ||
500 | |||
501 | *** File places | ||
502 | |||
503 | The =saveplace= package saves where I've been in the files I've | ||
504 | visited, so I can return back to them. | ||
505 | |||
506 | #+begin_src emacs-lisp | ||
507 | (require 'saveplace) | ||
508 | |||
509 | ;; Forget the place in unreadable files | ||
510 | (cuss save-place-forget-unreadable-files t) | ||
511 | |||
512 | (save-place-mode +1) | ||
513 | #+end_src | ||
514 | |||
515 | *** Recent files | ||
516 | |||
517 | #+begin_src emacs-lisp | ||
518 | (require 'recentf) | ||
519 | |||
520 | ;; Limit the number of items in the recentf menu | ||
521 | (cuss recentf-max-menu-items 100) | ||
522 | ;; But not the number of items in the actual list | ||
523 | (cuss recentf-max-saved-items nil) | ||
524 | |||
525 | (recentf-mode +1) | ||
526 | #+end_src | ||
527 | |||
528 | **** Save the recentf list periodically | ||
529 | |||
530 | #+begin_src emacs-lisp | ||
531 | (defun acdw/maybe-save-recentf () | ||
532 | "Save `recentf-file every five minutes, but only when out of focus." | ||
533 | (defvar recentf--last-save (time-convert nil 'integer) | ||
534 | "When we last ran `recentf-save-list'.") | ||
535 | |||
536 | (when (> (time-convert (time-since recentf--last-save) 'integer) | ||
537 | (* 60 5)) | ||
538 | (setq recentf--last-save (time-convert nil 'integer)) | ||
539 | (acdw/when-unfocused #'recentf-save-list))) | ||
540 | |||
541 | (add-function :after after-focus-change-function #'acdw/maybe-save-recentf) | ||
542 | #+end_src | ||
543 | |||
544 | ** Files | ||
545 | |||
546 | *** Encoding | ||
547 | |||
254 | * System-specific | 548 | * System-specific |
255 | 549 | ||
256 | I use both Linux (at home) and Windows (at work). To make Emacs | 550 | I use both Linux (at home) and Windows (at work). To make Emacs |
@@ -286,7 +580,7 @@ settings and written some ancillary scripts. | |||
286 | 580 | ||
287 | **** em | 581 | **** em |
288 | :PROPERTIES: | 582 | :PROPERTIES: |
289 | :header-args: :tangle-mode (identity #o755) :tangle bin/em | 583 | :header-args: :tangle-mode (identity #o755) :tangle bin/em :mkdirp yes |
290 | :END: | 584 | :END: |
291 | 585 | ||
292 | Here's a wrapper script that'll start =emacs --daemon= if there isn't | 586 | Here's a wrapper script that'll start =emacs --daemon= if there isn't |
@@ -302,7 +596,7 @@ your =$PATH= somewhere. | |||
302 | 596 | ||
303 | **** emacsclient.desktop | 597 | **** emacsclient.desktop |
304 | :PROPERTIES: | 598 | :PROPERTIES: |
305 | :header-args: :tangle bin/emacsclient.desktop | 599 | :header-args: :tangle bin/emacsclient.desktop :mkdirp yes |
306 | :END: | 600 | :END: |
307 | 601 | ||
308 | I haven't really tested this yet, but it should allow me to open other | 602 | I haven't really tested this yet, but it should allow me to open other |
@@ -338,7 +632,7 @@ section come from [[https://github.com/termitereform/JunkPile/blob/master/emacs- | |||
338 | 632 | ||
339 | *** Scripts | 633 | *** Scripts |
340 | :PROPERTIES: | 634 | :PROPERTIES: |
341 | :header-args: :noweb tangle | 635 | :header-args: :noweb tangle :mkdirp yes |
342 | :END: | 636 | :END: |
343 | 637 | ||
344 | **** Common variables | 638 | **** Common variables |
diff --git a/early-init.el b/early-init.el index f879ee4..2e78e43 100644 --- a/early-init.el +++ b/early-init.el | |||
@@ -7,21 +7,21 @@ | |||
7 | ;; I use =straight.el= instead. | 7 | ;; I use =straight.el= instead. |
8 | 8 | ||
9 | 9 | ||
10 | ;; [[file:config.org::*Disable loading of =package.el=][Disable loading of =package.el=:1]] | 10 | ;; [[file:~/.emacs.d/config.org::*Disable loading of =package.el=][Disable loading of =package.el=:1]] |
11 | (setq package-enable-at-startup nil) | 11 | (setq package-enable-at-startup nil) |
12 | ;; Disable loading of =package.el=:1 ends here | 12 | ;; Disable loading of =package.el=:1 ends here |
13 | 13 | ||
14 | ;; Don't resize the frame when loading fonts | 14 | ;; Don't resize the frame when loading fonts |
15 | 15 | ||
16 | 16 | ||
17 | ;; [[file:config.org::*Don't resize the frame when loading fonts][Don't resize the frame when loading fonts:1]] | 17 | ;; [[file:~/.emacs.d/config.org::*Don't resize the frame when loading fonts][Don't resize the frame when loading fonts:1]] |
18 | (setq frame-inhibit-implied-resize t) | 18 | (setq frame-inhibit-implied-resize t) |
19 | ;; Don't resize the frame when loading fonts:1 ends here | 19 | ;; Don't resize the frame when loading fonts:1 ends here |
20 | 20 | ||
21 | ;; Resize frame by pixels | 21 | ;; Resize frame by pixels |
22 | 22 | ||
23 | 23 | ||
24 | ;; [[file:config.org::*Resize frame by pixels][Resize frame by pixels:1]] | 24 | ;; [[file:~/.emacs.d/config.org::*Resize frame by pixels][Resize frame by pixels:1]] |
25 | (setq frame-resize-pixelwise t) | 25 | (setq frame-resize-pixelwise t) |
26 | ;; Resize frame by pixels:1 ends here | 26 | ;; Resize frame by pixels:1 ends here |
27 | 27 | ||
@@ -35,7 +35,7 @@ | |||
35 | ;; what I have for now. | 35 | ;; what I have for now. |
36 | 36 | ||
37 | 37 | ||
38 | ;; [[file:config.org::*Shoe-horned from elsewhere in =config.org=][Shoe-horned from elsewhere in =config.org=:1]] | 38 | ;; [[file:~/.emacs.d/config.org::*Shoe-horned from elsewhere in =config.org=][Shoe-horned from elsewhere in =config.org=:1]] |
39 | (add-to-list 'default-frame-alist | 39 | (add-to-list 'default-frame-alist |
40 | '(tool-bar-lines . 0)) | 40 | '(tool-bar-lines . 0)) |
41 | 41 | ||
diff --git a/init.el b/init.el index b902c0b..d0e30b2 100644 --- a/init.el +++ b/init.el | |||
@@ -5,7 +5,7 @@ | |||
5 | ;; Prefer newer files to older files | 5 | ;; Prefer newer files to older files |
6 | 6 | ||
7 | 7 | ||
8 | ;; [[file:config.org::*Prefer newer files to older files][Prefer newer files to older files:1]] | 8 | ;; [[file:~/.emacs.d/config.org::*Prefer newer files to older files][Prefer newer files to older files:1]] |
9 | (setq load-prefer-newer t) | 9 | (setq load-prefer-newer t) |
10 | ;; Prefer newer files to older files:1 ends here | 10 | ;; Prefer newer files to older files:1 ends here |
11 | 11 | ||
@@ -16,7 +16,7 @@ | |||
16 | ;; directly from Org if it's newer. | 16 | ;; directly from Org if it's newer. |
17 | 17 | ||
18 | 18 | ||
19 | ;; [[file:config.org::*Load the config][Load the config:1]] | 19 | ;; [[file:~/.emacs.d/config.org::*Load the config][Load the config:1]] |
20 | (let* (;; Speed up init | 20 | (let* (;; Speed up init |
21 | (gc-cons-threshold most-positive-fixnum) | 21 | (gc-cons-threshold most-positive-fixnum) |
22 | (file-name-handler-alist nil) | 22 | (file-name-handler-alist nil) |