NeoMutt  2023-05-17-16-g61469c
Teaching an old dog new tricks
DOXYGEN
lib.h File Reference

Auto-completion. More...

#include <stdbool.h>
#include <stdio.h>
#include "data.h"
+ Include dependency graph for lib.h:
+ This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

int mutt_command_complete (struct CompletionData *cd, char *buf, size_t buflen, int pos, int numtabs)
 Complete a command name. More...
 
int mutt_complete (struct CompletionData *cd, char *buf, size_t buflen)
 Attempt to complete a partial pathname. More...
 
int mutt_label_complete (struct CompletionData *cd, char *buf, size_t buflen, int numtabs)
 Complete a label name. More...
 
bool mutt_nm_query_complete (struct CompletionData *cd, char *buf, size_t buflen, int pos, int numtabs)
 Complete to the nearest notmuch tag. More...
 
bool mutt_nm_tag_complete (struct CompletionData *cd, char *buf, size_t buflen, int numtabs)
 Complete to the nearest notmuch tag. More...
 
int mutt_var_value_complete (struct CompletionData *cd, char *buf, size_t buflen, int pos)
 Complete a variable/value. More...
 

Detailed Description

Auto-completion.

Authors
  • Richard Russon

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file lib.h.

Function Documentation

◆ mutt_command_complete()

int mutt_command_complete ( struct CompletionData cd,
char *  buf,
size_t  buflen,
int  pos,
int  numtabs 
)

Complete a command name.

Parameters
cdCompletion Data
bufBuffer for the result
buflenLength of the buffer
posCursor position in the buffer
numtabsNumber of times the user has hit 'tab'
Return values
1Success, a match
0Error, no match

Definition at line 164 of file helpers.c.

166{
167 char *pt = buf;
168 int spaces; /* keep track of the number of leading spaces on the line */
169
170 SKIPWS(buf);
171 spaces = buf - pt;
172
173 pt = buf + pos - spaces;
174 while ((pt > buf) && !isspace((unsigned char) *pt))
175 pt--;
176
177 if (pt == buf) /* complete cmd */
178 {
179 /* first TAB. Collect all the matches */
180 if (numtabs == 1)
181 {
182 cd->num_matched = 0;
183 mutt_str_copy(cd->user_typed, pt, sizeof(cd->user_typed));
184 memset(cd->match_list, 0, cd->match_list_len);
185 memset(cd->completed, 0, sizeof(cd->completed));
186
187 struct Command *c = NULL;
188 for (size_t num = 0, size = commands_array(&c); num < size; num++)
189 candidate(cd, cd->user_typed, c[num].name, cd->completed, sizeof(cd->completed));
191 cd->match_list[cd->num_matched++] = cd->user_typed;
192
193 /* All matches are stored. Longest non-ambiguous string is ""
194 * i.e. don't change 'buf'. Fake successful return this time */
195 if (cd->user_typed[0] == '\0')
196 return 1;
197 }
198
199 if ((cd->completed[0] == '\0') && (cd->user_typed[0] != '\0'))
200 return 0;
201
202 /* cd->num_matched will _always_ be at least 1 since the initial
203 * user-typed string is always stored */
204 if ((numtabs == 1) && (cd->num_matched == 2))
205 {
206 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
207 }
208 else if ((numtabs > 1) && (cd->num_matched > 2))
209 {
210 /* cycle through all the matches */
211 snprintf(cd->completed, sizeof(cd->completed), "%s",
212 cd->match_list[(numtabs - 2) % cd->num_matched]);
213 }
214
215 /* return the completed command */
216 strncpy(buf, cd->completed, buflen - spaces);
217 }
218 else if (mutt_str_startswith(buf, "set") || mutt_str_startswith(buf, "unset") ||
219 mutt_str_startswith(buf, "reset") || mutt_str_startswith(buf, "toggle"))
220 { /* complete variables */
221 static const char *const prefixes[] = { "no", "inv", "?", "&", 0 };
222
223 pt++;
224 /* loop through all the possible prefixes (no, inv, ...) */
225 if (mutt_str_startswith(buf, "set"))
226 {
227 for (int num = 0; prefixes[num]; num++)
228 {
229 if (mutt_str_startswith(pt, prefixes[num]))
230 {
231 pt += mutt_str_len(prefixes[num]);
232 break;
233 }
234 }
235 }
236
237 /* first TAB. Collect all the matches */
238 if (numtabs == 1)
239 {
240 cd->num_matched = 0;
241 mutt_str_copy(cd->user_typed, pt, sizeof(cd->user_typed));
242 memset(cd->match_list, 0, cd->match_list_len);
243 memset(cd->completed, 0, sizeof(cd->completed));
244
245 struct HashElem *he = NULL;
246 struct HashElem **he_list = get_elem_list(NeoMutt->sub->cs);
247 for (size_t i = 0; he_list[i]; i++)
248 {
249 he = he_list[i];
250 const int type = DTYPE(he->type);
251
252 if ((type == DT_SYNONYM) || (type & DT_DEPRECATED))
253 continue;
254
255 candidate(cd, cd->user_typed, he->key.strkey, cd->completed, sizeof(cd->completed));
256 }
257 FREE(&he_list);
258
260 cd->match_list[cd->num_matched++] = cd->user_typed;
261
262 /* All matches are stored. Longest non-ambiguous string is ""
263 * i.e. don't change 'buf'. Fake successful return this time */
264 if (cd->user_typed[0] == '\0')
265 return 1;
266 }
267
268 if ((cd->completed[0] == 0) && cd->user_typed[0])
269 return 0;
270
271 /* cd->num_matched will _always_ be at least 1 since the initial
272 * user-typed string is always stored */
273 if ((numtabs == 1) && (cd->num_matched == 2))
274 {
275 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
276 }
277 else if ((numtabs > 1) && (cd->num_matched > 2))
278 {
279 /* cycle through all the matches */
280 snprintf(cd->completed, sizeof(cd->completed), "%s",
281 cd->match_list[(numtabs - 2) % cd->num_matched]);
282 }
283
284 strncpy(pt, cd->completed, buf + buflen - pt - spaces);
285 }
286 else if (mutt_str_startswith(buf, "exec"))
287 {
288 const enum MenuType mtype = menu_get_current_type();
289 const struct MenuFuncOp *funcs = km_get_table(mtype);
290 if (!funcs && (mtype != MENU_PAGER))
291 funcs = OpGeneric;
292
293 pt++;
294 /* first TAB. Collect all the matches */
295 if (numtabs == 1)
296 {
297 cd->num_matched = 0;
298 mutt_str_copy(cd->user_typed, pt, sizeof(cd->user_typed));
299 memset(cd->match_list, 0, cd->match_list_len);
300 memset(cd->completed, 0, sizeof(cd->completed));
301 for (int num = 0; funcs[num].name; num++)
302 candidate(cd, cd->user_typed, funcs[num].name, cd->completed, sizeof(cd->completed));
303 /* try the generic menu */
304 if ((mtype != MENU_PAGER) && (mtype != MENU_GENERIC))
305 {
306 funcs = OpGeneric;
307 for (int num = 0; funcs[num].name; num++)
308 candidate(cd, cd->user_typed, funcs[num].name, cd->completed,
309 sizeof(cd->completed));
310 }
312 cd->match_list[cd->num_matched++] = cd->user_typed;
313
314 /* All matches are stored. Longest non-ambiguous string is ""
315 * i.e. don't change 'buf'. Fake successful return this time */
316 if (cd->user_typed[0] == '\0')
317 return 1;
318 }
319
320 if ((cd->completed[0] == '\0') && (cd->user_typed[0] != '\0'))
321 return 0;
322
323 /* cd->num_matched will _always_ be at least 1 since the initial
324 * user-typed string is always stored */
325 if ((numtabs == 1) && (cd->num_matched == 2))
326 {
327 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
328 }
329 else if ((numtabs > 1) && (cd->num_matched > 2))
330 {
331 /* cycle through all the matches */
332 snprintf(cd->completed, sizeof(cd->completed), "%s",
333 cd->match_list[(numtabs - 2) % cd->num_matched]);
334 }
335
336 strncpy(pt, cd->completed, buf + buflen - pt - spaces);
337 }
338 else
339 {
340 return 0;
341 }
342
343 return 1;
344}
static bool candidate(struct CompletionData *cd, char *user, const char *src, char *dest, size_t dlen)
Helper function for completion.
Definition: helpers.c:76
static void matches_ensure_morespace(struct CompletionData *cd, int new_size)
Allocate more space for auto-completion.
Definition: helpers.c:52
size_t commands_array(struct Command **first)
Get Commands array.
Definition: command.c:75
const struct MenuFuncOp OpGeneric[]
Functions for the Generic Menu.
Definition: functions.c:288
const struct MenuFuncOp * km_get_table(enum MenuType mtype)
Lookup a Menu's functions.
Definition: keymap.c:1248
#define FREE(x)
Definition: memory.h:43
enum MenuType menu_get_current_type(void)
Get the type of the current Window.
Definition: menu.c:85
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:228
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:568
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:653
#define SKIPWS(ch)
Definition: string2.h:45
int match_list_len
Enough space for all of the config items.
Definition: data.h:37
char user_typed[1024]
Initial string that starts completion.
Definition: data.h:33
char completed[256]
Completed string (command or variable)
Definition: data.h:35
int num_matched
Number of matches for completion.
Definition: data.h:34
const char ** match_list
Matching strings.
Definition: data.h:36
struct ConfigSet * cs
Parent ConfigSet.
Definition: subset.h:51
The item stored in a Hash Table.
Definition: hash.h:44
union HashKey key
Key representing the data.
Definition: hash.h:46
int type
Type of data stored in Hash Table, e.g. DT_STRING.
Definition: hash.h:45
Mapping between a function and an operation.
Definition: keymap.h:92
const char * name
Name of the function.
Definition: keymap.h:93
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
struct HashElem ** get_elem_list(struct ConfigSet *cs)
Create a sorted list of all config items.
Definition: subset.c:76
MenuType
Types of GUI selections.
Definition: type.h:36
@ MENU_GENERIC
Generic selection list.
Definition: type.h:45
@ MENU_PAGER
Pager pager (email viewer)
Definition: type.h:54
#define DTYPE(x)
Mask for the Data Type.
Definition: types.h:45
#define DT_DEPRECATED
Config item shouldn't be used any more.
Definition: types.h:77
#define DT_SYNONYM
synonym for another variable
Definition: types.h:42
const char * strkey
String key.
Definition: hash.h:36
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_complete()

int mutt_complete ( struct CompletionData cd,
char *  buf,
size_t  buflen 
)

Attempt to complete a partial pathname.

Parameters
cdCompletion Data
bufBuffer containing pathname
buflenLength of buffer
Return values
0Ok
-1No matches

Given a partial pathname, fill in as much of the rest of the path as is unique.

Definition at line 58 of file complete.c.

59{
60 const char *p = NULL;
61 DIR *dir = NULL;
62 struct dirent *de = NULL;
63 int init = 0;
64 size_t len;
65 struct Buffer *dirpart = NULL;
66 struct Buffer *exp_dirpart = NULL;
67 struct Buffer *filepart = NULL;
68 struct Buffer *tmp = NULL;
69#ifdef USE_IMAP
70 struct Buffer *imap_path = NULL;
71 int rc;
72#endif
73
74 mutt_debug(LL_DEBUG2, "completing %s\n", buf);
75
76#ifdef USE_NNTP
77 if (OptNews)
78 return nntp_complete(buf, buflen);
79#endif
80
81 const char *const c_spool_file = cs_subset_string(NeoMutt->sub, "spool_file");
82 const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
83#ifdef USE_IMAP
84 imap_path = buf_pool_get();
85 /* we can use '/' as a delimiter, imap_complete rewrites it */
86 if ((*buf == '=') || (*buf == '+') || (*buf == '!'))
87 {
88 if (*buf == '!')
89 p = NONULL(c_spool_file);
90 else
91 p = NONULL(c_folder);
92
93 buf_concat_path(imap_path, p, buf + 1);
94 }
95 else
96 {
97 buf_strcpy(imap_path, buf);
98 }
99
100 if (imap_path_probe(buf_string(imap_path), NULL) == MUTT_IMAP)
101 {
102 rc = imap_complete(buf, buflen, buf_string(imap_path));
103 buf_pool_release(&imap_path);
104 return rc;
105 }
106
107 buf_pool_release(&imap_path);
108#endif
109
110 dirpart = buf_pool_get();
111 exp_dirpart = buf_pool_get();
112 filepart = buf_pool_get();
113 tmp = buf_pool_get();
114
115 if ((*buf == '=') || (*buf == '+') || (*buf == '!'))
116 {
117 buf_addch(dirpart, *buf);
118 if (*buf == '!')
119 buf_strcpy(exp_dirpart, NONULL(c_spool_file));
120 else
121 buf_strcpy(exp_dirpart, NONULL(c_folder));
122 p = strrchr(buf, '/');
123 if (p)
124 {
125 buf_concatn_path(tmp, buf_string(exp_dirpart), buf_len(exp_dirpart),
126 buf + 1, (size_t) (p - buf - 1));
127 buf_copy(exp_dirpart, tmp);
128 buf_substrcpy(dirpart, buf, p + 1);
129 buf_strcpy(filepart, p + 1);
130 }
131 else
132 {
133 buf_strcpy(filepart, buf + 1);
134 }
136 }
137 else
138 {
139 p = strrchr(buf, '/');
140 if (p)
141 {
142 if (p == buf) /* absolute path */
143 {
144 p = buf + 1;
145 buf_strcpy(dirpart, "/");
146 buf_strcpy(filepart, p);
148 }
149 else
150 {
151 buf_substrcpy(dirpart, buf, p);
152 buf_strcpy(filepart, p + 1);
153 buf_copy(exp_dirpart, dirpart);
154 buf_expand_path(exp_dirpart);
156 }
157 }
158 else
159 {
160 /* no directory name, so assume current directory. */
161 buf_strcpy(filepart, buf);
163 }
164 }
165
166 if (!dir)
167 {
168 mutt_debug(LL_DEBUG1, "%s: %s (errno %d)\n", buf_string(exp_dirpart),
169 strerror(errno), errno);
170 goto cleanup;
171 }
172
173 /* special case to handle when there is no filepart yet. find the first
174 * file/directory which is not "." or ".." */
175 len = buf_len(filepart);
176 if (len == 0)
177 {
178 while ((de = readdir(dir)))
179 {
180 if (!mutt_str_equal(".", de->d_name) && !mutt_str_equal("..", de->d_name))
181 {
182 buf_strcpy(filepart, de->d_name);
183 init++;
184 break;
185 }
186 }
187 }
188
189 while ((de = readdir(dir)))
190 {
191 if (mutt_strn_equal(de->d_name, buf_string(filepart), len))
192 {
193 if (init)
194 {
195 char *cp = filepart->data;
196
197 for (int i = 0; (*cp != '\0') && (de->d_name[i] != '\0'); i++, cp++)
198 {
199 if (*cp != de->d_name[i])
200 break;
201 }
202 *cp = '\0';
203 buf_fix_dptr(filepart);
204 }
205 else
206 {
207 struct stat st = { 0 };
208
209 buf_strcpy(filepart, de->d_name);
210
211 /* check to see if it is a directory */
212 if (buf_is_empty(dirpart))
213 {
214 buf_reset(tmp);
215 }
216 else
217 {
218 buf_copy(tmp, exp_dirpart);
219 buf_addch(tmp, '/');
220 }
221 buf_addstr(tmp, buf_string(filepart));
222 if ((stat(buf_string(tmp), &st) != -1) && (st.st_mode & S_IFDIR))
223 buf_addch(filepart, '/');
224 init = 1;
225 }
226 }
227 }
228 closedir(dir);
229
230 if (buf_is_empty(dirpart))
231 {
232 mutt_str_copy(buf, buf_string(filepart), buflen);
233 }
234 else
235 {
236 mutt_str_copy(buf, buf_string(dirpart), buflen);
237 if (!mutt_str_equal("/", buf_string(dirpart)) &&
238 (buf_string(dirpart)[0] != '=') && (buf_string(dirpart)[0] != '+'))
239 {
240 mutt_str_copy(buf + strlen(buf), "/", buflen - strlen(buf));
241 }
242 mutt_str_copy(buf + strlen(buf), buf_string(filepart), buflen - strlen(buf));
243 }
244
245cleanup:
246 buf_pool_release(&dirpart);
247 buf_pool_release(&exp_dirpart);
248 buf_pool_release(&filepart);
249 buf_pool_release(&tmp);
250
251 return init ? 0 : -1;
252}
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:414
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:86
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:301
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:192
size_t buf_concatn_path(struct Buffer *buf, const char *dir, size_t dirlen, const char *fname, size_t fnamelen)
Join a directory name and a filename.
Definition: buffer.c:469
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:251
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:236
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:370
size_t buf_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer's contents to another Buffer.
Definition: buffer.c:505
size_t buf_concat_path(struct Buffer *buf, const char *dir, const char *fname)
Join a directory name and a filename.
Definition: buffer.c:432
size_t buf_substrcpy(struct Buffer *buf, const char *beg, const char *end)
Copy a partial string into a Buffer.
Definition: buffer.c:400
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:78
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
DIR * mutt_file_opendir(const char *path, enum MuttOpenDirMode mode)
Open a directory.
Definition: file.c:614
@ MUTT_OPENDIR_NONE
Plain opendir()
Definition: file.h:73
bool OptNews
(pseudo) used to change reader mode
Definition: globals.c:79
#define mutt_debug(LEVEL,...)
Definition: logging2.h:84
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe() -.
Definition: imap.c:2441
int imap_complete(char *buf, size_t buflen, const char *path)
Try to complete an IMAP folder path.
Definition: imap.c:1367
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:41
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:40
@ MUTT_IMAP
'IMAP' Mailbox type
Definition: mailbox.h:50
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:798
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:497
void buf_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:333
int nntp_complete(char *buf, size_t buflen)
Auto-complete NNTP newsgroups.
Definition: complete.c:47
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:106
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:119
#define NONULL(x)
Definition: string2.h:37
String manipulation buffer.
Definition: buffer.h:34
char * data
Pointer to data.
Definition: buffer.h:35
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_label_complete()

int mutt_label_complete ( struct CompletionData cd,
char *  buf,
size_t  buflen,
int  numtabs 
)

Complete a label name.

Parameters
cdCompletion Data
bufBuffer for the result
buflenLength of the buffer
numtabsNumber of times the user has hit 'tab'
Return values
1Success, a match
0Error, no match

Definition at line 368 of file helpers.c.

369{
370 char *pt = buf;
371 int spaces; /* keep track of the number of leading spaces on the line */
372
373 struct Mailbox *m_cur = get_current_mailbox();
374 if (!m_cur || !m_cur->label_hash)
375 return 0;
376
377 SKIPWS(buf);
378 spaces = buf - pt;
379
380 /* first TAB. Collect all the matches */
381 if (numtabs == 1)
382 {
383 struct HashElem *he = NULL;
384 struct HashWalkState hws = { 0 };
385
386 cd->num_matched = 0;
387 mutt_str_copy(cd->user_typed, buf, sizeof(cd->user_typed));
388 memset(cd->match_list, 0, cd->match_list_len);
389 memset(cd->completed, 0, sizeof(cd->completed));
390 while ((he = mutt_hash_walk(m_cur->label_hash, &hws)))
391 candidate(cd, cd->user_typed, he->key.strkey, cd->completed, sizeof(cd->completed));
393 qsort(cd->match_list, cd->num_matched, sizeof(char *), label_sort);
394 cd->match_list[cd->num_matched++] = cd->user_typed;
395
396 /* All matches are stored. Longest non-ambiguous string is ""
397 * i.e. don't change 'buf'. Fake successful return this time */
398 if (cd->user_typed[0] == '\0')
399 return 1;
400 }
401
402 if ((cd->completed[0] == '\0') && (cd->user_typed[0] != '\0'))
403 return 0;
404
405 /* cd->num_matched will _always_ be at least 1 since the initial
406 * user-typed string is always stored */
407 if ((numtabs == 1) && (cd->num_matched == 2))
408 {
409 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
410 }
411 else if ((numtabs > 1) && (cd->num_matched > 2))
412 {
413 /* cycle through all the matches */
414 snprintf(cd->completed, sizeof(cd->completed), "%s",
415 cd->match_list[(numtabs - 2) % cd->num_matched]);
416 }
417
418 /* return the completed label */
419 strncpy(buf, cd->completed, buflen - spaces);
420
421 return 1;
422}
static int label_sort(const void *a, const void *b)
Sort two label strings.
Definition: helpers.c:354
struct HashElem * mutt_hash_walk(const struct HashTable *table, struct HashWalkState *state)
Iterate through all the HashElem's in a Hash Table.
Definition: hash.c:489
struct Mailbox * get_current_mailbox(void)
Get the current Mailbox.
Definition: index.c:662
Cursor to iterate through a Hash Table.
Definition: hash.h:133
A mailbox.
Definition: mailbox.h:79
struct HashTable * label_hash
Hash Table: "x-labels" -> Email.
Definition: mailbox.h:125
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_nm_query_complete()

bool mutt_nm_query_complete ( struct CompletionData cd,
char *  buf,
size_t  buflen,
int  pos,
int  numtabs 
)

Complete to the nearest notmuch tag.

Parameters
cdCompletion Data
bufBuffer for the result
buflenLength of the buffer
posCursor position in the buffer
numtabsNumber of times the user has hit 'tab'
Return values
trueSuccess, a match
falseError, no match

Complete the nearest "tag:"-prefixed string previous to pos.

Definition at line 437 of file helpers.c.

439{
440 char *pt = buf;
441 int spaces;
442
443 SKIPWS(buf);
444 spaces = buf - pt;
445
446 pt = (char *) mutt_strn_rfind((char *) buf, pos, "tag:");
447 if (pt)
448 {
449 pt += 4;
450 if (numtabs == 1)
451 {
452 /* First TAB. Collect all the matches */
453 complete_all_nm_tags(cd, pt);
454
455 /* All matches are stored. Longest non-ambiguous string is ""
456 * i.e. don't change 'buf'. Fake successful return this time. */
457 if (cd->user_typed[0] == '\0')
458 return true;
459 }
460
461 if ((cd->completed[0] == '\0') && (cd->user_typed[0] != '\0'))
462 return false;
463
464 /* cd->num_matched will _always_ be at least 1 since the initial
465 * user-typed string is always stored */
466 if ((numtabs == 1) && (cd->num_matched == 2))
467 {
468 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
469 }
470 else if ((numtabs > 1) && (cd->num_matched > 2))
471 {
472 /* cycle through all the matches */
473 snprintf(cd->completed, sizeof(cd->completed), "%s",
474 cd->match_list[(numtabs - 2) % cd->num_matched]);
475 }
476
477 /* return the completed query */
478 strncpy(pt, cd->completed, buf + buflen - pt - spaces);
479 }
480 else
481 {
482 return false;
483 }
484
485 return true;
486}
static int complete_all_nm_tags(struct CompletionData *cd, const char *pt)
Pass a list of Notmuch tags to the completion code.
Definition: helpers.c:110
const char * mutt_strn_rfind(const char *haystack, size_t haystack_length, const char *needle)
Find last instance of a substring.
Definition: string.c:847
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_nm_tag_complete()

bool mutt_nm_tag_complete ( struct CompletionData cd,
char *  buf,
size_t  buflen,
int  numtabs 
)

Complete to the nearest notmuch tag.

Parameters
cdCompletion Data
bufBuffer for the result
buflenLength of the buffer
numtabsNumber of times the user has hit 'tab'
Return values
trueSuccess, a match
falseError, no match

Complete the nearest "+" or "-" -prefixed string previous to pos.

Definition at line 501 of file helpers.c.

502{
503 if (!buf)
504 return false;
505
506 char *pt = buf;
507
508 /* Only examine the last token */
509 char *last_space = strrchr(buf, ' ');
510 if (last_space)
511 pt = (last_space + 1);
512
513 /* Skip the +/- */
514 if ((pt[0] == '+') || (pt[0] == '-'))
515 pt++;
516
517 if (numtabs == 1)
518 {
519 /* First TAB. Collect all the matches */
520 complete_all_nm_tags(cd, pt);
521
522 /* All matches are stored. Longest non-ambiguous string is ""
523 * i.e. don't change 'buf'. Fake successful return this time. */
524 if (cd->user_typed[0] == '\0')
525 return true;
526 }
527
528 if ((cd->completed[0] == '\0') && (cd->user_typed[0] != '\0'))
529 return false;
530
531 /* cd->num_matched will _always_ be at least 1 since the initial
532 * user-typed string is always stored */
533 if ((numtabs == 1) && (cd->num_matched == 2))
534 {
535 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
536 }
537 else if ((numtabs > 1) && (cd->num_matched > 2))
538 {
539 /* cycle through all the matches */
540 snprintf(cd->completed, sizeof(cd->completed), "%s",
541 cd->match_list[(numtabs - 2) % cd->num_matched]);
542 }
543
544 /* return the completed query */
545 strncpy(pt, cd->completed, buf + buflen - pt);
546
547 return true;
548}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_var_value_complete()

int mutt_var_value_complete ( struct CompletionData cd,
char *  buf,
size_t  buflen,
int  pos 
)

Complete a variable/value.

Parameters
cdCompletion Data
bufBuffer for the result
buflenLength of the buffer
posCursor position in the buffer
Return values
1Success
0Failure

Definition at line 560 of file helpers.c.

561{
562 char *pt = buf;
563
564 if (buf[0] == '\0')
565 return 0;
566
567 SKIPWS(buf);
568 const int spaces = buf - pt;
569
570 pt = buf + pos - spaces;
571 while ((pt > buf) && !isspace((unsigned char) *pt))
572 pt--;
573 pt++; /* move past the space */
574 if (*pt == '=') /* abort if no var before the '=' */
575 return 0;
576
577 if (mutt_str_startswith(buf, "set"))
578 {
579 char var[256] = { 0 };
580 mutt_str_copy(var, pt, sizeof(var));
581 /* ignore the trailing '=' when comparing */
582 int vlen = mutt_str_len(var);
583 if (vlen == 0)
584 return 0;
585
586 var[vlen - 1] = '\0';
587
588 struct HashElem *he = cs_subset_lookup(NeoMutt->sub, var);
589 if (!he)
590 return 0; /* no such variable. */
591
592 struct Buffer value = buf_make(256);
593 struct Buffer pretty = buf_make(256);
594 int rc = cs_subset_he_string_get(NeoMutt->sub, he, &value);
595 if (CSR_RESULT(rc) == CSR_SUCCESS)
596 {
597 pretty_var(value.data, &pretty);
598 snprintf(pt, buflen - (pt - buf), "%s=%s", var, pretty.data);
599 buf_dealloc(&value);
600 buf_dealloc(&pretty);
601 return 0;
602 }
603 buf_dealloc(&value);
604 buf_dealloc(&pretty);
605 return 1;
606 }
607 return 0;
608}
void buf_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:352
struct Buffer buf_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:68
#define CSR_RESULT(x)
Definition: set.h:52
#define CSR_SUCCESS
Action completed successfully.
Definition: set.h:35
size_t pretty_var(const char *str, struct Buffer *buf)
Escape and stringify a config item value.
Definition: dump.c:83
int cs_subset_he_string_get(const struct ConfigSubset *sub, struct HashElem *he, struct Buffer *result)
Get a config item as a string.
Definition: subset.c:359
struct HashElem * cs_subset_lookup(const struct ConfigSubset *sub, const char *name)
Find an inherited config item.
Definition: subset.c:184
+ Here is the call graph for this function:
+ Here is the caller graph for this function: