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.org2698
1 files changed, 390 insertions, 2308 deletions
diff --git a/config.org b/config.org index cf33168..2a9c97b 100644 --- a/config.org +++ b/config.org
@@ -1,2443 +1,525 @@
1#+TITLE: Emacs, emacs, emacs 1#+TITLE: Emacs configuration, literate-style
2#+AUTHOR: Case Duckworth 2#+AUTHOR: Case Duckworth
3#+PROPERTY: header-args :tangle config.el :tangle-mode (identity #o444) :comments both :mkdirp yes 3#+PROPERTY: header-args :tangle yes :tangle-mode (identity #o444) :comments both :mkdirp yes
4#+STARTUP: overview
5#+EXPORT_FILE_NAME: README.md
6#+OPTIONS: toc:nil
7#+BANKRUPTCY_COUNT: 3.2
8#+Time-stamp: <2021-01-18 23:33:24 acdw>
9 4
10* Basics 5* Settings
11 6
12** Disclaimer 7Basic settings necessary for a decent editing experience in Emacs.
8These should not require non-built-in packages.
13 9
14 #+begin_src emacs-lisp :comments no 10** Prelude
15 ;; config.el -*- lexical-binding: t -*-
16 ;; This file is automatically tangled from config.org.
17 ;; Hand edits will be overwritten!
18 #+end_src
19 11
20** About me 12*** Enable lexical binding
21
22 #+begin_src emacs-lisp
23 (setq user-full-name "Case Duckworth"
24 user-mail-address "acdw@acdw.net")
25 #+end_src
26
27** Correct =exec-path=
28
29 Straight depends on Git, so I need to tell Emacs where different paths are.
30
31 #+begin_src emacs-lisp
32 (let ((win-downloads "c:/Users/aduckworth/Downloads"))
33 (dolist (path (list
34 ;; Linux
35 (expand-file-name "bin"
36 user-emacs-directory)
37 (expand-file-name "~/bin")
38 (expand-file-name "~/.local/bin")
39 (expand-file-name "~/Scripts")
40 ;; Windows
41 (expand-file-name "emacs/bin"
42 win-downloads)
43 (expand-file-name "m/usr/bin"
44 win-downloads)
45 (expand-file-name "m/mingw64/bin"
46 win-downloads)
47 (expand-file-name "PortableGit/bin"
48 win-downloads)
49 (expand-file-name "PortableGit/usr/bin"
50 win-downloads)))
51 (when (file-exists-p path)
52 (add-to-list 'exec-path path))))
53 #+end_src
54
55** Package management
56
57*** Straight.el
58
59 Straight can't bootstrap itself on Windows, so I've wrapped the
60 bootstrap code from straight's repo in a function.
61
62 #+begin_src emacs-lisp
63 (defun acdw/bootstrap-straight ()
64 "Bootstrap straight.el."
65 (defvar bootstrap-version)
66 (let ((bootstrap-file
67 (expand-file-name
68 "straight/repos/straight.el/bootstrap.el"
69 user-emacs-directory))
70 (bootstrap-version 5))
71 (unless (file-exists-p bootstrap-file)
72 (with-current-buffer
73 (url-retrieve-synchronously
74 (concat
75 "https://raw.githubusercontent.com/"
76 "raxod502/straight.el/"
77 "develop/install.el")
78 'silent 'inhibit-cookies)
79 (goto-char (point-max))
80 (eval-print-last-sexp)))
81 (load bootstrap-file nil 'nomessage)))
82 #+end_src
83
84 Now, I'll /try/ running it regular-style, ignoring the errors. If it
85 doesn't work, I'll call git directly and clone the repo myself.
86
87 #+begin_src emacs-lisp
88 (unless (ignore-errors (acdw/bootstrap-straight))
89 (message "%s" "Straight.el didn't bootstrap correctly. Cloning directly...")
90 (call-process "git" nil
91 (get-buffer-create "*bootstrap-straight-messages*") nil
92 "clone"
93 "https://github.com/raxod502/straight.el"
94 (expand-file-name "straight/repos/straight.el"
95 user-emacs-directory))
96 (acdw/bootstrap-straight))
97 #+end_src
98
99** Ease-of-configuring functions
100
101*** Emulate use-package’s =:custom=
102
103 #+begin_src emacs-lisp
104 (defmacro cuss (var val &optional _docstring)
105 "Basically, `:custom' from `use-package', but without `use-package'."
106 (declare (doc-string 3)
107 (indent 2))
108 `(funcall (or (get ',var 'custom-set) #'set-default)
109 ',var ,val))
110 #+end_src
111
112*** Emulate use-package’s =:custom-face=, but better
113
114 #+begin_src emacs-lisp
115 (defvar acdw--custom-faces ()
116 "List of custom faces to run through acdw/set-custom-faces.")
117
118 (defun acdw/set-custom-faces ()
119 "Run `customize-set-faces' on `acdw--custom-faces'."
120 (message "%s" "Customizing faces...")
121 (apply #'custom-set-faces acdw--custom-faces))
122
123 (defun cussface (spec)
124 "Add SPEC to `acdw--custom-faces', and add a hook to run
125 `acdw/set-custom-faces' after init."
126 (add-to-list 'acdw--custom-faces spec)
127 (add-hook 'after-init-hook #'acdw/set-custom-faces))
128 #+end_src
129
130*** Determine whether any Emacs frame is focused or not
131
132 This comes in handy when I want to garbage collect, say, or save recent files.
133
134 #+begin_src emacs-lisp
135 (defun acdw/when-unfocused (func &rest args)
136 "Run FUNC with ARGS only if all frames are out of focus."
137 (if (seq-every-p #'null (mapcar #'frame-focus-state (frame-list)))
138 (apply func args)))
139 #+end_src
140
141*** Determine where I am
142
143 I use Emacs at home, with Linux, and at work, with Windows.
144
145 #+begin_src emacs-lisp
146 (defmacro when-at (conditions &rest commands)
147 "Only do COMMANDS when CONDITIONS are met.
148 CONDITIONS are one of `:work', `:home', or a list beginning with
149 the above and other conditions to check."
150 (declare (indent 1))
151 (let ((at-work (memq system-type '(ms-dos windows-nt)))
152 (at-home (memq system-type '(gnu gnu/linux gnu/kfreebsd))))
153 (pcase conditions
154 (:work `(when ',at-work ,@commands))
155 (:home `(when ',at-home ,@commands))
156 (`(:work ,others) `(when (and ',at-work ,others)
157 ,@commands))
158 (`(:home ,others) `(when (and ',at-home ,others)
159 ,@commands)))))
160 #+end_src
161
162** Clean =.emacs.d=
163
164 #+begin_src emacs-lisp
165 (straight-use-package 'no-littering)
166 (require 'no-littering)
167 #+end_src
168
169*** Don’t clutter =init.el= with customizations
170
171 #+begin_src emacs-lisp
172 (with-eval-after-load 'no-littering
173 (cuss custom-file (no-littering-expand-etc-file-name "custom.el")))
174 #+end_src
175
176** Look and feel
177
178*** Cursor
179
180 #+begin_src emacs-lisp
181 (cuss cursor-type 'bar
182 "Show a vertical bar for the cursor.")
183
184 (cuss cursor-in-non-selected-windows nil
185 "Don't show a cursor in non-selected windows.")
186
187 ;; Don't blink the cursor
188 (blink-cursor-mode -1)
189 #+end_src
190
191*** Tool Bars
192
193**** Tool bars and menu bars
194
195 #+begin_src emacs-lisp
196 (menu-bar-mode -1)
197 (tool-bar-mode -1)
198 #+end_src
199
200**** Scroll bars
201
202 #+begin_src emacs-lisp
203 (scroll-bar-mode -1)
204 (horizontal-scroll-bar-mode -1)
205 #+end_src
206
207*** Dialogs
208
209 #+begin_src emacs-lisp
210 (cuss use-dialog-box nil
211 "Don't use dialog boxes to ask questions.")
212 #+end_src
213
214**** Yes or no questions
215
216 #+begin_src emacs-lisp
217 (fset 'yes-or-no-p #'y-or-n-p)
218 #+end_src
219
220**** The Bell
221
222 from [[https://www.emacswiki.org/emacs/AlarmBell#h5o-3][EmacsWiki]].
223
224 #+begin_src emacs-lisp
225 (setq visible-bell nil
226 ring-bell-function 'flash-mode-line)
227
228 (defun flash-mode-line ()
229 (invert-face 'mode-line)
230 (run-with-timer 0.1 nil #'invert-face 'mode-line))
231 #+end_src
232
233*** Frames
234
235**** Frame titles
236
237 #+begin_src emacs-lisp
238 (cuss frame-title-format (concat invocation-name "@" system-name
239 ": %b %+%+ %f"))
240 #+end_src
241
242**** Fringes
243
244 #+begin_src emacs-lisp
245 (cuss indicate-empty-lines t
246 "Show an indicator on the left fringe of empty lines past the
247 end of the buffer.")
248 (cuss indicate-buffer-boundaries 'right
249 "Indicate the beginning and end of the buffer and whether it
250 scrolls off-window in the right fringe.")
251
252 (cuss visual-line-fringe-indicators '(left-curly-arrow nil)
253 "Indicate continuing lines with a curly arrow in the left fringe.")
254
255 ;; redefine the `left-curly-arrow' indicator to be less distracting.
256 (define-fringe-bitmap 'left-curly-arrow
257 [#b11000000
258 #b01100000
259 #b00110000
260 #b00011000])
261 #+end_src
262
263**** Minibuffer
264
265 #+begin_src emacs-lisp
266 (cuss minibuffer-prompt-properties
267 '(read-only t cursor-intangible t face minibuffer-prompt)
268 "Keep the cursor away from the minibuffer prompt.")
269 #+end_src
270
271**** Tabs
272
273 #+begin_src emacs-lisp
274 (cuss tab-bar-tab-name-function
275 #'tab-bar-tab-name-current-with-count
276 "Show the tab name as the name of the current buffer, plus a
277 count of the windows in the tab.")
278
279 (cuss tab-bar-show 1
280 "Show the tab bar, when there's more than one tab.")
281 #+end_src
282
283*** Windows
284
285**** Winner mode
286
287 #+begin_src emacs-lisp
288 (when (fboundp 'winner-mode)
289 (winner-mode +1))
290 #+end_src
291
292**** Switch windows or buffers if one window
293
294 from [[https://www.reddit.com/r/emacs/comments/kz347f/what_parts_of_your_config_do_you_like_best/gjlnp2c/][u/astoff1]].
295
296 #+begin_src emacs-lisp
297 (defun acdw/other-window-or-buffer ()
298 "Switch to other window, or previous buffer."
299 (interactive)
300 (if (eq (count-windows) 1)
301 (switch-to-buffer nil)
302 (other-window 1)))
303
304 (global-set-key (kbd "M-o") #'acdw/other-window-or-buffer)
305 #+end_src
306
307**** Pop-up windows
308
309 #+begin_src emacs-lisp
310 (straight-use-package 'popwin)
311 (popwin-mode +1)
312 #+end_src
313
314*** Buffers
315
316**** Uniquify buffers
317
318 #+begin_src emacs-lisp
319 (require 'uniquify)
320 (cuss uniquify-buffer-name-style 'forward
321 "Uniquify buffers' names by going up the path trees until they
322 become unique.")
323 #+end_src
324
325**** Startup buffers
326
327 #+begin_src emacs-lisp
328 (cuss inhibit-startup-screen t
329 "Don't show Emacs' startup buffer.")
330
331 (cuss initial-buffer-choice t
332 "Start with *scratch*.")
333
334 (cuss initial-scratch-message ""
335 "Empty *scratch* buffer.")
336 #+end_src
337
338**** COMMENT Focus and move buffers directionally
339
340 Commented for now because I really need to figure out the keybindings I want to use for this. The real issue is the collisions between Org-mode, Windows, and Emacs’ normal bindings.
341
342 #+begin_src emacs-lisp
343 ;; change focus
344 ;; for more on `ignore-error-wrapper', see
345 ;; https://www.emacswiki.org/emacs/WindMove#h5o-3
346 (defun ignore-error-wrapper (fn)
347 "Funtion return new function that ignore errors.
348 The function wraps a function with `ignore-errors' macro."
349 (lexical-let ((fn fn))
350 (lambda ()
351 (interactive)
352 (ignore-errors
353 (funcall fn)))))
354
355 (global-set-key [S-left] (ignore-error-wrapper 'windmove-left))
356 (global-set-key [S-right] (ignore-error-wrapper 'windmove-right))
357 (global-set-key [S-up] (ignore-error-wrapper 'windmove-up))
358 (global-set-key [S-down] (ignore-error-wrapper 'windmove-down))
359
360 ;; shift buffers
361 (straight-use-package 'buffer-move)
362
363 (global-set-key (kbd "<C-S-up>") 'buf-move-up)
364 (global-set-key (kbd "<C-S-down>") 'buf-move-down)
365 (global-set-key (kbd "<C-S-left>") 'buf-move-left)
366 (global-set-key (kbd "<C-S-right>") 'buf-move-right)
367 #+end_src
368
369**** Kill the current buffer
370
371 #+begin_src emacs-lisp
372 (defun acdw/kill-a-buffer (&optional prefix)
373 "Kill a buffer based on the following rules:
374
375 C-x k ⇒ Kill current buffer & window
376 C-u C-x k ⇒ Kill OTHER window and its buffer
377 C-u C-u C-x C-k ⇒ Kill all other buffers and windows
378
379 Prompt only if there are unsaved changes."
380 (interactive "P")
381 (pcase (or (car prefix) 0)
382 ;; C-x k ⇒ Kill current buffer & window
383 (0 (kill-current-buffer)
384 (unless (one-window-p) (delete-window)))
385 ;; C-u C-x k ⇒ Kill OTHER window and its buffer
386 (4 (other-window 1)
387 (kill-current-buffer)
388 (unless (one-window-p) (delete-window)))
389 ;; C-u C-u C-x C-k ⇒ Kill all other buffers and windows
390 (16 (mapc 'kill-buffer (delq (current-buffer) (buffer-list)))
391 (delete-other-windows))))
392
393 (define-key ctl-x-map "k" #'acdw/kill-a-buffer)
394 #+end_src
395
396***** Remap =C-x M-k= to bring up the buffer-killing menu
397
398 #+begin_src emacs-lisp
399 (define-key ctl-x-map (kbd "M-k") #'kill-buffer)
400 #+end_src
401
402**** Immortal =*scratch*= buffer
403
404 #+begin_src emacs-lisp
405 (defun immortal-scratch ()
406 (if (eq (current-buffer) (get-buffer "*scratch*"))
407 (progn (bury-buffer)
408 nil)
409 t))
410
411 (add-hook 'kill-buffer-query-functions 'immortal-scratch)
412 #+end_src
413
414*** Modeline
415
416**** Smart mode line
417
418 #+begin_src emacs-lisp
419 (straight-use-package 'smart-mode-line)
420
421 (cuss sml/no-confirm-load-theme t
422 "Pass the NO-CONFIRM flag to `load-theme'.")
423
424 (sml/setup)
425 #+end_src
426
427**** Rich minority
428
429 Since this /comes/ with smart mode line, I’m just going to use it,
430 instead of =diminish= or another package. I do have to write this
431 helper function, though, to add things to the whitelist.
432
433 #+begin_src emacs-lisp
434 (defun rm/whitelist-add (regexp)
435 "Add a REGEXP to the whitelist for `rich-minority'."
436 (if (listp 'rm--whitelist-regexps)
437 (add-to-list 'rm--whitelist-regexps regexp)
438 (setq rm--whitelist-regexps `(,regexp)))
439 (setq rm-whitelist
440 (mapconcat 'identity rm--whitelist-regexps "\\|")))
441
442 (straight-use-package 'rich-minority)
443
444 (rm/whitelist-add "^$")
445 #+end_src
446
447*** Theme
448
449**** Modus Themes
450
451 #+begin_src emacs-lisp
452 (straight-use-package 'modus-themes)
453
454 (cuss modus-themes-slanted-constructs t
455 "Use more slanted constructs.")
456 (cuss modus-themes-bold-constructs t
457 "Use more bold constructs.")
458
459 (cuss modus-themes-region 'bg-only
460 "Only highlight the background of the selected region.")
461
462 (cuss modus-themes-org-blocks 'grayscale
463 "Show org-blocks with a grayscale background.")
464 (cuss modus-themes-headings
465 '((1 . line)
466 (t . t))
467 "Highlight top headings with `line' style, and others by default.")
468
469 (cuss modus-themes-scale-headings t
470 "Scale headings by the ratios below.")
471 (cuss modus-themes-scale-1 1.1)
472 (cuss modus-themes-scale-2 1.15)
473 (cuss modus-themes-scale-3 1.21)
474 (cuss modus-themes-scale-4 1.27)
475 (cuss modus-themes-scale-5 1.33)
476
477 (load-theme 'modus-operandi t)
478 #+end_src
479
480**** Change themes based on time of day
481
482 #+begin_src emacs-lisp
483 (cuss calendar-latitude 30.4515)
484 (cuss calendar-longitude -91.1871)
485
486 (defun acdw/run-with-sun (sunrise-command sunset-command)
487 "Run commands at sunrise and sunset."
488 (let* ((times-regex (rx (* nonl)
489 (: (any ?s ?S) "unrise") " "
490 (group (repeat 1 2 digit) ":"
491 (repeat 1 2 digit)
492 (: (any ?a ?A ?p ?P) (any ?m ?M)))
493 (* nonl)
494 (: (any ?s ?S) "unset") " "
495 (group (repeat 1 2 digit) ":"
496 (repeat 1 2 digit)
497 (: (any ?a ?A ?p ?P) (any ?m ?M)))
498 (* nonl)))
499 (ss (sunrise-sunset))
500 (_m (string-match times-regex ss))
501 (sunrise-time (match-string 1 ss))
502 (sunset-time (match-string 2 ss)))
503 (run-at-time sunrise-time (* 60 60 24) sunrise-command)
504 (run-at-time sunset-time (* 60 60 24) sunset-command)))
505
506 (acdw/run-with-sun #'modus-themes-load-operandi #'modus-themes-load-vivendi)
507 #+end_src
508
509*** Fonts
510
511**** Define fonts
512
513 #+begin_src emacs-lisp
514 (defun set-face-from-alternatives (face frame &rest fontspecs)
515 "Set FACE on FRAME from first available spec from FONTSPECS.
516 FACE and FRAME work the same as with `set-face-attribute.'"
517 (catch :return
518 (dolist (spec fontspecs)
519 (when-let ((found (find-font (apply #'font-spec spec))))
520 (set-face-attribute face frame :font found)
521 (throw :return found)))))
522
523 (defun acdw/setup-fonts ()
524 "Setup fonts. This has to happen after the frame is setup for
525 the first time, so it should be added to `window-setup-hook'. It
526 removes itself from that hook."
527 (interactive)
528 (when (display-graphic-p)
529 (dolist (face '(default fixed-pitch))
530 ;; fixed-pitch /is/ the default
531 (set-face-from-alternatives face nil
532 '(:family "Input Mono"
533 :slant normal
534 :weight normal
535 :height 110)
536 '(:family "Go Mono"
537 :slant normal
538 :weight normal
539 :height 100)
540 '(:family "Consolas"
541 :slant normal
542 :weight normal
543 :height 100)))
544 ;; variable-pitch is different
545 (set-face-from-alternatives 'variable-pitch nil
546 '(:family "Input Sans"
547 :slant normal
548 :weight normal)
549 '(:family "Georgia"
550 :slant normal
551 :weight normal)))
552
553 ;; remove myself from the hook
554 (remove-function after-focus-change-function #'acdw/setup-fonts))
555
556 (add-function :before after-focus-change-function #'acdw/setup-fonts)
557 #+end_src
558
559**** Custom faces
560
561 #+begin_src emacs-lisp
562 (cussface '(font-lock-comment-face
563 ((t (:inherit (custom-comment italic variable-pitch))))))
564 #+end_src
565
566**** Line spacing
567
568 #+begin_src emacs-lisp
569 (cuss line-spacing 0.1
570 "Add 10% extra space below each line.")
571 #+end_src
572
573**** Underlines
574
575 #+begin_src emacs-lisp
576 (cuss x-underline-at-descent-line t
577 "Draw the underline at the same place as the descent line.")
578 #+end_src
579
580**** Unicode Fonts
581
582 #+begin_src emacs-lisp
583 (straight-use-package 'unicode-fonts)
584 (require 'unicode-fonts)
585 (unicode-fonts-setup)
586 #+end_src
587
588** Interactivity
589
590*** Completing read
591
592**** Shadow file names in =completing-read=.
593
594 #+begin_src emacs-lisp
595 (cuss file-name-shadow-properties '(invisible t))
596
597 (file-name-shadow-mode +1)
598 #+end_src
599
600**** Ignore case in =completing-read=
601
602 #+begin_src emacs-lisp
603 (cuss completion-ignore-case t)
604 (cuss read-buffer-completion-ignore-case t)
605 (cuss read-file-name-completion-ignore-case t)
606 #+end_src
607
608**** Minibuffer recursivity
609
610 #+begin_src emacs-lisp
611 (cuss enable-recursive-minibuffers t)
612 (minibuffer-depth-indicate-mode +1)
613 #+end_src
614
615**** Selectrum
616
617 #+begin_src emacs-lisp
618 (straight-use-package 'selectrum)
619 (require 'selectrum)
620 (selectrum-mode +1)
621 #+end_src
622
623**** Prescient
624
625 #+begin_src emacs-lisp
626 (straight-use-package 'prescient)
627 (require 'prescient)
628 (prescient-persist-mode +1)
629
630 (straight-use-package 'selectrum-prescient)
631 (require 'selectrum-prescient)
632 (selectrum-prescient-mode +1)
633 #+end_src
634
635**** Consult
636
637 #+begin_src emacs-lisp
638 (straight-use-package '(consult
639 :host github
640 :repo "minad/consult"
641 :files (:defaults "consult-pkg.el")))
642 (require 'consult)
643
644 (with-eval-after-load 'consult
645 (define-key ctl-x-map "b" #'consult-buffer)
646 (define-key ctl-x-map (kbd "C-r") #'consult-buffer)
647 (define-key ctl-x-map "4b" #'consult-buffer-other-window)
648 (define-key ctl-x-map "5b" #'consult-buffer-other-frame)
649
650 (define-key goto-map "o" #'consult-outline)
651 (define-key goto-map "g" #'consult-line)
652 (define-key goto-map (kbd "M-g") #'consult-line)
653 (define-key goto-map "l" #'consult-line)
654 (define-key goto-map "m" #'consult-mark)
655 (define-key goto-map "i" #'consult-imenu)
656 (define-key goto-map "e" #'consult-error)
657
658 (global-set-key (kbd "M-y") #'consult-yank-pop)
659
660 (define-key help-map "a" #'consult-apropos)
661
662 (fset 'multi-occur #'consult-multi-occur))
663 #+end_src
664
665**** Marginalia
666
667 #+begin_src emacs-lisp
668 (straight-use-package '(marginalia
669 :host github
670 :repo "minad/marginalia"
671 :branch "main"))
672
673 (cuss marginalia-annotators
674 '(marginalia-annotators-heavy
675 marginalia-annotators-light))
676
677 (marginalia-mode +1)
678 #+end_src
679
680**** COMMENT Ido
681
682 [[https://wandersoncferreira.github.io/blog/ido/][Let’s try this out]].
683
684 #+begin_src emacs-lisp
685 (defun ido-choose-from-recentf ()
686 "Use ido to select recently visited files."
687 (interactive)
688 (find-file (ido-completing-read "Open file: " recentf-list nil t)))
689
690 (defun bk/go-straight-home ()
691 (interactive)
692 (cond
693 ((looking-back "~/") (insert "projects/"))
694 ((looking-back "/") (insert "~/"))
695 (:else (call-interactively 'self-insert-command))))
696
697 (defun ido-disable-line-truncation ()
698 (set (make-local-variable 'truncate-lines) nil))
699
700 (defun ido-define-keys ()
701 (define-key ido-completion-map (kbd "C-n") 'ido-next-match)
702 (define-key ido-completion-map (kbd "C-p") 'ido-prev-match))
703
704 (setq ido-enable-flex-matching t
705 ido-use-filename-at-point nil
706 ido-create-new-buffer 'always
707 confirm-nonexistent-file-or-buffer nil
708 completion-ignored-extensions (cons "*.aux" completion-ignored-extensions)
709 max-mini-window-height 0.5
710 ido-enable-tramp-completion t
711 ido-auto-merge-work-directories-length -1
712 ido-confirm-unique-completion t
713 ido-default-file-method 'selected-window
714 ido-case-fold t
715 ido-show-dot-for-dired t
716 ido-everywhere t
717 ido-ignore-buffers (list (rx (or (and bos " ")
718 (and bos
719 (or "*Completions*"
720 "*Compile-Log*"
721 "*Ido Completions*"
722 "*Shell Command Output*"
723 "*vc-diff*")
724 eos))))
725 ido-decorations (quote ("\n-> " "" "\n " "\n ..." "[" "]" "
726 [No match]" " [Matched]" " [Not readable]" " [Too big]" "
727 [Confirm]")))
728
729 (with-eval-after-load 'ido
730 (define-key ido-common-completion-map (kbd "M-SPC") 'just-one-space)
731 (define-key ido-common-completion-map (kbd "SPC") 'self-insert-command)
732 (define-key ido-file-completion-map (kbd "~") 'bk/go-straight-home)
733
734 (add-hook 'ido-setup-hook 'ido-define-keys)
735
736 (add-hook 'ido-minibuffer-setup-hook 'ido-disable-line-truncation)
737
738 (set-default 'imenu-auto-rescan t)
739
740 (add-to-list 'ido-ignore-directories "target")
741 (add-to-list 'ido-ignore-directories "node_modules")
742 )
743
744 (defun setup-ido-mode ()
745 (require 'ido)
746 (ido-mode +1)
747 (ido-everywhere +1))
748
749 (add-hook 'after-init-hook #'setup-ido-mode)
750 #+end_src
751
752*** Completion
753
754 #+begin_src emacs-lisp
755 (global-set-key (kbd "M-/") #'hippie-expand)
756 #+end_src
757
758*** Garbage collection
759
760 #+begin_src emacs-lisp
761 (straight-use-package 'gcmh)
762 (gcmh-mode +1)
763
764 (defun dotfiles--gc-on-last-frame-out-of-focus ()
765 "GC if all frames are inactive."
766 (if (seq-every-p #'null (mapcar #'frame-focus-state (frame-list)))
767 (garbage-collect)))
768
769 (add-function :after after-focus-change-function
770 #'dotfiles--gc-on-last-frame-out-of-focus)
771 #+end_src
772
773** Keyboard
774
775*** =ESC= cancels all
776
777 #+begin_src emacs-lisp
778 (global-set-key (kbd "<escape>") #'keyboard-escape-quit)
779 #+end_src
780
781*** Personal prefix key: =C-z=
782
783 #+begin_src emacs-lisp
784 (defvar acdw/map
785 (let ((map (make-sparse-keymap))
786 (c-z (global-key-binding "\C-z")))
787 (global-unset-key "\C-z")
788 (define-key global-map "\C-z" map)
789 (define-key map "\C-z" c-z)
790 map))
791
792 (run-hooks 'acdw/map-defined-hook)
793 #+end_src
794
795*** Show keybindings
796
797 #+begin_src emacs-lisp
798 (straight-use-package 'which-key)
799 (which-key-mode +1)
800 #+end_src
801
802** Mouse
803
804*** Preserve screen position when scrolling with the mouse wheel
805
806 from [[https://www.reddit.com/r/emacs/comments/km9by4/weekly_tipstricketc_thread/ghg2c9d/][u/TheFrenchPoulp]].
807
808 #+begin_src emacs-lisp
809 (advice-add 'mwheel-scroll :around #'me/mwheel-scroll)
810
811 (defun me/mwheel-scroll (original &rest arguments)
812 "Like `mwheel-scroll' but preserve screen position.
813 See `scroll-preserve-screen-position'."
814 (let ((scroll-preserve-screen-position :always))
815 (apply original arguments)))
816 #+end_src
817
818*** Scroll much faster
819
820 from [[https://github.com/mpereira/.emacs.d#make-cursor-movement-an-order-of-magnitude-faster][mpereira]], from somewhere else.
821
822 #+begin_src emacs-lisp
823 (cuss auto-window-vscroll nil
824 "Don't auto-adjust `window-vscroll' to view long lines.")
825
826 (cuss fast-but-imprecise-scrolling t
827 "Scroll fast, but possibly with inaccurate text rendering.")
828
829 (cuss jit-lock-defer-time 0
830 "Only defer font-locking when input is pending.")
831 #+end_src
832
833** Persistence
834
835*** Minibuffer history
836
837 #+begin_src emacs-lisp
838 (require 'savehist)
839
840 (cuss savehist-additional-variables
841 '(kill-ring
842 search-ring
843 regexp-search-ring)
844 "Other variables to save alongside the minibuffer history.")
845
846 (cuss history-length t
847 "Don't truncate history.")
848
849 (cuss history-delete-duplicates t
850 "Delete history duplicates.")
851
852 (savehist-mode +1)
853 #+end_src
854
855*** File places
856
857 #+begin_src emacs-lisp
858 (require 'saveplace) ; this isn't required, but ... I like having it here
859
860 (cuss save-place-forget-unreadable-files t
861 "Don't check if files are readable or not.")
862
863 (save-place-mode +1)
864 #+end_src
865
866*** Recent files
867
868 #+begin_src emacs-lisp
869 (require 'recentf)
870
871 (cuss recentf-max-menu-items 100
872 "The maximum number of items in the recentf menu.")
873 (cuss recentf-max-saved-items nil
874 "Don't limit the number of recent files.")
875
876 (with-eval-after-load 'no-littering
877 (add-to-list 'recentf-exclude no-littering-var-directory)
878 (add-to-list 'recentf-exclude no-littering-etc-directory))
879
880 (recentf-mode +1)
881
882 ;; save recentf list when focusing away
883 (defun acdw/maybe-save-recentf ()
884 "Save `recentf-file' when out of focus, but only if we haven't
885 in five minutes."
886 (defvar recentf-last-save (time-convert nil 'integer)
887 "How long it's been since we last saved the recentf list.")
888
889 (when (> (time-convert (time-since recentf-last-save) 'integer)
890 (* 60 5))
891 (setq recentf-last-save (time-convert nil 'integer))
892 (acdw/when-unfocused #'recentf-save-list)))
893
894 (add-function :after after-focus-change-function
895 #'acdw/maybe-save-recentf)
896 #+end_src
897
898** Undo
899
900 #+begin_src emacs-lisp
901 (straight-use-package 'undo-fu)
902 (require 'undo-fu)
903
904 (global-set-key (kbd "C-/") #'undo-fu-only-undo)
905 (global-set-key (kbd "C-?") #'undo-fu-only-redo)
906
907 (straight-use-package 'undo-fu-session)
908 (require 'undo-fu-session)
909
910 (cuss undo-fu-session-incompatible-files
911 '("/COMMIT_EDITMSG\\'"
912 "/git-rebase-todo\\'")
913 "A list of files that are incompatible with the concept of undo sessions.")
914
915 (with-eval-after-load 'no-littering
916 (let ((dir (no-littering-expand-var-file-name "undos")))
917 (make-directory dir 'parents)
918 (cuss undo-fu-session-directory dir)))
919
920 (global-undo-fu-session-mode +1)
921 #+end_src
922
923** Files
924
925*** Encoding
926 13
927**** UTF-8 14#+begin_src emacs-lisp :comments no
15 ;; config.el -*- lexical-binding: t -*-
16#+end_src
928 17
929 from [[https://www.masteringemacs.org/article/working-coding-systems-unicode-emacs][Mastering Emacs]]. 18*** Disclaimer
930 19
931 #+begin_src emacs-lisp 20#+NAME: disclaimer
932 (prefer-coding-system 'utf-8) 21#+begin_src emacs-lisp :comments no
933 (set-default-coding-systems 'utf-8) 22 ;; This file is automatically tangled from config.org.
934 (set-terminal-coding-system 'utf-8) 23 ;; Hand edits will be overwritten!
935 (set-keyboard-coding-system 'utf-8) 24#+end_src
936 ;; backwards compatibility:
937 ;; `default-buffer-file-coding-system' is deprecated in 23.2.
938 (setq default-buffer-file-coding-system 'utf-8)
939 25
940 ;; Treat clipboard as UTF-8 string first; compound text next, etc. 26** Customization
941 (setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING))
942 #+end_src
943 27
944**** Convert all files to UNIX-style line endings 28*** Emulate use-package's =:custom=
945 29
946 from [[https://www.emacswiki.org/emacs/EndOfLineTips][Emacs Wiki]]. 30#+begin_src emacs-lisp
31 (defmacro cuss (var val &optional _docstring)
32 "`use-package''s `:custom', without `use-package'."
33 (declare (doc-string 3)
34 (indent 2))
35 `(funcall (or (get ',var 'custom-set) #'set-default)
36 ',var ,val))
37#+end_src
947 38
948 #+begin_src emacs-lisp 39*** Emulate use-package's =:custom-face=
949 (defun ewiki/no-junk-please-were-unixish ()
950 "Convert line endings to UNIX, dammit."
951 (let ((coding-str (symbol-name buffer-file-coding-system)))
952 (when (string-match "-\\(?:dos\\|mac\\)$" coding-str)
953 (set-buffer-file-coding-system 'unix))))
954 #+end_src
955 40
956 I add it to the ~find-file-hook~ /and/ ~before-save-hook~ because I 41#+begin_src emacs-lisp
957 don't want to ever work with anything other than UNIX line endings 42 (defvar acdw--custom-faces ()
958 ever again. I just don't care. Even Microsoft Notepad can handle 43 "List of custom faces to run through `acdw/set-custom-faces'.")
959 UNIX line endings, so I don't want to hear it.
960 44
961 #+begin_src emacs-lisp 45 (defun acdw/set-custom-faces ()
962 (add-hook 'find-file-hook #'ewiki/no-junk-please-were-unixish) 46 "Customize faces using `customize-set-faces'.
963 (add-hook 'before-save-hook #'ewiki/no-junk-please-were-unixish)
964 #+end_src
965 47
966*** Backups 48 I only want to run this once, per the documentation of `customize-set-faces'."
49 (when acdw--custom-faces
50 (let ((msg "Customizing faces"))
51 (message "%s..." msg)
52 (apply #'custom-set-faces acdw--custom-faces)
53 (message "%s...Done." msg)
54 (remove-function after-focuse-change-function #'acdw/set-custom-faces))))
967 55
968 #+begin_src emacs-lisp 56 (add-function :before after-focus-change-function #'acdw/set-custom-faces)
969 (cuss backup-by-copying 1)
970 (cuss delete-old-versions -1)
971 (cuss version-control t)
972 (cuss vc-make-backup-files t)
973 57
974 (with-eval-after-load 'no-littering 58 (defmacro cussface (face spec &optional _docstring)
975 (let ((dir (no-littering-expand-var-file-name "backup"))) 59 "Add the form (FACE SPEC) to `acdw--custom-faces', and add a
976 (make-directory dir 'parents) 60 hook to run `acdw/set-custom-faces' after init."
977 (cuss backup-directory-alist 61 (declare (doc-string 3)
978 `((".*" . ,dir))))) 62 (indent 2))
979 #+end_src 63 `(add-to-list acdw--custom-faces '(,face ,spec)))
64#+end_src
980 65
981*** Auto-saves 66*** Only do something when Emacs is unfocused
982 67
983 #+begin_src emacs-lisp 68Since Emacs is single-threaded, I only want to run really expensive
984 ;; turn off auto-save-mode until we can set up the right directory for them 69operations when I won't notice, say .. when I'm focused on another
985 (auto-save-mode -1) 70window.
986 71
987 (with-eval-after-load 'no-littering 72#+begin_src emacs-lisp
988 (let ((dir (no-littering-expand-var-file-name "autosaves"))) 73 (defun when-unfocused (func &rest args)
989 (make-directory dir 'parents) 74 "Run FUNC with ARGS iff all frames are out of focus."
990 (cuss auto-save-file-name-transforms 75 (when (seq-every-p #'null (mapcar #'frame-focus-state (frame-list)))
991 `((".*" ,dir t)))) 76 (apply func args)))
77#+end_src
992 78
993 (auto-save-mode +1)) 79*** Throw customizations away
994 #+end_src
995 80
996*** Super-save 81I use Emacs's Customize interface, but really only to learn about what
82options a package presents to /be/ customized. I don't want to use
83the custom file for anything at all.
997 84
998 Because I like /overkill/, or at least … over-/saving/. 85#+begin_src emacs-lisp
86 (cuss custom-file null-device)
87#+end_src
999 88
1000 #+begin_src emacs-lisp 89** About me
1001 (straight-use-package 'super-save)
1002
1003 (cuss super-save-remote-files nil
1004 "Don't super-save remote files.")
1005
1006 (cuss super-save-exclude '(".gpg")
1007 "Ignore these files when saving.")
1008
1009 (super-save-mode +1)
1010 #+end_src
1011
1012*** Auto-revert buffers to files on disk
1013
1014 #+begin_src emacs-lisp
1015 (global-auto-revert-mode +1)
1016 #+end_src
1017
1018*** Add a timestamp to files
1019
1020 #+begin_src emacs-lisp
1021 (add-hook 'before-save-hook #'time-stamp)
1022 #+end_src
1023
1024*** Require a final new line
1025
1026 #+begin_src emacs-lisp
1027 (cuss require-final-newline t)
1028 #+end_src
1029
1030*** Edit files with =sudo=
1031
1032 #+begin_src emacs-lisp
1033 (straight-use-package 'sudo-edit)
1034
1035 (with-eval-after-load 'sudo-edit
1036 (define-key acdw/map (kbd "C-r") #'sudo-edit))
1037 #+end_src
1038
1039**** Don’t add =/sudo:= files to =recentf=, though
1040
1041 I’ve pretty much cribbed this from [[https://github.com/ncaq/recentf-remove-sudo-tramp-prefix/][recentf-remove-sudo-tramp-prefix]] – it’s a small enough package that I can just include it completely here.
1042
1043 #+begin_src emacs-lisp
1044 ;; appease the compiler
1045 (declare-function tramp-tramp-file-p "tramp")
1046 (declare-function tramp-dissect-file-name "tramp")
1047 (declare-function tramp-file-name-method "tramp")
1048 (declare-function tramp-file-name-localname "tramp")
1049
1050 (defun recentf-remove-sudo-tramp-prefix (path)
1051 "Remove sudo from PATH."
1052 (require 'tramp)
1053 (if (tramp-tramp-file-p path)
1054 (let ((tx (tramp-dissect-file-name path)))
1055 (if (string-equal "sudo" (tramp-file-name-method tx))
1056 (tramp-file-name-localname tx)
1057 path))
1058 path)
1059
1060 (defun recentf-remove-sudo-tramp-prefix-from-recentf-list ()
1061 (require 'recentf)
1062 (setq recentf-list
1063 (mapcar #'recentf-remove-sudo-tramp-prefix recentf-list)))
1064
1065 (advice-add 'recentf-cleanup
1066 :before #'recentf-remove-sudo-tramp-prefix-from-recentf-list))
1067 #+end_src
1068
1069** Text editing
1070
1071*** Operate visually on lines
1072
1073 #+begin_src emacs-lisp
1074 (global-visual-line-mode +1)
1075 #+end_src
1076
1077*** View long lines like filled lines in the beginning
1078
1079 #+begin_src emacs-lisp
1080 (straight-use-package 'adaptive-wrap)
1081
1082 (when (fboundp 'adaptive-wrap-prefix-mode)
1083 (defun acdw/activate-adaptive-wrap-prefix-mode ()
1084 "Toggle `visual-line-mode' and `adaptive-wrap-prefix-mode' simultaneously."
1085 (adaptive-wrap-prefix-mode (if visual-line-mode
1086 +1
1087 -1)))
1088 (add-hook 'visual-line-mode-hook #'acdw/activate-adaptive-wrap-prefix-mode))
1089 #+end_src
1090
1091*** Stay snappy with long-lined files
1092
1093 #+begin_src emacs-lisp
1094 (when (fboundp 'global-so-long-mode)
1095 (global-so-long-mode +1))
1096 #+end_src
1097
1098*** Killing & Yanking
1099
1100**** Replace selection when typing
1101
1102 #+begin_src emacs-lisp
1103 (delete-selection-mode +1)
1104 #+end_src
1105
1106**** Work better with the system clipboard
1107
1108 #+begin_src emacs-lisp
1109 (cuss save-interprogram-paste-before-kill t
1110 "Save existing clipboard text into the kill ring before
1111 replacing it.")
1112
1113 (cuss yank-pop-change-selection t
1114 "Update the X selection when rotating the kill ring.")
1115
1116 (cuss x-select-enable-clipboard t)
1117 (cuss x-select-enable-primary t)
1118 (cuss mouse-drag-copy-region t)
1119 #+end_src
1120
1121**** Don’t append the same thing twice to the kill-ring
1122
1123 #+begin_src emacs-lisp
1124 (cuss kill-do-not-save-duplicates t)
1125 #+end_src
1126
1127*** Searching & Replacing
1128
1129**** COMMENT Search with CtrlF
1130
1131 For right now, I’m /just/ using Anzu – I don’t like parts of =isearch= but … CtrlF doesn’t match with that sweet replace flow.
1132
1133 #+begin_src emacs-lisp
1134 (straight-use-package 'ctrlf)
1135 (ctrlf-mode +1)
1136 #+end_src
1137
1138**** Replace with Anzu
1139
1140 #+begin_src emacs-lisp
1141 (straight-use-package 'anzu)
1142 (require 'anzu)
1143
1144 ;; show search count in the modeline
1145 (global-anzu-mode +1)
1146
1147 (cuss anzu-replace-to-string-separator " → "
1148 "What to separate the search from the replacement.")
1149
1150 (global-set-key [remap query-replace] #'anzu-query-replace)
1151 (global-set-key [remap query-replace-regexp] #'anzu-query-replace-regexp)
1152
1153 (define-key isearch-mode-map [remap isearch-query-replace] #'anzu-isearch-query-replace)
1154 (define-key isearch-mode-map [remap isearch-query-replace-regexp] #'anzu-isearch-query-replace-regexp)
1155
1156
1157 #+end_src
1158
1159*** Overwrite mode
1160
1161 #+begin_src emacs-lisp
1162 (defun acdw/overwrite-mode-change-cursor ()
1163 (setq cursor-type (if overwrite-mode t 'bar)))
1164
1165 (add-hook 'overwrite-mode-hook #'acdw/overwrite-mode-change-cursor)
1166
1167 (rm/whitelist-add "Ovwrt")
1168 #+end_src
1169
1170*** The Mark
1171
1172 #+begin_src emacs-lisp
1173 (cuss set-mark-repeat-command-pop t
1174 "Repeat `set-mark-command' with a prefix argument, without
1175 repeatedly entering the prefix argument.")
1176 #+end_src
1177
1178*** Whitespace
1179
1180 #+begin_src emacs-lisp
1181 (cuss whitespace-style
1182 '(empty ;; remove blank lines at the beginning and end of buffers
1183 indentation ;; clean up indentation
1184 space-before-tab ;; fix mixed spaces and tabs
1185 space-after-tab))
1186
1187 (defun acdw/whitespace-cleanup-maybe ()
1188 "Only cleanup whitespace when out-of-focus."
1189 (acdw/when-unfocused #'whitespace-cleanup))
1190
1191 (add-hook 'before-save-hook #'acdw/whitespace-cleanup-maybe)
1192 #+end_src
1193
1194*** Expand region
1195
1196 #+begin_src emacs-lisp
1197 (straight-use-package 'expand-region)
1198
1199 (global-set-key (kbd "C-=") #'er/expand-region)
1200 #+end_src
1201
1202*** Move where I mean
1203
1204 #+begin_src emacs-lisp
1205 (straight-use-package 'mwim)
1206 (require 'mwim)
1207
1208 (cuss mwim-beginning-of-line-function 'beginning-of-visual-line)
1209 (cuss mwim-end-of-line-function 'end-of-visual-line)
1210
1211 (global-set-key (kbd "C-a") #'mwim-beginning)
1212 (global-set-key (kbd "C-e") #'mwim-end)
1213 #+end_src
1214
1215* Programming
1216
1217** Prettify symbols
1218
1219 #+begin_src emacs-lisp
1220 (cuss prettify-symbols-unprettify-at-point 'right-edge
1221 "Unprettify a symbol when inside it or next to it.")
1222
1223 (add-hook 'prog-mode-hook #'prettify-symbols-mode)
1224 #+end_src
1225
1226** Parentheses
1227
1228*** Smart parentheses
1229
1230 #+begin_src emacs-lisp
1231 (straight-use-package 'smartparens)
1232 (require 'smartparens-config)
1233
1234 ;; replace show-paren
1235
1236 (cuss sp-show-pair-delay 0
1237 "Don't delay before showing the pairs.")
1238 (cuss sp-show-pair-from-inside t
1239 "Highlight the enclosing pair when immediately inside.")
1240
1241 (add-hook 'prog-mode-hook #'show-smartparens-mode +1)
1242
1243 ;; enable strict smartparens in prog mode
1244 (add-hook 'prog-mode-hook #'smartparens-strict-mode)
1245 #+end_src
1246
1247** Indent aggressively
1248
1249 #+begin_src emacs-lisp
1250 (straight-use-package 'aggressive-indent)
1251
1252 (global-aggressive-indent-mode +1)
1253 #+end_src
1254
1255** Auto-fill comments only
1256
1257from [[https://www.emacswiki.org/emacs/AutoFillMode][EmacsWiki]].
1258
1259 #+begin_src emacs-lisp
1260 (defun comment-auto-fill ()
1261 (setq-local comment-auto-fill-only-comments t)
1262 (auto-fill-mode 1))
1263
1264 (add-hook 'prog-mode-hook #'comment-auto-fill)
1265 #+end_src
1266
1267** Completion
1268
1269 #+begin_src emacs-lisp
1270 (straight-use-package 'company)
1271
1272 (add-hook 'prog-mode-hook #'company-mode)
1273
1274 (cuss company-idle-delay 0.1
1275 "Show company sooner.")
1276 (cuss company-minimum-prefix-length 3
1277 "Don't try to complete short words.")
1278
1279 (with-eval-after-load 'company
1280 (define-key company-active-map (kbd "C-n")
1281 (lambda () (interactive) (company-complete-common-or-cycle +1)))
1282 (define-key company-active-map (kbd "C-p")
1283 (lambda () (interactive) (company-complete-common-or-cycle -1))))
1284 #+end_src
1285
1286*** Give it a frame and better help
1287
1288 #+begin_src emacs-lisp
1289 (straight-use-package 'company-posframe)
1290
1291 (with-eval-after-load 'company
1292 (company-posframe-mode +1))
1293 #+end_src
1294
1295*** Prescient integration
1296
1297 #+begin_src emacs-lisp
1298 (straight-use-package 'company-prescient)
1299
1300 (add-hook 'company-mode-hook #'company-prescient-mode)
1301 #+end_src
1302
1303** Language-specific packages
1304
1305*** Executable scripts
1306
1307 #+begin_src emacs-lisp
1308 (add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p)
1309 #+end_src
1310
1311*** Emacs lisp
1312
1313 #+begin_src emacs-lisp
1314 (cuss eval-expression-print-length nil
1315 "Don't truncate printed expressions by length.")
1316 (cuss eval-expression-print-level nil
1317 "Don't truncate printed expressions by level.")
1318 #+end_src
1319
1320**** Eros (Evaluation Result OverlayS)
1321
1322 #+begin_src emacs-lisp
1323 (straight-use-package 'eros)
1324
1325 (cuss eros-eval-result-prefix ";; => "
1326 "Prefix displayed before eros overlays.")
1327
1328 (eros-mode +1)
1329 #+end_src
1330
1331**** Indent Elisp like Common Lisp
1332
1333 #+begin_src emacs-lisp
1334 (require 'cl-lib)
1335 (setq lisp-indent-function 'common-lisp-indent-function)
1336 (put 'cl-flet 'common-lisp-indent-function
1337 (get 'flet 'common-lisp-indent-function))
1338 (put 'cl-labels 'common-lisp-indent-function
1339 (get 'labels 'common-lisp-indent-function))
1340 (put 'if 'common-lisp-indent-function 2)
1341 (put 'dotimes-protect 'common-lisp-indent-function
1342 (get 'when 'common-lisp-indent-function))
1343 #+end_src
1344 90
1345*** Janet 91My name and email address.
1346 92
1347 #+begin_src emacs-lisp 93#+begin_src emacs-lisp
1348 (straight-use-package 'janet-mode) 94 (setq user-full-name "Case Duckworth"
1349 (require 'janet-mode) 95 user-mail-address "acdw@acdw.net")
96#+end_src
1350 97
1351 (straight-use-package '(inf-janet 98** Look and feel
1352 :host github
1353 :repo "velkyel/inf-janet"))
1354 99
1355 (add-hook 'janet-mode-hook #'inf-janet-minor-mode) 100*** Cursor
1356 #+end_src
1357 101
1358*** INI 102#+begin_src emacs-lisp
103 ;; Show a vertical bar cursor
104 (cuss cursor-type 'bar)
1359 105
1360 #+begin_src emacs-lisp 106 ;; Hide the cursor in other windows
1361 (straight-use-package 'ini-mode) 107 (cuss cursor-in-non-selected-windows nil)
1362 108
1363 (add-to-list 'auto-mode-alist 109 ;; Don't blink the cursor
1364 '("\\.ini\\'" . ini-mode)) 110 (blink-cursor-mode -1)
1365 #+end_src 111#+end_src
1366 112
1367*** PHP 113*** Dialogs and alerts
1368 114
1369 see also [[https://sasanidas.gitlab.io/f-site/php-development/][this post by Fermin M]], it looks really useful. 115**** Don't use a dialog box
1370 116
1371 #+begin_src emacs-lisp 117#+begin_src emacs-lisp
1372 (straight-use-package 'php-mode) 118 (cuss use-dialog-box nil)
1373 #+end_src 119#+end_src
1374 120
1375* Writing 121**** Yes or no questions
1376 122
1377** Visual fill column 123#+begin_src emacs-lisp
124 (fset 'yes-or-no-p #'y-or-n-p)
125#+end_src
1378 126
1379*** Fix scrolling in margins 127**** The Bell
1380 128
1381 This has to be done /before/ loading the package. It's included in =visual-fill-column=, too, but for some reason isn't loaded there. 129#+begin_src emacs-lisp
130 ;; Don't flash the whole screen on bell
131 (cuss visible-bell nil)
1382 132
1383 #+begin_src emacs-lisp 133 ;; Instead, flash the mode line
1384 (dolist (margin '(right-margin left-margin)) 134 (cuss ring-bell-function #'flash-mode-line)
1385 (dolist (button '(mouse-1 mouse-2 mouse-3))
1386 (global-set-key (vector margin button)
1387 (global-key-binding (vector button)))))
1388 135
1389 (mouse-wheel-mode +1) 136 (defun flash-mode-line ()
137 (invert-face 'mode-line)
138 (run-with-timer 0.2 nil #'invert-face 'mode-line))
139#+end_src
1390 140
1391 (when (bound-and-true-p mouse-wheel-mode) 141t*** Minibuffer
1392 (dolist (margin '(right-margin left-margin))
1393 (dolist (event '(mouse-wheel-down-event
1394 mouse-wheel-up-event
1395 wheel-down
1396 wheel-up
1397 mouse-4
1398 mouse-5))
1399 (global-set-key (vector margin event) #'mwheel-scroll))))
1400 #+end_src
1401 142
1402*** Load the package 143**** Keep the cursor away from the minibuffer prompt
1403 144
1404 #+begin_src emacs-lisp 145#+begin_src emacs-lisp
1405 (straight-use-package 'visual-fill-column) 146 (cuss minibuffer-prompt-properties
147 '(read-only t cursor-intangible t face minibuffer-prompt))
148#+end_src
1406 149
1407 (cuss visual-fill-column-center-text nil 150*** Tabs
1408 "Whether to center the text in the frame.")
1409 151
1410 (cuss fill-column 84 152**** Tab names should be current buffer + a count of windows
1411 "Width of fill-column, and thus, visual-fill-column.")
1412 153
1413 (advice-add 'text-scale-adjust 154#+begin_src emacs-lisp
1414 :after #'visual-fill-column-adjust) 155 (cuss tab-bar-tab-name-function
156 #'tab-bar-tab-name-current-with-count)
157#+end_src
1415 158
1416 (global-visual-fill-column-mode +1) 159**** Only show the tab bar when there's more than one tab
1417 #+end_src
1418 160
1419** Typographical niceties 161For some reason, this doesn't work with multiple frames.
1420 162
1421*** Variable pitch in text-modes 163#+begin_src emacs-lisp
164 (cuss tab-bar-show 1)
165#+end_src
1422 166
1423 #+begin_src emacs-lisp 167*** Frames
1424 (add-hook 'text-mode-hook #'variable-pitch-mode)
1425 #+end_src
1426 168
1427*** Typo mode 169/Frames/ are Emacs's concepts that generally correspond to other
170programs' /windows/ -- that is, they're the boxen on the screen that
171contain the Emacs programmen.
1428 172
1429 #+begin_src emacs-lisp 173**** Initial frame setup
1430 (straight-use-package 'typo) 174:PROPERTIES:
175:header-args: :noweb-ref initial-frame-setup
176:END:
1431 177
1432 (add-hook 'text-mode-hook #'typo-mode) 178***** Tool bar
1433 179
1434 ;; Disable `typo-mode' when inside an Org source block 180#+begin_src emacs-lisp
1435 (with-eval-after-load 'typo 181 (add-to-list 'default-frame-alist
1436 (add-to-list 'typo-disable-electricity-functions 182 '(tool-bar-lines . 0))
1437 #'org-in-src-block-p))
1438 #+end_src
1439 183
1440*** Show =^L= as a horizontal line 184 (tool-bar-mode -1)
185#+end_src
1441 186
1442 #+begin_src emacs-lisp 187***** Menu bar
1443 (straight-use-package 'form-feed)
1444 (global-form-feed-mode +1)
1445 #+end_src
1446 188
1447** Word count 189#+begin_src emacs-lisp
190 (add-to-list 'default-frame-alist
191 '(menu-bar-lines . 0))
1448 192
1449 #+begin_src emacs-lisp 193 (menu-bar-mode -1)
1450 (straight-use-package 'wc-mode) 194#+end_src
1451 195
1452 (add-hook 'text-mode-hook #'wc-mode) 196***** Scroll bars
1453 197
1454 (rm/whitelist-add "WC") 198#+begin_src emacs-lisp
1455 #+end_src 199 (add-to-list 'default-frame-alist
200 '(vertical-scroll-bars . nil)
201 '(horizontal-scroll-bars . nil))
1456 202
1457* Applications 203 (scroll-bar-mode -1)
204 (horizontal-scroll-bar-mode -1)
205#+end_src
1458 206
1459** Web browsing 207**** Frame titles
1460 208
1461 #+begin_src emacs-lisp 209Set the frame title to something more useful than the default: include
1462 (cuss browse-url-browser-function 'browse-url-firefox) 210the current buffer and the current filename.
1463 (cuss browse-url-new-window-flag t
1464 "Always open a new browser window.")
1465 211
1466 ;;(cuss browse-url-generic-program "firefox") 212#+begin_src emacs-lisp
1467 (cuss browse-url-firefox-new-window-is-tab t 213 (cuss frame-title-format
1468 "Or a new tab, in Firefox.") 214 (concat invocation-name "@" (system-name)
215 ": %b %+%+ %f"))
216#+end_src
1469 217
1470 ;; we need to add Firefox to `exec-path' on Windows 218**** Fringes
1471 (when-at :work
1472 (add-to-list 'exec-path "c:/Program Files/Mozilla Firefox"))
1473 #+end_src
1474 219
1475** Dired 220I have grown to love Emacs's little fringes on the side of the
221windows. In fact, I love them so much that I really went overboard
222and have made a custom fringe bitmap.
1476 223
1477*** Basic customization 224***** Indicate empty lines after the end of the buffer
1478 225
1479 #+begin_src emacs-lisp 226#+begin_src emacs-lisp
1480 (defun acdw/setup-dired-mode () 227 (cuss indicate-empty-lines t)
1481 (hl-line-mode) 228#+end_src
1482 (dired-hide-details-mode))
1483 229
1484 ;; highlight the current line in dired. 230***** Indicate the boundaries of the buffer
1485 (add-hook 'dired-mode-hook #'acdw/setup-dired-mode)
1486 231
1487 (cuss dired-recursive-copies 'always 232#+begin_src emacs-lisp
1488 "Always recursively copy.") 233 (cuss indicate-buffer-boundaries 'right)
234#+end_src
1489 235
1490 (cuss dired-recursive-deletes 'always 236***** Indicate continuation lines, but only on the left fringe
1491 "Always recursively delete.")
1492 237
1493 (cuss delete-by-moving-to-trash t) 238#+begin_src emacs-lisp
239 (cuss visual-line-fringe-indicators '(left-curly-arrow nil))
1494 240
1495 (cuss dired-listing-switches "-alh" 241 ;; And make the `left-curly-arrow' indicator less distracting.
1496 "Show (A)lmost all items,
1497 (l)isted out, with (h)uman-readable sizes.")
1498 #+end_src
1499 242
1500*** Expand subtrees 243 (define-fringe-bitmap 'left-curly-arrow
244 [#b11000000
245 #b01100000
246 #b00110000
247 #b00011000])
248#+end_src
1501 249
1502 250*** Windows
1503 #+begin_src emacs-lisp
1504 (straight-use-package 'dired-subtree)
1505 251
1506 (with-eval-after-load 'dired 252*** Buffers
1507 (define-key dired-mode-map "i" #'dired-subtree-toggle))
1508 #+end_src
1509 253
1510*** Collapse singleton directories 254* System-specific
255
256I use both Linux (at home) and Windows (at work). To make Emacs
257easier to use in both systems, I've included various system-specific
258settings and written some ancillary scripts.
259
260** Determine where I am
261
262#+begin_src emacs-lisp
263 (defmacro when-at (conditions &rest commands)
264 "Run COMMANDS when at a specific place.
265
266 CONDITIONS are one of `:work', `:home', or a list beginning with
267 those and other conditions to check. COMMANDS are only run if
268 all CONDITIONS are met."
269 (declare (indent 1))
270 (let ((at-work (memq system-type '(ms-dos windows-nt)))
271 (at-home (memq system-type '(gnu gnu/linux gnu/kfreebsd))))
272 (pcase conditions
273 (:work `(when ',at-work ,@commands))
274 (:home `(when ',at-home ,@commands))
275 (`(:work ,others) `(when (and ',at-work ,others)
276 ,@commands))
277 (`(:home ,others) `(when (and ',at-home ,others)
278 ,@commands)))))
279#+end_src
1511 280
1512 #+begin_src emacs-lisp 281** Linux
1513 (straight-use-package 'dired-collapse)
1514 282
1515 (add-hook 'dired-mode-hook #'dired-collapse-mode) 283*** Settings
1516 #+end_src
1517 284
1518*** Kill dired buffers 285*** Scripts
1519 286
1520 from [[https://github.com/munen/emacs.d/][munen]]. 287**** em
288:PROPERTIES:
289:header-args: :tangle-mode (identity #o755) :tangle bin/em
290:END:
1521 291
1522 #+begin_src emacs-lisp 292Here's a wrapper script that'll start =emacs --daemon= if there isn't
1523 (defun kill-dired-buffers () 293one, and then launch =emacsclient= with the arguments. Install it to
1524 "Kill all open dired buffers." 294your =$PATH= somewhere.
1525 (interactive)
1526 (mapc (lambda (buffer)
1527 (when (eq 'dired-mode (buffer-local-value 'major-mode buffer))
1528 (kill-buffer buffer)))
1529 (buffer-list)))
1530 #+end_src
1531 295
1532** Org mode 296#+begin_src sh :shebang "#!/bin/sh"
297 if ! emacsclient -nc "$@"; then
298 emacs --daemon
299 emacsclient -nc "$@"
300 fi
301#+end_src
1533 302
1534 I’ve put org mode under Applications, as opposed to Writing, because it’s more generally-applicable than that. 303**** emacsclient.desktop
304:PROPERTIES:
305:header-args: :tangle bin/emacsclient.desktop
306:END:
1535 307
1536*** Basics 308I haven't really tested this yet, but it should allow me to open other
309files and things in Emacs. From [[https://www.taingram.org/blog/emacs-client.html][taingram]].
1537 310
1538 #+begin_src emacs-lisp 311#+begin_src conf-desktop
1539 (straight-use-package 'org) 312 [Desktop Entry]
313 Name=Emacs Client
314 GenericName=Text Editor
315 Comment=Edit text
316 MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;
317 Exec=emacsclient -c %f
318 Icon=emacs
319 Type=Application
320 Terminal=false
321 Categories=Utility;TextEditor;
322#+end_src
1540 323
1541 (with-eval-after-load 'org 324** Windows
1542 (require 'org-tempo)
1543 (require 'ox-md)
1544 (define-key org-mode-map (kbd "M-n") #'outline-next-visible-heading)
1545 (define-key org-mode-map (kbd "M-p") #'outline-previous-visible-heading))
1546 325
1547 (cuss org-hide-emphasis-markers t) 326I use Windows at work, where I /also/ don't have Admin rights. So I
1548 (cuss org-fontify-done-headline t) 327kind of fly-by-night there. Much of the ideas and scripts in this
1549 (cuss org-fontify-whole-heading-line t) 328section come from [[https://github.com/termitereform/JunkPile/blob/master/emacs-on-windows.md][termitereform]] on Github.
1550 (cuss org-fontify-quote-and-verse-blocks t)
1551 (cuss org-pretty-entities t)
1552 (cuss org-src-tab-acts-natively t)
1553 (cuss org-src-fontify-natively t)
1554 (cuss org-src-window-setup 'current-window)
1555 (cuss org-confirm-babel-evaluate nil)
1556 (cuss org-directory "~/Org")
1557 (cuss org-ellipsis "…")
1558 (cuss org-catch-invisible-edits 'show)
1559 (cuss org-special-ctrl-a/e t)
1560 (cuss org-special-ctrl-k t)
1561
1562 (cuss org-export-headline-levels 8
1563 "Maximum level of headlines to export /as/ a headline.")
1564 #+end_src
1565
1566**** TODO configure =mwim= to work with Org mode
1567
1568**** Tags
1569
1570 #+begin_src emacs-lisp
1571 (cuss org-tags-column 0
1572 "Show tags directly after the headline.
1573 This is the best-looking option with variable-pitch fonts.")
1574
1575 (cussface
1576 '(org-tag
1577 ((t
1578 (:height 0.8 :weight normal :slant italic :foreground "grey40" :inherit
1579 (variable-pitch))))))
1580 #+end_src
1581
1582***** Align all tags in the buffer on changes
1583
1584 from [[https://github.com/mpereira/.emacs.d#align-all-tags-in-the-buffer-on-tag-changes][mpereira]].
1585
1586 #+begin_src emacs-lisp
1587 (defun acdw/org-align-all-tags ()
1588 "Align all org tags in the buffer."
1589 (interactive)
1590 (when (eq major-mode 'org-mode)
1591 (org-align-tags t)))
1592
1593 (add-hook 'org-after-tags-change-hook #'acdw/org-align-all-tags)
1594 #+end_src
1595
1596**** Source blocks
1597
1598 #+begin_src emacs-lisp
1599 (with-eval-after-load 'org-faces
1600 (set-face-attribute 'org-block-begin-line nil
1601 :height 0.85))
1602 #+end_src
1603
1604**** Prettify
1605
1606 #+begin_src emacs-lisp
1607 (defun acdw/org-mode-prettify ()
1608 "Prettify `org-mode'."
1609 (dolist (cell '(("[ ]" . ?☐) ("[X]" . ?☑) ("[-]" . ?◐)
1610 ("#+BEGIN_SRC" . ?✎) ("#+begin_src" . ?✎)
1611 ("#+END_SRC" . ?■) ("#+end_src" . ?■)))
1612 (add-to-list 'prettify-symbols-alist cell :append))
1613 (prettify-symbols-mode +1))
1614
1615 (add-hook 'org-mode-hook #'acdw/org-mode-prettify)
1616 #+end_src
1617
1618*** General
1619
1620**** [[https://github.com/alphapapa/unpackaged.el#org-return-dwim][Org Return: DWIM]]
1621
1622 #+begin_src emacs-lisp
1623 (defun unpackaged/org-element-descendant-of (type element)
1624 "Return non-nil if ELEMENT is a descendant of TYPE.
1625 TYPE should be an element type, like `item' or `paragraph'.
1626 ELEMENT should be a list like that returned by `org-element-context'."
1627 ;; MAYBE: Use `org-element-lineage'.
1628 (when-let* ((parent (org-element-property :parent element)))
1629 (or (eq type (car parent))
1630 (unpackaged/org-element-descendant-of type parent))))
1631
1632 ;;;###autoload
1633 (defun unpackaged/org-return-dwim (&optional default)
1634 "A helpful replacement for `org-return'. With prefix, call `org-return'.
1635
1636 On headings, move point to position after entry content. In
1637 lists, insert a new item or end the list, with checkbox if
1638 appropriate. In tables, insert a new row or end the table."
1639 ;; Inspired by John Kitchin: http://kitchingroup.cheme.cmu.edu/blog/2017/04/09/A-better-return-in-org-mode/
1640 (interactive "P")
1641 (if default
1642 (org-return)
1643 (cond
1644 ;; Act depending on context around point.
1645
1646 ;; NOTE: I prefer RET to not follow links, but by uncommenting this block, links will be
1647 ;; followed.
1648
1649 ;; ((eq 'link (car (org-element-context)))
1650 ;; ;; Link: Open it.
1651 ;; (org-open-at-point-global))
1652
1653 ((org-at-heading-p)
1654 ;; Heading: Move to position after entry content.
1655 ;; NOTE: This is probably the most interesting feature of this function.
1656 (let ((heading-start (org-entry-beginning-position)))
1657 (goto-char (org-entry-end-position))
1658 (cond ((and (org-at-heading-p)
1659 (= heading-start (org-entry-beginning-position)))
1660 ;; Entry ends on its heading; add newline after
1661 (end-of-line)
1662 (insert "\n\n"))
1663 (t
1664 ;; Entry ends after its heading; back up
1665 (forward-line -1)
1666 (end-of-line)
1667 (when (org-at-heading-p)
1668 ;; At the same heading
1669 (forward-line)
1670 (insert "\n")
1671 (forward-line -1))
1672 ;; FIXME: looking-back is supposed to be called with more arguments.
1673 (while (not (looking-back (rx (repeat 3 (seq (optional blank) "\n"))) nil))
1674 (insert "\n"))
1675 (forward-line -1)))))
1676
1677 ((org-at-item-checkbox-p)
1678 ;; Checkbox: Insert new item with checkbox.
1679 (org-insert-todo-heading nil))
1680
1681 ((org-in-item-p)
1682 ;; Plain list. Yes, this gets a little complicated...
1683 (let ((context (org-element-context)))
1684 (if (or (eq 'plain-list (car context)) ; First item in list
1685 (and (eq 'item (car context))
1686 (not (eq (org-element-property :contents-begin context)
1687 (org-element-property :contents-end context))))
1688 (unpackaged/org-element-descendant-of 'item context)) ; Element in list item, e.g. a link
1689 ;; Non-empty item: Add new item.
1690 (org-insert-item)
1691 ;; Empty item: Close the list.
1692 ;; TODO: Do this with org functions rather than operating on the text. Can't seem to find the right function.
1693 (delete-region (line-beginning-position) (line-end-position))
1694 (insert "\n"))))
1695
1696 ((when (fboundp 'org-inlinetask-in-task-p)
1697 (org-inlinetask-in-task-p))
1698 ;; Inline task: Don't insert a new heading.
1699 (org-return))
1700
1701 ((org-at-table-p)
1702 (cond ((save-excursion
1703 (beginning-of-line)
1704 ;; See `org-table-next-field'.
1705 (cl-loop with end = (line-end-position)
1706 for cell = (org-element-table-cell-parser)
1707 always (equal (org-element-property :contents-begin cell)
1708 (org-element-property :contents-end cell))
1709 while (re-search-forward "|" end t)))
1710 ;; Empty row: end the table.
1711 (delete-region (line-beginning-position) (line-end-position))
1712 (org-return))
1713 (t
1714 ;; Non-empty row: call `org-return'.
1715 (org-return))))
1716 (t
1717 ;; All other cases: call `org-return'.
1718 (org-return)))))
1719
1720 (with-eval-after-load 'org
1721 (define-key org-mode-map (kbd "RET") #'unpackaged/org-return-dwim))
1722 #+end_src
1723
1724**** Insert blank lines around headers
1725
1726 from [[https://github.com/alphapapa/unpackaged.el#ensure-blank-lines-between-headings-and-before-contents][unpackaged.el]].
1727
1728 #+begin_src emacs-lisp
1729 ;;;###autoload
1730 (defun unpackaged/org-fix-blank-lines (&optional prefix)
1731 "Ensure that blank lines exist between headings and between headings and their contents.
1732 With prefix, operate on whole buffer. Ensures that blank lines
1733 exist after each headings's drawers."
1734 (interactive "P")
1735 (org-map-entries (lambda ()
1736 (org-with-wide-buffer
1737 ;; `org-map-entries' narrows the buffer, which prevents us
1738 ;; from seeing newlines before the current heading, so we
1739 ;; do this part widened.
1740 (while (not (looking-back "\n\n" nil))
1741 ;; Insert blank lines before heading.
1742 (insert "\n")))
1743 (let ((end (org-entry-end-position)))
1744 ;; Insert blank lines before entry content
1745 (forward-line)
1746 (while (and (org-at-planning-p)
1747 (< (point) (point-max)))
1748 ;; Skip planning lines
1749 (forward-line))
1750 (while (re-search-forward org-drawer-regexp end t)
1751 ;; Skip drawers. You might think that `org-at-drawer-p'
1752 ;; would suffice, but for some reason it doesn't work
1753 ;; correctly when operating on hidden text. This
1754 ;; works, taken from `org-agenda-get-some-entry-text'.
1755 (re-search-forward "^[ \t]*:END:.*\n?" end t)
1756 (goto-char (match-end 0)))
1757 (unless (or (= (point) (point-max))
1758 (org-at-heading-p)
1759 (looking-at-p "\n"))
1760 (insert "\n"))))
1761 t (if prefix
1762 nil
1763 'tree)))
1764 #+end_src
1765
1766***** Add a before-save-hook
1767
1768 #+begin_src emacs-lisp
1769 (defun cribbed/org-mode-fix-blank-lines ()
1770 (when (eq major-mode 'org-mode)
1771 (let ((current-prefix-arg 4)) ; Emulate C-u
1772 (call-interactively 'unpackaged/org-fix-blank-lines))))
1773
1774 (add-hook 'before-save-hook #'cribbed/org-mode-fix-blank-lines)
1775 #+end_src
1776
1777*** Org Templates (=org-tempo=)
1778
1779 #+begin_src emacs-lisp
1780 (with-eval-after-load 'org
1781 (add-to-list 'org-structure-template-alist
1782 '("el" . "src emacs-lisp")))
1783 #+end_src
1784
1785*** Org Agenda
1786
1787 #+begin_src emacs-lisp
1788 (cuss org-agenda-files
1789 (let ((list))
1790 (dolist (file '(;; add more files to this list
1791 "home.org"
1792 "work.org")
1793 list)
1794 (push (expand-file-name file org-directory) list))))
1795
1796 (define-key acdw/map (kbd "C-a") #'org-agenda)
1797
1798 (cuss org-agenda-span 5
1799 "Show today + N days.")
1800
1801 (cuss org-todo-keywords
1802 '((sequence "RECUR(r)" "TODO(t)" "|" "DONE(d)")
1803 (sequence "APPT(a)")
1804 (sequence "|" "CANCELLED(c)")))
1805
1806 (cuss org-agenda-skip-scheduled-if-done t)
1807 (cuss org-agenda-skip-deadline-if-done t)
1808 (cuss org-deadline-warning-days 0
1809 "Don't warn of an impending deadline.")
1810
1811 (cuss org-log-into-drawer "LOGBOOK"
1812 "Log state changes into the LOGBOOK drawer, instead of after
1813 the headline.")
1814 (cuss org-log-done t
1815 "Save CLOSED timestamp when done.")
1816 #+end_src
1817
1818*** Capture
1819
1820**** Templates
1821
1822 #+begin_src emacs-lisp
1823 (cuss org-default-notes-file (expand-file-name "inbox.org"
1824 org-directory)
1825 "Put unfiled notes in ~/Org/inbox.org.")
1826
1827
1828 (cuss org-capture-templates
1829 '(("w" "Work Todo") ;;; Work stuff
1830 ("ws" "Small Business" entry
1831 (file+headline "work.org" "Small Business")
1832 "* TODO %?
1833 :PROPERTIES:
1834 :Via:
1835 :Note:
1836 :END:
1837 :LOGBOOK:
1838 - State \"TODO\" from \"\" %U
1839 :END:" :empty-lines 1)
1840 ("wc" "Career Center" entry
1841 (file+headline "work.org" "Career Center")
1842 "* TODO %?
1843 :PROPERTIES:
1844 :Via:
1845 :Note:
1846 :END:
1847 :LOGBOOK:
1848 - State \"TODO\" from \"\" %U
1849 :END:" :empty-lines 1)
1850 ("wg" "General" entry
1851 (file+headline "work.org" "General")
1852 "* TODO %?
1853 :PROPERTIES:
1854 :Via:
1855 :Note:
1856 :END:
1857 :LOGBOOK:
1858 - State \"TODO\" from \"\" %U
1859 :END:" :empty-lines 1)
1860
1861 ;;; Regular To-Dos
1862 ("t" "Todo")
1863 ("tt" "Todo" entry
1864 (file+headline "home.org" "TODOs")
1865 "* TODO %?
1866 :PROPERTIES:
1867 :Via:
1868 :Note:
1869 :END:
1870 :LOGBOOK:
1871 - State \"TODO\" from \"\" %U
1872 :END:" :empty-lines 1)
1873
1874 ("td" "Todo with deadline" entry
1875 (file+headline "home.org" "TODOs")
1876 "* TODO %?
1877 DEADLINE: %^t
1878 :PROPERTIES:
1879 :Via:
1880 :Note:
1881 :END:
1882 :LOGBOOK:
1883 - State \"TODO\" from \"\" %U
1884 :END:" :empty-lines 1)
1885
1886
1887 ("g" "Gift idea" entry
1888 (file+headline "home.org" "Gift ideas")
1889 "* %^{Who?}
1890 ,* %^{What?}" :empty-lines 1)
1891
1892 ("d" "Diary entry" entry
1893 (file+olp+datetree "diary.org")
1894 "* %?
1895 Entered on %U
1896
1897 " :empty-lines 1)))
1898 #+end_src
1899
1900**** Keybindings
1901
1902 #+begin_src emacs-lisp
1903 (require 'org-capture)
1904
1905 (with-eval-after-load 'org-capture
1906 (define-key acdw/map (kbd "C-c") #'org-capture))
1907 #+end_src
1908
1909*** Include Org links in source code
1910
1911 #+begin_src emacs-lisp
1912 (straight-use-package '(org-link-minor-mode
1913 :host github
1914 :repo "seanohalpin/org-link-minor-mode"))
1915
1916 ;; enable in elisp buffers
1917 (add-hook 'emacs-lisp-mode-hook #'org-link-minor-mode)
1918 #+end_src
1919
1920** Git
1921
1922 #+begin_src emacs-lisp
1923 (straight-use-package 'magit)
1924
1925 (define-key acdw/map "g" #'magit-status)
1926 #+end_src
1927
1928*** Git file modes
1929
1930 #+begin_src emacs-lisp
1931 (dolist (feat '(gitattributes-mode
1932 gitconfig-mode
1933 gitignore-mode))
1934 (straight-use-package feat)
1935 (require feat))
1936 #+end_src
1937
1938** Beancount mode
1939
1940 #+begin_src emacs-lisp
1941 (straight-use-package '(beancount-mode
1942 :host github
1943 :repo "beancount/beancount-mode"))
1944 (require 'beancount)
1945
1946 (add-to-list 'auto-mode-alist '("\\.beancount\\'" . beancount-mode))
1947
1948 (defun acdw/disable-aggressive-indent ()
1949 "Turn `aggressive-indent-mode' off for a buffer."
1950 (aggressive-indent-mode -1))
1951
1952 (add-hook 'beancount-mode-hook #'outline-minor-mode)
1953 (add-hook 'beancount-mode-hook #'acdw/disable-aggressive-indent)
1954
1955 (define-key beancount-mode-map (kbd "M-n") #'outline-next-visible-heading)
1956 (define-key beancount-mode-map (kbd "M-p") #'outline-previous-visible-heading)
1957 #+end_src
1958
1959*** Company integration with company-ledger
1960
1961 #+begin_src emacs-lisp
1962 (straight-use-package 'company-ledger)
1963
1964 (with-eval-after-load 'company
1965 (add-to-list 'company-backends 'company-ledger))
1966 #+end_src
1967
1968** PDF Tools
1969
1970 I’m only enabling this at home for now, since it requires building stuff.
1971
1972 #+begin_src emacs-lisp
1973 (defun acdw/disable-visual-fill-column-mode ()
1974 "Disable `visual-fill-column-mode'."
1975 (visual-fill-column-mode -1))
1976
1977 (eval-when-compile
1978 (when-at :home
1979 (straight-use-package 'pdf-tools)
1980 (pdf-loader-install)
1981
1982 (add-hook 'pdf-view-mode-hook #'acdw/disable-visual-fill-column-mode)))
1983 #+end_src
1984
1985** E-book tools
1986
1987 #+begin_src emacs-lisp
1988 (straight-use-package 'nov)
1989
1990 (add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode))
1991
1992 (cuss nov-text-width t
1993 "Disable text filling -- `visual-fill-column-mode' takes care
1994 of that.")
1995
1996 (defun acdw/setup-nov-mode ()
1997 (visual-fill-column-mode +1)
1998 (setq visual-fill-column-center-text t)
1999 (text-scale-increase +1))
2000
2001 (add-hook 'nov-mode-hook #'acdw/setup-nov-mode)
2002 #+end_src
2003
2004** Email
2005
2006 #+begin_src emacs-lisp
2007 (when-at (:home (executable-find "mu"))
2008 (add-to-list 'load-path
2009 "/usr/share/emacs/site-lisp/mu4e")
2010 (require 'mu4e)
2011
2012 (cuss mail-user-agent 'mu4e-user-agent)
2013
2014 (cuss mu4e-headers-skip-duplicates t)
2015 (cuss mu4e-view-show-images t)
2016 (cuss mu4e-view-show-addresses t)
2017 (cuss mu4e-compose-format-flowed t)
2018 (cuss mu4e-change-filenames-when-moving t)
2019 (cuss mu4e-attachments-dir "~/Downloads")
2020
2021 (cuss mu4e-maildir "~/.mail/fastmail")
2022 (cuss mu4e-refile-folder "/Archive")
2023 (cuss mu4e-sent-folder "/Sent")
2024 (cuss mu4e-drafts-folder "/Drafts")
2025 (cuss mu4e-trash-folder "/Trash")
2026
2027 (fset 'my-move-to-trash "mTrash")
2028 (define-key mu4e-headers-mode-map (kbd "d") 'my-move-to-trash)
2029 (define-key mu4e-view-mode-map (kbd "d") 'my-move-to-trash)
2030
2031 (cuss message-send-mail-function 'smtpmail-send-it)
2032 (cuss smtpmail-default-smtp-server "smtp.fastmail.com")
2033 (cuss smtpmail-smtp-server "smtp.fastmail.com")
2034 (cuss smtpmail-stream-type 'ssl)
2035 (cuss smtpmail-smtp-service 465)
2036 (cuss smtpmail-local-domain "acdw.net")
2037 (cuss mu4e-compose-signature
2038 "Best,\nCase\n")
2039
2040 (cuss mu4e-completing-read-function 'completing-read)
2041 (cuss message-kill-buffer-on-exit t)
2042 (cuss mu4e-confirm-quit nil)
2043
2044 (cuss mu4e-bookmarks
2045 '((:name "Unread"
2046 :query
2047 "flag:unread AND NOT flag:trashed AND NOT maildir:/Spam"
2048 :key ?u)
2049 (:name "Today"
2050 :query
2051 "date:today..now and not flag:trashed and not maildir:/Spam"
2052 :key ?t)
2053 (:name "This week"
2054 :query
2055 "date:7d..now and not maildir:/Spam and not flag:trashed"
2056 :hide-unread t
2057 :key ?w)))
2058
2059 (cuss mu4e-headers-fields
2060 '((:human-date . 12)
2061 (:flags . 6)
2062 (:mailing-list . 10)
2063 (:from-or-to . 22)
2064 (:thread-subject)))
2065
2066 (cuss mu4e-maildir-shortcuts
2067 `(("/INBOX" . ?i)
2068 (,mu4e-refile-folder . ?a)
2069 (,mu4e-sent-folder . ?s)
2070 (,mu4e-drafts-folder . ?d)
2071 (,mu4e-trash-folder . ?t)))
2072
2073 (cuss mu4e-get-mail-command (cond ((executable-find "mbsync")
2074 "mbsync -a"))
2075 "The command to update mail with.")
2076 (cuss mu4e-update-interval 300
2077 "Update automatically every 5 minutes.")
2078
2079 (defun acdw/setup-mu4e-headers-mode ()
2080 (visual-line-mode -1))
2081 (add-hook 'mu4e-headers-mode #'acdw/setup-mu4e-headers-mode)
2082
2083 (defun acdw/setup-mu4e-view-mode ()
2084 (setq visual-fill-column-center-text t)
2085 (visual-fill-column-mode +1))
2086 (add-hook 'mu4e-view-mode-hook #'acdw/setup-mu4e-view-mode)
2087 (add-hook 'mu4e-compose-mode-hook #'acdw/setup-mu4e-view-mode)
2088
2089 (declare-function mu4e "mu4e")
2090 (mu4e +1))
2091 #+end_src
2092
2093*** Add a keybinding
2094
2095 #+begin_src emacs-lisp
2096 (defun acdw/mu4e-or-warn ()
2097 "If `mu4e' is around, run it, or tell the user it isn't."
2098 (interactive)
2099 (if (featurep 'mu4e)
2100 (mu4e)
2101 (warn "Mu4e isn't available :/.")))
2102
2103 (define-key acdw/map "m" #'acdw/mu4e-or-warn)
2104 #+end_src
2105
2106** Smolweb
2107
2108*** A common function to make a cohesive smolweb experience
2109
2110 #+begin_src emacs-lisp
2111 (defun acdw/setup-smolweb ()
2112 "Configure emacs to view the smolweb."
2113 (setq visual-fill-column-center-text t)
2114 (visual-fill-column-mode +1)
2115 (visual-line-mode +1)
2116 (variable-pitch-mode -1)
2117 (text-scale-increase +1))
2118 #+end_src
2119
2120*** Elpher
2121
2122 #+begin_src emacs-lisp
2123 (straight-use-package '(elpher
2124 :repo "git://thelambdalab.xyz/elpher.git"))
2125
2126 (with-eval-after-load 'no-littering
2127 (cuss elpher-certificate-directory
2128 (no-littering-expand-var-file-name "elpher-certificates/")))
2129
2130 (cuss elpher-ipv4-always t)
2131
2132 (cussface '(elpher-gemini-heading1
2133 ((t (:inherit (modus-theme-heading-1 variable-pitch))))))
2134 (cussface '(elpher-gemini-heading2
2135 ((t (:inherit (modus-theme-heading-2 variable-pitch))))))
2136 (cussface '(elpher-gemini-heading3
2137 ((t (:inherit (modus-theme-heading-3 variable-pitch))))))
2138
2139 (defun elpher:eww-browse-url (original url &optional new-window)
2140 "Handle gemini/gopher links with eww."
2141 (cond ((string-match-p "\\`\\(gemini\\|gopher\\)://" url)
2142 (require 'elpher)
2143 (elpher-go url))
2144 (t (funcall original url new-window))))
2145 (advice-add 'eww-browse-url :around 'elpher:eww-browse-url)
2146
2147 (with-eval-after-load 'elpher
2148 (define-key elpher-mode-map "n" #'elpher-next-link)
2149 (define-key elpher-mode-map "p" #'elpher-prev-link)
2150 (define-key elpher-mode-map "o" #'elpher-follow-current-link)
2151 (define-key elpher-mode-map "G" #'elpher-go-current))
2152
2153 (add-hook 'elpher-mode-hook #'acdw/setup-smolweb)
2154
2155 (autoload 'elpher-bookmarks "elpher")
2156 (define-key acdw/map "e" #'elpher-bookmarks)
2157 #+end_src
2158 329
2159*** Gemini-mode 330*** Settings
2160
2161 #+begin_src emacs-lisp
2162 (straight-use-package '(gemini-mode
2163 :repo "https://git.carcosa.net/jmcbray/gemini.el.git"))
2164 331
2165 (add-to-list 'auto-mode-alist 332#+NAME: w32-settings
2166 '("\\.\\(gemini\\|gmi\\)\\'" . gemini-mode)) 333#+begin_src emacs-lisp
334 (when (memq system-type '(windows-nt ms-dos cygwin))
335 (setq w32-allow-system-shell t ; enable cmd.exe as shell
336 ))
337#+end_src
2167 338
2168 (cussface '(gemini-heading-face-1 339*** Scripts
2169 ((t (:inherit (elpher-gemini-heading1)))))) 340:PROPERTIES:
2170 (cussface '(gemini-heading-face2 341:header-args: :noweb tangle
2171 ((t (:inherit (elpher-gemini-heading2)))))) 342:END:
2172 (cussface '(gemini-heading-face3
2173 ((t (:inherit (elpher-gemini-heading3))))))
2174 343
2175 (add-hook 'gemini-mode-hook #'acdw/setup-smolweb) 344**** Common variables
2176 #+end_src
2177 345
2178*** Gemini-write 346#+NAME: w32-bat-common
347#+begin_src bat
348set HOME=%~dp0..\..\
349set EMACS=%~dp0..\..\..\bin\runemacs.exe
350#+end_src
2179 351
2180 #+begin_src emacs-lisp 352**** Emacs Daemon
2181 (straight-use-package '(gemini-write
2182 :repo "https://tildegit.org/acdw/gemini-write.git"))
2183 (require 'gemini-write)
2184 #+end_src
2185 353
2186** RSS 354Either run this once at startup, or put a shortcut of it in the
355Startup folder:
356=%USERPROFILE%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup=.
2187 357
2188*** elfeed 358#+begin_src bat :tangle "bin/Emacs Daemon.cmd"
359<<w32-bat-common>>
360"%EMACS%" --daemon
361#+end_src
2189 362
2190 #+begin_src emacs-lisp 363**** Emacs Client
2191 (straight-use-package 'elfeed)
2192 (require 'elfeed)
2193 (define-key acdw/map "w" 'elfeed)
2194 364
2195 (cuss elfeed-use-curl (executable-find "curl")) 365This will try to connect to the daemon above. If that fails, it'll
2196 (cuss elfeed-curl-extra-arguments '("--insecure") 366run =runemacs.exe=.
2197 "Extra arguments for curl.")
2198 (elfeed-set-timeout (* 60 3))
2199 367
2200 (defun acdw/setup-elfeed-show () 368*This is the main shortcut for running Emacs.*
2201 (setq visual-fill-column-center-text t)
2202 (visual-fill-column-mode +1))
2203 369
2204 (add-hook 'elfeed-show-mode-hook #'acdw/setup-elfeed-show) 370#+begin_src bat :tangle bin/Emacs.cmd
2205 #+end_src 371<<w32-bat-common>>
372set EMACSC=%~dp0..\..\..\bin\emacsclientw.exe
373"%EMACSC%" -n -c -a "%EMACS%" %*
374#+end_src
2206 375
2207*** elfeed-protocol 376**** Emacs Safe Start
2208 377
2209 #+begin_src emacs-lisp 378This runs Emacs with the factory settings.
2210 (straight-use-package 'elfeed-protocol)
2211 (require 'elfeed-protocol)
2212 379
2213 (cuss elfeed-protocol-ttrss-maxsize 200) 380#+begin_src bat :tangle "bin/Emacs Safe Start.cmd"
381<<w32-bat-common>>
382"%EMACS%" -Q %*
383#+end_src
2214 384
2215 (cuss elfeed-feeds (list 385**** Emacs Debug
2216 (list "ttrss+https://acdw@rss.tildeverse.org"
2217 :use-authinfo t)))
2218 386
2219 ;; Uncomment this line if elfeed is giving problems. 387This runs Emacs with the =--debug-init= option enabled.
2220 ;; (setq elfeed-log-level 'debug) 388
389#+begin_src bat :tangle "bin/Emacs Debug.cmd"
390<<w32-bat-common>>
391"%EMACS%" --debug-init %*
392#+end_src
393* Appendices
394** Emacs's files
2221 395
2222 (elfeed-protocol-enable) 396*** init.el
397:PROPERTIES:
398:header-args: :tangle init.el :comments both
399:END:
400
401The classic Emacs initiation file.
402
403**** Use lexical binding when evaluating =init.el=
404
405#+begin_src emacs-lisp :comments no :noweb tangle
406 ;; init.el -*- lexical-binding: t -*-
407 <<disclaimer>>
408#+end_src
409
410**** Prefer newer files to older files
411
412#+begin_src emacs-lisp
413 (setq load-prefer-newer t)
414#+end_src
415
416**** Load the config
417
418I keep most of my config in =config.el=, which is tangled directly
419from this file. This init just loads that file, either from lisp or
420directly from Org if it's newer.
421
422#+begin_src emacs-lisp
423 (let* (;; Speed up init
424 (gc-cons-threshold most-positive-fixnum)
425 (file-name-handler-alist nil)
426 ;; Config file names
427 (conf (expand-file-name "config"
428 user-emacs-directory))
429 (conf-el (concat conf ".el"))
430 (conf-org (concat conf ".org")))
431 (unless (and (file-newer-than-file-p conf-el conf-org)
432 (load conf 'no-error))
433 ;; A plain require here just loads the older `org'
434 ;; in Emacs' install dir. We need to add the newer
435 ;; one to the `load-path', hopefully that's all.
436 (add-to-list 'load-path (expand-file-name "straight/build/org"
437 user-emacs-directory))
438 (require 'org)
439 (org-babel-load-file conf-org)))
440#+end_src
2223 441
2224 (defun acdw/update-elfeed-protocol-feeds () 442*** early-init.el
2225 "Wrap various (non-working) protocol updaters. 443:PROPERTIES:
2226 I could probably do this in a much more ... better way." 444:header-args: :tangle early-init.el :comments both
2227 (interactive) 445:END:
2228 (elfeed-protocol-ttrss-reinit "https://rss.tildeverse.org"))
2229 446
2230 (with-eval-after-load 'elfeed 447Beginning with 27.1, Emacs also loads an =early-init.el= file, before
2231 (define-key elfeed-search-mode-map "G" #'acdw/update-elfeed-protocol-feeds)) 448the package manager or the UI code. The Info says we should put as
2232 #+end_src 449little as possible in this file, so I only have what I need.
2233 450
2234* System integration 451**** Don't byte-compile this file
2235 452
2236** Linux 453#+begin_src emacs-lisp :comments no :noweb tangle
454 ;; early-init.el -*- no-byte-compile: t; -*-
455 <<disclaimer>>
456#+end_src
2237 457
2238*** Exec path from shell 458**** Disable loading of =package.el=
2239 459
2240 #+begin_src emacs-lisp 460I use =straight.el= instead.
2241 (eval-when-compile
2242 (when-at :home
2243 (straight-use-package 'exec-path-from-shell)
2244 (defvar acdw/exec-path-from-shell-initialized nil
2245 "Stores whether we've initialized or not.")
2246 (unless acdw/exec-path-from-shell-initialized
2247 (exec-path-from-shell-initialize)
2248 (setq acdw/exec-path-from-shell-initialized (current-time)))))
2249 #+end_src
2250 461
2251* Areas for further research 462#+begin_src emacs-lisp
463 (setq package-enable-at-startup nil)
464#+end_src
2252 465
2253- [[https://github.com/larstvei/dot-emacs#key-bindings][Use a minor-mode for keybindings]] 466**** Don't resize the frame when loading fonts
2254 467
2255* Appendices 468#+begin_src emacs-lisp
2256 :PROPERTIES: 469 (setq frame-inhibit-implied-resize t)
2257 :header-args: :tangle LICENSE :comments no 470#+end_src
2258 :END:
2259 471
2260** Emacs' files 472**** Resize frame by pixels
2261 473
2262*** init.el 474#+begin_src emacs-lisp
475 (setq frame-resize-pixelwise t)
476#+end_src
2263 477
2264 #+begin_src emacs-lisp :tangle init.el 478**** Shoe-horned from elsewhere in =config.org=
2265 ;; init.el -*- lexical-binding: t -*-
2266
2267 (setq load-prefer-newer t)
2268
2269 (let* (;; Speed up init
2270 (gc-cons-threshold most-positive-fixnum)
2271 (file-name-handler-alist nil)
2272 ;; Config file names
2273 (conf (expand-file-name "config"
2274 user-emacs-directory))
2275 (conf-el (concat conf ".el"))
2276 (conf-org (concat conf ".org")))
2277 (unless (and (file-newer-than-file-p conf-el conf-org)
2278 (load conf 'no-error))
2279 ;; A plain require here just loads the older `org'
2280 ;; in Emacs' install dir. We need to add the newer
2281 ;; one to the `load-path', hopefully that's all.
2282 (add-to-list 'load-path (expand-file-name "straight/build/org"
2283 user-emacs-directory))
2284 (require 'org)
2285 (org-babel-load-file conf-org)))
2286 #+end_src
2287 479
2288*** early-init.el 480A fundamental tension of literal programming is logical versus
481programmatic ordering. I understand that's a problem it's meant to
482solve but hey, maybe I'm not quite there yet. I feel that having this
483weird shoe-horning of other bits of my config here, in a backwater
484heading in an appendix, isn't quite the future I wanted. But it's
485what I have for now.
2289 486
2290 #+begin_src emacs-lisp :tangle early-init.el 487#+begin_src emacs-lisp :noweb tangle
2291 ;; early-init.el -*- no-byte-compile: t; -*- 488 <<initial-frame-setup>>
2292 489#+end_src
2293 ;; I use `straight.el' instead of `package.el'.
2294 (setq package-enable-at-startup nil)
2295
2296 ;; Don't resize the frame when loading fonts
2297 (setq frame-inhibit-implied-resize t)
2298 ;; Resize frame by pixels
2299 (setq frame-resize-pixelwise t)
2300 #+end_src
2301
2302** Ease tangling and loading of Emacs' init
2303
2304 #+begin_src emacs-lisp :tangle config.el
2305 (defun refresh-emacs (&optional disable-load)
2306 "Tangle `config.org', then byte-compile the resulting files.
2307 Then, load the byte-compilations unless passed with a prefix argument."
2308 (interactive "P")
2309 (let ((config (expand-file-name "config.org" user-emacs-directory)))
2310 (save-mark-and-excursion
2311 (with-current-buffer (find-file-noselect config)
2312 (let ((prog-mode-hook nil))
2313 ;; generate the readme
2314 (when (file-newer-than-file-p config (expand-file-name
2315 "README.md"
2316 user-emacs-directory))
2317 (message "%s" "Exporting README.md...")
2318 (require 'ox-md)
2319 (with-demoted-errors "Problem exporting README.md: %S"
2320 (org-md-export-to-markdown)))
2321 ;; tangle config.org
2322 (when (file-newer-than-file-p config (expand-file-name
2323 "config.el"
2324 user-emacs-directory))
2325 (message "%s" "Tangling config.org...")
2326 (add-to-list 'load-path (expand-file-name "straight/build/org/"
2327 user-emacs-directory))
2328 (require 'org)
2329 (let ((inits (org-babel-tangle)))
2330 ;; byte-compile resulting files
2331 (message "%s" "Byte-compiling...")
2332 (dolist (f inits)
2333 (when (string-match "\\.el\\'" f)
2334 (byte-compile-file f (not disable-load)))))))))))
2335 #+end_src
2336
2337** Ancillary scripts
2338
2339*** emacsdc
2340 :PROPERTIES:
2341 :header-args: :mkdirp yes :tangle-mode (identity #o755)
2342 :END:
2343
2344 Here's a wrapper script that'll start =emacs --daemon= if there isn't
2345 one, and then launch =emacsclient= with the arguments. I'd recommend
2346 installing with either ~ln -s bin/emacsdc $HOME/.local/bin/~, or
2347 adding =$HOME/.local/bin= to your =$PATH=.
2348
2349 #+begin_src sh :tangle bin/emacsdc :shebang "#!/bin/sh"
2350 if ! emacsclient -nc "$@" 2>/dev/null; then
2351 emacs --daemon
2352 emacsclient -nc "$@"
2353 fi
2354 #+end_src
2355
2356*** Emacs.cmd
2357 :PROPERTIES:
2358 :header-args: :mkdirp yes
2359 :END:
2360
2361 Here's a wrapper script that'll run Emacs on Windows, with a
2362 custom =$HOME=. I have mine setup like this: Emacs is downloaded
2363 from [[https://mirrors.tripadvisor.com/gnu/emacs/windows/emacs-27/emacs-27.1-x86_64.zip][the GNU mirror]] and unzipped to =~/Downloads/emacs/=. For
2364 some reason, Emacs by default sets =$HOME= to =%APPDATA%=, which
2365 doesn’t make a lot of sense to me. I change it to
2366 =%USERPROFILE%\Downloads\home=, and then run Emacs with the
2367 supplied arguments.
2368
2369 As far as creating a shortcut to the Desktop, you’ll have to do
2370 that yourself – /apparently/ Windows doesn’t have a built-in
2371 shortcut-creating software >:(.
2372
2373 #+begin_src bat :tangle bin/Emacs.cmd
2374 REM Set variables
2375 set HOME=%USERPROFILE%\Downloads\home
2376 set EMACS=%USERPROFILE%\Downloads\emacs\bin\runemacs.exe
2377
2378 REM Change the directory
2379 chdir %HOME%
2380
2381 REM Run "Quick Mode"
2382 REM "%EMACS%" -Q %*
2383
2384 REM Regular
2385 "%EMACS%" %*
2386 #+end_src
2387
2388*** emacsclient.desktop
2389
2390 from [[https://www.taingram.org/blog/emacs-client.html][taingram]].
2391
2392 #+begin_src conf-desktop
2393 [Desktop Entry]
2394 Name=Emacs Client
2395 GenericName=Text Editor
2396 Comment=Edit text
2397 MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;
2398 Exec=emacsclient -c %f
2399 Icon=emacs
2400 Type=Application
2401 Terminal=false
2402 Categories=Utility;TextEditor;
2403 #+end_src
2404 490
2405** License 491** License
2406 :PROPERTIES: 492:PROPERTIES:
2407 :header-args: :tangle LICENSE :comments no 493:header-args: :tangle LICENSE :comments no
2408 :END: 494:END:
2409 495
2410 Copyright 2020 Case Duckworth <acdw@acdw.net> 496Copyright 2020 Case Duckworth <acdw@acdw.net>
2411 497
2412 This work is free. You can redistribute it and/or modify it under the 498This work is free. You can redistribute it and/or modify it under the
2413 terms of the Do What the Fuck You Want To Public License, Version 2, 499terms of the Do What the Fuck You Want To Public License, Version 2,
2414 as published by Sam Hocevar. See the =LICENSE= file, tangled from the 500as published by Sam Hocevar. See the =LICENSE= file, tangled from the
2415 following source block, for details. 501following source block, for details.
2416 502
2417 #+begin_src text 503#+begin_src text
2418 DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 504 DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2419 505
2420 Version 2, December 2004 506 Version 2, December 2004
2421 507
2422 Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> 508 Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
2423 509
2424 Everyone is permitted to copy and distribute verbatim or modified copies of 510 Everyone is permitted to copy and distribute verbatim or modified copies of
2425 this license document, and changing it is allowed as long as the name is changed. 511 this license document, and changing it is allowed as long as the name is changed.
2426 512
2427 DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 513 DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2428 514
2429 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 515 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
2430 516
2431 0. You just DO WHAT THE FUCK YOU WANT TO. 517 0. You just DO WHAT THE FUCK YOU WANT TO.
2432 #+end_src 518#+end_src
2433 519
2434*** Note on the license 520*** Note on the license
2435 521
2436 It's highly likely that the WTFPL is completely incompatible with the 522It's highly likely that the WTFPL is completely incompatible with the
2437 GPL, for what should be fairly obvious reasons. To that, I say: 523GPL, for what should be fairly obvious reasons. To that, I say:
2438
2439 *SUE ME, RMS!*
2440
2441** TODO Local variables
2442 524
2443 One day, I’m going to add a Local Variables block to the end of this file, which will add an =after-save-hook= to auto-tangle the file after saving. But first I need to research how best to do that asynchronously. So. 525*SUE ME, RMS!*