1

I used to use helm and really liked mu4e-view-save-attachments as a quick way to save multiple attachments in an email at once. I recently switched to Ivy and learned this function is helm specific. Does anyone have an ivy version of this or an equally efficient method of saving multiple attachments at once?

EDIT: This ugly hack almost works (just a slight modification of mu4e-view-save-attachments but I get an error wrong-type-argument listp "whatever-attachment-filename-I-select" the error is happening in the (member f files) function call.

(defun jds/mu4e-view-save-attachments (&optional arg)
  "Save mime parts from current mu4e gnus view buffer.

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 "P")
  (cl-assert (and (eq major-mode 'mu4e-view-mode)
                  (derived-mode-p 'gnus-article-mode)))
  (let* ((parts (mu4e~view-gather-mime-parts))
         (handles '())
         (files '())
         (helm-comp-read-use-marked t)
         (compfn (if (and (boundp 'helm-mode) helm-mode)
                     #'completing-read
                   ;; Fallback to `completing-read-multiple' with poor
                   ;; completion
                   ;;#'completing-read-multiple
                   #'ivy-completing-read))
        dir)
    (dolist (part parts)
      (let ((fname (cdr (assoc 'filename (assoc "attachment" (cdr part))))))
        (when fname
          (push `(,fname . ,(cdr part)) handles)
          (push fname files))))
    (if files
        (progn
          (setq files (funcall compfn "Save part(s): " files)
                dir (if arg (read-directory-name "Save to directory: ") mu4e-attachment-dir))
          (cl-loop for (f . h) in handles
                   when (member f files)
                   do (mm-save-part-to-file h (expand-file-name f dir))))
      (mu4e-message "No attached files found"))))
4
  • I don't think ivy supports multiple selections the way helm does. This might be related: github.com/tmalsburg/helm-bibtex/issues/306
    – Tyler
    Jul 26, 2021 at 15:39
  • 1
    Recent versions have support for multiple selection. oremacs.com/swiper/…
    – jds
    Jul 26, 2021 at 19:00
  • I'm very happy to hear that! I switched to helm for citations, but much prefer ivy. I'll try the new update and see if it works with bibtex now.
    – Tyler
    Jul 26, 2021 at 20:28
  • I think multiple selection only works for ivy-read, and only when an action is defined; it doesn't seem to work for ivy-completing-read. Which means you can't just swap that in as you have done, some additional work will be required.
    – Tyler
    Aug 3, 2021 at 15:33

1 Answer 1

1

As you've discovered, mu4e only accommodates helm-mode or Emacs' built-in completing-read-multiple. In order to use ivy for completions, you need to modify the code directly. In addition, to make use of Ivy's multiple selection features, you need to use ivy-read with an appropriate :action defines.

The following accomplishes this (with much room for refinement!):

(defun jds/mu4e-view-save-attachments (&optional arg)
  "Save mime parts from current mu4e gnus view buffer.

When helm-mode is enabled provide completion on attachments and
possibility to mark candidates to save, otherwise completion on
attachments is done with our custom 
`ivy-completing-read-mu4e-parts'.

Note, currently this does not work well with file names
containing commas."
  (interactive "P")
  (cl-assert (and (eq major-mode 'mu4e-view-mode)
                  (derived-mode-p 'gnus-article-mode)))
  (let* ((parts (mu4e~view-gather-mime-parts))
         (handles '())
         (files '())
         (helm-comp-read-use-marked t)
         (compfn (if (and (boundp 'helm-mode) helm-mode)
                     #'completing-read
                   ;; Use ivy completion:
                   #'ivy-completing-read-mu4e-parts
                   ))
        dir)
    (dolist (part parts)
      (let ((fname (cdr (assoc 'filename (assoc "attachment" (cdr part))))))
        (when fname
          (push `(,fname . ,(cdr part)) handles)
          (push fname files))))
    (if files
        (progn
          (setq files (funcall compfn "Save part(s): " files)
                dir (if arg (read-directory-name "Save to directory: ") mu4e-attachment-dir))
          (cl-loop for (f . h) in handles
                   when (member f files)
                   do (mm-save-part-to-file h (expand-file-name f dir))))
      (mu4e-message "No attached files found"))))

(defun ivy-completing-read-mu4e-parts (PROMPT COLLECTION)
  (let (PARTS)
    (ivy-read PROMPT COLLECTION
              :action (lambda (x)
                        (add-to-list 'PARTS x)))
    PARTS))
1
  • Thank you very much for this!
    – jds
    Aug 5, 2021 at 14:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.