NeoMutt  2025-09-05-70-gcfdde0
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, void *cdata)
 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:581
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,
void * cdata )

Complete a command name.

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

Definition at line 112 of file helpers.c.

114{
115 char *pt = buf->data;
116 int spaces; /* keep track of the number of leading spaces on the line */
117
118 SKIPWS(pt);
119 spaces = pt - buf->data;
120
121 pt = buf->data + pos - spaces;
122 while ((pt > buf->data) && !mutt_isspace(*pt))
123 pt--;
124
125 if (pt == buf->data) /* complete cmd */
126 {
127 /* first TAB. Collect all the matches */
128 if (numtabs == 1)
129 {
130 cd->num_matched = 0;
131 mutt_str_copy(cd->user_typed, pt, sizeof(cd->user_typed));
132 memset(cd->match_list, 0, cd->match_list_len);
133 memset(cd->completed, 0, sizeof(cd->completed));
134
135 const struct Command **cp = NULL;
137 {
138 const struct Command *cmd = *cp;
139
140 candidate(cd, cd->user_typed, cmd->name, cd->completed, sizeof(cd->completed));
141 }
142
144 cd->match_list[cd->num_matched++] = cd->user_typed;
145
146 /* All matches are stored. Longest non-ambiguous string is ""
147 * i.e. don't change 'buf'. Fake successful return this time */
148 if (cd->user_typed[0] == '\0')
149 return 1;
150 }
151
152 if ((cd->completed[0] == '\0') && (cd->user_typed[0] != '\0'))
153 return 0;
154
155 /* cd->num_matched will _always_ be at least 1 since the initial
156 * user-typed string is always stored */
157 if ((numtabs == 1) && (cd->num_matched == 2))
158 {
159 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
160 }
161 else if ((numtabs > 1) && (cd->num_matched > 2))
162 {
163 /* cycle through all the matches */
164 snprintf(cd->completed, sizeof(cd->completed), "%s",
165 cd->match_list[(numtabs - 2) % cd->num_matched]);
166 }
167
168 /* return the completed command */
169 buf_strcpy(buf, cd->completed);
170 }
171 else if (buf_startswith(buf, "set") || buf_startswith(buf, "unset") ||
172 buf_startswith(buf, "reset") || buf_startswith(buf, "toggle"))
173 { /* complete variables */
174 static const char *const prefixes[] = { "no", "inv", "?", "&", 0 };
175
176 pt++;
177 /* loop through all the possible prefixes (no, inv, ...) */
178 if (buf_startswith(buf, "set"))
179 {
180 for (int num = 0; prefixes[num]; num++)
181 {
182 if (mutt_str_startswith(pt, prefixes[num]))
183 {
184 pt += mutt_str_len(prefixes[num]);
185 break;
186 }
187 }
188 }
189
190 /* first TAB. Collect all the matches */
191 if (numtabs == 1)
192 {
193 cd->num_matched = 0;
194 mutt_str_copy(cd->user_typed, pt, sizeof(cd->user_typed));
195 memset(cd->match_list, 0, cd->match_list_len);
196 memset(cd->completed, 0, sizeof(cd->completed));
197
198 struct HashElemArray hea = get_elem_list(NeoMutt->sub->cs, GEL_ALL_CONFIG);
199 struct HashElem **hep = NULL;
200 ARRAY_FOREACH(hep, &hea)
201 {
202 candidate(cd, cd->user_typed, (*hep)->key.strkey, cd->completed,
203 sizeof(cd->completed));
204 }
205 ARRAY_FREE(&hea);
206
208 cd->match_list[cd->num_matched++] = cd->user_typed;
209
210 /* All matches are stored. Longest non-ambiguous string is ""
211 * i.e. don't change 'buf'. Fake successful return this time */
212 if (cd->user_typed[0] == '\0')
213 return 1;
214 }
215
216 if ((cd->completed[0] == 0) && cd->user_typed[0])
217 return 0;
218
219 /* cd->num_matched will _always_ be at least 1 since the initial
220 * user-typed string is always stored */
221 if ((numtabs == 1) && (cd->num_matched == 2))
222 {
223 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
224 }
225 else if ((numtabs > 1) && (cd->num_matched > 2))
226 {
227 /* cycle through all the matches */
228 snprintf(cd->completed, sizeof(cd->completed), "%s",
229 cd->match_list[(numtabs - 2) % cd->num_matched]);
230 }
231
232 strncpy(pt, cd->completed, buf->data + buf->dsize - pt - spaces);
233 buf_fix_dptr(buf);
234 }
235 else if (buf_startswith(buf, "exec"))
236 {
237 enum MenuType mtype = MENU_GENERIC;
238 if (cdata)
239 {
240 struct FileCompletionData *fcd = cdata;
241 struct MuttWindow *win = fcd->win;
242 if (win && win->wdata)
243 {
244 struct Menu *menu = win->wdata;
245 mtype = menu->type;
246 }
247 }
248 else
249 {
250 mtype = menu_get_current_type();
251 }
252 const struct MenuFuncOp *funcs = km_get_table(mtype);
253 if (!funcs && (mtype != MENU_PAGER))
254 funcs = OpGeneric;
255
256 pt++;
257 /* first TAB. Collect all the matches */
258 if (numtabs == 1)
259 {
260 cd->num_matched = 0;
261 mutt_str_copy(cd->user_typed, pt, sizeof(cd->user_typed));
262 memset(cd->match_list, 0, cd->match_list_len);
263 memset(cd->completed, 0, sizeof(cd->completed));
264 for (int num = 0; funcs[num].name; num++)
265 candidate(cd, cd->user_typed, funcs[num].name, cd->completed, sizeof(cd->completed));
266 /* try the generic menu */
267 if ((mtype != MENU_PAGER) && (mtype != MENU_GENERIC))
268 {
269 funcs = OpGeneric;
270 for (int num = 0; funcs[num].name; num++)
271 candidate(cd, cd->user_typed, funcs[num].name, cd->completed,
272 sizeof(cd->completed));
273 }
275 cd->match_list[cd->num_matched++] = cd->user_typed;
276
277 /* All matches are stored. Longest non-ambiguous string is ""
278 * i.e. don't change 'buf'. Fake successful return this time */
279 if (cd->user_typed[0] == '\0')
280 return 1;
281 }
282
283 if ((cd->completed[0] == '\0') && (cd->user_typed[0] != '\0'))
284 return 0;
285
286 /* cd->num_matched will _always_ be at least 1 since the initial
287 * user-typed string is always stored */
288 if ((numtabs == 1) && (cd->num_matched == 2))
289 {
290 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
291 }
292 else if ((numtabs > 1) && (cd->num_matched > 2))
293 {
294 /* cycle through all the matches */
295 snprintf(cd->completed, sizeof(cd->completed), "%s",
296 cd->match_list[(numtabs - 2) % cd->num_matched]);
297 }
298
299 strncpy(pt, cd->completed, buf->data + buf->dsize - pt - spaces);
300 buf_fix_dptr(buf);
301 }
302 else
303 {
304 return 0;
305 }
306
307 return 1;
308}
#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:482
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:232
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:498
#define SKIPWS(ch)
Definition string2.h:51
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
Input for the file completion function.
Definition curs_lib.h:39
struct MuttWindow * win
Current Focused Window.
Definition curs_lib.h:44
The item stored in a Hash Table.
Definition hash.h:44
Mapping between a function and an operation.
Definition lib.h:115
const char * name
Name of the function.
Definition lib.h:116
Definition lib.h:79
struct MuttWindow * win
Window holding the Menu.
Definition lib.h:86
enum MenuType type
Menu definition for keymap entries.
Definition lib.h:83
void * wdata
Private data.
Container for Accounts, Notifications.
Definition neomutt.h:42
struct CommandArray commands
NeoMutt commands.
Definition neomutt.h:50
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:46
struct HashElemArray get_elem_list(struct ConfigSet *cs, enum GetElemListFlags flags)
Create a sorted list of all config items.
Definition subset.c:81
@ GEL_ALL_CONFIG
All the normal config (no synonyms or deprecated)
Definition subset.h:81
MenuType
Types of GUI selections.
Definition type.h:35
@ MENU_GENERIC
Generic selection list.
Definition type.h:45
@ MENU_PAGER
Pager pager (email viewer)
Definition type.h:47
+ 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 326 of file helpers.c.

327{
328 char *pt = buf->data;
329
330 struct Mailbox *m_cur = get_current_mailbox();
331 if (!m_cur || !m_cur->label_hash)
332 return 0;
333
334 SKIPWS(pt);
335
336 /* first TAB. Collect all the matches */
337 if (numtabs == 1)
338 {
339 struct HashElem *he = NULL;
340 struct HashWalkState hws = { 0 };
341
342 cd->num_matched = 0;
343 mutt_str_copy(cd->user_typed, buf_string(buf), sizeof(cd->user_typed));
344 memset(cd->match_list, 0, cd->match_list_len);
345 memset(cd->completed, 0, sizeof(cd->completed));
346 while ((he = mutt_hash_walk(m_cur->label_hash, &hws)))
347 candidate(cd, cd->user_typed, he->key.strkey, cd->completed, sizeof(cd->completed));
349 mutt_qsort_r(cd->match_list, cd->num_matched, sizeof(char *), label_sort, NULL);
350 cd->match_list[cd->num_matched++] = cd->user_typed;
351
352 /* All matches are stored. Longest non-ambiguous string is ""
353 * i.e. don't change 'buf'. Fake successful return this time */
354 if (cd->user_typed[0] == '\0')
355 return 1;
356 }
357
358 if ((cd->completed[0] == '\0') && (cd->user_typed[0] != '\0'))
359 return 0;
360
361 /* cd->num_matched will _always_ be at least 1 since the initial
362 * user-typed string is always stored */
363 if ((numtabs == 1) && (cd->num_matched == 2))
364 {
365 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
366 }
367 else if ((numtabs > 1) && (cd->num_matched > 2))
368 {
369 /* cycle through all the matches */
370 snprintf(cd->completed, sizeof(cd->completed), "%s",
371 cd->match_list[(numtabs - 2) % cd->num_matched]);
372 }
373
374 /* return the completed label */
375 buf_strcpy(buf, cd->completed);
376
377 return 1;
378}
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:313
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
union HashKey key
Key representing the data.
Definition hash.h:46
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
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_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 388 of file helpers.c.

389{
390 char *pt = buf->data;
391
392 if (pt[0] == '\0')
393 return 0;
394
395 SKIPWS(pt);
396 const int spaces = pt - buf->data;
397
398 pt = buf->data + pos - spaces;
399 while ((pt > buf->data) && !mutt_isspace(*pt))
400 pt--;
401 pt++; /* move past the space */
402 if (*pt == '=') /* abort if no var before the '=' */
403 return 0;
404
405 if (buf_startswith(buf, "set"))
406 {
407 char var[256] = { 0 };
408 mutt_str_copy(var, pt, sizeof(var));
409 /* ignore the trailing '=' when comparing */
410 int vlen = mutt_str_len(var);
411 if (vlen == 0)
412 return 0;
413
414 var[vlen - 1] = '\0';
415
416 struct HashElem *he = cs_subset_lookup(NeoMutt->sub, var);
417 if (!he)
418 return 0; /* no such variable. */
419
420 struct Buffer *value = buf_pool_get();
421 struct Buffer *pretty = buf_pool_get();
422 int rc = cs_subset_he_string_get(NeoMutt->sub, he, value);
423 if (CSR_RESULT(rc) == CSR_SUCCESS)
424 {
425 pretty_var(value->data, pretty);
426 snprintf(pt, buf->dsize - (pt - buf->data), "%s=%s", var, pretty->data);
427 buf_pool_release(&value);
428 buf_pool_release(&pretty);
429 return 0;
430 }
431 buf_pool_release(&value);
432 buf_pool_release(&pretty);
433 return 1;
434 }
435 return 0;
436}
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:338
struct HashElem * cs_subset_lookup(const struct ConfigSubset *sub, const char *name)
Find an inherited config item.
Definition subset.c:193
+ 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:441

Auto-Completion of Commands.

Definition at line 492 of file helpers.c.

492 {
493 .complete = complete_command,
494};

◆ 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:466

Auto-Completion of Labels.

Definition at line 499 of file helpers.c.

499 {
500 .complete = complete_label,
501};