From d4c45fe56371e2f0ba40c93fb36a4331c684a872 Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Fri, 3 Jun 2016 01:57:52 +0100 Subject: [PATCH] [WIP PATCH] emacs: postpone/resume support --- ea/93210a93e10f25e6ececf530bbd93828b31125 | 257 ++++++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 ea/93210a93e10f25e6ececf530bbd93828b31125 diff --git a/ea/93210a93e10f25e6ececf530bbd93828b31125 b/ea/93210a93e10f25e6ececf530bbd93828b31125 new file mode 100644 index 000000000..247b9e849 --- /dev/null +++ b/ea/93210a93e10f25e6ececf530bbd93828b31125 @@ -0,0 +1,257 @@ +Return-Path: +X-Original-To: notmuch@notmuchmail.org +Delivered-To: notmuch@notmuchmail.org +Received: from localhost (localhost [127.0.0.1]) + by arlo.cworth.org (Postfix) with ESMTP id 1117F6DE01D0 + for ; Thu, 2 Jun 2016 17:58:09 -0700 (PDT) +X-Virus-Scanned: Debian amavisd-new at cworth.org +X-Spam-Flag: NO +X-Spam-Score: -0.323 +X-Spam-Level: +X-Spam-Status: No, score=-0.323 tagged_above=-999 required=5 tests=[AWL=0.247, + DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, + FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_LOW=-0.7, + RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, SPF_PASS=-0.001] + autolearn=disabled +Received: from arlo.cworth.org ([127.0.0.1]) + by localhost (arlo.cworth.org [127.0.0.1]) (amavisd-new, port 10024) + with ESMTP id mN0wW-DIWM9u for ; + Thu, 2 Jun 2016 17:58:01 -0700 (PDT) +Received: from mail-wm0-f68.google.com (mail-wm0-f68.google.com + [74.125.82.68]) by arlo.cworth.org (Postfix) with ESMTPS id 8E31C6DE00DB for + ; Thu, 2 Jun 2016 17:58:00 -0700 (PDT) +Received: by mail-wm0-f68.google.com with SMTP id a20so8343045wma.3 + for ; Thu, 02 Jun 2016 17:58:00 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; + h=from:to:cc:subject:date:message-id:in-reply-to:references; + bh=+uaVGQFH7IB3pkUFxo2nEl4IaRrZJdSb3Sfs0Nt70S8=; + b=xb/W0uSbMvuEE+Go1fmibUKWwS4TCVPEEz41SHy/RyZMPdPgGd3IyzliuyFu7gQrVT + juG0VxmcpaqENd+Z2O3hCNjfR/xMLTcDiHYCOx8TDZaFjbe/y2HG+pnw7+TUNZ6byYQO + mRU3jbiwpnLBijswbvUqQL9/+joIYTaoczZLyFCrLqR8WwM/qKs4Ft+QwDK3YWLKNb0M + c+DIeT2aduEJQOADJMtJW1+iWXBUGIdalWIr9wyT+lxHVXAiHQV1sJGGWo6XjiJioCk5 + a/wgAXXucGoBs0CjMw5CHnarjUF0KtGsbTMXbWkkbFT2QwA5HsgqH8UTuG5EMWrmtfB3 + z0Pg== +X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=1e100.net; s=20130820; + h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to + :references; + bh=+uaVGQFH7IB3pkUFxo2nEl4IaRrZJdSb3Sfs0Nt70S8=; + b=GxTtC72+t8zWmg4vTkAwZYx5keaXFFq9fa0IlKD8ZPjg+6jpQkjaZHt/8xBRf1cG/z + XC59hy0/keLBIx0pGZKCchHax9qkFmHfxe6L/KI5EEmU5u4hP4QeAkMFScJ4hO3V+C1f + CpbGEB3xWA7EHIRqHq2WMj9N3E8YtJjTFOMv7tHhJ9ARqqgKmsfFM0s+6D0aiQVeu/Ls + U1u1EeXocKwAho4ji1ufxeYRX+DtKA5hR194ELLJgmDF/LR6gS58ZEID0XDlIkfDev+y + G1QHqAnydstjvGR8Cm97w3eKLn3+IbdbfArBCpVIIdbXEi13OwwVvIsqAOWfhZ3YU0it + N6Iw== +X-Gm-Message-State: + ALyK8tKt61/2M9L5a7MZMr3pLImfThA1YcOyp1bbO0bX3ODISa1ayaQyGKvO/iZcxbbwww== +X-Received: by 10.194.235.193 with SMTP id uo1mr714133wjc.1.1464915478853; + Thu, 02 Jun 2016 17:57:58 -0700 (PDT) +Received: from localhost (5751dfa2.skybroadband.com. [87.81.223.162]) + by smtp.gmail.com with ESMTPSA id kc2sm3095384wjb.5.2016.06.02.17.57.57 + (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); + Thu, 02 Jun 2016 17:57:58 -0700 (PDT) +From: Mark Walters +To: notmuch@notmuchmail.org +Subject: [WIP PATCH] emacs: postpone/resume support +Date: Fri, 3 Jun 2016 01:57:52 +0100 +Message-Id: <1464915472-5669-1-git-send-email-markwalters1009@gmail.com> +X-Mailer: git-send-email 2.1.4 +In-Reply-To: <87mvn330zr.fsf@qmul.ac.uk> +References: <87mvn330zr.fsf@qmul.ac.uk> +X-BeenThere: notmuch@notmuchmail.org +X-Mailman-Version: 2.1.20 +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: Fri, 03 Jun 2016 00:58:09 -0000 + +This provides preliminary support for postponing and resuming in the +emacs frontend. On postponing it uses notmuch insert to put the +message in the notmuch database; resume gets the raw file from notmuch +and using the emacs function mime-to-mml reconstructs the message +(including attachments). +--- + +This is a WIP patch enabling postpone/resume. On very light testing so +far it seems to work surprisingly well. + +Please note that it is an early version, so it may eat your draft +message, mangle (or mysteriously corrupt) your draft message etc; and +it each draft will remain in your message store unless you manually +delete it. + +To use: to postpone just do M-x notmuch-message-postpone in your +compose buffer. To resume fo M-x notmuch-message-resume while on the +correct message in a show buffer. You may want to add "draft" to your +exclude tags in .notmuch-config so that drafts only show up if you +search for them explicitly. + +CAVEATS: + +Attachments work, but the attachment that is sent is the attachment +that was there when the message was postponed. (Attachments added +after resume are obviously not added until the message is sent.) + +If the database is locked when you try and postpone it will +mysteriously fail. I think this is actually caused by a bug elsewhere +in notmuch. But in any case the compose buffer should not be killed in +this case. + +It would probably be nice to delete the messages on resume. Since we +don't really have that functionality, we just add a deleted tag. This +will hide them in search buffers but, if you postpone a reply, it ill +still show up (collapsed) in the show buffer for the thread. + +If you resume a postponed message and then postpone again without +editting then the deleted tag will not be removed so the message may +be hard to find. (see below for possible fixed for this) + +Messages don't get a date header as emacs adds that when it sends it +so notmuch will list all postponed message as 1 Jan 1970. + +If you use signing or encryption then I don't know what will happen -- +I have not tested at all. You might sign a partial message that you +didn't mean too; you might expose plain text to someone. + +WHAT NEEDS DOING + +Trivially we would want some keybindings for the two commands (any +suggested keybindings gratefully received). More significantly, I +would either like a flag to notmuch insert to say "make these tag +changes even if the message already exists"; or perhaps a notmuch +delete command that (genuinely) deletes a single message. + +Best wishes + +Mark + + + +emacs/notmuch-message.el | 90 ++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 90 insertions(+) + +diff --git a/emacs/notmuch-message.el b/emacs/notmuch-message.el +index d437b85..fdd91c0 100644 +--- a/emacs/notmuch-message.el ++++ b/emacs/notmuch-message.el +@@ -25,6 +25,8 @@ + (require 'notmuch-tag) + (require 'notmuch-mua) + ++(declare-function notmuch-show-get-message-id "notmuch-show" (&optional bare)) ++ + (defcustom notmuch-message-replied-tags '("+replied") + "List of tag changes to apply to a message when it has been replied to. + +@@ -38,6 +40,28 @@ the \"inbox\" and \"todo\" tags, you would set: + :type '(repeat string) + :group 'notmuch-send) + ++(defcustom notmuch-message-draft-tags '("+draft") ++ "List of tags changes to apply to a draft message when it is saved in the database. ++ ++Tags starting with \"+\" (or not starting with either \"+\" or ++\"-\") in the list will be added, and tags starting with \"-\" ++will be removed from the message being stored. ++ ++For example, if you wanted to give the message a \"draft\" tag ++but not the (normally added by default) \"inbox\" tag, you would ++set: ++ (\"+draft\" \"-inbox\")" ++ :type '(repeat string) ++ :group 'notmuch-send) ++ ++(defcustom notmuch-message-draft-folder "drafts" ++ "Folder to save draft messages in. ++ ++This should be specified relative to the root of the notmuch ++database. It will be created if necessary." ++ :type 'string ++ :group 'notmuch-send) ++ + (defun notmuch-message-mark-replied () + ;; get the in-reply-to header and parse it for the message id. + (let ((rep (mail-header-parse-addresses (message-field-value "In-Reply-To")))) +@@ -45,6 +69,72 @@ the \"inbox\" and \"todo\" tags, you would set: + (notmuch-tag (notmuch-id-to-query (car (car rep))) + (notmuch-tag-change-list notmuch-message-replied-tags))))) + ++(defun notmuch-message-postpone () ++ "Save the current draft message in the notmuch database. ++ ++This saves the current message in the database with tags ++`notmuch-message-draft-tags` (in addition to any default tags ++applied to newly inserted messages)." ++ ;; This is taken from message-do-fcc but modified for our needs. In ++ ;; particular we don't want to process other fcc lines, nor remove ++ ;; them. Note some of the following may not be needed -- I tried to ++ ;; stay close to the original function. ++ (interactive) ++ (let ((case-fold-search t) ++ (buf (current-buffer)) ++ (mml-externalize-attachments nil)) ++ (with-current-buffer (get-buffer-create " *message temp*") ++ (erase-buffer) ++ (insert-buffer-substring buf) ++ (message-encode-message-body) ++ (save-restriction ++ (message-narrow-to-headers) ++ (let ((mail-parse-charset message-default-charset) ++ (rfc2047-header-encoding-alist ++ (cons '("Newsgroups" . default) ++ rfc2047-header-encoding-alist))) ++ (mail-encode-encoded-word-buffer))) ++ (goto-char (point-min)) ++ (when (re-search-forward ++ (concat "^" (regexp-quote mail-header-separator) "$") ++ nil t) ++ (replace-match "" t t )) ++ ++ ;; Unless the user has entered a message-id manually the message ++ ;; does not have one -- thus notmuch will use the sha1 of the ++ ;; message. Hence we should only get a collision if the message ++ ;; is identical to a previous draft. In this case we should ++ ;; remove the deleted tag, but it is not clear howto. ++ (apply 'notmuch-call-notmuch-process :stdin-string (buffer-string) ++ "insert" "--create-folder" ++ (concat "--folder=" notmuch-message-draft-folder) ++ notmuch-message-draft-tags))) ++ ;; The function notmuch-call-notmuch-process signals an error on failure, so ++ ;; to get to this point it must have succeeded. ++ (set-buffer-modified-p nil) ++ (kill-buffer)) ++ ++(defun notmuch-message-resume (&optional id) ++ "View the original source of the current message." ++ (interactive) ++ (let* ((id (or id (notmuch-show-get-message-id))) ++ (buf (get-buffer-create (concat "*notmuch-draft-" id "*"))) ++ (inhibit-read-only t)) ++ (switch-to-buffer buf) ++ (setq buffer-read-only nil) ++ (erase-buffer) ++ (let ((coding-system-for-read 'no-conversion)) ++ (call-process notmuch-command nil t nil "show" "--format=raw" id)) ++ (mime-to-mml) ++ (goto-char (point-min)) ++ (when (re-search-forward "^$" nil t) ++ (replace-match mail-header-separator t t)) ++ (notmuch-message-mode) ++ (set-buffer-modified-p t) ++ ;; Delete the message (since we can't do this yet just tag it deleted). ++ (notmuch-tag id '("+deleted")))) ++ ++ + (add-hook 'message-send-hook 'notmuch-message-mark-replied) + + (provide 'notmuch-message) +-- +2.1.4 + -- 2.26.2