summary refs log tree commit diff stats
path: root/config.org
diff options
context:
space:
mode:
Diffstat (limited to 'config.org')
-rw-r--r--config.org1998
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
10This 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
22Copyright © 2020 Case Duckworth <acdw@acdw.net> 30** Package management
23 31
24This 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
45It'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
53This 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
56git update-index --assume-unchanged init.el
57#+end_src
58 79
59If, 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=
62git update-index --no-assume-unchanged init.el
63#+end_src
64 88
65Otherwise, 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
78Honestly, 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
82After 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
105Since 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
111Finally, 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
127I 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
139While 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
150Starting 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
152Of 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
161Let'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
175When 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
185Elsewhere, 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
198So 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
200The 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
221For 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
231Like 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
240I'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)
251The =: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
259instead 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
267It'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
278I 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
289I 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
291Except. 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
303Protesilaos 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
305He'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
343Due 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
353I 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
369Of 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
382The [[*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
393from [[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
404I'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
436This 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
454Show 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
464from [[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
473I 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
483I 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
493These 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
510This 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
512See [[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
556This 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
566One 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
574This 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
584I 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
595I 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
635By 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
656These 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
677I 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
687The 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
704This 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
718Maybe 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
736By 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
744Instead 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
752This 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
761At 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
769Like 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
777Most 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
790Similarly to =ibuffer=, this is a Better default™.
791
792Of 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
803A 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
811I 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
820I 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
832Honestly, 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
836The 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
849To 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
863Since 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
865But 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
882Now 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
929The 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
931Note 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
950I 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
952I'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
975I'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
986Another 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
999Highlights 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 ()
1009I 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
1011Basically, 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
1022I'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
1031I 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
1033Also 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
1050Revert 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
1059This'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
1067Configurations 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
1080Center 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
1098In =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
1128Pretty self-explanatory, I think...
1129
1130I 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
1152from [[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
1193from [[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
1238from [[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
1344When 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
1354The Other Thing Emacs is Good For.
1355
1356** Formatting
1357
1358*** Indenting: [[https://github.com/Malabarba/aggressive-indent-mode][aggressive-indent-mode]]
1359
1360This 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
1372I 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
1389By 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
1399A =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
1410Automagically 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
1424Show 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
1433Show 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
1444I 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
1469These 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
1531Of course, the real reason we love emacs is for the application layer. What is it they say? 635** Magit
1532
1533#+begin_quote
1534Emacs is a great operating system, lacking only a decent editor.
1535#+end_quote
1536
1537Yeah, that's it 😎
1538
1539** Git: [[https://magit.vc/][magit]]
1540
1541The 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
1590I'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
1603Part 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
1614The 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))
1629Actually, =elpher= is the reason I started using Emacs. So thanks, smol web denizens! 692 (require 'org)
1630 693 (org-babel-tangle-file config)
1631Fun 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
1667A 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
1691Alex 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
1705My 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
1715Pastebins 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
1725I'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
1807I 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
1821I 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
1838Let'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
1850This 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
1864I 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
1880look at [[https://github.com/dieggsy/esh-autosuggest/][esh-autosuggest]] and [[https://www.masteringemacs.org/article/complete-guide-mastering-eshell][mastering eshell]] and stuff. idk.
1881oh and [[https://github.com/kyagi/shell-pop-el][shell pop]] too.
1882
1883** [[https://github.com/politza/pdf-tools][PDF Tools]]
1884
1885There'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
1898Here'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
1918Windows 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
1920Something 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
1922So 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
1926I 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
1930I 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
1934I 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
1938It 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
1942Allegedly it's really cool. 738 *SUE ME, RMS!*