unofficial mirror of notmuch@notmuchmail.org
 help / color / mirror / code / Atom feed
* [PATCH] cli: add options --offset and --limit to notmuch show
@ 2022-10-11 22:19 Robin Jarry
  2022-10-11 22:25 ` Robin Jarry
  2022-10-12 19:39 ` Tomi Ollila
  0 siblings, 2 replies; 5+ messages in thread
From: Robin Jarry @ 2022-10-11 22:19 UTC (permalink / raw)
  To: notmuch; +Cc: Robin Jarry, Tim Culverhouse

notmuch search does not output header values. However, when browsing
through a large email corpus, it can be time saving to be able to
paginate without running notmuch show for each message/thread.

Add --offset and --limit options to notmuch show. This is inspired from
commit 796b629c3b82 ("cli: add options --offset and --limit to notmuch
search").

Update man page, shell completion and add a test case to ensure it works
as expected.

Cc: Tim Culverhouse <tim@timculverhouse.com>
Signed-off-by: Robin Jarry <robin@jarry.cc>
---
 completion/notmuch-completion.bash |  2 +-
 completion/zsh/_notmuch            |  2 +
 doc/man1/notmuch-show.rst          |  9 ++++
 notmuch-client.h                   |  2 +
 notmuch-show.c                     | 49 +++++++++++++++---
 test/T131-show-limiting.sh         | 81 ++++++++++++++++++++++++++++++
 6 files changed, 138 insertions(+), 7 deletions(-)
 create mode 100755 test/T131-show-limiting.sh

diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash
index 0022b54bff5d..3748846edf83 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -530,7 +530,7 @@ _notmuch_show()
     ! $split &&
     case "${cur}" in
 	-*)
-	    local options="--entire-thread= --format= --exclude= --body= --format-version= --part= --verify --decrypt= --include-html ${_notmuch_shared_options}"
+	    local options="--entire-thread= --format= --exclude= --body= --format-version= --part= --verify --decrypt= --include-html --limit= --offset= ${_notmuch_shared_options}"
 	    compopt -o nospace
 	    COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
 	    ;;
diff --git a/completion/zsh/_notmuch b/completion/zsh/_notmuch
index e207d90b7202..0bdd7f772a7a 100644
--- a/completion/zsh/_notmuch
+++ b/completion/zsh/_notmuch
@@ -245,6 +245,8 @@ _notmuch_show() {
     '--exclude=[respect excluded tags setting]:exclude tags:(true false)' \
     '--body=[output body]:output body content:(true false)' \
     '--include-html[include text/html parts in the output]' \
+    '--limit=[limit the number of displayed results]:limit: ' \
+    '--offset=[skip displaying the first N results]:offset: ' \
     '*::search term:_notmuch_search_term'
 }
 
diff --git a/doc/man1/notmuch-show.rst b/doc/man1/notmuch-show.rst
index 2c0a0de6ad16..c13d94de0244 100644
--- a/doc/man1/notmuch-show.rst
+++ b/doc/man1/notmuch-show.rst
@@ -130,6 +130,15 @@ Supported options for **show** include
    By default, results will be displayed in reverse chronological
    order, (that is, the newest results will be displayed first).
 
+.. option:: --offset=[-]N
+
+   Skip displaying the first N results. With the leading '-', start
+   at the Nth result from the end.
+
+.. option:: --limit=N
+
+   Limit the number of displayed results to N.
+
 .. option:: --verify
 
    Compute and report the validity of any MIME cryptographic
diff --git a/notmuch-client.h b/notmuch-client.h
index 21b49908ae24..1a87240d3c21 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -77,6 +77,8 @@ typedef struct notmuch_show_params {
     bool output_body;
     int duplicate;
     int part;
+    int offset;
+    int limit;
     _notmuch_crypto_t crypto;
     bool include_html;
     GMimeStream *out_stream;
diff --git a/notmuch-show.c b/notmuch-show.c
index ee9efa7448d7..ad31e0123268 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -1159,6 +1159,18 @@ do_show_threaded (void *ctx,
     notmuch_thread_t *thread;
     notmuch_messages_t *messages;
     notmuch_status_t status, res = NOTMUCH_STATUS_SUCCESS;
+    int i;
+
+    if (params->offset < 0) {
+	unsigned count;
+	notmuch_status_t s = notmuch_query_count_threads (query, &count);
+	if (print_status_query ("notmuch search", query, s))
+	    return 1;
+
+	params->offset += count;
+	if (params->offset < 0)
+	    params->offset = 0;
+    }
 
     status = notmuch_query_search_threads (query, &threads);
     if (print_status_query ("notmuch show", query, status))
@@ -1166,11 +1178,16 @@ do_show_threaded (void *ctx,
 
     sp->begin_list (sp);
 
-    for (;
-	 notmuch_threads_valid (threads);
-	 notmuch_threads_move_to_next (threads)) {
+    for (i = 0;
+	 notmuch_threads_valid (threads) && (params->limit < 0 || i < params->offset + params->limit);
+	 notmuch_threads_move_to_next (threads), i++) {
 	thread = notmuch_threads_get (threads);
 
+	if (i < params->offset) {
+	    notmuch_thread_destroy (thread);
+	    continue;
+	}
+
 	messages = notmuch_thread_get_toplevel_messages (thread);
 
 	if (messages == NULL)
@@ -1201,6 +1218,18 @@ do_show_unthreaded (void *ctx,
     notmuch_message_t *message;
     notmuch_status_t status, res = NOTMUCH_STATUS_SUCCESS;
     notmuch_bool_t excluded;
+    int i;
+
+    if (params->offset < 0) {
+	unsigned count;
+	notmuch_status_t s = notmuch_query_count_messages (query, &count);
+	if (print_status_query ("notmuch search", query, s))
+	    return 1;
+
+	params->offset += count;
+	if (params->offset < 0)
+	    params->offset = 0;
+    }
 
     status = notmuch_query_search_messages (query, &messages);
     if (print_status_query ("notmuch show", query, status))
@@ -1208,9 +1237,13 @@ do_show_unthreaded (void *ctx,
 
     sp->begin_list (sp);
 
-    for (;
-	 notmuch_messages_valid (messages);
-	 notmuch_messages_move_to_next (messages)) {
+    for (i = 0;
+	 notmuch_messages_valid (messages) && (params->limit < 0 || i < params->offset + params->limit);
+	 notmuch_messages_move_to_next (messages), i++) {
+	if (i < params->offset) {
+	    continue;
+	}
+
 	sp->begin_list (sp);
 	sp->begin_list (sp);
 
@@ -1287,6 +1320,8 @@ notmuch_show_command (notmuch_database_t *notmuch, int argc, char *argv[])
     notmuch_show_params_t params = {
 	.part = -1,
 	.duplicate = 0,
+	.offset = 0,
+	.limit = -1, /* unlimited */
 	.omit_excluded = true,
 	.output_body = true,
 	.crypto = { .decrypt = NOTMUCH_DECRYPT_AUTO },
@@ -1328,6 +1363,8 @@ notmuch_show_command (notmuch_database_t *notmuch, int argc, char *argv[])
 	{ .opt_bool = &params.output_body, .name = "body" },
 	{ .opt_bool = &params.include_html, .name = "include-html" },
 	{ .opt_int = &params.duplicate, .name = "duplicate" },
+	{ .opt_int = &params.limit, .name = "limit" },
+	{ .opt_int = &params.offset, .name = "offset" },
 	{ .opt_inherit = notmuch_shared_options },
 	{ }
     };
diff --git a/test/T131-show-limiting.sh b/test/T131-show-limiting.sh
new file mode 100755
index 000000000000..a3da35944a3e
--- /dev/null
+++ b/test/T131-show-limiting.sh
@@ -0,0 +1,81 @@
+#!/usr/bin/env bash
+test_description='"notmuch show" --offset and --limit parameters'
+. $(dirname "$0")/test-lib.sh || exit 1
+
+add_email_corpus
+
+function show() {
+    local kind="$1"
+    shift
+    if [ "$kind" = messages ]; then
+        set -- --unthreaded "$@"
+    fi
+    notmuch show --body=false --format=text --entire-thread=false "$@" "*" |
+        sed -nre 's/^.message\{.*\<depth:0\>.*/&/p'
+}
+
+for outp in messages threads; do
+    test_begin_subtest "$outp: limit does the right thing"
+    show $outp | head -n 20 >expected
+    show $outp --limit=20 >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "$outp: concatenation of limited shows"
+    show $outp | head -n 20 >expected
+    show $outp --limit=10 >output
+    show $outp --limit=10 --offset=10 >>output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "$outp: limit larger than result set"
+    N=$(notmuch count --output=$outp "*")
+    show $outp >expected
+    show $outp --limit=$((1 + N)) >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "$outp: limit = 0"
+    test_expect_equal "$(show $outp --limit=0)" ""
+
+    test_begin_subtest "$outp: offset does the right thing"
+    # note: tail -n +N is 1-based
+    show $outp | tail -n +21 >expected
+    show $outp --offset=20 >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "${outp}: offset = 0"
+    show $outp >expected
+    show $outp --offset=0 >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "${outp}: negative offset"
+    show $outp | tail -n 20 >expected
+    show $outp --offset=-20 >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "${outp}: negative offset"
+    show $outp | tail -n 1 >expected
+    show $outp --offset=-1 >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "${outp}: negative offset combined with limit"
+    show $outp | tail -n 20 | head -n 10 >expected
+    show $outp --offset=-20 --limit=10 >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "${outp}: negative offset combined with equal limit"
+    show $outp | tail -n 20 >expected
+    show $outp --offset=-20 --limit=20 >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "${outp}: negative offset combined with large limit"
+    show $outp | tail -n 20 >expected
+    show $outp --offset=-20 --limit=50 >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "${outp}: negative offset larger than results"
+    N=$(notmuch count --output=$outp "*")
+    show $outp >expected
+    show $outp --offset=-$((1 + N)) >output
+    test_expect_equal_file expected output
+done
+
+test_done
-- 
2.37.3

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

end of thread, other threads:[~2022-10-14  8:16 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-10-11 22:19 [PATCH] cli: add options --offset and --limit to notmuch show Robin Jarry
2022-10-11 22:25 ` Robin Jarry
2022-10-12 19:39 ` Tomi Ollila
2022-10-12 20:02   ` Robin Jarry
2022-10-14  8:16     ` Tomi Ollila

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).