NeoMutt  2025-09-05-7-geaa2bd
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
helpers.c File Reference

Auto-completion helpers. More...

#include "config.h"
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include "mutt/lib.h"
#include "config/lib.h"
#include "core/lib.h"
#include "gui/lib.h"
#include "lib.h"
#include "editor/lib.h"
#include "index/lib.h"
#include "key/lib.h"
#include "menu/lib.h"
#include "compapi.h"
#include "data.h"
+ Include dependency graph for helpers.c:

Go to the source code of this file.

Functions

void matches_ensure_morespace (struct CompletionData *cd, int new_size)
 Allocate more space for auto-completion.
 
bool candidate (struct CompletionData *cd, char *user, const char *src, char *dest, size_t dlen)
 Helper function for completion.
 
int mutt_command_complete (struct CompletionData *cd, struct Buffer *buf, int pos, int numtabs)
 Complete a command name.
 
static int label_sort (const void *a, const void *b, void *sdata)
 Compare two label strings - Implements sort_t -.
 
int mutt_label_complete (struct CompletionData *cd, struct Buffer *buf, int numtabs)
 Complete a label name.
 
int mutt_var_value_complete (struct CompletionData *cd, struct Buffer *buf, int pos)
 Complete a variable/value.
 
enum FunctionRetval complete_command (struct EnterWindowData *wdata, int op)
 Complete a NeoMutt Command - Implements CompleteOps::complete() -.
 
enum FunctionRetval complete_label (struct EnterWindowData *wdata, int op)
 Complete a label - Implements CompleteOps::complete() -.
 

Variables

const struct CompleteOps CompleteCommandOps
 Auto-Completion of Commands.
 
const struct CompleteOps CompleteLabelOps
 Auto-Completion of Labels.
 

Detailed Description

Auto-completion helpers.

Authors
  • Richard Russon
  • Anna Figueiredo Gomes
  • Dennis Schön

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 helpers.c.

Function Documentation

◆ matches_ensure_morespace()

void matches_ensure_morespace ( struct CompletionData cd,
int  new_size 
)

Allocate more space for auto-completion.

Parameters
cdCompletion Data
new_sizeSpace required

Definition at line 53 of file helpers.c.

54{
55 if (new_size <= (cd->match_list_len - 2))
56 return;
57
58 new_size = ROUND_UP(new_size + 2, 512);
59
60 MUTT_MEM_REALLOC(&cd->match_list, new_size, const char *);
61 memset(&cd->match_list[cd->match_list_len], 0, new_size - cd->match_list_len);
62
63 cd->match_list_len = new_size;
64}
#define ROUND_UP(NUM, STEP)
Definition: memory.h:41
#define MUTT_MEM_REALLOC(pptr, n, type)
Definition: memory.h:50
int match_list_len
Enough space for all of the config items.
Definition: data.h:38
const char ** match_list
Matching strings.
Definition: data.h:37
+ Here is the caller graph for this function:

◆ candidate()

bool candidate ( struct CompletionData cd,
char *  user,
const char *  src,
char *  dest,
size_t  dlen 
)

Helper function for completion.

Parameters
cdCompletion Data
userUser entered data for completion
srcCandidate for completion
destCompletion result gets here
dlenLength of dest buffer
Return values
trueIf candidate string matches

Changes the dest buffer if necessary/possible to aid completion.

Definition at line 77 of file helpers.c.

78{
79 if (!dest || !user || !src)
80 return false;
81
82 if (strstr(src, user) != src)
83 return false;
84
86 cd->match_list[cd->num_matched++] = src;
87 if (dest[0] == '\0')
88 {
89 mutt_str_copy(dest, src, dlen);
90 }
91 else
92 {
93 int l;
94 for (l = 0; (src[l] != '\0') && (src[l] == dest[l]); l++)
95 ; // do nothing
96
97 dest[l] = '\0';
98 }
99 return true;
100}
void matches_ensure_morespace(struct CompletionData *cd, int new_size)
Allocate more space for auto-completion.
Definition: helpers.c:53
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:580
int num_matched
Number of matches for completion.
Definition: data.h:35
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_command_complete()

int mutt_command_complete ( struct CompletionData cd,
struct Buffer buf,
int  pos,
int  numtabs 
)

Complete a command name.

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

Definition at line 111 of file helpers.c.

112{
113 char *pt = buf->data;
114 int spaces; /* keep track of the number of leading spaces on the line */
115
116 SKIPWS(pt);
117 spaces = pt - buf->data;
118
119 pt = buf->data + pos - spaces;
120 while ((pt > buf->data) && !mutt_isspace(*pt))
121 pt--;
122
123 if (pt == buf->data) /* complete cmd */
124 {
125 /* first TAB. Collect all the matches */
126 if (numtabs == 1)
127 {
128 cd->num_matched = 0;
129 mutt_str_copy(cd->user_typed, pt, sizeof(cd->user_typed));
130 memset(cd->match_list, 0, cd->match_list_len);
131 memset(cd->completed, 0, sizeof(cd->completed));
132
133 const struct Command **cp = NULL;
135 {
136 const struct Command *cmd = *cp;
137
138 candidate(cd, cd->user_typed, cmd->name, cd->completed, sizeof(cd->completed));
139 }
140
142 cd->match_list[cd->num_matched++] = cd->user_typed;
143
144 /* All matches are stored. Longest non-ambiguous string is ""
145 * i.e. don't change 'buf'. Fake successful return this time */
146 if (cd->user_typed[0] == '\0')
147 return 1;
148 }
149
150 if ((cd->completed[0] == '\0') && (cd->user_typed[0] != '\0'))
151 return 0;
152
153 /* cd->num_matched will _always_ be at least 1 since the initial
154 * user-typed string is always stored */
155 if ((numtabs == 1) && (cd->num_matched == 2))
156 {
157 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
158 }
159 else if ((numtabs > 1) && (cd->num_matched > 2))
160 {
161 /* cycle through all the matches */
162 snprintf(cd->completed, sizeof(cd->completed), "%s",
163 cd->match_list[(numtabs - 2) % cd->num_matched]);
164 }
165
166 /* return the completed command */
167 buf_strcpy(buf, cd->completed);
168 }
169 else if (buf_startswith(buf, "set") || buf_startswith(buf, "unset") ||
170 buf_startswith(buf, "reset") || buf_startswith(buf, "toggle"))
171 { /* complete variables */
172 static const char *const prefixes[] = { "no", "inv", "?", "&", 0 };
173
174 pt++;
175 /* loop through all the possible prefixes (no, inv, ...) */
176 if (buf_startswith(buf, "set"))
177 {
178 for (int num = 0; prefixes[num]; num++)
179 {
180 if (mutt_str_startswith(pt, prefixes[num]))
181 {
182 pt += mutt_str_len(prefixes[num]);
183 break;
184 }
185 }
186 }
187
188 /* first TAB. Collect all the matches */
189 if (numtabs == 1)
190 {
191 cd->num_matched = 0;
192 mutt_str_copy(cd->user_typed, pt, sizeof(cd->user_typed));
193 memset(cd->match_list, 0, cd->match_list_len);
194 memset(cd->completed, 0, sizeof(cd->completed));
195
196 struct HashElemArray hea = get_elem_list(NeoMutt->sub->cs, GEL_ALL_CONFIG);
197 struct HashElem **hep = NULL;
198 ARRAY_FOREACH(hep, &hea)
199 {
200 struct HashElem *he = *hep;
201 const int type = CONFIG_TYPE(he->type);
202
204 continue;
205
206 candidate(cd, cd->user_typed, he->key.strkey, cd->completed, sizeof(cd->completed));
207 }
208 ARRAY_FREE(&hea);
209
211 cd->match_list[cd->num_matched++] = cd->user_typed;
212
213 /* All matches are stored. Longest non-ambiguous string is ""
214 * i.e. don't change 'buf'. Fake successful return this time */
215 if (cd->user_typed[0] == '\0')
216 return 1;
217 }
218
219 if ((cd->completed[0] == 0) && cd->user_typed[0])
220 return 0;
221
222 /* cd->num_matched will _always_ be at least 1 since the initial
223 * user-typed string is always stored */
224 if ((numtabs == 1) && (cd->num_matched == 2))
225 {
226 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
227 }
228 else if ((numtabs > 1) && (cd->num_matched > 2))
229 {
230 /* cycle through all the matches */
231 snprintf(cd->completed, sizeof(cd->completed), "%s",
232 cd->match_list[(numtabs - 2) % cd->num_matched]);
233 }
234
235 strncpy(pt, cd->completed, buf->data + buf->dsize - pt - spaces);
236 buf_fix_dptr(buf);
237 }
238 else if (buf_startswith(buf, "exec"))
239 {
240 const enum MenuType mtype = menu_get_current_type();
241 const struct MenuFuncOp *funcs = km_get_table(mtype);
242 if (!funcs && (mtype != MENU_PAGER))
243 funcs = OpGeneric;
244
245 pt++;
246 /* first TAB. Collect all the matches */
247 if (numtabs == 1)
248 {
249 cd->num_matched = 0;
250 mutt_str_copy(cd->user_typed, pt, sizeof(cd->user_typed));
251 memset(cd->match_list, 0, cd->match_list_len);
252 memset(cd->completed, 0, sizeof(cd->completed));
253 for (int num = 0; funcs[num].name; num++)
254 candidate(cd, cd->user_typed, funcs[num].name, cd->completed, sizeof(cd->completed));
255 /* try the generic menu */
256 if ((mtype != MENU_PAGER) && (mtype != MENU_GENERIC))
257 {
258 funcs = OpGeneric;
259 for (int num = 0; funcs[num].name; num++)
260 candidate(cd, cd->user_typed, funcs[num].name, cd->completed,
261 sizeof(cd->completed));
262 }
264 cd->match_list[cd->num_matched++] = cd->user_typed;
265
266 /* All matches are stored. Longest non-ambiguous string is ""
267 * i.e. don't change 'buf'. Fake successful return this time */
268 if (cd->user_typed[0] == '\0')
269 return 1;
270 }
271
272 if ((cd->completed[0] == '\0') && (cd->user_typed[0] != '\0'))
273 return 0;
274
275 /* cd->num_matched will _always_ be at least 1 since the initial
276 * user-typed string is always stored */
277 if ((numtabs == 1) && (cd->num_matched == 2))
278 {
279 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
280 }
281 else if ((numtabs > 1) && (cd->num_matched > 2))
282 {
283 /* cycle through all the matches */
284 snprintf(cd->completed, sizeof(cd->completed), "%s",
285 cd->match_list[(numtabs - 2) % cd->num_matched]);
286 }
287
288 strncpy(pt, cd->completed, buf->data + buf->dsize - pt - spaces);
289 buf_fix_dptr(buf);
290 }
291 else
292 {
293 return 0;
294 }
295
296 return 1;
297}
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition: array.h:214
#define ARRAY_FREE(head)
Release all memory.
Definition: array.h:204
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:182
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:395
size_t buf_startswith(const struct Buffer *buf, const char *prefix)
Check whether a buffer starts with a prefix.
Definition: buffer.c:707
bool candidate(struct CompletionData *cd, char *user, const char *src, char *dest, size_t dlen)
Helper function for completion.
Definition: helpers.c:77
bool mutt_isspace(int arg)
Wrapper for isspace(3)
Definition: ctype.c:95
const struct MenuFuncOp OpGeneric[]
Functions for the Generic Menu.
Definition: functions.c:69
const struct MenuFuncOp * km_get_table(enum MenuType mtype)
Lookup a Menu's functions.
Definition: lib.c:566
enum MenuType menu_get_current_type(void)
Get the type of the current Window.
Definition: menu.c:89
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:231
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:497
#define SKIPWS(ch)
Definition: string2.h:44
size_t dsize
Length of data.
Definition: buffer.h:39
char * data
Pointer to data.
Definition: buffer.h:37
const char * name
Name of the command.
Definition: command.h:51
char user_typed[1024]
Initial string that starts completion.
Definition: data.h:34
char completed[256]
Completed string (command or variable)
Definition: data.h:36
struct ConfigSet * cs
Parent ConfigSet.
Definition: subset.h:50
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: lib.h:112
const char * name
Name of the function.
Definition: lib.h:113
Container for Accounts, Notifications.
Definition: neomutt.h:43
struct CommandArray commands
NeoMutt commands.
Definition: neomutt.h:51
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:47
struct HashElemArray get_elem_list(struct ConfigSet *cs, enum GetElemListFlags flags)
Create a sorted list of all config items.
Definition: subset.c:80
@ GEL_ALL_CONFIG
All the normal config (no synonyms or deprecated)
Definition: subset.h:81
MenuType
Types of GUI selections.
Definition: type.h:36
@ MENU_GENERIC
Generic selection list.
Definition: type.h:46
@ MENU_PAGER
Pager pager (email viewer)
Definition: type.h:48
#define CONFIG_TYPE(t)
Definition: types.h:49
#define D_INTERNAL_DEPRECATED
Config item shouldn't be used any more.
Definition: types.h:87
@ DT_SYNONYM
synonym for another variable
Definition: types.h:45
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_label_complete()

int mutt_label_complete ( struct CompletionData cd,
struct Buffer buf,
int  numtabs 
)

Complete a label name.

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

Definition at line 315 of file helpers.c.

316{
317 char *pt = buf->data;
318
319 struct Mailbox *m_cur = get_current_mailbox();
320 if (!m_cur || !m_cur->label_hash)
321 return 0;
322
323 SKIPWS(pt);
324
325 /* first TAB. Collect all the matches */
326 if (numtabs == 1)
327 {
328 struct HashElem *he = NULL;
329 struct HashWalkState hws = { 0 };
330
331 cd->num_matched = 0;
332 mutt_str_copy(cd->user_typed, buf_string(buf), sizeof(cd->user_typed));
333 memset(cd->match_list, 0, cd->match_list_len);
334 memset(cd->completed, 0, sizeof(cd->completed));
335 while ((he = mutt_hash_walk(m_cur->label_hash, &hws)))
336 candidate(cd, cd->user_typed, he->key.strkey, cd->completed, sizeof(cd->completed));
338 mutt_qsort_r(cd->match_list, cd->num_matched, sizeof(char *), label_sort, NULL);
339 cd->match_list[cd->num_matched++] = cd->user_typed;
340
341 /* All matches are stored. Longest non-ambiguous string is ""
342 * i.e. don't change 'buf'. Fake successful return this time */
343 if (cd->user_typed[0] == '\0')
344 return 1;
345 }
346
347 if ((cd->completed[0] == '\0') && (cd->user_typed[0] != '\0'))
348 return 0;
349
350 /* cd->num_matched will _always_ be at least 1 since the initial
351 * user-typed string is always stored */
352 if ((numtabs == 1) && (cd->num_matched == 2))
353 {
354 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
355 }
356 else if ((numtabs > 1) && (cd->num_matched > 2))
357 {
358 /* cycle through all the matches */
359 snprintf(cd->completed, sizeof(cd->completed), "%s",
360 cd->match_list[(numtabs - 2) % cd->num_matched]);
361 }
362
363 /* return the completed label */
364 buf_strcpy(buf, cd->completed);
365
366 return 1;
367}
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:96
static int label_sort(const void *a, const void *b, void *sdata)
Compare two label strings - Implements sort_t -.
Definition: helpers.c:302
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:721
void mutt_qsort_r(void *base, size_t nmemb, size_t size, sort_t compar, void *sdata)
Sort an array, where the comparator has access to opaque data rather than requiring global variables.
Definition: qsort_r.c:67
Cursor to iterate through a Hash Table.
Definition: hash.h:134
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_var_value_complete()

int mutt_var_value_complete ( struct CompletionData cd,
struct Buffer buf,
int  pos 
)

Complete a variable/value.

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

Definition at line 377 of file helpers.c.

378{
379 char *pt = buf->data;
380
381 if (pt[0] == '\0')
382 return 0;
383
384 SKIPWS(pt);
385 const int spaces = pt - buf->data;
386
387 pt = buf->data + pos - spaces;
388 while ((pt > buf->data) && !mutt_isspace(*pt))
389 pt--;
390 pt++; /* move past the space */
391 if (*pt == '=') /* abort if no var before the '=' */
392 return 0;
393
394 if (buf_startswith(buf, "set"))
395 {
396 char var[256] = { 0 };
397 mutt_str_copy(var, pt, sizeof(var));
398 /* ignore the trailing '=' when comparing */
399 int vlen = mutt_str_len(var);
400 if (vlen == 0)
401 return 0;
402
403 var[vlen - 1] = '\0';
404
405 struct HashElem *he = cs_subset_lookup(NeoMutt->sub, var);
406 if (!he)
407 return 0; /* no such variable. */
408
409 struct Buffer *value = buf_pool_get();
410 struct Buffer *pretty = buf_pool_get();
411 int rc = cs_subset_he_string_get(NeoMutt->sub, he, value);
412 if (CSR_RESULT(rc) == CSR_SUCCESS)
413 {
414 pretty_var(value->data, pretty);
415 snprintf(pt, buf->dsize - (pt - buf->data), "%s=%s", var, pretty->data);
416 buf_pool_release(&value);
417 buf_pool_release(&pretty);
418 return 0;
419 }
420 buf_pool_release(&value);
421 buf_pool_release(&pretty);
422 return 1;
423 }
424 return 0;
425}
size_t pretty_var(const char *str, struct Buffer *buf)
Escape and stringify a config item value.
Definition: dump.c:85
#define CSR_RESULT(x)
Definition: set.h:50
#define CSR_SUCCESS
Action completed successfully.
Definition: set.h:33
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:96
String manipulation buffer.
Definition: buffer.h:36
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:334
struct HashElem * cs_subset_lookup(const struct ConfigSubset *sub, const char *name)
Find an inherited config item.
Definition: subset.c:189
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Variable Documentation

◆ CompleteCommandOps

const struct CompleteOps CompleteCommandOps
Initial value:
= {
.complete = complete_command,
}
enum FunctionRetval complete_command(struct EnterWindowData *wdata, int op)
Complete a NeoMutt Command - Implements CompleteOps::complete() -.
Definition: helpers.c:430

Auto-Completion of Commands.

Definition at line 481 of file helpers.c.

◆ CompleteLabelOps

const struct CompleteOps CompleteLabelOps
Initial value:
= {
.complete = complete_label,
}
enum FunctionRetval complete_label(struct EnterWindowData *wdata, int op)
Complete a label - Implements CompleteOps::complete() -.
Definition: helpers.c:455

Auto-Completion of Labels.

Definition at line 488 of file helpers.c.