mu
mu copied to clipboard
Help: `open` attachments with completing read (Gnus article view)
Note: for questions, please use the mailing-list: https://groups.google.com/g/mu-discuss
I apologise for disobeying this advice, but it appears i'd have to create an account with Google, Inc. to post to the mu-discuss
list, which i'd rather not be forced to do for this purpose.
Describe the issue
I'm trying to come to terms with the Gnus article view since it's probably better than the old mu4e-view, but old habits are hard to break 😅
I used to make heavy use of o
to open attachments using my system viewer (open
on macOS) or e
to save attachments. The "new" way e
works is actually really nice since it suggests filenames.
I'm less clear on how to open attachments easily. I don't consider moving point to the Gnus button and pressing RET
reasonable – this is very cumbersome.
I'd like mu4e-view-mime-part-action
(or something like it) to make use of Helm or completing read. I don't use my mouse when using Emacs, and i find it awkward to count which MIME part i want to open if there are many (for example, my electricity company sends me a PDF bill but the email has a dozen or so little image attachments).
I wonder if it's possible to have an interface to mu4e-view-mime-part-action
much more similar to mu4e-view-save-attachments
? Or better yet, some way of directly performing the open
action after completing-read from MIME attachments?
Environment
I am on mu
version 1.6.6 from Homebrew, on macOS 11.5.2 (20G95).
I couldn't sit still so i made something that scratches my own itch. I'll post it here in case it's useful to someone else:
;; I want to have a nice direct shortcut to open attachments with the system viewer, so i'm cribbing
;; a bit from `mu4e-view-save-attachments' from mu4e-view-gnus.el. Turns out that if attachments
;; are inline, the "attachment" field isn't correctly set, which messes with the way
;; `mu4e~view-mime-part-to-temp-file' expects to extract filenames. So instead we bring in a bunch
;; of that function here, with tmpdir logic and whatnot.
(defun paul/mu4e-view-open-attachments ()
"Open mime parts from current mu4e gnus view buffer with system
viewer.
When helm-mode is enabled provide completion on attachments and
possibility to mark candidates to save, otherwise completion on
attachments is done with `completing-read-multiple', in this case
use \",\" to separate candidate, completion is provided after
each \",\".
Note, currently this does not work well with file names
containing commas."
(interactive)
(cl-assert (and (eq major-mode 'mu4e-view-mode)
(derived-mode-p 'gnus-article-mode)))
(let* ((parts (mu4e~view-gather-mime-parts))
(handles '())
(files '())
(compfn (if (and (boundp 'helm-mode) helm-mode)
#'completing-read
;; Fallback to `completing-read-multiple' with poor
;; completion
#'completing-read-multiple)))
(dolist (part parts)
(let ((fname (or (cdr (assoc 'filename (assoc "attachment" (cdr part))))
(cl-loop for item in part
for name = (and (listp item) (assoc-default 'name item))
thereis (and (stringp name) name)))))
(when fname
(push `(,fname . ,(cdr part)) handles)
(push fname files))))
(if files
(progn
(setq files (let ((helm-comp-read-use-marked t))
(funcall compfn "Open attachment(s): " files)))
(cl-loop for (f . h) in handles
when (member f files)
do
(progn
(message (format "Opening %s..." f))
(let* ((tmpdir (make-temp-file "mu4e-temp-" t))
(fname (if f
(concat tmpdir "/" (replace-regexp-in-string "/" "-" f))
(let ((temporary-file-directory tmpdir))
(make-temp-file "mimepart")))))
(mm-save-part-to-file h fname)
(mu4e~view-open-file fname)
(run-at-time "30 sec" nil (lambda () (ignore-errors (delete-directory tmpdir t))))))))
(mu4e-message "No attached files found"))))
It's basically an amalgamation of mu4e-view-save-attachments
and mu4e~view-mime-part-to-temp-file
. I'll leave it up to the maintainers whether this still counts as a feature request or if it should simply be closed 😃
paul @.***> writes:
It's basically an amalgamation of mu4e-view-save-attachments and mu4e~view-mime-part-to-temp-file. I'll leave it up to the maintainers whether this still counts as a feature request or if it should simply be closed 😃
It is generally better to send a diff instead of the whole function redefined, this way it is easier to see only what have changed.
Thanks.
-- Thierry
Hey Thierry,
It is generally better to send a diff instead of the whole function redefined, this way it is easier to see only what have changed. Thanks.
My intention actually wasn't to redefine mu4e-view-save-attachments
– i like how it works! In fact i have created a new function for myself, which is inspired by mu4e-view-save-attachments
, but for opening attachments. Nevertheless, here's a diff, as you ask:
--- save.el 2021-09-24 17:58:17.000000000 +1000
+++ open.el 2021-09-24 17:57:26.000000000 +1000
@@ -1,5 +1,6 @@
-(defun mu4e-view-save-attachments (&optional arg)
- "Save mime parts from current mu4e gnus view buffer.
+(defun paul/mu4e-view-open-attachments ()
+ "Open mime parts from current mu4e gnus view buffer with system
+viewer.
When helm-mode is enabled provide completion on attachments and
possibility to mark candidates to save, otherwise completion on
@@ -9,7 +10,7 @@
Note, currently this does not work well with file names
containing commas."
- (interactive "P")
+ (interactive)
(cl-assert (and (eq major-mode 'mu4e-view-mode)
(derived-mode-p 'gnus-article-mode)))
(let* ((parts (mu4e~view-gather-mime-parts))
@@ -19,8 +20,7 @@
#'completing-read
;; Fallback to `completing-read-multiple' with poor
;; completion
- #'completing-read-multiple))
- dir)
+ #'completing-read-multiple)))
(dolist (part parts)
(let ((fname (or (cdr (assoc 'filename (assoc "attachment" (cdr part))))
(cl-loop for item in part
@@ -32,22 +32,18 @@
(if files
(progn
(setq files (let ((helm-comp-read-use-marked t))
- (funcall compfn "Save part(s): " files))
- dir (if arg (read-directory-name "Save to directory: ") mu4e-attachment-dir))
+ (funcall compfn "Open attachment(s): " files)))
(cl-loop for (f . h) in handles
when (member f files)
- do (mm-save-part-to-file
- h (let ((file (expand-file-name f dir)))
- (if (file-exists-p file)
- (let (newname (count 1))
- (while (and
- (setq newname
- (concat
- (file-name-sans-extension file)
- (format "(%s)" count)
- (file-name-extension file t)))
- (file-exists-p newname))
- (cl-incf count))
- newname)
- file)))))
+ do
+ (progn
+ (message (format "Opening %s..." f))
+ (let* ((tmpdir (make-temp-file "mu4e-temp-" t))
+ (fname (if f
+ (concat tmpdir "/" (replace-regexp-in-string "/" "-" f))
+ (let ((temporary-file-directory tmpdir))
+ (make-temp-file "mimepart")))))
+ (mm-save-part-to-file h fname)
+ (mu4e~view-open-file fname)
+ (run-at-time "30 sec" nil (lambda () (ignore-errors (delete-directory tmpdir t))))))))
(mu4e-message "No attached files found"))))
The old o <n>
maps can now be entered as <n> A o
. Really no need to redefine functions.