diff options
-rw-r--r-- | README.md | 1395 | ||||
-rw-r--r-- | config.org | 601 |
2 files changed, 768 insertions, 1228 deletions
diff --git a/README.md b/README.md index f725e84..02029dc 100644 --- a/README.md +++ b/README.md | |||
@@ -1,11 +1,19 @@ | |||
1 | Let’s configure Emacs using Org mode, they said. It’ll be fun, they said. | 1 | Why the hell not, let’s do this again. |
2 | 2 | ||
3 | 3 | ||
4 | # Pave the way | 4 | # Basics |
5 | |||
6 | |||
7 | ## About me | ||
8 | |||
9 | (setq user-full-name "Case Duckworth" | ||
10 | user-mail-address "acdw@acdw.net") | ||
5 | 11 | ||
6 | 12 | ||
7 | ## Correct `exec-path` | 13 | ## Correct `exec-path` |
8 | 14 | ||
15 | Straight depends on Git, so I need to tell Emacs where different paths are. | ||
16 | |||
9 | (let ((win-downloads "c:/Users/aduckworth/Downloads")) | 17 | (let ((win-downloads "c:/Users/aduckworth/Downloads")) |
10 | (dolist (path (list | 18 | (dolist (path (list |
11 | ;; Linux | 19 | ;; Linux |
@@ -34,30 +42,36 @@ Let’s configure Emacs using Org mode, they said. It’ll be fun, they said. | |||
34 | 42 | ||
35 | ### Straight.el | 43 | ### Straight.el |
36 | 44 | ||
37 | Since for whatever reason, Straight can't bootstrap itself on Windows | 45 | Straight can't bootstrap itself on Windows, so I've wrapped the |
38 | – I've wrapped it in a function here and added the direct git command | 46 | bootstrap code from straight's repo in a function. |
39 | when it errors. | ||
40 | 47 | ||
41 | (defun acdw/bootstrap-straight () | 48 | (defun acdw/bootstrap-straight () |
49 | "Bootstrap straight.el." | ||
42 | (defvar bootstrap-version) | 50 | (defvar bootstrap-version) |
43 | (let ((bootstrap-file | 51 | (let ((bootstrap-file |
44 | (expand-file-name | 52 | (expand-file-name |
45 | "straight/repos/straight.el/bootstrap.el" | 53 | "straight/repos/straight.el/bootstrap.el" |
46 | user-emacs-directory)) | 54 | user-emacs-directory)) |
47 | (bootstrap-version 5)) | 55 | (bootstrap-version 5)) |
48 | (unless (file-exists-p bootstrap-file) | 56 | (unless (file-exists-p bootstrap-file) |
49 | (with-current-buffer | 57 | (with-current-buffer |
50 | (url-retrieve-synchronously | 58 | (url-retrieve-synchronously |
51 | (concat "https://raw.githubusercontent.com/" | 59 | (concat |
52 | "raxod502/straight.el/develop/install.el") | 60 | "https://raw.githubusercontent.com/" |
53 | 'silent 'inhibit-cookies) | 61 | "raxod502/straight.el/" |
54 | (goto-char (point-max)) | 62 | "develop/install.el") |
55 | (eval-print-last-sexp))) | 63 | 'silent 'inhibit-cookies) |
56 | (load bootstrap-file nil 'nomessage))) | 64 | (goto-char (point-max)) |
57 | 65 | (eval-print-last-sexp))) | |
66 | (load bootstrap-file nil 'nomessage))) | ||
67 | |||
68 | Now, I'll *try* running it regular-style, ignoring the errors. If it | ||
69 | doesn't work, I'll call git directly and clone the repo myself. | ||
70 | |||
58 | (unless (ignore-errors (acdw/bootstrap-straight)) | 71 | (unless (ignore-errors (acdw/bootstrap-straight)) |
59 | (message "Straight.el didn't bootstrap correctly. Cloning directly...") | 72 | (message "Straight.el didn't bootstrap correctly. Cloning directly...") |
60 | (call-process "git" nil (get-buffer-create "*bootstrap-straight-messages*") nil | 73 | (call-process "git" nil |
74 | (get-buffer-create "*bootstrap-straight-messages*") nil | ||
61 | "clone" | 75 | "clone" |
62 | "https://github.com/raxod502/straight.el" | 76 | "https://github.com/raxod502/straight.el" |
63 | (expand-file-name "straight/repos/straight.el" | 77 | (expand-file-name "straight/repos/straight.el" |
@@ -65,317 +79,226 @@ when it errors. | |||
65 | (acdw/bootstrap-straight)) | 79 | (acdw/bootstrap-straight)) |
66 | 80 | ||
67 | 81 | ||
68 | ### Use-package | 82 | ## Customize macro |
69 | |||
70 | (setq straight-use-package-by-default t) | ||
71 | (setq use-package-hook-name-suffix nil) | ||
72 | (straight-use-package 'use-package) | ||
73 | |||
74 | |||
75 | ### Extra use-package keywords | ||
76 | |||
77 | 1. :custom-update | ||
78 | |||
79 | (straight-use-package | ||
80 | '(use-package-custom-update | ||
81 | :host github | ||
82 | :repo "a13/use-package-custom-update")) | ||
83 | |||
84 | (require 'use-package-custom-update) | ||
85 | |||
86 | |||
87 | ## Customize variables | ||
88 | |||
89 | |||
90 | ### Put customizations in a separate file | ||
91 | |||
92 | (setq custom-file | ||
93 | (expand-file-name "custom.el" user-emacs-directory)) | ||
94 | |||
95 | |||
96 | ### A macro for ease of customization | ||
97 | 83 | ||
98 | (defmacro cuss (var val &optional docstring) | 84 | (defmacro cuss (var val &optional docstring) |
99 | "Basically `:custom' from `use-package', broken out." | 85 | "Basically, `:custom' from `use-package', but without `use-package'." |
86 | (declare (doc-string 3) | ||
87 | (indent 2)) | ||
100 | `(funcall (or (get ',var 'custom-set) #'set-default) | 88 | `(funcall (or (get ',var 'custom-set) #'set-default) |
101 | ',var ,val)) | 89 | ',var ,val)) |
102 | 90 | ||
103 | 91 | ||
104 | ## Keep a tidy `~/.emacs` | 92 | ## Clean `.emacs.d` |
105 | 93 | ||
106 | (straight-use-package 'no-littering) | 94 | (straight-use-package 'no-littering) |
107 | 95 | (require 'no-littering) | |
108 | (cuss backup-directory-alist `((".*" . ,(no-littering-expand-var-file-name "backup/"))) | ||
109 | "Where to store backup files.") | ||
110 | |||
111 | (cuss auto-save-file-name-transforms | ||
112 | `((".*" ,(no-littering-expand-var-file-name "autosaves/") t)) | ||
113 | "Where to store auto-save files.") | ||
114 | |||
115 | (cuss save-place-file (no-littering-expand-var-file-name "places") | ||
116 | "Where to store place files.") | ||
117 | |||
118 | (cuss undo-fu-session-directory (no-littering-expand-var-file-name "undos/") | ||
119 | "Where to store undo information.") | ||
120 | |||
121 | (cuss elpher-certificate-directory | ||
122 | (no-littering-expand-var-file-name "elpher-certificates/") | ||
123 | "Where to store elpher client certificates.") | ||
124 | |||
125 | ;; Make all directories defined above | ||
126 | (dolist (dir '("backup" | ||
127 | "autosaves" | ||
128 | "undos" | ||
129 | "elpher-certificates")) | ||
130 | (make-directory (no-littering-expand-var-file-name dir) 'parents)) | ||
131 | |||
132 | 96 | ||
133 | ## About me | ||
134 | 97 | ||
135 | (setq user-full-name "Case Duckworth" | 98 | ## Look and feel |
136 | user-mail-address "acdw@acdw.net") | ||
137 | 99 | ||
138 | 100 | ||
139 | # Look and Feel | 101 | ### Fonts |
140 | |||
141 | |||
142 | ## Simplify the UI | ||
143 | |||
144 | |||
145 | ### Tool bars and menu bars | ||
146 | |||
147 | (cuss default-frame-alist | ||
148 | '((tool-bar-lines . 0) | ||
149 | (menu-bar-lines . 0))) | ||
150 | |||
151 | (menu-bar-mode -1) | ||
152 | (tool-bar-mode -1) | ||
153 | |||
154 | |||
155 | ### Scroll bars | ||
156 | |||
157 | (add-to-list 'default-frame-alist '(vertical-scroll-bars . nil)) | ||
158 | (scroll-bar-mode -1) | ||
159 | |||
160 | (add-to-list 'default-frame-alist '(horizontal-scroll-bars . nil)) | ||
161 | (horizontal-scroll-bar-mode -1) | ||
162 | 102 | ||
103 | 1. Define fonts | ||
163 | 104 | ||
164 | ### Dialog boxen | 105 | (defun set-face-from-alternatives (face fonts) |
106 | (catch :return | ||
107 | (dolist (font fonts) | ||
108 | (when (find-font (font-spec :family (car font))) | ||
109 | (apply #'set-face-attribute `(,face nil | ||
110 | :family ,(car font) | ||
111 | ,@(cdr font))) | ||
112 | (throw :return font))))) | ||
113 | |||
114 | (defun acdw/setup-fonts () | ||
115 | "Setup fonts. This has to happen after the frame is setup for | ||
116 | the first time, so it should be added to `window-setup-hook'. It | ||
117 | removes itself from that hook." | ||
118 | (interactive) | ||
119 | (when (display-graphic-p) | ||
120 | (set-face-from-alternatives 'default | ||
121 | '(("Libertinus Mono" | ||
122 | :height 110) | ||
123 | ("Linux Libertine Mono O" | ||
124 | :height 110) | ||
125 | ("Go Mono" | ||
126 | :height 100) | ||
127 | ("Consolas" | ||
128 | :height 100))) | ||
129 | |||
130 | (set-face-from-alternatives 'fixed-pitch | ||
131 | '(("Libertinus Mono" | ||
132 | :height 110) | ||
133 | ("Linux Libertine Mono O" | ||
134 | :height 110) | ||
135 | ("Go Mono" | ||
136 | :height 100) | ||
137 | ("Consolas" | ||
138 | :height 100))) | ||
139 | |||
140 | (set-face-from-alternatives 'variable-pitch | ||
141 | '(("Libertinus Serif" | ||
142 | :height 120) | ||
143 | ("Linux Libertine O" | ||
144 | :height 120) | ||
145 | ("Georgia" | ||
146 | :height 110))) | ||
147 | |||
148 | (remove-function after-focus-change-function #'acdw/setup-fonts))) | ||
149 | |||
150 | (add-function :before after-focus-change-function #'acdw/setup-fonts) | ||
165 | 151 | ||
166 | (cuss use-dialog-box nil) | 152 | 2. Line spacing |
167 | 153 | ||
154 | (cuss line-spacing 0.1 | ||
155 | "Add 10% extra space below each line.") | ||
168 | 156 | ||
169 | ### Shorten confirmations | 157 | 3. Unicode Fonts |
170 | 158 | ||
171 | (fset 'yes-or-no-p #'y-or-n-p) | 159 | (straight-use-package 'unicode-fonts) |
160 | (require 'unicode-fonts) | ||
161 | (unicode-fonts-setup) | ||
172 | 162 | ||
173 | 163 | ||
174 | ### Remove the bell | 164 | ### Cursor |
175 | 165 | ||
176 | (cuss visible-bell (not (string= (system-name) "larry"))) | 166 | (cuss cursor-type 'bar |
167 | "Show a vertical bar for the cursor.") | ||
177 | 168 | ||
178 | (defun acdw/ring-bell-function () | 169 | (cuss cursor-in-non-selected-windows 'hollow |
179 | "Custom bell-ringing function." | 170 | "Show an empty box in inactive windows.") |
180 | (let ((orig-face (face-foreground 'mode-line))) | ||
181 | (set-face-foreground 'modeline "#F2804F") | ||
182 | (run-with-idle-timer | ||
183 | 0.1 nil | ||
184 | (lambda (fg) | ||
185 | (set-face-foreground 'mode-line fg)) | ||
186 | orig-face))) | ||
187 | 171 | ||
188 | (cuss ring-bell-function #'acdw/ring-bell-function) | 172 | ;; Don't blink the cursor |
189 | 173 | (blink-cursor-mode -1) | |
190 | |||
191 | ### Tell Ediff to setup windows better | ||
192 | |||
193 | (declare-function ediff-setup-windows-plain "ediff-wind.el") | ||
194 | (cuss ediff-window-setup-function #'ediff-setup-windows-plain) | ||
195 | |||
196 | |||
197 | ## Tweak the remaining UI | ||
198 | |||
199 | |||
200 | ### Fringes | ||
201 | 174 | ||
202 | (add-to-list 'default-frame-alist '(left-fringe-width . 2)) | ||
203 | (add-to-list 'default-frame-alist '(right-fringe-width . 2)) | ||
204 | 175 | ||
176 | ### Tool Bars | ||
205 | 177 | ||
206 | ### Minibuffer | 178 | 1. Tool bars and menu bars |
207 | 179 | ||
208 | 1. Setup the minibuffer frame | 180 | (cuss default-frame-alist |
209 | 181 | '((tool-bar-lines . 0) | |
210 | (cuss minibuffer-frame-alist | 182 | (menu-bar-lines .0)) |
211 | '((width . 80) | 183 | "Setup the default frame alist.") |
212 | (height . 2) | ||
213 | (vertical-scrollbars . nil))) | ||
214 | 184 | ||
215 | (set-window-scroll-bars (minibuffer-window) nil nil) | 185 | (menu-bar-mode -1) |
216 | 186 | (tool-bar-mode -1) | |
217 | 2. Keep the cursor from going into the prompt | ||
218 | |||
219 | (cuss minibuffer-prompt-properties | ||
220 | '(read-only t cursor-intangible t face minibuffer-prompt)) | ||
221 | |||
222 | 187 | ||
223 | ### Tabs | 188 | 2. Scroll bars |
224 | 189 | ||
225 | 1. Show the tabs as current buffer, plus window count | 190 | (add-to-list 'default-frame-alist |
226 | 191 | '(vertical-scroll-bars . nil)) | |
227 | (cuss tab-bar-tab-name-function #'tab-bar-tab-name-current-with-count) | 192 | |
228 | 193 | (scroll-bar-mode -1) | |
229 | 2. Only show the tab bar when there's more than one tab | 194 | |
195 | (add-to-list 'default-frame-alist | ||
196 | '(horizontal-scroll-bars . nil)) | ||
197 | |||
198 | (horizontal-scroll-bar-mode -1) | ||
230 | 199 | ||
231 | (cuss tab-bar-show 1 | ||
232 | "Show the tab bar only when there's more than 1 tab.") | ||
233 | 200 | ||
201 | ### Dialogs | ||
234 | 202 | ||
235 | ### Cursor | 203 | (cuss use-dialog-box nil |
204 | "Don't use dialog boxes to ask questions.") | ||
236 | 205 | ||
237 | (cuss cursor-type 'bar | 206 | 1. Yes or no questions |
238 | "Show a vertical bar for the cursor.") | ||
239 | (cuss cursor-in-non-selected-windows 'hollow | ||
240 | "In inactive windows, make the cursor an empty box.") | ||
241 | (blink-cursor-mode 0) | ||
242 | 207 | ||
208 | (fset 'yes-or-no-p #'y-or-n-p) | ||
243 | 209 | ||
244 | ### Buffer names | 210 | 2. The Bell |
245 | 211 | ||
246 | (require 'uniquify) | 212 | (defun acdw/ring-bell-function () |
247 | (cuss uniquify-buffer-name-style 'forward) | 213 | "Ring the bell." |
214 | (let ((orig-face (face-foreground 'mode-line))) | ||
215 | (set-face-foreground 'mode-line "#F2804F") | ||
216 | (run-with-idle-timer | ||
217 | 0.1 nil | ||
218 | (lambda (fg) | ||
219 | (set-face-foreground 'mode-line fg)) | ||
220 | orig-face))) | ||
221 | |||
222 | (cuss ring-bell-function #'acdw/ring-bell-function) | ||
248 | 223 | ||
249 | 224 | ||
250 | ### Buffer boundaries | 225 | ### Frames |
251 | 226 | ||
252 | (cuss indicate-buffer-boundaries | 227 | 1. Fringes |
253 | '((up . right) | ||
254 | (down . right) | ||
255 | (t . nil))) | ||
256 | |||
257 | (cuss indicate-empty-lines t) | ||
258 | 228 | ||
229 | (cuss indicate-empty-lines t | ||
230 | "Show an indicator on the left fringe of empty lines past the | ||
231 | end of the buffer.") | ||
232 | (cuss indicate-buffer-boundaries 'right | ||
233 | "Indicate the beginning and end of the buffer and whether it | ||
234 | scrolls off-window in the right fringe.") | ||
259 | 235 | ||
260 | ## Windows | 236 | 2. Minibuffer |
261 | 237 | ||
238 | (cuss minibuffer-prompt-properties | ||
239 | '(read-only t cursor-intangible t face minibuffer-prompt) | ||
240 | "Keep the cursor away from the minibuffer prompt.") | ||
262 | 241 | ||
263 | ### Winner mode | 242 | 3. Tabs |
264 | 243 | ||
265 | (when (fboundp 'winner-mode) | 244 | (cuss tab-bar-tab-name-function |
266 | (winner-mode +1)) | 245 | #'tab-bar-tab-name-current-with-count |
246 | "Show the tab name as the name of the current buffer, plus a | ||
247 | count of the windows in the tab.") | ||
248 | |||
249 | (cuss tab-bar-show 1 | ||
250 | "Show the tab bar, when there's more than one tab.") | ||
267 | 251 | ||
268 | 252 | ||
269 | ### Windmove | 253 | ### Windows |
270 | 254 | ||
271 | (cuss windmove-create-window t | 255 | 1. Winner mode |
272 | "Create windows in a direction if they don't exist.") | ||
273 | (cuss windomove-wrap-around t | ||
274 | "Wrap window movements around frame edges.") | ||
275 | |||
276 | (windmove-default-keybindings) | ||
277 | |||
278 | |||
279 | ### Pop some buffers up in the same window | ||
280 | |||
281 | from [link0ff](https://github.com/link0ff/emacs-init). | ||
282 | |||
283 | (push `(,(rx bos | ||
284 | "*" | ||
285 | (or "Help" "Apropos" "Colors" "Buffer List" "Command History" | ||
286 | "Dictionary" "Locate" "Messages" "Proced" "eww" "snd" | ||
287 | (and "gud-" (+ (any "a-z0-9"))) | ||
288 | "compilation" "grep" "erlang" "haskell" | ||
289 | ;; Handle both "*shell*" and e.g. "*emacs-shell*" | ||
290 | ;; generated by `project-shell': | ||
291 | (and (? (* nonl) "-") "shell") | ||
292 | "Shell Command Output" | ||
293 | (and "SQL: " (+ (any "A-za-z"))) | ||
294 | "Diff" "vc-dir" "vc-log" "vc-search-log") | ||
295 | "*" | ||
296 | ;; Uniquifed buffer name with optional suffix in angle brackets | ||
297 | (? (and "<" (+ (not (any ">"))) ">")) | ||
298 | eos) | ||
299 | display-buffer-same-window | ||
300 | (inhibit-same-window . nil)) | ||
301 | display-buffer-alist) | ||
302 | |||
303 | (defun display-buffer-from-help-p (_buffer-name _action) | ||
304 | (unless current-prefix-arg | ||
305 | (with-current-buffer (window-buffer) | ||
306 | (eq major-mode 'help-mode)))) | ||
307 | |||
308 | (push '(display-buffer-from-help-p display-buffer-same-window) | ||
309 | display-buffer-alist) | ||
310 | 256 | ||
257 | (when (fboundp 'winner-mode) | ||
258 | (winner-mode +1)) | ||
311 | 259 | ||
312 | ## Startup | 260 | 2. Switch windows |
313 | 261 | ||
314 | (cuss inhibit-startup-screen t "Don't show Emacs' startup buffer.") | 262 | (global-set-key (kbd "M-o") #'other-window) |
315 | (cuss initial-buffer-choice t "Start at *scratch*.") | ||
316 | (cuss initial-scratch-message "" "Empty *scratch*.") | ||
317 | 263 | ||
318 | 264 | ||
319 | ## Theme | 265 | ### Buffers |
320 | 266 | ||
321 | (straight-use-package '(modus-themes | 267 | 1. Uniquify buffers |
322 | :host gitlab | ||
323 | :repo "protesilaos/modus-themes" | ||
324 | :branch "main")) | ||
325 | |||
326 | (cuss modus-themes-slanted-constructs t) | ||
327 | (cuss modus-themes-bold-constructs t) | ||
328 | (cuss modus-themes-fringes nil) | ||
329 | (cuss modus-themes-mode-line '3d) | ||
330 | (cuss modus-themes-syntax 'yellow-comments) | ||
331 | (cuss modus-themes-intense-hl-line nil) | ||
332 | (cuss modus-themes-paren-match 'intense-bold) | ||
333 | (cuss modus-themes-links nil) | ||
334 | (cuss modus-themes-no-mixed-fonts nil) | ||
335 | (cuss modus-themes-prompts nil) | ||
336 | (cuss modus-themes-completions nil) | ||
337 | (cuss modus-themes-diffs nil) | ||
338 | (cuss modus-themes-org-blocks 'grayscale) | ||
339 | (cuss modus-themes-headings | ||
340 | '((1 . line) | ||
341 | (t . t))) | ||
342 | (cuss modus-themes-variable-pitch-headings t) | ||
343 | (cuss modus-themes-scale-headings t) | ||
344 | (cuss modus-themes-scale-1 1.1) | ||
345 | (cuss modus-themes-scale-2 1.15) | ||
346 | (cuss modus-themes-scale-3 1.21) | ||
347 | (cuss modus-themes-scale-4 1.27) | ||
348 | (cuss modus-themes-scale-5 1.33) | ||
349 | |||
350 | ;; :custom-face | ||
351 | (custom-set-faces `(font-lock-comment-face | ||
352 | ((t (:inherit (custom-comment italic variable-pitch)))))) | ||
353 | |||
354 | (load-theme 'modus-operandi t) | ||
355 | 268 | ||
269 | (require 'uniquify) | ||
270 | (cuss uniquify-buffer-name-style 'forward | ||
271 | "Uniquify buffers' names by going up the path trees until they | ||
272 | become unique.") | ||
356 | 273 | ||
357 | ### Change theme based on time of day | 274 | 2. Startup buffers |
358 | 275 | ||
359 | (cuss calendar-latitude 30.4515) | 276 | (cuss inhibit-startup-screen t |
360 | (cuss calendar-longitude -91.1871) | 277 | "Don't show Emacs' startup buffer.") |
361 | 278 | ||
362 | (straight-use-package 'circadian) | 279 | (cuss initial-buffer-choice t |
363 | 280 | "Start with *scratch*.") | |
364 | (cuss circadian-themes '((:sunrise . modus-operandi) | 281 | |
365 | (:sunset . modus-vivendi))) | 282 | (cuss initial-scratch-message "" |
366 | 283 | "Empty *scratch* buffer.") | |
367 | (circadian-setup) | ||
368 | 284 | ||
369 | 285 | ||
370 | ### Modeline | 286 | ### Modeline |
371 | 287 | ||
372 | (straight-use-package 'smart-mode-line) | 288 | 1. Smart mode line |
373 | (cuss sml/no-confirm-load-theme t) | ||
374 | (sml/setup) | ||
375 | 289 | ||
376 | 1. Rich minority | 290 | (straight-use-package 'smart-mode-line) |
291 | |||
292 | (cuss sml/no-confirm-load-theme t | ||
293 | "Pass the NO-CONFIRM flag to `load-theme'.") | ||
294 | |||
295 | (sml/setup) | ||
377 | 296 | ||
378 | Since this *comes* with smart mode line, I’m just going to use it, instead of `diminish` or another package. I do have to write this helper function, though, to add things to the whitelist. | 297 | 2. Rich minority |
298 | |||
299 | Since this *comes* with smart mode line, I’m just going to use it, | ||
300 | instead of `diminish` or another package. I do have to write this | ||
301 | helper function, though, to add things to the whitelist. | ||
379 | 302 | ||
380 | (defun rm/whitelist-add (regexp) | 303 | (defun rm/whitelist-add (regexp) |
381 | "Add a REGEXP to the whitelist for `rich-minority'." | 304 | "Add a REGEXP to the whitelist for `rich-minority'." |
@@ -390,212 +313,138 @@ from [link0ff](https://github.com/link0ff/emacs-init). | |||
390 | (rm/whitelist-add "^$") | 313 | (rm/whitelist-add "^$") |
391 | 314 | ||
392 | 315 | ||
393 | ### Fonts | 316 | ### Theme |
394 | 317 | ||
395 | 1. Define fonts | 318 | 1. Modus Themes |
396 | 319 | ||
397 | (defun set-face-from-alternatives (face fonts) | 320 | (straight-use-package 'modus-themes) |
398 | (catch :return | ||
399 | (dolist (font fonts) | ||
400 | (when (find-font (font-spec :family (car font))) | ||
401 | (apply #'set-face-attribute `(,face nil | ||
402 | :family ,(car font) | ||
403 | ,@(cdr font))) | ||
404 | (throw :return font))))) | ||
405 | 321 | ||
406 | (defun acdw/setup-fonts () | 322 | (cuss modus-themes-slanted-constructs t |
407 | "Setup fonts. This has to happen after the frame is setup for | 323 | "Use more slanted constructs.") |
408 | the first time, so it should be added to `window-setup-hook'. It | 324 | (cuss modus-themes-bold-constructs t |
409 | removes itself from that hook." | 325 | "Use more bold constructs.") |
410 | (interactive) | ||
411 | (when (display-graphic-p) | ||
412 | (set-face-from-alternatives 'default | ||
413 | '(("Libertinus Mono" | ||
414 | :height 110) | ||
415 | ("Linux Libertine Mono O" | ||
416 | :height 110) | ||
417 | ("Go Mono" | ||
418 | :height 100) | ||
419 | ("Consolas" | ||
420 | :height 100))) | ||
421 | 326 | ||
422 | (set-face-from-alternatives 'fixed-pitch | 327 | (cuss modus-themes-region 'bg-only |
423 | '(("Libertinus Mono" | 328 | "Only highlight the background of the selected region.") |
424 | :height 110) | ||
425 | ("Linux Libertine Mono O" | ||
426 | :height 110) | ||
427 | ("Go Mono" | ||
428 | :height 100) | ||
429 | ("Consolas" | ||
430 | :height 100))) | ||
431 | 329 | ||
432 | (set-face-from-alternatives 'variable-pitch | 330 | (cuss modus-themes-org-blocks 'grayscale |
433 | '(("Libertinus Serif" | 331 | "Show org-blocks with a grayscale background.") |
434 | :height 120) | 332 | (cuss modus-themes-headings |
435 | ("Linux Libertine O" | 333 | '((1 . line) |
436 | :height 120) | 334 | (t . t)) |
437 | ("Georgia" | 335 | "Highlight top headings with `line' style, and others by default.") |
438 | :height 110))) | ||
439 | 336 | ||
440 | (remove-function after-focus-change-function #'acdw/setup-fonts))) | 337 | (cuss modus-themes-scale-headings t |
338 | "Scale headings by the ratios below.") | ||
339 | (cuss modus-themes-scale-1 1.1) | ||
340 | (cuss modus-themes-scale-2 1.15) | ||
341 | (cuss modus-themes-scale-3 1.21) | ||
342 | (cuss modus-themes-scale-4 1.27) | ||
343 | (cuss modus-themes-scale-5 1.33) | ||
441 | 344 | ||
442 | (add-function :before after-focus-change-function #'acdw/setup-fonts) | 345 | (load-theme 'modus-operandi t) |
443 | |||
444 | 2. Variable-pitch in text modes | ||
445 | |||
446 | (add-hook 'text-mode-hook #'variable-pitch-mode) | ||
447 | |||
448 | 3. Line spacing | ||
449 | 346 | ||
450 | (cuss line-spacing 0.1) | 347 | 2. Change themes based on time of day |
451 | 348 | ||
452 | 4. Unicode fonts | 349 | (cuss calendar-latitude 30.4515) |
453 | 350 | (cuss calendar-longitude -91.1871) | |
454 | (straight-use-package 'unicode-fonts) | 351 | |
455 | (with-eval-after-load 'unicode-fonts | 352 | (straight-use-package 'circadian) |
456 | (unicode-fonts-setup)) | 353 | |
457 | 354 | (cuss circadian-themes '((:sunrise . modus-operandi) | |
458 | 355 | (:sunset . modus-vivendi))) | |
459 | # Interactivity | 356 | |
460 | 357 | (circadian-setup) | |
461 | |||
462 | ## Async | ||
463 | |||
464 | (straight-use-package 'async) | ||
465 | (autoload 'dired-async-mode "dired-async.el" nil t) | ||
466 | (dired-async-mode +1) | ||
467 | |||
468 | (async-bytecomp-package-mode +1) | ||
469 | |||
470 | |||
471 | ## Completing-read | ||
472 | |||
473 | |||
474 | ### Shadow file names | ||
475 | |||
476 | (cuss file-name-shadow-properties '(invisible t)) | ||
477 | |||
478 | (file-name-shadow-mode +1) | ||
479 | 358 | ||
480 | 359 | ||
481 | ### Selectrum | 360 | ## Interactivity |
482 | 361 | ||
483 | (straight-use-package 'selectrum) | ||
484 | (require 'selectrum) | ||
485 | (selectrum-mode +1) | ||
486 | 362 | ||
363 | ### Completing read | ||
487 | 364 | ||
488 | ### Prescient | 365 | 1. Shadow file names in `completing-read`. |
489 | 366 | ||
490 | (straight-use-package 'prescient) | 367 | (cuss file-name-shadow-properties '(invisible t)) |
491 | (require 'prescient) | 368 | |
492 | 369 | (file-name-shadow-mode +1) | |
493 | (prescient-persist-mode +1) | ||
494 | |||
495 | (straight-use-package 'selectrum-prescient) | ||
496 | |||
497 | (with-eval-after-load 'prescient | ||
498 | (with-eval-after-load 'selectrum | ||
499 | (selectrum-prescient-mode +1))) | ||
500 | |||
501 | (straight-use-package 'company-prescient) | ||
502 | |||
503 | (with-eval-after-load 'prescient | ||
504 | (with-eval-after-load 'company | ||
505 | (company-prescient-mode +1))) | ||
506 | |||
507 | |||
508 | ### Consult | ||
509 | |||
510 | (use-package consult | ||
511 | :after (selectrum) | ||
512 | :straight (consult | ||
513 | :host github | ||
514 | :repo "minad/consult") | ||
515 | :bind | ||
516 | (("C-x b" . consult-buffer) | ||
517 | ("C-x 4 b" . consult-buffer-other-window) | ||
518 | ("C-x 5 b" . consult-buffer-other-frame) | ||
519 | ("M-g o" . consult-outline) | ||
520 | ("M-g l" . consult-line) | ||
521 | ("M-y" . consult-yank-pop) | ||
522 | ("<help> a" . consult-apropos)) | ||
523 | :init | ||
524 | (fset 'multi-occur #'consult-multi-occur)) | ||
525 | |||
526 | (use-package consult-selectrum | ||
527 | :straight (consult-selectrum | ||
528 | :host github | ||
529 | :repo "minad/consult")) | ||
530 | |||
531 | |||
532 | ### Marginalia | ||
533 | |||
534 | (straight-use-package '(marginalia | ||
535 | :host github | ||
536 | :repo "minad/marginalia" | ||
537 | :branch "main")) | ||
538 | |||
539 | (cuss marginalia-annotators | ||
540 | (if (eq system-type 'windows-nt) | ||
541 | '(marginalia-annotators-light | ||
542 | marginalia-annotators-heavy) | ||
543 | '(marginalia-annotators-heavy | ||
544 | marginalia-annotators-light))) | ||
545 | |||
546 | (marginalia-mode +1) | ||
547 | |||
548 | |||
549 | ## Ignore case | ||
550 | 370 | ||
551 | (cuss completion-ignore-case t) | 371 | 2. Ignore case in `completing-read` |
552 | (cuss read-buffer-completion-ignore-case t) | ||
553 | (cuss read-file-name-completion-ignore-case t) | ||
554 | 372 | ||
373 | (cuss completion-ignore-case t) | ||
374 | (cuss read-buffer-completion-ignore-case t) | ||
375 | (cuss read-file-name-completion-ignore-case t) | ||
555 | 376 | ||
556 | ## Search | 377 | 3. Selectrum |
557 | 378 | ||
558 | (use-package ctrlf | 379 | (straight-use-package 'selectrum) |
559 | :custom | 380 | (require 'selectrum) |
560 | (ctrlf-show-match-count-at-eol nil) | 381 | (selectrum-mode +1) |
561 | :bind | ||
562 | ("C-s" . ctrlf-forward-regexp) | ||
563 | ("C-r" . ctrlf-backward-regexp) | ||
564 | ("C-M-s" . ctrlf-forward-literal) | ||
565 | ("C-M-r" . ctrlf-backward-literal) | ||
566 | :config | ||
567 | (ctrlf-mode +1)) | ||
568 | 382 | ||
383 | 4. Prescient | ||
569 | 384 | ||
570 | ## Mouse | 385 | (straight-use-package 'prescient) |
386 | (require 'prescient) | ||
387 | (prescient-persist-mode +1) | ||
388 | |||
389 | (straight-use-package 'selectrum-prescient) | ||
390 | (require 'selectrum-prescient) | ||
391 | (selectrum-prescient-mode +1) | ||
571 | 392 | ||
393 | 5. Consult | ||
572 | 394 | ||
573 | ### Fix scrolling in margins | 395 | (straight-use-package '(consult |
396 | :host github | ||
397 | :repo "minad/consult")) | ||
398 | (require 'consult) | ||
399 | |||
400 | (straight-use-package '(consult-selectrum | ||
401 | :host github | ||
402 | :repo "minad/consult")) | ||
403 | (require 'consult-selectrum) | ||
404 | |||
405 | (define-key ctl-x-map "b" #'consult-buffer) | ||
406 | (define-key ctl-x-map (kbd "C-r") #'consult-buffer) | ||
407 | (define-key ctl-x-map "4b" #'consult-buffer-other-window) | ||
408 | (define-key ctl-x-map "5b" #'consult-buffer-other-frame) | ||
409 | |||
410 | (define-key goto-map "o" #'consult-outline) | ||
411 | (define-key goto-map "g" #'consult-line) | ||
412 | (define-key goto-map (kbd "M-g") #'consult-line) | ||
413 | (define-key goto-map "l" #'consult-line) | ||
414 | (define-key goto-map "m" #'consult-mark) | ||
415 | (define-key goto-map "k" #'consult-global-mark) | ||
416 | (define-key goto-map "i" #'consult-imenu) | ||
417 | (define-key goto-map "e" #'consult-error) | ||
418 | |||
419 | (global-set-key (kbd "M-y") #'consult-yank-pop) | ||
420 | |||
421 | (define-key help-map "a" #'consult-apropos) | ||
422 | |||
423 | (fset 'multi-occur #'consult-multi-occur) | ||
574 | 424 | ||
575 | This is not *quite* correct yet. For example, scrolling in the margins with a trackpad isn’t picked up (a trackpad sends different mouse events). | 425 | 6. Marginalia |
576 | 426 | ||
577 | (dolist (vec '([left-margin wheel-down] | 427 | (straight-use-package '(marginalia |
578 | [right-margin wheel-down] | 428 | :host github |
579 | [left-margin wheel-up] | 429 | :repo "minad/marginalia" |
580 | [right-margin wheel-up])) | 430 | :branch "main")) |
581 | (bind-key vec #'mwheel-scroll)) | 431 | |
432 | (cuss marginalia-annotators | ||
433 | '(marginalia-annotators-heavy | ||
434 | marginalia-annotators-light)) | ||
435 | |||
436 | (marginalia-mode +1) | ||
582 | 437 | ||
583 | 438 | ||
584 | ## Keyboard | 439 | ## Keyboard |
585 | 440 | ||
586 | 441 | ||
587 | ### Use `ESC` as a cancel key | 442 | ### `ESC` cancels all |
588 | |||
589 | From [link0ff](https://github.com/link0ff/emacs-init). I thought they made a great point that `ESC` isn’t necessary to copy the `META` key on window-systems, which is where I use Emacs, anyway. | ||
590 | |||
591 | (when window-system | ||
592 | (define-key global-map [escape] 'keyboard-escape-quit) | ||
593 | (define-key isearch-mode-map [escape] 'isearch-cancel)) | ||
594 | 443 | ||
444 | (global-set-key (kbd "<escape>") #'keyboard-escape-quit) | ||
595 | 445 | ||
596 | ### Make `C-z` more useful as a prefix key | ||
597 | 446 | ||
598 | Also from link0ff. See the above for a link. | 447 | ### Personal prefix key: `C-z` |
599 | 448 | ||
600 | (defvar acdw/map | 449 | (defvar acdw/map |
601 | (let ((map (make-sparse-keymap)) | 450 | (let ((map (make-sparse-keymap)) |
@@ -604,370 +453,330 @@ Also from link0ff. See the above for a link. | |||
604 | (define-key global-map "\C-z" map) | 453 | (define-key global-map "\C-z" map) |
605 | (define-key map "\C-z" c-z) | 454 | (define-key map "\C-z" c-z) |
606 | map)) | 455 | map)) |
607 | (run-hooks 'acdw/map-defined-hook) | ||
608 | |||
609 | |||
610 | ### Which-key | ||
611 | |||
612 | (straight-use-package 'which-key) | ||
613 | 456 | ||
614 | (which-key-mode +1) | 457 | (run-hooks 'acdw/map-defined-hook) |
615 | |||
616 | |||
617 | ### Bindings | ||
618 | |||
619 | 1. Switch to another window | ||
620 | |||
621 | (bind-key "M-o" #'other-window) | ||
622 | 458 | ||
623 | 459 | ||
624 | # Persistence | 460 | ## Persistence |
625 | 461 | ||
626 | 462 | ||
627 | ## Save history | 463 | ### Minibuffer history |
628 | 464 | ||
629 | (require 'savehist) | 465 | (require 'savehist) |
630 | 466 | ||
631 | (cuss savehist-additional-variables | 467 | (cuss savehist-additional-variables |
632 | '(kill-ring | 468 | '(kill-ring |
633 | search-ring | 469 | search-ring |
634 | regexp-search-ring)) | 470 | regexp-search-ring) |
471 | "Other variables to save alongside the minibuffer history.") | ||
635 | 472 | ||
636 | (cuss savehist-save-minibuffer-history t) | 473 | (cuss history-length t |
474 | "Don't truncate history.") | ||
637 | 475 | ||
638 | (cuss history-length t) | 476 | (cuss history-delete-duplicates t |
639 | 477 | "Delete history duplicates.") | |
640 | (cuss history-delete-duplicates t) | ||
641 | 478 | ||
642 | (savehist-mode +1) | 479 | (savehist-mode +1) |
643 | 480 | ||
644 | 481 | ||
645 | ## Save places in files | 482 | ### File places |
646 | 483 | ||
647 | (require 'saveplace) | 484 | (require 'saveplace) ; this isn't required, but ... I like having it here |
648 | 485 | ||
649 | (cuss save-place-forget-unreadable-files | 486 | (cuss save-place-forget-unreadable-files t |
650 | (not (eq system-type 'windows-nt))) | 487 | "Don't check if files are readable or not.") |
651 | 488 | ||
652 | (save-place-mode 1) | 489 | (save-place-mode +1) |
653 | 490 | ||
654 | 491 | ||
655 | ## Recent files | 492 | ### Recent files |
656 | 493 | ||
657 | (require 'recentf) | 494 | (require 'recentf) |
658 | 495 | ||
659 | (cuss recentf-max-menu-items 100) | 496 | (cuss recentf-max-menu-items 100 |
660 | (cuss recentf-max-saved-items 100) | 497 | "The maximum number of items in the recentf menu.") |
498 | (cuss recentf-max-saved-items nil | ||
499 | "Don't limit the number of recent files.") | ||
661 | 500 | ||
662 | (with-eval-after-load 'no-littering | 501 | (with-eval-after-load 'no-littering |
663 | (add-to-list 'recentf-exclude no-littering-var-directory) | 502 | (add-to-list 'recentf-exclude no-littering-var-directory) |
664 | (add-to-list 'recentf-exclude no-littering-etc-directory)) | 503 | (add-to-list 'recentf-exclude no-littering-etc-directory)) |
665 | 504 | ||
666 | (recentf-mode 1) | 505 | (recentf-mode +1) |
667 | |||
668 | |||
669 | ### Easily navigate recent files | ||
670 | |||
671 | (defun recentf-find-file () | ||
672 | "Find a recent file using `completing-read'." | ||
673 | (interactive) | ||
674 | (let ((file (completing-read "Recent file: " recentf-list nil t))) | ||
675 | (when file | ||
676 | (find-file file)))) | ||
677 | 506 | ||
678 | (global-set-key (kbd "C-x C-r") #'recentf-find-file) | 507 | ;; save the recentf-list every 5 minutes |
508 | (run-at-time nil (* 5 60) 'recentf-save-list) | ||
679 | 509 | ||
680 | 510 | ||
681 | ## Undo | 511 | ## Undo |
682 | 512 | ||
683 | (use-package undo-fu | 513 | (straight-use-package 'undo-fu) |
684 | :bind | 514 | (require 'undo-fu) |
685 | ("C-/" . undo-fu-only-undo) | 515 | |
686 | ("C-?" . undo-fu-only-redo)) | 516 | (global-set-key (kbd "C-/") #'undo-fu-only-undo) |
517 | (global-set-key (kbd "C-?") #'undo-fu-only-redo) | ||
687 | 518 | ||
688 | (straight-use-package 'undo-fu-session) | 519 | (straight-use-package 'undo-fu-session) |
520 | (require 'undo-fu-session) | ||
689 | 521 | ||
690 | (cuss undo-fu-session-incompatible-files | 522 | (cuss undo-fu-session-incompatible-files |
691 | '("/COMMIT_EDITMSG\\'" | 523 | '("/COMMIT_EDITMSG\\'" |
692 | "/git-rebase-todo\\'")) | 524 | "/git-rebase-todo\\'") |
525 | "A list of files that are incompatible with the concept of undo sessions.") | ||
526 | |||
527 | (with-eval-after-load 'no-littering | ||
528 | (let ((dir (no-littering-expand-var-file-name "undos"))) | ||
529 | (make-directory dir 'parents) | ||
530 | (cuss undo-fu-session-directory dir))) | ||
693 | 531 | ||
694 | (global-undo-fu-session-mode +1) | 532 | (global-undo-fu-session-mode +1) |
695 | 533 | ||
696 | 534 | ||
697 | # Editing | 535 | ## Files |
698 | |||
699 | 536 | ||
700 | ## Operate visually on lines | ||
701 | 537 | ||
702 | (global-visual-line-mode +1) | 538 | ### Encoding |
703 | |||
704 | |||
705 | ## Require a final newline | ||
706 | |||
707 | (cuss require-final-newline t) | ||
708 | 539 | ||
540 | 1. UTF-8 | ||
709 | 541 | ||
710 | ## Killing & Yanking | 542 | (set-language-environment "UTF-8") |
543 | (set-terminal-coding-system 'utf-8) | ||
544 | (cuss locale-coding-system 'utf-8) | ||
545 | (set-default-coding-systems 'utf-8) | ||
546 | (set-selection-coding-system 'utf-8) | ||
547 | (prefer-coding-system 'utf-8) | ||
711 | 548 | ||
549 | 2. Convert all files to UNIX-style line endings | ||
712 | 550 | ||
713 | ### Replace selection when typing | 551 | from [Emacs Wiki](https://www.emacswiki.org/emacs/EndOfLineTips). |
714 | 552 | ||
715 | (delete-selection-mode +1) | 553 | (defun ewiki/no-junk-please-were-unixish () |
716 | 554 | "Convert line endings to UNIX, dammit." | |
717 | 555 | (let ((coding-str (symbol-name buffer-file-coding-system))) | |
718 | ### Save existing clipboard text into kill ring before replacing it | 556 | (when (string-match "-\\(?:dos\\|mac\\)$" coding-str) |
719 | 557 | (set-buffer-file-coding-system 'unix)))) | |
720 | (cuss save-interprogram-paste-before-kill t) | 558 | |
721 | 559 | I add it to the `find-file-hook` *and* `before-save-hook` because I | |
722 | 560 | don't want to ever work with anything other than UNIX line endings | |
723 | ### Sync the system clipboard and the kill ring | 561 | ever again. I just don't care. Even Microsoft Notepad can handle |
724 | 562 | UNIX line endings, so I don't want to hear it. | |
725 | (cuss yank-pop-change-selection t) | 563 | |
726 | 564 | (add-hook 'find-file-hook #'ewiki/no-junk-please-were-unixish) | |
727 | 565 | (add-hook 'before-save-hook #'ewiki/no-junk-please-were-unixish) | |
728 | ## So long mode | ||
729 | |||
730 | (when (fboundp 'global-so-long-mode) | ||
731 | (global-so-long-mode)) | ||
732 | |||
733 | |||
734 | ## Multiple cursors | ||
735 | |||
736 | (use-package multiple-cursors | ||
737 | :bind | ||
738 | ("C->" . mc/mark-next-like-this) | ||
739 | ("C-<" . mc/mark-previous-like-this) | ||
740 | ("C-c C-<" . mc/mark-all-like-this)) | ||
741 | 566 | ||
742 | 567 | ||
743 | ## Expand region | 568 | ### Backups |
744 | 569 | ||
745 | (use-package expand-region | 570 | (cuss backup-by-copying 1) |
746 | :bind | 571 | (cuss delete-old-versions -1) |
747 | (("C-=" . er/expand-region) | 572 | (cuss version-control t) |
748 | ("C-+" . er/contract-region))) | 573 | (cuss vc-make-backup-files t) |
574 | |||
575 | (with-eval-after-load 'no-littering | ||
576 | (let ((dir (no-littering-expand-var-file-name "backup"))) | ||
577 | (make-directory dir 'parents) | ||
578 | (cuss backup-directory-alist | ||
579 | `((".*" . ,dir))))) | ||
749 | 580 | ||
750 | 581 | ||
751 | ## Highlight modified regions | 582 | ### Auto-saves |
752 | 583 | ||
753 | (straight-use-package 'goggles) | 584 | (with-eval-after-load 'no-littering |
754 | 585 | (let ((dir (no-littering-expand-var-file-name "autosaves"))) | |
755 | (cuss goggles-pulse nil) | 586 | (make-directory dir 'parents) |
587 | (cuss auto-save-file-name-transforms | ||
588 | `((".*" ,dir t)))) | ||
756 | 589 | ||
757 | (goggles-mode +1) | 590 | (auto-save-visited-mode +1)) |
758 | |||
759 | |||
760 | # Files | ||
761 | 591 | ||
762 | 592 | ||
763 | ## Encoding | 593 | ### Auto-revert buffers to files on disk |
764 | 594 | ||
595 | (global-auto-revert-mode +1) | ||
765 | 596 | ||
766 | ### UTF-8 | ||
767 | 597 | ||
768 | (set-language-environment "UTF-8") | 598 | ### Add a timestamp to files |
769 | (set-terminal-coding-system 'utf-8) | ||
770 | (cuss locale-coding-system 'utf-8) | ||
771 | (set-default-coding-systems 'utf-8) | ||
772 | (set-selection-coding-system 'utf-8) | ||
773 | (prefer-coding-system 'utf-8) | ||
774 | 599 | ||
600 | (add-hook 'before-save-hook #'time-stamp) | ||
775 | 601 | ||
776 | ### Convert all files to UNIX-style line endings | ||
777 | 602 | ||
778 | from [Emacs Wiki](https://www.emacswiki.org/emacs/EndOfLineTips). | 603 | ### Require a final new line |
779 | 604 | ||
780 | (defun ewiki/no-junk-please-were-unixish () | 605 | (cuss require-final-newline t) |
781 | "Convert line endings to UNIX, dammit." | ||
782 | (let ((coding-str (symbol-name buffer-file-coding-system))) | ||
783 | (when (string-match "-\\(?:dos\\|mac\\)$" coding-str) | ||
784 | (set-buffer-file-coding-system 'unix)))) | ||
785 | 606 | ||
786 | I add it to the `find-file-hook` *and* `before-save-hook` because I don't want to ever work with anything other than UNIX line endings ever again. I just don't care. Even Microsoft Notepad can handle UNIX line endings, so I don't want to hear it. | ||
787 | 607 | ||
788 | (add-hook 'find-file-hook #'ewiki/no-junk-please-were-unixish) | 608 | ## Text editing |
789 | (add-hook 'before-save-hook #'ewiki/no-junk-please-were-unixish) | ||
790 | 609 | ||
791 | 610 | ||
792 | ## Backups | 611 | ### Operate visually on lines |
793 | 612 | ||
794 | (cuss backup-by-copying 1) | 613 | (global-visual-line-mode +1) |
795 | (cuss delete-old-versions -1) | ||
796 | (cuss version-control t) | ||
797 | (cuss vc-make-backup-files t) | ||
798 | 614 | ||
799 | 615 | ||
800 | ## Auto-saves | 616 | ### Stay snappy with long-lined files |
801 | 617 | ||
802 | (auto-save-visited-mode 1) | 618 | (when (fboundp 'global-so-long-mode) |
619 | (global-so-long-mode +1)) | ||
803 | 620 | ||
804 | 621 | ||
805 | ## Revert files | 622 | ### Killing & Yanking |
806 | 623 | ||
807 | (cuss auto-revert-verbose nil) | 624 | 1. Replace selection when typing |
808 | (global-auto-revert-mode +1) | ||
809 | 625 | ||
626 | (delete-selection-mode +1) | ||
810 | 627 | ||
811 | ## Add a timestamp to files | 628 | 2. Work better with the system clipboard |
812 | 629 | ||
813 | (add-hook 'before-save-hook #'time-stamp) | 630 | (cuss save-interprogram-paste-before-kill t |
631 | "Save existing clipboard text into the kill ring before | ||
632 | replacing it.") | ||
633 | |||
634 | (cuss yank-pop-change-selection t | ||
635 | "Update the X selection when rotating the kill ring.") | ||
814 | 636 | ||
815 | 637 | ||
816 | # Programming | 638 | ### Searching & Replacing |
817 | 639 | ||
640 | (straight-use-package 'visual-regexp) | ||
641 | (require 'visual-regexp) | ||
642 | |||
643 | (with-eval-after-load 'visual-regexp | ||
644 | (global-set-key (kbd "M-C-%") #'vr/query-replace)) | ||
818 | 645 | ||
819 | ## Which function are we in? | ||
820 | 646 | ||
821 | (which-function-mode +1) | 647 | # Programming |
822 | 648 | ||
823 | 649 | ||
824 | ## Parentheses | 650 | ## Parentheses |
825 | 651 | ||
826 | 652 | ||
827 | ### Show parentheses | ||
828 | |||
829 | (cuss show-paren-delay 0 "Show matching parens immediately.") | ||
830 | (cuss show-paren-style 'mixed | ||
831 | "Show parenthesis, or whole expression, depending on visibility.") | ||
832 | (cuss show-paren-when-point-in-periphery t | ||
833 | "Show paren when point is near-to paren.") | ||
834 | (cuss show-paren-when-point-inside-paren t | ||
835 | "Show surrounding parens.") | ||
836 | |||
837 | (add-hook 'prog-mode-hook #'show-paren-mode) | ||
838 | |||
839 | |||
840 | ### Smart parentheses | 653 | ### Smart parentheses |
841 | 654 | ||
842 | (straight-use-package 'smartparens) | 655 | (straight-use-package 'smartparens) |
843 | (require 'smartparens-config) | 656 | (require 'smartparens-config) |
844 | 657 | ||
845 | (show-smartparens-global-mode +1) | 658 | ;; replace show-paren |
846 | (add-to-list 'sp-ignore-modes-list 'org-mode) | ||
847 | 659 | ||
660 | (cuss sp-show-pair-delay 0 | ||
661 | "Don't delay before showing the pairs.") | ||
662 | (cuss sp-show-pair-from-inside t | ||
663 | "Highlight the enclosing pair when immediately inside.") | ||
664 | |||
665 | (add-hook 'prog-mode-hook #'show-smartparens-mode +1) | ||
666 | |||
667 | ;; enable strict smartparens in prog mode | ||
848 | (add-hook 'prog-mode-hook #'smartparens-strict-mode) | 668 | (add-hook 'prog-mode-hook #'smartparens-strict-mode) |
849 | 669 | ||
850 | 670 | ||
851 | ## Indenting | 671 | ## Indent aggressively |
852 | 672 | ||
853 | (straight-use-package 'aggressive-indent) | 673 | (straight-use-package 'aggressive-indent) |
854 | 674 | ||
855 | (global-aggressive-indent-mode +1) | 675 | (global-aggressive-indent-mode +1) |
856 | 676 | ||
857 | 677 | ||
858 | ## Completion | 678 | ## Language-specific packages |
859 | |||
860 | (use-package company | ||
861 | :custom | ||
862 | (company-idle-delay 0.1) | ||
863 | (company-minimum-prefix-length 3) | ||
864 | |||
865 | :init | ||
866 | (defun acdw/company-complete-common-or-cycle+1 () | ||
867 | (interactive) | ||
868 | (company-complete-common-or-cycle +1)) | ||
869 | |||
870 | (defun acdw/company-complete-common-or-cycle-1 () | ||
871 | (interactive) | ||
872 | (company-complete-common-or-cycle -1)) | ||
873 | |||
874 | :bind | ||
875 | (:map company-active-map | ||
876 | ("C-n" . acdw/company-complete-common-or-cycle+1) | ||
877 | ("C-p" . acdw/company-complete-common-or-cycle-1)) | ||
878 | |||
879 | :hook | ||
880 | (prog-mode-hook . company-mode)) | ||
881 | |||
882 | (use-package company-prescient | ||
883 | :hook | ||
884 | (company-mode-hook . company-prescient-mode)) | ||
885 | |||
886 | ;; this comes with company-quickhelp, so.... | ||
887 | |||
888 | (use-package company-posframe | ||
889 | :after (company) | ||
890 | :config | ||
891 | (company-posframe-mode +1)) | ||
892 | |||
893 | |||
894 | ## Languages | ||
895 | |||
896 | |||
897 | ### Lua | ||
898 | |||
899 | (use-package lua-mode | ||
900 | :mode "\\.lua\\'" | ||
901 | :interpreter "lua") | ||
902 | |||
903 | |||
904 | ### Fennel | ||
905 | |||
906 | (use-package fennel-mode | ||
907 | :mode "\\.fnl\\'") | ||
908 | 679 | ||
909 | 680 | ||
910 | ### Emacs lisp | 681 | ### Emacs lisp |
911 | 682 | ||
912 | (cuss eval-expression-print-length nil | 683 | (cuss eval-expression-print-length nil |
913 | "Don't truncate printed expressions by length.") | 684 | "Don't truncate printed expressions by length.") |
914 | (cuss eval-expression-print-level nil | 685 | (cuss eval-expression-print-level nil |
915 | "Don't truncate printed expressions by level.") | 686 | "Don't truncate printed expressions by level.") |
916 | 687 | ||
917 | 688 | ||
918 | # Writing | 689 | # Writing |
919 | 690 | ||
920 | 691 | ||
921 | ## Visual Fill Column | 692 | ## Visual fill column |
693 | |||
694 | |||
695 | ### Fix scrolling in margins | ||
696 | |||
697 | This has to be done *before* loading the package. It's included in `visual-fill-column`, too, but for some reason isn't loaded there. | ||
698 | |||
699 | (global-set-key [right-margin mouse-1] (global-key-binding [mouse-1])) ; #'mouse-set-point | ||
700 | (global-set-key [right-margin mouse-2] (global-key-binding [mouse-2])) ; #'mouse-yank-primary | ||
701 | (global-set-key [right-margin mouse-3] (global-key-binding [mouse-3])) ; #'mouse-save-then-kill | ||
702 | (global-set-key [right-margin drag-mouse-1] #'ignore) | ||
703 | (global-set-key [right-margin drag-mouse-2] #'ignore) | ||
704 | (global-set-key [right-margin drag-mouse-3] #'ignore) | ||
705 | (global-set-key [right-margin double-mouse-1] #'ignore) | ||
706 | (global-set-key [right-margin double-mouse-2] #'ignore) | ||
707 | (global-set-key [right-margin double-mouse-3] #'ignore) | ||
708 | (global-set-key [right-margin triple-mouse-1] #'ignore) | ||
709 | (global-set-key [right-margin triple-mouse-2] #'ignore) | ||
710 | (global-set-key [right-margin triple-mouse-3] #'ignore) | ||
711 | (global-set-key [left-margin mouse-1] (global-key-binding [mouse-1])) ; #'mouse-set-point | ||
712 | (global-set-key [left-margin mouse-2] (global-key-binding [mouse-2])) ; #'mouse-yank-primary | ||
713 | (global-set-key [left-margin mouse-3] (global-key-binding [mouse-3])) ; #'mouse-save-then-kill | ||
714 | (global-set-key [left-margin drag-mouse-1] #'ignore) | ||
715 | (global-set-key [left-margin drag-mouse-2] #'ignore) | ||
716 | (global-set-key [left-margin drag-mouse-3] #'ignore) | ||
717 | (global-set-key [left-margin double-mouse-1] #'ignore) | ||
718 | (global-set-key [left-margin double-mouse-2] #'ignore) | ||
719 | (global-set-key [left-margin double-mouse-3] #'ignore) | ||
720 | (global-set-key [left-margin triple-mouse-1] #'ignore) | ||
721 | (global-set-key [left-margin triple-mouse-2] #'ignore) | ||
722 | (global-set-key [left-margin triple-mouse-3] #'ignore) | ||
723 | |||
724 | (mouse-wheel-mode +1) | ||
725 | |||
726 | (when (bound-and-true-p mouse-wheel-mode) | ||
727 | (global-set-key [right-margin mouse-wheel-down-event] #'mwheel-scroll) | ||
728 | (global-set-key [right-margin mouse-wheel-up-event] #'mwheel-scroll) | ||
729 | (global-set-key [right-margin wheel-down] #'mwheel-scroll) | ||
730 | (global-set-key [right-margin wheel-up] #'mwheel-scroll) | ||
731 | (global-set-key [left-margin mouse-wheel-down-event] #'mwheel-scroll) | ||
732 | (global-set-key [left-margin mouse-wheel-up-event] #'mwheel-scroll) | ||
733 | (global-set-key [left-margin wheel-down] #'mwheel-scroll) | ||
734 | (global-set-key [left-margin wheel-up] #'mwheel-scroll)) | ||
735 | |||
736 | |||
737 | ### Load the package | ||
922 | 738 | ||
923 | (straight-use-package 'visual-fill-column) | 739 | (straight-use-package 'visual-fill-column) |
924 | 740 | ||
925 | (cuss split-window-preferred-function | 741 | (cuss visual-fill-column-center-text nil |
926 | 'visual-fill-column-split-window-sensibly) | 742 | "Whether to center the text in the frame.") |
927 | (cuss visual-fill-column-center-text t) | 743 | |
928 | (cuss fill-column 80) | 744 | (cuss fill-column 84 |
745 | "Width of fill-column, and thus, visual-fill-column.") | ||
929 | 746 | ||
930 | (advice-add 'text-scale-adjust | 747 | (advice-add 'text-scale-adjust |
931 | :after #'visual-fill-column-adjust) | 748 | :after #'visual-fill-column-adjust) |
932 | 749 | ||
933 | (add-hook 'text-mode-hook #'visual-fill-column-mode) | 750 | (global-visual-fill-column-mode +1) |
751 | |||
934 | 752 | ||
753 | ## Typographical niceties | ||
935 | 754 | ||
936 | ## Type nice-looking quote-type marks | 755 | |
756 | ### Typo mode | ||
937 | 757 | ||
938 | (straight-use-package 'typo) | 758 | (straight-use-package 'typo) |
939 | 759 | ||
940 | (add-hook 'text-mode-hook #'typo-mode) | 760 | (add-hook 'text-mode-hook #'typo-mode) |
941 | 761 | ||
942 | 762 | ||
943 | ## Insert *kaomoji* | ||
944 | |||
945 | (use-package insert-kaomoji | ||
946 | :bind | ||
947 | ("C-x 8 k" . insert-kaomoji)) | ||
948 | |||
949 | |||
950 | # Applications | 763 | # Applications |
951 | 764 | ||
952 | 765 | ||
953 | ## Magit | ||
954 | |||
955 | (use-package magit | ||
956 | :bind | ||
957 | ("C-z g" . magit-status)) | ||
958 | |||
959 | |||
960 | ## Org mode | 766 | ## Org mode |
961 | 767 | ||
962 | I’ve put org mode under Applications, as opposed to Writing, because it’s more generally-applicable than that. | 768 | I’ve put org mode under Applications, as opposed to Writing, because it’s more generally-applicable than that. |
963 | 769 | ||
770 | |||
771 | ### Basics | ||
772 | |||
964 | (straight-use-package 'org) | 773 | (straight-use-package 'org) |
965 | 774 | ||
966 | (with-eval-after-load 'org | 775 | (with-eval-after-load 'org |
967 | (require 'org-tempo) | 776 | (require 'org-tempo) |
968 | (require 'ox-md) | 777 | (require 'ox-md) |
969 | (bind-key "M-n" #'outline-next-visible-heading 'org-mode-map) | 778 | (define-key org-mode-map (kbd "M-n") #'outline-next-visible-heading) |
970 | (bind-key "M-p" #'outline-previous-visible-heading 'org-mode-map)) | 779 | (define-key org-mode-map (kbd "M-p") #'outline-previous-visible-heading)) |
971 | 780 | ||
972 | (cuss org-hide-emphasis-markers t) | 781 | (cuss org-hide-emphasis-markers t) |
973 | (cuss org-fontify-done-headline t) | 782 | (cuss org-fontify-done-headline t) |
@@ -989,17 +798,8 @@ I’ve put org mode under Applications, as opposed to Writing, because it’s m | |||
989 | (if (and (stringp org-agenda-files) | 798 | (if (and (stringp org-agenda-files) |
990 | (not (file-exists-p org-agenda-files))) | 799 | (not (file-exists-p org-agenda-files))) |
991 | (with-temp-buffer (write-file org-agenda-files))) | 800 | (with-temp-buffer (write-file org-agenda-files))) |
992 | 801 | ||
993 | 802 | (define-key acdw/map (kbd "C-a") #'org-agenda) | |
994 | ### Make bullets look like bullets | ||
995 | |||
996 | (font-lock-add-keywords | ||
997 | 'org-mode | ||
998 | '(("^ *\\([-+]\\) " | ||
999 | (0 (prog1 () | ||
1000 | (compose-region (match-beginning 1) | ||
1001 | (match-end 1) | ||
1002 | "•")))))) | ||
1003 | 803 | ||
1004 | 804 | ||
1005 | ### [A better return in Org mode](http://kitchingroup.cheme.cmu.edu/blog/2017/04/09/A-better-return-in-org-mode/) | 805 | ### [A better return in Org mode](http://kitchingroup.cheme.cmu.edu/blog/2017/04/09/A-better-return-in-org-mode/) |
@@ -1076,7 +876,7 @@ I’ve put org mode under Applications, as opposed to Writing, because it’s m | |||
1076 | 'scimax/org-return) | 876 | 'scimax/org-return) |
1077 | 877 | ||
1078 | 878 | ||
1079 | ### Insert blank lines | 879 | ### Insert blank lines around headers |
1080 | 880 | ||
1081 | from [unpackaged.el](https://github.com/alphapapa/unpackaged.el#ensure-blank-lines-between-headings-and-before-contents). | 881 | from [unpackaged.el](https://github.com/alphapapa/unpackaged.el#ensure-blank-lines-between-headings-and-before-contents). |
1082 | 882 | ||
@@ -1126,172 +926,6 @@ from [unpackaged.el](https://github.com/alphapapa/unpackaged.el#ensure-blank-lin | |||
1126 | (add-hook 'before-save-hook #'cribbed/org-mode-fix-blank-lines) | 926 | (add-hook 'before-save-hook #'cribbed/org-mode-fix-blank-lines) |
1127 | 927 | ||
1128 | 928 | ||
1129 | ## Elpher | ||
1130 | |||
1131 | (use-package elpher | ||
1132 | :straight (elpher | ||
1133 | :repo "git://thelambdalab.xyz/elpher.git" | ||
1134 | :branch "patch_multiple_buffers") | ||
1135 | |||
1136 | :custom | ||
1137 | (elpher-ipv4-always t) | ||
1138 | |||
1139 | :custom-face | ||
1140 | (elpher-gemini-heading1 | ||
1141 | ((t (:inherit (modus-theme-heading-1))))) | ||
1142 | (elpher-gemini-heading2 | ||
1143 | ((t (:inherit (modus-theme-heading-2))))) | ||
1144 | (elpher-gemini-heading3 | ||
1145 | ((t (:inherit (modus-theme-heading-3))))) | ||
1146 | |||
1147 | :config | ||
1148 | (defun elpher:eww-browse-url (original url &optional new-window) | ||
1149 | "Handle gemini/gopher links with eww." | ||
1150 | (cond ((string-match-p "\\`\\(gemini\\|gopher\\)://" url) | ||
1151 | (require 'elpher) | ||
1152 | (elpher-go url)) | ||
1153 | (t (funcall original url new-window)))) | ||
1154 | (advice-add 'eww-browse-url :around 'elpher:eww-browse-url) | ||
1155 | |||
1156 | :bind (:map elpher-mode-map | ||
1157 | ("n" . elpher-next-link) | ||
1158 | ("p" . elpher-prev-link) | ||
1159 | ("o" . elpher-follow-current-link) | ||
1160 | ("G" . elpher-go-current)) | ||
1161 | |||
1162 | :hook | ||
1163 | (elpher-mode-hook . visual-fill-column-mode)) | ||
1164 | |||
1165 | |||
1166 | ### Gemini mode | ||
1167 | |||
1168 | (use-package gemini-mode | ||
1169 | :straight (gemini-mode | ||
1170 | :repo "https://git.carcosa.net/jmcbray/gemini.el.git") | ||
1171 | |||
1172 | :mode "\\.\\(gemini|gmi\\)\\'" | ||
1173 | |||
1174 | :custom-face | ||
1175 | (gemini-heading-face-1 | ||
1176 | ((t (:inherit (elpher-gemini-heading1))))) | ||
1177 | (gemini-heading-face2 | ||
1178 | ((t (:inherit (elpher-gemini-heading2))))) | ||
1179 | (gemini-heading-face3 | ||
1180 | ((t (:inherit (elpher-gemini-heading3))))) | ||
1181 | |||
1182 | :init | ||
1183 | (defun acdw/setup-gemini-mode () | ||
1184 | (visual-fill-column-mode 1) | ||
1185 | (variable-pitch-mode -1)) | ||
1186 | |||
1187 | :hook | ||
1188 | (gemini-mode-hook . acdw/setup-gemini-mode)) | ||
1189 | |||
1190 | |||
1191 | ### Gemini write | ||
1192 | |||
1193 | (use-package gemini-write | ||
1194 | :straight (gemini-write | ||
1195 | :repo "https://alexschroeder.ch/cgit/gemini-write")) | ||
1196 | |||
1197 | |||
1198 | ### Ox-gemini | ||
1199 | |||
1200 | (use-package ox-gemini | ||
1201 | :straight (ox-gemini | ||
1202 | :repo "https://git.sr.ht/~abrahms/ox-gemini" | ||
1203 | :branch "main")) | ||
1204 | |||
1205 | |||
1206 | ## Pastebin | ||
1207 | |||
1208 | (straight-use-package '0x0) | ||
1209 | |||
1210 | (cuss 0x0-default-service 'ttm) | ||
1211 | |||
1212 | |||
1213 | ## RSS | ||
1214 | |||
1215 | (use-package newsticker | ||
1216 | :custom | ||
1217 | (newsticker-url-list | ||
1218 | ;; LABEL URL [START-TIME] [INERVAL] [WGET-ARGUMENTS] | ||
1219 | '(("wsinatra" "http://lambdacreate.com/static/feed.rss") | ||
1220 | ("elioat" "https://eli.li/feed.rss") | ||
1221 | ("ACDW" "https://www.acdw.net/atom.xml") | ||
1222 | ("june" "https://text.causal.agency/feed.atom") | ||
1223 | ("kylie - notes" "https://www.somas.is/notes.atom") | ||
1224 | ("kylie - rhizome" "https://www.somas.is/rhizome.atom") | ||
1225 | ("brennan" "https://p1k3.com/all.xml") | ||
1226 | ("Planet Emacs" "https://planet.emacslife.com/atom.xml") | ||
1227 | ("nullprogram, Chris Wellons" "https://nullprogram.com/feed/") | ||
1228 | ("Malleable Systems" "https://malleable.systems/blog/index.xml")) | ||
1229 | ) | ||
1230 | :hook | ||
1231 | (newsticker-treeview-item-mode-hook . visual-fill-column-mode)) | ||
1232 | |||
1233 | |||
1234 | ## Web browsing | ||
1235 | |||
1236 | |||
1237 | ### Open youtube links in mpv | ||
1238 | |||
1239 | from [karthinks](https://karthinks.com/software/more-batteries-included-with-emacs/#regexp-builder--m-x-re-builder). | ||
1240 | |||
1241 | (require 'browse-url) | ||
1242 | |||
1243 | (when (executable-find "mpv") | ||
1244 | (defun browse-url-mpv (url &optional single) | ||
1245 | (start-process "mpv" nil (if single "mpv" "umpv") | ||
1246 | (shell-quote-wildcard-pattern url))) | ||
1247 | |||
1248 | (defun browse-url-at-point-mpv (&optional single) | ||
1249 | "Open a link in mpv." | ||
1250 | (interactive "P") | ||
1251 | (let ((browse-url-browser-function | ||
1252 | (if single | ||
1253 | (lambda | ||
1254 | (url &optional _new-window) | ||
1255 | (browse-url-mpv url t)) | ||
1256 | #'browse-url-mpv))) | ||
1257 | (browse-url-at-point))) | ||
1258 | |||
1259 | (cuss browse-url-browser-function | ||
1260 | '(("https?:\\/\\/www\\.youtu\\.*be." . browse-url-mpv) | ||
1261 | ("." . browse-url-generic)))) | ||
1262 | |||
1263 | |||
1264 | ## Reading e-books | ||
1265 | |||
1266 | (use-package nov | ||
1267 | :mode ("\\.epub\\'" . nov-mode) | ||
1268 | :init | ||
1269 | (defun acdw/setup-nov-mode () | ||
1270 | (visual-line-mode +1) | ||
1271 | (visual-fill-column-mode +1) | ||
1272 | (variable-pitch-mode +1) | ||
1273 | (setq cursor-type nil)) | ||
1274 | :config | ||
1275 | (cuss nov-text-width t) | ||
1276 | :hook | ||
1277 | (nov-mode-hook . acdw/setup-nov-mode)) | ||
1278 | |||
1279 | |||
1280 | ## Eshell | ||
1281 | |||
1282 | (when (executable-find "bash") | ||
1283 | (straight-use-package 'bash-completion)) | ||
1284 | |||
1285 | (when (executable-find "fish") | ||
1286 | (straight-use-package 'fish-completion) | ||
1287 | (require 'fish-completion) | ||
1288 | (cuss fish-completion-fallback-on-bash-p (executable-find "bash")) | ||
1289 | (global-fish-completion-mode +1) | ||
1290 | |||
1291 | (straight-use-package 'fish-mode) | ||
1292 | (add-to-list 'auto-mode-alist '("\\.fish\\'" . fish-mode))) | ||
1293 | |||
1294 | |||
1295 | # Appendices | 929 | # Appendices |
1296 | 930 | ||
1297 | 931 | ||
@@ -1300,40 +934,24 @@ from [karthinks](https://karthinks.com/software/more-batteries-included-with-ema | |||
1300 | 934 | ||
1301 | ### init.el | 935 | ### init.el |
1302 | 936 | ||
1303 | ;; init.el -*- lexical-binding: t -*- | 937 | I realized I didn’t need `early-init.el`, since it really only set `load-prefer-newer`. So I’ve set that here, and wrapped the actual loading of config in a `let*` form that speeds up init, and loads the newer of either `config.org` or `config.el`. |
1304 | |||
1305 | 1. Speed up init | ||
1306 | |||
1307 | (setq gc-cons-threshold most-positive-fixnum) | ||
1308 | (defvar old-file-name-handler file-name-handler-alist) | ||
1309 | (setq file-name-handler-alist nil) | ||
1310 | |||
1311 | 2. Load config | ||
1312 | |||
1313 | inspired by [Protesilaos Stavrou](https://protesilaos.com/dotemacs/#h:584c3604-55a1-49d0-9c31-abe46cb1f028). | ||
1314 | |||
1315 | (let* ((conf (expand-file-name "config" | ||
1316 | user-emacs-directory)) | ||
1317 | (conf-el (concat conf ".el")) | ||
1318 | (conf-org (concat conf ".org"))) | ||
1319 | (unless (and (file-newer-than-file-p conf-el conf-org) | ||
1320 | (load conf 'no-error)) | ||
1321 | (require 'org) | ||
1322 | (org-babel-load-file conf-org))) | ||
1323 | 938 | ||
1324 | 3. Reset for normal operation | 939 | ;; init.el -*- lexical-binding: t -*- |
1325 | |||
1326 | (setq gc-cons-threshold 16777216 ; 16mb | ||
1327 | gc-cons-percentage 0.1 | ||
1328 | file-name-handler-alist old-file-name-handler) | ||
1329 | |||
1330 | |||
1331 | ### early-init.el | ||
1332 | |||
1333 | ;; early-init.el -*- lexical-binding: t; no-byte-compile: t; -*- | ||
1334 | 940 | ||
1335 | (setq load-prefer-newer t) | 941 | (setq load-prefer-newer t) |
1336 | (setq frame-inhibit-implied-resize t) | 942 | |
943 | (let* (;; Speed up init | ||
944 | (gc-cons-threshold most-positive-fixnum) | ||
945 | (file-name-handler-alist nil) | ||
946 | ;; Config file names | ||
947 | (conf (expand-file-name "config" | ||
948 | user-emacs-directory)) | ||
949 | (conf-el (concat conf ".el")) | ||
950 | (conf-org (concat conf ".org"))) | ||
951 | (unless (and (file-newer-than-file-p conf-el conf-org) | ||
952 | (load conf 'no-error)) | ||
953 | (require 'org) | ||
954 | (org-babel-load-file conf-org))) | ||
1337 | 955 | ||
1338 | 956 | ||
1339 | ## Ease tangling and loading of Emacs' init | 957 | ## Ease tangling and loading of Emacs' init |
@@ -1350,34 +968,33 @@ from [karthinks](https://karthinks.com/software/more-batteries-included-with-ema | |||
1350 | (when (file-newer-than-file-p config (expand-file-name | 968 | (when (file-newer-than-file-p config (expand-file-name |
1351 | "README.md" | 969 | "README.md" |
1352 | user-emacs-directory)) | 970 | user-emacs-directory)) |
971 | (message "Exporting README.md...") | ||
1353 | (require 'ox-md) | 972 | (require 'ox-md) |
1354 | (org-md-export-to-markdown)) | 973 | (with-demoted-errors "Problem exporting README.md: %S" |
974 | (org-md-export-to-markdown))) | ||
1355 | ;; tangle config.org | 975 | ;; tangle config.org |
1356 | (when (file-newer-than-file-p config (expand-file-name | 976 | (when (file-newer-than-file-p config (expand-file-name |
1357 | "config.el" | 977 | "config.el" |
1358 | user-emacs-directory)) | 978 | user-emacs-directory)) |
979 | (message "Tangling config.org...") | ||
1359 | (require 'org) | 980 | (require 'org) |
1360 | (let ((inits (org-babel-tangle))) | 981 | (let ((inits (org-babel-tangle))) |
1361 | ;; byte-compile resulting files | 982 | ;; byte-compile resulting files |
983 | (message "Byte-compiling...") | ||
1362 | (dolist (f inits) | 984 | (dolist (f inits) |
1363 | (when (string-match "\\.el\\'" f) | 985 | (when (string-match "\\.el\\'" f) |
1364 | (byte-compile-file f (not disable-load))))))))))) | 986 | (byte-compile-file f (not disable-load))))))))))) |
1365 | 987 | ||
1366 | 988 | ||
1367 | ### Add a hook to tangle when quitting | ||
1368 | |||
1369 | (defun acdw/refresh-emacs-no-load () | ||
1370 | (refresh-emacs 'disable-load)) | ||
1371 | |||
1372 | (add-hook 'kill-emacs-hook #'acdw/refresh-emacs-no-load) | ||
1373 | |||
1374 | |||
1375 | ## Ancillary scripts | 989 | ## Ancillary scripts |
1376 | 990 | ||
1377 | 991 | ||
1378 | ### emacsdc | 992 | ### emacsdc |
1379 | 993 | ||
1380 | Here’s a wrapper script that’ll start `emacs –daemon` if there isn’t one, and then launch `emacsclient` with the arguments. I’d recommend installing with either `ln -s bin/emacsdc $HOME/.local/bin/`, or adding `$HOME/.local/bin` to your `$PATH`. | 994 | Here's a wrapper script that'll start `emacs –daemon` if there isn't |
995 | one, and then launch `emacsclient` with the arguments. I'd recommend | ||
996 | installing with either `ln -s bin/emacsdc $HOME/.local/bin/`, or | ||
997 | adding `$HOME/.local/bin` to your `$PATH`. | ||
1381 | 998 | ||
1382 | if ! emacsclient -nc "$@" 2>/dev/null; then | 999 | if ! emacsclient -nc "$@" 2>/dev/null; then |
1383 | emacs --daemon | 1000 | emacs --daemon |
@@ -1385,6 +1002,14 @@ Here’s a wrapper script that’ll start `emacs –daemon` if there isn’t one | |||
1385 | fi | 1002 | fi |
1386 | 1003 | ||
1387 | 1004 | ||
1005 | ### Emacs.cmd | ||
1006 | |||
1007 | Here’s a wrapper script that’ll run Emacs on Windows, with a custom `$HOME`. I have mine setup like this: Emacs is downloaded from [the GNU mirror](https://mirrors.tripadvisor.com/gnu/emacs/windows/emacs-27/emacs-27.1-x86_64.zip) and unzipped to `~/Downloads/emacs/`. `Emacs.cmd` sets `$HOME` to `~/Downloads/emacshome/`, which is where `.emacs.d` is, and whatever else I might want to throw in there. | ||
1008 | |||
1009 | set HOME=%~dp0..\..\emacshome | ||
1010 | "%~dp0runemacs.exe" %* | ||
1011 | |||
1012 | |||
1388 | ## License | 1013 | ## License |
1389 | 1014 | ||
1390 | Copyright © 2020 Case Duckworth <acdw@acdw.net> | 1015 | Copyright © 2020 Case Duckworth <acdw@acdw.net> |
diff --git a/config.org b/config.org index 8446fe9..2b9671d 100644 --- a/config.org +++ b/config.org | |||
@@ -1,435 +1,350 @@ | |||
1 | #+TITLE: Emacs, emacs, emacs | 1 | #+TITLE: Emacs, emacs, emacs |
2 | #+AUTHOR: Case Duckworth | 2 | #+AUTHOR: Case Duckworth |
3 | #+PROPERTY: header-args :tangle config.el :comments both :mkdirp yes | 3 | #+PROPERTY: header-args :tangle config.el :comments both :mkdirp yes |
4 | #+STARTUP: | ||
4 | #+EXPORT_FILE_NAME: README.md | 5 | #+EXPORT_FILE_NAME: README.md |
5 | #+OPTIONS: toc:nil | 6 | #+OPTIONS: toc:nil |
6 | #+BANKRUPTCY_COUNT: 3 | 7 | #+BANKRUPTCY_COUNT: 3.2 |
7 | #+Time-stamp: <2020-12-23 20:27:53 acdw> | 8 | #+Time-stamp: <2020-12-30 23:33:02 acdw> |
8 | 9 | ||
9 | Let’s configure Emacs using Org mode, they said. It’ll be fun, they said. | 10 | Why the hell not, let’s do this again. |
10 | 11 | ||
11 | * Pave the way | 12 | * Basics |
13 | |||
14 | ** About me | ||
15 | |||
16 | #+BEGIN_SRC emacs-lisp | ||
17 | (setq user-full-name "Case Duckworth" | ||
18 | user-mail-address "acdw@acdw.net") | ||
19 | #+END_SRC | ||
12 | 20 | ||
13 | ** Correct =exec-path= | 21 | ** Correct =exec-path= |
14 | 22 | ||
15 | #+begin_src emacs-lisp | 23 | Straight depends on Git, so I need to tell Emacs where different paths are. |
16 | (let ((win-downloads "c:/Users/aduckworth/Downloads")) | 24 | |
17 | (dolist (path (list | 25 | #+BEGIN_SRC emacs-lisp |
18 | ;; Linux | 26 | (let ((win-downloads "c:/Users/aduckworth/Downloads")) |
19 | (expand-file-name "bin" | 27 | (dolist (path (list |
20 | user-emacs-directory) | 28 | ;; Linux |
21 | (expand-file-name "~/bin") | 29 | (expand-file-name "bin" |
22 | (expand-file-name "~/.local/bin") | 30 | user-emacs-directory) |
23 | (expand-file-name "~/Scripts") | 31 | (expand-file-name "~/bin") |
24 | ;; Windows | 32 | (expand-file-name "~/.local/bin") |
25 | (expand-file-name "emacs/bin" | 33 | (expand-file-name "~/Scripts") |
26 | win-downloads) | 34 | ;; Windows |
27 | (expand-file-name "m/usr/bin" | 35 | (expand-file-name "emacs/bin" |
28 | win-downloads) | 36 | win-downloads) |
29 | (expand-file-name "m/mingw64/bin" | 37 | (expand-file-name "m/usr/bin" |
30 | win-downloads) | 38 | win-downloads) |
31 | (expand-file-name "PortableGit/bin" | 39 | (expand-file-name "m/mingw64/bin" |
32 | win-downloads) | 40 | win-downloads) |
33 | (expand-file-name "PortableGit/usr/bin" | 41 | (expand-file-name "PortableGit/bin" |
34 | win-downloads))) | 42 | win-downloads) |
35 | (when (file-exists-p path) | 43 | (expand-file-name "PortableGit/usr/bin" |
36 | (add-to-list 'exec-path path)))) | 44 | win-downloads))) |
37 | #+end_src | 45 | (when (file-exists-p path) |
46 | (add-to-list 'exec-path path)))) | ||
47 | #+END_SRC | ||
38 | 48 | ||
39 | ** Package management | 49 | ** Package management |
40 | 50 | ||
41 | *** Straight.el | 51 | *** Straight.el |
42 | 52 | ||
43 | Since for whatever reason, Straight can't bootstrap itself on Windows | 53 | Straight can't bootstrap itself on Windows, so I've wrapped the |
44 | -- I've wrapped it in a function here and added the direct git command | 54 | bootstrap code from straight's repo in a function. |
45 | when it errors. | ||
46 | |||
47 | #+begin_src emacs-lisp | ||
48 | (defun acdw/bootstrap-straight () | ||
49 | (defvar bootstrap-version) | ||
50 | (let ((bootstrap-file | ||
51 | (expand-file-name | ||
52 | "straight/repos/straight.el/bootstrap.el" | ||
53 | user-emacs-directory)) | ||
54 | (bootstrap-version 5)) | ||
55 | (unless (file-exists-p bootstrap-file) | ||
56 | (with-current-buffer | ||
57 | (url-retrieve-synchronously | ||
58 | (concat "https://raw.githubusercontent.com/" | ||
59 | "raxod502/straight.el/develop/install.el") | ||
60 | 'silent 'inhibit-cookies) | ||
61 | (goto-char (point-max)) | ||
62 | (eval-print-last-sexp))) | ||
63 | (load bootstrap-file nil 'nomessage))) | ||
64 | |||
65 | (unless (ignore-errors (acdw/bootstrap-straight)) | ||
66 | (message "Straight.el didn't bootstrap correctly. Cloning directly...") | ||
67 | (call-process "git" nil (get-buffer-create "*bootstrap-straight-messages*") nil | ||
68 | "clone" | ||
69 | "https://github.com/raxod502/straight.el" | ||
70 | (expand-file-name "straight/repos/straight.el" | ||
71 | user-emacs-directory)) | ||
72 | (acdw/bootstrap-straight)) | ||
73 | #+end_src | ||
74 | |||
75 | ** Customize variables | ||
76 | |||
77 | *** Put customizations in a separate file | ||
78 | |||
79 | #+begin_src emacs-lisp | ||
80 | (setq custom-file | ||
81 | (expand-file-name "custom.el" user-emacs-directory)) | ||
82 | #+end_src | ||
83 | |||
84 | *** A macro for ease of customization | ||
85 | |||
86 | #+begin_src emacs-lisp | ||
87 | (defmacro cuss (var val &optional docstring) | ||
88 | "Basically `:custom' from `use-package', broken out." | ||
89 | (declare (indent 2) | ||
90 | (doc-string 3)) | ||
91 | `(funcall (or (get ',var 'custom-set) #'set-default) | ||
92 | ',var ,val)) | ||
93 | #+end_src | ||
94 | |||
95 | ** Keep a tidy =~/.emacs= | ||
96 | 55 | ||
97 | #+begin_src emacs-lisp | 56 | #+BEGIN_SRC emacs-lisp |
98 | (straight-use-package 'no-littering) | 57 | (defun acdw/bootstrap-straight () |
99 | 58 | "Bootstrap straight.el." | |
100 | (cuss backup-directory-alist | 59 | (defvar bootstrap-version) |
101 | `((".*" . ,(no-littering-expand-var-file-name "backup/"))) | 60 | (let ((bootstrap-file |
102 | "Where to store backup files.") | 61 | (expand-file-name |
103 | 62 | "straight/repos/straight.el/bootstrap.el" | |
104 | (cuss auto-save-file-name-transforms | 63 | user-emacs-directory)) |
105 | `((".*" ,(no-littering-expand-var-file-name "autosaves/") t)) | 64 | (bootstrap-version 5)) |
106 | "Where to store auto-save files.") | 65 | (unless (file-exists-p bootstrap-file) |
107 | 66 | (with-current-buffer | |
108 | (cuss save-place-file | 67 | (url-retrieve-synchronously |
109 | (no-littering-expand-var-file-name "places") | 68 | (concat |
110 | "Where to store place files.") | 69 | "https://raw.githubusercontent.com/" |
111 | 70 | "raxod502/straight.el/" | |
112 | (cuss undo-fu-session-directory | 71 | "develop/install.el") |
113 | (no-littering-expand-var-file-name "undos/") | 72 | 'silent 'inhibit-cookies) |
114 | "Where to store undo information.") | 73 | (goto-char (point-max)) |
115 | 74 | (eval-print-last-sexp))) | |
116 | (cuss elpher-certificate-directory | 75 | (load bootstrap-file nil 'nomessage))) |
117 | (no-littering-expand-var-file-name "elpher-certificates/") | 76 | #+END_SRC |
118 | "Where to store elpher client certificates.") | ||
119 | |||
120 | ;; Make all directories defined above | ||
121 | (dolist (dir '("backup" | ||
122 | "autosaves" | ||
123 | "undos" | ||
124 | "elpher-certificates")) | ||
125 | (make-directory (no-littering-expand-var-file-name dir) 'parents)) | ||
126 | #+end_src | ||
127 | 77 | ||
128 | ** About me | 78 | Now, I'll /try/ running it regular-style, ignoring the errors. If it |
79 | doesn't work, I'll call git directly and clone the repo myself. | ||
129 | 80 | ||
130 | #+begin_src emacs-lisp | 81 | #+BEGIN_SRC emacs-lisp |
131 | (setq user-full-name "Case Duckworth" | 82 | (unless (ignore-errors (acdw/bootstrap-straight)) |
132 | user-mail-address "acdw@acdw.net") | 83 | (message "Straight.el didn't bootstrap correctly. Cloning directly...") |
133 | #+end_src | 84 | (call-process "git" nil |
85 | (get-buffer-create "*bootstrap-straight-messages*") nil | ||
86 | "clone" | ||
87 | "https://github.com/raxod502/straight.el" | ||
88 | (expand-file-name "straight/repos/straight.el" | ||
89 | user-emacs-directory)) | ||
90 | (acdw/bootstrap-straight)) | ||
91 | #+END_SRC | ||
134 | 92 | ||
135 | * Look and Feel | 93 | ** Customize macro |
136 | 94 | ||
137 | ** Simplify the UI | 95 | #+BEGIN_SRC emacs-lisp |
96 | (defmacro cuss (var val &optional docstring) | ||
97 | "Basically, `:custom' from `use-package', but without `use-package'." | ||
98 | (declare (doc-string 3) | ||
99 | (indent 2)) | ||
100 | `(funcall (or (get ',var 'custom-set) #'set-default) | ||
101 | ',var ,val)) | ||
102 | #+END_SRC | ||
138 | 103 | ||
139 | *** Tool bars and menu bars | 104 | ** Clean =.emacs.d= |
140 | 105 | ||
141 | #+begin_src emacs-lisp | 106 | #+BEGIN_SRC emacs-lisp |
142 | (cuss default-frame-alist | 107 | (straight-use-package 'no-littering) |
143 | '((tool-bar-lines . 0) | 108 | (require 'no-littering) |
144 | (menu-bar-lines . 0)) | 109 | #+END_SRC |
145 | "On a default frame, show no tool bars or menu bars.") | ||
146 | 110 | ||
147 | (menu-bar-mode -1) | 111 | ** Look and feel |
148 | (tool-bar-mode -1) | ||
149 | #+end_src | ||
150 | 112 | ||
151 | *** Scroll bars | 113 | *** Cursor |
152 | 114 | ||
153 | #+begin_src emacs-lisp | 115 | #+BEGIN_SRC emacs-lisp |
154 | (add-to-list 'default-frame-alist '(vertical-scroll-bars . nil)) | 116 | (cuss cursor-type 'bar |
155 | (scroll-bar-mode -1) | 117 | "Show a vertical bar for the cursor.") |
156 | 118 | ||
157 | (add-to-list 'default-frame-alist '(horizontal-scroll-bars . nil)) | 119 | (cuss cursor-in-non-selected-windows 'hollow |
158 | (horizontal-scroll-bar-mode -1) | 120 | "Show an empty box in inactive windows.") |
159 | #+end_src | ||
160 | 121 | ||
161 | *** Dialog boxen | 122 | ;; Don't blink the cursor |
123 | (blink-cursor-mode -1) | ||
124 | #+END_SRC | ||
162 | 125 | ||
163 | #+begin_src emacs-lisp | 126 | *** Tool Bars |
164 | (cuss use-dialog-box nil | ||
165 | "Don't show dialog boxes.") | ||
166 | #+end_src | ||
167 | 127 | ||
168 | *** Shorten confirmations | 128 | **** Tool bars and menu bars |
169 | 129 | ||
170 | #+begin_src emacs-lisp | 130 | #+BEGIN_SRC emacs-lisp |
171 | (fset 'yes-or-no-p #'y-or-n-p) | 131 | (cuss default-frame-alist |
172 | #+end_src | 132 | '((tool-bar-lines . 0) |
133 | (menu-bar-lines .0)) | ||
134 | "Setup the default frame alist.") | ||
173 | 135 | ||
174 | *** Remove the bell | 136 | (menu-bar-mode -1) |
137 | (tool-bar-mode -1) | ||
138 | #+END_SRC | ||
175 | 139 | ||
176 | #+begin_src emacs-lisp | 140 | **** Scroll bars |
177 | ;(cuss visible-bell | ||
178 | ; (not (string= (system-name) "larry")) | ||
179 | ; "Only show a visible bell when on 'larry'.") | ||
180 | |||
181 | (defun acdw/ring-bell-function () | ||
182 | "Custom bell-ringing function." | ||
183 | (let ((orig-face (face-foreground 'mode-line))) | ||
184 | (set-face-foreground 'modeline "#F2804F") | ||
185 | (run-with-idle-timer | ||
186 | 0.1 nil | ||
187 | (lambda (fg) | ||
188 | (set-face-foreground 'mode-line fg)) | ||
189 | orig-face))) | ||
190 | |||
191 | (cuss ring-bell-function #'acdw/ring-bell-function) | ||
192 | #+end_src | ||
193 | 141 | ||
194 | *** Tell Ediff to setup windows better | 142 | #+BEGIN_SRC emacs-lisp |
143 | (add-to-list 'default-frame-alist | ||
144 | '(vertical-scroll-bars . nil)) | ||
195 | 145 | ||
196 | #+begin_src emacs-lisp | 146 | (scroll-bar-mode -1) |
197 | (declare-function ediff-setup-windows-plain "ediff-wind.el") | ||
198 | (cuss ediff-window-setup-function #'ediff-setup-windows-plain) | ||
199 | #+end_src | ||
200 | 147 | ||
201 | ** Tweak the remaining UI | 148 | (add-to-list 'default-frame-alist |
149 | '(horizontal-scroll-bars . nil)) | ||
202 | 150 | ||
203 | *** Fringes | 151 | (horizontal-scroll-bar-mode -1) |
152 | #+END_SRC | ||
204 | 153 | ||
205 | #+begin_src emacs-lisp | 154 | *** Dialogs |
206 | (add-to-list 'default-frame-alist '(left-fringe-width . 2)) | ||
207 | (add-to-list 'default-frame-alist '(right-fringe-width . 2)) | ||
208 | #+end_src | ||
209 | 155 | ||
210 | *** Minibuffer | 156 | #+BEGIN_SRC emacs-lisp |
157 | (cuss use-dialog-box nil | ||
158 | "Don't use dialog boxes to ask questions.") | ||
159 | #+END_SRC | ||
211 | 160 | ||
212 | **** Setup the minibuffer frame | 161 | **** Yes or no questions |
213 | 162 | ||
214 | #+begin_src emacs-lisp | 163 | #+BEGIN_SRC emacs-lisp |
215 | (cuss minibuffer-frame-alist | 164 | (fset 'yes-or-no-p #'y-or-n-p) |
216 | '((width . 80) | 165 | #+END_SRC |
217 | (height . 2) | ||
218 | (vertical-scrollbars . nil)) | ||
219 | "Set up the minibuffer frame.") | ||
220 | 166 | ||
221 | (set-window-scroll-bars (minibuffer-window) nil nil) | 167 | **** The Bell |
222 | #+end_src | ||
223 | 168 | ||
224 | **** Keep the cursor from going into the prompt | 169 | #+BEGIN_SRC emacs-lisp |
170 | (defun acdw/ring-bell-function () | ||
171 | "Ring the bell." | ||
172 | (let ((orig-face (face-foreground 'mode-line))) | ||
173 | (set-face-foreground 'mode-line "#F2804F") | ||
174 | (run-with-idle-timer | ||
175 | 0.1 nil | ||
176 | (lambda (fg) | ||
177 | (set-face-foreground 'mode-line fg)) | ||
178 | orig-face))) | ||
179 | |||
180 | (cuss ring-bell-function #'acdw/ring-bell-function) | ||
181 | #+END_SRC | ||
225 | 182 | ||
226 | #+begin_src emacs-lisp | 183 | *** Frames |
227 | (cuss minibuffer-prompt-properties | ||
228 | '(read-only t cursor-intangible t face minibuffer-prompt) | ||
229 | "Disable moving the cursor into the minibuffer prompt.") | ||
230 | #+end_src | ||
231 | 184 | ||
232 | *** Tabs | 185 | **** Fringes |
233 | 186 | ||
234 | **** Show the tabs as current buffer, plus window count | 187 | #+BEGIN_SRC emacs-lisp |
188 | (cuss indicate-empty-lines t | ||
189 | "Show an indicator on the left fringe of empty lines past the | ||
190 | end of the buffer.") | ||
191 | (cuss indicate-buffer-boundaries 'right | ||
192 | "Indicate the beginning and end of the buffer and whether it | ||
193 | scrolls off-window in the right fringe.") | ||
194 | #+END_SRC | ||
235 | 195 | ||
236 | #+begin_src emacs-lisp | 196 | **** Minibuffer |
237 | (cuss tab-bar-tab-name-function | ||
238 | #'tab-bar-tab-name-current-with-count) | ||
239 | #+end_src | ||
240 | 197 | ||
241 | **** Only show the tab bar when there's more than one tab | 198 | #+BEGIN_SRC emacs-lisp |
199 | (cuss minibuffer-prompt-properties | ||
200 | '(read-only t cursor-intangible t face minibuffer-prompt) | ||
201 | "Keep the cursor away from the minibuffer prompt.") | ||
202 | #+END_SRC | ||
242 | 203 | ||
243 | #+begin_src emacs-lisp | 204 | **** Tabs |
244 | (cuss tab-bar-show 1 | ||
245 | "Show the tab bar only when there's more than 1 tab.") | ||
246 | #+end_src | ||
247 | 205 | ||
248 | *** Cursor | 206 | #+BEGIN_SRC emacs-lisp |
207 | (cuss tab-bar-tab-name-function | ||
208 | #'tab-bar-tab-name-current-with-count | ||
209 | "Show the tab name as the name of the current buffer, plus a | ||
210 | count of the windows in the tab.") | ||
249 | 211 | ||
250 | #+begin_src emacs-lisp | 212 | (cuss tab-bar-show 1 |
251 | (cuss cursor-type 'bar | 213 | "Show the tab bar, when there's more than one tab.") |
252 | "Show a vertical bar for the cursor.") | 214 | #+END_SRC |
253 | (cuss cursor-in-non-selected-windows 'hollow | ||
254 | "In inactive windows, make the cursor an empty box.") | ||
255 | 215 | ||
256 | (blink-cursor-mode 0) | 216 | *** Windows |
257 | #+end_src | ||
258 | 217 | ||
259 | *** Buffer names | 218 | **** Winner mode |
260 | 219 | ||
261 | #+begin_src emacs-lisp | 220 | #+BEGIN_SRC emacs-lisp |
262 | (require 'uniquify) | 221 | (when (fboundp 'winner-mode) |
263 | (cuss uniquify-buffer-name-style 'forward) | 222 | (winner-mode +1)) |
264 | #+end_src | 223 | #+END_SRC |
265 | 224 | ||
266 | *** Buffer boundaries | 225 | **** Switch windows |
267 | 226 | ||
268 | #+begin_src emacs-lisp | 227 | #+BEGIN_SRC emacs-lisp |
269 | (cuss indicate-buffer-boundaries | 228 | (global-set-key (kbd "M-o") #'other-window) |
270 | '((up . right) | 229 | #+END_SRC |
271 | (down . right) | ||
272 | (t . nil)) | ||
273 | "Show arrows on the right when there's more to the buffer up or down.") | ||
274 | |||
275 | (cuss indicate-empty-lines t | ||
276 | "Show a bitmap on the left for empty lines after the end of a buffer.") | ||
277 | #+end_src | ||
278 | 230 | ||
279 | ** Windows | 231 | *** Buffers |
280 | 232 | ||
281 | *** Winner mode | 233 | **** Uniquify buffers |
282 | 234 | ||
283 | #+begin_src emacs-lisp | 235 | #+BEGIN_SRC emacs-lisp |
284 | (when (fboundp 'winner-mode) | 236 | (require 'uniquify) |
285 | (winner-mode +1)) | 237 | (cuss uniquify-buffer-name-style 'forward |
286 | #+end_src | 238 | "Uniquify buffers' names by going up the path trees until they |
239 | become unique.") | ||
240 | #+END_SRC | ||
287 | 241 | ||
288 | *** Windmove | 242 | **** Startup buffers |
289 | 243 | ||
290 | #+begin_src emacs-lisp | 244 | #+BEGIN_SRC emacs-lisp |
291 | (cuss windmove-create-window t | 245 | (cuss inhibit-startup-screen t |
292 | "Create windows in a direction if they don't exist.") | 246 | "Don't show Emacs' startup buffer.") |
293 | (cuss windomove-wrap-around t | ||
294 | "Wrap window movements around frame edges.") | ||
295 | 247 | ||
296 | (windmove-default-keybindings) | 248 | (cuss initial-buffer-choice t |
297 | #+end_src | 249 | "Start with *scratch*.") |
298 | 250 | ||
299 | *** Pop some buffers up in the same window | 251 | (cuss initial-scratch-message "" |
252 | "Empty *scratch* buffer.") | ||
253 | #+END_SRC | ||
300 | 254 | ||
301 | from [[https://github.com/link0ff/emacs-init][link0ff]]. | 255 | *** Modeline |
302 | 256 | ||
303 | #+begin_src emacs-lisp | 257 | **** Smart mode line |
304 | (push `(,(rx bos | ||
305 | "*" | ||
306 | (or "Help" "Apropos" "Colors" "Buffer List" "Command History" | ||
307 | "Dictionary" "Locate" "Messages" "Proced" "eww" "snd" | ||
308 | (and "gud-" (+ (any "a-z0-9"))) | ||
309 | "compilation" "grep" "erlang" "haskell" | ||
310 | ;; Handle both "*shell*" and e.g. "*emacs-shell*" | ||
311 | ;; generated by `project-shell': | ||
312 | (and (? (* nonl) "-") "shell") | ||
313 | "Shell Command Output" | ||
314 | (and "SQL: " (+ (any "A-za-z"))) | ||
315 | "Diff" "vc-dir" "vc-log" "vc-search-log") | ||
316 | "*" | ||
317 | ;; Uniquifed buffer name with optional suffix in angle brackets | ||
318 | (? (and "<" (+ (not (any ">"))) ">")) | ||
319 | eos) | ||
320 | display-buffer-same-window | ||
321 | (inhibit-same-window . nil)) | ||
322 | display-buffer-alist) | ||
323 | |||
324 | (defun display-buffer-from-help-p (_buffer-name _action) | ||
325 | (unless current-prefix-arg | ||
326 | (with-current-buffer (window-buffer) | ||
327 | (eq major-mode 'help-mode)))) | ||
328 | |||
329 | (push '(display-buffer-from-help-p display-buffer-same-window) | ||
330 | display-buffer-alist) | ||
331 | #+end_src | ||
332 | 258 | ||
333 | ** Startup | 259 | #+BEGIN_SRC emacs-lisp |
260 | (straight-use-package 'smart-mode-line) | ||
334 | 261 | ||
335 | #+begin_src emacs-lisp | 262 | (cuss sml/no-confirm-load-theme t |
336 | (cuss inhibit-startup-screen t "Don't show Emacs' startup buffer.") | 263 | "Pass the NO-CONFIRM flag to `load-theme'.") |
337 | (cuss initial-buffer-choice t "Start at *scratch*.") | ||
338 | (cuss initial-scratch-message "" "Empty *scratch*.") | ||
339 | #+end_src | ||
340 | 264 | ||
341 | ** Theme | 265 | (sml/setup) |
266 | #+END_SRC | ||
342 | 267 | ||
343 | #+begin_src emacs-lisp | 268 | **** Rich minority |
344 | (straight-use-package '(modus-themes | ||
345 | :host gitlab | ||
346 | :repo "protesilaos/modus-themes" | ||
347 | :branch "main")) | ||
348 | |||
349 | (cuss modus-themes-slanted-constructs t) | ||
350 | (cuss modus-themes-bold-constructs t) | ||
351 | (cuss modus-themes-fringes nil) | ||
352 | (cuss modus-themes-mode-line '3d) | ||
353 | (cuss modus-themes-syntax 'yellow-comments) | ||
354 | (cuss modus-themes-intense-hl-line nil) | ||
355 | (cuss modus-themes-paren-match 'intense-bold) | ||
356 | (cuss modus-themes-links nil) | ||
357 | (cuss modus-themes-no-mixed-fonts nil) | ||
358 | (cuss modus-themes-prompts nil) | ||
359 | (cuss modus-themes-completions nil) | ||
360 | (cuss modus-themes-diffs nil) | ||
361 | (cuss modus-themes-org-blocks 'grayscale) | ||
362 | (cuss modus-themes-headings | ||
363 | '((1 . line) | ||
364 | (t . t))) | ||
365 | (cuss modus-themes-variable-pitch-headings t) | ||
366 | (cuss modus-themes-scale-headings t) | ||
367 | (cuss modus-themes-scale-1 1.1) | ||
368 | (cuss modus-themes-scale-2 1.15) | ||
369 | (cuss modus-themes-scale-3 1.21) | ||
370 | (cuss modus-themes-scale-4 1.27) | ||
371 | (cuss modus-themes-scale-5 1.33) | ||
372 | |||
373 | ;; :custom-face | ||
374 | (custom-set-faces `(font-lock-comment-face | ||
375 | ((t (:inherit (custom-comment italic variable-pitch)))))) | ||
376 | |||
377 | (load-theme 'modus-operandi t) | ||
378 | #+end_src | ||
379 | 269 | ||
380 | *** Change theme based on time of day | 270 | Since this /comes/ with smart mode line, I’m just going to use it, |
271 | instead of =diminish= or another package. I do have to write this | ||
272 | helper function, though, to add things to the whitelist. | ||
381 | 273 | ||
382 | #+begin_src emacs-lisp | 274 | #+BEGIN_SRC emacs-lisp |
383 | (cuss calendar-latitude 30.4515) | 275 | (defun rm/whitelist-add (regexp) |
384 | (cuss calendar-longitude -91.1871) | 276 | "Add a REGEXP to the whitelist for `rich-minority'." |
277 | (if (listp 'rm--whitelist-regexps) | ||
278 | (add-to-list 'rm--whitelist-regexps regexp) | ||
279 | (setq rm--whitelist-regexps `(,regexp))) | ||
280 | (setq rm-whitelist | ||
281 | (mapconcat 'identity rm--whitelist-regexps "\\|"))) | ||
385 | 282 | ||
386 | (straight-use-package 'circadian) | 283 | (straight-use-package 'rich-minority) |
387 | 284 | ||
388 | (cuss circadian-themes '((:sunrise . modus-operandi) | 285 | (rm/whitelist-add "^$") |
389 | (:sunset . modus-vivendi))) | 286 | #+END_SRC |
390 | 287 | ||
391 | (circadian-setup) | 288 | *** Theme |
392 | #+end_src | ||
393 | 289 | ||
394 | *** Modeline | 290 | **** Modus Themes |
395 | 291 | ||
396 | #+begin_src emacs-lisp | 292 | #+BEGIN_SRC emacs-lisp |
397 | (straight-use-package 'smart-mode-line) | 293 | (straight-use-package 'modus-themes) |
398 | (cuss sml/no-confirm-load-theme t) | 294 | |
399 | (sml/setup) | 295 | (cuss modus-themes-slanted-constructs t |
400 | #+end_src | 296 | "Use more slanted constructs.") |
297 | (cuss modus-themes-bold-constructs t | ||
298 | "Use more bold constructs.") | ||
299 | |||
300 | (cuss modus-themes-region 'bg-only | ||
301 | "Only highlight the background of the selected region.") | ||
302 | |||
303 | (cuss modus-themes-org-blocks 'grayscale | ||
304 | "Show org-blocks with a grayscale background.") | ||
305 | (cuss modus-themes-headings | ||
306 | '((1 . line) | ||
307 | (t . t)) | ||
308 | "Highlight top headings with `line' style, and others by default.") | ||
309 | |||
310 | (cuss modus-themes-scale-headings t | ||
311 | "Scale headings by the ratios below.") | ||
312 | (cuss modus-themes-scale-1 1.1) | ||
313 | (cuss modus-themes-scale-2 1.15) | ||
314 | (cuss modus-themes-scale-3 1.21) | ||
315 | (cuss modus-themes-scale-4 1.27) | ||
316 | (cuss modus-themes-scale-5 1.33) | ||
317 | |||
318 | (load-theme 'modus-operandi t) | ||
319 | #+END_SRC | ||
401 | 320 | ||
402 | **** Rich minority | 321 | **** Change themes based on time of day |
403 | 322 | ||
404 | Since this /comes/ with smart mode line, I’m just going to use it, instead of =diminish= or another package. I do have to write this helper function, though, to add things to the whitelist. | 323 | #+BEGIN_SRC emacs-lisp |
324 | (cuss calendar-latitude 30.4515) | ||
325 | (cuss calendar-longitude -91.1871) | ||
405 | 326 | ||
406 | #+begin_src emacs-lisp | 327 | (straight-use-package 'circadian) |
407 | (defun rm/whitelist-add (regexp) | ||
408 | "Add a REGEXP to the whitelist for `rich-minority'." | ||
409 | (if (listp 'rm--whitelist-regexps) | ||
410 | (add-to-list 'rm--whitelist-regexps regexp) | ||
411 | (setq rm--whitelist-regexps `(,regexp))) | ||
412 | (setq rm-whitelist | ||
413 | (mapconcat 'identity rm--whitelist-regexps "\\|"))) | ||
414 | 328 | ||
415 | (straight-use-package 'rich-minority) | 329 | (cuss circadian-themes '((:sunrise . modus-operandi) |
330 | (:sunset . modus-vivendi))) | ||
416 | 331 | ||
417 | (rm/whitelist-add "^$") | 332 | (circadian-setup) |
418 | #+end_src | 333 | #+END_SRC |
419 | 334 | ||
420 | *** Fonts | 335 | *** Fonts |
421 | 336 | ||
422 | **** Define fonts | 337 | **** Define fonts |
423 | 338 | ||
424 | #+begin_src emacs-lisp | 339 | #+BEGIN_SRC emacs-lisp |
425 | (defun set-face-from-alternatives (face fonts) | 340 | (defun set-face-from-alternatives (face fonts) |
426 | (catch :return | 341 | (catch :return |
427 | (dolist (font fonts) | 342 | (dolist (font fonts) |
428 | (when (find-font (font-spec :family (car font))) | 343 | (when (find-font (font-spec :family (car font))) |
429 | (apply #'set-face-attribute `(,face nil | 344 | (apply #'set-face-attribute `(,face nil |
430 | :family ,(car font) | 345 | :family ,(car font) |
431 | ,@(cdr font))) | 346 | ,@(cdr font))) |
432 | (throw :return font))))) | 347 | (throw :return font))))) |
433 | 348 | ||
434 | (defun acdw/setup-fonts () | 349 | (defun acdw/setup-fonts () |
435 | "Setup fonts. This has to happen after the frame is setup for | 350 | "Setup fonts. This has to happen after the frame is setup for |