about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--basics.el17
-rw-r--r--init.el121
-rw-r--r--lisp/acdw-mail.el24
-rw-r--r--lisp/acdw.el119
4 files changed, 260 insertions, 21 deletions
diff --git a/basics.el b/basics.el index 428dcd1..68cdc2a 100644 --- a/basics.el +++ b/basics.el
@@ -560,12 +560,23 @@ N spaces."
560 :hook 560 :hook
561 (embark-collect-mode . consult-preview-at-point-mode)) 561 (embark-collect-mode . consult-preview-at-point-mode))
562 562
563(use-package undo-fu
564 :ensure t
565 :init
566 (setq undo-limit 67108864) ; 64mb.
567 (setq undo-strong-limit 100663296) ; 96mb.
568 (setq undo-outer-limit 1006632960) ; 960mb.
569 :bind (("C-/" . undo-fu-only-undo)
570 ("C-?" . undo-fu-only-redo)))
571
563(use-package undo-fu-session 572(use-package undo-fu-session
564 :ensure t 573 :ensure t
565 :config 574 :config
566 (setq undo-fu-session-incompatible-files 575 (setopt undo-fu-session-compression (cond
567 '("/COMMIT_EDITMSG\\'" 576 ((executable-find "gunzip") 'gz)
568 "/git-rebase-todo\\'")) 577 ((executable-find "bzip2") 'bz2))
578 undo-fu-session-incompatible-files '("/COMMIT_EDITMSG\\'"
579 "/git-rebase-todo\\'"))
569 (global-undo-fu-session-mode)) 580 (global-undo-fu-session-mode))
570 581
571(use-package crux 582(use-package crux
diff --git a/init.el b/init.el index aed189c..864ac1b 100644 --- a/init.el +++ b/init.el
@@ -1,6 +1,17 @@
1;;; init.el --- An Emacs of one's own -*- lexical-binding: t -*- 1;;; init.el --- An Emacs of one's own -*- lexical-binding: t -*-
2
3;; Author: Case Duckworth <acdw@acdw.net>, with inspo from many others
4;; Homepage: https://git.acdw.net/emacs
5;; Config-Requires: ((emacs "29.0"))
2;; Bankruptcy: 9.4 6;; Bankruptcy: 9.4
3 7
8;; This configuration is Free Software. Everyone is permitted to do whatever
9;; they want with it, without limitation. This software comes without any
10;; warranty whatsoever, but with two pieces of advice:
11;;
12;; - Don't hurt others.
13;; - Make good choices.
14
4;;; Code: 15;;; Code:
5 16
6(load (locate-user-emacs-file "basics")) ; super basic stuff 17(load (locate-user-emacs-file "basics")) ; super basic stuff
@@ -106,6 +117,14 @@
106 :config 117 :config
107 (fringe-mode '(nil . 0))) 118 (fringe-mode '(nil . 0)))
108 119
120(use-package ispell
121 :config
122 (setopt ispell-program-name (choose-executable "ispell" "aspell"))
123 (add-hook 'before-save-hook
124 #'+ispell-move-buffer-words-to-dir-locals-hook)
125 (put 'ispell-buffer-session-localwords 'safe-local-variable
126 '+ispell-safe-local-p))
127
109(use-package flyspell 128(use-package flyspell
110 :hook org-mode-hook) 129 :hook org-mode-hook)
111 130
@@ -164,6 +183,8 @@
164(use-package time 183(use-package time
165 :config 184 :config
166 (setopt display-time-format " %H:%M" 185 (setopt display-time-format " %H:%M"
186 display-time-interval 60
187 display-time-use-mail-icon t
167 display-time-mail-function 188 display-time-mail-function
168 (defun +notmuch-new-mail-p () 189 (defun +notmuch-new-mail-p ()
169 (plist-get (cl-find "inbox+unread" 190 (plist-get (cl-find "inbox+unread"
@@ -173,8 +194,24 @@
173 :test #'equal) 194 :test #'equal)
174 :count)) 195 :count))
175 display-time-default-load-average nil) 196 display-time-default-load-average nil)
197 (with-eval-after-load 'notmuch
198 (add-hook 'notmuch-after-tag-hook #'display-time-update))
176 (display-time-mode)) 199 (display-time-mode))
177 200
201(use-package tab-bar
202 :config
203 (setopt tab-bar-show t
204 tab-bar-close-button-show t)
205 ;; (add-to-list 'tab-bar-format 'tab-bar-format-menu-bar)
206 (add-to-list 'tab-bar-format 'tab-bar-format-align-right :append)
207 (add-to-list 'tab-bar-format 'tab-bar-format-global :append)
208 (when (daemonp)
209 (add-hook 'server-after-make-frame-hook
210 (defun after-frame@tab-bar ()
211 (tab-bar-mode)
212 (remove-hook 'server-after-make-frame-hook
213 #'after-frame@tab-bar)))))
214
178 215
179;;; Applications 216;;; Applications
180 217
@@ -265,7 +302,9 @@ With prefix ARG, toggle the value of
265 302
266(use-package filldent 303(use-package filldent
267 :load-path "~/src/emacs/filldent/" 304 :load-path "~/src/emacs/filldent/"
268 :bind ("M-q" . filldent-dwim)) 305 :bind ("M-q" . filldent-dwim)
306 :config
307 (setopt filldent-fill-modes '(web-mode)))
269 308
270(use-package frowny 309(use-package frowny
271 :load-path "~/src/emacs/frowny/" 310 :load-path "~/src/emacs/frowny/"
@@ -338,8 +377,30 @@ With prefix ARG, toggle the value of
338 word-boundary))) 377 word-boundary)))
339 (hi-lock-unface-buffer regexp) 378 (hi-lock-unface-buffer regexp)
340 (highlight-regexp regexp 'font-lock-warning-face)))))) 379 (highlight-regexp regexp 'font-lock-warning-face))))))
380 (add-hook 'jabber-chat-mode-hook
381 (defun jabber-chat@leave-when-kill ()
382 (add-hook 'kill-buffer-hook
383 (defun @jabber-leave@kill ()
384 (apply #'jabber-muc-leave (jabber-muc-argument-list)))
385 nil :local)))
341 (when (fboundp 'jabber-chat-update-focus) 386 (when (fboundp 'jabber-chat-update-focus)
342 (add-hook 'window-configuration-change-hook #'jabber-chat-update-focus))) 387 (add-hook 'window-configuration-change-hook #'jabber-chat-update-focus))
388 (with-eval-after-load 'consult
389 (defvar jabber-chat-buffer-source
390 `( :name "Jabber"
391 :hidden nil
392 :narrow ?j
393 :category buffer
394 :state ,#'consult--buffer-state
395 :items ,(lambda ()
396 (mapcar #'buffer-name
397 (seq-filter (lambda (buf)
398 (with-current-buffer buf
399 (eq major-mode 'jabber-chat-mode)))
400 (buffer-list))))))
401 (add-to-list 'consult-buffer-sources 'jabber-chat-buffer-source :append)
402 (consult-customize
403 consult-buffer :preview-key (kbd "M-."))))
343 404
344(use-package keepassxc-shim 405(use-package keepassxc-shim
345 :load-path "~/src/emacs/keepassxc-shim/" 406 :load-path "~/src/emacs/keepassxc-shim/"
@@ -349,6 +410,14 @@ With prefix ARG, toggle the value of
349 410
350;;; External packages 411;;; External packages
351 412
413(use-package async
414 :ensure t
415 :config
416 ;; https://github.com/jwiegley/emacs-async/issues/64
417 ;; (setopt message-send-mail-function #'async-smtpmail-send-it)
418 (dired-async-mode)
419 (async-bytecomp-package-mode))
420
352(use-package trashed 421(use-package trashed
353 :ensure t) 422 :ensure t)
354 423
@@ -367,11 +436,45 @@ With prefix ARG, toggle the value of
367 :config (minions-mode)) 436 :config (minions-mode))
368 437
369(use-package visual-fill-column 438(use-package visual-fill-column
439 :preface
440 (defcustom visual-fill-column-widen-amount 4
441 "Amount to widen `fill-column' by in `visual-fill-column-mode'."
442 :type 'natnum
443 :group 'visual-fill-column)
444 (defun visual-fill-column--widen/narrow-handle-arg (cols)
445 (cond
446 ((null cols) visual-fill-column-widen-amount)
447 ((listp cols) (* visual-fill-column-widen-amount
448 (1+ (/ (car cols) 4))))
449 ((eq '- cols) (- visual-fill-column-widen-amount))
450 (:else cols)))
451 (defun visual-fill-column-widen (&optional cols)
452 "Widen `fill-column' by COLS, and re-display.
453If COLS is missing or nil, widen by
454`visual-fill-column-widen-amount'. When called with a plain
455\\[universal-argument], multiply that amount by 1 + the amount of
456\\[universal-argument]s. If called with a numerical prefix
457argument, widen by that number of columns."
458 (interactive "P")
459 (let ((cols (visual-fill-column--widen/narrow-handle-arg cols)))
460 (cl-incf fill-column cols)
461 (visual-fill-column-adjust)
462 (message "Fill-column: %s" fill-column)))
463 (defun visual-fill-column-narrow (&optional cols)
464 "Narrow `fill-column' by COLS, then redisplay.
465The prefix argument is as in `visual-fill-column-widen' but negated."
466 (interactive "P")
467 (let ((cols (visual-fill-column--widen/narrow-handle-arg cols)))
468 (cl-decf fill-column cols)
469 (visual-fill-column-adjust)
470 (message "Fill-column: %s" fill-column)))
370 :ensure t 471 :ensure t
371 :init 472 :init
372 (setopt visual-fill-column-center-text t 473 (setopt visual-fill-column-center-text t
373 visual-fill-column-extra-text-width '(3 . 3)) 474 visual-fill-column-extra-text-width '(3 . 3))
374 :config 475 :config
476 (keymap-set visual-fill-column-mode-map "C-x C->" #'visual-fill-column-widen)
477 (keymap-set visual-fill-column-mode-map "C-x C-<" #'visual-fill-column-narrow)
375 (add-hook 'visual-fill-column-mode-hook #'visual-line-mode) 478 (add-hook 'visual-fill-column-mode-hook #'visual-line-mode)
376 (add-hook 'eww-mode-hook #'visual-fill-column-mode) 479 (add-hook 'eww-mode-hook #'visual-fill-column-mode)
377 (advice-add 'text-scale-adjust :after #'visual-fill-column-adjust)) 480 (advice-add 'text-scale-adjust :after #'visual-fill-column-adjust))
@@ -726,15 +829,5 @@ With PREFIX, prompt to change the current dictionary."
726(use-package php-mode 829(use-package php-mode
727 :ensure t) 830 :ensure t)
728 831
729(use-package tab-bar 832(use-package rec-mode
730 :config 833 :ensure t)
731 (setopt tab-bar-show t)
732 (add-to-list 'tab-bar-format 'tab-bar-format-menu-bar)
733 (add-to-list 'tab-bar-format 'tab-bar-format-align-right :append)
734 (add-to-list 'tab-bar-format 'tab-bar-format-global :append)
735 (with-eval-after-load 'notmuch
736
737 ;; (remove-hook 'notmuch-after-tag-hook #'tab-bar-make-keymap-1)
738 (define-advice notmuch-update-tags (:after (&rest _) update-tab-bar)
739 (tab-bar--update-tab-bar-lines)))
740 (tab-bar-mode))
diff --git a/lisp/acdw-mail.el b/lisp/acdw-mail.el index 8b0ab25..ae78fa0 100644 --- a/lisp/acdw-mail.el +++ b/lisp/acdw-mail.el
@@ -74,6 +74,7 @@ for a free-form search. With any other PREFIX argument, open
74 (mapcar (lambda (elt) 74 (mapcar (lambda (elt)
75 (plist-get elt :name)) 75 (plist-get elt :name))
76 notmuch-saved-searches)) 76 notmuch-saved-searches))
77 notmuch-saved-searches
77 :key (lambda (elt) (plist-get elt :name)) 78 :key (lambda (elt) (plist-get elt :name))
78 :test #'equal) 79 :test #'equal)
79 :query))) 80 :query)))
@@ -127,6 +128,22 @@ the saved search as well."
127 (equal (plist-get a :name) 128 (equal (plist-get a :name)
128 (plist-get b :name)))))) 129 (plist-get b :name))))))
129 130
131(defun notmuch-async-poll ()
132 "Run `notmuch-poll' in an async process."
133 (interactive)
134 (if (require 'async nil t)
135 (progn
136 (message "Polling mail async...")
137 (async-start
138 (lambda ()
139 (push "~/usr/share/emacs/site-lisp/" load-path)
140 (require 'notmuch-lib)
141 (notmuch-poll))
142 (lambda (result)
143 (message "%s" result))))
144 (user-error "Feature `async' not found!")))
145
146
130;;; Packages 147;;; Packages
131 148
132(use-package bbdb 149(use-package bbdb
@@ -174,7 +191,9 @@ the saved search as well."
174 mail-specify-envelope-from t 191 mail-specify-envelope-from t
175 message-sendmail-envelope-from 'header 192 message-sendmail-envelope-from 'header
176 message-envelope-from 'header 193 message-envelope-from 'header
177 notmuch-saved-searches nil) 194 notmuch-saved-searches nil
195 notmuch-poll-script "~/usr/scripts/syncmail" ; XXX: Deprecated option
196 )
178 ;; Key bindings 197 ;; Key bindings
179 (keymap-set notmuch-search-mode-map "!" #'+notmuch-search-mark-spam) 198 (keymap-set notmuch-search-mode-map "!" #'+notmuch-search-mark-spam)
180 (keymap-set notmuch-search-mode-map "RET" #'notmuch-search-show-thread) 199 (keymap-set notmuch-search-mode-map "RET" #'notmuch-search-show-thread)
@@ -205,6 +224,9 @@ the saved search as well."
205 (add-hook 'notmuch-message-mode-hook #'visual-fill-column-mode) 224 (add-hook 'notmuch-message-mode-hook #'visual-fill-column-mode)
206 (add-hook 'notmuch-show-mode-hook #'visual-fill-column-mode) 225 (add-hook 'notmuch-show-mode-hook #'visual-fill-column-mode)
207 226
227 (define-advice notmuch-bury-or-kill-this-buffer (:after (&rest _) poll-async)
228 (notmuch-async-poll))
229
208 (define-advice notmuch-address-selection-function 230 (define-advice notmuch-address-selection-function
209 (:override (prompt collection _) no-initial-input) 231 (:override (prompt collection _) no-initial-input)
210 "Call `completing-read' with `notmuch-address-history'. 232 "Call `completing-read' with `notmuch-address-history'.
diff --git a/lisp/acdw.el b/lisp/acdw.el index a05295c..3b178db 100644 --- a/lisp/acdw.el +++ b/lisp/acdw.el
@@ -2,6 +2,9 @@
2 2
3;;; Code: 3;;; Code:
4 4
5(require 'cl-lib)
6(require 'seq)
7
5(defmacro defdir (name directory &optional docstring makedir) 8(defmacro defdir (name directory &optional docstring makedir)
6 "Define a variable and a function NAME expanding to DIRECTORY. 9 "Define a variable and a function NAME expanding to DIRECTORY.
7DOCSTRING is applied to the variable; its default is DIRECTORY's 10DOCSTRING is applied to the variable; its default is DIRECTORY's
@@ -22,9 +25,9 @@ be created."
22 (make-directory (file-name-directory file-name) :parents)) 25 (make-directory (file-name-directory file-name) :parents))
23 file-name)) 26 file-name))
24 ,(if makedir 27 ,(if makedir
25 `(make-directory ,directory :parents) 28 `(make-directory ,directory :parents)
26 `(unless (file-exists-p ,directory) 29 `(unless (file-exists-p ,directory)
27 (warn "Directory `%s' doesn't exist." ,directory))))) 30 (warn "Directory `%s' doesn't exist." ,directory)))))
28 31
29(defun choose-executable (&rest programs) 32(defun choose-executable (&rest programs)
30 "Return the first of PROGRAMS that exists in the system's $PATH. 33 "Return the first of PROGRAMS that exists in the system's $PATH.
@@ -85,6 +88,17 @@ If `:separator' is the first of STRINGS, the next string will be
85used as a separator." 88used as a separator."
86 (++concat #'format strings)) 89 (++concat #'format strings))
87 90
91(defun list-append-removing-duplicates (&rest lists)
92 "Append LISTS, removing duplicates from the result.
93Any keyword arguments to `cl-remove-duplicates' should come
94before the LISTS."
95 (let (cl-remove-duplicates-args)
96 (while (keywordp (car lists))
97 (push (pop lists) cl-remove-duplicates-args)
98 (push (pop lists) cl-remove-duplicates-args))
99 (apply #'cl-remove-duplicates (apply #'append lists)
100 (nreverse cl-remove-duplicates-args))))
101
88(defun mapc-buffers (func &optional predicate) 102(defun mapc-buffers (func &optional predicate)
89 "Map FUNC over buffers matching PREDICATE. 103 "Map FUNC over buffers matching PREDICATE.
90Both FUNC and PREDICATE will be executed with no arguments and in 104Both FUNC and PREDICATE will be executed with no arguments and in
@@ -125,6 +139,83 @@ each buffer."
125 (progress-reporter-done ,prog))))) 139 (progress-reporter-done ,prog)))))
126 140
127 141
142
143;;; Ispell in .dir-locals
144
145;; Let Emacs know a list of strings is safe
146(defun +ispell-safe-local-p (list)
147 (and (listp list)
148 (seq-every-p #'stringp list)))
149
150;; Can I instruct ispell to insert LocalWords in a different file?
151;; https://emacs.stackexchange.com/q/31396/2264
152
153;; How can I move all my file-local LocalWords to .dir-locals.el?
154;; https://emacs.stackexchange.com/q/31419
155
156;; Adapted from ispell.el:ispell-buffer-local-words
157(defun +ispell-buffer-local-words-list ()
158 (let (words)
159 (or ispell-buffer-local-name
160 (setf ispell-buffer-local-name (buffer-name)))
161 (save-excursion
162 (goto-char (point-min))
163 (while (search-forward ispell-words-keyword nil t)
164 (let ((end (point-at-eol))
165 (ispell-casechars (ispell-get-casechars))
166 string)
167 (while (re-search-forward " *\\([^ ]+\\)" end t)
168 (setf string (match-string-no-properties 1))
169 (if (and (< 1 (length string))
170 (equal 0 (string-match ispell-casechars string)))
171 (push string words))))))
172 words))
173
174;;;###autoload
175(defun +ispell-move-buffer-words-to-dir-locals (&optional arg)
176 "Move the current buffer-local words to .dir-locals.el.
177This function prompts the user to save .dir-locals.el, unless
178prefix ARG is non-nil; then it just saves them."
179 (interactive "P")
180 (unless (buffer-file-name)
181 (user-error "Buffer not attached to file"))
182 (hack-dir-local-variables)
183 (let ((print-level nil)
184 (print-length nil))
185 (when-let ((new-words (cl-remove-if
186 (lambda (el) (eq el '\.\.\.)) ; XXX: NO IDEA
187 ; where this came from
188 (list-append-removing-duplicates
189 :test #'string=
190 ispell-buffer-session-localwords
191 (alist-get 'ispell-buffer-session-localwords
192 dir-local-variables-alist)
193 (alist-get 'ispell-buffer-session-localwords
194 file-local-variables-alist)
195 (+ispell-buffer-local-words-list)))))
196 (save-excursion
197 (add-dir-local-variable
198 major-mode
199 'ispell-buffer-session-localwords
200 (setf ispell-buffer-session-localwords
201 new-words))
202 (when (or arg
203 (y-or-n-p "Save .dir-locals.el?"))
204 (save-buffer))
205 (bury-buffer))
206 (or ispell-buffer-local-name
207 (setf ispell-buffer-local-name (buffer-name)))
208 (save-excursion
209 (goto-char (point-min))
210 (while (search-forward ispell-words-keyword nil t)
211 (delete-region (point-at-bol) (1+ (point-at-eol))))))))
212
213;;;###autoload
214(defun +ispell-move-buffer-words-to-dir-locals-hook ()
215 "Convenience function for binding to a hook."
216 (+ispell-move-buffer-words-to-dir-locals t))
217
218
128;;; Comment-or-uncomment-sexp 219;;; Comment-or-uncomment-sexp
129;; from https://endlessparentheses.com/a-comment-or-uncomment-sexp-command.html 220;; from https://endlessparentheses.com/a-comment-or-uncomment-sexp-command.html
130 221
@@ -214,5 +305,27 @@ With a prefix argument N, (un)comment that many sexps."
214 (dotimes (_ (or n 1)) 305 (dotimes (_ (or n 1))
215 (+lisp-comment-sexp--raw)))) 306 (+lisp-comment-sexp--raw))))
216 307
308
309;;; Random shit
310
311(defun insert-iso-date (&optional arg)
312 "Insert current date formatted ISO-8601 style.
313When called with \\[universal-argument] \\[insert-iso-date],
314include the time. When called with \\[universal-argument]
315\\[universal-argument] \\[insert-iso-date], prompt the user for the
316`format-time-string' format to use."
317 (interactive "P")
318 (insert (format-time-string (pcase arg
319 ('nil "%F")
320 ('(4) "%FT%T%z")
321 (_ (read-string "Time format: "))))))
322
323(defun unfill-paragraph ()
324 "Unfill the current paragraph."
325 (interactive)
326 (let ((fill-column most-positive-fixnum)
327 (fill-paragraph-function nil))
328 (fill-paragraph)))
329
217(provide 'acdw) 330(provide 'acdw)
218;;; acdw.el ends here 331;;; acdw.el ends here