diff --git a/mu4e/mu4e-compose.el b/mu4e/mu4e-compose.el index 3c5642c7..c1ec8d66 100644 --- a/mu4e/mu4e-compose.el +++ b/mu4e/mu4e-compose.el @@ -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 diff --git a/mu4e/mu4e-contacts.el b/mu4e/mu4e-contacts.el index 3f7c2748..a4720ea4 100644 --- a/mu4e/mu4e-contacts.el +++ b/mu4e/mu4e-contacts.el @@ -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))) diff --git a/mu4e/mu4e-headers.el b/mu4e/mu4e-headers.el index f629cc67..dc3b8c04 100644 --- a/mu4e/mu4e-headers.el +++ b/mu4e/mu4e-headers.el @@ -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 diff --git a/mu4e/mu4e-helpers.el b/mu4e/mu4e-helpers.el index c3cb04a6..7ccca16b 100644 --- a/mu4e/mu4e-helpers.el +++ b/mu4e/mu4e-helpers.el @@ -721,7 +721,7 @@ shorter keys in some cases where there are multiple bindings." ;; not a perfect heuristic: e.g. '' 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))))) diff --git a/mu4e/mu4e-main.el b/mu4e/mu4e-main.el index ac6367e3..89b2ecdd 100644 --- a/mu4e/mu4e-main.el +++ b/mu4e/mu4e-main.el @@ -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)) diff --git a/mu4e/mu4e-mark.el b/mu4e/mu4e-mark.el index 42ccfc21..37a8144d 100644 --- a/mu4e/mu4e-mark.el +++ b/mu4e/mu4e-mark.el @@ -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 diff --git a/mu4e/mu4e-mime-parts.el b/mu4e/mu4e-mime-parts.el index 2ddea250..02514f95 100644 --- a/mu4e/mu4e-mime-parts.el +++ b/mu4e/mu4e-mime-parts.el @@ -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))) diff --git a/mu4e/mu4e-search.el b/mu4e/mu4e-search.el index ac1b1d96..d7a8c516 100644 --- a/mu4e/mu4e-search.el +++ b/mu4e/mu4e-search.el @@ -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))) diff --git a/mu4e/mu4e-view.el b/mu4e/mu4e-view.el index 56653125..04948e21 100644 --- a/mu4e/mu4e-view.el +++ b/mu4e/mu4e-view.el @@ -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 diff --git a/mu4e/mu4e-window.el b/mu4e/mu4e-window.el index d6e90827..aa9407ef 100644 --- a/mu4e/mu4e-window.el +++ b/mu4e/mu4e-window.el @@ -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.