mu4e: emacs 28.1 modernization

We can now use setq-local, length>, string-empty-p, affixations
functions and "choice" defcustoms.
This commit is contained in:
Dirk-Jan C. Binnema
2026-05-25 20:24:37 +03:00
committed by Seth Ladygo
parent dca5334295
commit cbe64d4cae
10 changed files with 79 additions and 58 deletions

View File

@ -238,8 +238,8 @@ When enabled, this attempts to put mu4e's completions at the
start of the buffer-local `completion-at-point-functions'. Other
completion functions still apply."
(when mu4e-compose-complete-addresses
(set (make-local-variable 'completion-ignore-case) t)
(set (make-local-variable 'completion-cycle-threshold) 7)
(setq-local completion-ignore-case t)
(setq-local completion-cycle-threshold 7)
(add-to-list (make-local-variable 'completion-styles) 'substring)
(add-hook 'completion-at-point-functions
#'mu4e--compose-complete-contact-field -10 t)))
@ -289,13 +289,13 @@ buffers; lets remap its faces so it uses the ones for mu4e."
(when (eq major-mode 'mu4e-compose-mode)
(mu4e-warn "Not available in mu4e")))
(defun mu4e--neutralize-undesirables ()
"Beware Gnus commands that do not work with mu4e."
;; the Field menu contains many items that don't apply.
(advice-add 'gnus-delay-article
:before #'mu4e--compose-unsupported) ;; # XXX does not work?!
(advice-add 'message-goto-newsgroups :before #'mu4e--compose-unsupported)
(advice-add 'message-insert-newsgroups :before #'mu4e--compose-unsupported))
;; Neutralize Gnus commands that do not work with mu4e. The advice is a no-op
;; outside mu4e-compose-mode (see `mu4e--compose-unsupported'), so it is safe
;; to install unconditionally at load time.
(advice-add 'gnus-delay-article
:before #'mu4e--compose-unsupported) ;; # XXX does not work?!
(advice-add 'message-goto-newsgroups :before #'mu4e--compose-unsupported)
(advice-add 'message-insert-newsgroups :before #'mu4e--compose-unsupported)
(define-derived-mode mu4e-compose-mode message-mode "mu4e:compose"
"Major mode for the mu4e message composition, derived from `message-mode'.
@ -303,13 +303,12 @@ buffers; lets remap its faces so it uses the ones for mu4e."
(progn
(use-local-map mu4e-compose-mode-map)
(mu4e-context-minor-mode)
(mu4e--neutralize-undesirables)
(mu4e--compose-remap-faces)
(setq-local nobreak-char-display nil)
;; set this to allow mu4e to work when gnus-agent is unplugged in gnus
(set (make-local-variable 'message-send-mail-real-function) nil)
(setq-local message-send-mail-real-function nil)
;; Set to nil to enable `electric-quote-local-mode' to work:
(set (make-local-variable 'comment-use-syntax) nil)
(setq-local comment-use-syntax nil)
(mu4e--compose-setup-completion) ;; maybe offer address completion
(if mu4e-compose-format-flowed ;; format-flowed
(progn

View File

@ -280,7 +280,7 @@ case a phrase contains a quote, it will be escaped."
"Get the full combination of name and email address from CONTACT."
(let* ((email (mu4e-contact-email contact))
(name (mu4e-contact-name contact)))
(if (and name (> (length name) 0))
(if (and name (not (string-empty-p name)))
(format "%s <%s>" (mu4e--rfc822-quote-phrase name) email)
email)))

View File

@ -1185,7 +1185,7 @@ The following specs are supported:
(use-local-map mu4e-headers-mode-map)
(make-local-variable 'mu4e~headers-proc)
(make-local-variable 'mu4e~highlighted-docid)
(set (make-local-variable 'hl-line-face) 'mu4e-header-highlight-face)
(setq-local hl-line-face 'mu4e-header-highlight-face)
;; Eldoc support
(when (and (featurep 'eldoc) mu4e-eldoc-support)
@ -1198,8 +1198,8 @@ The following specs are supported:
#'mu4e-headers-eldoc-function)))
;; support bookmarks.
(set (make-local-variable 'bookmark-make-record-function)
'mu4e--make-bookmark-record)
(setq-local bookmark-make-record-function
#'mu4e--make-bookmark-record)
;; maybe update the current headers upon indexing changes
(add-hook 'mu4e-index-updated-hook #'mu4e~headers-maybe-auto-update)
(setq

View File

@ -721,7 +721,7 @@ shorter keys in some cases where there are multiple bindings."
;; not a perfect heuristic: e.g. '<up>' is longer that 'C-p'
(car-safe
(seq-sort (lambda (b1 b2)
(< (length b1) (length b2)))
(length< b1 (length b2)))
(seq-map #'key-description
(where-is-internal cmd)))))

View File

@ -205,7 +205,7 @@ binding representation, remove that first letter."
(let* ((bindstr (or bindstr (mu4e-key-description cmd) alt
(mu4e-error "No binding for %s" cmd)))
(bindstr
(if (and alt (> (length bindstr) 1)) alt bindstr))
(if (and alt (length> bindstr 1)) alt bindstr))
(title ;; remove first letter afrer [] if it equal last of binding
(string-replace
(concat "[@]" (substring bindstr -1)) "[@]" title))

View File

@ -106,7 +106,7 @@ is the target directory (for \"move\")")
(defun mu4e--mark-initialize ()
"Initialize the marks-subsystem."
(set (make-local-variable 'mu4e--mark-map) (make-hash-table))
(setq-local mu4e--mark-map (make-hash-table))
;; ask user when kill buffer / emacs with live marks.
;; (subject to mu4e-headers-leave-behavior)
(add-hook 'kill-buffer-query-functions

View File

@ -72,6 +72,17 @@ See `mu4e--uniquify-file-name' for an example."
(mm-destroy-parts mu4e--view-gnus-article-mime-handles)
(setq mu4e--view-gnus-article-mime-handles nil)))
;; Temp directories created to hand attachments to external openers; cleaned up
;; when the view buffer is killed.
(defvar-local mu4e--view-temp-dirs nil)
(put 'mu4e--view-temp-dirs 'permanent-local t)
(defun mu4e--view-kill-temp-dirs ()
"Delete temp dirs created for opening MIME-parts externally."
(dolist (dir mu4e--view-temp-dirs)
(ignore-errors (delete-directory dir)))
(setq mu4e--view-temp-dirs nil))
;;; MIME-parts
(defvar-local mu4e--view-mime-parts nil
"Cached MIME parts for this message.")
@ -229,7 +240,7 @@ COMPLETIONS is the list of completion strings to affixate."
'face 'mu4e-system-face))
(target (propertize (or (plist-get part :target-dir) "")
'face 'mu4e-system-face))
(icon (or (and (> (length raw-filename) 0)
(icon (or (and (not (string-empty-p raw-filename))
(mu4e-file-name-to-icon raw-filename))
(mu4e-mime-type-to-icon
(plist-get part :mime-type))))
@ -430,32 +441,33 @@ Each of the actions is a plist with keys
The filename is deduced from the MIME-part's filename, or
otherwise random; the result is placed in a temporary directory
with a unique name. Returns the full path for the file created.
The directory and file are self-destructed."
The directory is registered for cleanup when the current view
buffer is killed (see `mu4e--view-kill-temp-dirs')."
(let* ((tmpdir (make-temp-file "mu4e-temp-" t))
(fname (mm-handle-filename handle))
(fname (and fname
(gnus-map-function mm-file-name-rewrite-functions
(file-name-nondirectory fname))))
(fname (if fname
(concat tmpdir "/" (replace-regexp-in-string "/" "-" fname))
(mu4e-join-paths
tmpdir (replace-regexp-in-string "/" "-" fname))
(let ((temporary-file-directory tmpdir))
(make-temp-file "mimepart")))))
(mm-save-part-to-file handle fname)
(run-at-time "30 sec" nil
(lambda () (ignore-errors (delete-directory tmpdir t))))
(push tmpdir mu4e--view-temp-dirs)
fname))
(defun mu4e--view-open-file (file &optional force-ask)
"Open FILE with default handler, if any.
Otherwise, or if FORCE-ASK is set, ask user for the program to
open with."
Otherwise, or if FORCE-ASK is set, ask user for the shell command
to open with."
(if (and (not force-ask)
(functionp mu4e-view-open-program))
(funcall mu4e-view-open-program file)
(let ((opener
(or (and (not force-ask) mu4e-view-open-program
(executable-find mu4e-view-open-program))
(read-shell-command "Open MIME-part with: "))))
(read-shell-command "Open MIME-part with shell command: "))))
(call-process opener nil 0 nil file))))
(defun mu4e-view-mime-part-action (&optional n)
@ -515,17 +527,16 @@ the third MIME-part."
(cond
((eq receives 'index)
(shell-command
(concat handler " " (shell-quote-argument id))))
(concat handler " " id)))
((eq receives 'pipe)
(progn
(mm-pipe-part handle handler)))
((eq receives 'temp)
(shell-command
(shell-command
(concat
handler " "
(shell-quote-argument
(mu4e--view-mime-part-to-temp-file handle))))))
(concat
handler " "
(shell-quote-argument
(mu4e--view-mime-part-to-temp-file handle)))))
(t (mu4e-error "Invalid action %S" action))))))))
ids)))

View File

@ -281,7 +281,7 @@ limit the stack size."
(unless (and stack (string= (car stack) query))
(push query stack)
;; limit the stack to `mu4e--search-query-stack-size' elements
(when (> (length stack) mu4e--search-query-stack-size)
(when (length> stack mu4e--search-query-stack-size)
(setq stack (cl-subseq stack 0 mu4e--search-query-stack-size)))
;; remove all duplicates of the new element
(seq-remove (lambda (elm) (string= elm (car stack))) (cdr stack))
@ -607,24 +607,30 @@ query before submitting it."
(longest-query
(seq-max (seq-map (lambda (c) (length (plist-get (cdr c) :query)))
candidates)))
(annotation-func
(lambda (candidate)
(let* ((item (cdr-safe (assoc candidate candidates)))
(name (propertize (or (plist-get item :name) "")
'face 'mu4e-header-key-face))
(query (propertize (or (plist-get item :query) "")
'face 'mu4e-header-value-face)))
(concat
" "
(make-string (- longest-name (length name)) ?\s)
query
(make-string (- longest-query (length query)) ?\s)
" "
(mu4e--query-item-display-counts item)))))
(completion-extra-properties
`(:annotation-function ,annotation-func))
(chosen (completing-read "Query: " candidates))
(affixation-func
(lambda (completions)
(mapcar
(lambda (candidate)
(let* ((item (cdr-safe (assoc candidate candidates)))
(query (propertize (or (plist-get item :query) "")
'face 'mu4e-header-value-face))
(suffix
(concat
" "
(make-string (- longest-name (length candidate)) ?\s)
query
(make-string (- longest-query (length query)) ?\s)
" "
(mu4e--query-item-display-counts item))))
(list candidate "" suffix)))
completions)))
(table (lambda (string pred action)
(if (eq action 'metadata)
`(metadata
(category . mu4e-query)
(affixation-function . ,affixation-func))
(complete-with-action action candidates string pred))))
(chosen (completing-read "Query: " table))
(query (or (plist-get (cdr-safe (assoc chosen candidates)) :query)
(mu4e-warn "No query for %s" chosen))))
(mu4e-search-bookmark query edit)))

View File

@ -651,10 +651,11 @@ activates URLs (in plain-text mode only)."
(unless (mu4e--view-html-displayed-p)
(mu4e--view-activate-urls))
(kill-local-variable 'bookmark-make-record-function)
(setq mu4e--gnus-article-mime-handles gnus-article-mime-handles
(setq mu4e--view-gnus-article-mime-handles gnus-article-mime-handles
gnus-article-decoded-p gnus-article-decode-hook)
(set-buffer-modified-p nil)
(add-hook 'kill-buffer-hook #'mu4e--view-kill-mime-handles))
(add-hook 'kill-buffer-hook #'mu4e--view-kill-mime-handles)
(add-hook 'kill-buffer-hook #'mu4e--view-kill-temp-dirs))
(epg-error
(mu4e-message "EPG error: %s; fall back to raw view"
(error-message-string err))))))
@ -789,7 +790,7 @@ filename."
;; (e.g. :user-agent): derive from the keyword name.
(capitalize (substring (symbol-name field) 1))))
(help (plist-get info :help)))
(if (and val (> (length val) 0))
(if (and val (not (string-empty-p val)))
(insert (propertize (concat key ":") 'help-echo help)
" " val "\n"))))
@ -803,7 +804,7 @@ filename."
field info)))
(val (funcall func msg))
(help (plist-get info :help)))
(when (and val (> (length val) 0))
(when (and val (not (string-empty-p val)))
(insert (propertize (concat key ":") 'help-echo help) " " val "\n"))))
(define-advice gnus-icalendar-event-from-handle

View File

@ -103,7 +103,11 @@ A symbol:
For backward compatibility with `mu4e-compose-in-new-frame', t is
treated as =\\'frame."
:type 'symbol
:type '(choice (const :tag "New buffer" nil)
(const :tag "New window" window)
(const :tag "New frame" frame)
(const :tag "New frame (compat)" t)
(const :tag "Use display-buffer" display-buffer))
:group 'mu4e-compose)
(declare-function mu4e-view-mode "mu4e-view")
@ -252,7 +256,7 @@ being created if CREATE is non-nil."
;; Required. The call chain of `mu4e-view-mode' ends up
;; calling `kill-all-local-variables', which destroys the
;; local binding.
(set (make-local-variable 'mu4e-linked-headers-buffer) headers-buffer))
(setq-local mu4e-linked-headers-buffer headers-buffer))
buffer)))
;; backward compat: `display-buffer-full-frame' only appears in emacs 29.