From: Bernard Pratz Date: Sun, 15 Jan 2017 19:53:09 +0000 (+0000) Subject: Searching with a window over notmuch vfolders X-Git-Tag: neomutt-20170128~30^2~4 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=cf8ed69d54112af02f086f83ef8361bbaef423fc;p=neomutt Searching with a window over notmuch vfolders following up on #278 Instead of adding a new command for searches that will result in windowed searches, this commit patches a lower function of the search mechanism, the `get_query_string()` function. There, any search is buffered in the `nm_query_window_current_search` global variable. And a newly introduced non-exposed function `window_query_from_query` is taking a `data->db_query` and adds a `date:` field implementing the window. That function won't add a `date:` field if there's no duration set or if there's already a date field in the query (to avoid risks of an empty intersection). To disable the feature, just set `nm_query_window_duration` to `0`. Finally, only two public functions are exposed: - `vfolder-window-forward` - `vfolder-window-backward` to slide the window over the search. The improvements over #278 implementation is that now it's working with any search done with notmuch, whether it's a `virtual-mailbox` or using the `vfolder-from-query` command. Have been removed: - `windowed-vfolder-from-query` Have been renamed: - `windowed-vfolder-forward` -> `vfolder-window-forward` - `windowed-vfolder-backward` -> `vfolder-window-backward` --- diff --git a/OPS.NOTMUCH b/OPS.NOTMUCH index 4508e4e2b..7fdbe7fb4 100644 --- a/OPS.NOTMUCH +++ b/OPS.NOTMUCH @@ -1,5 +1,8 @@ OP_MAIN_CHANGE_VFOLDER "open a different virtual folder" OP_MAIN_VFOLDER_FROM_QUERY "generate virtual folder from query" +OP_MAIN_WINDOWED_VFOLDER_FROM_QUERY "generate virtual folder from query and time window" +OP_MAIN_WINDOWED_VFOLDER_FORWARD "shifts virtual folder time window forwards" +OP_MAIN_WINDOWED_VFOLDER_BACKWARD "shifts virtual folder time window backwards" OP_MAIN_MODIFY_LABELS "modify (notmuch) tags" OP_MAIN_MODIFY_LABELS_THEN_HIDE "modify labels and then hide message" OP_MAIN_ENTIRE_THREAD "read entire thread of the current message" diff --git a/curs_main.c b/curs_main.c index b2bc9cc65..70daba7f4 100644 --- a/curs_main.c +++ b/curs_main.c @@ -1876,17 +1876,59 @@ int mutt_index_menu (void) } case OP_MAIN_VFOLDER_FROM_QUERY: - buf[0] = '\0'; + buf[0] = '\0'; if (mutt_get_field ("Query: ", buf, sizeof (buf), MUTT_NM_QUERY) != 0 || !buf[0]) { mutt_message (_("No query, aborting.")); break; } - if (!nm_uri_from_query(Context, buf, sizeof (buf))) - mutt_message (_("Failed to create query, aborting.")); - else - main_change_folder(menu, op, buf, sizeof (buf), &oldcount, &index_hint, 0); - break; + if (!nm_uri_from_query(Context, buf, sizeof (buf))) + mutt_message (_("Failed to create query, aborting.")); + else + main_change_folder(menu, op, buf, sizeof (buf), &oldcount, &index_hint, 0); + break; + + case OP_MAIN_WINDOWED_VFOLDER_BACKWARD: + dprint(2, (debugfile, "OP_MAIN_WINDOWED_VFOLDER_BACKWARD\n")); + if (NotmuchQueryWindowDuration <= 0) + { + mutt_message (_("Windowed queries disabled.")); + break; + } + if (NotmuchQueryWindowCurrentSearch == NULL) + { + mutt_message (_("No notmuch vfolder currently loaded.")); + break; + } + nm_query_window_backward(); + strncpy(buf, NotmuchQueryWindowCurrentSearch, sizeof(buf)); + if (!nm_uri_from_query(Context, buf, sizeof(buf))) + mutt_message (_("Failed to create query, aborting.")); + else + main_change_folder(menu, op, buf, sizeof (buf), &oldcount, &index_hint, 0); + break; + + case OP_MAIN_WINDOWED_VFOLDER_FORWARD: + if (NotmuchQueryWindowDuration <= 0) + { + mutt_message (_("Windowed queries disabled.")); + break; + } + if (NotmuchQueryWindowCurrentSearch == NULL) + { + mutt_message (_("No notmuch vfolder currently loaded.")); + break; + } + nm_query_window_forward(); + strncpy(buf, NotmuchQueryWindowCurrentSearch, sizeof(buf)); + if (!nm_uri_from_query(Context, buf, sizeof(buf))) + mutt_message (_("Failed to create query, aborting.")); + else + { + dprint(2, (debugfile, "nm: + windowed query (%s)\n", buf)); + main_change_folder(menu, op, buf, sizeof (buf), &oldcount, &index_hint, 0); + } + break; case OP_MAIN_CHANGE_VFOLDER: #endif diff --git a/doc/manual.xml.head b/doc/manual.xml.head index f6d69f308..083dbf89a 100644 --- a/doc/manual.xml.head +++ b/doc/manual.xml.head @@ -12292,6 +12292,16 @@ virtual-mailboxes "My INBOX" "notmuch://?query=tag:inbox" <vfolder-from-query> generate virtual folder from query + + index,pager + <vfolder-window-forward> + generate virtual folder by moving the query's time window forward + + + index,pager + <vfolder-window-backward> + generate virtual folder by moving the query's time window backward + @@ -12443,6 +12453,12 @@ set vfolder_format = "%6n(%6N) %f" # as a spoolfile. set virtual_spoolfile = no +# setup time window preferences +# first setup the duration, and then the time unit of that duration +# when set to 0 (the default) the search window feature is disabled +set nm_query_window_duration=2 +set nm_query_window_timebase="week" # or "hour", "day", "week", "month", "year" + # -------------------------------------------------------------------------- # FUNCTIONS - shown with an example mapping # -------------------------------------------------------------------------- @@ -12459,6 +12475,10 @@ bind index,pager \` modify-labels # generate virtual folder from query bind index,pager \eX vfolder-from-query +# generate virtual folder from query with time window +bind index,pager < vfolder-window-backward +bind index,pager > vfolder-window-forward + # modify labels and then hide message # bind index,pager ??? modify-labels-then-hide @@ -12556,6 +12576,7 @@ color index_tags green default Vladimir Marek Vladimir.Marek@oracle.com Víctor Manuel Jáquez Leal vjaquez@igalia.com Richard Russon rich@flatcap.org + Bernard 'Guyzmo' Pratz guyzmo+github+pub@m0g.net diff --git a/doc/muttrc.notmuch b/doc/muttrc.notmuch index d11f76839..768c4787f 100644 --- a/doc/muttrc.notmuch +++ b/doc/muttrc.notmuch @@ -44,6 +44,15 @@ set vfolder_format = "%6n(%6N) %f" # as a spoolfile. set virtual_spoolfile = no +# set the time base to apply to the time window +# valid values are: "hour", "day", "week", "month", "year" +set nm_query_window_timebase="week" + +# how large shall the window be? Any positive integer value is fine, in the +# unit of the previously defined timebase. +# When set to 0 (the default) the search window feature is disabled +set nm_query_window_duration=2 + # -------------------------------------------------------------------------- # FUNCTIONS - shown with an example mapping # -------------------------------------------------------------------------- @@ -60,6 +69,12 @@ bind index,pager \` modify-labels # generate virtual folder from query bind index,pager \eX vfolder-from-query +# move the time window forward +bind index > vfolder-window-forward + +# move the time window backward +bind index < vfolder-window-backward + # modify labels and then hide message # bind index,pager ??? modify-labels-then-hide diff --git a/doc/vimrc.notmuch b/doc/vimrc.notmuch index 5cbcfd3c4..3163f6787 100644 --- a/doc/vimrc.notmuch +++ b/doc/vimrc.notmuch @@ -6,6 +6,9 @@ syntax keyword muttrcVarBool contained skipwhite virtual_spoolfile nextgr syntax keyword muttrcVarNum contained skipwhite nm_db_limit nextgroup=muttrcSetNumAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr syntax keyword muttrcVarNum contained skipwhite nm_open_timeout nextgroup=muttrcSetNumAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax keyword muttrcVarNum contained skipwhite nm_query_window_duration nextgroup=muttrcSetNumAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax keyword muttrcVarNum contained skipwhite nm_query_window_timebase nextgroup=muttrcSetNumAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr + syntax keyword muttrcVarStr contained skipwhite nm_default_uri nextgroup=muttrcVarEqualsIdxFmt syntax keyword muttrcVarStr contained skipwhite nm_exclude_tags nextgroup=muttrcVarEqualsIdxFmt syntax keyword muttrcVarStr contained skipwhite nm_hidden_tags nextgroup=muttrcVarEqualsIdxFmt @@ -24,4 +27,6 @@ syntax match muttrcFunction contained "\" syntax match muttrcFunction contained "\" syntax match muttrcFunction contained "\" syntax match muttrcFunction contained "\" +syntax match muttrcFunction contained "\" +syntax match muttrcFunction contained "\" diff --git a/functions.h b/functions.h index 85e229fa3..2d5090e0e 100644 --- a/functions.h +++ b/functions.h @@ -209,6 +209,8 @@ const struct binding_t OpMain[] = { /* map: index */ #ifdef USE_NOTMUCH { "change-vfolder", OP_MAIN_CHANGE_VFOLDER, NULL }, { "vfolder-from-query", OP_MAIN_VFOLDER_FROM_QUERY, NULL }, + { "vfolder-window-backward", OP_MAIN_WINDOWED_VFOLDER_BACKWARD, NULL }, + { "vfolder-window-forward", OP_MAIN_WINDOWED_VFOLDER_FORWARD, NULL }, { "modify-labels", OP_MAIN_MODIFY_LABELS, NULL }, { "modify-labels-then-hide", OP_MAIN_MODIFY_LABELS_THEN_HIDE, NULL }, { "entire-thread", OP_MAIN_ENTIRE_THREAD, NULL }, diff --git a/globals.h b/globals.h index 8feda0b5d..9be77dbbd 100644 --- a/globals.h +++ b/globals.h @@ -321,6 +321,10 @@ WHERE char *VirtFolderFormat; WHERE int NotmuchDBLimit; WHERE char *NotmuchQueryType; WHERE char *NotmuchRecordTags; +WHERE int NotmuchQueryWindowDuration; +WHERE char *NotmuchQueryWindowTimebase; +WHERE int NotmuchQueryWindowCurrentPosition; +WHERE char *NotmuchQueryWindowCurrentSearch; #endif diff --git a/init.h b/init.h index 3a8b0987c..c191c5042 100644 --- a/init.h +++ b/init.h @@ -2048,6 +2048,30 @@ struct option_t MuttVars[] = { /* ** .pp ** This variable specifies the default tags applied to messages stored to the mutt record. + ** When set to 0 this variable disable the window feature. + */ + { "nm_query_window_duration", DT_NUM, R_NONE, UL &NotmuchQueryWindowDuration, 0 }, + /* + ** .pp + ** This variable sets the time base of a windowed notmuch query. + ** Accepted values are 'minute', 'hour', 'day', 'week', 'month', 'year' + */ + { "nm_query_window_timebase", DT_STR, R_NONE, UL &NotmuchQueryWindowTimebase, UL "week" }, + /* + ** .pp + ** This variable sets the time duration of a windowed notmuch query. + ** Accepted values all non negative integers. A value of 0 disables the feature. + */ + { "nm_query_window_current_search", DT_STR, R_NONE, UL &NotmuchQueryWindowCurrentSearch, UL "" }, + /* + ** .pp + ** This variable sets the time duration of a windowed notmuch query. + ** Accepted values all non negative integers. A value of 0 disables the feature. + */ + { "nm_query_window_current_position", DT_NUM, R_NONE, UL &NotmuchQueryWindowCurrentPosition, 0 }, + /* + ** .pp + ** This variable contains the currently setup notmuch search for window based vfolder. */ #endif { "pager", DT_PATH, R_NONE, UL &Pager, UL "builtin" }, diff --git a/mutt_notmuch.c b/mutt_notmuch.c index a9c9da2b2..e60d68eeb 100644 --- a/mutt_notmuch.c +++ b/mutt_notmuch.c @@ -429,9 +429,66 @@ static int string_to_query_type(const char *str) return NM_QUERY_TYPE_MESGS; } +static int query_window_check_timebase(char *timebase) +{ + if ((strcmp(timebase, "hour") == 0) || + (strcmp(timebase, "day") == 0) || + (strcmp(timebase, "week") == 0) || + (strcmp(timebase, "month") == 0) || + (strcmp(timebase, "year") == 0)) + return true; + return false; +} + +static void query_window_reset(void) +{ + dprint(2, (debugfile, "query_window_reset ()\n")); + NotmuchQueryWindowCurrentPosition = 0; +} + +static int windowed_query_from_query(char *query, char *buf, size_t bufsz) +{ + dprint(2, (debugfile, "windowed_query_from_query (%s)\n", query)); + + int beg = NotmuchQueryWindowDuration * (NotmuchQueryWindowCurrentPosition + 1); + int end = NotmuchQueryWindowDuration * NotmuchQueryWindowCurrentPosition; + + // if the duration is a non positive integer, disable the window + if (NotmuchQueryWindowDuration <= 0) + { + query_window_reset(); + return 0; + } + + // if the query has changed, reset the window position + if (NotmuchQueryWindowCurrentSearch == NULL || + strcmp(query, NotmuchQueryWindowCurrentSearch) != 0) + query_window_reset(); + + // + if (!query_window_check_timebase(NotmuchQueryWindowTimebase)) + { + mutt_message (_("Invalid nm_query_window_timebase value (valid values are: hour, day, week, month or year).")); + dprint(2, (debugfile, "Invalid nm_query_window_timebase value\n")); + return 0; + } + + if (end == 0) + snprintf(buf, bufsz, "date:%d%s..now and %s", + beg, NotmuchQueryWindowTimebase, NotmuchQueryWindowCurrentSearch); + else + snprintf(buf, bufsz, "date:%d%s..%d%s and %s", + beg, NotmuchQueryWindowTimebase, end, NotmuchQueryWindowTimebase, NotmuchQueryWindowCurrentSearch); + + return 1; +} + static char *get_query_string(struct nm_ctxdata *data) { + dprint(2, (debugfile, "nm: get_query_string()\n")); + struct uri_tag *item; + char buf[LONG_STRING]; if (!data) return NULL; @@ -458,6 +515,15 @@ static char *get_query_string(struct nm_ctxdata *data) if (!data->query_type) data->query_type = string_to_query_type(NULL); + mutt_str_replace(&NotmuchQueryWindowCurrentSearch, data->db_query); + + // if a date part is defined, do not apply windows (to avoid the risk of + // having a non-intersected date frame). A good improvement would be to + // accept if they intersect + if (!strstr(data->db_query, "date:") && + windowed_query_from_query(data->db_query, buf, sizeof(buf))) + data->db_query = safe_strdup(buf); + dprint(2, (debugfile, "nm: query '%s'\n", data->db_query)); return data->db_query; @@ -1265,12 +1331,13 @@ static int rename_maildir_filename(const char *old, char *newpath, size_t newsz, strfcpy(folder, old, sizeof(folder)); p = strrchr(folder, '/'); - if (p) { + if (p) + { *p = '\0'; p++; - } else { - p = folder; } + else + p = folder; strfcpy(filename, p, sizeof(filename)); @@ -1586,6 +1653,7 @@ done: char *nm_uri_from_query(CONTEXT *ctx, char *buf, size_t bufsz) { + dprint(2, (debugfile, "nm_uri_from_query (%s)\n", buf)); struct nm_ctxdata *data = get_ctxdata(ctx); char uri[_POSIX_PATH_MAX + LONG_STRING + 32]; /* path to DB + query + URI "decoration" */ @@ -1605,6 +1673,20 @@ char *nm_uri_from_query(CONTEXT *ctx, char *buf, size_t bufsz) return buf; } +void nm_query_window_forward(void) +{ + if (NotmuchQueryWindowCurrentPosition != 0) + NotmuchQueryWindowCurrentPosition -= 1; + + dprint(2, (debugfile, "nm_query_window_forward (%d)\n", NotmuchQueryWindowCurrentPosition)); +} + +void nm_query_window_backward(void) +{ + NotmuchQueryWindowCurrentPosition += 1; + dprint(2, (debugfile, "nm_query_window_backward (%d)\n", NotmuchQueryWindowCurrentPosition)); +} + int nm_modify_message_tags(CONTEXT *ctx, HEADER *hdr, char *buf) { struct nm_ctxdata *data = get_ctxdata(ctx); diff --git a/mutt_notmuch.h b/mutt_notmuch.h index 91814d49a..4d7f2a868 100644 --- a/mutt_notmuch.h +++ b/mutt_notmuch.h @@ -26,6 +26,9 @@ int nm_update_filename(CONTEXT *ctx, const char *old, const char *new, HEADER char *nm_uri_from_query(CONTEXT *ctx, char *buf, size_t bufsz); int nm_modify_message_tags(CONTEXT *ctx, HEADER *hdr, char *buf); +void nm_query_window_backward(void); +void nm_query_window_forward(void); + void nm_longrun_init(CONTEXT *ctx, int writable); void nm_longrun_done(CONTEXT *ctx);