NeoMutt  2024-11-14-34-g5aaf0d
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: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 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 struct Command *c = NULL;
135 for (size_t num = 0, size = commands_array(&c); num < size; num++)
136 candidate(cd, cd->user_typed, c[num].name, cd->completed, sizeof(cd->completed));
138 cd->match_list[cd->num_matched++] = cd->user_typed;
139
140 /* All matches are stored. Longest non-ambiguous string is ""
141 * i.e. don't change 'buf'. Fake successful return this time */
142 if (cd->user_typed[0] == '\0')
143 return 1;
144 }
145
146 if ((cd->completed[0] == '\0') && (cd->user_typed[0] != '\0'))
147 return 0;
148
149 /* cd->num_matched will _always_ be at least 1 since the initial
150 * user-typed string is always stored */
151 if ((numtabs == 1) && (cd->num_matched == 2))
152 {
153 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
154 }
155 else if ((numtabs > 1) && (cd->num_matched > 2))
156 {
157 /* cycle through all the matches */
158 snprintf(cd->completed, sizeof(cd->completed), "%s",
159 cd->match_list[(numtabs - 2) % cd->num_matched]);
160 }
161
162 /* return the completed command */
163 buf_strcpy(buf, cd->completed);
164 }
165 else if (buf_startswith(buf, "set") || buf_startswith(buf, "unset") ||
166 buf_startswith(buf, "reset") || buf_startswith(buf, "toggle"))
167 { /* complete variables */
168 static const char *const prefixes[] = { "no", "inv", "?", "&", 0 };
169
170 pt++;
171 /* loop through all the possible prefixes (no, inv, ...) */
172 if (buf_startswith(buf, "set"))
173 {
174 for (int num = 0; prefixes[num]; num++)
175 {
176 if (mutt_str_startswith(pt, prefixes[num]))
177 {
178 pt += mutt_str_len(prefixes[num]);
179 break;
180 }
181 }
182 }
183
184 /* first TAB. Collect all the matches */
185 if (numtabs == 1)
186 {
187 cd->num_matched = 0;
188 mutt_str_copy(cd->user_typed, pt, sizeof(cd->user_typed));
189 memset(cd->match_list, 0, cd->match_list_len);
190 memset(cd->completed, 0, sizeof(cd->completed));
191
192 struct HashElem *he = NULL;
193 struct HashElem **he_list = get_elem_list(NeoMutt->sub->cs);
194 for (size_t i = 0; he_list[i]; i++)
195 {
196 he = he_list[i];
197 const int type = DTYPE(he->type);
198
200 continue;
201
202 candidate(cd, cd->user_typed, he->key.strkey, cd->completed, sizeof(cd->completed));
203 }
204 FREE(&he_list);
205
207 cd->match_list[cd->num_matched++] = cd->user_typed;
208
209 /* All matches are stored. Longest non-ambiguous string is ""
210 * i.e. don't change 'buf'. Fake successful return this time */
211 if (cd->user_typed[0] == '\0')
212 return 1;
213 }
214
215 if ((cd->completed[0] == 0) && cd->user_typed[0])
216 return 0;
217
218 /* cd->num_matched will _always_ be at least 1 since the initial
219 * user-typed string is always stored */
220 if ((numtabs == 1) && (cd->num_matched == 2))
221 {
222 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
223 }
224 else if ((numtabs > 1) && (cd->num_matched > 2))
225 {
226 /* cycle through all the matches */
227 snprintf(cd->completed, sizeof(cd->completed), "%s",
228 cd->match_list[(numtabs - 2) % cd->num_matched]);
229 }
230
231 strncpy(pt, cd->completed, buf->data + buf->dsize - pt - spaces);
232 buf_fix_dptr(buf);
233 }
234 else if (buf_startswith(buf, "exec"))
235 {
236 const enum MenuType mtype = menu_get_current_type();
237 const struct MenuFuncOp *funcs = km_get_table(mtype);
238 if (!funcs && (mtype != MENU_PAGER))
239 funcs = OpGeneric;
240
241 pt++;
242 /* first TAB. Collect all the matches */
243 if (numtabs == 1)
244 {
245 cd->num_matched = 0;
246 mutt_str_copy(cd->user_typed, pt, sizeof(cd->user_typed));
247 memset(cd->match_list, 0, cd->match_list_len);
248 memset(cd->completed, 0, sizeof(cd->completed));
249 for (int num = 0; funcs[num].name; num++)
250 candidate(cd, cd->user_typed, funcs[num].name, cd->completed, sizeof(cd->completed));
251 /* try the generic menu */
252 if ((mtype != MENU_PAGER) && (mtype != MENU_GENERIC))
253 {
254 funcs = OpGeneric;
255 for (int num = 0; funcs[num].name; num++)
256 candidate(cd, cd->user_typed, funcs[num].name, cd->completed,
257 sizeof(cd->completed));
258 }
260 cd->match_list[cd->num_matched++] = cd->user_typed;
261
262 /* All matches are stored. Longest non-ambiguous string is ""
263 * i.e. don't change 'buf'. Fake successful return this time */
264 if (cd->user_typed[0] == '\0')
265 return 1;
266 }
267
268 if ((cd->completed[0] == '\0') && (cd->user_typed[0] != '\0'))
269 return 0;
270
271 /* cd->num_matched will _always_ be at least 1 since the initial
272 * user-typed string is always stored */
273 if ((numtabs == 1) && (cd->num_matched == 2))
274 {
275 snprintf(cd->completed, sizeof(cd->completed), "%s", cd->match_list[0]);
276 }
277 else if ((numtabs > 1) && (cd->num_matched > 2))
278 {
279 /* cycle through all the matches */
280 snprintf(cd->completed, sizeof(cd->completed), "%s",
281 cd->match_list[(numtabs - 2) % cd->num_matched]);
282 }
283
284 strncpy(pt, cd->completed, buf->data + buf->dsize - pt - spaces);
285 buf_fix_dptr(buf);
286 }
287 else
288 {
289 return 0;
290 }
291
292 return 1;
293}
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:709
bool candidate(struct CompletionData *cd, char *user, const char *src, char *dest, size_t dlen)
Helper function for completion.
Definition: helpers.c:78
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:68
const struct MenuFuncOp * km_get_table(enum MenuType mtype)
Lookup a Menu's functions.
Definition: lib.c:525
#define FREE(x)
Definition: memory.h:55
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:230
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:496
#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
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:43
union HashKey key
Key representing the data.
Definition: hash.h:45
int type
Type of data stored in Hash Table, e.g. DT_STRING.
Definition: hash.h:44
Mapping between a function and an operation.
Definition: lib.h:101
const char * name
Name of the function.
Definition: lib.h:102
Container for Accounts, Notifications.
Definition: neomutt.h:42
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:46
struct HashElem ** get_elem_list(struct ConfigSet *cs)
Create a sorted list of all config items.
Definition: subset.c:79
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:52
#define DTYPE(t)
Definition: types.h:50
#define D_INTERNAL_DEPRECATED
Config item shouldn't be used any more.
Definition: types.h:88
@ DT_SYNONYM
synonym for another variable
Definition: types.h:46
const char * strkey
String key.
Definition: hash.h:35
+ 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 311 of file helpers.c.

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

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

Auto-Completion of Commands.

Definition at line 477 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:451

Auto-Completion of Labels.

Definition at line 484 of file helpers.c.