NeoMutt  2025-09-05-43-g177ed6
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: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 )

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 candidate(cd, cd->user_typed, (*hep)->key.strkey, cd->completed,
201 sizeof(cd->completed));
202 }
203 ARRAY_FREE(&hea);
204
206 cd->match_list[cd->num_matched++] = cd->user_typed;
207
208 /* All matches are stored. Longest non-ambiguous string is ""
209 * i.e. don't change 'buf'. Fake successful return this time */
210 if (cd->user_typed[0] == '\0')
211 return 1;
212 }
213
214 if ((cd->completed[0] == 0) && cd->user_typed[0])
215 return 0;
216
217 /* cd->num_matched will _always_ be at least 1 since the initial
218 * user-typed string is always stored */
219 if ((numtabs == 1) && (cd->num_matched == 2))
220 {
221 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
222 }
223 else if ((numtabs > 1) && (cd->num_matched > 2))
224 {
225 /* cycle through all the matches */
226 snprintf(cd->completed, sizeof(cd->completed), "%s",
227 cd->match_list[(numtabs - 2) % cd->num_matched]);
228 }
229
230 strncpy(pt, cd->completed, buf->data + buf->dsize - pt - spaces);
231 buf_fix_dptr(buf);
232 }
233 else if (buf_startswith(buf, "exec"))
234 {
235 const enum MenuType mtype = menu_get_current_type();
236 const struct MenuFuncOp *funcs = km_get_table(mtype);
237 if (!funcs && (mtype != MENU_PAGER))
238 funcs = OpGeneric;
239
240 pt++;
241 /* first TAB. Collect all the matches */
242 if (numtabs == 1)
243 {
244 cd->num_matched = 0;
245 mutt_str_copy(cd->user_typed, pt, sizeof(cd->user_typed));
246 memset(cd->match_list, 0, cd->match_list_len);
247 memset(cd->completed, 0, sizeof(cd->completed));
248 for (int num = 0; funcs[num].name; num++)
249 candidate(cd, cd->user_typed, funcs[num].name, cd->completed, sizeof(cd->completed));
250 /* try the generic menu */
251 if ((mtype != MENU_PAGER) && (mtype != MENU_GENERIC))
252 {
253 funcs = OpGeneric;
254 for (int num = 0; funcs[num].name; num++)
255 candidate(cd, cd->user_typed, funcs[num].name, cd->completed,
256 sizeof(cd->completed));
257 }
259 cd->match_list[cd->num_matched++] = cd->user_typed;
260
261 /* All matches are stored. Longest non-ambiguous string is ""
262 * i.e. don't change 'buf'. Fake successful return this time */
263 if (cd->user_typed[0] == '\0')
264 return 1;
265 }
266
267 if ((cd->completed[0] == '\0') && (cd->user_typed[0] != '\0'))
268 return 0;
269
270 /* cd->num_matched will _always_ be at least 1 since the initial
271 * user-typed string is always stored */
272 if ((numtabs == 1) && (cd->num_matched == 2))
273 {
274 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
275 }
276 else if ((numtabs > 1) && (cd->num_matched > 2))
277 {
278 /* cycle through all the matches */
279 snprintf(cd->completed, sizeof(cd->completed), "%s",
280 cd->match_list[(numtabs - 2) % cd->num_matched]);
281 }
282
283 strncpy(pt, cd->completed, buf->data + buf->dsize - pt - spaces);
284 buf_fix_dptr(buf);
285 }
286 else
287 {
288 return 0;
289 }
290
291 return 1;
292}
#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:571
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
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
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:81
@ 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
+ 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 310 of file helpers.c.

311{
312 char *pt = buf->data;
313
314 struct Mailbox *m_cur = get_current_mailbox();
315 if (!m_cur || !m_cur->label_hash)
316 return 0;
317
318 SKIPWS(pt);
319
320 /* first TAB. Collect all the matches */
321 if (numtabs == 1)
322 {
323 struct HashElem *he = NULL;
324 struct HashWalkState hws = { 0 };
325
326 cd->num_matched = 0;
327 mutt_str_copy(cd->user_typed, buf_string(buf), sizeof(cd->user_typed));
328 memset(cd->match_list, 0, cd->match_list_len);
329 memset(cd->completed, 0, sizeof(cd->completed));
330 while ((he = mutt_hash_walk(m_cur->label_hash, &hws)))
331 candidate(cd, cd->user_typed, he->key.strkey, cd->completed, sizeof(cd->completed));
333 mutt_qsort_r(cd->match_list, cd->num_matched, sizeof(char *), label_sort, NULL);
334 cd->match_list[cd->num_matched++] = cd->user_typed;
335
336 /* All matches are stored. Longest non-ambiguous string is ""
337 * i.e. don't change 'buf'. Fake successful return this time */
338 if (cd->user_typed[0] == '\0')
339 return 1;
340 }
341
342 if ((cd->completed[0] == '\0') && (cd->user_typed[0] != '\0'))
343 return 0;
344
345 /* cd->num_matched will _always_ be at least 1 since the initial
346 * user-typed string is always stored */
347 if ((numtabs == 1) && (cd->num_matched == 2))
348 {
349 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
350 }
351 else if ((numtabs > 1) && (cd->num_matched > 2))
352 {
353 /* cycle through all the matches */
354 snprintf(cd->completed, sizeof(cd->completed), "%s",
355 cd->match_list[(numtabs - 2) % cd->num_matched]);
356 }
357
358 /* return the completed label */
359 buf_strcpy(buf, cd->completed);
360
361 return 1;
362}
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:297
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 372 of file helpers.c.

373{
374 char *pt = buf->data;
375
376 if (pt[0] == '\0')
377 return 0;
378
379 SKIPWS(pt);
380 const int spaces = pt - buf->data;
381
382 pt = buf->data + pos - spaces;
383 while ((pt > buf->data) && !mutt_isspace(*pt))
384 pt--;
385 pt++; /* move past the space */
386 if (*pt == '=') /* abort if no var before the '=' */
387 return 0;
388
389 if (buf_startswith(buf, "set"))
390 {
391 char var[256] = { 0 };
392 mutt_str_copy(var, pt, sizeof(var));
393 /* ignore the trailing '=' when comparing */
394 int vlen = mutt_str_len(var);
395 if (vlen == 0)
396 return 0;
397
398 var[vlen - 1] = '\0';
399
400 struct HashElem *he = cs_subset_lookup(NeoMutt->sub, var);
401 if (!he)
402 return 0; /* no such variable. */
403
404 struct Buffer *value = buf_pool_get();
405 struct Buffer *pretty = buf_pool_get();
406 int rc = cs_subset_he_string_get(NeoMutt->sub, he, value);
407 if (CSR_RESULT(rc) == CSR_SUCCESS)
408 {
409 pretty_var(value->data, pretty);
410 snprintf(pt, buf->dsize - (pt - buf->data), "%s=%s", var, pretty->data);
411 buf_pool_release(&value);
412 buf_pool_release(&pretty);
413 return 0;
414 }
415 buf_pool_release(&value);
416 buf_pool_release(&pretty);
417 return 1;
418 }
419 return 0;
420}
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:425

Auto-Completion of Commands.

Definition at line 476 of file helpers.c.

476 {
477 .complete = complete_command,
478};

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

Auto-Completion of Labels.

Definition at line 483 of file helpers.c.

483 {
484 .complete = complete_label,
485};