Re: How do you synchronize your notmuch tags across multiple machines?

2019-01-13 Thread Ioan-Adrian Ratiu
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

2017-05-23 Thread Ioan-Adrian Ratiu
On Tue, 23 May 2017, Jani Nikula <j...@nikula.org> wrote:
> On Fri, 19 May 2017, Ioan-Adrian Ratiu <a...@adirat.com> 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

2017-05-22 Thread Ioan-Adrian Ratiu
On Fri, 19 May 2017, Tomi Ollila <tomi.oll...@iki.fi> 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 <a...@adirat.com>
>> ---
>>  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, _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, _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

2017-05-18 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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 11/11] emacs: notmuch-show: add filesize to headerline

2017-05-18 Thread Ioan-Adrian Ratiu
Signed-off-by: Ioan-Adrian Ratiu <a...@adirat.com>
---
 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 09/11] notmuch-show: export message filesize

2017-05-18 Thread Ioan-Adrian Ratiu
Signed-off-by: Ioan-Adrian Ratiu <a...@adirat.com>
---
 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

2017-05-18 Thread Ioan-Adrian Ratiu
This works for all the search output formats (sexp, json, text).

Signed-off-by: Ioan-Adrian Ratiu <a...@adirat.com>
---
 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 07/11] lib: thread: add thread total size function

2017-05-18 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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

2017-05-18 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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, _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, _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

2017-05-18 Thread Ioan-Adrian Ratiu
We need to output unsigned long values for message and thread
(sum of all message's) file sizes.

Signed-off-by: Ioan-Adrian Ratiu <a...@adirat.com>
---
 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

2017-05-18 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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

2017-05-18 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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" ( query oldest-first 
target-thread target-line continuation))
+(declare-function notmuch-search "notmuch" ( query sort-order 
target-thread target-line continuation))
 (declare-function notmuch-poll "notmuch" ())
 (declare-function notmuch-tree "notmuch-tree"
   ( 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 saved-search :query))
-   (oldest-first
-(case (plist-get saved-s

[PATCH v2 01/11] lib: message: index message file sizes

2017-05-18 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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,
 _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, );
+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 ) {
+   _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
  * encoded by pre

[PATCH v2 00/11] Add filesize index, search, sort & emacs UI

2017-05-18 Thread Ioan-Adrian Ratiu
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

2017-05-18 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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 = _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

2017-05-13 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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

2017-05-13 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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" ( query oldest-first 
target-thread target-line continuation))
+(declare-function notmuch-search "notmuch" ( query sort-order 
target-thread target-line continuation))
 (declare-function notmuch-poll "notmuch" ())
 (declare-function notmuch-tree "notmuch-tree"
   ( 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 saved-search :query))
-   (oldest-first
-(case (plist-get saved-s

[PATCH 1/3] notmuch-search: implement 'filesize' search

2017-05-13 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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 = _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;
 
  

[RFC][PATCH] notmuch-search: add file size search feature

2017-05-13 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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 = _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

2016-12-10 Thread Ioan-Adrian Ratiu
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

2016-12-10 Thread Ioan-Adrian Ratiu
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, ))
+   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,
-))
-{
-   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

2016-12-10 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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, ))
-   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, )) {
+   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

2016-12-08 Thread Ioan-Adrian Ratiu
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, ))
+   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,
-))
-{
-   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

2016-12-08 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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, ))
-   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, )) {
+   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

2016-12-08 Thread Ioan-Adrian Ratiu
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

2016-12-05 Thread Ioan-Adrian Ratiu
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, ))
+   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,
-))
-{
-   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

2016-12-05 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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, ))
-   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, )) {
+   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

2016-12-05 Thread Ioan-Adrian Ratiu
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

2016-12-04 Thread Ioan-Adrian Ratiu
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, ))
+   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,
-))
-{
-   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

2016-12-04 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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, ))
-   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, ))
+   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

2016-12-04 Thread Ioan-Adrian Ratiu
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

2016-11-06 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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, ))
-   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, ))
+   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

2016-11-06 Thread Ioan-Adrian Ratiu
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, ))
+   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,
-))
-{
-   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

2016-11-06 Thread Ioan-Adrian Ratiu
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

2016-11-05 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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,
-))
+if (! get_config_from_file (config, ))
 {
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


[PATCH v4 5/6] emacs: add no-display arg to generic refresh functions

2016-10-08 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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

2016-10-08 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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 ( 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

2016-10-08 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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 ( query oldest-first target-thread target-line)
+(defun notmuch-search ( 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 ( ignore)
+(defun notmuch-search-refresh-view ( 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

2016-10-08 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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 ( 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

2016-10-08 Thread Ioan-Adrian Ratiu
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 ( no-display)
+(defun notmuch-hello-update ( 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 ( reset-state)
+(defun notmuch-show-refresh-view ( 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 ( 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 ( 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

2016-10-08 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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 ( 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

2016-10-08 Thread Ioan-Adrian Ratiu
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

2016-10-07 Thread Ioan-Adrian Ratiu
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 ( no-display)
> +(defun notmuch-hello-update ( 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 ( reset-state)
> +(defun notmuch-show-refresh-view ( 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 ( 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 

Re: [PATCH v3 3/4] emacs: add refresh buffer optional no-display arg

2016-10-06 Thread Ioan-Adrian Ratiu
On Thu, 06 Oct 2016, Mark Walters <markwalters1...@gmail.com> wrote:
> On Thu, 06 Oct 2016, Ioan-Adrian Ratiu <a...@adirat.com> 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 <a...@adirat.com>
>> ---
>>  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 ( 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/notm

Re: [PATCH v2 0/4] Add refresh all buffers functionality

2016-10-06 Thread Ioan-Adrian Ratiu
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

2016-10-06 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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 ( 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 ( query oldest-first target-thread target-line)
+(defun notmuch-search ( 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 ( 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))
(oldest-first notmuch-search-oldest-first)
(target-thread (notmuch-searc

[PATCH v3 1/4] emacs: reuse buffer when refreshing searches

2016-10-06 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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

2016-10-06 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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 ( 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

2016-10-06 Thread Ioan-Adrian Ratiu
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

2016-10-06 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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 ( 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

2016-10-06 Thread Ioan-Adrian Ratiu
On Sun, 25 Sep 2016, Mark Walters <markwalters1...@gmail.com> wrote:
> On Sat, 24 Sep 2016, Ioan-Adrian Ratiu <a...@adirat.com> 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 <a...@adirat.com>
>> ---
>>  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 ( 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 ( query oldest-first target-thread 
>> target-line)
>> +(defun notmuch-search ( 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 paramete

Re: [PATCH v2 2/4] emacs: notmuch-show: refresh all windows showing a buffer

2016-09-25 Thread Ioan-Adrian Ratiu

Hi Mark and thank you again for the great feedback.

On Sun, 25 Sep 2016, Mark Walters <markwalters1...@gmail.com> wrote:
> On Sat, 24 Sep 2016, Ioan-Adrian Ratiu <a...@adirat.com> 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 <a...@adirat.com>
>> ---
>>  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 ()
>>"Retu

Re: [PATCH v2 0/4] Add refresh all buffers functionality

2016-09-24 Thread Ioan-Adrian Ratiu
On Sat, 24 Sep 2016, David Bremner <da...@tethera.net> wrote:
> Ioan-Adrian Ratiu <a...@adirat.com> writes:
>
>> On Sat, 24 Sep 2016, Ioan-Adrian Ratiu <a...@adirat.com> 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

2016-09-24 Thread Ioan-Adrian Ratiu
On Sat, 24 Sep 2016, Ioan-Adrian Ratiu <a...@adirat.com> 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 <a...@adirat.com> 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

2016-09-24 Thread Ioan-Adrian Ratiu

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 <a...@adirat.com> 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

2016-09-24 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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 ( 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

2016-09-24 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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

2016-09-24 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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 ( 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 ( query oldest-first target-thread target-line)
+(defun notmuch-search ( 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 ( 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))
(oldest-first notmuch-search-oldest-first)
(target-thread (notmuch-se

[PATCH v2 2/4] emacs: notmuch-show: refresh all windows showing a buffer

2016-09-24 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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 ( 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

2016-09-24 Thread Ioan-Adrian Ratiu
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

2016-09-18 Thread Ioan-Adrian Ratiu
On Sun, 18 Sep 2016, Jani Nikula <j...@nikula.org> wrote:
> On Sun, 18 Sep 2016, Ioan-Adrian Ratiu <a...@adirat.com> wrote:
>> On Sun, 18 Sep 2016, Mark Walters <markwalters1...@gmail.com> wrote:
>>> On Sun, 18 Sep 2016, Ioan-Adrian Ratiu <a...@adirat.com> 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 <markwalters1...@gmail.com> 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

2016-09-18 Thread Ioan-Adrian Ratiu
On Sun, 18 Sep 2016, Mark Walters <markwalters1...@gmail.com> wrote:
> On Sun, 18 Sep 2016, Ioan-Adrian Ratiu <a...@adirat.com> 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 <markwalters1...@gmail.com> 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 ( 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

2016-09-18 Thread Ioan-Adrian Ratiu

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 ( 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

2016-09-17 Thread Ioan-Adrian Ratiu
On Fri, 16 Sep 2016, Mark Walters <markwalters1...@gmail.com> wrote:
> On Sat, 10 Sep 2016, Ioan-Adrian Ratiu <a...@adirat.com> 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 <a...@adirat.com>
>> ---
>>  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 ( 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

2016-09-17 Thread Ioan-Adrian Ratiu

Hi and thank you for the feedback!

On Fri, 16 Sep 2016, Mark Walters <markwalters1...@gmail.com> wrote:
> On Sat, 10 Sep 2016, Ioan-Adrian Ratiu <a...@adirat.com> 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 <a...@adirat.com>
>> ---
>>  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

2016-09-10 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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

2016-09-10 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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

2016-09-10 Thread Ioan-Adrian Ratiu
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

2016-09-10 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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 ( 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 ( query oldest-first target-thread target-line)
+(defun notmuch-search ( 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 ( 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))
(oldest-first notmuch-search-oldest-first)
(target-thread (notmuch-se

[PATCH 4/4] emacs: add refresh all buffers function

2016-09-10 Thread Ioan-Adrian Ratiu
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 <a...@adirat.com>
---
 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 ( 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