diff options
-rw-r--r-- | README.md | 1378 | ||||
-rw-r--r-- | config.org | 10 |
2 files changed, 833 insertions, 555 deletions
diff --git a/README.md b/README.md index 608d9f8..6d64472 100644 --- a/README.md +++ b/README.md | |||
@@ -1,809 +1,1077 @@ | |||
1 | This is my Emacs configuration. It's also a literate `org-mode` file. Yeah, I'm a cool guy. | ||
1 | 2 | ||
2 | 3 | ||
3 | # Preamble | 4 | # About me |
4 | 5 | ||
5 | I wanted to write my Emacs configuration in [Org mode](https://orgmode.org) for a while, but never could quite figure out how. Finally, I found [Lars Tveito](https://github.com/larstvei/dot-emacs)'s config, which does exactly what I want: `init.el` is small and simple, and replaced after the first run, and `init.org` is automatically tangled. So I'm very excited. | 6 | ;; init.el -*- lexical-binding: t -*- |
7 | (setq user-full-name "Case Duckworth" | ||
8 | user-mail-address "acdw@acdw.net") | ||
6 | 9 | ||
7 | 10 | ||
8 | # License | 11 | # License |
9 | 12 | ||
10 | WTFPL. For more info, see `LICENSE`. | 13 | Copyright © 2020 Case Duckworth <acdw@acdw.net> |
11 | 14 | ||
12 | Probably that's not legal under the terms of the GPL or whatever Emacs is licensed under. | 15 | This work is free. You can redistribute it and/or modify it under the terms of the Do What the Fuck You Want To Public License, Version 2, as published by Sam Hocevar. See the `LICENSE` file, tangled from the following source block, for details. |
13 | SUE ME, RMS | 16 | |
17 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | ||
18 | |||
19 | Version 2, December 2004 | ||
20 | |||
21 | Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> | ||
22 | |||
23 | Everyone is permitted to copy and distribute verbatim or modified copies of | ||
24 | this license document, and changing it is allowed as long as the name is changed. | ||
25 | |||
26 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | ||
27 | |||
28 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||
29 | |||
30 | 0. You just DO WHAT THE FUCK YOU WANT TO. | ||
31 | |||
32 | |||
33 | ## Note on the license | ||
34 | |||
35 | It's highly likely that the WTFPL is completely incompatible with the GPL, for what should be fairly obvious reasons. To that, I say: | ||
36 | |||
37 | **SUE ME, RMS!** | ||
14 | 38 | ||
15 | 39 | ||
16 | # Bootstrap | 40 | # Bootstrap |
17 | 41 | ||
18 | *Check out Lars's config for the reasoning behind this.* | ||
19 | 42 | ||
20 | When this configuration is loaded for the first time, this `init.el` is loaded: | 43 | ## Original init.el |
21 | 44 | ||
22 | ;; This file replaces itself with the actual configuration when first run. To keep only this version in git, run this command: | 45 | ;; This file replaces itself with the actual configuration when |
23 | ;; git update-index --assume-unchanged init.el | 46 | ;; first run. To keep only this version in git, run this command: |
24 | ;; | 47 | ;; |
25 | ;; If it needs to be changed, start tracking it again thusly: | 48 | ;; git update-index --assume-unchanged init.el |
26 | ;; git update-index --no-assume-unchanged init.el | 49 | ;; |
27 | 50 | ;; If it needs to be changed, start tracking it again thusly: | |
28 | (require 'org) | 51 | ;; |
29 | (find-file (concat user-emacs-directory "init.org")) | 52 | ;; git update-index --no-assume-unchanged init.el |
30 | (org-babel-tangle) | 53 | |
31 | (load-file (concat user-emacs-directory "early-init.el")) | 54 | (require 'org) |
32 | (load-file (concat user-emacs-directory "init.el")) | 55 | (find-file (concat user-emacs-directory "config.org")) |
33 | (byte-compile-file (concat user-emacs-directory "init.el")) | 56 | (org-babel-tangle) |
57 | (load-file (concat user-emacs-directory "early-init.el")) | ||
58 | (load-file (concat user-emacs-directory "init.el")) | ||
59 | (byte-compile-file (concat user-emacs-directory "init.el")) | ||
34 | 60 | ||
35 | 61 | ||
36 | ## Tangling | 62 | ## Tangling |
37 | 63 | ||
38 | After the first run, the above `init.el` will be replaced by the tangled stuff here. However, when *this* file is edited, we'll need to re-tangle everything. However, nobody has time to do that manually with `C-c C-v t`, *every time*! Luckily, Emacs is highly programmable. | 64 | (defun acdw/tangle-init () |
65 | "If the current buffer is `config.org', tangle it, then compile | ||
66 | and load the resulting files." | ||
67 | (when (equal (buffer-file-name) | ||
68 | (expand-file-name | ||
69 | (concat user-emacs-directory "config.org"))) | ||
70 | (require 'async) | ||
71 | (async-start | ||
72 | (lambda () | ||
73 | (let ((prog-mode-hook nil)) | ||
74 | (require 'org) | ||
75 | (org-babel-tangle-file | ||
76 | (expand-file-name | ||
77 | (concat user-emacs-directory "config.org"))))) | ||
78 | (lambda (response) | ||
79 | (acdw/load-init) | ||
80 | (message "Tangled and loaded: %s" response))))) | ||
81 | |||
82 | (add-hook 'after-save-hook #'acdw/tangle-init) | ||
83 | |||
84 | (defun acdw/load-init () | ||
85 | (interactive) | ||
86 | (load-file (expand-file-name | ||
87 | (concat user-emacs-directory "early-init.el"))) | ||
88 | (load-file (expand-file-name | ||
89 | (concat user-emacs-directory "init.el")))) | ||
90 | |||
91 | |||
92 | ## Miscellaneous bootstrappy stuff | ||
93 | |||
39 | 94 | ||
40 | (defun acdw/tangle-init () | 95 | ### Add `~/.emacs.d/lisp/` to `load-path` |
41 | "If the current buffer is `init.org', the code blocks are tangled, | 96 | |
42 | and the tangled file is compiled and loaded." | 97 | (add-to-list 'load-path |
43 | (interactive) | 98 | (concat user-emacs-directory |
44 | (when (equal (buffer-file-name) | 99 | (convert-standard-filename "lisp/"))) |
45 | (expand-file-name (concat user-emacs-directory "config.org"))) | 100 | |
46 | ;; Avoid running hooks when tangling. | 101 | |
47 | (let ((prog-mode-hook nil)) | 102 | ### Require my secrets |
48 | (org-babel-tangle)))) | 103 | |
49 | 104 | (require 'acdw-secrets) | |
50 | (add-hook 'after-save-hook #'acdw/tangle-init) | ||
51 | 105 | ||
52 | 106 | ||
53 | # Early initiation | 107 | # Early initiation |
54 | 108 | ||
55 | Emacs 27.1+ uses `early-init.el`, which is evaluated before things like `package.el` and other stuff. So I have a few settings in there. | 109 | ;; early-init.el -*- lexical-binding: t; no-byte-compile: t -*- |
56 | 110 | ||
111 | (setq load-prefer-newer t) | ||
57 | 112 | ||
58 | ## Preamble | ||
59 | 113 | ||
60 | Of course, first thing is the modeline. After that, I set `load-prefer-newer` because, well, it *should*. | 114 | ## Increase the garbage collector |
61 | 115 | ||
62 | ;;; early-init.el -*- lexical-binding: t; no-byte-compile: t -*- | 116 | (setq gc-cons-threshold (* 100 100 1000)) |
63 | |||
64 | (setq load-prefer-newer t) | ||
65 | 117 | ||
118 | (add-hook 'after-init-hook | ||
119 | (lambda () | ||
120 | (setq gc-cons-threshold (* 100 100 100)) | ||
121 | (message "gc-cons-threshold restored to %S" | ||
122 | gc-cons-threshold))) | ||
66 | 123 | ||
67 | ## Computers | ||
68 | 124 | ||
69 | I have to set these constants before bootstrapping the package manager, since `straight.el` depends on Git, and at work, those are in a weird place. | 125 | ## Add more paths to the `exec-path` when using Windows |
70 | 126 | ||
71 | (defconst *acdw/at-work* (eq system-type 'windows-nt)) | 127 | (when (eq system-type 'windows-nt) |
72 | (defconst *acdw/at-larry* (string= (system-name) "larry")) | 128 | (dolist (path '("~/bin" |
73 | (defconst *acdw/at-bax* (string= (system-name) "bax")) | 129 | "C:/Users/aduckworth/Downloads/PortableGit/bin" |
74 | (defconst *acdw/at-home* (or *acdw/at-larry* *acdw/at-bax*)) | 130 | "C:/Users/aduckworth/Downloads/PortableGit/usr/bin")) |
131 | (add-to-list 'exec-path path))) | ||
75 | 132 | ||
76 | 133 | ||
77 | ## Package management | 134 | ## Bootstrap `straight.el` |
78 | 135 | ||
79 | I've started using straight.el, which is great. It grabs packages from git, and apparently will let me fork and edit them, which I'll probably get around to … eventually. | 136 | (defvar bootstrap-version) |
137 | (let ((bootstrap-file | ||
138 | (expand-file-name "straight/repos/straight.el/bootstrap.el" | ||
139 | user-emacs-directory)) | ||
140 | (bootstrap-version 5)) | ||
141 | (unless (file-exists-p bootstrap-file) | ||
142 | (with-current-buffer | ||
143 | (url-retrieve-synchronously | ||
144 | "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el" | ||
145 | 'silent 'inhibit-cookies) | ||
146 | (goto-char (point-max)) | ||
147 | (eval-print-last-sexp))) | ||
148 | (load bootstrap-file nil 'nomessage)) | ||
80 | 149 | ||
81 | 150 | ||
82 | ### At work, Git's in a weird place | 151 | ## Use `use-package` |
83 | 152 | ||
84 | (when *acdw/at-work* | 153 | (setq straight-use-package-by-default t) |
85 | (add-to-list 'exec-path "~/bin") | 154 | (straight-use-package 'use-package) |
86 | (add-to-list 'exec-path "C:/Users/aduckworth/Downloads/PortableGit/bin")) | ||
87 | 155 | ||
88 | 156 | ||
89 | ### [straight.el](https://github.com/raxod502/straight.el) | 157 | ## Keep `~/.emacs.d` tidy |
90 | 158 | ||
91 | I don't know why, but for some reason the bootstrapping doesn't work on Windows. I have to download the repo directly from github and put it in the right place (`~/.emacs.d/straight/repos/straight.el/`). | 159 | (straight-use-package 'no-littering) |
160 | (require 'no-littering) | ||
92 | 161 | ||
93 | (defvar bootstrap-version) | ||
94 | (let ((bootstrap-file | ||
95 | (expand-file-name "straight/repos/straight.el/bootstrap.el" | ||
96 | user-emacs-directory)) | ||
97 | (bootstrap-version 5)) | ||
98 | (unless (file-exists-p bootstrap-file) | ||
99 | (with-current-buffer | ||
100 | (url-retrieve-synchronously | ||
101 | "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el" | ||
102 | 'silent 'inhibit-cookies) | ||
103 | (goto-char (point-max)) | ||
104 | (eval-print-last-sexp))) | ||
105 | (load bootstrap-file nil 'nomessage)) | ||
106 | 162 | ||
163 | ## Additional `use-package` keywords | ||
107 | 164 | ||
108 | ### [use-package](https://github.com/jwiegley/use-package) | ||
109 | 165 | ||
110 | Yeah, you know it, I know it, we all love it. It's use-package. | 166 | ### `:custom-update` |
111 | 167 | ||
112 | (setq straight-use-package-by-default t) | 168 | (use-package use-package-custom-update |
113 | (straight-use-package 'use-package) | 169 | :straight (use-package-custom-update |
170 | :host github | ||
171 | :repo "a13/use-package-custom-update")) | ||
114 | 172 | ||
115 | 173 | ||
116 | # Begin init.el | 174 | ## Setup async |
117 | 175 | ||
118 | ;;; init.el -*- lexical-binding: t; coding: utf-8 -*- | 176 | (straight-use-package 'async) |
119 | <<tangle-on-save>> | 177 | (require 'async) |
120 | 178 | ||
121 | 179 | ||
122 | # Macros | 180 | # Macros |
123 | 181 | ||
124 | 182 | ||
125 | ## cuss | 183 | ## Customizing variables |
126 | 184 | ||
127 | I like `use-package`, but I don't like doing the weird "pseudo-package" stuff a lot of people do in their emacs configs. Partially because I have to set `:straight nil` on a lot of built-in packages, but also because I think being *that* obsessive over one interface through the whole config is … I don't know, short-sighted? | 185 | (defmacro cuss (var val) |
186 | "Basically `use-package''s `:custom', but without using either." | ||
187 | `(progn | ||
188 | (funcall (or (get ',var 'custom-set) #'set-default) | ||
189 | ',var ,val))) | ||
128 | 190 | ||
129 | Either way, I *do* like the `:custom` interface that `use-package` has, so I've re-implemented it in my own macro. This way I don't have to worry about whether to `setq` or `custom-set-variable` or whatever. Just `cuss`! | ||
130 | 191 | ||
131 | (defmacro cuss (var val) | 192 | # Theme |
132 | "Basically `use-package''s `:custom', but without either." | ||
133 | `(progn | ||
134 | (funcall (or (get ',var 'custom-set) #'set-default) | ||
135 | ',var ,val))) | ||
136 | 193 | ||
194 | I'm using the [Modus](https://protesilaos.com/modus-themes/) themes. | ||
137 | 195 | ||
138 | # Files | 196 | (defmacro modus-themes-format-sexp (sexp &rest objects) |
197 | `(eval (read (format ,(format "%S" sexp) ,@objects)))) | ||
139 | 198 | ||
199 | (dolist (theme '("operandi" "vivendi")) | ||
200 | (modus-themes-format-sexp | ||
201 | (use-package modus-%1$s-theme | ||
202 | :init | ||
203 | (setq modus-%1$s-theme-slanted-constructs t | ||
204 | modus-%1$s-theme-bold-constructs t | ||
205 | modus-%1$s-theme-fringes 'subtle | ||
206 | modus-%1$s-theme-mode-line '3d | ||
207 | modus-%1$s-theme-syntax 'yellow-comments | ||
208 | modus-%1$s-theme-intense-hl-line nil | ||
209 | modus-%1$s-theme-intense-paren-match t | ||
210 | modus-%1$s-theme-links nil | ||
211 | modus-%1$s-theme-no-mixed-fonts nil | ||
212 | modus-%1$s-theme-prompts nil | ||
213 | modus-%1$s-theme-completions nil | ||
214 | modus-%1$s-theme-diffs nil | ||
215 | modus-%1$s-theme-org-blocks 'grayscale | ||
216 | modus-%1$s-theme-headings | ||
217 | '((1 . section) | ||
218 | (2 . line) | ||
219 | (t . rainbow-line-no-bold)) | ||
220 | modus-%1$s-theme-variable-pitch-headings nil | ||
221 | modus-%1$s-theme-scale-headings t | ||
222 | modus-%1$s-theme-scale-1 1.1 | ||
223 | modus-%1$s-theme-scale-2 1.15 | ||
224 | modus-%1$s-theme-scale-3 1.21 | ||
225 | modus-%1$s-theme-scale-4 1.27 | ||
226 | modus-%1$s-theme-scale-5 1.33)) | ||
227 | theme)) | ||
140 | 228 | ||
141 | ## [Keep .emacs.d tidy](https://github.com/emacscollective/no-littering) | 229 | I also want to switch themes between night and day. |
142 | 230 | ||
143 | (straight-use-package 'no-littering) | 231 | (use-package theme-changer |
144 | (require 'no-littering) | 232 | :custom |
233 | (calendar-latitude 30.39) | ||
234 | (calendar-longitude -91.83) | ||
235 | :config | ||
236 | (change-theme 'modus-operandi 'modus-vivendi)) | ||
145 | 237 | ||
146 | 238 | ||
147 | ## Customize | 239 | # Simplify GUI |
148 | 240 | ||
149 | I don't like the customize interface, but I still sometimes use it when I'm not sure what the name of a variable is. So I save the stuff to a file, I just don't load it or keep track of it. | ||
150 | 241 | ||
151 | (cuss custom-file (no-littering-expand-etc-file-name "custom.el")) | 242 | ## Remove unneeded GUI elements |
152 | 243 | ||
244 | (menu-bar-mode -1) | ||
245 | (tool-bar-mode -1) | ||
246 | (scroll-bar-mode -1) | ||
247 | (horizontal-scroll-bar-mode -1) | ||
153 | 248 | ||
154 | ## Encoding | ||
155 | 249 | ||
156 | (prefer-coding-system 'utf-8-unix) | 250 | ## Word wrap and operate visually |
157 | (set-default-coding-systems 'utf-8-unix) | ||
158 | (set-terminal-coding-system 'utf-8-unix) | ||
159 | (set-keyboard-coding-system 'utf-8-unix) | ||
160 | (set-selection-coding-system 'utf-8-unix) | ||
161 | (set-file-name-coding-system 'utf-8-unix) | ||
162 | (set-clipboard-coding-system 'utf-8-unix) | ||
163 | (set-buffer-file-coding-system 'utf-8-unix) | ||
164 | (cuss locale-coding-system 'utf-8) | ||
165 | (cuss x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)) | ||
166 | 251 | ||
252 | (global-visual-line-mode 1) | ||
167 | 253 | ||
168 | ## Recent files | ||
169 | 254 | ||
170 | (use-package recentf | 255 | ## Modeline |
171 | :config | ||
172 | (add-to-list 'recentf-exclude no-littering-var-directory) | ||
173 | (add-to-list 'recentf-exclude no-littering-etc-directory) | ||
174 | :custom | ||
175 | (recentf-max-menu-items 100) | ||
176 | (recentf-max-saved-items 100) | ||
177 | :config | ||
178 | (recentf-mode 1)) | ||
179 | 256 | ||
257 | (use-package smart-mode-line | ||
258 | :custom | ||
259 | (sml/no-confirm-load-theme t) | ||
260 | :config | ||
261 | (sml/setup)) | ||
180 | 262 | ||
181 | ## Backups | 263 | (defun rm/whitelist-add (regexp) |
264 | "Add a REGEXP to the whitelist for `rich-minority'." | ||
265 | (if (listp 'rm--whitelist-regexps) | ||
266 | (add-to-list 'rm--whitelist-regexps regexp) | ||
267 | (setq rm--whitelist-regexps `(,regexp))) | ||
268 | (setq rm-whitelist | ||
269 | (mapconcat 'identity rm--whitelist-regexps "\\|"))) | ||
182 | 270 | ||
183 | (cuss backup-directory-alist | 271 | (use-package rich-minority |
184 | `((".*" . ,(no-littering-expand-var-file-name "backup/")))) | 272 | :config |
273 | (rm/whitelist-add "^$")) | ||
185 | 274 | ||
186 | 275 | ||
187 | ## [Autosave](https://github.com/bbatsov/super-save) | 276 | ## Show `^L` as a line |
188 | 277 | ||
189 | (use-package super-save | 278 | (use-package form-feed |
190 | :custom | 279 | :hook |
191 | (auto-save-default nil) | 280 | ((text-mode prog-mode) . form-feed-mode)) |
192 | (super-save-exclude '(".gpg")) | ||
193 | :config | ||
194 | (super-save-mode 1)) | ||
195 | 281 | ||
196 | 282 | ||
197 | ## [Save places](https://www.emacswiki.org/emacs/SavePlace) | 283 | ## Cursor |
198 | 284 | ||
199 | (use-package saveplace | 285 | (cuss cursor-type 'bar) |
200 | :custom | 286 | (cuss cursor-in-non-selected-windows 'hollow) |
201 | (save-place-file (no-littering-expand-var-file-name "places")) | ||
202 | (save-place-forget-unreadable-files (not *acdw/at-work*)) | ||
203 | :config | ||
204 | (save-place-mode 1)) | ||
205 | 287 | ||
206 | 288 | ||
207 | ## [Save history](https://www.emacswiki.org/emacs/SaveHist) | 289 | # Fonts |
208 | 290 | ||
209 | (use-package savehist | 291 | See [this StackExchange question and answer](https://emacs.stackexchange.com/questions/12351/when-to-call-find-font-if-launching-emacs-in-daemon-mode) for more information on why I have these font settings applied in a hook. |
210 | :custom | ||
211 | (savehist-addtional-variables | ||
212 | '(kill-ring | ||
213 | search-ring | ||
214 | regexp-search-ring)) | ||
215 | (savehist-save-minibuffer-history t) | ||
216 | :config | ||
217 | (savehist-mode 1)) | ||
218 | 292 | ||
293 | (require 'cl) | ||
294 | (defun font-candidate (&rest fonts) | ||
295 | (loop for font in fonts | ||
296 | when (find-font (font-spec :name font)) | ||
297 | return font)) | ||
219 | 298 | ||
220 | # User interface | 299 | (defun acdw/setup-fonts () |
300 | "Setup fonts. This has to happen after the frame is set up for | ||
301 | the first time, so add it to `focus-in-hook'. It removes | ||
302 | itself." | ||
303 | (interactive) | ||
304 | (set-face-attribute 'default nil | ||
305 | :font | ||
306 | (font-candidate | ||
307 | "Libertinus Mono-11" | ||
308 | "Linux Libertine Mono O-11" | ||
309 | "Go Mono-11" | ||
310 | "Consolas-11")) | ||
221 | 311 | ||
312 | (set-face-attribute 'fixed-pitch nil | ||
313 | :font | ||
314 | (font-candidate | ||
315 | "Libertinus Mono-11" | ||
316 | "Linux Libertine Mono O-11" | ||
317 | "Go Mono-11" | ||
318 | "Consolas-11")) | ||
222 | 319 | ||
223 | ## Look | 320 | (set-face-attribute 'variable-pitch nil |
321 | :font | ||
322 | (font-candidate | ||
323 | "Libertinus Serif-14" | ||
324 | "Linux Libertine O-12" | ||
325 | "Georgia-11")) | ||
224 | 326 | ||
327 | (remove-hook 'focus-in-hook #'acdw/setup-fonts)) | ||
225 | 328 | ||
226 | ### Frames and windows | 329 | (add-hook 'focus-in-hook #'acdw/setup-fonts) |
227 | 330 | ||
228 | 1. Frame defaults | ||
229 | 331 | ||
230 | (cuss default-frame-alist '((tool-bar-lines . 0) | 332 | ## Unicode |
231 | (menu-bar-lines . 0) | ||
232 | (vertical-scroll-bars . nil) | ||
233 | (horizontal-scroll-bars . nil) | ||
234 | (right-divider-width . 2) | ||
235 | (bottom-divider-width . 2) | ||
236 | (left-fringe-width . 2) | ||
237 | (right-fringe-width . 2))) | ||
238 | |||
239 | ;; also disable these with modes, so I can re-enable them more easily | ||
240 | (menu-bar-mode -1) | ||
241 | (tool-bar-mode -1) | ||
242 | (scroll-bar-mode -1) | ||
243 | 333 | ||
244 | 2. Resizing | 334 | (use-package unicode-fonts |
335 | :config | ||
336 | (unicode-fonts-setup)) | ||
245 | 337 | ||
246 | (cuss frame-resize-pixelwise t) | ||
247 | (cuss window-combination-resize t) | ||
248 | 338 | ||
339 | ## Variable pitch faces | ||
249 | 340 | ||
250 | ### Buffers | 341 | (add-hook 'text-mode-hook #'variable-pitch-mode) |
251 | 342 | ||
252 | (cuss uniquify-buffer-name-style 'forward) | ||
253 | |||
254 | (cuss indicate-buffer-boundaries | ||
255 | '((top . right) | ||
256 | (bottom . right) | ||
257 | (t . nil))) | ||
258 | 343 | ||
259 | 1. Startup buffer | 344 | # Ease of use |
260 | 345 | ||
261 | (cuss inhibit-startup-buffer-menu t) | ||
262 | (cuss inhibit-startup-screen t) | ||
263 | (cuss initial-buffer-choice t) ; start in *scratch* | ||
264 | (cuss initial-scratch-message nil) | ||
265 | 346 | ||
347 | ## Selectrum & Prescient | ||
266 | 348 | ||
267 | ### Cursor | 349 | (use-package selectrum |
350 | :config | ||
351 | (selectrum-mode 1)) | ||
268 | 352 | ||
269 | (cuss cursor-type 'bar) | 353 | (use-package prescient |
270 | (cuss cursor-in-non-selected-windows 'hollow) | 354 | :config |
271 | (blink-cursor-mode 0) | 355 | (prescient-persist-mode 1)) |
272 | 356 | ||
357 | (use-package selectrum-prescient | ||
358 | :after (selectrum prescient) | ||
359 | :config | ||
360 | (selectrum-prescient-mode 1)) | ||
273 | 361 | ||
274 | ### Interactivity | ||
275 | 362 | ||
276 | 1. Mouse | 363 | ## CtrlF |
277 | 364 | ||
278 | (cuss mouse-yank-at-point t) | 365 | (use-package ctrlf |
366 | :custom | ||
367 | (ctrlf-show-match-count-at-eol nil) | ||
368 | :bind | ||
369 | ("C-s" . ctrlf-forward-regexp) | ||
370 | ("C-r" . ctrlf-backward-regexp) | ||
371 | ("C-M-s" . ctrlf-forward-literal) | ||
372 | ("C-M-r" . ctrlf-backward-literal) | ||
373 | :config | ||
374 | (ctrlf-mode 1)) | ||
279 | 375 | ||
280 | 2. Dialogs | ||
281 | 376 | ||
282 | (cuss use-dialog-box nil) | 377 | ## Startup |
283 | 378 | ||
284 | 3. Disabled functions | 379 | (cuss inhibit-startup-buffer-menu t) |
380 | (cuss inhibit-startup-screen t) | ||
381 | (cuss initial-buffer-choice t) | ||
382 | (cuss initial-scratch-message ";; Hi there!\n") | ||
285 | 383 | ||
286 | (cuss disabled-command-function nil) | ||
287 | 384 | ||
288 | 4. Function aliases | 385 | ## Ignore case |
289 | 386 | ||
290 | (fset 'yes-or-no-p #'y-or-n-p) | 387 | (cuss completion-ignore-case t) |
388 | (cuss read-buffer-completion-ignore-case t) | ||
389 | (cuss read-file-name-completion-ignore-case t) | ||
291 | 390 | ||
292 | 391 | ||
293 | ### Miscellaneous | 392 | ## Which key |
294 | 393 | ||
295 | 1. Convert `^L` to a line | 394 | (use-package which-key |
395 | :custom | ||
396 | (which-key-popup-type 'minibuffer) | ||
397 | :config | ||
398 | (which-key-mode)) | ||
296 | 399 | ||
297 | (use-package page-break-lines | ||
298 | :config | ||
299 | (global-page-break-lines-mode 1)) | ||
300 | 400 | ||
401 | ## Miscellaneous settings | ||
301 | 402 | ||
302 | ## Themes: [Modus](https://github.com/protesilaos/modus-themes) | ||
303 | 403 | ||
304 | (use-package modus-operandi-theme) | 404 | ### Set view mode when in a read-only file |
305 | (use-package modus-vivendi-theme) | ||
306 | 405 | ||
406 | (cuss view-read-only t) | ||
307 | 407 | ||
308 | ### [Change themes](https://github.com/hadronzoo/theme-changer) based on time of day | ||
309 | 408 | ||
310 | (use-package theme-changer | 409 | ### Don't use dialog boxen |
311 | :init | ||
312 | (setq calendar-location-name "Baton Rouge, LA" | ||
313 | calendar-latitude 30.39 | ||
314 | calendar-longitude -91.83) | ||
315 | :config | ||
316 | (change-theme 'modus-operandi 'modus-vivendi)) | ||
317 | 410 | ||
411 | (cuss use-dialog-box nil) | ||
318 | 412 | ||
319 | ### Disable the current theme when a theme is interactively loaded | ||
320 | 413 | ||
321 | This doesn't happen often, but I'll be ready when it does. | 414 | ### Enable all functions |
322 | 415 | ||
323 | (defadvice load-theme | 416 | (cuss disabled-command-function nil) |
324 | (before disable-before-load (theme &optional no-confirm no-enable) activate) | ||
325 | (mapc 'disable-theme custom-enabled-themes)) | ||
326 | 417 | ||
327 | 418 | ||
328 | ## Modeline: [smart-mode-line](https://github.com/Malabarba/smart-mode-line) | 419 | ### Shorter confirmations |
329 | 420 | ||
330 | (use-package smart-mode-line | 421 | (fset 'yes-or-no-p #'y-or-n-p) |
331 | :config | ||
332 | (sml/setup)) | ||
333 | 422 | ||
334 | I hide all minor-modes by default for a clean modeline. However, I can add them back by adding them to the whitelist with `(add-to-list 'rm-whitelist " REGEX")`. | ||
335 | 423 | ||
336 | (defun rm-whitelist-add (regexp) | 424 | ### Uniquify buffer names |
337 | "Add a regexp to the whitelist." | ||
338 | (setq rm-whitelist | ||
339 | (mapconcat 'identity rm--whitelist-regexps "\\|"))) | ||
340 | |||
341 | (setq rm--whitelist-regexps '("^$")) | ||
342 | |||
343 | (use-package rich-minority | ||
344 | :custom | ||
345 | (rm-whitelist | ||
346 | (mapconcat 'identity rm--whitelist-regexps "\\|"))) | ||
347 | 425 | ||
426 | (require 'uniquify) | ||
427 | (cuss uniquify-buffer-name-style 'forward) | ||
348 | 428 | ||
349 | ## Fonts | ||
350 | 429 | ||
351 | I'm sure there's a better way to do this, but for now, this is the best I've got. I append to the `face-font-family-alternatives` because I don't know what kind of weird magic they're doing in there. | 430 | ### Show buffer boundaries |
352 | 431 | ||
353 | (cuss face-font-family-alternatives | 432 | (cuss indicate-buffer-boundaries |
354 | '(("Monospace" "courier" "fixed") | 433 | '((top . right) |
355 | ("Monospace Serif" "Courier 10 Pitch" "Consolas" "Courier Std" "FreeMono" "Nimbus Mono L" "courier" "fixed") | 434 | (bottom . right) |
356 | ("courier" "CMU Typewriter Text" "fixed") | 435 | (t . nil))) |
357 | ("Sans Serif" "helv" "helvetica" "arial" "fixed") | ||
358 | ("helv" "helvetica" "arial" "fixed") | ||
359 | ;; now mine | ||
360 | ("FixedPitch" "Go Mono" "DejaVu Sans Mono" "Consolas" "fixed") | ||
361 | ("VariablePitch" "Go" "DejaVu Sans" "Georgia" "fixed"))) | ||
362 | |||
363 | (set-face-attribute 'default nil | ||
364 | :family "FixedPitch" | ||
365 | :height 110) | ||
366 | |||
367 | (set-face-attribute 'fixed-pitch nil | ||
368 | :family "FixedPitch" | ||
369 | :height 110) | ||
370 | |||
371 | (set-face-attribute 'variable-pitch nil | ||
372 | :family "VariablePitch" | ||
373 | :height 130) | ||
374 | 436 | ||
375 | 437 | ||
376 | ### Ligatures | 438 | ### Hippie expand |
377 | 439 | ||
378 | These cause big problems with cc-mode (as in, totally freezing everything), so I'm going to comment it out. | 440 | (global-set-key (kbd "M-/") 'hippie-expand) |
379 | 441 | ||
380 | ;; (use-package ligature | ||
381 | ;; :straight (ligature | ||
382 | ;; :host github | ||
383 | ;; :repo "mickeynp/ligature.el") | ||
384 | ;; :config | ||
385 | ;; (ligature-set-ligatures 'prog-mode | ||
386 | ;; '("++" "--" "/=" "&&" "||" "||=" | ||
387 | ;; "->" "=>" "::" "__" | ||
388 | ;; "==" "===" "!=" "=/=" "!==" | ||
389 | ;; "<=" ">=" "<=>" | ||
390 | ;; "/*" "*/" "//" "///" | ||
391 | ;; "\\n" "\\\\" | ||
392 | ;; "<<" "<<<" "<<=" ">>" ">>>" ">>=" | ||
393 | ;; "|=" "^=" | ||
394 | ;; "**" "--" "---" "----" "-----" | ||
395 | ;; "==" "===" "====" "=====" | ||
396 | ;; "</" "<!--" "</>" "-->" "/>" | ||
397 | ;; ":=" "..." ":>" ":<" ">:" "<:" | ||
398 | ;; "::=" ;; add others here | ||
399 | ;; )) | ||
400 | ;; :config | ||
401 | ;; (global-ligature-mode)) | ||
402 | 442 | ||
443 | ### iBuffer | ||
403 | 444 | ||
404 | ### [Unicode fonts](https://github.com/rolandwalker/unicode-fonts) | 445 | (global-set-key (kbd "C-x C-b") 'ibuffer) |
405 | 446 | ||
406 | (use-package persistent-soft) | ||
407 | |||
408 | (use-package unicode-fonts | ||
409 | :after persistent-soft | ||
410 | :config | ||
411 | (unicode-fonts-setup)) | ||
412 | 447 | ||
448 | ### Zap-up-to-char, not zap-to-char | ||
413 | 449 | ||
414 | # Editing | 450 | (autoload 'zap-up-to-char "misc" |
451 | "Kill up to, but not including, ARGth occurrence of CHAR." t) | ||
415 | 452 | ||
453 | (global-set-key (kbd "M-z") 'zap-up-to-char) | ||
416 | 454 | ||
417 | ## Completion | ||
418 | 455 | ||
419 | I was using company, but I think it might've been causing issues with `awk-mode`, so I'm trying `hippie-mode` right now. So far, I'm also enjoying not having a popup all the time. | 456 | ### Other "[better defaults](https://git.sr.ht/~technomancy/better-defaults/tree/master/better-defaults.el)" |
420 | 457 | ||
421 | (bind-key "M-/" #'hippie-expand) | 458 | (cuss save-interprogram-paste-before-kill t) |
459 | (cuss apropos-do-all t) | ||
460 | (cuss mouse-yank-at-point t) | ||
461 | (cuss require-final-newline t) | ||
462 | (cuss visible-bell (not (string= (system-name) "larry"))) | ||
463 | (cuss ediff-window-setup-function #'ediff-setup-windows-plain) | ||
422 | 464 | ||
423 | 465 | ||
424 | ## Ignore case | 466 | ### So-long-mode |
425 | 467 | ||
426 | (cuss completion-ignore-case t) | 468 | (if (boundp 'global-so-long-mode) |
427 | (cuss read-buffer-completion-ignore-case t) | 469 | (global-so-long-mode)) |
428 | (cuss read-file-name-completion-ignore-case t) | ||
429 | 470 | ||
430 | 471 | ||
431 | ## Selection & Minibuffer | 472 | ### Change `just-one-space` to `cycle-space` |
432 | 473 | ||
474 | (defun acdw/cycle-spacing-1 () | ||
475 | (interactive) | ||
476 | (cycle-spacing -1)) | ||
433 | 477 | ||
434 | ### Selectrum & Prescient | 478 | (bind-key [remap just-one-space] #'acdw/cycle-spacing-1) |
435 | 479 | ||
436 | (use-package selectrum | ||
437 | :config | ||
438 | (selectrum-mode +1)) | ||
439 | |||
440 | (use-package prescient | ||
441 | :config | ||
442 | (prescient-persist-mode +1)) | ||
443 | |||
444 | (use-package selectrum-prescient | ||
445 | :after (selectrum prescient) | ||
446 | :config | ||
447 | (selectrum-prescient-mode +1)) | ||
448 | 480 | ||
481 | # Persistence | ||
449 | 482 | ||
450 | ## Search | ||
451 | 483 | ||
484 | ## Auto-saves | ||
452 | 485 | ||
453 | ### CtrlF for searching | 486 | (use-package super-save |
487 | :custom | ||
488 | (auto-save-default nil) | ||
489 | (super-save-exclue '(".gpg")) | ||
490 | :config | ||
491 | (super-save-mode 1)) | ||
454 | 492 | ||
455 | (use-package ctrlf | ||
456 | :custom | ||
457 | (ctrlf-show-match-count-at-eol nil) | ||
458 | :config | ||
459 | (ctrlf-mode +1) | ||
460 | :bind | ||
461 | ("C-s" . ctrlf-forward-regexp)) | ||
462 | 493 | ||
494 | ## Backup files | ||
495 | |||
496 | (cuss backup-directory-alist | ||
497 | `((".*" . ,(no-littering-expand-var-file-name "backup/")))) | ||
498 | |||
499 | (cuss backup-by-copying 1) | ||
500 | (cuss delete-old-versions -1) | ||
501 | (cuss version-control t) | ||
502 | (cuss vc-make-backup-files t) | ||
503 | |||
504 | |||
505 | ## Recent files | ||
506 | |||
507 | (use-package recentf | ||
508 | :custom-update | ||
509 | (recentf-exclude | ||
510 | '(no-littering-var-directory | ||
511 | no-littering-etc-directory)) | ||
512 | :custom | ||
513 | (recentf-max-menu-items 100) | ||
514 | (recentf-max-saved-items 100) | ||
515 | :config | ||
516 | (recentf-mode 1)) | ||
517 | |||
518 | |||
519 | ### Easily navigate recent files | ||
520 | |||
521 | (defun recentf-find-file () | ||
522 | "Find a recent file using `completing-read'." | ||
523 | (interactive) | ||
524 | (let ((file (completing-read "Recent file: " recentf-list nil t))) | ||
525 | (when file | ||
526 | (find-file file)))) | ||
527 | |||
528 | (bind-key "C-x C-r" #'recentf-find-file) | ||
529 | |||
530 | |||
531 | ## Save places in visited files | ||
532 | |||
533 | (use-package saveplace | ||
534 | :custom | ||
535 | (save-place-file (no-littering-expand-var-file-name "places")) | ||
536 | (save-place-forget-unreadable-files (not | ||
537 | (eq system-type 'windows-nt)) | ||
538 | :config | ||
539 | (save-place-mode 1))) | ||
463 | 540 | ||
464 | ### [Visual Regexp](https://github.com/benma/visual-regexp.el) | ||
465 | 541 | ||
466 | (use-package visual-regexp | 542 | ## Save history |
467 | :bind | 543 | |
468 | ([remap query-replace] . 'vr/query-replace)) | 544 | (use-package savehist |
545 | :custom | ||
546 | (savehist-additional-variables | ||
547 | '(kill-ring | ||
548 | search-ring | ||
549 | regexp-search-ring)) | ||
550 | (savehist-save-minibuffer-history t) | ||
551 | (history-length t) | ||
552 | (history-delete-duplicates t) | ||
553 | :config | ||
554 | (savehist-mode 1)) | ||
469 | 555 | ||
470 | 556 | ||
471 | ## Undo | 557 | ## Undo |
472 | 558 | ||
473 | (use-package undo-fu | 559 | (use-package undo-fu-session |
474 | :bind | 560 | :after (no-littering undo-fu) |
475 | ("C-/" . undo-fu-only-undo) | 561 | :custom |
476 | ("C-?" . undo-fu-only-redo)) | 562 | (undo-fu-session-incompatible-files |
477 | 563 | '("COMMIT_EDITMSG\\'" | |
478 | (use-package undo-fu-session | 564 | "/git-rebase-todo\\'")) |
479 | :after no-littering | 565 | (undo-fu-session-directory |
480 | :custom | 566 | (no-littering-expand-var-file-name "undos/")) |
481 | (undo-fu-session-incompatible-files | 567 | :config |
482 | '("/COMMIT_EDITMSG\\'" | 568 | (global-undo-fu-session-mode 1)) |
483 | "/git-rebase-todo\\'")) | ||
484 | (undo-fu-session-directory | ||
485 | (no-littering-expand-var-file-name "undos/")) | ||
486 | :config | ||
487 | (global-undo-fu-session-mode +1)) | ||
488 | 569 | ||
489 | 570 | ||
490 | ## Visual editing | 571 | # General editing |
491 | 572 | ||
492 | 573 | ||
493 | ### `zap-to-char` replacement | 574 | ## File encoding |
494 | 575 | ||
495 | (use-package zop-to-char | 576 | I'm going to be honest – most of this is a stab in the dark. |
496 | :bind | ||
497 | ([remap zap-to-char] . zop-to-char) | ||
498 | ([remap zap-up-to-char] . zop-up-to-char)) | ||
499 | 577 | ||
578 | (set-language-environment 'utf-8) | ||
579 | (set-terminal-coding-system 'utf-8) | ||
580 | (cuss locale-coding-system 'utf-8) | ||
581 | (set-default-coding-systems 'utf-8) | ||
582 | (set-selection-coding-system 'utf-8) | ||
583 | (prefer-coding-system 'utf-8) | ||
500 | 584 | ||
501 | ### Operate on a line if there's no current region | 585 | ;; from https://www.emacswiki.org/emacs/EndOfLineTips |
502 | 586 | ||
503 | (use-package whole-line-or-region | 587 | (defun acdw/no-junk-please-were-unixish () |
504 | :config | 588 | "Convert line endings to UNIX, dammit." |
505 | (whole-line-or-region-global-mode +1)) | 589 | (let ((coding-str (symbol-name buffer-file-coding-system))) |
590 | (when (string-match "-\\(?:dos\\|mac\\)$" coding-str) | ||
591 | (set-buffer-file-coding-system 'unix)))) | ||
506 | 592 | ||
593 | (add-hook 'find-file-hooks #'acdw/no-junk-please-were-unixish) | ||
594 | |||
595 | |||
596 | ## Undo | ||
507 | 597 | ||
508 | ### Expand-region | 598 | (use-package undo-fu |
599 | :bind | ||
600 | ("C-/" . undo-fu-only-undo) | ||
601 | ("C-?" . undo-fu-only-redo)) | ||
509 | 602 | ||
510 | (use-package expand-region | 603 | |
511 | :bind | 604 | ## Find/replace |
512 | ("C-=" . er/expand-region) | 605 | |
513 | ("C-+" . er/contract-region)) | 606 | (use-package visual-regexp |
607 | :bind | ||
608 | ("C-c r" . 'vr/replace) | ||
609 | ("C-c q" . 'vr/query-replace)) | ||
610 | |||
611 | |||
612 | ## Visual editing | ||
514 | 613 | ||
515 | 614 | ||
516 | ### Volatile highlights | 615 | ### Volatile highlights |
517 | 616 | ||
518 | (use-package volatile-highlights | 617 | (use-package volatile-highlights |
519 | :config | 618 | :config |
520 | (volatile-highlights-mode 1)) | 619 | (volatile-highlights-mode 1)) |
521 | 620 | ||
522 | 621 | ||
523 | ### Visual line mode | 622 | ### Expand region |
524 | 623 | ||
525 | (global-visual-line-mode 1) | 624 | (use-package expand-region |
625 | :bind | ||
626 | ("C-=" . er/expand-region) | ||
627 | ("C-+" . er/contract-region)) | ||
526 | 628 | ||
527 | 629 | ||
528 | ### A better `move-beginning-of-line` | 630 | ## Clean up white space on save |
529 | 631 | ||
530 | (defun my/smarter-move-beginning-of-line (arg) | 632 | (add-hook 'before-save-hook #'whitespace-cleanup) |
531 | "Move point back to indentation of beginning of line. | 633 | (add-hook 'before-save-hook #'delete-trailing-whitespace) |
532 | |||
533 | Move point to the first non-whitespace character on this line. | ||
534 | If point is already there, move to the beginning of the line. | ||
535 | Effectively toggle between the first non-whitespace character and | ||
536 | the beginning of the line. | ||
537 | |||
538 | If ARG is not nil or 1, move forward ARG - 1 lines first. If | ||
539 | point reaches the beginning or end of the buffer, stop there." | ||
540 | (interactive "^p") | ||
541 | (setq arg (or arg 1)) | ||
542 | |||
543 | ;; Move lines first | ||
544 | (when (/= arg 1) | ||
545 | (let ((line-move-visual nil)) | ||
546 | (forward-line (1- arg)))) | ||
547 | |||
548 | (let ((orig-point (point))) | ||
549 | (back-to-indentation) | ||
550 | (when (= orig-point (point)) | ||
551 | (move-beginning-of-line 1)))) | ||
552 | |||
553 | (bind-key "C-a" #'my/smarter-move-beginning-of-line) | ||
554 | 634 | ||
555 | 635 | ||
556 | ## Delete the selection when typing | 636 | ## Automatically revert a file to what it is on disk |
557 | 637 | ||
558 | (delete-selection-mode 1) | 638 | (global-auto-revert-mode 1) |
559 | 639 | ||
560 | 640 | ||
561 | ## Clipboard | 641 | # Writing |
562 | 642 | ||
563 | (cuss save-interprogram-paste-before-kill t) | ||
564 | 643 | ||
644 | ## Word count | ||
565 | 645 | ||
566 | ## Tabs & Spaces | 646 | (use-package wc-mode |
647 | :config | ||
648 | (rm/whitelist-add "WC") | ||
649 | :hook text-mode) | ||
567 | 650 | ||
568 | (cuss indent-tabs-mode nil) | ||
569 | (cuss sentence-end-double-space t) | ||
570 | 651 | ||
652 | ## Visual fill column mode | ||
571 | 653 | ||
572 | # Programming | 654 | (use-package visual-fill-column |
655 | :custom | ||
656 | (split-window-preferred-function | ||
657 | 'visual-fill-column-split-window-sensibly) | ||
658 | (visual-fill-column-center-text t) | ||
659 | (fill-column 80) | ||
660 | :config | ||
661 | (advice-add 'text-scale-adjust | ||
662 | :after #'visual-fill-column-adjust) | ||
663 | :hook | ||
664 | (text-mode . visual-fill-column-mode)) | ||
573 | 665 | ||
574 | 666 | ||
575 | ## Git | 667 | ## Org mode |
668 | |||
669 | (use-package org | ||
670 | :custom | ||
671 | (org-startup-indented t) | ||
672 | (org-src-tab-acts-natively t) | ||
673 | (org-hide-emphasis-markers t) | ||
674 | (org-fontify-done-headline t) | ||
675 | (org-fontify-whole-heading-line t) | ||
676 | (org-hide-leading-stars t) | ||
677 | (org-hidden-keywords '(author date title)) | ||
678 | (org-src-window-setup 'current-window) | ||
679 | (org-pretty-entities t)) | ||
680 | |||
681 | |||
682 | ### Enable markdown export | ||
683 | |||
684 | (require 'ox-md) | ||
685 | |||
686 | |||
687 | ### Ensure blank lines between headings and before contents | ||
688 | |||
689 | from [unpackaged.el](https://github.com/alphapapa/unpackaged.el#ensure-blank-lines-between-headings-and-before-contents) | ||
690 | |||
691 | ;;;###autoload | ||
692 | (defun unpackaged/org-fix-blank-lines (&optional prefix) | ||
693 | "Ensure that blank lines exist between headings and between | ||
694 | headings and their contents. With prefix, operate on whole | ||
695 | buffer. Ensures that blank lines exist after each headings's | ||
696 | drawers." | ||
697 | (interactive "P") | ||
698 | (org-map-entries | ||
699 | (lambda () | ||
700 | (org-with-wide-buffer | ||
701 | ;; `org-map-entries' narrows the buffer, which prevents us | ||
702 | ;; from seeing newlines before the current heading, so we | ||
703 | ;; do this part widened. | ||
704 | (while (not (looking-back "\n\n" nil)) | ||
705 | ;; Insert blank lines before heading. | ||
706 | (insert "\n"))) | ||
707 | (let ((end (org-entry-end-position))) | ||
708 | ;; Insert blank lines before entry content. | ||
709 | (forward-line) | ||
710 | (while (and (org-at-planning-p) | ||
711 | (< (point) (point-max))) | ||
712 | ;; Skip planning lines | ||
713 | (forward-line)) | ||
714 | (while (re-search-forward org-drawer-regexp end t) | ||
715 | ;; Skip drawers. You might think that | ||
716 | ;; `org-at-drawer-p' would suffice, but for some reason | ||
717 | ;; it doesn't work correctly when operating on hidden | ||
718 | ;; text. This works, taken from | ||
719 | ;; `org-agenda-get-some-entry-text'. | ||
720 | (re-search-forward "^[ \t]*:END:.*\n?" end t) | ||
721 | (goto-char (match-end 0))) | ||
722 | (unless (or (= (point) (point-max)) | ||
723 | (org-at-heading-p) | ||
724 | (looking-at-p "\n")) | ||
725 | (insert "\n")))) | ||
726 | t (if prefix | ||
727 | nil | ||
728 | 'tree))) | ||
729 | |||
730 | |||
731 | ### `org-return-dwim` | ||
732 | |||
733 | from [unpackaged.el](https://github.com/alphapapa/unpackaged.el#org-return-dwim) | ||
734 | |||
735 | (defun unpackaged/org-element-descendant-of (type element) | ||
736 | "Return non-nil if ELEMENT is a descendant of TYPE. | ||
737 | TYPE should be an element type, like `item' or `paragraph'. | ||
738 | ELEMENT should be a list like that returned by | ||
739 | `org-element-context'." | ||
740 | ;; MAYBE: Use `org-element-lineage'. | ||
741 | (when-let* ((parent (org-element-property :parent element))) | ||
742 | (or (eq type (car parent)) | ||
743 | (unpackaged/org-element-descendant-of type parent)))) | ||
744 | |||
745 | ;;;###autoload | ||
746 | (defun unpackaged/org-return-dwim (&optional default) | ||
747 | "A helpful replacement for `org-return'. With prefix, call `org-return'. | ||
748 | |||
749 | On headings, move point to position after entry content. In | ||
750 | lists, insert a new item or end the list, with checkbox if | ||
751 | appropriate. In tables, insert a new row or end the table." | ||
752 | ;; Inspired by John Kitchin: http://kitchingroup.cheme.cmu.edu/blog/2017/04/09/A-better-return-in-org-mode/ | ||
753 | (interactive "P") | ||
754 | (if default | ||
755 | (org-return) | ||
756 | (cond | ||
757 | ;; Act depending on context around point. | ||
758 | |||
759 | ;; NOTE: I prefer RET to not follow links, but by uncommenting this block, | ||
760 | ;; links will be followed. | ||
761 | |||
762 | ;; ((eq 'link (car (org-element-context))) | ||
763 | ;; ;; Link: Open it. | ||
764 | ;; (org-open-at-point-global)) | ||
765 | |||
766 | ((org-at-heading-p) | ||
767 | ;; Heading: Move to position after entry content. | ||
768 | ;; NOTE: This is probably the most interesting feature of this function. | ||
769 | (let ((heading-start (org-entry-beginning-position))) | ||
770 | (goto-char (org-entry-end-position)) | ||
771 | (cond ((and (org-at-heading-p) | ||
772 | (= heading-start (org-entry-beginning-position))) | ||
773 | ;; Entry ends on its heading; add newline after | ||
774 | (end-of-line) | ||
775 | (insert "\n\n")) | ||
776 | (t | ||
777 | ;; Entry ends after its heading; back up | ||
778 | (forward-line -1) | ||
779 | (end-of-line) | ||
780 | (when (org-at-heading-p) | ||
781 | ;; At the same heading | ||
782 | (forward-line) | ||
783 | (insert "\n") | ||
784 | (forward-line -1)) | ||
785 | ;; FIXME: looking-back is supposed to be called with more arguments. | ||
786 | (while (not (looking-back (rx (repeat 3 (seq (optional blank) "\n"))))) | ||
787 | (insert "\n")) | ||
788 | (forward-line -1))))) | ||
789 | |||
790 | ((org-at-item-checkbox-p) | ||
791 | ;; Checkbox: Insert new item with checkbox. | ||
792 | (org-insert-todo-heading nil)) | ||
793 | |||
794 | ((org-in-item-p) | ||
795 | ;; Plain list. Yes, this gets a little complicated... | ||
796 | (let ((context (org-element-context))) | ||
797 | (if (or (eq 'plain-list (car context)) ; First item in list | ||
798 | (and (eq 'item (car context)) | ||
799 | (not (eq (org-element-property :contents-begin context) | ||
800 | (org-element-property :contents-end context)))) | ||
801 | (unpackaged/org-element-descendant-of 'item context)) ; Element in list item, e.g. a link | ||
802 | ;; Non-empty item: Add new item. | ||
803 | (org-insert-item) | ||
804 | ;; Empty item: Close the list. | ||
805 | ;; TODO: Do this with org functions rather than operating on the text. Can't seem to find the right function. | ||
806 | (delete-region (line-beginning-position) (line-end-position)) | ||
807 | (insert "\n")))) | ||
808 | |||
809 | ((when (fboundp 'org-inlinetask-in-task-p) | ||
810 | (org-inlinetask-in-task-p)) | ||
811 | ;; Inline task: Don't insert a new heading. | ||
812 | (org-return)) | ||
813 | |||
814 | ((org-at-table-p) | ||
815 | (cond ((save-excursion | ||
816 | (beginning-of-line) | ||
817 | ;; See `org-table-next-field'. | ||
818 | (cl-loop with end = (line-end-position) | ||
819 | for cell = (org-element-table-cell-parser) | ||
820 | always (equal (org-element-property :contents-begin cell) | ||
821 | (org-element-property :contents-end cell)) | ||
822 | while (re-search-forward "|" end t))) | ||
823 | ;; Empty row: end the table. | ||
824 | (delete-region (line-beginning-position) (line-end-position)) | ||
825 | (org-return)) | ||
826 | (t | ||
827 | ;; Non-empty row: call `org-return'. | ||
828 | (org-return)))) | ||
829 | (t | ||
830 | ;; All other cases: call `org-return'. | ||
831 | (org-return))))) | ||
832 | |||
833 | (bind-key "RET" #'unpackaged/org-return-dwim 'org-mode-map) | ||
834 | |||
835 | |||
836 | # Coding | ||
837 | |||
838 | |||
839 | ## Formatting | ||
840 | |||
841 | |||
842 | ### Indenting | ||
843 | |||
844 | (use-package aggressive-indent | ||
845 | :config | ||
846 | (global-aggressive-indent-mode 1)) | ||
576 | 847 | ||
577 | (use-package magit | ||
578 | :bind | ||
579 | ("C-x g" . magit-status) | ||
580 | :config | ||
581 | (add-to-list 'magit-no-confirm 'stage-all-changes)) | ||
582 | |||
583 | ;; hook into `prescient' | ||
584 | (define-advice magit-list-refs | ||
585 | (:around (orig &optional namespaces format sortby) | ||
586 | prescient-sort) | ||
587 | "Apply prescient sorting when listing refs." | ||
588 | (let ((res (funcall orig namespaces format sortby))) | ||
589 | (if (or sortby | ||
590 | magit-list-refs-sortby | ||
591 | (not selectrum-should-sort-p)) | ||
592 | res | ||
593 | (prescient-sort res)))) | ||
594 | |||
595 | (when (executable-find "cmake") | ||
596 | (use-package libgit) | ||
597 | (use-package magit-libgit)) | ||
598 | |||
599 | (use-package forge | ||
600 | :after magit | ||
601 | :custom | ||
602 | (forge-owned-accounts '(("duckwork")))) | ||
603 | 848 | ||
849 | ### Smart tabs | ||
850 | |||
851 | (use-package smart-tabs-mode | ||
852 | :custom | ||
853 | (whitespace-style | ||
854 | '(face trailing tabs spaces lines newline | ||
855 | empty indentation space-before-tab | ||
856 | space-mark tab-mark newline-mark)) | ||
857 | :config | ||
858 | (smart-tabs-insinuate 'c 'c++ 'javascript 'java 'ruby)) | ||
604 | 859 | ||
605 | ## Code display | ||
606 | 860 | ||
607 | (add-hook 'prog-mode-hook #'prettify-symbols-mode) | 861 | ## Display |
608 | 862 | ||
609 | 863 | ||
610 | ### Parentheses | 864 | ### Prettify symbols mode |
611 | 865 | ||
612 | (cuss show-paren-style 'mixed) | 866 | (add-hook 'prog-mode-hook #'prettify-symbols-mode) |
613 | (show-paren-mode +1) | ||
614 | |||
615 | (use-package smartparens | ||
616 | :init | ||
617 | (defun acdw/setup-smartparens () | ||
618 | (require 'smartparens-config) | ||
619 | (smartparens-mode +1)) | ||
620 | :hook | ||
621 | (prog-mode . acdw/setup-smartparens)) | ||
622 | |||
623 | (use-package rainbow-delimiters | ||
624 | :hook | ||
625 | (prog-mode . rainbow-delimiters-mode)) | ||
626 | 867 | ||
627 | 868 | ||
628 | ## Line numbers | 869 | ### Parentheses and frens |
629 | 870 | ||
630 | (add-hook 'prog-mode-hook | 871 | 1. `show-paren-style` |
631 | (if (and (fboundp 'display-line-numbers-mode) | ||
632 | (display-graphic-p)) | ||
633 | #'display-line-numbers-mode | ||
634 | #'linum-mode)) | ||
635 | 872 | ||
873 | (cuss show-paren-style 'mixed) | ||
874 | (show-paren-mode 1) | ||
636 | 875 | ||
637 | ## Languages | 876 | 2. Smartparens |
638 | 877 | ||
878 | (use-package smartparens | ||
879 | :init | ||
880 | (defun acdw/setup-smartparens () | ||
881 | (require 'smartparens-config) | ||
882 | (smartparens-mode 1)) | ||
883 | :hook | ||
884 | (prog-mode . acdw/setup-smartparens)) | ||
639 | 885 | ||
640 | ### Shell | 886 | 3. Rainbow delimiters |
641 | 887 | ||
642 | (use-package shfmt | 888 | (use-package rainbow-delimiters |
643 | :custom | 889 | :hook (prog-mode . rainbow-delimiters-mode)) |
644 | (shfmt-arguments '("-i" "4" "-ci")) | ||
645 | :hook | ||
646 | (sh-mode . shfmt-on-save-mode)) | ||
647 | |||
648 | ;; fish | ||
649 | (use-package fish-mode) | ||
650 | 890 | ||
651 | 891 | ||
652 | ### Lua | 892 | ### Rainbow mode |
653 | 893 | ||
654 | (use-package lua-mode | 894 | (use-package rainbow-mode |
655 | :mode "\\.lua\\'" | 895 | :custom |
656 | :interpreter "lua") | 896 | (rainbow-x-colors nil) |
897 | :hook prog-mode) | ||
657 | 898 | ||
658 | 899 | ||
659 | ### Fennel | 900 | ### Line numbers |
660 | 901 | ||
661 | (use-package fennel-mode | 902 | (defun acdw/enable-line-numbers () |
662 | :mode "\\.fnl\\'") | 903 | "Enable line numbers, either through `display-line-numbers-mode' |
904 | or through `linum-mode'." | ||
905 | (if (and (fboundp 'display-line-numbers-mode) | ||
906 | (display-graphic-p)) | ||
907 | (progn | ||
908 | (display-line-numbers-mode 1) | ||
909 | (cuss display-line-numbers-width 2)) | ||
910 | (linum-mode 1))) | ||
663 | 911 | ||
912 | (add-hook 'prog-mode-hook #'acdw/enable-line-numbers) | ||
664 | 913 | ||
665 | ### Web | ||
666 | 914 | ||
667 | (use-package web-mode | 915 | ## Git |
668 | :custom | ||
669 | (web-mode-markup-indent-offset 2) | ||
670 | (web-mode-code-indent-offset 2) | ||
671 | (web-mode-css-indent-offset 2) | ||
672 | :mode (("\\.ts\\'" . web-mode) | ||
673 | ("\\.html?\\'" . web-mode) | ||
674 | ("\\.css?\\'" . web-mode) | ||
675 | ("\\.js\\'" . web-mode))) | ||
676 | 916 | ||
917 | (use-package magit | ||
918 | :bind | ||
919 | ("C-x g" . magit-status) | ||
920 | :custom-update | ||
921 | (magit-no-confirm '(stage-all-changes))) | ||
677 | 922 | ||
678 | ### SSH config | ||
679 | 923 | ||
680 | (use-package ssh-config-mode) | 924 | ### Hook into `prescient` |
681 | 925 | ||
926 | (define-advice magit-list-refs | ||
927 | (:around (orig &optional namespaces format sortby) | ||
928 | prescient-sort) | ||
929 | "Apply prescient sorting when listing refs." | ||
930 | (let ((res (funcall orig namespaces format sortby))) | ||
931 | (if (or sortby | ||
932 | magit-list-refs-sortby | ||
933 | (not selectrum-should-sort-p)) | ||
934 | res | ||
935 | (prescient-sort res)))) | ||
682 | 936 | ||
683 | # Writing | ||
684 | 937 | ||
938 | ### Use `libgit` when I can build it (requires `cmake`) | ||
685 | 939 | ||
686 | ## Word count | 940 | (when (executable-find "cmake") |
941 | (use-package libgit) | ||
942 | (use-package magit-libgit)) | ||
687 | 943 | ||
688 | (use-package wc-mode | ||
689 | :init | ||
690 | (rm-whitelist-add "WC") | ||
691 | :hook | ||
692 | (text-mode . wc-mode)) | ||
693 | 944 | ||
945 | ### Git "forge" capabilities | ||
694 | 946 | ||
695 | ## Visual fill column | 947 | (use-package forge |
948 | :after magit | ||
949 | :unless (eq system-type 'windows-nt) | ||
950 | :custom | ||
951 | (forge-owned-accounts | ||
952 | '(("duckwork")))) | ||
696 | 953 | ||
697 | (use-package visual-fill-column | ||
698 | :custom | ||
699 | (split-window-preferred-function 'visual-fill-column-split-window-sensibly) | ||
700 | (visual-fill-column-center-text t) | ||
701 | (fill-column 100) | ||
702 | :config | ||
703 | (advice-add 'text-scale-adjust | ||
704 | :after #'visual-fill-column-adjust) | ||
705 | :hook | ||
706 | (org-mode . visual-fill-column-mode)) | ||
707 | 954 | ||
955 | ## Programming languages | ||
708 | 956 | ||
709 | ## Mixed-pitch | ||
710 | 957 | ||
711 | (use-package mixed-pitch | 958 | ### Fish shell |
712 | :hook | ||
713 | (text-mode . mixed-pitch-mode)) | ||
714 | 959 | ||
960 | (use-package fish-mode) | ||
715 | 961 | ||
716 | ## Org mode | ||
717 | 962 | ||
718 | (use-package org | 963 | ### Lisps |
719 | :custom | ||
720 | (org-startup-indented t) | ||
721 | (org-src-tab-acts-natively t) | ||
722 | (org-hide-emphasis-markers t) | ||
723 | (org-fontify-done-headline t) | ||
724 | (org-hide-leading-stars t) | ||
725 | (org-pretty-entities t) | ||
726 | (org-src-window-setup 'current-window)) | ||
727 | |||
728 | (use-package org-superstar | ||
729 | :hook | ||
730 | (org-mode . org-superstar-mode)) | ||
731 | 964 | ||
965 | 1. Common Lisp (SLIME) | ||
732 | 966 | ||
733 | # Applications | 967 | (use-package slime |
968 | :when (executable-find "sbcl") | ||
969 | :custom | ||
970 | (inferior-lisp-program "sbcl") | ||
971 | (slime-contribs '(slime-fancy))) | ||
734 | 972 | ||
973 | 2. Fennel | ||
974 | |||
975 | (use-package fennel-mode | ||
976 | :mode "\\.fnl\\'") | ||
977 | |||
978 | |||
979 | ### Lua | ||
980 | |||
981 | (use-package lua-mode | ||
982 | :mode "\\.lua\\'" | ||
983 | :interpreter "lua") | ||
735 | 984 | ||
736 | ## Gemini & Gopher | ||
737 | |||
738 | (use-package elpher | ||
739 | :straight (elpher | ||
740 | :repo "git://thelambdalab.xyz/elpher.git") | ||
741 | :bind (:map elpher-mode-map | ||
742 | ("n" . elpher-next-link) | ||
743 | ("p" . elpher-prev-link) | ||
744 | ("o" . elpher-follow-current-link) | ||
745 | ("G" . elpher-go-current)) | ||
746 | :hook | ||
747 | (elpher-mode . visual-fill-column-mode)) | ||
748 | |||
749 | (use-package gemini-mode | ||
750 | :straight (gemini-mode | ||
751 | :repo "https://git.carcosa.net/jmcbray/gemini.el.git") | ||
752 | :mode "\\.\\(gemini|gmi\\)\\'") | ||
753 | |||
754 | (use-package gemini-write | ||
755 | :straight (gemini-write | ||
756 | :repo "https://alexschroeder.ch/cgit/gemini-write")) | ||
757 | |||
758 | (use-package post-to-gemlog-blue | ||
759 | :straight (post-to-gemlog-blue | ||
760 | :repo "https://git.sr.ht/~acdw/post-to-gemlog-blue.el")) | ||
761 | |||
762 | |||
763 | ## Pastebin | ||
764 | |||
765 | (use-package 0x0 | ||
766 | :custom | ||
767 | (0x0-default-service 'ttm)) | ||
768 | 985 | ||
986 | ### Web (HTML/CSS/JS) | ||
769 | 987 | ||
770 | ## Gnus | 988 | (use-package web-mode |
989 | :mode (("\\.ts\\'" . web-mode) | ||
990 | ("\\.html?\\'" . web-mode) | ||
991 | ("\\.css?\\'" . web-mode) | ||
992 | ("\\.js\\'" . web-mode))) | ||
771 | 993 | ||
772 | (cuss gnus-select-method | ||
773 | '(nnimap "imap.fastmail.com" | ||
774 | (nnimap-inbox "INBOX") | ||
775 | (nnimap-split-methods default) | ||
776 | (nnimap-expunge t) | ||
777 | (nnimap-stream ssl))) | ||
778 | |||
779 | (cuss gnus-secondary-select-methods | ||
780 | '((nntp "news.gwene.org"))) | ||
781 | 994 | ||
995 | ### `~/.ssh/config` | ||
782 | 996 | ||
783 | ## Nov.el: read Ebooks | 997 | (use-package ssh-config-mode) |
784 | 998 | ||
785 | (use-package nov | 999 | |
786 | :mode ("\\.epub\\'" . nov-mode) | 1000 | ### Go |
1001 | |||
1002 | (use-package go-mode | ||
1003 | :mode "\\.go\\'") | ||
1004 | |||
1005 | |||
1006 | # Applications | ||
1007 | |||
1008 | |||
1009 | ## Elpher | ||
1010 | |||
1011 | (use-package elpher | ||
1012 | :straight (elpher | ||
1013 | :repo "git://thelambdalab.xyz/elpher.git") | ||
1014 | :custom | ||
1015 | (elpher-certificate-directory | ||
1016 | (no-littering-expand-var-file-name "elpher-certificates/")) | ||
1017 | (elpher-ipv4-always t) | ||
1018 | :custom-face | ||
1019 | (elpher-gemini-heading1 | ||
1020 | ((t (:inherit (modus-theme-heading-1))))) | ||
1021 | (elpher-gemini-heading2 | ||
1022 | ((t (:inherit (modus-theme-heading-2))))) | ||
1023 | (elpher-gemini-heading3 | ||
1024 | ((t (:inherit (modus-theme-heading-3))))) | ||
1025 | :config | ||
1026 | (defun elpher:eww-browse-url (original url &optional new-window) | ||
1027 | "Handle gemini/gopher links with eww." | ||
1028 | (cond ((string-match-p "\\`\\(gemini\\|gopher\\)://" url) | ||
1029 | (require 'elpher) | ||
1030 | (elpher-go url)) | ||
1031 | (t (funcall original url new-window)))) | ||
1032 | (advice-add 'eww-browse-url :around 'elpher:eww-browse-url) | ||
1033 | :bind (:map elpher-mode-map | ||
1034 | ("n" . elpher-next-link) | ||
1035 | ("p" . elpher-prev-link) | ||
1036 | ("o" . elpher-follow-current-link) | ||
1037 | ("G" . elpher-go-current)) | ||
1038 | :hook | ||
1039 | (elpher-mode . visual-fill-column-mode)) | ||
1040 | |||
1041 | (use-package gemini-mode | ||
1042 | :straight (gemini-mode | ||
1043 | :repo "https://git.carcosa.net/jmcbray/gemini.el.git") | ||
1044 | :mode "\\.\\(gemini|gmi\\)\\'" | ||
1045 | :custom-face | ||
1046 | (gemini-heading-face-1 | ||
1047 | ((t (:inherit (elpher-gemini-heading1))))) | ||
1048 | (gemini-heading-face2 | ||
1049 | ((t (:inherit (elpher-gemini-heading2))))) | ||
1050 | (gemini-heading-face3 | ||
1051 | ((t (:inherit (elpher-gemini-heading3))))) | ||
1052 | :hook | ||
1053 | (gemini-mode . visual-fill-column-mode)) | ||
1054 | |||
1055 | (use-package gemini-write | ||
1056 | :straight (gemini-write | ||
1057 | :repo "https://alexschroeder.ch/cgit/gemini-write") | ||
1058 | :config | ||
1059 | (add-to-list 'elpher-gemini-tokens | ||
1060 | (acdw-secrets/elpher-gemini-tokens))) | ||
1061 | |||
1062 | (use-package post-to-gemlog-blue | ||
1063 | :straight (post-to-gemlog-blue | ||
1064 | :repo "https://git.sr.ht/~acdw/post-to-gemlog-blue.el")) | ||
1065 | |||
1066 | |||
1067 | ## Pastebin (0x0) | ||
1068 | |||
1069 | (use-package 0x0 | ||
787 | :custom | 1070 | :custom |
788 | (nov-text-width t) | 1071 | (0x0-default-service 'ttm)) |
789 | :hook | 1072 | |
790 | (nov-mode . visual-line-mode) | 1073 | |
791 | (nov-mode . visual-fill-column-mode)) | 1074 | ## EMMS |
792 | |||
793 | |||
794 | # Machine-specific configurations | ||
795 | |||
796 | (cond | ||
797 | (*acdw/at-home* | ||
798 | (use-package su | ||
799 | :config | ||
800 | (su-mode 1)) | ||
801 | (use-package trashed | ||
802 | :custom | ||
803 | (delete-by-moving-to-trash t)) | ||
804 | (use-package exec-path-from-shell | ||
805 | :demand | ||
806 | :config | ||
807 | (exec-path-from-shell-initialize))) | ||
808 | ) | ||
809 | 1075 | ||
1076 | (use-package bongo | ||
1077 | :commands 'bongo) | ||
diff --git a/config.org b/config.org index 39d62ba..7b5de4e 100644 --- a/config.org +++ b/config.org | |||
@@ -1,9 +1,13 @@ | |||
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 init.el | 3 | #+PROPERTY: header-args :tangle init.el |
4 | #+EXPORT_FILE_NAME: README.md | ||
4 | #+OPTIONS: toc:nil | 5 | #+OPTIONS: toc:nil |
6 | #+OPTIONS: title:t | ||
5 | #+BANKRUPTCY_COUNT: 2 | 7 | #+BANKRUPTCY_COUNT: 2 |
6 | 8 | ||
9 | This is my Emacs configuration. It's also a literate =org-mode= file. Yeah, I'm a cool guy. | ||
10 | |||
7 | * About me | 11 | * About me |
8 | 12 | ||
9 | #+begin_src emacs-lisp | 13 | #+begin_src emacs-lisp |
@@ -731,6 +735,12 @@ I'm going to be honest -- most of this is a stab in the dark. | |||
731 | (org-pretty-entities t)) | 735 | (org-pretty-entities t)) |
732 | #+end_src | 736 | #+end_src |
733 | 737 | ||
738 | *** Enable markdown export | ||
739 | |||
740 | #+begin_src emacs-lisp | ||
741 | (require 'ox-md) | ||
742 | #+end_src | ||
743 | |||
734 | *** Ensure blank lines between headings and before contents | 744 | *** Ensure blank lines between headings and before contents |
735 | 745 | ||
736 | from [[https://github.com/alphapapa/unpackaged.el#ensure-blank-lines-between-headings-and-before-contents][unpackaged.el]] | 746 | from [[https://github.com/alphapapa/unpackaged.el#ensure-blank-lines-between-headings-and-before-contents][unpackaged.el]] |