[notmuch] [PATCH] Rework saving of attachments.
From: Keith AmidonWith this commit notmuch-show-mode supports saving a single attachment from a message (bound to 'w') and saving all attachments to the message (bound to 'W'). The new variable notmuch-default-save-dir allows the user to specify a directory within which attachments should be saved by default. The user can modify this default path, even specifying a non-existent directory path, in which case he or she will be prompted to create the path. Reporting of the actions taken is also improved. --- notmuch.el | 93 ++- 1 files changed, 72 insertions(+), 21 deletions(-) diff --git a/notmuch.el b/notmuch.el index 97914f2..b72548d 100644 --- a/notmuch.el +++ b/notmuch.el @@ -64,7 +64,8 @@ (define-key map "f" 'notmuch-show-forward-current) (define-key map "r" 'notmuch-show-reply) (define-key map "|" 'notmuch-show-pipe-message) -(define-key map "w" 'notmuch-show-save-attachments) +(define-key map "w" 'notmuch-show-save-attachment) +(define-key map "W" 'notmuch-show-save-all-attachments) (define-key map "V" 'notmuch-show-view-raw-message) (define-key map "v" 'notmuch-show-view-all-mime-parts) (define-key map "-" 'notmuch-show-remove-tag) @@ -98,6 +99,9 @@ pattern can still test against the entire line).") (defvar notmuch-command "notmuch" "Command to run the notmuch binary.") +(defvar notmuch-default-save-dir (file-name-as-directory (getenv "HOME" )) + "Default directory in which attachments are saved.") + (defvar notmuch-show-message-begin-regexp"\fmessage{") (defvar notmuch-show-message-end-regexp "\fmessage}") (defvar notmuch-show-header-begin-regexp "\fheader{") @@ -329,28 +333,75 @@ buffer." mm-handle) count)) -(defun notmuch-save-attachments (mm-handle queryp) - (notmuch-foreach-mime-part - (lambda (p) - (let ((disposition (mm-handle-disposition p))) - (and (listp disposition) -(or (equal (car disposition) "attachment") -(and (equal (car disposition) "inline") - (assq 'filename disposition))) -(or (not queryp) -(y-or-n-p - (concat "Save '" (cdr (assq 'filename disposition)) "' "))) -(mm-save-part p - mm-handle)) - -(defun notmuch-show-save-attachments () - "Save all attachments from the current message." - (interactive) +(defun notmuch-attachment-q (mm-handle) + (let ((disposition (mm-handle-disposition p))) +(and (listp disposition) + (assq 'filename disposition + +(defun notmuch-get-save-path (filename default-dir) + (let ((savepath nil)) +(while (not savepath) + (let* ((ddir (file-name-as-directory default-dir)) + (fn (read-file-name "Save to: " ddir nil nil filename)) + (efn (expand-file-name fn)) + (dir (file-name-directory efn))) +(when (not (file-accessible-directory-p dir)) + (when (y-or-n-p (concat "Create directory " dir " ")) +(make-directory dir t))) +(if (file-accessible-directory-p dir) +(setq savepath fn) + (setq default-dir (file-name-directory fn) +savepath)) + +(defun notmuch-save-attachment (mm-handle default-dir) + "Save the current attachment part to a file." + (cond ((not (notmuch-attachment-q mm-handle)) + (message "Current part is not an attachment.") + nil) +(t + (let* ((fn (cdr (assq 'filename (mm-handle-disposition mm-handle +(savepath (notmuch-get-save-path fn default-dir))) + (mm-save-part-to-file mm-handle savepath) + savepath + +(defun notmuch-save-attachment-num (mm-handle part-num) + "Save the nth part number" + (let ((nfound 0) +(nsaved 0)) +(notmuch-foreach-mime-part + (lambda (p) + (when (notmuch-attachment-q p) + (cond ((equal (+ 1 nfound) part-num) +(when (notmuch-save-attachment p notmuch-default-save-dir) + (incf nsaved + (incf nfound))) mm-handle) +(equal nsaved 1))) + +(defun notmuch-show-save-attachment (num) + "Save a single attachment." + (interactive "p") (with-current-notmuch-show-message (let ((mm-handle (mm-dissect-buffer))) - (notmuch-save-attachments - mm-handle (> (notmuch-count-attachments mm-handle) 1 - (message "Done")) + (if (notmuch-save-attachment-num mm-handle num) + (message "Attachment %d saved." num) + (message "Failed to save attachment.") + +(defun notmuch-show-save-all-attachments () + "Save all attachments of a message to a directory." + (interactive) + (with-current-notmuch-show-message + (let ((nfound 0) + (nsaved 0) + (default-dir notmuch-default-save-dir) + (mm-handle (mm-dissect-buffer))) + (notmuch-foreach-mime-part + (lambda (p) +(when (notmuch-attachment-q p) +
[notmuch] Rework of attachment saving
I think I've reworked the attachment savings to behave as we've been discussing. I don't know anything about the button handling so I haven't attempted to implement the direct manipulation approach of of saving using the buttons. That would certainly be nice to have however and I belive this change should make it easy for someone who knows how buttons work to implement it. --- Keith
[notmuch] [PATCH 9/9] Key binding rearrangement for save attachments in show mode
From: Keith AmidonThe most obvious bindings for save attachments are already taken. The existing 'w' binding was bound to view the raw message. This commit moves it to 'V' which still seems somewhat mnemonic and uses 'w' for save (write) attachments. --- notmuch.el |3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/notmuch.el b/notmuch.el index 0c6b527..f0e8d65 100644 --- a/notmuch.el +++ b/notmuch.el @@ -76,7 +76,8 @@ (define-key map "r" 'notmuch-show-reply) (define-key map "s" 'notmuch-search) (define-key map "v" 'notmuch-show-view-all-mime-parts) -(define-key map "w" 'notmuch-show-view-raw-message) +(define-key map "V" 'notmuch-show-view-raw-message) +(define-key map "w" 'notmuch-show-save-attachments) (define-key map "x" 'kill-this-buffer) (define-key map "+" 'notmuch-show-add-tag) (define-key map "-" 'notmuch-show-remove-tag) -- 1.6.5.3
[notmuch] [PATCH 8/9] Provide ability to save attachments
From: Keith AmidonPreviously the only way to save an attachment was to attempt to view it and then save it from within the viewer program. --- notmuch.el | 41 + 1 files changed, 41 insertions(+), 0 deletions(-) diff --git a/notmuch.el b/notmuch.el index eaa5798..0c6b527 100644 --- a/notmuch.el +++ b/notmuch.el @@ -303,6 +303,47 @@ buffer." (with-current-notmuch-show-message (mm-display-parts (mm-dissect-buffer +(defun notmuch-foreach-mime-part (function mm-handle) + (cond ((stringp (car mm-handle)) + (dolist (part (cdr mm-handle)) + (notmuch-foreach-mime-part function part))) +((bufferp (car mm-handle)) + (funcall function mm-handle)) +(t (dolist (part mm-handle) + (notmuch-foreach-mime-part function part) + +(defun notmuch-count-attachments (mm-handle) + (let ((count 0)) +(notmuch-foreach-mime-part + (lambda (p) + (let ((disposition (mm-handle-disposition p))) + (and (listp disposition) + (equal (car disposition) "attachment") + (incf count + mm-handle) +count)) + +(defun notmuch-save-attachments (mm-handle queryp) + (notmuch-foreach-mime-part + (lambda (p) + (let ((disposition (mm-handle-disposition p))) + (and (listp disposition) +(equal (car disposition) "attachment") +(or (not queryp) +(y-or-n-p + (concat "Save '" (cdr (assq 'filename disposition)) "' "))) +(mm-save-part p + mm-handle)) + +(defun notmuch-show-save-attachments () + "Save the attachments to a message" + (interactive) + (with-current-notmuch-show-message + (let ((mm-handle (mm-dissect-buffer))) + (notmuch-save-attachments + mm-handle (> (notmuch-count-attachments mm-handle) 1 + (message "Done")) + (defun notmuch-reply (query-string) (switch-to-buffer (generate-new-buffer "notmuch-draft")) (call-process notmuch-command nil t nil "reply" query-string) -- 1.6.5.3
[notmuch] [PATCH 7/9] Key bindings for message library based replies
From: Keith Amidon--- notmuch.el |3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/notmuch.el b/notmuch.el index fe20b54..eaa5798 100644 --- a/notmuch.el +++ b/notmuch.el @@ -64,6 +64,9 @@ (define-key map "A" 'notmuch-show-mark-read-then-archive-thread) (define-key map "f" 'notmuch-show-forward-current) (define-key map "m" 'message-mail) +(define-key map "MA" 'notmuch-show-wide-reply-current) +(define-key map "MR" 'notmuch-show-reply-current) +(define-key map "MF" 'notmuch-show-followup-current) (define-key map "n" 'notmuch-show-next-message) (define-key map "N" 'notmuch-show-mark-read-then-next-open-message) (define-key map "p" 'notmuch-show-previous-message) -- 1.6.5.3
[notmuch] [PATCH 6/9] Reply to individual messages using message library
From: Keith AmidonAs an alternative to creating a reply from the current thread, this commit provides functions to create replies directly in emacs using the message library. A future commit will provide keybindings so that they are easy to change if a different set is preferred. --- notmuch.el | 15 +++ 1 files changed, 15 insertions(+), 0 deletions(-) diff --git a/notmuch.el b/notmuch.el index d3d75f9..fe20b54 100644 --- a/notmuch.el +++ b/notmuch.el @@ -323,6 +323,21 @@ buffer." (with-current-notmuch-show-message (message-forward))) +(defun notmuch-show-reply-current () + (interactive) + (with-current-notmuch-show-message + (message-reply))) + +(defun notmuch-show-wide-reply-current () + (interactive) + (with-current-notmuch-show-message + (message-wide-reply))) + +(defun notmuch-show-followup-current () + (interactive) + (with-current-notmuch-show-message + (message-followup))) + (defun notmuch-show-pipe-message (command) "Pipe the contents of the current message to the given command. -- 1.6.5.3
[notmuch] [PATCH 4/9] Factor out message buffer mgmt from notmuch-show-view-all-mime-parts
From: Keith AmidonThe ability to temporarily create a buffer containing only the contents of the currently selected message in notmuch show mode is generally useful. This commit factors the majority of the code required to do so out of notmuch-show-view-all-mime-parts into a macro called with-current-notmuch-show-message and rewrites the original function in terms of the macro. A future set of commits will provide additional functionality using the macro as well. --- notmuch.el | 20 1 files changed, 12 insertions(+), 8 deletions(-) diff --git a/notmuch.el b/notmuch.el index cd6609d..a71a9f7 100644 --- a/notmuch.el +++ b/notmuch.el @@ -283,17 +283,21 @@ buffer." (interactive) (view-file (notmuch-show-get-filename))) +(defmacro with-current-notmuch-show-message ( body) + "Evaluate body with current buffer set to the text of current message" + `(save-excursion + (let ((filename (notmuch-show-get-filename))) + (let ((buf (generate-new-buffer (concat "*notmuch-msg-" filename "*" + (with-current-buffer buf + (insert-file-contents filename nil nil nil t) + , at body) +(kill-buffer buf) + (defun notmuch-show-view-all-mime-parts () "Use external viewers (according to mailcap) to view all MIME-encoded parts." (interactive) - (save-excursion -(let ((filename (notmuch-show-get-filename))) - (switch-to-buffer (generate-new-buffer (concat "*notmuch-mime-" -filename -"*"))) - (insert-file-contents filename nil nil nil t) - (mm-display-parts (mm-dissect-buffer)) - (kill-this-buffer + (with-current-notmuch-show-message + (mm-display-parts (mm-dissect-buffer (defun notmuch-reply (query-string) (switch-to-buffer (generate-new-buffer "notmuch-draft")) -- 1.6.5.3
[notmuch] [PATCH 3/9] Add key binding for notmuch-search in show-mode
From: Keith AmidonIt's not uncommon to want to start a search as a result of something read in a message so this is convenient. --- notmuch.el |1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/notmuch.el b/notmuch.el index 6400199..cd6609d 100644 --- a/notmuch.el +++ b/notmuch.el @@ -70,6 +70,7 @@ (define-key map (kbd "C-p") 'notmuch-show-previous-line) (define-key map "q" 'kill-this-buffer) (define-key map "r" 'notmuch-show-reply) +(define-key map "s" 'notmuch-search) (define-key map "v" 'notmuch-show-view-all-mime-parts) (define-key map "w" 'notmuch-show-view-raw-message) (define-key map "x" 'kill-this-buffer) -- 1.6.5.3
[notmuch] [PATCH 2/9] Adjust autoload comments
From: Keith AmidonThe previous location of autoload comments didn't seem to correspond with the functions most likely to be the entry points for using notmuch. This change adjusts them to match those likely entry points. --- notmuch.el |6 -- 1 files changed, 4 insertions(+), 2 deletions(-) diff --git a/notmuch.el b/notmuch.el index a1efa4f..6400199 100644 --- a/notmuch.el +++ b/notmuch.el @@ -707,8 +707,6 @@ view, (remove the \"inbox\" tag from each), with mode-name "notmuch-show") (setq buffer-read-only t)) -;;;###autoload - (defgroup notmuch nil "Notmuch mail reader for Emacs." :group 'mail) @@ -1002,6 +1000,7 @@ This function advances point to the next line when finished." (set 'more nil)) (delete-process proc +;;;###autoload (defun notmuch-search (query oldest-first) "Run \"notmuch search\" with the given query string and display results." (interactive "sNotmuch search: ") @@ -1081,6 +1080,8 @@ current search results AND that are tagged with the given tag." (list (notmuch-select-tag-with-completion "Filter by tag: "))) (notmuch-search (concat notmuch-search-query-string " and tag:" tag) notmuch-search-oldest-first)) + +;;;###autoload (defun notmuch () "Run notmuch to display all mail with tag of 'inbox'" (interactive) @@ -1156,6 +1157,7 @@ results for the search terms in that line. (if search (notmuch-search (cdr search) notmuch-search-oldest-first +;;;###autoload (defun notmuch-folder () "Show the notmuch folder view and update the displayed counts." (interactive) -- 1.6.5.3
[notmuch] [PATCH 1/9] Explicitly require the message library
From: Keith AmidonFunctions provided by the message library were being used without ensuring it was loaded. --- notmuch.el |1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/notmuch.el b/notmuch.el index d7c973c..a1efa4f 100644 --- a/notmuch.el +++ b/notmuch.el @@ -49,6 +49,7 @@ (require 'cl) (require 'mm-view) +(require 'message) (defvar notmuch-show-mode-map (let ((map (make-sparse-keymap))) -- 1.6.5.3
[notmuch] Show mode enhancements
I started using notmuch a few days ago and was astounded at how much more efficiently I could process email with it. For my usage I needed a bit more flexibility in replying/forwarding messages and the ability to conveniently save attachments. The set of patches that follow contain these two enhancements, a few minor fixes, and the addition of a binding of 's' in show mode to notmuch-search. I'd be happy to address anything neded to get these changes into shape to be included in the main distribution. Thanks for all your work putting together such a promising email client. --- Keith