mu icon indicating copy to clipboard operation
mu copied to clipboard

Help: `open` attachments with completing read (Gnus article view)

Open toothbrush opened this issue 2 years ago • 4 comments

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).

toothbrush avatar Sep 24 '21 04:09 toothbrush

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 😃

toothbrush avatar Sep 24 '21 06:09 toothbrush

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

thierryvolpiatto avatar Sep 24 '21 07:09 thierryvolpiatto

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"))))

toothbrush avatar Sep 24 '21 07:09 toothbrush

The old o <n> maps can now be entered as <n> A o. Really no need to redefine functions.

djcb avatar Sep 26 '21 10:09 djcb