Re: How do you synchronize your notmuch tags across multiple machines?
My solution to this problem is to have only one notmuch instance run on a remote VPS and connect to it via "thin" notmuch-emacs front-ends, very similar to [1]. It has been working very well for the last few years. [1] https://notmuchmail.org/remoteusage/ On Mon, 17 Dec 2018, Dan Čermák wrote: Hi list, first and foremost: thanks to everyone who contributed to notmuch, it's a truly awesome piece of software. Unfortunately, I am facing the problem (which I have been postponing to face for a while) that I'll need to keep my notmuch database in sync over multiple machines. Thus my question: how are you achieving that? How well does your solution work? (I have found muchsync, but unfortunately very little reports about how well it works, which isn't necessarily a bad thing. Beside that only some scripts around notmuch dump.) Thanks in advance, Dan ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH v2 00/11] Add filesize index, search, sort & emacs UI
On Tue, 23 May 2017, Jani Nikula wrote: > On Fri, 19 May 2017, Ioan-Adrian Ratiu wrote: >> I'd like to add a feature to quickly work with mail file sizes >> because using custom scripts / external programs which parse >> maildir contents is slow, and non-intuitive, especially since >> notmuch does incremental parsing and has such a nice emacs UI. > > Before we dive into the details, I guess at least I'd like to see better > rationale for the feature. I don't see myself searching or sorting based > on message size. What's the use case? (I wouldn't mind adding the > message size to the formatted output, but that doesn't require indexing > the size or adding the search support for it.) The use case is very simple: I routinely get big mail and want to know about it to take various actions (extract attach, forward, archive etc). Of course I can always write a script to search the maildir but that takes too long, I'd very much rather have incremental indexing, instant search and a very nice GUI front-end (emacs saved searches in my case). With this series I now have saved searches based on filesize which tell me exactly instantly how many messages I have in a certain size range. > > One fundamental issue David noted on IRC, it'll be hard to decide what > to index for "message" size when there are duplicates that typically > have different sizes. For other things, we've indexed whatever comes > first, but I suppose we'd like to fix that. Yes, agreed, and I'll go even further: notmuch search shows thread results - what I did in this patch series is sum up the total messages file sizes in a thread for the search results. We could do something similar, i.e. add all duplicate file sizes to the "message" size and those in turn get added to a thread size. I'm not saying we should necessarily do this, I'm just pointing it out as an alternative which would work very well for me. Ionel > > BR, > Jani. > >> >> >> Ioan-Adrian Ratiu (11): >> lib: message: index message file sizes >> lib: database: store message filesize & add range processor >> notmuch-search: add filesize based sort order >> emacs: make notmuch-search-oldest-first generic >> emacs: notmuch-search: add filesize sorting >> sprinter: add unsigned_long printer function >> lib: thread: add thread total size function >> notmuch-search: output total_filesize thread result >> notmuch-show: export message filesize >> emacs: notmuch-search: add display thread sizes capability >> emacs: notmuch-show: add filesize to headerline >> >> devel/schemata | 1 + >> doc/notmuch-emacs.rst | 4 ++-- >> emacs/notmuch-hello.el | 24 +--- >> emacs/notmuch-jump.el | 11 +-- >> emacs/notmuch-lib.el | 9 ++--- >> emacs/notmuch-show.el | 5 - >> emacs/notmuch-tree.el | 2 +- >> emacs/notmuch.el | 48 +--- >> lib/database-private.h | 1 + >> lib/database.cc| 6 ++ >> lib/index.cc | 10 ++ >> lib/message-file.c | 18 +- >> lib/message.cc | 29 + >> lib/notmuch-private.h | 16 >> lib/notmuch.h | 21 + >> lib/query.cc | 6 ++ >> lib/thread.cc | 12 >> notmuch-search.c | 8 +++- >> notmuch-show.c | 5 + >> sprinter-json.c| 9 + >> sprinter-sexp.c| 9 + >> sprinter-text.c| 9 + >> sprinter.h | 1 + >> 23 files changed, 223 insertions(+), 41 deletions(-) >> >> -- >> 2.13.0 >> >> ___ >> notmuch mailing list >> notmuch@notmuchmail.org >> https://notmuchmail.org/mailman/listinfo/notmuch ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH v2 03/11] notmuch-search: add filesize based sort order
On Fri, 19 May 2017, Tomi Ollila wrote: > On Fri, May 19 2017, Ioan-Adrian Ratiu wrote: > >> With this it now becomes possible to order the search results by >> filesize using the --sort=biggest-first/smallest-first args. > > Quick drive-by comment (for now): In many places elsewhere we're talking > about 'large' files (e.g. LARGEFILE_SOURCE...) so should we use that > terminology here too (?) instead of 'big*' Sure. I'll update to use 'large' in v3, but I'll wait a little longer to get more feedback on v2 before sending it because it's a small change. Ionel > > Tomi > > >> >> Signed-off-by: Ioan-Adrian Ratiu >> --- >> lib/notmuch.h| 8 >> lib/query.cc | 6 ++ >> notmuch-search.c | 2 ++ >> 3 files changed, 16 insertions(+) >> >> diff --git a/lib/notmuch.h b/lib/notmuch.h >> index f90458ce..b7bf3526 100644 >> --- a/lib/notmuch.h >> +++ b/lib/notmuch.h >> @@ -743,6 +743,14 @@ typedef enum { >> */ >> NOTMUCH_SORT_MESSAGE_ID, >> /** >> + * Smallest first. >> + */ >> +NOTMUCH_SORT_SMALLEST_FIRST, >> +/** >> + * Biggest first >> + */ >> +NOTMUCH_SORT_BIGGEST_FIRST, >> +/** >> * Do not sort. >> */ >> NOTMUCH_SORT_UNSORTED >> diff --git a/lib/query.cc b/lib/query.cc >> index 9c6ecc8d..72b725e3 100644 >> --- a/lib/query.cc >> +++ b/lib/query.cc >> @@ -330,6 +330,12 @@ _notmuch_query_search_documents (notmuch_query_t *query, >> case NOTMUCH_SORT_MESSAGE_ID: >> enquire.set_sort_by_value (NOTMUCH_VALUE_MESSAGE_ID, FALSE); >> break; >> +case NOTMUCH_SORT_SMALLEST_FIRST: >> +enquire.set_sort_by_value (NOTMUCH_VALUE_FILESIZE, FALSE); >> +break; >> +case NOTMUCH_SORT_BIGGEST_FIRST: >> +enquire.set_sort_by_value (NOTMUCH_VALUE_FILESIZE, TRUE); >> +break; >> case NOTMUCH_SORT_UNSORTED: >> break; >> } >> diff --git a/notmuch-search.c b/notmuch-search.c >> index 019e14ee..65ecfaab 100644 >> --- a/notmuch-search.c >> +++ b/notmuch-search.c >> @@ -778,6 +778,8 @@ static const notmuch_opt_desc_t common_options[] = { >> { NOTMUCH_OPT_KEYWORD, &search_context.sort, "sort", 's', >>(notmuch_keyword_t []){ { "oldest-first", NOTMUCH_SORT_OLDEST_FIRST }, >>{ "newest-first", NOTMUCH_SORT_NEWEST_FIRST }, >> + { "smallest-first", NOTMUCH_SORT_SMALLEST_FIRST }, >> + { "biggest-first", NOTMUCH_SORT_BIGGEST_FIRST }, >>{ 0, 0 } } }, >> { NOTMUCH_OPT_KEYWORD, &search_context.format_sel, "format", 'f', >>(notmuch_keyword_t []){ { "json", NOTMUCH_FORMAT_JSON }, >> -- >> 2.13.0 >> >> ___ >> notmuch mailing list >> notmuch@notmuchmail.org >> https://notmuchmail.org/mailman/listinfo/notmuch > ___ > notmuch mailing list > notmuch@notmuchmail.org > https://notmuchmail.org/mailman/listinfo/notmuch ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 10/11] emacs: notmuch-search: add display thread sizes capability
By default this is off because it's tiresome to look at all those numbers in every search view. It's much more pleasant to have it enabled by default in notmuch-show even if you apply searches and sort results based on file size. Signed-off-by: Ioan-Adrian Ratiu --- emacs/notmuch.el | 11 ++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/emacs/notmuch.el b/emacs/notmuch.el index 5b9c1d07..dbcd67eb 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -73,7 +73,7 @@ ("subject" . "%s ") ("tags" . "(%s)")) "Search result formatting. Supported fields are: - date, count, authors, subject, tags + date, count, total_filesize, authors, subject, tags For example: (setq notmuch-search-result-format \(\(\"authors\" . \"%-40s\"\) \(\"subject\" . \"%s\"\)\)\) @@ -262,6 +262,12 @@ there will be called at other points of notmuch execution." :group 'notmuch-show :group 'notmuch-faces) +(defface notmuch-search-thread-total-filesize + '((t :inherit default)) + "Face used in search mode for total thread filesizes." + :group 'notmuch-search + :group 'notmuch-faces) + (defface notmuch-search-date '((t :inherit default)) "Face used in search mode for dates." @@ -801,6 +807,9 @@ non-authors is found, assume that all of the authors match." (defun notmuch-search-insert-field (field format-string result) (cond + ((string-equal field "total-filesize") +(insert (propertize (format format-string (file-size-human-readable (plist-get result :total_filesize))) + 'face 'notmuch-search-thread-total-filesize))) ((string-equal field "date") (insert (propertize (format format-string (plist-get result :date_relative)) 'face 'notmuch-search-date))) -- 2.13.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 09/11] notmuch-show: export message filesize
Signed-off-by: Ioan-Adrian Ratiu --- notmuch-show.c | 5 + 1 file changed, 5 insertions(+) diff --git a/notmuch-show.c b/notmuch-show.c index 7021008e..8229c85c 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -91,6 +91,7 @@ format_message_sprinter (sprinter_t *sp, notmuch_message_t *message) notmuch_tags_t *tags; time_t date; const char *relative_date; +unsigned long filesize; sp->map_key (sp, "id"); sp->string (sp, notmuch_message_get_message_id (message)); @@ -117,6 +118,10 @@ format_message_sprinter (sprinter_t *sp, notmuch_message_t *message) sp->string (sp, notmuch_message_get_filename (message)); } +sp->map_key (sp, "filesize"); +filesize = notmuch_message_get_filesize (message); +sp->unsigned_long (sp, filesize); + sp->map_key (sp, "timestamp"); date = notmuch_message_get_date (message); sp->integer (sp, date); -- 2.13.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 08/11] notmuch-search: output total_filesize thread result
This works for all the search output formats (sexp, json, text). Signed-off-by: Ioan-Adrian Ratiu --- devel/schemata | 1 + notmuch-search.c | 6 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/devel/schemata b/devel/schemata index 00ebb7a6..c9e05623 100644 --- a/devel/schemata +++ b/devel/schemata @@ -153,6 +153,7 @@ search_tags = [string*] thread_summary = { thread: threadid, +total_filesize: unsigned long, timestamp: unix_time, date_relative: string, # user-friendly timestamp matched:int, # number of matched messages diff --git a/notmuch-search.c b/notmuch-search.c index 65ecfaab..3cf2a65a 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -161,6 +161,7 @@ do_search_threads (search_context_t *ctx) const char *thread_id = notmuch_thread_get_thread_id (thread); int matched = notmuch_thread_get_matched_messages (thread); int total = notmuch_thread_get_total_messages (thread); + unsigned long total_filesize = notmuch_thread_get_total_filesize (thread); const char *relative_date = NULL; notmuch_bool_t first_tag = TRUE; @@ -175,8 +176,9 @@ do_search_threads (search_context_t *ctx) if (format->is_text_printer) { /* Special case for the text formatter */ - printf ("thread:%s %12s [%d/%d] %s; %s (", + printf ("thread:%s %lu %12s [%d/%d] %s; %s (", thread_id, + total_filesize, relative_date, matched, total, @@ -185,6 +187,8 @@ do_search_threads (search_context_t *ctx) } else { /* Structured Output */ format->map_key (format, "thread"); format->string (format, thread_id); + format->map_key (format, "total_filesize"); + format->unsigned_long (format, total_filesize); format->map_key (format, "timestamp"); format->integer (format, date); format->map_key (format, "date_relative"); -- 2.13.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 11/11] emacs: notmuch-show: add filesize to headerline
Signed-off-by: Ioan-Adrian Ratiu --- emacs/notmuch-show.el | 5 - 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el index c670160d..f2cb09d2 100644 --- a/emacs/notmuch-show.el +++ b/emacs/notmuch-show.el @@ -446,7 +446,7 @@ unchanged ADDRESS if parsing fails." ;; Otherwise format the name and address together. (concat p-name " <" p-address ">" -(defun notmuch-show-insert-headerline (headers date tags depth) +(defun notmuch-show-insert-headerline (headers date filesize tags depth) "Insert a notmuch style headerline based on HEADERS for a message at DEPTH in the current thread." (let ((start (point))) @@ -456,6 +456,8 @@ message at DEPTH in the current thread." " (" date ") (" + (file-size-human-readable filesize) + ") (" (notmuch-tag-format-tags tags tags) ")\n") (overlay-put (make-overlay start (point)) 'face 'notmuch-message-summary-face))) @@ -1038,6 +1040,7 @@ is t, hide the part initially and show the button." (plist-get msg :date_relative) nil) (plist-get headers :Date)) + (plist-get msg :filesize) (plist-get msg :tags) depth) (setq content-start (point-marker)) -- 2.13.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 07/11] lib: thread: add thread total size function
Given a thread object this function computes the total disk space used by all messages contained in the thread. Signed-off-by: Ioan-Adrian Ratiu --- lib/notmuch.h | 7 +++ lib/thread.cc | 12 2 files changed, 19 insertions(+) diff --git a/lib/notmuch.h b/lib/notmuch.h index b7bf3526..4f2afbe4 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -1185,6 +1185,13 @@ time_t notmuch_thread_get_newest_date (notmuch_thread_t *thread); /** + * Get the total disk space used by the thread i.e. a sum of the + * thread messages filesizes. + */ +unsigned long +notmuch_thread_get_total_filesize (notmuch_thread_t *thread); + +/** * Get the tags for 'thread', returning a notmuch_tags_t object which * can be used to iterate over all tags. * diff --git a/lib/thread.cc b/lib/thread.cc index 1a1ecfa5..72878b62 100644 --- a/lib/thread.cc +++ b/lib/thread.cc @@ -596,6 +596,18 @@ notmuch_thread_get_newest_date (notmuch_thread_t *thread) return thread->newest; } +unsigned long +notmuch_thread_get_total_filesize (notmuch_thread_t *thread) +{ +notmuch_message_node_t *node; +unsigned long total_filesize = 0; + +for (node = thread->message_list->head; node; node = node->next) + total_filesize += notmuch_message_get_filesize (node->message); + +return total_filesize; +} + notmuch_tags_t * notmuch_thread_get_tags (notmuch_thread_t *thread) { -- 2.13.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 03/11] notmuch-search: add filesize based sort order
With this it now becomes possible to order the search results by filesize using the --sort=biggest-first/smallest-first args. Signed-off-by: Ioan-Adrian Ratiu --- lib/notmuch.h| 8 lib/query.cc | 6 ++ notmuch-search.c | 2 ++ 3 files changed, 16 insertions(+) diff --git a/lib/notmuch.h b/lib/notmuch.h index f90458ce..b7bf3526 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -743,6 +743,14 @@ typedef enum { */ NOTMUCH_SORT_MESSAGE_ID, /** + * Smallest first. + */ +NOTMUCH_SORT_SMALLEST_FIRST, +/** + * Biggest first + */ +NOTMUCH_SORT_BIGGEST_FIRST, +/** * Do not sort. */ NOTMUCH_SORT_UNSORTED diff --git a/lib/query.cc b/lib/query.cc index 9c6ecc8d..72b725e3 100644 --- a/lib/query.cc +++ b/lib/query.cc @@ -330,6 +330,12 @@ _notmuch_query_search_documents (notmuch_query_t *query, case NOTMUCH_SORT_MESSAGE_ID: enquire.set_sort_by_value (NOTMUCH_VALUE_MESSAGE_ID, FALSE); break; + case NOTMUCH_SORT_SMALLEST_FIRST: + enquire.set_sort_by_value (NOTMUCH_VALUE_FILESIZE, FALSE); + break; + case NOTMUCH_SORT_BIGGEST_FIRST: + enquire.set_sort_by_value (NOTMUCH_VALUE_FILESIZE, TRUE); + break; case NOTMUCH_SORT_UNSORTED: break; } diff --git a/notmuch-search.c b/notmuch-search.c index 019e14ee..65ecfaab 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -778,6 +778,8 @@ static const notmuch_opt_desc_t common_options[] = { { NOTMUCH_OPT_KEYWORD, &search_context.sort, "sort", 's', (notmuch_keyword_t []){ { "oldest-first", NOTMUCH_SORT_OLDEST_FIRST }, { "newest-first", NOTMUCH_SORT_NEWEST_FIRST }, + { "smallest-first", NOTMUCH_SORT_SMALLEST_FIRST }, + { "biggest-first", NOTMUCH_SORT_BIGGEST_FIRST }, { 0, 0 } } }, { NOTMUCH_OPT_KEYWORD, &search_context.format_sel, "format", 'f', (notmuch_keyword_t []){ { "json", NOTMUCH_FORMAT_JSON }, -- 2.13.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 06/11] sprinter: add unsigned_long printer function
We need to output unsigned long values for message and thread (sum of all message's) file sizes. Signed-off-by: Ioan-Adrian Ratiu --- sprinter-json.c | 9 + sprinter-sexp.c | 9 + sprinter-text.c | 9 + sprinter.h | 1 + 4 files changed, 28 insertions(+) diff --git a/sprinter-json.c b/sprinter-json.c index 0a077907..de1dbec2 100644 --- a/sprinter-json.c +++ b/sprinter-json.c @@ -132,6 +132,14 @@ json_integer (struct sprinter *sp, int val) } static void +json_unsigned_long (struct sprinter *sp, unsigned long val) +{ +struct sprinter_json *spj = json_begin_value (sp); + +fprintf (spj->stream, "%lu", val); +} + +static void json_boolean (struct sprinter *sp, notmuch_bool_t val) { struct sprinter_json *spj = json_begin_value (sp); @@ -181,6 +189,7 @@ sprinter_json_create (const void *ctx, FILE *stream) .string = json_string, .string_len = json_string_len, .integer = json_integer, + .unsigned_long = json_unsigned_long, .boolean = json_boolean, .null = json_null, .map_key = json_map_key, diff --git a/sprinter-sexp.c b/sprinter-sexp.c index 08783e11..3162ad9c 100644 --- a/sprinter-sexp.c +++ b/sprinter-sexp.c @@ -169,6 +169,14 @@ sexp_integer (struct sprinter *sp, int val) } static void +sexp_unsigned_long (struct sprinter *sp, unsigned long val) +{ +struct sprinter_sexp *sps = sexp_begin_value (sp); + +fprintf (sps->stream, "%lu", val); +} + +static void sexp_boolean (struct sprinter *sp, notmuch_bool_t val) { struct sprinter_sexp *sps = sexp_begin_value (sp); @@ -216,6 +224,7 @@ sprinter_sexp_create (const void *ctx, FILE *stream) .string = sexp_string, .string_len = sexp_string_len, .integer = sexp_integer, + .unsigned_long = sexp_unsigned_long, .boolean = sexp_boolean, .null = sexp_null, .map_key = sexp_map_key, diff --git a/sprinter-text.c b/sprinter-text.c index 7779488f..5d1607e9 100644 --- a/sprinter-text.c +++ b/sprinter-text.c @@ -52,6 +52,14 @@ text_integer (struct sprinter *sp, int val) } static void +text_unsigned_long (struct sprinter *sp, unsigned long val) +{ +struct sprinter_text *sptxt = (struct sprinter_text *) sp; + +fprintf (sptxt->stream, "%lu", val); +} + +static void text_boolean (struct sprinter *sp, notmuch_bool_t val) { struct sprinter_text *sptxt = (struct sprinter_text *) sp; @@ -123,6 +131,7 @@ sprinter_text_create (const void *ctx, FILE *stream) .string = text_string, .string_len = text_string_len, .integer = text_integer, + .unsigned_long = text_unsigned_long, .boolean = text_boolean, .null = text_null, .map_key = text_map_key, diff --git a/sprinter.h b/sprinter.h index f859672f..2495a7d1 100644 --- a/sprinter.h +++ b/sprinter.h @@ -34,6 +34,7 @@ typedef struct sprinter { void (*string) (struct sprinter *, const char *); void (*string_len) (struct sprinter *, const char *, size_t); void (*integer) (struct sprinter *, int); +void (*unsigned_long) (struct sprinter *, unsigned long); void (*boolean) (struct sprinter *, notmuch_bool_t); void (*null) (struct sprinter *); -- 2.13.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 05/11] emacs: notmuch-search: add filesize sorting
Besides the previous date-based result orderings (oldest-first and newest-first) add two more filesize-based orderings: biggest-first smallest-first. The orderings are interchangeable, you can specify any one as the default via notmuch-search-default-sort-order or as the preffered ordering for a saved search (via the :sort-order property). Signed-off-by: Ioan-Adrian Ratiu --- emacs/notmuch-hello.el | 9 ++--- emacs/notmuch-lib.el | 4 +++- emacs/notmuch.el | 12 ++-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el index 3ba2a16b..51117577 100644 --- a/emacs/notmuch-hello.el +++ b/emacs/notmuch-hello.el @@ -95,7 +95,9 @@ searches so they still work in customize." (choice :tag " Sort Order" (const :tag "Default" nil) (const :tag "Oldest-first" oldest-first) - (const :tag "Newest-first" newest-first))) + (const :tag "Newest-first" newest-first) + (const :tag "Biggest-first" biggest-first) + (const :tag "Smallest-first" smallest-first))) (group :format "%v" :inline t (const :format "" :search-type) (choice :tag " Search Type" (const :tag "Search mode" nil) @@ -120,8 +122,9 @@ a plist. Supported properties are shown. If not present then the :query property is used. :sort-order Specify the sort order to be used for the search. - Possible values are 'oldest-first 'newest-first or - nil. Nil means use the default sort order. + Possible values are 'oldest-first 'newest-first + 'biggest-first 'smallest-first or nil. + Nil means use the default sort order. :search-type Specify whether to run the search in search-mode or tree mode. Set to 'tree to specify tree mode, set to nil (or anything except tree) to diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el index 34ffa712..ded75c2e 100644 --- a/emacs/notmuch-lib.el +++ b/emacs/notmuch-lib.el @@ -100,7 +100,9 @@ search results. Note that any filtered searches created by `notmuch-search-filter' retain the search order of the parent search." :type '(choice (const :tag "oldest-first" oldest-first) -(const :tag "newest-first" newest-first)) +(const :tag "newest-first" newest-first) +(const :tag "biggest-first" biggest-first) +(const :tag "smallest-first" smallest-first)) :group 'notmuch-search) (defcustom notmuch-poll-script nil diff --git a/emacs/notmuch.el b/emacs/notmuch.el index 248b97c7..5b9c1d07 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -1023,12 +1023,20 @@ same relative position within the new buffer." "Toggle the current search order. This command toggles the sort order for the current search. The -default sort order is defined by `notmuch-search-default-sort-order'." +default sort order is defined by `notmuch-search-default-sort-order'. + +There are two types of orderings: by date and by filesize. Toggling is +permitted only within a specific type (for ex. you can't toggle from +oldest-first to smallest-first, only from oldest-first to newest-first) +to avoid confusion." (interactive) (setq notmuch-search-default-sort-order (case notmuch-search-default-sort-order ('oldest-first 'newest-first) - (otherwise 'oldest-first))) + ('newest-first 'biggest-first) + ('biggest-first 'smallest-first) + ('smallest-first 'oldest-first) + (otherwise notmuch-search-default-sort-order))) (notmuch-search-refresh-view)) (defun notmuch-group-disjunctive-query-string (query-string) -- 2.13.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 04/11] emacs: make notmuch-search-oldest-first generic
The current search result order logic assumes results are always sorted by date and thus uses a boolean switch for oldest/newest ordering specifications. This is problematic if I want to introduce other result orderings, like for example based on the mail-file size with smallest/biggest ordering specifications. In the interest of keeping all current logic intact and reusable, while at the same time supporting multiple search result orderings, change the defcustom configuration notmuch-search-oldest-first to notmuch-search-default-sort-order which takes values 'oldest-first and 'newest-first (for now). Implementing new result orderings thus becomes a simple matter of adding more possible entries for notmuch-search-default-sort-order. Aside from the UI variable rename change, this commit should be totally transparent for the user, it does not modify or add any new result sorting logic. Signed-off-by: Ioan-Adrian Ratiu --- doc/notmuch-emacs.rst | 4 ++-- emacs/notmuch-hello.el | 15 +++ emacs/notmuch-jump.el | 11 +-- emacs/notmuch-lib.el | 7 --- emacs/notmuch-tree.el | 2 +- emacs/notmuch.el | 29 +++-- 6 files changed, 34 insertions(+), 34 deletions(-) diff --git a/doc/notmuch-emacs.rst b/doc/notmuch-emacs.rst index 5e25996f..66a69bb8 100644 --- a/doc/notmuch-emacs.rst +++ b/doc/notmuch-emacs.rst @@ -169,8 +169,8 @@ variables. Control how each thread of messages is presented in the ``notmuch-show-mode`` buffer -:index:`notmuch-search-oldest-first` -Display the oldest threads at the top of the buffer +:index:`notmuch-search-default-sort-order` +Control the default search result method .. _notmuch-show: diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el index c858a20b..3ba2a16b 100644 --- a/emacs/notmuch-hello.el +++ b/emacs/notmuch-hello.el @@ -28,7 +28,7 @@ (require 'notmuch-lib) (require 'notmuch-mua) -(declare-function notmuch-search "notmuch" (&optional query oldest-first target-thread target-line continuation)) +(declare-function notmuch-search "notmuch" (&optional query sort-order target-thread target-line continuation)) (declare-function notmuch-poll "notmuch" ()) (declare-function notmuch-tree "notmuch-tree" (&optional query query-context target buffer-name open-target)) @@ -381,7 +381,7 @@ afterwards.") (setq search (notmuch-hello-trim search)) (let ((history-delete-duplicates t)) (add-to-history 'notmuch-search-history search))) - (notmuch-search search notmuch-search-oldest-first)) + (notmuch-search search notmuch-search-default-sort-order)) (defun notmuch-hello-add-saved-search (widget) (interactive) @@ -443,7 +443,7 @@ diagonal." (notmuch-search (widget-get widget :notmuch-search-terms) (widget-get widget - :notmuch-search-oldest-first + :notmuch-search-sort-order (defun notmuch-saved-search-count (search) (car (process-lines notmuch-command "count" search))) @@ -575,10 +575,9 @@ with `notmuch-hello-query-counts'." (widget-insert (make-string column-indent ? ))) (let* ((name (plist-get elem :name)) (query (plist-get elem :query)) -(oldest-first (case (plist-get elem :sort-order) -(newest-first nil) -(oldest-first t) -(otherwise notmuch-search-oldest-first))) +(sort-order (if (plist-get elem :sort-order) +(plist-get elem :sort-order) + notmuch-search-default-sort-order)) (search-type (eq (plist-get elem :search-type) 'tree)) (msg-count (plist-get elem :count))) (widget-insert (format "%8s " @@ -586,7 +585,7 @@ with `notmuch-hello-query-counts'." (widget-create 'push-button :notify #'notmuch-hello-widget-search :notmuch-search-terms query - :notmuch-search-oldest-first oldest-first + :notmuch-search-sort-order sort-order :notmuch-search-type search-type name) (setq column-indent diff --git a/emacs/notmuch-jump.el b/emacs/notmuch-jump.el index 3e20b8c7..716d39b8 100644 --- a/emacs/notmuch-jump.el +++ b/emacs/notmuch-jump.el @@ -50,15 +50,14 @@ fast way to jump to a saved search from anywhere in Notmuch." (when key (let ((name (plist-get saved-search :name)) (query (plist-get s
[PATCH v2 01/11] lib: message: index message file sizes
Parse & store the file sizes inside notmuch_message_t objects while indexing. This is a useful foundation to build upon to provide per message and per thread size statistics, sorting and filtering mesages based on their sizes, etc. Signed-off-by: Ioan-Adrian Ratiu --- lib/index.cc | 10 ++ lib/message-file.c| 18 +- lib/message.cc| 29 + lib/notmuch-private.h | 16 lib/notmuch.h | 6 ++ 5 files changed, 78 insertions(+), 1 deletion(-) diff --git a/lib/index.cc b/lib/index.cc index 8c145540..e8655bc1 100644 --- a/lib/index.cc +++ b/lib/index.cc @@ -441,6 +441,8 @@ _notmuch_message_index_file (notmuch_message_t *message, InternetAddressList *addresses; const char *from, *subject; notmuch_status_t status; +unsigned long filesize; +char *filesize_str; status = _notmuch_message_file_get_mime_message (message_file, &mime_message); @@ -464,6 +466,14 @@ _notmuch_message_index_file (notmuch_message_t *message, subject = g_mime_message_get_subject (mime_message); _notmuch_message_gen_terms (message, "subject", subject); +filesize = _notmuch_message_file_get_size (message_file); +filesize_str = talloc_asprintf(NULL, "%lu", filesize); +if (! filesize_str) + return NOTMUCH_STATUS_OUT_OF_MEMORY; + +_notmuch_message_add_term (message, "filesize", filesize_str); +talloc_free (filesize_str); + _index_mime_part (message, g_mime_message_get_mime_part (mime_message)); return NOTMUCH_STATUS_SUCCESS; diff --git a/lib/message-file.c b/lib/message-file.c index db18b163..f75593e3 100644 --- a/lib/message-file.c +++ b/lib/message-file.c @@ -26,10 +26,13 @@ #include /* GHashTable */ +#include + struct _notmuch_message_file { /* File object */ FILE *file; char *filename; +unsigned long filesize; /* in bytes */ /* Cache for decoded headers */ GHashTable *headers; @@ -64,7 +67,7 @@ _notmuch_message_file_open_ctx (notmuch_database_t *notmuch, if (unlikely (message == NULL)) return NULL; -/* Only needed for error messages during parsing. */ +/* Only needed during parsing */ message->filename = talloc_strdup (message, filename); if (message->filename == NULL) goto FAIL; @@ -98,6 +101,12 @@ _notmuch_message_file_close (notmuch_message_file_t *message) talloc_free (message); } +unsigned long +_notmuch_message_file_get_size (notmuch_message_file_t *message) +{ +return message->filesize; +} + static notmuch_bool_t _is_mbox (FILE *file) { @@ -122,6 +131,8 @@ _notmuch_message_file_parse (notmuch_message_file_t *message) notmuch_status_t status = NOTMUCH_STATUS_SUCCESS; static int initialized = 0; notmuch_bool_t is_mbox; +GStatBuf statResult; +int ret; if (message->message) return NOTMUCH_STATUS_SUCCESS; @@ -133,6 +144,11 @@ _notmuch_message_file_parse (notmuch_message_file_t *message) initialized = 1; } +/* filesize defaults to zero which is ignored */ +ret = g_stat(message->filename, &statResult); +if (! ret) + message->filesize = statResult.st_size; + message->headers = g_hash_table_new_full (strcase_hash, strcase_equal, free, g_free); if (! message->headers) diff --git a/lib/message.cc b/lib/message.cc index b330dcce..c6b6e507 100644 --- a/lib/message.cc +++ b/lib/message.cc @@ -988,6 +988,26 @@ notmuch_message_get_date (notmuch_message_t *message) return Xapian::sortable_unserialise (value); } +unsigned long +notmuch_message_get_filesize (notmuch_message_t *message) +{ +std::string value; + +try { + value = message->doc.get_value (NOTMUCH_VALUE_FILESIZE); +} catch (Xapian::Error &error) { + _notmuch_database_log(_notmuch_message_database (message), "A Xapian exception occurred when reading filesize: %s\n", +error.get_msg().c_str()); + message->notmuch->exception_reported = TRUE; + return 0; +} + +if (value.empty ()) + /* sortable_unserialise is undefined on empty string */ + return 0; +return Xapian::sortable_unserialise (value); +} + notmuch_tags_t * notmuch_message_get_tags (notmuch_message_t *message) { @@ -1208,6 +1228,15 @@ _notmuch_message_close (notmuch_message_t *message) } } +void +_notmuch_message_add_filesize (notmuch_message_t *message, + notmuch_message_file_t *message_file) +{ +unsigned long filesize = _notmuch_message_file_get_size(message_file); +message->doc.add_value (NOTMUCH_VALUE_FILESIZE, + Xapian::sortable_serialise (filesize)); +} + /* Add a name:value term to 'message', (the actual term will be * en
[PATCH v2 00/11] Add filesize index, search, sort & emacs UI
I'd like to add a feature to quickly work with mail file sizes because using custom scripts / external programs which parse maildir contents is slow, and non-intuitive, especially since notmuch does incremental parsing and has such a nice emacs UI. Ioan-Adrian Ratiu (11): lib: message: index message file sizes lib: database: store message filesize & add range processor notmuch-search: add filesize based sort order emacs: make notmuch-search-oldest-first generic emacs: notmuch-search: add filesize sorting sprinter: add unsigned_long printer function lib: thread: add thread total size function notmuch-search: output total_filesize thread result notmuch-show: export message filesize emacs: notmuch-search: add display thread sizes capability emacs: notmuch-show: add filesize to headerline devel/schemata | 1 + doc/notmuch-emacs.rst | 4 ++-- emacs/notmuch-hello.el | 24 +--- emacs/notmuch-jump.el | 11 +-- emacs/notmuch-lib.el | 9 ++--- emacs/notmuch-show.el | 5 - emacs/notmuch-tree.el | 2 +- emacs/notmuch.el | 48 +--- lib/database-private.h | 1 + lib/database.cc| 6 ++ lib/index.cc | 10 ++ lib/message-file.c | 18 +- lib/message.cc | 29 + lib/notmuch-private.h | 16 lib/notmuch.h | 21 + lib/query.cc | 6 ++ lib/thread.cc | 12 notmuch-search.c | 8 +++- notmuch-show.c | 5 + sprinter-json.c| 9 + sprinter-sexp.c| 9 + sprinter-text.c| 9 + sprinter.h | 1 + 23 files changed, 223 insertions(+), 41 deletions(-) -- 2.13.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 02/11] lib: database: store message filesize & add range processor
The filesize and range processor are very useful to do search queries like "filesize:1000..1". All sizes are in bytes for now because that's what I'm interested in. I think the database needs to be re-created for this to work so that all newly indexed messages have the new xapian filesize value, I did not have the time to test a "hybrid" database where just some of the messages have the value. Signed-off-by: Ioan-Adrian Ratiu --- lib/database-private.h | 1 + lib/database.cc| 6 ++ 2 files changed, 7 insertions(+) diff --git a/lib/database-private.h b/lib/database-private.h index 727b1d61..d4d5ab6b 100644 --- a/lib/database-private.h +++ b/lib/database-private.h @@ -215,6 +215,7 @@ struct _notmuch_database { Xapian::ValueRangeProcessor *value_range_processor; Xapian::ValueRangeProcessor *date_range_processor; Xapian::ValueRangeProcessor *last_mod_range_processor; +Xapian::ValueRangeProcessor *filesize_range_processor; }; /* Prior to database version 3, features were implied by the database diff --git a/lib/database.cc b/lib/database.cc index 5b13f541..84e616cf 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -295,6 +295,7 @@ prefix_t prefix_table[] = { { "subject", "XSUBJECT", NOTMUCH_FIELD_EXTERNAL | NOTMUCH_FIELD_PROBABILISTIC | NOTMUCH_FIELD_PROCESSOR}, +{ "filesize", "XFILESIZE",NOTMUCH_FIELD_EXTERNAL }, }; static void @@ -1082,6 +1083,7 @@ notmuch_database_open_verbose (const char *path, notmuch->value_range_processor = new Xapian::NumberValueRangeProcessor (NOTMUCH_VALUE_TIMESTAMP); notmuch->date_range_processor = new ParseTimeValueRangeProcessor (NOTMUCH_VALUE_TIMESTAMP); notmuch->last_mod_range_processor = new Xapian::NumberValueRangeProcessor (NOTMUCH_VALUE_LAST_MOD, "lastmod:"); + notmuch->filesize_range_processor = new Xapian::NumberValueRangeProcessor (NOTMUCH_VALUE_FILESIZE, "filesize:"); notmuch->query_parser->set_default_op (Xapian::Query::OP_AND); notmuch->query_parser->set_database (*notmuch->xapian_db); @@ -1090,6 +1092,7 @@ notmuch_database_open_verbose (const char *path, notmuch->query_parser->add_valuerangeprocessor (notmuch->value_range_processor); notmuch->query_parser->add_valuerangeprocessor (notmuch->date_range_processor); notmuch->query_parser->add_valuerangeprocessor (notmuch->last_mod_range_processor); + notmuch->query_parser->add_valuerangeprocessor (notmuch->filesize_range_processor); for (i = 0; i < ARRAY_SIZE (prefix_table); i++) { const prefix_t *prefix = &prefix_table[i]; @@ -1166,6 +1169,8 @@ notmuch_database_close (notmuch_database_t *notmuch) notmuch->date_range_processor = NULL; delete notmuch->last_mod_range_processor; notmuch->last_mod_range_processor = NULL; +delete notmuch->filesize_range_processor; +notmuch->filesize_range_processor = NULL; return status; } @@ -2563,6 +2568,7 @@ notmuch_database_add_message (notmuch_database_t *notmuch, } _notmuch_message_add_filename (message, filename); + _notmuch_message_add_filesize (message, message_file); /* Is this a newly created message object or a ghost * message? We have to be slightly careful: if this is a -- 2.13.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH 3/3] emacs: notmuch-search: add filesize sorting
Besides the previous date-based result orderings (oldest-first and newest-first) add two more filesize-based orderings: biggest-first smallest-first. The orderings are interchangeable, you can specify any one as the default via notmuch-search-default-sort-order or as the preffered ordering for a saved search (via the :sort-order property). The only restriction I've imposed is that you can't toggle from a date-based result ordering to a filesize-based one. Meaning you can toggle as before from oldest-first to newest-first or from the new smallest-first to biggest-first and viceversa, but you can't toggle from oldest-first to biggest-first. I've found this kind of toggling confusing so I've prohibited it, but enabling it is trivial if need be. Signed-off-by: Ioan-Adrian Ratiu --- emacs/notmuch-hello.el | 9 ++--- emacs/notmuch-lib.el | 4 +++- emacs/notmuch.el | 12 ++-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el index 3ba2a16b..51117577 100644 --- a/emacs/notmuch-hello.el +++ b/emacs/notmuch-hello.el @@ -95,7 +95,9 @@ searches so they still work in customize." (choice :tag " Sort Order" (const :tag "Default" nil) (const :tag "Oldest-first" oldest-first) - (const :tag "Newest-first" newest-first))) + (const :tag "Newest-first" newest-first) + (const :tag "Biggest-first" biggest-first) + (const :tag "Smallest-first" smallest-first))) (group :format "%v" :inline t (const :format "" :search-type) (choice :tag " Search Type" (const :tag "Search mode" nil) @@ -120,8 +122,9 @@ a plist. Supported properties are shown. If not present then the :query property is used. :sort-order Specify the sort order to be used for the search. - Possible values are 'oldest-first 'newest-first or - nil. Nil means use the default sort order. + Possible values are 'oldest-first 'newest-first + 'biggest-first 'smallest-first or nil. + Nil means use the default sort order. :search-type Specify whether to run the search in search-mode or tree mode. Set to 'tree to specify tree mode, set to nil (or anything except tree) to diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el index 34ffa712..ded75c2e 100644 --- a/emacs/notmuch-lib.el +++ b/emacs/notmuch-lib.el @@ -100,7 +100,9 @@ search results. Note that any filtered searches created by `notmuch-search-filter' retain the search order of the parent search." :type '(choice (const :tag "oldest-first" oldest-first) -(const :tag "newest-first" newest-first)) +(const :tag "newest-first" newest-first) +(const :tag "biggest-first" biggest-first) +(const :tag "smallest-first" smallest-first)) :group 'notmuch-search) (defcustom notmuch-poll-script nil diff --git a/emacs/notmuch.el b/emacs/notmuch.el index 248b97c7..4ba29880 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -1023,12 +1023,20 @@ same relative position within the new buffer." "Toggle the current search order. This command toggles the sort order for the current search. The -default sort order is defined by `notmuch-search-default-sort-order'." +default sort order is defined by `notmuch-search-default-sort-order'. + +There are two types of orderings: by date and by filesize. Toggling is +permitted only within a specific type (for ex. you can't toggle from +oldest-first to smallest-first, only from oldest-first to newest-first) +to avoid confusion." (interactive) (setq notmuch-search-default-sort-order (case notmuch-search-default-sort-order ('oldest-first 'newest-first) - (otherwise 'oldest-first))) + ('newest-first 'oldest-first) + ('biggest-first 'smallest-first) + ('smallest-first 'biggest-first) + (otherwise notmuch-search-default-sort-order))) (notmuch-search-refresh-view)) (defun notmuch-group-disjunctive-query-string (query-string) -- 2.12.2 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH 2/3] emacs: make notmuch-search-oldest-first generic
The current search result order logic assumes results are always sorted by date and thus uses a boolean switch for oldest/newest ordering specifications. This is problematic if I want to introduce other result orderings, like for example based on the mail-file size with smallest/biggest ordering specifications. In the interest of keeping all current logic intact and reusable, while at the same time supporting multiple search result orderings, change the defcustom configuration notmuch-search-oldest-first to notmuch-search-default-sort-order which takes values 'oldest-first and 'newest-first (for now). Implementing new result orderings thus becomes a simple matter of adding more possible entries for notmuch-search-default-sort-order. Aside from the UI variable rename change, this commit should be totally transparent for the user, it does not modify or add any new result sorting logic. Signed-off-by: Ioan-Adrian Ratiu --- doc/notmuch-emacs.rst | 4 ++-- emacs/notmuch-hello.el | 15 +++ emacs/notmuch-jump.el | 11 +-- emacs/notmuch-lib.el | 7 --- emacs/notmuch-tree.el | 2 +- emacs/notmuch.el | 29 +++-- 6 files changed, 34 insertions(+), 34 deletions(-) diff --git a/doc/notmuch-emacs.rst b/doc/notmuch-emacs.rst index 5e25996f..66a69bb8 100644 --- a/doc/notmuch-emacs.rst +++ b/doc/notmuch-emacs.rst @@ -169,8 +169,8 @@ variables. Control how each thread of messages is presented in the ``notmuch-show-mode`` buffer -:index:`notmuch-search-oldest-first` -Display the oldest threads at the top of the buffer +:index:`notmuch-search-default-sort-order` +Control the default search result method .. _notmuch-show: diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el index c858a20b..3ba2a16b 100644 --- a/emacs/notmuch-hello.el +++ b/emacs/notmuch-hello.el @@ -28,7 +28,7 @@ (require 'notmuch-lib) (require 'notmuch-mua) -(declare-function notmuch-search "notmuch" (&optional query oldest-first target-thread target-line continuation)) +(declare-function notmuch-search "notmuch" (&optional query sort-order target-thread target-line continuation)) (declare-function notmuch-poll "notmuch" ()) (declare-function notmuch-tree "notmuch-tree" (&optional query query-context target buffer-name open-target)) @@ -381,7 +381,7 @@ afterwards.") (setq search (notmuch-hello-trim search)) (let ((history-delete-duplicates t)) (add-to-history 'notmuch-search-history search))) - (notmuch-search search notmuch-search-oldest-first)) + (notmuch-search search notmuch-search-default-sort-order)) (defun notmuch-hello-add-saved-search (widget) (interactive) @@ -443,7 +443,7 @@ diagonal." (notmuch-search (widget-get widget :notmuch-search-terms) (widget-get widget - :notmuch-search-oldest-first + :notmuch-search-sort-order (defun notmuch-saved-search-count (search) (car (process-lines notmuch-command "count" search))) @@ -575,10 +575,9 @@ with `notmuch-hello-query-counts'." (widget-insert (make-string column-indent ? ))) (let* ((name (plist-get elem :name)) (query (plist-get elem :query)) -(oldest-first (case (plist-get elem :sort-order) -(newest-first nil) -(oldest-first t) -(otherwise notmuch-search-oldest-first))) +(sort-order (if (plist-get elem :sort-order) +(plist-get elem :sort-order) + notmuch-search-default-sort-order)) (search-type (eq (plist-get elem :search-type) 'tree)) (msg-count (plist-get elem :count))) (widget-insert (format "%8s " @@ -586,7 +585,7 @@ with `notmuch-hello-query-counts'." (widget-create 'push-button :notify #'notmuch-hello-widget-search :notmuch-search-terms query - :notmuch-search-oldest-first oldest-first + :notmuch-search-sort-order sort-order :notmuch-search-type search-type name) (setq column-indent diff --git a/emacs/notmuch-jump.el b/emacs/notmuch-jump.el index 3e20b8c7..716d39b8 100644 --- a/emacs/notmuch-jump.el +++ b/emacs/notmuch-jump.el @@ -50,15 +50,14 @@ fast way to jump to a saved search from anywhere in Notmuch." (when key (let ((name (plist-get saved-search :name)) (query (plist-get s
[PATCH 1/3] notmuch-search: implement 'filesize' search
I need to keep track of my mail file sizes and writing custom parsers/scripts and running them on the maildir is problematic especially for managing large collections of email. Having a way to search email based on file sizes would be very useful, also copled with notmuch's incremental maildir indexing/parsing, rapid result navigation/sorting via Emacs makes my life a lot easier. So this commit adds the following capability (sizes are in bytes because that's what I'm interested in, other inputs can be added I guess). Replace the range param to get mails of a specific size: notmuch search --sort=biggest-first filesize:1000..10 I didn't think of another front-end for this other than the command line and saved searches from the Emacs GUI, which work well enough for me with this patch series, but it would be simple to also expose the filesize info on the screen somewhere in the results/show buffer. Another interesting idea would be to offer some optional statistics in the UI buffers like we do for saved searches `query-count` to show stuff like disk space occupied by mail files maching a search, etc. Signed-off-by: Ioan-Adrian Ratiu --- lib/database-private.h | 1 + lib/database.cc| 6 ++ lib/index.cc | 10 ++ lib/message-file.c | 18 +- lib/message.cc | 29 + lib/notmuch-private.h | 16 lib/notmuch.h | 14 ++ lib/query.cc | 6 ++ notmuch-search.c | 2 ++ 9 files changed, 101 insertions(+), 1 deletion(-) diff --git a/lib/database-private.h b/lib/database-private.h index ab3d9691..a7e0a020 100644 --- a/lib/database-private.h +++ b/lib/database-private.h @@ -217,6 +217,7 @@ struct _notmuch_database { Xapian::ValueRangeProcessor *value_range_processor; Xapian::ValueRangeProcessor *date_range_processor; Xapian::ValueRangeProcessor *last_mod_range_processor; +Xapian::ValueRangeProcessor *filesize_range_processor; }; /* Prior to database version 3, features were implied by the database diff --git a/lib/database.cc b/lib/database.cc index 5bc131a3..e6d5dd11 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -290,6 +290,7 @@ prefix_t prefix_table[] = { { "subject", "XSUBJECT", NOTMUCH_FIELD_EXTERNAL | NOTMUCH_FIELD_PROBABILISTIC | NOTMUCH_FIELD_PROCESSOR}, +{ "filesize", "XFILESIZE",NOTMUCH_FIELD_EXTERNAL }, }; static void @@ -1076,6 +1077,7 @@ notmuch_database_open_verbose (const char *path, notmuch->value_range_processor = new Xapian::NumberValueRangeProcessor (NOTMUCH_VALUE_TIMESTAMP); notmuch->date_range_processor = new ParseTimeValueRangeProcessor (NOTMUCH_VALUE_TIMESTAMP); notmuch->last_mod_range_processor = new Xapian::NumberValueRangeProcessor (NOTMUCH_VALUE_LAST_MOD, "lastmod:"); + notmuch->filesize_range_processor = new Xapian::NumberValueRangeProcessor (NOTMUCH_VALUE_FILESIZE, "filesize:"); notmuch->query_parser->set_default_op (Xapian::Query::OP_AND); notmuch->query_parser->set_database (*notmuch->xapian_db); @@ -1084,6 +1086,7 @@ notmuch_database_open_verbose (const char *path, notmuch->query_parser->add_valuerangeprocessor (notmuch->value_range_processor); notmuch->query_parser->add_valuerangeprocessor (notmuch->date_range_processor); notmuch->query_parser->add_valuerangeprocessor (notmuch->last_mod_range_processor); + notmuch->query_parser->add_valuerangeprocessor (notmuch->filesize_range_processor); for (i = 0; i < ARRAY_SIZE (prefix_table); i++) { const prefix_t *prefix = &prefix_table[i]; @@ -1160,6 +1163,8 @@ notmuch_database_close (notmuch_database_t *notmuch) notmuch->date_range_processor = NULL; delete notmuch->last_mod_range_processor; notmuch->last_mod_range_processor = NULL; +delete notmuch->filesize_range_processor; +notmuch->filesize_range_processor = NULL; return status; } @@ -2557,6 +2562,7 @@ notmuch_database_add_message (notmuch_database_t *notmuch, } _notmuch_message_add_filename (message, filename); + _notmuch_message_add_filesize (message, message_file); /* Is this a newly created message object or a ghost * message? We have to be slightly careful: if this is a diff --git a/lib/index.cc b/lib/index.cc index 8c145540..e8655bc1 100644 --- a/lib/index.cc +++ b/lib/index.cc @@ -441,6 +441,8 @@ _notmuch_message_index_file (notmuch_message_t *message, InternetAddressList *addresses; const char *from, *subject; notmuch_status_t status; +unsigned long filesize; +char *files
[RFC][PATCH] notmuch-search: add file size search feature
I need to keep track of my maildir file sizes and for a while the parsers/scripts which I wrote were problematic, especially for viewing & interacting with large collections of email. Since I started using notmuch, having a way to filter my email search results based on file sizes would prove handy, together with notmuch's incremental indexing/parsing, that would really be helpful. So this commit adds the following capability (sizes are expressed in bytes because that's what I'm interested in, other inputs can be added further on). Replace the range arg to get mails of a specific size: notmuch search --output=files --sort=biggest-first filesize:1000..10 | xargs du -s I didn't think of another front-end for this other than creating saved searches from the Emacs GUI, which work well enough for me. One thing I'm not sure how to handle in code is duplicate files for a message ID (only the MsgID determines whether files are "duplicates", right?). Any ideas how best to store/handle duplicates? Signed-off-by: Ioan-Adrian Ratiu --- lib/database-private.h | 1 + lib/database.cc| 6 ++ lib/index.cc | 10 ++ lib/message-file.c | 18 +- lib/message.cc | 29 + lib/notmuch-private.h | 16 lib/notmuch.h | 14 ++ lib/query.cc | 6 ++ notmuch-search.c | 2 ++ 9 files changed, 101 insertions(+), 1 deletion(-) diff --git a/lib/database-private.h b/lib/database-private.h index ab3d9691..a7e0a020 100644 --- a/lib/database-private.h +++ b/lib/database-private.h @@ -217,6 +217,7 @@ struct _notmuch_database { Xapian::ValueRangeProcessor *value_range_processor; Xapian::ValueRangeProcessor *date_range_processor; Xapian::ValueRangeProcessor *last_mod_range_processor; +Xapian::ValueRangeProcessor *filesize_range_processor; }; /* Prior to database version 3, features were implied by the database diff --git a/lib/database.cc b/lib/database.cc index 5bc131a3..e6d5dd11 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -290,6 +290,7 @@ prefix_t prefix_table[] = { { "subject", "XSUBJECT", NOTMUCH_FIELD_EXTERNAL | NOTMUCH_FIELD_PROBABILISTIC | NOTMUCH_FIELD_PROCESSOR}, +{ "filesize", "XFILESIZE",NOTMUCH_FIELD_EXTERNAL }, }; static void @@ -1076,6 +1077,7 @@ notmuch_database_open_verbose (const char *path, notmuch->value_range_processor = new Xapian::NumberValueRangeProcessor (NOTMUCH_VALUE_TIMESTAMP); notmuch->date_range_processor = new ParseTimeValueRangeProcessor (NOTMUCH_VALUE_TIMESTAMP); notmuch->last_mod_range_processor = new Xapian::NumberValueRangeProcessor (NOTMUCH_VALUE_LAST_MOD, "lastmod:"); + notmuch->filesize_range_processor = new Xapian::NumberValueRangeProcessor (NOTMUCH_VALUE_FILESIZE, "filesize:"); notmuch->query_parser->set_default_op (Xapian::Query::OP_AND); notmuch->query_parser->set_database (*notmuch->xapian_db); @@ -1084,6 +1086,7 @@ notmuch_database_open_verbose (const char *path, notmuch->query_parser->add_valuerangeprocessor (notmuch->value_range_processor); notmuch->query_parser->add_valuerangeprocessor (notmuch->date_range_processor); notmuch->query_parser->add_valuerangeprocessor (notmuch->last_mod_range_processor); + notmuch->query_parser->add_valuerangeprocessor (notmuch->filesize_range_processor); for (i = 0; i < ARRAY_SIZE (prefix_table); i++) { const prefix_t *prefix = &prefix_table[i]; @@ -1160,6 +1163,8 @@ notmuch_database_close (notmuch_database_t *notmuch) notmuch->date_range_processor = NULL; delete notmuch->last_mod_range_processor; notmuch->last_mod_range_processor = NULL; +delete notmuch->filesize_range_processor; +notmuch->filesize_range_processor = NULL; return status; } @@ -2557,6 +2562,7 @@ notmuch_database_add_message (notmuch_database_t *notmuch, } _notmuch_message_add_filename (message, filename); + _notmuch_message_add_filesize (message, message_file); /* Is this a newly created message object or a ghost * message? We have to be slightly careful: if this is a diff --git a/lib/index.cc b/lib/index.cc index 8c145540..e8655bc1 100644 --- a/lib/index.cc +++ b/lib/index.cc @@ -441,6 +441,8 @@ _notmuch_message_index_file (notmuch_message_t *message, InternetAddressList *addresses; const char *from, *subject; notmuch_status_t status; +unsigned long filesize; +char *filesize_str; status = _notmuch_message_file_get_mime_message (message_file,
[PATCH v6 0/2] Refactor config reading to support non-regular files
Changes since v5 (based on Tomi's feedback): * Return the same error message when config file is not found to avoid breaking test T040-setup.1. Ioan-Adrian Ratiu (1): notmuch-config: replace config reading function Jani Nikula (1): cli: abstract config file reading to a separate function notmuch-config.c | 112 --- 1 file changed, 82 insertions(+), 30 deletions(-) -- 2.10.2 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v6 1/2] cli: abstract config file reading to a separate function
From: Jani Nikula Simplify and fix the coding style while at it. --- notmuch-config.c | 65 ++-- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/notmuch-config.c b/notmuch-config.c index e5d42a0..bd52790 100644 --- a/notmuch-config.c +++ b/notmuch-config.c @@ -202,6 +202,38 @@ get_username_from_passwd_file (void *ctx) return name; } +static notmuch_bool_t +get_config_from_file (notmuch_config_t *config, notmuch_bool_t create_new) +{ +GError *error = NULL; +notmuch_bool_t ret = FALSE; + +if (g_key_file_load_from_file (config->key_file, config->filename, + G_KEY_FILE_KEEP_COMMENTS, &error)) + return TRUE; + +if (error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT) { + /* If create_new is true, then the caller is prepared for a +* default configuration file in the case of FILE NOT FOUND. +*/ + if (create_new) { + config->is_new = TRUE; + ret = TRUE; + } else { + fprintf (stderr, "Configuration file %s not found.\n" +"Try running 'notmuch setup' to create a configuration.\n", +config->filename); + } +} else { + fprintf (stderr, "Error reading configuration file %s: %s\n", +config->filename, error->message); +} + +g_error_free (error); + +return ret; +} + /* Open the named notmuch configuration file. If the filename is NULL, * the value of the environment variable $NOTMUCH_CONFIG will be used. * If $NOTMUCH_CONFIG is unset, the default configuration file @@ -289,36 +321,9 @@ notmuch_config_open (void *ctx, config->search_exclude_tags_length = 0; config->crypto_gpg_path = NULL; -if (! g_key_file_load_from_file (config->key_file, -config->filename, -G_KEY_FILE_KEEP_COMMENTS, -&error)) -{ - if (error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT) { - /* If create_new is true, then the caller is prepared for a -* default configuration file in the case of FILE NOT -* FOUND. -*/ - if (create_new) { - g_error_free (error); - config->is_new = TRUE; - } else { - fprintf (stderr, "Configuration file %s not found.\n" -"Try running 'notmuch setup' to create a configuration.\n", -config->filename); - talloc_free (config); - g_error_free (error); - return NULL; - } - } - else - { - fprintf (stderr, "Error reading configuration file %s: %s\n", -config->filename, error->message); - talloc_free (config); - g_error_free (error); - return NULL; - } +if (! get_config_from_file (config, create_new)) { + talloc_free (config); + return NULL; } /* Whenever we know of configuration sections that don't appear in -- 2.10.2 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v6 2/2] notmuch-config: replace config reading function
Config files are currently read using glib's g_key_file_load_from_file function which is very inconvenient because it's limited by design to read only from "regular data files" in a filesystem. Because of this limitation notmuch can't read configs from pipes, fifos, sockets, stdin, etc. Not even "notmuch --config=/dev/stdin" works: Error reading configuration file /dev/stdin: Not a regular file So replace g_key_file_load_from_file with g_key_file_load_from_data which gives us much more freedom to read configs from multiple sources. This also helps the more security sensitive users: If someone has private information in the config file, it can be encrypted on disk, then decrypted in RAM and passed through a pipe directly to notmuch without the use of intermediate plain text files. Signed-off-by: Ioan-Adrian Ratiu --- notmuch-config.c | 65 1 file changed, 56 insertions(+), 9 deletions(-) diff --git a/notmuch-config.c b/notmuch-config.c index bd52790..cdb4088 100644 --- a/notmuch-config.c +++ b/notmuch-config.c @@ -205,32 +205,79 @@ get_username_from_passwd_file (void *ctx) static notmuch_bool_t get_config_from_file (notmuch_config_t *config, notmuch_bool_t create_new) { +#define BUF_SIZE 4096 +char *config_str = NULL; +int config_len = 0; +int config_bufsize = BUF_SIZE; +size_t len; GError *error = NULL; notmuch_bool_t ret = FALSE; -if (g_key_file_load_from_file (config->key_file, config->filename, - G_KEY_FILE_KEEP_COMMENTS, &error)) - return TRUE; - -if (error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT) { +FILE *fp = fopen(config->filename, "r"); +if (fp == NULL) { /* If create_new is true, then the caller is prepared for a * default configuration file in the case of FILE NOT FOUND. */ if (create_new) { config->is_new = TRUE; ret = TRUE; - } else { + goto out; + } else if (errno == ENOENT) { fprintf (stderr, "Configuration file %s not found.\n" "Try running 'notmuch setup' to create a configuration.\n", config->filename); + goto out; + } else { + fprintf (stderr, "Error opening config file '%s': %s\n" +"Try running 'notmuch setup' to create a configuration.\n", +config->filename, strerror(errno)); + goto out; + } +} + +config_str = talloc_zero_array (config, char, config_bufsize); +if (config_str == NULL) { + fprintf (stderr, "Error reading '%s': Out of memory\n", config->filename); + goto out; +} + +while ((len = fread (config_str + config_len, 1, +config_bufsize - config_len, fp)) > 0) { + config_len += len; + if (config_len == config_bufsize) { + config_bufsize += BUF_SIZE; + config_str = talloc_realloc (config, config_str, char, config_bufsize); + if (config_str == NULL) { + fprintf (stderr, "Error reading '%s': Failed to reallocate memory\n", +config->filename); + goto out; + } } -} else { - fprintf (stderr, "Error reading configuration file %s: %s\n", -config->filename, error->message); } +if (ferror (fp)) { + fprintf (stderr, "Error reading '%s': I/O error\n", config->filename); + goto out; +} + +if (g_key_file_load_from_data (config->key_file, config_str, config_len, + G_KEY_FILE_KEEP_COMMENTS, &error)) { + ret = TRUE; + goto out; +} + +fprintf (stderr, "Error parsing config file '%s': %s\n", +config->filename, error->message); + g_error_free (error); +out: +if (fp) + fclose(fp); + +if (config_str) + talloc_free(config_str); + return ret; } -- 2.10.2 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v5 1/2] cli: abstract config file reading to a separate function
From: Jani Nikula Simplify and fix the coding style while at it. --- notmuch-config.c | 65 ++-- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/notmuch-config.c b/notmuch-config.c index e5d42a0..bd52790 100644 --- a/notmuch-config.c +++ b/notmuch-config.c @@ -202,6 +202,38 @@ get_username_from_passwd_file (void *ctx) return name; } +static notmuch_bool_t +get_config_from_file (notmuch_config_t *config, notmuch_bool_t create_new) +{ +GError *error = NULL; +notmuch_bool_t ret = FALSE; + +if (g_key_file_load_from_file (config->key_file, config->filename, + G_KEY_FILE_KEEP_COMMENTS, &error)) + return TRUE; + +if (error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT) { + /* If create_new is true, then the caller is prepared for a +* default configuration file in the case of FILE NOT FOUND. +*/ + if (create_new) { + config->is_new = TRUE; + ret = TRUE; + } else { + fprintf (stderr, "Configuration file %s not found.\n" +"Try running 'notmuch setup' to create a configuration.\n", +config->filename); + } +} else { + fprintf (stderr, "Error reading configuration file %s: %s\n", +config->filename, error->message); +} + +g_error_free (error); + +return ret; +} + /* Open the named notmuch configuration file. If the filename is NULL, * the value of the environment variable $NOTMUCH_CONFIG will be used. * If $NOTMUCH_CONFIG is unset, the default configuration file @@ -289,36 +321,9 @@ notmuch_config_open (void *ctx, config->search_exclude_tags_length = 0; config->crypto_gpg_path = NULL; -if (! g_key_file_load_from_file (config->key_file, -config->filename, -G_KEY_FILE_KEEP_COMMENTS, -&error)) -{ - if (error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT) { - /* If create_new is true, then the caller is prepared for a -* default configuration file in the case of FILE NOT -* FOUND. -*/ - if (create_new) { - g_error_free (error); - config->is_new = TRUE; - } else { - fprintf (stderr, "Configuration file %s not found.\n" -"Try running 'notmuch setup' to create a configuration.\n", -config->filename); - talloc_free (config); - g_error_free (error); - return NULL; - } - } - else - { - fprintf (stderr, "Error reading configuration file %s: %s\n", -config->filename, error->message); - talloc_free (config); - g_error_free (error); - return NULL; - } +if (! get_config_from_file (config, create_new)) { + talloc_free (config); + return NULL; } /* Whenever we know of configuration sections that don't appear in -- 2.10.2 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v5 2/2] notmuch-config: replace config reading function
Config files are currently read using glib's g_key_file_load_from_file function which is very inconvenient because it's limited by design to read only from "regular data files" in a filesystem. Because of this limitation notmuch can't read configs from pipes, fifos, sockets, stdin, etc. Not even "notmuch --config=/dev/stdin" works: Error reading configuration file /dev/stdin: Not a regular file So replace g_key_file_load_from_file with g_key_file_load_from_data which gives us much more freedom to read configs from multiple sources. This also helps the more security sensitive users: If someone has private information in the config file, it can be encrypted on disk, then decrypted in RAM and passed through a pipe directly to notmuch without the use of intermediate plain text files. Signed-off-by: Ioan-Adrian Ratiu --- notmuch-config.c | 62 +++- 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/notmuch-config.c b/notmuch-config.c index bd52790..30823cb 100644 --- a/notmuch-config.c +++ b/notmuch-config.c @@ -205,32 +205,74 @@ get_username_from_passwd_file (void *ctx) static notmuch_bool_t get_config_from_file (notmuch_config_t *config, notmuch_bool_t create_new) { +#define BUF_SIZE 4096 +char *config_str = NULL; +int config_len = 0; +int config_bufsize = BUF_SIZE; +size_t len; GError *error = NULL; notmuch_bool_t ret = FALSE; -if (g_key_file_load_from_file (config->key_file, config->filename, - G_KEY_FILE_KEEP_COMMENTS, &error)) - return TRUE; - -if (error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT) { +FILE *fp = fopen(config->filename, "r"); +if (fp == NULL) { /* If create_new is true, then the caller is prepared for a * default configuration file in the case of FILE NOT FOUND. */ if (create_new) { config->is_new = TRUE; ret = TRUE; + goto out; } else { - fprintf (stderr, "Configuration file %s not found.\n" + fprintf (stderr, "Error opening config file '%s': %s\n" "Try running 'notmuch setup' to create a configuration.\n", -config->filename); +config->filename, strerror(errno)); + goto out; + } +} + +config_str = talloc_zero_array (config, char, config_bufsize); +if (config_str == NULL) { + fprintf (stderr, "Error reading '%s': Out of memory\n", config->filename); + goto out; +} + +while ((len = fread (config_str + config_len, 1, +config_bufsize - config_len, fp)) > 0) { + config_len += len; + if (config_len == config_bufsize) { + config_bufsize += BUF_SIZE; + config_str = talloc_realloc (config, config_str, char, config_bufsize); + if (config_str == NULL) { + fprintf (stderr, "Error reading '%s': Failed to reallocate memory\n", +config->filename); + goto out; + } } -} else { - fprintf (stderr, "Error reading configuration file %s: %s\n", -config->filename, error->message); } +if (ferror (fp)) { + fprintf (stderr, "Error reading '%s': I/O error\n", config->filename); + goto out; +} + +if (g_key_file_load_from_data (config->key_file, config_str, config_len, + G_KEY_FILE_KEEP_COMMENTS, &error)) { + ret = TRUE; + goto out; +} + +fprintf (stderr, "Error parsing config file '%s': %s\n", +config->filename, error->message); + g_error_free (error); +out: +if (fp) + fclose(fp); + +if (config_str) + talloc_free(config_str); + return ret; } -- 2.10.2 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v5 0/2] Refactor config reading to support non-regular files
Changes since v4: (based on Tomi's feedback): * Replaced strlen with config_len in the call to g_key_file_load_from_data() * Added a null pointer check and minor whitespace fixes before exiting get_config_from_file Ioan-Adrian Ratiu (1): notmuch-config: replace config reading function Jani Nikula (1): cli: abstract config file reading to a separate function notmuch-config.c | 107 +++ 1 file changed, 77 insertions(+), 30 deletions(-) -- 2.10.2 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v4 1/2] cli: abstract config file reading to a separate function
From: Jani Nikula Simplify and fix the coding style while at it. --- notmuch-config.c | 65 ++-- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/notmuch-config.c b/notmuch-config.c index e5d42a0..bd52790 100644 --- a/notmuch-config.c +++ b/notmuch-config.c @@ -202,6 +202,38 @@ get_username_from_passwd_file (void *ctx) return name; } +static notmuch_bool_t +get_config_from_file (notmuch_config_t *config, notmuch_bool_t create_new) +{ +GError *error = NULL; +notmuch_bool_t ret = FALSE; + +if (g_key_file_load_from_file (config->key_file, config->filename, + G_KEY_FILE_KEEP_COMMENTS, &error)) + return TRUE; + +if (error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT) { + /* If create_new is true, then the caller is prepared for a +* default configuration file in the case of FILE NOT FOUND. +*/ + if (create_new) { + config->is_new = TRUE; + ret = TRUE; + } else { + fprintf (stderr, "Configuration file %s not found.\n" +"Try running 'notmuch setup' to create a configuration.\n", +config->filename); + } +} else { + fprintf (stderr, "Error reading configuration file %s: %s\n", +config->filename, error->message); +} + +g_error_free (error); + +return ret; +} + /* Open the named notmuch configuration file. If the filename is NULL, * the value of the environment variable $NOTMUCH_CONFIG will be used. * If $NOTMUCH_CONFIG is unset, the default configuration file @@ -289,36 +321,9 @@ notmuch_config_open (void *ctx, config->search_exclude_tags_length = 0; config->crypto_gpg_path = NULL; -if (! g_key_file_load_from_file (config->key_file, -config->filename, -G_KEY_FILE_KEEP_COMMENTS, -&error)) -{ - if (error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT) { - /* If create_new is true, then the caller is prepared for a -* default configuration file in the case of FILE NOT -* FOUND. -*/ - if (create_new) { - g_error_free (error); - config->is_new = TRUE; - } else { - fprintf (stderr, "Configuration file %s not found.\n" -"Try running 'notmuch setup' to create a configuration.\n", -config->filename); - talloc_free (config); - g_error_free (error); - return NULL; - } - } - else - { - fprintf (stderr, "Error reading configuration file %s: %s\n", -config->filename, error->message); - talloc_free (config); - g_error_free (error); - return NULL; - } +if (! get_config_from_file (config, create_new)) { + talloc_free (config); + return NULL; } /* Whenever we know of configuration sections that don't appear in -- 2.10.2 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v4 2/2] notmuch-config: replace config reading function
Config files are currently read using glib's g_key_file_load_from_file function which is very inconvenient because it's limited by design to read only from "regular data files" in a filesystem. Because of this limitation notmuch can't read configs from pipes, fifos, sockets, stdin, etc. Not even "notmuch --config=/dev/stdin" works: Error reading configuration file /dev/stdin: Not a regular file So replace g_key_file_load_from_file with g_key_file_load_from_data which gives us much more freedom to read configs from multiple sources. This also helps the more security sensitive users: If someone has private information in the config file, it can be encrypted on disk, then decrypted in RAM and passed through a pipe directly to notmuch without the use of intermediate plain text files. Signed-off-by: Ioan-Adrian Ratiu --- notmuch-config.c | 58 ++-- 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/notmuch-config.c b/notmuch-config.c index bd52790..fe16fa3 100644 --- a/notmuch-config.c +++ b/notmuch-config.c @@ -205,32 +205,70 @@ get_username_from_passwd_file (void *ctx) static notmuch_bool_t get_config_from_file (notmuch_config_t *config, notmuch_bool_t create_new) { +#define BUF_SIZE 4096 +char *config_str = NULL; +int config_len = 0; +int config_bufsize = BUF_SIZE; +size_t len; GError *error = NULL; notmuch_bool_t ret = FALSE; -if (g_key_file_load_from_file (config->key_file, config->filename, - G_KEY_FILE_KEEP_COMMENTS, &error)) - return TRUE; - -if (error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT) { +FILE *fp = fopen(config->filename, "r"); +if (fp == NULL) { /* If create_new is true, then the caller is prepared for a * default configuration file in the case of FILE NOT FOUND. */ if (create_new) { config->is_new = TRUE; ret = TRUE; + goto out; } else { - fprintf (stderr, "Configuration file %s not found.\n" + fprintf (stderr, "Error opening config file '%s': %s\n" "Try running 'notmuch setup' to create a configuration.\n", -config->filename); +config->filename, strerror(errno)); + goto out; } -} else { - fprintf (stderr, "Error reading configuration file %s: %s\n", -config->filename, error->message); } +config_str = talloc_zero_array (config, char, config_bufsize); +if (config_str == NULL) { + fprintf (stderr, "Error reading '%s': Out of memory\n", config->filename); + goto out; +} + +while ((len = fread (config_str + config_len, 1, +config_bufsize - config_len, fp)) > 0) { + config_len += len; + if (config_len == config_bufsize) { + config_bufsize += BUF_SIZE; + config_str = talloc_realloc (config, config_str, char, config_bufsize); + if (config_str == NULL) { + fprintf (stderr, "Error reading '%s': Failed to reallocate memory\n", +config->filename); + goto out; + } + } +} + +if (ferror (fp)) { + fprintf (stderr, "Error reading '%s': I/O error\n", config->filename); + goto out; +} + +if (g_key_file_load_from_data (config->key_file, config_str, strlen(config_str), + G_KEY_FILE_KEEP_COMMENTS, &error)) { + ret = TRUE; + goto out; +} + +fprintf (stderr, "Error parsing config file '%s': %s\n", +config->filename, error->message); + g_error_free (error); +out: +fclose(fp); +if (config_str) talloc_free(config_str); return ret; } -- 2.10.2 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v4 0/2] Refactor config reading to support non-regular files
Changes since v3: (thanks Tomi for the awesome feedback): * Rewrote the config-reading function's exit logic to use a 'linux kernel' style & cleanup based on goto's. * Minor variable NULL initialization change, comment removal. Ioan-Adrian Ratiu (1): notmuch-config: replace config reading function Jani Nikula (1): cli: abstract config file reading to a separate function notmuch-config.c | 104 +++ 1 file changed, 74 insertions(+), 30 deletions(-) -- 2.10.2 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v3 1/2] cli: abstract config file reading to a separate function
From: Jani Nikula Simplify and fix the coding style while at it. --- notmuch-config.c | 65 ++-- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/notmuch-config.c b/notmuch-config.c index e5d42a0..bd52790 100644 --- a/notmuch-config.c +++ b/notmuch-config.c @@ -202,6 +202,38 @@ get_username_from_passwd_file (void *ctx) return name; } +static notmuch_bool_t +get_config_from_file (notmuch_config_t *config, notmuch_bool_t create_new) +{ +GError *error = NULL; +notmuch_bool_t ret = FALSE; + +if (g_key_file_load_from_file (config->key_file, config->filename, + G_KEY_FILE_KEEP_COMMENTS, &error)) + return TRUE; + +if (error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT) { + /* If create_new is true, then the caller is prepared for a +* default configuration file in the case of FILE NOT FOUND. +*/ + if (create_new) { + config->is_new = TRUE; + ret = TRUE; + } else { + fprintf (stderr, "Configuration file %s not found.\n" +"Try running 'notmuch setup' to create a configuration.\n", +config->filename); + } +} else { + fprintf (stderr, "Error reading configuration file %s: %s\n", +config->filename, error->message); +} + +g_error_free (error); + +return ret; +} + /* Open the named notmuch configuration file. If the filename is NULL, * the value of the environment variable $NOTMUCH_CONFIG will be used. * If $NOTMUCH_CONFIG is unset, the default configuration file @@ -289,36 +321,9 @@ notmuch_config_open (void *ctx, config->search_exclude_tags_length = 0; config->crypto_gpg_path = NULL; -if (! g_key_file_load_from_file (config->key_file, -config->filename, -G_KEY_FILE_KEEP_COMMENTS, -&error)) -{ - if (error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT) { - /* If create_new is true, then the caller is prepared for a -* default configuration file in the case of FILE NOT -* FOUND. -*/ - if (create_new) { - g_error_free (error); - config->is_new = TRUE; - } else { - fprintf (stderr, "Configuration file %s not found.\n" -"Try running 'notmuch setup' to create a configuration.\n", -config->filename); - talloc_free (config); - g_error_free (error); - return NULL; - } - } - else - { - fprintf (stderr, "Error reading configuration file %s: %s\n", -config->filename, error->message); - talloc_free (config); - g_error_free (error); - return NULL; - } +if (! get_config_from_file (config, create_new)) { + talloc_free (config); + return NULL; } /* Whenever we know of configuration sections that don't appear in -- 2.10.2 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v3 2/2] notmuch-config: replace config reading function
Config files are currently read using glib's g_key_file_load_from_file function which is very inconvenient because it's limited by design to read only from "regular data files" in a filesystem. Because of this limitation notmuch can't read configs from pipes, fifos, sockets, stdin, etc. Not even "notmuch --config=/dev/stdin" works: Error reading configuration file /dev/stdin: Not a regular file So replace g_key_file_load_from_file with g_key_file_load_from_data which gives us much more freedom to read configs from multiple sources. This also helps the more security sensitive users: If someone has private information in the config file, it can be encrypted on disk, then decrypted in RAM and passed through a pipe directly to notmuch without the use of intermediate plain text files. Signed-off-by: Ioan-Adrian Ratiu --- notmuch-config.c | 60 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/notmuch-config.c b/notmuch-config.c index bd52790..1ea897a 100644 --- a/notmuch-config.c +++ b/notmuch-config.c @@ -205,33 +205,67 @@ get_username_from_passwd_file (void *ctx) static notmuch_bool_t get_config_from_file (notmuch_config_t *config, notmuch_bool_t create_new) { +// #define BUF_SIZE 200 // test line (use debug code or strace(1) to verify) +#define BUF_SIZE 4096 +char *config_str; +int config_len = 0; +int config_bufsize = BUF_SIZE; +size_t len; GError *error = NULL; -notmuch_bool_t ret = FALSE; -if (g_key_file_load_from_file (config->key_file, config->filename, - G_KEY_FILE_KEEP_COMMENTS, &error)) - return TRUE; - -if (error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT) { +FILE *fp = fopen(config->filename, "r"); +if (fp == NULL) { /* If create_new is true, then the caller is prepared for a * default configuration file in the case of FILE NOT FOUND. */ if (create_new) { config->is_new = TRUE; - ret = TRUE; + return TRUE; } else { - fprintf (stderr, "Configuration file %s not found.\n" + fprintf (stderr, "Error opening config file '%s': %s\n" "Try running 'notmuch setup' to create a configuration.\n", -config->filename); +config->filename, strerror(errno)); + return FALSE; } -} else { - fprintf (stderr, "Error reading configuration file %s: %s\n", -config->filename, error->message); } +config_str = talloc_zero_array (config, char, config_bufsize); +if (config_str == NULL) { + fprintf (stderr, "Error reading '%s': Out of memory\n", config->filename); + return FALSE; +} + +while ((len = fread (config_str + config_len, 1, +config_bufsize - config_len, fp)) > 0) { + config_len += len; + if (config_len == config_bufsize) { + config_bufsize += BUF_SIZE; + config_str = talloc_realloc (config, config_str, char, config_bufsize); + if (config_str == NULL) { + fprintf (stderr, "Error reading '%s': Failed to reallocate memory\n", +config->filename); + return FALSE; + } + } +} + +if (ferror (fp)) { + fprintf (stderr, "Error reading '%s': I/O error\n", config->filename); + return FALSE; +} + +fclose(fp); + +if (g_key_file_load_from_data (config->key_file, config_str, strlen(config_str), + G_KEY_FILE_KEEP_COMMENTS, &error)) + return TRUE; + +fprintf (stderr, "Error parsing config file '%s': %s\n", +config->filename, error->message); + g_error_free (error); -return ret; +return FALSE; } /* Open the named notmuch configuration file. If the filename is NULL, -- 2.10.2 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v3 0/2] Refactor config reading to support non-regular files
Changes since v2 (based on Tomi's feedback): * Rewrote config reading loop to use fread instead of fgets (behaves the same but the code looks much better now). Ioan-Adrian Ratiu (1): notmuch-config: replace config reading function Jani Nikula (1): cli: abstract config file reading to a separate function notmuch-config.c | 99 +++- 1 file changed, 69 insertions(+), 30 deletions(-) -- 2.10.2 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 2/2] notmuch-config: replace config reading function
Config files are currently read using glib's g_key_file_load_from_file function which is very inconvenient because it's limited by design to read only from "regular data files" in a filesystem. Because of this limitation notmuch can't read configs from pipes, fifos, sockets, stdin, etc. Not even "notmuch --config=/dev/stdin" works: Error reading configuration file /dev/stdin: Not a regular file So replace g_key_file_load_from_file with g_key_file_load_from_data which gives us much more freedom to read configs from multiple sources. This also helps the more security sensitive users: If someone has private information in the config file, it can be encrypted on disk, then decrypted in RAM and passed through a pipe directly to notmuch without the use of intermediate plain text files. Signed-off-by: Ioan-Adrian Ratiu --- notmuch-config.c | 53 + 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/notmuch-config.c b/notmuch-config.c index bd52790..569cf0b 100644 --- a/notmuch-config.c +++ b/notmuch-config.c @@ -205,33 +205,62 @@ get_username_from_passwd_file (void *ctx) static notmuch_bool_t get_config_from_file (notmuch_config_t *config, notmuch_bool_t create_new) { +#define BUF_SIZE 4096 +char buffer[BUF_SIZE]; +size_t content_size = 1; // includes NULL +char *config_str = NULL; GError *error = NULL; -notmuch_bool_t ret = FALSE; -if (g_key_file_load_from_file (config->key_file, config->filename, - G_KEY_FILE_KEEP_COMMENTS, &error)) - return TRUE; - -if (error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT) { +FILE *fp = fopen(config->filename, "r"); +if (fp == NULL) { /* If create_new is true, then the caller is prepared for a * default configuration file in the case of FILE NOT FOUND. */ if (create_new) { config->is_new = TRUE; - ret = TRUE; + return TRUE; } else { - fprintf (stderr, "Configuration file %s not found.\n" + fprintf (stderr, "Error opening config file '%s': %s\n" "Try running 'notmuch setup' to create a configuration.\n", +config->filename, strerror(errno)); + return FALSE; + } +} + +config_str = talloc_zero_array (config, char, BUF_SIZE); +if (config_str == NULL) { + fprintf (stderr, "Error reading '%s': Out of memory\n", config->filename); + return FALSE; +} + +while (fgets (buffer, BUF_SIZE, fp)) { + content_size += strlen(buffer); + config_str = talloc_realloc(config, config_str, char, content_size); + if (config_str == NULL) { + fprintf (stderr, "Error reading '%s': Failed to reallocate memory\n", config->filename); + return FALSE; } -} else { - fprintf (stderr, "Error reading configuration file %s: %s\n", -config->filename, error->message); + strcat (config_str, buffer); +} + +if (ferror (fp)) { + fprintf (stderr, "Error reading '%s': I/O error\n", config->filename); + return FALSE; } +fclose(fp); + +if (g_key_file_load_from_data (config->key_file, config_str, strlen(config_str), + G_KEY_FILE_KEEP_COMMENTS, &error)) + return TRUE; + +fprintf (stderr, "Error parsing config file '%s': %s\n", +config->filename, error->message); + g_error_free (error); -return ret; +return FALSE; } /* Open the named notmuch configuration file. If the filename is NULL, -- 2.10.2 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 1/2] cli: abstract config file reading to a separate function
From: Jani Nikula Simplify and fix the coding style while at it. --- notmuch-config.c | 65 ++-- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/notmuch-config.c b/notmuch-config.c index e5d42a0..bd52790 100644 --- a/notmuch-config.c +++ b/notmuch-config.c @@ -202,6 +202,38 @@ get_username_from_passwd_file (void *ctx) return name; } +static notmuch_bool_t +get_config_from_file (notmuch_config_t *config, notmuch_bool_t create_new) +{ +GError *error = NULL; +notmuch_bool_t ret = FALSE; + +if (g_key_file_load_from_file (config->key_file, config->filename, + G_KEY_FILE_KEEP_COMMENTS, &error)) + return TRUE; + +if (error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT) { + /* If create_new is true, then the caller is prepared for a +* default configuration file in the case of FILE NOT FOUND. +*/ + if (create_new) { + config->is_new = TRUE; + ret = TRUE; + } else { + fprintf (stderr, "Configuration file %s not found.\n" +"Try running 'notmuch setup' to create a configuration.\n", +config->filename); + } +} else { + fprintf (stderr, "Error reading configuration file %s: %s\n", +config->filename, error->message); +} + +g_error_free (error); + +return ret; +} + /* Open the named notmuch configuration file. If the filename is NULL, * the value of the environment variable $NOTMUCH_CONFIG will be used. * If $NOTMUCH_CONFIG is unset, the default configuration file @@ -289,36 +321,9 @@ notmuch_config_open (void *ctx, config->search_exclude_tags_length = 0; config->crypto_gpg_path = NULL; -if (! g_key_file_load_from_file (config->key_file, -config->filename, -G_KEY_FILE_KEEP_COMMENTS, -&error)) -{ - if (error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT) { - /* If create_new is true, then the caller is prepared for a -* default configuration file in the case of FILE NOT -* FOUND. -*/ - if (create_new) { - g_error_free (error); - config->is_new = TRUE; - } else { - fprintf (stderr, "Configuration file %s not found.\n" -"Try running 'notmuch setup' to create a configuration.\n", -config->filename); - talloc_free (config); - g_error_free (error); - return NULL; - } - } - else - { - fprintf (stderr, "Error reading configuration file %s: %s\n", -config->filename, error->message); - talloc_free (config); - g_error_free (error); - return NULL; - } +if (! get_config_from_file (config, create_new)) { + talloc_free (config); + return NULL; } /* Whenever we know of configuration sections that don't appear in -- 2.10.2 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 0/2] Refactor config reading to support non-regular files
Changes since v1 (Based on Jani's feedback): * Incorporated Jani's patch into this series and rebased my changes on top Ioan-Adrian Ratiu (1): notmuch-config: replace config reading function Jani Nikula (1): cli: abstract config file reading to a separate function notmuch-config.c | 94 ++-- 1 file changed, 64 insertions(+), 30 deletions(-) -- 2.10.2 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH] notmuch-config: replace config reading function
Config files are currently read using glib's g_key_file_load_from_file function which is very inconvenient because it's limited by design to read only from "regular data files" in a filesystem. Because of this limitation notmuch can't read configs from pipes, fifos, sockets, stdin, etc. Not even "notmuch --config=/dev/stdin" works: Error reading configuration file /dev/stdin: Not a regular file So replace g_key_file_load_from_file with g_key_file_load_from_data which gives us much more freedom to read configs from multiple sources. This also helps the more security sensitive users: If someone has private information in the config file, it can be encrypted on disk, then decrypted in RAM and passed through a pipe directly to notmuch without the use of intermediate plain text files. Signed-off-by: Ioan-Adrian Ratiu --- notmuch-config.c | 63 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/notmuch-config.c b/notmuch-config.c index e5d42a0..8435815 100644 --- a/notmuch-config.c +++ b/notmuch-config.c @@ -202,6 +202,64 @@ get_username_from_passwd_file (void *ctx) return name; } +static gboolean +get_config_from_file (notmuch_config_t *config, GError **error) +{ +#define BUF_SIZE 4096 +char buffer[BUF_SIZE]; +size_t content_size = 1; // includes NULL + +FILE *fp = fopen(config->filename, "r"); +if (fp == NULL) { + *error = g_error_new(G_FILE_ERROR, +G_FILE_ERROR_NOENT, +"Couldn't open config file '%s': %s.\n", +config->filename, +strerror(errno)); + return FALSE; +} + +char *config_str = talloc_zero_array (config, char, BUF_SIZE); +if (config_str == NULL) +{ + *error = g_error_new(G_FILE_ERROR, +G_FILE_ERROR_NOMEM, +"Out of memory while reading config.\n"); + return FALSE; +} + +while (fgets (buffer, BUF_SIZE, fp)) +{ + content_size += strlen(buffer); + config_str = talloc_realloc(config, config_str, char, content_size); + if (config_str == NULL) + { + *error = g_error_new(G_FILE_ERROR, +G_FILE_ERROR_NOMEM, +"Failed to reallocate config memory.\n"); + return FALSE; + } + strcat (config_str, buffer); +} + +if (ferror (fp)) +{ + *error = g_error_new(G_FILE_ERROR, +G_FILE_ERROR_IO, +"I/O error reading configuration from '%s'.\n", +config->filename); + return FALSE; +} + +fclose(fp); + +return g_key_file_load_from_data (config->key_file, + config_str, + strlen(config_str), + G_KEY_FILE_KEEP_COMMENTS, + error); +} + /* Open the named notmuch configuration file. If the filename is NULL, * the value of the environment variable $NOTMUCH_CONFIG will be used. * If $NOTMUCH_CONFIG is unset, the default configuration file @@ -289,10 +347,7 @@ notmuch_config_open (void *ctx, config->search_exclude_tags_length = 0; config->crypto_gpg_path = NULL; -if (! g_key_file_load_from_file (config->key_file, -config->filename, -G_KEY_FILE_KEEP_COMMENTS, -&error)) +if (! get_config_from_file (config, &error)) { if (error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT) { /* If create_new is true, then the caller is prepared for a -- 2.10.2 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH v5 0/7] Add refresh all buffers functionality
On Sun, 09 Oct 2016, Mark Walters wrote: > This is a simplified version of the series at > id:20161008210139.25322-1-...@adirat.com > > The main change is that it makes all the refresh functions refresh the > buffer without forcing the buffer to be displayed. > > In tree and show mode this was already the case; in search mode it is > a change but since the refresh function has to be called in the buffer > it is likely to already be displayed. In hello mode it a genuine > change, but notmuch-hello-update is a trivial wrapped of notmuch-hello > so anyone who wants to force display can just call notmuch-hello. > > Once this change is made Ionel's changes become very clean. LGTM. I had a few minutes to apply and test this series and all my use-cases work perfectly. Thank you for helping with this. > > Best wishes > > Mark > > > Ioan-Adrian Ratiu (4): > emacs: notmuch-search: add no-display functionality > emacs: notmuch-search-refresh-view: reuse buffer > emacs: notmuch-show: refresh all windows displaying buffer > emacs: notmuch-lib: add refresh all buffers function > > Mark Walters (3): > emacs: tree: make refresh use generic binding > emacs: make the refresh functions more consistent > emacs: hello: stop update from forcing the buffer to be displayed > > emacs/notmuch-hello.el | 7 --- > emacs/notmuch-lib.el | 23 ++- > emacs/notmuch-show.el | 19 +-- > emacs/notmuch-tree.el | 1 - > emacs/notmuch.el | 16 +++- > 5 files changed, 46 insertions(+), 20 deletions(-) > > -- > 2.1.4 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v4 5/6] emacs: add no-display arg to generic refresh functions
As stated in the documentation for notmuch-buffer-refresh-function, each buffer major mode's refresh function takes two args, but notmuch-refresh-this-buffer and notmuch-poll-and-refresh-this-buffer only pass the first arg. Add the second arg because it's very useful for background refreshing. Signed-off-by: Ioan-Adrian Ratiu --- emacs/notmuch-lib.el | 8 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el index 2d27e56..e1b5066 100644 --- a/emacs/notmuch-lib.el +++ b/emacs/notmuch-lib.el @@ -418,17 +418,17 @@ interactively, the second requests that the refresh call not display the buffer.") (make-variable-buffer-local 'notmuch-buffer-refresh-function) -(defun notmuch-refresh-this-buffer (prefix) +(defun notmuch-refresh-this-buffer (prefix no-display) "Refresh the current buffer." (interactive "P") (when notmuch-buffer-refresh-function -(funcall notmuch-buffer-refresh-function prefix))) +(funcall notmuch-buffer-refresh-function prefix no-display))) -(defun notmuch-poll-and-refresh-this-buffer (prefix) +(defun notmuch-poll-and-refresh-this-buffer (prefix no-display) "Invoke `notmuch-poll' to import mail, then refresh the current buffer." (interactive "P") (notmuch-poll) - (notmuch-refresh-this-buffer prefix)) + (notmuch-refresh-this-buffer prefix no-display)) (defun notmuch-prettify-subject (subject) ;; This function is used by `notmuch-search-process-filter' which -- 2.10.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v4 2/6] emacs: notmuch-search-refresh-view: reuse buffer
There's no reason to completely kill a buffer while refreshing its search results because the buffer name is constant between refreshes (based on the search query), only its contents may change and notmuch search kills all local variables, so it's safe to reuse. Reusing the same buffer also makes it possible to do things like refreshing a buffer which is not focused or even not shown in any window - this will be used in the next commits to add auto-refresh capabilities to all existing notmuch buffers + a function to call after syncing mail to refresh everything. Signed-off-by: Ioan-Adrian Ratiu --- emacs/notmuch.el | 8 +--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/emacs/notmuch.el b/emacs/notmuch.el index ee1bb54..17a784d 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -985,7 +985,7 @@ the configured default sort order." (defun notmuch-search-refresh-view (&rest ignore) "Refresh the current view. -Kills the current buffer and runs a new search with the same +Erases the current buffer and runs a new search with the same query string as the current search. If the current thread is in the new search results, then point will be placed on the same thread. Otherwise, point will be moved to attempt to be in the @@ -993,8 +993,10 @@ same relative position within the new buffer." (let ((target-line (line-number-at-pos)) (oldest-first notmuch-search-oldest-first) (target-thread (notmuch-search-find-thread-id 'bare)) - (query notmuch-search-query-string)) -(notmuch-bury-or-kill-this-buffer) + (query notmuch-search-query-string) + (inhibit-read-only t)) +(remove-overlays) +(erase-buffer) (notmuch-search query oldest-first target-thread target-line) (goto-char (point-min -- 2.10.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v4 3/6] emacs: notmuch-search: add no-display functionality
If no-display is non-nil when updating a notmuch-search buffer, do not force bring to foreground in a window said search results buffer. Signed-off-by: Ioan-Adrian Ratiu --- emacs/notmuch.el | 13 + 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/emacs/notmuch.el b/emacs/notmuch.el index 17a784d..8d5e20c 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -925,7 +925,7 @@ PROMPT is the string to prompt with." ;;;###autoload (put 'notmuch-search 'notmuch-doc "Search for messages.") -(defun notmuch-search (&optional query oldest-first target-thread target-line) +(defun notmuch-search (&optional query oldest-first target-thread target-line no-display) "Display threads matching QUERY in a notmuch-search buffer. If QUERY is nil, it is read interactively from the minibuffer. @@ -936,6 +936,9 @@ Other optional parameters are used as follows: current if it appears in the search results. TARGET-LINE: The line number to move to if the target thread does not appear in the search results. + NO-DISPLAY: Do not try to foreground the search results buffer. If it is + already foregrounded i.e. displayed in a window, this has no + effect, meaning the buffer will remain visible. When called interactively, this will prompt for a query and use the configured default sort order." @@ -949,7 +952,9 @@ the configured default sort order." (let* ((query (or query (notmuch-read-query "Notmuch search: "))) (buffer (get-buffer-create (notmuch-search-buffer-title query -(switch-to-buffer buffer) +(if no-display + (set-buffer buffer) + (switch-to-buffer buffer)) (notmuch-search-mode) ;; Don't track undo information for this buffer (set 'buffer-undo-list t) @@ -982,7 +987,7 @@ the configured default sort order." (set-process-query-on-exit-flag proc nil (run-hooks 'notmuch-search-hook))) -(defun notmuch-search-refresh-view (&rest ignore) +(defun notmuch-search-refresh-view (&optional ignore no-display) "Refresh the current view. Erases the current buffer and runs a new search with the same @@ -997,7 +1002,7 @@ same relative position within the new buffer." (inhibit-read-only t)) (remove-overlays) (erase-buffer) -(notmuch-search query oldest-first target-thread target-line) +(notmuch-search query oldest-first target-thread target-line no-display) (goto-char (point-min (defun notmuch-search-toggle-order () -- 2.10.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v4 4/6] emacs: notmuch-show: refresh all windows displaying buffer
This updates all windows displaying a notmuch-show buffer when the buffer refresh function is called. Each window displaying a notmuch-show buffer has its own currently displayed message based on the (point) location. We store the state of all displayed windows when refreshing a notmuch-show buffer and re-apply the current shown message (point) for all windows. Implementation note: Each window has it's own (point) value, besides the buffer's (point) value. Sometimes these values are identical like in the case where a single window displays a buffer. When multiple windows display a buffer, (point) returns each window's specific value. What we are storing in this changeset is the window values not the buffer point values. The buffer's point is returned only if no window is displaying the buffer, a case we do not care about here. Signed-off-by: Ioan-Adrian Ratiu --- emacs/notmuch-show.el | 19 +-- 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el index 1772d10..bd69751 100644 --- a/emacs/notmuch-show.el +++ b/emacs/notmuch-show.el @@ -1317,8 +1317,13 @@ If no messages match the query return NIL." This includes: - the list of open messages, - - the current message." - (list (notmuch-show-get-message-id) (notmuch-show-get-message-ids-for-open-messages))) + - the combination of current message id with/for each visible window." + (let* ((win-list (get-buffer-window-list (current-buffer) nil t)) +(win-id-combo (mapcar (lambda (win) +(with-selected-window win + (list win (notmuch-show-get-message-id + win-list))) +(list win-id-combo (notmuch-show-get-message-ids-for-open-messages (defun notmuch-show-get-query () "Return the current query in this show buffer" @@ -1345,8 +1350,8 @@ This includes: This includes: - opening the messages previously opened, - closing all other messages, - - moving to the correct current message." - (let ((current (car state)) + - moving to the correct current message in every displayed window." + (let ((win-msg-alist (car state)) (open (cadr state))) ;; Open those that were open. @@ -1355,8 +1360,10 @@ This includes: (member (notmuch-show-get-message-id) open)) until (not (notmuch-show-goto-message-next))) -;; Go to the previously open message. -(notmuch-show-goto-message current))) +(dolist (win-msg-pair win-msg-alist) + (with-selected-window (car win-msg-pair) + ;; Go to the previously open message in this window + (notmuch-show-goto-message (cadr win-msg-pair)) (defun notmuch-show-refresh-view (&optional reset-state ignore) "Refresh the current view. -- 2.10.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v4 1/6] emacs: make the refresh code more consistent
From: Mark Walters The current refresh code is a little haphazard with some of the refresh functions called interactively, and some not. Some of the refresh functions take arguments and they aren't consistent. This makes all the functions have the same form. --- emacs/notmuch-hello.el | 2 +- emacs/notmuch-lib.el | 22 -- emacs/notmuch-show.el | 2 +- emacs/notmuch-tree.el | 5 ++--- emacs/notmuch.el | 2 +- 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el index d582bff..97280ca 100644 --- a/emacs/notmuch-hello.el +++ b/emacs/notmuch-hello.el @@ -604,7 +604,7 @@ with `notmuch-hello-query-counts'." (defimage notmuch-hello-logo ((:type png :file "notmuch-logo.png"))) -(defun notmuch-hello-update (&optional no-display) +(defun notmuch-hello-update (&optional ignore no-display) "Update the current notmuch view." ;; Lazy - rebuild everything. (notmuch-hello no-display)) diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el index b2cdace..2d27e56 100644 --- a/emacs/notmuch-lib.el +++ b/emacs/notmuch-lib.el @@ -410,23 +410,25 @@ of its command symbol." (pop-to-buffer (help-buffer) (defvar notmuch-buffer-refresh-function nil - "Function to call to refresh the current buffer.") + "Function to call to refresh the current buffer. + +It will be called with two arguments: the first is the prefix +argument when notmuch-refresh-this-buffer is called +interactively, the second requests that the refresh call not +display the buffer.") (make-variable-buffer-local 'notmuch-buffer-refresh-function) -(defun notmuch-refresh-this-buffer () +(defun notmuch-refresh-this-buffer (prefix) "Refresh the current buffer." - (interactive) + (interactive "P") (when notmuch-buffer-refresh-function -(if (commandp notmuch-buffer-refresh-function) - ;; Pass prefix argument, etc. - (call-interactively notmuch-buffer-refresh-function) - (funcall notmuch-buffer-refresh-function +(funcall notmuch-buffer-refresh-function prefix))) -(defun notmuch-poll-and-refresh-this-buffer () +(defun notmuch-poll-and-refresh-this-buffer (prefix) "Invoke `notmuch-poll' to import mail, then refresh the current buffer." - (interactive) + (interactive "P") (notmuch-poll) - (notmuch-refresh-this-buffer)) + (notmuch-refresh-this-buffer prefix)) (defun notmuch-prettify-subject (subject) ;; This function is used by `notmuch-search-process-filter' which diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el index f2487ab..1772d10 100644 --- a/emacs/notmuch-show.el +++ b/emacs/notmuch-show.el @@ -1358,7 +1358,7 @@ This includes: ;; Go to the previously open message. (notmuch-show-goto-message current))) -(defun notmuch-show-refresh-view (&optional reset-state) +(defun notmuch-show-refresh-view (&optional reset-state ignore) "Refresh the current view. Refreshes the current view, observing changes in display diff --git a/emacs/notmuch-tree.el b/emacs/notmuch-tree.el index 1555812..c347712 100644 --- a/emacs/notmuch-tree.el +++ b/emacs/notmuch-tree.el @@ -271,7 +271,6 @@ FUNC." (define-key map "x" 'notmuch-tree-quit) (define-key map "A" 'notmuch-tree-archive-thread) (define-key map "a" 'notmuch-tree-archive-message-then-next) -(define-key map "=" 'notmuch-tree-refresh-view) (define-key map "z" 'notmuch-tree-to-tree) (define-key map "n" 'notmuch-tree-next-matching-message) (define-key map "p" 'notmuch-tree-prev-matching-message) @@ -571,9 +570,9 @@ message will be \"unarchived\", i.e. the tag changes in (when (window-live-p notmuch-tree-message-window) (notmuch-tree-show-message-in))) -(defun notmuch-tree-refresh-view () +(defun notmuch-tree-refresh-view (&rest ignore) "Refresh view." - (interactive) + (interactive "P") (let ((inhibit-read-only t) (basic-query notmuch-tree-basic-query) (query-context notmuch-tree-query-context) diff --git a/emacs/notmuch.el b/emacs/notmuch.el index 888672b..ee1bb54 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -982,7 +982,7 @@ the configured default sort order." (set-process-query-on-exit-flag proc nil (run-hooks 'notmuch-search-hook))) -(defun notmuch-search-refresh-view () +(defun notmuch-search-refresh-view (&rest ignore) "Refresh the current view. Kills the current buffer and runs a new search with the same -- 2.10.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v4 6/6] emacs: notmuch-lib: add refresh all buffers function
notmuch-refresh-all-buffers calls each buffer's major mode specific refresh function using the generic notmuch-refresh-this-buffer function. Each buffer-specific refresh function has the same form, taking a prefix and a no-display arg. Passing the no-display arg is very useful because it tells the buffer-specific refresh functions to work in the background and not bring forward the refreshed buffer in a window. This again is very useful for silent async background updating the emacs display when new mail is fetched. Signed-off-by: Ioan-Adrian Ratiu --- emacs/notmuch-lib.el | 15 +++ 1 file changed, 15 insertions(+) diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el index e1b5066..1162130 100644 --- a/emacs/notmuch-lib.el +++ b/emacs/notmuch-lib.el @@ -430,6 +430,21 @@ display the buffer.") (notmuch-poll) (notmuch-refresh-this-buffer prefix no-display)) +(defun notmuch-refresh-all-buffers (&optional no-display) + "Invoke `notmuch-refresh-this-buffer' on all notmuch major-mode buffers. + +If no-display is non-nil all buffers are silently refreshed, i.e. they are +not foregrounded to a window if not already displayed. If no-display is nil, +then each buffer's mode-specific refresh function uses its default behaviour." + (dolist (buffer (buffer-list)) +(let ((buffer-mode (buffer-local-value 'major-mode buffer))) + (when (memq buffer-mode '(notmuch-show-mode + notmuch-tree-mode + notmuch-search-mode + notmuch-hello-mode)) + (with-current-buffer buffer + (notmuch-refresh-this-buffer nil no-display)) + (defun notmuch-prettify-subject (subject) ;; This function is used by `notmuch-search-process-filter' which ;; requires that we not disrupt its' matching state. -- 2.10.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v4 0/6] Add refresh all buffers functionality
Changes since v3: This is a complete rewrite based on Mark's awesome patch to make all buffer mode's refresh functions non-interactive and consistent wrt to their arguments (Mark's patch is included in this series). For a complete example how I use this feature you can look at https://github.com/10ne1/emacs-config/blob/master/lisp/my-notmuch.el Ioan-Adrian Ratiu (5): emacs: notmuch-search-refresh-view: reuse buffer emacs: notmuch-search: add no-display functionality emacs: notmuch-show: refresh all windows displaying buffer emacs: add no-display arg to generic refresh functions emacs: notmuch-lib: add refresh all buffers function Mark Walters (1): emacs: make the refresh code more consistent emacs/notmuch-hello.el | 2 +- emacs/notmuch-lib.el | 37 +++-- emacs/notmuch-show.el | 21 ++--- emacs/notmuch-tree.el | 5 ++--- emacs/notmuch.el | 21 ++--- 5 files changed, 58 insertions(+), 28 deletions(-) -- 2.10.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH] emacs: make the refresh code more consistent
On Thu, 06 Oct 2016, Mark Walters wrote: > The current refresh code is a little haphazard with some of the > refresh functions called interactively, and some not. Some of the > refresh functions take arguments and they aren't consistent. > > This makes all the functions have the same form. > --- > > This might be a sensible change to make before the series > id:20161006134227.17194-1-...@adirat.com (or merge into that series). > > I think the refresh functions should all be called non-interactively > as that will make it easier to pass arguments, and they should also > take the same arguments (though they can feel free to ignore them). Thank you for this, I'll pull it as is in my patch series and rebase my patches on top of it and send v4 tonight. Regards, Ionel > > Best wishes > > Mark > > > emacs/notmuch-hello.el | 2 +- > emacs/notmuch-lib.el | 22 -- > emacs/notmuch-show.el | 2 +- > emacs/notmuch-tree.el | 5 ++--- > emacs/notmuch.el | 2 +- > 5 files changed, 17 insertions(+), 16 deletions(-) > > diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el > index d582bff..97280ca 100644 > --- a/emacs/notmuch-hello.el > +++ b/emacs/notmuch-hello.el > @@ -604,7 +604,7 @@ with `notmuch-hello-query-counts'." > > (defimage notmuch-hello-logo ((:type png :file "notmuch-logo.png"))) > > -(defun notmuch-hello-update (&optional no-display) > +(defun notmuch-hello-update (&optional ignore no-display) >"Update the current notmuch view." >;; Lazy - rebuild everything. >(notmuch-hello no-display)) > diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el > index b2cdace..2d27e56 100644 > --- a/emacs/notmuch-lib.el > +++ b/emacs/notmuch-lib.el > @@ -410,23 +410,25 @@ of its command symbol." >(pop-to-buffer (help-buffer) > > (defvar notmuch-buffer-refresh-function nil > - "Function to call to refresh the current buffer.") > + "Function to call to refresh the current buffer. > + > +It will be called with two arguments: the first is the prefix > +argument when notmuch-refresh-this-buffer is called > +interactively, the second requests that the refresh call not > +display the buffer.") > (make-variable-buffer-local 'notmuch-buffer-refresh-function) > > -(defun notmuch-refresh-this-buffer () > +(defun notmuch-refresh-this-buffer (prefix) >"Refresh the current buffer." > - (interactive) > + (interactive "P") >(when notmuch-buffer-refresh-function > -(if (commandp notmuch-buffer-refresh-function) > - ;; Pass prefix argument, etc. > - (call-interactively notmuch-buffer-refresh-function) > - (funcall notmuch-buffer-refresh-function > +(funcall notmuch-buffer-refresh-function prefix))) > > -(defun notmuch-poll-and-refresh-this-buffer () > +(defun notmuch-poll-and-refresh-this-buffer (prefix) >"Invoke `notmuch-poll' to import mail, then refresh the current buffer." > - (interactive) > + (interactive "P") >(notmuch-poll) > - (notmuch-refresh-this-buffer)) > + (notmuch-refresh-this-buffer prefix)) > > (defun notmuch-prettify-subject (subject) >;; This function is used by `notmuch-search-process-filter' which > diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el > index f2487ab..1772d10 100644 > --- a/emacs/notmuch-show.el > +++ b/emacs/notmuch-show.el > @@ -1358,7 +1358,7 @@ This includes: > ;; Go to the previously open message. > (notmuch-show-goto-message current))) > > -(defun notmuch-show-refresh-view (&optional reset-state) > +(defun notmuch-show-refresh-view (&optional reset-state ignore) >"Refresh the current view. > > Refreshes the current view, observing changes in display > diff --git a/emacs/notmuch-tree.el b/emacs/notmuch-tree.el > index 1555812..c347712 100644 > --- a/emacs/notmuch-tree.el > +++ b/emacs/notmuch-tree.el > @@ -271,7 +271,6 @@ FUNC." > (define-key map "x" 'notmuch-tree-quit) > (define-key map "A" 'notmuch-tree-archive-thread) > (define-key map "a" 'notmuch-tree-archive-message-then-next) > -(define-key map "=" 'notmuch-tree-refresh-view) > (define-key map "z" 'notmuch-tree-to-tree) > (define-key map "n" 'notmuch-tree-next-matching-message) > (define-key map "p" 'notmuch-tree-prev-matching-message) > @@ -571,9 +570,9 @@ message will be \"unarchived\", i.e. the tag changes in >(when (window-live-p notmuch-tree-message-window) > (notmuch-tree-show-message-in))) > > -(defun notmuch-tree-refresh-view () > +(defun notmuch-tree-refresh-view (&rest ignore) >"Refresh view." > - (interactive) > + (interactive "P") >(let ((inhibit-read-only t) > (basic-query notmuch-tree-basic-query) > (query-context notmuch-tree-query-context) > diff --git a/emacs/notmuch.el b/emacs/notmuch.el > index 888672b..ee1bb54 100644 > --- a/emacs/notmuch.el > +++ b/emacs/notmuch.el > @@ -982,7 +982,7 @@ the configured default sort order." > (set-process-query-on-exit-flag proc nil > (run-hooks 'n
Re: [PATCH v3 3/4] emacs: add refresh buffer optional no-display arg
On Thu, 06 Oct 2016, Mark Walters wrote: > On Thu, 06 Oct 2016, Ioan-Adrian Ratiu wrote: >> Add an optional no-display arg to the generic buffer refresh function, >> notmuch-refresh-this-buffer, which works the same way as notmuch-hello >> mode's notmuch-hello-update no-display arg. >> >> The idea is for the generic notmuch-refresh-this-buffer to pass down >> this arg to the "mode-specific" refresh functions to be able to update >> buffers without bringing them to the foreground (if they are already >> foregrounded, i.e. displayed in a window, this has no effect). >> >> When updating a search buffer, notmuch currently always brings results >> in a window to the foreground. Perhaps counter-intuitively, we do not >> want this behaviour necessarily, because we want to auto-refresh any >> kind of search buffers, even those backgrounded (not displayed in any >> window/frame) from previous searches. This is why we add a no-display >> arg to notmuch-search. >> >> We do this to show which mails have appeard or dissapeared since the >> last search refresh and have this information updated in real time >> even when switching buffers. The ultimate goal of this is to have all >> notmuch buffers auto-refresh when the email client syncs (this function >> is added in the next commit). >> >> Signed-off-by: Ioan-Adrian Ratiu >> --- >> emacs/notmuch-lib.el | 10 +++--- >> emacs/notmuch.el | 17 - >> 2 files changed, 19 insertions(+), 8 deletions(-) >> >> diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el >> index b2cdace..af6a8f4 100644 >> --- a/emacs/notmuch-lib.el >> +++ b/emacs/notmuch-lib.el >> @@ -413,14 +413,18 @@ of its command symbol." >>"Function to call to refresh the current buffer.") >> (make-variable-buffer-local 'notmuch-buffer-refresh-function) >> >> -(defun notmuch-refresh-this-buffer () >> - "Refresh the current buffer." >> +(defun notmuch-refresh-this-buffer (&optional no-display) >> + "Refresh the current buffer. >> + >> +If no-display is non-nil do not try to bring the buffer to the >> +foreground. If the buffer is already foregrounded i.e. displayed >> +in a window on screen, no-display has no effect." >>(interactive) >>(when notmuch-buffer-refresh-function >> (if (commandp notmuch-buffer-refresh-function) >> ;; Pass prefix argument, etc. >> (call-interactively notmuch-buffer-refresh-function) >> - (funcall notmuch-buffer-refresh-function >> + (funcall notmuch-buffer-refresh-function no-display > > Hi > > I think this is very fragile -- it relies on the fact that the refresh > functions in show and tree mode are interactive, so get called but the > call-interactive line (which doesn't have the no-display argument) > whereas the refresh functions in hello and search mode are not > interactive so get called by the funcall line and so do get the > no-display argument. > > [In fact the notmuch-tree seems to bind "=" to notmuch-tree-refresh-view > whereas it could use the generic framework, which would mean it would > plausibly lose the interactive.] > > However, I am not sure what the correct solution is. I agree it's messy, but I don't know if adding the no-display arg to notmuch-show and notmuch-tree makes any sense because they don't force the buffer to become visible (like how notmuch-search/hello do by using switch-to-buffer). notmuch-show and tree assume the current buffer and don't change it's visibility. This is how to current code works. If it does make sense to add to them the no-display arg then we either have to make all refresh functions interactive or non-interactive. By doing this we can call all functions using a single code path and pass the no-display arg to all in one call. For notmuch-hello-update the situation looks pretty simple, it calls notmuch-hello which is interactive. It already has the no-display arg, we make it interactive and we're done. With notmuch-search-refresh-view the situation is similar. Then call-interactively all and pass no-display. Making them all non-interactive seems much harder and I think will likely break stuff. So I prefer 1. using the code as is or 2. making all interactive. Any other ideas, everyone? I'm open to any kind of sugestions on this. Ionel > > Best wishes > > Mark > > > >> >> (defun notmuch-poll-and-refresh-this-buffer () >>"Invoke `notmuch-poll' to import mail, then refresh the current buffer." >> diff --git a/emacs/notmuch.el b/emacs
Re: [PATCH v2 0/4] Add refresh all buffers functionality
On Thu, 06 Oct 2016, Daniel Kahn Gillmor wrote: > > > On Sun 2016-09-25 03:32:08 -0400, Tomi Ollila wrote: >> 2) then, minor commit message related comment: if there is going to be v3, >> in id:20160924200735.25425-2-...@adirat.com adi mentioned 'next patches' >> -- those are not patches (anymore) when commits are made, so it would be >> better to reword that sentence. If anythine else doesn't come up, simplest >> thing is to change the word to 'commits'. As said, this is minor thing, >> and we have worse things in commit messages; if there is no need to send >> v3, or the message change is forgotten then it may go in as it is now... > > Before these things are accepted into whatever you consider the > canonical git repo to be, while they're still patches floating around in > our various mailboxes, they aren't really "commits" either. I'd use > "changesets" as the generic term. > > I have v3 ready to send (will send it later today when I get home). I've reworded to use the word "commits". IMO this is just bikeshedding. It doesn't matter how we call these, either patches, commits, changesets are ok for me, just pleaso don't make me reword these too many times. > > --dkg ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v3 3/4] emacs: add refresh buffer optional no-display arg
Add an optional no-display arg to the generic buffer refresh function, notmuch-refresh-this-buffer, which works the same way as notmuch-hello mode's notmuch-hello-update no-display arg. The idea is for the generic notmuch-refresh-this-buffer to pass down this arg to the "mode-specific" refresh functions to be able to update buffers without bringing them to the foreground (if they are already foregrounded, i.e. displayed in a window, this has no effect). When updating a search buffer, notmuch currently always brings results in a window to the foreground. Perhaps counter-intuitively, we do not want this behaviour necessarily, because we want to auto-refresh any kind of search buffers, even those backgrounded (not displayed in any window/frame) from previous searches. This is why we add a no-display arg to notmuch-search. We do this to show which mails have appeard or dissapeared since the last search refresh and have this information updated in real time even when switching buffers. The ultimate goal of this is to have all notmuch buffers auto-refresh when the email client syncs (this function is added in the next commit). Signed-off-by: Ioan-Adrian Ratiu --- emacs/notmuch-lib.el | 10 +++--- emacs/notmuch.el | 17 - 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el index b2cdace..af6a8f4 100644 --- a/emacs/notmuch-lib.el +++ b/emacs/notmuch-lib.el @@ -413,14 +413,18 @@ of its command symbol." "Function to call to refresh the current buffer.") (make-variable-buffer-local 'notmuch-buffer-refresh-function) -(defun notmuch-refresh-this-buffer () - "Refresh the current buffer." +(defun notmuch-refresh-this-buffer (&optional no-display) + "Refresh the current buffer. + +If no-display is non-nil do not try to bring the buffer to the +foreground. If the buffer is already foregrounded i.e. displayed +in a window on screen, no-display has no effect." (interactive) (when notmuch-buffer-refresh-function (if (commandp notmuch-buffer-refresh-function) ;; Pass prefix argument, etc. (call-interactively notmuch-buffer-refresh-function) - (funcall notmuch-buffer-refresh-function + (funcall notmuch-buffer-refresh-function no-display (defun notmuch-poll-and-refresh-this-buffer () "Invoke `notmuch-poll' to import mail, then refresh the current buffer." diff --git a/emacs/notmuch.el b/emacs/notmuch.el index 586c84e..f3912d4 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -925,7 +925,7 @@ PROMPT is the string to prompt with." ;;;###autoload (put 'notmuch-search 'notmuch-doc "Search for messages.") -(defun notmuch-search (&optional query oldest-first target-thread target-line) +(defun notmuch-search (&optional query oldest-first target-thread target-line no-display) "Display threads matching QUERY in a notmuch-search buffer. If QUERY is nil, it is read interactively from the minibuffer. @@ -936,6 +936,9 @@ Other optional parameters are used as follows: current if it appears in the search results. TARGET-LINE: The line number to move to if the target thread does not appear in the search results. + NO-DISPLAY: Do not try to foreground the search results buffer. If it is + already foregrounded i.e. displayed in a window, this has no + effect, meaning the buffer will remain visible. When called interactively, this will prompt for a query and use the configured default sort order." @@ -949,7 +952,9 @@ the configured default sort order." (let* ((query (or query (notmuch-read-query "Notmuch search: "))) (buffer (get-buffer-create (notmuch-search-buffer-title query -(switch-to-buffer buffer) +(if no-display + (set-buffer buffer) + (switch-to-buffer buffer)) (notmuch-search-mode) ;; Don't track undo information for this buffer (set 'buffer-undo-list t) @@ -982,14 +987,16 @@ the configured default sort order." (set-process-query-on-exit-flag proc nil (run-hooks 'notmuch-search-hook))) -(defun notmuch-search-refresh-view () +(defun notmuch-search-refresh-view (&optional no-display) "Refresh the current view. Erases the current buffer and runs a new search with the same query string as the current search. If the current thread is in the new search results, then point will be placed on the same thread. Otherwise, point will be moved to attempt to be in the -same relative position within the new buffer." +same relative position within the new buffer. If no-display is +non-nil, the search results buffer will not be foregrounded, if +it already is displayed in a window, then no-display has no effect." (let ((target-line (line-number-at-pos))
[PATCH v3 1/4] emacs: reuse buffer when refreshing searches
There's no reason to completely kill a buffer while refreshing its search results because the buffer name is constant between refreshes (based on the search query), only its contents may change and notmuch search kills all local variables, so it's safe to reuse. Reusing the same buffer also makes it possible to do things like refreshing a buffer which is not focused or even not shown in any window - this will be used in the next commits to add auto-refresh capabilities to all existing notmuch buffers + a function to call after syncing mail to refresh everything. Signed-off-by: Ioan-Adrian Ratiu --- emacs/notmuch.el | 8 +--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/emacs/notmuch.el b/emacs/notmuch.el index 888672b..586c84e 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -985,7 +985,7 @@ the configured default sort order." (defun notmuch-search-refresh-view () "Refresh the current view. -Kills the current buffer and runs a new search with the same +Erases the current buffer and runs a new search with the same query string as the current search. If the current thread is in the new search results, then point will be placed on the same thread. Otherwise, point will be moved to attempt to be in the @@ -993,8 +993,10 @@ same relative position within the new buffer." (let ((target-line (line-number-at-pos)) (oldest-first notmuch-search-oldest-first) (target-thread (notmuch-search-find-thread-id 'bare)) - (query notmuch-search-query-string)) -(notmuch-bury-or-kill-this-buffer) + (query notmuch-search-query-string) + (inhibit-read-only t)) +(remove-overlays) +(erase-buffer) (notmuch-search query oldest-first target-thread target-line) (goto-char (point-min -- 2.10.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v3 2/4] emacs: notmuch-show: refresh all windows showing a buffer
This updates all windows displaying a notmuch-show buffer when the buffer refresh function is called. Each window displaying a notmuch-show buffer has its own currently displayed messaged based on the (point) location. Store the state of all displayed windows when refreshing a notmuch-show buffer and re-apply the current shown message for all windows. Signed-off-by: Ioan-Adrian Ratiu --- emacs/notmuch-show.el | 19 +-- 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el index f2487ab..ac7eb77 100644 --- a/emacs/notmuch-show.el +++ b/emacs/notmuch-show.el @@ -1317,8 +1317,13 @@ If no messages match the query return NIL." This includes: - the list of open messages, - - the current message." - (list (notmuch-show-get-message-id) (notmuch-show-get-message-ids-for-open-messages))) + - the combination of current message id with/for each visible window." + (let* ((win-list (get-buffer-window-list (current-buffer) nil t)) +(win-id-combo (mapcar (lambda (win) +(with-selected-window win + (list win (notmuch-show-get-message-id + win-list))) +(list win-id-combo (notmuch-show-get-message-ids-for-open-messages (defun notmuch-show-get-query () "Return the current query in this show buffer" @@ -1345,8 +1350,8 @@ This includes: This includes: - opening the messages previously opened, - closing all other messages, - - moving to the correct current message." - (let ((current (car state)) + - moving to the correct current message in every displayed window." + (let ((win-msg-alist (car state)) (open (cadr state))) ;; Open those that were open. @@ -1355,8 +1360,10 @@ This includes: (member (notmuch-show-get-message-id) open)) until (not (notmuch-show-goto-message-next))) -;; Go to the previously open message. -(notmuch-show-goto-message current))) +(dolist (win-msg-pair win-msg-alist) + (with-selected-window (car win-msg-pair) + ;; Go to the previously open message in this window + (notmuch-show-goto-message (cadr win-msg-pair)) (defun notmuch-show-refresh-view (&optional reset-state) "Refresh the current view. -- 2.10.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v3 0/4] Add refresh all buffers functionality
Changes since v2 (based on Mark & Tomi's feedback) * fixed a missing parameter in the get-buffer-window-list call in notmuch-show-capture-state * added a (remove-overlays) call before (erase-buffer) in notmuch-search-refresh-view * replaced the (string-prefix-p "notmuch") in notmuch-refresh-all-buffers with explicit list * reworded the commit messages to replace the word patches with commits What I intentionally did not touch in v3 is the notmuch-show call to generate-new-buffer-name which creates now buffers when opening threads from notmuch-search; that fix should go in another patch series. Ioan-Adrian Ratiu (4): emacs: reuse buffer when refreshing searches emacs: notmuch-show: refresh all windows showing a buffer emacs: add refresh buffer optional no-display arg emacs: notmuch-lib: add refresh all buffers function emacs/notmuch-lib.el | 25 ++--- emacs/notmuch-show.el | 19 +-- emacs/notmuch.el | 25 + 3 files changed, 52 insertions(+), 17 deletions(-) -- 2.10.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v3 4/4] emacs: notmuch-lib: add refresh all buffers function
notmuch-refresh-all-buffers calls each buffer's major mode specific refresh function using the generic notmuch-refresh-this-buffer function. It is very useful because by passing a non-nil arg to the buffer specific refresh functions it refreshes all notmuch buffers in the background and this again is very useful when doing periodic timer-based mail syncing. Signed-off-by: Ioan-Adrian Ratiu --- emacs/notmuch-lib.el | 15 +++ 1 file changed, 15 insertions(+) diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el index af6a8f4..01733a2 100644 --- a/emacs/notmuch-lib.el +++ b/emacs/notmuch-lib.el @@ -432,6 +432,21 @@ in a window on screen, no-display has no effect." (notmuch-poll) (notmuch-refresh-this-buffer)) +(defun notmuch-refresh-all-buffers (&optional no-display) + "Invoke `notmuch-refresh-this-buffer' on all notmuch major-mode buffers. + +If no-display is non-nil all buffers are silently refreshed, i.e. they are +not foregrounded even if not displayed in any window. If no-display is nil +then each buffer's mode-specific refresh function uses its default behaviour." + (dolist (buffer (buffer-list)) +(let ((buffer-mode (buffer-local-value 'major-mode buffer))) + (when (memq buffer-mode '(notmuch-show-mode + notmuch-tree-mode + notmuch-search-mode + notmuch-hello-mode)) + (with-current-buffer buffer + (notmuch-refresh-this-buffer no-display)) + (defun notmuch-prettify-subject (subject) ;; This function is used by `notmuch-search-process-filter' which ;; requires that we not disrupt its' matching state. -- 2.10.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH v2 3/4] emacs: add refresh buffer optional no-display arg
On Sun, 25 Sep 2016, Mark Walters wrote: > On Sat, 24 Sep 2016, Ioan-Adrian Ratiu wrote: >> Add an optional no-display arg to the generic buffer refresh function, >> notmuch-refresh-this-buffer, which works the same way as notmuch-hello >> mode's notmuch-hello-update no-display arg. >> >> The idea is for the generic notmuch-refresh-this-buffer to pass down >> this arg to the "mode-specific" refresh functions to be able to update >> buffers without bringing them to the foreground (if they are already >> foregrounded, i.e. displayed in a window, this has no effect). >> >> When updating a search buffer, notmuch currently always brings results >> in a window to the foreground. Perhaps counter-intuitively, we do not >> want this behaviour necessarily, because we want to auto-refresh any >> kind of search buffers, even those backgrounded (not displayed in any >> window/frame) from previous searches. This is why we add a no-display >> arg to notmuch-search. >> >> We do this to show which mails have appeard or dissapeared since the >> last search refresh and have this information updated in real time >> even when switching buffers. The ultimate goal of this is to have all >> notmuch buffers auto-refresh when the email client syncs (this function >> is added in the next patch). >> >> Signed-off-by: Ioan-Adrian Ratiu >> --- >> emacs/notmuch-lib.el | 10 +++--- >> emacs/notmuch.el | 17 - >> 2 files changed, 19 insertions(+), 8 deletions(-) > > I feel I am missing something here: why do you not need to change > notmuch-show-refresh-view and notmuch-tree-refresh-view as well? Note > notmuch-show-refresh-view already has an optional argument. Sorry I've missed this mail on my previous reading. Only when refreshing notmuch-search (switch-to-buffer) is called which brings a buffer up in a window, so adding the optional argument to notmuch show or tree doesn't make sense because the buffer is not forced to be visible like in notmuch-search's refresh view case. Of course instead of adding the no-display arg to notmuch-search's refresh function we could remove the switch-to-buffer call and make it behave like notmuch-show or tree, but this changes the user-visible behaviour and I guess people expect the notmuch-search buffer to be made visible by default. Also notmuch-search creates new buffers based on the serach queries so it doesn't make sense to reuse them and that's why we're forcing them to be displayed by default, right? What do you think about this? Do you with me to change the patch? Ionel > > Best wishes > > Mark > >> >> diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el >> index 2f015b0..6618365 100644 >> --- a/emacs/notmuch-lib.el >> +++ b/emacs/notmuch-lib.el >> @@ -409,14 +409,18 @@ of its command symbol." >>"Function to call to refresh the current buffer.") >> (make-variable-buffer-local 'notmuch-buffer-refresh-function) >> >> -(defun notmuch-refresh-this-buffer () >> - "Refresh the current buffer." >> +(defun notmuch-refresh-this-buffer (&optional no-display) >> + "Refresh the current buffer. >> + >> +If no-display is non-nil do not try to bring the buffer to the >> +foreground. If the buffer is already foregrounded i.e. displayed >> +in a window on screen, no-display has no effect." >>(interactive) >>(when notmuch-buffer-refresh-function >> (if (commandp notmuch-buffer-refresh-function) >> ;; Pass prefix argument, etc. >> (call-interactively notmuch-buffer-refresh-function) >> - (funcall notmuch-buffer-refresh-function >> + (funcall notmuch-buffer-refresh-function no-display >> >> (defun notmuch-poll-and-refresh-this-buffer () >>"Invoke `notmuch-poll' to import mail, then refresh the current buffer." >> diff --git a/emacs/notmuch.el b/emacs/notmuch.el >> index 05687b7..ec7a242 100644 >> --- a/emacs/notmuch.el >> +++ b/emacs/notmuch.el >> @@ -924,7 +924,7 @@ PROMPT is the string to prompt with." >> >> ;;;###autoload >> (put 'notmuch-search 'notmuch-doc "Search for messages.") >> -(defun notmuch-search (&optional query oldest-first target-thread >> target-line) >> +(defun notmuch-search (&optional query oldest-first target-thread >> target-line no-display) >>"Display threads matching QUERY in a notmuch-search buffer. >> >> If QUERY is nil, it is read interactively from the minibuffer. >> @@ -935,6 +935,9 @@ Othe
Re: [PATCH v2 2/4] emacs: notmuch-show: refresh all windows showing a buffer
Hi Mark and thank you again for the great feedback. On Sun, 25 Sep 2016, Mark Walters wrote: > On Sat, 24 Sep 2016, Ioan-Adrian Ratiu wrote: >> This updates all windows displaying a notmuch-show buffer when the >> buffer refresh function is called. >> >> Each window displaying a notmuch-show buffer has its own currently >> displayed messaged based on the (point) location. Store the state >> of all displayed windows when refreshing a notmuch-show buffer and >> re-apply the current shown message for all windows. >> >> Signed-off-by: Ioan-Adrian Ratiu >> --- >> emacs/notmuch-show.el | 19 +-- >> 1 file changed, 13 insertions(+), 6 deletions(-) >> >> diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el >> index 641398d..c39065f 100644 >> --- a/emacs/notmuch-show.el >> +++ b/emacs/notmuch-show.el >> @@ -1317,8 +1317,13 @@ If no messages match the query return NIL." >> >> This includes: >> - the list of open messages, >> - - the current message." >> - (list (notmuch-show-get-message-id) >> (notmuch-show-get-message-ids-for-open-messages))) >> + - the combination of current message id with/for each visible window." >> + (let* ((win-list (get-buffer-window-list (current-buffer) t)) > > Should this be (get-buffer-window-list (current-buffer) nil t)) ? I am > assuming you don't care about the minibuffer, but do want all frames? Yes, exactly. I've forgotten that nil arg. Great catch. > >> + (win-id-combo (mapcar (lambda (win) >> + (with-selected-window win >> + (list win (notmuch-show-get-message-id >> + win-list))) >> +(list win-id-combo (notmuch-show-get-message-ids-for-open-messages > > Before I make a comment here I should stay I rather unsure about how > emacs deals with point when there are multiple windows. I think each > window has a value for point for each buffer regardless of whether that > buffer is currently displayed in that window. Based on all the documentation I could find and code/testing I've done: 1. Each emacs buffer is displayed in a window or not displayed at all. 2. Each window has only one point value which it always displays if the window is visible. 3. Each buffer has a point value which is used only when the buffer is not displayed in any window (used as storage for restoring windows). 4. A window's point value is restored from the buffer point storage value only when the first window switches to a previously undisplayed buffer (so buffer point overwrites window point) 5. A buffer's point value is written with the window point value when the last window displaying said buffer switches to another buffer (so window point overwrites buffer point) 6. When a single window displays a buffer, the window's point and the buffer point are identical (they are kept in sync by the same mechanism above at 5.) I hope I explained this inteligibely :) Based on these rules my code works (of course it can always contain bugs, gotta squash them all). > > If I understand the code correctly this only resets point for the > windows currently displaying buffer. > > I note that this is better than the current refresh-single-buffer code: > however, if you actually want it running on a timer in the background, > rather then you may require better behaviour. As it is improvement on > what we currently do I leave this to you to decide. The problem we have to solve here is that all point values for all windows displaying current-buffer are lost the moment we call erase-buffer because when each window displays an empty buffer after erase, the point is reset, so we need to store them for all windows before erasing the underlying buffer (if we want to restore all windows). The current code in origin/master does not bother with this logic because it only restores one window, so it needs only one current message id (based on point) in the state. What I do is add to state all current messages (points) for every window so we can restore them when applying the state after erase-buffer. We only need to do this for each (window current-message) combination in the state, the other list stored in the state, the open/closed messages list per buffer and identical to all windows. If you have any suggestions on how to modify the commit message to make all of this clearer, they are very welcome :) I usually spend hours figuring out all this logic and it's very hard for me to put it in simple, understandable and concise wording. > > Best wishes > > Mark > > >> (defun notmuch-show-get-query () >>"Return the current query in this show buffer&qu
Re: [PATCH v2 0/4] Add refresh all buffers functionality
On Sat, 24 Sep 2016, David Bremner wrote: > Ioan-Adrian Ratiu writes: > >> On Sat, 24 Sep 2016, Ioan-Adrian Ratiu wrote: >>> Argh, so right after I posted this I found a bug: for every new window >>> in which you open the same notmuch-show buffer it creates a new buffer. >>> >>> For example if from notmuch-search you open a thread "hello" in multiple >>> windows, each window will show a different "hello<1>" "hello<2>" etc >>> buffer instead of showing a single "hello" buffer for all windows. >> >> This is really weird. I'm experiencing this bug even without my patches >> so it's not a fault in my code. I've tried with both emacs 25.1 and the >> latest emacs git rev, does anyone else experience this behaviour? >> >> Am I missing something, is this an expected behaviour and not a bug? > > I don't (yet) have an opinion on whether this is a bug, but I can > confirm the behaviour exists, e.g. using devel/try-emacs-mua -Q in emacs > 24.5.1 It's caused by the generate-new-buffer-name call in notmuch-show(), it's been there since cca 2010 by 9bee20aed (notmuch.el: Make notmuch-show buffer name first subject...) I don't quite understand why generate-new-buffer-name is called at all there. What's wrong with the existing buffer names and why do we want to create others? :) ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH v2 0/4] Add refresh all buffers functionality
On Sat, 24 Sep 2016, Ioan-Adrian Ratiu wrote: > Argh, so right after I posted this I found a bug: for every new window > in which you open the same notmuch-show buffer it creates a new buffer. > > For example if from notmuch-search you open a thread "hello" in multiple > windows, each window will show a different "hello<1>" "hello<2>" etc > buffer instead of showing a single "hello" buffer for all windows. This is really weird. I'm experiencing this bug even without my patches so it's not a fault in my code. I've tried with both emacs 25.1 and the latest emacs git rev, does anyone else experience this behaviour? Am I missing something, is this an expected behaviour and not a bug? > > I'm aware of this issue and I'll fix it in v3, however please if you > have time & feedback for v2 I'd greatly appreciate it. > > Best wishes, > Ionel > > On Sat, 24 Sep 2016, Ioan-Adrian Ratiu wrote: >> Changes since v1 (thank you Mark for your feedback): >> * Major rewrite/simplification of the notmuch-show refresh patch to >> support simultaneous refreshing multiple windows displaying a buffer >> * Removed the notmuch-show-message-adjust () patch because it's not >> needed after the above rewrite >> * Rewrote notmuch-refresh-all-buffers () to use dolist instead of >> while loop >> * Minor commit message/metadata improvements >> >> This patch series adds a function to refresh all buffers, including an >> option to silently refresh them in the background i.e. to not show the >> newly refreshed buffer in any window. >> >> This is very useful for asynchronously updating all buffers when new >> mail arrives, using logic similar to the following (it's what I use): >> >> (setq process-connection-type nil) >> >> (defun done-index-sentinel (process event) >> (notmuch-refresh-all-buffers t) >> (message "Mail sync complete")) >> >> (defun done-sync-sentinel (process event) >> (message "Indexing mail using notmuch") >> (set-process-sentinel (start-process "notmuch" nil "notmuch" "new") >> 'done-index-sentinel)) >> >> (defun run-mail-sync () >> (message "Syncing mail in background") >> (set-process-sentinel (start-process "mbsync" nil "mbsync" "gmail") >>'done-sync-sentinel)) >> >> (run-with-idle-timer 600 nil 'run-mail-sync) >> >> Ioan-Adrian Ratiu (4): >> emacs: reuse buffer when refreshing searches >> emacs: notmuch-show: refresh all windows showing a buffer >> emacs: add refresh buffer optional no-display arg >> emacs: notmuch-lib: add refresh all buffers function >> >> emacs/notmuch-lib.el | 22 +++--- >> emacs/notmuch-show.el | 19 +-- >> emacs/notmuch.el | 24 >> 3 files changed, 48 insertions(+), 17 deletions(-) >> >> -- >> 2.10.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH v2 0/4] Add refresh all buffers functionality
Argh, so right after I posted this I found a bug: for every new window in which you open the same notmuch-show buffer it creates a new buffer. For example if from notmuch-search you open a thread "hello" in multiple windows, each window will show a different "hello<1>" "hello<2>" etc buffer instead of showing a single "hello" buffer for all windows. I'm aware of this issue and I'll fix it in v3, however please if you have time & feedback for v2 I'd greatly appreciate it. Best wishes, Ionel On Sat, 24 Sep 2016, Ioan-Adrian Ratiu wrote: > Changes since v1 (thank you Mark for your feedback): > * Major rewrite/simplification of the notmuch-show refresh patch to > support simultaneous refreshing multiple windows displaying a buffer > * Removed the notmuch-show-message-adjust () patch because it's not > needed after the above rewrite > * Rewrote notmuch-refresh-all-buffers () to use dolist instead of > while loop > * Minor commit message/metadata improvements > > This patch series adds a function to refresh all buffers, including an > option to silently refresh them in the background i.e. to not show the > newly refreshed buffer in any window. > > This is very useful for asynchronously updating all buffers when new > mail arrives, using logic similar to the following (it's what I use): > > (setq process-connection-type nil) > > (defun done-index-sentinel (process event) > (notmuch-refresh-all-buffers t) > (message "Mail sync complete")) > > (defun done-sync-sentinel (process event) > (message "Indexing mail using notmuch") > (set-process-sentinel (start-process "notmuch" nil "notmuch" "new") > 'done-index-sentinel)) > > (defun run-mail-sync () > (message "Syncing mail in background") > (set-process-sentinel (start-process "mbsync" nil "mbsync" "gmail") > 'done-sync-sentinel)) > > (run-with-idle-timer 600 nil 'run-mail-sync) > > Ioan-Adrian Ratiu (4): > emacs: reuse buffer when refreshing searches > emacs: notmuch-show: refresh all windows showing a buffer > emacs: add refresh buffer optional no-display arg > emacs: notmuch-lib: add refresh all buffers function > > emacs/notmuch-lib.el | 22 +++--- > emacs/notmuch-show.el | 19 +-- > emacs/notmuch.el | 24 > 3 files changed, 48 insertions(+), 17 deletions(-) > > -- > 2.10.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 4/4] emacs: notmuch-lib: add refresh all buffers function
notmuch-refresh-all-buffers calls each buffer's major mode specific refresh function using the generic notmuch-refresh-this-buffer function. It is very useful because by passing a non-nil arg to the buffer specific refresh functions it refreshes all notmuch buffers in the background and this again is very useful when doing periodic timer-based mail syncing. Signed-off-by: Ioan-Adrian Ratiu --- emacs/notmuch-lib.el | 12 1 file changed, 12 insertions(+) diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el index 6618365..72fee4d 100644 --- a/emacs/notmuch-lib.el +++ b/emacs/notmuch-lib.el @@ -428,6 +428,18 @@ in a window on screen, no-display has no effect." (notmuch-poll) (notmuch-refresh-this-buffer)) +(defun notmuch-refresh-all-buffers (&optional no-display) + "Invoke `notmuch-refresh-this-buffer' on all notmuch major-mode buffers. + +If no-display is non-nil all buffers are silently refreshed, i.e. they are +not foregrounded even if not displayed in any window. If no-display is nil +then each buffer's mode-specific refresh function uses its default behaviour." + (dolist (buffer (buffer-list)) +(let ((buffer-mode (buffer-local-value 'major-mode buffer))) + (when (string-prefix-p "notmuch" (format "%s" buffer-mode)) + (with-current-buffer buffer + (notmuch-refresh-this-buffer no-display)) + (defun notmuch-prettify-subject (subject) ;; This function is used by `notmuch-search-process-filter' which ;; requires that we not disrupt its' matching state. -- 2.10.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 1/4] emacs: reuse buffer when refreshing searches
There's no reason to completely kill a buffer while refreshing its search results because the buffer name is constant between refreshes (based on the search query), only its contents may change and notmuch search kills all local variables, so it's safe to reuse. Reusing the same buffer also makes it possible to do things like refreshing a buffer which is not focused or even not shown in any window - this will be used in the next patches to add auto-refresh capabilities to all existing notmuch buffers + a function to call after syncing mail to refresh everything. Signed-off-by: Ioan-Adrian Ratiu --- emacs/notmuch.el | 7 --- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/emacs/notmuch.el b/emacs/notmuch.el index 8e14692..05687b7 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -984,7 +984,7 @@ the configured default sort order." (defun notmuch-search-refresh-view () "Refresh the current view. -Kills the current buffer and runs a new search with the same +Erases the current buffer and runs a new search with the same query string as the current search. If the current thread is in the new search results, then point will be placed on the same thread. Otherwise, point will be moved to attempt to be in the @@ -992,8 +992,9 @@ same relative position within the new buffer." (let ((target-line (line-number-at-pos)) (oldest-first notmuch-search-oldest-first) (target-thread (notmuch-search-find-thread-id 'bare)) - (query notmuch-search-query-string)) -(notmuch-bury-or-kill-this-buffer) + (query notmuch-search-query-string) + (inhibit-read-only t)) +(erase-buffer) (notmuch-search query oldest-first target-thread target-line) (goto-char (point-min -- 2.10.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 3/4] emacs: add refresh buffer optional no-display arg
Add an optional no-display arg to the generic buffer refresh function, notmuch-refresh-this-buffer, which works the same way as notmuch-hello mode's notmuch-hello-update no-display arg. The idea is for the generic notmuch-refresh-this-buffer to pass down this arg to the "mode-specific" refresh functions to be able to update buffers without bringing them to the foreground (if they are already foregrounded, i.e. displayed in a window, this has no effect). When updating a search buffer, notmuch currently always brings results in a window to the foreground. Perhaps counter-intuitively, we do not want this behaviour necessarily, because we want to auto-refresh any kind of search buffers, even those backgrounded (not displayed in any window/frame) from previous searches. This is why we add a no-display arg to notmuch-search. We do this to show which mails have appeard or dissapeared since the last search refresh and have this information updated in real time even when switching buffers. The ultimate goal of this is to have all notmuch buffers auto-refresh when the email client syncs (this function is added in the next patch). Signed-off-by: Ioan-Adrian Ratiu --- emacs/notmuch-lib.el | 10 +++--- emacs/notmuch.el | 17 - 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el index 2f015b0..6618365 100644 --- a/emacs/notmuch-lib.el +++ b/emacs/notmuch-lib.el @@ -409,14 +409,18 @@ of its command symbol." "Function to call to refresh the current buffer.") (make-variable-buffer-local 'notmuch-buffer-refresh-function) -(defun notmuch-refresh-this-buffer () - "Refresh the current buffer." +(defun notmuch-refresh-this-buffer (&optional no-display) + "Refresh the current buffer. + +If no-display is non-nil do not try to bring the buffer to the +foreground. If the buffer is already foregrounded i.e. displayed +in a window on screen, no-display has no effect." (interactive) (when notmuch-buffer-refresh-function (if (commandp notmuch-buffer-refresh-function) ;; Pass prefix argument, etc. (call-interactively notmuch-buffer-refresh-function) - (funcall notmuch-buffer-refresh-function + (funcall notmuch-buffer-refresh-function no-display (defun notmuch-poll-and-refresh-this-buffer () "Invoke `notmuch-poll' to import mail, then refresh the current buffer." diff --git a/emacs/notmuch.el b/emacs/notmuch.el index 05687b7..ec7a242 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -924,7 +924,7 @@ PROMPT is the string to prompt with." ;;;###autoload (put 'notmuch-search 'notmuch-doc "Search for messages.") -(defun notmuch-search (&optional query oldest-first target-thread target-line) +(defun notmuch-search (&optional query oldest-first target-thread target-line no-display) "Display threads matching QUERY in a notmuch-search buffer. If QUERY is nil, it is read interactively from the minibuffer. @@ -935,6 +935,9 @@ Other optional parameters are used as follows: current if it appears in the search results. TARGET-LINE: The line number to move to if the target thread does not appear in the search results. + NO-DISPLAY: Do not try to foreground the search results buffer. If it is + already foregrounded i.e. displayed in a window, this has no + effect, meaning the buffer will remain visible. When called interactively, this will prompt for a query and use the configured default sort order." @@ -948,7 +951,9 @@ the configured default sort order." (let* ((query (or query (notmuch-read-query "Notmuch search: "))) (buffer (get-buffer-create (notmuch-search-buffer-title query -(switch-to-buffer buffer) +(if no-display + (set-buffer buffer) + (switch-to-buffer buffer)) (notmuch-search-mode) ;; Don't track undo information for this buffer (set 'buffer-undo-list t) @@ -981,21 +986,23 @@ the configured default sort order." (set-process-query-on-exit-flag proc nil (run-hooks 'notmuch-search-hook))) -(defun notmuch-search-refresh-view () +(defun notmuch-search-refresh-view (&optional no-display) "Refresh the current view. Erases the current buffer and runs a new search with the same query string as the current search. If the current thread is in the new search results, then point will be placed on the same thread. Otherwise, point will be moved to attempt to be in the -same relative position within the new buffer." +same relative position within the new buffer. If no-display is +non-nil, the search results buffer will not be foregrounded, if +it already is displayed in a window, then no-display has no effect." (let ((target-line (line-number-at-pos)) (oldes
[PATCH v2 2/4] emacs: notmuch-show: refresh all windows showing a buffer
This updates all windows displaying a notmuch-show buffer when the buffer refresh function is called. Each window displaying a notmuch-show buffer has its own currently displayed messaged based on the (point) location. Store the state of all displayed windows when refreshing a notmuch-show buffer and re-apply the current shown message for all windows. Signed-off-by: Ioan-Adrian Ratiu --- emacs/notmuch-show.el | 19 +-- 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el index 641398d..c39065f 100644 --- a/emacs/notmuch-show.el +++ b/emacs/notmuch-show.el @@ -1317,8 +1317,13 @@ If no messages match the query return NIL." This includes: - the list of open messages, - - the current message." - (list (notmuch-show-get-message-id) (notmuch-show-get-message-ids-for-open-messages))) + - the combination of current message id with/for each visible window." + (let* ((win-list (get-buffer-window-list (current-buffer) t)) +(win-id-combo (mapcar (lambda (win) +(with-selected-window win + (list win (notmuch-show-get-message-id + win-list))) +(list win-id-combo (notmuch-show-get-message-ids-for-open-messages (defun notmuch-show-get-query () "Return the current query in this show buffer" @@ -1345,8 +1350,8 @@ This includes: This includes: - opening the messages previously opened, - closing all other messages, - - moving to the correct current message." - (let ((current (car state)) + - moving to the correct current message in every displayed window." + (let ((win-msg-alist (car state)) (open (cadr state))) ;; Open those that were open. @@ -1355,8 +1360,10 @@ This includes: (member (notmuch-show-get-message-id) open)) until (not (notmuch-show-goto-message-next))) -;; Go to the previously open message. -(notmuch-show-goto-message current))) +(dolist (win-msg-pair win-msg-alist) + (with-selected-window (car win-msg-pair) + ;; Go to the previously open message in this window + (notmuch-show-goto-message (cadr win-msg-pair)) (defun notmuch-show-refresh-view (&optional reset-state) "Refresh the current view. -- 2.10.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 0/4] Add refresh all buffers functionality
Changes since v1 (thank you Mark for your feedback): * Major rewrite/simplification of the notmuch-show refresh patch to support simultaneous refreshing multiple windows displaying a buffer * Removed the notmuch-show-message-adjust () patch because it's not needed after the above rewrite * Rewrote notmuch-refresh-all-buffers () to use dolist instead of while loop * Minor commit message/metadata improvements This patch series adds a function to refresh all buffers, including an option to silently refresh them in the background i.e. to not show the newly refreshed buffer in any window. This is very useful for asynchronously updating all buffers when new mail arrives, using logic similar to the following (it's what I use): (setq process-connection-type nil) (defun done-index-sentinel (process event) (notmuch-refresh-all-buffers t) (message "Mail sync complete")) (defun done-sync-sentinel (process event) (message "Indexing mail using notmuch") (set-process-sentinel (start-process "notmuch" nil "notmuch" "new") 'done-index-sentinel)) (defun run-mail-sync () (message "Syncing mail in background") (set-process-sentinel (start-process "mbsync" nil "mbsync" "gmail") 'done-sync-sentinel)) (run-with-idle-timer 600 nil 'run-mail-sync) Ioan-Adrian Ratiu (4): emacs: reuse buffer when refreshing searches emacs: notmuch-show: refresh all windows showing a buffer emacs: add refresh buffer optional no-display arg emacs: notmuch-lib: add refresh all buffers function emacs/notmuch-lib.el | 22 +++--- emacs/notmuch-show.el | 19 +-- emacs/notmuch.el | 24 3 files changed, 48 insertions(+), 17 deletions(-) -- 2.10.0 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH] emacs: add tag jump menu
On Sun, 18 Sep 2016, Jani Nikula wrote: > On Sun, 18 Sep 2016, Ioan-Adrian Ratiu wrote: >> On Sun, 18 Sep 2016, Mark Walters wrote: >>> On Sun, 18 Sep 2016, Ioan-Adrian Ratiu wrote: >>>> Hi >>>> >>>> I have implemented something similar in my tree and I really like the >>>> idea. I have one issue though. >>>> >>>> On Sat, 17 Sep 2016, Mark Walters wrote: >>>>> Add a "jump" style menu for doing tagging operations. >>>>> --- >>>>> >>>>> Jani suggested something like this on irc today. This is a first cut >>>>> to see if people like it. By default the tagging jump menu is bound to >>>>> k (which works in search/show/tree mode), and has the following options >>>>> >>>>> a (Archive) -inbox -unread >>>>> u (Mark Read) -unread >>>>> d (Delete) +deleted >>>>> >>>>> If you do ctrl-u k the it will do the reverse operation. >>>> >>>> I know C-u is default emacs behaviour but I find very cumbersone to do >>>> C-u for unapplying the tag. What I do and want is to simply apply the >>>> tag when pressing "d" then unapply it when pressing "d" again if the >>>> mail/thread already contains the deleted tag (basically it's a toggle). >>> >>> I agree that C-u is a little cumbersome -- I think I would be happy for >>> a toggle for single messages (with a single tag change), but for >>> multiple messages like a thread I think it would be very unclear what it >>> was doing. >> >> My workflow with the kind of code shown above is as follows: >> >> If in notmuch-search then pressing 'd' "deletes" everything selected, >> including multiple messages in a region and if a thread was selected in >> that region then the entire thread is deleted. > > You are not addressing the toggle case where some of the messages in > those threads have the tag, and some do not. How should notmuch know > whether you want to add or remove the deleted tag? In my usecase when I toggle a tag to a thread in notmuch-search, it is applied to all mails, obviously, and if I want to do per-mail tagging I go into notmuch-show. To answer your question I'll give an example: Suppose I have just some subset of a thread's mails tagged "+inbox" and I toggle pressing "i", then all the mails in that thread are marked "+inbox" and if I toggle again all are "-inbox". So removing a tag for a subset of the thread's mails is just a matter of toggling twice. I can always go in notmuch-show and do per-email tagging using toggles, say after I removed the "inbox" tag in the example above by toggling twice, I open the thread in notmuch-show and press "i" to toggle the tag back on any specific email. I'm using this for some time and it works really well :) > >> IMO this is the simplest and the clearest workflow. > > While working on Notmuch, one of the main lessons I've learned is that > *everyone* has their own, personal email workflow. We need to try to > give people discoverable and intuitive *mechanisms* on dealing with > email, and let people build their own workflows that suit them. Yes, I agree that it's better to provide mechanism than policy because workflows are so different. Tag toggling is just another mechanism, it can be customized, for example, when you toggle on a thread of which some mails already are tagged, should it add or remove that tag? This can be configured through a variable. > > (That said, I always try to encourage people to rethink their workflows > when switching to Notmuch. But it's still *their* workflow.) > > > BR, > Jani. ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH] emacs: add tag jump menu
On Sun, 18 Sep 2016, Mark Walters wrote: > On Sun, 18 Sep 2016, Ioan-Adrian Ratiu wrote: >> Hi >> >> I have implemented something similar in my tree and I really like the >> idea. I have one issue though. >> >> On Sat, 17 Sep 2016, Mark Walters wrote: >>> Add a "jump" style menu for doing tagging operations. >>> --- >>> >>> Jani suggested something like this on irc today. This is a first cut >>> to see if people like it. By default the tagging jump menu is bound to >>> k (which works in search/show/tree mode), and has the following options >>> >>> a (Archive) -inbox -unread >>> u (Mark Read) -unread >>> d (Delete) +deleted >>> >>> If you do ctrl-u k the it will do the reverse operation. >> >> I know C-u is default emacs behaviour but I find very cumbersone to do >> C-u for unapplying the tag. What I do and want is to simply apply the >> tag when pressing "d" then unapply it when pressing "d" again if the >> mail/thread already contains the deleted tag (basically it's a toggle). > > I agree that C-u is a little cumbersome -- I think I would be happy for > a toggle for single messages (with a single tag change), but for > multiple messages like a thread I think it would be very unclear what it > was doing. My workflow with the kind of code shown above is as follows: If in notmuch-search then pressing 'd' "deletes" everything selected, including multiple messages in a region and if a thread was selected in that region then the entire thread is deleted. If I want to delete just a single message from the thread, I go to notmuch-show where 'd' operates on a single message, never on a thread. If an entire thread was already marked "deleted" and I want to undelete a single message from that thread, I go to notmuch-show and press 'd' on it. IMO this is the simplest and the clearest workflow. > > In your example I think d on a thread with a deleted message would > undelete the thread, rather than deleting the other messages in the > thread. But whichever of the two we chose I could see people being > unsure which it was going to do. > > It would be plausible to modify my patch so that k u does the same as > Ctrl-u k (i.e. takes you to the reverse tag operations) which would > avoid the awkward ctrl-u. (I don't want to add another key to the top > level maps as we are really very short on free keys) I agree to not pollute the top level maps and 'k u' would work for me, what is important for me is to support toggling :) > > Best wishes > > Mark > > > > >> Here's an example of code I'm using: >> >> (define-key notmuch-show-mode-map "d" >> (lambda () >> "toggle deleted tag for message" >> (interactive) >> (if (member "deleted" (notmuch-show-get-tags)) >> (notmuch-show-tag (list "-deleted")) >> (notmuch-show-tag (list "+deleted") >> >> (define-key notmuch-search-mode-map "d" >> (lambda (&optional beg end) >> "toggle deleted tag for message" >> (interactive (notmuch-search-interactive-region)) >> (if (member "deleted" (notmuch-search-get-tags)) >> (notmuch-search-tag (list "-deleted") beg end) >> (notmuch-search-tag (list "+deleted") beg end >> >> It works really well for me :). "inbox" and other tags work similarly. >> ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH] emacs: add tag jump menu
Hi I have implemented something similar in my tree and I really like the idea. I have one issue though. On Sat, 17 Sep 2016, Mark Walters wrote: > Add a "jump" style menu for doing tagging operations. > --- > > Jani suggested something like this on irc today. This is a first cut > to see if people like it. By default the tagging jump menu is bound to > k (which works in search/show/tree mode), and has the following options > > a (Archive) -inbox -unread > u (Mark Read) -unread > d (Delete) +deleted > > If you do ctrl-u k the it will do the reverse operation. I know C-u is default emacs behaviour but I find very cumbersone to do C-u for unapplying the tag. What I do and want is to simply apply the tag when pressing "d" then unapply it when pressing "d" again if the mail/thread already contains the deleted tag (basically it's a toggle). Here's an example of code I'm using: (define-key notmuch-show-mode-map "d" (lambda () "toggle deleted tag for message" (interactive) (if (member "deleted" (notmuch-show-get-tags)) (notmuch-show-tag (list "-deleted")) (notmuch-show-tag (list "+deleted") (define-key notmuch-search-mode-map "d" (lambda (&optional beg end) "toggle deleted tag for message" (interactive (notmuch-search-interactive-region)) (if (member "deleted" (notmuch-search-get-tags)) (notmuch-search-tag (list "-deleted") beg end) (notmuch-search-tag (list "+deleted") beg end It works really well for me :). "inbox" and other tags work similarly. > > To customize you want the variable notmuch-tagging-keys in the group > notmuch-tag. It is only very lightly tested but seems to work. And the > docstrings will definitely need some work. > > Best wishes > > Mark > > > > > > > > emacs/notmuch-lib.el | 4 > emacs/notmuch-show.el | 1 + > emacs/notmuch-tag.el | 51 > +++ > emacs/notmuch-tree.el | 1 + > emacs/notmuch.el | 1 + > 5 files changed, 58 insertions(+) > > diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el > index 2f015b0..b2cdace 100644 > --- a/emacs/notmuch-lib.el > +++ b/emacs/notmuch-lib.el > @@ -57,6 +57,10 @@ > > (custom-add-to-group 'notmuch-send 'message 'custom-group) > > +(defgroup notmuch-tag nil > + "Tags and tagging in Notmuch." > + :group 'notmuch) > + > (defgroup notmuch-crypto nil >"Processing and display of cryptographic MIME parts." >:group 'notmuch) > diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el > index 5a585f3..756c7dd 100644 > --- a/emacs/notmuch-show.el > +++ b/emacs/notmuch-show.el > @@ -1428,6 +1428,7 @@ reset based on the original query." > (define-key map "V" 'notmuch-show-view-raw-message) > (define-key map "c" 'notmuch-show-stash-map) > (define-key map "h" 'notmuch-show-toggle-visibility-headers) > +(define-key map "k" 'notmuch-tag-jump) > (define-key map "*" 'notmuch-show-tag-all) > (define-key map "-" 'notmuch-show-remove-tag) > (define-key map "+" 'notmuch-show-add-tag) > diff --git a/emacs/notmuch-tag.el b/emacs/notmuch-tag.el > index ec3c964..4d2feef 100644 > --- a/emacs/notmuch-tag.el > +++ b/emacs/notmuch-tag.el > @@ -28,6 +28,37 @@ > (require 'crm) > (require 'notmuch-lib) > > +(declare-function notmuch-search-tag "notmuch" tag-changes) > +(declare-function notmuch-show-tag "notmuch-show" tag-changes) > +(declare-function notmuch-tree-tag "notmuch-tree" tag-changes) > + > +(autoload 'notmuch-jump "notmuch-jump") > + > + > +(define-widget 'notmuch-tag-key-type 'list > + "A single key tagging binding" > + :format "%v" > + :args '((list :inline t > + :format "%v" > + (key-sequence :tag "Key") > + (repeat :tag "Tag operations" (string :format "%v" :tag > "change")) > + (checklist :inline t > +(string :tag "Short Name") > + > +(defcustom notmuch-tagging-keys > + `((,(kbd "a") ("-inbox" "-unread") "Archive") > +(,(kbd "u") ("-unread") "Mark read") > +(,(kbd "d") ("+deleted") "Delete")) > + "A list of keys and corresponding tagging operations > + > +For each key you can specify a sequence of tagging operations to > +apply. By default they will appear in the tagging buffer just as > +this sequence of tags, but you can specify a short name if you > +prefer." > + :tag "List of tagging bindings" > + :type '(repeat notmuch-tag-key-type) > + :group 'notmuch-tag) > + > (define-widget 'notmuch-tag-format-type 'lazy >"Customize widget for notmuch-tag-format and friends" >:type '(alist :key-type (regexp :tag "Tag") > @@ -437,6 +468,26 @@ begin with a \"+\" or a \"-\". If REVERSE is non-nil, > replace all > s))) > tags)) > > +(defun notmuch-tag-jump (reverse) > + (interactive "P") > + (let (action-map) > +(dolist (binding notmuch-tagging-keys) > + (let* ((tag-function (case major-mode > +
Re: [PATCH 4/4] emacs: add refresh all buffers function
On Fri, 16 Sep 2016, Mark Walters wrote: > On Sat, 10 Sep 2016, Ioan-Adrian Ratiu wrote: >> This new notmuch-refresh-all-buffers function calls each buffer's major >> mode specific refresh function using the generic function >> notmuch-refresh-this-buffer. >> >> It is very useful because by passing a non-nil arg to the buffer specific >> refresh functions it refreshes all notmuch buffers in the background and >> this again is very useful when doing periodic timer-based mail syncing. >> >> Signed-off-by: Ioan-Adrian Ratiu >> --- >> emacs/notmuch-lib.el | 15 +++ >> 1 file changed, 15 insertions(+) >> >> diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el >> index 6618365..4cc2041 100644 >> --- a/emacs/notmuch-lib.el >> +++ b/emacs/notmuch-lib.el >> @@ -428,6 +428,21 @@ in a window on screen, no-display has no effect." >>(notmuch-poll) >>(notmuch-refresh-this-buffer)) >> >> +(defun notmuch-refresh-all-buffers (&optional no-display) >> + "Invoke `notmuch-refresh-this-buffer' on all notmuch major-mode buffers. >> + >> +If no-display is non-nil all buffers are silently refreshed, i.e. they are >> +not foregrounded even if not displayed in any window. If no-display is nil >> +then each buffer's mode-specific refresh function uses its default >> behaviour." >> + (let ((buffers (buffer-list))) >> +(while buffers >> + (setq buffer (car buffers) >> +buffers (cdr buffers) >> +buffer-mode (buffer-local-value 'major-mode buffer)) > > I think this is a case where dolist might be more idiomatic (and maybe > you want buffer to be local to this function?) Yes, good idea, I'll rewrite in v2. > >> + (when (string-prefix-p "notmuch" (format "%s" buffer-mode)) >> +(with-current-buffer buffer >> + (notmuch-refresh-this-buffer no-display)) > > Is there a problem with this being slow if there are lots of show > buffers? notmuch show is synchronous? I am not saying it is a problem, > just wondered if you had considered it. I hadn't encountered any problems in practice. I tested with a maximum of 20 or so show buffers at a time and didn't notice any slowness, but I wasn't paying close attention because updates were happening in the background (only a few buffers were actually shown in windows). If this is a problem, then we can try to make it faster, sure. > > Best wishes > > Mark > >> + >> (defun notmuch-prettify-subject (subject) >>;; This function is used by `notmuch-search-process-filter' which >>;; requires that we not disrupt its' matching state. >> -- >> 2.9.3 >> >> ___ >> notmuch mailing list >> notmuch@notmuchmail.org >> https://notmuchmail.org/mailman/listinfo/notmuch ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH 2/4] emacs: adjust all types of notmuch show buffers
Hi and thank you for the feedback! On Fri, 16 Sep 2016, Mark Walters wrote: > On Sat, 10 Sep 2016, Ioan-Adrian Ratiu wrote: >> The current notmuch-show-message-adjust logic only adjusts the buffer >> focused in the current window. Extend it to adjust any kind of buffer, >> even buffers in a window without focus or in a different frame or even >> not shown at all. >> >> This new logic is very useful to build upon for the auto-refresh all >> buffers feature because you can use similar code to refresh any buffer: >> >> (with-current-buffer "*random show buffer*" >> (notmuch-refresh-this-buffer)) >> >> Signed-off-by: Ioan-Adrian Ratiu >> --- >> emacs/notmuch-show.el | 11 ++- >> 1 file changed, 10 insertions(+), 1 deletion(-) >> >> diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el >> index 6d3149b..74818cc 100644 >> --- a/emacs/notmuch-show.el >> +++ b/emacs/notmuch-show.el >> @@ -1505,7 +1505,16 @@ All currently available key bindings: >>(goto-char (notmuch-show-message-bottom))) >> >> (defun notmuch-show-message-adjust () >> - (recenter 0)) >> + (let ((buffer-window (get-buffer-window (current-buffer) t)) >> +(msg-position (point))) >> +(if buffer-window >> +(with-selected-window buffer-window >> + (goto-char msg-position) >> + (recenter 0)) >> + (save-window-excursion >> +(select-window (display-buffer (current-buffer))) >> +(goto-char msg-position) >> +(recenter 0) > > Hi > > I haven't tested things yet, but what happens if the buffer is open in > multiple windows? Good catch. If one of the windows has focus, its point gets reset to the message containing point before the refresh call (the standard notmuch-show current refresh behaviour). The windows which don't have focus get reset to the first message. We want to make all windows showing a buffer get adjusted, right? I can add this to v2. > > I think it would be worth adding something to the commit message along > the lines of > > notmuch-show-refresh-view calls notmuch-show-message-adjust in its > call chain. Since we want to call notmuch-show-refresh-view on > buffers than are not displayed we need to modify > notmuch-show-message-adjust to work in this case. Yes, this is much clearer, I will update in v2. > > Best wishes > > Mark > > >> ;; Movement related functions. >> >> -- >> 2.9.3 >> >> ___ >> notmuch mailing list >> notmuch@notmuchmail.org >> https://notmuchmail.org/mailman/listinfo/notmuch ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH 2/4] emacs: adjust all types of notmuch show buffers
The current notmuch-show-message-adjust logic only adjusts the buffer focused in the current window. Extend it to adjust any kind of buffer, even buffers in a window without focus or in a different frame or even not shown at all. This new logic is very useful to build upon for the auto-refresh all buffers feature because you can use similar code to refresh any buffer: (with-current-buffer "*random show buffer*" (notmuch-refresh-this-buffer)) Signed-off-by: Ioan-Adrian Ratiu --- emacs/notmuch-show.el | 11 ++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el index 6d3149b..74818cc 100644 --- a/emacs/notmuch-show.el +++ b/emacs/notmuch-show.el @@ -1505,7 +1505,16 @@ All currently available key bindings: (goto-char (notmuch-show-message-bottom))) (defun notmuch-show-message-adjust () - (recenter 0)) + (let ((buffer-window (get-buffer-window (current-buffer) t)) + (msg-position (point))) +(if buffer-window + (with-selected-window buffer-window + (goto-char msg-position) + (recenter 0)) + (save-window-excursion + (select-window (display-buffer (current-buffer))) + (goto-char msg-position) + (recenter 0) ;; Movement related functions. -- 2.9.3 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH 1/4] emacs: reuse buffer when refreshing searches
There's no reason to completely kill a buffer while refreshing its search results because its buffer name is constant between refreshes (based on the search query) and only its contents may change. Reusing the same buffer also makes it possible to do things like refreshing a buffer which is not focused or even not shown in any window - this will be used in the next patches to add auto-refresh capabilities to all existing notmuch buffers + a function to call after syncing mail to refresh everything. Signed-off-by: Ioan-Adrian Ratiu --- emacs/notmuch.el | 7 --- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/emacs/notmuch.el b/emacs/notmuch.el index 43d56f7..c33c55c 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -990,7 +990,7 @@ the configured default sort order." (defun notmuch-search-refresh-view () "Refresh the current view. -Kills the current buffer and runs a new search with the same +Erases the current buffer and runs a new search with the same query string as the current search. If the current thread is in the new search results, then point will be placed on the same thread. Otherwise, point will be moved to attempt to be in the @@ -998,8 +998,9 @@ same relative position within the new buffer." (let ((target-line (line-number-at-pos)) (oldest-first notmuch-search-oldest-first) (target-thread (notmuch-search-find-thread-id 'bare)) - (query notmuch-search-query-string)) -(notmuch-bury-or-kill-this-buffer) + (query notmuch-search-query-string) + (inhibit-read-only t)) +(erase-buffer) (notmuch-search query oldest-first target-thread target-line) (goto-char (point-min -- 2.9.3 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH 0/4] Add refresh all buffers functionality
This patch series adds a function to refresh all buffers, including an option to silently refresh them in the background i.e. to not show the newly refreshed buffer in any window. This is very useful for asynchronously updating all buffers when new mail arrives, using logic similar to the following (it's what I use): (setq process-connection-type nil) (defun done-index-sentinel (process event) (notmuch-refresh-all-buffers t) (message "Mail sync complete")) (defun done-sync-sentinel (process event) (message "Indexing mail using notmuch") (set-process-sentinel (start-process "notmuch" nil "notmuch" "new") 'done-index-sentinel)) (defun run-mail-sync () (message "Syncing mail in background") (set-process-sentinel (start-process "mbsync" nil "mbsync" "gmail") 'done-sync-sentinel)) (run-with-idle-timer 600 nil 'run-mail-sync) Ioan-Adrian Ratiu (4): emacs: reuse buffer when refreshing searches emacs: adjust all types of notmuch show buffers emacs: add refresh buffer optional no-display arg emacs: add refresh all buffers function emacs/notmuch-lib.el | 25 ++--- emacs/notmuch-show.el | 11 ++- emacs/notmuch.el | 24 3 files changed, 48 insertions(+), 12 deletions(-) -- 2.9.3 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch
[PATCH 3/4] emacs: add refresh buffer optional no-display arg
Add an optional no-display arg to the generic buffer refresh function, notmuch-refresh-this-buffer, which works the same way as notmuch-hello mode's notmuch-hello-update no-display arg. The idea is for the generic notmuch-refresh-this-buffer to pass down this arg to the "mode-specific" refresh functions to be able to update buffers without bringing them to the foreground (if they are already foregrounded, i.e. displayed in a window, this has no effect). When updating a search buffer, notmuch currently always brings results in a window to the foreground. Perhaps counter-intuitively, we do not want this behaviour necessarily, because we want to auto-refresh any kind of search buffers, even those backgrounded (not displayed in any window/frame) from previous searches. This is why we add a no-display arg to notmuch-search. We do this to show which mails have appeard or dissapeared since the last search refresh and have this information updated in real time even when switching buffers. The ultimate goal of this is to have all notmuch buffers auto-refresh when the email client syncs (this function is added in the next patch). Signed-off-by: Ioan-Adrian Ratiu --- emacs/notmuch-lib.el | 10 +++--- emacs/notmuch.el | 17 - 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el index 2f015b0..6618365 100644 --- a/emacs/notmuch-lib.el +++ b/emacs/notmuch-lib.el @@ -409,14 +409,18 @@ of its command symbol." "Function to call to refresh the current buffer.") (make-variable-buffer-local 'notmuch-buffer-refresh-function) -(defun notmuch-refresh-this-buffer () - "Refresh the current buffer." +(defun notmuch-refresh-this-buffer (&optional no-display) + "Refresh the current buffer. + +If no-display is non-nil do not try to bring the buffer to the +foreground. If the buffer is already foregrounded i.e. displayed +in a window on screen, no-display has no effect." (interactive) (when notmuch-buffer-refresh-function (if (commandp notmuch-buffer-refresh-function) ;; Pass prefix argument, etc. (call-interactively notmuch-buffer-refresh-function) - (funcall notmuch-buffer-refresh-function + (funcall notmuch-buffer-refresh-function no-display (defun notmuch-poll-and-refresh-this-buffer () "Invoke `notmuch-poll' to import mail, then refresh the current buffer." diff --git a/emacs/notmuch.el b/emacs/notmuch.el index c33c55c..ed93e66 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -930,7 +930,7 @@ PROMPT is the string to prompt with." ;;;###autoload (put 'notmuch-search 'notmuch-doc "Search for messages.") -(defun notmuch-search (&optional query oldest-first target-thread target-line) +(defun notmuch-search (&optional query oldest-first target-thread target-line no-display) "Display threads matching QUERY in a notmuch-search buffer. If QUERY is nil, it is read interactively from the minibuffer. @@ -941,6 +941,9 @@ Other optional parameters are used as follows: current if it appears in the search results. TARGET-LINE: The line number to move to if the target thread does not appear in the search results. + NO-DISPLAY: Do not try to foreground the search results buffer. If it is + already foregrounded i.e. displayed in a window, this has no + effect, meaning the buffer will remain visible. When called interactively, this will prompt for a query and use the configured default sort order." @@ -954,7 +957,9 @@ the configured default sort order." (let* ((query (or query (notmuch-read-query "Notmuch search: "))) (buffer (get-buffer-create (notmuch-search-buffer-title query -(switch-to-buffer buffer) +(if no-display + (set-buffer buffer) + (switch-to-buffer buffer)) (notmuch-search-mode) ;; Don't track undo information for this buffer (set 'buffer-undo-list t) @@ -987,21 +992,23 @@ the configured default sort order." (set-process-query-on-exit-flag proc nil (run-hooks 'notmuch-search-hook))) -(defun notmuch-search-refresh-view () +(defun notmuch-search-refresh-view (&optional no-display) "Refresh the current view. Erases the current buffer and runs a new search with the same query string as the current search. If the current thread is in the new search results, then point will be placed on the same thread. Otherwise, point will be moved to attempt to be in the -same relative position within the new buffer." +same relative position within the new buffer. If no-display is +non-nil, the search results buffer will not be foregrounded, if +it already is displayed in a window, then no-display has no effect." (let ((target-line (line-number-at-pos)) (oldes
[PATCH 4/4] emacs: add refresh all buffers function
This new notmuch-refresh-all-buffers function calls each buffer's major mode specific refresh function using the generic function notmuch-refresh-this-buffer. It is very useful because by passing a non-nil arg to the buffer specific refresh functions it refreshes all notmuch buffers in the background and this again is very useful when doing periodic timer-based mail syncing. Signed-off-by: Ioan-Adrian Ratiu --- emacs/notmuch-lib.el | 15 +++ 1 file changed, 15 insertions(+) diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el index 6618365..4cc2041 100644 --- a/emacs/notmuch-lib.el +++ b/emacs/notmuch-lib.el @@ -428,6 +428,21 @@ in a window on screen, no-display has no effect." (notmuch-poll) (notmuch-refresh-this-buffer)) +(defun notmuch-refresh-all-buffers (&optional no-display) + "Invoke `notmuch-refresh-this-buffer' on all notmuch major-mode buffers. + +If no-display is non-nil all buffers are silently refreshed, i.e. they are +not foregrounded even if not displayed in any window. If no-display is nil +then each buffer's mode-specific refresh function uses its default behaviour." + (let ((buffers (buffer-list))) +(while buffers + (setq buffer (car buffers) + buffers (cdr buffers) + buffer-mode (buffer-local-value 'major-mode buffer)) + (when (string-prefix-p "notmuch" (format "%s" buffer-mode)) + (with-current-buffer buffer + (notmuch-refresh-this-buffer no-display)) + (defun notmuch-prettify-subject (subject) ;; This function is used by `notmuch-search-process-filter' which ;; requires that we not disrupt its' matching state. -- 2.9.3 ___ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch