NeoMutt  2025-12-11-435-g4ac674
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.
 
static int complete_sort_strings (const void *a, const void *b, void *sdata)
 Compare two strings - Implements sort_t -.
 
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
  • Thomas Klausner

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 54 of file helpers.c.

55{
56 if (new_size <= (cd->match_list_len - 2))
57 return;
58
59 new_size = ROUND_UP(new_size + 2, 512);
60
61 MUTT_MEM_REALLOC(&cd->match_list, new_size, const char *);
62 memset(&cd->match_list[cd->match_list_len], 0, new_size - cd->match_list_len);
63
64 cd->match_list_len = new_size;
65}
#define ROUND_UP(NUM, STEP)
Round up NUM to the nearest multiple of STEP.
Definition memory.h:46
#define MUTT_MEM_REALLOC(pptr, n, type)
Definition memory.h:55
int match_list_len
Enough space for all of the config items.
Definition data.h:37
const char ** match_list
Matching strings.
Definition data.h:36
+ 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 78 of file helpers.c.

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

126{
127 char *pt = buf->data;
128 int spaces; /* keep track of the number of leading spaces on the line */
129
130 SKIPWS(pt);
131 spaces = pt - buf->data;
132
133 pt = buf->data + pos - spaces;
134 while ((pt > buf->data) && !mutt_isspace(*pt))
135 pt--;
136
137 if (pt == buf->data) /* complete cmd */
138 {
139 /* first TAB. Collect all the matches */
140 if (numtabs == 1)
141 {
142 cd->num_matched = 0;
143 mutt_str_copy(cd->user_typed, pt, sizeof(cd->user_typed));
144 memset(cd->match_list, 0, cd->match_list_len);
145 memset(cd->completed, 0, sizeof(cd->completed));
146
147 const struct Command **cp = NULL;
149 {
150 const struct Command *cmd = *cp;
151
152 // For synonyms, match against the synonym name but complete to the real command name
153 if ((cmd->flags & CF_SYNONYM) && cmd->help)
154 {
155 // Check if user-typed matches the beginning of the synonym name
156 if (strstr(cmd->name, cd->user_typed) == cmd->name)
157 {
158 // Use the real command name for completion
159 const char *real_name = cmd->help;
160
161 // Manually add the real command name to the match list
163 cd->match_list[cd->num_matched++] = real_name;
164
165 // Update the completion result
166 if (cd->completed[0] == '\0')
167 {
168 mutt_str_copy(cd->completed, real_name, sizeof(cd->completed));
169 }
170 else
171 {
172 int l;
173 for (l = 0; (real_name[l] != '\0') && (real_name[l] == cd->completed[l]); l++)
174 ; // do nothing
175 cd->completed[l] = '\0';
176 }
177 }
178 }
179 else
180 {
181 candidate(cd, cd->user_typed, cmd->name, cd->completed, sizeof(cd->completed));
182 }
183 }
184
186 cd->match_list[cd->num_matched++] = cd->user_typed;
187
188 /* All matches are stored. Longest non-ambiguous string is ""
189 * i.e. don't change 'buf'. Fake successful return this time */
190 if (cd->user_typed[0] == '\0')
191 return 1;
192 }
193
194 if ((cd->completed[0] == '\0') && (cd->user_typed[0] != '\0'))
195 return 0;
196
197 /* cd->num_matched will _always_ be at least 1 since the initial
198 * user-typed string is always stored */
199 if ((numtabs == 1) && (cd->num_matched == 2))
200 {
201 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
202 }
203 else if ((numtabs > 1) && (cd->num_matched > 2))
204 {
205 /* cycle through all the matches */
206 snprintf(cd->completed, sizeof(cd->completed), "%s",
207 cd->match_list[(numtabs - 2) % cd->num_matched]);
208 }
209
210 /* return the completed command */
211 buf_strcpy(buf, cd->completed);
212 }
213 else if (buf_startswith(buf, "set") || buf_startswith(buf, "unset") ||
214 buf_startswith(buf, "reset") || buf_startswith(buf, "toggle"))
215 { /* complete variables */
216 static const char *const prefixes[] = { "no", "inv", "?", "&", 0 };
217
218 pt++;
219 /* loop through all the possible prefixes (no, inv, ...) */
220 if (buf_startswith(buf, "set"))
221 {
222 for (int num = 0; prefixes[num]; num++)
223 {
224 if (mutt_str_startswith(pt, prefixes[num]))
225 {
226 pt += mutt_str_len(prefixes[num]);
227 break;
228 }
229 }
230 }
231
232 /* first TAB. Collect all the matches */
233 if (numtabs == 1)
234 {
235 cd->num_matched = 0;
236 mutt_str_copy(cd->user_typed, pt, sizeof(cd->user_typed));
237 memset(cd->match_list, 0, cd->match_list_len);
238 memset(cd->completed, 0, sizeof(cd->completed));
239
240 struct HashElemArray hea = get_elem_list(NeoMutt->sub->cs, GEL_ALL_CONFIG);
241 struct HashElem **hep = NULL;
242 ARRAY_FOREACH(hep, &hea)
243 {
244 candidate(cd, cd->user_typed, (*hep)->key.strkey, cd->completed,
245 sizeof(cd->completed));
246 }
247 ARRAY_FREE(&hea);
248
250 cd->match_list[cd->num_matched++] = cd->user_typed;
251
252 /* All matches are stored. Longest non-ambiguous string is ""
253 * i.e. don't change 'buf'. Fake successful return this time */
254 if (cd->user_typed[0] == '\0')
255 return 1;
256 }
257
258 if ((cd->completed[0] == 0) && cd->user_typed[0])
259 return 0;
260
261 /* cd->num_matched will _always_ be at least 1 since the initial
262 * user-typed string is always stored */
263 if ((numtabs == 1) && (cd->num_matched == 2))
264 {
265 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
266 }
267 else if ((numtabs > 1) && (cd->num_matched > 2))
268 {
269 /* cycle through all the matches */
270 snprintf(cd->completed, sizeof(cd->completed), "%s",
271 cd->match_list[(numtabs - 2) % cd->num_matched]);
272 }
273
274 strncpy(pt, cd->completed, buf->data + buf->dsize - pt - spaces);
275 buf_fix_dptr(buf);
276 }
277 else if (buf_startswith(buf, "exec"))
278 {
279 pt++;
280 /* first TAB. Collect all the matches */
281 if (numtabs == 1)
282 {
283 cd->num_matched = 0;
284 mutt_str_copy(cd->user_typed, pt, sizeof(cd->user_typed));
285 memset(cd->match_list, 0, cd->match_list_len);
286 memset(cd->completed, 0, sizeof(cd->completed));
287
288 enum MenuType mtype = MENU_GENERIC;
289 if (cdata)
290 {
291 struct FileCompletionData *fcd = cdata;
292 struct MuttWindow *win = fcd->win;
293 if (win && win->wdata)
294 {
295 struct Menu *menu = win->wdata;
296 mtype = menu->md->id;
297 }
298 }
299 else
300 {
301 mtype = menu_get_current_type();
302 }
303
304 const struct MenuDefinition *md = menu_find(mtype);
305 struct StringArray fna = km_get_func_array(md);
306
308 const char **strp = NULL;
309 ARRAY_FOREACH(strp, &fna)
310 {
311 candidate(cd, cd->user_typed, *strp, cd->completed, sizeof(cd->completed));
312 }
313 ARRAY_FREE(&fna);
314
316 cd->match_list[cd->num_matched++] = cd->user_typed;
317
318 /* All matches are stored. Longest non-ambiguous string is ""
319 * i.e. don't change 'buf'. Fake successful return this time */
320 if (cd->user_typed[0] == '\0')
321 return 1;
322 }
323
324 if ((cd->completed[0] == '\0') && (cd->user_typed[0] != '\0'))
325 return 0;
326
327 /* cd->num_matched will _always_ be at least 1 since the initial
328 * user-typed string is always stored */
329 if ((numtabs == 1) && (cd->num_matched == 2))
330 {
331 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
332 }
333 else if ((numtabs > 1) && (cd->num_matched > 2))
334 {
335 /* cycle through all the matches */
336 snprintf(cd->completed, sizeof(cd->completed), "%s",
337 cd->match_list[(numtabs - 2) % cd->num_matched]);
338 }
339
340 strncpy(pt, cd->completed, buf->data + buf->dsize - pt - spaces);
341 buf_fix_dptr(buf);
342 }
343 else
344 {
345 return 0;
346 }
347
348 return 1;
349}
#define ARRAY_SORT(head, fn, sdata)
Sort an array.
Definition array.h:373
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition array.h:223
#define ARRAY_FREE(head)
Release all memory.
Definition array.h:209
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
#define CF_SYNONYM
Command is a synonym for another command.
Definition command.h:49
bool candidate(struct CompletionData *cd, char *user, const char *src, char *dest, size_t dlen)
Helper function for completion.
Definition helpers.c:78
bool mutt_isspace(int arg)
Wrapper for isspace(3)
Definition ctype.c:96
static int complete_sort_strings(const void *a, const void *b, void *sdata)
Compare two strings - Implements sort_t -.
Definition helpers.c:106
struct StringArray km_get_func_array(const struct MenuDefinition *md)
Get array of function names for a Menu.
Definition dump.c:475
struct MenuDefinition * menu_find(int menu)
Find a Menu Definition by Menu type.
Definition menu.c:245
enum MenuType menu_get_current_type(void)
Get the type of the current Window.
Definition menu.c:91
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition string.c:234
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:500
#define SKIPWS(ch)
Definition string2.h:52
size_t dsize
Length of data.
Definition buffer.h:39
char * data
Pointer to data.
Definition buffer.h:37
CommandFlags flags
Command flags, e.g. CF_SYNONYM.
Definition command.h:184
const char * help
One-line description of the Command.
Definition command.h:180
const char * name
Name of the Command.
Definition command.h:159
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
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
Functions for a Dialog or Window.
Definition menu.h:80
int id
Menu ID, e.g. MENU_ALIAS.
Definition menu.h:81
Definition lib.h:80
struct MuttWindow * win
Window holding the Menu.
Definition lib.h:88
const struct MenuDefinition * md
Menu definition for keymap entries.
Definition lib.h:84
void * wdata
Private data.
Container for Accounts, Notifications.
Definition neomutt.h:41
struct CommandArray commands
NeoMutt commands.
Definition neomutt.h:53
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
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:33
@ MENU_GENERIC
Generic selection list.
Definition type.h:43
+ 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 367 of file helpers.c.

368{
369 char *pt = buf->data;
370
371 struct Mailbox *m_cur = get_current_mailbox();
372 if (!m_cur || !m_cur->label_hash)
373 return 0;
374
375 SKIPWS(pt);
376
377 /* first TAB. Collect all the matches */
378 if (numtabs == 1)
379 {
380 struct HashElem *he = NULL;
381 struct HashWalkState hws = { 0 };
382
383 cd->num_matched = 0;
384 mutt_str_copy(cd->user_typed, buf_string(buf), sizeof(cd->user_typed));
385 memset(cd->match_list, 0, cd->match_list_len);
386 memset(cd->completed, 0, sizeof(cd->completed));
387 while ((he = mutt_hash_walk(m_cur->label_hash, &hws)))
388 candidate(cd, cd->user_typed, he->key.strkey, cd->completed, sizeof(cd->completed));
390 mutt_qsort_r(cd->match_list, cd->num_matched, sizeof(char *), label_sort, NULL);
391 cd->match_list[cd->num_matched++] = cd->user_typed;
392
393 /* All matches are stored. Longest non-ambiguous string is ""
394 * i.e. don't change 'buf'. Fake successful return this time */
395 if (cd->user_typed[0] == '\0')
396 return 1;
397 }
398
399 if ((cd->completed[0] == '\0') && (cd->user_typed[0] != '\0'))
400 return 0;
401
402 /* cd->num_matched will _always_ be at least 1 since the initial
403 * user-typed string is always stored */
404 if ((numtabs == 1) && (cd->num_matched == 2))
405 {
406 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
407 }
408 else if ((numtabs > 1) && (cd->num_matched > 2))
409 {
410 /* cycle through all the matches */
411 snprintf(cd->completed, sizeof(cd->completed), "%s",
412 cd->match_list[(numtabs - 2) % cd->num_matched]);
413 }
414
415 /* return the completed label */
416 buf_strcpy(buf, cd->completed);
417
418 return 1;
419}
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: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:491
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:78
struct HashTable * label_hash
Hash Table: "x-labels" -> Email.
Definition mailbox.h:124
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 429 of file helpers.c.

430{
431 char *pt = buf->data;
432
433 if (pt[0] == '\0')
434 return 0;
435
436 SKIPWS(pt);
437 const int spaces = pt - buf->data;
438
439 pt = buf->data + pos - spaces;
440 while ((pt > buf->data) && !mutt_isspace(*pt))
441 pt--;
442 pt++; /* move past the space */
443 if (*pt == '=') /* abort if no var before the '=' */
444 return 0;
445
446 if (buf_startswith(buf, "set"))
447 {
448 char var[256] = { 0 };
449 mutt_str_copy(var, pt, sizeof(var));
450 /* ignore the trailing '=' when comparing */
451 int vlen = mutt_str_len(var);
452 if (vlen == 0)
453 return 0;
454
455 var[vlen - 1] = '\0';
456
457 struct HashElem *he = cs_subset_lookup(NeoMutt->sub, var);
458 if (!he)
459 return 0; /* no such variable. */
460
461 struct Buffer *value = buf_pool_get();
462 struct Buffer *pretty = buf_pool_get();
463 int rc = cs_subset_he_string_get(NeoMutt->sub, he, value);
464 if (CSR_RESULT(rc) == CSR_SUCCESS)
465 {
466 pretty_var(value->data, pretty);
467 snprintf(pt, buf->dsize - (pt - buf->data), "%s=%s", var, pretty->data);
468 buf_pool_release(&value);
469 buf_pool_release(&pretty);
470 return 0;
471 }
472 buf_pool_release(&value);
473 buf_pool_release(&pretty);
474 return 1;
475 }
476 return 0;
477}
size_t pretty_var(const char *str, struct Buffer *buf)
Escape and stringify a config item value.
Definition dump.c:87
#define CSR_RESULT(x)
Extract the result code from CSR_* flags.
Definition set.h:52
#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:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
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:482

Auto-Completion of Commands.

Definition at line 533 of file helpers.c.

533 {
534 .complete = complete_command,
535};

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

Auto-Completion of Labels.

Definition at line 540 of file helpers.c.

540 {
541 .complete = complete_label,
542};