unofficial mirror of notmuch@notmuchmail.org
 help / color / mirror / code / Atom feed
* v2 undo tag operations in emacs
@ 2022-01-29 19:44 David Bremner
  2022-01-29 19:44 ` [RFC PATCH v2 1/8] test: split variable settings to their own file David Bremner
                   ` (7 more replies)
  0 siblings, 8 replies; 10+ messages in thread
From: David Bremner @ 2022-01-29 19:44 UTC (permalink / raw)
  To: notmuch

The first 4 patches are just to get a performance test running for
tagging in emacs.  It seems to me that test shows (after setting
notuch-tag-argument-limit to 0) that we can simplify #'notmuch-tag to
always use notmuch tag --batch. Before I do that, I'll tosss this out
there in case someone else wants to test.

This second version of the series implements a notmuch-tag-undo. The
tag history is buffer local, although it does not do the requisite
refreshing yet.

It is also not bound to a key.

This obsoletes the series at id:20220127230740.1576802-1-david@tethera.net

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [RFC PATCH v2 1/8] test: split variable settings to their own file
  2022-01-29 19:44 v2 undo tag operations in emacs David Bremner
@ 2022-01-29 19:44 ` David Bremner
  2022-01-29 19:44 ` [RFC PATCH v2 2/8] test/emacs: split out emacs related tests David Bremner
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: David Bremner @ 2022-01-29 19:44 UTC (permalink / raw)
  To: notmuch

This allows sharing more variable settings between the (correctness)
tests and the performance-tests. Unfortunately it seems a bit tricky
to move settings to test-lib-common.sh, as that is sourced late in
test-lib.sh, and moving it earlier breaks things.
---
 performance-test/perf-test-lib.sh |  2 ++
 test/test-lib.sh                  | 55 +----------------------------
 test/test-vars.sh                 | 58 +++++++++++++++++++++++++++++++
 3 files changed, 61 insertions(+), 54 deletions(-)
 create mode 100644 test/test-vars.sh

diff --git a/performance-test/perf-test-lib.sh b/performance-test/perf-test-lib.sh
index 41b1ddfd..0e4915e3 100644
--- a/performance-test/perf-test-lib.sh
+++ b/performance-test/perf-test-lib.sh
@@ -41,6 +41,8 @@ done
 # Ensure NOTMUCH_SRCDIR and NOTMUCH_BUILDDIR are set.
 . $(dirname "$0")/../test/export-dirs.sh || exit 1
 
+. "$NOTMUCH_SRCDIR/test/test-vars.sh" || exit 1
+
 # Where to run the tests
 TEST_DIRECTORY=$NOTMUCH_BUILDDIR/performance-test
 
diff --git a/test/test-lib.sh b/test/test-lib.sh
index 833bf5fe..89e5c6d8 100644
--- a/test/test-lib.sh
+++ b/test/test-lib.sh
@@ -64,55 +64,7 @@ exec 6>&1 7>&2
 BASH_XTRACEFD=7
 export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
 
-# Keep the original TERM for say_color and test_emacs
-ORIGINAL_TERM=$TERM
-
-# Set SMART_TERM to vt100 for known dumb/unknown terminal.
-# Otherwise use whatever TERM is currently used so that
-# users' actual TERM environments are being used in tests.
-case ${TERM-} in
-	'' | dumb | unknown )
-		SMART_TERM=vt100 ;;
-	*)
-		SMART_TERM=$TERM ;;
-esac
-
-# For repeatability, reset the environment to known value.
-LANG=C
-LC_ALL=C
-PAGER=cat
-TZ=UTC
-TERM=dumb
-export LANG LC_ALL PAGER TERM TZ
-GIT_TEST_CMP=${GIT_TEST_CMP:-diff -u}
-if [[ ( -n "$TEST_EMACS" && -z "$TEST_EMACSCLIENT" ) || \
-      ( -z "$TEST_EMACS" && -n "$TEST_EMACSCLIENT" ) ]]; then
-    echo "error: must specify both or neither of TEST_EMACS and TEST_EMACSCLIENT" >&2
-    exit 1
-fi
-TEST_EMACS=${TEST_EMACS:-${EMACS:-emacs}}
-TEST_EMACSCLIENT=${TEST_EMACSCLIENT:-emacsclient}
-TEST_GDB=${TEST_GDB:-gdb}
-TEST_CC=${TEST_CC:-cc}
-TEST_CFLAGS=${TEST_CFLAGS:-"-g -O0"}
-TEST_SHIM_CFLAGS=${TEST_SHIM_CFLAGS:-"-fpic -shared"}
-TEST_SHIM_LDFLAGS=${TEST_SHIM_LDFLAGS:-"-ldl"}
-
-# Protect ourselves from common misconfiguration to export
-# CDPATH into the environment
-unset CDPATH
-
-unset GREP_OPTIONS
-
-# For lib/open.cc:_load_key_file
-unset XDG_CONFIG_HOME
-
-# For emacsclient
-unset ALTERNATE_EDITOR
-
-# for reproducibility
-unset EMAIL
-unset NAME
+. "$NOTMUCH_SRCDIR/test/test-vars.sh" || exit 1
 
 add_gnupg_home () {
     [ -e "${GNUPGHOME}/gpg.conf" ] && return
@@ -330,11 +282,6 @@ die () {
 	exit 1
 }
 
-GIT_EXIT_OK=
-# Note: TEST_TMPDIR *NOT* exported!
-TEST_TMPDIR=$(mktemp -d "${TMPDIR:-/tmp}/notmuch-test-$$.XXXXXX")
-# Put GNUPGHOME in TMPDIR to avoid problems with long paths.
-export GNUPGHOME="${TEST_TMPDIR}/gnupg"
 trap 'trap_exit' EXIT
 trap 'trap_signal' HUP INT TERM
 
diff --git a/test/test-vars.sh b/test/test-vars.sh
new file mode 100644
index 00000000..812bcf81
--- /dev/null
+++ b/test/test-vars.sh
@@ -0,0 +1,58 @@
+# Common variable settings for (correctness) tests and performance
+# tests.
+
+# Keep the original TERM for say_color and test_emacs
+ORIGINAL_TERM=$TERM
+
+# Set SMART_TERM to vt100 for known dumb/unknown terminal.
+# Otherwise use whatever TERM is currently used so that
+# users' actual TERM environments are being used in tests.
+case ${TERM-} in
+	'' | dumb | unknown )
+		SMART_TERM=vt100 ;;
+	*)
+		SMART_TERM=$TERM ;;
+esac
+
+# For repeatability, reset the environment to known value.
+LANG=C
+LC_ALL=C
+PAGER=cat
+TZ=UTC
+TERM=dumb
+export LANG LC_ALL PAGER TERM TZ
+GIT_TEST_CMP=${GIT_TEST_CMP:-diff -u}
+if [[ ( -n "$TEST_EMACS" && -z "$TEST_EMACSCLIENT" ) || \
+      ( -z "$TEST_EMACS" && -n "$TEST_EMACSCLIENT" ) ]]; then
+    echo "error: must specify both or neither of TEST_EMACS and TEST_EMACSCLIENT" >&2
+    exit 1
+fi
+TEST_EMACS=${TEST_EMACS:-${EMACS:-emacs}}
+TEST_EMACSCLIENT=${TEST_EMACSCLIENT:-emacsclient}
+TEST_GDB=${TEST_GDB:-gdb}
+TEST_CC=${TEST_CC:-cc}
+TEST_CFLAGS=${TEST_CFLAGS:-"-g -O0"}
+TEST_SHIM_CFLAGS=${TEST_SHIM_CFLAGS:-"-fpic -shared"}
+TEST_SHIM_LDFLAGS=${TEST_SHIM_LDFLAGS:-"-ldl"}
+
+# Protect ourselves from common misconfiguration to export
+# CDPATH into the environment
+unset CDPATH
+
+unset GREP_OPTIONS
+
+# For lib/open.cc:_load_key_file
+unset XDG_CONFIG_HOME
+
+# For emacsclient
+unset ALTERNATE_EDITOR
+
+# for reproducibility
+unset EMAIL
+unset NAME
+
+GIT_EXIT_OK=
+# Note: TEST_TMPDIR *NOT* exported!
+TEST_TMPDIR=$(mktemp -d "${TMPDIR:-/tmp}/notmuch-test-$$.XXXXXX")
+# Put GNUPGHOME in TMPDIR to avoid problems with long paths.
+export GNUPGHOME="${TEST_TMPDIR}/gnupg"
-- 
2.34.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC PATCH v2 2/8] test/emacs: split out emacs related tests
  2022-01-29 19:44 v2 undo tag operations in emacs David Bremner
  2022-01-29 19:44 ` [RFC PATCH v2 1/8] test: split variable settings to their own file David Bremner
@ 2022-01-29 19:44 ` David Bremner
  2022-01-29 19:44 ` [RFC PATCH v2 3/8] WIP enable running test_emacs from performance tests David Bremner
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: David Bremner @ 2022-01-29 19:44 UTC (permalink / raw)
  To: notmuch

T310-emacs is one of the largest and longest running sets of
tests. Splitting out the tagging operations will help maintainability
as well as potentially improve the parallel running time of the test
suite. Some slowdown in running the tests sequentially may result
since there is repeated setup.
---
 test/T310-emacs.sh         |  93 --------------------------------
 test/T315-emacs-tagging.sh | 107 +++++++++++++++++++++++++++++++++++++
 2 files changed, 107 insertions(+), 93 deletions(-)
 create mode 100755 test/T315-emacs-tagging.sh

diff --git a/test/T310-emacs.sh b/test/T310-emacs.sh
index a05b828a..9d0df187 100755
--- a/test/T310-emacs.sh
+++ b/test/T310-emacs.sh
@@ -130,75 +130,6 @@ test_emacs '(notmuch-search "tag:inbox")
 	    (test-output)'
 test_expect_equal_file $EXPECTED/notmuch-show-thread-maildir-storage OUTPUT
 
-test_begin_subtest "Add tag from search view"
-os_x_darwin_thread=$(notmuch search --output=threads id:ddd65cda0911171950o4eea4389v86de9525e46052d3@mail.gmail.com)
-test_emacs "(notmuch-search \"$os_x_darwin_thread\")
-	    (notmuch-test-wait)
-	    (execute-kbd-macro \"+tag-from-search-view\")"
-output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox tag-from-search-view unread)"
-
-test_begin_subtest "Remove tag from search view"
-test_emacs "(notmuch-search \"$os_x_darwin_thread\")
-	    (notmuch-test-wait)
-	    (execute-kbd-macro \"-tag-from-search-view\")"
-output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox unread)"
-
-test_begin_subtest "Add tag (large query)"
-# We use a long query to force us into batch mode and use a funny tag
-# that requires escaping for batch tagging.
-test_emacs "(notmuch-tag (concat \"$os_x_darwin_thread\" \" or \" (mapconcat #'identity (make-list notmuch-tag-argument-limit \"x\") \"-\")) (list \"+tag-from-%-large-query\"))"
-output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox tag-from-%-large-query unread)"
-notmuch tag -tag-from-%-large-query $os_x_darwin_thread
-
-test_begin_subtest "notmuch-show: add single tag to single message"
-test_emacs "(notmuch-show \"$os_x_darwin_thread\")
-	    (execute-kbd-macro \"+tag-from-show-view\")"
-output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox tag-from-show-view unread)"
-
-test_begin_subtest "notmuch-show: remove single tag from single message"
-test_emacs "(notmuch-show \"$os_x_darwin_thread\")
-	    (execute-kbd-macro \"-tag-from-show-view\")"
-output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox unread)"
-
-test_begin_subtest "notmuch-show: add multiple tags to single message"
-test_emacs "(notmuch-show \"$os_x_darwin_thread\")
-	    (execute-kbd-macro \"+tag1-from-show-view +tag2-from-show-view\")"
-output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox tag1-from-show-view tag2-from-show-view unread)"
-
-test_begin_subtest "notmuch-show: remove multiple tags from single message"
-test_emacs "(notmuch-show \"$os_x_darwin_thread\")
-	    (execute-kbd-macro \"-tag1-from-show-view -tag2-from-show-view\")"
-output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox unread)"
-
-test_begin_subtest "notmuch-show: before-tag-hook is run, variables are defined"
-output=$(test_emacs '(let ((notmuch-test-tag-hook-output nil)
-	          (notmuch-before-tag-hook (function notmuch-test-tag-hook)))
-	       (notmuch-show "id:ddd65cda0911171950o4eea4389v86de9525e46052d3@mail.gmail.com")
-	       (execute-kbd-macro "+activate-hook\n")
-	       (execute-kbd-macro "-activate-hook\n")
-	       notmuch-test-tag-hook-output)')
-test_expect_equal "$output" \
-'(("id:ddd65cda0911171950o4eea4389v86de9525e46052d3@mail.gmail.com" "-activate-hook")
- ("id:ddd65cda0911171950o4eea4389v86de9525e46052d3@mail.gmail.com" "+activate-hook"))'
-
-test_begin_subtest "notmuch-show: after-tag-hook is run, variables are defined"
-output=$(test_emacs '(let ((notmuch-test-tag-hook-output nil)
-	          (notmuch-after-tag-hook (function notmuch-test-tag-hook)))
-	       (notmuch-show "id:ddd65cda0911171950o4eea4389v86de9525e46052d3@mail.gmail.com")
-	       (execute-kbd-macro "+activate-hook\n")
-	       (execute-kbd-macro "-activate-hook\n")
-	       notmuch-test-tag-hook-output)')
-test_expect_equal "$output" \
-'(("id:ddd65cda0911171950o4eea4389v86de9525e46052d3@mail.gmail.com" "-activate-hook")
- ("id:ddd65cda0911171950o4eea4389v86de9525e46052d3@mail.gmail.com" "+activate-hook"))'
-
 test_begin_subtest "Message with .. in Message-Id:"
 add_message [id]=123..456@example '[subject]="Message with .. in Message-Id"'
 test_emacs '(notmuch-search "id:\"123..456@example\"")
@@ -1133,30 +1064,6 @@ This is a warning (see *Notmuch errors* for more details)
 This is a warning
 This is another warning"
 
-test_begin_subtest "Search thread tag operations are race-free"
-add_message '[subject]="Search race test"'
-gen_msg_id_1=$gen_msg_id
-generate_message '[in-reply-to]="<'$gen_msg_id_1'>"' \
-	    '[references]="<'$gen_msg_id_1'>"' \
-	    '[subject]="Search race test two"'
-test_emacs '(notmuch-search "subject:\"search race test\"")
-	    (notmuch-test-wait)
-	    (notmuch-poll)
-	    (execute-kbd-macro "+search-thread-race-tag")'
-output=$(notmuch search --output=messages 'tag:search-thread-race-tag')
-test_expect_equal "$output" "id:$gen_msg_id_1"
-
-test_begin_subtest "Search global tag operations are race-free"
-generate_message '[in-reply-to]="<'$gen_msg_id_1'>"' \
-	    '[references]="<'$gen_msg_id_1'>"' \
-	    '[subject]="Re: Search race test"'
-test_emacs '(notmuch-search "subject:\"search race test\" -subject:two")
-	    (notmuch-test-wait)
-	    (notmuch-poll)
-	    (execute-kbd-macro "*+search-global-race-tag")'
-output=$(notmuch search --output=messages 'tag:search-global-race-tag')
-test_expect_equal "$output" "id:$gen_msg_id_1"
-
 test_begin_subtest "Term escaping"
 output=$(test_emacs "(mapcar 'notmuch-escape-boolean-term (list
 	\"\"
diff --git a/test/T315-emacs-tagging.sh b/test/T315-emacs-tagging.sh
new file mode 100755
index 00000000..02fd3d27
--- /dev/null
+++ b/test/T315-emacs-tagging.sh
@@ -0,0 +1,107 @@
+#!/usr/bin/env bash
+
+test_description="emacs interface"
+. $(dirname "$0")/test-lib.sh || exit 1
+. $NOTMUCH_SRCDIR/test/test-lib-emacs.sh || exit 1
+
+EXPECTED=$NOTMUCH_SRCDIR/test/emacs.expected-output
+
+test_require_emacs
+add_email_corpus
+
+test_begin_subtest "Add tag from search view"
+os_x_darwin_thread=$(notmuch search --output=threads id:ddd65cda0911171950o4eea4389v86de9525e46052d3@mail.gmail.com)
+test_emacs "(notmuch-search \"$os_x_darwin_thread\")
+	    (notmuch-test-wait)
+	    (execute-kbd-macro \"+tag-from-search-view\")"
+output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox tag-from-search-view unread)"
+
+test_begin_subtest "Remove tag from search view"
+test_emacs "(notmuch-search \"$os_x_darwin_thread\")
+	    (notmuch-test-wait)
+	    (execute-kbd-macro \"-tag-from-search-view\")"
+output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox unread)"
+
+test_begin_subtest "Add tag (large query)"
+# We use a long query to force us into batch mode and use a funny tag
+# that requires escaping for batch tagging.
+test_emacs "(notmuch-tag (concat \"$os_x_darwin_thread\" \" or \" (mapconcat #'identity (make-list notmuch-tag-argument-limit \"x\") \"-\")) (list \"+tag-from-%-large-query\"))"
+output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox tag-from-%-large-query unread)"
+notmuch tag -tag-from-%-large-query $os_x_darwin_thread
+
+test_begin_subtest "notmuch-show: add single tag to single message"
+test_emacs "(notmuch-show \"$os_x_darwin_thread\")
+	    (execute-kbd-macro \"+tag-from-show-view\")"
+output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox tag-from-show-view unread)"
+
+test_begin_subtest "notmuch-show: remove single tag from single message"
+test_emacs "(notmuch-show \"$os_x_darwin_thread\")
+	    (execute-kbd-macro \"-tag-from-show-view\")"
+output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox unread)"
+
+test_begin_subtest "notmuch-show: add multiple tags to single message"
+test_emacs "(notmuch-show \"$os_x_darwin_thread\")
+	    (execute-kbd-macro \"+tag1-from-show-view +tag2-from-show-view\")"
+output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox tag1-from-show-view tag2-from-show-view unread)"
+
+test_begin_subtest "notmuch-show: remove multiple tags from single message"
+test_emacs "(notmuch-show \"$os_x_darwin_thread\")
+	    (execute-kbd-macro \"-tag1-from-show-view -tag2-from-show-view\")"
+output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox unread)"
+
+test_begin_subtest "notmuch-show: before-tag-hook is run, variables are defined"
+output=$(test_emacs '(let ((notmuch-test-tag-hook-output nil)
+	          (notmuch-before-tag-hook (function notmuch-test-tag-hook)))
+	       (notmuch-show "id:ddd65cda0911171950o4eea4389v86de9525e46052d3@mail.gmail.com")
+	       (execute-kbd-macro "+activate-hook\n")
+	       (execute-kbd-macro "-activate-hook\n")
+	       notmuch-test-tag-hook-output)')
+test_expect_equal "$output" \
+'(("id:ddd65cda0911171950o4eea4389v86de9525e46052d3@mail.gmail.com" "-activate-hook")
+ ("id:ddd65cda0911171950o4eea4389v86de9525e46052d3@mail.gmail.com" "+activate-hook"))'
+
+test_begin_subtest "notmuch-show: after-tag-hook is run, variables are defined"
+output=$(test_emacs '(let ((notmuch-test-tag-hook-output nil)
+	          (notmuch-after-tag-hook (function notmuch-test-tag-hook)))
+	       (notmuch-show "id:ddd65cda0911171950o4eea4389v86de9525e46052d3@mail.gmail.com")
+	       (execute-kbd-macro "+activate-hook\n")
+	       (execute-kbd-macro "-activate-hook\n")
+	       notmuch-test-tag-hook-output)')
+test_expect_equal "$output" \
+'(("id:ddd65cda0911171950o4eea4389v86de9525e46052d3@mail.gmail.com" "-activate-hook")
+ ("id:ddd65cda0911171950o4eea4389v86de9525e46052d3@mail.gmail.com" "+activate-hook"))'
+
+
+test_begin_subtest "Search thread tag operations are race-free"
+add_message '[subject]="Search race test"'
+gen_msg_id_1=$gen_msg_id
+generate_message '[in-reply-to]="<'$gen_msg_id_1'>"' \
+	    '[references]="<'$gen_msg_id_1'>"' \
+	    '[subject]="Search race test two"'
+test_emacs '(notmuch-search "subject:\"search race test\"")
+	    (notmuch-test-wait)
+	    (notmuch-poll)
+	    (execute-kbd-macro "+search-thread-race-tag")'
+output=$(notmuch search --output=messages 'tag:search-thread-race-tag')
+test_expect_equal "$output" "id:$gen_msg_id_1"
+
+test_begin_subtest "Search global tag operations are race-free"
+generate_message '[in-reply-to]="<'$gen_msg_id_1'>"' \
+	    '[references]="<'$gen_msg_id_1'>"' \
+	    '[subject]="Re: Search race test"'
+test_emacs '(notmuch-search "subject:\"search race test\" -subject:two")
+	    (notmuch-test-wait)
+	    (notmuch-poll)
+	    (execute-kbd-macro "*+search-global-race-tag")'
+output=$(notmuch search --output=messages 'tag:search-global-race-tag')
+test_expect_equal "$output" "id:$gen_msg_id_1"
+
+
+test_done
-- 
2.34.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC PATCH v2 3/8] WIP enable running test_emacs from performance tests
  2022-01-29 19:44 v2 undo tag operations in emacs David Bremner
  2022-01-29 19:44 ` [RFC PATCH v2 1/8] test: split variable settings to their own file David Bremner
  2022-01-29 19:44 ` [RFC PATCH v2 2/8] test/emacs: split out emacs related tests David Bremner
@ 2022-01-29 19:44 ` David Bremner
  2022-01-29 19:44 ` [RFC PATCH v2 4/8] perf-test: inital emacs tests David Bremner
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: David Bremner @ 2022-01-29 19:44 UTC (permalink / raw)
  To: notmuch

The extra "" around the time output is a bit annoying, but maybe not
worth blocking on.
---
 configure                         |  3 +++
 performance-test/perf-test-lib.sh |  5 +++++
 test/test-lib-common.sh           | 14 ++++++++++++++
 test/test-lib.el                  |  4 ++++
 test/test-lib.sh                  | 14 --------------
 5 files changed, 26 insertions(+), 14 deletions(-)

diff --git a/configure b/configure
index 36f3f606..9097cee7 100755
--- a/configure
+++ b/configure
@@ -1551,6 +1551,9 @@ cat > sh.config <<EOF
 
 NOTMUCH_SRCDIR='${NOTMUCH_SRCDIR}'
 
+# Emacs exists, and notmuch was configured with it.
+NOTMUCH_WITH_EMACS=${WITH_EMACS}
+
 # Whether to have Xapian retry lock
 NOTMUCH_HAVE_XAPIAN_DB_RETRY_LOCK=${WITH_RETRY_LOCK}
 
diff --git a/performance-test/perf-test-lib.sh b/performance-test/perf-test-lib.sh
index 0e4915e3..c34f8cd6 100644
--- a/performance-test/perf-test-lib.sh
+++ b/performance-test/perf-test-lib.sh
@@ -210,6 +210,11 @@ print_header ()
     printf "\t\t\tWall(s)\tUsr(s)\tSys(s)\tRes(K)\tIn/Out(512B)\n"
 }
 
+print_emacs_header ()
+{
+    printf "\t\t\tWall(s)\tGCs\tGC time(s)\n"
+}
+
 time_run ()
 {
     printf "  %-22s" "$1"
diff --git a/test/test-lib-common.sh b/test/test-lib-common.sh
index ebbf4cdf..18fa29c0 100644
--- a/test/test-lib-common.sh
+++ b/test/test-lib-common.sh
@@ -29,6 +29,20 @@ if [[ -z "$NOTMUCH_SRCDIR" ]] || [[ -z "$NOTMUCH_BUILDDIR" ]]; then
 	exit 1
 fi
 
+# Explicitly require external prerequisite.  Useful when binary is
+# called indirectly (e.g. from emacs).
+# Returns success if dependency is available, failure otherwise.
+test_require_external_prereq () {
+	local binary
+	binary="$1"
+	if [[ ${test_missing_external_prereq_["${binary}"]} == t ]]; then
+		# dependency is missing, call the replacement function to note it
+		eval "$binary"
+	else
+		true
+	fi
+}
+
 backup_database () {
     test_name=$(basename $0 .sh)
     rm -rf $TMP_DIRECTORY/notmuch-dir-backup."$test_name"
diff --git a/test/test-lib.el b/test/test-lib.el
index 6831b46f..755081ed 100644
--- a/test/test-lib.el
+++ b/test/test-lib.el
@@ -186,6 +186,10 @@ running, quit if it terminated."
        (t (message "%s" err)))
      (with-current-buffer "*Messages*" (test-output "MESSAGES"))))
 
+(defmacro test-time (title &rest body)
+  `(let ((results (mapcar (lambda (x) (/ x 5.0)) (benchmark-run 5 ,@body))))
+     (format "%s\t\t%0.2f\t%0.2f\t%0.2f" ,title (nth 0 results) (nth 1 results) (nth 2 results))))
+
 ;; For historical reasons, we hide deleted tags by default in the test
 ;; suite
 (setq notmuch-tag-deleted-formats
diff --git a/test/test-lib.sh b/test/test-lib.sh
index 89e5c6d8..59b6079d 100644
--- a/test/test-lib.sh
+++ b/test/test-lib.sh
@@ -603,20 +603,6 @@ $binary () {
 	fi
 }
 
-# Explicitly require external prerequisite.  Useful when binary is
-# called indirectly (e.g. from emacs).
-# Returns success if dependency is available, failure otherwise.
-test_require_external_prereq () {
-	local binary
-	binary="$1"
-	if [[ ${test_missing_external_prereq_["${binary}"]} == t ]]; then
-		# dependency is missing, call the replacement function to note it
-		eval "$binary"
-	else
-		true
-	fi
-}
-
 # You are not expected to call test_ok_ and test_failure_ directly, use
 # the text_expect_* functions instead.
 
-- 
2.34.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC PATCH v2 4/8] perf-test: inital emacs tests
  2022-01-29 19:44 v2 undo tag operations in emacs David Bremner
                   ` (2 preceding siblings ...)
  2022-01-29 19:44 ` [RFC PATCH v2 3/8] WIP enable running test_emacs from performance tests David Bremner
@ 2022-01-29 19:44 ` David Bremner
  2022-01-29 19:44 ` [RFC PATCH v2 5/8] cli/tag: add --output={none,lastmod} argument David Bremner
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: David Bremner @ 2022-01-29 19:44 UTC (permalink / raw)
  To: notmuch

Time tag operations, to see if it is worthwhile keeping both the
batch and the none-batch calls to notmuch tag.
---
 performance-test/T06-emacs.sh | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)
 create mode 100755 performance-test/T06-emacs.sh

diff --git a/performance-test/T06-emacs.sh b/performance-test/T06-emacs.sh
new file mode 100755
index 00000000..ca0b3ab5
--- /dev/null
+++ b/performance-test/T06-emacs.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+test_description='emacs operations'
+
+. $(dirname "$0")/perf-test-lib.sh || exit 1
+. $NOTMUCH_SRCDIR/test/test-lib-emacs.sh || exit 1
+
+if [ "${NOTMUCH_with_EMACS}" = "0" ]; then
+    echo "notmuch was not configured with emacs"
+    exit 0
+fi
+
+time_start
+
+print_emacs_header
+
+MSGS=$(notmuch search --output=messages "*" | shuf -n 50 | awk '{printf " \"%s\"",$1}')
+
+test_emacs "(test-time
+ \"tag messages\"
+ (dolist (msg (list $MSGS))
+   (notmuch-tag msg (list \"+test\"))
+   (notmuch-tag msg (list \"-test\"))))
+"
+
+time_done
-- 
2.34.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC PATCH v2 5/8] cli/tag: add --output={none,lastmod} argument
  2022-01-29 19:44 v2 undo tag operations in emacs David Bremner
                   ` (3 preceding siblings ...)
  2022-01-29 19:44 ` [RFC PATCH v2 4/8] perf-test: inital emacs tests David Bremner
@ 2022-01-29 19:44 ` David Bremner
  2022-01-29 19:44 ` [RFC PATCH v2 6/8] WIP: support tag --output=lastmod David Bremner
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: David Bremner @ 2022-01-29 19:44 UTC (permalink / raw)
  To: notmuch

This commit only changes the command line parsing, no new output is
added.
---
 notmuch-tag.c        | 10 ++++++++++
 test/T150-tagging.sh | 20 ++++++++++++++++++++
 2 files changed, 30 insertions(+)

diff --git a/notmuch-tag.c b/notmuch-tag.c
index 71ff06bf..c6ac51d2 100644
--- a/notmuch-tag.c
+++ b/notmuch-tag.c
@@ -22,6 +22,11 @@
 #include "tag-util.h"
 #include "string-util.h"
 
+typedef enum {
+    OUTPUT_NONE	= 0,
+    OUTPUT_LASTMOD	= 1 << 0,
+} output_t;
+
 static volatile sig_atomic_t interrupted;
 
 static void
@@ -228,6 +233,7 @@ notmuch_tag_command (notmuch_database_t *notmuch, int argc, char *argv[])
     const char *input_file_name = NULL;
     int opt_index;
     int ret;
+    int output = OUTPUT_NONE;
     notmuch_bool_t synchronize_flags;
 
     /* Set up our handler for SIGINT */
@@ -240,6 +246,10 @@ notmuch_tag_command (notmuch_database_t *notmuch, int argc, char *argv[])
     notmuch_opt_desc_t options[] = {
 	{ .opt_bool = &batch, .name = "batch" },
 	{ .opt_string = &input_file_name, .name = "input" },
+	{ .opt_keyword = &output, .name = "output", .keywords =
+	      (notmuch_keyword_t []){ { "none", OUTPUT_NONE },
+				      { "lastmod", OUTPUT_LASTMOD },
+				      { 0, 0 } } },
 	{ .opt_bool = &remove_all, .name = "remove-all" },
 	{ .opt_inherit = notmuch_shared_options },
 	{ }
diff --git a/test/T150-tagging.sh b/test/T150-tagging.sh
index 1a2fd77e..ce33dade 100755
--- a/test/T150-tagging.sh
+++ b/test/T150-tagging.sh
@@ -48,6 +48,12 @@ test_expect_equal "$output" "\
 thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; One (inbox tag1 unread)
 thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag1 unread)"
 
+test_begin_subtest "--output=none"
+output=$(notmuch tag --output=none +tag7 '*' 2>&1)
+notmuch tag -tag7 '*'
+test_expect_equal "$output" ""
+
+
 test_begin_subtest "Remove all"
 notmuch tag --remove-all One
 notmuch tag --remove-all +tag5 +tag6 +unread Two
@@ -124,6 +130,13 @@ notmuch search \* | notmuch_search_sanitize > OUTPUT
 notmuch restore --format=batch-tag < backup.tags
 test_expect_equal_file batch.expected OUTPUT
 
+test_begin_subtest "--input --output=none"
+notmuch dump --format=batch-tag > backup.tags
+notmuch tag --input=batch.in --output=none > OUTPUT
+notmuch search \* | notmuch_search_sanitize >> OUTPUT
+notmuch restore --format=batch-tag < backup.tags
+test_expect_equal_file batch.expected OUTPUT
+
 test_begin_subtest "--batch --input"
 notmuch dump --format=batch-tag > backup.tags
 notmuch tag --batch --input=batch.in
@@ -131,6 +144,13 @@ notmuch search \* | notmuch_search_sanitize > OUTPUT
 notmuch restore --format=batch-tag < backup.tags
 test_expect_equal_file batch.expected OUTPUT
 
+test_begin_subtest "--batch --input --output=none"
+notmuch dump --format=batch-tag > backup.tags
+notmuch tag --batch --input=batch.in --output=none > OUTPUT
+notmuch search \* | notmuch_search_sanitize >> OUTPUT
+notmuch restore --format=batch-tag < backup.tags
+test_expect_equal_file batch.expected OUTPUT
+
 test_begin_subtest "--batch --input --remove-all"
 notmuch dump --format=batch-tag > backup.tags
 notmuch tag +foo +bar -- One
-- 
2.34.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC PATCH v2 6/8] WIP: support tag --output=lastmod
  2022-01-29 19:44 v2 undo tag operations in emacs David Bremner
                   ` (4 preceding siblings ...)
  2022-01-29 19:44 ` [RFC PATCH v2 5/8] cli/tag: add --output={none,lastmod} argument David Bremner
@ 2022-01-29 19:44 ` David Bremner
  2022-01-29 19:44 ` [RFC PATCH v2 7/8] WIP/emacs: keep tag history David Bremner
  2022-01-29 19:44 ` [RFC PATCH v2 8/8] WIP: add notmuch-tag-undo David Bremner
  7 siblings, 0 replies; 10+ messages in thread
From: David Bremner @ 2022-01-29 19:44 UTC (permalink / raw)
  To: notmuch

needs documentation, possibly more tests.

output format subject to change.

See the tests for examples of how this could be used in an undo facility
---
 notmuch-tag.c        | 25 ++++++++++++++++++++-----
 test/T150-tagging.sh | 31 +++++++++++++++++++++++++++++++
 2 files changed, 51 insertions(+), 5 deletions(-)

diff --git a/notmuch-tag.c b/notmuch-tag.c
index c6ac51d2..95598091 100644
--- a/notmuch-tag.c
+++ b/notmuch-tag.c
@@ -124,12 +124,14 @@ _optimize_tag_query (void *ctx, const char *orig_query_string,
  */
 static int
 tag_query (void *ctx, notmuch_database_t *notmuch, const char *query_string,
-	   tag_op_list_t *tag_ops, tag_op_flag_t flags)
+	   tag_op_list_t *tag_ops, tag_op_flag_t flags, int output)
 {
     notmuch_query_t *query;
     notmuch_messages_t *messages;
     notmuch_message_t *message;
     notmuch_status_t status;
+    unsigned long before;
+    const char* uuid_before = NULL;
 
     int ret = NOTMUCH_STATUS_SUCCESS;
 
@@ -159,6 +161,10 @@ tag_query (void *ctx, notmuch_database_t *notmuch, const char *query_string,
     if (print_status_query ("notmuch tag", query, status))
 	return status;
 
+    if (output == OUTPUT_LASTMOD) {
+	before = notmuch_database_get_revision (notmuch, &uuid_before);
+    }
+
     for (;
 	 notmuch_messages_valid (messages) && ! interrupted;
 	 notmuch_messages_move_to_next (messages)) {
@@ -171,12 +177,21 @@ tag_query (void *ctx, notmuch_database_t *notmuch, const char *query_string,
 
     notmuch_query_destroy (query);
 
+    if (output == OUTPUT_LASTMOD) {
+	const char *uuid_after = NULL;
+	unsigned long after = notmuch_database_get_revision (notmuch, &uuid_after);
+	if (strcmp (uuid_before, uuid_after))
+	    INTERNAL_ERROR ("uuid changed while holding write lock %s %s", uuid_before, uuid_after);
+
+	printf ("%s\t%lu\t%lu\n", uuid_before, before + 1, after);
+    }
+
     return ret || interrupted;
 }
 
 static int
 tag_file (void *ctx, notmuch_database_t *notmuch, tag_op_flag_t flags,
-	  FILE *input)
+	  FILE *input, int output)
 {
     char *line = NULL;
     char *query_string = NULL;
@@ -209,7 +224,7 @@ tag_file (void *ctx, notmuch_database_t *notmuch, tag_op_flag_t flags,
 	if (ret < 0)
 	    break;
 
-	ret = tag_query (ctx, notmuch, query_string, tag_ops, flags);
+	ret = tag_query (ctx, notmuch, query_string, tag_ops, flags, output);
 	if (ret)
 	    break;
     }
@@ -314,9 +329,9 @@ notmuch_tag_command (notmuch_database_t *notmuch, int argc, char *argv[])
 	tag_flags |= TAG_FLAG_REMOVE_ALL;
 
     if (batch)
-	ret = tag_file (notmuch, notmuch, tag_flags, input);
+	ret = tag_file (notmuch, notmuch, tag_flags, input, output);
     else
-	ret = tag_query (notmuch, notmuch, query_string, tag_ops, tag_flags);
+	ret = tag_query (notmuch, notmuch, query_string, tag_ops, tag_flags, output);
 
     notmuch_database_destroy (notmuch);
 
diff --git a/test/T150-tagging.sh b/test/T150-tagging.sh
index ce33dade..baf55598 100755
--- a/test/T150-tagging.sh
+++ b/test/T150-tagging.sh
@@ -53,6 +53,21 @@ output=$(notmuch tag --output=none +tag7 '*' 2>&1)
 notmuch tag -tag7 '*'
 test_expect_equal "$output" ""
 
+test_begin_subtest "--output=lastmod produces 3 columns"
+output=$(notmuch tag --output=lastmod +tag7 '*' | sed 's/[0-9a-f][0-9a-f]*/n/g' )
+notmuch tag -tag7 '*'
+test_expect_equal "$output" "n-n-n-n-n	n	n"
+
+test_begin_subtest "--output=lastmod provides undo"
+notmuch dump > BEFORE
+tag=${RANDOM}
+notmuch count --lastmod '*' | cut -f 2 >> BEFORE
+notmuch tag --output=lastmod "+$tag" '*' > OUTPUT
+read uuid from to < OUTPUT
+notmuch tag --uuid="$uuid" "-$tag" lastmod:"$from".."$to"
+notmuch dump > AFTER
+echo "$uuid" >> AFTER
+test_expect_equal_file BEFORE AFTER
 
 test_begin_subtest "Remove all"
 notmuch tag --remove-all One
@@ -118,6 +133,13 @@ cat > batch.in <<EOF
 -tag5 +tag6 Two
 EOF
 
+# partial input for use in undoing above changes
+cat > restore.in <<EOF
+-%40 +tag5 -tag6 --
+-tag1 +tag1 +tag4 +tag4 --
++tag5 -tag6 --
+EOF
+
 cat > batch.expected <<EOF
 thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; One (@ inbox tag6 unread)
 thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag4 tag6 unread)
@@ -151,6 +173,15 @@ notmuch search \* | notmuch_search_sanitize >> OUTPUT
 notmuch restore --format=batch-tag < backup.tags
 test_expect_equal_file batch.expected OUTPUT
 
+backup_database
+test_begin_subtest "--batch --input --output=lastmod"
+notmuch dump > EXPECTED
+notmuch tag --batch --input=batch.in --output=lastmod | awk '{printf " lastmod:%d..%d\n", $2, $3}' > queries
+paste restore.in queries | notmuch tag --batch
+notmuch dump > OUTPUT
+test_expect_equal_file EXPECTED OUTPUT
+restore_database
+
 test_begin_subtest "--batch --input --remove-all"
 notmuch dump --format=batch-tag > backup.tags
 notmuch tag +foo +bar -- One
-- 
2.34.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC PATCH v2 7/8] WIP/emacs: keep tag history
  2022-01-29 19:44 v2 undo tag operations in emacs David Bremner
                   ` (5 preceding siblings ...)
  2022-01-29 19:44 ` [RFC PATCH v2 6/8] WIP: support tag --output=lastmod David Bremner
@ 2022-01-29 19:44 ` David Bremner
  2022-01-29 19:44 ` [RFC PATCH v2 8/8] WIP: add notmuch-tag-undo David Bremner
  7 siblings, 0 replies; 10+ messages in thread
From: David Bremner @ 2022-01-29 19:44 UTC (permalink / raw)
  To: notmuch

---
 emacs/notmuch-tag.el       | 30 ++++++++++++++++++++++--------
 test/T315-emacs-tagging.sh | 12 ++++++++++++
 2 files changed, 34 insertions(+), 8 deletions(-)

diff --git a/emacs/notmuch-tag.el b/emacs/notmuch-tag.el
index 8af09e68..28a2b596 100644
--- a/emacs/notmuch-tag.el
+++ b/emacs/notmuch-tag.el
@@ -275,6 +275,10 @@ This can be used with `notmuch-tag-format-image-data'."
   </g>
 </svg>")
 
+;;; track history of tag operations
+(defvar-local notmuch-tag-history nil
+  "Global history of `notmuch-tag' function.")
+
 ;;; Format Handling
 
 (defvar notmuch-tag--format-cache (make-hash-table :test 'equal)
@@ -460,7 +464,7 @@ from TAGS if present."
 This limits the length of arguments passed to the notmuch CLI to
 avoid system argument length limits and performance problems.")
 
-(defun notmuch-tag (query tag-changes)
+(defun notmuch-tag (query tag-changes &optional omit-hist)
   "Add/remove tags in TAG-CHANGES to messages matching QUERY.
 
 QUERY should be a string containing the search-terms.
@@ -481,13 +485,23 @@ notmuch-after-tag-hook will be run."
     (notmuch-dlet ((tag-changes tag-changes)
 		   (query query))
       (run-hooks 'notmuch-before-tag-hook))
-    (if (<= (length query) notmuch-tag-argument-limit)
-	(apply 'notmuch-call-notmuch-process "tag"
-	       (append tag-changes (list "--" query)))
-      ;; Use batch tag mode to avoid argument length limitations
-      (let ((batch-op (concat (mapconcat #'notmuch-hex-encode tag-changes " ")
-			      " -- " query)))
-	(notmuch-call-notmuch-process :stdin-string batch-op "tag" "--batch")))
+    (let ((parent-buffer (current-buffer))
+	  (batch-mode (> (length query) notmuch-tag-argument-limit)))
+      (with-temp-buffer
+	(when batch-mode
+	  (insert (concat (mapconcat #'notmuch-hex-encode tag-changes " ") " -- " query)))
+	(apply #'notmuch--call-process-region
+	       (point-min) (point-max)
+	       notmuch-command t t nil "tag" "--output=lastmod"
+	       (if batch-mode '("--batch")
+		 (append tag-changes (list "--" query))))
+	(unless omit-hist
+	  (goto-char (point-min))
+	  (let ((uuid (read (current-buffer)))
+		(from (read (current-buffer)))
+		(to   (read (current-buffer))))
+	    (with-current-buffer parent-buffer
+	      (push (list :uuid uuid :from from :to to :tag-changes tag-changes) notmuch-tag-history))))))
     (notmuch-dlet ((tag-changes tag-changes)
 		   (query query))
       (run-hooks 'notmuch-after-tag-hook))))
diff --git a/test/T315-emacs-tagging.sh b/test/T315-emacs-tagging.sh
index 02fd3d27..e4540a3a 100755
--- a/test/T315-emacs-tagging.sh
+++ b/test/T315-emacs-tagging.sh
@@ -103,5 +103,17 @@ test_emacs '(notmuch-search "subject:\"search race test\" -subject:two")
 output=$(notmuch search --output=messages 'tag:search-global-race-tag')
 test_expect_equal "$output" "id:$gen_msg_id_1"
 
+test_begin_subtest "history interval matches size of thread"
+output=$(test_emacs "(let ((notmuch-tag-history nil))
+  (notmuch-search \"$os_x_darwin_thread\")
+  (notmuch-test-wait)
+  (execute-kbd-macro \"+tag-for-history\")
+  (let* ((plist (car notmuch-tag-history))
+         (from (plist-get plist :from))
+         (to (plist-get plist :to)))
+        (+ 1 (- to from))))
+  ")
+count=$(notmuch count "$os_x_darwin_thread")
+test_expect_equal "$output" "$count"
 
 test_done
-- 
2.34.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC PATCH v2 8/8] WIP: add notmuch-tag-undo
  2022-01-29 19:44 v2 undo tag operations in emacs David Bremner
                   ` (6 preceding siblings ...)
  2022-01-29 19:44 ` [RFC PATCH v2 7/8] WIP/emacs: keep tag history David Bremner
@ 2022-01-29 19:44 ` David Bremner
  2022-02-02 14:34   ` David Bremner
  7 siblings, 1 reply; 10+ messages in thread
From: David Bremner @ 2022-01-29 19:44 UTC (permalink / raw)
  To: notmuch

---
 emacs/notmuch-tag.el       | 12 ++++++++++++
 test/T315-emacs-tagging.sh | 19 +++++++++++++++++++
 2 files changed, 31 insertions(+)

diff --git a/emacs/notmuch-tag.el b/emacs/notmuch-tag.el
index 28a2b596..7ed5c1a5 100644
--- a/emacs/notmuch-tag.el
+++ b/emacs/notmuch-tag.el
@@ -506,6 +506,18 @@ notmuch-after-tag-hook will be run."
 		   (query query))
       (run-hooks 'notmuch-after-tag-hook))))
 
+(defun notmuch-tag-undo ()
+  (interactive)
+  (when (null notmuch-tag-history)
+    (error "no further notmuch undo information"))
+  (let* ((action (pop notmuch-tag-history))
+	 (from (plist-get action :from))
+	 (to (plist-get action :to))
+	 (changes (notmuch-tag-change-list (plist-get action :tag-changes) t))
+	 (query (format "lastmod:%d..%d" from to)))
+    (when (<= from to)
+      (notmuch-tag query changes t))))
+
 (defun notmuch-tag-change-list (tags &optional reverse)
   "Convert TAGS into a list of tag changes.
 
diff --git a/test/T315-emacs-tagging.sh b/test/T315-emacs-tagging.sh
index e4540a3a..72d8b5b0 100755
--- a/test/T315-emacs-tagging.sh
+++ b/test/T315-emacs-tagging.sh
@@ -116,4 +116,23 @@ output=$(test_emacs "(let ((notmuch-tag-history nil))
 count=$(notmuch count "$os_x_darwin_thread")
 test_expect_equal "$output" "$count"
 
+test_begin_subtest "undo with empty history is an error"
+test_emacs "(let ((notmuch-tag-history nil))
+  (test-log-error
+   (notmuch-tag-undo)))
+  "
+cat <<EOF > EXPECTED
+(error no further notmuch undo information)
+EOF
+test_expect_equal_file EXPECTED MESSAGES
+
+test_begin_subtest "undo tagging in search mode"
+test_emacs "(let ((notmuch-tag-history nil))
+  (notmuch-search \"$os_x_darwin_thread\")
+  (notmuch-test-wait)
+  (execute-kbd-macro \"+tag-to-be-undone\")
+  (notmuch-tag-undo))"
+count=$(notmuch count "tag:tag-to-be-undone")
+test_expect_equal "$count" "0"
+
 test_done
-- 
2.34.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [RFC PATCH v2 8/8] WIP: add notmuch-tag-undo
  2022-01-29 19:44 ` [RFC PATCH v2 8/8] WIP: add notmuch-tag-undo David Bremner
@ 2022-02-02 14:34   ` David Bremner
  0 siblings, 0 replies; 10+ messages in thread
From: David Bremner @ 2022-02-02 14:34 UTC (permalink / raw)
  To: notmuch

David Bremner <david@tethera.net> writes:

> +(defun notmuch-tag-undo ()
> +  (interactive)
> +  (when (null notmuch-tag-history)
> +    (error "no further notmuch undo information"))
> +  (let* ((action (pop notmuch-tag-history))
> +	 (from (plist-get action :from))
> +	 (to (plist-get action :to))
> +	 (changes (notmuch-tag-change-list (plist-get action :tag-changes) t))
> +	 (query (format "lastmod:%d..%d" from to)))
> +    (when (<= from to)
> +      (notmuch-tag query changes t))))

I got pretty far down this path before I realized there is a fundamantal
flaw with using lastmod info for undo. With some extra work, we can only
manage one level of undo, since the undo operation itself invalidates
the queries in the undo history. So now I don't really know the best
approach.

d

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2022-02-02 14:34 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-29 19:44 v2 undo tag operations in emacs David Bremner
2022-01-29 19:44 ` [RFC PATCH v2 1/8] test: split variable settings to their own file David Bremner
2022-01-29 19:44 ` [RFC PATCH v2 2/8] test/emacs: split out emacs related tests David Bremner
2022-01-29 19:44 ` [RFC PATCH v2 3/8] WIP enable running test_emacs from performance tests David Bremner
2022-01-29 19:44 ` [RFC PATCH v2 4/8] perf-test: inital emacs tests David Bremner
2022-01-29 19:44 ` [RFC PATCH v2 5/8] cli/tag: add --output={none,lastmod} argument David Bremner
2022-01-29 19:44 ` [RFC PATCH v2 6/8] WIP: support tag --output=lastmod David Bremner
2022-01-29 19:44 ` [RFC PATCH v2 7/8] WIP/emacs: keep tag history David Bremner
2022-01-29 19:44 ` [RFC PATCH v2 8/8] WIP: add notmuch-tag-undo David Bremner
2022-02-02 14:34   ` David Bremner

Code repositories for project(s) associated with this public inbox

	https://yhetil.org/notmuch.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).