NeoMutt  2025-01-09-117-gace867
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
helpers.c File Reference

Auto-completion helpers. More...

#include "config.h"
#include <ctype.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 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)
Definition: memory.h:36
#define MUTT_MEM_REALLOC(pptr, n, type)
Definition: memory.h:43
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 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:582
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 112 of file helpers.c.

113{
114 char *pt = buf->data;
115 int spaces; /* keep track of the number of leading spaces on the line */
116
117 SKIPWS(pt);
118 spaces = pt - buf->data;
119
120 pt = buf->data + pos - spaces;
121 while ((pt > buf->data) && !isspace((unsigned char) *pt))
122 pt--;
123
124 if (pt == buf->data) /* complete cmd */
125 {
126 /* first TAB. Collect all the matches */
127 if (numtabs == 1)
128 {
129 cd->num_matched = 0;
130 mutt_str_copy(cd->user_typed, pt, sizeof(cd->user_typed));
131 memset(cd->match_list, 0, cd->match_list_len);
132 memset(cd->completed, 0, sizeof(cd->completed));
133
134 const struct Command **cp = NULL;
136 {
137 const struct Command *cmd = *cp;
138
139 candidate(cd, cd->user_typed, cmd->name, cd->completed, sizeof(cd->completed));
140 }
141
143 cd->match_list[cd->num_matched++] = cd->user_typed;
144
145 /* All matches are stored. Longest non-ambiguous string is ""
146 * i.e. don't change 'buf'. Fake successful return this time */
147 if (cd->user_typed[0] == '\0')
148 return 1;
149 }
150
151 if ((cd->completed[0] == '\0') && (cd->user_typed[0] != '\0'))
152 return 0;
153
154 /* cd->num_matched will _always_ be at least 1 since the initial
155 * user-typed string is always stored */
156 if ((numtabs == 1) && (cd->num_matched == 2))
157 {
158 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
159 }
160 else if ((numtabs > 1) && (cd->num_matched > 2))
161 {
162 /* cycle through all the matches */
163 snprintf(cd->completed, sizeof(cd->completed), "%s",
164 cd->match_list[(numtabs - 2) % cd->num_matched]);
165 }
166
167 /* return the completed command */
168 buf_strcpy(buf, cd->completed);
169 }
170 else if (buf_startswith(buf, "set") || buf_startswith(buf, "unset") ||
171 buf_startswith(buf, "reset") || buf_startswith(buf, "toggle"))
172 { /* complete variables */
173 static const char *const prefixes[] = { "no", "inv", "?", "&", 0 };
174
175 pt++;
176 /* loop through all the possible prefixes (no, inv, ...) */
177 if (buf_startswith(buf, "set"))
178 {
179 for (int num = 0; prefixes[num]; num++)
180 {
181 if (mutt_str_startswith(pt, prefixes[num]))
182 {
183 pt += mutt_str_len(prefixes[num]);
184 break;
185 }
186 }
187 }
188
189 /* first TAB. Collect all the matches */
190 if (numtabs == 1)
191 {
192 cd->num_matched = 0;
193 mutt_str_copy(cd->user_typed, pt, sizeof(cd->user_typed));
194 memset(cd->match_list, 0, cd->match_list_len);
195 memset(cd->completed, 0, sizeof(cd->completed));
196
197 struct HashElemArray hea = get_elem_list(NeoMutt->sub->cs, GEL_ALL_CONFIG);
198 struct HashElem **hep = NULL;
199 ARRAY_FOREACH(hep, &hea)
200 {
201 struct HashElem *he = *hep;
202 const int type = CONFIG_TYPE(he->type);
203
205 continue;
206
207 candidate(cd, cd->user_typed, he->key.strkey, cd->completed, sizeof(cd->completed));
208 }
209 ARRAY_FREE(&hea);
210
212 cd->match_list[cd->num_matched++] = cd->user_typed;
213
214 /* All matches are stored. Longest non-ambiguous string is ""
215 * i.e. don't change 'buf'. Fake successful return this time */
216 if (cd->user_typed[0] == '\0')
217 return 1;
218 }
219
220 if ((cd->completed[0] == 0) && cd->user_typed[0])
221 return 0;
222
223 /* cd->num_matched will _always_ be at least 1 since the initial
224 * user-typed string is always stored */
225 if ((numtabs == 1) && (cd->num_matched == 2))
226 {
227 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
228 }
229 else if ((numtabs > 1) && (cd->num_matched > 2))
230 {
231 /* cycle through all the matches */
232 snprintf(cd->completed, sizeof(cd->completed), "%s",
233 cd->match_list[(numtabs - 2) % cd->num_matched]);
234 }
235
236 strncpy(pt, cd->completed, buf->data + buf->dsize - pt - spaces);
237 buf_fix_dptr(buf);
238 }
239 else if (buf_startswith(buf, "exec"))
240 {
241 const enum MenuType mtype = menu_get_current_type();
242 const struct MenuFuncOp *funcs = km_get_table(mtype);
243 if (!funcs && (mtype != MENU_PAGER))
244 funcs = OpGeneric;
245
246 pt++;
247 /* first TAB. Collect all the matches */
248 if (numtabs == 1)
249 {
250 cd->num_matched = 0;
251 mutt_str_copy(cd->user_typed, pt, sizeof(cd->user_typed));
252 memset(cd->match_list, 0, cd->match_list_len);
253 memset(cd->completed, 0, sizeof(cd->completed));
254 for (int num = 0; funcs[num].name; num++)
255 candidate(cd, cd->user_typed, funcs[num].name, cd->completed, sizeof(cd->completed));
256 /* try the generic menu */
257 if ((mtype != MENU_PAGER) && (mtype != MENU_GENERIC))
258 {
259 funcs = OpGeneric;
260 for (int num = 0; funcs[num].name; num++)
261 candidate(cd, cd->user_typed, funcs[num].name, cd->completed,
262 sizeof(cd->completed));
263 }
265 cd->match_list[cd->num_matched++] = cd->user_typed;
266
267 /* All matches are stored. Longest non-ambiguous string is ""
268 * i.e. don't change 'buf'. Fake successful return this time */
269 if (cd->user_typed[0] == '\0')
270 return 1;
271 }
272
273 if ((cd->completed[0] == '\0') && (cd->user_typed[0] != '\0'))
274 return 0;
275
276 /* cd->num_matched will _always_ be at least 1 since the initial
277 * user-typed string is always stored */
278 if ((numtabs == 1) && (cd->num_matched == 2))
279 {
280 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
281 }
282 else if ((numtabs > 1) && (cd->num_matched > 2))
283 {
284 /* cycle through all the matches */
285 snprintf(cd->completed, sizeof(cd->completed), "%s",
286 cd->match_list[(numtabs - 2) % cd->num_matched]);
287 }
288
289 strncpy(pt, cd->completed, buf->data + buf->dsize - pt - spaces);
290 buf_fix_dptr(buf);
291 }
292 else
293 {
294 return 0;
295 }
296
297 return 1;
298}
#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:78
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:557
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:45
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 316 of file helpers.c.

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

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

Auto-Completion of Commands.

Definition at line 482 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:456

Auto-Completion of Labels.

Definition at line 489 of file helpers.c.