diff options
Diffstat (limited to 'config.org')
-rw-r--r-- | config.org | 1998 |
1 files changed, 397 insertions, 1601 deletions
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!* |