From: Austin Clements Date: Mon, 27 May 2013 20:46:33 +0000 (+2000) Subject: [PATCH 3/4] emacs: Simplify MIME part command implementation X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=49dae3dfa05d408121f355684e4b1880304a5b4b;p=notmuch-archives.git [PATCH 3/4] emacs: Simplify MIME part command implementation --- diff --git a/0a/2a1121c74b6e95e532c01d247f437b5e7988d5 b/0a/2a1121c74b6e95e532c01d247f437b5e7988d5 new file mode 100644 index 000000000..8afb1beaa --- /dev/null +++ b/0a/2a1121c74b6e95e532c01d247f437b5e7988d5 @@ -0,0 +1,295 @@ +Return-Path: +X-Original-To: notmuch@notmuchmail.org +Delivered-To: notmuch@notmuchmail.org +Received: from localhost (localhost [127.0.0.1]) + by olra.theworths.org (Postfix) with ESMTP id 19452431FAE + for ; Mon, 27 May 2013 13:47:06 -0700 (PDT) +X-Virus-Scanned: Debian amavisd-new at olra.theworths.org +X-Spam-Flag: NO +X-Spam-Score: -0.7 +X-Spam-Level: +X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5 + tests=[RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled +Received: from olra.theworths.org ([127.0.0.1]) + by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024) + with ESMTP id 7R8hPPbCv6rg for ; + Mon, 27 May 2013 13:47:00 -0700 (PDT) +Received: from dmz-mailsec-scanner-5.mit.edu (DMZ-MAILSEC-SCANNER-5.MIT.EDU + [18.7.68.34]) + by olra.theworths.org (Postfix) with ESMTP id 272B5431FC0 + for ; Mon, 27 May 2013 13:46:54 -0700 (PDT) +X-AuditID: 12074422-b7f5b6d00000095d-9c-51a3c63d5e40 +Received: from mailhub-auth-3.mit.edu ( [18.9.21.43]) + by dmz-mailsec-scanner-5.mit.edu (Symantec Messaging Gateway) with SMTP + id 83.3B.02397.D36C3A15; Mon, 27 May 2013 16:46:53 -0400 (EDT) +Received: from outgoing.mit.edu (OUTGOING-AUTH-1.MIT.EDU [18.9.28.11]) + by mailhub-auth-3.mit.edu (8.13.8/8.9.2) with ESMTP id r4RKkgLn032252; + Mon, 27 May 2013 16:46:43 -0400 +Received: from drake.dyndns.org (c-76-21-105-205.hsd1.ca.comcast.net + [76.21.105.205]) (authenticated bits=0) + (User authenticated as amdragon@ATHENA.MIT.EDU) + by outgoing.mit.edu (8.13.8/8.12.4) with ESMTP id r4RKke5W026423 + (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT); + Mon, 27 May 2013 16:46:42 -0400 +Received: from amthrax by drake.dyndns.org with local (Exim 4.77) + (envelope-from ) + id 1Uh4JT-0008NF-Tl; Mon, 27 May 2013 16:46:39 -0400 +From: Austin Clements +To: notmuch@notmuchmail.org +Subject: [PATCH 3/4] emacs: Simplify MIME part command implementation +Date: Mon, 27 May 2013 16:46:33 -0400 +Message-Id: <1369687594-31774-4-git-send-email-amdragon@mit.edu> +X-Mailer: git-send-email 1.7.10.4 +In-Reply-To: <1369687594-31774-1-git-send-email-amdragon@mit.edu> +References: <1369687594-31774-1-git-send-email-amdragon@mit.edu> +X-Brightmail-Tracker: + H4sIAAAAAAAAA+NgFjrBIsWRmVeSWpSXmKPExsUixCmqrWt7bHGgwZtV5har5/JYXL85k9mB + yWPnrLvsHs9W3WIOYIrisklJzcksSy3St0vgyli1YxpjwQm/ip7dF9gaGPc5djFyckgImEi8 + WPiYDcIWk7hwbz2QzcUhJLCPUeLX/OtMEM5GRom1u7dCOaeZJHasWcQE0iIkMJdRYle7JojN + JqAhsW3/ckYQW0RAWmLn3dmsIDazgKPE5/2LwFYIC7hKvN19hQXEZhFQlbgw6zA7iM0r4CDx + 8V8fO8QZihLdzyaA1XMC9V7Z9ZsVYpeDxOrXHUwTGPkXMDKsYpRNya3SzU3MzClOTdYtTk7M + y0st0jXVy80s0UtNKd3ECAokdhelHYw/DyodYhTgYFTi4bXIXBwoxJpYVlyZe4hRkoNJSZSX + /QhQiC8pP6UyI7E4I76oNCe1+BCjBAezkgjv9hVAOd6UxMqq1KJ8mJQ0B4uSOO+1lJv+QgLp + iSWp2ampBalFMFkZDg4lCd6mo0CNgkWp6akVaZk5JQhpJg5OkOE8QMPDQGp4iwsSc4sz0yHy + pxh1OTafn/yOUYglLz8vVUqcNwWkSACkKKM0D24OLAG8YhQHekuYNx+kigeYPOAmvQJawgS0 + RJwZbElJIkJKqoHR4RHDTpVj+W4nzTWjvl4Tb1nG7yH2bKaq0p6muZGXt117WC8+59ubFy9V + eV/oPtlQstcwXGhjxa5tsw6Y5Pw/WeD5NSneId9CsP7f2ZoN1hx+Fls1hK6VBV5cfUO6pLXr + 8+XLjiK/nevdbVrnpP5/+cO4+/X1N1Klp6ReTPvwT40jOOgw14QcJZbijERDLeai4kQAlV7j + dNsCAAA= +X-BeenThere: notmuch@notmuchmail.org +X-Mailman-Version: 2.1.13 +Precedence: list +List-Id: "Use and development of the notmuch mail system." + +List-Unsubscribe: , + +List-Archive: +List-Post: +List-Help: +List-Subscribe: , + +X-List-Received-Date: Mon, 27 May 2013 20:47:06 -0000 + +This unifies the part button actions and the underlying part action +functions into single interactive command that simply applies to the +part containing point using the just-added part p-list text property +instead of button properties. Since all part actions can be performed +by applying the appropriate mm function to an mm-handle, this patch +abstracts out the creation of mm handles, making the implementations +of the part commands trivial. This also eliminates our special +handling for part save in favor of using the appropriate mm function. + +This necessarily modifies the way we handle the default part button +action, but in a way that does not change the meaning of the +notmuch-show-part-button-default-action defcustom. + +Since these commands are no longer specific to buttons, this patch +eliminates the extra metadata stored with each button. This also +eliminates one rather special-purpose macro for a collection of +general purpose part handling utilities. +--- + emacs/notmuch-show.el | 133 +++++++++++++++++++++---------------------------- + test/emacs | 4 +- + 2 files changed, 61 insertions(+), 76 deletions(-) + +diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el +index acd0b55..b33d92d 100644 +--- a/emacs/notmuch-show.el ++++ b/emacs/notmuch-show.el +@@ -474,10 +474,10 @@ message at DEPTH in the current thread." + (defvar notmuch-show-part-button-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map button-map) +- (define-key map "s" 'notmuch-show-part-button-save) +- (define-key map "v" 'notmuch-show-part-button-view) +- (define-key map "o" 'notmuch-show-part-button-interactively-view) +- (define-key map "|" 'notmuch-show-part-button-pipe) ++ (define-key map "s" 'notmuch-show-save-part) ++ (define-key map "v" 'notmuch-show-view-part) ++ (define-key map "o" 'notmuch-show-interactively-view-part) ++ (define-key map "|" 'notmuch-show-pipe-part) + map) + "Submap for button commands") + (fset 'notmuch-show-part-button-map notmuch-show-part-button-map) +@@ -494,61 +494,11 @@ message at DEPTH in the current thread." + (insert-button + (concat "[ " base-label " ]") + :base-label base-label +- :type 'notmuch-show-part-button-type +- :notmuch-part nth +- :notmuch-filename name +- :notmuch-content-type content-type)) ++ :type 'notmuch-show-part-button-type)) + (insert "\n") + ;; return button + button)) + +-;; Functions handling particular MIME parts. +- +-(defmacro notmuch-with-temp-part-buffer (message-id nth &rest body) +- (declare (indent 2)) +- (let ((process-crypto (make-symbol "process-crypto"))) +- `(let ((,process-crypto notmuch-show-process-crypto)) +- (with-temp-buffer +- (setq notmuch-show-process-crypto ,process-crypto) +- ;; Always acquires the part via `notmuch part', even if it is +- ;; available in the JSON output. +- (insert (notmuch-get-bodypart-internal ,message-id ,nth notmuch-show-process-crypto)) +- ,@body)))) +- +-(defun notmuch-show-save-part (message-id nth &optional filename content-type) +- (notmuch-with-temp-part-buffer message-id nth +- (let ((file (read-file-name +- "Filename to save as: " +- (or mailcap-download-directory "~/") +- nil nil +- filename))) +- ;; Don't re-compress .gz & al. Arguably we should make +- ;; `file-name-handler-alist' nil, but that would chop +- ;; ange-ftp, which is reasonable to use here. +- (mm-write-region (point-min) (point-max) file nil nil nil 'no-conversion t)))) +- +-(defun notmuch-show-view-part (message-id nth &optional filename content-type ) +- (notmuch-with-temp-part-buffer message-id nth +- (let* ((disposition (if filename `(attachment (filename . ,filename)))) +- (handle (mm-make-handle (current-buffer) (list content-type) +- nil nil disposition)) +- ;; Set the default save directory to be consistent with +- ;; `notmuch-show-save-part'. +- (mm-default-directory (or mailcap-download-directory "~/")) +- ;; set mm-inlined-types to nil to force an external viewer +- (mm-inlined-types nil)) +- (mm-display-part handle)))) +- +-(defun notmuch-show-interactively-view-part (message-id nth &optional filename content-type) +- (notmuch-with-temp-part-buffer message-id nth +- (let ((handle (mm-make-handle (current-buffer) (list content-type)))) +- (mm-interactively-view-part handle)))) +- +-(defun notmuch-show-pipe-part (message-id nth &optional filename content-type) +- (notmuch-with-temp-part-buffer message-id nth +- (let ((handle (mm-make-handle (current-buffer) (list content-type)))) +- (mm-pipe-part handle)))) +- + ;; This is taken from notmuch-wash: maybe it should be unified? + (defun notmuch-show-toggle-part-invisibility (&optional button) + (interactive) +@@ -570,6 +520,8 @@ message at DEPTH in the current thread." + (delete-region (point) old-end)) + (goto-char (min old-point (1- (button-end button)))))))) + ++;; MIME part renderers ++ + (defun notmuch-show-multipart/*-to-list (part) + (mapcar (lambda (inner-part) (plist-get inner-part :content-type)) + (plist-get part :content))) +@@ -2016,40 +1968,71 @@ the user (see `notmuch-show-stash-mlarchive-link-alist')." + (notmuch-show-stash-mlarchive-link mla) + (browse-url (current-kill 0 t))) + +-;; Commands typically bound to buttons. ++;; Interactive part functions and their helpers ++ ++(defun notmuch-show-generate-part-buffer (message-id nth) ++ "Return a temporary buffer containing the specified part's content." ++ (let ((buf (generate-new-buffer " *notmuch-part*")) ++ (process-crypto notmuch-show-process-crypto)) ++ (with-current-buffer buf ++ (setq notmuch-show-process-crypto process-crypto) ++ ;; Always acquires the part via `notmuch part', even if it is ++ ;; available in the JSON output. ++ (insert (notmuch-get-bodypart-internal message-id nth notmuch-show-process-crypto))) ++ buf)) ++ ++(defun notmuch-show-current-part-handle () ++ "Return an mm-handle for the part containing point. ++ ++This creates a temporary buffer for the part's content; the ++caller is responsible for killing this buffer as appropriate." ++ (let* ((part (notmuch-show-get-part-properties)) ++ (message-id (notmuch-show-get-message-id)) ++ (nth (plist-get part :id)) ++ (buf (notmuch-show-generate-part-buffer message-id nth)) ++ (content-type (plist-get part :content-type)) ++ (filename (plist-get part :filename)) ++ (disposition (if filename `(attachment (filename . ,filename))))) ++ (mm-make-handle buf (list content-type) nil nil disposition))) ++ ++(defun notmuch-show-apply-to-current-part-handle (fn) ++ "Apply FN to an mm-handle for the part containing point. ++ ++This ensures that the temporary buffer created for the mm-handle ++is destroyed when FN returns." ++ (let ((handle (notmuch-show-current-part-handle))) ++ (unwind-protect ++ (funcall fn handle) ++ (kill-buffer (mm-handle-buffer handle))))) + + (defun notmuch-show-part-button-default (&optional button) + (interactive) + (let ((button (or button (button-at (point))))) + (if (button-get button 'overlay) + (notmuch-show-toggle-part-invisibility button) +- (notmuch-show-part-button-internal button notmuch-show-part-button-default-action)))) ++ (call-interactively notmuch-show-part-button-default-action)))) + +-(defun notmuch-show-part-button-save (&optional button) ++(defun notmuch-show-save-part () ++ "Save the MIME part containing point to a file." + (interactive) +- (notmuch-show-part-button-internal button #'notmuch-show-save-part)) ++ (notmuch-show-apply-to-current-part-handle #'mm-save-part)) + +-(defun notmuch-show-part-button-view (&optional button) ++(defun notmuch-show-view-part () ++ "View the MIME part containing point in an external viewer." + (interactive) +- (notmuch-show-part-button-internal button #'notmuch-show-view-part)) ++ ;; Set mm-inlined-types to nil to force an external viewer ++ (let ((mm-inlined-types nil)) ++ (notmuch-show-apply-to-current-part-handle #'mm-display-part))) + +-(defun notmuch-show-part-button-interactively-view (&optional button) ++(defun notmuch-show-interactively-view-part () ++ "View the MIME part containing point, prompting for a viewer." + (interactive) +- (notmuch-show-part-button-internal button #'notmuch-show-interactively-view-part)) ++ (notmuch-show-apply-to-current-part-handle #'mm-interactively-view-part)) + +-(defun notmuch-show-part-button-pipe (&optional button) ++(defun notmuch-show-pipe-part () ++ "Pipe the MIME part containing point to an external command." + (interactive) +- (notmuch-show-part-button-internal button #'notmuch-show-pipe-part)) ++ (notmuch-show-apply-to-current-part-handle #'mm-pipe-part)) + +-(defun notmuch-show-part-button-internal (button handler) +- (let ((button (or button (button-at (point))))) +- (if button +- (let ((nth (button-get button :notmuch-part))) +- (if nth +- (funcall handler (notmuch-show-get-message-id) nth +- (button-get button :notmuch-filename) +- (button-get button :notmuch-content-type))))))) +- +-;; + + (provide 'notmuch-show) +diff --git a/test/emacs b/test/emacs +index f033bdf..3b26d32 100755 +--- a/test/emacs ++++ b/test/emacs +@@ -525,7 +525,9 @@ test_expect_equal_file attachment1.gz "$EXPECTED/attachment" + test_begin_subtest "Save attachment from within emacs using notmuch-show-save-part" + # save as archive to test that Emacs does not re-compress .gz + test_emacs '(let ((standard-input "\"attachment2.gz\"")) +- (notmuch-show-save-part "id:cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com" 5))' ++ (notmuch-show "id:cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com") ++ (search-forward "0001-Deal-with") ++ (notmuch-show-save-part))' + test_expect_equal_file attachment2.gz "$EXPECTED/attachment" + + test_begin_subtest "Save 8bit attachment from within emacs using notmuch-show-save-attachments" +-- +1.7.10.4 +