From: Tim Stoakes Date: Sat, 22 Sep 2012 07:31:54 +0000 (+0200) Subject: Add TAB completion of tag: names in X-Git-Tag: neomutt-20160404~13^2~40 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=61388c13e383a92c3bf490b45c0eb806c023c4ca;p=neomutt Add TAB completion of tag: names in 's 'Query' prompt can now use TAB completion of tag names supplied to the 'tag:' parameter. --- diff --git a/README.notmuch b/README.notmuch index aa609e355..c6a19508a 100644 --- a/README.notmuch +++ b/README.notmuch @@ -82,6 +82,7 @@ notmuch support for mutt vfolder-from-query: - generate new virtual folder from notmuch search query - default key: X + - note: TAB completion of 'tag:' names is available modify-labels: - add or remove notmuch tags; [+] to add, - to remove diff --git a/curs_main.c b/curs_main.c index 55a19a105..25341a03c 100644 --- a/curs_main.c +++ b/curs_main.c @@ -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 23610ae5d..e07a901ff 100644 --- 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 0ff92feb5..e356ae44c 100644 --- 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 8b48ff616..faed9fbbe 100644 --- a/mutt.h +++ b/mutt.h @@ -101,6 +101,9 @@ #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 */ diff --git a/protos.h b/protos.h index f69d736ed..6bcb28c90 100644 --- 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 *);