Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package maildir-utils for openSUSE:Factory checked in at 2026-06-16 18:33:01 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/maildir-utils (Old) and /work/SRC/openSUSE:Factory/.maildir-utils.new.1981 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "maildir-utils" Tue Jun 16 18:33:01 2026 rev:66 rq:1359794 version:1.14.2 Changes: -------- --- /work/SRC/openSUSE:Factory/maildir-utils/maildir-utils.changes 2026-04-28 12:03:27.895181646 +0200 +++ /work/SRC/openSUSE:Factory/.maildir-utils.new.1981/maildir-utils.changes 2026-06-16 18:33:04.134762990 +0200 @@ -1,0 +2,11 @@ +Tue Jun 16 13:37:25 UTC 2026 - Michael Vetter <[email protected]> + +- Update to 1.14.2: + * silence some MIME part errors + * some emacs 28.1+ modernization + * list org links with M-x mu4e-org-agenda-links + * when replying to a message with some part selected, cite only that part + * fix parsing of the Keywords: headers. If you use those, you're recommend to + re-index your message + +------------------------------------------------------------------- Old: ---- mu-1.14.1.tar.xz New: ---- mu-1.14.2.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ maildir-utils.spec ++++++ --- /var/tmp/diff_new_pack.njKeXD/_old 2026-06-16 18:33:05.690828262 +0200 +++ /var/tmp/diff_new_pack.njKeXD/_new 2026-06-16 18:33:05.702828766 +0200 @@ -17,7 +17,7 @@ Name: maildir-utils -Version: 1.14.1 +Version: 1.14.2 Release: 0 Summary: Maildir indexer and searcher License: GPL-3.0-or-later ++++++ mu-1.14.1.tar.xz -> mu-1.14.2.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.14.1/NEWS.org new/mu-1.14.2/NEWS.org --- old/mu-1.14.1/NEWS.org 2026-04-27 21:03:29.000000000 +0200 +++ new/mu-1.14.2/NEWS.org 2026-06-15 21:48:49.000000000 +0200 @@ -41,6 +41,13 @@ - ~mu4e-compose-crypto-policy~ now also recognizes ~sign-signed-replies~, to automatically sign when replying to signed messages (1.14.1). + - For Org-integration, there is a new command ~mu4e-org-agenda-links~ to pop up + a buffer with a tabulated list of the mu4e links in your ~org-agenda-files~ + (1.14.2). + + - When replying to message with a region marked, cite only the marked region. + A very only feature-request, finally implemented. + *** scm - added new ~--eval~ command-line option, so you can do e.g. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.14.1/lib/message/mu-message.cc new/mu-1.14.2/lib/message/mu-message.cc --- old/mu-1.14.1/lib/message/mu-message.cc 2026-04-27 21:03:29.000000000 +0200 +++ new/mu-1.14.2/lib/message/mu-message.cc 2026-06-15 21:48:49.000000000 +0200 @@ -317,7 +317,7 @@ extract_tags(const MimeMessage& mime_msg) { constexpr std::array<std::pair<const char*, char>, 3> tag_headers = {{ - {"X-Label", ' '}, {"X-Keywords", ','}, {"Keywords", ' '} + {"X-Label", ' '}, {"X-Keywords", ','}, {"Keywords", ','} }}; std::vector<std::string> tags; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.14.1/lib/tests/test-mu-msg.cc new/mu-1.14.2/lib/tests/test-mu-msg.cc --- old/mu-1.14.1/lib/tests/test-mu-msg.cc 2026-04-27 21:03:29.000000000 +0200 +++ new/mu-1.14.2/lib/tests/test-mu-msg.cc 2026-06-15 21:48:49.000000000 +0200 @@ -278,6 +278,18 @@ } static void +test_mu_msg_tags_keyword() +{ + g_test_bug("2933"); + + const auto msg{Message::make_from_path(MU_TESTMAILDIR4 "/181736.eml").value()}; + const auto expected = std::to_array<std::string>({"aap", "noot", "mies"}); + + assert_equal_seq_str(msg.tags(), expected); +} + + +static void test_mu_msg_comp_unix_programmer() { auto msg{Message::make_from_path(MU_TESTMAILDIR4 "/181736.eml").value()}; @@ -380,6 +392,8 @@ g_test_add_func("/msg/mu-msg-flags", test_mu_msg_flags); g_test_add_func("/msg/mu-msg-tags", test_mu_msg_tags); + g_test_add_func("/msg/mu-msg-tags-keyword", test_mu_msg_tags_keyword); + g_test_add_func("/msg/mu-msg-references", test_mu_msg_references); g_test_add_func("/msg/mu-msg-references_dups", test_mu_msg_references_dups); g_test_add_func("/msg/mu-msg-references_many", test_mu_msg_references_many); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.14.1/man/mu-move.1.org new/mu-1.14.2/man/mu-move.1.org --- old/mu-1.14.1/man/mu-move.1.org 2026-04-27 21:03:29.000000000 +0200 +++ new/mu-1.14.2/man/mu-move.1.org 2026-06-15 21:48:49.000000000 +0200 @@ -84,7 +84,6 @@ Absolute flags just specify the new flags by their letters; e.g. to specify a /Trashed/, /Seen/, /Replied/ message, you'd use *--flags STR*. -#+end_example Relative flags are relative to the current flags for some message, and each of the flags is prefixed with either *+* ("add this flag") or *-* ("remove this flag"). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.14.1/meson.build new/mu-1.14.2/meson.build --- old/mu-1.14.1/meson.build 2026-04-27 21:03:29.000000000 +0200 +++ new/mu-1.14.2/meson.build 2026-06-15 21:48:49.000000000 +0200 @@ -17,7 +17,7 @@ ################################################################################ # project setup project('mu', ['c', 'cpp'], - version: '1.14.1', + version: '1.14.2', meson_version: '>= 1.3.2', license: 'GPL-3.0-or-later', default_options : [ @@ -28,7 +28,7 @@ # hard-code the date here (for reproducibility); we derive the dates used in # e.g. documentation from this. -mu_date='2026-04-27' +mu_date='2026-06-15' # installation paths prefixdir = get_option('prefix') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.14.1/mu4e/mu4e-actions.el new/mu-1.14.2/mu4e/mu4e-actions.el --- old/mu-1.14.1/mu4e/mu4e-actions.el 2026-04-27 21:03:29.000000000 +0200 +++ new/mu-1.14.2/mu4e/mu4e-actions.el 2026-06-15 21:48:49.000000000 +0200 @@ -27,7 +27,6 @@ ;;; Code: -(require 'ido) (require 'browse-url) (require 'mu4e-helpers) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.14.1/mu4e/mu4e-compose.el new/mu-1.14.2/mu4e/mu4e-compose.el --- old/mu-1.14.1/mu4e/mu4e-compose.el 2026-04-27 21:03:29.000000000 +0200 +++ new/mu-1.14.2/mu4e/mu4e-compose.el 2026-06-15 21:48:49.000000000 +0200 @@ -238,8 +238,8 @@ 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))) @@ -268,9 +268,6 @@ (define-key map (kbd "C-c C-u") #'mu4e-update-mail-and-index) (define-key map (kbd "C-c ;") #'mu4e-compose-context-switch) - ;; emacs 29 - ;;(keymap-set map "<remap> <beginning-of-buffer>" #'mu4e-compose-goto-top) - ;;(keymap-set map "<remap> <end-of-buffer>" #'mu4e-compose-goto-bottom) (define-key map (vector 'remap #'beginning-of-buffer) #'mu4e-compose-goto-top) (define-key map (vector 'remap #'end-of-buffer) @@ -289,13 +286,13 @@ (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 +300,12 @@ (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 @@ -329,19 +325,26 @@ (message-cite-original-without-signature) (delete-region (point-min) (point-max)))) -(defun mu4e--compose-cite (msg) - "Return a cited version of the ORIG message MSG (a string). -This function uses `message-cite-function', and its settings apply." - (with-temp-buffer - (insert (mu4e-view-message-text msg)) - (goto-char (point-min)) - (push-mark (point-max)) - (let ((message-signature-separator "^-- *$") - (message-signature-insert-empty-line t)) - (funcall message-cite-function)) - (pop-mark) - (goto-char (point-min)) - (buffer-string))) +(defun mu4e--compose-cite (msg &optional region) + "Return a cited version of the message MSG (a string). +If REGION is non-nil, cite it instead of MSG' body. This function +uses `message-cite-function', and its settings apply." + (let ((orig (mu4e-view-message-text msg))) + (with-temp-buffer + (insert orig) + (goto-char (point-min)) + ;; if we have a region, replace the body with it. + (save-excursion + (when (and region (re-search-forward "\n\n" nil 'noerror)) + (delete-region (point) (point-max)) + (insert region))) + (push-mark (point-max)) + (let ((message-signature-separator "^-- *$") + (message-signature-insert-empty-line t)) + (funcall message-cite-function)) + (pop-mark) + (goto-char (point-min)) + (buffer-string)))) ;;; Interactive functions @@ -369,15 +372,24 @@ (defun mu4e-compose-reply-to (&optional to wide) "Reply to the message at point. Optional TO can be the To: address for the message. If WIDE is -non-nil, make it a \"wide\" reply (a.k.a. \"reply-to-all\")." +non-nil, make it a \"wide\" reply (a.k.a. \"reply-to-all\"). + +The original message body is cited (as per +`message-cite-function'). If some region in the view buffer is +selected, that region is used instead of the original message +body for citing.'" (interactive) - (let ((parent (mu4e-message-at-point))) + (let ((parent (mu4e-message-at-point)) + (region (when (and (eq major-mode 'mu4e-view-mode) + (use-region-p)) + (buffer-substring-no-properties + (region-beginning) (region-end))))) (mu4e--draft-with-parent 'reply parent (lambda () (with-current-buffer (mu4e--message-call #'message-reply to wide) (message-goto-body) - (insert (mu4e--compose-cite parent)) + (insert (mu4e--compose-cite parent region)) ;; include matching MIME parts from the parent (mu4e--reply-insert-mime-parts parent) (current-buffer)))))) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.14.1/mu4e/mu4e-contacts.el new/mu-1.14.2/mu4e/mu4e-contacts.el --- old/mu-1.14.1/mu4e/mu4e-contacts.el 2026-04-27 21:03:29.000000000 +0200 +++ new/mu-1.14.2/mu4e/mu4e-contacts.el 2026-06-15 21:48:49.000000000 +0200 @@ -280,7 +280,7 @@ "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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.14.1/mu4e/mu4e-contrib.el new/mu-1.14.2/mu4e/mu4e-contrib.el --- old/mu-1.14.1/mu4e/mu4e-contrib.el 2026-04-27 21:03:29.000000000 +0200 +++ new/mu-1.14.2/mu4e/mu4e-contrib.el 2026-06-15 21:48:49.000000000 +0200 @@ -132,20 +132,6 @@ -;; backward compat until 27.1 is univeral. -(defalias 'mu4e--flatten-list - (if (fboundp 'flatten-list) - #'flatten-list - (with-no-warnings - #'eshell-flatten-list))) - -;; backward compat ntil 28.1 is universal. -(defalias 'mu4e--mm-default-file-type - (if (fboundp 'mm-default-file-type) - #'mm-default-file-type - (with-no-warnings - #'mm-default-file-encoding))) - (defun eshell/mu4e-attach (&rest args) "Attach files to a mu4e message using eshell with ARGS. If no mu4e buffers found, compose a new message and then attach @@ -159,7 +145,7 @@ (lambda (f) (when (and (file-exists-p f) (not (file-directory-p f))) (expand-file-name f))) - (mu4e--flatten-list (reverse args))))) + (flatten-list (reverse args))))) ;; warn if user tries to attach without any files marked (if (null files-to-attach) (error "No files to attach") @@ -188,7 +174,7 @@ (goto-char (point-max)) ; attach at end of buffer (while files-to-attach (mml-attach-file (car files-to-attach) - (or (mu4e--mm-default-file-type + (or (mm-default-file-type (car files-to-attach)) "application/octet-stream") nil) (setq files-to-attach (cdr files-to-attach))) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.14.1/mu4e/mu4e-draft.el new/mu-1.14.2/mu4e/mu4e-draft.el --- old/mu-1.14.1/mu4e/mu4e-draft.el 2026-04-27 21:03:29.000000000 +0200 +++ new/mu-1.14.2/mu4e/mu4e-draft.el 2026-06-15 21:48:49.000000000 +0200 @@ -254,8 +254,7 @@ This filename is used for the draft message and the sent message, depending on `mu4e-sent-messages-behavior'." - (let* ((sysname (if (fboundp 'system-name) - (system-name) (with-no-warnings system-name))) + (let* ((sysname (system-name)) (sysname (if (string= sysname "") "localhost" sysname)) (hostname (downcase (save-match-data @@ -364,8 +363,9 @@ (defun mu4e--draft-set-friendly-buffer-name () "Use some friendly name for this draft buffer." (let* ((subj (message-field-value "subject")) - (subj (if (or (not subj) (string-match "^[:blank:]*$" subj)) - "No subject" subj))) + (subj (if (or (not subj) + (string-match-p (rx bos (* blank) eos) subj)) + "No subject" subj))) (rename-buffer (generate-new-buffer-name (format "\"%s\"" (truncate-string-to-width subj diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.14.1/mu4e/mu4e-headers.el new/mu-1.14.2/mu4e/mu4e-headers.el --- old/mu-1.14.1/mu4e/mu4e-headers.el 2026-04-27 21:03:29.000000000 +0200 +++ new/mu-1.14.2/mu4e/mu4e-headers.el 2026-06-15 21:48:49.000000000 +0200 @@ -1185,21 +1185,16 @@ (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) - (if (boundp 'eldoc-documentation-functions) - ;; Emacs 28 or newer - (add-hook 'eldoc-documentation-functions - #'mu4e-headers-eldoc-function nil t) - ;; Emacs 27 or older - (add-function :before-until (local 'eldoc-documentation-function) - #'mu4e-headers-eldoc-function))) + (add-hook 'eldoc-documentation-functions + #'mu4e-headers-eldoc-function nil t)) ;; 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.14.1/mu4e/mu4e-helpers.el new/mu-1.14.2/mu4e/mu4e-helpers.el --- old/mu-1.14.1/mu4e/mu4e-helpers.el 2026-04-27 21:03:29.000000000 +0200 +++ new/mu-1.14.2/mu4e/mu4e-helpers.el 2026-06-15 21:48:49.000000000 +0200 @@ -28,7 +28,6 @@ ;;; Code: (require 'seq) -(require 'ido) (require 'cl-lib) (require 'bookmark) (require 'message) @@ -360,9 +359,8 @@ (unless (get-buffer mu4e--log-buffer-name) (with-current-buffer (get-buffer-create mu4e--log-buffer-name) (view-mode) - (when (fboundp 'so-long-mode) - (unless (eq major-mode 'so-long-mode) - (eval '(so-long-mode)))) + (unless (eq major-mode 'so-long-mode) + (so-long-mode)) (setq buffer-undo-list t))) mu4e--log-buffer-name) @@ -536,11 +534,16 @@ (let ((timestr (read-string (mu4e-format "%s" prompt)))) (apply 'encode-time (parse-time-string timestr)))) + +(defvar mu4e--temp-dir nil "Directory for temporary files. +Created when mu4e starts, and removed when it closes.") + (defun mu4e-make-temp-file (ext) "Create a self-destructing temporary file with extension EXT. The file will self-destruct in a short while, enough to open it in an external program." - (let ((tmpfile (make-temp-file "mu4e-" nil (concat "." ext)))) + (let* ((temporary-file-directory (or mu4e--temp-dir temporary-file-directory)) + (tmpfile (make-temp-file "mu4e-" nil (concat "." ext)))) (run-at-time "30 sec" nil (lambda () (ignore-errors (delete-file tmpfile)))) tmpfile)) @@ -722,7 +725,7 @@ ;; 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))))) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.14.1/mu4e/mu4e-main.el new/mu-1.14.2/mu4e/mu4e-main.el --- old/mu-1.14.1/mu4e/mu4e-main.el 2026-04-27 21:03:29.000000000 +0200 +++ new/mu-1.14.2/mu4e/mu4e-main.el 2026-06-15 21:48:49.000000000 +0200 @@ -205,7 +205,7 @@ (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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.14.1/mu4e/mu4e-mark.el new/mu-1.14.2/mu4e/mu4e-mark.el --- old/mu-1.14.1/mu4e/mu4e-mark.el 2026-04-27 21:03:29.000000000 +0200 +++ new/mu-1.14.2/mu4e/mu4e-mark.el 2026-06-15 21:48:49.000000000 +0200 @@ -106,7 +106,7 @@ (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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.14.1/mu4e/mu4e-message.el new/mu-1.14.2/mu4e/mu4e-message.el --- old/mu-1.14.1/mu4e/mu4e-message.el 2026-04-27 21:03:29.000000000 +0200 +++ new/mu-1.14.2/mu4e/mu4e-message.el 2026-06-15 21:48:49.000000000 +0200 @@ -249,9 +249,7 @@ (kill-buffer mu4e--sexp-buffer-name)) (with-current-buffer-window (get-buffer-create mu4e--sexp-buffer-name) nil nil - (if (fboundp 'lisp-data-mode) - (lisp-data-mode) - (lisp-mode)) + (lisp-data-mode) (insert (pp-to-string msg)) (font-lock-ensure) ;; add basic `quit-window' bindings diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.14.1/mu4e/mu4e-mime-parts.el new/mu-1.14.2/mu4e/mu4e-mime-parts.el --- old/mu-1.14.1/mu4e/mu4e-mime-parts.el 2026-04-27 21:03:29.000000000 +0200 +++ new/mu-1.14.2/mu4e/mu4e-mime-parts.el 2026-06-15 21:48:49.000000000 +0200 @@ -1,6 +1,6 @@ ;;; mu4e-mime-parts.el --- Dealing with MIME-parts & URLs -*- lexical-binding: t -*- -;; Copyright (C) 2023-2024 Dirk-Jan C. Binnema +;; Copyright (C) 2023-2026 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema <[email protected]> ;; Maintainer: Dirk-Jan C. Binnema <[email protected]> @@ -63,14 +63,25 @@ ;; remember the mime-handles, so we can clean them up when ;; we quit this buffer. -(defvar-local mu4e~gnus-article-mime-handles nil) -(put 'mu4e~gnus-article-mime-handles 'permanent-local t) -(defun mu4e--view-kill-mime-handles () - "Kill cached MIME-handles, if any." - (when mu4e~gnus-article-mime-handles - (mm-destroy-parts mu4e~gnus-article-mime-handles) - (setq mu4e~gnus-article-mime-handles nil))) +(defvar-local mu4e--view-gnus-article-mime-handles nil + "MIME handles for the message in this buffer.") +(put 'mu4e--view-gnus-article-mime-handles 'permanent-local t) + +(defvar-local mu4e--view-temp-files nil + "Temporary files.") +(put 'mu4e--view-temp-files 'permanent-local t) + +(defun mu4e--view-buffer-cleanup () + "Clean-up some internals when killing the view buffer." + ;; Kill cached MIME-handles, if any. + (when mu4e--view-gnus-article-mime-handles + (mm-destroy-parts mu4e--view-gnus-article-mime-handles) + (setq mu4e--view-gnus-article-mime-handles nil)) + ;; Delete temp files created for opening MIME-parts externally. + (dolist (file mu4e--view-temp-files) + (ignore-errors (delete-file file))) + (setq mu4e--view-temp-files nil)) ;;; MIME-parts (defvar-local mu4e--view-mime-parts nil @@ -229,7 +240,7 @@ '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)))) @@ -427,35 +438,39 @@ (defun mu4e--view-mime-part-to-temp-file (handle) "Write MIME-part HANDLE to a temporary file and return the file name. + 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." - (let* ((tmpdir (make-temp-file "mu4e-temp-" t)) - (fname (mm-handle-filename handle)) + +The file is registered for cleanup when the current view +buffer is killed." + (let* ((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)) - (let ((temporary-file-directory tmpdir)) - (make-temp-file "mimepart"))))) + (mu4e--uniquify-file-name + (mu4e-join-paths + mu4e--temp-dir + (replace-regexp-in-string "/" "-" fname))) + (let ((temporary-file-directory mu4e--temp-dir)) + (make-temp-file "mime-part-"))))) (mm-save-part-to-file handle fname) - (run-at-time "30 sec" nil - (lambda () (ignore-errors (delete-directory tmpdir t)))) + (push fname mu4e--view-temp-files) 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 +530,16 @@ (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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.14.1/mu4e/mu4e-org.el new/mu-1.14.2/mu4e/mu4e-org.el --- old/mu-1.14.1/mu4e/mu4e-org.el 2026-04-27 21:03:29.000000000 +0200 +++ new/mu-1.14.2/mu4e/mu4e-org.el 2026-06-15 21:48:49.000000000 +0200 @@ -1,6 +1,6 @@ ;;; mu4e-org --- Org-links to mu4e messages/queries -*- lexical-binding: t -*- -;; Copyright (C) 2012-2024 Dirk-Jan C. Binnema +;; Copyright (C) 2012-2026 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema <[email protected]> ;; Maintainer: Dirk-Jan C. Binnema <[email protected]> @@ -28,6 +28,8 @@ ;;; Code: (require 'org) +(require 'org-element) +(require 'tabulated-list) (require 'mu4e-view) (require 'mu4e-contacts) @@ -147,5 +149,146 @@ (org-link-set-parameters "mu4e" :follow #'mu4e-org-open :store #'mu4e-org-store-link) + + +(defun mu4e-org-links-in-buffer (&optional file) + "List all mu4e:msgid links in the current `org-mode' buffer. + +The list elements are plists with: + :link the org-link + :msgid the message-id it refers to + :context a string describing the org item + :file the FILE in which this link was found + +If FILE is non-nil: + :start the start position of the link +plists." + (save-excursion + (org-with-wide-buffer + (goto-char (point-min)) + (org-element-map (org-element-parse-buffer) 'link + (lambda (link) + (let* ((type (org-element-property :type link)) + (raw-link (org-element-property :raw-link link)) + (start (org-element-property :contents-begin link)) + (path (org-element-property :path link)) + (msgid "msgid:")) + ;; a little hacky; seems org-mode doesn't have something built-in for + ;; this. + (when (string= type "mu4e") + (when-let* ((msgid (when (string-prefix-p msgid path) + (substring path (length msgid))))) + (let* ((begin (org-element-property :begin link)) + (context-line + (save-excursion + (goto-char begin) + (org-link-display-format + (string-trim + (buffer-substring-no-properties + (line-beginning-position) + (line-end-position)))))) + (info (list :link raw-link + :msgid msgid + :context context-line + :start start)) + (info + (if file + (plist-put info :file file) + info))) + info))))) + nil nil nil nil t)))) + +(defun mu4e-org-links-in-agenda-files () + "List all mu4e:msgid: links in `org-agenda-files'. + +The list elements are plists as per `mu4e-org-links-in-buffer'.." + (let ((msgid-links '())) + (dolist (file org-agenda-files) + (when (file-exists-p file) + (with-current-buffer (find-file-noselect file) + (when-let* ((buflinks (mu4e-org-links-in-buffer file))) + (setq msgid-links (append msgid-links buflinks)))))) + msgid-links)) + +(defcustom mu4e-org-agenda-links-context-length 60 + "Maximum length of the Context column in `mu4e-org-agenda-links'. +Longer context strings are ellipsized to fit." + :type 'integer + :group 'mu4e-org) + +(defvar-keymap mu4e-org-agenda-links-mode-map + :doc "Keymap for `mu4e-org-agenda-links-mode'." + :parent tabulated-list-mode-map + "RET" #'mu4e-org-agenda-links-follow + "o" #'mu4e-org-agenda-links-visit-file) + +(define-derived-mode mu4e-org-agenda-links-mode tabulated-list-mode + "mu4e-org-agenda-links" + "Major mode for listing mu4e:msgid links found in `org-agenda-files'." + (setq tabulated-list-format + (vector + (list "Context" mu4e-org-agenda-links-context-length nil) + (list "File" 25 t))) + (setq tabulated-list-padding 2) + (tabulated-list-init-header)) + +(defun mu4e-org-agenda-links--entry-at-point () + "Return the link entry at point, or signal an error." + (or (tabulated-list-get-id) + (mu4e-error "No entry at point"))) + +(defun mu4e-org-agenda-links-follow (&optional other-window) + "Follow the mu4e link at point. +With prefix arg OTHER-WINDOW, display the result in another window." + (interactive "P") + (let ((link (plist-get (mu4e-org-agenda-links--entry-at-point) :link))) + (if other-window + (let ((display-buffer-overriding-action + '((display-buffer-reuse-window + display-buffer-pop-up-window) + (inhibit-same-window . t)))) + (org-link-open-from-string link))1 + (org-link-open-from-string link)))) + +(defun mu4e-org-agenda-links-visit-file (&optional other-window) + "Visit the org file for the link at point, at its source location. +With prefix arg OTHER-WINDOW, visit in another window." + (interactive "P") + (let* ((entry (mu4e-org-agenda-links--entry-at-point)) + (file (plist-get entry :file)) + (start (plist-get entry :start))) + (unless (and file (file-exists-p file)) + (mu4e-error "File not found: %s" file)) + (if other-window + (find-file-other-window file) + (find-file file)) + (when start (goto-char start)))) + +;;;###autoload +(defun mu4e-org-agenda-links () + "Show mu4e:msgid links found in `org-agenda-files'. +Display a tabulated-list buffer with Context and File columns." + (interactive) + (let ((buf (get-buffer-create "*mu4e-org-agenda-links*"))) + (with-current-buffer buf + (mu4e-org-agenda-links-mode) + (setq tabulated-list-entries + (mapcar + (lambda (entry) + (let* ((file (or (plist-get entry :file) "")) + (context (or (plist-get entry :context) "")) + (short (truncate-string-to-width + context + mu4e-org-agenda-links-context-length + nil nil t))) + (list entry + (vector + (propertize short 'face 'mu4e-link-face) + (propertize (file-name-nondirectory file) + 'face 'mu4e-title-face))))) + (mu4e-org-links-in-agenda-files))) + (tabulated-list-print)) + (pop-to-buffer buf))) + (provide 'mu4e-org) ;;; mu4e-org.el ends here diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.14.1/mu4e/mu4e-search.el new/mu-1.14.2/mu4e/mu4e-search.el --- old/mu-1.14.1/mu4e/mu4e-search.el 2026-04-27 21:03:29.000000000 +0200 +++ new/mu-1.14.2/mu4e/mu4e-search.el 2026-06-15 21:48:49.000000000 +0200 @@ -281,7 +281,7 @@ (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 @@ (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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.14.1/mu4e/mu4e-vars.el new/mu-1.14.2/mu4e/mu4e-vars.el --- old/mu-1.14.1/mu4e/mu4e-vars.el 2026-04-27 21:03:29.000000000 +0200 +++ new/mu-1.14.2/mu4e/mu4e-vars.el 2026-06-15 21:48:49.000000000 +0200 @@ -128,8 +128,7 @@ :group 'mu4e-faces) (defface mu4e-header-highlight-face - `((t :inherit hl-line :weight bold :underline t - ,@(and (>= emacs-major-version 27) '(:extend t)))) + '((t :inherit hl-line :weight bold :underline t :extend t)) "Face for the header at point." :group 'mu4e-faces) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.14.1/mu4e/mu4e-view.el new/mu-1.14.2/mu4e/mu4e-view.el --- old/mu-1.14.1/mu4e/mu4e-view.el 2026-04-27 21:03:29.000000000 +0200 +++ new/mu-1.14.2/mu4e/mu4e-view.el 2026-06-15 21:48:49.000000000 +0200 @@ -474,6 +474,12 @@ (if (memq mu4e-split-view '(horizontal vertical)) (delete-windows-on existing-buffer t)) (kill-buffer existing-buffer)) + + ;; HACK: we create a *Summary* buffer, since its mere existence + ;; is enough for some gnus commands / functions to work in + ;; in the mu4e view as well. + (get-buffer-create (or gnus-summary-buffer "*Summary*")) + (setq gnus-article-buffer (mu4e-get-view-buffer nil t)) (with-current-buffer gnus-article-buffer (when linked-headers-buffer @@ -645,10 +651,10 @@ (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-buffer-cleanup)) (epg-error (mu4e-message "EPG error: %s; fall back to raw view" (error-message-string err)))))) @@ -759,7 +765,7 @@ (dolist (p positions) (when-let* ((handle (get-text-property p 'gnus-data)) ((listp handle)) - (name (mm-handle-filename handle)) + (name (ignore-errors (mm-handle-filename handle))) (icon (mu4e-file-name-to-icon name))) (goto-char p) (insert icon " ")))))) @@ -783,7 +789,7 @@ ;; (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")))) @@ -797,7 +803,7 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.14.1/mu4e/mu4e-window.el new/mu-1.14.2/mu4e/mu4e-window.el --- old/mu-1.14.1/mu4e/mu4e-window.el 2026-04-27 21:03:29.000000000 +0200 +++ new/mu-1.14.2/mu4e/mu4e-window.el 2026-06-15 21:48:49.000000000 +0200 @@ -103,7 +103,11 @@ 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") @@ -187,16 +191,6 @@ (eq (mu4e--get-current-buffer-type) type)) -;; backward-compat; buffer-local-boundp was introduced in emacs 28. -(defun mu4e--buffer-local-boundp (symbol buffer) - "Return non-nil if SYMBOL is bound in BUFFER. -Also see `local-variable-p'." - (condition-case nil - (buffer-local-value symbol buffer) - (:success t) - (void-variable nil))) - - (defun mu4e-get-view-buffer (&optional headers-buffer create) "Return a view buffer belonging optionally to HEADERS-BUFFER. @@ -225,7 +219,7 @@ (linked-buffer (mu4e-get-view-buffers (lambda (buf) - (and (mu4e--buffer-local-boundp 'mu4e-linked-headers-buffer buf) + (and (buffer-local-boundp 'mu4e-linked-headers-buffer buf) (eq mu4e-linked-headers-buffer headers-buffer)))))) ;; If such a linked buffer exists and its buffer is live, we use that ;; buffer. @@ -252,7 +246,7 @@ ;; 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. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.14.1/mu4e/mu4e.el new/mu-1.14.2/mu4e/mu4e.el --- old/mu-1.14.1/mu4e/mu4e.el 2026-04-27 21:03:29.000000000 +0200 +++ new/mu-1.14.2/mu4e/mu4e.el 2026-06-15 21:48:49.000000000 +0200 @@ -158,6 +158,9 @@ Otherwise, check requirements, then start mu4e. When successful, invoke FUNC (if available) afterwards." + ;; create dir for temporary files. + (unless (and mu4e--temp-dir (file-directory-p mu4e--temp-dir)) + (setq mu4e--temp-dir (make-temp-file "mu4e-" t))) (unless (mu4e-context-current) (mu4e--context-autoswitch nil mu4e-context-policy)) (setq mu4e-pong-func @@ -216,7 +219,10 @@ (when (member major-mode '(mu4e-headers-mode mu4e-view-mode mu4e-main-mode)) (kill-buffer))))) - (buffer-list))) + (buffer-list)) + (when mu4e--temp-dir + (ignore-errors (delete-directory mu4e--temp-dir 'recursive)) + (setq mu4e--temp-dir nil))) ;;; Handlers (defun mu4e--default-handler (&rest args) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.14.1/mu4e/mu4e.texi new/mu-1.14.2/mu4e/mu4e.texi --- old/mu-1.14.1/mu4e/mu4e.texi 2026-04-27 21:03:29.000000000 +0200 +++ new/mu-1.14.2/mu4e/mu4e.texi 2026-06-15 21:48:49.000000000 +0200 @@ -1819,7 +1819,8 @@ By default, the reply will cite the message being replied to. If you do not want that, you can set (or @code{let}-bind) @code{message-cite-function} to -@code{mu4e-message-cite-nothing}. +@code{mu4e-message-cite-nothing}. When you have a region selected while +replying, only the selected part is cited. See @ref{(message) Reply} and @ref{(message) Wide Reply} for further details. @@ -3826,6 +3827,15 @@ (define-key mu4e-view-mode-map (kbd "C-c c") 'mu4e-org-store-and-capture) @end lisp +@subsection Listing linked messages + +@t{mu4e} offers a command @kbd{mu4e-org-agenda-links}. This provides a buffer +that shows a tabulated list of all the mu4e message links found in your +@code{org-agenda-files}. + +Pressing @kbd{RET} opens the link, while @kbd{o} visits the Org-file where the +links was found. When prefixed by @kbd{C-u} these use the 'other window'. + @subsection Tracking sent mail To build on the above, it can be useful to automatically track outgoing e-mail diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.14.1/testdata/testdir4/181736.eml new/mu-1.14.2/testdata/testdir4/181736.eml --- old/mu-1.14.1/testdata/testdir4/181736.eml 2026-04-27 21:03:29.000000000 +0200 +++ new/mu-1.14.2/testdata/testdir4/181736.eml 2026-06-15 21:48:49.000000000 +0200 @@ -10,6 +10,7 @@ X-Complaints-To: [email protected] Message-ID: <[email protected]> Date: 08 Mar 2011 17:04:20 GMT +Keywords: aap, noot,mies Lines: 27 Xref: uutiset.elisa.fi comp.unix.programmer:181736
