diff options
Diffstat (limited to 'lisp/acdw.el')
-rw-r--r-- | lisp/acdw.el | 412 |
1 files changed, 226 insertions, 186 deletions
diff --git a/lisp/acdw.el b/lisp/acdw.el index 62778e3..9aa0821 100644 --- a/lisp/acdw.el +++ b/lisp/acdw.el | |||
@@ -22,9 +22,9 @@ | |||
22 | ;;; Utilities | 22 | ;;; Utilities |
23 | 23 | ||
24 | (defconst acdw/system (pcase system-type | 24 | (defconst acdw/system (pcase system-type |
25 | ('gnu/linux :home) | 25 | ('gnu/linux :home) |
26 | ((or 'msdos 'windows-nt) :work) | 26 | ((or 'msdos 'windows-nt) :work) |
27 | (_ :other)) | 27 | (_ :other)) |
28 | "Which system is currently being used.") | 28 | "Which system is currently being used.") |
29 | 29 | ||
30 | (defun acdw/when-unfocused (func &rest args) | 30 | (defun acdw/when-unfocused (func &rest args) |
@@ -37,220 +37,260 @@ Ready for use with `after-focus-change-function'." | |||
37 | (defun acdw/sunrise-sunset (sunrise-command sunset-command) | 37 | (defun acdw/sunrise-sunset (sunrise-command sunset-command) |
38 | "Run commands at sunrise and sunset." | 38 | "Run commands at sunrise and sunset." |
39 | (let* ((times-regex (rx (* nonl) | 39 | (let* ((times-regex (rx (* nonl) |
40 | (: (any ?s ?S) "unrise") " " | 40 | (: (any ?s ?S) "unrise") " " |
41 | (group (repeat 1 2 digit) ":" | 41 | (group (repeat 1 2 digit) ":" |
42 | (repeat 1 2 digit) | 42 | (repeat 1 2 digit) |
43 | (: (any ?a ?A ?p ?P) (any ?m ?M))) | 43 | (: (any ?a ?A ?p ?P) (any ?m ?M))) |
44 | (* nonl) | 44 | (* nonl) |
45 | (: (any ?s ?S) "unset") " " | 45 | (: (any ?s ?S) "unset") " " |
46 | (group (repeat 1 2 digit) ":" | 46 | (group (repeat 1 2 digit) ":" |
47 | (repeat 1 2 digit) | 47 | (repeat 1 2 digit) |
48 | (: (any ?a ?A ?p ?P) (any ?m ?M))) | 48 | (: (any ?a ?A ?p ?P) (any ?m ?M))) |
49 | (* nonl))) | 49 | (* nonl))) |
50 | (ss (sunrise-sunset)) | 50 | (ss (sunrise-sunset)) |
51 | (_m (string-match times-regex ss)) | 51 | (_m (string-match times-regex ss)) |
52 | (sunrise-time (match-string 1 ss)) | 52 | (sunrise-time (match-string 1 ss)) |
53 | (sunset-time (match-string 2 ss))) | 53 | (sunset-time (match-string 2 ss))) |
54 | (run-at-time sunrise-time (* 60 60 24) sunrise-command) | 54 | (run-at-time sunrise-time (* 60 60 24) sunrise-command) |
55 | (run-at-time sunset-time (* 60 60 24) sunset-command) | 55 | (run-at-time sunset-time (* 60 60 24) sunset-command) |
56 | (run-at-time "12:00am" (* 60 60 24) sunset-command))) | 56 | (run-at-time "12:00am" (* 60 60 24) sunset-command))) |
57 | 57 | ||
58 | ;;; Garbage collection hacks | ||
59 | |||
60 | (defconst acdw/gc-cons-threshold-basis (* 800 1024 1024) | ||
61 | "Basis value for `gc-cons-threshold' to return to after jumping. | ||
62 | 800 KB is Emacs's default.") | ||
63 | |||
64 | (defconst acdw/gc-cons-percentage-basis 0.1 | ||
65 | "Basis value for `gc-cons-percentage' to return to after jumping. | ||
66 | 0.1 is Emacs's default.") | ||
67 | |||
68 | (defun acdw/gc-disable () | ||
69 | "Disable garbage collection by setting relevant variables to their maxima." | ||
70 | (setq gc-cons-threshold most-positive-fixnum | ||
71 | gc-cons-percentage 0.8)) | ||
72 | |||
73 | (defun acdw/gc-enable () | ||
74 | "Re-enable garbage collection by setting relevant variables back to bases." | ||
75 | (setq gc-cons-threshold acdw/gc-cons-threshold-basis | ||
76 | gc-cons-percentage acdw/gc-cons-percentage-basis)) | ||
77 | |||
58 | ;;; Directories (think `no-littering') | 78 | ;;; Directories (think `no-littering') |
59 | 79 | ||
60 | (defvar acdw/dir (expand-file-name | 80 | (defvar acdw/dir (expand-file-name |
61 | (convert-standard-filename "var/") | 81 | (convert-standard-filename "var/") |
62 | user-emacs-directory) | 82 | user-emacs-directory) |
63 | "A directory to hold extra configuration and emacs data.") | 83 | "A directory to hold extra configuration and emacs data.") |
64 | 84 | ||
65 | (defun acdw/in-dir (file &optional make-directory) | 85 | (defun acdw/in-dir (file &optional make-directory) |
66 | "Expand FILE relative to `acdw/dir', optionally creating its | 86 | "Expand FILE relative to `acdw/dir', optionally creating its |
67 | directory." | 87 | directory." |
68 | (let ((f (expand-file-name (convert-standard-filename file) | 88 | (let ((f (expand-file-name (convert-standard-filename file) |
69 | acdw/dir))) | 89 | acdw/dir))) |
70 | (when make-directory | 90 | (when make-directory |
71 | (make-directory (file-name-directory f) 'parents)) | 91 | (make-directory (file-name-directory f) 'parents)) |
72 | f)) | 92 | f)) |
73 | 93 | ||
74 | ;;; Settings | 94 | ;;; Settings |
75 | 95 | ||
76 | (defun acdw/set (assignments) | 96 | ;; (defun acdw/set (assignments) |
77 | "Perform `customize-set-variable' on each of ASSIGNMENTS. | 97 | ;; "Perform `customize-set-variable' on each of ASSIGNMENTS. |
78 | 98 | ||
79 | ASSIGNMENTS is a list where each element is of the form | 99 | ;; ASSIGNMENTS is a list where each element is of the form |
80 | (VARIABLE VALUE [COMMENT])." | 100 | ;; (VARIABLE VALUE [COMMENT])." |
81 | (let (setting) ; for return value | 101 | ;; (let (setting) ; for return value |
82 | (dolist (assignment assignments setting) | 102 | ;; (dolist (assignment assignments setting) |
83 | (customize-set-variable (car assignment) | 103 | ;; (customize-set-variable (car assignment) |
84 | (cadr assignment) | 104 | ;; (cadr assignment) |
85 | (if (and (caddr assignment) | 105 | ;; (if (and (caddr assignment) |
86 | (stringp (caddr assignment))) | 106 | ;; (stringp (caddr assignment))) |
87 | (caddr assignment) | 107 | ;; (caddr assignment) |
88 | "Customized by `acdw/set'.")) | 108 | ;; "Customized by `acdw/set'.")) |
89 | (setq setting (car assignment))))) | 109 | ;; (setq setting (car assignment))))) |
90 | 110 | ||
91 | ;;; Faces | 111 | ;;; Faces |
92 | 112 | ||
93 | (defun acdw/set-face (face spec) | 113 | ;; (defun acdw/set-face (face spec) |
94 | "Customize FACE according to SPEC, and register it with `customize'. | 114 | ;; "Customize FACE according to SPEC, and register it with `customize'. |
95 | SPEC is as for `defface'." | 115 | ;; SPEC is as for `defface'." |
96 | (put face 'customized-face spec) | 116 | ;; (put face 'customized-face spec) |
97 | (face-spec-set face spec)) | 117 | ;; (face-spec-set face spec)) |
98 | 118 | ||
99 | (defmacro acdw/set-faces (face-specs) | 119 | ;; (defmacro acdw/set-faces (face-specs) |
100 | "Run `acdw/set-face' over each face in FACE-SPECS." | 120 | ;; "Run `acdw/set-face' over each face in FACE-SPECS." |
101 | (let (face-list) | 121 | ;; (let (face-list) |
102 | (dolist (face face-specs) | 122 | ;; (dolist (face face-specs) |
103 | (push `(acdw/set-face ',(car face) ',(cdr face)) face-list)) | 123 | ;; (push `(acdw/set-face ',(car face) ',(cdr face)) face-list)) |
104 | `(progn | 124 | ;; `(progn |
105 | ,@face-list))) | 125 | ;; ,@face-list))) |
106 | 126 | ||
107 | ;;; Hooks | 127 | ;;; Hooks |
108 | (defmacro acdw/hooks (hook-specs &rest args) | 128 | ;; (defmacro acdw/hooks (hook-specs &rest args) |
109 | "Add functions to hooks, according to HOOK-SPECS. | 129 | ;; "Add functions to hooks, according to HOOK-SPECS. |
110 | 130 | ||
111 | Each HOOK-SPEC is of the following format: (HOOKS FUNCS [DEPTH] [LOCAL]). | 131 | ;; Each HOOK-SPEC is of the following format: (HOOKS FUNCS [DEPTH] [LOCAL]). |
112 | Either HOOKS or FUNCS can also be a list, in which case `add-hook' is called | 132 | ;; Either HOOKS or FUNCS can also be a list, in which case `add-hook' is called |
113 | over the Cartesian product of HOOKS and FUNCS. In each HOOK-SPEC, DEPTH and | 133 | ;; over the Cartesian product of HOOKS and FUNCS. In each HOOK-SPEC, DEPTH and |
114 | LOCAL apply to all hooks defined; if finer control is needed, either pass the | 134 | ;; LOCAL apply to all hooks defined; if finer control is needed, either pass the |
115 | same hooks and functions in different HOOK-SPECs, or just use `add-hook'. | 135 | ;; same hooks and functions in different HOOK-SPECs, or just use `add-hook'. |
116 | 136 | ||
117 | ARGS accept the following keywords: | 137 | ;; ARGS accept the following keywords: |
118 | 138 | ||
119 | :after FEATURE .. `autoload' all functions after FEATURE." | 139 | ;; :after FEATURE .. `autoload' all functions after FEATURE." |
120 | (let ((after (plist-get args :after)) | 140 | ;; (let ((after (plist-get args :after)) |
121 | (command-list)) | 141 | ;; (command-list)) |
122 | (dolist (spec hook-specs) | 142 | ;; (dolist (spec hook-specs) |
123 | (let* ((hooks (car spec)) | 143 | ;; (let* ((hooks (car spec)) |
124 | (funcs (cadr spec)) | 144 | ;; (funcs (cadr spec)) |
125 | (depth (or (caddr spec) 0)) | 145 | ;; (depth (or (caddr spec) 0)) |
126 | (local (cadddr spec))) | 146 | ;; (local (cadddr spec))) |
127 | (when (not (listp hooks)) (setq hooks (list hooks))) | 147 | ;; (when (not (listp hooks)) (setq hooks (list hooks))) |
128 | (when (not (listp funcs)) (setq funcs (list funcs))) | 148 | ;; (when (not (listp funcs)) (setq funcs (list funcs))) |
129 | (dolist (hook hooks) | 149 | ;; (dolist (hook hooks) |
130 | (dolist (func funcs) | 150 | ;; (dolist (func funcs) |
131 | (push `(add-hook ',hook #',func ,depth ,local) command-list) | 151 | ;; (push `(add-hook ',hook #',func ,depth ,local) command-list) |
132 | (when after | 152 | ;; (when after |
133 | (push `(autoload #',func ,after) command-list)))))) | 153 | ;; (push `(autoload #',func ,after) command-list)))))) |
134 | `(progn | 154 | ;; `(progn |
135 | ,@command-list))) | 155 | ;; ,@command-list))) |
136 | 156 | ||
137 | ;;; Keybindings | 157 | ;;; Keybindings |
138 | 158 | ||
139 | (defvar acdw/bind-default-map 'acdw/map | 159 | ;; (defvar acdw/bind-default-map 'acdw/map |
140 | "The default keymap to use with `acdw/bind'.") | 160 | ;; "The default keymap to use with `acdw/bind'.") |
141 | 161 | ||
142 | (defmacro acdw/bind (key command &rest args) | 162 | ;; (defmacro acdw/bind (key command &rest args) |
143 | "A simple key-binding macro to take care of the repetitive stuff | 163 | ;; "A simple key-binding macro to take care of the repetitive stuff |
144 | automatically. | 164 | ;; automatically. |
145 | 165 | ||
146 | If KEY is a vector, it's passed directly to `define-key', | 166 | ;; If KEY is a vector, it's passed directly to `define-key', |
147 | otherwise it's wrapped in `kbd'. | 167 | ;; otherwise it's wrapped in `kbd'. |
148 | 168 | ||
149 | The following keywords are recognized: | 169 | ;; The following keywords are recognized: |
150 | 170 | ||
151 | :after ARGS .. call `autoload' on COMMAND using ARGS before | 171 | ;; :after ARGS .. call `autoload' on COMMAND using ARGS before |
152 | binding the key. ARGS can be just the filename to | 172 | ;; binding the key. ARGS can be just the filename to |
153 | load; in that case it's wrapped in a list. | 173 | ;; load; in that case it's wrapped in a list. |
154 | 174 | ||
155 | :map KEYMAP .. define KEY in KEYMAP instead of the | 175 | ;; :map KEYMAP .. define KEY in KEYMAP instead of the |
156 | default `acdw/bind-default-map'. If `:after' is also supplied, | 176 | ;; default `acdw/bind-default-map'. If `:after' is also supplied, |
157 | run `autoload' on KEYMAP (except when using `:map-after', see). | 177 | ;; run `autoload' on KEYMAP (except when using `:map-after', see). |
158 | 178 | ||
159 | :map-after FILE .. run the underlying `define-key' command in an | 179 | ;; :map-after FILE .. run the underlying `define-key' command in an |
160 | `with-eval-after-load'. For the rare occasion when the keymap is | 180 | ;; `with-eval-after-load'. For the rare occasion when the keymap is |
161 | defined in a different file than the command it binds (looking | 181 | ;; defined in a different file than the command it binds (looking |
162 | at you, `org-mode')." | 182 | ;; at you, `org-mode')." |
163 | (let ((after (when-let (sym (plist-get args :after)) | 183 | ;; (let ((after (when-let (sym (plist-get args :after)) |
164 | (if (not (listp sym)) | 184 | ;; (if (not (listp sym)) |
165 | (list sym) | 185 | ;; (list sym) |
166 | sym))) | 186 | ;; sym))) |
167 | (map-after (plist-get args :map-after)) | 187 | ;; (map-after (plist-get args :map-after)) |
168 | (keymap (or (plist-get args :map) acdw/bind-default-map)) | 188 | ;; (keymap (or (plist-get args :map) acdw/bind-default-map)) |
169 | (keycode (if (vectorp key) key (kbd key))) | 189 | ;; (keycode (if (vectorp key) key (kbd key))) |
170 | (command-list)) | 190 | ;; (command-list)) |
171 | (let ((define-key-command `(define-key ,keymap ,keycode ',command))) | 191 | ;; (let ((define-key-command `(define-key ,keymap ,keycode ',command))) |
172 | (if map-after | 192 | ;; (if map-after |
173 | (push `(with-eval-after-load ,map-after | 193 | ;; (push `(with-eval-after-load ,map-after |
174 | ,define-key-command) | 194 | ;; ,define-key-command) |
175 | command-list) | 195 | ;; command-list) |
176 | (push define-key-command command-list))) | 196 | ;; (push define-key-command command-list))) |
177 | (when after | 197 | ;; (when after |
178 | (unless (fboundp command) | 198 | ;; (unless (fboundp command) |
179 | (push `(autoload ',command ,@after) command-list)) | 199 | ;; (push `(autoload ',command ,@after) command-list)) |
180 | (unless (or map-after | 200 | ;; (unless (or map-after |
181 | (eq keymap acdw/bind-default-map)) | 201 | ;; (eq keymap acdw/bind-default-map)) |
182 | (push `(autoload ',keymap ,(car after) nil nil 'keymap) command-list))) | 202 | ;; (push `(autoload ',keymap ,(car after) nil nil 'keymap) command-list))) |
183 | `(progn | 203 | ;; `(progn |
184 | ,@command-list))) | 204 | ;; ,@command-list))) |
185 | 205 | ||
186 | (defmacro acdw/binds (bindings) | 206 | ;; (defmacro acdw/binds (bindings) |
187 | "Bind multiple keys at once." | 207 | ;; "Bind multiple keys at once." |
188 | (let (bind-list) | 208 | ;; (let (bind-list) |
189 | (dolist (bind bindings) | 209 | ;; (dolist (bind bindings) |
190 | (push `(acdw/bind ,@bind) bind-list)) | 210 | ;; (push `(acdw/bind ,@bind) bind-list)) |
191 | `(progn | 211 | ;; `(progn |
192 | ,@bind-list))) | 212 | ;; ,@bind-list))) |
193 | 213 | ||
194 | ;; convenience | 214 | ;; convenience |
195 | (defmacro acdw/bind-after-map (file keymap bindings) | 215 | ;; (defmacro acdw/bind-after-map (file keymap bindings) |
196 | "Wrap multiple calls of `acdw/bind' after FILE and with KEYMAP. | 216 | ;; "Wrap multiple calls of `acdw/bind' after FILE and with KEYMAP. |
197 | KEYMAP can be nil." | 217 | ;; KEYMAP can be nil." |
198 | (declare (indent 2)) | 218 | ;; (declare (indent 2)) |
199 | (let ((bind-list) | 219 | ;; (let ((bind-list) |
200 | (extra-args (if keymap | 220 | ;; (extra-args (if keymap |
201 | `(:after ,file :map ,keymap) | 221 | ;; `(:after ,file :map ,keymap) |
202 | `(:after ,file)))) | 222 | ;; `(:after ,file)))) |
203 | (dolist (binding bindings) | 223 | ;; (dolist (binding bindings) |
204 | (push `(acdw/bind ,@binding ,@extra-args) bind-list)) | 224 | ;; (push `(acdw/bind ,@binding ,@extra-args) bind-list)) |
205 | `(progn | 225 | ;; `(progn |
206 | ,@bind-list))) | 226 | ;; ,@bind-list))) |
207 | 227 | ||
208 | ;;; Packages | 228 | ;;; Packages |
209 | 229 | ||
210 | (defmacro acdw/pkg (package &rest args) | 230 | ;; (defmacro acdw/pkg (package &rest args) |
211 | "Set up a package using `straight.el'. | 231 | ;; "Set up a package using `straight.el'. |
212 | 232 | ||
213 | ARGS can include the following keywords: | 233 | ;; ARGS can include the following keywords: |
214 | 234 | ||
215 | :local BOOL .. if BOOL is non-nil, don't run `straight-use-package' on | 235 | ;; :local BOOL .. if BOOL is non-nil, don't run `straight-use-package' on |
216 | PACKAGE. Good for using `acdw/pkg' on local features. | 236 | ;; PACKAGE. Good for using `acdw/pkg' on local features. |
217 | :require BOOL .. if BOOL is non-nil, run `require' on PACKAGE before anything. | 237 | ;; :require BOOL .. if BOOL is non-nil, run `require' on PACKAGE before anything. |
218 | :now FORMS .. run FORMS immediately. | 238 | ;; :now FORMS .. run FORMS immediately. |
219 | :then FORMS .. run FORMS after loading PACKAGE, using `with-eval-after-load'. | 239 | ;; :then FORMS .. run FORMS after loading PACKAGE, using `with-eval-after-load'. |
220 | :set SETTINGS .. pass SETTINGS to `acdw/set', right after `:now' forms. | 240 | ;; :set SETTINGS .. pass SETTINGS to `acdw/set', right after `:now' forms. |
221 | SETTINGS should be properly quoted, just like they'd be passed | 241 | ;; SETTINGS should be properly quoted, just like they'd be passed |
222 | to the function. | 242 | ;; to the function. |
223 | :binds BINDS .. run `acdw/bind-after-map' on BINDS. | 243 | ;; :binds BINDS .. run `acdw/bind-after-map' on BINDS. |
224 | :hooks HOOKS .. run `acdw/hooks' on HOOKS." | 244 | ;; :hooks HOOKS .. run `acdw/hooks' on HOOKS." |
225 | (declare (indent 1)) | 245 | ;; (declare (indent 1)) |
226 | (let ((local-pkg (plist-get args :local)) | 246 | ;; (let ((local-pkg (plist-get args :local)) |
227 | (require-pkg (plist-get args :require)) | 247 | ;; (require-pkg (plist-get args :require)) |
228 | (now-forms (plist-get args :now)) | 248 | ;; (now-forms (plist-get args :now)) |
229 | (settings (plist-get args :set)) | 249 | ;; (settings (plist-get args :set)) |
230 | (binds (plist-get args :binds)) | 250 | ;; (binds (plist-get args :binds)) |
231 | (hooks (plist-get args :hooks)) | 251 | ;; (hooks (plist-get args :hooks)) |
232 | (then-forms (plist-get args :then)) | 252 | ;; (then-forms (plist-get args :then)) |
233 | (requirement (if (listp package) | 253 | ;; (requirement (if (listp package) |
234 | (car package) | 254 | ;; (car package) |
235 | package)) | 255 | ;; package)) |
236 | (final-form)) | 256 | ;; (final-form)) |
237 | (when then-forms | 257 | ;; (when then-forms |
238 | (push `(with-eval-after-load ',requirement ,@then-forms) final-form)) | 258 | ;; (push `(with-eval-after-load ',requirement ,@then-forms) final-form)) |
239 | (when hooks | 259 | ;; (when hooks |
240 | (push `(acdw/hooks ,hooks :after ,(symbol-name requirement)) final-form)) | 260 | ;; (push `(acdw/hooks ,hooks :after ,(symbol-name requirement)) final-form)) |
241 | (when binds | 261 | ;; (when binds |
242 | (push `(acdw/bind-after-map ,(symbol-name requirement) nil ,binds) | 262 | ;; (push `(acdw/bind-after-map ,(symbol-name requirement) nil ,binds) |
243 | final-form)) | 263 | ;; final-form)) |
244 | (when settings | 264 | ;; (when settings |
245 | (push `(acdw/set ,settings) final-form)) | 265 | ;; (push `(acdw/set ,settings) final-form)) |
246 | (when now-forms | 266 | ;; (when now-forms |
247 | (push `(progn ,@now-forms) final-form)) | 267 | ;; (push `(progn ,@now-forms) final-form)) |
248 | (unless local-pkg | 268 | ;; (unless local-pkg |
249 | (push `(straight-use-package ',package) final-form)) | 269 | ;; (push `(straight-use-package ',package) final-form)) |
250 | (when require-pkg | 270 | ;; (when require-pkg |
251 | (push `(require ',requirement) final-form)) | 271 | ;; (push `(require ',requirement) final-form)) |
252 | `(progn | 272 | ;; `(progn |
253 | ,@final-form))) | 273 | ;; ,@final-form))) |
274 | |||
275 | ;;; Reading mode | ||
276 | |||
277 | (define-minor-mode acdw/reading-mode | ||
278 | "A mode for reading." | ||
279 | :init-value t | ||
280 | :lighter " Read" | ||
281 | (if acdw/reading-mode | ||
282 | (progn ;; turn on | ||
283 | (display-fill-column-indicator-mode -1) | ||
284 | (dolist (mode '(visual-fill-column-mode | ||
285 | iscroll-mode)) | ||
286 | (when (fboundp mode) | ||
287 | (funcall mode +1)))) | ||
288 | ;; turn off | ||
289 | (display-fill-column-indicator-mode +1) | ||
290 | (dolist (mode '(visual-fill-column-mode | ||
291 | iscroll-mode)) | ||
292 | (when (fboundp mode) | ||
293 | (funcall mode -1))))) | ||
254 | 294 | ||
255 | ;;; Keymap & Mode | 295 | ;;; Keymap & Mode |
256 | 296 | ||
@@ -273,7 +313,7 @@ ARGS can include the following keywords: | |||
273 | ;; Set up a leader key for `acdw/mode' | 313 | ;; Set up a leader key for `acdw/mode' |
274 | (defvar acdw/leader | 314 | (defvar acdw/leader |
275 | (let ((map (make-sparse-keymap)) | 315 | (let ((map (make-sparse-keymap)) |
276 | (c-z (global-key-binding "\C-z"))) | 316 | (c-z (global-key-binding "\C-z"))) |
277 | (define-key acdw/map "\C-z" map) | 317 | (define-key acdw/map "\C-z" map) |
278 | (define-key map "\C-z" c-z) | 318 | (define-key map "\C-z" c-z) |
279 | map)) | 319 | map)) |