diff options
-rw-r--r-- | README.md | 2030 | ||||
-rw-r--r-- | config.org | 1998 |
2 files changed, 1032 insertions, 2996 deletions
diff --git a/README.md b/README.md index 3f1adfa..fd7ed85 100644 --- a/README.md +++ b/README.md | |||
@@ -1,1656 +1,896 @@ | |||
1 | This is my Emacs configuration. It's also a literate `org-mode` file. Yeah, I'm a cool guy. | ||
2 | 1 | ||
3 | 2 | # Table of Contents | |
4 | # About me | 3 | |
5 | 4 | 1. [Pave the way](#org24d31f9) | |
6 | ;; init.el -*- lexical-binding: t -*- | 5 | 1. [Correct `exec-path`](#org82dd805) |
7 | (setq user-full-name "Case Duckworth" | 6 | 2. [Package management](#org947e1de) |
8 | user-mail-address "acdw@acdw.net") | 7 | 1. [Straight.el](#orgd711f6b) |
9 | 8 | 2. [Use-package](#org9392b5d) | |
10 | 9 | 3. [Extra use-package keywords](#orgc93ae09) | |
11 | # License | 10 | 3. [Customize variables](#org7cae7fe) |
12 | 11 | 1. [Put customizations in a separate file](#org126d855) | |
13 | Copyright © 2020 Case Duckworth <acdw@acdw.net> | 12 | 2. [A macro for ease of customization](#org2cf1d1a) |
14 | 13 | 4. [Keep a tidy `~/.emacs`](#orga6c0096) | |
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. | 14 | 2. [Look and Feel](#org1ecbcc5) |
16 | 15 | 1. [Simplify the UI](#org23fb19e) | |
17 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | 16 | 1. [Tool bars and menu bars](#orgad64258) |
18 | 17 | 2. [Scroll bars](#org9b2f49e) | |
19 | Version 2, December 2004 | 18 | 3. [Dialog boxen](#orgf1c5f65) |
20 | 19 | 4. [Shorten confirmations](#orgedf9e78) | |
21 | Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> | 20 | 5. [Remove the bell](#org1643ce2) |
22 | 21 | 6. [Tell Ediff to setup windows better](#org3996a6f) | |
23 | Everyone is permitted to copy and distribute verbatim or modified copies of | 22 | 2. [Tweak the remaining UI](#orgcdf874b) |
24 | this license document, and changing it is allowed as long as the name is changed. | 23 | 1. [Window dividers](#org187505c) |
25 | 24 | 2. [Fringes](#org3fd2bc6) | |
26 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | 25 | 3. [Minibuffer](#org8ff32ea) |
27 | 26 | 4. [Tabs](#orgef2a000) | |
28 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | 27 | 5. [Cursor](#orge57d1b2) |
29 | 28 | 6. [Buffer names](#orgb3f29a9) | |
30 | 0. You just DO WHAT THE FUCK YOU WANT TO. | 29 | 7. [Buffer boundaries](#org2627b1e) |
31 | 30 | 3. [Startup](#org1fc3c6d) | |
32 | 31 | 4. [Theme](#org207a1bd) | |
33 | ## Note on the license | 32 | 1. [Fonts](#org52f6c8c) |
34 | 33 | 3. [Interactivity](#org6cbcfe5) | |
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: | 34 | 1. [Selectrum](#org7f26398) |
36 | 35 | 2. [Prescient](#orgea8df9e) | |
37 | **SUE ME, RMS!** | 36 | 3. [Consult](#org8818eb9) |
38 | 37 | 4. [Marginalia](#orgd31a964) | |
39 | 38 | 5. [Ignore case](#org6e4913f) | |
40 | # Bootstrap | 39 | 6. [Search](#org416dd18) |
41 | 40 | 4. [Persistence](#orgb20768d) | |
42 | 41 | 1. [Save history](#org7dfee32) | |
43 | ## Original init.el | 42 | 2. [Save places in files](#org0f20005) |
44 | 43 | 3. [Recent files](#org6d1a477) | |
45 | This file replaces itself with the actual configuration when it's first run. For easy installation, *this* is the `init.el` file in git – and you probably want to keep it that way. To keep git from trying to update `init.el` when it's re-tangled, type this in the repo: | 44 | 1. [Easily navigate recent files](#org9368a6b) |
46 | 45 | 4. [Undo](#orgbb4f91a) | |
47 | git update-index --assume-unchanged init.el | 46 | 5. [Editing](#org52b008a) |
48 | 47 | 1. [Operate visually on lines](#orgce838ba) | |
49 | If, for some reason, you want to change this original file to be re-tracked, run this command: | 48 | 2. [Require a final newline](#org6f67996) |
50 | 49 | 3. [Killing & Yanking](#orga2bdb3e) | |
51 | git update-index --no-assume-unchanged init.el | 50 | 1. [Replace selection when typing](#org16c1a6b) |
52 | 51 | 2. [Save existing clipboard text into kill ring before replacing it](#orgea7fd73) | |
53 | Otherwise, here's the actual, original `init.el` that tangles this Org file and gets us going. | 52 | 4. [So long mode](#org27f430f) |
54 | 53 | 6. [Files](#org8cc8ee8) | |
55 | (require 'org) | 54 | 1. [Encoding](#org8ca2e9b) |
56 | (find-file (concat user-emacs-directory "config.org")) | 55 | 1. [UTF-8](#org54363a7) |
57 | (org-babel-tangle) | 56 | 2. [Convert all files to UNIX-style line endings](#orgeaed3bd) |
58 | (load-file (concat user-emacs-directory "early-init.el")) | 57 | 2. [Backups](#org7239c47) |
59 | (load-file (concat user-emacs-directory "init.el")) | 58 | 3. [Auto-saves](#org32fc658) |
60 | (byte-compile-file (concat user-emacs-directory "init.el")) | 59 | 4. [Revert files](#org94456e2) |
61 | 60 | 5. [Add a timestamp to files](#orgb586b3b) | |
62 | 61 | 7. [Programming](#org738fbd9) | |
63 | ### TODO What I should do instead | 62 | 1. [Which function are we in?](#org080eb2f) |
64 | 63 | 8. [Writing](#org6bf5097) | |
65 | Honestly, I should just change this "Original init.el" thing to a Makefile I can tangle in `config.org`, and track – since it won't be overwritten or need any special `git` invocations to stop tracking it, I can edit it as I think about what would work best. I could also maybe give it more of a "cross-platform" vibe by installing, say, `straight.el` in the Makefile on Windows. One day … | 64 | 1. [Visual Fill Column](#org6f01971) |
66 | 65 | 2. [Type nice-looking quote-type marks](#org03e747d) | |
67 | 66 | 9. [Applications](#org9528516) | |
68 | ## Tangling | 67 | 1. [Magit](#orgd2a60aa) |
69 | 68 | 10. [Appendices](#org7339cf2) | |
70 | After our first tangle, each time we edit `config.org` we want to go ahead and re-tangle our config. To that end, I've written `acdw/tangle-init`, which automatically tangles `config.org`. | 69 | 1. [Emacs' files](#org6070b2c) |
71 | 70 | 1. [init.el](#org0d720f6) | |
72 | (defun acdw/tangle-init () | 71 | 2. [early-init.el](#orgd6bffd2) |
73 | "If the current buffer is `config.org', tangle it, then compile | 72 | 2. [Ease tangling and loading of Emacs' init](#org9c5b437) |
74 | and load the resulting files." | 73 | 3. [License](#org1a4bb4d) |
75 | (when (equal (buffer-file-name) | 74 | 1. [Note on the license](#orga6047ee) |
76 | (expand-file-name | 75 | |
77 | (concat user-emacs-directory "config.org"))) | 76 | |
78 | ;; Tangle and load init.el and early-init.el | 77 | |
79 | (require 'async) | 78 | <a id="org24d31f9"></a> |
80 | (async-start | 79 | |
81 | (lambda () | 80 | # Pave the way |
82 | (let ((prog-mode-hook nil)) | 81 | |
83 | (require 'org) | 82 | |
84 | (org-babel-tangle-file | 83 | <a id="org82dd805"></a> |
85 | (expand-file-name | 84 | |
86 | (concat user-emacs-directory "config.org"))))) | 85 | ## Correct `exec-path` |
87 | (lambda (response) | 86 | |
88 | (acdw/load-init) | 87 | (let ((win-downloads "c:/Users/aduckworth/Downloads")) |
89 | (message "Tangled and loaded: %s" response))))) | 88 | (dolist (path `(;; Linux |
90 | 89 | ,(expand-file-name "bin" | |
91 | Since I want to tangle every time I save `config.org`, I've added `acdw/tangle-init` to a hook. | 90 | user-emacs-directory) |
92 | 91 | ,(expand-file-name "~/bin") | |
93 | (add-hook 'after-save-hook #'acdw/tangle-init) | 92 | ,(expand-file-name "~/.local/bin") |
94 | 93 | ,(expand-file-name "~/Scripts") | |
95 | Finally, I want an easier way to load the generated init files than the old `M-x load-file RET ~/.config/emacs/init.el RET`. So I've written `acdw/load-init` – which also gets called at the end of the async part of `acdw/tangle-init`. | 94 | ;; Windows |
96 | 95 | ,(expand-file-name "emacs/bin" | |
97 | (defun acdw/load-init () | 96 | win-downloads) |
98 | (interactive) | 97 | ,(expand-file-name "PortableGit/bin" |
99 | (load-file (expand-file-name | 98 | win-downloads) |
100 | (concat user-emacs-directory "early-init.el"))) | 99 | ,(expand-file-name "PortableGit/usr/bin" |
101 | (load-file (expand-file-name | 100 | win-downloads))) |
102 | (concat user-emacs-directory "init.el")))) | 101 | (when (file-exists-p path) |
103 | 102 | (add-to-list 'exec-path path)))) | |
104 | 103 | ||
105 | ## Miscellaneous bootstrappy stuff | 104 | |
106 | 105 | <a id="org947e1de"></a> | |
107 | 106 | ||
108 | ### Add directories to `load-path` | 107 | ## Package management |
109 | 108 | ||
110 | I also put lispy stuff in the `lisp/` subdirectory of my Emacs config, and under my SyncThing directory (for easy syncing ;P). | 109 | |
111 | 110 | <a id="orgd711f6b"></a> | |
112 | (dolist (dir `(,(concat user-emacs-directory | 111 | |
113 | (convert-standard-filename "lisp/")) | 112 | ### Straight.el |
114 | ,(expand-file-name "~/Sync/elisp/"))) | 113 | |
115 | (add-to-list 'load-path dir)) | 114 | (defvar bootstrap-version) |
116 | 115 | (let ((bootstrap-file | |
117 | 116 | (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory)) | |
118 | ### TODO Require my secrets | 117 | (bootstrap-version 5)) |
119 | 118 | (unless (file-exists-p bootstrap-file) | |
120 | While this is like, the *dumbest* way to do this, it's what I'm doing right now. I'm going to slap a TODO on here because I really should make it better – like, `auth-sources` hooked into KeePassXC somehow… ? Maybe follow [Bill Dietrich's setup](https://www.billdietrich.me/Authentication.html?expandall=1#KeePassXCandSecretService). | 119 | (with-current-buffer |
121 | |||
122 | (require 'acdw-secrets) | ||
123 | |||
124 | |||
125 | # Early initiation | ||
126 | |||
127 | Starting with version 27.1, Emacs loads `early-init.el` *before* `init.el`, setting up early stuff like package management, etc. Since I use an alternative package manager, I have to bootstrap it here. | ||
128 | |||
129 | Of course, I also want to set some really early-on settings here too, like `load-prefer-newer` – why not? | ||
130 | |||
131 | ;; early-init.el -*- lexical-binding: t; no-byte-compile: t -*- | ||
132 | (setq load-prefer-newer t) | ||
133 | |||
134 | |||
135 | ## Increase the garbage collector | ||
136 | |||
137 | Let's try to speed startup times by increasing the garbage collector's threshold while running init. Note the hook afterwards that restores it to a reasonable default. | ||
138 | |||
139 | (setq gc-cons-threshold (* 100 100 1000)) | ||
140 | |||
141 | (add-hook 'after-init-hook | ||
142 | (lambda () | ||
143 | (setq gc-cons-threshold (* 100 100 100)) | ||
144 | (message "gc-cons-threshold restored to %S" | ||
145 | gc-cons-threshold))) | ||
146 | |||
147 | |||
148 | ## Add more paths to the `exec-path` | ||
149 | |||
150 | When using Windows (at work), I need to use the PortableGit installation I've downloaded, since I don't have Admin privileges. | ||
151 | |||
152 | (when (eq system-type 'windows-nt) | ||
153 | (dolist (path '("c:/Users/aduckworth/Downloads/emacs/bin" | ||
154 | "C:/Users/aduckworth/Downloads/PortableGit/bin" | ||
155 | "C:/Users/aduckworth/Downloads/PortableGit/usr/bin")) | ||
156 | (add-to-list 'exec-path path))) | ||
157 | |||
158 | Elsewhere, I want to add a few more paths to the `exec-path` as well, since I store scripts in a couple of places at ~. | ||
159 | |||
160 | (dolist (path `(,(expand-file-name "bin" | ||
161 | user-emacs-directory) | ||
162 | ,(expand-file-name "~/bin") | ||
163 | ,(expand-file-name "~/.local/bin") | ||
164 | ,(expand-file-name "~/Scripts"))) | ||
165 | (add-to-list 'exec-path path)) | ||
166 | |||
167 | |||
168 | ## Bootstrap [straight.el](https://github.com/raxod502/straight.el) | ||
169 | |||
170 | So far, this is the best package manager I've used. It allows for *truly* declarative package management (if I don't specify a package here, it doesn't get loaded), easy installation from pretty much any source (as long as it's got a git repo), *and* it hooks into `use-package`! | ||
171 | |||
172 | The one annoying thing is that this bootstrap code doesn't work on Windows for some reason. I'm too lazy to really try and figure out why, so when I need to bootstrap on Windows (pretty rare, TBH), I just [download the master-branch zip file](https://github.com/raxod502/straight.el/archive/master.zip) and extract it to `~/.emacs.d/straight/repos/`. | ||
173 | |||
174 | (defvar bootstrap-version) | ||
175 | (let ((bootstrap-file | ||
176 | (expand-file-name "straight/repos/straight.el/bootstrap.el" | ||
177 | user-emacs-directory)) | ||
178 | (bootstrap-version 5)) | ||
179 | (unless (file-exists-p bootstrap-file) | ||
180 | (with-current-buffer | ||
181 | (url-retrieve-synchronously | ||
182 | "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el" | ||
183 | 'silent 'inhibit-cookies) | ||
184 | (goto-char (point-max)) | ||
185 | (eval-print-last-sexp))) | ||
186 | (load bootstrap-file nil 'nomessage)) | ||
187 | |||
188 | |||
189 | ## … but first: override the definition of straight.el to use the `develop` branch | ||
190 | |||
191 | For the KeePassXC bits later, I need the `develop` branch of straight, so I can pass a `:build` keyword. I can do this, but I have to override the recipe for `straight.el` itself. I do that here. For more information, see [the README](https://github.com/raxod502/straight.el#overriding-recipes). | ||
192 | |||
193 | (setq straight-repository-branch "develop") | ||
194 | |||
195 | (defvar bootstrap-version) | ||
196 | (let ((bootstrap-file | ||
197 | (expand-file-name "straight/repos/straight.el/bootstrap.el" | ||
198 | user-emacs-directory)) | ||
199 | (bootstrap-version 5)) | ||
200 | (unless (file-exists-p bootstrap-file) | ||
201 | (with-current-buffer | ||
202 | (url-retrieve-synchronously | 120 | (url-retrieve-synchronously |
203 | "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el" | 121 | "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el" |
204 | 'silent 'inhibit-cookies) | 122 | 'silent 'inhibit-cookies) |
205 | (goto-char (point-max)) | 123 | (goto-char (point-max)) |
206 | (eval-print-last-sexp))) | 124 | (eval-print-last-sexp))) |
207 | (load bootstrap-file nil 'nomessage)) | 125 | (load bootstrap-file nil 'nomessage)) |
208 | |||
209 | |||
210 | ## Use [use-package](https://jwiegley.github.io/use-package/) | ||
211 | |||
212 | Like I said, `straight.el` hooks into `use-package` easily. These two lines get the latter to use the former by default. | ||
213 | |||
214 | (setq straight-use-package-by-default t) | ||
215 | (straight-use-package 'use-package) | ||
216 | |||
217 | |||
218 | ## Keep `~/.emacs.d` tidy with [no-littering](https://github.com/emacscollective/no-littering) | ||
219 | |||
220 | I'll be honest – I don't really notice this package. But I think that's the point. It keeps Emacs (and packages) from throwing files all over the place, so I have a clean `ls -l`. Since I want to run this code as early as possible, I use the `straight-use-package` form instead of `use-package`. | ||
221 | |||
222 | (straight-use-package 'no-littering) | ||
223 | (require 'no-littering) | ||
224 | 126 | ||
225 | 127 | ||
226 | ## Additional `use-package` keywords | 128 | <a id="org9392b5d"></a> |
227 | 129 | ||
130 | ### Use-package | ||
228 | 131 | ||
229 | ### [:custom-update](https://github.com/a13/use-package-custom-update) | 132 | (setq straight-use-package-by-default t) |
133 | (straight-use-package 'use-package) | ||
230 | 134 | ||
231 | The `:custom-update` keyword lets me do this: | ||
232 | 135 | ||
233 | (use-package package | 136 | <a id="orgc93ae09"></a> |
234 | :custom-update | ||
235 | (package-list '(1 2 3))) | ||
236 | 137 | ||
237 | instead of this: | 138 | ### Extra use-package keywords |
238 | 139 | ||
239 | (use-package package | 140 | 1. :custom-update |
240 | :config | ||
241 | (add-to-list 'package-list '(1 2 3))) | ||
242 | 141 | ||
243 | It's not … perfect, but it's kind of nice. | 142 | (straight-use-package |
143 | '(use-package-custom-update | ||
144 | :host "github" | ||
145 | :repo "a13/use-package-custom-update")) | ||
146 | |||
147 | (require 'use-package-custom-update) | ||
244 | 148 | ||
245 | (use-package use-package-custom-update | ||
246 | :straight (use-package-custom-update | ||
247 | :host github | ||
248 | :repo "a13/use-package-custom-update")) | ||
249 | |||
250 | |||
251 | ## Setup [async](https://github.com/jwiegley/emacs-async) | ||
252 | |||
253 | I thought this was included in Emacs at first, but it's not – so we need to install and require it. | ||
254 | |||
255 | (straight-use-package 'async) | ||
256 | (require 'async) | ||
257 | |||
258 | |||
259 | # Macros | ||
260 | |||
261 | |||
262 | ## Customizing variables | ||
263 | 149 | ||
264 | I like `use-package` a lot, but I don't like using those shims you see in a lot of other Emacs configs where they use `(use-package emacs)` forms and stuff like that – it just feels dirty. Plus, `straight` gets confused about those packages sometimes. So, since I'm actually *configuring* Emacs in this Org file, which is nicely organized anyway, I can just set settings the old-school way. | 150 | <a id="org7cae7fe"></a> |
265 | 151 | ||
266 | Except. Using `setq` is actually *not* recommended any more, because `customize-set-variable` is more expressive and can include side-effects. However, not all settings are customizable, *and* `customize-set-variable` is like, way longer to type. So I've decided to write a little macro (my first!) to copy `use-package`'s `:custom` keyword, except … *outside* `use-package`. I've called it `cuss`, because I have a terrible sense of humor. | 152 | ## Customize variables |
267 | 153 | ||
268 | (defmacro cuss (var val) | ||
269 | "Basically `use-package''s `:custom', but without using either." | ||
270 | `(progn | ||
271 | (funcall (or (get ',var 'custom-set) #'set-default) | ||
272 | ',var ,val))) | ||
273 | 154 | ||
155 | <a id="org126d855"></a> | ||
274 | 156 | ||
275 | # Theme: [Modus](https://protesilaos.com/modus-themes/) | 157 | ### Put customizations in a separate file |
276 | 158 | ||
277 | Protesilaos Stavrou's *excellent* theme pair. At some point I'll probably write my own that's really minimal and does some funky stuff with faces, but until then, these really are the best I've used. | 159 | (setq custom-file |
160 | (expand-file-name "custom.el" user-emacs-directory)) | ||
278 | 161 | ||
279 | The big `dolist` form is from [his documentation](https://protesilaos.com/modus-themes/#h:a897b302-8e10-4a26-beab-3caaee1e1193); it basically allows me to configure both themes before loading them. I've tweaked his code a little to use `use-package`. | ||
280 | 162 | ||
281 | (defmacro modus-themes-format-sexp (sexp &rest objects) | 163 | <a id="org2cf1d1a"></a> |
282 | `(eval (read (format ,(format "%S" sexp) ,@objects)))) | ||
283 | 164 | ||
284 | (dolist (theme '("operandi" "vivendi")) | 165 | ### A macro for ease of customization |
285 | (modus-themes-format-sexp | ||
286 | (use-package modus-%1$s-theme | ||
287 | :custom | ||
288 | (modus-%1$s-theme-slanted-constructs t) | ||
289 | (modus-%1$s-theme-bold-constructs t) | ||
290 | (modus-%1$s-theme-fringes nil) | ||
291 | (modus-%1$s-theme-mode-line '3d) | ||
292 | (modus-%1$s-theme-syntax 'yellow-comments) | ||
293 | (modus-%1$s-theme-intense-hl-line nil) | ||
294 | (modus-%1$s-theme-intense-paren-match t) | ||
295 | (modus-%1$s-theme-links nil) | ||
296 | (modus-%1$s-theme-no-mixed-fonts nil) | ||
297 | (modus-%1$s-theme-prompts nil) | ||
298 | (modus-%1$s-theme-completions nil) | ||
299 | (modus-%1$s-theme-diffs nil) | ||
300 | (modus-%1$s-theme-org-blocks 'grayscale) | ||
301 | (modus-%1$s-theme-headings | ||
302 | '((1 . section) | ||
303 | (2 . line) | ||
304 | (t . rainbow-line))) | ||
305 | (modus-%1$s-theme-variable-pitch-headings t) | ||
306 | (modus-%1$s-theme-scale-headings t) | ||
307 | (modus-%1$s-theme-scale-1 1.1) | ||
308 | (modus-%1$s-theme-scale-2 1.15) | ||
309 | (modus-%1$s-theme-scale-3 1.21) | ||
310 | (modus-%1$s-theme-scale-4 1.27) | ||
311 | (modus-%1$s-theme-scale-5 1.33) | ||
312 | :custom-face | ||
313 | (font-lock-comment-face | ||
314 | ((t (:inherit (custom-comment italic variable-pitch)))))) | ||
315 | theme)) | ||
316 | 166 | ||
167 | (defmacro cuss (var val &optional docstring) | ||
168 | "Basically `:custom' from `use-package', broken out." | ||
169 | `(funcall (or (get ',var 'custom-set) #'set-default) | ||
170 | ',var ,val)) | ||
317 | 171 | ||
318 | ## Theme changer | ||
319 | 172 | ||
320 | I also want to switch themes between night and day. | 173 | <a id="orga6c0096"></a> |
321 | 174 | ||
322 | (use-package theme-changer | 175 | ## Keep a tidy `~/.emacs` |
323 | :custom | ||
324 | (calendar-latitude 30.39) | ||
325 | (calendar-longitude -91.83) | ||
326 | :config | ||
327 | (change-theme 'modus-operandi 'modus-vivendi)) | ||
328 | 176 | ||
177 | (use-package no-littering | ||
178 | :custom | ||
179 | (backup-directory-alist | ||
180 | `((".*" . ,(no-littering-expand-var-file-name "backup/")))) | ||
181 | (auto-save-file-name-transforms | ||
182 | `((".*" ,(no-littering-expand-var-file-name "autosaves/") t))) | ||
183 | (save-place-file | ||
184 | (no-littering-expand-var-file-name "places")) | ||
185 | (undo-fu-session-directory | ||
186 | (no-littering-expand-var-file-name "undos/")) | ||
187 | (undohist-directory | ||
188 | (no-littering-expand-var-file-name "undos/")) | ||
189 | (elpher-certificate-directory | ||
190 | (no-littering-expand-var-file-name "elpher-certificates/"))) | ||
191 | |||
192 | (dolist (dir '("backup" | ||
193 | "autosaves" | ||
194 | "undos" | ||
195 | "elpher-certificates")) | ||
196 | (make-directory (no-littering-expand-var-file-name dir) t)) | ||
329 | 197 | ||
330 | # Simplify GUI | ||
331 | 198 | ||
199 | <a id="org1ecbcc5"></a> | ||
332 | 200 | ||
333 | <a id="org1f12d35"></a> | 201 | # Look and Feel |
334 | 202 | ||
335 | ## Frame defaults | ||
336 | 203 | ||
337 | I want no toolbar, menubar, or scrollbars (ideally I'd have a vertical scrollbar if necessary, but apparently that's too much to ask the Emacs devs); and fringes and window dividers 2 pixels wide. | 204 | <a id="org23fb19e"></a> |
338 | 205 | ||
339 | (cuss default-frame-alist | 206 | ## Simplify the UI |
340 | '((tool-bar-lines . 0) | ||
341 | (menu-bar-lines . 0) | ||
342 | (vertical-scroll-bars . nil) | ||
343 | (horizontal-scroll-bars . nil) | ||
344 | (right-divider-width . 2) | ||
345 | (bottom-divider-width . 2) | ||
346 | (left-fringe-width . 2) | ||
347 | (right-fringe-width . 2))) | ||
348 | 207 | ||
349 | 208 | ||
350 | ## Minibuffer window/frame defaults | 209 | <a id="orgad64258"></a> |
351 | 210 | ||
352 | Of course, on the minibuffer, I want to make sure there's no scrollbar – even if I change my mind on `vertical-scroll-bars`, above. | 211 | ### Tool bars and menu bars |
353 | 212 | ||
354 | (cuss minibuffer-frame-alist | 213 | (cuss default-frame-alist |
355 | '((width . 80) | 214 | '((tool-bar-lines . 0) |
356 | (height . 2) | 215 | (menu-bar-lines . 0))) |
357 | (vertical-scrollbars . nil))) | 216 | |
217 | (menu-bar-mode -1) | ||
218 | (tool-bar-mode -1) | ||
358 | 219 | ||
359 | (set-window-scroll-bars (minibuffer-window) nil nil) | ||
360 | 220 | ||
221 | <a id="org9b2f49e"></a> | ||
361 | 222 | ||
362 | ## Remove unneeded GUI elements | 223 | ### Scroll bars |
363 | 224 | ||
364 | The [Frame Defaults](#org1f12d35) section sets up the frame to be free of visual clutter, but *this* section allows us to toggle that clutter's visibility easily, with one call to each of these functions. | 225 | (add-to-list 'default-frame-alist '(vertical-scroll-bars . nil)) |
226 | (scroll-bar-mode -1) | ||
227 | |||
228 | (add-to-list 'default-frame-alist '(horizontal-scroll-bars . nil)) | ||
229 | (horizontal-scroll-bar-mode -1) | ||
365 | 230 | ||
366 | (menu-bar-mode -1) | ||
367 | (tool-bar-mode -1) | ||
368 | (scroll-bar-mode -1) | ||
369 | (horizontal-scroll-bar-mode -1) | ||
370 | 231 | ||
232 | <a id="orgf1c5f65"></a> | ||
371 | 233 | ||
372 | ## Tabs | 234 | ### Dialog boxen |
373 | 235 | ||
374 | I'm kind of getting into Emacs tabs – but I like not showing the `tab-bar` when there's only one. | 236 | (cuss use-dialog-box nil) |
375 | 237 | ||
376 | (cuss tab-bar-show 1) | ||
377 | 238 | ||
378 | (cuss tab-bar-tab-name-function 'tab-bar-tab-name-current-with-count) | 239 | <a id="orgedf9e78"></a> |
379 | 240 | ||
241 | ### Shorten confirmations | ||
380 | 242 | ||
381 | ## Word wrap and operate visually | 243 | (fset 'yes-or-no-p #'y-or-n-p) |
382 | 244 | ||
383 | `global-visual-line-mode` is one of those which, in my opinion, should be a default. There's only one place I don't want to wrap words, and that's in `dired`, which I can set individually in its config. | ||
384 | 245 | ||
385 | (global-visual-line-mode 1) | 246 | <a id="org1643ce2"></a> |
386 | 247 | ||
248 | ### Remove the bell | ||
387 | 249 | ||
388 | ## Modeline | 250 | (cuss visible-bell (not (string= (system-name) "larry"))) |
389 | 251 | ||
390 | 252 | ||
391 | ### [smart-mode-line](https://github.com/Malabarba/smart-mode-line) | 253 | <a id="org3996a6f"></a> |
392 | 254 | ||
393 | (use-package smart-mode-line | 255 | ### Tell Ediff to setup windows better |
394 | :custom | ||
395 | (sml/no-confirm-load-theme t) | ||
396 | :config | ||
397 | (sml/setup)) | ||
398 | 256 | ||
257 | (declare-function ediff-setup-windows-plain "ediff-wind.el") | ||
258 | (cuss ediff-window-setup-function #'ediff-setup-windows-plain) | ||
399 | 259 | ||
400 | ### [rich-minority](https://github.com/Malabarba/rich-minority) | ||
401 | 260 | ||
402 | `smart-mode-line` comes with `rich-minority` for taking care of minor modes in the modeline, so I'm not going to *also* use `diminish` or anything. However, `rich-minority` has kind of a hinky way of adding modes to the whitelist, so I had to write my own function to do so. | 261 | <a id="orgcdf874b"></a> |
403 | 262 | ||
404 | This confuration means that, by default, no minor modes are shown; if you want a minor mode to be shown (like `word-count-mode` for me), call `(rm/whitelist-add "REGEXP")`. | 263 | ## Tweak the remaining UI |
405 | 264 | ||
406 | (defun rm/whitelist-add (regexp) | ||
407 | "Add a REGEXP to the whitelist for `rich-minority'." | ||
408 | (if (listp 'rm--whitelist-regexps) | ||
409 | (add-to-list 'rm--whitelist-regexps regexp) | ||
410 | (setq rm--whitelist-regexps `(,regexp))) | ||
411 | (setq rm-whitelist | ||
412 | (mapconcat 'identity rm--whitelist-regexps "\\|"))) | ||
413 | 265 | ||
414 | (use-package rich-minority | 266 | <a id="org187505c"></a> |
415 | :config | ||
416 | (rm/whitelist-add "^$")) | ||
417 | 267 | ||
268 | ### Window dividers | ||
418 | 269 | ||
419 | ## Minibuffer | 270 | (add-to-list 'default-frame-alist '(right-divider-width . 2)) |
271 | (add-to-list 'default-frame-alist '(bottom-divider-width . 2)) | ||
420 | 272 | ||
421 | 273 | ||
422 | ### Keep cursor from going into the prompt | 274 | <a id="org3fd2bc6"></a> |
423 | 275 | ||
424 | from [Ergo Emacs](http://ergoemacs.org/emacs/emacs_stop_cursor_enter_prompt.html). | 276 | ### Fringes |
425 | 277 | ||
426 | (cuss minibuffer-prompt-properties | 278 | (add-to-list 'default-frame-alist '(left-fringe-width . 2)) |
427 | '(read-only t cursor-intangible t face minibuffer-prompt)) | 279 | (add-to-list 'default-frame-alist '(right-fringe-width . 2)) |
428 | 280 | ||
429 | 281 | ||
430 | ## Show `^L` as a line | 282 | <a id="org8ff32ea"></a> |
431 | 283 | ||
432 | I like using the form-feed character to separate pages, it turns out. 'Tis nice. This package turns that character into a nice long line. | 284 | ### Minibuffer |
433 | 285 | ||
434 | (use-package form-feed | 286 | 1. Setup the minibuffer frame |
435 | :hook | ||
436 | ((text-mode prog-mode) . form-feed-mode)) | ||
437 | 287 | ||
288 | (cuss minibuffer-frame-alist | ||
289 | '((width . 80) | ||
290 | (height . 2) | ||
291 | (vertical-scrollbars . nil))) | ||
292 | |||
293 | (set-window-scroll-bars (minibuffer-window) nil nil) | ||
438 | 294 | ||
439 | ## Cursor | 295 | 2. Keep the cursor from going into the prompt |
440 | 296 | ||
441 | I want my cursor to be a bar in focused windows, but a hollow box in non-focused windows. | 297 | (cuss minibuffer-prompt-properties |
298 | '(read-only t cursor-intangible t face minibuffer-prompt)) | ||
442 | 299 | ||
443 | (cuss cursor-type 'bar) | ||
444 | (cuss cursor-in-non-selected-windows 'hollow) | ||
445 | 300 | ||
301 | <a id="orgef2a000"></a> | ||
446 | 302 | ||
447 | # Typesetting | 303 | ### Tabs |
448 | 304 | ||
305 | 1. Show the tabs as current buffer, plus window count | ||
449 | 306 | ||
450 | ## Fonts | 307 | (cuss tab-bar-tab-name-function #'tab-bar-tab-name-current-with-count) |
451 | 308 | ||
452 | This is the best way I've come up with to specify a number of different fonts that apply depending on what's applied. To be honest, I didn't really come up with the `font-candidate` function, though – I got it from the ["Testing if fonts are available?"](https://www.emacswiki.org/emacs/SetFonts#toc11) section of the SetFonts page on EmacsWiki. | 309 | 2. Only show the tab bar when there's more than one tab |
453 | 310 | ||
454 | 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. | 311 | (cuss tab-bar-show 1) |
455 | 312 | ||
456 | (require 'cl) | ||
457 | (defun font-candidate (&rest fonts) | ||
458 | (loop for font in fonts | ||
459 | when (find-font (font-spec :name font)) | ||
460 | return font)) | ||
461 | 313 | ||
462 | (defun acdw/setup-fonts () | 314 | <a id="orge57d1b2"></a> |
463 | "Setup fonts. This has to happen after the frame is set up for | ||
464 | the first time, so add it to `focus-in-hook'. It removes | ||
465 | itself." | ||
466 | (interactive) | ||
467 | (set-face-attribute 'default nil | ||
468 | :font | ||
469 | (font-candidate | ||
470 | "Libertinus Mono-11" | ||
471 | "Linux Libertine Mono O-11" | ||
472 | "Go Mono-11" | ||
473 | "Consolas-11")) | ||
474 | 315 | ||
475 | (set-face-attribute 'fixed-pitch nil | 316 | ### Cursor |
476 | :font | ||
477 | (font-candidate | ||
478 | "Libertinus Mono-11" | ||
479 | "Linux Libertine Mono O-11" | ||
480 | "Go Mono-11" | ||
481 | "Consolas-11")) | ||
482 | 317 | ||
483 | (set-face-attribute 'variable-pitch nil | 318 | (cuss cursor-type 'bar) |
484 | :font | 319 | (cuss cursor-in-non-selected-windows 'hollow) |
485 | (font-candidate | ||
486 | "Libertinus Serif-13" | ||
487 | "Linux Libertine O-12" | ||
488 | "Georgia-11")) | ||
489 | 320 | ||
490 | (remove-hook 'focus-in-hook #'acdw/setup-fonts)) | ||
491 | 321 | ||
492 | (add-hook 'focus-in-hook #'acdw/setup-fonts) | 322 | <a id="orgb3f29a9"></a> |
493 | 323 | ||
324 | ### Buffer names | ||
494 | 325 | ||
495 | ## [unicode-fonts](https://github.com/rolandwalker/unicode-fonts) | 326 | (require 'uniquify) |
327 | (cuss uniquify-buffer-name-style 'forward) | ||
496 | 328 | ||
497 | This does something similar to the above code, but for the entirety of the Unicode field (I think). | ||
498 | 329 | ||
499 | (use-package unicode-fonts | 330 | <a id="org2627b1e"></a> |
500 | :config | ||
501 | (unicode-fonts-setup)) | ||
502 | 331 | ||
332 | ### Buffer boundaries | ||
503 | 333 | ||
504 | ## Variable pitch faces | 334 | (cuss indicate-buffer-boundaries |
505 | 335 | '((top . right) | |
506 | One reason I like the Modus themes so much is that they have *excellent* support for variable-pitch faces, and mixing them with fixed-pitch faces in, say, Org Mode. That means I can enable `variable-pitch-mode` in all my `text-mode`-derived buffers. | 336 | (bottom . right) |
507 | 337 | (t . nil))) | |
508 | (add-hook 'text-mode-hook #'variable-pitch-mode) | 338 | |
509 | 339 | (cuss indicate-empty-lines t) | |
510 | |||
511 | ## Padding | ||
512 | |||
513 | This has been taken from ["Ricing Org Mode"](https://lepisma.xyz/2017/10/28/ricing-org-mode/) – of course, I want the typographic niceties everywhere. | ||
514 | |||
515 | (cuss line-spacing 0.1) | ||
516 | |||
517 | 340 | ||
518 | # Ease of use | ||
519 | 341 | ||
342 | <a id="org1fc3c6d"></a> | ||
520 | 343 | ||
521 | ## Startup | 344 | ## Startup |
522 | 345 | ||
523 | I want a minimal screen when I start Emacs. Based on the beauty of configs like [Nicolas Rougier's](https://github.com/rougier/elegant-emacs) [splash screen](https://github.com/rougier/emacs-splash) [experiments](https://github.com/rougier/nano-emacs), I might try my hand at some kind of splash screen or dashboard – but until then, a simple "Hi there!" will suffice 😎 | 346 | (cuss inhibit-startup-buffer-menu t) |
524 | 347 | (cuss inhibit-start-screen t) | |
525 | (cuss inhibit-startup-buffer-menu t) | 348 | (cuss initial-buffer-choice t) |
526 | (cuss inhibit-startup-screen t) | 349 | (cuss initial-scratch-message ";; Hi there!\n") |
527 | (cuss initial-buffer-choice t) | 350 | |
528 | (cuss initial-scratch-message ";; Hi there!\n") | 351 | |
529 | 352 | <a id="org207a1bd"></a> | |
530 | 353 | ||
531 | ## Completing-read niceties | 354 | ## Theme |
532 | 355 | ||
533 | `completing-read` is Emacs's selection-narrowing-slash-completion framework thing. There's a bunch of packages for it, including `ido`, `icomplete`, `ivy`, and `helm`. I use raxod52's `selectrum` and others, which *extend* without *clobbering* existing Emacs functionality. Plus they seem to run faster, at least on Windows. | 356 | (use-package modus-themes |
534 | 357 | :straight (modus-themes | |
535 | 358 | :host gitlab | |
536 | ### [selectrum](https://github.com/raxod502/selectrum) | 359 | :repo "protesilaos/modus-themes" |
537 | 360 | :branch "main") | |
538 | `selectrum` is the basic *sorting and selecting items from a list* functionality. It's a drop-in replacement for `ido` or the really basic tab-completion Emacs has for, say, `find-file`. | 361 | :custom |
539 | 362 | (modus-themes-slanted-constructs t) | |
540 | (use-package selectrum | 363 | (modus-themes-bold-constructs t) |
541 | :config | 364 | (modus-themes-fringes nil) |
542 | (selectrum-mode 1)) | 365 | (modus-themes-mode-line '3d) |
543 | 366 | (modus-themes-syntax 'yellow-comments) | |
544 | 367 | (modus-themes-intense-hl-line nil) | |
545 | ### [prescient](https://github.com/raxod502/prescient.el) | 368 | (modus-themes-paren-match 'intense-bold) |
546 | 369 | (modus-themes-links nil) | |
547 | `prescient` helps `selectrum` be more intelligent about sorting the candidates in a list – it's in charge of the *filtering and sorting* bit of `completing-read` and friends. It has an algorithm that works well enough for me, though I keep hearing about [orderless](https://github.com/oantolin/orderless), enough to maybe try it as well sometime. | 370 | (modus-themes-no-mixed-fonts nil) |
548 | 371 | (modus-themes-prompts nil) | |
549 | (use-package prescient | 372 | (modus-themes-completions nil) |
550 | :config | 373 | (modus-themes-diffs nil) |
551 | (prescient-persist-mode 1)) | 374 | (modus-themes-org-blocks 'grayscale) |
552 | 375 | (modus-themes-headings | |
553 | (use-package selectrum-prescient | 376 | '()) |
554 | :after (selectrum prescient) | 377 | (modus-themes-variable-pitch-headings t) |
555 | :config | 378 | (modus-themes-scale-headings t) |
556 | (selectrum-prescient-mode 1)) | 379 | (modus-themes-scale-1 1.1) |
557 | 380 | (modus-themes-scale-2 1.15) | |
558 | 381 | (modus-themes-scale-3 1.21) | |
559 | ### [consult](https://github.com/minad/cconsult) | 382 | (modus-themes-scale-4 1.27) |
560 | 383 | (modus-themes-scale-5 1.33) | |
561 | `consult` is the newest package I have with this setup, and it kind of brings the `selectrum` experience up to par with `ivy`'s – it provides functions that list, say, recently used files *alongside* buffers, allow you to search lines and go to them, etc. It seems pretty nice so far. | 384 | :custom-face |
385 | (font-lock-comment-face | ||
386 | ((t (:inherit (custom-comment italic variable-pitch))))) | ||
387 | :init | ||
388 | (load-theme 'modus-operandi t)) | ||
389 | |||
390 | |||
391 | <a id="org52f6c8c"></a> | ||
392 | |||
393 | ### Fonts | ||
394 | |||
395 | 1. Define fonts | ||
396 | |||
397 | (defun font-candidate (&rest fonts) | ||
398 | (catch :font | ||
399 | (dolist (font fonts) | ||
400 | (if (find-font (font-spec :name font)) | ||
401 | (throw :font font))))) | ||
402 | |||
403 | (defun acdw/setup-fonts () | ||
404 | "Setup fonts. This has to happen after the frame is setup for | ||
405 | the first time, so it should be added to `window-setup-hook'. It | ||
406 | removes itself from that hook." | ||
407 | (interactive) | ||
408 | (set-face-attribute 'default nil | ||
409 | :font | ||
410 | (font-candidate | ||
411 | "Libertinus Mono-11" | ||
412 | "Linux Libertine Mono O-11" | ||
413 | "Go Mono-10" | ||
414 | "Consolas-10")) | ||
415 | |||
416 | (set-face-attribute 'fixed-pitch nil | ||
417 | :font | ||
418 | (font-candidate | ||
419 | "Libertinus Mono-11" | ||
420 | "Linux Libertine Mono O-11" | ||
421 | "Go Mono-10" | ||
422 | "Consolas-10")) | ||
423 | |||
424 | (set-face-attribute 'variable-pitch nil | ||
425 | :font | ||
426 | (font-candidate | ||
427 | "Libertinus Serif-13" | ||
428 | "Linux Libertine O-12" | ||
429 | "Georgia-11")) | ||
430 | |||
431 | (remove-hook 'window-setup-hook #'acdw/setup-fonts)) | ||
432 | |||
433 | (add-hook 'window-setup-hook #'acdw/setup-fonts) | ||
434 | |||
435 | 2. Variable-pitch in text modes | ||
436 | |||
437 | (add-hook 'text-mode-hook #'variable-pitch-mode) | ||
438 | |||
439 | 3. Line spacing | ||
440 | |||
441 | (cuss line-spacing 0.1) | ||
442 | |||
443 | 4. Unicode fonts | ||
444 | |||
445 | (use-package unicode-fonts | ||
446 | :config | ||
447 | (unicode-fonts-setup)) | ||
448 | |||
449 | |||
450 | <a id="org6cbcfe5"></a> | ||
451 | |||
452 | # Interactivity | ||
453 | |||
454 | |||
455 | <a id="org7f26398"></a> | ||
456 | |||
457 | ## Selectrum | ||
458 | |||
459 | (use-package selectrum | ||
460 | :config | ||
461 | (selectrum-mode +1)) | ||
462 | |||
463 | |||
464 | <a id="orgea8df9e"></a> | ||
465 | |||
466 | ## Prescient | ||
467 | |||
468 | (use-package prescient | ||
469 | :config | ||
470 | (prescient-persist-mode +1)) | ||
471 | |||
472 | (use-package selectrum-prescient | ||
473 | :after (selectrum prescient) | ||
474 | :config | ||
475 | (selectrum-prescient-mode +1)) | ||
476 | |||
477 | |||
478 | <a id="org8818eb9"></a> | ||
562 | 479 | ||
563 | By the way, the [Reddit announcement thread for consult](https://www.reddit.com/r/emacs/comments/k3c0u7) has a great comment by the author detailing [the differences between different completing-read implementations](https://www.reddit.com/r/emacs/comments/k3c0u7/consult_counselswiper_alternative_for/ge460z3/) that actually is what convinced me to try `consult`. | 480 | ## Consult |
564 | 481 | ||
565 | (use-package consult | 482 | (use-package consult |
566 | :after (selectrum) | 483 | :after (selectrum) |
567 | :straight (consult | 484 | :straight (consult |
568 | :host github | 485 | :host github |
569 | :repo "minad/consult") | 486 | :repo "minad/consult") |
570 | :bind (("C-x b" . consult-buffer) | 487 | :bind |
571 | ("C-x 4 b" . consult-buffer-other-window) | 488 | (("C-x b" . consult-buffer) |
572 | ("C-x 5 b" . consult-buffer-other-frame) | 489 | ("C-x 4 b" . consult-buffer-other-window) |
573 | ("M-g o" . consult-outline) | 490 | ("C-x 5 b" . consult-buffer-other-frame) |
574 | ("M-g l" . consult-line) | 491 | ("M-g o" . consult-outline) |
575 | ("M-y" . consult-yank-pop) | 492 | ("M-g l" . consult-line) |
576 | ("<help> a" . consult-apropos)) | 493 | ("M-y" . consult-yank-pop) |
577 | :init | 494 | ("<help> a" . consult-apropos)) |
578 | (fset 'multi-occur #'consult-multi-occur) | 495 | :init |
579 | (consult-annotate-mode) | 496 | (fset 'multi-occur #'consult-multi-occur)) |
580 | :config | ||
581 | (setf (alist-get 'execute-extended-command consult-annotate-alist) | ||
582 | #'consult-annotate-command-full)) | ||
583 | |||
584 | |||
585 | ### Ignore case | ||
586 | |||
587 | I don't like holding the Shift key if I can help it. | ||
588 | |||
589 | (cuss completion-ignore-case t) | ||
590 | (cuss read-buffer-completion-ignore-case t) | ||
591 | (cuss read-file-name-completion-ignore-case t) | ||
592 | |||
593 | |||
594 | ## [ctrlf](https://github.com/raxod502/ctrlf) | ||
595 | |||
596 | The biggest reason I use this over the default functionality of `C-s` is that `ctrlf-forward-*` wraps the search around by default. | ||
597 | |||
598 | (use-package ctrlf | ||
599 | :custom | ||
600 | (ctrlf-show-match-count-at-eol nil) | ||
601 | :bind | ||
602 | ("C-s" . ctrlf-forward-regexp) | ||
603 | ("C-r" . ctrlf-backward-regexp) | ||
604 | ("C-M-s" . ctrlf-forward-literal) | ||
605 | ("C-M-r" . ctrlf-backward-literal) | ||
606 | :config | ||
607 | (ctrlf-mode 1)) | ||
608 | |||
609 | |||
610 | ## [which-key](https://github.com/justbur/emacs-which-key) | ||
611 | |||
612 | This package is really helpful for discovering functionality. When I get more adept in my Emacs-fu, I might remove this. | ||
613 | |||
614 | (use-package which-key | ||
615 | :custom | ||
616 | (which-key-popup-type 'minibuffer) | ||
617 | :config | ||
618 | (which-key-mode)) | ||
619 | |||
620 | |||
621 | ## Miscellaneous settings | ||
622 | |||
623 | Maybe a better title for this section is **Other settings** – or maybe I should put them somewhere else entirely. | ||
624 | |||
625 | |||
626 | ### Set `view-mode` when in a read-only file | ||
627 | 497 | ||
628 | `view-mode` gives easy-to-use keybindings, like Space for page-down, etc., which are nice to have when you can't edit the file anyway. | ||
629 | 498 | ||
630 | (cuss view-read-only t) | 499 | <a id="orgd31a964"></a> |
631 | 500 | ||
501 | ## Marginalia | ||
632 | 502 | ||
633 | ### Don't use dialog boxen | 503 | (use-package marginalia |
634 | 504 | :straight (marginalia | |
635 | (cuss use-dialog-box nil) | 505 | :host github |
636 | 506 | :repo "minad/marginalia" | |
637 | 507 | :branch "main") | |
638 | ### Enable all functions | 508 | :custom |
639 | 509 | (marginalia-annotators | |
640 | By default, Emacs disables some commands, because NeWbIeS wOuLd GeT cOnFuSeD or some ish. I just want to use the dang editor! | 510 | '((command . marginalia-annotate-command-full) |
641 | 511 | (customize-group . marginalia-annotate-customize-group) | |
642 | (cuss disabled-command-function nil) | 512 | (variable . marginalia-annotate-variable) |
643 | 513 | (face . marginalia-annotate-face) | |
644 | 514 | (symbol . marginalia-annotate-symbol) | |
645 | ### Shorter confirmations | 515 | (variable . marginalia-annotate-variable) |
646 | 516 | (package . marginalia-annotate-package))) | |
647 | Instead of making me type *yes* or *no*, just let me hit the *y* or *n* key. | 517 | :init |
648 | 518 | (marginalia-mode +1)) | |
649 | (fset 'yes-or-no-p #'y-or-n-p) | ||
650 | |||
651 | |||
652 | ### Uniquify buffer names | ||
653 | |||
654 | This names buffers with the same basename (e.g., `~/.config/emacs/config.org` and `~/.emacs.d/config.org`) in a better way than the default (`config.org<1>`, etc). | ||
655 | |||
656 | (require 'uniquify) | ||
657 | (cuss uniquify-buffer-name-style 'forward) | ||
658 | |||
659 | |||
660 | ### Show buffer boundaries | ||
661 | |||
662 | These little L-shaped graphics at the top and bottom of buffers don't do anything, but I like 'em. | ||
663 | |||
664 | (cuss indicate-buffer-boundaries | ||
665 | '((top . right) | ||
666 | (bottom . right) | ||
667 | (t . nil))) | ||
668 | |||
669 | |||
670 | ### Hippie expand | ||
671 | |||
672 | At some point, will probably replace with [company](https://company-mode.github.io/). | ||
673 | |||
674 | (global-set-key (kbd "M-/") 'hippie-expand) | ||
675 | |||
676 | |||
677 | ### "[better defaults](https://git.sr.ht/~technomancy/better-defaults/tree/master/better-defaults.el)" | ||
678 | |||
679 | Most of these come from technomancy's repo, linked above, just copy-pasted into here. | ||
680 | |||
681 | (cuss save-interprogram-paste-before-kill t) | ||
682 | (cuss apropos-do-all t) | ||
683 | (cuss mouse-yank-at-point t) | ||
684 | (cuss require-final-newline t) | ||
685 | (cuss visible-bell (not (string= (system-name) "larry"))) | ||
686 | (cuss ediff-window-setup-function #'ediff-setup-windows-plain) | ||
687 | |||
688 | 1. Zap-up-to-char, not zap-to-char | ||
689 | |||
690 | Similarly to `ibuffer`, this is a Better default™. | ||
691 | |||
692 | (autoload 'zap-up-to-char "misc" | ||
693 | "Kill up to, but not including, ARGth occurrence of CHAR." t) | ||
694 | |||
695 | (global-set-key (kbd "M-z") 'zap-up-to-char) | ||
696 | |||
697 | 2. iBuffer | ||
698 | |||
699 | A Better Default™ for `C-x C-b`. I don't really use this, but everyone says it's worth it, so it's there. | ||
700 | |||
701 | (global-set-key (kbd "C-x C-b") 'ibuffer) | ||
702 | 519 | ||
703 | 520 | ||
704 | ### So-long-mode | 521 | <a id="org6e4913f"></a> |
705 | 522 | ||
706 | I figure, why not go ahead and make Emacs deal with really long lines better? Can't hurt, right? | 523 | ## Ignore case |
707 | 524 | ||
708 | (if (boundp 'global-so-long-mode) | 525 | (cuss completion-ignore-case t) |
709 | (global-so-long-mode)) | 526 | (cuss read-buffer-completion-ignore-case t) |
527 | (cuss read-file-name-completion-ignore-case t) | ||
710 | 528 | ||
711 | 529 | ||
712 | ### Change `just-one-space` to `cycle-space` | 530 | <a id="org416dd18"></a> |
713 | 531 | ||
714 | I keep forgetting to actually *use* this keybind (I think it's `M-SPC`?), but cycling spacing seems *way* more useful than the default `just-one-space` function. | 532 | ## Search |
715 | 533 | ||
716 | (defun acdw/cycle-spacing-1 () | 534 | (use-package ctrlf |
717 | (interactive) | 535 | :custom |
718 | (cycle-spacing -1)) | 536 | (ctrlf-show-match-count-at-eol nil) |
537 | :bind | ||
538 | ("C-s" . ctrlf-forward-regexp) | ||
539 | ("C-r" . ctrlf-backward-regexp) | ||
540 | ("C-M-s" . ctrlf-forward-literal) | ||
541 | ("C-M-r" . ctrlf-backward-literal) | ||
542 | :config | ||
543 | (ctrlf-mode +1)) | ||
719 | 544 | ||
720 | (bind-key [remap just-one-space] #'acdw/cycle-spacing-1) | ||
721 | 545 | ||
546 | <a id="orgb20768d"></a> | ||
722 | 547 | ||
723 | # Persistence | 548 | # Persistence |
724 | 549 | ||
725 | Honestly, persistence across sessions was one of the best things about my well-tuned Vim setup. Here's where I try to repeat that with Emacs. | ||
726 | |||
727 | |||
728 | ## Auto-saves with [super-save](https://github.com/bbatsov/super-save) | ||
729 | |||
730 | The default `auto-save` functionality isn't … *enough* for me. I want to *actually* save the files, and I don't care about `#file#` stuff. So … I use this package. | ||
731 | |||
732 | (use-package super-save | ||
733 | :custom | ||
734 | (auto-save-default nil) | ||
735 | (super-save-exclue '(".gpg")) | ||
736 | :config | ||
737 | (super-save-mode 1)) | ||
738 | |||
739 | |||
740 | ## Backup files | ||
741 | |||
742 | To be honest, I probably don't need backup files at all. At some point, I will probably delete this. | ||
743 | |||
744 | (cuss backup-directory-alist | ||
745 | `((".*" . ,(no-littering-expand-var-file-name "backup/")))) | ||
746 | |||
747 | (cuss backup-by-copying 1) | ||
748 | (cuss delete-old-versions -1) | ||
749 | (cuss version-control t) | ||
750 | (cuss vc-make-backup-files t) | ||
751 | |||
752 | |||
753 | ## Recent files | ||
754 | |||
755 | Since I apparently *only* edit my `config.org`, this is also probably not necessary – I'd be better off just adding a `(find-file (concat (user-emacs-directory "config.org")))` at the end 😎 | ||
756 | |||
757 | But until then, it's really nice to have a `recentf` list. | ||
758 | |||
759 | (require 'recentf) | ||
760 | |||
761 | (add-to-list 'recentf-exclude | ||
762 | '(no-littering-var-directory | ||
763 | no-littering-etc-directory)) | ||
764 | |||
765 | (cuss recentf-max-menu-items 100) | ||
766 | (cuss recentf-max-saved-items 100) | ||
767 | |||
768 | (recentf-mode 1) | ||
769 | |||
770 | |||
771 | ### Easily navigate recent files | ||
772 | |||
773 | Now I'm going through this, I might not need this function any more. I'll have to see how `consult` goes. | ||
774 | |||
775 | (defun recentf-find-file () | ||
776 | "Find a recent file using `completing-read'." | ||
777 | (interactive) | ||
778 | (let ((file (completing-read "Recent file: " recentf-list nil t))) | ||
779 | (when file | ||
780 | (find-file file)))) | ||
781 | |||
782 | (bind-key "C-x C-r" #'recentf-find-file) | ||
783 | |||
784 | |||
785 | ## Save places in visited files | ||
786 | |||
787 | (require 'saveplace) | ||
788 | |||
789 | (cuss save-place-file (no-littering-expand-var-file-name "places")) | ||
790 | |||
791 | (cuss save-place-forget-unreadable-files | ||
792 | (not (eq system-type 'windows-nt))) | ||
793 | |||
794 | (save-place-mode 1) | ||
795 | 550 | ||
551 | <a id="org7dfee32"></a> | ||
796 | 552 | ||
797 | ## Save history | 553 | ## Save history |
798 | 554 | ||
799 | (require 'savehist) | 555 | (require 'savehist) |
800 | 556 | ||
801 | (cuss savehist-additional-variables | 557 | (cuss savehist-additional-variables |
802 | '(kill-ring | 558 | '(kill-ring |
803 | search-ring | 559 | search-ring |
804 | regexp-search-ring)) | 560 | regexp-search-ring)) |
561 | |||
562 | (cuss savehist-save-minibuffer-history t) | ||
563 | |||
564 | (cuss history-length t) | ||
565 | |||
566 | (cuss history-delete-duplicates t) | ||
567 | |||
568 | (savehist-mode +1) | ||
805 | 569 | ||
806 | (cuss savehist-save-minibuffer-history t) | ||
807 | |||
808 | (cuss history-length t) | ||
809 | |||
810 | (cuss history-delete-duplicates t) | ||
811 | 570 | ||
812 | (savehist-mode 1) | 571 | <a id="org0f20005"></a> |
813 | 572 | ||
573 | ## Save places in files | ||
814 | 574 | ||
815 | ## Undo: [undo-fu-session](https://gitlab.com/ideasman42/emacs-undo-fu-session) | 575 | (require 'saveplace) |
576 | |||
577 | (cuss save-place-forget-unreadable-files | ||
578 | (not (eq system-type 'windows-nt))) | ||
579 | |||
580 | (save-place-mode 1) | ||
816 | 581 | ||
817 | The other Killer Feature of Neovim when I used it was the perisistent undo. I *think* this works the same. Honestly, undo is giving me a little grief recently; I need to look into it. | ||
818 | 582 | ||
819 | Note to self: if I *do* switch away from `undo-fu`, look at [undohist](https://github.com/emacsorphanage/undohist). | 583 | <a id="org6d1a477"></a> |
820 | 584 | ||
821 | (use-package undo-fu-session | 585 | ## Recent files |
822 | :after (no-littering undo-fu) | ||
823 | :custom | ||
824 | (undo-fu-session-incompatible-files | ||
825 | '("COMMIT_EDITMSG\\'" | ||
826 | "/git-rebase-todo\\'")) | ||
827 | (undo-fu-session-directory | ||
828 | (no-littering-expand-var-file-name "undos/")) | ||
829 | :config | ||
830 | (global-undo-fu-session-mode 1)) | ||
831 | |||
832 | |||
833 | # General editing | ||
834 | |||
835 | |||
836 | ## File encoding | ||
837 | |||
838 | I just want to use UTF-8 everywhere, and end all files with UNIX line endings (`^J`, or `LF`). Hell, even Windows Notepad correctly reads UNIX files nowadays (though of course you can't configure it to *save* the files in UNIX-mode). However, since Emacs is ~40 years old, it has a billion different ways to set encodings. This is my best attempt at setting everything up how I want it. | ||
839 | |||
840 | I'm going to be honest – most of this is a stab in the dark. | ||
841 | |||
842 | (set-language-environment 'utf-8) | ||
843 | (set-terminal-coding-system 'utf-8) | ||
844 | (cuss locale-coding-system 'utf-8) | ||
845 | (set-default-coding-systems 'utf-8) | ||
846 | (set-selection-coding-system 'utf-8) | ||
847 | (prefer-coding-system 'utf-8) | ||
848 | |||
849 | ;; from https://www.emacswiki.org/emacs/EndOfLineTips | ||
850 | |||
851 | (defun acdw/no-junk-please-were-unixish () | ||
852 | "Convert line endings to UNIX, dammit." | ||
853 | (let ((coding-str (symbol-name buffer-file-coding-system))) | ||
854 | (when (string-match "-\\(?:dos\\|mac\\)$" coding-str) | ||
855 | (set-buffer-file-coding-system 'unix)))) | ||
856 | |||
857 | (add-hook 'find-file-hooks #'acdw/no-junk-please-were-unixish) | ||
858 | |||
859 | |||
860 | ## [undo-fu](https://gitlab.com/ideasman42/emacs-undo-fu) | ||
861 | |||
862 | I've heard that Emacs' undo is weird, so here I am, trying to make it …. *less* weird. I keep forgetting I've installed this though, so I might uninstall it at some point. | ||
863 | |||
864 | (use-package undo-fu | ||
865 | :bind | ||
866 | ("C-/" . undo-fu-only-undo) | ||
867 | ("C-?" . undo-fu-only-redo)) | ||
868 | |||
869 | |||
870 | ## Find/replace: [visual-regexp](https://github.com/benma/visual-regexp.el) | ||
871 | |||
872 | Another replacement for a Killer Feature in Neovim – the ease of regexp find/replace was so wonderful, because I could easily see *what* I'd be changing with a `%s` command, as well as *how* it'd change. This works… pretty similarly. It could be a little better. | ||
873 | |||
874 | (use-package visual-regexp | ||
875 | :bind | ||
876 | ("C-c r" . 'vr/replace) | ||
877 | ("C-c q" . 'vr/query-replace)) | ||
878 | |||
879 | |||
880 | ## Visual editing | ||
881 | |||
882 | |||
883 | ### [volatile-highlights](https://github.com/k-talo/volatile-highlights.el) | ||
884 | 586 | ||
885 | Highlights text changed by certain operations. | 587 | (require 'recentf) |
588 | |||
589 | (cuss recentf-max-menu-items 100) | ||
590 | (cuss recentf-max-saved-items 100) | ||
591 | |||
592 | (with-eval-after-load 'no-littering | ||
593 | (add-to-list 'recentf-exclude no-littering-var-directory) | ||
594 | (add-to-list 'recentf-exclude no-littering-etc-directory)) | ||
595 | |||
596 | (recentf-mode 1) | ||
886 | 597 | ||
887 | (use-package volatile-highlights | ||
888 | :config | ||
889 | (volatile-highlights-mode 1)) | ||
890 | 598 | ||
599 | <a id="org9368a6b"></a> | ||
891 | 600 | ||
892 | ### [expand-region](https://github.com/magnars/expand-region.el) | 601 | ### Easily navigate recent files |
893 | 602 | ||
894 | I don't use this a *ton*, but not because it's not useful – I just forget it's there sometimes. | 603 | (defun recentf-find-file () |
604 | "Find a recent file using `completing-read'." | ||
605 | (interactive) | ||
606 | (let ((file (completing-read "Recent file: " recentf-list nil t))) | ||
607 | (when file | ||
608 | (find-file file)))) | ||
609 | |||
610 | (global-set-key (kbd "C-x C-r") #'recentf-find-file) | ||
895 | 611 | ||
896 | Basically, it allows you to do like a Kakoune-style incremental widening of the selection by semantic units. | ||
897 | 612 | ||
898 | (use-package expand-region | 613 | <a id="orgbb4f91a"></a> |
899 | :bind | ||
900 | ("C-=" . er/expand-region) | ||
901 | ("C-+" . er/contract-region)) | ||
902 | 614 | ||
615 | ## Undo | ||
903 | 616 | ||
904 | ## Clean up white space on save | 617 | (use-package undohist |
618 | :config | ||
619 | (undohist-initialize)) | ||
905 | 620 | ||
906 | I'm not sure if I'll *keep* this forever, because in combination with `super-save` I lose the final "thinking spaces" when I shift contexts to another window. | ||
907 | 621 | ||
908 | (add-hook 'before-save-hook #'whitespace-cleanup) | 622 | <a id="org52b008a"></a> |
909 | (add-hook 'before-save-hook #'delete-trailing-whitespace) | ||
910 | 623 | ||
624 | # Editing | ||
911 | 625 | ||
912 | ## Automatically revert a file to what it is on disk | ||
913 | 626 | ||
914 | Revert a buffer to reflect what's on disk if it's changed outside of Emacs. | 627 | <a id="orgce838ba"></a> |
915 | 628 | ||
916 | (global-auto-revert-mode 1) | 629 | ## Operate visually on lines |
917 | 630 | ||
631 | (global-visual-line-mode +1) | ||
918 | 632 | ||
919 | # Writing | ||
920 | 633 | ||
921 | Configurations related to writing prose or verse. | 634 | <a id="org6f67996"></a> |
922 | 635 | ||
636 | ## Require a final newline | ||
923 | 637 | ||
924 | ## Word count: [wc-mode](https://github.com/bnbeckwith/wc-mode) | 638 | (cuss require-final-newline t) |
925 | 639 | ||
926 | (use-package wc-mode | ||
927 | :config | ||
928 | (rm/whitelist-add "WC") | ||
929 | :hook text-mode) | ||
930 | 640 | ||
641 | <a id="orga2bdb3e"></a> | ||
931 | 642 | ||
932 | ## [visual-fill-column-mode](https://github.com/joostkremers/visual-fill-column) | 643 | ## Killing & Yanking |
933 | 644 | ||
934 | Center the text part of the frame within a `fill-column`-sized area in the frame as a whole. | ||
935 | 645 | ||
936 | (use-package visual-fill-column | 646 | <a id="org16c1a6b"></a> |
937 | :custom | ||
938 | (split-window-preferred-function | ||
939 | 'visual-fill-column-split-window-sensibly) | ||
940 | (visual-fill-column-center-text t) | ||
941 | (fill-column 80) | ||
942 | :config | ||
943 | (advice-add 'text-scale-adjust | ||
944 | :after #'visual-fill-column-adjust) | ||
945 | :hook | ||
946 | (text-mode . visual-fill-column-mode)) | ||
947 | |||
948 | |||
949 | ### Fix mouse bindings | ||
950 | |||
951 | In `visual-fill-column-mode`, mouse bindings on the margins don't work. In fact, they don't work when *not* in `visual-fill-column-mode`. Let's bind those bindings. | ||
952 | |||
953 | (dolist (vec '([left-margin wheel-down] | ||
954 | [left-margin mouse-5] | ||
955 | [right-margin wheel-down] | ||
956 | [right-margin mouse-5])) | ||
957 | (bind-key vec 'scroll-down-command)) | ||
958 | |||
959 | (dolist (vec '([left-margin wheel-up] | ||
960 | [left-margin mouse-4] | ||
961 | [right-margin wheel-up] | ||
962 | [right-margin mouse-4])) | ||
963 | (bind-key vec 'scroll-up-command)) | ||
964 | |||
965 | |||
966 | ## [org-mode](https://orgmode.org/) | ||
967 | |||
968 | Pretty self-explanatory, I think… | ||
969 | |||
970 | I need to break this config up and like, comment it better. | ||
971 | |||
972 | (use-package org | ||
973 | :custom | ||
974 | (org-startup-indented t) | ||
975 | (org-src-tab-acts-natively t) | ||
976 | (org-hide-emphasis-markers t) | ||
977 | (org-fontify-done-headline t) | ||
978 | (org-fontify-whole-heading-line t) | ||
979 | (org-fontify-quote-and-verse-blocks t) | ||
980 | (org-hide-leading-stars t) | ||
981 | (org-hidden-keywords '(author date title)) | ||
982 | (org-src-window-setup 'current-window) | ||
983 | (org-pretty-entities t) | ||
984 | (org-ellipsis " ⋯ ")) | ||
985 | |||
986 | |||
987 | ### Make bullets look like centered dots | ||
988 | |||
989 | from [zzamboni.org](https://zzamboni.org/post/beautifying-org-mode-in-emacs/) | ||
990 | |||
991 | (font-lock-add-keywords | ||
992 | 'org-mode | ||
993 | '(("^ *\\([-+]\\) " | ||
994 | (0 (prog1 () | ||
995 | (compose-region (match-beginning 1) | ||
996 | (match-end 1) | ||
997 | "•")))))) | ||
998 | |||
999 | |||
1000 | ### [org-superstar](https://github.com/integral-dw/org-superstar-mode) | ||
1001 | |||
1002 | (use-package org-superstar | ||
1003 | :custom | ||
1004 | (org-superstar-headline-bullets-list | ||
1005 | '(?❧ ?✪ ?③ ?④ ?⑤ ?⑥ ?⑦ ?⑧ ?⑨ ?●)) | ||
1006 | (org-superstar-cycle-headline-bullets nil) | ||
1007 | (org-superstar-item-bullet-alist | ||
1008 | '((?* . ?★) | ||
1009 | (?+ . ?‣) | ||
1010 | (?- . ?•))) | ||
1011 | :custom-face | ||
1012 | (org-superstar-header-bullet | ||
1013 | ((t (:weight normal)))) | ||
1014 | :hook | ||
1015 | (org-mode . org-superstar-mode)) | ||
1016 | |||
1017 | |||
1018 | ### Enable markdown export | ||
1019 | |||
1020 | (require 'ox-md) | ||
1021 | |||
1022 | |||
1023 | ### Ensure blank lines between headings and before contents | ||
1024 | |||
1025 | from [unpackaged.el](https://github.com/alphapapa/unpackaged.el#ensure-blank-lines-between-headings-and-before-contents) | ||
1026 | |||
1027 | ;;;###autoload | ||
1028 | (defun unpackaged/org-fix-blank-lines (&optional prefix) | ||
1029 | "Ensure that blank lines exist between headings and between | ||
1030 | headings and their contents. With prefix, operate on whole | ||
1031 | buffer. Ensures that blank lines exist after each headings's | ||
1032 | drawers." | ||
1033 | (interactive "P") | ||
1034 | (org-map-entries | ||
1035 | (lambda () | ||
1036 | (org-with-wide-buffer | ||
1037 | ;; `org-map-entries' narrows the buffer, which prevents us | ||
1038 | ;; from seeing newlines before the current heading, so we | ||
1039 | ;; do this part widened. | ||
1040 | (while (not (looking-back "\n\n" nil)) | ||
1041 | ;; Insert blank lines before heading. | ||
1042 | (insert "\n"))) | ||
1043 | (let ((end (org-entry-end-position))) | ||
1044 | ;; Insert blank lines before entry content. | ||
1045 | (forward-line) | ||
1046 | (while (and (org-at-planning-p) | ||
1047 | (< (point) (point-max))) | ||
1048 | ;; Skip planning lines | ||
1049 | (forward-line)) | ||
1050 | (while (re-search-forward org-drawer-regexp end t) | ||
1051 | ;; Skip drawers. You might think that | ||
1052 | ;; `org-at-drawer-p' would suffice, but for some reason | ||
1053 | ;; it doesn't work correctly when operating on hidden | ||
1054 | ;; text. This works, taken from | ||
1055 | ;; `org-agenda-get-some-entry-text'. | ||
1056 | (re-search-forward "^[ \t]*:END:.*\n?" end t) | ||
1057 | (goto-char (match-end 0))) | ||
1058 | (unless (or (= (point) (point-max)) | ||
1059 | (org-at-heading-p) | ||
1060 | (looking-at-p "\n")) | ||
1061 | (insert "\n")))) | ||
1062 | t (if prefix | ||
1063 | nil | ||
1064 | 'tree))) | ||
1065 | |||
1066 | |||
1067 | ### `org-return-dwim` | ||
1068 | |||
1069 | from [unpackaged.el](https://github.com/alphapapa/unpackaged.el#org-return-dwim) | ||
1070 | |||
1071 | (defun unpackaged/org-element-descendant-of (type element) | ||
1072 | "Return non-nil if ELEMENT is a descendant of TYPE. | ||
1073 | TYPE should be an element type, like `item' or `paragraph'. | ||
1074 | ELEMENT should be a list like that returned by | ||
1075 | `org-element-context'." | ||
1076 | ;; MAYBE: Use `org-element-lineage'. | ||
1077 | (when-let* ((parent (org-element-property :parent element))) | ||
1078 | (or (eq type (car parent)) | ||
1079 | (unpackaged/org-element-descendant-of type parent)))) | ||
1080 | |||
1081 | ;;;###autoload | ||
1082 | (defun unpackaged/org-return-dwim (&optional default) | ||
1083 | "A helpful replacement for `org-return'. With prefix, call `org-return'. | ||
1084 | |||
1085 | On headings, move point to position after entry content. In | ||
1086 | lists, insert a new item or end the list, with checkbox if | ||
1087 | appropriate. In tables, insert a new row or end the table." | ||
1088 | ;; Inspired by John Kitchin: http://kitchingroup.cheme.cmu.edu/blog/2017/04/09/A-better-return-in-org-mode/ | ||
1089 | (interactive "P") | ||
1090 | (if default | ||
1091 | (org-return) | ||
1092 | (cond | ||
1093 | ;; Act depending on context around point. | ||
1094 | |||
1095 | ;; NOTE: I prefer RET to not follow links, but by uncommenting this block, | ||
1096 | ;; links will be followed. | ||
1097 | |||
1098 | ;; ((eq 'link (car (org-element-context))) | ||
1099 | ;; ;; Link: Open it. | ||
1100 | ;; (org-open-at-point-global)) | ||
1101 | |||
1102 | ((org-at-heading-p) | ||
1103 | ;; Heading: Move to position after entry content. | ||
1104 | ;; NOTE: This is probably the most interesting feature of this function. | ||
1105 | (let ((heading-start (org-entry-beginning-position))) | ||
1106 | (goto-char (org-entry-end-position)) | ||
1107 | (cond ((and (org-at-heading-p) | ||
1108 | (= heading-start (org-entry-beginning-position))) | ||
1109 | ;; Entry ends on its heading; add newline after | ||
1110 | (end-of-line) | ||
1111 | (insert "\n\n")) | ||
1112 | (t | ||
1113 | ;; Entry ends after its heading; back up | ||
1114 | (forward-line -1) | ||
1115 | (end-of-line) | ||
1116 | (when (org-at-heading-p) | ||
1117 | ;; At the same heading | ||
1118 | (forward-line) | ||
1119 | (insert "\n") | ||
1120 | (forward-line -1)) | ||
1121 | ;; FIXME: looking-back is supposed to be called with more arguments. | ||
1122 | (while (not (looking-back (rx (repeat 3 (seq (optional blank) "\n"))))) | ||
1123 | (insert "\n")) | ||
1124 | (forward-line -1))))) | ||
1125 | |||
1126 | ((org-at-item-checkbox-p) | ||
1127 | ;; Checkbox: Insert new item with checkbox. | ||
1128 | (org-insert-todo-heading nil)) | ||
1129 | |||
1130 | ((org-in-item-p) | ||
1131 | ;; Plain list. Yes, this gets a little complicated... | ||
1132 | (let ((context (org-element-context))) | ||
1133 | (if (or (eq 'plain-list (car context)) ; First item in list | ||
1134 | (and (eq 'item (car context)) | ||
1135 | (not (eq (org-element-property :contents-begin context) | ||
1136 | (org-element-property :contents-end context)))) | ||
1137 | (unpackaged/org-element-descendant-of 'item context)) ; Element in list item, e.g. a link | ||
1138 | ;; Non-empty item: Add new item. | ||
1139 | (org-insert-item) | ||
1140 | ;; Empty item: Close the list. | ||
1141 | ;; TODO: Do this with org functions rather than operating on the text. Can't seem to find the right function. | ||
1142 | (delete-region (line-beginning-position) (line-end-position)) | ||
1143 | (insert "\n")))) | ||
1144 | |||
1145 | ((when (fboundp 'org-inlinetask-in-task-p) | ||
1146 | (org-inlinetask-in-task-p)) | ||
1147 | ;; Inline task: Don't insert a new heading. | ||
1148 | (org-return)) | ||
1149 | |||
1150 | ((org-at-table-p) | ||
1151 | (cond ((save-excursion | ||
1152 | (beginning-of-line) | ||
1153 | ;; See `org-table-next-field'. | ||
1154 | (cl-loop with end = (line-end-position) | ||
1155 | for cell = (org-element-table-cell-parser) | ||
1156 | always (equal (org-element-property :contents-begin cell) | ||
1157 | (org-element-property :contents-end cell)) | ||
1158 | while (re-search-forward "|" end t))) | ||
1159 | ;; Empty row: end the table. | ||
1160 | (delete-region (line-beginning-position) (line-end-position)) | ||
1161 | (org-return)) | ||
1162 | (t | ||
1163 | ;; Non-empty row: call `org-return'. | ||
1164 | (org-return)))) | ||
1165 | (t | ||
1166 | ;; All other cases: call `org-return'. | ||
1167 | (org-return))))) | ||
1168 | 647 | ||
1169 | (bind-key "RET" #'unpackaged/org-return-dwim 'org-mode-map) | 648 | ### Replace selection when typing |
1170 | 649 | ||
650 | (delete-selection-mode +1) | ||
1171 | 651 | ||
1172 | # Coding | ||
1173 | 652 | ||
1174 | The Other Thing Emacs is Good For. | 653 | <a id="orgea7fd73"></a> |
1175 | 654 | ||
655 | ### Save existing clipboard text into kill ring before replacing it | ||
1176 | 656 | ||
1177 | ## Formatting | 657 | (cuss save-interprogram-paste-before-kill t) |
1178 | 658 | ||
1179 | 659 | ||
1180 | ### Indenting: [aggressive-indent-mode](https://github.com/Malabarba/aggressive-indent-mode) | 660 | <a id="org27f430f"></a> |
1181 | 661 | ||
1182 | This automagically indents code on every change, as opposed to `electric-indent-mode`, which only does when I like, hit `RET` or whatever. As such, I can turn `electric-indent-mode` off. | 662 | ## So long mode |
1183 | 663 | ||
1184 | (use-package aggressive-indent | 664 | (when (fboundp 'global-so-long-mode) |
1185 | :init | 665 | (global-so-long-mode)) |
1186 | (electric-indent-mode -1) | ||
1187 | :config | ||
1188 | (global-aggressive-indent-mode 1)) | ||
1189 | 666 | ||
1190 | 667 | ||
1191 | ### [Smart tabs](https://github.com/jcsalomon/smarttabs) | 668 | <a id="org8cc8ee8"></a> |
1192 | 669 | ||
1193 | I really want to like, use tabs all the time. But I thought the `smart-tabs` package author made some good points about using tabs for semantic indentation, and spaces for the rest. So. | 670 | # Files |
1194 | 671 | ||
1195 | (use-package smart-tabs-mode | ||
1196 | :custom | ||
1197 | (whitespace-style | ||
1198 | '(face trailing tabs spaces lines newline | ||
1199 | empty indentation space-before-tab | ||
1200 | space-mark tab-mark newline-mark)) | ||
1201 | :config | ||
1202 | (smart-tabs-insinuate 'c 'c++ 'javascript 'java 'ruby)) | ||
1203 | 672 | ||
673 | <a id="org8ca2e9b"></a> | ||
1204 | 674 | ||
1205 | ## Display | 675 | ## Encoding |
1206 | 676 | ||
1207 | 677 | ||
1208 | ### Prettify symbols mode | 678 | <a id="org54363a7"></a> |
1209 | 679 | ||
1210 | By default, I think `prettify-symbols-mode` only changes `lambda` to `λ`. I should, at some point, add some prettified symbols. | 680 | ### UTF-8 |
1211 | 681 | ||
1212 | (add-hook 'prog-mode-hook #'prettify-symbols-mode) | 682 | (set-language-environment 'utf-8) |
683 | (set-terminal-coding-system 'utf-8) | ||
684 | (cuss locale-coding-system 'utf-8) | ||
685 | (set-default-coding-systems 'utf-8) | ||
686 | (set-selection-coding-system 'utf-8) | ||
687 | (prefer-coding-system 'utf-8) | ||
1213 | 688 | ||
1214 | 689 | ||
1215 | ### Parentheses and frens | 690 | <a id="orgeaed3bd"></a> |
1216 | 691 | ||
1217 | 1. `show-paren-style` | 692 | ### Convert all files to UNIX-style line endings |
1218 | 693 | ||
1219 | A `mixed` `show-paren-style` means that, when both parentheses are visible, it just highlights them. If one is *not*, though, it highlights the entire block. | 694 | from [Emacs Wiki](https://www.emacswiki.org/emacs/EndOfLineTips). |
1220 | 695 | ||
1221 | (cuss show-paren-style 'mixed) | 696 | (defun ewiki/no-junk-please-were-unixish () |
1222 | (show-paren-mode 1) | 697 | "Convert line endings to UNIX, dammit." |
698 | (let ((coding-str (symbol-name buffer-file-coding-system))) | ||
699 | (when (string-match "-\\(?:dos\\|mac\\)$" coding-str) | ||
700 | (set-buffer-file-coding-system 'unix)))) | ||
1223 | 701 | ||
1224 | 2. [smartparens](https://github.com/Fuco1/smartparens) | 702 | 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. |
1225 | 703 | ||
1226 | Automagically close pairs and stuff. See also [ParEdit](https://www.emacswiki.org/emacs/ParEdit) – maybe test that one? | 704 | (add-hook 'find-file-hook #'ewiki/no-junk-please-were-unixish) |
705 | (add-hook 'before-save-hook #'ewiki/no-junk-please-were-unixish) | ||
1227 | 706 | ||
1228 | (use-package smartparens | ||
1229 | :init | ||
1230 | (defun acdw/setup-smartparens () | ||
1231 | (require 'smartparens-config) | ||
1232 | (smartparens-mode 1)) | ||
1233 | :hook | ||
1234 | (prog-mode . acdw/setup-smartparens)) | ||
1235 | 707 | ||
1236 | 3. [rainbow-delimiters](https://github.com/Fanael/rainbow-delimiters) | 708 | <a id="org7239c47"></a> |
1237 | 709 | ||
1238 | Show different pairs of delimiters in diffferent colors. Pretty! Useful! | 710 | ## Backups |
1239 | 711 | ||
1240 | (use-package rainbow-delimiters | 712 | (cuss backup-by-copying 1) |
1241 | :hook (prog-mode . rainbow-delimiters-mode)) | 713 | (cuss delete-old-versions -1) |
714 | (cuss version-control t) | ||
715 | (cuss vc-make-backup-files t) | ||
1242 | 716 | ||
1243 | 717 | ||
1244 | ### [rainbow-mode](https://elpa.gnu.org/packages/rainbow-mode.html) | 718 | <a id="org32fc658"></a> |
1245 | 719 | ||
1246 | Show different colors *in that color*. Useful! Pretty! | 720 | ## Auto-saves |
1247 | 721 | ||
1248 | (use-package rainbow-mode | 722 | (auto-save-visited-mode 1) |
1249 | :custom | ||
1250 | (rainbow-x-colors nil) | ||
1251 | :hook prog-mode) | ||
1252 | 723 | ||
1253 | 724 | ||
1254 | ### Line numbers | 725 | <a id="org94456e2"></a> |
1255 | 726 | ||
1256 | I only want line numbers in `prog-mode`-derived modes. In addition, apparently `linum-mode` works better in TUI, but is slower that `display-line-numbers`. So I want to do some logic to see what to use. | 727 | ## Revert files |
1257 | 728 | ||
1258 | (defun acdw/enable-line-numbers () | 729 | (cuss auto-revert-verbose nil) |
1259 | "Enable line numbers, either through `display-line-numbers-mode' | 730 | (global-auto-revert-mode +1) |
1260 | or through `linum-mode'." | ||
1261 | (if (and (fboundp 'display-line-numbers-mode) | ||
1262 | (display-graphic-p)) | ||
1263 | (progn | ||
1264 | (display-line-numbers-mode 1) | ||
1265 | (cuss display-line-numbers-width 2)) | ||
1266 | (linum-mode 1))) | ||
1267 | 731 | ||
1268 | (add-hook 'prog-mode-hook #'acdw/enable-line-numbers) | ||
1269 | 732 | ||
733 | <a id="orgb586b3b"></a> | ||
1270 | 734 | ||
1271 | ## Programming languages | 735 | ## Add a timestamp to files |
1272 | 736 | ||
1273 | These are the programming languages I (don't really) use. | 737 | (add-hook 'before-save-hook #'time-stamp) |
1274 | 738 | ||
1275 | 739 | ||
1276 | ### Fish shell | 740 | <a id="org738fbd9"></a> |
1277 | 741 | ||
1278 | (use-package fish-mode) | 742 | # Programming |
1279 | 743 | ||
1280 | 744 | ||
1281 | ### Lisps | 745 | <a id="org080eb2f"></a> |
1282 | 746 | ||
1283 | 1. Common Lisp (SLIME) | 747 | ## Which function are we in? |
1284 | 748 | ||
1285 | (use-package slime | 749 | (which-function-mode +1) |
1286 | :when (executable-find "sbcl") | ||
1287 | :custom | ||
1288 | (inferior-lisp-program "sbcl") | ||
1289 | (slime-contribs '(slime-fancy))) | ||
1290 | 750 | ||
1291 | 2. Fennel | ||
1292 | 751 | ||
1293 | (use-package fennel-mode | 752 | <a id="org6bf5097"></a> |
1294 | :mode "\\.fnl\\'") | ||
1295 | 753 | ||
1296 | 754 | # Writing | |
1297 | ### Lua | ||
1298 | |||
1299 | (use-package lua-mode | ||
1300 | :mode "\\.lua\\'" | ||
1301 | :interpreter "lua") | ||
1302 | 755 | ||
1303 | 756 | ||
1304 | ### Web (HTML/CSS/JS) | 757 | <a id="org6f01971"></a> |
1305 | 758 | ||
1306 | (use-package web-mode | 759 | ## Visual Fill Column |
1307 | :mode (("\\.ts\\'" . web-mode) | ||
1308 | ("\\.html?\\'" . web-mode) | ||
1309 | ("\\.css?\\'" . web-mode) | ||
1310 | ("\\.js\\'" . web-mode))) | ||
1311 | 760 | ||
761 | (use-package visual-fill-column | ||
762 | :custom | ||
763 | (split-window-preferred-function | ||
764 | 'visual-fill-column-split-window-sensibly) | ||
765 | (visual-fill-column-center-text t) | ||
766 | (fill-column 80) | ||
767 | :config | ||
768 | (advice-add 'text-scale-adjust | ||
769 | :after #'visual-fill-column-adjust) | ||
770 | :hook | ||
771 | (text-mode . visual-fill-column-mode)) | ||
1312 | 772 | ||
1313 | ### `~/.ssh/config` | ||
1314 | 773 | ||
1315 | (use-package ssh-config-mode) | 774 | <a id="org03e747d"></a> |
1316 | 775 | ||
776 | ## Type nice-looking quote-type marks | ||
1317 | 777 | ||
1318 | ### Go | 778 | (use-package typo |
779 | :hook | ||
780 | (text-mode . typo-mode)) | ||
1319 | 781 | ||
1320 | (use-package go-mode | ||
1321 | :mode "\\.go\\'" | ||
1322 | :hook | ||
1323 | (before-save . gofmt-before-save)) | ||
1324 | 782 | ||
783 | <a id="org9528516"></a> | ||
1325 | 784 | ||
1326 | # Applications | 785 | # Applications |
1327 | 786 | ||
1328 | Of course, the real reason we love emacs is for the application layer. What is it they say? | ||
1329 | |||
1330 | > Emacs is a great operating system, lacking only a decent editor. | ||
1331 | |||
1332 | Yeah, that's it 😎 | ||
1333 | |||
1334 | |||
1335 | ## Git: [magit](https://magit.vc/) | ||
1336 | 787 | ||
1337 | The magical porcelain. | 788 | <a id="orgd2a60aa"></a> |
1338 | 789 | ||
1339 | (use-package magit | 790 | ## Magit |
1340 | :bind | ||
1341 | ("C-x g" . magit-status) | ||
1342 | :custom-update | ||
1343 | (magit-no-confirm '(stage-all-changes)) | ||
1344 | :config | ||
1345 | (add-hook 'magit-process-find-password-functions | ||
1346 | #'magit-process-password-auth-source)) | ||
1347 | 791 | ||
792 | (use-package magit | ||
793 | :bind | ||
794 | ("C-x g" . magit-status)) | ||
1348 | 795 | ||
1349 | ### Hook into `prescient` | ||
1350 | 796 | ||
1351 | (define-advice magit-list-refs | 797 | <a id="org7339cf2"></a> |
1352 | (:around (orig &optional namespaces format sortby) | ||
1353 | prescient-sort) | ||
1354 | "Apply prescient sorting when listing refs." | ||
1355 | (let ((res (funcall orig namespaces format sortby))) | ||
1356 | (if (or sortby | ||
1357 | magit-list-refs-sortby | ||
1358 | (not selectrum-should-sort-p)) | ||
1359 | res | ||
1360 | (prescient-sort res)))) | ||
1361 | 798 | ||
799 | # Appendices | ||
1362 | 800 | ||
1363 | ### Use `libgit` when I can build it (requires `cmake`) | ||
1364 | 801 | ||
1365 | (when (executable-find "cmake") | 802 | <a id="org6070b2c"></a> |
1366 | (use-package libgit) | ||
1367 | (use-package magit-libgit)) | ||
1368 | 803 | ||
804 | ## Emacs' files | ||
1369 | 805 | ||
1370 | ### Git "forge" capabilities | ||
1371 | 806 | ||
1372 | (use-package forge | 807 | <a id="org0d720f6"></a> |
1373 | :after magit | ||
1374 | :unless (eq system-type 'windows-nt) | ||
1375 | :custom | ||
1376 | (forge-owned-accounts | ||
1377 | '(("duckwork")))) | ||
1378 | 808 | ||
809 | ### init.el | ||
1379 | 810 | ||
1380 | ## Dired | 811 | ;; init.el -*- lexical-binding: t -*- |
1381 | 812 | ||
1382 | I'm still figuring out what all I can do with `dired`. | 813 | 1. Load config |
1383 | 814 | ||
1384 | (with-eval-after-load 'dired | 815 | from [Protesilaos Stavrou](https://protesilaos.com/dotemacs/#h:584c3604-55a1-49d0-9c31-abe46cb1f028). |
1385 | (cuss dired-dwim-target t) | 816 | |
1386 | (cuss dired-listing-switches "-alDh") | 817 | (let* ((conf (expand-file-name "config" |
818 | user-emacs-directory)) | ||
819 | (elc (concat conf ".elc")) | ||
820 | (el (concat conf ".el")) | ||
821 | (org (concat conf ".org"))) | ||
822 | (cond ((file-exists-p elc) (load-file elc)) | ||
823 | ((file-exists-p el) (load-file el)) | ||
824 | (t (require 'org) | ||
825 | (org-babel-load-file org)))) | ||
1387 | 826 | ||
1388 | (cuss wdired-allow-to-change-permissions t) | ||
1389 | (bind-key "C-c w" #'wdired-change-to-wdired-mode 'dired-mode-map)) | ||
1390 | 827 | ||
828 | <a id="orgd6bffd2"></a> | ||
1391 | 829 | ||
1392 | ### dired-subtree | 830 | ### early-init.el |
1393 | 831 | ||
1394 | Part of the [dired-hacks](https://github.com/Fuco1/dired-hacks) package. | 832 | ;; early-init.el -*- lexical-binding: t; no-byte-compile: t; -*- |
1395 | 833 | ||
1396 | (use-package dired-subtree | 834 | (setq load-prefer-newer t) |
1397 | :bind (:map dired-mode-map | 835 | (setq frame-inhibit-implied-resize t) |
1398 | (("i" . dired-subtree-insert) | ||
1399 | (";" . dired-subtree-remove)))) | ||
1400 | 836 | ||
1401 | 837 | ||
1402 | ## Proced | 838 | <a id="org9c5b437"></a> |
1403 | 839 | ||
1404 | The process editor. | 840 | ## Ease tangling and loading of Emacs' init |
1405 | 841 | ||
1406 | (defun acdw/setup-proced () | 842 | (defun acdw/tangle-and-load-init () |
1407 | (variable-pitch-mode -1) | 843 | (interactive) |
1408 | (toggle-truncate-lines 1) | 844 | "If the current buffer is `config.org', tangle it, then byte-compile." |
1409 | (proced-toggle-auto-update 1)) | 845 | (let ((config (expand-file-name "config.org" user-emacs-directory))) |
846 | (when (string= (buffer-file-name) config) | ||
847 | (let ((prog-mode-hook nil)) | ||
848 | (require 'org) | ||
849 | (org-babel-tangle-file config) | ||
850 | (org-md-export-to-markdown) | ||
851 | |||
852 | (dolist (file `(,(expand-file-name "init.el" | ||
853 | user-emacs-directory) | ||
854 | ,(expand-file-name "config.el" | ||
855 | user-emacs-directory))) | ||
856 | (byte-compile-file file t)))))) | ||
857 | |||
858 | (add-hook 'after-save-hook #'acdw/tangle-and-load-init) | ||
1410 | 859 | ||
1411 | (add-hook 'proced-mode-hook #'acdw/setup-proced) | ||
1412 | 860 | ||
861 | <a id="org1a4bb4d"></a> | ||
1413 | 862 | ||
1414 | ## Gemini (and gopher) | 863 | ## License |
1415 | 864 | ||
865 | Copyright 2020 Case Duckworth <acdw@acdw.net> | ||
1416 | 866 | ||
1417 | ### [elpher](https://thelambdalab.xyz/elpher/) | 867 | This work is free. You can redistribute it and/or modify it under the |
868 | terms of the Do What the Fuck You Want To Public License, Version 2, | ||
869 | as published by Sam Hocevar. See the `LICENSE` file, tangled from the | ||
870 | following source block, for details. | ||
1418 | 871 | ||
1419 | Actually, `elpher` is the reason I started using Emacs. So thanks, smol web denizens! | 872 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE |
873 | |||
874 | Version 2, December 2004 | ||
875 | |||
876 | Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> | ||
877 | |||
878 | Everyone is permitted to copy and distribute verbatim or modified copies of | ||
879 | this license document, and changing it is allowed as long as the name is changed. | ||
880 | |||
881 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | ||
882 | |||
883 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||
884 | |||
885 | 0. You just DO WHAT THE FUCK YOU WANT TO. | ||
1420 | 886 | ||
1421 | Fun fact: these packages are *also* why I use `straight.el`, since they're none of them on GitHub. | ||
1422 | 887 | ||
1423 | (use-package elpher | 888 | <a id="orga6047ee"></a> |
1424 | :straight (elpher | ||
1425 | :repo "git://thelambdalab.xyz/elpher.git") | ||
1426 | :custom | ||
1427 | (elpher-certificate-directory | ||
1428 | (no-littering-expand-var-file-name "elpher-certificates/")) | ||
1429 | (elpher-ipv4-always t) | ||
1430 | :custom-face | ||
1431 | (elpher-gemini-heading1 | ||
1432 | ((t (:inherit (modus-theme-heading-1))))) | ||
1433 | (elpher-gemini-heading2 | ||
1434 | ((t (:inherit (modus-theme-heading-2))))) | ||
1435 | (elpher-gemini-heading3 | ||
1436 | ((t (:inherit (modus-theme-heading-3))))) | ||
1437 | :config | ||
1438 | (defun elpher:eww-browse-url (original url &optional new-window) | ||
1439 | "Handle gemini/gopher links with eww." | ||
1440 | (cond ((string-match-p "\\`\\(gemini\\|gopher\\)://" url) | ||
1441 | (require 'elpher) | ||
1442 | (elpher-go url)) | ||
1443 | (t (funcall original url new-window)))) | ||
1444 | (advice-add 'eww-browse-url :around 'elpher:eww-browse-url) | ||
1445 | :bind (:map elpher-mode-map | ||
1446 | ("n" . elpher-next-link) | ||
1447 | ("p" . elpher-prev-link) | ||
1448 | ("o" . elpher-follow-current-link) | ||
1449 | ("G" . elpher-go-current)) | ||
1450 | :hook | ||
1451 | (elpher-mode . visual-fill-column-mode)) | ||
1452 | 889 | ||
890 | ### Note on the license | ||
1453 | 891 | ||
1454 | ### [gemini-mode](https://git.carcosa.net/jmcbray/gemini.el) | 892 | It's highly likely that the WTFPL is completely incompatible with the |
893 | GPL, for what should be fairly obvious reasons. To that, I say: | ||
1455 | 894 | ||
1456 | A major mode for `text/gemini` files. I've changed the headings to match Elpher's. | 895 | **SUE ME, RMS!** |
1457 | |||
1458 | (use-package gemini-mode | ||
1459 | :straight (gemini-mode | ||
1460 | :repo "https://git.carcosa.net/jmcbray/gemini.el.git") | ||
1461 | :mode "\\.\\(gemini|gmi\\)\\'" | ||
1462 | :custom-face | ||
1463 | (gemini-heading-face-1 | ||
1464 | ((t (:inherit (elpher-gemini-heading1))))) | ||
1465 | (gemini-heading-face2 | ||
1466 | ((t (:inherit (elpher-gemini-heading2))))) | ||
1467 | (gemini-heading-face3 | ||
1468 | ((t (:inherit (elpher-gemini-heading3))))) | ||
1469 | :init | ||
1470 | (defun acdw/setup-gemini-mode () | ||
1471 | (visual-fill-column-mode 1) | ||
1472 | (variable-pitch-mode -1)) | ||
1473 | :hook | ||
1474 | (gemini-mode . acdw/setup-gemini-mode)) | ||
1475 | |||
1476 | |||
1477 | ### [gemini-write](https://alexschroeder.ch/cgit/gemini-write/about/) | ||
1478 | |||
1479 | Alex Schroeder's Emacs implementation of the Titan protocol. This is why I use his Gemini server, [Phoebe](https://alexschroeder.ch/cgit/phoebe/)! | ||
1480 | |||
1481 | (use-package gemini-write | ||
1482 | :straight (gemini-write | ||
1483 | :repo "https://alexschroeder.ch/cgit/gemini-write") | ||
1484 | :config | ||
1485 | (when (boundp 'acdw-secrets/elpher-gemini-tokens) | ||
1486 | (dolist (token acdw-secrets/elpher-gemini-tokens) | ||
1487 | (add-to-list 'elpher-gemini-tokens token)))) | ||
1488 | |||
1489 | |||
1490 | ### [post-to-gemlog-blue](https://git.sr.ht/~acdw/post-to-gemlog-blue.el) | ||
1491 | |||
1492 | My first (!) Emacs package, to allow posting to [gemlog.blue's web interface](https://gemlog.blue). I don't use gemlog.blue any more, but if I didn't have this package, no one would 😎 | ||
1493 | |||
1494 | (use-package post-to-gemlog-blue | ||
1495 | :straight (post-to-gemlog-blue | ||
1496 | :repo "https://git.sr.ht/~acdw/post-to-gemlog-blue.el")) | ||
1497 | |||
1498 | |||
1499 | ## Pastebin: [0x0](https://git.sr.ht/~zge/nullpointer-emacs) | ||
1500 | |||
1501 | Pastebins are so useful. Now I can use them from Emacs. | ||
1502 | |||
1503 | (use-package 0x0 | ||
1504 | :custom | ||
1505 | (0x0-default-service 'ttm)) | ||
1506 | |||
1507 | |||
1508 | ## [mu4e](https://www.djcbsoftware.nl/code/mu/mu4e.html) | ||
1509 | |||
1510 | I've just recently started (again) using mu4e. We'll see how it goes. | ||
1511 | |||
1512 | (when (executable-find "mu") | ||
1513 | (add-to-list 'load-path | ||
1514 | "/usr/share/emacs/site-lisp/mu4e") | ||
1515 | (require 'mu4e) | ||
1516 | |||
1517 | (cuss mail-user-agent 'mu4e-user-agent) | ||
1518 | |||
1519 | (cuss mu4e-headers-skip-duplicates t) | ||
1520 | (cuss mu4e-view-show-images t) | ||
1521 | (cuss mu4e-view-show-addresses t) | ||
1522 | (cuss mu4e-compose-format-flowed t) | ||
1523 | (cuss mu4e-change-filenames-when-moving t) | ||
1524 | (cuss mu4e-attachments-dir "~/Downloads") | ||
1525 | |||
1526 | (cuss mu4e-maildir "~/.mail/fastmail") | ||
1527 | (cuss mu4e-refile-folder "/Archive") | ||
1528 | (cuss mu4e-sent-folder "/Sent") | ||
1529 | (cuss mu4e-drafts-folder "/Drafts") | ||
1530 | (cuss mu4e-trash-folder "/Trash") | ||
1531 | |||
1532 | (fset 'my-move-to-trash "mTrash") | ||
1533 | (define-key mu4e-headers-mode-map (kbd "d") 'my-move-to-trash) | ||
1534 | (define-key mu4e-view-mode-map (kbd "d") 'my-move-to-trash) | ||
1535 | |||
1536 | (cuss message-send-mail-function 'smtpmail-send-it) | ||
1537 | (cuss smtpmail-default-smtp-server "smtp.fastmail.com") | ||
1538 | (cuss smtpmail-smtp-server "smtp.fastmail.com") | ||
1539 | (cuss smtpmail-stream-type 'ssl) | ||
1540 | (cuss smtpmail-smtp-service 465) | ||
1541 | (cuss smtpmail-local-domain "acdw.net") | ||
1542 | (cuss mu4e-compose-signature | ||
1543 | "Best,\nCase\n") | ||
1544 | |||
1545 | (cuss mu4e-get-mail-command "mbsync -a") | ||
1546 | (cuss mu4e-update-interval 300) | ||
1547 | |||
1548 | (cuss mu4e-completing-read-function 'completing-read) | ||
1549 | (cuss message-kill-buffer-on-exit t) | ||
1550 | (cuss mu4e-confirm-quit nil) | ||
1551 | |||
1552 | (cuss mu4e-bookmarks | ||
1553 | '(( | ||
1554 | :name "Unread" | ||
1555 | :query | ||
1556 | "flag:unread AND NOT flag:trashed AND NOT maildir:/Spam" | ||
1557 | :key ?u) | ||
1558 | ( | ||
1559 | :name "Today" | ||
1560 | :query "date:today..now and not maildir:/Spam" | ||
1561 | :key ?t) | ||
1562 | ( | ||
1563 | :name "This week" | ||
1564 | :query "date:7d..now and not maildir:/Spam" | ||
1565 | :hide-unread t | ||
1566 | :key ?w))) | ||
1567 | |||
1568 | (cuss mu4e-headers-fields | ||
1569 | '((:human-date . 12) | ||
1570 | (:flags . 6) | ||
1571 | (:mailing-list . 10) | ||
1572 | (:from-or-to . 22) | ||
1573 | (:subject))) | ||
1574 | |||
1575 | (defun acdw/setup-mu4e-view-mode () | ||
1576 | (visual-fill-column-mode)) | ||
1577 | |||
1578 | (add-hook 'mu4e-view-mode-hook #'acdw/setup-mu4e-view-mode)) | ||
1579 | |||
1580 | ;; not sure about this... | ||
1581 | (use-package mu4e-dashboard | ||
1582 | :straight (mu4e-dashboard | ||
1583 | :host github | ||
1584 | :repo "rougier/mu4e-dashboard" | ||
1585 | :branch "main")) | ||
1586 | |||
1587 | |||
1588 | <a id="orgb5347d4"></a> | ||
1589 | |||
1590 | ## KeePassXC integration | ||
1591 | |||
1592 | I use KeePassXC, and I'd *like* to use KeePassXC to get passwords and stuff at home. So I'm trying this library out, since the secret-tool integration isn't built into the KeePassXC on Void. If this doesn't work, looks like I'll have to become a packager 😜 | ||
1593 | |||
1594 | (when (eq system-type 'gnu/linux) | ||
1595 | (use-package sodium | ||
1596 | :straight (sodium | ||
1597 | :host github | ||
1598 | :repo "dakra/sodium.el" | ||
1599 | :build ("make")) | ||
1600 | :init | ||
1601 | (add-to-list 'load-path | ||
1602 | (expand-file-name "straight/repos/sodium.el" | ||
1603 | user-emacs-directory))) | ||
1604 | (use-package keepassxc | ||
1605 | :straight (keepassxc | ||
1606 | :host github | ||
1607 | :repo "dakra/keepassxc.el") | ||
1608 | :after (sodium))) | ||
1609 | |||
1610 | |||
1611 | ### libsodium integration | ||
1612 | |||
1613 | I had to manually run `make` in the repo this time around, for some reason. Should probably look into that … when it's not 1:00 AM. | ||
1614 | |||
1615 | (use-package sodium | ||
1616 | :straight (sodium | ||
1617 | :host github | ||
1618 | :repo "dakra/sodium.el" | ||
1619 | :build ("make")) | ||
1620 | :init | ||
1621 | (add-to-list 'load-path | ||
1622 | (expand-file-name "straight/repos/sodium.el" | ||
1623 | user-emacs-directory))) | ||
1624 | |||
1625 | |||
1626 | # Appendix A: Scripts | ||
1627 | |||
1628 | |||
1629 | ## `emacsdc` | ||
1630 | |||
1631 | Here's a wrapper script that'll start `emacs --daemon` if there isn't one, and then launche `emacsclient` on the arguments. I'd recommend installing with `ln -s emacsdc ~/.local/bin/` or something. Then you can set it as your `$EDITOR`! | ||
1632 | |||
1633 | if ! emacsclient -nc "$@" 2>/dev/null; then | ||
1634 | emacs --daemon | ||
1635 | emacsclient -nc "$@" | ||
1636 | fi | ||
1637 | |||
1638 | |||
1639 | # Appendix B: areas for further research | ||
1640 | |||
1641 | - [ebuku](https://github.com/flexibeast/ebuku) (of course, I'd need [buku](https://github.com/jarun/buku) as well) – bookmarks | ||
1642 | - [KeePassXC as Secret Service](https://www.billdietrich.me/Authentication.html?expandall=1#KeePassXCandSecretService) – see [14.7](#orgb5347d4) | ||
1643 | - [Ignoramus](https://github.com/rolandwalker/ignoramus) – this might not e necessary | ||
1644 | - [Dynamic fonts](https://github.com/rolandwalker/dynamic-fonts) – take a look @ this and compare with my fonts section | ||
1645 | - [Simple clipboard integration](https://github.com/rolandwalker/simpleclip) – test with Windows, maybe | ||
1646 | - [visible mark](https://git.sr.ht/~iank/visible-mark) – show where the marks are … | ||
1647 | - consider this Reddit thread: [speeding up magit](https://www.reddit.com/r/emacs/comments/k3xfa1/speeding_up_magit/) | ||
1648 | |||
1649 | |||
1650 | ## export org to ODT on Windows | ||
1651 | |||
1652 | Windows doesn't have `zip` natively (though it *does* have `curl` – go figure!), so the ODT export for Org-mode won't work. There *is* a [StackOverflow discussion](https://stackoverflow.com/questions/8625306/org-mode-zip-needed-how-to-over-come) that mentions `p7zip` and a possible batch file, but I couldn't get that to work with a little testing. It might need more work. | ||
1653 | |||
1654 | Something that *did* work was downloading `zip.exe` from [Info-ZIP](http://infozip.sourceforge.net/), and placing it somewhere in `exec-path` – though of course, *then* you need `unzip.exe`, apparently in the same folder. Git Portable ships with `unzip.exe`, for example, but even though it's in `exec-path`, org-export threw an error unless `zip` and `unzip` were in the *same* folder. | ||
1655 | 896 | ||
1656 | So I might either (a) have to set up computers in this way when I use new ones, or (b) include both `zip.exe` and `unzip.exe` in *this* Git repo, or … something else. A true quandry. | ||
diff --git a/config.org b/config.org index 7ec4a00..c025f03 100644 --- a/config.org +++ b/config.org | |||
@@ -1,315 +1,256 @@ | |||
1 | #+TITLE:Emacs configuration, literate style | 1 | #+TITLE: Emacs, emacs, emacs |
2 | #+AUTHOR:Case Duckworth | 2 | #+AUTHOR: Case Duckworth |
3 | #+PROPERTY: header-args :tangle init.el :comments both :mkdirp yes | 3 | #+PROPERTY: header-args :tangle config.el :comments both :mkdirp yes |
4 | #+EXPORT_FILE_NAME: README.md | 4 | #+EXPORT_FILE_NAME: README.md |
5 | #+OPTIONS: toc:nil | 5 | #+BANKRUPTCY_COUNT: 3 |
6 | #+OPTIONS: title:t | 6 | #+Time-stamp: <2020-12-08 23:51:18 acdw> |
7 | #+BANKRUPTCY_COUNT: 2 | ||
8 | #+Time-stamp: <2020-12-07 08:25:11 acdw> | ||
9 | 7 | ||
10 | This is my Emacs configuration. It's also a literate =org-mode= file. Yeah, I'm a cool guy. | 8 | * Pave the way |
9 | ** Correct =exec-path= | ||
11 | 10 | ||
12 | * About me | 11 | #+begin_src emacs-lisp |
13 | 12 | (let ((win-downloads "c:/Users/aduckworth/Downloads")) | |
14 | #+begin_src emacs-lisp :comments no | 13 | (dolist (path `(;; Linux |
15 | ;; init.el -*- lexical-binding: t -*- | 14 | ,(expand-file-name "bin" |
16 | (setq user-full-name "Case Duckworth" | 15 | user-emacs-directory) |
17 | user-mail-address "acdw@acdw.net") | 16 | ,(expand-file-name "~/bin") |
18 | #+end_src | 17 | ,(expand-file-name "~/.local/bin") |
19 | 18 | ,(expand-file-name "~/Scripts") | |
20 | * License | 19 | ;; Windows |
20 | ,(expand-file-name "emacs/bin" | ||
21 | win-downloads) | ||
22 | ,(expand-file-name "PortableGit/bin" | ||
23 | win-downloads) | ||
24 | ,(expand-file-name "PortableGit/usr/bin" | ||
25 | win-downloads))) | ||
26 | (when (file-exists-p path) | ||
27 | (add-to-list 'exec-path path)))) | ||
28 | #+end_src | ||
21 | 29 | ||
22 | Copyright © 2020 Case Duckworth <acdw@acdw.net> | 30 | ** Package management |
23 | 31 | ||
24 | 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. | 32 | *** Straight.el |
25 | 33 | ||
26 | #+begin_src text :tangle LICENSE :comments no | 34 | #+begin_src emacs-lisp |
27 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | 35 | (defvar bootstrap-version) |
36 | (let ((bootstrap-file | ||
37 | (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory)) | ||
38 | (bootstrap-version 5)) | ||
39 | (unless (file-exists-p bootstrap-file) | ||
40 | (with-current-buffer | ||
41 | (url-retrieve-synchronously | ||
42 | "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el" | ||
43 | 'silent 'inhibit-cookies) | ||
44 | (goto-char (point-max)) | ||
45 | (eval-print-last-sexp))) | ||
46 | (load bootstrap-file nil 'nomessage)) | ||
47 | #+end_src | ||
28 | 48 | ||
29 | Version 2, December 2004 | 49 | *** Use-package |
30 | 50 | ||
31 | Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> | 51 | #+begin_src emacs-lisp |
52 | (setq straight-use-package-by-default t) | ||
53 | (straight-use-package 'use-package) | ||
54 | #+end_src | ||
32 | 55 | ||
33 | Everyone is permitted to copy and distribute verbatim or modified copies of | 56 | *** Extra use-package keywords |
34 | this license document, and changing it is allowed as long as the name is changed. | ||
35 | 57 | ||
36 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | 58 | **** :custom-update |
37 | 59 | ||
38 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | 60 | #+begin_src emacs-lisp |
61 | (straight-use-package | ||
62 | '(use-package-custom-update | ||
63 | :host "github" | ||
64 | :repo "a13/use-package-custom-update")) | ||
39 | 65 | ||
40 | 0. You just DO WHAT THE FUCK YOU WANT TO. | 66 | (require 'use-package-custom-update) |
41 | #+end_src | 67 | #+end_src |
42 | 68 | ||
43 | ** Note on the license | 69 | ** Customize variables |
44 | 70 | ||
45 | It's highly likely that the WTFPL is completely incompatible with the GPL, for what should be fairly obvious reasons. To that, I say: | 71 | *** Put customizations in a separate file |
46 | 72 | ||
47 | *SUE ME, RMS!* | 73 | #+begin_src emacs-lisp |
48 | 74 | (setq custom-file | |
49 | * Bootstrap | 75 | (expand-file-name "custom.el" user-emacs-directory)) |
50 | 76 | #+end_src | |
51 | ** Original init.el | ||
52 | |||
53 | This file replaces itself with the actual configuration when it's first run. For easy installation, /this/ is the =init.el= file in git -- and you probably want to keep it that way. To keep git from trying to update =init.el= when it's re-tangled, type this in the repo: | ||
54 | 77 | ||
55 | #+begin_src sh :tangle no | 78 | *** A macro for ease of customization |
56 | git update-index --assume-unchanged init.el | ||
57 | #+end_src | ||
58 | 79 | ||
59 | If, for some reason, you want to change this original file to be re-tracked, run this command: | 80 | #+begin_src emacs-lisp |
81 | (defmacro cuss (var val &optional docstring) | ||
82 | "Basically `:custom' from `use-package', broken out." | ||
83 | `(funcall (or (get ',var 'custom-set) #'set-default) | ||
84 | ',var ,val)) | ||
85 | #+end_src | ||
60 | 86 | ||
61 | #+begin_src sh :tangle no | 87 | ** Keep a tidy =~/.emacs= |
62 | git update-index --no-assume-unchanged init.el | ||
63 | #+end_src | ||
64 | 88 | ||
65 | Otherwise, here's the actual, original =init.el= that tangles this Org file and gets us going. | 89 | #+begin_src emacs-lisp |
90 | (use-package no-littering | ||
91 | :custom | ||
92 | (backup-directory-alist | ||
93 | `((".*" . ,(no-littering-expand-var-file-name "backup/")))) | ||
94 | (auto-save-file-name-transforms | ||
95 | `((".*" ,(no-littering-expand-var-file-name "autosaves/") t))) | ||
96 | (save-place-file | ||
97 | (no-littering-expand-var-file-name "places")) | ||
98 | (undo-fu-session-directory | ||
99 | (no-littering-expand-var-file-name "undos/")) | ||
100 | (undohist-directory | ||
101 | (no-littering-expand-var-file-name "undos/")) | ||
102 | (elpher-certificate-directory | ||
103 | (no-littering-expand-var-file-name "elpher-certificates/"))) | ||
66 | 104 | ||
67 | #+begin_src emacs-lisp :tangle no | 105 | (dolist (dir '("backup" |
68 | (require 'org) | 106 | "autosaves" |
69 | (find-file (concat user-emacs-directory "config.org")) | 107 | "undos" |
70 | (org-babel-tangle) | 108 | "elpher-certificates")) |
71 | (load-file (concat user-emacs-directory "early-init.el")) | 109 | (make-directory (no-littering-expand-var-file-name dir) t)) |
72 | (load-file (concat user-emacs-directory "init.el")) | ||
73 | (byte-compile-file (concat user-emacs-directory "init.el")) | ||
74 | #+end_src | 110 | #+end_src |
75 | 111 | ||
76 | *** TODO What I should do instead | 112 | * Look and Feel |
77 | 113 | ||
78 | Honestly, I should just change this "Original init.el" thing to a Makefile I can tangle in =config.org=, and track -- since it won't be overwritten or need any special =git= invocations to stop tracking it, I can edit it as I think about what would work best. I could also maybe give it more of a "cross-platform" vibe by installing, say, =straight.el= in the Makefile on Windows. One day ... | 114 | ** Simplify the UI |
79 | 115 | ||
80 | ** Tangling | 116 | *** Tool bars and menu bars |
81 | |||
82 | After our first tangle, each time we edit =config.org= we want to go ahead and re-tangle our config. To that end, I've written ~acdw/tangle-init~, which automatically tangles =config.org=. | ||
83 | 117 | ||
84 | #+begin_src emacs-lisp | 118 | #+begin_src emacs-lisp |
85 | (defun acdw/tangle-init () | 119 | (cuss default-frame-alist |
86 | "If the current buffer is `config.org', tangle it, then compile | 120 | '((tool-bar-lines . 0) |
87 | and load the resulting files." | 121 | (menu-bar-lines . 0))) |
88 | (when (equal (buffer-file-name) | ||
89 | (expand-file-name | ||
90 | (concat user-emacs-directory "config.org"))) | ||
91 | ;; Tangle and load init.el and early-init.el | ||
92 | (require 'async) | ||
93 | (async-start | ||
94 | (lambda () | ||
95 | (let ((prog-mode-hook nil)) | ||
96 | (require 'org) | ||
97 | (org-babel-tangle-file | ||
98 | (expand-file-name | ||
99 | (concat user-emacs-directory "config.org"))))) | ||
100 | (lambda (response) | ||
101 | (acdw/load-init) | ||
102 | (message "Tangled and loaded: %s" response))))) | ||
103 | #+end_src | ||
104 | |||
105 | Since I want to tangle every time I save =config.org=, I've added ~acdw/tangle-init~ to a hook. | ||
106 | 122 | ||
107 | #+begin_src emacs-lisp | 123 | (menu-bar-mode -1) |
108 | (add-hook 'after-save-hook #'acdw/tangle-init) | 124 | (tool-bar-mode -1) |
109 | #+end_src | 125 | #+end_src |
110 | 126 | ||
111 | Finally, I want an easier way to load the generated init files than the old =M-x load-file RET ~/.config/emacs/init.el RET=. So I've written ~acdw/load-init~ -- which also gets called at the end of the async part of ~acdw/tangle-init~. | 127 | *** Scroll bars |
112 | 128 | ||
113 | #+begin_src emacs-lisp | 129 | #+begin_src emacs-lisp |
114 | (defun acdw/load-init () | 130 | (add-to-list 'default-frame-alist '(vertical-scroll-bars . nil)) |
115 | "Load my init files." | 131 | (scroll-bar-mode -1) |
116 | (interactive) | ||
117 | (load-file (expand-file-name | ||
118 | (concat user-emacs-directory "early-init.el"))) | ||
119 | (load-file (expand-file-name | ||
120 | (concat user-emacs-directory "init.el")))) | ||
121 | #+end_src | ||
122 | |||
123 | ** Miscellaneous bootstrappy stuff | ||
124 | 132 | ||
125 | *** Add directories to =load-path= | 133 | (add-to-list 'default-frame-alist '(horizontal-scroll-bars . nil)) |
134 | (horizontal-scroll-bar-mode -1) | ||
135 | #+end_src | ||
126 | 136 | ||
127 | I also put lispy stuff in the =lisp/= subdirectory of my Emacs config, and under my SyncThing directory (for easy syncing ;P). | 137 | *** Dialog boxen |
128 | 138 | ||
129 | #+begin_src emacs-lisp | 139 | #+begin_src emacs-lisp |
130 | (dolist (dir `(,(concat user-emacs-directory | 140 | (cuss use-dialog-box nil) |
131 | (convert-standard-filename "lisp/")) | ||
132 | ,(expand-file-name "~/Sync/elisp/"))) | ||
133 | (add-to-list 'load-path dir)) | ||
134 | |||
135 | #+end_src | 141 | #+end_src |
136 | 142 | ||
137 | *** TODO Require my secrets | 143 | *** Shorten confirmations |
138 | |||
139 | While this is like, the /dumbest/ way to do this, it's what I'm doing right now. I'm going to slap a TODO on here because I really should make it better -- like, =auth-sources= hooked into KeePassXC somehow... ? Maybe follow [[https://www.billdietrich.me/Authentication.html?expandall=1#KeePassXCandSecretService][Bill Dietrich's setup]]. | ||
140 | 144 | ||
141 | #+begin_src emacs-lisp | 145 | #+begin_src emacs-lisp |
142 | (require 'acdw-secrets) | 146 | (fset 'yes-or-no-p #'y-or-n-p) |
143 | #+end_src | 147 | #+end_src |
144 | 148 | ||
145 | * Early initiation | 149 | *** Remove the bell |
146 | :PROPERTIES: | ||
147 | :header-args: :tangle early-init.el | ||
148 | :END: | ||
149 | |||
150 | Starting with version 27.1, Emacs loads =early-init.el= /before/ =init.el=, setting up early stuff like package management, etc. Since I use an alternative package manager, I have to bootstrap it here. | ||
151 | |||
152 | Of course, I also want to set some really early-on settings here too, like =load-prefer-newer= -- why not? | ||
153 | 150 | ||
154 | #+begin_src emacs-lisp :comments no | 151 | #+begin_src emacs-lisp |
155 | ;; early-init.el -*- lexical-binding: t; no-byte-compile: t -*- | 152 | (cuss visible-bell (not (string= (system-name) "larry"))) |
156 | (setq load-prefer-newer t) | ||
157 | #+end_src | 153 | #+end_src |
158 | 154 | ||
159 | ** Increase the garbage collector | 155 | *** Tell Ediff to setup windows better |
160 | |||
161 | Let's try to speed startup times by increasing the garbage collector's threshold while running init. Note the hook afterwards that restores it to a reasonable default. | ||
162 | 156 | ||
163 | #+begin_src emacs-lisp | 157 | #+begin_src emacs-lisp |
164 | (setq gc-cons-threshold (* 100 100 1000)) | 158 | (declare-function ediff-setup-windows-plain "ediff-wind.el") |
165 | 159 | (cuss ediff-window-setup-function #'ediff-setup-windows-plain) | |
166 | (add-hook 'after-init-hook | ||
167 | (lambda () | ||
168 | (setq gc-cons-threshold (* 100 100 100)) | ||
169 | (message "gc-cons-threshold restored to %S" | ||
170 | gc-cons-threshold))) | ||
171 | #+end_src | 160 | #+end_src |
172 | 161 | ||
173 | ** Add more paths to the =exec-path= | 162 | ** Tweak the remaining UI |
174 | 163 | ||
175 | When using Windows (at work), I need to use the PortableGit installation I've downloaded, since I don't have Admin privileges. | 164 | *** Window dividers |
176 | 165 | ||
177 | #+begin_src emacs-lisp | 166 | #+begin_src emacs-lisp |
178 | (when (eq system-type 'windows-nt) | 167 | (add-to-list 'default-frame-alist '(right-divider-width . 2)) |
179 | (dolist (path '("c:/Users/aduckworth/Downloads/emacs/bin" | 168 | (add-to-list 'default-frame-alist '(bottom-divider-width . 2)) |
180 | "C:/Users/aduckworth/Downloads/PortableGit/bin" | ||
181 | "C:/Users/aduckworth/Downloads/PortableGit/usr/bin")) | ||
182 | (add-to-list 'exec-path path))) | ||
183 | #+end_src | 169 | #+end_src |
184 | 170 | ||
185 | Elsewhere, I want to add a few more paths to the =exec-path= as well, since I store scripts in a couple of places at ~. | 171 | *** Fringes |
186 | 172 | ||
187 | #+begin_src emacs-lisp | 173 | #+begin_src emacs-lisp |
188 | (dolist (path `(,(expand-file-name "bin" | 174 | (add-to-list 'default-frame-alist '(left-fringe-width . 2)) |
189 | user-emacs-directory) | 175 | (add-to-list 'default-frame-alist '(right-fringe-width . 2)) |
190 | ,(expand-file-name "~/bin") | ||
191 | ,(expand-file-name "~/.local/bin") | ||
192 | ,(expand-file-name "~/Scripts"))) | ||
193 | (add-to-list 'exec-path path)) | ||
194 | #+end_src | 176 | #+end_src |
195 | 177 | ||
196 | ** Bootstrap [[https://github.com/raxod502/straight.el][straight.el]] | 178 | *** Minibuffer |
197 | 179 | ||
198 | So far, this is the best package manager I've used. It allows for /truly/ declarative package management (if I don't specify a package here, it doesn't get loaded), easy installation from pretty much any source (as long as it's got a git repo), /and/ it hooks into =use-package=! | 180 | **** Setup the minibuffer frame |
199 | 181 | ||
200 | The one annoying thing is that this bootstrap code doesn't work on Windows for some reason. I'm too lazy to really try and figure out why, so when I need to bootstrap on Windows (pretty rare, TBH), I just [[https://github.com/raxod502/straight.el/archive/master.zip][download the master-branch zip file]] and extract it to =~/.emacs.d/straight/repos/=. | ||
201 | |||
202 | #+NAME: straight-bootstrap | ||
203 | #+begin_src emacs-lisp | 182 | #+begin_src emacs-lisp |
204 | (defvar bootstrap-version) | 183 | (cuss minibuffer-frame-alist |
205 | (let ((bootstrap-file | 184 | '((width . 80) |
206 | (expand-file-name "straight/repos/straight.el/bootstrap.el" | 185 | (height . 2) |
207 | user-emacs-directory)) | 186 | (vertical-scrollbars . nil))) |
208 | (bootstrap-version 5)) | ||
209 | (unless (file-exists-p bootstrap-file) | ||
210 | (with-current-buffer | ||
211 | (url-retrieve-synchronously | ||
212 | "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el" | ||
213 | 'silent 'inhibit-cookies) | ||
214 | (goto-char (point-max)) | ||
215 | (eval-print-last-sexp))) | ||
216 | (load bootstrap-file nil 'nomessage)) | ||
217 | #+end_src | ||
218 | |||
219 | ** ... but first: override the definition of straight.el to use the =develop= branch | ||
220 | |||
221 | For the KeePassXC bits later, I need the =develop= branch of straight, so I can pass a =:build= keyword. I can do this, but I have to override the recipe for =straight.el= itself. I do that here. For more information, see [[https://github.com/raxod502/straight.el#overriding-recipes][the README]]. | ||
222 | |||
223 | #+begin_src emacs-lisp :noweb yes :tangle no | ||
224 | (setq straight-repository-branch "develop") | ||
225 | 187 | ||
226 | <<straight-bootstrap>> | 188 | (set-window-scroll-bars (minibuffer-window) nil nil) |
227 | #+end_src | 189 | #+end_src |
228 | 190 | ||
229 | ** Use [[https://jwiegley.github.io/use-package/][use-package]] | 191 | **** Keep the cursor from going into the prompt |
230 | |||
231 | Like I said, =straight.el= hooks into =use-package= easily. These two lines get the latter to use the former by default. | ||
232 | 192 | ||
233 | #+begin_src emacs-lisp | 193 | #+begin_src emacs-lisp |
234 | (setq straight-use-package-by-default t) | 194 | (cuss minibuffer-prompt-properties |
235 | (straight-use-package 'use-package) | 195 | '(read-only t cursor-intangible t face minibuffer-prompt)) |
236 | #+end_src | 196 | #+end_src |
237 | 197 | ||
238 | ** Keep =~/.emacs.d= tidy with [[https://github.com/emacscollective/no-littering][no-littering]] | 198 | *** Tabs |
239 | 199 | ||
240 | I'll be honest -- I don't really notice this package. But I think that's the point. It keeps Emacs (and packages) from throwing files all over the place, so I have a clean =ls -l=. Since I want to run this code as early as possible, I use the =straight-use-package= form instead of =use-package=. | 200 | **** Show the tabs as current buffer, plus window count |
241 | 201 | ||
242 | #+begin_src emacs-lisp | 202 | #+begin_src emacs-lisp |
243 | (straight-use-package 'no-littering) | 203 | (cuss tab-bar-tab-name-function #'tab-bar-tab-name-current-with-count) |
244 | (require 'no-littering) | ||
245 | #+end_src | 204 | #+end_src |
246 | 205 | ||
247 | ** Additional =use-package= keywords | 206 | **** Only show the tab bar when there's more than one tab |
248 | 207 | ||
249 | *** [[https://github.com/a13/use-package-custom-update][:custom-update]] | 208 | #+begin_src emacs-lisp |
250 | 209 | (cuss tab-bar-show 1) | |
251 | The =:custom-update= keyword lets me do this: | ||
252 | |||
253 | #+begin_src emacs-lisp :tangle no | ||
254 | (use-package package | ||
255 | :custom-update | ||
256 | (package-list '(1 2 3))) | ||
257 | #+end_src | ||
258 | |||
259 | instead of this: | ||
260 | |||
261 | #+begin_src emacs-lisp :tangle no | ||
262 | (use-package package | ||
263 | :config | ||
264 | (add-to-list 'package-list '(1 2 3))) | ||
265 | #+end_src | 210 | #+end_src |
266 | 211 | ||
267 | It's not ... perfect, but it's kind of nice. | 212 | *** Cursor |
268 | 213 | ||
269 | #+begin_src emacs-lisp | 214 | #+begin_src emacs-lisp |
270 | (use-package use-package-custom-update | 215 | (cuss cursor-type 'bar) |
271 | :straight (use-package-custom-update | 216 | (cuss cursor-in-non-selected-windows 'hollow) |
272 | :host github | ||
273 | :repo "a13/use-package-custom-update")) | ||
274 | #+end_src | 217 | #+end_src |
275 | 218 | ||
276 | ** Setup [[https://github.com/jwiegley/emacs-async][async]] | 219 | *** Buffer names |
277 | |||
278 | I thought this was included in Emacs at first, but it's not -- so we need to install and require it. | ||
279 | 220 | ||
280 | #+begin_src emacs-lisp | 221 | #+begin_src emacs-lisp |
281 | (straight-use-package 'async) | 222 | (require 'uniquify) |
282 | (require 'async) | 223 | (cuss uniquify-buffer-name-style 'forward) |
283 | #+end_src | 224 | #+end_src |
284 | 225 | ||
285 | * Macros | 226 | *** Buffer boundaries |
286 | 227 | ||
287 | ** Customizing variables | 228 | #+begin_src emacs-lisp |
229 | (cuss indicate-buffer-boundaries | ||
230 | '((top . right) | ||
231 | (bottom . right) | ||
232 | (t . nil))) | ||
288 | 233 | ||
289 | I like =use-package= a lot, but I don't like using those shims you see in a lot of other Emacs configs where they use ~(use-package emacs)~ forms and stuff like that -- it just feels dirty. Plus, =straight= gets confused about those packages sometimes. So, since I'm actually /configuring/ Emacs in this Org file, which is nicely organized anyway, I can just set settings the old-school way. | 234 | (cuss indicate-empty-lines t) |
235 | #+end_src | ||
290 | 236 | ||
291 | Except. Using =setq= is actually /not/ recommended any more, because =customize-set-variable= is more expressive and can include side-effects. However, not all settings are customizable, /and/ =customize-set-variable= is like, way longer to type. So I've decided to write a little macro (my first!) to copy =use-package='s =:custom= keyword, except ... /outside/ =use-package=. I've called it =cuss=, because I have a terrible sense of humor. | 237 | ** Startup |
292 | 238 | ||
293 | #+begin_src emacs-lisp | 239 | #+begin_src emacs-lisp |
294 | (defmacro cuss (var val) | 240 | (cuss inhibit-startup-buffer-menu t) |
295 | "Basically `use-package''s `:custom', but without using either." | 241 | (cuss inhibit-start-screen t) |
296 | `(progn | 242 | (cuss initial-buffer-choice t) |
297 | (funcall (or (get ',var 'custom-set) #'set-default) | 243 | (cuss initial-scratch-message ";; Hi there!\n") |
298 | ',var ,val))) | ||
299 | #+end_src | 244 | #+end_src |
300 | 245 | ||
301 | * Theme: [[https://protesilaos.com/modus-themes/][Modus]] | 246 | ** Theme |
302 | |||
303 | Protesilaos Stavrou's /excellent/ theme pair. At some point I'll probably write my own that's really minimal and does some funky stuff with faces, but until then, these really are the best I've used. | ||
304 | |||
305 | He's recently updated the themes to 1.0.0, with a refactor and betterment; my config here reflects that change. | ||
306 | 247 | ||
307 | #+begin_src emacs-lisp | 248 | #+begin_src emacs-lisp |
308 | (use-package modus-themes | 249 | (use-package modus-themes |
309 | :straight (modus-themes | 250 | :straight (modus-themes |
310 | :host gitlab | 251 | :host gitlab |
311 | :repo "protesilaos/modus-themes" | 252 | :repo "protesilaos/modus-themes" |
312 | :branch "main") | 253 | :branch "main") |
313 | :custom | 254 | :custom |
314 | (modus-themes-slanted-constructs t) | 255 | (modus-themes-slanted-constructs t) |
315 | (modus-themes-bold-constructs t) | 256 | (modus-themes-bold-constructs t) |
@@ -340,351 +281,143 @@ He's recently updated the themes to 1.0.0, with a refactor and betterment; my co | |||
340 | (load-theme 'modus-operandi t)) | 281 | (load-theme 'modus-operandi t)) |
341 | #+end_src | 282 | #+end_src |
342 | 283 | ||
343 | Due to the new =modus-themes-load-operandi= and =modus-themes-load-vivendi= funcitons, I don't need =theme-changer= any more -- but I still need to set up the themes to change at sunrise and sunset. Well, I'll do that later -- for now I'll use a key to toggle them. | 284 | *** Fonts |
344 | |||
345 | #+begin_src emacs-lisp | ||
346 | (global-set-key (kbd "<f10>") #'modus-themes-toggle) | ||
347 | #+end_src | ||
348 | |||
349 | * GUI | ||
350 | |||
351 | ** Frame defaults | ||
352 | |||
353 | I want no toolbar, menubar, or scrollbars (ideally I'd have a vertical scrollbar if necessary, but apparently that's too much to ask the Emacs devs); and fringes and window dividers 2 pixels wide. | ||
354 | |||
355 | #+begin_src emacs-lisp | ||
356 | (cuss default-frame-alist | ||
357 | '((tool-bar-lines . 0) | ||
358 | (menu-bar-lines . 0) | ||
359 | (vertical-scroll-bars . nil) | ||
360 | (horizontal-scroll-bars . nil) | ||
361 | (right-divider-width . 2) | ||
362 | (bottom-divider-width . 2) | ||
363 | (left-fringe-width . 2) | ||
364 | (right-fringe-width . 2))) | ||
365 | #+end_src | ||
366 | |||
367 | ** Minibuffer window/frame defaults | ||
368 | |||
369 | Of course, on the minibuffer, I want to make sure there's no scrollbar -- even if I change my mind on =vertical-scroll-bars=, above. | ||
370 | |||
371 | #+begin_src emacs-lisp | ||
372 | (cuss minibuffer-frame-alist | ||
373 | '((width . 80) | ||
374 | (height . 2) | ||
375 | (vertical-scrollbars . nil))) | ||
376 | |||
377 | (set-window-scroll-bars (minibuffer-window) nil nil) | ||
378 | #+end_src | ||
379 | |||
380 | ** Remove unneeded GUI elements | ||
381 | |||
382 | The [[*Frame defaults][Frame Defaults]] section sets up the frame to be free of visual clutter, but /this/ section allows us to toggle that clutter's visibility easily, with one call to each of these functions. | ||
383 | |||
384 | #+begin_src emacs-lisp | ||
385 | (menu-bar-mode -1) | ||
386 | (tool-bar-mode -1) | ||
387 | (scroll-bar-mode -1) | ||
388 | (horizontal-scroll-bar-mode -1) | ||
389 | #+end_src | ||
390 | |||
391 | ** Silky scrolling | ||
392 | |||
393 | from [[https://pizza.eli.li/wiki/emacs-config/][elioat]]. I think it's causing a slowdown in scrolling for me, so I'm disabling it until I figure that out. | ||
394 | |||
395 | #+begin_src emacs-lisp :tangle no | ||
396 | (cuss scroll-margin 0) | ||
397 | (cuss scroll-conservatively 10000) | ||
398 | (cuss scroll-preserve-screen-position t) | ||
399 | (cuss auto-window-vscroll nil) | ||
400 | #+end_src | ||
401 | |||
402 | ** Tabs | ||
403 | |||
404 | I'm kind of getting into Emacs tabs -- but I like not showing the =tab-bar= when there's only one. | ||
405 | |||
406 | #+begin_src emacs-lisp | ||
407 | (cuss tab-bar-show 1) | ||
408 | |||
409 | (cuss tab-bar-tab-name-function 'tab-bar-tab-name-current-with-count) | ||
410 | #+end_src | ||
411 | |||
412 | ** Word wrap and operate visually | ||
413 | |||
414 | =global-visual-line-mode= is one of those which, in my opinion, should be a default. There's only one place I don't want to wrap words, and that's in =dired=, which I can set individually in its config. | ||
415 | |||
416 | #+begin_src emacs-lisp | ||
417 | (global-visual-line-mode 1) | ||
418 | #+end_src | ||
419 | |||
420 | ** Modeline | ||
421 | |||
422 | *** [[https://github.com/Malabarba/smart-mode-line][smart-mode-line]] | ||
423 | |||
424 | #+begin_src emacs-lisp | ||
425 | (use-package smart-mode-line | ||
426 | :custom | ||
427 | (sml/no-confirm-load-theme t) | ||
428 | :config | ||
429 | (sml/setup)) | ||
430 | #+end_src | ||
431 | |||
432 | *** [[https://github.com/Malabarba/rich-minority][rich-minority]] | ||
433 | |||
434 | =smart-mode-line= comes with =rich-minority= for taking care of minor modes in the modeline, so I'm not going to /also/ use =diminish= or anything. However, =rich-minority= has kind of a hinky way of adding modes to the whitelist, so I had to write my own function to do so. | ||
435 | |||
436 | This confuration means that, by default, no minor modes are shown; if you want a minor mode to be shown (like =word-count-mode= for me), call ~(rm/whitelist-add "REGEXP")~. | ||
437 | |||
438 | #+begin_src emacs-lisp | ||
439 | (defun rm/whitelist-add (regexp) | ||
440 | "Add a REGEXP to the whitelist for `rich-minority'." | ||
441 | (if (listp 'rm--whitelist-regexps) | ||
442 | (add-to-list 'rm--whitelist-regexps regexp) | ||
443 | (setq rm--whitelist-regexps `(,regexp))) | ||
444 | (setq rm-whitelist | ||
445 | (mapconcat 'identity rm--whitelist-regexps "\\|"))) | ||
446 | |||
447 | (use-package rich-minority | ||
448 | :config | ||
449 | (rm/whitelist-add "^$")) | ||
450 | #+end_src | ||
451 | |||
452 | *** Which-function-mode | ||
453 | |||
454 | Show the name of the current function in the modeline. Also works in Org mode to display the current header. Very cool! | ||
455 | |||
456 | #+begin_src emacs-lisp | ||
457 | (which-function-mode 1) | ||
458 | #+end_src | ||
459 | |||
460 | ** Minibuffer | ||
461 | |||
462 | *** Keep cursor from going into the prompt | ||
463 | |||
464 | from [[http://ergoemacs.org/emacs/emacs_stop_cursor_enter_prompt.html][Ergo Emacs]]. | ||
465 | |||
466 | #+begin_src emacs-lisp | ||
467 | (cuss minibuffer-prompt-properties | ||
468 | '(read-only t cursor-intangible t face minibuffer-prompt)) | ||
469 | #+end_src | ||
470 | |||
471 | ** Show =^L= as a line | ||
472 | |||
473 | I like using the form-feed character to separate pages, it turns out. 'Tis nice. This package turns that character into a nice long line. | ||
474 | |||
475 | #+begin_src emacs-lisp | ||
476 | (use-package form-feed | ||
477 | :hook | ||
478 | ((text-mode prog-mode) . form-feed-mode)) | ||
479 | #+end_src | ||
480 | |||
481 | ** Cursor | ||
482 | |||
483 | I want my cursor to be a bar in focused windows, but a hollow box in non-focused windows. | ||
484 | |||
485 | #+begin_src emacs-lisp | ||
486 | (cuss cursor-type 'bar) | ||
487 | (cuss cursor-in-non-selected-windows 'hollow) | ||
488 | #+end_src | ||
489 | |||
490 | ** Buffer decorations | ||
491 | *** Show buffer boundaries | ||
492 | 285 | ||
493 | These little L-shaped graphics at the top and bottom of buffers don't do anything, but I like 'em. | 286 | **** Define fonts |
494 | 287 | ||
495 | #+begin_src emacs-lisp | 288 | #+begin_src emacs-lisp |
496 | (cuss indicate-buffer-boundaries | ||
497 | '((top . right) | ||
498 | (bottom . right) | ||
499 | (t . nil))) | ||
500 | #+end_src | ||
501 | |||
502 | *** Indicate empty lines at the end of the buffer | ||
503 | #+begin_src emacs-lisp | ||
504 | (cuss indicate-empty-lines t) | ||
505 | #+end_src | ||
506 | |||
507 | * Typesetting | ||
508 | |||
509 | ** Fonts | ||
510 | This is the best way I've come up with to specify a number of different fonts that apply depending on what's applied. To be honest, I didn't really come up with the =font-candidate= function, though -- I got it from the [[https://www.emacswiki.org/emacs/SetFonts#toc11]["Testing if fonts are available?"]] section of the SetFonts page on EmacsWiki. | ||
511 | |||
512 | See [[https://emacs.stackexchange.com/questions/12351/when-to-call-find-font-if-launching-emacs-in-daemon-mode][this StackExchange question and answer]] for more information on why I have these font settings applied in a hook. | ||
513 | |||
514 | #+begin_src emacs-lisp | ||
515 | (require 'cl) | ||
516 | (defun font-candidate (&rest fonts) | 289 | (defun font-candidate (&rest fonts) |
517 | (loop for font in fonts | 290 | (catch :font |
518 | when (find-font (font-spec :name font)) | 291 | (dolist (font fonts) |
519 | return font)) | 292 | (if (find-font (font-spec :name font)) |
293 | (throw :font font))))) | ||
520 | 294 | ||
521 | (defun acdw/setup-fonts () | 295 | (defun acdw/setup-fonts () |
522 | "Setup fonts. This has to happen after the frame is set up for | 296 | "Setup fonts. This has to happen after the frame is setup for |
523 | the first time, so add it to `focus-in-hook'. It removes | 297 | the first time, so it should be added to `window-setup-hook'. It |
524 | itself." | 298 | removes itself from that hook." |
525 | (interactive) | 299 | (interactive) |
526 | (set-face-attribute 'default nil | 300 | (set-face-attribute 'default nil |
527 | :font | 301 | :font |
528 | (font-candidate | 302 | (font-candidate |
529 | "Libertinus Mono-11" | 303 | "Libertinus Mono-11" |
530 | "Linux Libertine Mono O-11" | 304 | "Linux Libertine Mono O-11" |
531 | "Go Mono-10" | 305 | "Go Mono-10" |
532 | "Consolas-10")) | 306 | "Consolas-10")) |
533 | 307 | ||
534 | (set-face-attribute 'fixed-pitch nil | 308 | (set-face-attribute 'fixed-pitch nil |
535 | :font | 309 | :font |
536 | (font-candidate | 310 | (font-candidate |
537 | "Libertinus Mono-11" | 311 | "Libertinus Mono-11" |
538 | "Linux Libertine Mono O-11" | 312 | "Linux Libertine Mono O-11" |
539 | "Go Mono-10" | 313 | "Go Mono-10" |
540 | "Consolas-10")) | 314 | "Consolas-10")) |
541 | 315 | ||
542 | (set-face-attribute 'variable-pitch nil | 316 | (set-face-attribute 'variable-pitch nil |
543 | :font | 317 | :font |
544 | (font-candidate | 318 | (font-candidate |
545 | "Libertinus Serif-14" | 319 | "Libertinus Serif-13" |
546 | "Linux Libertine O-12" | 320 | "Linux Libertine O-12" |
547 | "Georgia-11")) | 321 | "Georgia-11")) |
548 | |||
549 | (remove-hook 'focus-in-hook #'acdw/setup-fonts)) | ||
550 | |||
551 | (add-hook 'focus-in-hook #'acdw/setup-fonts) | ||
552 | #+end_src | ||
553 | |||
554 | ** [[https://github.com/rolandwalker/unicode-fonts][unicode-fonts]] | ||
555 | 322 | ||
556 | This does something similar to the above code, but for the entirety of the Unicode field (I think). | 323 | (remove-hook 'window-setup-hook #'acdw/setup-fonts)) |
557 | 324 | ||
558 | #+begin_src emacs-lisp | 325 | (add-hook 'window-setup-hook #'acdw/setup-fonts) |
559 | (use-package unicode-fonts | ||
560 | :config | ||
561 | (unicode-fonts-setup)) | ||
562 | #+end_src | 326 | #+end_src |
563 | 327 | ||
564 | ** Variable pitch faces | 328 | **** Variable-pitch in text modes |
565 | |||
566 | One reason I like the Modus themes so much is that they have /excellent/ support for variable-pitch faces, and mixing them with fixed-pitch faces in, say, Org Mode. That means I can enable =variable-pitch-mode= in all my =text-mode=-derived buffers. | ||
567 | 329 | ||
568 | #+begin_src emacs-lisp | 330 | #+begin_src emacs-lisp |
569 | (add-hook 'text-mode-hook #'variable-pitch-mode) | 331 | (add-hook 'text-mode-hook #'variable-pitch-mode) |
570 | #+end_src | 332 | #+end_src |
571 | 333 | ||
572 | ** Padding | 334 | **** Line spacing |
573 | |||
574 | This has been taken from [[https://lepisma.xyz/2017/10/28/ricing-org-mode/]["Ricing Org Mode"]] -- of course, I want the typographic niceties everywhere. | ||
575 | 335 | ||
576 | #+begin_src emacs-lisp | 336 | #+begin_src emacs-lisp |
577 | (cuss line-spacing 0.1) | 337 | (cuss line-spacing 0.1) |
578 | #+end_src | 338 | #+end_src |
579 | 339 | ||
580 | * Ease of use | 340 | **** Unicode fonts |
581 | |||
582 | ** Startup | ||
583 | |||
584 | I want a minimal screen when I start Emacs. Based on the beauty of configs like [[https://github.com/rougier/elegant-emacs][Nicolas Rougier's]] [[https://github.com/rougier/emacs-splash][splash screen]] [[https://github.com/rougier/nano-emacs][experiments]], I might try my hand at some kind of splash screen or dashboard -- but until then, a simple "Hi there!" will suffice 😎 | ||
585 | 341 | ||
586 | #+begin_src emacs-lisp | 342 | #+begin_src emacs-lisp |
587 | (cuss inhibit-startup-buffer-menu t) | 343 | (use-package unicode-fonts |
588 | (cuss inhibit-startup-screen t) | 344 | :config |
589 | (cuss initial-buffer-choice t) | 345 | (unicode-fonts-setup)) |
590 | (cuss initial-scratch-message ";; Hi there!\n") | ||
591 | #+end_src | ||
592 | |||
593 | ** Don't use =customize= | ||
594 | |||
595 | I use customize to discover different things Emacs can do, but I (a) don't want to write the customizations to my =init.el= and (b) I don't want to load them on startup. One source of truth for me thanks! | ||
596 | |||
597 | #+begin_src emacs-lisp | ||
598 | (cuss custom-file | ||
599 | (no-littering-expand-etc-file-name "custom.el")) | ||
600 | #+end_src | 346 | #+end_src |
601 | 347 | ||
602 | ** Completing-read niceties | 348 | * Interactivity |
603 | |||
604 | =completing-read= is Emacs's selection-narrowing-slash-completion framework thing. There's a bunch of packages for it, including =ido=, =icomplete=, =ivy=, and =helm=. I use raxod52's =selectrum= and others, which /extend/ without /clobbering/ existing Emacs functionality. Plus they seem to run faster, at least on Windows. | ||
605 | |||
606 | *** [[https://github.com/raxod502/selectrum][selectrum]] | ||
607 | 349 | ||
608 | =selectrum= is the basic /sorting and selecting items from a list/ functionality. It's a drop-in replacement for =ido= or the really basic tab-completion Emacs has for, say, =find-file=. | 350 | ** Selectrum |
609 | 351 | ||
610 | #+begin_src emacs-lisp | 352 | #+begin_src emacs-lisp |
611 | (use-package selectrum | 353 | (use-package selectrum |
612 | :config | 354 | :config |
613 | (selectrum-mode 1)) | 355 | (selectrum-mode +1)) |
614 | #+end_src | 356 | #+end_src |
615 | 357 | ||
616 | *** [[https://github.com/raxod502/prescient.el][prescient]] | 358 | ** Prescient |
617 | |||
618 | =prescient= helps =selectrum= be more intelligent about sorting the candidates in a list -- it's in charge of the /filtering and sorting/ bit of =completing-read= and friends. It has an algorithm that works well enough for me, though I keep hearing about [[https://github.com/oantolin/orderless][orderless]], enough to maybe try it as well sometime. | ||
619 | 359 | ||
620 | #+begin_src emacs-lisp | 360 | #+begin_src emacs-lisp |
621 | (use-package prescient | 361 | (use-package prescient |
622 | :config | 362 | :config |
623 | (prescient-persist-mode 1)) | 363 | (prescient-persist-mode +1)) |
624 | 364 | ||
625 | (use-package selectrum-prescient | 365 | (use-package selectrum-prescient |
626 | :after (selectrum prescient) | 366 | :after (selectrum prescient) |
627 | :config | 367 | :config |
628 | (selectrum-prescient-mode 1)) | 368 | (selectrum-prescient-mode +1)) |
629 | #+end_src | 369 | #+end_src |
630 | 370 | ||
631 | *** [[https://github.com/minad/cconsult][consult]] | 371 | ** Consult |
632 | |||
633 | =consult= is the newest package I have with this setup, and it kind of brings the =selectrum= experience up to par with =ivy='s -- it provides functions that list, say, recently used files /alongside/ buffers, allow you to search lines and go to them, etc. It seems pretty nice so far. | ||
634 | |||
635 | By the way, the [[https://www.reddit.com/r/emacs/comments/k3c0u7][Reddit announcement thread for consult]] has a great comment by the author detailing [[https://www.reddit.com/r/emacs/comments/k3c0u7/consult_counselswiper_alternative_for/ge460z3/][the differences between different completing-read implementations]] that actually is what convinced me to try =consult=. | ||
636 | 372 | ||
637 | #+begin_src emacs-lisp | 373 | #+begin_src emacs-lisp |
638 | (use-package consult | 374 | (use-package consult |
639 | :after (selectrum) | 375 | :after (selectrum) |
640 | :straight (consult | 376 | :straight (consult |
641 | :host github | 377 | :host github |
642 | :repo "minad/consult") | 378 | :repo "minad/consult") |
643 | :bind (("C-x b" . consult-buffer) | 379 | :bind |
644 | ("C-x 4 b" . consult-buffer-other-window) | 380 | (("C-x b" . consult-buffer) |
645 | ("C-x 5 b" . consult-buffer-other-frame) | 381 | ("C-x 4 b" . consult-buffer-other-window) |
646 | ("M-g o" . consult-outline) | 382 | ("C-x 5 b" . consult-buffer-other-frame) |
647 | ("M-g l" . consult-line) | 383 | ("M-g o" . consult-outline) |
648 | ("M-y" . consult-yank-pop) | 384 | ("M-g l" . consult-line) |
649 | ("<help> a" . consult-apropos)) | 385 | ("M-y" . consult-yank-pop) |
386 | ("<help> a" . consult-apropos)) | ||
650 | :init | 387 | :init |
651 | (fset 'multi-occur #'consult-multi-occur)) | 388 | (fset 'multi-occur #'consult-multi-occur)) |
652 | #+end_src | 389 | #+end_src |
653 | 390 | ||
654 | *** [[https://github.com/minad/marginalia/][Marginalia]] | 391 | ** Marginalia |
655 | |||
656 | These provide /marginalia/ in the minibuffer. Until like, December 4, 2020, they were part of =consult=. So let's try them out. | ||
657 | 392 | ||
658 | #+begin_src emacs-lisp | 393 | #+begin_src emacs-lisp |
659 | (use-package marginalia | 394 | (use-package marginalia |
660 | :straight (marginalia | 395 | :straight (marginalia |
661 | :host github | 396 | :host github |
662 | :repo "minad/marginalia" | 397 | :repo "minad/marginalia" |
663 | :branch "main") | 398 | :branch "main") |
399 | :custom | ||
400 | (marginalia-annotators | ||
401 | '((command . marginalia-annotate-command-full) | ||
402 | (customize-group . marginalia-annotate-customize-group) | ||
403 | (variable . marginalia-annotate-variable) | ||
404 | (face . marginalia-annotate-face) | ||
405 | (symbol . marginalia-annotate-symbol) | ||
406 | (variable . marginalia-annotate-variable) | ||
407 | (package . marginalia-annotate-package))) | ||
664 | :init | 408 | :init |
665 | (marginalia-mode) | 409 | (marginalia-mode +1)) |
666 | ;; Enable richer annotations for M-x. | ||
667 | ;; Only keybindings are shown by default, in order to reduce noise for this very common command. | ||
668 | ;; * marginalia-annotate-symbol: Annotate with the documentation string | ||
669 | ;; * marginalia-annotate-command-binding (default): Annotate only with the keybinding | ||
670 | ;; * marginalia-annotate-command-full: Annotate with the keybinding and the documentation string | ||
671 | (setf (alist-get 'command marginalia-annotator-alist) | ||
672 | #'marginalia-annotate-command-full)) | ||
673 | #+end_src | 410 | #+end_src |
674 | 411 | ||
675 | *** Ignore case | 412 | ** Ignore case |
676 | |||
677 | I don't like holding the Shift key if I can help it. | ||
678 | 413 | ||
679 | #+BEGIN_SRC emacs-lisp | 414 | #+begin_src emacs-lisp |
680 | (cuss completion-ignore-case t) | 415 | (cuss completion-ignore-case t) |
681 | (cuss read-buffer-completion-ignore-case t) | 416 | (cuss read-buffer-completion-ignore-case t) |
682 | (cuss read-file-name-completion-ignore-case t) | 417 | (cuss read-file-name-completion-ignore-case t) |
683 | #+END_SRC | 418 | #+end_src |
684 | |||
685 | ** [[https://github.com/raxod502/ctrlf][ctrlf]] | ||
686 | 419 | ||
687 | The biggest reason I use this over the default functionality of =C-s= is that =ctrlf-forward-*= wraps the search around by default. | 420 | ** Search |
688 | 421 | ||
689 | #+begin_src emacs-lisp | 422 | #+begin_src emacs-lisp |
690 | (use-package ctrlf | 423 | (use-package ctrlf |
@@ -696,388 +429,184 @@ The biggest reason I use this over the default functionality of =C-s= is that =c | |||
696 | ("C-M-s" . ctrlf-forward-literal) | 429 | ("C-M-s" . ctrlf-forward-literal) |
697 | ("C-M-r" . ctrlf-backward-literal) | 430 | ("C-M-r" . ctrlf-backward-literal) |
698 | :config | 431 | :config |
699 | (ctrlf-mode 1)) | 432 | (ctrlf-mode +1)) |
700 | #+end_src | ||
701 | |||
702 | ** [[https://github.com/justbur/emacs-which-key][which-key]] | ||
703 | |||
704 | This package is really helpful for discovering functionality. When I get more adept in my Emacs-fu, I might remove this. | ||
705 | |||
706 | #+begin_src emacs-lisp | ||
707 | (use-package which-key | ||
708 | :custom | ||
709 | (which-key-popup-type 'minibuffer) | ||
710 | (which-key-separator " ") | ||
711 | (which-key-prefix-prefix "+") | ||
712 | :config | ||
713 | (which-key-mode)) | ||
714 | #+end_src | ||
715 | |||
716 | ** Miscellaneous settings | ||
717 | |||
718 | Maybe a better title for this section is *Other settings* -- or maybe I should put them somewhere else entirely. | ||
719 | |||
720 | *** Set =view-mode= when in a read-only file | ||
721 | |||
722 | =view-mode= gives easy-to-use keybindings, like Space for page-down, etc., which are nice to have when you can't edit the file anyway. | ||
723 | |||
724 | #+begin_src emacs-lisp | ||
725 | (cuss view-read-only t) | ||
726 | #+end_src | ||
727 | |||
728 | *** Don't use dialog boxen | ||
729 | |||
730 | #+begin_src emacs-lisp | ||
731 | (cuss use-dialog-box nil) | ||
732 | #+end_src | ||
733 | |||
734 | *** Enable all functions | ||
735 | |||
736 | By default, Emacs disables some commands, because NeWbIeS wOuLd GeT cOnFuSeD or some ish. I just want to use the dang editor! | ||
737 | |||
738 | #+begin_src emacs-lisp | ||
739 | (cuss disabled-command-function nil) | ||
740 | #+end_src | ||
741 | |||
742 | *** Shorter confirmations | ||
743 | |||
744 | Instead of making me type /yes/ or /no/, just let me hit the /y/ or /n/ key. | ||
745 | |||
746 | #+begin_src emacs-lisp | ||
747 | (fset 'yes-or-no-p #'y-or-n-p) | ||
748 | #+end_src | 433 | #+end_src |
749 | 434 | ||
750 | *** Uniquify buffer names | 435 | * Persistence |
751 | |||
752 | This names buffers with the same basename (e.g., =~/.config/emacs/config.org= and =~/.emacs.d/config.org=) in a better way than the default (=config.org<1>=, etc). | ||
753 | |||
754 | #+begin_src emacs-lisp | ||
755 | (require 'uniquify) | ||
756 | (cuss uniquify-buffer-name-style 'forward) | ||
757 | #+end_src | ||
758 | |||
759 | *** Hippie expand | ||
760 | |||
761 | At some point, will probably replace with [[https://company-mode.github.io/][company]]. | ||
762 | |||
763 | #+begin_src emacs-lisp | ||
764 | (global-set-key (kbd "M-/") 'hippie-expand) | ||
765 | #+end_src | ||
766 | |||
767 | *** Delete the selection | ||
768 | |||
769 | Like modern editors, if I have text selected and start typing – just /delete the selection/. | ||
770 | |||
771 | #+BEGIN_SRC emacs-lisp | ||
772 | (delete-selection-mode 1) | ||
773 | #+end_src | ||
774 | |||
775 | *** "[[https://git.sr.ht/~technomancy/better-defaults/tree/master/better-defaults.el][better defaults]]" | ||
776 | |||
777 | Most of these come from technomancy's repo, linked above, just copy-pasted into here. | ||
778 | |||
779 | #+begin_src emacs-lisp | ||
780 | (cuss save-interprogram-paste-before-kill t) | ||
781 | (cuss apropos-do-all t) | ||
782 | (cuss mouse-yank-at-point t) | ||
783 | (cuss require-final-newline t) | ||
784 | (cuss visible-bell (not (string= (system-name) "larry"))) | ||
785 | (cuss ediff-window-setup-function #'ediff-setup-windows-plain) | ||
786 | #+end_src | ||
787 | |||
788 | **** Zap-up-to-char, not zap-to-char | ||
789 | |||
790 | Similarly to =ibuffer=, this is a Better default™. | ||
791 | |||
792 | Of course, could be even betterered with [[https://github.com/mrkkrp/zzz-to-char][zzz-to-char]]. | ||
793 | |||
794 | #+begin_src emacs-lisp | ||
795 | (autoload 'zap-up-to-char "misc" | ||
796 | "Kill up to, but not including, ARGth occurrence of CHAR." t) | ||
797 | |||
798 | (global-set-key (kbd "M-z") 'zap-up-to-char) | ||
799 | #+end_src | ||
800 | |||
801 | **** iBuffer | ||
802 | 436 | ||
803 | A Better Default™ for =C-x C-b=. I don't really use this, but everyone says it's worth it, so it's there. | 437 | ** Save history |
804 | 438 | ||
805 | #+begin_src emacs-lisp | 439 | #+begin_src emacs-lisp |
806 | (global-set-key (kbd "C-x C-b") 'ibuffer) | 440 | (require 'savehist) |
807 | #+end_src | ||
808 | |||
809 | *** So-long-mode | ||
810 | |||
811 | I figure, why not go ahead and make Emacs deal with really long lines better? Can't hurt, right? | ||
812 | 441 | ||
813 | #+begin_src emacs-lisp | 442 | (cuss savehist-additional-variables |
814 | (if (boundp 'global-so-long-mode) | 443 | '(kill-ring |
815 | (global-so-long-mode)) | 444 | search-ring |
816 | #+end_src | 445 | regexp-search-ring)) |
817 | 446 | ||
818 | *** Change =just-one-space= to =cycle-space= | 447 | (cuss savehist-save-minibuffer-history t) |
819 | 448 | ||
820 | I keep forgetting to actually /use/ this keybind (I think it's =M-SPC=?), but cycling spacing seems /way/ more useful than the default =just-one-space= function. | 449 | (cuss history-length t) |
821 | 450 | ||
822 | #+begin_src emacs-lisp | 451 | (cuss history-delete-duplicates t) |
823 | (defun acdw/cycle-spacing-1 () | ||
824 | (interactive) | ||
825 | (cycle-spacing -1)) | ||
826 | 452 | ||
827 | (bind-key [remap just-one-space] #'acdw/cycle-spacing-1) | 453 | (savehist-mode +1) |
828 | #+end_src | 454 | #+end_src |
829 | 455 | ||
830 | * Persistence | 456 | ** Save places in files |
831 | |||
832 | Honestly, persistence across sessions was one of the best things about my well-tuned Vim setup. Here's where I try to repeat that with Emacs. | ||
833 | |||
834 | ** Auto-saves with [[https://github.com/bbatsov/super-save][super-save]] | ||
835 | |||
836 | The default =auto-save= functionality isn't ... /enough/ for me. I want to /actually/ save the files, and I don't care about =#file#= stuff. So ... I use this package. | ||
837 | 457 | ||
838 | #+begin_src emacs-lisp | 458 | #+begin_src emacs-lisp |
839 | (use-package super-save | 459 | (require 'saveplace) |
840 | :custom | ||
841 | (auto-save-default nil) | ||
842 | (super-save-exclue '(".gpg")) | ||
843 | :config | ||
844 | (super-save-mode 1)) | ||
845 | #+end_src | ||
846 | |||
847 | ** Backup files | ||
848 | |||
849 | To be honest, I probably don't need backup files at all. At some point, I will probably delete this. | ||
850 | 460 | ||
851 | #+begin_src emacs-lisp | 461 | (cuss save-place-forget-unreadable-files |
852 | (cuss backup-directory-alist | 462 | (not (eq system-type 'windows-nt))) |
853 | `((".*" . ,(no-littering-expand-var-file-name "backup/")))) | ||
854 | 463 | ||
855 | (cuss backup-by-copying 1) | 464 | (save-place-mode 1) |
856 | (cuss delete-old-versions -1) | ||
857 | (cuss version-control t) | ||
858 | (cuss vc-make-backup-files t) | ||
859 | #+end_src | 465 | #+end_src |
860 | 466 | ||
861 | ** Recent files | 467 | ** Recent files |
862 | 468 | ||
863 | Since I apparently /only/ edit my =config.org=, this is also probably not necessary -- I'd be better off just adding a ~(find-file (concat (user-emacs-directory "config.org")))~ at the end 😎 | ||
864 | |||
865 | But until then, it's really nice to have a =recentf= list. | ||
866 | |||
867 | #+begin_src emacs-lisp | 469 | #+begin_src emacs-lisp |
868 | (require 'recentf) | 470 | (require 'recentf) |
869 | 471 | ||
870 | (add-to-list 'recentf-exclude | ||
871 | '(no-littering-var-directory | ||
872 | no-littering-etc-directory)) | ||
873 | |||
874 | (cuss recentf-max-menu-items 100) | 472 | (cuss recentf-max-menu-items 100) |
875 | (cuss recentf-max-saved-items 100) | 473 | (cuss recentf-max-saved-items 100) |
876 | 474 | ||
475 | (with-eval-after-load 'no-littering | ||
476 | (add-to-list 'recentf-exclude no-littering-var-directory) | ||
477 | (add-to-list 'recentf-exclude no-littering-etc-directory)) | ||
478 | |||
877 | (recentf-mode 1) | 479 | (recentf-mode 1) |
878 | #+end_src | 480 | #+end_src |
879 | 481 | ||
880 | *** Easily navigate recent files | 482 | *** Easily navigate recent files |
881 | 483 | ||
882 | Now I'm going through this, I might not need this function any more. I'll have to see how =consult= goes. | ||
883 | |||
884 | #+begin_src emacs-lisp | 484 | #+begin_src emacs-lisp |
885 | (defun recentf-find-file () | 485 | (defun recentf-find-file () |
886 | "Find a recent file using `completing-read'." | 486 | "Find a recent file using `completing-read'." |
887 | (interactive) | 487 | (interactive) |
888 | (let ((file (completing-read "Recent file: " recentf-list nil t))) | 488 | (let ((file (completing-read "Recent file: " recentf-list nil t))) |
889 | (when file | 489 | (when file |
890 | (find-file file)))) | 490 | (find-file file)))) |
891 | 491 | ||
892 | (bind-key "C-x C-r" #'recentf-find-file) | 492 | (global-set-key (kbd "C-x C-r") #'recentf-find-file) |
893 | #+end_src | 493 | #+end_src |
894 | 494 | ||
895 | ** Save places in visited files | 495 | ** Undo |
896 | 496 | ||
897 | #+begin_src emacs-lisp | 497 | #+begin_src emacs-lisp |
898 | (require 'saveplace) | 498 | (use-package undohist |
899 | 499 | :config | |
900 | (cuss save-place-file (no-littering-expand-var-file-name "places")) | 500 | (undohist-initialize)) |
901 | |||
902 | (cuss save-place-forget-unreadable-files | ||
903 | (not (eq system-type 'windows-nt))) | ||
904 | |||
905 | (save-place-mode 1) | ||
906 | #+end_src | 501 | #+end_src |
907 | 502 | ||
908 | ** Save history | 503 | * Editing |
909 | |||
910 | #+begin_src emacs-lisp | ||
911 | (require 'savehist) | ||
912 | |||
913 | (cuss savehist-additional-variables | ||
914 | '(kill-ring | ||
915 | search-ring | ||
916 | regexp-search-ring)) | ||
917 | |||
918 | (cuss savehist-save-minibuffer-history t) | ||
919 | 504 | ||
920 | (cuss history-length t) | 505 | ** Operate visually on lines |
921 | 506 | ||
922 | (cuss history-delete-duplicates t) | 507 | #+begin_src emacs-lisp |
923 | 508 | (global-visual-line-mode +1) | |
924 | (savehist-mode 1) | ||
925 | #+end_src | 509 | #+end_src |
926 | 510 | ||
927 | ** Undo: [[https://gitlab.com/ideasman42/emacs-undo-fu-session][undo-fu-session]] | 511 | ** Require a final newline |
928 | |||
929 | The other Killer Feature of Neovim when I used it was the perisistent undo. I /think/ this works the same. Honestly, undo is giving me a little grief recently; I need to look into it. | ||
930 | |||
931 | Note to self: if I /do/ switch away from =undo-fu=, look at [[https://github.com/emacsorphanage/undohist][undohist]]. | ||
932 | 512 | ||
933 | #+begin_src emacs-lisp | 513 | #+begin_src emacs-lisp |
934 | (use-package undo-fu-session | 514 | (cuss require-final-newline t) |
935 | :after (no-littering undo-fu) | ||
936 | :custom | ||
937 | (undo-fu-session-incompatible-files | ||
938 | '("COMMIT_EDITMSG\\'" | ||
939 | "/git-rebase-todo\\'")) | ||
940 | (undo-fu-session-directory | ||
941 | (no-littering-expand-var-file-name "undos/")) | ||
942 | :config | ||
943 | (global-undo-fu-session-mode 1)) | ||
944 | #+end_src | 515 | #+end_src |
945 | 516 | ||
946 | * General editing | 517 | ** Killing & Yanking |
947 | |||
948 | ** File encoding | ||
949 | 518 | ||
950 | I just want to use UTF-8 everywhere, and end all files with UNIX line endings (=^J=, or =LF=). Hell, even Windows Notepad correctly reads UNIX files nowadays (though of course you can't configure it to /save/ the files in UNIX-mode). However, since Emacs is ~40 years old, it has a billion different ways to set encodings. This is my best attempt at setting everything up how I want it. | 519 | *** Replace selection when typing |
951 | |||
952 | I'm going to be honest -- most of this is a stab in the dark. | ||
953 | 520 | ||
954 | #+begin_src emacs-lisp | 521 | #+begin_src emacs-lisp |
955 | (set-language-environment 'utf-8) | 522 | (delete-selection-mode +1) |
956 | (set-terminal-coding-system 'utf-8) | ||
957 | (cuss locale-coding-system 'utf-8) | ||
958 | (set-default-coding-systems 'utf-8) | ||
959 | (set-selection-coding-system 'utf-8) | ||
960 | (prefer-coding-system 'utf-8) | ||
961 | |||
962 | ;; from https://www.emacswiki.org/emacs/EndOfLineTips | ||
963 | |||
964 | (defun acdw/no-junk-please-were-unixish () | ||
965 | "Convert line endings to UNIX, dammit." | ||
966 | (let ((coding-str (symbol-name buffer-file-coding-system))) | ||
967 | (when (string-match "-\\(?:dos\\|mac\\)$" coding-str) | ||
968 | (set-buffer-file-coding-system 'unix)))) | ||
969 | |||
970 | (add-hook 'find-file-hooks #'acdw/no-junk-please-were-unixish) | ||
971 | #+end_src | 523 | #+end_src |
972 | 524 | ||
973 | ** [[https://gitlab.com/ideasman42/emacs-undo-fu][undo-fu]] | 525 | *** Save existing clipboard text into kill ring before replacing it |
974 | |||
975 | I've heard that Emacs' undo is weird, so here I am, trying to make it .... /less/ weird. I keep forgetting I've installed this though, so I might uninstall it at some point. | ||
976 | 526 | ||
977 | #+begin_src emacs-lisp | 527 | #+begin_src emacs-lisp |
978 | (use-package undo-fu | 528 | (cuss save-interprogram-paste-before-kill t) |
979 | :bind | ||
980 | ("C-/" . undo-fu-only-undo) | ||
981 | ("C-?" . undo-fu-only-redo)) | ||
982 | #+end_src | 529 | #+end_src |
983 | 530 | ||
984 | ** Find/replace: [[https://github.com/benma/visual-regexp.el][visual-regexp]] | 531 | ** So long mode |
985 | |||
986 | Another replacement for a Killer Feature in Neovim -- the ease of regexp find/replace was so wonderful, because I could easily see /what/ I'd be changing with a =%s= command, as well as /how/ it'd change. This works... pretty similarly. It could be a little better. | ||
987 | 532 | ||
988 | #+begin_src emacs-lisp | 533 | #+begin_src emacs-lisp |
989 | (use-package visual-regexp | 534 | (when (fboundp 'global-so-long-mode) |
990 | :bind | 535 | (global-so-long-mode)) |
991 | ("C-c r" . 'vr/replace) | ||
992 | ("C-c q" . 'vr/query-replace)) | ||
993 | #+end_src | 536 | #+end_src |
994 | 537 | ||
995 | ** Visual editing | 538 | * Files |
539 | |||
540 | ** Encoding | ||
541 | *** UTF-8 | ||
996 | 542 | ||
997 | *** [[https://github.com/k-talo/volatile-highlights.el][volatile-highlights]] | 543 | #+begin_src emacs-lisp |
544 | (set-language-environment 'utf-8) | ||
545 | (set-terminal-coding-system 'utf-8) | ||
546 | (cuss locale-coding-system 'utf-8) | ||
547 | (set-default-coding-systems 'utf-8) | ||
548 | (set-selection-coding-system 'utf-8) | ||
549 | (prefer-coding-system 'utf-8) | ||
550 | #+end_src | ||
998 | 551 | ||
999 | Highlights text changed by certain operations. | 552 | *** Convert all files to UNIX-style line endings |
1000 | 553 | ||
1001 | #+begin_src emacs-lisp | 554 | from [[https://www.emacswiki.org/emacs/EndOfLineTips][Emacs Wiki]]. |
1002 | (use-package volatile-highlights | ||
1003 | :config | ||
1004 | (volatile-highlights-mode 1)) | ||
1005 | #+end_src | ||
1006 | 555 | ||
1007 | *** [[https://github.com/magnars/expand-region.el][expand-region]] | 556 | #+begin_src emacs-lisp |
1008 | 557 | (defun ewiki/no-junk-please-were-unixish () | |
1009 | I don't use this a /ton/, but not because it's not useful -- I just forget it's there sometimes. | 558 | "Convert line endings to UNIX, dammit." |
559 | (let ((coding-str (symbol-name buffer-file-coding-system))) | ||
560 | (when (string-match "-\\(?:dos\\|mac\\)$" coding-str) | ||
561 | (set-buffer-file-coding-system 'unix)))) | ||
562 | #+end_src | ||
1010 | 563 | ||
1011 | Basically, it allows you to do like a Kakoune-style incremental widening of the selection by semantic units. | 564 | 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. |
1012 | 565 | ||
1013 | #+begin_src emacs-lisp | 566 | #+begin_src emacs-lisp |
1014 | (use-package expand-region | 567 | (add-hook 'find-file-hook #'ewiki/no-junk-please-were-unixish) |
1015 | :bind | 568 | (add-hook 'before-save-hook #'ewiki/no-junk-please-were-unixish) |
1016 | ("C-=" . er/expand-region) | ||
1017 | ("C-+" . er/contract-region)) | ||
1018 | #+end_src | 569 | #+end_src |
1019 | 570 | ||
1020 | ** Clean up white space on save | 571 | ** Backups |
1021 | |||
1022 | I'm not sure if I'll /keep/ this forever, because in combination with =super-save= I lose the final "thinking spaces" when I shift contexts to another window. | ||
1023 | 572 | ||
1024 | #+begin_src emacs-lisp | 573 | #+begin_src emacs-lisp |
1025 | (add-hook 'before-save-hook #'whitespace-cleanup) | 574 | (cuss backup-by-copying 1) |
1026 | (add-hook 'before-save-hook #'delete-trailing-whitespace) | 575 | (cuss delete-old-versions -1) |
576 | (cuss version-control t) | ||
577 | (cuss vc-make-backup-files t) | ||
1027 | #+end_src | 578 | #+end_src |
1028 | 579 | ||
1029 | ** [[https://github.com/nflath/hungry-delete][hungry-delete]] | 580 | ** Auto-saves |
1030 | |||
1031 | I find myself typing delete /way/ too much. I really like Emacs's =TAB= functionality -- it tabs to where I need and that's it -- but backspace leaves much to be desired. Enter =hungry-delete=. | ||
1032 | |||
1033 | Also of interest: [[https://github.com/hrehfeld/emacs-smart-hungry-delete][smart-hungry-delete]]. | ||
1034 | 581 | ||
1035 | #+begin_src emacs-lisp | 582 | #+begin_src emacs-lisp |
1036 | (use-package hungry-delete | 583 | (auto-save-visited-mode 1) |
1037 | :custom | ||
1038 | (hungry-delete-join-reluctantly t) | ||
1039 | :custom-update | ||
1040 | (hungry-delete-except-modes | ||
1041 | '(emacs-lisp-mode | ||
1042 | lisp-mode | ||
1043 | fennel-mode)) | ||
1044 | (global-hungry-delete-mode)) | ||
1045 | :config | ||
1046 | #+end_src | 584 | #+end_src |
1047 | 585 | ||
1048 | ** Automatically revert a file to what it is on disk | 586 | ** Revert files |
1049 | |||
1050 | Revert a buffer to reflect what's on disk if it's changed outside of Emacs. | ||
1051 | 587 | ||
1052 | #+begin_src emacs-lisp | 588 | #+begin_src emacs-lisp |
1053 | (global-auto-revert-mode 1) | ||
1054 | (cuss auto-revert-verbose nil) | 589 | (cuss auto-revert-verbose nil) |
590 | (global-auto-revert-mode +1) | ||
1055 | #+end_src | 591 | #+end_src |
1056 | 592 | ||
1057 | ** Add a timestamp | 593 | ** Add a timestamp to files |
1058 | |||
1059 | This'll add a timestamp to a file when I save it, if the first 8 lines includes the string =Time-stamp: <>=. | ||
1060 | 594 | ||
1061 | #+begin_src emacs-lisp | 595 | #+begin_src emacs-lisp |
1062 | (add-hook 'before-save-hook #'time-stamp) | 596 | (add-hook 'before-save-hook #'time-stamp) |
1063 | #+end_src | 597 | #+end_src |
1064 | 598 | ||
1065 | * Writing | 599 | * Programming |
1066 | 600 | ||
1067 | Configurations related to writing prose or verse. | 601 | ** Which function are we in? |
1068 | |||
1069 | ** Word count: [[https://github.com/bnbeckwith/wc-mode][wc-mode]] | ||
1070 | 602 | ||
1071 | #+begin_src emacs-lisp | 603 | #+begin_src emacs-lisp |
1072 | (use-package wc-mode | 604 | (which-function-mode +1) |
1073 | :config | ||
1074 | (rm/whitelist-add "WC") | ||
1075 | :hook text-mode) | ||
1076 | #+end_src | 605 | #+end_src |
1077 | 606 | ||
1078 | ** [[https://github.com/joostkremers/visual-fill-column][visual-fill-column-mode]] | 607 | * Writing |
1079 | 608 | ||
1080 | Center the text part of the frame within a =fill-column=-sized area in the frame as a whole. | 609 | ** Visual Fill Column |
1081 | 610 | ||
1082 | #+begin_src emacs-lisp | 611 | #+begin_src emacs-lisp |
1083 | (use-package visual-fill-column | 612 | (use-package visual-fill-column |
@@ -1088,855 +617,122 @@ Center the text part of the frame within a =fill-column=-sized area in the frame | |||
1088 | (fill-column 80) | 617 | (fill-column 80) |
1089 | :config | 618 | :config |
1090 | (advice-add 'text-scale-adjust | 619 | (advice-add 'text-scale-adjust |
1091 | :after #'visual-fill-column-adjust) | 620 | :after #'visual-fill-column-adjust) |
1092 | :hook | 621 | :hook |
1093 | (text-mode . visual-fill-column-mode)) | 622 | (text-mode . visual-fill-column-mode)) |
1094 | #+end_src | 623 | #+end_src |
1095 | 624 | ||
1096 | *** Fix mouse bindings | 625 | ** Type nice-looking quote-type marks |
1097 | |||
1098 | In =visual-fill-column-mode=, mouse bindings on the margins don't work. In fact, they don't work when /not/ in =visual-fill-column-mode=. Let's bind those bindings. | ||
1099 | |||
1100 | #+begin_src emacs-lisp | ||
1101 | (dolist (vec '([left-margin wheel-down] | ||
1102 | [right-margin wheel-down])) | ||
1103 | (bind-key vec 'scroll-down-command)) | ||
1104 | |||
1105 | (dolist (vec '([left-margin wheel-up] | ||
1106 | [right-margin wheel-up])) | ||
1107 | (bind-key vec 'scroll-up-command)) | ||
1108 | #+end_src | ||
1109 | |||
1110 | =mouse-4= and =mouse-5= are also =wheel-up= and =wheel-down=, but they're reversed on *larry*, which uses "natural scrolling." I don't know, I like it. | ||
1111 | |||
1112 | #+begin_src emacs-lisp | ||
1113 | (dolist (vec '([left-margin mouse-4] | ||
1114 | [right-margin mouse-4])) | ||
1115 | (if (string= system-name "larry") | ||
1116 | (bind-key vec 'scroll-down-command) | ||
1117 | (bind-key vec 'scroll-up-command))) | ||
1118 | |||
1119 | (dolist (vec '([left-margin mouse-5] | ||
1120 | [right-margin mouse-5])) | ||
1121 | (if (string= system-name "larry") | ||
1122 | (bind-key vec 'scroll-up-command) | ||
1123 | (bind-key vec 'scroll-down-command))) | ||
1124 | #+end_src | ||
1125 | |||
1126 | ** [[https://orgmode.org/][org-mode]] | ||
1127 | |||
1128 | Pretty self-explanatory, I think... | ||
1129 | |||
1130 | I need to break this config up and like, comment it better. | ||
1131 | |||
1132 | #+begin_src emacs-lisp | ||
1133 | (use-package org | ||
1134 | :custom | ||
1135 | (org-startup-indented t) | ||
1136 | (org-src-tab-acts-natively t) | ||
1137 | (org-src-fontify-natively t) | ||
1138 | (org-confirm-babel-evaluate nil) | ||
1139 | (org-hide-emphasis-markers t) | ||
1140 | (org-fontify-done-headline t) | ||
1141 | (org-fontify-whole-heading-line t) | ||
1142 | (org-fontify-quote-and-verse-blocks t) | ||
1143 | (org-hide-leading-stars nil) | ||
1144 | (org-hidden-keywords '(author date title)) | ||
1145 | (org-src-window-setup 'current-window) | ||
1146 | (org-pretty-entities t) | ||
1147 | (org-ellipsis " ⋯ ")) | ||
1148 | #+end_src | ||
1149 | |||
1150 | *** Make bullets look like centered dots | ||
1151 | |||
1152 | from [[https://zzamboni.org/post/beautifying-org-mode-in-emacs/][zzamboni.org]] | ||
1153 | |||
1154 | #+begin_src emacs-lisp :tangle no | ||
1155 | (font-lock-add-keywords | ||
1156 | 'org-mode | ||
1157 | '(("^ *\\([-+]\\) " | ||
1158 | (0 (prog1 () | ||
1159 | (compose-region (match-beginning 1) | ||
1160 | (match-end 1) | ||
1161 | "•")))))) | ||
1162 | #+end_src | ||
1163 | |||
1164 | *** [[https://github.com/integral-dw/org-superstar-mode][org-superstar]] | ||
1165 | |||
1166 | #+begin_src emacs-lisp | ||
1167 | (use-package org-superstar | ||
1168 | :custom | ||
1169 | (org-superstar-headline-bullets-list | ||
1170 | '(?—)) | ||
1171 | (org-superstar-cycle-headline-bullets nil) | ||
1172 | (org-superstar-item-bullet-alist | ||
1173 | '((?* . ?★) | ||
1174 | (?+ . ?‣) | ||
1175 | (?- . ?•))) | ||
1176 | (org-superstar-special-todo-items t) | ||
1177 | (org-superstar-leading-bullet '(" .")) | ||
1178 | :custom-face | ||
1179 | (org-superstar-header-bullet | ||
1180 | ((t (:weight normal)))) | ||
1181 | :hook | ||
1182 | (org-mode . org-superstar-mode)) | ||
1183 | #+end_src | ||
1184 | |||
1185 | *** Enable markdown export | ||
1186 | |||
1187 | #+begin_src emacs-lisp | ||
1188 | (require 'ox-md) | ||
1189 | #+end_src | ||
1190 | |||
1191 | *** Ensure blank lines between headings and before contents | ||
1192 | |||
1193 | from [[https://github.com/alphapapa/unpackaged.el#ensure-blank-lines-between-headings-and-before-contents][unpackaged.el]] | ||
1194 | |||
1195 | #+begin_src emacs-lisp | ||
1196 | ;;;###autoload | ||
1197 | (defun unpackaged/org-fix-blank-lines (&optional prefix) | ||
1198 | "Ensure that blank lines exist between headings and between | ||
1199 | headings and their contents. With prefix, operate on whole | ||
1200 | buffer. Ensures that blank lines exist after each headings's | ||
1201 | drawers." | ||
1202 | (interactive "P") | ||
1203 | (org-map-entries | ||
1204 | (lambda () | ||
1205 | (org-with-wide-buffer | ||
1206 | ;; `org-map-entries' narrows the buffer, which prevents us | ||
1207 | ;; from seeing newlines before the current heading, so we | ||
1208 | ;; do this part widened. | ||
1209 | (while (not (looking-back "\n\n" nil)) | ||
1210 | ;; Insert blank lines before heading. | ||
1211 | (insert "\n"))) | ||
1212 | (let ((end (org-entry-end-position))) | ||
1213 | ;; Insert blank lines before entry content. | ||
1214 | (forward-line) | ||
1215 | (while (and (org-at-planning-p) | ||
1216 | (< (point) (point-max))) | ||
1217 | ;; Skip planning lines | ||
1218 | (forward-line)) | ||
1219 | (while (re-search-forward org-drawer-regexp end t) | ||
1220 | ;; Skip drawers. You might think that | ||
1221 | ;; `org-at-drawer-p' would suffice, but for some reason | ||
1222 | ;; it doesn't work correctly when operating on hidden | ||
1223 | ;; text. This works, taken from | ||
1224 | ;; `org-agenda-get-some-entry-text'. | ||
1225 | (re-search-forward "^[ \t]*:END:.*\n?" end t) | ||
1226 | (goto-char (match-end 0))) | ||
1227 | (unless (or (= (point) (point-max)) | ||
1228 | (org-at-heading-p) | ||
1229 | (looking-at-p "\n")) | ||
1230 | (insert "\n")))) | ||
1231 | t (if prefix | ||
1232 | nil | ||
1233 | 'tree))) | ||
1234 | #+end_src | ||
1235 | |||
1236 | *** ~org-return-dwim~ | ||
1237 | |||
1238 | from [[https://github.com/alphapapa/unpackaged.el#org-return-dwim][unpackaged.el]] | ||
1239 | |||
1240 | #+begin_src emacs-lisp | ||
1241 | (defun unpackaged/org-element-descendant-of (type element) | ||
1242 | "Return non-nil if ELEMENT is a descendant of TYPE. | ||
1243 | TYPE should be an element type, like `item' or `paragraph'. | ||
1244 | ELEMENT should be a list like that returned by | ||
1245 | `org-element-context'." | ||
1246 | ;; MAYBE: Use `org-element-lineage'. | ||
1247 | (when-let* ((parent (org-element-property :parent element))) | ||
1248 | (or (eq type (car parent)) | ||
1249 | (unpackaged/org-element-descendant-of type parent)))) | ||
1250 | |||
1251 | ;;;###autoload | ||
1252 | (defun unpackaged/org-return-dwim (&optional default) | ||
1253 | "A helpful replacement for `org-return'. With prefix, call `org-return'. | ||
1254 | |||
1255 | On headings, move point to position after entry content. In | ||
1256 | lists, insert a new item or end the list, with checkbox if | ||
1257 | appropriate. In tables, insert a new row or end the table." | ||
1258 | ;; Inspired by John Kitchin: http://kitchingroup.cheme.cmu.edu/blog/2017/04/09/A-better-return-in-org-mode/ | ||
1259 | (interactive "P") | ||
1260 | (if default | ||
1261 | (org-return) | ||
1262 | (cond | ||
1263 | ;; Act depending on context around point. | ||
1264 | |||
1265 | ;; NOTE: I prefer RET to not follow links, but by uncommenting this block, | ||
1266 | ;; links will be followed. | ||
1267 | |||
1268 | ;; ((eq 'link (car (org-element-context))) | ||
1269 | ;; ;; Link: Open it. | ||
1270 | ;; (org-open-at-point-global)) | ||
1271 | |||
1272 | ((org-at-heading-p) | ||
1273 | ;; Heading: Move to position after entry content. | ||
1274 | ;; NOTE: This is probably the most interesting feature of this function. | ||
1275 | (let ((heading-start (org-entry-beginning-position))) | ||
1276 | (goto-char (org-entry-end-position)) | ||
1277 | (cond ((and (org-at-heading-p) | ||
1278 | (= heading-start (org-entry-beginning-position))) | ||
1279 | ;; Entry ends on its heading; add newline after | ||
1280 | (end-of-line) | ||
1281 | (insert "\n\n")) | ||
1282 | (t | ||
1283 | ;; Entry ends after its heading; back up | ||
1284 | (forward-line -1) | ||
1285 | (end-of-line) | ||
1286 | (when (org-at-heading-p) | ||
1287 | ;; At the same heading | ||
1288 | (forward-line) | ||
1289 | (insert "\n") | ||
1290 | (forward-line -1)) | ||
1291 | ;; FIXME: looking-back is supposed to be called with more arguments. | ||
1292 | (while (not (looking-back (rx (repeat 3 (seq (optional blank) "\n"))))) | ||
1293 | (insert "\n")) | ||
1294 | (forward-line -1))))) | ||
1295 | |||
1296 | ((org-at-item-checkbox-p) | ||
1297 | ;; Checkbox: Insert new item with checkbox. | ||
1298 | (org-insert-todo-heading nil)) | ||
1299 | |||
1300 | ((org-in-item-p) | ||
1301 | ;; Plain list. Yes, this gets a little complicated... | ||
1302 | (let ((context (org-element-context))) | ||
1303 | (if (or (eq 'plain-list (car context)) ; First item in list | ||
1304 | (and (eq 'item (car context)) | ||
1305 | (not (eq (org-element-property :contents-begin context) | ||
1306 | (org-element-property :contents-end context)))) | ||
1307 | (unpackaged/org-element-descendant-of 'item context)) ; Element in list item, e.g. a link | ||
1308 | ;; Non-empty item: Add new item. | ||
1309 | (org-insert-item) | ||
1310 | ;; Empty item: Close the list. | ||
1311 | ;; TODO: Do this with org functions rather than operating on the text. Can't seem to find the right function. | ||
1312 | (delete-region (line-beginning-position) (line-end-position)) | ||
1313 | (insert "\n")))) | ||
1314 | |||
1315 | ((when (fboundp 'org-inlinetask-in-task-p) | ||
1316 | (org-inlinetask-in-task-p)) | ||
1317 | ;; Inline task: Don't insert a new heading. | ||
1318 | (org-return)) | ||
1319 | |||
1320 | ((org-at-table-p) | ||
1321 | (cond ((save-excursion | ||
1322 | (beginning-of-line) | ||
1323 | ;; See `org-table-next-field'. | ||
1324 | (cl-loop with end = (line-end-position) | ||
1325 | for cell = (org-element-table-cell-parser) | ||
1326 | always (equal (org-element-property :contents-begin cell) | ||
1327 | (org-element-property :contents-end cell)) | ||
1328 | while (re-search-forward "|" end t))) | ||
1329 | ;; Empty row: end the table. | ||
1330 | (delete-region (line-beginning-position) (line-end-position)) | ||
1331 | (org-return)) | ||
1332 | (t | ||
1333 | ;; Non-empty row: call `org-return'. | ||
1334 | (org-return)))) | ||
1335 | (t | ||
1336 | ;; All other cases: call `org-return'. | ||
1337 | (org-return))))) | ||
1338 | |||
1339 | (bind-key "RET" #'unpackaged/org-return-dwim 'org-mode-map) | ||
1340 | #+end_src | ||
1341 | |||
1342 | ** “Typographical editing” with [[https://github.com/jorgenschaefer/typoel][Typo]] | ||
1343 | |||
1344 | When I'm writing, I've been annoyed at the straight quotes. | ||
1345 | 626 | ||
1346 | #+begin_src emacs-lisp | 627 | #+begin_src emacs-lisp |
1347 | (use-package typo | 628 | (use-package typo |
1348 | :hook | ||
1349 | (text-mode . typo-mode)) | ||
1350 | #+end_src | ||
1351 | |||
1352 | * Coding | ||
1353 | |||
1354 | The Other Thing Emacs is Good For. | ||
1355 | |||
1356 | ** Formatting | ||
1357 | |||
1358 | *** Indenting: [[https://github.com/Malabarba/aggressive-indent-mode][aggressive-indent-mode]] | ||
1359 | |||
1360 | This automagically indents code on every change, as opposed to =electric-indent-mode=, which only does when I like, hit =RET= or whatever. As such, I can turn =electric-indent-mode= off. | ||
1361 | |||
1362 | #+begin_src emacs-lisp | ||
1363 | (use-package aggressive-indent | ||
1364 | :init | ||
1365 | (electric-indent-mode -1) | ||
1366 | :config | ||
1367 | (global-aggressive-indent-mode 1)) | ||
1368 | #+end_src | ||
1369 | |||
1370 | *** [[https://github.com/jcsalomon/smarttabs][Smart tabs]] | ||
1371 | |||
1372 | I really want to like, use tabs all the time. But I thought the =smart-tabs= package author made some good points about using tabs for semantic indentation, and spaces for the rest. So. | ||
1373 | |||
1374 | #+begin_src emacs-lisp | ||
1375 | (use-package smart-tabs-mode | ||
1376 | :custom | ||
1377 | (whitespace-style | ||
1378 | '(face trailing tabs spaces lines newline | ||
1379 | empty indentation space-before-tab | ||
1380 | space-mark tab-mark newline-mark)) | ||
1381 | :config | ||
1382 | (smart-tabs-insinuate 'c 'c++ 'javascript 'java 'ruby)) | ||
1383 | #+end_src | ||
1384 | |||
1385 | ** Display | ||
1386 | |||
1387 | *** Prettify symbols mode | ||
1388 | |||
1389 | By default, I think =prettify-symbols-mode= only changes =lambda= to =λ=. I should, at some point, add some prettified symbols. | ||
1390 | |||
1391 | #+begin_src emacs-lisp | ||
1392 | (add-hook 'prog-mode-hook #'prettify-symbols-mode) | ||
1393 | #+end_src | ||
1394 | |||
1395 | *** Parentheses and frens | ||
1396 | |||
1397 | **** =show-paren-style= | ||
1398 | |||
1399 | A =mixed= =show-paren-style= means that, when both parentheses are visible, it just highlights them. If one is /not/, though, it highlights the entire block. | ||
1400 | |||
1401 | #+begin_src emacs-lisp | ||
1402 | (cuss show-paren-style 'mixed) | ||
1403 | (cuss show-paren-delay 0) | ||
1404 | |||
1405 | (show-paren-mode 1) | ||
1406 | #+end_src | ||
1407 | |||
1408 | **** [[https://github.com/Fuco1/smartparens][smartparens]] | ||
1409 | |||
1410 | Automagically close pairs and stuff. See also [[https://www.emacswiki.org/emacs/ParEdit][ParEdit]] -- maybe test that one? | ||
1411 | |||
1412 | #+begin_src emacs-lisp | ||
1413 | (use-package smartparens | ||
1414 | :init | ||
1415 | (defun acdw/setup-smartparens () | ||
1416 | (require 'smartparens-config) | ||
1417 | (smartparens-mode 1)) | ||
1418 | :hook | 629 | :hook |
1419 | (prog-mode . acdw/setup-smartparens)) | 630 | (text-mode . typo-mode)) |
1420 | #+end_src | ||
1421 | |||
1422 | **** [[https://github.com/Fanael/rainbow-delimiters][rainbow-delimiters]] | ||
1423 | |||
1424 | Show different pairs of delimiters in diffferent colors. Pretty! Useful! | ||
1425 | |||
1426 | #+begin_src emacs-lisp | ||
1427 | (use-package rainbow-delimiters | ||
1428 | :hook (prog-mode . rainbow-delimiters-mode)) | ||
1429 | #+end_src | ||
1430 | |||
1431 | *** [[https://elpa.gnu.org/packages/rainbow-mode.html][rainbow-mode]] | ||
1432 | |||
1433 | Show different colors /in that color/. Useful! Pretty! | ||
1434 | |||
1435 | #+begin_src emacs-lisp | ||
1436 | (use-package rainbow-mode | ||
1437 | :custom | ||
1438 | (rainbow-x-colors nil) | ||
1439 | :hook prog-mode) | ||
1440 | #+end_src | ||
1441 | |||
1442 | *** Line numbers | ||
1443 | |||
1444 | I only want line numbers in =prog-mode=-derived modes. In addition, apparently =linum-mode= works better in TUI, but is slower that =display-line-numbers=. So I want to do some logic to see what to use. | ||
1445 | |||
1446 | #+begin_src emacs-lisp | ||
1447 | (defun acdw/enable-line-numbers () | ||
1448 | "Enable line numbers, either through `display-line-numbers-mode' | ||
1449 | or through `linum-mode'." | ||
1450 | (if (and (fboundp 'display-line-numbers-mode) | ||
1451 | (display-graphic-p)) | ||
1452 | (progn | ||
1453 | (display-line-numbers-mode 1) | ||
1454 | (cuss display-line-numbers-width 2)) | ||
1455 | (linum-mode 1))) | ||
1456 | |||
1457 | (add-hook 'prog-mode-hook #'acdw/enable-line-numbers) | ||
1458 | #+end_src | ||
1459 | |||
1460 | *** [[https://github.com/tarsius/hl-todo][hl-todo]] | ||
1461 | |||
1462 | #+begin_src emacs-lisp | ||
1463 | (use-package hl-todo | ||
1464 | :hook (prog-mode . hl-todo-mode)) | ||
1465 | #+end_src | ||
1466 | |||
1467 | ** Programming languages | ||
1468 | |||
1469 | These are the programming languages I (don't really) use. | ||
1470 | |||
1471 | *** Fish shell | ||
1472 | |||
1473 | #+begin_src emacs-lisp | ||
1474 | (use-package fish-mode) | ||
1475 | #+end_src | ||
1476 | |||
1477 | *** Lisps | ||
1478 | |||
1479 | **** Common Lisp (SLIME) | ||
1480 | |||
1481 | #+begin_src emacs-lisp | ||
1482 | (use-package slime | ||
1483 | :when (executable-find "sbcl") | ||
1484 | :custom | ||
1485 | (inferior-lisp-program "sbcl") | ||
1486 | (slime-contribs '(slime-fancy))) | ||
1487 | #+end_src | ||
1488 | |||
1489 | **** Fennel | ||
1490 | |||
1491 | #+begin_src emacs-lisp | ||
1492 | (use-package fennel-mode | ||
1493 | :mode "\\.fnl\\'") | ||
1494 | #+end_src | ||
1495 | |||
1496 | *** Lua | ||
1497 | |||
1498 | #+begin_src emacs-lisp | ||
1499 | (use-package lua-mode | ||
1500 | :mode "\\.lua\\'" | ||
1501 | :interpreter "lua") | ||
1502 | #+end_src | ||
1503 | |||
1504 | *** Web (HTML/CSS/JS) | ||
1505 | |||
1506 | #+begin_src emacs-lisp | ||
1507 | (use-package web-mode | ||
1508 | :mode (("\\.ts\\'" . web-mode) | ||
1509 | ("\\.html?\\'" . web-mode) | ||
1510 | ("\\.css?\\'" . web-mode) | ||
1511 | ("\\.js\\'" . web-mode))) | ||
1512 | #+end_src | ||
1513 | |||
1514 | *** =~/.ssh/config= | ||
1515 | |||
1516 | #+begin_src emacs-lisp | ||
1517 | (use-package ssh-config-mode) | ||
1518 | #+end_src | ||
1519 | |||
1520 | *** Go | ||
1521 | |||
1522 | #+begin_src emacs-lisp | ||
1523 | (use-package go-mode | ||
1524 | :mode "\\.go\\'" | ||
1525 | :hook | ||
1526 | (before-save . gofmt-before-save)) | ||
1527 | #+end_src | 631 | #+end_src |
1528 | 632 | ||
1529 | * Applications | 633 | * Applications |
1530 | 634 | ||
1531 | Of course, the real reason we love emacs is for the application layer. What is it they say? | 635 | ** Magit |
1532 | |||
1533 | #+begin_quote | ||
1534 | Emacs is a great operating system, lacking only a decent editor. | ||
1535 | #+end_quote | ||
1536 | |||
1537 | Yeah, that's it 😎 | ||
1538 | |||
1539 | ** Git: [[https://magit.vc/][magit]] | ||
1540 | |||
1541 | The magical porcelain. | ||
1542 | 636 | ||
1543 | #+begin_src emacs-lisp | 637 | #+begin_src emacs-lisp |
1544 | (use-package magit | 638 | (use-package magit |
1545 | :bind | 639 | :bind |
1546 | ("C-x g" . magit-status) | 640 | ("C-x g" . magit-status)) |
1547 | :custom-update | 641 | #+end_src |
1548 | (magit-no-confirm '(stage-all-changes)) | 642 | |
1549 | :config | 643 | * Appendices |
1550 | (add-hook 'magit-process-find-password-functions | 644 | ** Emacs' files |
1551 | #'magit-process-password-auth-source)) | 645 | *** init.el |
1552 | #+end_src | 646 | :PROPERTIES: |
1553 | 647 | :header-args: :tangle init.el | |
1554 | *** Hook into =prescient= | 648 | :END: |
1555 | 649 | ||
1556 | #+begin_src emacs-lisp | 650 | #+begin_src emacs-lisp :comments no |
1557 | (define-advice magit-list-refs | 651 | ;; init.el -*- lexical-binding: t -*- |
1558 | (:around (orig &optional namespaces format sortby) | 652 | #+end_src |
1559 | prescient-sort) | 653 | **** Load config |
1560 | "Apply prescient sorting when listing refs." | 654 | |
1561 | (let ((res (funcall orig namespaces format sortby))) | 655 | from [[https://protesilaos.com/dotemacs/#h:584c3604-55a1-49d0-9c31-abe46cb1f028][Protesilaos Stavrou]]. |
1562 | (if (or sortby | 656 | |
1563 | magit-list-refs-sortby | 657 | #+begin_src emacs-lisp |
1564 | (not selectrum-should-sort-p)) | 658 | (let* ((conf (expand-file-name "config" |
1565 | res | 659 | user-emacs-directory)) |
1566 | (prescient-sort res)))) | 660 | (elc (concat conf ".elc")) |
1567 | #+end_src | 661 | (el (concat conf ".el")) |
1568 | 662 | (org (concat conf ".org"))) | |
1569 | *** Use =libgit= when I can build it (requires =cmake=) | 663 | (cond ((file-exists-p elc) (load-file elc)) |
1570 | 664 | ((file-exists-p el) (load-file el)) | |
1571 | #+begin_src emacs-lisp | 665 | (t (require 'org) |
1572 | (when (executable-find "cmake") | 666 | (org-babel-load-file org)))) |
1573 | (use-package libgit) | 667 | #+end_src |
1574 | (use-package magit-libgit)) | 668 | |
1575 | #+end_src | 669 | *** early-init.el |
1576 | 670 | :PROPERTIES: | |
1577 | *** Git "forge" capabilities | 671 | :header-args: :tangle early-init.el |
1578 | 672 | :END: | |
1579 | #+begin_src emacs-lisp | 673 | |
1580 | (use-package forge | 674 | #+begin_src emacs-lisp :comments no |
1581 | :after magit | 675 | ;; early-init.el -*- lexical-binding: t; no-byte-compile: t; -*- |
1582 | :unless (eq system-type 'windows-nt) | 676 | #+end_src |
1583 | :custom | 677 | |
1584 | (forge-owned-accounts | 678 | #+begin_src emacs-lisp |
1585 | '(("duckwork")))) | 679 | (setq load-prefer-newer t) |
1586 | #+end_src | 680 | (setq frame-inhibit-implied-resize t) |
1587 | 681 | #+end_src | |
1588 | ** Dired | 682 | |
1589 | 683 | ** Ease tangling and loading of Emacs' init | |
1590 | I'm still figuring out what all I can do with =dired=. | ||
1591 | |||
1592 | #+begin_src emacs-lisp | ||
1593 | (with-eval-after-load 'dired | ||
1594 | (cuss dired-dwim-target t) | ||
1595 | (cuss dired-listing-switches "-alDh") | ||
1596 | |||
1597 | (cuss wdired-allow-to-change-permissions t) | ||
1598 | (bind-key "C-c w" #'wdired-change-to-wdired-mode 'dired-mode-map)) | ||
1599 | #+end_src | ||
1600 | |||
1601 | *** dired-subtree | ||
1602 | |||
1603 | Part of the [[https://github.com/Fuco1/dired-hacks][dired-hacks]] package. | ||
1604 | |||
1605 | #+begin_src emacs-lisp | ||
1606 | (use-package dired-subtree | ||
1607 | :bind (:map dired-mode-map | ||
1608 | (("i" . dired-subtree-insert) | ||
1609 | (";" . dired-subtree-remove)))) | ||
1610 | #+end_src | ||
1611 | |||
1612 | ** Proced | ||
1613 | |||
1614 | The process editor. | ||
1615 | |||
1616 | #+begin_src emacs-lisp | ||
1617 | (defun acdw/setup-proced () | ||
1618 | (variable-pitch-mode -1) | ||
1619 | (toggle-truncate-lines 1) | ||
1620 | (proced-toggle-auto-update 1)) | ||
1621 | 684 | ||
1622 | (add-hook 'proced-mode-hook #'acdw/setup-proced) | 685 | #+begin_src emacs-lisp |
1623 | #+end_src | 686 | (defun acdw/tangle-and-load-init () |
1624 | 687 | (interactive) | |
1625 | ** Gemini (and gopher) | 688 | "If the current buffer is `config.org', tangle it, then byte-compile." |
1626 | 689 | (let ((config (expand-file-name "config.org" user-emacs-directory))) | |
1627 | *** [[https://thelambdalab.xyz/elpher/][elpher]] | 690 | (when (string= (buffer-file-name) config) |
1628 | 691 | (let ((prog-mode-hook nil)) | |
1629 | Actually, =elpher= is the reason I started using Emacs. So thanks, smol web denizens! | 692 | (require 'org) |
1630 | 693 | (org-babel-tangle-file config) | |
1631 | Fun fact: these packages are /also/ why I use =straight.el=, since they're none of them on GitHub. | 694 | (org-md-export-to-markdown) |
1632 | 695 | ||
1633 | #+BEGIN_SRC emacs-lisp | 696 | (dolist (file `(,(expand-file-name "init.el" |
1634 | (use-package elpher | 697 | user-emacs-directory) |
1635 | :straight (elpher | 698 | ,(expand-file-name "config.el" |
1636 | :repo "git://thelambdalab.xyz/elpher.git") | 699 | user-emacs-directory))) |
1637 | :custom | 700 | (byte-compile-file file t)))))) |
1638 | (elpher-certificate-directory | 701 | |
1639 | (no-littering-expand-var-file-name "elpher-certificates/")) | 702 | (add-hook 'after-save-hook #'acdw/tangle-and-load-init) |
1640 | (elpher-ipv4-always t) | 703 | #+end_src |
1641 | :custom-face | 704 | ** License |
1642 | (elpher-gemini-heading1 | 705 | :PROPERTIES: |
1643 | ((t (:inherit (modus-theme-heading-1))))) | 706 | :header-args: :tangle LICENSE :comments no |
1644 | (elpher-gemini-heading2 | 707 | :END: |
1645 | ((t (:inherit (modus-theme-heading-2))))) | ||
1646 | (elpher-gemini-heading3 | ||
1647 | ((t (:inherit (modus-theme-heading-3))))) | ||
1648 | :config | ||
1649 | (defun elpher:eww-browse-url (original url &optional new-window) | ||
1650 | "Handle gemini/gopher links with eww." | ||
1651 | (cond ((string-match-p "\\`\\(gemini\\|gopher\\)://" url) | ||
1652 | (require 'elpher) | ||
1653 | (elpher-go url)) | ||
1654 | (t (funcall original url new-window)))) | ||
1655 | (advice-add 'eww-browse-url :around 'elpher:eww-browse-url) | ||
1656 | :bind (:map elpher-mode-map | ||
1657 | ("n" . elpher-next-link) | ||
1658 | ("p" . elpher-prev-link) | ||
1659 | ("o" . elpher-follow-current-link) | ||
1660 | ("G" . elpher-go-current)) | ||
1661 | :hook | ||
1662 | (elpher-mode . visual-fill-column-mode)) | ||
1663 | #+end_src | ||
1664 | |||
1665 | *** [[https://git.carcosa.net/jmcbray/gemini.el][gemini-mode]] | ||
1666 | |||
1667 | A major mode for =text/gemini= files. I've changed the headings to match Elpher's. | ||
1668 | |||
1669 | #+BEGIN_SRC emacs-lisp | ||
1670 | (use-package gemini-mode | ||
1671 | :straight (gemini-mode | ||
1672 | :repo "https://git.carcosa.net/jmcbray/gemini.el.git") | ||
1673 | :mode "\\.\\(gemini|gmi\\)\\'" | ||
1674 | :custom-face | ||
1675 | (gemini-heading-face-1 | ||
1676 | ((t (:inherit (elpher-gemini-heading1))))) | ||
1677 | (gemini-heading-face2 | ||
1678 | ((t (:inherit (elpher-gemini-heading2))))) | ||
1679 | (gemini-heading-face3 | ||
1680 | ((t (:inherit (elpher-gemini-heading3))))) | ||
1681 | :init | ||
1682 | (defun acdw/setup-gemini-mode () | ||
1683 | (visual-fill-column-mode 1) | ||
1684 | (variable-pitch-mode -1)) | ||
1685 | :hook | ||
1686 | (gemini-mode . acdw/setup-gemini-mode)) | ||
1687 | #+end_src | ||
1688 | |||
1689 | *** [[https://alexschroeder.ch/cgit/gemini-write/about/][gemini-write]] | ||
1690 | |||
1691 | Alex Schroeder's Emacs implementation of the Titan protocol. This is why I use his Gemini server, [[https://alexschroeder.ch/cgit/phoebe/][Phoebe]]! | ||
1692 | |||
1693 | #+BEGIN_SRC emacs-lisp | ||
1694 | (use-package gemini-write | ||
1695 | :straight (gemini-write | ||
1696 | :repo "https://alexschroeder.ch/cgit/gemini-write") | ||
1697 | :config | ||
1698 | (when (boundp 'acdw-secrets/elpher-gemini-tokens) | ||
1699 | (dolist (token acdw-secrets/elpher-gemini-tokens) | ||
1700 | (add-to-list 'elpher-gemini-tokens token)))) | ||
1701 | #+end_src | ||
1702 | |||
1703 | *** [[https://git.sr.ht/~acdw/post-to-gemlog-blue.el][post-to-gemlog-blue]] | ||
1704 | |||
1705 | My first (!) Emacs package, to allow posting to [[https://gemlog.blue][gemlog.blue's web interface]]. I don't use gemlog.blue any more, but if I didn't have this package, no one would 😎 | ||
1706 | |||
1707 | #+BEGIN_SRC emacs-lisp | ||
1708 | (use-package post-to-gemlog-blue | ||
1709 | :straight (post-to-gemlog-blue | ||
1710 | :repo "https://git.sr.ht/~acdw/post-to-gemlog-blue.el")) | ||
1711 | #+END_SRC | ||
1712 | |||
1713 | ** Pastebin: [[https://git.sr.ht/~zge/nullpointer-emacs][0x0]] | ||
1714 | |||
1715 | Pastebins are so useful. Now I can use them from Emacs. | ||
1716 | |||
1717 | #+BEGIN_SRC emacs-lisp | ||
1718 | (use-package 0x0 | ||
1719 | :custom | ||
1720 | (0x0-default-service 'ttm)) | ||
1721 | #+END_SRC | ||
1722 | |||
1723 | ** [[https://www.djcbsoftware.nl/code/mu/mu4e.html][mu4e]] | ||
1724 | |||
1725 | I've just recently started (again) using mu4e. We'll see how it goes. | ||
1726 | |||
1727 | #+begin_src emacs-lisp | ||
1728 | (when (executable-find "mu") | ||
1729 | (add-to-list 'load-path | ||
1730 | "/usr/share/emacs/site-lisp/mu4e") | ||
1731 | (require 'mu4e) | ||
1732 | |||
1733 | (cuss mail-user-agent 'mu4e-user-agent) | ||
1734 | |||
1735 | (cuss mu4e-headers-skip-duplicates t) | ||
1736 | (cuss mu4e-view-show-images t) | ||
1737 | (cuss mu4e-view-show-addresses t) | ||
1738 | (cuss mu4e-compose-format-flowed t) | ||
1739 | (cuss mu4e-change-filenames-when-moving t) | ||
1740 | (cuss mu4e-attachments-dir "~/Downloads") | ||
1741 | |||
1742 | (cuss mu4e-maildir "~/.mail/fastmail") | ||
1743 | (cuss mu4e-refile-folder "/Archive") | ||
1744 | (cuss mu4e-sent-folder "/Sent") | ||
1745 | (cuss mu4e-drafts-folder "/Drafts") | ||
1746 | (cuss mu4e-trash-folder "/Trash") | ||
1747 | |||
1748 | (fset 'my-move-to-trash "mTrash") | ||
1749 | (define-key mu4e-headers-mode-map (kbd "d") 'my-move-to-trash) | ||
1750 | (define-key mu4e-view-mode-map (kbd "d") 'my-move-to-trash) | ||
1751 | |||
1752 | (cuss message-send-mail-function 'smtpmail-send-it) | ||
1753 | (cuss smtpmail-default-smtp-server "smtp.fastmail.com") | ||
1754 | (cuss smtpmail-smtp-server "smtp.fastmail.com") | ||
1755 | (cuss smtpmail-stream-type 'ssl) | ||
1756 | (cuss smtpmail-smtp-service 465) | ||
1757 | (cuss smtpmail-local-domain "acdw.net") | ||
1758 | (cuss mu4e-compose-signature | ||
1759 | "Best,\nCase\n") | ||
1760 | |||
1761 | (cuss mu4e-get-mail-command "mbsync -a") | ||
1762 | (cuss mu4e-update-interval 300) | ||
1763 | |||
1764 | (cuss mu4e-completing-read-function 'completing-read) | ||
1765 | (cuss message-kill-buffer-on-exit t) | ||
1766 | (cuss mu4e-confirm-quit nil) | ||
1767 | |||
1768 | (cuss mu4e-bookmarks | ||
1769 | '(( | ||
1770 | :name "Unread" | ||
1771 | :query | ||
1772 | "flag:unread AND NOT flag:trashed AND NOT maildir:/Spam" | ||
1773 | :key ?u) | ||
1774 | ( | ||
1775 | :name "Today" | ||
1776 | :query "date:today..now and not maildir:/Spam" | ||
1777 | :key ?t) | ||
1778 | ( | ||
1779 | :name "This week" | ||
1780 | :query "date:7d..now and not maildir:/Spam" | ||
1781 | :hide-unread t | ||
1782 | :key ?w))) | ||
1783 | |||
1784 | (cuss mu4e-headers-fields | ||
1785 | '((:human-date . 12) | ||
1786 | (:flags . 6) | ||
1787 | (:mailing-list . 10) | ||
1788 | (:from-or-to . 22) | ||
1789 | (:subject))) | ||
1790 | |||
1791 | (defun acdw/setup-mu4e-view-mode () | ||
1792 | (visual-fill-column-mode)) | ||
1793 | |||
1794 | (add-hook 'mu4e-view-mode-hook #'acdw/setup-mu4e-view-mode)) | ||
1795 | |||
1796 | ;; not sure about this... | ||
1797 | (use-package mu4e-dashboard | ||
1798 | :straight (mu4e-dashboard | ||
1799 | :host github | ||
1800 | :repo "rougier/mu4e-dashboard" | ||
1801 | :branch "main")) | ||
1802 | |||
1803 | #+end_src | ||
1804 | |||
1805 | ** KeePassXC integration | ||
1806 | |||
1807 | I use KeePassXC, and I'd /like/ to use KeePassXC to get passwords and stuff at home. So I'm trying this library out, since the secret-tool integration isn't built into the KeePassXC on Void. If this doesn't work, looks like I'll have to become a packager 😜 | ||
1808 | |||
1809 | #+begin_src emacs-lisp :noweb yes :tangle no | ||
1810 | (when (eq system-type 'gnu/linux) | ||
1811 | <<use-package-sodium>> | ||
1812 | (use-package keepassxc | ||
1813 | :straight (keepassxc | ||
1814 | :host github | ||
1815 | :repo "dakra/keepassxc.el") | ||
1816 | :after (sodium))) | ||
1817 | #+end_src | ||
1818 | |||
1819 | *** libsodium integration | ||
1820 | |||
1821 | I had to manually run ~make~ in the repo this time around, for some reason. Should probably look into that ... when it's not 1:00 AM. | ||
1822 | |||
1823 | #+NAME: use-package-sodium | ||
1824 | #+begin_src emacs-lisp :tangle no | ||
1825 | (use-package sodium | ||
1826 | :straight (sodium | ||
1827 | :host github | ||
1828 | :repo "dakra/sodium.el" | ||
1829 | :build ("make")) | ||
1830 | :init | ||
1831 | (add-to-list 'load-path | ||
1832 | (expand-file-name "straight/repos/sodium.el" | ||
1833 | user-emacs-directory))) | ||
1834 | #+end_src | ||
1835 | |||
1836 | ** [[https://github.com/skeeto/elfeed][elfeed]] | ||
1837 | |||
1838 | Let's use Emacs as a feed reader! A number of these tweaks are from [[https://karthinks.com/software/lazy-elfeed/]["Lazy Elfeed"]]. | ||
1839 | |||
1840 | #+begin_src emacs-lisp | ||
1841 | (use-package elfeed | ||
1842 | :when (executable-find "curl") | ||
1843 | :hook | ||
1844 | (elfeed-show-mode . visual-fill-column-mode) | ||
1845 | (kill-emacs . elfeed-db-compact)) | ||
1846 | #+end_src | ||
1847 | |||
1848 | *** [[elfeed-org][elfeed-org]] | ||
1849 | |||
1850 | This way, I can configure my feeds in an [[file:elfeed.org][org file]]! | ||
1851 | |||
1852 | #+begin_src emacs-lisp | ||
1853 | (use-package elfeed-org | ||
1854 | :custom | ||
1855 | (rmh-elfeed-org-files | ||
1856 | (list (expand-file-name "elfeed.org" | ||
1857 | user-emacs-directory))) | ||
1858 | :init | ||
1859 | (elfeed-org)) | ||
1860 | #+end_src | ||
1861 | |||
1862 | ** Emacs Web WOWser | ||
1863 | |||
1864 | I can /not/ get over how hilarious this name is 😜 | ||
1865 | |||
1866 | #+begin_src emacs-lisp | ||
1867 | (use-package eww | ||
1868 | :custom | ||
1869 | (shr-animate-image nil) | ||
1870 | :bind (:map eww-mode-map | ||
1871 | ("b" . #'eww-back-url) | ||
1872 | ("f" . #'eww-forward-url)) | ||
1873 | :config | ||
1874 | (require 'url-cookie) | ||
1875 | (cuss url-cookie-confirmation t)) | ||
1876 | #+end_src | ||
1877 | |||
1878 | ** TODO eshell | ||
1879 | |||
1880 | look at [[https://github.com/dieggsy/esh-autosuggest/][esh-autosuggest]] and [[https://www.masteringemacs.org/article/complete-guide-mastering-eshell][mastering eshell]] and stuff. idk. | ||
1881 | oh and [[https://github.com/kyagi/shell-pop-el][shell pop]] too. | ||
1882 | |||
1883 | ** [[https://github.com/politza/pdf-tools][PDF Tools]] | ||
1884 | |||
1885 | There's a few things that have to be installed to use PDF Tools, but I've got them installed on =larry=. Once I write them down, I'll expand this out to both my linux machines. | ||
1886 | |||
1887 | #+begin_src emacs-lisp | ||
1888 | (when (string= (system-name) "larry") | ||
1889 | (use-package pdf-tools | ||
1890 | :init | ||
1891 | (pdf-loader-install))) | ||
1892 | #+end_src | ||
1893 | |||
1894 | * Appendix A: Scripts | ||
1895 | |||
1896 | ** ~emacsdc~ | ||
1897 | |||
1898 | Here's a wrapper script that'll start =emacs --daemon= if there isn't one, and then launche =emacsclient= on the arguments. I'd recommend installing with ~ln -s emacsdc ~/.local/bin/~ or something. Then you can set it as your ~$EDITOR~! | ||
1899 | |||
1900 | #+begin_src sh :shebang "#!/bin/sh" :tangle bin/emacsdc | ||
1901 | if ! emacsclient -nc "$@" 2>/dev/null; then | ||
1902 | emacs --daemon | ||
1903 | emacsclient -nc "$@" | ||
1904 | fi | ||
1905 | #+end_src | ||
1906 | |||
1907 | * Appendix B: areas for further research | ||
1908 | |||
1909 | - [[https://github.com/caisah/emacs.dz][big list of popular emacs configs]] | ||
1910 | |||
1911 | ** TODO [[https://github.com/flexibeast/ebuku][ebuku]] (of course, I'd need [[https://github.com/jarun/buku][buku]] as well) -- bookmarks | ||
1912 | ** TODO [[https://github.com/rolandwalker/ignoramus][Ignoramus]] -- this might not e necessary | ||
1913 | ** TODO [[https://git.sr.ht/~iank/visible-mark][visible mark]] -- show where the marks are ... | ||
1914 | ** TODO consider this Reddit thread: [[https://www.reddit.com/r/emacs/comments/k3xfa1/speeding_up_magit/][speeding up magit]] | ||
1915 | ** TODO [[https://github.com/legalnonsense/org-visual-outline][org-visual-outline]] -- interesting org organization tool | ||
1916 | ** TODO export org to ODT on Windows | ||
1917 | |||
1918 | Windows doesn't have =zip= natively (though it /does/ have =curl= -- go figure!), so the ODT export for Org-mode won't work. There /is/ a [[https://stackoverflow.com/questions/8625306/org-mode-zip-needed-how-to-over-come][StackOverflow discussion]] that mentions =p7zip= and a possible batch file, but I couldn't get that to work with a little testing. It might need more work. | ||
1919 | 708 | ||
1920 | Something that /did/ work was downloading =zip.exe= from [[http://infozip.sourceforge.net/][Info-ZIP]], and placing it somewhere in =exec-path= -- though of course, /then/ you need =unzip.exe=, apparently in the same folder. Git Portable ships with =unzip.exe=, for example, but even though it's in =exec-path=, org-export threw an error unless =zip= and =unzip= were in the /same/ folder. | 709 | Copyright 2020 Case Duckworth <acdw@acdw.net> |
1921 | 710 | ||
1922 | So I might either (a) have to set up computers in this way when I use new ones, or (b) include both =zip.exe= and =unzip.exe= in /this/ Git repo, or ... something else. A true quandry. | 711 | This work is free. You can redistribute it and/or modify it under the |
712 | terms of the Do What the Fuck You Want To Public License, Version 2, | ||
713 | as published by Sam Hocevar. See the =LICENSE= file, tangled from the | ||
714 | following source block, for details. | ||
1923 | 715 | ||
1924 | ** TODO Fix slowdown on =C-x 8 RET= | 716 | #+begin_src text |
717 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | ||
1925 | 718 | ||
1926 | I think it has something to do with the package =consult=, because it was /slow/, but not *this slow* before installing it. Since it's slow anyway, I'm thinking I should figure out how to turn off =selectrum= when selecting a Unicode character anyway. Maybe advice? | 719 | Version 2, December 2004 |
1927 | 720 | ||
1928 | ** TODO Figure out Org-mode TODOs | 721 | Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> |
1929 | 722 | ||
1930 | I need some standard once, and also some specific ones for this section. | 723 | Everyone is permitted to copy and distribute verbatim or modified copies of |
724 | this license document, and changing it is allowed as long as the name is changed. | ||
1931 | 725 | ||
1932 | ** TODO Add =C-z= as a me-mode map | 726 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE |
1933 | 727 | ||
1934 | I saw someone do this, and honestly it's a great idea. I could use it for my applications. Just have to find it ...... | 728 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION |
1935 | 729 | ||
1936 | ** TODO Really think about [[https://github.com/abo-abo/avy][avy]] again | 730 | 0. You just DO WHAT THE FUCK YOU WANT TO. |
731 | #+end_src | ||
1937 | 732 | ||
1938 | It could actually be pretty useful, and it could be hooked into things like [[https://github.com/abo-abo/ace-link][ace-link]]. | 733 | *** Note on the license |
1939 | 734 | ||
1940 | ** TODO Test out [[https://github.com/magnars/multiple-cursors.el][multiple cursors]] | 735 | It's highly likely that the WTFPL is completely incompatible with the |
736 | GPL, for what should be fairly obvious reasons. To that, I say: | ||
1941 | 737 | ||
1942 | Allegedly it's really cool. | 738 | *SUE ME, RMS!* |