summary refs log tree commit diff stats
path: root/init.el
diff options
context:
space:
mode:
Diffstat (limited to 'init.el')
-rw-r--r--init.el1030
1 files changed, 9 insertions, 1021 deletions
diff --git a/init.el b/init.el index 94e2a0e..7f6da30 100644 --- a/init.el +++ b/init.el
@@ -1,10 +1,10 @@
1;;; init.el -*- lexical-binding: t; coding: utf-8 -*- 1:;;; init.el -*- lexical-binding: t; coding: utf-8-unix -*-
2;; Copyright (C) 2020-2021 Case Duckworth
3;; 2;;
4;; Author: Case Duckworth <acdw@acdw.net> 3;; Author: Case Duckworth <acdw@acdw.net>
5;; Created: Sometime during Covid-19, 2020 4;; Created: Sometime during Covid-19, 2020
6;; Keywords: configuration 5;; Keywords: configuration
7;; URL https://tildegit.org/acdw/emacs 6;; URL: https://tildegit.org/acdw/emacs
7;; Bankruptcy: 5b
8;; 8;;
9;; This file is NOT part of GNU Emacs. 9;; This file is NOT part of GNU Emacs.
10;; 10;;
@@ -16,1024 +16,12 @@
16;; - Don't hurt yourself. 16;; - Don't hurt yourself.
17;; - Make good choices. 17;; - Make good choices.
18;; 18;;
19;;; Comentary:
20;;
21;;; Research:
22;; (map! :leader (:prefix "w" :desc "Toggle full screen buffer" "f"
23;; #'toggle-maximize-buffer))
24;;
25;;; Code: 19;;; Code:
26 20
27;; User information 21;; Add `acdw.el'
28(setq user-full-name "Case Duckworth" 22(add-to-list 'load-path (expand-file-name "lisp/"
29 user-mail-address "acdw@acdw.net" 23 user-emacs-directory))
30 calendar-location-name "Baton Rouge, LA" 24(require 'acdw)
31 calendar-latitude 30.4
32 calendar-longitude -91.1
33 calendar-date-style 'iso
34 custom-file (expand-file-name "custom.el" acdw/etc-dir))
35
36;; Load newer files first
37(setq-default load-prefer-newer t)
38
39;; No littering
40(use-package no-littering
41 :demand
42 :init (setq no-littering-etc-directory acdw/etc-dir
43 no-littering-var-directory acdw/var-dir))
44
45(defun when-unfocused (func &rest args)
46 "Run FUNC with ARGS iff all frames are out of focus."
47 (when (seq-every-p #'null (mapcar #'frame-focus-state (frame-list)))
48 (apply func args)))
49
50(define-minor-mode acdw/reading-mode
51 "Make reading comfier."
52 :lighter " Read"
53 (if acdw/reading-mode
54 (progn ;; turn on
55 ;; (text-scale-increase +1)
56 (display-fill-column-indicator-mode -1)
57 (setq cursor-type 'hbar)
58 (dolist (func '(visual-fill-column-mode
59 iscroll-mode))
60 (when (fboundp func)
61 (funcall func +1))))
62 (progn ;; turn off
63 ;; (text-scale-increase 0)
64 (setq cursor-type 'bar)
65 (display-fill-column-indicator-mode +1)
66 (dolist (func '(visual-fill-column-mode
67 iscroll-mode))
68 (when (fboundp func)
69 (funcall func -1))))))
70
71(defun hook--read-only-cursor ()
72 (setq cursor-type (if buffer-read-only 'hbar 'bar)))
73(add-hook 'read-only-mode-hook #'hook--read-only-cursor)
74
75;; Dialogs & alerts
76(setq-default use-dialog-box nil) ; Don't use a dialog box
77(fset 'yes-or-no-p #'y-or-n-p)
78
79(defun flash-mode-line ()
80 (ding)
81 (invert-face 'mode-line)
82 (run-with-timer 0.2 nil #'invert-face 'mode-line))
83
84(setq-default visible-bell nil ; Don't use a visible bell
85 ring-bell-function #'flash-mode-line)
86
87(defun hook--gc-when-unfocused ()
88 (when-unfocused #'garbage-collect))
89
90(add-function :after after-focus-change-function
91 #'hook--gc-when-unfocused)
92
93;; Minibuffer
94(setq-default
95 minibuffer-prompt-properties '(read-only t
96 cursor-intangible t
97 face minibuffer-prompt)
98 enable-recursive-minibuffers t
99 file-name-shadow-properties '(invisible t))
100(file-name-shadow-mode +1)
101(minibuffer-depth-indicate-mode +1)
102
103(use-package savehist
104 :straight nil
105 :init
106 (setq-default
107 savehist-file (expand-file-name "history" acdw/var-dir)
108 savehist-additional-variables '(kill-ring search-ring regexp-search-ring)
109 history-length t
110 history-delete-duplicates t
111 savehist-autosave-interval 60)
112 :config (savehist-mode +1))
113
114;; Backups
115(setq-default backup-by-copying t
116 delete-old-versions -1 ; Don't delete old versions
117 version-control t ; Make numeric backups
118 vc-make-backup-files t ; Backup version-controlled files
119 )
120
121(let ((dir (expand-file-name "backup" acdw/var-dir)))
122 (make-directory dir 'parents)
123 (setq-default backup-directory-alist
124 `((".*" . ,dir))))
125
126;; Lockfiles
127(setq-default create-lockfiles nil) ; Are these necessary?
128
129;; Autosaves
130(setq auto-save-default nil ; Don't use `auto-save' system
131 )
132(use-package super-save
133 :defer 5 ; This package can wait
134 :init
135 (setq-default
136 super-save-remote-files nil ; Don't save remote files
137 super-save-exclude '(".gpg") ; Wouldn't work anyway
138 super-save-auto-save-when-idle t)
139 :config
140 (super-save-mode +1))
141
142;; Auto-revert
143(global-auto-revert-mode +1) ; Automatically revert a file
144 ; to its on-disk contents
145
146(use-package saveplace
147 :straight nil
148 :init
149 (setq-default
150 save-place-file (expand-file-name "places" acdw/var-dir)
151 save-place-forget-unreadable-files (eq acdw/system :home))
152 :config (save-place-mode +1))
153
154(use-package recentf
155 :straight nil
156 :init
157 (setq recentf-save-file (expand-file-name "recentf" acdw/var-dir)
158 recentf-max-menu-items 100
159 recentf-max-saved-items nil
160 recentf-auto-cleanup 'never)
161 (defun maybe-save-recentf ()
162 "Save `recentf-file' every five minutes, but only when out of focus."
163 (defvar recentf--last-save (time-convert nil 'integer)
164 "When we last saved the `recentf-save-list'.")
165
166 (when (> (time-convert (time-since recentf--last-save) 'integer)
167 (* 60 5))
168 (setq-default recentf--last-save (time-convert nil 'integer))
169 (when-unfocused #'recentf-save-list)))
170 :config
171 (recentf-mode +1)
172 (add-to-list 'recentf-exclude acdw/var-dir)
173 (add-to-list 'recentf-exclude acdw/etc-dir)
174 (add-function :after after-focus-change-function
175 #'maybe-save-recentf))
176
177
178;; Uniquify
179(use-package uniquify
180 :straight nil
181 :init
182 (setq-default
183 uniquify-buffer-name-style 'forward ; bubble 'up' the directory tree
184 uniquify-separator "/" ; separate path elements
185 uniquify-after-kill-buffer-p t ; hook into buffer kills
186 uniquify-ignore-buffers-re "^\\*" ; don't worry about special buffers
187 ))
188
189;; Scratch
190(setq-default
191 inhibit-startup-screen t ; Don't show the splash screen
192 initial-buffer-choice t ; Start on *scratch*
193 initial-scratch-message
194 (concat ";; Howdy, "
195 (nth 0 (split-string user-full-name)) "!"
196 " Welcome to GNU Emacs.\n\n"))
197
198(defun immortal-scratch ()
199 "Don't kill *scratch* when asked to by `kill-buffer'."
200 (if (not (eq (current-buffer) (get-buffer "*scratch*")))
201 t
202 (bury-buffer)
203 nil))
204(add-hook 'kill-buffer-query-functions #'immortal-scratch)
205
206;; Easier buffer-killing
207(defun kill-a-buffer (&optional prefix)
208 "Kill a buffer and its window, prompting only on unsaved changes.
209
210`kill-a-buffer' uses the PREFIX argument to determine which buffer(s) to kill:
2110 => Kill THIS buffer & window
2124 (C-u) => Kill OTHER buffer & window
21316 (C-u C-u) => Run the default `kill-buffer'."
214 (interactive "P")
215 (pcase (or (car prefix) 0)
216 (0 (kill-current-buffer)
217 (unless (one-window-p) (delete-window)))
218 (4 (other-window 1)
219 (kill-current-buffer)
220 (unless (one-window-p) (delete-window)))
221 (16 (let ((current-prefix-arg nil))
222 (kill-buffer)))))
223
224(bind-key "C-x k" #'kill-a-buffer)
225
226;; UTF-8 with LF line endings
227(set-charset-priority 'unicode)
228(set-language-environment "UTF-8")
229
230(prefer-coding-system 'utf-8-unix)
231(set-default-coding-systems 'utf-8-unix)
232(set-terminal-coding-system 'utf-8-unix)
233(set-keyboard-coding-system 'utf-8-unix)
234(set-selection-coding-system 'utf-8-unix)
235
236(setq-default
237 locale-coding-system 'utf-8-unix
238 coding-system-for-read 'utf-8-unix
239 coding-system-for-write 'utf-8-unix
240 buffer-file-coding-system 'utf-8-unix
241
242 org-export-coding-system 'utf-8-unix
243 org-html-coding-system 'utf-8-unix ; doesn't take from above
244
245 default-process-coding-system '(utf-8-unix . utf-8-unix)
246 x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING))
247
248(defun ewiki/no-junk-please-were-unixish ()
249 "Convert line endings to UNIX, dammit."
250 (let ((coding-str (symbol-name buffer-file-coding-system)))
251 (when (string-match "-\\(?:dos\\|mac\\)$" coding-str)
252 (set-buffer-file-coding-system 'unix))))
253
254(add-hook 'find-file-hook #'ewiki/no-junk-please-were-unixish)
255(add-hook 'before-save-hook #'ewiki/no-junk-please-were-unixish)
256
257;; Cursor
258(setq-default cursor-type 'bar
259 cursor-in-non-selected-windows nil)
260(blink-cursor-mode 0)
261
262;; Filling text
263(setq-default fill-column 80)
264(global-display-fill-column-indicator-mode +1)
265
266(bind-key "C-x f" #'find-file) ; I don't set `fill-column', ever
267
268(setq-default comment-auto-fill-only-comments t)
269;; Enable `auto-fill-mode' everywhere
270(add-hook 'text-mode-hook #'auto-fill-mode)
271(add-hook 'prog-mode-hook #'auto-fill-mode)
272;; Also enable `visual-line-mode' everywhere
273(global-visual-line-mode +1)
274;; "Fix" `visual-line-mode' in `org-mode'
275(defun hook--visual-line-fix-org-keys ()
276 (when (derived-mode-p 'org-mode)
277 (local-set-key (kbd "C-a") #'org-beginning-of-line)
278 (local-set-key (kbd "C-e") #'org-end-of-line)
279 (local-set-key (kbd "C-k") #'org-kill-line)))
280(add-hook 'visual-line-mode-hook #'hook--visual-line-fix-org-keys)
281
282(dolist (margin '(right-margin left-margin))
283 (dolist (button '(mouse-1 mouse-2 mouse-3))
284 (global-set-key (vector margin button)
285 (global-key-binding (vector button)))))
286
287(mouse-wheel-mode +1)
288
289(when (bound-and-true-p mouse-wheel-mode)
290 (dolist (margin '(right-margin left-margin))
291 (dolist (event '(mouse-wheel-down-event
292 mouse-wheel-up-event
293 wheel-down
294 wheel-up
295 mouse-4
296 mouse-5))
297 (global-set-key (vector margin event) #'mwheel-scroll))))
298
299(use-package visual-fill-column
300 :init (setq-default visual-fill-column-center-text t)
301 (add-hook 'visual-fill-column-mode-hook #'visual-line-mode)
302 :config
303 (advice-add 'text-scale-adjust :after #'visual-fill-column-adjust))
304
305(when (fboundp 'global-so-long-mode)
306 (global-so-long-mode +1))
307
308;; Whitespace
309(setq-default whitespace-style '(empty ; remove blank lines at buffer edges
310 indentation ; clean up indentation
311 ;; mixed tabs & spaces
312 space-before-tab
313 space-after-tab))
314(add-hook 'before-save-hook #'whitespace-cleanup)
315
316(setq-default indent-tabs-mode t
317 tab-width 8)
318
319(use-package smart-tabs-mode
320 :config
321 (smart-tabs-insinuate 'c 'c++ 'java 'javascript 'cperl 'python 'ruby 'nxml))
322
323;; Window layouts
324(setq-default
325 split-width-threshold 100 ; minimum width for window splits
326 split-height-threshold 50 ; minimum height for window splits
327 display-buffer-alist ; how to display buffers
328 '((".*" . (display-buffer-reuse-window display-buffer-same-window)))
329 display-buffer-reuse-frames t ; allow reuse of frames
330 even-window-sizes nil ; avoid resizing windows to even them
331 help-window-select t ; select *Help* window when opened
332 )
333
334(defun vsplit-other-window ()
335 "Split the window vertically and switch to the new window."
336 (interactive)
337 (split-window-vertically)
338 (other-window 1 nil))
339
340(defun hsplit-other-window ()
341 "Split the window horizontally and switch to the new window."
342 (interactive)
343 (split-window-horizontally)
344 (other-window 1 nil))
345
346(bind-key "C-x 2" #'vsplit-other-window)
347(bind-key "C-x 3" #'hsplit-other-window)
348
349;; Theming
350
351(use-package form-feed
352 :demand
353 :config (global-form-feed-mode +1))
354
355(use-package modus-themes
356 :straight (:host gitlab :repo "protesilaos/modus-themes")
357 :demand
358 :init
359 (setq-default modus-themes-slanted-constructs t
360 modus-themes-bold-constructs t
361 modus-themes-region 'bg-only
362 modus-themes-org-blocks 'grayscale
363 modus-themes-headings '((1 . section)
364 (t . no-color))
365 modus-themes-scale-headings nil
366 modus-themes-mode-line nil)
367 :custom-face
368 (modus-theme-heading-1
369 ((t (:inherit (modus-theme-heading-1 fixed-pitch bold)))))
370 (modus-theme-heading-2
371 ((t (:inherit (modus-theme-heading-2 fixed-pitch bold)))))
372 (modus-theme-heading-3
373 ((t (:inherit (modus-theme-heading-3 fixed-pitch bold)))))
374 (modus-theme-heading-4
375 ((t (:inherit (modus-theme-heading-4 fixed-pitch bold)))))
376 (modus-theme-heading-5
377 ((t (:inherit (modus-theme-heading-5 fixed-pitch bold)))))
378 (modus-theme-heading-6
379 ((t (:inherit (modus-theme-heading-6 fixed-pitch bold)))))
380 (modus-theme-heading-7
381 ((t (:inherit (modus-theme-heading-7 fixed-pitch bold)))))
382 (modus-theme-heading-8
383 ((t (:inherit (modus-theme-heading-8 fixed-pitch bold))))))
384
385;; Change themes based on time of day
386
387(defun acdw/run-with-sun (sunrise-command sunset-command)
388 "Run commands at sunrise and sunset."
389 (let* ((times-regex (rx (* nonl)
390 (: (any ?s ?S) "unrise") " "
391 (group (repeat 1 2 digit) ":"
392 (repeat 1 2 digit)
393 (: (any ?a ?A ?p ?P) (any ?m ?M)))
394 (* nonl)
395 (: (any ?s ?S) "unset") " "
396 (group (repeat 1 2 digit) ":"
397 (repeat 1 2 digit)
398 (: (any ?a ?A ?p ?P) (any ?m ?M)))
399 (* nonl)))
400 (ss (sunrise-sunset))
401 (_m (string-match times-regex ss))
402 (sunrise-time (match-string 1 ss))
403 (sunset-time (match-string 2 ss)))
404 (run-at-time sunrise-time (* 60 60 24) sunrise-command)
405 (run-at-time sunset-time (* 60 60 24) sunset-command)
406 (run-at-time "0:00" (* 60 60 24) sunset-command)))
407
408(acdw/run-with-sun #'modus-themes-load-operandi
409 #'modus-themes-load-vivendi)
410
411(use-package minions
412 :config (minions-mode +1))
413
414(use-package which-key
415 :config (which-key-mode +1))
416
417(delete-selection-mode +1)
418
419(setq-default
420 save-interprogram-paste-before-kill t ; save existing text before replacing
421 yank-pop-change-selection t ; update X selection when rotating ring
422 x-select-enable-clipboard t ; Enable X clipboards
423 x-select-enable-primary t
424 mouse-drag-copy-region t ; Copy a region when mouse-selected
425 kill-do-not-save-duplicates t ; Don't append the same thing twice
426 )
427
428(use-package smartscan
429 :config (global-smartscan-mode +1))
430
431(when (fboundp 'global-goto-address-mode)
432 (global-goto-address-mode +1))
433
434(use-package flyspell
435 :init
436 (setenv "LANG" "en_US")
437 (setq-default ispell-program-name "hunspell"
438 ispell-dictionary "en_US"
439 ispell-personal-dictionary "~/.hunspell_personal")
440 :hook
441 (text-mode . flyspell-mode)
442 (prog-mode . flyspell-prog-mode)
443 :config
444 (ispell-set-spellchecker-params)
445 (unless (file-exists-p ispell-personal-dictionary)
446 (write-region "" nil ispell-personal-dictionary nil 0)))
447
448(use-package flyspell-correct
449 :after flyspell
450 :bind (:map flyspell-mode-map
451 ("C-;" . flyspell-correct-wrapper)))
452
453(setq-default show-paren-delay 0
454 show-paren-style 'mixed
455 show-paren-when-point-inside-paren t
456 show-paren-when-point-in-periphery t)
457(show-paren-mode +1)
458
459(add-hook 'prog-mode-hook #'electric-pair-local-mode)
460
461(setq-default prettify-symbols-unprettify-at-point 'right-edge)
462(add-hook 'prog-mode-hook #'prettify-symbols-mode)
463
464(add-hook 'after-save-hook
465 #'executable-make-buffer-file-executable-if-script-p)
466
467(setq-default compilation-ask-about-save nil ; just save the buffer
468 compilation-always-kill t ; kill the processes without asking
469 compilation-scroll-output 'first-error)
470
471(use-package reformatter
472 :demand)
473
474(setq-default smie-indent-basic 8)
475
476;; Shell scripts
477(setq-default sh-basic-offset 8
478 ;; try to indent like shfmt
479 sh-indent-after-case 0
480 sh-indent-for-case-alt '+
481 sh-indent-for-case-label 0)
482
483(use-package flymake-shellcheck
484 :when (executable-find "shellcheck")
485 :hook (sh-mode . flymake-shellcheck-load))
486
487(when (executable-find "shfmt")
488 (reformatter-define sh-format
489 :program "shfmt"
490 :lighter "Shfmt")
491 (add-hook 'sh-mode-hook #'sh-format-on-save-mode))
492
493(bind-key "M-/" #'hippie-expand)
494
495;; Tabs
496(setq-default
497 tab-bar-show 1 ; show the tab bar when more than one
498 tab-bar-new-tab-choice "*scratch*" ; what to show on a new tab
499 tab-bar-tab-name-function ; how to name a new tab
500 #'tab-bar-tab-name-current-with-count
501 tab-bar-history-limit 25 ; how many tabs to save in history
502 )
503
504(tab-bar-history-mode +1)
505
506;; Smart hungry delete
507(use-package smart-hungry-delete
508 :defer nil
509 :bind (("<backspace>" . smart-hungry-delete-backward-char)
510 ("C-d" . smart-hungry-delete-forward-char))
511 :config (smart-hungry-delete-add-default-hooks))
512
513;; Enable all commands
514(setq-default disabled-command-function nil)
515
516;; Magit
517(use-package magit
518 :bind ("C-x g" . magit-status)
519 :init (when (eq acdw/system :work)
520 (setenv "GIT_ASKPASS" "git-gui--askpass"))
521 (defun magit-display-buffer-same-window (buffer)
522 "Show `magit' in the same buffer, like god intended."
523 (display-buffer buffer '(display-buffer-same-window)))
524 (setq magit-display-buffer-function 'magit-display-buffer-same-window
525 magit-popup-display-buffer-action '((display-buffer-same-window))))
526(use-package forge
527 :after magit)
528(use-package gitattributes-mode
529 :mode "\\.gitattributes\\'")
530(use-package gitconfig-mode
531 :mode "\\.gitconfig\\'")
532(use-package gitignore-mode
533 :mode "\\.gitignore\\'")
534
535;; crux
536(use-package crux
537 :straight (:host github :repo "bbatsov/crux")
538 :bind
539 ("M-o" . crux-other-window-or-switch-buffer)
540 :config
541 (crux-with-region-or-line kill-ring-save)
542 (crux-with-region-or-line kill-region)
543 (crux-with-region-or-line comment-or-uncomment-region))
544
545;; Completion and... stuff
546(setq-default
547 completion-ignore-case t
548 read-buffer-completion-ignore-case t
549 read-file-name-completion-ignore-case t
550 minibuffer-eldef-shorten-default t)
551
552(minibuffer-electric-default-mode +1)
553
554(use-package icomplete-vertical
555 :demand
556 :init
557 (setq-default
558 icomplete-delay-completions-threshold 0
559 icomplete-max-delay-chars 0
560 icomplete-compute-delay 0
561 icomplete-show-matches-on-no-input t
562 icomplete-hide-common-prefix nil
563 icomplete-with-completion-tables t
564 icomplete-in-buffer t)
565 :bind (:map icomplete-minibuffer-map
566 ("<down>" . icomplete-forward-completions)
567 ("C-n" . icomplete-forward-completions)
568 ("<up>" . icomplete-backward-completions)
569 ("C-p" . icomplete-backward-completions)
570 ("C-v" . icomplete-vertical-toggle))
571 :config
572 (fido-mode -1)
573 (icomplete-mode +1)
574 (icomplete-vertical-mode +1))
575
576(use-package orderless
577 :after icomplete
578 :init (setq-default completion-styles '(orderless)))
579
580(use-package marginalia
581 :after icomplete
582 :init (setq-default marginalia-annotators
583 '(marginalia-annotators-heavy
584 marginalia-annotators-light))
585 :config (marginalia-mode +1))
586
587(use-package consult
588 :after icomplete
589 :bind (;; C-c bindings (mode-specific-map)
590 ("C-c h" . consult-history)
591 ("C-c m" . consult-mode-command)
592 ("C-c b" . consult-bookmark)
593 ("C-c k" . consult-kmacro)
594 ;; C-x bindings (ctl-x-map)
595 ("C-x M-:" . consult-complex-command)
596 ("C-x b" . consult-buffer)
597 ("C-x 4 b" . consult-buffer-other-window)
598 ("C-x 5 b" . consult-buffer-other-frame)
599 ;; Custom M-# bindings for fast register access
600 ("M-#" . consult-register-load)
601 ("M-'" . consult-register-store)
602 ("C-M-#" . consult-register)
603 ;; Other custom bindings
604 ("M-y" . consult-yank-pop)
605 ("<help> a" . consult-apropos)
606 ;; M-g bindings (goto-map)
607 ("M-g e" . consult-compile-error)
608 ("M-g g" . consult-goto-line)
609 ("M-g M-g" . consult-goto-line)
610 ("M-g o" . consult-outline)
611 ("M-g m" . consult-mark)
612 ("M-g k" . consult-global-mark)
613 ("M-g i" . consult-imenu)
614 ("M-g I" . consult-project-imenu)
615 ;; M-s bindings (search-map)
616 ("M-s f" . consult-find)
617 ("M-s L" . consult-locate)
618 ("M-s g" . consult-grep)
619 ("M-s G" . consult-git-grep)
620 ("M-s r" . consult-ripgrep)
621 ("M-s l" . consult-line)
622 ("M-s m" . consult-multi-occur)
623 ("M-s k" . consult-keep-lines)
624 ("M-s u" . consult-focus-lines)
625 ;; Isearch integration
626 ("M-s e" . consult-isearch)
627 :map isearch-mode-map
628 ("M-e" . consult-isearch)
629 ("M-s e" . consult-isearch)
630 ("M-s l" . consult-line))
631 :init
632 (setq register-preview-delay 0
633 register-preview-function #'consult-register-format)
634 (advice-add #'register-preview :override #'consult-register-window)
635 (setq xref-show-xrefs-function #'consult-xref
636 xref-show-definitions-function #'consult-xref)
637 :config
638 ;; (setq consult-preview-key 'any)
639 ;; (setq consult-preview-key (kbd "M-p"))
640 (setq consult-narrow-key "<"))
641
642;; Language: Emacs-Lisp
643(setq-default eval-expression-print-length nil ; don't limit print length
644 eval-expression-print-level nil
645 )
646;; indent like common lisp
647(require 'cl-lib)
648(setq-default lisp-indent-function #'common-lisp-indent-function)
649(put 'cl-flet 'common-lisp-indent-function
650 (get 'flet 'common-lisp-indent-function))
651(put 'cl-labels 'common-lisp-indent-function
652 (get 'labels 'common-lisp-indent-function))
653(put 'if 'common-lisp-indent-function 2)
654(put 'dotimes-protect 'common-lisp-indent-function
655 (get 'when 'common-lisp-indent-function))
656;; eldoc mode
657(use-package eldoc
658 :straight nil
659 :init (setq-default eldoc-echo-area-display-truncation-message nil
660 eldoc-idle-delay 0))
661
662;; Dired
663(use-package dired
664 :straight nil
665 :init
666 (setq-default dired-recursive-copies 'always
667 dired-recursive-deletes 'always
668 delete-by-moving-to-trash t
669 dired-listing-switches "-AFgho --group-directories-first"
670 dired-auto-revert-buffer t
671 dired-dwim-target t)
672 :bind ("C-x C-j" . dired-jump))
673(defun hook--dired-mode ()
674 (hl-line-mode +1)
675 (dired-hide-details-mode +1))
676(add-hook 'dired-mode-hook #'hook--dired-mode)
677
678(use-package dired-subtree
679 :bind (:map dired-mode-map
680 ("i" . dired-subtree-toggle)))
681
682(use-package dired-collapse
683 :hook dired-mode)
684
685(use-package 0x0
686 :straight (:repo "https://git.sr.ht/~zge/nullpointer-emacs")
687 :init (setq 0x0-default-service 'ttm)
688 :commands (0x0-upload
689 0x0-upload-file
690 0x0-upload-string
691 0x0-upload-kill-ring
692 0x0-popup))
693
694(use-package elpher
695 :straight (:repo "git://thelambdalab.xyz/elpher.git")
696 :commands (elpher elpher-bookmarks)
697 :init (setq elpher-ipv4-always t
698 elpher-certificate-directory
699 (expand-file-name "elpher-certificates/"
700 acdw/var-dir))
701 (add-hook 'elpher-mode-hook #'acdw/reading-mode)
702 :custom-face
703 (elpher-gemini-heading1
704 ((t (:inherit (modus-theme-heading-1)
705 :height 1.0))))
706 (elpher-gemini-heading2
707 ((t (:inherit (modus-theme-heading-2)
708 :height 1.0))))
709 (elpher-gemini-heading3
710 ((t (:inherit (modus-theme-heading-3)
711 :height 1.0))))
712 :bind (:map elpher-mode-map
713 ("n" . elpher-next-link)
714 ("p" . elpher-prev-link)
715 ("o" . elpher-follow-current-link)
716 ("G" . elpher-go-current)))
717
718(use-package gemini-write
719 :straight (:repo
720 "https://alexschroeder.ch/cgit/gemini-write"
721 :fork (:repo "https://tildegit.org/acdw/gemini-write"
722 :branch "main"))
723 :after elpher)
724
725(use-package gemini-mode
726 :straight (:repo "https://git.carcosa.net/jmcbray/gemini.el.git")
727 :mode "\\.\\(gemini\\|gmi\\)\\'"
728 :custom-face
729 (gemini-heading-face-1
730 ((t (:inherit (elpher-gemini-heading1)))))
731 (gemini-heading-face2
732 ((t (:inherit (elpher-gemini-heading2)))))
733 (gemini-heading-face3
734 ((t (:inherit (elpher-gemini-heading3))))))
735
736(use-package nov
737 :init (setq nov-text-width fill-column)
738 (add-hook 'nov-mode-hook #'acdw/reading-mode)
739 :mode ("\\.epub\\'" . nov-mode))
740
741(use-package undo-fu
742 :bind (("C-/" . undo-fu-only-undo)
743 ("C-?" . undo-fu-only-redo)))
744(use-package undo-fu-session
745 :init (setq undo-fu-session-directory (expand-file-name "undo/"
746 acdw/var-dir)
747 undo-fu-session-incompatible-files
748 '("/COMMIT_EDITMSG\\'" "/git-rebase-todo\\'"))
749 :config (global-undo-fu-session-mode))
750
751(setq-default set-mark-repeat-command-pop t) ; repeat mark pops w/o prefix
752
753(use-package expand-region
754 :bind ("C-=" . er/expand-region))
755
756(use-package goggles
757 :hook ((text-mode prog-mode) . goggles-mode))
758
759(use-package isearch
760 :straight nil)
761
762(use-package anzu
763 :init (setq search-default-mode t)
764 :bind (([remap query-replace] . anzu-query-replace-regexp)
765 ([remap query-replace-regexp] . anzu-query-replace)
766 :map isearch-mode-map
767 ([remap isearch-query-replace] . anzu-isearch-query-replace)
768 ([remap isearch-query-replace-regexp] .
769 anzu-isearch-query-replace-regexp)))
770
771(use-package iscroll
772 :hook (text-mode . iscroll-mode))
773
774(use-package ibuffer
775 :straight nil
776 :bind ([remap list-buffers] . #'ibuffer)
777 ;; from http://martinowen.net/blog/2010/02/03/tips-for-emacs-ibuffer.html
778 :init
779 (setq ibuffer-saved-filter-groups ; this could still be improved
780 '(("home"
781 ("emacs-config" (or (filename . ".emacs.d")
782 (filename . "etc/emacs")))
783 ("Org" (or (mode . org-mode)
784 (filename . "OrgMode")))
785 ("Dired" (mode . dired-mode))
786 ("Magit" (name . "magit"))
787 ("Help" (or (name . "\*Help\*")
788 (name . "\*Apropos\*")
789 (name . "\*info\*")))))
790 ibuffer-expert t ; don't ask if i wanna kill unmodifieds
791 ibuffer-show-empty-filter-groups nil
792 )
793 (defun hook--ibuffer-setup ()
794 (ibuffer-auto-mode +1)
795 (ibuffer-switch-to-saved-filter-groups "home"))
796 (add-hook 'ibuffer-mode-hook #'hook--ibuffer-setup))
797
798(setq-default
799 browse-url-browser-function 'browse-url-firefox
800 browse-url-new-window-flag t
801 browse-url-firefox-new-window-is-tab t
802 shr-max-width fill-column
803 shr-width fill-column)
804
805(when (eq acdw/system :work)
806 (add-to-list 'exec-path "C:/Program Files/Mozilla Firefox"))
807
808(bind-key [remap just-one-space] #'cycle-spacing)
809
810;; Org mode
811;; org-return-dwim (unpacakged)
812(defun unpackaged/org-element-descendant-of (type element)
813 "Return non-nil if ELEMENT is a descendant of TYPE.
814 TYPE should be an element type, like `item' or `paragraph'.
815 ELEMENT should be a list like that returned by `org-element-context'."
816 ;; MAYBE: Use `org-element-lineage'.
817 (when-let* ((parent (org-element-property :parent element)))
818 (or (eq type (car parent))
819 (unpackaged/org-element-descendant-of type parent))))
820
821(defun unpackaged/org-return-dwim (&optional default)
822 "A helpful replacement for `org-return'. With prefix, call `org-return'.
823
824 On headings, move point to position after entry content. In
825 lists, insert a new item or end the list, with checkbox if
826 appropriate. In tables, insert a new row or end the table."
827 ;; Inspired by John Kitchin:
828 ;; http://kitchingroup.cheme.cmu.edu/blog/2017/04/09/A-better-return-in-org-mode/
829 (interactive "P")
830 (if default
831 (org-return)
832 (cond
833 ;; Act depending on context around point.
834
835 ;; NOTE: I prefer RET to not follow links, but by uncommenting
836 ;; this block, links will be followed.
837 ;; FURTHER NOTE: Ideally, I would follow links unless point
838 ;; /appeared/ to be at the end of the line (even if it's still
839 ;; inside the link) -- when it would do `org-return'. That
840 ;; would take some /doing/, however.
841
842 ;; ((eq 'link (car (org-element-context)))
843 ;; ;; Link: Open it.
844 ;; (org-open-at-point-global))
845
846 ((org-at-heading-p)
847 ;; Heading: Move to position after entry content.
848 ;; NOTE: This is probably the most interesting feature of this function.
849 (let ((heading-start (org-entry-beginning-position)))
850 (goto-char (org-entry-end-position))
851 (cond ((and (org-at-heading-p)
852 (= heading-start (org-entry-beginning-position)))
853 ;; Entry ends on its heading; add newline after
854 (end-of-line)
855 (insert "\n\n"))
856 (t
857 ;; Entry ends after its heading; back up
858 (forward-line -1)
859 (end-of-line)
860 (when (org-at-heading-p)
861 ;; At the same heading
862 (forward-line)
863 (insert "\n")
864 (forward-line -1))
865 ;; FIXME: looking-back is supposed to be called with
866 ;; more arguments.
867 (while (not (looking-back (rx
868 (repeat 3
869 (seq (optional blank)
870 "\n")))
871 nil))
872 (insert "\n"))
873 (forward-line -1)))))
874
875 ((org-at-item-checkbox-p)
876 ;; Checkbox: Insert new item with checkbox.
877 (org-insert-todo-heading nil))
878
879 ((org-in-item-p)
880 ;; Plain list. Yes, this gets a little complicated...
881 (let ((context (org-element-context)))
882 (if (or (eq 'plain-list (car context)) ; First item in list
883 (and (eq 'item (car context))
884 (not (eq (org-element-property :contents-begin context)
885 (org-element-property :contents-end context))))
886 ;; Element in list item, e.g. a link
887 (unpackaged/org-element-descendant-of 'item context))
888 ;; Non-empty item: Add new item.
889 (org-insert-item)
890 ;; Empty item: Close the list.
891 ;; TODO: Do this with org functions rather than operating
892 ;; on the text. Can't seem to find the right function.
893 (delete-region (line-beginning-position) (line-end-position))
894 (insert "\n"))))
895
896 ((when (fboundp 'org-inlinetask-in-task-p)
897 (org-inlinetask-in-task-p))
898 ;; Inline task: Don't insert a new heading.
899 (org-return))
900
901 ((org-at-table-p)
902 (cond ((save-excursion
903 (beginning-of-line)
904 ;; See `org-table-next-field'.
905 (cl-loop with end = (line-end-position)
906 for cell = (org-element-table-cell-parser)
907 always (equal (org-element-property :contents-begin cell)
908 (org-element-property :contents-end cell))
909 while (re-search-forward "|" end t)))
910 ;; Empty row: end the table.
911 (delete-region (line-beginning-position) (line-end-position))
912 (org-return))
913 (t
914 ;; Non-empty row: call `org-return'.
915 (org-return))))
916 (t
917 ;; All other cases: call `org-return'.
918 (org-return)))))
919
920;; org-fix-blank-lines (unpackaged)
921(defun unpackaged/org-fix-blank-lines (&optional prefix)
922 "Ensure that blank lines exist between headings and between headings and their contents.
923 With prefix, operate on whole buffer. Ensures that blank lines
924 exist after each headings's drawers."
925 (interactive "P")
926 (org-map-entries (lambda ()
927 (org-with-wide-buffer
928 ;; `org-map-entries' narrows the buffer, which prevents us
929 ;; from seeing newlines before the current heading, so we
930 ;; do this part widened.
931 (while (not (looking-back "\n\n" nil))
932 ;; Insert blank lines before heading.
933 (insert "\n")))
934 (let ((end (org-entry-end-position)))
935 ;; Insert blank lines before entry content
936 (forward-line)
937 (while (and (org-at-planning-p)
938 (< (point) (point-max)))
939 ;; Skip planning lines
940 (forward-line))
941 (while (re-search-forward org-drawer-regexp end t)
942 ;; Skip drawers. You might think that `org-at-drawer-p'
943 ;; would suffice, but for some reason it doesn't work
944 ;; correctly when operating on hidden text. This
945 ;; works, taken from `org-agenda-get-some-entry-text'.
946 (re-search-forward "^[ \t]*:END:.*\n?" end t)
947 (goto-char (match-end 0)))
948 (unless (or (= (point) (point-max))
949 (org-at-heading-p)
950 (looking-at-p "\n"))
951 (insert "\n"))))
952 t (if prefix
953 nil
954 'tree)))
955
956(defun hook--org-mode-fix-blank-lines ()
957 (when (eq major-mode 'org-mode)
958 (let ((current-prefix-arg 4)) ; Emulate C-u
959 (call-interactively 'unpackaged/org-fix-blank-lines))))
960(add-hook 'before-save-hook #'hook--org-mode-fix-blank-lines)
961
962(use-package org
963 :straight (:repo "https://code.orgmode.org/bzg/org-mode.git")
964 :init
965 (setq-default
966 org-directory "~/org" ; where to search for org files
967 ;; typesetting
968 org-hide-emphasis-markers t
969 org-fontify-whole-heading-line t
970 org-fontify-done-headline t
971 org-fontify-quote-and-verse-blocks t
972 org-src-fontify-natively t
973 org-ellipsis " ≡"
974 org-pretty-entities t
975 org-tags-column (- 0 fill-column (- (length org-ellipsis)))
976 ;; Source blocks
977 org-src-tab-acts-natively t
978 org-src-window-setup 'current-window
979 org-confirm-babel-evaluate nil
980 ;; Behavior
981 org-adapt-indentation t ; indent text after a header
982 org-catch-invisible-edits 'smart
983 org-special-ctrl-a/e t
984 org-special-ctrl-k t
985 org-imenu-depth 8 ; catch all headings
986 ;; Exporting
987 org-export-headline-levels 8 ; export all headings
988 org-export-with-smart-quotes t
989 org-export-with-sub-superscripts t
990 ;; Modules
991 org-modules '(;; default (commented if unused)
992 ;; bbdb
993 ;; bibtex
994 ;; docview
995 eww
996 ;; gnus
997 info
998 ;; irc
999 ;; mhe
1000 ;; rmail
1001 ;; w3m
1002 ;; extra stuff for me
1003 ;; habit ; track your consistency with habits
1004 ;; inlinetask ; tasks independent of outline hierarchy
1005 mouse ; additional mouse support
1006 ;; protocol ; intercept calls from emacsclient
1007 ;; man
1008 tempo ; templates
1009 )
1010 org-export-backends '(;; defaults
1011 ascii
1012 html
1013 latex
1014 odt
1015 ;; added by me
1016 man
1017 md
1018 )
1019 )
1020 :config
1021 (require 'org-tempo)
1022 (require 'ox-md)
1023 :bind (:map org-mode-map
1024 ("RET" . unpackaged/org-return-dwim)))
1025
1026(use-package goto-addr
1027 :straight nil
1028 :config
1029 (goto-address-mode +1))
1030 25
1031(use-package web-mode 26(autoload 'ehelp-command "ehelp")
1032 :mode (("\\.phtml\\'" . web-mode) 27(define-key acdw/map (kbd "C-h") #'ehelp-command)
1033 ("\\.tpl\\.php\\'" . web-mode)
1034 ("\\.[agj]sp\\'" . web-mode)
1035 ("\\as[cp]x\\'" . web-mode)
1036 ("\\.erb\\'" . web-mode)
1037 ("\\.mustache\\'" . web-mode)
1038 ("\\.djhtml\\'" . web-mode)
1039 ("\\.html?\\'" . web-mode)))