]> granicus.if.org Git - neomutt/commitdiff
Add TAB completion of tag: names in <vfolder-from-query>
authorTim Stoakes <tim@stoakes.net>
Sat, 22 Sep 2012 07:31:54 +0000 (09:31 +0200)
committerRichard Russon <rich@flatcap.org>
Mon, 4 Apr 2016 15:30:06 +0000 (16:30 +0100)
<vfolder-from-query>'s 'Query' prompt can now use TAB completion of tag names
supplied to the 'tag:' parameter.

README.notmuch
curs_main.c
enter.c
init.c
mutt.h
protos.h

index aa609e3557358679f8e39ff2dc11b4c790118dc2..c6a19508a3f798ce516c622f8b5cd2c505bf7f27 100644 (file)
@@ -82,6 +82,7 @@ notmuch support for mutt
    vfolder-from-query:
       - generate new virtual folder from notmuch search query
       - default key: <Esc>X
+      - note: TAB completion of 'tag:' names is available
 
    modify-labels:
       - add or remove notmuch tags; [+]<tag> to add, -<tag> to remove
index 55a19a1051d27c07ec7045a8b77e869aaae1ac7a..25341a03c549a475a8b8aea74dbb13499f2453d8 100644 (file)
@@ -1456,7 +1456,7 @@ int mutt_index_menu (void)
 
       case OP_MAIN_VFOLDER_FROM_QUERY:
        buf[0] = '\0';
-        if (mutt_get_field ("Query: ", buf, sizeof (buf), 0) != 0 || !buf[0])
+        if (mutt_get_field ("Query: ", buf, sizeof (buf), M_NM_QUERY) != 0 || !buf[0])
         {
           mutt_message _("No query, aborting.");
           break;
diff --git a/enter.c b/enter.c
index 23610ae5dc074b451329af0e47fedad1e361457e..e07a901ffadd9b84daa0b354d945598bc884fd08 100644 (file)
--- a/enter.c
+++ b/enter.c
@@ -630,6 +630,17 @@ int _mutt_enter_string (char *buf, size_t buflen, int y, int x,
              BEEP (); /* let the user know that nothing matched */
            replace_part (state, 0, buf);
          }
+#if USE_NOTMUCH
+         else if (flags & M_NM_QUERY)
+         {
+           my_wcstombs (buf, buflen, state->wbuf, state->curpos);
+           i = strlen (buf);
+           if (!mutt_nm_query_complete(buf, buflen, i, state->tabs))
+             BEEP ();
+
+           replace_part (state, 0, buf);
+         }
+#endif
          else
            goto self_insert;
          break;
diff --git a/init.c b/init.c
index 0ff92feb5327ef212c8d6909eb5cfd6c8573262b..e356ae44ce4278a77e5e5192f0b0572fae395540 100644 (file)
--- a/init.c
+++ b/init.c
@@ -37,7 +37,9 @@
 #include "mutt_ssl.h"
 #endif
 
-
+#if USE_NOTMUCH
+#include "mutt_notmuch.h"
+#endif
 
 #include "mx.h"
 #include "init.h"
@@ -76,6 +78,12 @@ static void myvar_set (const char* var, const char* val);
 static const char* myvar_get (const char* var);
 static void myvar_del (const char* var);
 
+#if USE_NOTMUCH
+/* List of tags found in last call to mutt_nm_query_complete(). */
+static char **nm_tags;
+#endif
+
+
 static void toggle_quadoption (int opt)
 {
   int n = opt/4;
@@ -2636,6 +2644,154 @@ int mutt_var_value_complete (char *buffer, size_t len, int pos)
   return 0;
 }
 
+#if USE_NOTMUCH
+
+/* Fetch a list of all notmuch tags and insert them into the completion
+ * machinery.
+ */
+static int complete_all_nm_tags (const char *pt)
+{
+  int num;
+  int tag_count_1 = 0;
+  int tag_count_2 = 0;
+
+  Num_matched = 0;
+  strfcpy (User_typed, pt, sizeof (User_typed));
+  memset (Matches, 0, Matches_listsize);
+  memset (Completed, 0, sizeof (Completed));
+
+  /* Work out how many tags there are. */
+  if (nm_get_all_tags(Context, NULL, &tag_count_1) || tag_count_1 == 0)
+    return 0;
+
+  /* Free the old list, if any. */
+  if (nm_tags != NULL) {
+    int i;
+    for (i = 0; nm_tags[i] != NULL; i++)
+      FREE (&nm_tags[i]);
+    FREE (&nm_tags);
+  }
+  /* Allocate a new list, with sentinel. */
+  nm_tags = safe_malloc((tag_count_1 + 1) * sizeof (char *));
+  nm_tags[tag_count_1] = NULL;
+
+  /* Get all the tags. */
+  if (nm_get_all_tags(Context, nm_tags, &tag_count_2) ||
+      tag_count_1 != tag_count_2) {
+    FREE (&nm_tags);
+    nm_tags = NULL;
+    return -1;
+  }
+
+  /* Put them into the completion machinery. */
+  for (num = 0; num < tag_count_1; num++) {
+    candidate (Completed, User_typed, nm_tags[num], sizeof (Completed));
+  }
+
+  matches_ensure_morespace (Num_matched);
+  Matches[Num_matched++] = User_typed;
+  return 0;
+}
+
+/* Return the last instance of needle in the haystack, or NULL.
+ * Like strstr(), only backwards, and for a limited haystack length.
+ */
+static const char* rstrnstr(const char* haystack,
+                            size_t haystack_length,
+                            const char* needle)
+{
+  int needle_length = strlen(needle);
+  const char* haystack_end = haystack + haystack_length - needle_length;
+  const char* p;
+
+  for (p = haystack_end; p >= haystack; --p)
+  {
+    size_t i;
+    for (i = 0; i < needle_length; ++i) {
+      if (p[i] != needle[i])
+        goto next;
+    }
+    return p;
+
+    next:;
+  }
+  return NULL;
+}
+
+/* Complete the nearest "tag:"-prefixed string previous to pos. */
+int mutt_nm_query_complete (char *buffer, size_t len, int pos, int numtabs)
+{
+  char *pt = buffer;
+  int spaces;
+
+  SKIPWS (buffer);
+  spaces = buffer - pt;
+
+  pt = (char *)rstrnstr((char *)buffer, pos, "tag:");
+  if (pt != NULL) {
+    pt += 4;
+    if (numtabs == 1) {
+      /* First TAB. Collect all the matches */
+      complete_all_nm_tags(pt);
+
+      /* All matches are stored. Longest non-ambiguous string is ""
+       * i.e. don't change 'buffer'. Fake successful return this time.
+       */
+      if (User_typed[0] == 0)
+       return 1;
+    }
+
+    if (Completed[0] == 0 && User_typed[0])
+      return 0;
+
+    /* Num_matched will _always_ be atleast 1 since the initial
+     * user-typed string is always stored */
+    if (numtabs == 1 && Num_matched == 2)
+      snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
+    else if (numtabs > 1 && Num_matched > 2)
+      /* cycle thru all the matches */
+      snprintf(Completed, sizeof(Completed), "%s",
+              Matches[(numtabs - 2) % Num_matched]);
+
+    /* return the completed query */
+    strncpy (pt, Completed, buffer + len - pt - spaces);
+  }
+  else
+    return 0;
+
+  return 1;
+}
+
+
+      /* All matches are stored. Longest non-ambiguous string is ""
+       * i.e. don't change 'buffer'. Fake successful return this time.
+       */
+      if (User_typed[0] == 0)
+       return 1;
+    }
+
+    if (Completed[0] == 0 && User_typed[0])
+      return 0;
+
+    /* Num_matched will _always_ be atleast 1 since the initial
+     * user-typed string is always stored */
+    if (numtabs == 1 && Num_matched == 2)
+      snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
+    else if (numtabs > 1 && Num_matched > 2)
+      /* cycle thru all the matches */
+      snprintf(Completed, sizeof(Completed), "%s",
+              Matches[(numtabs - 2) % Num_matched]);
+
+    /* return the completed query */
+    strncpy (pt, Completed, buffer + len - pt - spaces);
+  }
+  else
+    return 0;
+
+  return 1;
+}
+#endif
+
 static int var_to_string (int idx, char* val, size_t len)
 {
   char tmp[LONG_STRING];
diff --git a/mutt.h b/mutt.h
index 8b48ff616a5a6e74442749578477aebcbf3ce238..faed9fbbe5b36bd98f9d7d87f6855e44c93ba93f 100644 (file)
--- a/mutt.h
+++ b/mutt.h
 #define  M_CLEAR   (1<<5) /* clear input if printable character is pressed */
 #define  M_COMMAND (1<<6) /* do command completion */
 #define  M_PATTERN (1<<7) /* pattern mode - only used for history classes */
+#if USE_NOTMUCH
+#define  M_NM_QUERY (1<<8) /* Notmuch query mode. */
+#endif
 
 /* flags for mutt_get_token() */
 #define M_TOKEN_EQUAL          1       /* treat '=' as a special */
index f69d736eda3cfff2303c8a023876665b6674a1e2..6bcb28c901eea3d3b3861a3a177343cda50c2ee7 100644 (file)
--- a/protos.h
+++ b/protos.h
@@ -289,6 +289,9 @@ int mutt_check_overwrite (const char *, const char *, char *, size_t, int *, cha
 int mutt_check_traditional_pgp (HEADER *, int *);
 int mutt_command_complete (char *, size_t, int, int);
 int mutt_var_value_complete (char *, size_t, int);
+#if USE_NOTMUCH
+int mutt_nm_query_complete (char *buffer, size_t len, int pos, int numtabs);
+#endif
 int mutt_complete (char *, size_t);
 int mutt_compose_attachment (BODY *a);
 int mutt_copy_body (FILE *, BODY **, BODY *);