summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorCase Duckworth2021-03-07 22:14:38 -0600
committerCase Duckworth2021-03-07 22:14:38 -0600
commit983c7330e87481227393670b2151cb0539dc4de4 (patch)
tree79c5262d8ef6db8f8f3392e0c7923b37016f7185
parentMerge branch 'no-org' of tildegit.org:acdw/emacs into no-org (diff)
downloademacs-983c7330e87481227393670b2151cb0539dc4de4.tar.gz
emacs-983c7330e87481227393670b2151cb0539dc4de4.zip
5c
-rw-r--r--early-init.el260
-rw-r--r--init.el1030
-rw-r--r--lisp/acdw.el83
3 files changed, 172 insertions, 1201 deletions
diff --git a/early-init.el b/early-init.el index 1bf78eb..352d4e6 100644 --- a/early-init.el +++ b/early-init.el
@@ -1,10 +1,9 @@
1;;; early-init.el -*- lexical-binding: t; coding: utf-8 -*- 1;;; early-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
8;; 7;;
9;; This file is NOT part of GNU Emacs. 8;; This file is NOT part of GNU Emacs.
10;; 9;;
@@ -23,174 +22,85 @@
23;; 22;;
24;;; Code: 23;;; Code:
25 24
26;; Speed up init 25;;; Define personal-use constants
27(setq gc-cons-threshold most-positive-fixnum
28 gc-cons-percentage 0.6
29 comp-deferred-compilation nil)
30
31(defconst gc-cons-basis (* 800 1024)
32 "The basis value to which to return after a max jump.
33800,000 (800 KB) is Emacs' default.")
34
35(add-hook 'after-init-hook #'(lambda ()
36 (setq gc-cons-threshold gc-cons-basis
37 gc-cons-percentage 0.1)))
38
39(defun hook--gc-cons-maximize ()
40 "Set `gc-cons-threshold' to the highest possible.
41 For memory-intensive features."
42 (setq gc-cons-threshold most-positive-fixnum))
43
44(defun hook--gc-cons-baseline ()
45 "Return `gc-cons-threshold' to `gc-cons-basis'.
46 For after memory intensive operations."
47 (setq gc-cons-threshold gc-cons-basis))
48
49(add-hook 'minibuffer-setup-hook #'hook--gc-cons-maximize)
50(add-hook 'minibuffer-exit-hook #'hook--gc-cons-baseline)
51
52;; From doom-emacs
53(unless (daemonp)
54 (defvar doom--initial-file-name-handler-alist file-name-handler-alist)
55 (setq file-name-handler-alist nil)
56 (defun hook--reset-file-handler-alist ()
57 (dolist (handler file-name-handler-alist)
58 (add-to-list 'doom--initial-file-name-handler-alist handler))
59 (setq file-name-handler-alist doom--initial-file-name-handler-alist))
60 (add-hook 'emacs-startup-hook #'hook--reset-file-handler-alist))
61
62;; Where are we?
63(defconst acdw/system (pcase system-type 26(defconst acdw/system (pcase system-type
64 ('gnu/linux :home) 27 ('gnu/linux :home)
65 ((or 'msdos 'windows-nt) :work) 28 ((or 'msdos 'windows-nt) :work)
66 (_ :other))) 29 (_ :other))
67 30 "Which system is currently being used.")
68;; Frame initiation
69 31
70;; Initialize frames with as little UI as possible. 32(defvar acdw/dir (expand-file-name
71(setq-default 33 (convert-standard-filename "var/")
72 default-frame-alist ; The default look of frames 34 user-emacs-directory)
73 `((tool-bar-lines . 0) ; Remove tool bar 35 "A directory to hold extra configuration and emacs data.")
74 (menu-bar-lines . 0) ; Remove menu bar
75 (vertical-scroll-bars) ; Remove vertical scroll bars
76 (horizontal-scroll-bars) ; Remove horizontal scroll bars
77 (width . 84) ; A /little/ wider than `fill-column'
78 (height . 30) ; Text characters
79 (left-fringe . 8) ; Width of fringes
80 (right-fringe . 8) ; (8 is the default)
81 (font . ,(pcase acdw/system ; Default font
82 (:home "Terminus 12")
83 (:work "Consolas 11")))
84 )
85 36
86 x-underline-at-descent-line t ; underline at the descent line 37;;; Speed up init
38;; see doom-emacs, et al.
87 39
88 scroll-margin 0 ; how many lines to show at window edge 40(defconst gc-cons-threshold-basis (* 800 1000)
89 scroll-conservatively 101 ; just enough to bring text into view 41 "The basis value for `gc-cons-threshold' to return to after a jump.
90 scroll-preserve-screen-position 1 ; always keep screen position 42800 KB is Emacs's default `gc-cons-threshold'.")
91
92 frame-title-format ; Titles for frames
93 '((:eval (if (buffer-file-name) ; (cf. `mode-line-format')
94 (abbreviate-file-name (buffer-file-name))
95 "%b"))
96 " "
97 mode-line-client
98 mode-line-modified
99 " - GNU Emacs")
100 43
101 mode-line-format ; Mode line 44(defconst gc-cons-percentage-basis 0.1
102 `("%e" 45 "The basis value for `gc-cons-percentage' to return to after init.
103 mode-line-front-space 460.1 is Emacs's default `gc-cons-percentage'.")
104 ;; mode-line-mule-info
105 mode-line-client
106 mode-line-modified
107 mode-line-remote
108 mode-line-frame-identification
109 mode-line-buffer-identification " "
110 mode-line-position
111 (vc-mode vc-mode) " "
112 minions-mode-line-modes
113 mode-line-misc-info
114 mode-line-end-spaces
115 ))
116 47
117;; Set the rest of the fonts after initiation 48(defvar orig-file-name-handler-alist file-name-handler-alist
118(defun hook--setup-fonts () 49 "The original value of `file-name-handler-alist' will be restored
119 (pcase acdw/system 50 after init.")
120 (:home (set-face-attribute 'default nil
121 :family "Terminus"
122 :height 120)
123 (set-face-attribute 'fixed-pitch nil
124 :family "Terminus"
125 :height 1.0)
126 (set-face-attribute 'variable-pitch nil
127 :family "DejaVu Sans"
128 :height 1.0))
129 (:work (set-face-attribute 'default nil
130 :family "Consolas"
131 :height 110)
132 (set-face-attribute 'fixed-pitch nil
133 :family "Consolas"
134 :height 1.0)
135 (set-face-attribute 'variable-pitch nil
136 :family "Cambria"
137 :height 1.0))))
138 51
139(add-hook 'after-init-hook #'hook--setup-fonts) 52(setq gc-cons-threshold most-positive-fixnum
140 53 gc-cons-percentage 0.6
141;; In case I do want the UI elements later, I also disable the modes 54 file-name-handler-alist nil)
142;; -- otherwise I'd have to run the mode twice to actually show the 55
143;; thing. 56(defun hook--post-init-reset ()
57 "Reset `gc-cons-threshold', `gc-cons-percentage', and
58 `file-name-handler-alist' to their defaults after init."
59 (setq gc-cons-threshold gc-cons-threshold-basis
60 gc-cons-percentage gc-cons-percentage-basis)
61 (dolist (handler file-name-handler-alist)
62 (add-to-list 'orig-file-name-handler-alist handler))
63 (setq file-name-handler-alist orig-file-name-handler-alist))
64
65(add-hook 'after-init-hook #'hook--post-init-reset)
66
67;; ;;; Frame settings
68
69(setq default-frame-alist ; Remove most UI
70 `((tool-bar-lines . 0) ; No tool bar
71 (menu-bar-lines . 0) ; No menu bar
72 (vertical-scroll-bars) ; No scroll bars
73 (horizontal-scroll-bars) ; ... at all
74 (width . 84) ; A /little/ wider than
75 ; `fill-column' (set later)
76 (height . 30)
77 (left-fringe . 8) ; Width of fringes
78 (right-fringe . 8) ; (8 is default)
79 (font . ,(pcase acdw/system
80 (:home "Terminus 12")
81 (:work "Consolas 10"))))
82 frame-inhibit-implied-resize t ; Don't resize randomly
83 frame-resize-pixelwise t ; Resize by pixels, not chars
84 )
144 85
145(defun hook--disable-ui-modes () 86(defun hook--disable-ui-modes ()
146 (dolist (mode '(tool-bar-mode 87 "Disable frame UI using modes, for toggling later."
147 menu-bar-mode 88 (dolist (mode ;; each mode is of the form (MODE . FRAME-ALIST-VAR)
148 scroll-bar-mode 89 '((tool-bar-mode . tool-bar-lines)
149 horizontal-scroll-bar-mode)) 90 (menu-bar-mode . menu-bar-lines)
150 (funcall mode -1))) 91 (scroll-bar-mode . vertical-scroll-bars)
92 (horizontal-scroll-bar-mode . horizontal-scroll-bars)
93 ))
94 (let ((setting (alist-get (cdr mode) default-frame-alist)))
95 (when (or (not setting)
96 (= 0 setting))
97 (funcall (car mode) -1)))))
151 98
152;; I run it on the `after-init-hook' so it doesn't slow down init so much.
153(add-hook 'after-init-hook #'hook--disable-ui-modes) 99(add-hook 'after-init-hook #'hook--disable-ui-modes)
154 100
155;; Customize the fringe 101;;; Bootstrap package manager (`straight.el')
156(setq-default
157 indicate-empty-lines t ; show an indicator at the end of the buffer
158 indicate-buffer-boundaries 'right ; show buffer boundaries on the right
159 visual-line-fringe-indicators ; show continuation indicators on the left
160 '(left-curly-arrow nil))
161
162(defun hook--setup-fringe-bitmaps ()
163 (define-fringe-bitmap 'left-curly-arrow
164 [#b11000000
165 #b01100000
166 #b00110000
167 #b00011000])
168 (define-fringe-bitmap 'right-curly-arrow
169 [#b00011000
170 #b00110000
171 #b01100000
172 #b11000000])
173 (define-fringe-bitmap 'left-arrow
174 [#b00000000
175 #b01010100
176 #b01010100
177 #b00000000])
178 (define-fringe-bitmap 'right-arrow
179 [#b00000000
180 #b00101010
181 #b00101010
182 #b00000000])
183 (remove-function after-focus-change-function #'hook--setup-fringe-bitmaps))
184(add-function :after after-focus-change-function #'hook--setup-fringe-bitmaps)
185
186;; Resize like it's 2021
187(setq-default frame-inhibit-implied-resize t
188 frame-resize-pixelwise t)
189
190;; Bootstrap package manager (`straight')
191
192;; First, I need to make sure it's in the `exec-path'.
193 102
103;; 1. Update `exec-path'.
194(let ((win-app-dir "~/Applications")) 104(let ((win-app-dir "~/Applications"))
195 (dolist (path (list 105 (dolist (path (list
196 ;; Windows 106 ;; Windows
@@ -210,26 +120,19 @@
210 )) 120 ))
211 (when (file-exists-p path) 121 (when (file-exists-p path)
212 (add-to-list 'exec-path path :append)))) 122 (add-to-list 'exec-path path :append))))
213 123;; 1.5. Update $PATH to reflect changes.
214;; Set $PATH back to `exec-path', for symmetry's sake.
215(setenv "PATH" (mapconcat #'identity exec-path path-separator)) 124(setenv "PATH" (mapconcat #'identity exec-path path-separator))
216 125
217;; Set some variables 126;; 2. Set `package' and `straight' variables.
218(defvar acdw/etc-dir 127(setq package-enable-at-startup nil ; not sure if strictly
219 (expand-file-name "etc/" user-emacs-directory) 128 ; necessary
220 "Where to put other configurations.") 129 package-quickstart nil ; ditto
221(defvar acdw/var-dir 130 straight-host-usernames '((github . "duckwork")
222 (expand-file-name "var/" user-emacs-directory) 131 (gitlab . "acdw"))
223 "Where to put variable stuff.") 132 straight-base-dir acdw/dir ; don't clutter ~/.emacs.d
133 )
224 134
225(setq-default package-enable-at-startup nil 135;; 3. Bootstrap `straight'.
226 package-quickstart nil
227 straight-use-package-by-default t
228 straight-host-usernames '((github . "duckwork")
229 (gitlab . "acdw"))
230 straight-base-dir acdw/var-dir)
231
232;; Run `straight''s bootstrap code
233(defvar bootstrap-version) 136(defvar bootstrap-version)
234(let ((bootstrap-file 137(let ((bootstrap-file
235 (expand-file-name 138 (expand-file-name
@@ -239,20 +142,17 @@
239 (unless (file-exists-p bootstrap-file) 142 (unless (file-exists-p bootstrap-file)
240 (with-current-buffer 143 (with-current-buffer
241 (url-retrieve-synchronously 144 (url-retrieve-synchronously
242 "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el" 145 (concat "https://raw.githubusercontent.com/"
146 "raxod502/straight.el/develop/install.el")
243 'silent 'inhibit-cookies) 147 'silent 'inhibit-cookies)
244 (goto-char (point-max)) 148 (goto-char (point-max))
245 (eval-print-last-sexp))) 149 (eval-print-last-sexp)))
246 (load bootstrap-file nil 'nomessage)) 150 (load bootstrap-file nil 'nomessage))
247 151
248;; `use-package' 152;;; Message startup time for profiling
249
250(straight-use-package 'use-package)
251(require 'use-package)
252 153
253;; Message startup time
254(defun hook--message-startup-time () 154(defun hook--message-startup-time ()
255 "Message Emacs' startup time." 155 "Show Emacs's startup time in the message buffer. For profiling."
256 (message "Emacs ready in %s with %d garbage collections." 156 (message "Emacs ready in %s with %d garbage collections."
257 (format "%.2f seconds" 157 (format "%.2f seconds"
258 (float-time (time-subtract after-init-time 158 (float-time (time-subtract after-init-time
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)))
diff --git a/lisp/acdw.el b/lisp/acdw.el new file mode 100644 index 0000000..03e4a62 --- /dev/null +++ b/lisp/acdw.el
@@ -0,0 +1,83 @@
1:;;; acdw.el -*- lexical-binding: t; coding: utf-8-unix -*-
2;;
3;; Author: Case Duckworth <acdw@acdw.net>
4;; Created: Sometime during Covid-19, 2020
5;; Keywords: configuration
6;; URL: https://tildegit.org/acdw/emacs
7;; Bankruptcy: 5b
8;;
9;; This file is NOT part of GNU Emacs.
10;;
11;;; License:
12;;
13;; Everyone is permitted to do whatever with this software, without
14;; limitation. This software comes without any warranty whatsoever,
15;; but with two pieces of advice:
16;; - Don't hurt yourself.
17;; - Make good choices.
18;;
19;;; Commentary:
20;; `acdw.el' contains `acdw/map', its mode, and assorted ease-of-life
21;; functions for me, acdw.
22;;
23;;; Code:
24
25;;; Directories (think `no-littering')
26
27(defvar acdw/dir (expand-file-name
28 (convert-standard-filename "var/")
29 user-emacs-directory)
30 "A directory to hold extra configuration and emacs data.")
31
32(defun acdw/in-dir (file &optional make-directory)
33 "Expand FILE relative to `acdw/dir', optionally creating its
34directory."
35 (let ((f (expand-file-name (convert-standard-filename file)
36 acdw/dir)))
37 (when make-directory
38 (make-directory (file-name-directory) 'parents))
39 f))
40
41;;; Settings
42
43(defun acdw/set (assignments)
44 "Perform `customize-set-variable' on each of ASSIGNMENTS.
45
46ASSIGNMENTS is a list where each element is of the form
47(VARIABLE VALUE [COMMENT])."
48 (dolist (assn assignments)
49 (customize-set-variable (car assignment)
50 (cadr assignment)
51 (if (and (caddr assignment)
52 (stringp (caddr assignment)))
53 (caddr assignment)
54 "Customized by `acdw/set'."))))
55
56;;; Keymap & Mode
57
58(defvar acdw/map (make-sparse-keymap)
59 "A keymap for my custom bindings.")
60
61(define-minor-mode acdw/mode
62 "A mode for `acdw/map'."
63 :init-value t
64 :lighter " acdw"
65 :keymap acdw/map)
66(define-globalized-minor-mode acdw/global-mode acdw/mode acdw/mode)
67
68;; Disable `acdw/mode' in the minibuffer
69(defun acdw/mode--disable ()
70 "Disable `acdw/mode'."
71 (acdw/mode -1))
72(add-hook 'minibuffer-setup-hook #'acdw/mode--disable)
73
74;; Set up a leader key for `acdw/mode'
75(defvar acdw/leader
76 (let ((map (make-sparse-keymap))
77 (c-z (global-key-binding "\C-z")))
78 (define-key acdw/map "\C-z" map)
79 (define-key map "\C-z" c-z)
80 map))
81
82(provide 'acdw)
83;;; acdw.el ends here