]> granicus.if.org Git - neomutt/commitdiff
Searching with a window over notmuch vfolders
authorBernard Pratz <guyzmo+github+pub@m0g.net>
Sun, 15 Jan 2017 19:53:09 +0000 (19:53 +0000)
committerRichard Russon <rich@flatcap.org>
Wed, 18 Jan 2017 16:41:33 +0000 (16:41 +0000)
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`

OPS.NOTMUCH
curs_main.c
doc/manual.xml.head
doc/muttrc.notmuch
doc/vimrc.notmuch
functions.h
globals.h
init.h
mutt_notmuch.c
mutt_notmuch.h

index 4508e4e2babc96de73ccdd61658bb94fc3d89be7..7fdbe7fb4b0edf16dee07c205e6f177463a05b30 100644 (file)
@@ -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"
index b2bc9cc655a1c1a056185e0586b60e697017516e..70daba7f466c9cf93cc81edb63db22cd77e01045 100644 (file)
@@ -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
index f6d69f308d1849bef38ccf42f1d62857ddfe9f6f..083dbf89a351a428f4205fc31e5562720629d3ae 100644 (file)
@@ -12292,6 +12292,16 @@ virtual-mailboxes "My INBOX" "notmuch://?query=tag:inbox"
             <entry><literal>&lt;vfolder-from-query&gt;</literal></entry>
             <entry>generate virtual folder from query</entry>
           </row>
+          <row>
+            <entry>index,pager</entry>
+            <entry><literal>&lt;vfolder-window-forward&gt;</literal></entry>
+            <entry>generate virtual folder by moving the query's time window forward</entry>
+          </row>
+          <row>
+            <entry>index,pager</entry>
+            <entry><literal>&lt;vfolder-window-backward&gt;</literal></entry>
+            <entry>generate virtual folder by moving the query's time window backward</entry>
+          </row>
         </tbody>
       </tgroup>
     </table>
@@ -12443,6 +12453,12 @@ set vfolder_format = &quot;%6n(%6N) %f&quot;
 # as a spoolfile.</emphasis>
 set virtual_spoolfile = no
 
+<emphasis role="comment"># 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</emphasis>
+set nm_query_window_duration=2
+set nm_query_window_timebase="week" # or "hour", "day", "week", "month", "year"
+
 <emphasis role="comment"># --------------------------------------------------------------------------
 # FUNCTIONS - shown with an example mapping
 # --------------------------------------------------------------------------
@@ -12459,6 +12475,10 @@ bind index,pager \` modify-labels
 <emphasis role="comment"># generate virtual folder from query</emphasis>
 bind index,pager \eX vfolder-from-query
 
+<emphasis role="comment"># generate virtual folder from query with time window</emphasis>
+bind index,pager &lt; vfolder-window-backward
+bind index,pager &gt; vfolder-window-forward
+
 <emphasis role="comment"># modify labels and then hide message
 # bind index,pager ??? modify-labels-then-hide
 
@@ -12556,6 +12576,7 @@ color index_tags green default
     <listitem><para>Vladimir Marek <email>Vladimir.Marek@oracle.com</email></para></listitem>
     <listitem><para>Víctor Manuel Jáquez Leal <email>vjaquez@igalia.com</email></para></listitem>
     <listitem><para>Richard Russon <email>rich@flatcap.org</email></para></listitem>
+    <listitem><para>Bernard 'Guyzmo' Pratz <email>guyzmo+github+pub@m0g.net</email></para></listitem>
     </itemizedlist>
   </sect2>
 </sect1>
index d11f76839629d17a2ea3a962de4f71128bfb3e65..768c4787f4a5bc4a42bc5c600c4666edd7763008 100644 (file)
@@ -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</emphasis>
+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
 
index 5cbcfd3c4dad479cf5fde29be2766c4f908a2fd8..3163f6787065e9898e3ddec2d83f50e2f4607773 100644 (file)
@@ -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 "\<modify-labels-then-hide\>"
 syntax match muttrcFunction     contained "\<modify-labels\>"
 syntax match muttrcFunction     contained "\<sidebar-toggle-virtual\>"
 syntax match muttrcFunction     contained "\<vfolder-from-query\>"
+syntax match muttrcFunction     contained "\<vfolder-window-forward\>"
+syntax match muttrcFunction     contained "\<vfolder-window-backward\>"
 
index 85e229fa3502e98ed41455e62fb3287002a9a236..2d5090e0ee8cadda026f6c3eaf553699e11f6268 100644 (file)
@@ -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 },
index 8feda0b5d0dbac514c3b8efb99f109e62bcdbb25..9be77dbbdbcdcbc1a0e74a430e7eee1c96baec57 100644 (file)
--- 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 3a8b0987c578f1673eaeb619538faeabb6e19fec..c191c5042998c5a3b29db413a5041451faa69620 100644 (file)
--- 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" },
index a9c9da2b2e0b6fd8cfb369cabd522a5d7d59cc16..e60d68eebe59af38bab697b35f99b493356bd339 100644 (file)
@@ -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);
index 91814d49a4c88c50d07046e69eb0d6e0b06a349b..4d7f2a868d6b5d9bb77925f4fd4cc05d2d1218ea 100644 (file)
@@ -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);