diff options
-rw-r--r-- | config.org | 913 |
1 files changed, 540 insertions, 373 deletions
diff --git a/config.org b/config.org index ac18133..b27fb35 100644 --- a/config.org +++ b/config.org | |||
@@ -1,143 +1,27 @@ | |||
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 | ||
4 | 3 | ||
5 | * Settings | 4 | * About me |
6 | 5 | ||
7 | Basic settings necessary for a decent editing experience in Emacs. | 6 | #+begin_src emacs-lisp :noweb-ref settings |
8 | These should not require non-built-in packages. | ||
9 | |||
10 | ** Prelude | ||
11 | |||
12 | *** Enable lexical binding | ||
13 | |||
14 | #+begin_src emacs-lisp :comments no | ||
15 | ;; config.el -*- lexical-binding: t -*- | ||
16 | #+end_src | ||
17 | |||
18 | *** Disclaimer | ||
19 | |||
20 | #+NAME: disclaimer | ||
21 | #+begin_src emacs-lisp :comments no | ||
22 | ;; This file is automatically tangled from config.org. | ||
23 | ;; Hand edits will be overwritten! | ||
24 | #+end_src | ||
25 | |||
26 | ** Customization | ||
27 | |||
28 | *** Emulate use-package's =:custom= | ||
29 | |||
30 | #+begin_src emacs-lisp | ||
31 | (defmacro cuss (var val &optional _docstring) | ||
32 | "`use-package''s `:custom', without `use-package'." | ||
33 | (declare (doc-string 3) | ||
34 | (indent 2)) | ||
35 | `(funcall (or (get ',var 'custom-set) #'set-default) | ||
36 | ',var ,val)) | ||
37 | #+end_src | ||
38 | |||
39 | *** Emulate use-package's =:custom-face= | ||
40 | |||
41 | #+begin_src emacs-lisp | ||
42 | (defvar acdw--custom-faces () | ||
43 | "List of custom faces to run through `acdw/set-custom-faces'.") | ||
44 | |||
45 | (defun acdw/set-custom-faces () | ||
46 | "Customize faces using `customize-set-faces'. | ||
47 | |||
48 | I only want to run this once, per the documentation of `customize-set-faces'." | ||
49 | (when acdw--custom-faces | ||
50 | (let ((msg "Customizing faces")) | ||
51 | (message "%s..." msg) | ||
52 | (apply #'custom-set-faces acdw--custom-faces) | ||
53 | (message "%s...Done." msg) | ||
54 | (remove-function after-focuse-change-function #'acdw/set-custom-faces)))) | ||
55 | |||
56 | (add-function :before after-focus-change-function #'acdw/set-custom-faces) | ||
57 | |||
58 | (defmacro cussface (face spec &optional _docstring) | ||
59 | "Add the form (FACE SPEC) to `acdw--custom-faces'." | ||
60 | (declare (doc-string 3) | ||
61 | (indent defun)) | ||
62 | `(add-to-list 'acdw--custom-faces '(,face ,spec))) | ||
63 | #+end_src | ||
64 | |||
65 | *** Only do something when Emacs is unfocused | ||
66 | |||
67 | Since Emacs is single-threaded, I only want to run really expensive | ||
68 | operations when I won't notice, say .. when I'm focused on another | ||
69 | window. | ||
70 | |||
71 | #+begin_src emacs-lisp | ||
72 | (defun when-unfocused (func &rest args) | ||
73 | "Run FUNC with ARGS iff all frames are out of focus." | ||
74 | (when (seq-every-p #'null (mapcar #'frame-focus-state (frame-list))) | ||
75 | (apply func args))) | ||
76 | #+end_src | ||
77 | |||
78 | *** Throw customizations away | ||
79 | |||
80 | I use Emacs's Customize interface, but really only to learn about what | ||
81 | options a package presents to /be/ customized. I don't want to use | ||
82 | the custom file for anything at all. | ||
83 | |||
84 | #+begin_src emacs-lisp | ||
85 | (cuss custom-file null-device) | ||
86 | #+end_src | ||
87 | |||
88 | ** About me | ||
89 | |||
90 | My name and email address. | ||
91 | |||
92 | #+begin_src emacs-lisp | ||
93 | (setq user-full-name "Case Duckworth" | 7 | (setq user-full-name "Case Duckworth" |
94 | user-mail-address "acdw@acdw.net") | 8 | user-mail-address "acdw@acdw.net") |
95 | #+end_src | 9 | #+end_src |
96 | 10 | ||
97 | ** Look and feel | 11 | * Look and feel |
98 | |||
99 | *** Cursor | ||
100 | |||
101 | #+begin_src emacs-lisp | ||
102 | ;; Show a vertical bar cursor | ||
103 | (cuss cursor-type 'bar) | ||
104 | |||
105 | ;; Hide the cursor in other windows | ||
106 | (cuss cursor-in-non-selected-windows nil) | ||
107 | |||
108 | ;; Don't blink the cursor | ||
109 | (blink-cursor-mode -1) | ||
110 | #+end_src | ||
111 | |||
112 | *** Tabs | ||
113 | |||
114 | **** Tab names should be current buffer + a count of windows | ||
115 | |||
116 | #+begin_src emacs-lisp | ||
117 | (cuss tab-bar-tab-name-function | ||
118 | #'tab-bar-tab-name-current-with-count) | ||
119 | #+end_src | ||
120 | |||
121 | **** Only show the tab bar when there's more than one tab | ||
122 | |||
123 | For some reason, this doesn't work with multiple frames. | ||
124 | |||
125 | #+begin_src emacs-lisp | ||
126 | (cuss tab-bar-show 1) | ||
127 | #+end_src | ||
128 | |||
129 | *** Frames | ||
130 | 12 | ||
131 | /Frames/ are Emacs's concepts that generally correspond to other | 13 | ** Frames |
132 | programs' /windows/ -- that is, they're the boxen on the screen that | ||
133 | contain the Emacs programmen. | ||
134 | 14 | ||
135 | **** Initial frame setup | 15 | *** Initial frame setup |
136 | :PROPERTIES: | 16 | :PROPERTIES: |
137 | :header-args: :noweb-ref initial-frame-setup | 17 | :header-args: :noweb-ref initial-frame-setup |
138 | :END: | 18 | :END: |
139 | 19 | ||
140 | ***** Tool bar | 20 | I tangle this section to =early-init.el=, since that's evaluated |
21 | before GUI set-up. Which, in turn, means Emacs will skip the "flash | ||
22 | of unstyled content" thing. | ||
23 | |||
24 | **** Tool bar | ||
141 | 25 | ||
142 | #+begin_src emacs-lisp | 26 | #+begin_src emacs-lisp |
143 | (add-to-list 'default-frame-alist | 27 | (add-to-list 'default-frame-alist |
@@ -146,7 +30,7 @@ contain the Emacs programmen. | |||
146 | (tool-bar-mode -1) | 30 | (tool-bar-mode -1) |
147 | #+end_src | 31 | #+end_src |
148 | 32 | ||
149 | ***** Menu bar | 33 | **** Menu bar |
150 | 34 | ||
151 | #+begin_src emacs-lisp | 35 | #+begin_src emacs-lisp |
152 | (add-to-list 'default-frame-alist | 36 | (add-to-list 'default-frame-alist |
@@ -155,7 +39,7 @@ contain the Emacs programmen. | |||
155 | (menu-bar-mode -1) | 39 | (menu-bar-mode -1) |
156 | #+end_src | 40 | #+end_src |
157 | 41 | ||
158 | ***** Scroll bars | 42 | **** Scroll bars |
159 | 43 | ||
160 | #+begin_src emacs-lisp | 44 | #+begin_src emacs-lisp |
161 | (add-to-list 'default-frame-alist | 45 | (add-to-list 'default-frame-alist |
@@ -166,44 +50,44 @@ contain the Emacs programmen. | |||
166 | (horizontal-scroll-bar-mode -1) | 50 | (horizontal-scroll-bar-mode -1) |
167 | #+end_src | 51 | #+end_src |
168 | 52 | ||
169 | **** Frame titles | 53 | *** Frame titles |
170 | |||
171 | Set the frame title to something more useful than the default: include | ||
172 | the current buffer and the current filename. | ||
173 | 54 | ||
174 | #+begin_src emacs-lisp | 55 | #+begin_src emacs-lisp :noweb-ref settings |
175 | (cuss frame-title-format | 56 | (setq-default frame-title-format |
176 | (concat invocation-name "@" (system-name) | 57 | (concat invocation-name "@" (system-name) |
177 | ": %b %+%+ %f")) | 58 | ": %b %+%+ %f")) |
178 | #+end_src | 59 | #+end_src |
179 | 60 | ||
180 | **** Fringes | 61 | *** Fringes |
62 | :PROPERTIES: | ||
63 | :header-args: :noweb-ref settings | ||
64 | :END: | ||
181 | 65 | ||
182 | I have grown to love Emacs's little fringes on the side of the | 66 | I have grown to love Emacs's little fringes on the side of the |
183 | windows. In fact, I love them so much that I really went overboard | 67 | windows. In fact, I love them so much that I really went overboard |
184 | and have made a custom fringe bitmap. | 68 | and have made a custom fringe bitmap. |
185 | 69 | ||
186 | ***** Indicate empty lines after the end of the buffer | 70 | **** Indicate empty lines after the end of the buffer |
187 | 71 | ||
188 | #+begin_src emacs-lisp | 72 | #+begin_src emacs-lisp |
189 | (cuss indicate-empty-lines t) | 73 | (setq-default indicate-empty-lines t) |
190 | #+end_src | 74 | #+end_src |
191 | 75 | ||
192 | ***** Indicate the boundaries of the buffer | 76 | **** Indicate the boundaries of the buffer |
193 | 77 | ||
194 | #+begin_src emacs-lisp | 78 | #+begin_src emacs-lisp |
195 | (cuss indicate-buffer-boundaries 'right) | 79 | (setq-default indicate-buffer-boundaries 'right) |
196 | #+end_src | 80 | #+end_src |
197 | 81 | ||
198 | ***** Indicate continuation lines, but only on the left fringe | 82 | **** Indicate continuation lines, but only on the left fringe |
199 | 83 | ||
200 | #+begin_src emacs-lisp | 84 | #+begin_src emacs-lisp |
201 | (cuss visual-line-fringe-indicators '(left-curly-arrow nil)) | 85 | (setq-default visual-line-fringe-indicators '(left-curly-arrow nil)) |
202 | #+end_src | 86 | #+end_src |
203 | 87 | ||
204 | ***** Customize fringe bitmaps | 88 | **** Customize fringe bitmaps |
205 | 89 | ||
206 | ****** Curly arrows (continuation lines) | 90 | ***** Curly arrows (continuation lines) |
207 | 91 | ||
208 | #+begin_src emacs-lisp | 92 | #+begin_src emacs-lisp |
209 | (define-fringe-bitmap 'left-curly-arrow | 93 | (define-fringe-bitmap 'left-curly-arrow |
@@ -219,7 +103,7 @@ and have made a custom fringe bitmap. | |||
219 | #b11000000]) | 103 | #b11000000]) |
220 | #+end_src | 104 | #+end_src |
221 | 105 | ||
222 | ****** Arrows (truncation lines) | 106 | ***** Arrows (truncation lines) |
223 | 107 | ||
224 | #+begin_src emacs-lisp | 108 | #+begin_src emacs-lisp |
225 | (define-fringe-bitmap 'left-arrow | 109 | (define-fringe-bitmap 'left-arrow |
@@ -235,139 +119,162 @@ and have made a custom fringe bitmap. | |||
235 | #b00000000]) | 119 | #b00000000]) |
236 | #+end_src | 120 | #+end_src |
237 | 121 | ||
238 | *** Windows | 122 | ** Windows |
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 | 123 | ||
251 | **** Switch windows, or buffers if there's only one | 124 | *** Switch to other window or buffer |
252 | 125 | ||
253 | from [[https://www.reddit.com/r/emacs/comments/kz347f/what_parts_of_your_config_do_you_like_best/gjlnp2c/][u/astoff1]]. | 126 | I grabbed this from [[https://www.reddit.com/r/emacs/comments/kz347f/what_parts_of_your_config_do_you_like_best/gjlnp2c/][u/astoff1]]: it extends the =other-window= command |
127 | to switch to the other buffer if there's only one window in the frame. | ||
254 | 128 | ||
255 | #+begin_src emacs-lisp | 129 | #+begin_src emacs-lisp :noweb-ref functions |
256 | (defun other-window-or-buffer () | 130 | (defun other-window-or-buffer () |
257 | "Switch to the other window, or previous buffer." | 131 | "Switch to other window, or the previous buffer." |
258 | (interactive) | 132 | (interactive) |
259 | (if (eq (count-windows) 1) | 133 | (if (eq (count-windows) 1) |
260 | (switch-to-buffer nil) | 134 | (switch-to-buffer nil) |
261 | (other-window 1))) | 135 | (other-window 1))) |
262 | #+end_src | 136 | #+end_src |
263 | 137 | ||
264 | *** Buffers | 138 | And I'll bind it to =M-o=, since that's easier to reach than =C-x o=. |
265 | 139 | ||
266 | **** Startup buffers | 140 | #+begin_src emacs-lisp :noweb-ref bindings |
141 | (define-key global-map (kbd "M-o") #'other-window-or-buffer) | ||
142 | #+end_src | ||
267 | 143 | ||
268 | I don't want to see Emacs's splash screen, and I want the =*scratch*= | 144 | ** Buffers |
269 | buffer to have a little message. | ||
270 | 145 | ||
271 | #+begin_src emacs-lisp | 146 | *** Uniquify buffers |
272 | (cuss inhibit-startup-screen t | ||
273 | "Don't show the startup buffer.") | ||
274 | 147 | ||
275 | (cuss initial-buffer-choice t | 148 | The default way Emacs makes buffer names unique is really ugly and, |
276 | "Start with *scratch*.") | 149 | dare I say it, stupid. Instead, I want them to be uniquified by their |
150 | filesystem paths. | ||
277 | 151 | ||
278 | (cuss initial-scratch-message | 152 | #+begin_src emacs-lisp :noweb-ref requires |
279 | (concat ";; Hello, " (nth 0 (split-string user-full-name)) "!\n" | 153 | (require 'uniquify) |
280 | ";; Happy hacking ...")) | ||
281 | #+end_src | 154 | #+end_src |
282 | 155 | ||
283 | **** Immortal =*scratch*= buffer | 156 | #+begin_src emacs-lisp :noweb-ref settings |
157 | (setq-default uniquify-buffer-name-style 'forward) | ||
158 | #+end_src | ||
284 | 159 | ||
285 | I don't want to accidentally kill the =*scratch*= buffer. | 160 | *** Startup buffers |
286 | 161 | ||
287 | #+begin_src emacs-lisp | 162 | When Emacs starts up, I want a blank slate: the *scratch* buffer. I |
288 | (defun immortal-scratch () | 163 | also want it to show a cute little message to myself. |
289 | (if (eq (current-buffer) (get-buffer "*scratch*")) | ||
290 | (progn (bury-buffer) | ||
291 | nil) | ||
292 | t)) | ||
293 | 164 | ||
294 | (add-hook 'kill-buffer-query-functions #'immortal-scratch) | 165 | #+begin_src emacs-lisp :noweb-ref settings |
166 | (setq-default inhibit-startup-screen t ; Don't show that splash screen thing. | ||
167 | initial-buffer-choice t ; Start on *scratch* | ||
168 | initial-scratch-message | ||
169 | (concat ";; Howdy, " | ||
170 | (nth 0 (split-string user-full-name)) "!\n" | ||
171 | ";; Welcome to Emacs." | ||
172 | "\n\n")) | ||
295 | #+end_src | 173 | #+end_src |
296 | 174 | ||
297 | **** Uniquify buffers | 175 | *** Immortal =*scratch*= buffer |
298 | 176 | ||
299 | I like the =forward= style, which uniquifies buffers by including path | 177 | I don't want to accidentally kill the *scratch* buffer. So, I add a |
300 | elements up the tree until the names are unique. | 178 | function to the =kill-buffer-query-functions= hook that will return |
179 | =nil= if the buffer is *scratch*. | ||
301 | 180 | ||
302 | #+begin_src emacs-lisp | 181 | #+begin_src emacs-lisp :noweb-ref functions |
303 | (require 'uniquify) | 182 | (defun immortal-scratch () |
304 | (cuss uniquify-buffer-name-style 'forward) | 183 | (if (not (eq (current-buffer) (get-buffer "*scratch*"))) |
184 | t | ||
185 | (bury-buffer) | ||
186 | nil)) | ||
305 | #+end_src | 187 | #+end_src |
306 | 188 | ||
307 | **** Kill buffers more smarter-ly | 189 | #+begin_src emacs-lisp :noweb-ref hooks |
308 | 190 | (add-hook 'kill-buffer-query-functions #'immortal-scratch) | |
309 | #+begin_src emacs-lisp | 191 | #+end_src |
310 | (defun kill-a-buffer (&optional prefix) | ||
311 | "Kill buffers and windows sanely. | ||
312 | 192 | ||
313 | `kill-a-buffer' works based on the prefix argument as follows: | 193 | *** Kill buffers better |
314 | 194 | ||
315 | - 0 => kill the CURRENT buffer and window | 195 | #+begin_src emacs-lisp :noweb-ref functions |
316 | - 4 (C-u) => kill the OTHER window and its buffer | 196 | (defun kill-a-buffer (&optional prefix) |
317 | - 16 (C-u C-u) => kill ALL OTHER buffers and windows | 197 | "Kill a buffer and its window, prompting only on unsaved changes. |
318 | 198 | ||
319 | Prompt iff there are unsaved changes." | 199 | `kill-a-buffer' uses the PREFIX argument to determine which buffer(s) to kill: |
200 | 0 => Kill current buffer & window | ||
201 | 4 (C-u) => Kill OTHER buffer & window | ||
202 | 16 (C-u C-u) => Kill ALL OTHER buffers & windows" | ||
320 | (interactive "P") | 203 | (interactive "P") |
321 | (pcase (or (car prefix) 0) | 204 | (pcase (or (car prefix) 0) |
322 | (0 (kill-current-buffer) | 205 | (0 (kill-current-buffer) |
323 | (unless (one-window-p) (delete-window))) | 206 | (unless (one-window-p) (delete-window))) |
324 | (4 (other-window 1) | 207 | (4 (other-window 1) |
325 | (kill-current-buffer) | 208 | (kill-current-buffer) |
326 | (unless (one-window-p) (delete-window))) | 209 | (unless (one-window-p) (delete-window))) |
327 | (16 (mapc #'kill-buffer (delq (current-buffer) (buffer-list))) | 210 | (16 (mapc #'kill-buffer (delq (current-buffer) (buffer-list))) |
328 | (delete-other-windows)))) | 211 | (delete-other-windows)))) |
329 | #+end_src | 212 | #+end_src |
330 | 213 | ||
331 | *** Fonts | 214 | #+begin_src emacs-lisp :noweb-ref bindings |
215 | (define-key ctl-x-map "k" #'kill-a-buffer) | ||
216 | #+end_src | ||
332 | 217 | ||
333 | **** Function: =set-face-from-alternatives= | 218 | ** Cursor |
334 | 219 | ||
335 | To be honest, this might be better off using =cussface=, but that's | 220 | *** Cursor shape |
336 | another story for another day. | ||
337 | 221 | ||
338 | #+begin_src emacs-lisp | 222 | I like a vertical bar, but only in the selected window. |
339 | (defun set-face-from-alternatives (face frame &rest fontspecs) | 223 | |
340 | "Set FACE on FRAME from first available font from FONTSPECS. | 224 | #+begin_src emacs-lisp :noweb-ref settings |
341 | FACE and FRAME work the same as with `set-face-attribute'." | 225 | (setq-default cursor-type 'bar |
342 | (catch :return | 226 | cursor-in-non-selected-windows nil) |
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 | 227 | #+end_src |
348 | 228 | ||
349 | **** Add a hook to setup fonts after the first window focus change | 229 | *** Don't blink the cursor |
350 | 230 | ||
351 | Of course, I only need to setup the fonts on a graphical session. | 231 | #+begin_src emacs-lisp :noweb-ref modes |
232 | (blink-cursor-mode -1) | ||
233 | #+end_src | ||
352 | 234 | ||
353 | #+begin_src emacs-lisp | 235 | ** Tabs |
354 | (when (display-graphic-p) | 236 | |
355 | (add-function :before after-focus-change-function #'acdw/setup-fonts)) | 237 | *** Tab names |
238 | |||
239 | #+begin_src emacs-lisp :noweb-ref settings | ||
240 | (setq-default tab-bar-tab-name-function | ||
241 | #'tab-bar-tab-name-current-with-count) | ||
356 | #+end_src | 242 | #+end_src |
357 | 243 | ||
358 | **** Setup my fonts | 244 | *** When to show the tab bar |
359 | 245 | ||
360 | Notice that this function removes itself from | 246 | Only when there's more than one tab. |
361 | =after-focus-change-function=, since ideally you'll only need to set | ||
362 | the fonts once. | ||
363 | 247 | ||
364 | #+begin_src emacs-lisp | 248 | #+begin_src emacs-lisp :noweb-ref settings |
365 | (defun acdw/setup-fonts () | 249 | (setq-default tab-bar-show 1) |
366 | "Setup fonts. | 250 | #+end_src |
251 | |||
252 | ** Fonts | ||
367 | 253 | ||
368 | This has to happen after the frame is setup for the first time, | 254 | *** Find an installed font from a list of alternatives |
369 | so it should be added to `after-focus-change-function'. It | 255 | |
370 | removes itself." | 256 | #+begin_src emacs-lisp :noweb-ref functions |
257 | (defun set-face-from-alternatives (face frame &rest fontspecs) | ||
258 | "Set FACE on FRAME from first available spec from FONTSPECS. | ||
259 | FACE and FRAME work the same as with `set-face-attribute.'" | ||
260 | (catch :return | ||
261 | (dolist (spec fontspecs) | ||
262 | (when-let ((found (find-font (apply #'font-spec spec)))) | ||
263 | (set-face-attribute face frame :font found) | ||
264 | (throw :return found))))) | ||
265 | #+end_src | ||
266 | |||
267 | *** Setup fonts on first window focus | ||
268 | |||
269 | At the end of this function, it removes itself from | ||
270 | =after-focus-change-function=, so it only runs once. | ||
271 | |||
272 | #+begin_src emacs-lisp :noweb-ref functions | ||
273 | (defun acdw/setup-fonts () | ||
274 | "Setup fonts. This has to happen after the frame is setup for | ||
275 | the first time, so it should be added to `after-focus-change-function'. It | ||
276 | removes itself from that hook." | ||
277 | (interactive) | ||
371 | (set-face-from-alternatives 'default nil | 278 | (set-face-from-alternatives 'default nil |
372 | '(:family "Input Mono" | 279 | '(:family "Input Mono" |
373 | :slant normal | 280 | :slant normal |
@@ -377,9 +284,9 @@ the fonts once. | |||
377 | :slant normal | 284 | :slant normal |
378 | :weight normal | 285 | :weight normal |
379 | :height 100)) | 286 | :height 100)) |
380 | ;; `fixed-pitch' should just inherit from `default' | 287 | ;; `fixed-pitch' inherits from `default' |
381 | (set-face-attribute 'fixed-pitch nil :inherit 'default) | 288 | (set-face-attribute 'fixed-pitch nil :inherit 'default) |
382 | 289 | ;; variable-pitch is different | |
383 | (set-face-from-alternatives 'variable-pitch nil | 290 | (set-face-from-alternatives 'variable-pitch nil |
384 | '(:family "Input Sans" | 291 | '(:family "Input Sans" |
385 | :slant normal | 292 | :slant normal |
@@ -387,163 +294,318 @@ the fonts once. | |||
387 | '(:family "Georgia" | 294 | '(:family "Georgia" |
388 | :slant normal | 295 | :slant normal |
389 | :weight normal)) | 296 | :weight normal)) |
390 | 297 | ;; remove self from hook | |
391 | (remove-function after-focus-change-function #'acdw/setup-fonts)) | 298 | (remove-function after-focus-change-function #'acdw/setup-fonts)) |
392 | #+end_src | 299 | #+end_src |
393 | 300 | ||
394 | **** Underlines | 301 | Of course, it only makes sense to run the font setup at all if I'm |
302 | using the graphical Emacs. | ||
395 | 303 | ||
396 | #+begin_src emacs-lisp | 304 | #+begin_src emacs-lisp :noweb-ref hooks |
397 | (cuss x-underline-at-descent-line t) | 305 | (when (display-graphic-p) |
306 | (add-function :before after-focus-change-function | ||
307 | #'acdw/setup-fonts)) | ||
398 | #+end_src | 308 | #+end_src |
399 | 309 | ||
400 | ** Interactivity | 310 | *** Underlines |
401 | *** Dialogs and alerts | ||
402 | 311 | ||
403 | **** Don't use a dialog box | 312 | I like the /fancy/ underlines in newer browsers that skip all the |
313 | descenders. Emacs doesn't /quite/ have that, but it can put the | ||
314 | underline below all the text. | ||
404 | 315 | ||
405 | #+begin_src emacs-lisp | 316 | #+begin_src emacs-lisp :noweb-ref settings |
406 | (cuss use-dialog-box nil) | 317 | (setq-default x-underline-at-descent-line t) |
407 | #+end_src | 318 | #+end_src |
408 | 319 | ||
409 | **** Yes or no questions | 320 | * Interactivity |
410 | 321 | ||
411 | #+begin_src emacs-lisp | 322 | ** Dialogs and alerts |
323 | |||
324 | *** Don't use a dialog box | ||
325 | |||
326 | Ask in the modeline instead. | ||
327 | |||
328 | #+begin_src emacs-lisp :noweb-ref settings | ||
329 | (setq-default use-dialog-box nil) | ||
330 | #+end_src | ||
331 | |||
332 | *** Yes or no questions | ||
333 | |||
334 | I just want to type =y= or =n=, okay? | ||
335 | |||
336 | #+begin_src emacs-lisp :noweb-ref functions | ||
412 | (fset 'yes-or-no-p #'y-or-n-p) | 337 | (fset 'yes-or-no-p #'y-or-n-p) |
413 | #+end_src | 338 | #+end_src |
414 | 339 | ||
415 | **** The Bell | 340 | *** The Bell |
416 | 341 | ||
417 | #+begin_src emacs-lisp | 342 | The only system I /sort of/ like the bell on is my Thinkpad, which |
418 | ;; Don't flash the whole screen on bell | 343 | does a little on-board speaker beep. Until I can figure out how to |
419 | (cuss visible-bell nil) | 344 | let it do its thing, though, I'll just change the bell on all my |
345 | systems. | ||
420 | 346 | ||
421 | ;; Instead, flash the mode line | 347 | #+begin_src emacs-lisp :noweb-ref settings |
422 | (cuss ring-bell-function #'flash-mode-line) | 348 | (setq-default visible-bell nil |
349 | ring-bell-function #'flash-mode-line) | ||
350 | #+end_src | ||
351 | |||
352 | **** Flash the mode-line | ||
423 | 353 | ||
354 | #+begin_src emacs-lisp :noweb-ref functions | ||
424 | (defun flash-mode-line () | 355 | (defun flash-mode-line () |
425 | (invert-face 'mode-line) | 356 | (invert-face 'mode-line) |
426 | (run-with-timer 0.2 nil #'invert-face 'mode-line)) | 357 | (run-with-timer 0.2 nil #'invert-face 'mode-line)) |
427 | #+end_src | 358 | #+end_src |
428 | 359 | ||
429 | t*** Minibuffer | 360 | ** Minibuffer |
430 | 361 | ||
431 | *** Minibuffer | 362 | *** Keep the cursor away from the minibuffer prompt |
432 | 363 | ||
433 | **** Keep the cursor away from the minibuffer prompt | 364 | #+begin_src emacs-lisp :noweb-ref settings |
434 | 365 | (setq-default minibuffer-prompt-properties | |
435 | #+begin_src emacs-lisp | 366 | '(read-only t |
436 | (cuss minibuffer-prompt-properties | 367 | cursor-intangible t |
437 | '(read-only t cursor-intangible t face minibuffer-prompt)) | 368 | face minibuffer-prompt)) |
438 | #+end_src | 369 | #+end_src |
439 | 370 | ||
440 | **** Enable recursive minibuffer | 371 | *** Enable a recursive minibuffer |
441 | 372 | ||
442 | #+begin_src emacs-lisp | 373 | #+begin_src emacs-lisp :noweb-ref |
443 | (cuss enable-recursive-minibuffers t) | 374 | (setq-default enable-recursive-minibuffers t) |
444 | #+end_src | 375 | #+end_src |
445 | 376 | ||
446 | **** Show how deep the minibuffer goes in the modeline | 377 | *** Show the recursivity of the minibuffer in the mode-line |
447 | 378 | ||
448 | #+begin_src emacs-lisp | 379 | #+begin_src emacs-lisp :noweb-ref modes |
449 | (minibuffer-depth-indicate-mode +1) | 380 | (minibuffer-depth-indicate-mode +1) |
450 | #+end_src | 381 | #+end_src |
451 | 382 | ||
452 | *** Completing-read | 383 | ** Completing-read |
453 | 384 | ||
454 | **** Shadow file names | 385 | *** Shadow file names |
455 | 386 | ||
456 | When typing =~= or =/= in the file-selection dialog, Emacs "pretends" | 387 | When typing =~= or =/= in the file-selection dialog, Emacs "pretends" |
457 | that you've typed them at the beginning of the line. By default, | 388 | 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 | 389 | however, it only /fades out/ the previous contents of the line. I want |
459 | want to /hide/ those contents. | 390 | to /hide/ those contents. |
460 | 391 | ||
461 | #+begin_src emacs-lisp | 392 | #+begin_src emacs-lisp :noweb-ref settings |
462 | (cuss file-name-shadow-properties '(invisible t)) | 393 | (setq-default file-name-shadow-properties '(invisible t)) |
394 | #+end_src | ||
463 | 395 | ||
396 | #+begin_src emacs-lisp :noweb-ref modes | ||
464 | (file-name-shadow-mode +1) | 397 | (file-name-shadow-mode +1) |
465 | #+end_src | 398 | #+end_src |
466 | 399 | ||
467 | **** Ignore case | 400 | *** Ignore case |
468 | 401 | ||
469 | #+begin_src emacs-lisp | 402 | #+begin_src emacs-lisp :noweb-ref |
470 | (cuss completion-ignore-case t) | 403 | (setq-default completion-ignore-case t |
471 | (cuss read-buffer-completion-ignore-case t) | 404 | read-buffer-completion-ignore-case t |
472 | (cuss read-file-name-completion-ignore-case t) | 405 | read-file-name-completion-ignore-case t) |
473 | #+end_src | 406 | #+end_src |
474 | 407 | ||
475 | ** Persistence | 408 | * Persistence |
476 | 409 | ||
477 | *** Minibuffer history | 410 | ** Minibuffer history |
478 | 411 | ||
479 | The =savehist= package saves the minibuffer history between sessions. | 412 | The =savehist= package saves minibuffer history between sessions, as |
480 | It can also save some other variables alongside the minibuffer | 413 | well as the option for some other variables. Since storage is cheap, |
481 | history. Since storage is cheap, I'm also going to keep all my | 414 | I keep all of it. |
482 | history. | ||
483 | 415 | ||
484 | #+begin_src emacs-lisp | 416 | #+begin_src emacs-lisp :noweb-ref requires |
485 | (require 'savehist) | 417 | (require 'savehist) |
418 | #+end_src | ||
486 | 419 | ||
487 | (cuss savehist-additional-variables | 420 | #+begin_src emacs-lisp :noweb-ref modes |
488 | '(kill-ring | 421 | (setq-default savehist-additional-variables |
489 | search-ring | 422 | '(kill-ring |
490 | regexp-search-ring)) | 423 | search-ring |
491 | 424 | regexp-search-ring) | |
492 | ;; Don't truncate history | 425 | history-length t ; Don't truncate |
493 | (cuss history-length t) | 426 | history-delete-duplicates t) |
494 | 427 | #+end_src | |
495 | ;; Delete history duplicates | ||
496 | (cuss history-delete-duplicates t) | ||
497 | 428 | ||
429 | #+begin_src emacs-lisp :noweb-ref modes | ||
498 | (savehist-mode +1) | 430 | (savehist-mode +1) |
499 | #+end_src | 431 | #+end_src |
500 | 432 | ||
501 | *** File places | 433 | ** File places |
502 | 434 | ||
503 | The =saveplace= package saves where I've been in the files I've | 435 | The =saveplace= package saves where I've been in my visited files. |
504 | visited, so I can return back to them. | ||
505 | 436 | ||
506 | #+begin_src emacs-lisp | 437 | #+begin_src emacs-lisp :noweb-ref requires |
507 | (require 'saveplace) | 438 | (require 'saveplace) |
439 | #+end_src | ||
508 | 440 | ||
509 | ;; Forget the place in unreadable files | 441 | Since storage is cheap, but I'm impatient -- especially on Windows -- |
510 | (cuss save-place-forget-unreadable-files t) | 442 | I'm not going to check whether the files =save-place= saves the places |
443 | of are readable or not. | ||
444 | |||
445 | #+begin_src emacs-lisp :noweb-ref settings | ||
446 | (setq-default save-place-forget-unreadable-files (when-at :home)) | ||
447 | #+end_src | ||
511 | 448 | ||
449 | #+begin_src emacs-lisp :noweb-ref modes | ||
512 | (save-place-mode +1) | 450 | (save-place-mode +1) |
513 | #+end_src | 451 | #+end_src |
514 | 452 | ||
515 | *** Recent files | 453 | ** Recent files |
516 | 454 | ||
517 | #+begin_src emacs-lisp | 455 | I also like to keep track of recently-opened files. =recentf= helps |
456 | with that. | ||
457 | |||
458 | #+begin_src emacs-lisp :noweb-ref requires | ||
518 | (require 'recentf) | 459 | (require 'recentf) |
460 | #+end_src | ||
519 | 461 | ||
520 | ;; Limit the number of items in the recentf menu | 462 | #+begin_src emacs-lisp :noweb-ref settings |
521 | (cuss recentf-max-menu-items 100) | 463 | (setq-default recentf-max-menu-items 100 |
522 | ;; But not the number of items in the actual list | 464 | recentf-max-saved-items nil) |
523 | (cuss recentf-max-saved-items nil) | 465 | #+end_src |
524 | 466 | ||
467 | #+begin_src emacs-lisp :noweb-ref modes | ||
525 | (recentf-mode +1) | 468 | (recentf-mode +1) |
526 | #+end_src | 469 | #+end_src |
527 | 470 | ||
528 | **** Save the recentf list periodically | 471 | *** Save the recentf list periodically |
529 | 472 | ||
530 | #+begin_src emacs-lisp | 473 | #+begin_src emacs-lisp :noweb-ref functions |
531 | (defun acdw/maybe-save-recentf () | 474 | (defun maybe-save-recentf () |
532 | "Save `recentf-file every five minutes, but only when out of focus." | 475 | "Save `recentf-file' every five minutes, but only when out of focus." |
533 | (defvar recentf--last-save (time-convert nil 'integer) | 476 | (defvar recentf--last-save (time-convert nil 'integer) |
534 | "When we last ran `recentf-save-list'.") | 477 | "When we last saved the `recentf-save-list'.") |
535 | 478 | ||
536 | (when (> (time-convert (time-since recentf--last-save) 'integer) | 479 | (when (> (time-convert (time-since recentf--last-save) 'integer) |
537 | (* 60 5)) | 480 | (* 60 5)) |
538 | (setq recentf--last-save (time-convert nil 'integer)) | 481 | (setq-default recentf--last-save (time-convert nil 'integer)) |
539 | (acdw/when-unfocused #'recentf-save-list))) | 482 | (when-unfocused #'recentf-save-list))) |
483 | #+end_src | ||
484 | |||
485 | #+begin_src emacs-lisp :noweb-ref hooks | ||
486 | (add-function :after after-focus-change-function | ||
487 | #'maybe-save-recentf) | ||
488 | #+end_src | ||
489 | |||
490 | * Responsiveness | ||
491 | |||
492 | Emacs has a slew of well-documented problems with snappiness. | ||
493 | Luckily, there are a number of solutions. | ||
494 | |||
495 | ** Only do things when unfocused | ||
496 | |||
497 | Sometimes, we can fake responsiveness by only performing commands when | ||
498 | the user is looking at something else. | ||
499 | |||
500 | #+begin_src emacs-lisp :noweb-ref functions | ||
501 | (defun when-unfocused (func &rest args) | ||
502 | "Run FUNC, with ARGS, iff all frames are out of focus." | ||
503 | (when (seq-every-p #'null (mapcar #'frame-focus-state (frame-list))) | ||
504 | (apply func args))) | ||
505 | #+end_src | ||
506 | |||
507 | * Files | ||
508 | |||
509 | ** Encoding | ||
510 | |||
511 | *** UTF-8 | ||
512 | |||
513 | It's 2020. Let's encode files like it is. | ||
514 | |||
515 | #+begin_src emacs-lisp :noweb-ref settings | ||
516 | (prefer-coding-system 'utf-8) | ||
517 | (set-default-coding-systems 'utf-8) | ||
518 | (set-terminal-coding-system 'utf-8) | ||
519 | (set-keyboard-coding-system 'utf-8) | ||
520 | |||
521 | (setq-default buffer-file-coding-system 'utf-8 | ||
522 | x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)) | ||
523 | #+end_src | ||
524 | |||
525 | *** UNIX-style line endings | ||
526 | |||
527 | This function is from the [[https://www.emacswiki.org/emacs/EndOfLineTips][Emacs Wiki]]. | ||
528 | |||
529 | #+begin_src emacs-lisp :noweb-ref functions | ||
530 | (defun ewiki/no-junk-please-were-unixish () | ||
531 | "Convert line endings to UNIX, dammit." | ||
532 | (let ((coding-str (symbol-name buffer-file-coding-system))) | ||
533 | (when (string-match "-\\(?:dos\\|mac\\)$" coding-str) | ||
534 | (set-buffer-file-coding-system 'unix)))) | ||
535 | #+end_src | ||
536 | |||
537 | I add it to both =file-find-hook= /and/ =before-save-hook= because I'm | ||
538 | /that/ over it. I don't want to ever work with anything other than | ||
539 | UNIX line endings ever again. I just don't care. Even Microsoft | ||
540 | Notepad can handle UNIX line endings, so I don't want to hear it. | ||
541 | |||
542 | #+begin_src emacs-lisp :noweb-ref hooks | ||
543 | (add-hook 'find-file-hook #'ewiki/no-junk-please-were-unixish) | ||
544 | (add-hook 'before-save-hook #'ewiki/no-junk-please-were-unixish) | ||
545 | #+end_src | ||
546 | |||
547 | ** Keep =~/.emacs.d= clean :package: | ||
540 | 548 | ||
541 | (add-function :after after-focus-change-function #'acdw/maybe-save-recentf) | 549 | * Package management :package: |
550 | :PROPERTIES: | ||
551 | :header-args: :noweb-ref package-bootstrap | ||
552 | :END: | ||
553 | |||
554 | Emacs is the /extensible/ editor, and that means I want to use | ||
555 | third-party packages. Of course, first I have to /manage/ those | ||
556 | packages. I use the excellent =straight.el=. | ||
557 | |||
558 | ** TODO Update the PATH | ||
559 | |||
560 | ** Disable =package.el= | ||
561 | |||
562 | #+begin_src emacs-lisp | ||
563 | (setq package-enable-at-startup nil) | ||
542 | #+end_src | 564 | #+end_src |
543 | 565 | ||
544 | ** Files | 566 | ** Bootstrap |
545 | 567 | ||
546 | *** Encoding | 568 | The following is straight (heh) from the straight repo, wrapped in a |
569 | function so I can call it in another wrapper. | ||
570 | |||
571 | #+begin_src emacs-lisp | ||
572 | (defun acdw/bootstrap-straight () | ||
573 | "Bootstrap straight.el." | ||
574 | (defvar bootstrap-version) | ||
575 | (let ((bootstrap-file | ||
576 | (expand-file-name | ||
577 | "straight/repos/straight.el/bootstrap.el" | ||
578 | user-emacs-directory)) | ||
579 | (bootstrap-version 5)) | ||
580 | (unless (file-exists-p bootstrap-file) | ||
581 | (with-current-buffer | ||
582 | (url-retrieve-synchronously | ||
583 | (concat | ||
584 | "https://raw.githubusercontent.com/" | ||
585 | "raxod502/straight.el/develop/install.el") | ||
586 | 'silent 'inhibit-cookies) | ||
587 | (goto-char (point-max)) | ||
588 | (eval-print-last-sexp))) | ||
589 | (load bootstrap-file nil 'nomessage))) | ||
590 | #+end_src | ||
591 | |||
592 | To actually bootstrap straight, I'll first try running the above | ||
593 | directly. If it errors (it tends to on Windows), I'll directly clone | ||
594 | the repo using git, /then/ run the bootstrap code. | ||
595 | |||
596 | #+begin_src emacs-lisp | ||
597 | (unless (ignore-errors (acdw/bootstrap-straight)) | ||
598 | (let ((msg "Straight.el didn't bootstrap correctly. Cloning directly")) | ||
599 | (message "%s..." msg) | ||
600 | (call-process "git" nil | ||
601 | (get-buffer-create "*bootstrap-straight-messages*") nil | ||
602 | "clone" | ||
603 | "https://github.com/raxod502/straight.el" | ||
604 | (expand-file-name "straight/repos/straight.el" | ||
605 | user-emacs-directory)) | ||
606 | (message "%s...Done." msg) | ||
607 | (acdw/bootstrap-straight))) | ||
608 | #+end_src | ||
547 | 609 | ||
548 | * System-specific | 610 | * System-specific |
549 | 611 | ||
@@ -552,27 +614,41 @@ easier to use in both systems, I've included various system-specific | |||
552 | settings and written some ancillary scripts. | 614 | settings and written some ancillary scripts. |
553 | 615 | ||
554 | ** Determine where I am | 616 | ** Determine where I am |
617 | :PROPERTIES: | ||
618 | :header-args: :noweb-ref functions | ||
619 | :END: | ||
555 | 620 | ||
556 | #+begin_src emacs-lisp | 621 | #+begin_src emacs-lisp |
557 | (defmacro when-at (conditions &rest commands) | 622 | (defmacro when-at (conditions &rest commands) |
558 | "Run COMMANDS when at a specific place. | 623 | "Run COMMANDS, or let the user know, when at a specific place. |
559 | 624 | ||
560 | CONDITIONS are one of `:work', `:home', or a list beginning with | 625 | CONDITIONS are one of `:work', `:home', or a list beginning with |
561 | those and other conditions to check. COMMANDS are only run if | 626 | those and other conditions to check. COMMANDS are only run if |
562 | all CONDITIONS are met." | 627 | all CONDITIONS are met. |
628 | |||
629 | If COMMANDS is empty or nil, simply return the result of CONDITIONS." | ||
563 | (declare (indent 1)) | 630 | (declare (indent 1)) |
564 | (let ((at-work (memq system-type '(ms-dos windows-nt))) | 631 | (let ((at-work '(memq system-type '(ms-dos windows-nt))) |
565 | (at-home (memq system-type '(gnu gnu/linux gnu/kfreebsd)))) | 632 | (at-home '(memq system-type '(gnu gnu/linux gnu/kfreebsd)))) |
566 | (pcase conditions | 633 | (pcase conditions |
567 | (:work `(when ',at-work ,@commands)) | 634 | (:work (if commands `(when ,at-work ,@commands) at-work)) |
568 | (:home `(when ',at-home ,@commands)) | 635 | (:home (if commands `(when ,at-home ,@commands) at-home)) |
569 | (`(:work ,others) `(when (and ',at-work ,others) | 636 | ((guard (eq (car conditions) :work)) |
570 | ,@commands)) | 637 | (if commands |
571 | (`(:home ,others) `(when (and ',at-home ,others) | 638 | `(when (and ,at-work ,@(cdr conditions)) |
572 | ,@commands))))) | 639 | ,@commands) |
573 | #+end_src | 640 | `(and ,at-work ,@(cdr conditions)))) |
574 | 641 | ((guard (eq (car conditions) :home)) | |
575 | ** Linux | 642 | (if commands |
643 | `(when (and ,at-home ,@(cdr conditions)) | ||
644 | ,@commands) | ||
645 | `(and ,at-work ,@(cdr conditions))))))) | ||
646 | #+end_src | ||
647 | |||
648 | ** Linux (home) | ||
649 | :PROPERTIES: | ||
650 | :header-args: :noweb-ref linux-specific | ||
651 | :END: | ||
576 | 652 | ||
577 | *** Settings | 653 | *** Settings |
578 | 654 | ||
@@ -580,14 +656,14 @@ settings and written some ancillary scripts. | |||
580 | 656 | ||
581 | **** em | 657 | **** em |
582 | :PROPERTIES: | 658 | :PROPERTIES: |
583 | :header-args: :tangle-mode (identity #o755) :tangle bin/em :mkdirp yes | 659 | :header-args: :tangle-mode (identity #o755) :mkdirp yes |
584 | :END: | 660 | :END: |
585 | 661 | ||
586 | Here's a wrapper script that'll start =emacs --daemon= if there isn't | 662 | Here's a wrapper script that'll start =emacs --daemon= if there isn't |
587 | one, and then launch =emacsclient= with the arguments. Install it to | 663 | one, and then launch =emacsclient= with the arguments. Install it to |
588 | your =$PATH= somewhere. | 664 | your =$PATH= somewhere. |
589 | 665 | ||
590 | #+begin_src sh :shebang "#!/bin/sh" | 666 | #+begin_src sh :shebang "#!/bin/sh" :tangle (when-at :home "~/bin/em") |
591 | if ! emacsclient -nc "$@"; then | 667 | if ! emacsclient -nc "$@"; then |
592 | emacs --daemon | 668 | emacs --daemon |
593 | emacsclient -nc "$@" | 669 | emacsclient -nc "$@" |
@@ -596,13 +672,13 @@ your =$PATH= somewhere. | |||
596 | 672 | ||
597 | **** emacsclient.desktop | 673 | **** emacsclient.desktop |
598 | :PROPERTIES: | 674 | :PROPERTIES: |
599 | :header-args: :tangle bin/emacsclient.desktop :mkdirp yes | 675 | :header-args: :mkdirp yes |
600 | :END: | 676 | :END: |
601 | 677 | ||
602 | I haven't really tested this yet, but it should allow me to open other | 678 | I haven't really tested this yet, but it should allow me to open other |
603 | files and things in Emacs. From [[https://www.taingram.org/blog/emacs-client.html][taingram]]. | 679 | files and things in Emacs. From [[https://www.taingram.org/blog/emacs-client.html][taingram]]. |
604 | 680 | ||
605 | #+begin_src conf-desktop | 681 | #+begin_src conf-desktop :tangle (when-at :home "~/.local/share/applications/emacsclient.desktop") |
606 | [Desktop Entry] | 682 | [Desktop Entry] |
607 | Name=Emacs Client | 683 | Name=Emacs Client |
608 | GenericName=Text Editor | 684 | GenericName=Text Editor |
@@ -615,7 +691,10 @@ files and things in Emacs. From [[https://www.taingram.org/blog/emacs-client.ht | |||
615 | Categories=Utility;TextEditor; | 691 | Categories=Utility;TextEditor; |
616 | #+end_src | 692 | #+end_src |
617 | 693 | ||
618 | ** Windows | 694 | ** Windows (work) |
695 | :PROPERTIES: | ||
696 | :header-args: :noweb-ref windows-specific | ||
697 | :END: | ||
619 | 698 | ||
620 | I use Windows at work, where I /also/ don't have Admin rights. So I | 699 | I use Windows at work, where I /also/ don't have Admin rights. So I |
621 | kind of fly-by-night there. Much of the ideas and scripts in this | 700 | kind of fly-by-night there. Much of the ideas and scripts in this |
@@ -623,24 +702,21 @@ section come from [[https://github.com/termitereform/JunkPile/blob/master/emacs- | |||
623 | 702 | ||
624 | *** Settings | 703 | *** Settings |
625 | 704 | ||
626 | #+NAME: w32-settings | ||
627 | #+begin_src emacs-lisp | 705 | #+begin_src emacs-lisp |
628 | (when (memq system-type '(windows-nt ms-dos cygwin)) | 706 | (setq-default w32-allow-system-shell t) ; enable cmd.exe as shell |
629 | (setq w32-allow-system-shell t ; enable cmd.exe as shell | ||
630 | )) | ||
631 | #+end_src | 707 | #+end_src |
632 | 708 | ||
633 | *** Scripts | 709 | *** Scripts |
634 | :PROPERTIES: | 710 | :PROPERTIES: |
635 | :header-args: :noweb tangle :mkdirp yes | 711 | :header-args: :noweb yes :mkdirp yes |
636 | :END: | 712 | :END: |
637 | 713 | ||
638 | **** Common variables | 714 | **** Common variables |
639 | 715 | ||
640 | #+NAME: w32-bat-common | 716 | #+NAME: w32-bat-common |
641 | #+begin_src bat | 717 | #+begin_src bat |
642 | set HOME=%~dp0..\..\ | 718 | set HOME=%~dp0..\.. |
643 | set EMACS=%~dp0..\..\..\bin\runemacs.exe | 719 | set EMACS=%HOME%\Applications\Emacs\bin\runemacs.exe |
644 | #+end_src | 720 | #+end_src |
645 | 721 | ||
646 | **** Emacs Daemon | 722 | **** Emacs Daemon |
@@ -649,9 +725,9 @@ Either run this once at startup, or put a shortcut of it in the | |||
649 | Startup folder: | 725 | Startup folder: |
650 | =%USERPROFILE%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup=. | 726 | =%USERPROFILE%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup=. |
651 | 727 | ||
652 | #+begin_src bat :tangle "bin/Emacs Daemon.cmd" | 728 | #+begin_src bat :tangle (when-at :work "~/Applications/cmd/Emacs Daemon.cmd") |
653 | <<w32-bat-common>> | 729 | <<w32-bat-common>> |
654 | "%EMACS%" --daemon | 730 | %EMACS% --daemon |
655 | #+end_src | 731 | #+end_src |
656 | 732 | ||
657 | **** Emacs Client | 733 | **** Emacs Client |
@@ -661,9 +737,9 @@ run =runemacs.exe=. | |||
661 | 737 | ||
662 | *This is the main shortcut for running Emacs.* | 738 | *This is the main shortcut for running Emacs.* |
663 | 739 | ||
664 | #+begin_src bat :tangle bin/Emacs.cmd | 740 | #+begin_src bat :tangle (when-at :work "~/Applications/cmd/Emacs.cmd") |
665 | <<w32-bat-common>> | 741 | <<w32-bat-common>> |
666 | set EMACSC=%~dp0..\..\..\bin\emacsclientw.exe | 742 | set EMACSC=%HOME%\Applications\Emacs\bin\emacsclientw.exe |
667 | "%EMACSC%" -n -c -a "%EMACS%" %* | 743 | "%EMACSC%" -n -c -a "%EMACS%" %* |
668 | #+end_src | 744 | #+end_src |
669 | 745 | ||
@@ -671,7 +747,7 @@ set EMACSC=%~dp0..\..\..\bin\emacsclientw.exe | |||
671 | 747 | ||
672 | This runs Emacs with the factory settings. | 748 | This runs Emacs with the factory settings. |
673 | 749 | ||
674 | #+begin_src bat :tangle "bin/Emacs Safe Start.cmd" | 750 | #+begin_src bat :tangle (when-at :work "~/Applications/cmd/Emacs Safe Start.cmd") |
675 | <<w32-bat-common>> | 751 | <<w32-bat-common>> |
676 | "%EMACS%" -Q %* | 752 | "%EMACS%" -Q %* |
677 | #+end_src | 753 | #+end_src |
@@ -680,23 +756,76 @@ This runs Emacs with the factory settings. | |||
680 | 756 | ||
681 | This runs Emacs with the =--debug-init= option enabled. | 757 | This runs Emacs with the =--debug-init= option enabled. |
682 | 758 | ||
683 | #+begin_src bat :tangle "bin/Emacs Debug.cmd" | 759 | #+begin_src bat :tangle (when-at :work "~/Applications/cmd/Emacs Debug.cmd") |
684 | <<w32-bat-common>> | 760 | <<w32-bat-common>> |
685 | "%EMACS%" --debug-init %* | 761 | "%EMACS%" --debug-init %* |
686 | #+end_src | 762 | #+end_src |
687 | * Appendices | 763 | * Appendices |
764 | |||
765 | ** =config.el= | ||
766 | :PROPERTIES: | ||
767 | :header-args: :tangle config.el :noweb yes | ||
768 | :END: | ||
769 | |||
770 | While =config.el= is written above, I use Noweb references to tangle | ||
771 | them all together in the following block, which enables me to organize | ||
772 | my config here /logically/, while keeping the generated file organized | ||
773 | /programmatically/. | ||
774 | |||
775 | *** Enable lexical binding | ||
776 | |||
777 | #+begin_src emacs-lisp | ||
778 | ;; config.el -*- lexical-binding: t -*- | ||
779 | #+end_src | ||
780 | |||
781 | *** Disclaimer | ||
782 | :PROPERTIES: | ||
783 | :header-args: :noweb-ref disclaimer | ||
784 | :END: | ||
785 | |||
786 | #+begin_src emacs-lisp | ||
787 | ;; This file is automatically tangled from config.org. | ||
788 | ;; Hand edits will be overwritten! | ||
789 | #+end_src | ||
790 | |||
791 | *** The rest | ||
792 | |||
793 | #+begin_src emacs-lisp | ||
794 | ;;; REQUIRES | ||
795 | <<requires>> | ||
796 | ;;; PACKAGES | ||
797 | <<packages>> | ||
798 | ;;; FUNCTIONS | ||
799 | <<functions>> | ||
800 | ;;; SETTINGS | ||
801 | <<settings>> | ||
802 | ;;; MODES | ||
803 | <<modes>> | ||
804 | ;;; SYSTEM-DEPENDENT SETTINGS | ||
805 | (when-at :home | ||
806 | <<linux-specific>> | ||
807 | ) | ||
808 | (when-at :work | ||
809 | <<windows-specific>> | ||
810 | ) | ||
811 | ;;; HOOKS | ||
812 | <<hooks>> | ||
813 | ;;; BINDINGS | ||
814 | <<bindings>> | ||
815 | #+end_src | ||
816 | |||
688 | ** Emacs's files | 817 | ** Emacs's files |
689 | 818 | ||
690 | *** init.el | 819 | *** init.el |
691 | :PROPERTIES: | 820 | :PROPERTIES: |
692 | :header-args: :tangle init.el :comments both | 821 | :header-args: :tangle init.el :noweb yes |
693 | :END: | 822 | :END: |
694 | 823 | ||
695 | The classic Emacs initiation file. | 824 | The classic Emacs initiation file. |
696 | 825 | ||
697 | **** Use lexical binding when evaluating =init.el= | 826 | **** Use lexical binding when evaluating =init.el= |
698 | 827 | ||
699 | #+begin_src emacs-lisp :comments no :noweb tangle | 828 | #+begin_src emacs-lisp |
700 | ;; init.el -*- lexical-binding: t -*- | 829 | ;; init.el -*- lexical-binding: t -*- |
701 | <<disclaimer>> | 830 | <<disclaimer>> |
702 | #+end_src | 831 | #+end_src |
@@ -704,7 +833,7 @@ The classic Emacs initiation file. | |||
704 | **** Prefer newer files to older files | 833 | **** Prefer newer files to older files |
705 | 834 | ||
706 | #+begin_src emacs-lisp | 835 | #+begin_src emacs-lisp |
707 | (setq load-prefer-newer t) | 836 | (setq-default load-prefer-newer t) |
708 | #+end_src | 837 | #+end_src |
709 | 838 | ||
710 | **** Load the config | 839 | **** Load the config |
@@ -715,27 +844,33 @@ directly from Org if it's newer. | |||
715 | 844 | ||
716 | #+begin_src emacs-lisp | 845 | #+begin_src emacs-lisp |
717 | (let* (;; Speed up init | 846 | (let* (;; Speed up init |
718 | (gc-cons-threshold most-positive-fixnum) | 847 | (gc-cons-threshold most-positive-fixnum) |
719 | (file-name-handler-alist nil) | 848 | (file-name-handler-alist nil) |
720 | ;; Config file names | 849 | ;; Config file names |
721 | (conf (expand-file-name "config" | 850 | (config (expand-file-name "config" |
722 | user-emacs-directory)) | 851 | user-emacs-directory)) |
723 | (conf-el (concat conf ".el")) | 852 | (config.el (concat config ".el")) |
724 | (conf-org (concat conf ".org"))) | 853 | (config.org (concat config ".org")) |
725 | (unless (and (file-newer-than-file-p conf-el conf-org) | 854 | (straight-org-dir (expand-file-name "straight/build/org" |
726 | (load conf 'no-error)) | 855 | user-emacs-directory))) |
727 | ;; A plain require here just loads the older `org' | 856 | ;; Unless config.org is /newer/ than config.el, *or* the config |
728 | ;; in Emacs' install dir. We need to add the newer | 857 | ;; is able to be loaded without errors, load the config from |
729 | ;; one to the `load-path', hopefully that's all. | 858 | ;; config.org. |
730 | (add-to-list 'load-path (expand-file-name "straight/build/org" | 859 | (unless (or (file-newer-than-file-p config.org config.el) |
731 | user-emacs-directory)) | 860 | (load config 'no-error)) |
732 | (require 'org) | 861 | ;; A plain require here just loads the older `org' |
733 | (org-babel-load-file conf-org))) | 862 | ;; in Emacs' install dir. We need to add the newer |
863 | ;; one to the `load-path', hopefully that's all. | ||
864 | (when (file-exists-p straight-org-dir) | ||
865 | (add-to-list 'load-path straight-org-dir)) | ||
866 | ;; Load config.org | ||
867 | (require 'org) | ||
868 | (org-babel-load-file config.org))) | ||
734 | #+end_src | 869 | #+end_src |
735 | 870 | ||
736 | *** early-init.el | 871 | *** early-init.el |
737 | :PROPERTIES: | 872 | :PROPERTIES: |
738 | :header-args: :tangle early-init.el :comments both | 873 | :header-args: :tangle early-init.el :noweb yes |
739 | :END: | 874 | :END: |
740 | 875 | ||
741 | Beginning with 27.1, Emacs also loads an =early-init.el= file, before | 876 | Beginning with 27.1, Emacs also loads an =early-init.el= file, before |
@@ -744,29 +879,31 @@ little as possible in this file, so I only have what I need. | |||
744 | 879 | ||
745 | **** Don't byte-compile this file | 880 | **** Don't byte-compile this file |
746 | 881 | ||
747 | #+begin_src emacs-lisp :comments no :noweb tangle | 882 | #+begin_src emacs-lisp |
748 | ;; early-init.el -*- no-byte-compile: t; -*- | 883 | ;; early-init.el -*- no-byte-compile: t; -*- |
749 | <<disclaimer>> | 884 | <<disclaimer>> |
750 | #+end_src | 885 | #+end_src |
751 | 886 | ||
752 | **** Disable loading of =package.el= | 887 | **** Package management |
753 | 888 | ||
754 | I use =straight.el= instead. | 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=. | ||
755 | 892 | ||
756 | #+begin_src emacs-lisp | 893 | #+begin_src emacs-lisp |
757 | (setq package-enable-at-startup nil) | 894 | <<package-bootstrap>> |
758 | #+end_src | 895 | #+end_src |
759 | 896 | ||
760 | **** Don't resize the frame when loading fonts | 897 | **** Don't resize the frame when loading fonts |
761 | 898 | ||
762 | #+begin_src emacs-lisp | 899 | #+begin_src emacs-lisp |
763 | (setq frame-inhibit-implied-resize t) | 900 | (setq-default frame-inhibit-implied-resize t) |
764 | #+end_src | 901 | #+end_src |
765 | 902 | ||
766 | **** Resize frame by pixels | 903 | **** Resize frame by pixels |
767 | 904 | ||
768 | #+begin_src emacs-lisp | 905 | #+begin_src emacs-lisp |
769 | (setq frame-resize-pixelwise t) | 906 | (setq-default frame-resize-pixelwise t) |
770 | #+end_src | 907 | #+end_src |
771 | 908 | ||
772 | **** Shoe-horned from elsewhere in =config.org= | 909 | **** Shoe-horned from elsewhere in =config.org= |
@@ -778,13 +915,13 @@ weird shoe-horning of other bits of my config here, in a backwater | |||
778 | heading in an appendix, isn't quite the future I wanted. But it's | 915 | heading in an appendix, isn't quite the future I wanted. But it's |
779 | what I have for now. | 916 | what I have for now. |
780 | 917 | ||
781 | #+begin_src emacs-lisp :noweb tangle | 918 | #+begin_src emacs-lisp |
782 | <<initial-frame-setup>> | 919 | <<initial-frame-setup>> |
783 | #+end_src | 920 | #+end_src |
784 | 921 | ||
785 | ** License | 922 | ** License |
786 | :PROPERTIES: | 923 | :PROPERTIES: |
787 | :header-args: :tangle LICENSE :comments no | 924 | :header-args: :tangle LICENSE |
788 | :END: | 925 | :END: |
789 | 926 | ||
790 | Copyright © 2020 Case Duckworth <acdw@acdw.net> | 927 | Copyright © 2020 Case Duckworth <acdw@acdw.net> |
@@ -817,3 +954,33 @@ It's highly likely that the WTFPL is completely incompatible with the | |||
817 | GPL, for what should be fairly obvious reasons. To that, I say: | 954 | GPL, for what should be fairly obvious reasons. To that, I say: |
818 | 955 | ||
819 | *SUE ME, RMS!* | 956 | *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: | ||