NeoMutt  2023-11-03-85-g512e01
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.
 
int complete_command (struct EnterWindowData *wdata, int op)
 Complete a NeoMutt Command - Implements complete_function_t -.
 
int complete_label (struct EnterWindowData *wdata, int op)
 Complete a label - Implements complete_function_t -.
 

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

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

53{
54 if (new_size <= (cd->match_list_len - 2))
55 return;
56
57 new_size = ROUND_UP(new_size + 2, 512);
58
59 mutt_mem_realloc(&cd->match_list, new_size * sizeof(char *));
60 memset(&cd->match_list[cd->match_list_len], 0, new_size - cd->match_list_len);
61
62 cd->match_list_len = new_size;
63}
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
#define ROUND_UP(NUM, STEP)
Definition: memory.h:36
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 call graph for this function:
+ 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 76 of file helpers.c.

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

111{
112 char *pt = buf->data;
113 int spaces; /* keep track of the number of leading spaces on the line */
114
115 SKIPWS(pt);
116 spaces = pt - buf->data;
117
118 pt = buf->data + pos - spaces;
119 while ((pt > buf->data) && !isspace((unsigned char) *pt))
120 pt--;
121
122 if (pt == buf->data) /* complete cmd */
123 {
124 /* first TAB. Collect all the matches */
125 if (numtabs == 1)
126 {
127 cd->num_matched = 0;
128 mutt_str_copy(cd->user_typed, pt, sizeof(cd->user_typed));
129 memset(cd->match_list, 0, cd->match_list_len);
130 memset(cd->completed, 0, sizeof(cd->completed));
131
132 struct Command *c = NULL;
133 for (size_t num = 0, size = commands_array(&c); num < size; num++)
134 candidate(cd, cd->user_typed, c[num].name, cd->completed, sizeof(cd->completed));
136 cd->match_list[cd->num_matched++] = cd->user_typed;
137
138 /* All matches are stored. Longest non-ambiguous string is ""
139 * i.e. don't change 'buf'. Fake successful return this time */
140 if (cd->user_typed[0] == '\0')
141 return 1;
142 }
143
144 if ((cd->completed[0] == '\0') && (cd->user_typed[0] != '\0'))
145 return 0;
146
147 /* cd->num_matched will _always_ be at least 1 since the initial
148 * user-typed string is always stored */
149 if ((numtabs == 1) && (cd->num_matched == 2))
150 {
151 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
152 }
153 else if ((numtabs > 1) && (cd->num_matched > 2))
154 {
155 /* cycle through all the matches */
156 snprintf(cd->completed, sizeof(cd->completed), "%s",
157 cd->match_list[(numtabs - 2) % cd->num_matched]);
158 }
159
160 /* return the completed command */
161 buf_strcpy(buf, cd->completed);
162 }
163 else if (buf_startswith(buf, "set") || buf_startswith(buf, "unset") ||
164 buf_startswith(buf, "reset") || buf_startswith(buf, "toggle"))
165 { /* complete variables */
166 static const char *const prefixes[] = { "no", "inv", "?", "&", 0 };
167
168 pt++;
169 /* loop through all the possible prefixes (no, inv, ...) */
170 if (buf_startswith(buf, "set"))
171 {
172 for (int num = 0; prefixes[num]; num++)
173 {
174 if (mutt_str_startswith(pt, prefixes[num]))
175 {
176 pt += mutt_str_len(prefixes[num]);
177 break;
178 }
179 }
180 }
181
182 /* first TAB. Collect all the matches */
183 if (numtabs == 1)
184 {
185 cd->num_matched = 0;
186 mutt_str_copy(cd->user_typed, pt, sizeof(cd->user_typed));
187 memset(cd->match_list, 0, cd->match_list_len);
188 memset(cd->completed, 0, sizeof(cd->completed));
189
190 struct HashElem *he = NULL;
191 struct HashElem **he_list = get_elem_list(NeoMutt->sub->cs);
192 for (size_t i = 0; he_list[i]; i++)
193 {
194 he = he_list[i];
195 const int type = DTYPE(he->type);
196
197 if ((type == DT_SYNONYM) || (type & DT_DEPRECATED))
198 continue;
199
200 candidate(cd, cd->user_typed, he->key.strkey, cd->completed, sizeof(cd->completed));
201 }
202 FREE(&he_list);
203
205 cd->match_list[cd->num_matched++] = cd->user_typed;
206
207 /* All matches are stored. Longest non-ambiguous string is ""
208 * i.e. don't change 'buf'. Fake successful return this time */
209 if (cd->user_typed[0] == '\0')
210 return 1;
211 }
212
213 if ((cd->completed[0] == 0) && cd->user_typed[0])
214 return 0;
215
216 /* cd->num_matched will _always_ be at least 1 since the initial
217 * user-typed string is always stored */
218 if ((numtabs == 1) && (cd->num_matched == 2))
219 {
220 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
221 }
222 else if ((numtabs > 1) && (cd->num_matched > 2))
223 {
224 /* cycle through all the matches */
225 snprintf(cd->completed, sizeof(cd->completed), "%s",
226 cd->match_list[(numtabs - 2) % cd->num_matched]);
227 }
228
229 strncpy(pt, cd->completed, buf->data + buf->dsize - pt - spaces);
230 buf_fix_dptr(buf);
231 }
232 else if (buf_startswith(buf, "exec"))
233 {
234 const enum MenuType mtype = menu_get_current_type();
235 const struct MenuFuncOp *funcs = km_get_table(mtype);
236 if (!funcs && (mtype != MENU_PAGER))
237 funcs = OpGeneric;
238
239 pt++;
240 /* first TAB. Collect all the matches */
241 if (numtabs == 1)
242 {
243 cd->num_matched = 0;
244 mutt_str_copy(cd->user_typed, pt, sizeof(cd->user_typed));
245 memset(cd->match_list, 0, cd->match_list_len);
246 memset(cd->completed, 0, sizeof(cd->completed));
247 for (int num = 0; funcs[num].name; num++)
248 candidate(cd, cd->user_typed, funcs[num].name, cd->completed, sizeof(cd->completed));
249 /* try the generic menu */
250 if ((mtype != MENU_PAGER) && (mtype != MENU_GENERIC))
251 {
252 funcs = OpGeneric;
253 for (int num = 0; funcs[num].name; num++)
254 candidate(cd, cd->user_typed, funcs[num].name, cd->completed,
255 sizeof(cd->completed));
256 }
258 cd->match_list[cd->num_matched++] = cd->user_typed;
259
260 /* All matches are stored. Longest non-ambiguous string is ""
261 * i.e. don't change 'buf'. Fake successful return this time */
262 if (cd->user_typed[0] == '\0')
263 return 1;
264 }
265
266 if ((cd->completed[0] == '\0') && (cd->user_typed[0] != '\0'))
267 return 0;
268
269 /* cd->num_matched will _always_ be at least 1 since the initial
270 * user-typed string is always stored */
271 if ((numtabs == 1) && (cd->num_matched == 2))
272 {
273 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
274 }
275 else if ((numtabs > 1) && (cd->num_matched > 2))
276 {
277 /* cycle through all the matches */
278 snprintf(cd->completed, sizeof(cd->completed), "%s",
279 cd->match_list[(numtabs - 2) % cd->num_matched]);
280 }
281
282 strncpy(pt, cd->completed, buf->data + buf->dsize - pt - spaces);
283 buf_fix_dptr(buf);
284 }
285 else
286 {
287 return 0;
288 }
289
290 return 1;
291}
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:194
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:407
size_t buf_startswith(const struct Buffer *buf, const char *prefix)
Check whether a buffer starts with a prefix.
Definition: buffer.c:677
bool candidate(struct CompletionData *cd, char *user, const char *src, char *dest, size_t dlen)
Helper function for completion.
Definition: helpers.c:76
size_t commands_array(struct Command **first)
Get Commands array.
Definition: command.c:75
const struct MenuFuncOp OpGeneric[]
Functions for the Generic Menu.
Definition: functions.c:67
const struct MenuFuncOp * km_get_table(enum MenuType mtype)
Lookup a Menu's functions.
Definition: lib.c:529
#define FREE(x)
Definition: memory.h:45
enum MenuType menu_get_current_type(void)
Get the type of the current Window.
Definition: menu.c:85
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:228
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:568
#define SKIPWS(ch)
Definition: string2.h:45
size_t dsize
Length of data.
Definition: buffer.h:37
char * data
Pointer to data.
Definition: buffer.h:35
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:51
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:102
const char * name
Name of the function.
Definition: lib.h:103
Container for Accounts, Notifications.
Definition: neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45
struct HashElem ** get_elem_list(struct ConfigSet *cs)
Create a sorted list of all config items.
Definition: subset.c:70
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:55
#define DTYPE(x)
Mask for the Data Type.
Definition: types.h:45
#define DT_DEPRECATED
Config item shouldn't be used any more.
Definition: types.h:78
#define DT_SYNONYM
synonym for another variable
Definition: types.h:42
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 309 of file helpers.c.

310{
311 char *pt = buf->data;
312
313 struct Mailbox *m_cur = get_current_mailbox();
314 if (!m_cur || !m_cur->label_hash)
315 return 0;
316
317 SKIPWS(pt);
318
319 /* first TAB. Collect all the matches */
320 if (numtabs == 1)
321 {
322 struct HashElem *he = NULL;
323 struct HashWalkState hws = { 0 };
324
325 cd->num_matched = 0;
326 mutt_str_copy(cd->user_typed, buf_string(buf), sizeof(cd->user_typed));
327 memset(cd->match_list, 0, cd->match_list_len);
328 memset(cd->completed, 0, sizeof(cd->completed));
329 while ((he = mutt_hash_walk(m_cur->label_hash, &hws)))
330 candidate(cd, cd->user_typed, he->key.strkey, cd->completed, sizeof(cd->completed));
332 mutt_qsort_r(cd->match_list, cd->num_matched, sizeof(char *), label_sort, NULL);
333 cd->match_list[cd->num_matched++] = cd->user_typed;
334
335 /* All matches are stored. Longest non-ambiguous string is ""
336 * i.e. don't change 'buf'. Fake successful return this time */
337 if (cd->user_typed[0] == '\0')
338 return 1;
339 }
340
341 if ((cd->completed[0] == '\0') && (cd->user_typed[0] != '\0'))
342 return 0;
343
344 /* cd->num_matched will _always_ be at least 1 since the initial
345 * user-typed string is always stored */
346 if ((numtabs == 1) && (cd->num_matched == 2))
347 {
348 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
349 }
350 else if ((numtabs > 1) && (cd->num_matched > 2))
351 {
352 /* cycle through all the matches */
353 snprintf(cd->completed, sizeof(cd->completed), "%s",
354 cd->match_list[(numtabs - 2) % cd->num_matched]);
355 }
356
357 /* return the completed label */
358 buf_strcpy(buf, cd->completed);
359
360 return 1;
361}
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:93
static int label_sort(const void *a, const void *b, void *sdata)
Compare two label strings - Implements sort_t -.
Definition: helpers.c:296
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:662
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:66
Cursor to iterate through a Hash Table.
Definition: hash.h:133
A mailbox.
Definition: mailbox.h:79
struct HashTable * label_hash
Hash Table: "x-labels" -> Email.
Definition: mailbox.h:124
+ 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 371 of file helpers.c.

372{
373 char *pt = buf->data;
374
375 if (pt[0] == '\0')
376 return 0;
377
378 SKIPWS(pt);
379 const int spaces = pt - buf->data;
380
381 pt = buf->data + pos - spaces;
382 while ((pt > buf->data) && !isspace((unsigned char) *pt))
383 pt--;
384 pt++; /* move past the space */
385 if (*pt == '=') /* abort if no var before the '=' */
386 return 0;
387
388 if (buf_startswith(buf, "set"))
389 {
390 char var[256] = { 0 };
391 mutt_str_copy(var, pt, sizeof(var));
392 /* ignore the trailing '=' when comparing */
393 int vlen = mutt_str_len(var);
394 if (vlen == 0)
395 return 0;
396
397 var[vlen - 1] = '\0';
398
399 struct HashElem *he = cs_subset_lookup(NeoMutt->sub, var);
400 if (!he)
401 return 0; /* no such variable. */
402
403 struct Buffer *value = buf_pool_get();
404 struct Buffer *pretty = buf_pool_get();
405 int rc = cs_subset_he_string_get(NeoMutt->sub, he, value);
406 if (CSR_RESULT(rc) == CSR_SUCCESS)
407 {
408 pretty_var(value->data, pretty);
409 snprintf(pt, buf->dsize - (pt - buf->data), "%s=%s", var, pretty->data);
410 buf_pool_release(&value);
411 buf_pool_release(&pretty);
412 return 0;
413 }
414 buf_pool_release(&value);
415 buf_pool_release(&pretty);
416 return 1;
417 }
418 return 0;
419}
size_t pretty_var(const char *str, struct Buffer *buf)
Escape and stringify a config item value.
Definition: dump.c:83
#define CSR_RESULT(x)
Definition: set.h:52
#define CSR_SUCCESS
Action completed successfully.
Definition: set.h:35
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
String manipulation buffer.
Definition: buffer.h:34
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:353
struct HashElem * cs_subset_lookup(const struct ConfigSubset *sub, const char *name)
Find an inherited config item.
Definition: subset.c:178
+ 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,
}
int complete_command(struct EnterWindowData *wdata, int op)
Complete a NeoMutt Command - Implements complete_function_t -.
Definition: helpers.c:424

Auto-Completion of Commands.

Definition at line 475 of file helpers.c.

◆ CompleteLabelOps

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

Auto-Completion of Labels.

Definition at line 482 of file helpers.c.